From 4ba4f12c51c6186348ba6a48c3f83d8d38b9fba8 Mon Sep 17 00:00:00 2001 From: Oli Wenman Date: Fri, 17 Jul 2026 10:25:17 +0000 Subject: [PATCH 1/7] Update to ophyd-async v0.20 --- pyproject.toml | 4 ++-- .../electron_analyser/mbs/mbs_detector.py | 2 -- .../electron_analyser/specs/specs_detector.py | 1 - .../vgscienta/vgscienta_detector.py | 1 - .../insertion_device/apple2_controller.py | 18 +++++++++++++----- src/dodal/testing/fixtures/devices/apple2.py | 9 +++++---- .../base/test_base_detector.py | 2 ++ .../insertion_device/test_apple2_undulator.py | 11 ++++++----- .../test_apple_knot_undulator.py | 7 +++---- uv.lock | 10 +++++----- 10 files changed, 36 insertions(+), 29 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0fed946874c..7b52b077fa3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ description = "Ophyd devices and other utils that could be used across DLS beaml dependencies = [ "click", "ophyd", - "ophyd-async[ca,pva]>=0.19.3", + "ophyd-async[ca,pva]>=0.20.0", "bluesky>=1.14.5", "pyepics", "pillow", @@ -51,7 +51,7 @@ dev = [ # Commented out due to dependency version conflict with pydantic 1.x # "copier", "myst-parser", - "ophyd_async[sim]>=v0.19.3", + "ophyd_async[sim]>=0.20.0", "pre-commit", "psutil", "pydata-sphinx-theme>=0.12", diff --git a/src/dodal/devices/electron_analyser/mbs/mbs_detector.py b/src/dodal/devices/electron_analyser/mbs/mbs_detector.py index 98b8ca5098d..7030b97ae54 100644 --- a/src/dodal/devices/electron_analyser/mbs/mbs_detector.py +++ b/src/dodal/devices/electron_analyser/mbs/mbs_detector.py @@ -47,8 +47,6 @@ def __init__( driver.slices, driver.iterations, driver.total_steps, - driver.acquire_time, - driver.acquire_period, driver.total_time, driver.energy_axis, driver.angle_axis, diff --git a/src/dodal/devices/electron_analyser/specs/specs_detector.py b/src/dodal/devices/electron_analyser/specs/specs_detector.py index 78117604264..55d9966e1b2 100644 --- a/src/dodal/devices/electron_analyser/specs/specs_detector.py +++ b/src/dodal/devices/electron_analyser/specs/specs_detector.py @@ -45,7 +45,6 @@ def __init__( driver.energy_step, driver.pass_energy, driver.slices, - driver.acquire_time, driver.iterations, driver.total_steps, driver.total_time, diff --git a/src/dodal/devices/electron_analyser/vgscienta/vgscienta_detector.py b/src/dodal/devices/electron_analyser/vgscienta/vgscienta_detector.py index a214939a4fc..cf9b10b1a8f 100644 --- a/src/dodal/devices/electron_analyser/vgscienta/vgscienta_detector.py +++ b/src/dodal/devices/electron_analyser/vgscienta/vgscienta_detector.py @@ -53,7 +53,6 @@ def __init__( driver.slices, driver.iterations, driver.total_steps, - driver.acquire_time, driver.total_time, driver.energy_axis, driver.angle_axis, diff --git a/src/dodal/devices/insertion_device/apple2_controller.py b/src/dodal/devices/insertion_device/apple2_controller.py index 8a14744bcc1..35d6b4ae7be 100644 --- a/src/dodal/devices/insertion_device/apple2_controller.py +++ b/src/dodal/devices/insertion_device/apple2_controller.py @@ -128,23 +128,31 @@ def __init__( phase = self.apple2().phase() # check if undulator phase is unlocked. if isinstance(phase, UndulatorPhaseAxes): - top_inner = phase.top_inner.user_readback - btm_outer = phase.btm_outer.user_readback + self.top_inner = phase.top_inner.user_readback + self.btm_outer = phase.btm_outer.user_readback else: # If locked phase axes make the locked phase 0. - top_inner = btm_outer = soft_signal_rw(float, initial_value=0.0) + self.top_inner = soft_signal_rw(float, initial_value=0.0) + self.btm_outer = soft_signal_rw(float, initial_value=0.0) with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL): # Hardware backed read/write for polarisation. + print(self.polarisation_setpoint.name) + print(phase.top_outer.user_readback.name) + print(self.top_inner.name) + print(phase.btm_inner.user_readback.name) + print(self.btm_outer.name) + print(self.apple2().gap().user_readback.name) + self.polarisation = derived_signal_rw( raw_to_derived=self._read_pol, set_derived=self._set_pol, pol=self.polarisation_setpoint, top_outer=phase.top_outer.user_readback, - top_inner=top_inner, + top_inner=self.top_inner, btm_inner=phase.btm_inner.user_readback, - btm_outer=btm_outer, + btm_outer=self.btm_outer, gap=self.apple2().gap().user_readback, ) with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL): diff --git a/src/dodal/testing/fixtures/devices/apple2.py b/src/dodal/testing/fixtures/devices/apple2.py index a8d06cedc14..ff7bc0d75de 100644 --- a/src/dodal/testing/fixtures/devices/apple2.py +++ b/src/dodal/testing/fixtures/devices/apple2.py @@ -109,8 +109,9 @@ async def mock_locked_apple2( mock_id_gap: UndulatorGap, mock_locked_phase_axes: UndulatorLockedPhaseAxes, ) -> Apple2[UndulatorLockedPhaseAxes]: - mock_locked_apple2 = Apple2[UndulatorLockedPhaseAxes]( - id_gap=mock_id_gap, - id_phase=mock_locked_phase_axes, - ) + with init_devices(mock=True): + mock_locked_apple2 = Apple2[UndulatorLockedPhaseAxes]( + id_gap=mock_id_gap, + id_phase=mock_locked_phase_axes, + ) return mock_locked_apple2 diff --git a/tests/devices/electron_analyser/base/test_base_detector.py b/tests/devices/electron_analyser/base/test_base_detector.py index fc4ff5e78b8..f83aef1084e 100644 --- a/tests/devices/electron_analyser/base/test_base_detector.py +++ b/tests/devices/electron_analyser/base/test_base_detector.py @@ -16,6 +16,7 @@ from tests.devices.electron_analyser.helper_util import ( generate_fixture_regions_pair, load_b07_specs_test_seq, + load_i05_mbs_test_xml_seq, load_i09_vgscienta_test_seq, ) @@ -81,6 +82,7 @@ def test_analyser_detector_set_called_region_logic_setup_with_region( [ pytest.param("ew4000", load_i09_vgscienta_test_seq()), pytest.param("b07b_specs150", load_b07_specs_test_seq()), + pytest.param("i05_mbs_analyser", load_i05_mbs_test_xml_seq()), ], indirect=["sim_detector"], ) diff --git a/tests/devices/insertion_device/test_apple2_undulator.py b/tests/devices/insertion_device/test_apple2_undulator.py index 80820441057..66c537be959 100644 --- a/tests/devices/insertion_device/test_apple2_undulator.py +++ b/tests/devices/insertion_device/test_apple2_undulator.py @@ -422,11 +422,12 @@ async def mock_locked_controller( configured_gap: float, configured_phase: float, ) -> DummyLockedApple2Controller: - mock_locked_controller = DummyLockedApple2Controller( - apple2=mock_locked_apple2, - gap_energy_motor_converter=lambda value, pol: configured_gap, - phase_energy_motor_converter=lambda value, pol: configured_phase, - ) + with init_devices(mock=True): + mock_locked_controller = DummyLockedApple2Controller( + apple2=mock_locked_apple2, + gap_energy_motor_converter=lambda value, pol: configured_gap, + phase_energy_motor_converter=lambda value, pol: configured_phase, + ) return mock_locked_controller diff --git a/tests/devices/insertion_device/test_apple_knot_undulator.py b/tests/devices/insertion_device/test_apple_knot_undulator.py index 007eee81ef0..cbc67906e76 100644 --- a/tests/devices/insertion_device/test_apple_knot_undulator.py +++ b/tests/devices/insertion_device/test_apple_knot_undulator.py @@ -1,8 +1,5 @@ import pytest -from ophyd_async.core import ( - init_devices, - set_mock_value, -) +from ophyd_async.core import init_devices, set_mock_value from dodal.devices.beamlines.i05_shared import ( energy_to_gap_converter, @@ -59,6 +56,8 @@ async def test_id_set_energy_const_pol( initial_phase_top_outer: float, expected_pol: Pol, ): + await mock_locked_apple2.connect(mock=True) + await mock_apple_knot_i05_controller.connect(mock=True) set_mock_value( mock_locked_apple2.phase().top_outer.user_readback, initial_phase_top_outer ) diff --git a/uv.lock b/uv.lock index 54cdb03efb8..0a7bc2865bb 100644 --- a/uv.lock +++ b/uv.lock @@ -824,7 +824,7 @@ requires-dist = [ { name = "numpy" }, { name = "opencv-python-headless" }, { name = "ophyd" }, - { name = "ophyd-async", extras = ["ca", "pva"], specifier = ">=0.19.3" }, + { name = "ophyd-async", extras = ["ca", "pva"], specifier = ">=0.20.0" }, { name = "pillow" }, { name = "pydantic", specifier = ">=2.0" }, { name = "pyepics" }, @@ -843,7 +843,7 @@ dev = [ { name = "ispyb" }, { name = "mypy" }, { name = "myst-parser" }, - { name = "ophyd-async", extras = ["sim"], specifier = ">=0.19.3" }, + { name = "ophyd-async", extras = ["sim"], specifier = ">=0.20.0" }, { name = "pre-commit" }, { name = "psutil" }, { name = "pydata-sphinx-theme", specifier = ">=0.12" }, @@ -2256,7 +2256,7 @@ wheels = [ [[package]] name = "ophyd-async" -version = "0.19.3" +version = "0.20.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "bluesky" }, @@ -2270,9 +2270,9 @@ dependencies = [ { name = "stamina" }, { name = "velocity-profile" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1d/20/1241051befc05c21c958206611f9ed058d81b8e8f92e3acf9f9d6936f2fc/ophyd_async-0.19.3.tar.gz", hash = "sha256:9cb76a8a08794a26c619a0420012e7248542501d036f16f3314221869e989984", size = 595679, upload-time = "2026-06-29T15:21:37.859Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/ee/0bc3ffcb7586c95be6d2d053ad7e1fb0f383e5e068bad9943bbc5bed885e/ophyd_async-0.20.0.tar.gz", hash = "sha256:2138489bb2543a08ca674cc86de243245209626886e502694c84ecdeeccf97ac", size = 652415, upload-time = "2026-07-17T08:50:46.01Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/2c/76ac5e485f6405fc0f1dbbd1c98b7c1137d4aa5c1e49ea9e69bbd8d22ae1/ophyd_async-0.19.3-py3-none-any.whl", hash = "sha256:ef6201edb52e5a54008d57cad1808626aa6493ba4a8fd015e08d14bcffbb697a", size = 232796, upload-time = "2026-06-29T15:21:36.46Z" }, + { url = "https://files.pythonhosted.org/packages/83/05/1001c7b589430a45219348c8c32622305304be2173b74d202fd596a39c3e/ophyd_async-0.20.0-py3-none-any.whl", hash = "sha256:3459199794587131bd03ee65b75122f6656fd7500d5fbf91cc5ecfd14c889ca3", size = 258059, upload-time = "2026-07-17T08:50:44.244Z" }, ] [package.optional-dependencies] From 7ccbc2836facfeeade88b620311070e08835328f Mon Sep 17 00:00:00 2001 From: Oli Wenman Date: Fri, 17 Jul 2026 10:37:22 +0000 Subject: [PATCH 2/7] Update naming issue --- .../insertion_device/apple2_controller.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/dodal/devices/insertion_device/apple2_controller.py b/src/dodal/devices/insertion_device/apple2_controller.py index 35d6b4ae7be..47027c6ede0 100644 --- a/src/dodal/devices/insertion_device/apple2_controller.py +++ b/src/dodal/devices/insertion_device/apple2_controller.py @@ -128,31 +128,22 @@ def __init__( phase = self.apple2().phase() # check if undulator phase is unlocked. if isinstance(phase, UndulatorPhaseAxes): - self.top_inner = phase.top_inner.user_readback - self.btm_outer = phase.btm_outer.user_readback + top_inner = phase.top_inner.user_readback + btm_outer = phase.btm_outer.user_readback else: # If locked phase axes make the locked phase 0. - self.top_inner = soft_signal_rw(float, initial_value=0.0) - self.btm_outer = soft_signal_rw(float, initial_value=0.0) + top_inner = btm_outer = 0 with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL): # Hardware backed read/write for polarisation. - - print(self.polarisation_setpoint.name) - print(phase.top_outer.user_readback.name) - print(self.top_inner.name) - print(phase.btm_inner.user_readback.name) - print(self.btm_outer.name) - print(self.apple2().gap().user_readback.name) - self.polarisation = derived_signal_rw( raw_to_derived=self._read_pol, set_derived=self._set_pol, pol=self.polarisation_setpoint, top_outer=phase.top_outer.user_readback, - top_inner=self.top_inner, + top_inner=top_inner, btm_inner=phase.btm_inner.user_readback, - btm_outer=self.btm_outer, + btm_outer=btm_outer, gap=self.apple2().gap().user_readback, ) with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL): From 8f040e2b0999eaf8d596105ceff0b43c57fc98cc Mon Sep 17 00:00:00 2001 From: Oli Wenman Date: Fri, 17 Jul 2026 12:39:06 +0000 Subject: [PATCH 3/7] Update remaining tests that are not bluesky verb errors --- .../high_field_magnet/high_field_magnet.py | 1 - .../insertion_device/apple2_controller.py | 2 +- tests/devices/beamlines/b07_1/test_ccmc.py | 12 +++++++ .../i06_shared/test_i06_apple2controller.py | 25 +++++++++------ .../test_high_field_magnet.py | 16 ++-------- tests/devices/beamlines/i22/test_dcm.py | 32 +++++++++++++++++++ .../test_apple2_controller.py | 13 ++++---- tests/devices/test_undulator.py | 10 +++++- 8 files changed, 79 insertions(+), 32 deletions(-) diff --git a/src/dodal/devices/beamlines/i10_1/high_field_magnet/high_field_magnet.py b/src/dodal/devices/beamlines/i10_1/high_field_magnet/high_field_magnet.py index 7cd9b78fd75..742196cec17 100644 --- a/src/dodal/devices/beamlines/i10_1/high_field_magnet/high_field_magnet.py +++ b/src/dodal/devices/beamlines/i10_1/high_field_magnet/high_field_magnet.py @@ -107,7 +107,6 @@ def __init__( read_pv=prefix + "RBV:SETPOINTFIELD", write_pv=prefix + "SET:SETPOINTFIELD", ) - self._set_success = True self._fly_info: FlyMagInfo | None = None self._fly_status: WatchableAsyncStatus | None = None diff --git a/src/dodal/devices/insertion_device/apple2_controller.py b/src/dodal/devices/insertion_device/apple2_controller.py index 47027c6ede0..8a10d5259ea 100644 --- a/src/dodal/devices/insertion_device/apple2_controller.py +++ b/src/dodal/devices/insertion_device/apple2_controller.py @@ -132,7 +132,7 @@ def __init__( btm_outer = phase.btm_outer.user_readback else: # If locked phase axes make the locked phase 0. - top_inner = btm_outer = 0 + top_inner = btm_outer = 0.0 with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL): # Hardware backed read/write for polarisation. diff --git a/tests/devices/beamlines/b07_1/test_ccmc.py b/tests/devices/beamlines/b07_1/test_ccmc.py index 6ce7e586f8d..47191aff902 100644 --- a/tests/devices/beamlines/b07_1/test_ccmc.py +++ b/tests/devices/beamlines/b07_1/test_ccmc.py @@ -36,12 +36,24 @@ async def test_read_config_includes(mock_ccmc: ChannelCutMonochromator): f"{mock_ccmc.name}-_xyz-x-motor_egu": partial_reading(""), f"{mock_ccmc.name}-_xyz-x-offset": partial_reading(0.0), f"{mock_ccmc.name}-_xyz-x-velocity": partial_reading(1000.0), + f"{mock_ccmc.name}-_xyz-x-encoder_resolution": partial_reading(0), + f"{mock_ccmc.name}-_xyz-x-motor_resolution": partial_reading(0), + f"{mock_ccmc.name}-_xyz-x-steps_per_revolution": partial_reading(0), + f"{mock_ccmc.name}-_xyz-x-units_per_revolution": partial_reading(0), f"{mock_ccmc.name}-_xyz-y-motor_egu": partial_reading(""), f"{mock_ccmc.name}-_xyz-y-offset": partial_reading(0.0), f"{mock_ccmc.name}-_xyz-y-velocity": partial_reading(1000.0), + f"{mock_ccmc.name}-_xyz-y-encoder_resolution": partial_reading(0), + f"{mock_ccmc.name}-_xyz-y-motor_resolution": partial_reading(0), + f"{mock_ccmc.name}-_xyz-y-steps_per_revolution": partial_reading(0), + f"{mock_ccmc.name}-_xyz-y-units_per_revolution": partial_reading(0), f"{mock_ccmc.name}-_xyz-z-motor_egu": partial_reading(""), f"{mock_ccmc.name}-_xyz-z-offset": partial_reading(0.0), f"{mock_ccmc.name}-_xyz-z-velocity": partial_reading(1000.0), + f"{mock_ccmc.name}-_xyz-z-encoder_resolution": partial_reading(0), + f"{mock_ccmc.name}-_xyz-z-motor_resolution": partial_reading(0), + f"{mock_ccmc.name}-_xyz-z-steps_per_revolution": partial_reading(0), + f"{mock_ccmc.name}-_xyz-z-units_per_revolution": partial_reading(0), }, ) diff --git a/tests/devices/beamlines/i06_shared/test_i06_apple2controller.py b/tests/devices/beamlines/i06_shared/test_i06_apple2controller.py index c97e1044aa0..8a2aa1c72b9 100644 --- a/tests/devices/beamlines/i06_shared/test_i06_apple2controller.py +++ b/tests/devices/beamlines/i06_shared/test_i06_apple2controller.py @@ -1,6 +1,7 @@ from unittest.mock import AsyncMock, MagicMock import pytest +from ophyd_async.core import init_devices from dodal.devices.beamlines.i06_shared import I06Apple2Controller from dodal.devices.insertion_device import ( @@ -13,16 +14,19 @@ ) from dodal.devices.insertion_device.energy_motor_lookup import EnergyMotorLookup +# add mock_id_gap, mock_phase and mock_jaw_phase_axes to pytest. +pytest_plugins = ["dodal.testing.fixtures.devices.apple2"] + @pytest.fixture async def mock_apple2( mock_id_gap: UndulatorGap, mock_phase_axes: UndulatorPhaseAxes, ) -> Apple2[UndulatorPhaseAxes]: - mock_apple2 = Apple2[UndulatorPhaseAxes]( - id_gap=mock_id_gap, - id_phase=mock_phase_axes, - ) + with init_devices(mock=True): + mock_apple2 = Apple2[UndulatorPhaseAxes]( + id_gap=mock_id_gap, id_phase=mock_phase_axes + ) return mock_apple2 @@ -38,12 +42,13 @@ async def mock_i06_controller( mock_inverse_gap_energy_motor_lut.find_value_in_lookup_table = MagicMock( return_value=100 ) - mock_i06_controller = I06Apple2Controller( - apple2=mock_apple2, - gap_energy_motor_lut=mock_gap_energy_motor_lut, - phase_energy_motor_lut=mock_phase_energy_motor_lut, - inverse_gap_energy_motor_lut=mock_inverse_gap_energy_motor_lut, - ) + with init_devices(mock=True): + mock_i06_controller = I06Apple2Controller( + apple2=mock_apple2, + gap_energy_motor_lut=mock_gap_energy_motor_lut, + phase_energy_motor_lut=mock_phase_energy_motor_lut, + inverse_gap_energy_motor_lut=mock_inverse_gap_energy_motor_lut, + ) return mock_i06_controller diff --git a/tests/devices/beamlines/i10_1/high_field_magnet/test_high_field_magnet.py b/tests/devices/beamlines/i10_1/high_field_magnet/test_high_field_magnet.py index 86f8fb89c09..b9395cf4ac3 100644 --- a/tests/devices/beamlines/i10_1/high_field_magnet/test_high_field_magnet.py +++ b/tests/devices/beamlines/i10_1/high_field_magnet/test_high_field_magnet.py @@ -9,6 +9,7 @@ from ophyd_async.core import ( DEFAULT_TIMEOUT, AsyncStatus, + get_mock_put, init_devices, set_mock_value, ) @@ -36,11 +37,9 @@ async def test_locate(high_field_magnet: HighFieldMagnet): async def test_stop_success(high_field_magnet: HighFieldMagnet): - set_mock_value(high_field_magnet.user_readback, 7.5) - set_mock_value(high_field_magnet.user_readback, 1.5) + initial_val = await high_field_magnet.user_readback.get_value() await high_field_magnet.stop() - assert high_field_magnet._set_success is False - assert await high_field_magnet.user_setpoint.get_value() == 1.5 + get_mock_put(high_field_magnet.user_setpoint).assert_awaited_once_with(initial_val) async def test_set_raises_runtime_error_when_stopped( @@ -151,15 +150,6 @@ async def test_read(high_field_magnet: HighFieldMagnet): ) -async def test_tolerance_logic_stop_clears_set_success_and_restores_setpoint( - high_field_magnet: HighFieldMagnet, -): - set_mock_value(high_field_magnet.movable_logic.readback, 1.5) - await high_field_magnet.stop() - assert high_field_magnet._set_success is False - assert await high_field_magnet.movable_logic.setpoint.get_value() == 1.5 - - async def test_tolerance_logic_calculate_timeout_with_zero_speed( high_field_magnet: HighFieldMagnet, ): diff --git a/tests/devices/beamlines/i22/test_dcm.py b/tests/devices/beamlines/i22/test_dcm.py index b262b655d32..23eff96ea56 100644 --- a/tests/devices/beamlines/i22/test_dcm.py +++ b/tests/devices/beamlines/i22/test_dcm.py @@ -95,6 +95,10 @@ async def test_configuration(dcm: DCM): "dcm-bragg_in_degrees-motor_egu": partial_reading(""), "dcm-bragg_in_degrees-offset": partial_reading(0.0), "dcm-bragg_in_degrees-velocity": partial_reading(1000.0), + "dcm-bragg_in_degrees-encoder_resolution": partial_reading(0), + "dcm-bragg_in_degrees-motor_resolution": partial_reading(0), + "dcm-bragg_in_degrees-steps_per_revolution": partial_reading(0), + "dcm-bragg_in_degrees-units_per_revolution": partial_reading(0), "dcm-crystal_1_d_spacing": partial_reading(0.31356), "dcm-crystal_1_reflection": partial_reading([1, 1, 1]), "dcm-crystal_1_type": partial_reading("silicon"), @@ -106,23 +110,51 @@ async def test_configuration(dcm: DCM): "dcm-energy_in_keV-motor_egu": partial_reading(""), "dcm-energy_in_keV-offset": partial_reading(0.0), "dcm-energy_in_keV-velocity": partial_reading(1000.0), + "dcm-energy_in_keV-encoder_resolution": partial_reading(0), + "dcm-energy_in_keV-motor_resolution": partial_reading(0), + "dcm-energy_in_keV-steps_per_revolution": partial_reading(0), + "dcm-energy_in_keV-units_per_revolution": partial_reading(0), "dcm-offset_in_mm-motor_egu": partial_reading(""), "dcm-offset_in_mm-offset": partial_reading(0.0), "dcm-offset_in_mm-velocity": partial_reading(1000.0), + "dcm-offset_in_mm-encoder_resolution": partial_reading(0), + "dcm-offset_in_mm-motor_resolution": partial_reading(0), + "dcm-offset_in_mm-steps_per_revolution": partial_reading(0), + "dcm-offset_in_mm-units_per_revolution": partial_reading(0), "dcm-perp-motor_egu": partial_reading(""), "dcm-perp-offset": partial_reading(0.0), "dcm-perp-velocity": partial_reading(1000.0), + "dcm-perp-encoder_resolution": partial_reading(0), + "dcm-perp-motor_resolution": partial_reading(0), + "dcm-perp-steps_per_revolution": partial_reading(0), + "dcm-perp-units_per_revolution": partial_reading(0), "dcm-wavelength_in_a-motor_egu": partial_reading(""), "dcm-wavelength_in_a-offset": partial_reading(0.0), "dcm-wavelength_in_a-velocity": partial_reading(1000.0), + "dcm-wavelength_in_a-encoder_resolution": partial_reading(0), + "dcm-wavelength_in_a-motor_resolution": partial_reading(0), + "dcm-wavelength_in_a-steps_per_revolution": partial_reading(0), + "dcm-wavelength_in_a-units_per_revolution": partial_reading(0), "dcm-xtal_1-roll_in_mrad-motor_egu": partial_reading(""), "dcm-xtal_1-roll_in_mrad-offset": partial_reading(0.0), "dcm-xtal_1-roll_in_mrad-velocity": partial_reading(1000.0), + "dcm-xtal_1-roll_in_mrad-encoder_resolution": partial_reading(0), + "dcm-xtal_1-roll_in_mrad-motor_resolution": partial_reading(0), + "dcm-xtal_1-roll_in_mrad-steps_per_revolution": partial_reading(0), + "dcm-xtal_1-roll_in_mrad-units_per_revolution": partial_reading(0), "dcm-xtal_2-pitch_in_mrad-motor_egu": partial_reading(""), "dcm-xtal_2-pitch_in_mrad-offset": partial_reading(0.0), "dcm-xtal_2-pitch_in_mrad-velocity": partial_reading(1000.0), + "dcm-xtal_2-pitch_in_mrad-encoder_resolution": partial_reading(0), + "dcm-xtal_2-pitch_in_mrad-motor_resolution": partial_reading(0), + "dcm-xtal_2-pitch_in_mrad-steps_per_revolution": partial_reading(0), + "dcm-xtal_2-pitch_in_mrad-units_per_revolution": partial_reading(0), "dcm-xtal_2-roll_in_mrad-motor_egu": partial_reading(""), "dcm-xtal_2-roll_in_mrad-offset": partial_reading(0.0), "dcm-xtal_2-roll_in_mrad-velocity": partial_reading(1000.0), + "dcm-xtal_2-roll_in_mrad-encoder_resolution": partial_reading(0), + "dcm-xtal_2-roll_in_mrad-motor_resolution": partial_reading(0), + "dcm-xtal_2-roll_in_mrad-steps_per_revolution": partial_reading(0), + "dcm-xtal_2-roll_in_mrad-units_per_revolution": partial_reading(0), }, ) diff --git a/tests/devices/insertion_device/test_apple2_controller.py b/tests/devices/insertion_device/test_apple2_controller.py index 9a7fae1fe2c..01d061171c6 100644 --- a/tests/devices/insertion_device/test_apple2_controller.py +++ b/tests/devices/insertion_device/test_apple2_controller.py @@ -135,12 +135,13 @@ async def mock_energy_readback_controller( configured_phase: float, configured_energy: float, ) -> DummyEnergyReadbackApple2Controller: - mock_locked_controller = DummyEnergyReadbackApple2Controller( - apple2=mock_locked_apple2, - gap_energy_motor_converter=lambda value, pol: configured_gap, - phase_energy_motor_converter=lambda value, pol: configured_phase, - inverse_gap_energy_motor_converter=lambda value, pol: configured_energy, - ) + with init_devices(mock=True): + mock_locked_controller = DummyEnergyReadbackApple2Controller( + apple2=mock_locked_apple2, + gap_energy_motor_converter=lambda value, pol: configured_gap, + phase_energy_motor_converter=lambda value, pol: configured_phase, + inverse_gap_energy_motor_converter=lambda value, pol: configured_energy, + ) return mock_locked_controller diff --git a/tests/devices/test_undulator.py b/tests/devices/test_undulator.py index c04e1fd4317..cdb69bbd223 100644 --- a/tests/devices/test_undulator.py +++ b/tests/devices/test_undulator.py @@ -76,6 +76,10 @@ async def test_undulator_mm_config_default_parameters(undulator_in_mm: Undulator "undulator_mm-gap_motor-motor_egu": partial_reading(""), "undulator_mm-gap_motor-offset": partial_reading(0.0), "undulator_mm-gap_motor-velocity": partial_reading(1000.0), + "undulator_mm-gap_motor-encoder_resolution": partial_reading(0), + "undulator_mm-gap_motor-motor_resolution": partial_reading(0), + "undulator_mm-gap_motor-steps_per_revolution": partial_reading(0), + "undulator_mm-gap_motor-units_per_revolution": partial_reading(0), }, ) @@ -104,10 +108,14 @@ async def test_configuration_includes_configuration_fields(undulator: UndulatorI { "undulator-gap_motor-motor_egu": partial_reading(""), "undulator-gap_motor-velocity": partial_reading(1000.0), + "undulator-gap_motor-encoder_resolution": partial_reading(0), + "undulator-gap_motor-offset": partial_reading(0.0), + "undulator-gap_motor-motor_resolution": partial_reading(0), + "undulator-gap_motor-steps_per_revolution": partial_reading(0), + "undulator-gap_motor-units_per_revolution": partial_reading(0), "undulator-length": partial_reading(2.0), "undulator-poles": partial_reading(80), "undulator-gap_discrepancy_tolerance_mm": partial_reading(0.002), - "undulator-gap_motor-offset": partial_reading(0.0), "undulator-undulator_period": partial_reading(27), }, ) From d5c165f21f4fb21bbc5aa4230bcb3e7ed5b8d380 Mon Sep 17 00:00:00 2001 From: Oli Wenman Date: Fri, 17 Jul 2026 12:52:52 +0000 Subject: [PATCH 4/7] Fix apple2 mock_config:_client errors --- src/dodal/testing/fixtures/devices/apple2.py | 4 +++- tests/devices/test_undulator.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dodal/testing/fixtures/devices/apple2.py b/src/dodal/testing/fixtures/devices/apple2.py index ff7bc0d75de..9c44e1adb5a 100644 --- a/src/dodal/testing/fixtures/devices/apple2.py +++ b/src/dodal/testing/fixtures/devices/apple2.py @@ -26,7 +26,9 @@ def mock_config_client() -> ConfigClient: mock_config_client.get_file_contents = MagicMock(spec=["get_file_contents"]) - def my_side_effect(file_path, reset_cached_result) -> str: + def my_side_effect( + file_path, desired_return_type, reset_cached_result, force_parser + ) -> str: assert reset_cached_result is True with open(file_path) as f: return f.read() diff --git a/tests/devices/test_undulator.py b/tests/devices/test_undulator.py index cdb69bbd223..8f01fdd3631 100644 --- a/tests/devices/test_undulator.py +++ b/tests/devices/test_undulator.py @@ -108,8 +108,8 @@ async def test_configuration_includes_configuration_fields(undulator: UndulatorI { "undulator-gap_motor-motor_egu": partial_reading(""), "undulator-gap_motor-velocity": partial_reading(1000.0), - "undulator-gap_motor-encoder_resolution": partial_reading(0), "undulator-gap_motor-offset": partial_reading(0.0), + "undulator-gap_motor-encoder_resolution": partial_reading(0), "undulator-gap_motor-motor_resolution": partial_reading(0), "undulator-gap_motor-steps_per_revolution": partial_reading(0), "undulator-gap_motor-units_per_revolution": partial_reading(0), From 572edb6a9b8aaea0f643cba49a92bdccf9e194d8 Mon Sep 17 00:00:00 2001 From: Oli Wenman Date: Fri, 17 Jul 2026 12:54:30 +0000 Subject: [PATCH 5/7] update comment --- tests/devices/beamlines/i06_shared/test_i06_apple2controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/devices/beamlines/i06_shared/test_i06_apple2controller.py b/tests/devices/beamlines/i06_shared/test_i06_apple2controller.py index 8a2aa1c72b9..597d365c962 100644 --- a/tests/devices/beamlines/i06_shared/test_i06_apple2controller.py +++ b/tests/devices/beamlines/i06_shared/test_i06_apple2controller.py @@ -14,7 +14,7 @@ ) from dodal.devices.insertion_device.energy_motor_lookup import EnergyMotorLookup -# add mock_id_gap, mock_phase and mock_jaw_phase_axes to pytest. +# add mock_id_gap, mock_phase and mock_phase_axes pytest_plugins = ["dodal.testing.fixtures.devices.apple2"] From ab0dbcfb6435c213bc07febc2cd0313114f07ddf Mon Sep 17 00:00:00 2001 From: Oli Wenman Date: Fri, 17 Jul 2026 12:55:14 +0000 Subject: [PATCH 6/7] Remove unneeded connects --- tests/devices/insertion_device/test_apple_knot_undulator.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/devices/insertion_device/test_apple_knot_undulator.py b/tests/devices/insertion_device/test_apple_knot_undulator.py index cbc67906e76..205a720c9e6 100644 --- a/tests/devices/insertion_device/test_apple_knot_undulator.py +++ b/tests/devices/insertion_device/test_apple_knot_undulator.py @@ -56,8 +56,6 @@ async def test_id_set_energy_const_pol( initial_phase_top_outer: float, expected_pol: Pol, ): - await mock_locked_apple2.connect(mock=True) - await mock_apple_knot_i05_controller.connect(mock=True) set_mock_value( mock_locked_apple2.phase().top_outer.user_readback, initial_phase_top_outer ) From 83d0d5b35bf710318c69edac54fbbbbd3712c4f2 Mon Sep 17 00:00:00 2001 From: Oli Wenman Date: Fri, 17 Jul 2026 14:50:32 +0000 Subject: [PATCH 7/7] Fix fasteiger test --- .../plans/configure_arm_trigger_and_disarm_detector.py | 2 +- .../test_configure_arm_trigger_and_disarm_detector.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/dodal/plans/configure_arm_trigger_and_disarm_detector.py b/src/dodal/plans/configure_arm_trigger_and_disarm_detector.py index 8691010812d..17f847b0184 100644 --- a/src/dodal/plans/configure_arm_trigger_and_disarm_detector.py +++ b/src/dodal/plans/configure_arm_trigger_and_disarm_detector.py @@ -54,7 +54,7 @@ def configure_arm_trigger_and_disarm_detector( yield from bps.kickoff(eiger, wait=True) LOGGER.info(f"Kickoff Eiger: {time.time() - start}s") start = time.time() - yield from bps.trigger(eiger.detector.trigger, wait=True) + yield from bps.trigger(eiger.detector.trigger_, wait=True) LOGGER.info(f"Triggering Eiger: {time.time() - start}s") start = time.time() yield from bps.complete(eiger, wait=True) diff --git a/tests/plans/test_configure_arm_trigger_and_disarm_detector.py b/tests/plans/test_configure_arm_trigger_and_disarm_detector.py index 17c8fdd6f0a..4dee443f930 100644 --- a/tests/plans/test_configure_arm_trigger_and_disarm_detector.py +++ b/tests/plans/test_configure_arm_trigger_and_disarm_detector.py @@ -8,6 +8,7 @@ TriggerInfo, callback_on_mock_execute, get_mock, + init_devices, set_mock_value, ) from ophyd_async.fastcs.eiger import EigerDetector as FastEiger @@ -21,8 +22,8 @@ @pytest.fixture async def fake_eiger() -> FastEiger: - fake_eiger = FastEiger("", MagicMock()) - await fake_eiger.connect(mock=True) + with init_devices(mock=True): + fake_eiger = FastEiger("", MagicMock()) set_mock_value(fake_eiger.detector.bit_depth_image, 32) return fake_eiger @@ -55,7 +56,7 @@ def set_detector_into_writing_state(*args, **kwargs) -> None: def set_frames_written(*args, **kwargs) -> None: set_mock_value(fake_eiger.od.fp.frames_written, 1) - callback_on_mock_execute(fake_eiger.detector.trigger, set_frames_written) + callback_on_mock_execute(fake_eiger.arm_when_ready, set_frames_written) run_engine( configure_arm_trigger_and_disarm_detector(