diff --git a/pyproject.toml b/pyproject.toml index 454073e1bd2..7c8c22198cf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ dependencies = [ "scanspec>=0.7.3", "pyzmq==26.3.0", # Until we can move to RHEL 8 https://github.com/DiamondLightSource/mx-bluesky/issues/1139 "deepdiff", - "daq-config-server>=v1.0.0", # For getting Configuration settings. + "daq-config-server>=v1.1.2", ] dynamic = ["version"] diff --git a/src/dodal/devices/undulator.py b/src/dodal/devices/undulator.py index efa4557ac34..869c2b1218c 100644 --- a/src/dodal/devices/undulator.py +++ b/src/dodal/devices/undulator.py @@ -3,6 +3,8 @@ import numpy as np from bluesky.protocols import Locatable, Location, Movable +from daq_config_server.client import ConfigServer +from daq_config_server.models import UndulatorEnergyGapLookupTable from numpy import ndarray from ophyd_async.core import ( AsyncStatus, @@ -19,7 +21,6 @@ from dodal.log import LOGGER from .baton import Baton -from .util.lookup_tables import energy_distance_table class AccessError(Exception): @@ -185,6 +186,7 @@ def __init__( baton (optional): Baton object if provided. name (str, optional): Name for device. Defaults to "". """ + self.config_server = ConfigServer(url="https://daq-config.diamond.ac.uk") self.id_gap_lookup_table_path = id_gap_lookup_table_path super().__init__( @@ -216,14 +218,17 @@ async def _get_gap_to_match_energy(self, energy_kev: float) -> float: get a 2d np.array from lookup table that converts energies to undulator gap distance """ - energy_to_distance_table: np.ndarray = await energy_distance_table( - self.id_gap_lookup_table_path + + energy_to_distance_table = self.config_server.get_file_contents( + self.id_gap_lookup_table_path, + UndulatorEnergyGapLookupTable, + reset_cached_result=True, ) # Use the lookup table to get the undulator gap associated with this dcm energy return _get_gap_for_energy( energy_kev * 1000, - energy_to_distance_table, + np.array(energy_to_distance_table.rows), ) diff --git a/tests/conftest.py b/tests/conftest.py index 6a80d6b92e6..5edac968e95 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ import importlib +import json import logging import os import sys @@ -8,6 +9,7 @@ from unittest.mock import MagicMock, patch import pytest +from daq_config_server.models import ConfigModel from ophyd_async.core import PathProvider from dodal.common.beamlines import beamline_parameters, beamline_utils @@ -159,3 +161,31 @@ def eiger_params(tmp_path: Path) -> DetectorParams: det_dist_to_beam_converter_path=TEST_LUT_TXT, detector_size_constants=EIGER2_X_16M_SIZE.det_type_string, # type: ignore ) + + +def _fake_config_server_get_file_contents( + filepath: str | Path, + desired_return_type: type[str] | type[dict] | ConfigModel = str, + reset_cached_result: bool = True, +): + filepath = Path(filepath) + # Minimal logic required for unit tests + with filepath.open("r") as f: + contents = f.read() + if desired_return_type is str: + return contents + elif desired_return_type is dict: + return json.loads(contents) + elif issubclass(desired_return_type, ConfigModel): # type: ignore + return desired_return_type.model_validate(json.loads(contents)) + + +@pytest.fixture(autouse=True) +def mock_config_server(): + # Don't actually talk to central service during unit tests, and reset caches between test + + with patch( + "daq_config_server.client.ConfigServer.get_file_contents", + side_effect=_fake_config_server_get_file_contents, + ): + yield diff --git a/tests/devices/i03/test_undulator_dcm.py b/tests/devices/i03/test_undulator_dcm.py index d167c45cc7a..19d27db517e 100644 --- a/tests/devices/i03/test_undulator_dcm.py +++ b/tests/devices/i03/test_undulator_dcm.py @@ -3,6 +3,7 @@ import numpy as np import pytest +from daq_config_server.models import UndulatorEnergyGapLookupTable from ophyd_async.core import AsyncStatus, get_mock_put, init_devices, set_mock_value from dodal.common.enums import EnabledDisabledUpper @@ -79,16 +80,23 @@ async def test_fixed_offset_decoded(fake_undulator_dcm: UndulatorDCM): assert fake_undulator_dcm.dcm_fixed_offset_mm == 25.6 -@patch("dodal.devices.util.lookup_tables.loadtxt") @patch("dodal.devices.undulator.LOGGER") +@patch("dodal.devices.undulator.ConfigServer.get_file_contents") async def test_if_gap_is_wrong_then_logger_info_is_called_and_gap_is_set_correctly( - mock_logger: MagicMock, mock_load: MagicMock, fake_undulator_dcm: UndulatorDCM + mock_get_file_contents: MagicMock, + mock_logger: MagicMock, + fake_undulator_dcm: UndulatorDCM, ): + mock_get_file_contents.return_value = UndulatorEnergyGapLookupTable( + rows=[ + [5700, 5.4606], + [7000, 6.045], + [9700, 6.404], + ], + ) set_mock_value(fake_undulator_dcm.undulator_ref().current_gap, 5.3) set_mock_value(fake_undulator_dcm.dcm_ref().energy_in_keV.user_readback, 5.7) - mock_load.return_value = np.array([[5700, 5.4606], [7000, 6.045], [9700, 6.404]]) - await fake_undulator_dcm.set(6.9) assert ( diff --git a/tests/devices/test_daq_configuration/lookup/BeamLineEnergy_DCM_Pitch_converter.txt b/tests/devices/test_daq_configuration/lookup/BeamLineEnergy_DCM_Pitch_converter.txt index 449e920f738..54d32ddbe2b 100644 --- a/tests/devices/test_daq_configuration/lookup/BeamLineEnergy_DCM_Pitch_converter.txt +++ b/tests/devices/test_daq_configuration/lookup/BeamLineEnergy_DCM_Pitch_converter.txt @@ -1,25 +1,23 @@ -# Bragg pitch -# Degree values for pitch are interpreted as mrad -# The values cannot change direction. -# last update 2023/06/26 NP -Units Deg mrad -Units Deg Deg -19.24347 -0.79775 -16.40949 -0.78679 -14.31123 -0.77838 -12.69287 -0.77276 -11.40555 -0.77276 -10.35662 -0.77031 -9.48522 -0.76693 -8.95826 -0.76387 -8.74953 -0.76387 -8.12020 -0.76387 -7.57556 -0.76354 -7.09950 -0.76166 -6.67997 -0.76044 -6.30732 -0.75953 -5.97411 -0.75845 -5.67434 -0.75796 -5.40329 -0.75789 -5.15700 -0.75551 -4.93218 -0.75513 +{ + "rows": [ + [19.24347, -0.79775], + [16.40949, -0.78679], + [14.31123, -0.77838], + [12.69287, -0.77276], + [11.40555, -0.77276], + [10.35662, -0.77031], + [9.48522, -0.76693], + [8.95826, -0.76387], + [8.74953, -0.76387], + [8.1202, -0.76387], + [7.57556, -0.76354], + [7.0995, -0.76166], + [6.67997, -0.76044], + [6.30732, -0.75953], + [5.97411, -0.75845], + [5.67434, -0.75796], + [5.40329, -0.75789], + [5.157, -0.75551], + [4.93218, -0.75513] + ] +} diff --git a/tests/devices/test_daq_configuration/lookup/BeamLineEnergy_DCM_Roll_converter.txt b/tests/devices/test_daq_configuration/lookup/BeamLineEnergy_DCM_Roll_converter.txt index 9b5b52dcb78..7f2a6fc2f95 100644 --- a/tests/devices/test_daq_configuration/lookup/BeamLineEnergy_DCM_Roll_converter.txt +++ b/tests/devices/test_daq_configuration/lookup/BeamLineEnergy_DCM_Roll_converter.txt @@ -1,6 +1,6 @@ -#Bragg angle against roll( absolute number) -#reloadLookupTables() -# last update 2023/01/19 NP -Units Deg mrad -26.4095 -0.2799 -6.3075 -0.2799 +{ + "rows": [ + [26.4095, -0.2799], + [6.3075, -0.2799] + ] +} diff --git a/tests/devices/test_data/test_beamline_undulator_to_gap_lookup_table.txt b/tests/devices/test_data/test_beamline_undulator_to_gap_lookup_table.txt index 66a4fb684ee..21f20e04948 100644 --- a/tests/devices/test_data/test_beamline_undulator_to_gap_lookup_table.txt +++ b/tests/devices/test_data/test_beamline_undulator_to_gap_lookup_table.txt @@ -1,60 +1,54 @@ -####################### -# # -# 5.5mm CPMU 20/11/22 # -# # -####################### -# Used to convert from energy to gap. Constructed from tables for 3rd, 5th and 7th harmonic. -# It is important that at the point of change from one harmonic to another that there is -# point for the same energy from both harmomics to prevent invalid interpolation. -# run reloadLookupTables() when done -Units eV mm -5700 5.4606 -5760 5.5 -6000 5.681 -6500 6.045 -7000 6.404 -7500 6.765 -8000 7.124 -8500 7.491 -9000 7.872 -9500 8.258 -9700 8.424 -9700 5.542 -10000 5.675 -10500 5.895 -11000 6.113 -11500 6.328 -12000 6.545 -12500 6.758 -12700 6.843 -13000 6.98 -13443 7.168 -13443 5.5 -13500 5.517 -14000 5.674 -14500 5.831 -15000 5.987 -15500 6.139 -16000 6.294 -16500 6.447 -17000 6.603 -17320 6.697 -17320 5.5 -17500 5.552 -18000 5.674 -18500 5.794 -19000 5.912 -19500 6.037 -20000 6.157 -20500 6.277 -20939 6.378 -20939 5.5 -21000 5.517 -21500 5.577 -22000 5.674 -22500 5.773 -23000 5.871 -23500 5.97 -24000 6.072 -24500 6.167 -25000 6.264 +{ + "rows": [ + [5700, 5.4606], + [5760, 5.5], + [6000, 5.681], + [6500, 6.045], + [7000, 6.404], + [7500, 6.765], + [8000, 7.124], + [8500, 7.491], + [9000, 7.872], + [9500, 8.258], + [9700, 8.424], + [9700, 5.542], + [10000, 5.675], + [10500, 5.895], + [11000, 6.113], + [11500, 6.328], + [12000, 6.545], + [12500, 6.758], + [12700, 6.843], + [13000, 6.98], + [13443, 7.168], + [13443, 5.5], + [13500, 5.517], + [14000, 5.674], + [14500, 5.831], + [15000, 5.987], + [15500, 6.139], + [16000, 6.294], + [16500, 6.447], + [17000, 6.603], + [17320, 6.697], + [17320, 5.5], + [17500, 5.552], + [18000, 5.674], + [18500, 5.794], + [19000, 5.912], + [19500, 6.037], + [20000, 6.157], + [20500, 6.277], + [20939, 6.378], + [20939, 5.5], + [21000, 5.517], + [21500, 5.577], + [22000, 5.674], + [22500, 5.773], + [23000, 5.871], + [23500, 5.97], + [24000, 6.072], + [24500, 6.167], + [25000, 6.264] + ] +} diff --git a/tests/devices/test_undulator.py b/tests/devices/test_undulator.py index b28d6496075..5a9aaf7ba3e 100644 --- a/tests/devices/test_undulator.py +++ b/tests/devices/test_undulator.py @@ -1,10 +1,11 @@ from collections.abc import Generator -from unittest.mock import AsyncMock, patch +from unittest.mock import MagicMock, patch import numpy as np import pytest from bluesky import RunEngine from bluesky.plan_stubs import mv +from daq_config_server.models import UndulatorEnergyGapLookupTable from ophyd_async.core import get_mock_put, init_devices, set_mock_value from ophyd_async.testing import ( assert_configuration, @@ -151,13 +152,14 @@ async def test_when_gap_access_is_disabled_set_then_error_is_raised( await undulator.set(5) -@patch( - "dodal.devices.undulator.energy_distance_table", - AsyncMock(return_value=np.array([[0, 10], [10, 20]])), -) +@patch("dodal.devices.undulator.ConfigServer.get_file_contents") async def test_gap_access_check_disabled_and_move_inhibited_when_commissioning_mode_enabled( + mock_get_file_contents: MagicMock, undulator_in_commissioning_mode: UndulatorInKeV, ): + mock_get_file_contents.return_value = UndulatorEnergyGapLookupTable( + rows=[[0, 10], [10, 20]] + ) set_mock_value( undulator_in_commissioning_mode.gap_access, EnabledDisabledUpper.DISABLED ) @@ -168,13 +170,14 @@ async def test_gap_access_check_disabled_and_move_inhibited_when_commissioning_m ).assert_not_called() -@patch( - "dodal.devices.undulator.energy_distance_table", - AsyncMock(return_value=np.array([[0, 10], [10000, 20]])), -) +@patch("dodal.devices.undulator.ConfigServer.get_file_contents") async def test_gap_access_check_move_not_inhibited_when_commissioning_mode_disabled( + mock_get_file_contents: MagicMock, undulator: UndulatorInKeV, ): + mock_get_file_contents.return_value = UndulatorEnergyGapLookupTable( + rows=[[0, 10], [10000, 20]] + ) set_mock_value(undulator.gap_access, EnabledDisabledUpper.ENABLED) await undulator.set(5) diff --git a/tests/devices/util/test_lookup_tables.py b/tests/devices/util/test_lookup_tables.py index 35a78a0f036..b326c50352d 100644 --- a/tests/devices/util/test_lookup_tables.py +++ b/tests/devices/util/test_lookup_tables.py @@ -1,8 +1,10 @@ +import numpy as np import pytest +from daq_config_server.client import ConfigServer +from daq_config_server.models import UndulatorEnergyGapLookupTable from pytest import mark from dodal.devices.util.lookup_tables import ( - energy_distance_table, linear_extrapolation_lut, linear_interpolation_lut, parse_lookup_table, @@ -21,7 +23,12 @@ async def test_energy_to_distance_table_correct_format(): - table = await energy_distance_table(TEST_BEAMLINE_UNDULATOR_TO_GAP_LUT) + config_server = ConfigServer() + table = np.array( + config_server.get_file_contents( + TEST_BEAMLINE_UNDULATOR_TO_GAP_LUT, UndulatorEnergyGapLookupTable + ).rows + ) assert table[0][0] == 5700 assert table[49][1] == 6.264 assert table.shape == (50, 2)