Skip to content

Commit

Permalink
Move get_changed_parameters logic to convenience function in tickit_d…
Browse files Browse the repository at this point in the history
…evices.eiger.eiger

Test response of get_changed_parameters serialises properly
  • Loading branch information
jsouter committed Oct 8, 2024
1 parent 5be9a1e commit 1c8ca55
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 85 deletions.
70 changes: 70 additions & 0 deletions src/tickit_devices/eiger/eiger.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,73 @@ def _set_state(self, state: State) -> None:

def _is_in_state(self, state: State) -> bool:
return self.get_state() is state


def get_changed_parameters(key: str) -> list[str]:
"""Get the list of parameters that may have changed as a result of putting
to the parameter provided.
Args:
key: string key of the changed parameter within the detector subsystem
Returns:
list[str]: a list of keys which may have been changed after a PUT request
"""
match key:
case "auto_summation":
return ["auto_summation", "frame_count_time"]
case "count_time" | "frame_time":
return [
"bit_depth_image",
"bit_depth_readout",
"count_time",
"countrate_correction_count_cutoff",
"frame_count_time",
"frame_time",
]
case "flatfield":
return ["flatfield", "threshold/1/flatfield"]
case "incident_energy" | "photon_energy":
return [
"element",
"flatfield",
"incident_energy",
"photon_energy",
"threshold/1/energy",
"threshold/1/flatfield",
"threshold/2/energy",
"threshold/2/flatfield",
"threshold_energy",
"wavelength",
]
case "pixel_mask":
return ["pixel_mask", "threshold/1/pixel_mask"]
case "threshold/1/flatfield":
return ["flatfield", "threshold/1/flatfield"]
case "roi_mode":
return ["count_time", "frame_time", "roi_mode"]
case "threshold_energy" | "threshold/1/energy":
return [
"flatfield",
"threshold/1/energy",
"threshold/1/flatfield",
"threshold/2/flatfield",
"threshold_energy",
]
case "threshold/2/energy":
return [
"flatfield",
"threshold/1/flatfield",
"threshold/2/energy",
"threshold/2/flatfield",
]
case "threshold/1/mode":
return ["threshold/1/mode", "threshold/difference/mode"]
case "threshold/2/mode":
return ["threshold/2/mode", "threshold/difference/mode"]
case "threshold/1/pixel_mask":
return ["pixel_mask", "threshold/1/pixel_mask"]
case "threshold/difference/mode":
return ["difference_mode"] # replicating API inconsistency
case _:
return [key]
89 changes: 5 additions & 84 deletions src/tickit_devices/eiger/eiger_adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from tickit.adapters.specifications import HttpEndpoint
from tickit.adapters.zmq import ZeroMqPushAdapter

from tickit_devices.eiger.eiger import EigerDevice
from tickit_devices.eiger.eiger import EigerDevice, get_changed_parameters
from tickit_devices.eiger.eiger_schema import SequenceComplete, construct_value
from tickit_devices.eiger.stream.eiger_stream import EigerStream
from tickit_devices.eiger.stream.eiger_stream_2 import EigerStream2
Expand All @@ -24,78 +24,6 @@ def command_404(key: str) -> str:

LOGGER = logging.getLogger("EigerAdapter")

_changed_parameters = { # if not given, changed parameter list for key is [key]
"auto_summation": ["auto_summation", "frame_count_time"],
"count_time": [
"bit_depth_image",
"bit_depth_readout",
"count_time",
"countrate_correction_count_cutoff",
"frame_count_time",
"frame_time",
],
"flatfield": ["flatfield", "threshold/1/flatfield"],
"frame_time": [
"bit_depth_image",
"bit_depth_readout",
"count_time",
"countrate_correction_count_cutoff",
"frame_count_time",
"frame_time",
],
"incident_energy": [
"element",
"flatfield",
"incident_energy",
"photon_energy",
"threshold/1/energy",
"threshold/1/flatfield",
"threshold/2/energy",
"threshold/2/flatfield",
"threshold_energy",
"wavelength",
],
"photon_energy": [
"element",
"flatfield",
"incident_energy",
"photon_energy",
"threshold/1/energy",
"threshold/1/flatfield",
"threshold/2/energy",
"threshold/2/flatfield",
"threshold_energy",
"wavelength",
],
"pixel_mask": ["pixel_mask", "threshold/1/pixel_mask"],
"roi_mode": ["count_time", "frame_time", "roi_mode"],
"threshold/1/energy": [
"flatfield",
"threshold/1/energy",
"threshold/1/flatfield",
"threshold/2/flatfield",
"threshold_energy",
],
"threshold/1/flatfield": ["flatfield", "threshold/1/flatfield"],
"threshold/1/mode": ["threshold/1/mode", "threshold/difference/mode"],
"threshold/1/pixel_mask": ["pixel_mask", "threshold/1/pixel_mask"],
"threshold/2/energy": [
"flatfield",
"threshold/1/flatfield",
"threshold/2/energy",
"threshold/2/flatfield",
],
"threshold/2/mode": ["threshold/2/mode", "threshold/difference/mode"],
"threshold_energy": [
"flatfield",
"threshold/1/energy",
"threshold/1/flatfield",
"threshold/2/flatfield",
"threshold_energy",
],
"threshold/difference/mode": ["difference_mode"], # replicating API inconsistency
}


