From b4dbd8e515cf46d7f51d65fad9bdc834ca396ffe Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 9 Dec 2025 09:37:13 +0000 Subject: [PATCH 01/34] Add mock config server fixture --- tests/conftest.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 0563b9cae39..00c15d98876 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.converters.models import ConfigModel from ophyd_async.core import ( PathProvider, ) @@ -167,3 +169,33 @@ 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() + print(contents) + if desired_return_type is str: + return contents + elif desired_return_type is dict: + print("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 From bf08ced66ca14ee181900cf1ce6b0b8e2e0baa7f Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 9 Dec 2025 10:24:27 +0000 Subject: [PATCH 02/34] Use config server to read beamlineParmaeters --- .../common/beamlines/beamline_parameters.py | 39 +++---------------- 1 file changed, 6 insertions(+), 33 deletions(-) diff --git a/src/dodal/common/beamlines/beamline_parameters.py b/src/dodal/common/beamlines/beamline_parameters.py index fb66035e4f4..31f76b06c37 100644 --- a/src/dodal/common/beamlines/beamline_parameters.py +++ b/src/dodal/common/beamlines/beamline_parameters.py @@ -1,6 +1,8 @@ import ast from typing import Any, cast +from daq_config_server.client import ConfigServer + from dodal.log import LOGGER from dodal.utils import get_beamline_name @@ -24,41 +26,12 @@ def __repr__(self) -> str: def __getitem__(self, item: str): return self.params[item] - @classmethod - def from_lines(cls, file_name: str, config_lines: list[str]): - config_lines_nocomments = [line.split("#", 1)[0] for line in config_lines] - config_lines_sep_key_and_value = [ - # XXX removes all whitespace instead of just trim - line.translate(str.maketrans("", "", " \n\t\r")).split("=") - for line in config_lines_nocomments - ] - config_pairs: list[tuple[str, Any]] = [ - cast(tuple[str, Any], param) - for param in config_lines_sep_key_and_value - if len(param) == 2 - ] - for i, (param, value) in enumerate(config_pairs): - try: - # BEAMLINE_PARAMETER_KEYWORDS effectively raw string but whitespace removed - if value not in BEAMLINE_PARAMETER_KEYWORDS: - config_pairs[i] = ( - param, - cls.parse_value(value), - ) - except Exception as e: - LOGGER.warning(f"Unable to parse {file_name} line {i}: {e}") - - return cls(params=dict(config_pairs)) - @classmethod def from_file(cls, path: str): - with open(path) as f: - config_lines = f.readlines() - return cls.from_lines(path, config_lines) - - @classmethod - def parse_value(cls, value: str): - return ast.literal_eval(value.replace("Yes", "True").replace("No", "False")) + config_server = ConfigServer(url="https://daq-config.diamond.ac.uk") + return cls( + params=config_server.get_file_contents(path, dict, reset_cached_result=True) + ) def get_beamline_parameters(beamline_param_path: str | None = None): From 91fc322159056b8d52c4d22ed4654c92476efdef Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 9 Dec 2025 10:29:13 +0000 Subject: [PATCH 03/34] Remove tests for removed functions --- .../beamlines/test_beamline_parameters.py | 60 ------------------- 1 file changed, 60 deletions(-) diff --git a/tests/common/beamlines/test_beamline_parameters.py b/tests/common/beamlines/test_beamline_parameters.py index aeba8295f1d..3af042869fb 100644 --- a/tests/common/beamlines/test_beamline_parameters.py +++ b/tests/common/beamlines/test_beamline_parameters.py @@ -34,40 +34,6 @@ def test_i03_beamline_parameters(): ] -@patch("dodal.common.beamlines.beamline_parameters.LOGGER") -def test_parse_exception_causes_warning(mock_logger): - params = GDABeamlineParameters.from_file(BAD_BEAMLINE_PARAMETERS) - assert params["flux_predict_polynomial_coefficients_5"] == [ - -0.0000707134131045123, - 7.0205491504418, - -194299.6440518530, - 1835805807.3974800, - -3280251055671.100, - ] - mock_logger.warning.assert_called_once() - - params = GDABeamlineParameters.from_file(BAD_BEAMLINE_PARAMETERS) - assert params["flux_predict_polynomial_coefficients_5"] == [ - -0.0000707134131045123, - 7.0205491504418, - -194299.6440518530, - 1835805807.3974800, - -3280251055671.100, - ] - - -def test_parse_list(): - test_data = [([1, 2, 3], "[1, 2, 3]"), ([1, True, 3], "[1, Yes, 3]")] - for expected, input in test_data: - actual = GDABeamlineParameters.parse_value(input) - assert expected == actual, f"Actual:{actual}, expected: {expected}\n" - - -def test_parse_list_raises_exception(): - with pytest.raises(SyntaxError): - GDABeamlineParameters.parse_value("[1, 2") - - def test_get_beamline_parameters_works_with_no_environment_variable_set(): if environ.get("BEAMLINE"): del environ["BEAMLINE"] @@ -108,32 +74,6 @@ def test_get_beamline_parameters_raises_error_when_beamline_not_found( get_beamline_parameters() -def test_parse_nested_list(): - actual = GDABeamlineParameters.parse_value("[[1, 2], [3, 4]]") - expected = [[1, 2], [3, 4]] - assert actual == expected - - -def test_parse_nested_nested_list(): - actual = GDABeamlineParameters.parse_value("[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]") - expected = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] - assert actual == expected - - -def test_leading_comma_in_list_causes_error(): - with pytest.raises(SyntaxError): - GDABeamlineParameters.parse_value("[,1, 2, 3, 4]") - with pytest.raises(SyntaxError): - GDABeamlineParameters.parse_value("[[1, 2], [ ,3, 4]]") - - -def test_Yes_and_No_replaced_with_bool_values(): # noqa: N802 - value = "[Yes, No, True, False, 0, 1]" - expected = [True, False, True, False, 0, 1] - actual = GDABeamlineParameters.parse_value(value) - assert actual == expected - - @pytest.fixture(autouse=True) def i03_beamline_parameters(): with patch.dict( From 386b521eb5babc69cba1ec94e1f5437c3d9bc5d8 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 9 Dec 2025 10:29:42 +0000 Subject: [PATCH 04/34] Convert test beamlineParameters files --- .../domain/beamlineParameters | 216 ++--- .../plan_stubs/test_data/topup_long_delay.txt | 6 +- .../test_data/topup_short_params.txt | 6 +- tests/test_data/i04_beamlineParameters | 786 +++++++----------- tests/test_data/test_beamline_parameters.txt | 483 +++++------ 5 files changed, 553 insertions(+), 944 deletions(-) diff --git a/tests/devices/test_daq_configuration/domain/beamlineParameters b/tests/devices/test_daq_configuration/domain/beamlineParameters index 0a783cd2f81..030fbcc9423 100644 --- a/tests/devices/test_daq_configuration/domain/beamlineParameters +++ b/tests/devices/test_daq_configuration/domain/beamlineParameters @@ -1,139 +1,77 @@ -# -# -BeamLine BL03S - -## Test data for device instantiation -BLSE=FB - -## BPFB (Beam Position FeedBack) -## HALF (default) only off during data collection -## FULL only off for XBPM2 during attenuation optimisation, fluo when trans < 2% and wedged MAD -## UNAVAILABLE (not default) prevents xbpm_feedback.py trying to access EPICS IOC that may not be running -BPFB=FULL -## Note: only beamline scientists control whether feedback is enabled -## via the XBPM feedback EDM screen in Synoptic - -# DCM parameters -DCM_Perp_Offset_FIXED = 25.6 -# -# beamstop -# -parked_x = 4.49 -parked_y = -50.0 -parked_y_plate = -50.5 -parked_z = -49.5 -parked_z_robot = 30.0 - -in_beam_z_MIN_START_POS = 60.0 - - -#Aperture - Scatterguard positions -# 100 micron ap -miniap_x_LARGE_APERTURE = 2.389 -miniap_y_LARGE_APERTURE = 40.986 -miniap_z_LARGE_APERTURE = 15.8 - -sg_x_LARGE_APERTURE = 5.25 -sg_y_LARGE_APERTURE = 4.43 - -# 50 micron ap -miniap_x_MEDIUM_APERTURE = 2.384 -miniap_y_MEDIUM_APERTURE = 44.967 -miniap_z_MEDIUM_APERTURE = 15.8 -sg_x_MEDIUM_APERTURE = 5.285 -sg_y_MEDIUM_APERTURE = 0.46 - -# 20 micron ap -miniap_x_SMALL_APERTURE = 2.430 -miniap_y_SMALL_APERTURE = 48.974 -miniap_z_SMALL_APERTURE = 15.8 -sg_x_SMALL_APERTURE = 5.3375 -sg_y_SMALL_APERTURE = -3.55 - -# Robot load -miniap_x_ROBOT_LOAD = 2.386 -miniap_y_ROBOT_LOAD = 31.40 -miniap_z_ROBOT_LOAD = 15.8 -sg_x_ROBOT_LOAD = 5.25 -sg_y_ROBOT_LOAD = 4.43 - -# manual mount -miniap_x_MANUAL_LOAD = -4.91 -miniap_y_MANUAL_LOAD = -49.0 -miniap_z_MANUAL_LOAD = -10.0 - -sg_x_MANUAL_LOAD = -4.7 -sg_y_MANUAL_LOAD = 1.8 - -miniap_x_SCIN_MOVE = -4.91 -# prion setting -#miniap_x_SCIN_MOVE = 0.0 -sg_x_SCIN_MOVE = -4.75 - -scin_y_SCIN_IN = 100.855 -scin_y_SCIN_OUT = -0.02 -scin_z_SCIN_IN = 101.5115 - - -scin_z_SCIN_OUT = 0.1 - -#distance to move gonx,y,z when scintillator is put in with standard pins -# For old gonio: -gon_x_SCIN_OUT_DISTANCE = 1.0 -# For SmarGon: -gon_x_SCIN_OUT_DISTANCE_smargon = 1 - -gon_y_SCIN_OUT_DISTANCE = 2.0 -gon_z_SCIN_OUT_DISTANCE = -0.5 - -# StandardEnergy on i03 is 12700eV -StandardEnergy = 12700 - -keyence_max_attempts = 1 -# Move gonio 100 microns, see difference in keyence values -# Then do 100/difference, put that number below -# Sign may change between Smargon and MiniKappa -keyence_slopeYToX = 2.5 -keyence_slopeYToY = -2.5 -keyence_slopeXToZ = 3.23 - -YAGSamX = 1022 -YAGSamY = -98.0 -YAGSamZ = -147 -YAGOmega = 0.0 - -#ipin value must be < ipin_threshold above background for data collection -ipin_threshold = 0.1 - -# energy thresholds for mirror stripes -# - first threshold is between bare/Rh stripes (e.g. 7000) -# - second threshold is between Rh/Pt stripes (e.g. 18000) -mirror_threshold_bare_rh = 6900 -mirror_threshold_rh_pt = 30000 - -# flux conversion factors -flux_factor_no_aperture = 1 -flux_factor_LARGE_APERTURE = 0.738 -flux_factor_MEDIUM_APERTURE = 0.36 -flux_factor_SMALL_APERTURE = 0.084 -flux_factor_no_aperture_plate = 1 -flux_factor_LARGE_APERTURE_plate = 0.738 -flux_factor_MEDIUM_APERTURE_plate = 0.36 -flux_factor_SMALL_APERTURE_plate = 0.084 - -#Deadtime settings -fluorescence_analyser_deadtimeThreshold=0.002 # used by edge scans -fluorescence_spectrum_deadtimeThreshold=0.0005 # used by spectrum - -#Other settings -fluorescence_attenuation_low_roi = 100 -fluorescence_attenuation_high_roi = 2048 -attenuation_optimisation_optimisation_cycles = 10 -attenuation_optimisation_start_transmission = 0.1 # per cent -fluorescence_mca_sca_offset = 400 - -#Total count settings -attenuation_optimisation_multiplier = 2 -attenuation_optimisation_target_count = 2000 -attenuation_optimisation_upper_limit = 50000 -attenuation_optimisation_lower_limit = 20000 +{ + "BLSE": "FB", + "BPFB": "FULL", + "DCM_Perp_Offset_FIXED": 25.6, + "parked_x": 4.49, + "parked_y": -50.0, + "parked_y_plate": -50.5, + "parked_z": -49.5, + "parked_z_robot": 30.0, + "in_beam_z_MIN_START_POS": 60.0, + "miniap_x_LARGE_APERTURE": 2.389, + "miniap_y_LARGE_APERTURE": 40.986, + "miniap_z_LARGE_APERTURE": 15.8, + "sg_x_LARGE_APERTURE": 5.25, + "sg_y_LARGE_APERTURE": 4.43, + "miniap_x_MEDIUM_APERTURE": 2.384, + "miniap_y_MEDIUM_APERTURE": 44.967, + "miniap_z_MEDIUM_APERTURE": 15.8, + "sg_x_MEDIUM_APERTURE": 5.285, + "sg_y_MEDIUM_APERTURE": 0.46, + "miniap_x_SMALL_APERTURE": 2.43, + "miniap_y_SMALL_APERTURE": 48.974, + "miniap_z_SMALL_APERTURE": 15.8, + "sg_x_SMALL_APERTURE": 5.3375, + "sg_y_SMALL_APERTURE": -3.55, + "miniap_x_ROBOT_LOAD": 2.386, + "miniap_y_ROBOT_LOAD": 31.4, + "miniap_z_ROBOT_LOAD": 15.8, + "sg_x_ROBOT_LOAD": 5.25, + "sg_y_ROBOT_LOAD": 4.43, + "miniap_x_MANUAL_LOAD": -4.91, + "miniap_y_MANUAL_LOAD": -49.0, + "miniap_z_MANUAL_LOAD": -10.0, + "sg_x_MANUAL_LOAD": -4.7, + "sg_y_MANUAL_LOAD": 1.8, + "miniap_x_SCIN_MOVE": -4.91, + "sg_x_SCIN_MOVE": -4.75, + "scin_y_SCIN_IN": 100.855, + "scin_y_SCIN_OUT": -0.02, + "scin_z_SCIN_IN": 101.5115, + "scin_z_SCIN_OUT": 0.1, + "gon_x_SCIN_OUT_DISTANCE": 1.0, + "gon_x_SCIN_OUT_DISTANCE_smargon": 1, + "gon_y_SCIN_OUT_DISTANCE": 2.0, + "gon_z_SCIN_OUT_DISTANCE": -0.5, + "StandardEnergy": 12700, + "keyence_max_attempts": 1, + "keyence_slopeYToX": 2.5, + "keyence_slopeYToY": -2.5, + "keyence_slopeXToZ": 3.23, + "YAGSamX": 1022, + "YAGSamY": -98.0, + "YAGSamZ": -147, + "YAGOmega": 0.0, + "ipin_threshold": 0.1, + "mirror_threshold_bare_rh": 6900, + "mirror_threshold_rh_pt": 30000, + "flux_factor_no_aperture": 1, + "flux_factor_LARGE_APERTURE": 0.738, + "flux_factor_MEDIUM_APERTURE": 0.36, + "flux_factor_SMALL_APERTURE": 0.084, + "flux_factor_no_aperture_plate": 1, + "flux_factor_LARGE_APERTURE_plate": 0.738, + "flux_factor_MEDIUM_APERTURE_plate": 0.36, + "flux_factor_SMALL_APERTURE_plate": 0.084, + "fluorescence_analyser_deadtimeThreshold": 0.002, + "fluorescence_spectrum_deadtimeThreshold": 0.0005, + "fluorescence_attenuation_low_roi": 100, + "fluorescence_attenuation_high_roi": 2048, + "attenuation_optimisation_optimisation_cycles": 10, + "attenuation_optimisation_start_transmission": 0.1, + "fluorescence_mca_sca_offset": 400, + "attenuation_optimisation_multiplier": 2, + "attenuation_optimisation_target_count": 2000, + "attenuation_optimisation_upper_limit": 50000, + "attenuation_optimisation_lower_limit": 20000 +} diff --git a/tests/plan_stubs/test_data/topup_long_delay.txt b/tests/plan_stubs/test_data/topup_long_delay.txt index 41eb4100971..2f66fe87f06 100644 --- a/tests/plan_stubs/test_data/topup_long_delay.txt +++ b/tests/plan_stubs/test_data/topup_long_delay.txt @@ -1,2 +1,4 @@ -dodal_topup_threshold_exposure_s = 30 -dodal_topup_end_delay_s = 19 +{ + "dodal_topup_threshold_exposure_s": 30, + "dodal_topup_end_delay_s": 19 +} \ No newline at end of file diff --git a/tests/plan_stubs/test_data/topup_short_params.txt b/tests/plan_stubs/test_data/topup_short_params.txt index 497b0d790e4..7707e91a61f 100644 --- a/tests/plan_stubs/test_data/topup_short_params.txt +++ b/tests/plan_stubs/test_data/topup_short_params.txt @@ -1,2 +1,4 @@ -dodal_topup_threshold_exposure_s = 35 -dodal_topup_end_delay_s = 1 +{ + "dodal_topup_threshold_exposure_s": 35, + "dodal_topup_end_delay_s": 1 +} \ No newline at end of file diff --git a/tests/test_data/i04_beamlineParameters b/tests/test_data/i04_beamlineParameters index 87beab7f2cf..3e24a3e69f5 100644 --- a/tests/test_data/i04_beamlineParameters +++ b/tests/test_data/i04_beamlineParameters @@ -1,503 +1,283 @@ -# -# -BeamLine BL04I - -## BLSE=FB switches between scan alignment and feedback alignment -## by creating bl energy scannable with beamLineSpecificEnergy_FB -## after changing you must restart servers or >>> reset_namespace -BLSE=FB - -## BPFB (Beam Position FeedBack) -## HALF (default) only off during data collection -## FULL only off for XBPM2 during attenuation optimisation, fluo when trans < 2% and wedged MAD -## UNAVAILABLE (not default) prevents /dls_sw/i04/software/gda/mx-config/scripts/xbpm_feedback.py trying to access EPICS IOC that may not be running -BPFB=FULL -## Note: only beamline scientists control whether feedback is enabled -## via the I04 XBPM feedback EDM screen in Synoptic - -DCM_Perp_Offset_FIXED = 25.75 - -# -# beamstop -# -parked_x = 4.98 #4.48 -parked_y =-49.1 -parked_z = -49.3 -parked_z_robot = 49.50 #55, 17/11/2020 value changed see Jira I04-421 - -in_beam_z_MIN_START_POS = 49.5 #40.0 - -in_beam_x_STANDARD = -2.7 #-2.8 -in_beam_y_STANDARD = 44.99 #44.98 #44.96 #44.95 #44.95 #44.64 #44.645 #44.63 #44.64 #44.68 #44.53 # 45.00 (11/Oct/2023) -in_beam_z_STANDARD = 25.0 - -in_beam_x_HIGHRES = -2.7 #2.50 #-3.84 -in_beam_y_HIGHRES = 44.99 #44.97 #44.96 #44.95 #44.95 #44.60 #44.61 #44.645 #44.63 #44.64 #44.68 #44.65(11/Oct/2023) -# #in_beam_z_HIGHRES = 12 -# # this is used for fluo spectra; original distance 0f 12.0 gives W contamination - in_beam_z_HIGHRES = 25.0 - - -in_beam_x_LOWRES = -2.75 -in_beam_y_LOWRES = 44.93 #44.92 #44.89 #44.90 #44.53 #44.55 #44.58 #474.57 #44.58 #44.61 #44.59 #44.48 (09/Oct/2023) -in_beam_z_LOWRES = 49.50 - - -## in_beam_col_tilt = -120.0 ## what is this????; This refers to the old end station and is no longer needed (RF) - -checkCryoy=Yes -#If is to be moved in by the script. If not Yes then control is handed to the robot on activate script -#To force the cryojet run hutch_utilities.hutch.forceCryoOut() -manualCryojet=Yes - -############################################################################### -# # -# 2015-07-03 - values to use during miniAPY failure # -# with no scatterguard or aperture during this period # -# # -############################################################################### -#Aperture - Scatterguard positions new block with 200, 20 and 10 micron ap's -#200 micron ap -#miniap_x_LARGE_APERTURE=-4.0 -#miniap_y_LARGE_APERTURE=-48.95 -#miniap_z_LARGE_APERTURE=-12.0 -#sg_x_LARGE_APERTURE=-3.0 -#sg_y_LARGE_APERTURE=-4.4 - - -# 20 micron ap - new block with 200, 20 and 10 micron ap's - -#miniap_x_MEDIUM_APERTURE=-4.0 -#miniap_y_MEDIUM_APERTURE=-48.95 -#miniap_z_MEDIUM_APERTURE=-12.0 -#sg_x_MEDIUM_APERTURE=-3.0 -#sg_y_MEDIUM_APERTURE=-4.4 - -# 10 micron ap - new block with 200, 20 and 10 micron ap's - REALLY 20 um as miniap_y cannot reach its position for 10 um -#miniap_x_SMALL_APERTURE=-4.0 -#miniap_y_SMALL_APERTURE=-48.95 -#miniap_z_SMALL_APERTURE=-12.0 -#sg_x_SMALL_APERTURE=-3.0 -#sg_y_SMALL_APERTURE=-4.4 - -# Robot load -#miniap_x_ROBOT_LOAD=-4.0 -#miniap_y_ROBOT_LOAD=-48.95 -#miniap_z_ROBOT_LOAD=-12.0 -#sg_x_ROBOT_LOAD=-3.0 -#sg_y_ROBOT_LOAD=-4.4 - -# manual mount -#miniap_x_MANUAL_LOAD=-4.0 -#miniap_y_MANUAL_LOAD=-48.95 -#miniap_z_MANUAL_LOAD=-12.0 -#sg_x_MANUAL_LOAD=-3.0 -#sg_y_MANUAL_LOAD=-4.4 - - - - -############################################################################### -# 2015-01-19 - 200,20, 10 CRLS - set so to use 200 micron all the time # -# # -# 2015-07-03 - commented out until miniapY is fixed - values above to work # -# with no scatterguard or aperture during this period # -# # -############################################################################### -#Aperture - Scatterguard positions new block with 200, 20 and 10 micron ap's -#200 micron ap updated 2023-04-26 -miniap_x_LARGE_APERTURE= 4.10 #4.13 # until March 2023 4.38 #4.34 #4.35 #4.34 #3.65 # 4.29 #4.500 #4.6843 #4.717 #4.7 -miniap_y_LARGE_APERTURE= 41.13 #41.14 #until March 2023 41.88 #41.81 #41.86 #41.88 #41.8184 #41.25 #41.5384 #41.801 #42.155 #40.7385 -miniap_z_LARGE_APERTURE=16.9 -sg_x_LARGE_APERTURE= 4.51 #4.66 #4.78 #4.800 #4.8 #4.4782 #4.85 #3.9 -sg_y_LARGE_APERTURE= 4.53 #4.637 #4.682 #4.137 #3.6589 #3.68 #3.4 - - -# 20 micron ap - new block with 200, 20 and 10 micron ap's - -miniap_x_MEDIUM_APERTURE=4.303 #4.65 #4.607 -miniap_y_MEDIUM_APERTURE=45.245 #46.168 #44.746 -miniap_z_MEDIUM_APERTURE=16.9 -sg_x_MEDIUM_APERTURE=4.04 #4.85 #3.88 -sg_y_MEDIUM_APERTURE=0.15 - -# 10 micron ap - new block with 200, 20 and 10 micron ap's - REALLY 20 um as miniap_y cannot reach its position for 10 um -miniap_x_SMALL_APERTURE=4.3 #4.605 #4.61 -miniap_y_SMALL_APERTURE=49.765 #50.13 -miniap_z_SMALL_APERTURE=16.9 -sg_x_SMALL_APERTURE=4.85 #3.9 -sg_y_SMALL_APERTURE=-4.25 #3.35 - - -# Robot load, see Jira ticket I04-421 -miniap_x_ROBOT_LOAD=-4.0 # -4.9 -miniap_y_ROBOT_LOAD=24.9 #0.0 #-48.95 #0.0 -miniap_z_ROBOT_LOAD=16.9 -sg_x_ROBOT_LOAD=-3.0 #-4.9 -sg_y_ROBOT_LOAD=-4.4 - -# manual mount -miniap_x_MANUAL_LOAD=-4.0 # -4.9 -miniap_y_MANUAL_LOAD=-48.95 #-49 -miniap_z_MANUAL_LOAD=-12. -sg_x_MANUAL_LOAD=-3.0 #-4.9 -sg_y_MANUAL_LOAD=-4.4 - -miniap_x_SCIN_MOVE=-4.0 # -4.9 -sg_x_SCIN_MOVE=-3.0 # -4.9 - -###I04 Scintillator### -scin_y_SCIN_IN= 97.45 #97.25 #97.1 #96.22 #93.42 #96.92 -scin_y_SCIN_OUT=-0.1 #-0.8 , 17/11/2020 value changed see Jira I04-421 -scin_z_SCIN_IN= 93.8 #93.81 #93.87 #93.97 # 15-11-22 Home done, scan scin z value -scin_z_SCIN_OUT=0.2 - -###Tomography Scintillator### -#scin_y_SCIN_IN=102.0 -#scin_y_SCIN_OUT=-0.1 -#scin_z_SCIN_IN=99.17 -#scin_z_SCIN_OUT=0.2 - - -#distance to move gonx,y,z when scintillator is put in with standard pins -#gon_x_SCIN_OUT_DISTANCE=0.5 -#use with mini kappa: -#gon_x_SCIN_OUT_DISTANCE_kappa = 1.5 - -# For SmarGon: -gon_x_SCIN_OUT_DISTANCE_smargon = 1 - -#Required for single axis because _smargon won't be used -#gon_x_SCIN_OUT_DISTANCE=1.0 - -# -gon_y_SCIN_OUT_DISTANCE=2 -gon_z_SCIN_OUT_DISTANCE=-1.5 - -# For SmarGon with EM Grid holder (13-03-2018): -#gon_x_SCIN_OUT_DISTANCE_smargon = 0 -## -#gon_y_SCIN_OUT_DISTANCE=0 -#gon_z_SCIN_OUT_DISTANCE=0 - - - -#distance to move gonx,y,z when scintillator is put in with crosshair wire mounted -#gon_x_SCIN_OUT_DISTANCE=-7 -#gon_y_SCIN_OUT_DISTANCE=0 -#gon_z_SCIN_OUT_DISTANCE=0 - - -#CASS motor position tolerances (mm) -miniap_x_tolerance=0.001 -miniap_y_tolerance=0.001 -miniap_z_tolerance=0.1 -sg_x_tolerance=0.1 -sg_y_tolerance=0.1 -scin_y_tolerance=1.2 -scin_z_tolerance=0.1 -gon_x_tolerance=0.01 -gon_y_tolerance=0.1 -gon_z_tolerance=0.001 -bs_x_tolerance=0.005 -bs_y_tolerance=0.005 -bs_z_tolerance=0.2 -crl_x_tolerance=0.01 -crl_y_tolerance=0.01 -crl_pitch_tolerance=0.01 -crl_yaw_tolerance=0.01 -sg_y_up_movement_tolerance=1.0 - -sg_x_timeout=10 -sg_y_timeout=10 -miniap_x_timeout=10 -miniap_y_timeout=80 -gon_x_timeout=60 -gon_y_timeout=30 -gon_z_timeout=30 -crl_x_timeout=120 -crl_y_timeout=10 -crl_pitch_timeout=10 -crl_yaw_timeout=10 - -## CRL positions for low and high energy lens sets. Should deliver beam to same position on scintillator. -## Normally should only adjust the low energy set to match the position of the high energy that you've -## already checked on the scintillator screen. - -#crl_x_LOWE=-7.337 -#crl_y_LOWE=0.785 -#crl_pitch_LOWE=3.030 -#crl_yaw_LOWE=7.245 - -############################################################################################ -# All values set to NOCRL position to avoid CRL being moved in beam when energy is changed -# until GDA bug is fixed -############################################################################################ - -crl_x_LOWE=0.0 -crl_y_LOWE=0.8277 -crl_pitch_LOWE=3.0065 -crl_yaw_LOWE=7.1015 - -crl_x_NOCRL = 0.0 -crl_y_NOCRL = 0.8277 -crl_pitch_NOCRL= 3.0065 -crl_yaw_NOCRL = 7.1015 - -crl_x_HIGHE=0.0 -crl_y_HIGHE=0.8277 -crl_pitch_HIGHE=3.0065 -crl_yaw_HIGHE=7.1015 - -### Positions with Mirrors #### -#crl_x_LOWE=-7.5 -#crl_y_LOWE=-1.65 -#crl_pitch_LOWE=1.4 -#crl_yaw_LOWE=0.04 -# -#crl_x_NOCRL = 0.0 -#crl_y_NOCRL = 0.8277 -#crl_pitch_NOCRL= 3.0065 -#crl_yaw_NOCRL = 7.1015 -# -#crl_x_HIGHE=6.4 -#crl_y_HIGHE=-1.55 -#crl_pitch_HIGHE=0.74 -#crl_yaw_HIGHE=-1.555 -################################# - - -#Beam visualisation parameters -MinBackStopZ = 10.0 -BackStopYsafe = 20.0 -BackStopXyag = -17.95 -BackStopYyag = 24.05 -BackStopZyag = 18.0 -SampleYnormal = 2.65 -SampleYshift = 2.0 -parked_fluo_x=1.1 -#in_beam_fluo_x=1.0086 -#in_beam_fluo_x=-35.0 -in_beam_fluo_x=-40.0 -move_fluo = Yes -safe_det_z_default=1000 -safe_det_z_sampleChanger=333 -store_data_collections_in_ispyb=Yes -TakePNGsOfSample=Yes - -#robot requires these values -gonio_parked_x=0.0 -gonio_parked_y=0.0 -gonio_parked_z=0.0 -gonio_parked_omega=0 -gonio_parked_kappa = -7.5 -gonio_parked_chi = 0 -gonio_parked_phi = 0 - -col_inbeam_tolerance = 1.0 - -#Run 3 2015 - Set offsets to 0 at 12658eV on 25/6/2015 - see standing instruction -col_parked_tolerance=1.0 -col_parked_upstream_x=0.0 -col_parked_downstream_x=0.0 -col_parked_upstream_y=0.0 -col_parked_inboard_y=0.0 -col_parked_outboard_y=0.0 - - -# The following used by setupBeamLine script -setupBeamLine_energyStart = 6000. -setupBeamLine_energyEnd = 18000. -setupBeamLine_energyStep = 500. -setupBeamLine_rollStart = -1.95 -setupBeamLine_rollEnd = -1.55 -setupBeamLine_rollSteps = 80 -setupBeamLine_pitchStart = -0.65 -setupBeamLine_pitchEnd = -0.45 -setupBeamLine_pitchSteps = 200 -#values below in microns -beamXCentre=0. -beamYCentre=0. -beamXYSettleTime=6.0 -beamXYTolerance=5.0 -DataCollection_TurboMode=Yes -#time in seconds. If not set then the default is 0.1 - -#The following are used by beamLineenergy script -beamLineEnergy_rollBeamX = 100 -beamLineEnergy_rollBeamY = 400 -beamLineEnergy__rollWidth = .075 -beamLineEnergy__rollStep = .005 -beamLineEnergy__pitchWidth = .025 -beamLineEnergy__pitchStep = .001 -beamLineEnergy__fpitchWidth = .02 -beamLineEnergy__fpitchStep = .001 -beamLineEnergy__adjustSlits=Yes - -# "Beam stabilising, data collection will resume in " ... -dataCollectionMinSampleCurrent=-100 -dataCollectionSampleCurrent XBPM1Intensity - -#Mark is using the following in some test scripts -MinIPin = 1.0 -YAGPin = 1 -RotationAxisPin = 2 -PtPin = 3 -PowderPin = 4 - -#################################################################### -# I04 standard use settings -# -# Do Not Edit/Delete - Ralf - 31/1/2013 -# -# iPin In positions, Mark is going to try and use these in scripts -iPinInDetX = 31.52 -iPinInDetYaw = 1.4542 -iPinInDetY = 93.0 -iPinInDetZ = 200.0 -###################################################################### - - -#################################################################### -# -# iPin Out positions - for diffraction data collection with ADSC with CRLS -# -#DataCollectionDetY = 58.7 -#DataCollectionDetX = -42.5498 -#DataCollectionDetXUpstream = -26.9237 -#DataCollectionDetXDownstream = -57.8741 -#DataCollectionDetYaw = -37.32719 -#################################################################### - -#################################################################### -# -# iPin Out positions - for diffraction data collection with ADSC with Mirrors -# -DataCollectionDetY = 89.7 -DataCollectionDetX = 27.4 -DataCollectionDetXUpstream = 26.4 -DataCollectionDetXDownstream = 28.402 -DataCollectionDetYaw = 2.4132 -#################################################################### - -#################################################################### -## I04 tomography settings - PCO camera -# -# values updated 07/07/12 -# iPin In positions, Mark is going to try and use these in scripts -#iPinInDetX = 8.854 -#iPinInDetYaw = -30.0909 -#iPinInDetY = 315.2 -#iPinInDetZ = 300.0 -#################################################################### - - -# StandardEnergy on i04 is 12658eV -StandardEnergy=12658 - - -keyence_max_attempts=1 -#Keyence on YtoX and YtoY needs changing is using single axis -#See comment in I04-532 for details -keyence_slopeYToX=6.78 -keyence_slopeYToY=-6.72 -keyence_slopeXToZ=8.37 - - -# WITH MIRRORS # -#hfm_bare_vert = 5.0 -#hfm_bare_yaw = 0.0 -#hfm_bare_roll = 0.0 -#hfm_rh_vert = 5.0 -#hfm_rh_yaw = 0.0 -#hfm_rh_roll = 0.0 -#hfm_pt_vert = 5.0 -#hfm_pt_yaw = 0.0 -#hfm_pt_roll = 0.0 - -#vfm_bare_lat = 2.000 -#vfm_bare_yaw = 0.0 -#vfm_bare_roll = 0.0 -#vfm_rh_lat = 15.00 -#vfm_rh_yaw = 0.0 -#vfm_rh_roll = 0.0 -#vfm_pt_lat = -10 -#vfm_pt_yaw = 0.0 -#vfm_pt_roll = 0.0 - -# WITH CRLS # -hfm_bare_vert = -30 -hfm_bare_yaw = -30.0 -hfm_bare_roll = -30.0 -hfm_rh_vert = -30.0 -hfm_rh_yaw = -30.0 -hfm_rh_roll = -30.0 -hfm_pt_vert = -30.0 -hfm_pt_yaw = -30.0 -hfm_pt_roll = -30.0 - -vfm_bare_lat = 15 -vfm_bare_yaw = 15 -vfm_bare_roll = 15 -vfm_rh_lat = 15 -vfm_rh_yaw = 15 -vfm_rh_roll = 15 -vfm_pt_lat = 15 -vfm_pt_yaw = 15 -vfm_pt_roll = 15 - -# energy thresholds for mirror stripes -# - first threshold is between bare/Rh stripes (e.g. 7000) -# - second threshold is between Rh/Pt stripes (e.g. 18000) -mirror_threshold_bare_rh = 6900 -mirror_threshold_rh_pt = 30000 - -# flux conversion factors -#flux_factor_no_aperture = 1.0 -flux_factor_LARGE_APERTURE = 1.0 -flux_factor_MEDIUM_APERTURE = 0.11765 -flux_factor_SMALL_APERTURE = 0.00914 -flux_scale_factor = 0.372 - -# assuming gain 10^3 -#pin_diode_factor = 3.2E12 original -#from cross-calibration with calibrated diode -pin_diode_factor = 2.83E12 - -#ipin value must be < ipin_threshold above background for data collection -ipin_threshold = 0.1 - -# Predict flux by energy and beamsize settings #I04-521 -# N.B. Left most coefficient (at index 0 in the collection / array) is the quartic term, the right most coefficient is the zeroth order "offset" term -# UPDATED 2022/Jul/15 with data from redis key i04:energy_flux:lookup:20220714 - -flux_predict_polynomial_coefficients_5 = [-0.0000707134131045123, 7.0205491504418, -194299.6440518530, 1835805807.3974800, -3280251055671.100] -flux_predict_polynomial_coefficients_10 = [-0.0000294993821003877, 5.2802845275010, -169996.5290700170, 1715224280.7823100, -3138739154146.230] -flux_predict_polynomial_coefficients_15 = [-0.000116949636502, 9.7753003322588, -254199.7776101, 2389060415.280310, -5025997585036.5] -flux_predict_polynomial_coefficients_20 = [-0.000148647038038, 11.2819868214984, -279103.295297639, 2545953771.80574, -5238247429860.13] -flux_predict_polynomial_coefficients_30 = [-0.000116165765376, 9.94125586103289, -260734.485522517, 2447741129.31429, -4986276938582.08] -flux_predict_polynomial_coefficients_40 = [-0.000343179106809, 21.5410025335892, -476062.885598809, 4148019661.82909, -9657928196914.84] -flux_predict_polynomial_coefficients_50 = [-0.000131960426420, 10.8653440810523, -280456.000029892, 2613195448.12884, -5280016683595.84] -flux_predict_polynomial_coefficients_75 = [-0.000391735497188, 24.7767312725528, -553079.202372348, 4894987195.36134, -11870695542358.4] -flux_predict_polynomial_coefficients_100 = [-0.000644176658542, 38.0955904622075, -809187.061558403, 6988666352.26412, -17740487002411.2] - -flux_predict_polynomial_coefficients_undulator_singularity = [0.0000155500286383152,-0.003037473267702,1.89061626835703] -flux_predict_polynomial_energyranges_undulator_singularity = [[7365,9275],[11080,12995]] - -# Fluorescence/Vortex detector settings -attenuation_optimisation_type = deadtime # deadtime or total_counts - -#Deadtime settings -fluorescence_analyser_deadtimeThreshold=0.0015 # used by edge scans -fluorescence_spectrum_deadtimeThreshold=0.0010 # used by spectrum - -#Other settings -fluorescence_attenuation_low_roi = 100 -fluorescence_attenuation_high_roi = 2047 -attenuation_optimisation_optimisation_cycles = 10 -attenuation_optimisation_start_transmission = 1 # per cent -fluorescence_mca_sca_offset = 200 - -#Total count settings -attenuation_optimisation_multiplier = 2 -attenuation_optimisation_target_count = 28000 -attenuation_optimisation_upper_limit = 50000 -attenuation_optimisation_lower_limit = 20000 +{ + "BLSE": "FB", + "BPFB": "FULL", + "DCM_Perp_Offset_FIXED": 25.75, + "parked_x": 4.98, + "parked_y": -49.1, + "parked_z": -49.3, + "parked_z_robot": 49.5, + "in_beam_z_MIN_START_POS": 49.5, + "in_beam_x_STANDARD": -2.7, + "in_beam_y_STANDARD": 44.99, + "in_beam_z_STANDARD": 25.0, + "in_beam_x_HIGHRES": -2.7, + "in_beam_y_HIGHRES": 44.99, + "in_beam_z_HIGHRES": 25.0, + "in_beam_x_LOWRES": -2.75, + "in_beam_y_LOWRES": 44.93, + "in_beam_z_LOWRES": 49.5, + "checkCryoy": true, + "manualCryojet": true, + "miniap_x_LARGE_APERTURE": 4.1, + "miniap_y_LARGE_APERTURE": 41.13, + "miniap_z_LARGE_APERTURE": 16.9, + "sg_x_LARGE_APERTURE": 4.51, + "sg_y_LARGE_APERTURE": 4.53, + "miniap_x_MEDIUM_APERTURE": 4.303, + "miniap_y_MEDIUM_APERTURE": 45.245, + "miniap_z_MEDIUM_APERTURE": 16.9, + "sg_x_MEDIUM_APERTURE": 4.04, + "sg_y_MEDIUM_APERTURE": 0.15, + "miniap_x_SMALL_APERTURE": 4.3, + "miniap_y_SMALL_APERTURE": 49.765, + "miniap_z_SMALL_APERTURE": 16.9, + "sg_x_SMALL_APERTURE": 4.85, + "sg_y_SMALL_APERTURE": -4.25, + "miniap_x_ROBOT_LOAD": -4.0, + "miniap_y_ROBOT_LOAD": 24.9, + "miniap_z_ROBOT_LOAD": 16.9, + "sg_x_ROBOT_LOAD": -3.0, + "sg_y_ROBOT_LOAD": -4.4, + "miniap_x_MANUAL_LOAD": -4.0, + "miniap_y_MANUAL_LOAD": -48.95, + "miniap_z_MANUAL_LOAD": -12.0, + "sg_x_MANUAL_LOAD": -3.0, + "sg_y_MANUAL_LOAD": -4.4, + "miniap_x_SCIN_MOVE": -4.0, + "sg_x_SCIN_MOVE": -3.0, + "scin_y_SCIN_IN": 97.45, + "scin_y_SCIN_OUT": -0.1, + "scin_z_SCIN_IN": 93.8, + "scin_z_SCIN_OUT": 0.2, + "gon_x_SCIN_OUT_DISTANCE_smargon": 1, + "gon_y_SCIN_OUT_DISTANCE": 2, + "gon_z_SCIN_OUT_DISTANCE": -1.5, + "miniap_x_tolerance": 0.001, + "miniap_y_tolerance": 0.001, + "miniap_z_tolerance": 0.1, + "sg_x_tolerance": 0.1, + "sg_y_tolerance": 0.1, + "scin_y_tolerance": 1.2, + "scin_z_tolerance": 0.1, + "gon_x_tolerance": 0.01, + "gon_y_tolerance": 0.1, + "gon_z_tolerance": 0.001, + "bs_x_tolerance": 0.005, + "bs_y_tolerance": 0.005, + "bs_z_tolerance": 0.2, + "crl_x_tolerance": 0.01, + "crl_y_tolerance": 0.01, + "crl_pitch_tolerance": 0.01, + "crl_yaw_tolerance": 0.01, + "sg_y_up_movement_tolerance": 1.0, + "sg_x_timeout": 10, + "sg_y_timeout": 10, + "miniap_x_timeout": 10, + "miniap_y_timeout": 80, + "gon_x_timeout": 60, + "gon_y_timeout": 30, + "gon_z_timeout": 30, + "crl_x_timeout": 120, + "crl_y_timeout": 10, + "crl_pitch_timeout": 10, + "crl_yaw_timeout": 10, + "crl_x_LOWE": 0.0, + "crl_y_LOWE": 0.8277, + "crl_pitch_LOWE": 3.0065, + "crl_yaw_LOWE": 7.1015, + "crl_x_NOCRL": 0.0, + "crl_y_NOCRL": 0.8277, + "crl_pitch_NOCRL": 3.0065, + "crl_yaw_NOCRL": 7.1015, + "crl_x_HIGHE": 0.0, + "crl_y_HIGHE": 0.8277, + "crl_pitch_HIGHE": 3.0065, + "crl_yaw_HIGHE": 7.1015, + "MinBackStopZ": 10.0, + "BackStopYsafe": 20.0, + "BackStopXyag": -17.95, + "BackStopYyag": 24.05, + "BackStopZyag": 18.0, + "SampleYnormal": 2.65, + "SampleYshift": 2.0, + "parked_fluo_x": 1.1, + "in_beam_fluo_x": -40.0, + "move_fluo": true, + "safe_det_z_default": 1000, + "safe_det_z_sampleChanger": 333, + "store_data_collections_in_ispyb": true, + "TakePNGsOfSample": true, + "gonio_parked_x": 0.0, + "gonio_parked_y": 0.0, + "gonio_parked_z": 0.0, + "gonio_parked_omega": 0, + "gonio_parked_kappa": -7.5, + "gonio_parked_chi": 0, + "gonio_parked_phi": 0, + "col_inbeam_tolerance": 1.0, + "col_parked_tolerance": 1.0, + "col_parked_upstream_x": 0.0, + "col_parked_downstream_x": 0.0, + "col_parked_upstream_y": 0.0, + "col_parked_inboard_y": 0.0, + "col_parked_outboard_y": 0.0, + "setupBeamLine_energyStart": 6000.0, + "setupBeamLine_energyEnd": 18000.0, + "setupBeamLine_energyStep": 500.0, + "setupBeamLine_rollStart": -1.95, + "setupBeamLine_rollEnd": -1.55, + "setupBeamLine_rollSteps": 80, + "setupBeamLine_pitchStart": -0.65, + "setupBeamLine_pitchEnd": -0.45, + "setupBeamLine_pitchSteps": 200, + "beamXCentre": 0.0, + "beamYCentre": 0.0, + "beamXYSettleTime": 6.0, + "beamXYTolerance": 5.0, + "DataCollection_TurboMode": true, + "beamLineEnergy_rollBeamX": 100, + "beamLineEnergy_rollBeamY": 400, + "beamLineEnergy__rollWidth": 0.075, + "beamLineEnergy__rollStep": 0.005, + "beamLineEnergy__pitchWidth": 0.025, + "beamLineEnergy__pitchStep": 0.001, + "beamLineEnergy__fpitchWidth": 0.02, + "beamLineEnergy__fpitchStep": 0.001, + "beamLineEnergy__adjustSlits": true, + "dataCollectionMinSampleCurrent": -100, + "MinIPin": 1.0, + "YAGPin": 1, + "RotationAxisPin": 2, + "PtPin": 3, + "PowderPin": 4, + "iPinInDetX": 31.52, + "iPinInDetYaw": 1.4542, + "iPinInDetY": 93.0, + "iPinInDetZ": 200.0, + "DataCollectionDetY": 89.7, + "DataCollectionDetX": 27.4, + "DataCollectionDetXUpstream": 26.4, + "DataCollectionDetXDownstream": 28.402, + "DataCollectionDetYaw": 2.4132, + "StandardEnergy": 12658, + "keyence_max_attempts": 1, + "keyence_slopeYToX": 6.78, + "keyence_slopeYToY": -6.72, + "keyence_slopeXToZ": 8.37, + "hfm_bare_vert": -30, + "hfm_bare_yaw": -30.0, + "hfm_bare_roll": -30.0, + "hfm_rh_vert": -30.0, + "hfm_rh_yaw": -30.0, + "hfm_rh_roll": -30.0, + "hfm_pt_vert": -30.0, + "hfm_pt_yaw": -30.0, + "hfm_pt_roll": -30.0, + "vfm_bare_lat": 15, + "vfm_bare_yaw": 15, + "vfm_bare_roll": 15, + "vfm_rh_lat": 15, + "vfm_rh_yaw": 15, + "vfm_rh_roll": 15, + "vfm_pt_lat": 15, + "vfm_pt_yaw": 15, + "vfm_pt_roll": 15, + "mirror_threshold_bare_rh": 6900, + "mirror_threshold_rh_pt": 30000, + "flux_factor_LARGE_APERTURE": 1.0, + "flux_factor_MEDIUM_APERTURE": 0.11765, + "flux_factor_SMALL_APERTURE": 0.00914, + "flux_scale_factor": 0.372, + "pin_diode_factor": 2830000000000.0, + "ipin_threshold": 0.1, + "flux_predict_polynomial_coefficients_5": [ + -7.07134131045123e-05, + 7.0205491504418, + -194299.644051853, + 1835805807.39748, + -3280251055671.1 + ], + "flux_predict_polynomial_coefficients_10": [ + -2.94993821003877e-05, + 5.280284527501, + -169996.529070017, + 1715224280.78231, + -3138739154146.23 + ], + "flux_predict_polynomial_coefficients_15": [ + -0.000116949636502, + 9.7753003322588, + -254199.7776101, + 2389060415.28031, + -5025997585036.5 + ], + "flux_predict_polynomial_coefficients_20": [ + -0.000148647038038, + 11.2819868214984, + -279103.295297639, + 2545953771.80574, + -5238247429860.13 + ], + "flux_predict_polynomial_coefficients_30": [ + -0.000116165765376, + 9.94125586103289, + -260734.485522517, + 2447741129.31429, + -4986276938582.08 + ], + "flux_predict_polynomial_coefficients_40": [ + -0.000343179106809, + 21.5410025335892, + -476062.885598809, + 4148019661.82909, + -9657928196914.84 + ], + "flux_predict_polynomial_coefficients_50": [ + -0.00013196042642, + 10.8653440810523, + -280456.000029892, + 2613195448.12884, + -5280016683595.84 + ], + "flux_predict_polynomial_coefficients_75": [ + -0.000391735497188, + 24.7767312725528, + -553079.202372348, + 4894987195.36134, + -11870695542358.4 + ], + "flux_predict_polynomial_coefficients_100": [ + -0.000644176658542, + 38.0955904622075, + -809187.061558403, + 6988666352.26412, + -17740487002411.2 + ], + "flux_predict_polynomial_coefficients_undulator_singularity": [ + 1.55500286383152e-05, + -0.003037473267702, + 1.89061626835703 + ], + "flux_predict_polynomial_energyranges_undulator_singularity": [ + [ + 7365, + 9275 + ], + [ + 11080, + 12995 + ] + ], + "attenuation_optimisation_type": "deadtime", + "fluorescence_analyser_deadtimeThreshold": 0.0015, + "fluorescence_spectrum_deadtimeThreshold": 0.001, + "fluorescence_attenuation_low_roi": 100, + "fluorescence_attenuation_high_roi": 2047, + "attenuation_optimisation_optimisation_cycles": 10, + "attenuation_optimisation_start_transmission": 1, + "fluorescence_mca_sca_offset": 200, + "attenuation_optimisation_multiplier": 2, + "attenuation_optimisation_target_count": 28000, + "attenuation_optimisation_upper_limit": 50000, + "attenuation_optimisation_lower_limit": 20000 +} diff --git a/tests/test_data/test_beamline_parameters.txt b/tests/test_data/test_beamline_parameters.txt index 5247b15d3fb..b757968398c 100644 --- a/tests/test_data/test_beamline_parameters.txt +++ b/tests/test_data/test_beamline_parameters.txt @@ -1,298 +1,185 @@ -# -# -BeamLine BL03I - -## BLSE=FB switches between scan alignment and feedback alignment -## by creating bl energy scannable with beamLineSpecificEnergy_FB -## after changing you must restart servers or >>> reset_namespace -BLSE=FB - -## BPFB (Beam Position FeedBack) -## HALF (default) only off during data collection -## FULL only off for XBPM2 during attenuation optimisation, fluo when trans < 2% and wedged MAD -## UNAVAILABLE (not default) prevents xbpm_feedback.py trying to access EPICS IOC that may not be running -BPFB=FULL -## Note: only beamline scientists control whether feedback is enabled -## via the XBPM feedback EDM screen in Synoptic - -# DCM parameters -DCM_Perp_Offset_FIXED = 25.6 -# -# beamstop -# -parked_x = 4.49 -parked_y = -50.0 -parked_y_plate = -50.5 -parked_z = -49.5 -parked_z_robot = 30.0 - -in_beam_z_MIN_START_POS = 60.0 - -in_beam_x_HIGHRES = 1.52 -in_beam_y_HIGHRES = 44.78 -in_beam_z_HIGHRES = 30.0 - -in_beam_x_STANDARD = 1.52 -in_beam_y_STANDARD = 44.78 -in_beam_z_STANDARD = 30.0 - -in_beam_x_LOWRES = 1.52 -in_beam_y_LOWRES = 44.78 -in_beam_z_LOWRES = 48 - -checkCryojet = No -#If is to be moved in by the script. If not Yes then control is handed to the robot on activate script -#To force the cryojet run hutch_utilities.hutch.forceCryoOut() -manualCryojet = Yes - -######################################################### -############# All these need checking! ############ -######################################################### - -#Aperture - Scatterguard positions -# 100 micron ap -miniap_x_LARGE_APERTURE = 2.389 -miniap_y_LARGE_APERTURE = 40.986 -miniap_z_LARGE_APERTURE = 15.8 - -sg_x_LARGE_APERTURE = 5.25 -sg_y_LARGE_APERTURE = 4.43 - -# 50 micron ap -miniap_x_MEDIUM_APERTURE = 2.384 -miniap_y_MEDIUM_APERTURE = 44.967 -miniap_z_MEDIUM_APERTURE = 15.8 -sg_x_MEDIUM_APERTURE = 5.285 -sg_y_MEDIUM_APERTURE = 0.46 - -# 20 micron ap -miniap_x_SMALL_APERTURE = 2.430 -miniap_y_SMALL_APERTURE = 48.974 -miniap_z_SMALL_APERTURE = 15.8 -sg_x_SMALL_APERTURE = 5.3375 -sg_y_SMALL_APERTURE = -3.55 - -# Robot load -miniap_x_ROBOT_LOAD = 2.386 -miniap_y_ROBOT_LOAD = 31.40 -miniap_z_ROBOT_LOAD = 15.8 -sg_x_ROBOT_LOAD = 5.25 -sg_y_ROBOT_LOAD = 4.43 - -# manual mount -miniap_x_MANUAL_LOAD = -4.91 -miniap_y_MANUAL_LOAD = -49.0 -miniap_z_MANUAL_LOAD = -10.0 - -sg_x_MANUAL_LOAD = -4.7 -sg_y_MANUAL_LOAD = 1.8 - -miniap_x_SCIN_MOVE = -4.91 -# prion setting -#miniap_x_SCIN_MOVE = 0.0 -sg_x_SCIN_MOVE = -4.75 - -scin_y_SCIN_IN = 100.855 -scin_y_SCIN_OUT = -0.02 -scin_z_SCIN_IN = 101.5115 - - -scin_z_SCIN_OUT = 0.1 - -#distance to move gonx,y,z when scintillator is put in with standard pins -# For old gonio: -gon_x_SCIN_OUT_DISTANCE = 1.0 -# For SmarGon: -gon_x_SCIN_OUT_DISTANCE_smargon = 1 - -gon_y_SCIN_OUT_DISTANCE = 2.0 -gon_z_SCIN_OUT_DISTANCE = -0.5 - -#CASS motor position tolerances (mm) -miniap_x_tolerance = 0.004 -miniap_y_tolerance = 0.1 -miniap_z_tolerance = 0.1 -sg_x_tolerance = 0.1 -sg_y_tolerance = 0.1 -scin_y_tolerance = 0.1 -scin_z_tolerance = 0.12 -gon_x_tolerance = 0.01 -gon_y_tolerance = 0.1 -gon_z_tolerance = 0.001 -bs_x_tolerance = 0.02 -bs_y_tolerance = 0.005 -bs_z_tolerance = 0.3 -crl_x_tolerance = 0.01 -crl_y_tolerance = 0.01 -crl_pitch_tolerance = 0.01 -crl_yaw_tolerance = 0.01 -sg_y_up_movement_tolerance = 1.0 - -sg_x_timeout = 10 -sg_y_timeout = 10 -miniap_x_timeout = 60 -miniap_y_timeout = 10 -gon_x_timeout = 60 -gon_y_timeout = 30 -gon_z_timeout = 30 -crl_x_timeout = 10 -crl_y_timeout = 10 -crl_pitch_timeout = 10 -crl_yaw_timeout = 10 - -col_inbeam_tolerance = 1.0 - -# robot load collimation table reference positions (mm) -col_parked_tolerance = 1.0 -col_parked_upstream_x = 0.0 -col_parked_downstream_x = 0.0 -col_parked_upstream_y = 0.0 -col_parked_inboard_y = 0.0 -col_parked_outboard_y = 0.0 - -## CRL positions for low and high energy lens sets. Should deliver beam to same position on scintillator. -## Normally should only adjust the low energy set to match the position of the high energy that you've -## already checked on the scintillator screen. - -crl_x_LOWE = -11.78 -crl_y_LOWE = -4.3 -crl_pitch_LOWE = -4.75 -crl_yaw_LOWE = -1.0 - -crl_x_HIGHE = 2.22 -crl_y_HIGHE = -4.30 -crl_pitch_HIGHE = -2.75 -crl_yaw_HIGHE = 0 - - -######################################################### -########## End of new parameters ########### -######################################################### - - -#Beam visualisation parameters -MinBackStopZ = 30.0 -BackStopYsafe = 20.0 -BackStopXyag = -4.8 -BackStopYyag = 17.20 -BackStopZyag = 19.1 -SampleYnormal = 2.65 -SampleYshift = 2.0 -parked_fluo_x = -18.0 -in_beam_fluo_x = 12.0 -move_fluo = Yes -safe_det_z_default = 900 -safe_det_z_sampleChanger = 337 -store_data_collections_in_ispyb = Yes -TakePNGsOfSample = Yes - -#robot requires these values -gonio_parked_x = 0.0 -gonio_parked_y = 0.0 -gonio_parked_z = 0.0 -gonio_parked_omega = 0 -gonio_parked_chi = 0 -gonio_parked_phi = 0 - -# The following used by setupBeamLine script -setupBeamLine_energyStart = 7000.0 -setupBeamLine_energyEnd = 17000.0 -setupBeamLine_energyStep = 500 -setupBeamLine_rollStart = -4 -setupBeamLine_rollEnd = 4 -setupBeamLine_rollSteps = 21 -setupBeamLine_pitchStart = -3.7 -setupBeamLine_pitchEnd = -3.5 -setupBeamLine_pitchSteps = 200 -#values below in microns -beamXCentre = 0 -beamYCentre = 0 -beamXYSettleTime = 6.0 -beamXYTolerance = 5.0 -DataCollection_TurboMode = Yes -#time in seconds. If not set then the default is 0.1 - -#The following are used by beamLineenergy script -beamLineEnergy_rollBeamX 50 -beamLineEnergy_rollBeamY 200 -beamLineEnergy__rollWidth = .2 -beamLineEnergy__rollStep = .02 -beamLineEnergy__pitchWidth = .02 -beamLineEnergy__pitchStep = .002 -beamLineEnergy__fpitchWidth = .02 -beamLineEnergy__fpitchStep = .001 -beamLineEnergy__adjustSlits = No -#dataCollectionMinSampleCurrent = 0.245 -dataCollectionMinSampleCurrent = 0.000 -dataCollectionSampleCurrent qbpm3 - -#Mark is using the following in some test scripts -MinIPin = 1.0 -YAGPin = 1 -RotationAxisPin = 2 -PtPin = 3 -PowderPin = 4 - -iPinInDetZ = 340.0 - -DataCollectionDetX = -7.8504 -DataCollectionDetYaw = 6.499 -DataCollectionDetY = 48.0 - -# StandardEnergy on i03 is 12700eV -StandardEnergy = 12700 - -keyence_max_attempts = 1 -# Move gonio 100 microns, see difference in keyence values -# Then do 100/difference, put that number below -# Sign may change between Smargon and MiniKappa -keyence_slopeYToX = 2.5 -keyence_slopeYToY = -2.5 -keyence_slopeXToZ = 3.23 - -YAGSamX = 1022 -YAGSamY = -98.0 -YAGSamZ = -147 -YAGOmega = 0.0 - -#ipin value must be < ipin_threshold above background for data collection -ipin_threshold = 0.1 - -# energy thresholds for mirror stripes -# - first threshold is between bare/Rh stripes (e.g. 7000) -# - second threshold is between Rh/Pt stripes (e.g. 18000) -mirror_threshold_bare_rh = 6900 -mirror_threshold_rh_pt = 30000 - -# flux conversion factors -flux_factor_no_aperture = 1 -flux_factor_LARGE_APERTURE = 0.738 -flux_factor_MEDIUM_APERTURE = 0.36 -flux_factor_SMALL_APERTURE = 0.084 -flux_factor_no_aperture_plate = 1 -flux_factor_LARGE_APERTURE_plate = 0.738 -flux_factor_MEDIUM_APERTURE_plate = 0.36 -flux_factor_SMALL_APERTURE_plate = 0.084 - -# assuming gain 10^3 -pin_diode_factor = 2.66E19 - -# Fluorescence/Vortex detector settings -attenuation_optimisation_type = deadtime # deadtime or total_counts - -#Deadtime settings -fluorescence_analyser_deadtimeThreshold=0.002 # used by edge scans -fluorescence_spectrum_deadtimeThreshold=0.0005 # used by spectrum - -#Other settings -fluorescence_attenuation_low_roi = 100 -fluorescence_attenuation_high_roi = 2048 -attenuation_optimisation_optimisation_cycles = 10 -attenuation_optimisation_start_transmission = 0.1 # per cent -fluorescence_mca_sca_offset = 400 - -#Total count settings -attenuation_optimisation_multiplier = 2 -attenuation_optimisation_target_count = 2000 -attenuation_optimisation_upper_limit = 50000 -attenuation_optimisation_lower_limit = 20000 +{ + "BLSE": "FB", + "BPFB": "FULL", + "DCM_Perp_Offset_FIXED": 25.6, + "parked_x": 4.49, + "parked_y": -50.0, + "parked_y_plate": -50.5, + "parked_z": -49.5, + "parked_z_robot": 30.0, + "in_beam_z_MIN_START_POS": 60.0, + "in_beam_x_HIGHRES": 1.52, + "in_beam_y_HIGHRES": 44.78, + "in_beam_z_HIGHRES": 30.0, + "in_beam_x_STANDARD": 1.52, + "in_beam_y_STANDARD": 44.78, + "in_beam_z_STANDARD": 30.0, + "in_beam_x_LOWRES": 1.52, + "in_beam_y_LOWRES": 44.78, + "in_beam_z_LOWRES": 48, + "checkCryojet": false, + "manualCryojet": true, + "miniap_x_LARGE_APERTURE": 2.389, + "miniap_y_LARGE_APERTURE": 40.986, + "miniap_z_LARGE_APERTURE": 15.8, + "sg_x_LARGE_APERTURE": 5.25, + "sg_y_LARGE_APERTURE": 4.43, + "miniap_x_MEDIUM_APERTURE": 2.384, + "miniap_y_MEDIUM_APERTURE": 44.967, + "miniap_z_MEDIUM_APERTURE": 15.8, + "sg_x_MEDIUM_APERTURE": 5.285, + "sg_y_MEDIUM_APERTURE": 0.46, + "miniap_x_SMALL_APERTURE": 2.43, + "miniap_y_SMALL_APERTURE": 48.974, + "miniap_z_SMALL_APERTURE": 15.8, + "sg_x_SMALL_APERTURE": 5.3375, + "sg_y_SMALL_APERTURE": -3.55, + "miniap_x_ROBOT_LOAD": 2.386, + "miniap_y_ROBOT_LOAD": 31.4, + "miniap_z_ROBOT_LOAD": 15.8, + "sg_x_ROBOT_LOAD": 5.25, + "sg_y_ROBOT_LOAD": 4.43, + "miniap_x_MANUAL_LOAD": -4.91, + "miniap_y_MANUAL_LOAD": -49.0, + "miniap_z_MANUAL_LOAD": -10.0, + "sg_x_MANUAL_LOAD": -4.7, + "sg_y_MANUAL_LOAD": 1.8, + "miniap_x_SCIN_MOVE": -4.91, + "sg_x_SCIN_MOVE": -4.75, + "scin_y_SCIN_IN": 100.855, + "scin_y_SCIN_OUT": -0.02, + "scin_z_SCIN_IN": 101.5115, + "scin_z_SCIN_OUT": 0.1, + "gon_x_SCIN_OUT_DISTANCE": 1.0, + "gon_x_SCIN_OUT_DISTANCE_smargon": 1, + "gon_y_SCIN_OUT_DISTANCE": 2.0, + "gon_z_SCIN_OUT_DISTANCE": -0.5, + "miniap_x_tolerance": 0.004, + "miniap_y_tolerance": 0.1, + "miniap_z_tolerance": 0.1, + "sg_x_tolerance": 0.1, + "sg_y_tolerance": 0.1, + "scin_y_tolerance": 0.1, + "scin_z_tolerance": 0.12, + "gon_x_tolerance": 0.01, + "gon_y_tolerance": 0.1, + "gon_z_tolerance": 0.001, + "bs_x_tolerance": 0.02, + "bs_y_tolerance": 0.005, + "bs_z_tolerance": 0.3, + "crl_x_tolerance": 0.01, + "crl_y_tolerance": 0.01, + "crl_pitch_tolerance": 0.01, + "crl_yaw_tolerance": 0.01, + "sg_y_up_movement_tolerance": 1.0, + "sg_x_timeout": 10, + "sg_y_timeout": 10, + "miniap_x_timeout": 60, + "miniap_y_timeout": 10, + "gon_x_timeout": 60, + "gon_y_timeout": 30, + "gon_z_timeout": 30, + "crl_x_timeout": 10, + "crl_y_timeout": 10, + "crl_pitch_timeout": 10, + "crl_yaw_timeout": 10, + "col_inbeam_tolerance": 1.0, + "col_parked_tolerance": 1.0, + "col_parked_upstream_x": 0.0, + "col_parked_downstream_x": 0.0, + "col_parked_upstream_y": 0.0, + "col_parked_inboard_y": 0.0, + "col_parked_outboard_y": 0.0, + "crl_x_LOWE": -11.78, + "crl_y_LOWE": -4.3, + "crl_pitch_LOWE": -4.75, + "crl_yaw_LOWE": -1.0, + "crl_x_HIGHE": 2.22, + "crl_y_HIGHE": -4.3, + "crl_pitch_HIGHE": -2.75, + "crl_yaw_HIGHE": 0, + "MinBackStopZ": 30.0, + "BackStopYsafe": 20.0, + "BackStopXyag": -4.8, + "BackStopYyag": 17.2, + "BackStopZyag": 19.1, + "SampleYnormal": 2.65, + "SampleYshift": 2.0, + "parked_fluo_x": -18.0, + "in_beam_fluo_x": 12.0, + "move_fluo": true, + "safe_det_z_default": 900, + "safe_det_z_sampleChanger": 337, + "store_data_collections_in_ispyb": true, + "TakePNGsOfSample": true, + "gonio_parked_x": 0.0, + "gonio_parked_y": 0.0, + "gonio_parked_z": 0.0, + "gonio_parked_omega": 0, + "gonio_parked_chi": 0, + "gonio_parked_phi": 0, + "setupBeamLine_energyStart": 7000.0, + "setupBeamLine_energyEnd": 17000.0, + "setupBeamLine_energyStep": 500, + "setupBeamLine_rollStart": -4, + "setupBeamLine_rollEnd": 4, + "setupBeamLine_rollSteps": 21, + "setupBeamLine_pitchStart": -3.7, + "setupBeamLine_pitchEnd": -3.5, + "setupBeamLine_pitchSteps": 200, + "beamXCentre": 0, + "beamYCentre": 0, + "beamXYSettleTime": 6.0, + "beamXYTolerance": 5.0, + "DataCollection_TurboMode": true, + "beamLineEnergy__rollWidth": 0.2, + "beamLineEnergy__rollStep": 0.02, + "beamLineEnergy__pitchWidth": 0.02, + "beamLineEnergy__pitchStep": 0.002, + "beamLineEnergy__fpitchWidth": 0.02, + "beamLineEnergy__fpitchStep": 0.001, + "beamLineEnergy__adjustSlits": false, + "dataCollectionMinSampleCurrent": 0.0, + "MinIPin": 1.0, + "YAGPin": 1, + "RotationAxisPin": 2, + "PtPin": 3, + "PowderPin": 4, + "iPinInDetZ": 340.0, + "DataCollectionDetX": -7.8504, + "DataCollectionDetYaw": 6.499, + "DataCollectionDetY": 48.0, + "StandardEnergy": 12700, + "keyence_max_attempts": 1, + "keyence_slopeYToX": 2.5, + "keyence_slopeYToY": -2.5, + "keyence_slopeXToZ": 3.23, + "YAGSamX": 1022, + "YAGSamY": -98.0, + "YAGSamZ": -147, + "YAGOmega": 0.0, + "ipin_threshold": 0.1, + "mirror_threshold_bare_rh": 6900, + "mirror_threshold_rh_pt": 30000, + "flux_factor_no_aperture": 1, + "flux_factor_LARGE_APERTURE": 0.738, + "flux_factor_MEDIUM_APERTURE": 0.36, + "flux_factor_SMALL_APERTURE": 0.084, + "flux_factor_no_aperture_plate": 1, + "flux_factor_LARGE_APERTURE_plate": 0.738, + "flux_factor_MEDIUM_APERTURE_plate": 0.36, + "flux_factor_SMALL_APERTURE_plate": 0.084, + "pin_diode_factor": 2.66e+19, + "attenuation_optimisation_type": "deadtime", + "fluorescence_analyser_deadtimeThreshold": 0.002, + "fluorescence_spectrum_deadtimeThreshold": 0.0005, + "fluorescence_attenuation_low_roi": 100, + "fluorescence_attenuation_high_roi": 2048, + "attenuation_optimisation_optimisation_cycles": 10, + "attenuation_optimisation_start_transmission": 0.1, + "fluorescence_mca_sca_offset": 400, + "attenuation_optimisation_multiplier": 2, + "attenuation_optimisation_target_count": 2000, + "attenuation_optimisation_upper_limit": 50000, + "attenuation_optimisation_lower_limit": 20000 +} From acd5805f0e9eb2c9bb6b17a2be06bdd0a6b7bae8 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 9 Dec 2025 10:29:52 +0000 Subject: [PATCH 05/34] Fix test --- tests/plan_stubs/test_topup_plan.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/plan_stubs/test_topup_plan.py b/tests/plan_stubs/test_topup_plan.py index 5be58415278..5ca1e7711b7 100644 --- a/tests/plan_stubs/test_topup_plan.py +++ b/tests/plan_stubs/test_topup_plan.py @@ -26,6 +26,10 @@ async def synchrotron() -> Synchrotron: @patch("dodal.plan_stubs.check_topup.wait_for_topup_complete") @patch("dodal.plan_stubs.check_topup.bps.sleep") +@patch( + "dodal.common.beamlines.beamline_parameters.BEAMLINE_PARAMETER_PATHS", + {"i03": TEST_BEAMLINE_PARAMETERS_TXT}, +) def test_when_topup_before_end_of_collection_wait( fake_sleep: MagicMock, fake_wait: MagicMock, From 28b7823a597aa95279a2e4f95d9f5387d9e0d11d Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 9 Dec 2025 10:53:55 +0000 Subject: [PATCH 06/34] Fix lint --- src/dodal/common/beamlines/beamline_parameters.py | 4 +--- tests/common/beamlines/test_beamline_parameters.py | 1 - tests/plan_stubs/test_data/topup_long_delay.txt | 2 +- tests/plan_stubs/test_data/topup_short_params.txt | 2 +- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/dodal/common/beamlines/beamline_parameters.py b/src/dodal/common/beamlines/beamline_parameters.py index 31f76b06c37..a29856f0460 100644 --- a/src/dodal/common/beamlines/beamline_parameters.py +++ b/src/dodal/common/beamlines/beamline_parameters.py @@ -1,9 +1,7 @@ -import ast -from typing import Any, cast +from typing import Any from daq_config_server.client import ConfigServer -from dodal.log import LOGGER from dodal.utils import get_beamline_name BEAMLINE_PARAMETER_KEYWORDS = ["FB", "FULL", "deadtime"] diff --git a/tests/common/beamlines/test_beamline_parameters.py b/tests/common/beamlines/test_beamline_parameters.py index 3af042869fb..b0e6132ed6a 100644 --- a/tests/common/beamlines/test_beamline_parameters.py +++ b/tests/common/beamlines/test_beamline_parameters.py @@ -8,7 +8,6 @@ get_beamline_parameters, ) from tests.test_data import ( - BAD_BEAMLINE_PARAMETERS, I04_BEAMLINE_PARAMETERS, TEST_BEAMLINE_PARAMETERS_TXT, ) diff --git a/tests/plan_stubs/test_data/topup_long_delay.txt b/tests/plan_stubs/test_data/topup_long_delay.txt index 2f66fe87f06..9a036944631 100644 --- a/tests/plan_stubs/test_data/topup_long_delay.txt +++ b/tests/plan_stubs/test_data/topup_long_delay.txt @@ -1,4 +1,4 @@ { "dodal_topup_threshold_exposure_s": 30, "dodal_topup_end_delay_s": 19 -} \ No newline at end of file +} diff --git a/tests/plan_stubs/test_data/topup_short_params.txt b/tests/plan_stubs/test_data/topup_short_params.txt index 7707e91a61f..0bbcbd9fa23 100644 --- a/tests/plan_stubs/test_data/topup_short_params.txt +++ b/tests/plan_stubs/test_data/topup_short_params.txt @@ -1,4 +1,4 @@ { "dodal_topup_threshold_exposure_s": 35, "dodal_topup_end_delay_s": 1 -} \ No newline at end of file +} From 84f08367d135255521fc59601f9fc2b6786f2896 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Mon, 12 Jan 2026 16:33:11 +0000 Subject: [PATCH 07/34] Fix --- tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 00c15d98876..eeede34f328 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,7 +9,7 @@ from unittest.mock import MagicMock, patch import pytest -from daq_config_server.converters.models import ConfigModel +from daq_config_server.models import ConfigModel from ophyd_async.core import ( PathProvider, ) From afd4fbc552b8a44a8bf2ceae1f5a5d7892077182 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Mon, 12 Jan 2026 16:47:39 +0000 Subject: [PATCH 08/34] Require latest daq-config-server --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 545b7399444..15f0a841825 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", # For getting Configuration settings. ] dynamic = ["version"] From 2d92790b1133ee47e9fea1d036608dbb751fee7a Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 20 Jan 2026 15:42:16 +0000 Subject: [PATCH 09/34] PR comments WIP --- src/dodal/common/beamlines/beamline_parameters.py | 6 ++++-- tests/conftest.py | 2 -- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dodal/common/beamlines/beamline_parameters.py b/src/dodal/common/beamlines/beamline_parameters.py index a29856f0460..d6632013a92 100644 --- a/src/dodal/common/beamlines/beamline_parameters.py +++ b/src/dodal/common/beamlines/beamline_parameters.py @@ -4,8 +4,10 @@ from dodal.utils import get_beamline_name -BEAMLINE_PARAMETER_KEYWORDS = ["FB", "FULL", "deadtime"] - +BEAMLINE_CONFIG_SERVER_ENDPOINTS = { + "i03": "https://daq-config.diamond.ac.uk", + "i04": "https://daq-config.diamond.ac.uk", +} BEAMLINE_PARAMETER_PATHS = { "i03": "/dls_sw/i03/software/daq_configuration/domain/beamlineParameters", "i04": "/dls_sw/i04/software/daq_configuration/domain/beamlineParameters", diff --git a/tests/conftest.py b/tests/conftest.py index eeede34f328..708c4aff340 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -180,11 +180,9 @@ def _fake_config_server_get_file_contents( # Minimal logic required for unit tests with filepath.open("r") as f: contents = f.read() - print(contents) if desired_return_type is str: return contents elif desired_return_type is dict: - print("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)) From e3f6fadbf2f2be36c182dea394089fda17c3cdaa Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Fri, 23 Jan 2026 13:35:31 +0000 Subject: [PATCH 10/34] Parameterise config server URL --- src/dodal/common/beamlines/beamline_parameters.py | 11 +++++++---- tests/common/beamlines/test_beamline_parameters.py | 4 ++-- tests/devices/mx_phase1/test_beamstop.py | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/dodal/common/beamlines/beamline_parameters.py b/src/dodal/common/beamlines/beamline_parameters.py index d6632013a92..d472cd6663f 100644 --- a/src/dodal/common/beamlines/beamline_parameters.py +++ b/src/dodal/common/beamlines/beamline_parameters.py @@ -27,8 +27,8 @@ def __getitem__(self, item: str): return self.params[item] @classmethod - def from_file(cls, path: str): - config_server = ConfigServer(url="https://daq-config.diamond.ac.uk") + def from_server(cls, path: str, url="https://daq-config.diamond.ac.uk"): + config_server = ConfigServer(url=url) return cls( params=config_server.get_file_contents(path, dict, reset_cached_result=True) ) @@ -37,11 +37,14 @@ def from_file(cls, path: str): def get_beamline_parameters(beamline_param_path: str | None = None): """Loads the beamline parameters from the specified path, or according to the environment variable if none is given""" + beamline_name = get_beamline_name("i03") + config_server_url = BEAMLINE_CONFIG_SERVER_ENDPOINTS.get( + beamline_name, "https://daq-config.diamond.ac.uk" + ) if not beamline_param_path: - beamline_name = get_beamline_name("i03") beamline_param_path = BEAMLINE_PARAMETER_PATHS.get(beamline_name) if beamline_param_path is None: raise KeyError( "No beamline parameter path found, maybe 'BEAMLINE' environment variable is not set!" ) - return GDABeamlineParameters.from_file(beamline_param_path) + return GDABeamlineParameters.from_server(beamline_param_path, config_server_url) diff --git a/tests/common/beamlines/test_beamline_parameters.py b/tests/common/beamlines/test_beamline_parameters.py index b0e6132ed6a..401272b604e 100644 --- a/tests/common/beamlines/test_beamline_parameters.py +++ b/tests/common/beamlines/test_beamline_parameters.py @@ -14,7 +14,7 @@ def test_beamline_parameters(): - params = GDABeamlineParameters.from_file(TEST_BEAMLINE_PARAMETERS_TXT) + params = GDABeamlineParameters.from_server(TEST_BEAMLINE_PARAMETERS_TXT) assert params["sg_x_MEDIUM_APERTURE"] == 5.285 assert params["col_parked_downstream_x"] == 0 assert params["beamLineEnergy__pitchStep"] == 0.002 @@ -23,7 +23,7 @@ def test_beamline_parameters(): def test_i03_beamline_parameters(): - params = GDABeamlineParameters.from_file(I04_BEAMLINE_PARAMETERS) + params = GDABeamlineParameters.from_server(I04_BEAMLINE_PARAMETERS) assert params["flux_predict_polynomial_coefficients_5"] == [ -0.0000707134131045123, 7.0205491504418, diff --git a/tests/devices/mx_phase1/test_beamstop.py b/tests/devices/mx_phase1/test_beamstop.py index 2b47ae91d61..155502b1cbc 100644 --- a/tests/devices/mx_phase1/test_beamstop.py +++ b/tests/devices/mx_phase1/test_beamstop.py @@ -15,7 +15,7 @@ @pytest.fixture def beamline_parameters() -> GDABeamlineParameters: - return GDABeamlineParameters.from_file(TEST_BEAMLINE_PARAMETERS_TXT) + return GDABeamlineParameters.from_server(TEST_BEAMLINE_PARAMETERS_TXT) @pytest.mark.parametrize( From 3dbb8815bf9b871335b277bf6ad9f9dc25d95cbb Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Fri, 23 Jan 2026 16:59:24 +0000 Subject: [PATCH 11/34] Refactor get_beamline_parameters --- src/dodal/beamlines/i03.py | 6 +- src/dodal/beamlines/i04.py | 6 +- .../common/beamlines/beamline_parameters.py | 43 +++-------- src/dodal/common/beamlines/config_client.py | 13 ++++ src/dodal/devices/aperturescatterguard.py | 8 +- src/dodal/devices/i03/undulator_dcm.py | 4 +- src/dodal/devices/mx_phase1/beamstop.py | 5 +- src/dodal/devices/scintillator.py | 4 +- src/dodal/plan_stubs/check_topup.py | 5 +- .../beamlines/test_beamline_parameters.py | 33 ++------- tests/devices/conftest.py | 73 +++++++++---------- tests/devices/mx_phase1/test_beamstop.py | 15 ++-- tests/devices/test_scintillator.py | 24 +++--- 13 files changed, 101 insertions(+), 138 deletions(-) create mode 100644 src/dodal/common/beamlines/config_client.py diff --git a/src/dodal/beamlines/i03.py b/src/dodal/beamlines/i03.py index 3987b4563d5..4a69d108328 100644 --- a/src/dodal/beamlines/i03.py +++ b/src/dodal/beamlines/i03.py @@ -95,7 +95,7 @@ def daq_configuration_path() -> str: @devices.factory() def aperture_scatterguard() -> ApertureScatterguard: - params = get_beamline_parameters() + params = get_beamline_parameters(BL) return ApertureScatterguard( aperture_prefix=f"{PREFIX.beamline_prefix}-MO-MAPT-01:", scatterguard_prefix=f"{PREFIX.beamline_prefix}-MO-SCAT-01:", @@ -116,7 +116,7 @@ def attenuator() -> BinaryFilterAttenuator: def beamstop() -> Beamstop: return Beamstop( prefix=f"{PREFIX.beamline_prefix}-MO-BS-01:", - beamline_parameters=get_beamline_parameters(), + beamline_parameters=get_beamline_parameters(BL), ) @@ -346,7 +346,7 @@ def scintillator(aperture_scatterguard: ApertureScatterguard) -> Scintillator: return Scintillator( f"{PREFIX.beamline_prefix}-MO-SCIN-01:", Reference(aperture_scatterguard), - get_beamline_parameters(), + get_beamline_parameters(BL), ) diff --git a/src/dodal/beamlines/i04.py b/src/dodal/beamlines/i04.py index 18cc611344b..c6ed15de735 100644 --- a/src/dodal/beamlines/i04.py +++ b/src/dodal/beamlines/i04.py @@ -99,7 +99,7 @@ def ipin() -> IPin: def beamstop() -> Beamstop: return Beamstop( f"{PREFIX.beamline_prefix}-MO-BS-01:", - beamline_parameters=get_beamline_parameters(), + beamline_parameters=get_beamline_parameters(BL), ) @@ -148,7 +148,7 @@ def backlight() -> Backlight: @devices.factory() def aperture_scatterguard() -> ApertureScatterguard: - params = get_beamline_parameters() + params = get_beamline_parameters(BL) return ApertureScatterguard( aperture_prefix=f"{PREFIX.beamline_prefix}-MO-MAPT-01:", scatterguard_prefix=f"{PREFIX.beamline_prefix}-MO-SCAT-01:", @@ -281,7 +281,7 @@ def scintillator(aperture_scatterguard: ApertureScatterguard) -> Scintillator: return Scintillator( f"{PREFIX.beamline_prefix}-MO-SCIN-01:", Reference(aperture_scatterguard), - get_beamline_parameters(), + get_beamline_parameters(BL), ) diff --git a/src/dodal/common/beamlines/beamline_parameters.py b/src/dodal/common/beamlines/beamline_parameters.py index d472cd6663f..77614acbcc6 100644 --- a/src/dodal/common/beamlines/beamline_parameters.py +++ b/src/dodal/common/beamlines/beamline_parameters.py @@ -1,50 +1,25 @@ from typing import Any -from daq_config_server.client import ConfigServer +from dodal.common.beamlines.config_client import get_config_client -from dodal.utils import get_beamline_name - -BEAMLINE_CONFIG_SERVER_ENDPOINTS = { - "i03": "https://daq-config.diamond.ac.uk", - "i04": "https://daq-config.diamond.ac.uk", -} BEAMLINE_PARAMETER_PATHS = { "i03": "/dls_sw/i03/software/daq_configuration/domain/beamlineParameters", "i04": "/dls_sw/i04/software/daq_configuration/domain/beamlineParameters", } -class GDABeamlineParameters: - params: dict[str, Any] - - def __init__(self, params: dict[str, Any]): - self.params = params - - def __repr__(self) -> str: - return repr(self.params) - - def __getitem__(self, item: str): - return self.params[item] - - @classmethod - def from_server(cls, path: str, url="https://daq-config.diamond.ac.uk"): - config_server = ConfigServer(url=url) - return cls( - params=config_server.get_file_contents(path, dict, reset_cached_result=True) - ) - - -def get_beamline_parameters(beamline_param_path: str | None = None): +def get_beamline_parameters( + beamline: str, beamline_param_path: str | None = None +) -> dict[str, Any]: """Loads the beamline parameters from the specified path, or according to the environment variable if none is given""" - beamline_name = get_beamline_name("i03") - config_server_url = BEAMLINE_CONFIG_SERVER_ENDPOINTS.get( - beamline_name, "https://daq-config.diamond.ac.uk" - ) if not beamline_param_path: - beamline_param_path = BEAMLINE_PARAMETER_PATHS.get(beamline_name) + beamline_param_path = BEAMLINE_PARAMETER_PATHS.get(beamline) if beamline_param_path is None: raise KeyError( "No beamline parameter path found, maybe 'BEAMLINE' environment variable is not set!" ) - return GDABeamlineParameters.from_server(beamline_param_path, config_server_url) + config_client = get_config_client(beamline) + return config_client.get_file_contents( + beamline_param_path, dict, reset_cached_result=True + ) diff --git a/src/dodal/common/beamlines/config_client.py b/src/dodal/common/beamlines/config_client.py new file mode 100644 index 00000000000..266c8b0214b --- /dev/null +++ b/src/dodal/common/beamlines/config_client.py @@ -0,0 +1,13 @@ +from daq_config_server.client import ConfigServer + +BEAMLINE_CONFIG_SERVER_ENDPOINTS = { + "i03": "https://daq-config.diamond.ac.uk", + "i04": "https://daq-config.diamond.ac.uk", +} + + +def get_config_client(beamline: str) -> ConfigServer: + url = BEAMLINE_CONFIG_SERVER_ENDPOINTS.get( + beamline, "https://daq-config.diamond.ac.uk" + ) + return ConfigServer(url=url) diff --git a/src/dodal/devices/aperturescatterguard.py b/src/dodal/devices/aperturescatterguard.py index 403000fb7da..5920976785f 100644 --- a/src/dodal/devices/aperturescatterguard.py +++ b/src/dodal/devices/aperturescatterguard.py @@ -2,6 +2,7 @@ import asyncio from math import inf +from typing import Any from bluesky.protocols import Preparable from ophyd_async.core import ( @@ -14,7 +15,6 @@ ) from pydantic import BaseModel, Field -from dodal.common.beamlines.beamline_parameters import GDABeamlineParameters from dodal.devices.aperture import Aperture from dodal.devices.motors import XYStage @@ -65,7 +65,7 @@ def values(self) -> tuple[float, float, float, float, float]: @staticmethod def tolerances_from_gda_params( - params: GDABeamlineParameters, + params: dict[str, Any], ) -> AperturePosition: return AperturePosition( aperture_x=params["miniap_x_tolerance"], @@ -79,7 +79,7 @@ def tolerances_from_gda_params( def from_gda_params( name: _GDAParamApertureValue, diameter: float, - params: GDABeamlineParameters, + params: dict[str, Any], ) -> AperturePosition: return AperturePosition( aperture_x=params[f"miniap_x_{name.value}"], @@ -109,7 +109,7 @@ def __str__(self): def load_positions_from_beamline_parameters( - params: GDABeamlineParameters, + params: dict[str, Any], ) -> dict[ApertureValue, AperturePosition]: return { ApertureValue.OUT_OF_BEAM: AperturePosition.from_gda_params( diff --git a/src/dodal/devices/i03/undulator_dcm.py b/src/dodal/devices/i03/undulator_dcm.py index 0973df2698d..1e4990b8060 100644 --- a/src/dodal/devices/i03/undulator_dcm.py +++ b/src/dodal/devices/i03/undulator_dcm.py @@ -7,6 +7,7 @@ from dodal.devices.i03.dcm import DCM from dodal.devices.undulator import UndulatorInKeV from dodal.log import LOGGER +from dodal.utils import get_beamline_name ENERGY_TIMEOUT_S: float = 30.0 @@ -49,7 +50,8 @@ def __init__( # I03 configures the DCM Perp as a side effect of applying this fixed value to the DCM Offset after an energy change # Nb this parameter is misleadingly named to confuse you self.dcm_fixed_offset_mm = get_beamline_parameters( - daq_configuration_path + "/domain/beamlineParameters" + get_beamline_name("i03"), + daq_configuration_path + "/domain/beamlineParameters", )["DCM_Perp_Offset_FIXED"] super().__init__(name) diff --git a/src/dodal/devices/mx_phase1/beamstop.py b/src/dodal/devices/mx_phase1/beamstop.py index ede8f25a2ee..409065b8f79 100644 --- a/src/dodal/devices/mx_phase1/beamstop.py +++ b/src/dodal/devices/mx_phase1/beamstop.py @@ -1,5 +1,6 @@ import asyncio from math import isclose +from typing import Any from ophyd_async.core import ( StandardReadable, @@ -8,8 +9,6 @@ ) from ophyd_async.epics.motor import Motor -from dodal.common.beamlines.beamline_parameters import GDABeamlineParameters - _BEAMSTOP_OUT_DELTA_Y_MM = -2 @@ -48,7 +47,7 @@ class Beamstop(StandardReadable): def __init__( self, prefix: str, - beamline_parameters: GDABeamlineParameters, + beamline_parameters: dict[str, Any], name: str = "", ): with self.add_children_as_readables(): diff --git a/src/dodal/devices/scintillator.py b/src/dodal/devices/scintillator.py index bfa4506b1e7..baa6890abe6 100644 --- a/src/dodal/devices/scintillator.py +++ b/src/dodal/devices/scintillator.py @@ -1,9 +1,9 @@ from math import isclose +from typing import Any from ophyd_async.core import Reference, StandardReadable, StrictEnum, derived_signal_rw from ophyd_async.epics.motor import Motor -from dodal.common.beamlines.beamline_parameters import GDABeamlineParameters from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureValue @@ -29,7 +29,7 @@ def __init__( self, prefix: str, aperture_scatterguard: Reference[ApertureScatterguard], - beamline_parameters: GDABeamlineParameters, + beamline_parameters: dict[str, Any], name: str = "", ): with self.add_children_as_readables(): diff --git a/src/dodal/plan_stubs/check_topup.py b/src/dodal/plan_stubs/check_topup.py index 61a0f905dc8..f56a536e66a 100644 --- a/src/dodal/plan_stubs/check_topup.py +++ b/src/dodal/plan_stubs/check_topup.py @@ -7,6 +7,7 @@ ) from dodal.devices.synchrotron import Synchrotron, SynchrotronMode from dodal.log import LOGGER +from dodal.utils import get_beamline_name ALLOWED_MODES = [SynchrotronMode.USER, SynchrotronMode.SPECIAL] DECAY_MODE_COUNTDOWN = -1 # Value of the start_countdown PV when in decay mode @@ -133,5 +134,5 @@ def check_topup_and_wait_if_necessary( def _load_topup_configuration_from_properties_file() -> dict[str, Any]: - params = get_beamline_parameters() - return params.params + params = get_beamline_parameters(get_beamline_name("i03")) + return params diff --git a/tests/common/beamlines/test_beamline_parameters.py b/tests/common/beamlines/test_beamline_parameters.py index 401272b604e..af0d2ca84fc 100644 --- a/tests/common/beamlines/test_beamline_parameters.py +++ b/tests/common/beamlines/test_beamline_parameters.py @@ -4,7 +4,6 @@ import pytest from dodal.common.beamlines.beamline_parameters import ( - GDABeamlineParameters, get_beamline_parameters, ) from tests.test_data import ( @@ -12,9 +11,11 @@ TEST_BEAMLINE_PARAMETERS_TXT, ) +BL = "i03" + def test_beamline_parameters(): - params = GDABeamlineParameters.from_server(TEST_BEAMLINE_PARAMETERS_TXT) + params = get_beamline_parameters(BL, TEST_BEAMLINE_PARAMETERS_TXT) assert params["sg_x_MEDIUM_APERTURE"] == 5.285 assert params["col_parked_downstream_x"] == 0 assert params["beamLineEnergy__pitchStep"] == 0.002 @@ -23,7 +24,7 @@ def test_beamline_parameters(): def test_i03_beamline_parameters(): - params = GDABeamlineParameters.from_server(I04_BEAMLINE_PARAMETERS) + params = get_beamline_parameters(BL, I04_BEAMLINE_PARAMETERS) assert params["flux_predict_polynomial_coefficients_5"] == [ -0.0000707134131045123, 7.0205491504418, @@ -36,41 +37,19 @@ def test_i03_beamline_parameters(): def test_get_beamline_parameters_works_with_no_environment_variable_set(): if environ.get("BEAMLINE"): del environ["BEAMLINE"] - assert get_beamline_parameters() + assert get_beamline_parameters(BL) def test_get_beamline_parameters(): - original_beamline = environ.get("BEAMLINE") - environ["BEAMLINE"] = "i03" with patch.dict( "dodal.common.beamlines.beamline_parameters.BEAMLINE_PARAMETER_PATHS", {"i03": TEST_BEAMLINE_PARAMETERS_TXT}, ): - params = get_beamline_parameters() + params = get_beamline_parameters("i03") assert params["col_parked_downstream_x"] == 0 assert params["BackStopZyag"] == 19.1 assert params["store_data_collections_in_ispyb"] is True assert params["attenuation_optimisation_type"] == "deadtime" - if original_beamline: - environ["BEAMLINE"] = original_beamline - else: - del environ["BEAMLINE"] - - -@patch("dodal.common.beamlines.beamline_parameters.get_beamline_name") -def test_get_beamline_parameters_raises_error_when_beamline_not_set(get_beamline_name): - get_beamline_name.return_value = None - with pytest.raises(KeyError): - get_beamline_parameters() - - -@patch("dodal.common.beamlines.beamline_parameters.get_beamline_name") -def test_get_beamline_parameters_raises_error_when_beamline_not_found( - get_beamline_name, -): - get_beamline_name.return_value = "invalid_beamline" - with pytest.raises(KeyError): - get_beamline_parameters() @pytest.fixture(autouse=True) diff --git a/tests/devices/conftest.py b/tests/devices/conftest.py index 33c2a241210..5e42b83b22c 100644 --- a/tests/devices/conftest.py +++ b/tests/devices/conftest.py @@ -3,7 +3,6 @@ import pytest from ophyd_async.core import init_devices -from dodal.common.beamlines.beamline_parameters import GDABeamlineParameters from dodal.devices.aperturescatterguard import ( AperturePosition, ApertureScatterguard, @@ -15,50 +14,46 @@ @pytest.fixture def aperture_positions() -> dict[ApertureValue, AperturePosition]: return load_positions_from_beamline_parameters( - GDABeamlineParameters( - params={ - "miniap_x_LARGE_APERTURE": 2.389, - "miniap_y_LARGE_APERTURE": 40.986, - "miniap_z_LARGE_APERTURE": 15.8, - "sg_x_LARGE_APERTURE": 5.25, - "sg_y_LARGE_APERTURE": 4.43, - "miniap_x_MEDIUM_APERTURE": 2.384, - "miniap_y_MEDIUM_APERTURE": 44.967, - "miniap_z_MEDIUM_APERTURE": 15.8, - "sg_x_MEDIUM_APERTURE": 5.285, - "sg_y_MEDIUM_APERTURE": 0.46, - "miniap_x_SMALL_APERTURE": 2.430, - "miniap_y_SMALL_APERTURE": 48.974, - "miniap_z_SMALL_APERTURE": 15.8, - "sg_x_SMALL_APERTURE": 5.3375, - "sg_y_SMALL_APERTURE": -3.55, - "miniap_x_ROBOT_LOAD": 2.386, - "miniap_y_ROBOT_LOAD": 31.40, - "miniap_z_ROBOT_LOAD": 15.8, - "sg_x_ROBOT_LOAD": 5.25, - "sg_y_ROBOT_LOAD": 4.43, - "miniap_x_MANUAL_LOAD": -4.91, - "miniap_y_MANUAL_LOAD": -48.70, - "miniap_z_MANUAL_LOAD": -10.0, - "sg_x_MANUAL_LOAD": -4.7, - "sg_y_MANUAL_LOAD": 1.8, - } - ) + { + "miniap_x_LARGE_APERTURE": 2.389, + "miniap_y_LARGE_APERTURE": 40.986, + "miniap_z_LARGE_APERTURE": 15.8, + "sg_x_LARGE_APERTURE": 5.25, + "sg_y_LARGE_APERTURE": 4.43, + "miniap_x_MEDIUM_APERTURE": 2.384, + "miniap_y_MEDIUM_APERTURE": 44.967, + "miniap_z_MEDIUM_APERTURE": 15.8, + "sg_x_MEDIUM_APERTURE": 5.285, + "sg_y_MEDIUM_APERTURE": 0.46, + "miniap_x_SMALL_APERTURE": 2.430, + "miniap_y_SMALL_APERTURE": 48.974, + "miniap_z_SMALL_APERTURE": 15.8, + "sg_x_SMALL_APERTURE": 5.3375, + "sg_y_SMALL_APERTURE": -3.55, + "miniap_x_ROBOT_LOAD": 2.386, + "miniap_y_ROBOT_LOAD": 31.40, + "miniap_z_ROBOT_LOAD": 15.8, + "sg_x_ROBOT_LOAD": 5.25, + "sg_y_ROBOT_LOAD": 4.43, + "miniap_x_MANUAL_LOAD": -4.91, + "miniap_y_MANUAL_LOAD": -48.70, + "miniap_z_MANUAL_LOAD": -10.0, + "sg_x_MANUAL_LOAD": -4.7, + "sg_y_MANUAL_LOAD": 1.8, + } ) @pytest.fixture def aperture_tolerances(): return AperturePosition.tolerances_from_gda_params( - GDABeamlineParameters( - { - "miniap_x_tolerance": 0.004, - "miniap_y_tolerance": 0.1, - "miniap_z_tolerance": 0.1, - "sg_x_tolerance": 0.1, - "sg_y_tolerance": 0.1, - } - ) + { + "miniap_x_tolerance": 0.004, + "miniap_y_tolerance": 0.1, + "miniap_z_tolerance": 0.1, + "sg_x_tolerance": 0.1, + "sg_y_tolerance": 0.1, + } ) diff --git a/tests/devices/mx_phase1/test_beamstop.py b/tests/devices/mx_phase1/test_beamstop.py index 155502b1cbc..ecccb050edb 100644 --- a/tests/devices/mx_phase1/test_beamstop.py +++ b/tests/devices/mx_phase1/test_beamstop.py @@ -1,4 +1,5 @@ from itertools import dropwhile +from typing import Any from unittest.mock import MagicMock, Mock, call import pytest @@ -8,14 +9,14 @@ from bluesky.run_engine import RunEngine from ophyd_async.core import get_mock, get_mock_put, set_mock_value -from dodal.common.beamlines.beamline_parameters import GDABeamlineParameters +from dodal.common.beamlines.beamline_parameters import get_beamline_parameters from dodal.devices.i03 import Beamstop, BeamstopPositions from tests.common.beamlines.test_beamline_parameters import TEST_BEAMLINE_PARAMETERS_TXT @pytest.fixture -def beamline_parameters() -> GDABeamlineParameters: - return GDABeamlineParameters.from_server(TEST_BEAMLINE_PARAMETERS_TXT) +def beamline_parameters() -> dict[str, Any]: + return get_beamline_parameters("i03", TEST_BEAMLINE_PARAMETERS_TXT) @pytest.mark.parametrize( @@ -31,7 +32,7 @@ def beamline_parameters() -> GDABeamlineParameters: ], ) async def test_beamstop_pos_read_selected_pos( - beamline_parameters: GDABeamlineParameters, + beamline_parameters: dict[str, Any], run_engine: RunEngine, x: float, y: float, @@ -77,7 +78,7 @@ def check_in_beam(): async def test_set_beamstop_position_to_data_collection_moves_beamstop( demanded_pos: BeamstopPositions, expected_coords: tuple[float, float, float], - beamline_parameters: GDABeamlineParameters, + beamline_parameters: dict[str, Any], run_engine: RunEngine, ): beamstop = Beamstop("-MO-BS-01:", beamline_parameters, name="beamstop") @@ -102,7 +103,7 @@ async def test_set_beamstop_position_to_data_collection_moves_beamstop( async def test_set_beamstop_position_to_unknown_raises_error( - beamline_parameters: GDABeamlineParameters, run_engine: RunEngine + beamline_parameters: dict[str, Any], run_engine: RunEngine ): beamstop = Beamstop("-MO-BS-01:", beamline_parameters, name="beamstop") await beamstop.connect(mock=True) @@ -114,7 +115,7 @@ async def test_set_beamstop_position_to_unknown_raises_error( async def test_beamstop_select_pos_moves_z_axis_first( - run_engine: RunEngine, beamline_parameters: GDABeamlineParameters + run_engine: RunEngine, beamline_parameters: dict[str, Any] ): beamstop = Beamstop("-MO-BS-01:", beamline_parameters, name="beamstop") await beamstop.connect(mock=True) diff --git a/tests/devices/test_scintillator.py b/tests/devices/test_scintillator.py index ce1579bc1ba..e65586c2c30 100644 --- a/tests/devices/test_scintillator.py +++ b/tests/devices/test_scintillator.py @@ -1,31 +1,29 @@ +from typing import Any from unittest.mock import AsyncMock, MagicMock import pytest from ophyd_async.core import get_mock_put, init_devices from ophyd_async.testing import assert_value -from dodal.common.beamlines.beamline_parameters import GDABeamlineParameters from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureValue from dodal.devices.scintillator import InOut, Scintillator @pytest.fixture -def mock_beamline_parameters() -> GDABeamlineParameters: - return GDABeamlineParameters( - params={ - "scin_y_SCIN_IN": 100.855, - "scin_y_SCIN_OUT": -0.02, - "scin_z_SCIN_IN": 101.5115, - "scin_z_SCIN_OUT": 0.1, - "scin_y_tolerance": 0.1, - "scin_z_tolerance": 0.12, - } - ) +def mock_beamline_parameters() -> dict[str, Any]: + return { + "scin_y_SCIN_IN": 100.855, + "scin_y_SCIN_OUT": -0.02, + "scin_z_SCIN_IN": 101.5115, + "scin_z_SCIN_OUT": 0.1, + "scin_y_tolerance": 0.1, + "scin_z_tolerance": 0.12, + } @pytest.fixture async def scintillator_and_ap_sg( - mock_beamline_parameters: GDABeamlineParameters, + mock_beamline_parameters: dict[str, Any], ) -> tuple[Scintillator, MagicMock]: async with init_devices(mock=True): mock_ap_sg = MagicMock() From 9517b6a909aefbb874ebf952515516c202f1e9fc Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Fri, 23 Jan 2026 17:36:51 +0000 Subject: [PATCH 12/34] Update lockfile --- uv.lock | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/uv.lock b/uv.lock index eca15cb4b9a..297e2bf7747 100644 --- a/uv.lock +++ b/uv.lock @@ -644,7 +644,7 @@ wheels = [ [[package]] name = "daq-config-server" -version = "1.0.0" +version = "1.1.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cachetools" }, @@ -657,10 +657,11 @@ dependencies = [ { name = "requests" }, { name = "urllib3" }, { name = "uvicorn" }, + { name = "xmltodict" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/84/649f039a658994fdbe6ecf53e92ba01b65c53635965fe2f4c0c64fbb21a4/daq_config_server-1.0.0.tar.gz", hash = "sha256:63b4989c563520683fbda12aaa42ffeab5fcccc9cc2b25953fd6bc673ab91afd", size = 113002, upload-time = "2025-12-04T17:01:19.77Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5d/1e/37373402723769ced976215ffb1716353d89c0772d5bec8116d9f981d095/daq_config_server-1.1.2.tar.gz", hash = "sha256:2f8c9e43a41534d90512be7ecab37de0fdf33d4b00a6b61bd04e3c82d886062f", size = 126067, upload-time = "2026-01-12T11:28:21.034Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/05/5edbc534abf7edbd9cdd971f25809f7ae916e0d417756cb96fb11c49252c/daq_config_server-1.0.0-py3-none-any.whl", hash = "sha256:cfea960c4b6652784f598ce784fcd236f5fd1234f483c2c287b3a4ab96efb3c8", size = 19808, upload-time = "2025-12-04T17:01:18.392Z" }, + { url = "https://files.pythonhosted.org/packages/13/9f/b8df9fad0b1005e95460ee92092a1da33c282c158336bc23bd7635981f29/daq_config_server-1.1.2-py3-none-any.whl", hash = "sha256:c2bca297feb01a51883e3df7d6ade11af9a3f311602e5584320b1e12881cf636", size = 29273, upload-time = "2026-01-12T11:28:19.635Z" }, ] [[package]] @@ -763,7 +764,7 @@ requires-dist = [ { name = "aiohttp" }, { name = "bluesky", specifier = ">=1.14.5" }, { name = "click" }, - { name = "daq-config-server", specifier = ">=1.0.0" }, + { name = "daq-config-server", specifier = ">=1.1.2" }, { name = "deepdiff" }, { name = "graypy" }, { name = "numpy" }, @@ -3831,6 +3832,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/47/7902c3cea79f6a1964fac59b97fb9f11e5ea85e0c0582cc89b2c3193ea48/workflows-3.2-py3-none-any.whl", hash = "sha256:38eed7d209d626b371277bcbcd9c3d476bce9945467d1341c578b1c21ff4eec3", size = 66736, upload-time = "2025-02-27T17:37:40.441Z" }, ] +[[package]] +name = "xmltodict" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/aa/917ceeed4dbb80d2f04dbd0c784b7ee7bba8ae5a54837ef0e5e062cd3cfb/xmltodict-1.0.2.tar.gz", hash = "sha256:54306780b7c2175a3967cad1db92f218207e5bc1aba697d887807c0fb68b7649", size = 25725, upload-time = "2025-09-17T21:59:26.459Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/20/69a0e6058bc5ea74892d089d64dfc3a62ba78917ec5e2cfa70f7c92ba3a5/xmltodict-1.0.2-py3-none-any.whl", hash = "sha256:62d0fddb0dcbc9f642745d8bbf4d81fd17d6dfaec5a15b5c1876300aad92af0d", size = 13893, upload-time = "2025-09-17T21:59:24.859Z" }, +] + [[package]] name = "yarl" version = "1.22.0" From bc858b25cf99624597a189e1f24cd19f9e5e4499 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Thu, 29 Jan 2026 16:51:51 +0000 Subject: [PATCH 13/34] Use server deployed on i03 beamline cluster for i03 config --- src/dodal/common/beamlines/config_client.py | 2 +- tests/common/beamlines/test_config_client.py | 29 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tests/common/beamlines/test_config_client.py diff --git a/src/dodal/common/beamlines/config_client.py b/src/dodal/common/beamlines/config_client.py index 266c8b0214b..a5479760bdc 100644 --- a/src/dodal/common/beamlines/config_client.py +++ b/src/dodal/common/beamlines/config_client.py @@ -1,7 +1,7 @@ from daq_config_server.client import ConfigServer BEAMLINE_CONFIG_SERVER_ENDPOINTS = { - "i03": "https://daq-config.diamond.ac.uk", + "i03": "https://i03-daq-config.diamond.ac.uk", "i04": "https://daq-config.diamond.ac.uk", } diff --git a/tests/common/beamlines/test_config_client.py b/tests/common/beamlines/test_config_client.py new file mode 100644 index 00000000000..5958760b641 --- /dev/null +++ b/tests/common/beamlines/test_config_client.py @@ -0,0 +1,29 @@ +from unittest.mock import MagicMock, patch + +from dodal.common.beamlines.config_client import get_config_client + + +@patch("dodal.common.beamlines.config_client.ConfigServer") +def test_by_default_get_config_client_uses_centrally_deployed_config_server( + mock_config_server: MagicMock, +): + get_config_client("") + mock_config_server.assert_called_once_with(url="https://daq-config.diamond.ac.uk") + + +@patch("dodal.common.beamlines.config_client.ConfigServer") +def test_get_config_client_uses_i03_beamline_cluster_server_for_i03( + mock_config_server: MagicMock, +): + get_config_client("i03") + mock_config_server.assert_called_once_with( + url="https://i03-daq-config.diamond.ac.uk" + ) + + +@patch("dodal.common.beamlines.config_client.ConfigServer") +def test_get_config_client_uses_entrally_deployed_config_server_for_i04( + mock_config_server: MagicMock, +): + get_config_client("i04") + mock_config_server.assert_called_once_with(url="https://daq-config.diamond.ac.uk") From 9529dc1e87b05c38a323d791e7cac0b9275eb666 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Wed, 4 Feb 2026 15:55:34 +0000 Subject: [PATCH 14/34] typo --- tests/common/beamlines/test_config_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/beamlines/test_config_client.py b/tests/common/beamlines/test_config_client.py index 5958760b641..99857012dc6 100644 --- a/tests/common/beamlines/test_config_client.py +++ b/tests/common/beamlines/test_config_client.py @@ -22,7 +22,7 @@ def test_get_config_client_uses_i03_beamline_cluster_server_for_i03( @patch("dodal.common.beamlines.config_client.ConfigServer") -def test_get_config_client_uses_entrally_deployed_config_server_for_i04( +def test_get_config_client_uses_centrally_deployed_config_server_for_i04( mock_config_server: MagicMock, ): get_config_client("i04") From eaa20a22c6c73dfaf3876ce0a0ab1b614e0afb9e Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Wed, 11 Feb 2026 16:22:22 +0000 Subject: [PATCH 15/34] WIP --- .../common/beamlines/beamline_parameters.py | 28 +++++++------- src/dodal/common/beamlines/config_client.py | 3 ++ .../devices/beamlines/i03/undulator_dcm.py | 7 ++-- src/dodal/testing/fixtures/config_server.py | 37 +++++++++++++++++++ src/dodal/utils.py | 2 +- .../beamlines/test_beamline_parameters.py | 36 +++++++++--------- tests/common/beamlines/test_config_client.py | 35 ++++++++++++++++-- tests/conftest.py | 36 +++--------------- tests/devices/beamlines/i03/conftest.py | 6 +++ .../beamlines/i03/test_undulator_dcm.py | 10 +++++ tests/devices/mx_phase1/test_beamstop.py | 3 +- 11 files changed, 130 insertions(+), 73 deletions(-) create mode 100644 src/dodal/testing/fixtures/config_server.py create mode 100644 tests/devices/beamlines/i03/conftest.py diff --git a/src/dodal/common/beamlines/beamline_parameters.py b/src/dodal/common/beamlines/beamline_parameters.py index 61dcdcefae2..b6f984a8655 100644 --- a/src/dodal/common/beamlines/beamline_parameters.py +++ b/src/dodal/common/beamlines/beamline_parameters.py @@ -8,19 +8,19 @@ } -def get_beamline_parameters( - beamline: str, beamline_param_path: str | None = None -) -> dict[str, Any]: - """Loads the beamline parameters from the specified path, or according to the - environment variable if none is given. +def get_beamline_parameters(beamline: str) -> dict[str, Any]: + """Loads the beamline parameters for a specified beamline from the config server. + + Args: + beamline (str): The beamline for which beamline parameters will be retrieved. + + Returns: + dict[str, Any]: Dict of beamline parameters. """ - if not beamline_param_path: - beamline_param_path = BEAMLINE_PARAMETER_PATHS.get(beamline) - if beamline_param_path is None: - raise KeyError( - "No beamline parameter path found, maybe 'BEAMLINE' environment variable is not set!" - ) + beamline_param_path = BEAMLINE_PARAMETER_PATHS.get(beamline) + if beamline_param_path is None: + raise KeyError( + "No beamline parameter path found, maybe 'BEAMLINE' environment variable is not set!" + ) config_client = get_config_client(beamline) - return config_client.get_file_contents( - beamline_param_path, dict, reset_cached_result=True - ) + return config_client.get_file_contents(beamline_param_path, dict) diff --git a/src/dodal/common/beamlines/config_client.py b/src/dodal/common/beamlines/config_client.py index a5479760bdc..23ace33f862 100644 --- a/src/dodal/common/beamlines/config_client.py +++ b/src/dodal/common/beamlines/config_client.py @@ -1,3 +1,5 @@ +from functools import cache + from daq_config_server.client import ConfigServer BEAMLINE_CONFIG_SERVER_ENDPOINTS = { @@ -6,6 +8,7 @@ } +@cache def get_config_client(beamline: str) -> ConfigServer: url = BEAMLINE_CONFIG_SERVER_ENDPOINTS.get( beamline, "https://daq-config.diamond.ac.uk" diff --git a/src/dodal/devices/beamlines/i03/undulator_dcm.py b/src/dodal/devices/beamlines/i03/undulator_dcm.py index 5ecf7d31233..80c9b227e7c 100644 --- a/src/dodal/devices/beamlines/i03/undulator_dcm.py +++ b/src/dodal/devices/beamlines/i03/undulator_dcm.py @@ -48,10 +48,9 @@ def __init__( ) # I03 configures the DCM Perp as a side effect of applying this fixed value to the DCM Offset after an energy change # Nb this parameter is misleadingly named to confuse you - self.dcm_fixed_offset_mm = get_beamline_parameters( - get_beamline_name("i03"), - daq_configuration_path + "/domain/beamlineParameters", - )["DCM_Perp_Offset_FIXED"] + self.dcm_fixed_offset_mm = get_beamline_parameters(get_beamline_name())[ + "DCM_Perp_Offset_FIXED" + ] super().__init__(name) diff --git a/src/dodal/testing/fixtures/config_server.py b/src/dodal/testing/fixtures/config_server.py new file mode 100644 index 00000000000..1092cb120b9 --- /dev/null +++ b/src/dodal/testing/fixtures/config_server.py @@ -0,0 +1,37 @@ +import json +from pathlib import Path +from typing import TypeVar +from unittest.mock import patch + +import pytest +from daq_config_server.models import ConfigModel + +T = TypeVar("T", str, dict, ConfigModel) + + +def fake_config_server_get_file_contents( + filepath: str | Path, + desired_return_type: type[T] = str, + reset_cached_result: bool = True, +) -> T: + 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/src/dodal/utils.py b/src/dodal/utils.py index aa10ff5ea03..f2c602427a3 100644 --- a/src/dodal/utils.py +++ b/src/dodal/utils.py @@ -66,7 +66,7 @@ AnyDeviceFactory: TypeAlias = V1DeviceFactory | V2DeviceFactory -def get_beamline_name(default: str) -> str: +def get_beamline_name(default: str | None = None) -> str: return environ.get("BEAMLINE") or default diff --git a/tests/common/beamlines/test_beamline_parameters.py b/tests/common/beamlines/test_beamline_parameters.py index af0d2ca84fc..f33683afc72 100644 --- a/tests/common/beamlines/test_beamline_parameters.py +++ b/tests/common/beamlines/test_beamline_parameters.py @@ -11,11 +11,22 @@ TEST_BEAMLINE_PARAMETERS_TXT, ) -BL = "i03" + +@pytest.fixture(autouse=True) +def patch_beamline_parameter_paths(): + with patch( + "dodal.common.beamlines.beamline_parameters.BEAMLINE_PARAMETER_PATHS", + { + "test": TEST_BEAMLINE_PARAMETERS_TXT, + "i04": I04_BEAMLINE_PARAMETERS, + "i03": I04_BEAMLINE_PARAMETERS, + }, + ): + yield def test_beamline_parameters(): - params = get_beamline_parameters(BL, TEST_BEAMLINE_PARAMETERS_TXT) + params = get_beamline_parameters("test") assert params["sg_x_MEDIUM_APERTURE"] == 5.285 assert params["col_parked_downstream_x"] == 0 assert params["beamLineEnergy__pitchStep"] == 0.002 @@ -24,7 +35,7 @@ def test_beamline_parameters(): def test_i03_beamline_parameters(): - params = get_beamline_parameters(BL, I04_BEAMLINE_PARAMETERS) + params = get_beamline_parameters("i03") assert params["flux_predict_polynomial_coefficients_5"] == [ -0.0000707134131045123, 7.0205491504418, @@ -34,28 +45,15 @@ def test_i03_beamline_parameters(): ] -def test_get_beamline_parameters_works_with_no_environment_variable_set(): +def test_get_beamline_parameters_errors_with_no_environment_variable_set(): if environ.get("BEAMLINE"): del environ["BEAMLINE"] - assert get_beamline_parameters(BL) + assert get_beamline_parameters("i03") def test_get_beamline_parameters(): - with patch.dict( - "dodal.common.beamlines.beamline_parameters.BEAMLINE_PARAMETER_PATHS", - {"i03": TEST_BEAMLINE_PARAMETERS_TXT}, - ): - params = get_beamline_parameters("i03") + params = get_beamline_parameters("test") assert params["col_parked_downstream_x"] == 0 assert params["BackStopZyag"] == 19.1 assert params["store_data_collections_in_ispyb"] is True assert params["attenuation_optimisation_type"] == "deadtime" - - -@pytest.fixture(autouse=True) -def i03_beamline_parameters(): - with patch.dict( - "dodal.common.beamlines.beamline_parameters.BEAMLINE_PARAMETER_PATHS", - {"i03": TEST_BEAMLINE_PARAMETERS_TXT}, - ) as params: - yield params diff --git a/tests/common/beamlines/test_config_client.py b/tests/common/beamlines/test_config_client.py index 99857012dc6..648edb2d294 100644 --- a/tests/common/beamlines/test_config_client.py +++ b/tests/common/beamlines/test_config_client.py @@ -1,11 +1,18 @@ from unittest.mock import MagicMock, patch +import pytest + from dodal.common.beamlines.config_client import get_config_client +@pytest.fixture() +def clear_cache(): + get_config_client.cache_clear() + + @patch("dodal.common.beamlines.config_client.ConfigServer") def test_by_default_get_config_client_uses_centrally_deployed_config_server( - mock_config_server: MagicMock, + mock_config_server: MagicMock, clear_cache ): get_config_client("") mock_config_server.assert_called_once_with(url="https://daq-config.diamond.ac.uk") @@ -13,7 +20,7 @@ def test_by_default_get_config_client_uses_centrally_deployed_config_server( @patch("dodal.common.beamlines.config_client.ConfigServer") def test_get_config_client_uses_i03_beamline_cluster_server_for_i03( - mock_config_server: MagicMock, + mock_config_server: MagicMock, clear_cache ): get_config_client("i03") mock_config_server.assert_called_once_with( @@ -23,7 +30,29 @@ def test_get_config_client_uses_i03_beamline_cluster_server_for_i03( @patch("dodal.common.beamlines.config_client.ConfigServer") def test_get_config_client_uses_centrally_deployed_config_server_for_i04( - mock_config_server: MagicMock, + mock_config_server: MagicMock, clear_cache +): + get_config_client("i04") + mock_config_server.assert_called_once_with(url="https://daq-config.diamond.ac.uk") + + +@patch("dodal.common.beamlines.config_client.ConfigServer") +def test_get_config_client_caches_if_called_with_same_beamline( + mock_config_server: MagicMock, clear_cache +): + get_config_client("i04") + mock_config_server.assert_called_once() + get_config_client("i04") + mock_config_server.assert_called_once() + + +@patch("dodal.common.beamlines.config_client.ConfigServer") +def test_get_config_client_resets_cache_if_called_with_same_beamline( + mock_config_server: MagicMock, clear_cache ): + assert mock_config_server.assert_not_called get_config_client("i04") mock_config_server.assert_called_once_with(url="https://daq-config.diamond.ac.uk") + get_config_client("i03") + assert mock_config_server.call_count == 2 + mock_config_server.assert_called_with(url="https://i03-daq-config.diamond.ac.uk") diff --git a/tests/conftest.py b/tests/conftest.py index 8af64fa9791..66d3a524151 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,4 @@ import importlib -import json import logging import os import sys @@ -9,7 +8,6 @@ from unittest.mock import MagicMock, patch import pytest -from daq_config_server.models import ConfigModel from ophyd_async.core import ( PathProvider, ) @@ -59,7 +57,11 @@ # Add run_engine and util fixtures to be used in tests -pytest_plugins = ["dodal.testing.fixtures.run_engine", "dodal.testing.fixtures.utils"] +pytest_plugins = [ + "dodal.testing.fixtures.run_engine", + "dodal.testing.fixtures.utils", + "dodal.testing.fixtures.config_server", +] @pytest.fixture(autouse=True) @@ -169,31 +171,3 @@ 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/beamlines/i03/conftest.py b/tests/devices/beamlines/i03/conftest.py new file mode 100644 index 00000000000..c116593b798 --- /dev/null +++ b/tests/devices/beamlines/i03/conftest.py @@ -0,0 +1,6 @@ +import pytest + + +@pytest.fixture(autouse=True) +def use_beamline_i03(monkeypatch): + monkeypatch.setenv("BEAMLINE", "i03") diff --git a/tests/devices/beamlines/i03/test_undulator_dcm.py b/tests/devices/beamlines/i03/test_undulator_dcm.py index 9bcf7bd435e..859b58477b0 100644 --- a/tests/devices/beamlines/i03/test_undulator_dcm.py +++ b/tests/devices/beamlines/i03/test_undulator_dcm.py @@ -19,6 +19,16 @@ from tests.devices.test_data import ( TEST_BEAMLINE_UNDULATOR_TO_GAP_LUT, ) +from tests.test_data import TEST_BEAMLINE_PARAMETERS_TXT + + +@pytest.fixture(autouse=True) +def patch_beamline_parameter_paths(): + with patch( + "dodal.common.beamlines.beamline_parameters.BEAMLINE_PARAMETER_PATHS", + {"i03": TEST_BEAMLINE_PARAMETERS_TXT}, + ): + yield @pytest.fixture(autouse=True) diff --git a/tests/devices/mx_phase1/test_beamstop.py b/tests/devices/mx_phase1/test_beamstop.py index 4494a20be11..5ca3d626c1d 100644 --- a/tests/devices/mx_phase1/test_beamstop.py +++ b/tests/devices/mx_phase1/test_beamstop.py @@ -11,12 +11,13 @@ from dodal.common.beamlines.beamline_parameters import get_beamline_parameters from dodal.devices.beamlines.i03 import Beamstop, BeamstopPositions +from dodal.testing.fixtures.config_server import fake_config_server_get_file_contents from tests.common.beamlines.test_beamline_parameters import TEST_BEAMLINE_PARAMETERS_TXT @pytest.fixture def beamline_parameters() -> dict[str, Any]: - return get_beamline_parameters("i03", TEST_BEAMLINE_PARAMETERS_TXT) + return fake_config_server_get_file_contents(TEST_BEAMLINE_PARAMETERS_TXT, dict) @pytest.mark.parametrize( From 0d29dbe46a175ff0675f4e65b0b68b3a89952d9a Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Wed, 11 Feb 2026 16:29:16 +0000 Subject: [PATCH 16/34] Reset cache between tests --- tests/common/beamlines/test_config_client.py | 17 +++++------------ tests/conftest.py | 6 ++++++ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/tests/common/beamlines/test_config_client.py b/tests/common/beamlines/test_config_client.py index 648edb2d294..303b1c2110f 100644 --- a/tests/common/beamlines/test_config_client.py +++ b/tests/common/beamlines/test_config_client.py @@ -1,18 +1,11 @@ from unittest.mock import MagicMock, patch -import pytest - from dodal.common.beamlines.config_client import get_config_client -@pytest.fixture() -def clear_cache(): - get_config_client.cache_clear() - - @patch("dodal.common.beamlines.config_client.ConfigServer") def test_by_default_get_config_client_uses_centrally_deployed_config_server( - mock_config_server: MagicMock, clear_cache + mock_config_server: MagicMock, ): get_config_client("") mock_config_server.assert_called_once_with(url="https://daq-config.diamond.ac.uk") @@ -20,7 +13,7 @@ def test_by_default_get_config_client_uses_centrally_deployed_config_server( @patch("dodal.common.beamlines.config_client.ConfigServer") def test_get_config_client_uses_i03_beamline_cluster_server_for_i03( - mock_config_server: MagicMock, clear_cache + mock_config_server: MagicMock, ): get_config_client("i03") mock_config_server.assert_called_once_with( @@ -30,7 +23,7 @@ def test_get_config_client_uses_i03_beamline_cluster_server_for_i03( @patch("dodal.common.beamlines.config_client.ConfigServer") def test_get_config_client_uses_centrally_deployed_config_server_for_i04( - mock_config_server: MagicMock, clear_cache + mock_config_server: MagicMock, ): get_config_client("i04") mock_config_server.assert_called_once_with(url="https://daq-config.diamond.ac.uk") @@ -38,7 +31,7 @@ def test_get_config_client_uses_centrally_deployed_config_server_for_i04( @patch("dodal.common.beamlines.config_client.ConfigServer") def test_get_config_client_caches_if_called_with_same_beamline( - mock_config_server: MagicMock, clear_cache + mock_config_server: MagicMock, ): get_config_client("i04") mock_config_server.assert_called_once() @@ -48,7 +41,7 @@ def test_get_config_client_caches_if_called_with_same_beamline( @patch("dodal.common.beamlines.config_client.ConfigServer") def test_get_config_client_resets_cache_if_called_with_same_beamline( - mock_config_server: MagicMock, clear_cache + mock_config_server: MagicMock, ): assert mock_config_server.assert_not_called get_config_client("i04") diff --git a/tests/conftest.py b/tests/conftest.py index 66d3a524151..7c83dd8a692 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,6 +14,7 @@ from dodal.common.beamlines import beamline_parameters, beamline_utils from dodal.common.beamlines.beamline_utils import clear_path_provider +from dodal.common.beamlines.config_client import get_config_client from dodal.common.visit import ( DirectoryServiceClient, LocalDirectoryServiceClient, @@ -171,3 +172,8 @@ 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 ) + + +@pytest.fixture(autouse=True) +def clear_cache(): + get_config_client.cache_clear() From d4fda7b822c9e2398060eb52a15c2691a16eed47 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Wed, 11 Feb 2026 16:56:23 +0000 Subject: [PATCH 17/34] Fix --- src/dodal/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dodal/utils.py b/src/dodal/utils.py index f2c602427a3..08c0fb16bde 100644 --- a/src/dodal/utils.py +++ b/src/dodal/utils.py @@ -67,7 +67,10 @@ def get_beamline_name(default: str | None = None) -> str: - return environ.get("BEAMLINE") or default + beamline_name = environ.get("BEAMLINE") or default + if beamline_name is None: + raise ValueError("Set BEAMLINE environment variable or provide default.") + return beamline_name def is_test_mode() -> bool: From a947f607330f0252321771697f1d430a2a9d3791 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Wed, 11 Feb 2026 16:59:02 +0000 Subject: [PATCH 18/34] Fix lint --- tests/devices/mx_phase1/test_beamstop.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/devices/mx_phase1/test_beamstop.py b/tests/devices/mx_phase1/test_beamstop.py index 5ca3d626c1d..312ecab3f96 100644 --- a/tests/devices/mx_phase1/test_beamstop.py +++ b/tests/devices/mx_phase1/test_beamstop.py @@ -9,7 +9,6 @@ from bluesky.run_engine import RunEngine from ophyd_async.core import get_mock, get_mock_put, set_mock_value -from dodal.common.beamlines.beamline_parameters import get_beamline_parameters from dodal.devices.beamlines.i03 import Beamstop, BeamstopPositions from dodal.testing.fixtures.config_server import fake_config_server_get_file_contents from tests.common.beamlines.test_beamline_parameters import TEST_BEAMLINE_PARAMETERS_TXT From d5bd077d96cdb62240ee8ac549ef2b5a502ffe2c Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Thu, 12 Feb 2026 09:44:07 +0000 Subject: [PATCH 19/34] Fix lint --- src/dodal/testing/fixtures/config_server.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/dodal/testing/fixtures/config_server.py b/src/dodal/testing/fixtures/config_server.py index 1092cb120b9..d9193980886 100644 --- a/src/dodal/testing/fixtures/config_server.py +++ b/src/dodal/testing/fixtures/config_server.py @@ -18,12 +18,13 @@ def fake_config_server_get_file_contents( # 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)) + if desired_return_type is str: + return contents # type: ignore + elif desired_return_type is dict: + return json.loads(contents) + elif issubclass(desired_return_type, ConfigModel): + return desired_return_type.model_validate(json.loads(contents)) + raise ValueError("Invalid return type requested") @pytest.fixture(autouse=True) From 357a6c9dd0efcf7bd72ef384097627e9a68a5db9 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Fri, 13 Feb 2026 11:26:39 +0000 Subject: [PATCH 20/34] PR comments and coverage --- tests/common/beamlines/test_beamline_parameters.py | 7 +++---- tests/test_utils.py | 9 +++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/common/beamlines/test_beamline_parameters.py b/tests/common/beamlines/test_beamline_parameters.py index f33683afc72..3bd680699a7 100644 --- a/tests/common/beamlines/test_beamline_parameters.py +++ b/tests/common/beamlines/test_beamline_parameters.py @@ -45,10 +45,9 @@ def test_i03_beamline_parameters(): ] -def test_get_beamline_parameters_errors_with_no_environment_variable_set(): - if environ.get("BEAMLINE"): - del environ["BEAMLINE"] - assert get_beamline_parameters("i03") +def test_get_beamline_parameters_errors_if_no_filepath_found_for_beamline(): + with pytest.raises(KeyError): + assert get_beamline_parameters("not a key") def test_get_beamline_parameters(): diff --git a/tests/test_utils.py b/tests/test_utils.py index 9dd86e1cbce..d4877ab2a4b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -18,6 +18,7 @@ collect_factories, filter_ophyd_devices, get_beamline_based_on_environment_variable, + get_beamline_name, get_hostname, get_run_number, is_v2_device_type, @@ -484,3 +485,11 @@ def test_filter_ophyd_devices_raises_for_extra_types(): ) def test_is_v2_device_type(input: Any, expected_result: bool): assert is_v2_device_type(input) == expected_result + + +def test_get_beamline_name_raises_error_if_environment_variable_not_set_and_no_default_given( + monkeypatch, +): + monkeypatch.delenv("BEAMLINE", raising=False) + with pytest.raises(ValueError): + get_beamline_name() From 286a17cc0592ecb5242a78b44a618a2e6db3c260 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Fri, 13 Feb 2026 11:35:21 +0000 Subject: [PATCH 21/34] Lint --- tests/common/beamlines/test_beamline_parameters.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/common/beamlines/test_beamline_parameters.py b/tests/common/beamlines/test_beamline_parameters.py index 3bd680699a7..40a86350cdf 100644 --- a/tests/common/beamlines/test_beamline_parameters.py +++ b/tests/common/beamlines/test_beamline_parameters.py @@ -1,4 +1,3 @@ -from os import environ from unittest.mock import patch import pytest From 4534194f7869b721d7ded298ce85c44c211ce4cf Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Mon, 8 Dec 2025 17:45:19 +0000 Subject: [PATCH 22/34] Use config server in undulator device --- src/dodal/devices/undulator.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/dodal/devices/undulator.py b/src/dodal/devices/undulator.py index 962ad2013fe..40ab647f22e 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.converters.models import GenericLookupTable 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): @@ -207,14 +208,15 @@ 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 + config_server = ConfigServer(url="https://daq-config.diamond.ac.uk") + energy_to_distance_table = config_server.get_file_contents( + self.id_gap_lookup_table_path, GenericLookupTable ) # 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), ) From c49807334a7f40c32252bdfe509720c3f3c512cb Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Mon, 8 Dec 2025 17:45:29 +0000 Subject: [PATCH 23/34] Fix tests --- src/dodal/devices/undulator.py | 8 +- .../beamlines/i03/test_undulator_dcm.py | 17 ++- .../BeamLineEnergy_DCM_Pitch_converter.txt | 49 ++++---- .../BeamLineEnergy_DCM_Roll_converter.txt | 16 ++- ...beamline_undulator_to_gap_lookup_table.txt | 115 +++++++++--------- tests/devices/test_undulator.py | 21 ++-- tests/devices/util/test_lookup_tables.py | 10 +- 7 files changed, 128 insertions(+), 108 deletions(-) diff --git a/src/dodal/devices/undulator.py b/src/dodal/devices/undulator.py index 40ab647f22e..3663683c877 100644 --- a/src/dodal/devices/undulator.py +++ b/src/dodal/devices/undulator.py @@ -180,6 +180,8 @@ def __init__( baton: Baton | None = None, name: str = "", ) -> None: + self.config_server = ConfigServer(url="https://daq-config.diamond.ac.uk") + self.id_gap_lookup_table_path = id_gap_lookup_table_path super().__init__( prefix=prefix, @@ -208,9 +210,9 @@ 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. """ - config_server = ConfigServer(url="https://daq-config.diamond.ac.uk") - energy_to_distance_table = config_server.get_file_contents( - self.id_gap_lookup_table_path, GenericLookupTable + + energy_to_distance_table = self.config_server.get_file_contents( + self.id_gap_lookup_table_path, GenericLookupTable, reset_cached_result=True ) # Use the lookup table to get the undulator gap associated with this dcm energy diff --git a/tests/devices/beamlines/i03/test_undulator_dcm.py b/tests/devices/beamlines/i03/test_undulator_dcm.py index a6b9273cf34..fd3148b4b3d 100644 --- a/tests/devices/beamlines/i03/test_undulator_dcm.py +++ b/tests/devices/beamlines/i03/test_undulator_dcm.py @@ -3,6 +3,7 @@ import numpy as np import pytest +from daq_config_server.converters.models import GenericLookupTable from ophyd_async.core import AsyncStatus, get_mock_put, init_devices, set_mock_value from dodal.common.enums import EnabledDisabledUpper @@ -89,16 +90,24 @@ 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 = GenericLookupTable( + column_names=["energy_eV", "gap_mm"], + 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..45528733929 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,24 @@ -# 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 +{ + "column_names": ["bragg_angle_deg", "pitch_mrad"], + "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..b59f9dae6d9 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,10 @@ -#Bragg angle against roll( absolute number) -#reloadLookupTables() -# last update 2023/01/19 NP -Units Deg mrad -26.4095 -0.2799 -6.3075 -0.2799 +{ + "column_names": [ + "bragg_angle_deg", + "roll_mrad" + ], + "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..92b390d51d7 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,55 @@ -####################### -# # -# 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 +{ + "column_names": ["energy_eV", "gap_mm"], + "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 846e6b93665..3820249e250 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.converters.models import GenericLookupTable 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 = GenericLookupTable( + column_names=["energy_eV", "gap_mm"], 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 = GenericLookupTable( + column_names=["energy_eV", "gap_mm"], 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..e2ccad3b461 100644 --- a/tests/devices/util/test_lookup_tables.py +++ b/tests/devices/util/test_lookup_tables.py @@ -1,4 +1,7 @@ +import numpy as np import pytest +from daq_config_server.client import ConfigServer +from daq_config_server.converters.models import GenericLookupTable from pytest import mark from dodal.devices.util.lookup_tables import ( @@ -21,7 +24,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, GenericLookupTable + ).rows + ) assert table[0][0] == 5700 assert table[49][1] == 6.264 assert table.shape == (50, 2) From 79dbb4add1691cb71b4d13db434444aa0e61af83 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 6 Jan 2026 13:28:29 +0000 Subject: [PATCH 24/34] Use specific lut models for each undulator lut --- src/dodal/devices/undulator.py | 6 ++++-- tests/devices/beamlines/i03/test_undulator_dcm.py | 5 ++--- .../lookup/BeamLineEnergy_DCM_Pitch_converter.txt | 1 - .../lookup/BeamLineEnergy_DCM_Roll_converter.txt | 4 ---- .../test_beamline_undulator_to_gap_lookup_table.txt | 1 - tests/devices/test_undulator.py | 10 +++++----- tests/devices/util/test_lookup_tables.py | 5 ++--- 7 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/dodal/devices/undulator.py b/src/dodal/devices/undulator.py index 3663683c877..09d018d38ba 100644 --- a/src/dodal/devices/undulator.py +++ b/src/dodal/devices/undulator.py @@ -4,7 +4,7 @@ import numpy as np from bluesky.protocols import Locatable, Location, Movable from daq_config_server.client import ConfigServer -from daq_config_server.converters.models import GenericLookupTable +from daq_config_server.models import UndulatorEnergyGapLookupTable from numpy import ndarray from ophyd_async.core import ( AsyncStatus, @@ -212,7 +212,9 @@ async def _get_gap_to_match_energy(self, energy_kev: float) -> float: """ energy_to_distance_table = self.config_server.get_file_contents( - self.id_gap_lookup_table_path, GenericLookupTable, reset_cached_result=True + 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 diff --git a/tests/devices/beamlines/i03/test_undulator_dcm.py b/tests/devices/beamlines/i03/test_undulator_dcm.py index fd3148b4b3d..959aec77667 100644 --- a/tests/devices/beamlines/i03/test_undulator_dcm.py +++ b/tests/devices/beamlines/i03/test_undulator_dcm.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from daq_config_server.converters.models import GenericLookupTable +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 @@ -97,8 +97,7 @@ async def test_if_gap_is_wrong_then_logger_info_is_called_and_gap_is_set_correct mock_logger: MagicMock, fake_undulator_dcm: UndulatorDCM, ): - mock_get_file_contents.return_value = GenericLookupTable( - column_names=["energy_eV", "gap_mm"], + mock_get_file_contents.return_value = UndulatorEnergyGapLookupTable( rows=[ [5700, 5.4606], [7000, 6.045], 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 45528733929..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,5 +1,4 @@ { - "column_names": ["bragg_angle_deg", "pitch_mrad"], "rows": [ [19.24347, -0.79775], [16.40949, -0.78679], 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 b59f9dae6d9..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,8 +1,4 @@ { - "column_names": [ - "bragg_angle_deg", - "roll_mrad" - ], "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 92b390d51d7..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,5 +1,4 @@ { - "column_names": ["energy_eV", "gap_mm"], "rows": [ [5700, 5.4606], [5760, 5.5], diff --git a/tests/devices/test_undulator.py b/tests/devices/test_undulator.py index 3820249e250..e61d60fe8df 100644 --- a/tests/devices/test_undulator.py +++ b/tests/devices/test_undulator.py @@ -5,7 +5,7 @@ import pytest from bluesky import RunEngine from bluesky.plan_stubs import mv -from daq_config_server.converters.models import GenericLookupTable +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, @@ -157,8 +157,8 @@ async def test_gap_access_check_disabled_and_move_inhibited_when_commissioning_m mock_get_file_contents: MagicMock, undulator_in_commissioning_mode: UndulatorInKeV, ): - mock_get_file_contents.return_value = GenericLookupTable( - column_names=["energy_eV", "gap_mm"], rows=[[0, 10], [10, 20]] + mock_get_file_contents.return_value = UndulatorEnergyGapLookupTable( + rows=[[0, 10], [10, 20]] ) set_mock_value( undulator_in_commissioning_mode.gap_access, EnabledDisabledUpper.DISABLED @@ -175,8 +175,8 @@ async def test_gap_access_check_move_not_inhibited_when_commissioning_mode_disab mock_get_file_contents: MagicMock, undulator: UndulatorInKeV, ): - mock_get_file_contents.return_value = GenericLookupTable( - column_names=["energy_eV", "gap_mm"], rows=[[0, 10], [10000, 20]] + 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 e2ccad3b461..b326c50352d 100644 --- a/tests/devices/util/test_lookup_tables.py +++ b/tests/devices/util/test_lookup_tables.py @@ -1,11 +1,10 @@ import numpy as np import pytest from daq_config_server.client import ConfigServer -from daq_config_server.converters.models import GenericLookupTable +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, @@ -27,7 +26,7 @@ async def test_energy_to_distance_table_correct_format(): config_server = ConfigServer() table = np.array( config_server.get_file_contents( - TEST_BEAMLINE_UNDULATOR_TO_GAP_LUT, GenericLookupTable + TEST_BEAMLINE_UNDULATOR_TO_GAP_LUT, UndulatorEnergyGapLookupTable ).rows ) assert table[0][0] == 5700 From e897e2ac75fc76e7cc0c1287294df1db18dac629 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Fri, 27 Feb 2026 16:11:44 +0000 Subject: [PATCH 25/34] PR comments --- src/dodal/devices/undulator.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dodal/devices/undulator.py b/src/dodal/devices/undulator.py index 09d018d38ba..07196a799b1 100644 --- a/src/dodal/devices/undulator.py +++ b/src/dodal/devices/undulator.py @@ -3,7 +3,6 @@ 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 ( @@ -17,8 +16,10 @@ from ophyd_async.epics.core import epics_signal_r from ophyd_async.epics.motor import Motor +from dodal.common.beamlines.config_client import get_config_client from dodal.common.enums import EnabledDisabledUpper from dodal.log import LOGGER +from dodal.utils import get_beamline_name from .baton import Baton @@ -180,7 +181,7 @@ def __init__( baton: Baton | None = None, name: str = "", ) -> None: - self.config_server = ConfigServer(url="https://daq-config.diamond.ac.uk") + self.config_server = get_config_client(get_beamline_name()) self.id_gap_lookup_table_path = id_gap_lookup_table_path super().__init__( @@ -210,7 +211,6 @@ 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 = self.config_server.get_file_contents( self.id_gap_lookup_table_path, UndulatorEnergyGapLookupTable, From 9a5537ac3421c67e9354c33d7f6cb633bb4e46a0 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Fri, 27 Feb 2026 16:11:50 +0000 Subject: [PATCH 26/34] Fix tests --- .../beamlines/i03/test_undulator_dcm.py | 20 +++++++++++-------- tests/devices/beamlines/i07/test_id.py | 5 +++++ tests/devices/test_undulator.py | 17 ++++++++-------- .../test_verify_undulator_gap.py | 6 ++++++ tests/plans/test_verify_undulator_gap_plan.py | 6 ++++++ 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/tests/devices/beamlines/i03/test_undulator_dcm.py b/tests/devices/beamlines/i03/test_undulator_dcm.py index 959aec77667..4597bc4fcee 100644 --- a/tests/devices/beamlines/i03/test_undulator_dcm.py +++ b/tests/devices/beamlines/i03/test_undulator_dcm.py @@ -3,6 +3,7 @@ import numpy as np import pytest +from daq_config_server.client import ConfigServer from daq_config_server.models import UndulatorEnergyGapLookupTable from ophyd_async.core import AsyncStatus, get_mock_put, init_devices, set_mock_value @@ -91,19 +92,22 @@ async def test_fixed_offset_decoded(fake_undulator_dcm: UndulatorDCM): @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_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], - ], + mock_config_server = MagicMock() + mock_config_server.get_file_contents = MagicMock( + return_value=UndulatorEnergyGapLookupTable( + rows=[ + [5700, 5.4606], + [7000, 6.045], + [9700, 6.404], + ], + ) ) + fake_undulator_dcm.undulator_ref().config_server = mock_config_server + 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) diff --git a/tests/devices/beamlines/i07/test_id.py b/tests/devices/beamlines/i07/test_id.py index 999081b5219..de92da33a99 100644 --- a/tests/devices/beamlines/i07/test_id.py +++ b/tests/devices/beamlines/i07/test_id.py @@ -24,6 +24,11 @@ async def id(harmonic: UndulatorOrder) -> InsertionDevice: return id +@pytest.fixture(autouse=True) +def set_beamline_env_variable(monkeypatch): + monkeypatch.setenv("BEAMLINE", "i07") + + @pytest.mark.parametrize( "energy_kev, gap", [(14, 5.81), (15, 6.25)], diff --git a/tests/devices/test_undulator.py b/tests/devices/test_undulator.py index e61d60fe8df..bbe766780a7 100644 --- a/tests/devices/test_undulator.py +++ b/tests/devices/test_undulator.py @@ -64,6 +64,11 @@ def undulator_in_commissioning_mode( yield undulator +@pytest.fixture(autouse=True) +def set_beamline_env_variable(monkeypatch): + monkeypatch.setenv("BEAMLINE", "test") + + async def test_undulator_mm_config_default_parameters(undulator_in_mm: UndulatorInMm): await assert_configuration( undulator_in_mm, @@ -152,13 +157,11 @@ async def test_when_gap_access_is_disabled_set_then_error_is_raised( await undulator.set(5) -@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]] + undulator_in_commissioning_mode.config_server.get_file_contents = MagicMock( + return_value=UndulatorEnergyGapLookupTable(rows=[[0, 10], [10, 20]]) ) set_mock_value( undulator_in_commissioning_mode.gap_access, EnabledDisabledUpper.DISABLED @@ -170,13 +173,11 @@ async def test_gap_access_check_disabled_and_move_inhibited_when_commissioning_m ).assert_not_called() -@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]] + undulator.config_server.get_file_contents = MagicMock( + return_value=UndulatorEnergyGapLookupTable(rows=[[0, 10], [10000, 20]]) ) set_mock_value(undulator.gap_access, EnabledDisabledUpper.ENABLED) await undulator.set(5) diff --git a/tests/plans/test_preprocessors/test_verify_undulator_gap.py b/tests/plans/test_preprocessors/test_verify_undulator_gap.py index f51e4fe3157..0c8afe89190 100644 --- a/tests/plans/test_preprocessors/test_verify_undulator_gap.py +++ b/tests/plans/test_preprocessors/test_verify_undulator_gap.py @@ -2,6 +2,7 @@ import bluesky.plan_stubs as bps import bluesky.preprocessors as bpp +import pytest from bluesky.run_engine import RunEngine from dodal.plans.preprocessors.verify_undulator_gap import ( @@ -12,6 +13,11 @@ RUN_KEY = "test_run" +@pytest.fixture(autouse=True) +def set_beamline_env_variable(monkeypatch): + monkeypatch.setenv("BEAMLINE", "test") + + @patch("dodal.plans.verify_undulator_gap.verify_undulator_gap") def test_verify_undulator_gap_decorator_does_nothing_on_wrong_run( mock_verify: MagicMock, diff --git a/tests/plans/test_verify_undulator_gap_plan.py b/tests/plans/test_verify_undulator_gap_plan.py index a818d7adb66..c1dc7b7baed 100644 --- a/tests/plans/test_verify_undulator_gap_plan.py +++ b/tests/plans/test_verify_undulator_gap_plan.py @@ -1,5 +1,6 @@ from unittest.mock import AsyncMock, patch +import pytest from bluesky.run_engine import RunEngine from ophyd_async.core import set_mock_value @@ -7,6 +8,11 @@ from tests.plans.conftest import UndulatorGapCheckDevices +@pytest.fixture(autouse=True) +def set_beamline_env_variable(monkeypatch): + monkeypatch.setenv("BEAMLINE", "test") + + @patch("dodal.devices.undulator.BaseUndulator._set_gap", new_callable=AsyncMock) def test_verify_undulator_gap( mock_set: AsyncMock, From 667e94a5d6e6ff5dffe4355cebb6af8ee984f4ef Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Mon, 2 Mar 2026 10:11:28 +0000 Subject: [PATCH 27/34] Fix lint --- tests/devices/beamlines/i03/test_undulator_dcm.py | 1 - tests/devices/test_undulator.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/devices/beamlines/i03/test_undulator_dcm.py b/tests/devices/beamlines/i03/test_undulator_dcm.py index 4597bc4fcee..e2b884f98a0 100644 --- a/tests/devices/beamlines/i03/test_undulator_dcm.py +++ b/tests/devices/beamlines/i03/test_undulator_dcm.py @@ -3,7 +3,6 @@ import numpy as np import pytest -from daq_config_server.client import ConfigServer from daq_config_server.models import UndulatorEnergyGapLookupTable from ophyd_async.core import AsyncStatus, get_mock_put, init_devices, set_mock_value diff --git a/tests/devices/test_undulator.py b/tests/devices/test_undulator.py index bbe766780a7..0bb682490f0 100644 --- a/tests/devices/test_undulator.py +++ b/tests/devices/test_undulator.py @@ -1,5 +1,5 @@ from collections.abc import Generator -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock import numpy as np import pytest From 759b306caec992bc8497d47827cf8fb9b2c66ad8 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 3 Mar 2026 17:23:47 +0000 Subject: [PATCH 28/34] Use config server deployed on i04 cluster --- src/dodal/common/beamlines/config_client.py | 2 +- tests/common/beamlines/test_config_client.py | 32 +++++++++++--------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/dodal/common/beamlines/config_client.py b/src/dodal/common/beamlines/config_client.py index 23ace33f862..543b31dac12 100644 --- a/src/dodal/common/beamlines/config_client.py +++ b/src/dodal/common/beamlines/config_client.py @@ -4,7 +4,7 @@ BEAMLINE_CONFIG_SERVER_ENDPOINTS = { "i03": "https://i03-daq-config.diamond.ac.uk", - "i04": "https://daq-config.diamond.ac.uk", + "i04": "https://i04-daq-config.diamond.ac.uk", } diff --git a/tests/common/beamlines/test_config_client.py b/tests/common/beamlines/test_config_client.py index 303b1c2110f..70f51a0cc34 100644 --- a/tests/common/beamlines/test_config_client.py +++ b/tests/common/beamlines/test_config_client.py @@ -1,5 +1,7 @@ from unittest.mock import MagicMock, patch +import pytest + from dodal.common.beamlines.config_client import get_config_client @@ -11,22 +13,20 @@ def test_by_default_get_config_client_uses_centrally_deployed_config_server( mock_config_server.assert_called_once_with(url="https://daq-config.diamond.ac.uk") +@pytest.mark.parametrize( + "beamline, expected_url", + [ + ("i03", "https://i03-daq-config.diamond.ac.uk"), + ("i04", "https://i04-daq-config.diamond.ac.uk"), + ("default", "https://daq-config.diamond.ac.uk"), + ], +) @patch("dodal.common.beamlines.config_client.ConfigServer") -def test_get_config_client_uses_i03_beamline_cluster_server_for_i03( - mock_config_server: MagicMock, +def test_get_config_client_uses_correct_url_for_each_beamline( + mock_config_server: MagicMock, beamline: str, expected_url: str ): - get_config_client("i03") - mock_config_server.assert_called_once_with( - url="https://i03-daq-config.diamond.ac.uk" - ) - - -@patch("dodal.common.beamlines.config_client.ConfigServer") -def test_get_config_client_uses_centrally_deployed_config_server_for_i04( - mock_config_server: MagicMock, -): - get_config_client("i04") - mock_config_server.assert_called_once_with(url="https://daq-config.diamond.ac.uk") + get_config_client(beamline) + mock_config_server.assert_called_once_with(url=expected_url) @patch("dodal.common.beamlines.config_client.ConfigServer") @@ -45,7 +45,9 @@ def test_get_config_client_resets_cache_if_called_with_same_beamline( ): assert mock_config_server.assert_not_called get_config_client("i04") - mock_config_server.assert_called_once_with(url="https://daq-config.diamond.ac.uk") + mock_config_server.assert_called_once_with( + url="https://i04-daq-config.diamond.ac.uk" + ) get_config_client("i03") assert mock_config_server.call_count == 2 mock_config_server.assert_called_with(url="https://i03-daq-config.diamond.ac.uk") From b89130c8d31c6f037abd25975d277f74bb7532c0 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 3 Mar 2026 17:35:46 +0000 Subject: [PATCH 29/34] Improve patching of BEAMLINE env variable in tests --- tests/conftest.py | 5 +++++ tests/devices/beamlines/i07/test_id.py | 7 +------ tests/devices/test_undulator.py | 11 +++-------- tests/plans/conftest.py | 2 +- .../test_preprocessors/test_verify_undulator_gap.py | 6 ------ tests/plans/test_verify_undulator_gap_plan.py | 6 ------ 6 files changed, 10 insertions(+), 27 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 7c83dd8a692..46e5ed9306a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -177,3 +177,8 @@ def eiger_params(tmp_path: Path) -> DetectorParams: @pytest.fixture(autouse=True) def clear_cache(): get_config_client.cache_clear() + + +@pytest.fixture +def set_beamline_env_variable(monkeypatch): + monkeypatch.setenv("BEAMLINE", "test") diff --git a/tests/devices/beamlines/i07/test_id.py b/tests/devices/beamlines/i07/test_id.py index de92da33a99..903ef05e2ea 100644 --- a/tests/devices/beamlines/i07/test_id.py +++ b/tests/devices/beamlines/i07/test_id.py @@ -14,7 +14,7 @@ def harmonic() -> UndulatorOrder: @pytest.fixture -async def id(harmonic: UndulatorOrder) -> InsertionDevice: +async def id(harmonic: UndulatorOrder, set_beamline_env_variable) -> InsertionDevice: async with init_devices(mock=True): id = InsertionDevice( "ID-01", @@ -24,11 +24,6 @@ async def id(harmonic: UndulatorOrder) -> InsertionDevice: return id -@pytest.fixture(autouse=True) -def set_beamline_env_variable(monkeypatch): - monkeypatch.setenv("BEAMLINE", "i07") - - @pytest.mark.parametrize( "energy_kev, gap", [(14, 5.81), (15, 6.25)], diff --git a/tests/devices/test_undulator.py b/tests/devices/test_undulator.py index 0bb682490f0..2b5f8edcff3 100644 --- a/tests/devices/test_undulator.py +++ b/tests/devices/test_undulator.py @@ -30,7 +30,7 @@ @pytest.fixture -async def undulator() -> UndulatorInKeV: +async def undulator(set_beamline_env_variable) -> UndulatorInKeV: async with init_devices(mock=True): baton = Baton("BATON-01") undulator = UndulatorInKeV( @@ -64,11 +64,6 @@ def undulator_in_commissioning_mode( yield undulator -@pytest.fixture(autouse=True) -def set_beamline_env_variable(monkeypatch): - monkeypatch.setenv("BEAMLINE", "test") - - async def test_undulator_mm_config_default_parameters(undulator_in_mm: UndulatorInMm): await assert_configuration( undulator_in_mm, @@ -114,7 +109,7 @@ async def test_configuration_includes_configuration_fields(undulator: UndulatorI ) -async def test_poles_not_propagated_if_not_supplied(): +async def test_poles_not_propagated_if_not_supplied(set_beamline_env_variable): async with init_devices(mock=True): undulator = UndulatorInKeV( "UND-01", @@ -126,7 +121,7 @@ async def test_poles_not_propagated_if_not_supplied(): assert "undulator-poles" not in (await undulator.read_configuration()) -async def test_length_not_propagated_if_not_supplied(): +async def test_length_not_propagated_if_not_supplied(set_beamline_env_variable): async with init_devices(mock=True): undulator = UndulatorInKeV( "UND-01", diff --git a/tests/plans/conftest.py b/tests/plans/conftest.py index 38369eac5df..0c21e0610e8 100644 --- a/tests/plans/conftest.py +++ b/tests/plans/conftest.py @@ -21,7 +21,7 @@ def __init__(self, undulator: UndulatorInKeV, dcm: DoubleCrystalMonochromatorBas @pytest.fixture -async def mock_undulator_and_dcm() -> UndulatorGapCheckDevices: +async def mock_undulator_and_dcm(set_beamline_env_variable) -> UndulatorGapCheckDevices: async with init_devices(mock=True): undulator = UndulatorInKeV( "", diff --git a/tests/plans/test_preprocessors/test_verify_undulator_gap.py b/tests/plans/test_preprocessors/test_verify_undulator_gap.py index 0c8afe89190..f51e4fe3157 100644 --- a/tests/plans/test_preprocessors/test_verify_undulator_gap.py +++ b/tests/plans/test_preprocessors/test_verify_undulator_gap.py @@ -2,7 +2,6 @@ import bluesky.plan_stubs as bps import bluesky.preprocessors as bpp -import pytest from bluesky.run_engine import RunEngine from dodal.plans.preprocessors.verify_undulator_gap import ( @@ -13,11 +12,6 @@ RUN_KEY = "test_run" -@pytest.fixture(autouse=True) -def set_beamline_env_variable(monkeypatch): - monkeypatch.setenv("BEAMLINE", "test") - - @patch("dodal.plans.verify_undulator_gap.verify_undulator_gap") def test_verify_undulator_gap_decorator_does_nothing_on_wrong_run( mock_verify: MagicMock, diff --git a/tests/plans/test_verify_undulator_gap_plan.py b/tests/plans/test_verify_undulator_gap_plan.py index c1dc7b7baed..a818d7adb66 100644 --- a/tests/plans/test_verify_undulator_gap_plan.py +++ b/tests/plans/test_verify_undulator_gap_plan.py @@ -1,6 +1,5 @@ from unittest.mock import AsyncMock, patch -import pytest from bluesky.run_engine import RunEngine from ophyd_async.core import set_mock_value @@ -8,11 +7,6 @@ from tests.plans.conftest import UndulatorGapCheckDevices -@pytest.fixture(autouse=True) -def set_beamline_env_variable(monkeypatch): - monkeypatch.setenv("BEAMLINE", "test") - - @patch("dodal.devices.undulator.BaseUndulator._set_gap", new_callable=AsyncMock) def test_verify_undulator_gap( mock_set: AsyncMock, From 21b0c7110c20f642fbd39546e89532551a509058 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 17 Mar 2026 15:59:11 +0000 Subject: [PATCH 30/34] Pass config client into undulator --- src/dodal/beamlines/i03.py | 3 +++ src/dodal/beamlines/i04.py | 3 +++ src/dodal/beamlines/i07.py | 3 +++ src/dodal/beamlines/i18.py | 4 +++- src/dodal/beamlines/i22.py | 3 +++ src/dodal/beamlines/p38.py | 3 +++ src/dodal/devices/beamlines/i07/id.py | 4 +++- src/dodal/devices/undulator.py | 6 +++--- tests/devices/beamlines/i03/test_undulator_dcm.py | 2 ++ tests/devices/beamlines/i07/test_id.py | 2 ++ tests/devices/test_undulator.py | 4 ++++ tests/plans/conftest.py | 2 ++ 12 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/dodal/beamlines/i03.py b/src/dodal/beamlines/i03.py index 964c2aef87f..e973435f4ba 100644 --- a/src/dodal/beamlines/i03.py +++ b/src/dodal/beamlines/i03.py @@ -9,6 +9,7 @@ from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.common.beamlines.beamline_utils import set_path_provider from dodal.common.beamlines.commissioning_mode import set_commissioning_signal +from dodal.common.beamlines.config_client import get_config_client from dodal.common.udc_directory_provider import PandASubpathProvider from dodal.device_manager import DeviceManager from dodal.devices.aperturescatterguard import ( @@ -71,6 +72,7 @@ DAQ_CONFIGURATION_PATH = "/dls_sw/i03/software/daq_configuration" BL = get_beamline_name("i03") +CONFIG_CLIENT = get_config_client(BL) set_log_beamline(BL) set_utils_beamline(BL) @@ -226,6 +228,7 @@ def synchrotron() -> Synchrotron: def undulator(baton: Baton, daq_configuration_path: str) -> UndulatorInKeV: return UndulatorInKeV( f"{BeamlinePrefix(BL).insertion_prefix}-MO-SERVC-01:", + config_client=CONFIG_CLIENT, id_gap_lookup_table_path=f"{daq_configuration_path}/lookup/BeamLine_Undulator_toGap.txt", baton=baton, ) diff --git a/src/dodal/beamlines/i04.py b/src/dodal/beamlines/i04.py index 95e0cc6c0e7..e5c0d14d633 100644 --- a/src/dodal/beamlines/i04.py +++ b/src/dodal/beamlines/i04.py @@ -2,6 +2,7 @@ from dodal.common.beamlines.beamline_parameters import get_beamline_parameters from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.common.beamlines.config_client import get_config_client from dodal.device_manager import DeviceManager from dodal.devices.aperturescatterguard import ( AperturePosition, @@ -60,6 +61,7 @@ BL = get_beamline_name("i04") +CONFIG_CLIENT = get_config_client(BL) set_log_beamline(BL) set_utils_beamline(BL) @@ -182,6 +184,7 @@ def daq_configuration_path() -> str: def undulator(baton: Baton, daq_configuration_path: str) -> UndulatorInKeV: return UndulatorInKeV( prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:", + config_client=CONFIG_CLIENT, id_gap_lookup_table_path=f"{daq_configuration_path}/lookup/BeamLine_Undulator_toGap.txt", baton=baton, ) diff --git a/src/dodal/beamlines/i07.py b/src/dodal/beamlines/i07.py index df344f25657..8b7a51759d7 100644 --- a/src/dodal/beamlines/i07.py +++ b/src/dodal/beamlines/i07.py @@ -1,4 +1,5 @@ from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.common.beamlines.config_client import get_config_client from dodal.device_manager import DeviceManager from dodal.devices.beamlines.i07.dcm import DCM from dodal.devices.beamlines.i07.id import InsertionDevice @@ -7,6 +8,7 @@ from dodal.utils import BeamlinePrefix, get_beamline_name BL = get_beamline_name("i07") +CONFIG_CLIENT = get_config_client(BL) set_log_beamline(BL) set_utils_beamline(BL) PREFIX = BeamlinePrefix(BL) @@ -35,5 +37,6 @@ def id(harmonic: UndulatorOrder) -> InsertionDevice: return InsertionDevice( f"{PREFIX.insertion_prefix}-MO-SERVC-01:", harmonic, + CONFIG_CLIENT, id_gap_lookup_table_path="/dls_sw/i07/software/gda/config/lookupTables/IIDCalibrationTable.txt", ) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index f52772220d4..c832d873d6c 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -8,6 +8,7 @@ set_path_provider, ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.common.beamlines.config_client import get_config_client from dodal.common.visit import ( LocalDirectoryServiceClient, StaticVisitPathProvider, @@ -28,6 +29,7 @@ from dodal.utils import BeamlinePrefix, get_beamline_name BL = get_beamline_name("i18") +CONFIG_CLIENT = get_config_client(BL) PREFIX = BeamlinePrefix(BL) set_log_beamline(BL) set_utils_beamline(BL) @@ -54,7 +56,7 @@ def synchrotron() -> Synchrotron: @device_factory() def undulator() -> UndulatorInKeV: - return UndulatorInKeV(f"{PREFIX.insertion_prefix}-MO-SERVC-01:") + return UndulatorInKeV(f"{PREFIX.insertion_prefix}-MO-SERVC-01:", CONFIG_CLIENT) # See https://github.com/DiamondLightSource/dodal/issues/1180 diff --git a/src/dodal/beamlines/i22.py b/src/dodal/beamlines/i22.py index 2373220569c..bca797fbace 100644 --- a/src/dodal/beamlines/i22.py +++ b/src/dodal/beamlines/i22.py @@ -12,6 +12,7 @@ from ophyd_async.fastcs.panda import HDFPanda from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.common.beamlines.config_client import get_config_client from dodal.common.beamlines.device_helpers import CAM_SUFFIX, DET_SUFFIX, HDF5_SUFFIX from dodal.common.crystal_metadata import ( MaterialsEnum, @@ -39,6 +40,7 @@ BL = get_beamline_name("i22") PREFIX = BeamlinePrefix(BL) +CONFIG_CLIENT = get_config_client(BL) set_log_beamline(BL) set_utils_beamline(BL) @@ -181,6 +183,7 @@ def dcm() -> DCM: def undulator() -> UndulatorInKeV: return UndulatorInKeV( prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:", + config_client=CONFIG_CLIENT, id_gap_lookup_table_path="/dls_sw/i22/software/daq_configuration/lookup/BeamLine_Undulator_toGap.txt", poles=80, length=2.0, diff --git a/src/dodal/beamlines/p38.py b/src/dodal/beamlines/p38.py index c4fc10f0a31..ca3501ad6fc 100644 --- a/src/dodal/beamlines/p38.py +++ b/src/dodal/beamlines/p38.py @@ -9,6 +9,7 @@ set_path_provider, ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.common.beamlines.config_client import get_config_client from dodal.common.beamlines.device_helpers import HDF5_SUFFIX from dodal.common.crystal_metadata import ( MaterialsEnum, @@ -29,6 +30,7 @@ BL = get_beamline_name("p38") PREFIX = BeamlinePrefix(BL) +CONFIG_CLIENT = get_config_client(BL) set_log_beamline(BL) set_utils_beamline(BL) @@ -160,6 +162,7 @@ def dcm() -> DCM: def undulator() -> UndulatorInKeV: return UndulatorInKeV( f"{PREFIX.insertion_prefix}-MO-SERVC-01:", + CONFIG_CLIENT, poles=80, length=2.0, ) diff --git a/src/dodal/devices/beamlines/i07/id.py b/src/dodal/devices/beamlines/i07/id.py index f33e12c042f..7ee32736826 100644 --- a/src/dodal/devices/beamlines/i07/id.py +++ b/src/dodal/devices/beamlines/i07/id.py @@ -1,4 +1,5 @@ import numpy as np +from daq_config_server.client import ConfigServer from dodal.devices.undulator import UndulatorInKeV, UndulatorOrder from dodal.devices.util.lookup_tables import energy_distance_table @@ -13,11 +14,12 @@ def __init__( self, prefix: str, harmonic: UndulatorOrder, + config_client: ConfigServer, id_gap_lookup_table_path: str = "/dls_sw/i07/software/gda/config/lookupTables/" + "IIDCalibrationTable.txt", name: str = "", ): - super().__init__(prefix, id_gap_lookup_table_path, name=name) + super().__init__(prefix, config_client, id_gap_lookup_table_path, name=name) self.harmonic = harmonic async def _get_gap_to_match_energy(self, energy_kev: float) -> float: diff --git a/src/dodal/devices/undulator.py b/src/dodal/devices/undulator.py index 07196a799b1..00bb413391d 100644 --- a/src/dodal/devices/undulator.py +++ b/src/dodal/devices/undulator.py @@ -3,6 +3,7 @@ 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 ( @@ -16,10 +17,8 @@ from ophyd_async.epics.core import epics_signal_r from ophyd_async.epics.motor import Motor -from dodal.common.beamlines.config_client import get_config_client from dodal.common.enums import EnabledDisabledUpper from dodal.log import LOGGER -from dodal.utils import get_beamline_name from .baton import Baton @@ -174,6 +173,7 @@ class UndulatorInKeV(BaseUndulator): def __init__( self, prefix: str, + config_client: ConfigServer, id_gap_lookup_table_path: str = os.devnull, poles: int | None = None, length: float | None = None, @@ -181,7 +181,7 @@ def __init__( baton: Baton | None = None, name: str = "", ) -> None: - self.config_server = get_config_client(get_beamline_name()) + self.config_server = config_client self.id_gap_lookup_table_path = id_gap_lookup_table_path super().__init__( diff --git a/tests/devices/beamlines/i03/test_undulator_dcm.py b/tests/devices/beamlines/i03/test_undulator_dcm.py index e2b884f98a0..cc1b8bc0793 100644 --- a/tests/devices/beamlines/i03/test_undulator_dcm.py +++ b/tests/devices/beamlines/i03/test_undulator_dcm.py @@ -3,6 +3,7 @@ import numpy as np import pytest +from daq_config_server.client import ConfigServer from daq_config_server.models import UndulatorEnergyGapLookupTable from ophyd_async.core import AsyncStatus, get_mock_put, init_devices, set_mock_value @@ -49,6 +50,7 @@ async def fake_undulator_dcm() -> UndulatorDCM: baton = Baton("BATON-01:") undulator = UndulatorInKeV( "UND-01", + ConfigServer(""), name="undulator", poles=80, id_gap_lookup_table_path=TEST_BEAMLINE_UNDULATOR_TO_GAP_LUT, diff --git a/tests/devices/beamlines/i07/test_id.py b/tests/devices/beamlines/i07/test_id.py index 903ef05e2ea..a178fb708df 100644 --- a/tests/devices/beamlines/i07/test_id.py +++ b/tests/devices/beamlines/i07/test_id.py @@ -1,4 +1,5 @@ import pytest +from daq_config_server.client import ConfigServer from ophyd_async.core import init_devices from dodal.devices.beamlines.i07.id import InsertionDevice @@ -19,6 +20,7 @@ async def id(harmonic: UndulatorOrder, set_beamline_env_variable) -> InsertionDe id = InsertionDevice( "ID-01", harmonic, + ConfigServer(""), TEST_LOOKUP_TABLE_PATH, ) return id diff --git a/tests/devices/test_undulator.py b/tests/devices/test_undulator.py index 2b5f8edcff3..5de32f91b00 100644 --- a/tests/devices/test_undulator.py +++ b/tests/devices/test_undulator.py @@ -5,6 +5,7 @@ import pytest from bluesky import RunEngine from bluesky.plan_stubs import mv +from daq_config_server.client import ConfigServer 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 ( @@ -35,6 +36,7 @@ async def undulator(set_beamline_env_variable) -> UndulatorInKeV: baton = Baton("BATON-01") undulator = UndulatorInKeV( "UND-01", + ConfigServer(""), name="undulator", poles=80, length=2.0, @@ -113,6 +115,7 @@ async def test_poles_not_propagated_if_not_supplied(set_beamline_env_variable): async with init_devices(mock=True): undulator = UndulatorInKeV( "UND-01", + ConfigServer(""), name="undulator", length=2.0, id_gap_lookup_table_path=TEST_BEAMLINE_UNDULATOR_TO_GAP_LUT, @@ -125,6 +128,7 @@ async def test_length_not_propagated_if_not_supplied(set_beamline_env_variable): async with init_devices(mock=True): undulator = UndulatorInKeV( "UND-01", + ConfigServer(""), name="undulator", poles=80, id_gap_lookup_table_path=TEST_BEAMLINE_UNDULATOR_TO_GAP_LUT, diff --git a/tests/plans/conftest.py b/tests/plans/conftest.py index 0c21e0610e8..d738504c810 100644 --- a/tests/plans/conftest.py +++ b/tests/plans/conftest.py @@ -3,6 +3,7 @@ from unittest.mock import patch import pytest +from daq_config_server.client import ConfigServer from ophyd_async.core import PathProvider, StandardDetector, init_devices from ophyd_async.sim import PatternGenerator, SimBlobDetector, SimMotor @@ -25,6 +26,7 @@ async def mock_undulator_and_dcm(set_beamline_env_variable) -> UndulatorGapCheck async with init_devices(mock=True): undulator = UndulatorInKeV( "", + ConfigServer(""), id_gap_lookup_table_path=TEST_BEAMLINE_UNDULATOR_TO_GAP_LUT, ) dcm = DCM("") From a32f35833afd40fc9f78f1fe302c92317d281769 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Wed, 18 Mar 2026 10:20:11 +0000 Subject: [PATCH 31/34] Fix imports --- src/dodal/devices/beamlines/i07/id.py | 4 ++-- src/dodal/devices/undulator.py | 8 +++++--- tests/devices/beamlines/i03/test_undulator_dcm.py | 8 +++++--- tests/devices/beamlines/i07/test_id.py | 4 ++-- tests/devices/test_undulator.py | 12 +++++++----- tests/devices/util/test_lookup_tables.py | 8 +++++--- tests/plans/conftest.py | 4 ++-- 7 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/dodal/devices/beamlines/i07/id.py b/src/dodal/devices/beamlines/i07/id.py index 7ee32736826..dd2334a281f 100644 --- a/src/dodal/devices/beamlines/i07/id.py +++ b/src/dodal/devices/beamlines/i07/id.py @@ -1,5 +1,5 @@ import numpy as np -from daq_config_server.client import ConfigServer +from daq_config_server import ConfigClient from dodal.devices.undulator import UndulatorInKeV, UndulatorOrder from dodal.devices.util.lookup_tables import energy_distance_table @@ -14,7 +14,7 @@ def __init__( self, prefix: str, harmonic: UndulatorOrder, - config_client: ConfigServer, + config_client: ConfigClient, id_gap_lookup_table_path: str = "/dls_sw/i07/software/gda/config/lookupTables/" + "IIDCalibrationTable.txt", name: str = "", diff --git a/src/dodal/devices/undulator.py b/src/dodal/devices/undulator.py index 00bb413391d..b465352845b 100644 --- a/src/dodal/devices/undulator.py +++ b/src/dodal/devices/undulator.py @@ -3,8 +3,10 @@ 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 daq_config_server import ConfigClient +from daq_config_server.models.lookup_tables.insertion_device import ( + UndulatorEnergyGapLookupTable, +) from numpy import ndarray from ophyd_async.core import ( AsyncStatus, @@ -173,7 +175,7 @@ class UndulatorInKeV(BaseUndulator): def __init__( self, prefix: str, - config_client: ConfigServer, + config_client: ConfigClient, id_gap_lookup_table_path: str = os.devnull, poles: int | None = None, length: float | None = None, diff --git a/tests/devices/beamlines/i03/test_undulator_dcm.py b/tests/devices/beamlines/i03/test_undulator_dcm.py index cc1b8bc0793..670e70a09c0 100644 --- a/tests/devices/beamlines/i03/test_undulator_dcm.py +++ b/tests/devices/beamlines/i03/test_undulator_dcm.py @@ -3,8 +3,10 @@ import numpy as np import pytest -from daq_config_server.client import ConfigServer -from daq_config_server.models import UndulatorEnergyGapLookupTable +from daq_config_server import ConfigClient +from daq_config_server.models.lookup_tables.insertion_device import ( + UndulatorEnergyGapLookupTable, +) from ophyd_async.core import AsyncStatus, get_mock_put, init_devices, set_mock_value from dodal.common.enums import EnabledDisabledUpper @@ -50,7 +52,7 @@ async def fake_undulator_dcm() -> UndulatorDCM: baton = Baton("BATON-01:") undulator = UndulatorInKeV( "UND-01", - ConfigServer(""), + ConfigClient(""), name="undulator", poles=80, id_gap_lookup_table_path=TEST_BEAMLINE_UNDULATOR_TO_GAP_LUT, diff --git a/tests/devices/beamlines/i07/test_id.py b/tests/devices/beamlines/i07/test_id.py index a178fb708df..03715572bda 100644 --- a/tests/devices/beamlines/i07/test_id.py +++ b/tests/devices/beamlines/i07/test_id.py @@ -1,5 +1,5 @@ import pytest -from daq_config_server.client import ConfigServer +from daq_config_server import ConfigClient from ophyd_async.core import init_devices from dodal.devices.beamlines.i07.id import InsertionDevice @@ -20,7 +20,7 @@ async def id(harmonic: UndulatorOrder, set_beamline_env_variable) -> InsertionDe id = InsertionDevice( "ID-01", harmonic, - ConfigServer(""), + ConfigClient(""), TEST_LOOKUP_TABLE_PATH, ) return id diff --git a/tests/devices/test_undulator.py b/tests/devices/test_undulator.py index 5de32f91b00..30c8dc9c4cd 100644 --- a/tests/devices/test_undulator.py +++ b/tests/devices/test_undulator.py @@ -5,8 +5,10 @@ import pytest from bluesky import RunEngine from bluesky.plan_stubs import mv -from daq_config_server.client import ConfigServer -from daq_config_server.models import UndulatorEnergyGapLookupTable +from daq_config_server import ConfigClient +from daq_config_server.models.lookup_tables.insertion_device import ( + UndulatorEnergyGapLookupTable, +) from ophyd_async.core import get_mock_put, init_devices, set_mock_value from ophyd_async.testing import ( assert_configuration, @@ -36,7 +38,7 @@ async def undulator(set_beamline_env_variable) -> UndulatorInKeV: baton = Baton("BATON-01") undulator = UndulatorInKeV( "UND-01", - ConfigServer(""), + ConfigClient(""), name="undulator", poles=80, length=2.0, @@ -115,7 +117,7 @@ async def test_poles_not_propagated_if_not_supplied(set_beamline_env_variable): async with init_devices(mock=True): undulator = UndulatorInKeV( "UND-01", - ConfigServer(""), + ConfigClient(""), name="undulator", length=2.0, id_gap_lookup_table_path=TEST_BEAMLINE_UNDULATOR_TO_GAP_LUT, @@ -128,7 +130,7 @@ async def test_length_not_propagated_if_not_supplied(set_beamline_env_variable): async with init_devices(mock=True): undulator = UndulatorInKeV( "UND-01", - ConfigServer(""), + ConfigClient(""), name="undulator", poles=80, id_gap_lookup_table_path=TEST_BEAMLINE_UNDULATOR_TO_GAP_LUT, diff --git a/tests/devices/util/test_lookup_tables.py b/tests/devices/util/test_lookup_tables.py index b326c50352d..d18d62e8964 100644 --- a/tests/devices/util/test_lookup_tables.py +++ b/tests/devices/util/test_lookup_tables.py @@ -1,7 +1,9 @@ import numpy as np import pytest -from daq_config_server.client import ConfigServer -from daq_config_server.models import UndulatorEnergyGapLookupTable +from daq_config_server import ConfigClient +from daq_config_server.models.lookup_tables.insertion_device import ( + UndulatorEnergyGapLookupTable, +) from pytest import mark from dodal.devices.util.lookup_tables import ( @@ -23,7 +25,7 @@ async def test_energy_to_distance_table_correct_format(): - config_server = ConfigServer() + config_server = ConfigClient("") table = np.array( config_server.get_file_contents( TEST_BEAMLINE_UNDULATOR_TO_GAP_LUT, UndulatorEnergyGapLookupTable diff --git a/tests/plans/conftest.py b/tests/plans/conftest.py index d738504c810..0cdc7da67da 100644 --- a/tests/plans/conftest.py +++ b/tests/plans/conftest.py @@ -3,7 +3,7 @@ from unittest.mock import patch import pytest -from daq_config_server.client import ConfigServer +from daq_config_server import ConfigClient from ophyd_async.core import PathProvider, StandardDetector, init_devices from ophyd_async.sim import PatternGenerator, SimBlobDetector, SimMotor @@ -26,7 +26,7 @@ async def mock_undulator_and_dcm(set_beamline_env_variable) -> UndulatorGapCheck async with init_devices(mock=True): undulator = UndulatorInKeV( "", - ConfigServer(""), + ConfigClient(""), id_gap_lookup_table_path=TEST_BEAMLINE_UNDULATOR_TO_GAP_LUT, ) dcm = DCM("") From 464b030e59f90d628ea1de36a4a3df7ca9b0188e Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Wed, 18 Mar 2026 10:41:54 +0000 Subject: [PATCH 32/34] Fix test --- tests/common/beamlines/test_config_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/beamlines/test_config_client.py b/tests/common/beamlines/test_config_client.py index f90a7ea450b..a9420cac89e 100644 --- a/tests/common/beamlines/test_config_client.py +++ b/tests/common/beamlines/test_config_client.py @@ -21,7 +21,7 @@ def test_by_default_get_config_client_uses_centrally_deployed_config_server( ("default", "https://daq-config.diamond.ac.uk"), ], ) -@patch("dodal.common.beamlines.config_client.ConfigServer") +@patch("dodal.common.beamlines.config_client.ConfigClient") def test_get_config_client_uses_correct_url_for_each_beamline( mock_config_client: MagicMock, beamline: str, expected_url: str ): From 3ca40b4abfb02c72aa4215b75e8da849a6885831 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Wed, 18 Mar 2026 15:47:54 +0000 Subject: [PATCH 33/34] Remove uneeded fixtures --- tests/devices/beamlines/i07/test_id.py | 2 +- tests/devices/test_undulator.py | 6 +++--- tests/plans/conftest.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/devices/beamlines/i07/test_id.py b/tests/devices/beamlines/i07/test_id.py index 03715572bda..07554ae898e 100644 --- a/tests/devices/beamlines/i07/test_id.py +++ b/tests/devices/beamlines/i07/test_id.py @@ -15,7 +15,7 @@ def harmonic() -> UndulatorOrder: @pytest.fixture -async def id(harmonic: UndulatorOrder, set_beamline_env_variable) -> InsertionDevice: +async def id(harmonic: UndulatorOrder) -> InsertionDevice: async with init_devices(mock=True): id = InsertionDevice( "ID-01", diff --git a/tests/devices/test_undulator.py b/tests/devices/test_undulator.py index 30c8dc9c4cd..c04e1fd4317 100644 --- a/tests/devices/test_undulator.py +++ b/tests/devices/test_undulator.py @@ -33,7 +33,7 @@ @pytest.fixture -async def undulator(set_beamline_env_variable) -> UndulatorInKeV: +async def undulator() -> UndulatorInKeV: async with init_devices(mock=True): baton = Baton("BATON-01") undulator = UndulatorInKeV( @@ -113,7 +113,7 @@ async def test_configuration_includes_configuration_fields(undulator: UndulatorI ) -async def test_poles_not_propagated_if_not_supplied(set_beamline_env_variable): +async def test_poles_not_propagated_if_not_supplied(): async with init_devices(mock=True): undulator = UndulatorInKeV( "UND-01", @@ -126,7 +126,7 @@ async def test_poles_not_propagated_if_not_supplied(set_beamline_env_variable): assert "undulator-poles" not in (await undulator.read_configuration()) -async def test_length_not_propagated_if_not_supplied(set_beamline_env_variable): +async def test_length_not_propagated_if_not_supplied(): async with init_devices(mock=True): undulator = UndulatorInKeV( "UND-01", diff --git a/tests/plans/conftest.py b/tests/plans/conftest.py index 0cdc7da67da..8cc97adb658 100644 --- a/tests/plans/conftest.py +++ b/tests/plans/conftest.py @@ -22,7 +22,7 @@ def __init__(self, undulator: UndulatorInKeV, dcm: DoubleCrystalMonochromatorBas @pytest.fixture -async def mock_undulator_and_dcm(set_beamline_env_variable) -> UndulatorGapCheckDevices: +async def mock_undulator_and_dcm() -> UndulatorGapCheckDevices: async with init_devices(mock=True): undulator = UndulatorInKeV( "", From f2d2cb59244f45f0fe2cc4bc07ba600b13a5af34 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Thu, 19 Mar 2026 17:22:59 +0000 Subject: [PATCH 34/34] Remove beamline config client constants --- src/dodal/beamlines/i03.py | 3 +-- src/dodal/beamlines/i04.py | 3 +-- src/dodal/beamlines/i07.py | 3 +-- src/dodal/beamlines/i18.py | 5 +++-- src/dodal/beamlines/i22.py | 3 +-- src/dodal/beamlines/p38.py | 3 +-- src/dodal/common/beamlines/config_client.py | 2 ++ 7 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/dodal/beamlines/i03.py b/src/dodal/beamlines/i03.py index e973435f4ba..9a4d30e1f54 100644 --- a/src/dodal/beamlines/i03.py +++ b/src/dodal/beamlines/i03.py @@ -72,7 +72,6 @@ DAQ_CONFIGURATION_PATH = "/dls_sw/i03/software/daq_configuration" BL = get_beamline_name("i03") -CONFIG_CLIENT = get_config_client(BL) set_log_beamline(BL) set_utils_beamline(BL) @@ -228,7 +227,7 @@ def synchrotron() -> Synchrotron: def undulator(baton: Baton, daq_configuration_path: str) -> UndulatorInKeV: return UndulatorInKeV( f"{BeamlinePrefix(BL).insertion_prefix}-MO-SERVC-01:", - config_client=CONFIG_CLIENT, + config_client=get_config_client(BL), id_gap_lookup_table_path=f"{daq_configuration_path}/lookup/BeamLine_Undulator_toGap.txt", baton=baton, ) diff --git a/src/dodal/beamlines/i04.py b/src/dodal/beamlines/i04.py index e5c0d14d633..b69f621fe59 100644 --- a/src/dodal/beamlines/i04.py +++ b/src/dodal/beamlines/i04.py @@ -61,7 +61,6 @@ BL = get_beamline_name("i04") -CONFIG_CLIENT = get_config_client(BL) set_log_beamline(BL) set_utils_beamline(BL) @@ -184,7 +183,7 @@ def daq_configuration_path() -> str: def undulator(baton: Baton, daq_configuration_path: str) -> UndulatorInKeV: return UndulatorInKeV( prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:", - config_client=CONFIG_CLIENT, + config_client=get_config_client(BL), id_gap_lookup_table_path=f"{daq_configuration_path}/lookup/BeamLine_Undulator_toGap.txt", baton=baton, ) diff --git a/src/dodal/beamlines/i07.py b/src/dodal/beamlines/i07.py index 8b7a51759d7..0ec3137ee19 100644 --- a/src/dodal/beamlines/i07.py +++ b/src/dodal/beamlines/i07.py @@ -8,7 +8,6 @@ from dodal.utils import BeamlinePrefix, get_beamline_name BL = get_beamline_name("i07") -CONFIG_CLIENT = get_config_client(BL) set_log_beamline(BL) set_utils_beamline(BL) PREFIX = BeamlinePrefix(BL) @@ -37,6 +36,6 @@ def id(harmonic: UndulatorOrder) -> InsertionDevice: return InsertionDevice( f"{PREFIX.insertion_prefix}-MO-SERVC-01:", harmonic, - CONFIG_CLIENT, + get_config_client(BL), id_gap_lookup_table_path="/dls_sw/i07/software/gda/config/lookupTables/IIDCalibrationTable.txt", ) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index c832d873d6c..8698bba157d 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -29,7 +29,6 @@ from dodal.utils import BeamlinePrefix, get_beamline_name BL = get_beamline_name("i18") -CONFIG_CLIENT = get_config_client(BL) PREFIX = BeamlinePrefix(BL) set_log_beamline(BL) set_utils_beamline(BL) @@ -56,7 +55,9 @@ def synchrotron() -> Synchrotron: @device_factory() def undulator() -> UndulatorInKeV: - return UndulatorInKeV(f"{PREFIX.insertion_prefix}-MO-SERVC-01:", CONFIG_CLIENT) + return UndulatorInKeV( + f"{PREFIX.insertion_prefix}-MO-SERVC-01:", get_config_client(BL) + ) # See https://github.com/DiamondLightSource/dodal/issues/1180 diff --git a/src/dodal/beamlines/i22.py b/src/dodal/beamlines/i22.py index bca797fbace..d09c8ab2cc6 100644 --- a/src/dodal/beamlines/i22.py +++ b/src/dodal/beamlines/i22.py @@ -40,7 +40,6 @@ BL = get_beamline_name("i22") PREFIX = BeamlinePrefix(BL) -CONFIG_CLIENT = get_config_client(BL) set_log_beamline(BL) set_utils_beamline(BL) @@ -183,7 +182,7 @@ def dcm() -> DCM: def undulator() -> UndulatorInKeV: return UndulatorInKeV( prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:", - config_client=CONFIG_CLIENT, + config_client=get_config_client(BL), id_gap_lookup_table_path="/dls_sw/i22/software/daq_configuration/lookup/BeamLine_Undulator_toGap.txt", poles=80, length=2.0, diff --git a/src/dodal/beamlines/p38.py b/src/dodal/beamlines/p38.py index ca3501ad6fc..a750bc8d722 100644 --- a/src/dodal/beamlines/p38.py +++ b/src/dodal/beamlines/p38.py @@ -30,7 +30,6 @@ BL = get_beamline_name("p38") PREFIX = BeamlinePrefix(BL) -CONFIG_CLIENT = get_config_client(BL) set_log_beamline(BL) set_utils_beamline(BL) @@ -162,7 +161,7 @@ def dcm() -> DCM: def undulator() -> UndulatorInKeV: return UndulatorInKeV( f"{PREFIX.insertion_prefix}-MO-SERVC-01:", - CONFIG_CLIENT, + get_config_client(BL), poles=80, length=2.0, ) diff --git a/src/dodal/common/beamlines/config_client.py b/src/dodal/common/beamlines/config_client.py index 2334b43f076..1194573e61e 100644 --- a/src/dodal/common/beamlines/config_client.py +++ b/src/dodal/common/beamlines/config_client.py @@ -10,6 +10,8 @@ @cache def get_config_client(beamline: str) -> ConfigClient: + print(beamline) + print(BEAMLINE_CONFIG_SERVER_ENDPOINTS) url = BEAMLINE_CONFIG_SERVER_ENDPOINTS.get( beamline, "https://daq-config.diamond.ac.uk" )