diff --git a/src/tickit_devices/eiger/eiger.py b/src/tickit_devices/eiger/eiger.py index 74a93e3a..22b78f9e 100644 --- a/src/tickit_devices/eiger/eiger.py +++ b/src/tickit_devices/eiger/eiger.py @@ -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] diff --git a/src/tickit_devices/eiger/eiger_adapters.py b/src/tickit_devices/eiger/eiger_adapters.py index f4a47f05..f1e927ee 100644 --- a/src/tickit_devices/eiger/eiger_adapters.py +++ b/src/tickit_devices/eiger/eiger_adapters.py @@ -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 @@ -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.""" @@ -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) @@ -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) diff --git a/tests/eiger/test_eiger_adapters.py b/tests/eiger/test_eiger_adapters.py index 931f1f0c..779fd2ca 100644 --- a/tests/eiger/test_eiger_adapters.py +++ b/tests/eiger/test_eiger_adapters.py @@ -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 @@ -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"]'