Skip to content

Commit

Permalink
Merge pull request #44 from DiamondLightSource/asyncio-run
Browse files Browse the repository at this point in the history
Update to new Backend API
  • Loading branch information
GDYendell authored Aug 19, 2024
2 parents 03c7030 + eda516c commit 42ee5ae
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 32 deletions.
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ classifiers = [
description = "Eiger control system integration with FastCS"
dependencies = [
"aiohttp",
"fastcs~=0.5.0",
"fastcs~=0.6.0",
"numpy",
"pillow",
"typer",
Expand All @@ -35,6 +35,7 @@ dev = [
"pytest",
"pytest-asyncio",
"pytest-cov",
"pytest-mock",
"ruff",
"sphinx-autobuild",
"sphinx-copybutton",
Expand Down
17 changes: 5 additions & 12 deletions src/eiger_fastcs/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from fastcs.backends.asyncio_backend import AsyncioBackend
from fastcs.backends.epics.backend import EpicsBackend
from fastcs.backends.epics.gui import EpicsGUIOptions
from fastcs.mapping import Mapping

from eiger_fastcs import __version__
from eiger_fastcs.eiger_controller import EigerController
Expand Down Expand Up @@ -51,27 +50,21 @@ def ioc(
):
ui_path = OPI_PATH if OPI_PATH.is_dir() else Path.cwd()

mapping = get_controller_mapping(ip, port)
controller = EigerController(ip, port)

backend = EpicsBackend(mapping, pv_prefix)
backend = EpicsBackend(controller, pv_prefix)
backend.create_gui(
EpicsGUIOptions(output_path=ui_path / "eiger.bob", title=f"Eiger - {pv_prefix}")
)
backend.get_ioc().run()
backend.run()


@app.command()
def asyncio(ip: str = EigerIp, port: int = EigerPort):
mapping = get_controller_mapping(ip, port)

backend = AsyncioBackend(mapping)
backend.run_interactive_session()


def get_controller_mapping(ip: str, port: int) -> Mapping:
controller = EigerController(ip, port)

return Mapping(controller)
backend = AsyncioBackend(controller)
backend.run_interactive_session()


# test with: python -m eiger_fastcs
Expand Down
19 changes: 2 additions & 17 deletions src/eiger_fastcs/eiger_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
from typing import Any, Literal

import numpy as np
from attr import Attribute
from fastcs.attributes import AttrR, AttrRW, AttrW
from fastcs.attributes import Attribute, AttrR, AttrRW, AttrW
from fastcs.controller import Controller
from fastcs.datatypes import Bool, Float, Int, String
from fastcs.wrappers import command, scan
Expand Down Expand Up @@ -177,14 +176,6 @@ def __init__(self, ip: str, port: int) -> None:
self._parameter_updates: set[str] = set()
self._parameter_update_lock = asyncio.Lock()

# Initialize parameters from hardware - run on ephemeral asyncio loop
# TODO: Make the backend asyncio loop available earlier
asyncio.run(self.initialise())

async def connect(self) -> None:
"""Reopen connection on backend asyncio loop"""
self.connection.open()

async def initialise(self) -> None:
"""Create attributes by introspecting detector.
Expand All @@ -197,7 +188,7 @@ async def initialise(self) -> None:
state_val = await self.connection.get("detector/api/1.8.0/status/state")
if state_val["value"] == "na":
print("Initializing Detector")
await self.connection.put("detector/api/1.8.0/command/initialize")
await self.initialize()

try:
parameters = await self._introspect_detector()
Expand All @@ -210,8 +201,6 @@ async def initialise(self) -> None:
for name, attribute in attributes.items():
setattr(self, name, attribute)

await self.connection.close()

async def _introspect_detector(self) -> list[EigerParameter]:
parameters = []
for subsystem, mode in product(
Expand Down Expand Up @@ -302,10 +291,6 @@ def _tag_key_clashes(parameters: list[EigerParameter]):
other.has_unique_key = False
break

async def close(self) -> None:
"""Closing HTTP connection with device"""
await self.connection.close()

@detector_command
async def initialize(self):
await self.connection.put(command_uri("initialize"))
Expand Down
13 changes: 11 additions & 2 deletions tests/system/test_introspection.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from time import sleep

import pytest
from fastcs.attributes import AttrR
from fastcs.datatypes import Float

from eiger_fastcs.eiger_controller import EigerController, EigerParameter

Expand Down Expand Up @@ -65,7 +67,7 @@ async def test_introspection(sim_eiger_controller: EigerController):
controller = sim_eiger_controller
# controller = eiger_controller

await controller.connect()
controller.connection.open()
_parameters = await controller._introspect_detector()
controller._tag_key_clashes(_parameters)
parameters = {p.name: _serialise_parameter(p) for p in _parameters}
Expand All @@ -78,4 +80,11 @@ async def test_introspection(sim_eiger_controller: EigerController):

assert parameters == expected_parameters, "Detector API does not match"

await controller.close()
attributes = controller._create_attributes(_parameters)

assert len(attributes) == 91
assert isinstance(attributes["humidity"], AttrR)
assert isinstance(attributes["humidity"].datatype, Float)
assert attributes["humidity"]._group == "DetectorStatus"

await controller.connection.close()
45 changes: 45 additions & 0 deletions tests/test_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import pytest
from pytest_mock import MockerFixture

from eiger_fastcs.eiger_controller import EigerController


@pytest.mark.asyncio
async def test_initialise(mocker: MockerFixture):
controller = EigerController("127.0.0.1", 80)

connection = mocker.patch.object(controller, "connection")
connection.get = mocker.AsyncMock()
connection.get.return_value = {"value": "idle"}
initialize = mocker.patch.object(controller, "initialize")
introspect = mocker.patch.object(controller, "_introspect_detector")
create_attributes = mocker.patch.object(controller, "_create_attributes")
attr = mocker.MagicMock()
create_attributes.return_value = {"attr_name": attr}

await controller.initialise()

connection.get.assert_called_once_with("detector/api/1.8.0/status/state")
initialize.assert_not_called()
introspect.assert_awaited_once_with()
create_attributes.assert_called_once_with(introspect.return_value)
assert controller.attr_name == attr, "Attribute not added to controller"


@pytest.mark.asyncio
async def test_initialise_state_na(mocker: MockerFixture):
controller = EigerController("127.0.0.1", 80)

connection = mocker.patch.object(controller, "connection")
connection.get = mocker.AsyncMock()
connection.get.return_value = {"value": "na"}
initialize = mocker.patch.object(controller, "initialize")
introspect = mocker.patch.object(controller, "_introspect_detector")
create_attributes = mocker.patch.object(controller, "_create_attributes")

await controller.initialise()

connection.get.assert_called_once_with("detector/api/1.8.0/status/state")
initialize.assert_awaited_once_with()
introspect.assert_awaited_once_with()
create_attributes.assert_called_once_with(introspect.return_value)

0 comments on commit 42ee5ae

Please sign in to comment.