class EigerRESTAdapter(HttpAdapter):
"""An Eiger adapter which parses the commands sent to the HTTP server."""
Expand Down Expand Up @@ -148,12 +76,9 @@ async def put_config(self, request: web.Request) -> web.Response:

LOGGER.debug("Set " + str(param) + " to " + str(attr))

if param in _changed_parameters:
list_of_params = _changed_parameters[param]
else:
list_of_params = [param]
changed_parameters = get_changed_parameters(param)

return web.json_response(serialize(list_of_params))
return web.json_response(serialize(changed_parameters))
else:
LOGGER.debug("Eiger has no config variable: " + str(param))
return web.json_response(status=404)
Expand Down Expand Up @@ -206,13 +131,9 @@ async def put_threshold_config(self, request: web.Request) -> web.Response:
LOGGER.debug(f"Set threshold/{threshold}{str(param)} to {str(attr)}")

full_param = f"threshold/{threshold}/{param}"
if full_param in _changed_parameters:
param_list = _changed_parameters[full_param]
print(full_param, param_list)
else:
param_list = [full_param]
changed_parameters = get_changed_parameters(full_param)

return web.json_response(serialize(param_list))
return web.json_response(serialize(changed_parameters))
else:
LOGGER.debug("Eiger has no config variable: " + str(param))
return web.json_response(status=404)
Expand Down
59 changes: 58 additions & 1 deletion tests/eiger/test_eiger_adapters.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest
from pytest_mock import MockerFixture

from tickit_devices.eiger.eiger import EigerDevice
from tickit_devices.eiger.eiger import EigerDevice, get_changed_parameters
from tickit_devices.eiger.eiger_adapters import EigerRESTAdapter, EigerZMQAdapter


Expand Down Expand Up @@ -72,3 +72,60 @@ async def test_rest_adapter_command_404(mocker: MockerFixture):
assert (await eiger_adapter.trigger_eiger(request)).status == 404
assert (await eiger_adapter.cancel_eiger(request)).status == 404
assert (await eiger_adapter.abort_eiger(request)).status == 404


@pytest.mark.asyncio
async def test_detector_put_responses(mocker: MockerFixture):
# test special keys have non-trivial response
custom_response_keys = [
"auto_summation",
"count_time",
"frame_time",
"flatfield",
"incident_energy",
"photon_energy",
"pixel_mask",
"threshold/1/flatfield",
"roi_mode",
"threshold_energy",
"threshold/1/energy",
"threshold/2/energy",
"threshold/1/mode",
"threshold/2/mode",
"threshold/1/pixel_mask",
"threshold/difference/mode",
]

for key in custom_response_keys:
assert get_changed_parameters(key) != [key]

assert get_changed_parameters("phi_start") == ["phi_start"]
assert get_changed_parameters("threshold/1/pixel_mask") == [
"pixel_mask",
"threshold/1/pixel_mask",
]
assert get_changed_parameters("threshold/difference/mode") == ["difference_mode"]

eiger_adapter = EigerRESTAdapter(EigerDevice())

request = mocker.MagicMock()
request.json = mocker.AsyncMock()

request.match_info = {"parameter_name": "count_time", "value": 1.0}
response = await eiger_adapter.put_config(request)
assert response.body == (
b'["bit_depth_image", "bit_depth_readout", "count_time",'
b' "countrate_correction_count_cutoff",'
b' "frame_count_time", "frame_time"]'
)
# trivial case just returns the single parameter
request.match_info = {"parameter_name": "phi_start", "value": 1.0}
response = await eiger_adapter.put_config(request)
assert response.body == b'["phi_start"]'

# test threshold responses work

request.match_info = {"parameter_name": "mode", "threshold": "1", "value": 1}
request.json = mocker.AsyncMock(return_value={"value": 1})
response = await eiger_adapter.put_threshold_config(request)
assert response.body == b'["threshold/1/mode", "threshold/difference/mode"]'

0 comments on commit 1c8ca55

Please sign in to comment.