diff --git a/src/dodal/beamlines/k07.py b/src/dodal/beamlines/k07.py index bdde96eb80..49c123fbf7 100644 --- a/src/dodal/beamlines/k07.py +++ b/src/dodal/beamlines/k07.py @@ -4,6 +4,14 @@ device_factory, ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.devices.apple2_undulator import ( + Apple2, + InsertionDeviceEnergy, + InsertionDevicePolarisation, + UndulatorGap, + UndulatorPhaseAxes, +) +from dodal.devices.k07 import K07Apple2Controller from dodal.devices.pgm import PGM from dodal.devices.synchrotron import Synchrotron from dodal.log import set_beamline as set_log_beamline @@ -28,4 +36,55 @@ class Grating(StrictEnum): # Grating does not exist yet - this class is a placeholder for when it does @device_factory(skip=True) def pgm() -> PGM: - return PGM(prefix=f"{PREFIX.beamline_prefix}-OP-PGM-01:", grating=Grating) + return PGM( + prefix=f"{PREFIX.beamline_prefix}-OP-PGM-01:", + grating=Grating, + ) + + +# Insertion device objects + + +# Insertion device gap and phase do not exist yet - these classes are placeholders for when they do +@device_factory(skip=True) +def id_gap() -> UndulatorGap: + return UndulatorGap( + prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:", + ) + + +@device_factory(skip=True) +def id_phase() -> UndulatorPhaseAxes: + return UndulatorPhaseAxes( + prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:", + top_outer="RPQ1", + top_inner="RPQ2", + btm_inner="RPQ3", + btm_outer="RPQ4", + ) + + +# Insertion device raw does not exist yet - this class is a placeholder for when it does +@device_factory(skip=True) +def id() -> Apple2: + return Apple2( + id_gap=id_gap(), + id_phase=id_phase(), + ) + + +# Insertion device controller does not exist yet - this class is a placeholder for when it does +@device_factory(skip=True) +def id_controller() -> K07Apple2Controller: + return K07Apple2Controller(apple2=id()) + + +# Insertion device energy does not exist yet - this class is a placeholder for when it does +@device_factory(skip=True) +def id_energy() -> InsertionDeviceEnergy: + return InsertionDeviceEnergy(id_controller=id_controller()) + + +@device_factory(skip=True) +def id_polarisation() -> InsertionDevicePolarisation: + return InsertionDevicePolarisation(id_controller=id_controller()) diff --git a/src/dodal/devices/k07/__init__.py b/src/dodal/devices/k07/__init__.py new file mode 100644 index 0000000000..8b710bb516 --- /dev/null +++ b/src/dodal/devices/k07/__init__.py @@ -0,0 +1,3 @@ +from .insertion_device import K07Apple2Controller + +__all__ = ["K07Apple2Controller"] diff --git a/src/dodal/devices/k07/insertion_device.py b/src/dodal/devices/k07/insertion_device.py new file mode 100644 index 0000000000..8b8e11e8b4 --- /dev/null +++ b/src/dodal/devices/k07/insertion_device.py @@ -0,0 +1,23 @@ +from dodal.devices.apple2_undulator import Apple2, Apple2Controller + + +# Inversion device on K07 does not exist yet - this class is a placeholder for when it does +class K07Apple2Controller(Apple2Controller): + """K07 insertion device controller""" + + def __init__( + self, + apple2: Apple2, + name: str = "", + ) -> None: + super().__init__( + apple2=apple2, + energy_to_motor_converter=lambda energy, pol: (0.0, 0.0), + name=name, + ) + + async def _set_motors_from_energy(self, value: float) -> None: + """ + Set the undulator motors for a given energy and polarisation. + """ + pass diff --git a/tests/devices/k07/__init__.py b/tests/devices/k07/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/devices/k07/test_id.py b/tests/devices/k07/test_id.py new file mode 100644 index 0000000000..f7a0190e62 --- /dev/null +++ b/tests/devices/k07/test_id.py @@ -0,0 +1,58 @@ +from unittest.mock import MagicMock + +import pytest +from ophyd_async.core import init_devices + +from dodal.devices.apple2_undulator import ( + Apple2, + InsertionDeviceEnergy, + UndulatorGap, + UndulatorPhaseAxes, +) +from dodal.devices.k07 import K07Apple2Controller + + +@pytest.fixture +async def id_gap() -> UndulatorGap: + async with init_devices(mock=True): + return UndulatorGap(prefix="TEST-MO-SERVC-01:") + + +@pytest.fixture +async def id_phase() -> UndulatorPhaseAxes: + async with init_devices(mock=True): + return UndulatorPhaseAxes( + prefix="TEST-MO-SERVC-01:", + top_outer="RPQ1", + top_inner="RPQ2", + btm_inner="RPQ3", + btm_outer="RPQ4", + ) + + +@pytest.fixture +async def id( + id_gap: UndulatorGap, + id_phase: UndulatorPhaseAxes, +) -> Apple2: + async with init_devices(mock=True): + return Apple2(id_gap=id_gap, id_phase=id_phase) + + +@pytest.fixture +async def id_controller(id: Apple2) -> K07Apple2Controller: + async with init_devices(mock=True): + return K07Apple2Controller(apple2=id) + + +# Insertion device controller does not exist yet - this class is a placeholder for when it does +async def test_id_controller_set_energy(id_controller: K07Apple2Controller) -> None: + async with init_devices(mock=True): + id_energy = InsertionDeviceEnergy(id_controller=id_controller) + assert id_energy is not None + id_controller._set_motors_from_energy = MagicMock( + side_effect=id_controller._set_motors_from_energy + ) + await id_energy.set(500.0) + id_controller._set_motors_from_energy.assert_called_once_with(500.0) + assert await id_controller.energy.get_value() == 500.0