From 9ee2a373416ab56422465a2daf8778d34e6f6d0b Mon Sep 17 00:00:00 2001 From: Matthew Carre Date: Mon, 18 May 2026 09:24:29 +0100 Subject: [PATCH 1/8] Adds initial circular plans --- .../test_arithmetic_conversions.py | 21 ++++++++++++++ .../test_transmission_interconversion.py | 29 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/tests/common/general_maths/test_arithmetic_conversions.py b/tests/common/general_maths/test_arithmetic_conversions.py index 6a69519692f..fdd40f09706 100644 --- a/tests/common/general_maths/test_arithmetic_conversions.py +++ b/tests/common/general_maths/test_arithmetic_conversions.py @@ -56,6 +56,27 @@ def test_conversion_from_microns_to_centimetres(input, result): assert convert_microns_to_cm(input) == pytest.approx(result) +# circularity tests (expected success, all numbers here arbitrary) + + +@pytest.mark.parametrize("input", [1.0, 10.0, 100.0]) +def test_circular_cm_to_mm_and_back(input): + assert convert_cm_to_mm(convert_mm_to_cm(input)) == input + assert convert_mm_to_cm(convert_cm_to_mm(input)) == input + + +@pytest.mark.parametrize("input", [1.0, 10.0, 100.0]) +def test_circular_microns_to_mm_and_back(input): + assert convert_microns_to_mm(convert_mm_to_microns(input)) == input + assert convert_mm_to_microns(convert_microns_to_mm(input)) == input + + +@pytest.mark.parametrize("input", [1.0, 10.0, 100.0]) +def test_circular_percentage_to_factor(input): + assert convert_percentage_to_factor(convert_factor_to_percentage(input)) == input + assert convert_factor_to_percentage(convert_percentage_to_factor(input)) == input + + # The inauspicuous path @pytest.mark.parametrize( "bad_input", diff --git a/tests/common/general_maths/test_transmission_interconversion.py b/tests/common/general_maths/test_transmission_interconversion.py index db3fa312932..ad1289ab797 100644 --- a/tests/common/general_maths/test_transmission_interconversion.py +++ b/tests/common/general_maths/test_transmission_interconversion.py @@ -98,6 +98,35 @@ def test_attenuation_from_natural_log_of_transmission(ln_t, result): assert attenuation_from_natural_log_of_transmission(ln_t) == pytest.approx(result) +# circularity tests, all numbers here are arbitrary + + +@pytest.mark.parametrize("input", [1.0, 10.0, 100.0]) +def test_circular_attenuation_from_log_and_back(input): + assert ( + attenuation_from_natural_log_of_transmission( + natural_log_of_transmission_from_attenuation(input) + ) + == input + ) + assert ( + natural_log_of_transmission_from_attenuation( + attenuation_from_natural_log_of_transmission(input) + ) + == input + ) + + +@pytest.mark.parametrize("input", [1.0, 10.0, 100.0]) +def test_circular_attenuation_from_transmission_and_back(input): + assert attenuation_from_transmission( + transmission_from_attenutation(input) + ) == pytest.approx(input) + assert transmission_from_attenutation( + attenuation_from_transmission(input) + ) == pytest.approx(input) + + # inauspicious: @pytest.mark.parametrize("bad_input", ["a", [], None, math.sin, object(), False]) def test_natural_log_of_transmission_from_attenuation_raises_error(bad_input): From 1be9005dada95c51afa02dd36496809f9bc1d2f1 Mon Sep 17 00:00:00 2001 From: Matthew Carre Date: Wed, 20 May 2026 09:21:42 +0100 Subject: [PATCH 2/8] initial commit - need to check the PV values but can't seem to ATM --- src/dodal/beamlines/i19_optics.py | 6 +++++ .../beamlines/i19/access_controlled/dcm.py | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/dodal/devices/beamlines/i19/access_controlled/dcm.py diff --git a/src/dodal/beamlines/i19_optics.py b/src/dodal/beamlines/i19_optics.py index 4ff840b82bf..ef7e590754f 100644 --- a/src/dodal/beamlines/i19_optics.py +++ b/src/dodal/beamlines/i19_optics.py @@ -6,6 +6,7 @@ from dodal.device_manager import DeviceManager from dodal.devices.attenuator.filter import FilterWheel from dodal.devices.attenuator.filter_selections import I19FilterOneSelections +from dodal.devices.beamlines.i19.access_controlled.dcm import DCM from dodal.devices.beamlines.i19.access_controlled.hutch_access import ( ACCESS_DEVICE_NAME, HutchAccessControl, @@ -24,6 +25,11 @@ devices = DeviceManager() +@devices.factory() +def dcm() -> DCM: + return DCM(f"{PREFIX.beamline_prefix}-MO-DCM-01:") + + @devices.factory() def access_control() -> HutchAccessControl: """Device factory for access control device. diff --git a/src/dodal/devices/beamlines/i19/access_controlled/dcm.py b/src/dodal/devices/beamlines/i19/access_controlled/dcm.py new file mode 100644 index 00000000000..07979345b78 --- /dev/null +++ b/src/dodal/devices/beamlines/i19/access_controlled/dcm.py @@ -0,0 +1,27 @@ +import numpy as np +from ophyd_async.epics.core import epics_signal_r +from ophyd_async.epics.motor import Motor + +from dodal.common.crystal_metadata import ( + CrystalMetadata, +) +from dodal.devices.common_dcm import ( + DoubleCrystalMonochromatorWithDSpacing, + PitchAndRollCrystal, + StationaryCrystal, +) + + +class DCM( + DoubleCrystalMonochromatorWithDSpacing[PitchAndRollCrystal, StationaryCrystal] +): + def __init__( + self, + prefix: str, + name: str = "", + crystal_metadata: CrystalMetadata | None = None, + ) -> None: + with self.add_children_as_readables(): + self.energy_in_eV= epics_signal_r(float,prefix + ) + self.wavelength_in_a= epics_signal_r(float,prefix +) + From 044f38ed0ab33fad43a722750aa690ca03d65f34 Mon Sep 17 00:00:00 2001 From: Matthew Carre Date: Wed, 20 May 2026 09:26:28 +0100 Subject: [PATCH 3/8] added DCM to i19-1/-2 which i think is the right approach --- src/dodal/beamlines/i19_1.py | 7 +++++++ src/dodal/beamlines/i19_2.py | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/dodal/beamlines/i19_1.py b/src/dodal/beamlines/i19_1.py index b58c6dacede..03f5d28baf5 100644 --- a/src/dodal/beamlines/i19_1.py +++ b/src/dodal/beamlines/i19_1.py @@ -13,6 +13,8 @@ AttenuatorMotorSquad, ) from dodal.devices.beamlines.i19.access_controlled.blueapi_device import HutchState + +# from dodal.devices.beamlines.i19.access_controlled.dcm import DCM from dodal.devices.beamlines.i19.access_controlled.piezo_control import ( AccessControlledPiezoActuator, FocusingMirrorName, @@ -67,6 +69,11 @@ def config_client() -> ConfigClient: return client +# @devices.factory() +# def dcm() -> DCM: +# return DCM(f"{PREFIX.beamline_prefix}-MO-DCM-01:") ? + + @devices.factory() def attenuator_motor_squad() -> AttenuatorMotorSquad: return AttenuatorMotorSquad( diff --git a/src/dodal/beamlines/i19_2.py b/src/dodal/beamlines/i19_2.py index d341c764e8a..1bedaf8ff6e 100644 --- a/src/dodal/beamlines/i19_2.py +++ b/src/dodal/beamlines/i19_2.py @@ -14,6 +14,8 @@ AttenuatorMotorSquad, ) from dodal.devices.beamlines.i19.access_controlled.blueapi_device import HutchState + +# from dodal.devices.beamlines.i19.access_controlled.dcm import DCM from dodal.devices.beamlines.i19.access_controlled.piezo_control import ( AccessControlledPiezoActuator, FocusingMirrorName, @@ -63,6 +65,11 @@ def path_provider() -> PathProvider: ) +# @devices.factory() +# def dcm() -> DCM: +# return DCM(f"{PREFIX.beamline_prefix}-MO-DCM-01:") ? + + @devices.factory() def attenuator_motor_squad() -> AttenuatorMotorSquad: return AttenuatorMotorSquad( From 889c389ee0a2e48b67ce811333ab16e22210889b Mon Sep 17 00:00:00 2001 From: Matthew Carre Date: Wed, 20 May 2026 09:43:48 +0100 Subject: [PATCH 4/8] fixes it? Adds factories that are just standard readables in _1,_2, adds a factory of the actual device to _optics, makes dcm.py a standard readable with energy and wavelength --- src/dodal/beamlines/i19_1.py | 9 ++++---- src/dodal/beamlines/i19_2.py | 9 ++++---- src/dodal/beamlines/i19_optics.py | 12 +++++++--- .../beamlines/i19/access_controlled/dcm.py | 22 ++++--------------- 4 files changed, 21 insertions(+), 31 deletions(-) diff --git a/src/dodal/beamlines/i19_1.py b/src/dodal/beamlines/i19_1.py index 03f5d28baf5..196289ee923 100644 --- a/src/dodal/beamlines/i19_1.py +++ b/src/dodal/beamlines/i19_1.py @@ -13,8 +13,7 @@ AttenuatorMotorSquad, ) from dodal.devices.beamlines.i19.access_controlled.blueapi_device import HutchState - -# from dodal.devices.beamlines.i19.access_controlled.dcm import DCM +from dodal.devices.beamlines.i19.access_controlled.dcm import DCM from dodal.devices.beamlines.i19.access_controlled.piezo_control import ( AccessControlledPiezoActuator, FocusingMirrorName, @@ -69,9 +68,9 @@ def config_client() -> ConfigClient: return client -# @devices.factory() -# def dcm() -> DCM: -# return DCM(f"{PREFIX.beamline_prefix}-MO-DCM-01:") ? +@devices.factory() +def dcm() -> DCM: + return DCM(f"{PREFIX.beamline_prefix}-MO-DCM-01:") @devices.factory() diff --git a/src/dodal/beamlines/i19_2.py b/src/dodal/beamlines/i19_2.py index 1bedaf8ff6e..cd8b89c506c 100644 --- a/src/dodal/beamlines/i19_2.py +++ b/src/dodal/beamlines/i19_2.py @@ -14,8 +14,7 @@ AttenuatorMotorSquad, ) from dodal.devices.beamlines.i19.access_controlled.blueapi_device import HutchState - -# from dodal.devices.beamlines.i19.access_controlled.dcm import DCM +from dodal.devices.beamlines.i19.access_controlled.dcm import DCM from dodal.devices.beamlines.i19.access_controlled.piezo_control import ( AccessControlledPiezoActuator, FocusingMirrorName, @@ -65,9 +64,9 @@ def path_provider() -> PathProvider: ) -# @devices.factory() -# def dcm() -> DCM: -# return DCM(f"{PREFIX.beamline_prefix}-MO-DCM-01:") ? +@devices.factory() +def dcm() -> DCM: + return DCM(f"{PREFIX.beamline_prefix}-MO-DCM-01:") @devices.factory() diff --git a/src/dodal/beamlines/i19_optics.py b/src/dodal/beamlines/i19_optics.py index ef7e590754f..ce628c74830 100644 --- a/src/dodal/beamlines/i19_optics.py +++ b/src/dodal/beamlines/i19_optics.py @@ -6,11 +6,15 @@ from dodal.device_manager import DeviceManager from dodal.devices.attenuator.filter import FilterWheel from dodal.devices.attenuator.filter_selections import I19FilterOneSelections -from dodal.devices.beamlines.i19.access_controlled.dcm import DCM from dodal.devices.beamlines.i19.access_controlled.hutch_access import ( ACCESS_DEVICE_NAME, HutchAccessControl, ) +from dodal.devices.common_dcm import ( + DoubleCrystalMonochromatorWithDSpacing, + PitchAndRollCrystal, + StationaryCrystal, +) from dodal.devices.focusing_mirror import FocusingMirrorWithPiezo from dodal.devices.hutch_shutter import InterlockedHutchShutter from dodal.devices.interlocks import PSSInterlock @@ -26,8 +30,10 @@ @devices.factory() -def dcm() -> DCM: - return DCM(f"{PREFIX.beamline_prefix}-MO-DCM-01:") +def dcm() -> DoubleCrystalMonochromatorWithDSpacing: + return DoubleCrystalMonochromatorWithDSpacing( + f"{PREFIX.beamline_prefix}-MO-DCM-01:", PitchAndRollCrystal, StationaryCrystal + ) @devices.factory() diff --git a/src/dodal/devices/beamlines/i19/access_controlled/dcm.py b/src/dodal/devices/beamlines/i19/access_controlled/dcm.py index 07979345b78..539b57b2450 100644 --- a/src/dodal/devices/beamlines/i19/access_controlled/dcm.py +++ b/src/dodal/devices/beamlines/i19/access_controlled/dcm.py @@ -1,27 +1,13 @@ -import numpy as np +from ophyd_async.core import StandardReadable from ophyd_async.epics.core import epics_signal_r -from ophyd_async.epics.motor import Motor -from dodal.common.crystal_metadata import ( - CrystalMetadata, -) -from dodal.devices.common_dcm import ( - DoubleCrystalMonochromatorWithDSpacing, - PitchAndRollCrystal, - StationaryCrystal, -) - -class DCM( - DoubleCrystalMonochromatorWithDSpacing[PitchAndRollCrystal, StationaryCrystal] -): +class DCM(StandardReadable): def __init__( self, prefix: str, name: str = "", - crystal_metadata: CrystalMetadata | None = None, ) -> None: with self.add_children_as_readables(): - self.energy_in_eV= epics_signal_r(float,prefix + ) - self.wavelength_in_a= epics_signal_r(float,prefix +) - + self.energy_in_eV = epics_signal_r(float, f"{prefix}Energy") + self.wavelength_in_a = epics_signal_r(float, f"{prefix}Wavelength") From 2be5bd0b459fa20adb03e0d891cedf3d09df1376 Mon Sep 17 00:00:00 2001 From: Matthew Carre Date: Wed, 20 May 2026 09:45:30 +0100 Subject: [PATCH 5/8] removes extra bit accidentally added before --- .../test_arithmetic_conversions.py | 21 -------------- .../test_transmission_interconversion.py | 29 ------------------- 2 files changed, 50 deletions(-) diff --git a/tests/common/general_maths/test_arithmetic_conversions.py b/tests/common/general_maths/test_arithmetic_conversions.py index fdd40f09706..6a69519692f 100644 --- a/tests/common/general_maths/test_arithmetic_conversions.py +++ b/tests/common/general_maths/test_arithmetic_conversions.py @@ -56,27 +56,6 @@ def test_conversion_from_microns_to_centimetres(input, result): assert convert_microns_to_cm(input) == pytest.approx(result) -# circularity tests (expected success, all numbers here arbitrary) - - -@pytest.mark.parametrize("input", [1.0, 10.0, 100.0]) -def test_circular_cm_to_mm_and_back(input): - assert convert_cm_to_mm(convert_mm_to_cm(input)) == input - assert convert_mm_to_cm(convert_cm_to_mm(input)) == input - - -@pytest.mark.parametrize("input", [1.0, 10.0, 100.0]) -def test_circular_microns_to_mm_and_back(input): - assert convert_microns_to_mm(convert_mm_to_microns(input)) == input - assert convert_mm_to_microns(convert_microns_to_mm(input)) == input - - -@pytest.mark.parametrize("input", [1.0, 10.0, 100.0]) -def test_circular_percentage_to_factor(input): - assert convert_percentage_to_factor(convert_factor_to_percentage(input)) == input - assert convert_factor_to_percentage(convert_percentage_to_factor(input)) == input - - # The inauspicuous path @pytest.mark.parametrize( "bad_input", diff --git a/tests/common/general_maths/test_transmission_interconversion.py b/tests/common/general_maths/test_transmission_interconversion.py index ad1289ab797..db3fa312932 100644 --- a/tests/common/general_maths/test_transmission_interconversion.py +++ b/tests/common/general_maths/test_transmission_interconversion.py @@ -98,35 +98,6 @@ def test_attenuation_from_natural_log_of_transmission(ln_t, result): assert attenuation_from_natural_log_of_transmission(ln_t) == pytest.approx(result) -# circularity tests, all numbers here are arbitrary - - -@pytest.mark.parametrize("input", [1.0, 10.0, 100.0]) -def test_circular_attenuation_from_log_and_back(input): - assert ( - attenuation_from_natural_log_of_transmission( - natural_log_of_transmission_from_attenuation(input) - ) - == input - ) - assert ( - natural_log_of_transmission_from_attenuation( - attenuation_from_natural_log_of_transmission(input) - ) - == input - ) - - -@pytest.mark.parametrize("input", [1.0, 10.0, 100.0]) -def test_circular_attenuation_from_transmission_and_back(input): - assert attenuation_from_transmission( - transmission_from_attenutation(input) - ) == pytest.approx(input) - assert transmission_from_attenutation( - attenuation_from_transmission(input) - ) == pytest.approx(input) - - # inauspicious: @pytest.mark.parametrize("bad_input", ["a", [], None, math.sin, object(), False]) def test_natural_log_of_transmission_from_attenuation_raises_error(bad_input): From a6e8f2a04a1c74a28840fd3680e97370c7d1649f Mon Sep 17 00:00:00 2001 From: Matthew Carre Date: Wed, 20 May 2026 09:53:09 +0100 Subject: [PATCH 6/8] connects devices --- src/dodal/beamlines/i19_1.py | 2 +- src/dodal/beamlines/i19_2.py | 2 +- src/dodal/beamlines/i19_optics.py | 4 +++- src/dodal/devices/beamlines/i19/access_controlled/dcm.py | 2 ++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/dodal/beamlines/i19_1.py b/src/dodal/beamlines/i19_1.py index 196289ee923..c751e26f6fd 100644 --- a/src/dodal/beamlines/i19_1.py +++ b/src/dodal/beamlines/i19_1.py @@ -70,7 +70,7 @@ def config_client() -> ConfigClient: @devices.factory() def dcm() -> DCM: - return DCM(f"{PREFIX.beamline_prefix}-MO-DCM-01:") + return DCM(prefix=f"{PREFIX.beamline_prefix}-MO-DCM-01:") @devices.factory() diff --git a/src/dodal/beamlines/i19_2.py b/src/dodal/beamlines/i19_2.py index cd8b89c506c..250b5406289 100644 --- a/src/dodal/beamlines/i19_2.py +++ b/src/dodal/beamlines/i19_2.py @@ -66,7 +66,7 @@ def path_provider() -> PathProvider: @devices.factory() def dcm() -> DCM: - return DCM(f"{PREFIX.beamline_prefix}-MO-DCM-01:") + return DCM(prefix=f"{PREFIX.beamline_prefix}-MO-DCM-01:") @devices.factory() diff --git a/src/dodal/beamlines/i19_optics.py b/src/dodal/beamlines/i19_optics.py index ce628c74830..48125d5746a 100644 --- a/src/dodal/beamlines/i19_optics.py +++ b/src/dodal/beamlines/i19_optics.py @@ -32,7 +32,9 @@ @devices.factory() def dcm() -> DoubleCrystalMonochromatorWithDSpacing: return DoubleCrystalMonochromatorWithDSpacing( - f"{PREFIX.beamline_prefix}-MO-DCM-01:", PitchAndRollCrystal, StationaryCrystal + prefix=f"{PREFIX.beamline_prefix}-MO-DCM-01:", + xtal_1=PitchAndRollCrystal, + xtal_2=StationaryCrystal, ) diff --git a/src/dodal/devices/beamlines/i19/access_controlled/dcm.py b/src/dodal/devices/beamlines/i19/access_controlled/dcm.py index 539b57b2450..66aa5444962 100644 --- a/src/dodal/devices/beamlines/i19/access_controlled/dcm.py +++ b/src/dodal/devices/beamlines/i19/access_controlled/dcm.py @@ -11,3 +11,5 @@ def __init__( with self.add_children_as_readables(): self.energy_in_eV = epics_signal_r(float, f"{prefix}Energy") self.wavelength_in_a = epics_signal_r(float, f"{prefix}Wavelength") + + super().__init__(prefix) From da57d588b7a7a4573d56e9cd2d1115e7127a5da3 Mon Sep 17 00:00:00 2001 From: Matthew Carre Date: Thu, 21 May 2026 12:02:36 +0100 Subject: [PATCH 7/8] alters code based on comments --- src/dodal/beamlines/i19_1.py | 6 +++--- src/dodal/beamlines/i19_2.py | 6 +++--- src/dodal/beamlines/i19_optics.py | 4 ++-- .../{dcm.py => read_only_dcm.py} | 6 +++--- .../access_controlled/test_read_only_dcm.py | 18 ++++++++++++++++++ 5 files changed, 29 insertions(+), 11 deletions(-) rename src/dodal/devices/beamlines/i19/access_controlled/{dcm.py => read_only_dcm.py} (86%) create mode 100644 tests/devices/beamlines/i19/access_controlled/test_read_only_dcm.py diff --git a/src/dodal/beamlines/i19_1.py b/src/dodal/beamlines/i19_1.py index c751e26f6fd..33c6f917a24 100644 --- a/src/dodal/beamlines/i19_1.py +++ b/src/dodal/beamlines/i19_1.py @@ -13,11 +13,11 @@ AttenuatorMotorSquad, ) from dodal.devices.beamlines.i19.access_controlled.blueapi_device import HutchState -from dodal.devices.beamlines.i19.access_controlled.dcm import DCM from dodal.devices.beamlines.i19.access_controlled.piezo_control import ( AccessControlledPiezoActuator, FocusingMirrorName, ) +from dodal.devices.beamlines.i19.access_controlled.read_only_dcm import ReadOnlyDCM from dodal.devices.beamlines.i19.access_controlled.shutter import ( AccessControlledShutter, ) @@ -69,8 +69,8 @@ def config_client() -> ConfigClient: @devices.factory() -def dcm() -> DCM: - return DCM(prefix=f"{PREFIX.beamline_prefix}-MO-DCM-01:") +def read_only_dcm() -> ReadOnlyDCM: + return ReadOnlyDCM(prefix=f"{PREFIX.beamline_prefix}-MO-DCM-01:") @devices.factory() diff --git a/src/dodal/beamlines/i19_2.py b/src/dodal/beamlines/i19_2.py index 250b5406289..5e91a9390a2 100644 --- a/src/dodal/beamlines/i19_2.py +++ b/src/dodal/beamlines/i19_2.py @@ -14,11 +14,11 @@ AttenuatorMotorSquad, ) from dodal.devices.beamlines.i19.access_controlled.blueapi_device import HutchState -from dodal.devices.beamlines.i19.access_controlled.dcm import DCM from dodal.devices.beamlines.i19.access_controlled.piezo_control import ( AccessControlledPiezoActuator, FocusingMirrorName, ) +from dodal.devices.beamlines.i19.access_controlled.read_only_dcm import ReadOnlyDCM from dodal.devices.beamlines.i19.access_controlled.shutter import ( AccessControlledShutter, ) @@ -65,8 +65,8 @@ def path_provider() -> PathProvider: @devices.factory() -def dcm() -> DCM: - return DCM(prefix=f"{PREFIX.beamline_prefix}-MO-DCM-01:") +def read_only_dcm() -> ReadOnlyDCM: + return ReadOnlyDCM(prefix=f"{PREFIX.beamline_prefix}-MO-DCM-01:") @devices.factory() diff --git a/src/dodal/beamlines/i19_optics.py b/src/dodal/beamlines/i19_optics.py index 48125d5746a..16f210a73fe 100644 --- a/src/dodal/beamlines/i19_optics.py +++ b/src/dodal/beamlines/i19_optics.py @@ -33,8 +33,8 @@ def dcm() -> DoubleCrystalMonochromatorWithDSpacing: return DoubleCrystalMonochromatorWithDSpacing( prefix=f"{PREFIX.beamline_prefix}-MO-DCM-01:", - xtal_1=PitchAndRollCrystal, - xtal_2=StationaryCrystal, + xtal_1=StationaryCrystal, + xtal_2=PitchAndRollCrystal, ) diff --git a/src/dodal/devices/beamlines/i19/access_controlled/dcm.py b/src/dodal/devices/beamlines/i19/access_controlled/read_only_dcm.py similarity index 86% rename from src/dodal/devices/beamlines/i19/access_controlled/dcm.py rename to src/dodal/devices/beamlines/i19/access_controlled/read_only_dcm.py index 66aa5444962..36e66f7586e 100644 --- a/src/dodal/devices/beamlines/i19/access_controlled/dcm.py +++ b/src/dodal/devices/beamlines/i19/access_controlled/read_only_dcm.py @@ -2,14 +2,14 @@ from ophyd_async.epics.core import epics_signal_r -class DCM(StandardReadable): +class ReadOnlyDCM(StandardReadable): def __init__( self, prefix: str, name: str = "", ) -> None: with self.add_children_as_readables(): - self.energy_in_eV = epics_signal_r(float, f"{prefix}Energy") - self.wavelength_in_a = epics_signal_r(float, f"{prefix}Wavelength") + self.energy_in_eV = epics_signal_r(float, f"{prefix}ENERGY") + self.wavelength_in_a = epics_signal_r(float, f"{prefix}WAVELENGTH") super().__init__(prefix) diff --git a/tests/devices/beamlines/i19/access_controlled/test_read_only_dcm.py b/tests/devices/beamlines/i19/access_controlled/test_read_only_dcm.py new file mode 100644 index 00000000000..aababdd69fe --- /dev/null +++ b/tests/devices/beamlines/i19/access_controlled/test_read_only_dcm.py @@ -0,0 +1,18 @@ +import pytest +from ophyd_async.core import set_mock_value + +from dodal.devices.beamlines.i19.access_controlled.read_only_dcm import ReadOnlyDCM + + +@pytest.fixture +async def mock_dcm() -> ReadOnlyDCM: + mock_dcm = ReadOnlyDCM(prefix="FOO-MO") + await mock_dcm.connect(mock=True) + set_mock_value(mock_dcm.energy_in_eV, 1) + set_mock_value(mock_dcm.wavelength_in_a, 100) + return mock_dcm + + +async def test_reading(mock_dcm: ReadOnlyDCM): + assert await mock_dcm.energy_in_eV.get_value() == 1 + assert await mock_dcm.wavelength_in_a.get_value() == 100 From 55c53b77d2179312fbe5144f5bf83592c780275b Mon Sep 17 00:00:00 2001 From: Matthew Carre Date: Thu, 21 May 2026 16:48:52 +0100 Subject: [PATCH 8/8] updates test to use assert_reading --- .../i19/access_controlled/test_read_only_dcm.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/devices/beamlines/i19/access_controlled/test_read_only_dcm.py b/tests/devices/beamlines/i19/access_controlled/test_read_only_dcm.py index aababdd69fe..9179ac170b5 100644 --- a/tests/devices/beamlines/i19/access_controlled/test_read_only_dcm.py +++ b/tests/devices/beamlines/i19/access_controlled/test_read_only_dcm.py @@ -1,5 +1,7 @@ import pytest +from bluesky.run_engine import RunEngine from ophyd_async.core import set_mock_value +from ophyd_async.testing import assert_reading, partial_reading from dodal.devices.beamlines.i19.access_controlled.read_only_dcm import ReadOnlyDCM @@ -13,6 +15,13 @@ async def mock_dcm() -> ReadOnlyDCM: return mock_dcm -async def test_reading(mock_dcm: ReadOnlyDCM): - assert await mock_dcm.energy_in_eV.get_value() == 1 - assert await mock_dcm.wavelength_in_a.get_value() == 100 +async def test_reading(mock_dcm: ReadOnlyDCM, run_engine: RunEngine): + prefix = "FOO-MO" + await assert_reading( + mock_dcm, + { + f"{prefix}-energy_in_eV": partial_reading(1.0), + f"{prefix}-wavelength_in_a": partial_reading(100.0), + }, + False, + )