From b4dbd8e515cf46d7f51d65fad9bdc834ca396ffe Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 9 Dec 2025 09:37:13 +0000 Subject: [PATCH 01/21] 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 0563b9cae3..00c15d9887 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/21] 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 fb66035e4f..31f76b06c3 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/21] 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 aeba8295f1..3af042869f 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/21] 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 0a783cd2f8..030fbcc942 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 41eb410097..2f66fe87f0 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 497b0d790e..7707e91a61 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 87beab7f2c..3e24a3e69f 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 5247b15d3f..b757968398 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/21] 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 5be5841527..5ca1e7711b 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/21] 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 31f76b06c3..a29856f046 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 3af042869f..b0e6132ed6 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 2f66fe87f0..9a03694463 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 7707e91a61..0bbcbd9fa2 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/21] Fix --- tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 00c15d9887..eeede34f32 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/21] Require latest daq-config-server --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 545b739944..15f0a84182 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/21] 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 a29856f046..d6632013a9 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 eeede34f32..708c4aff34 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/21] 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 d6632013a9..d472cd6663 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 b0e6132ed6..401272b604 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 2b47ae91d6..155502b1cb 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/21] 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 3987b4563d..4a69d10832 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 18cc611344..c6ed15de73 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 d472cd6663..77614acbcc 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 0000000000..266c8b0214 --- /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 403000fb7d..5920976785 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 0973df2698..1e4990b806 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 ede8f25a2e..409065b8f7 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 bfa4506b1e..baa6890abe 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 61a0f905dc..f56a536e66 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 401272b604..af0d2ca84f 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 33c2a24121..5e42b83b22 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 155502b1cb..ecccb050ed 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 ce1579bc1b..e65586c2c3 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/21] Update lockfile --- uv.lock | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/uv.lock b/uv.lock index eca15cb4b9..297e2bf774 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/21] 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 266c8b0214..a5479760bd 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 0000000000..5958760b64 --- /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/21] 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 5958760b64..99857012dc 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/21] 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 61dcdcefae..b6f984a865 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 a5479760bd..23ace33f86 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 5ecf7d3123..80c9b227e7 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 0000000000..1092cb120b --- /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 aa10ff5ea0..f2c602427a 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 af0d2ca84f..f33683afc7 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 99857012dc..648edb2d29 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 8af64fa979..66d3a52415 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 0000000000..c116593b79 --- /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 9bcf7bd435..859b58477b 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 4494a20be1..5ca3d626c1 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/21] 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 648edb2d29..303b1c2110 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 66d3a52415..7c83dd8a69 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/21] 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 f2c602427a..08c0fb16bd 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/21] 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 5ca3d626c1..312ecab3f9 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/21] 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 1092cb120b..d919398088 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/21] 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 f33683afc7..3bd680699a 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 9dd86e1cbc..d4877ab2a4 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/21] 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 3bd680699a..40a86350cd 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