From 0baece222141ce155dac06d84235c8e985603ad6 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Thu, 1 Aug 2024 11:50:09 +0100 Subject: [PATCH 01/93] add i18 beamline definition --- src/dodal/beamlines/i18.py | 218 +++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 src/dodal/beamlines/i18.py diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py new file mode 100644 index 0000000000..e5a53cde0b --- /dev/null +++ b/src/dodal/beamlines/i18.py @@ -0,0 +1,218 @@ +from pathlib import Path + +from ophyd_async.panda import HDFPanda + +from dodal.common.beamlines.beamline_utils import ( + device_instantiation, + get_directory_provider, + set_directory_provider, +) +from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.common.beamlines.device_helpers import numbered_slits +from dodal.common.visit import DirectoryServiceClient, StaticVisitDirectoryProvider +from dodal.devices.focusing_mirror import FocusingMirror +from dodal.devices.i22.dcm import CrystalMetadata, DoubleCrystalMonochromator +from dodal.devices.slits import Slits +from dodal.devices.synchrotron import Synchrotron +from dodal.devices.tetramm import TetrammDetector +from dodal.devices.undulator import Undulator +from dodal.devices.xspress3.xspress3 import Xspress3 +from dodal.log import set_beamline as set_log_beamline +from dodal.utils import BeamlinePrefix, get_beamline_name, skip_device + +BL = get_beamline_name("i18") +set_log_beamline(BL) +set_utils_beamline(BL) + +# +# table again, t1 theta and d7bdiode +# MO table is ok +# +# pinhole PVs? +# -AL-APTR-01:TEMP1 +# +# diode reading and also DRAIN +# diode is ok, there are A and B variants +# motors A and B +# camera not used + +# Currently we must hard-code the visit, determining the visit at runtime requires +# infrastructure that is still WIP. +# Communication with GDA is also WIP so for now we determine an arbitrary scan number +# locally and write the commissioning directory. The scan number is not guaranteed to +# be unique and the data is at risk - this configuration is for testing only. +set_directory_provider( + StaticVisitDirectoryProvider( + BL, + Path("/dls/i18/data/2024/cm37264-2/bluesky"), + client=DirectoryServiceClient("http://i18-control:8088/api"), + ) +) + + +def synchrotron( + wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False +) -> Synchrotron: + return device_instantiation( + Synchrotron, + "synchrotron", + "", + wait_for_connection, + fake_with_ophyd_sim, + ) + + +def undulator( + wait_for_connection: bool = True, + fake_with_ophyd_sim: bool = False, +) -> Undulator: + return device_instantiation( + Undulator, + "undulator", + f"{BeamlinePrefix(BL).insertion_prefix}-MO-SERVC-01:", + # add CURRGAPD + wait_for_connection, + fake_with_ophyd_sim, + bl_prefix=False, + poles=80, + length=2.0, + ) + + +@skip_device() +def slits_1( + wait_for_connection: bool = True, + fake_with_ophyd_sim: bool = False, +) -> Slits: + return numbered_slits( + 1, + # BL18I-AL-SLITS-01 + wait_for_connection, + fake_with_ophyd_sim, + ) + + +# Must document what PandAs are physically connected to +# See: https://github.com/bluesky/ophyd-async/issues/284 +@skip_device() +def panda1( + wait_for_connection: bool = True, + fake_with_ophyd_sim: bool = False, +) -> HDFPanda: + return device_instantiation( + HDFPanda, + "panda1", + "-MO-PANDA-01:", + wait_for_connection, + fake_with_ophyd_sim, + directory_provider=get_directory_provider(), + ) + + +@skip_device() +def xspress3( + wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False +) -> Xspress3: + """ + 16 channels Xspress3 detector + -EA-XPS-02:CAM:MaxSizeX_RBV + also ArraySize + also :CONNECTED + """ + + return device_instantiation( + Xspress3, + # prefix="-EA-DET-03:", + prefix="-EA-XPS-02:", + name="Xspress3", + num_channels=16, + wait=wait_for_connection, + fake=fake_with_ophyd_sim, + ) + + +def dcm( + wait_for_connection: bool = True, + fake_with_ophyd_sim: bool = False, +) -> DoubleCrystalMonochromator: + return device_instantiation( + DoubleCrystalMonochromator, + "dcm", + "", + wait_for_connection, + fake_with_ophyd_sim, + bl_prefix=False, + motion_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-MO-DCM-01:", + temperature_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-DI-DCM-01:", + crystal_1_metadata=CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), + ), + crystal_2_metadata=CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), + ), + ) + + +@skip_device() +def i0( + wait_for_connection: bool = True, + fake_with_ophyd_sim: bool = False, +) -> TetrammDetector: + return device_instantiation( + TetrammDetector, + "i0", + "-EA-XBPM-02:", + # -DI-XBPM-02:DEV:Firmware + wait_for_connection, + fake_with_ophyd_sim, + type="Cividec Diamond XBPM", + directory_provider=get_directory_provider(), + ) + + +@skip_device() +def it( + wait_for_connection: bool = True, + fake_with_ophyd_sim: bool = False, +) -> TetrammDetector: + return device_instantiation( + TetrammDetector, + "it", + "-EA-TTRM-02:", + wait_for_connection, + fake_with_ophyd_sim, + type="PIN Diode", + directory_provider=get_directory_provider(), + ) + + +def vfm( + wait_for_connection: bool = True, + fake_with_ophyd_sim: bool = False, +) -> FocusingMirror: + return device_instantiation( + FocusingMirror, + "vfm", + "-OP-KBM-01:VFM:", + wait_for_connection, + fake_with_ophyd_sim, + ) + + +def hfm( + wait_for_connection: bool = True, + fake_with_ophyd_sim: bool = False, +) -> FocusingMirror: + return device_instantiation( + FocusingMirror, + "hfm", + "-OP-KBM-01:HFM:", + wait_for_connection, + fake_with_ophyd_sim, + ) From c902167147021b04c119dd26df344bd7871434a3 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Thu, 1 Aug 2024 15:52:42 +0100 Subject: [PATCH 02/93] add metadata to dcm crystals --- src/dodal/beamlines/i18.py | 48 +++++++++++------------------------- src/dodal/devices/i22/dcm.py | 1 + 2 files changed, 16 insertions(+), 33 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index e5a53cde0b..dd81d52fbd 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -9,9 +9,10 @@ ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.common.beamlines.device_helpers import numbered_slits +from dodal.common.crystal_metadata import CrystalMetadata from dodal.common.visit import DirectoryServiceClient, StaticVisitDirectoryProvider from dodal.devices.focusing_mirror import FocusingMirror -from dodal.devices.i22.dcm import CrystalMetadata, DoubleCrystalMonochromator +from dodal.devices.i22.dcm import DoubleCrystalMonochromator from dodal.devices.slits import Slits from dodal.devices.synchrotron import Synchrotron from dodal.devices.tetramm import TetrammDetector @@ -24,17 +25,6 @@ set_log_beamline(BL) set_utils_beamline(BL) -# -# table again, t1 theta and d7bdiode -# MO table is ok -# -# pinhole PVs? -# -AL-APTR-01:TEMP1 -# -# diode reading and also DRAIN -# diode is ok, there are A and B variants -# motors A and B -# camera not used # Currently we must hard-code the visit, determining the visit at runtime requires # infrastructure that is still WIP. @@ -70,7 +60,6 @@ def undulator( Undulator, "undulator", f"{BeamlinePrefix(BL).insertion_prefix}-MO-SERVC-01:", - # add CURRGAPD wait_for_connection, fake_with_ophyd_sim, bl_prefix=False, @@ -86,7 +75,6 @@ def slits_1( ) -> Slits: return numbered_slits( 1, - # BL18I-AL-SLITS-01 wait_for_connection, fake_with_ophyd_sim, ) @@ -115,14 +103,10 @@ def xspress3( ) -> Xspress3: """ 16 channels Xspress3 detector - -EA-XPS-02:CAM:MaxSizeX_RBV - also ArraySize - also :CONNECTED """ return device_instantiation( Xspress3, - # prefix="-EA-DET-03:", prefix="-EA-XPS-02:", name="Xspress3", num_channels=16, @@ -131,6 +115,13 @@ def xspress3( ) +crystal_1_metadata = CrystalMetadata("Si111") +crystal_2_metadata = CrystalMetadata("Si111") + +_unused_crystal_metadata_1 = CrystalMetadata("Si311") +_unused_crystal_metadata_2 = CrystalMetadata("Si333") + + def dcm( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, @@ -144,18 +135,8 @@ def dcm( bl_prefix=False, motion_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-MO-DCM-01:", temperature_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-DI-DCM-01:", - crystal_1_metadata=CrystalMetadata( - usage="Bragg", - type="silicon", - reflection=(1, 1, 1), - d_spacing=(3.13475, "nm"), - ), - crystal_2_metadata=CrystalMetadata( - usage="Bragg", - type="silicon", - reflection=(1, 1, 1), - d_spacing=(3.13475, "nm"), - ), + crystal_1_metadata=crystal_1_metadata, + crystal_2_metadata=crystal_2_metadata, ) @@ -168,7 +149,6 @@ def i0( TetrammDetector, "i0", "-EA-XBPM-02:", - # -DI-XBPM-02:DEV:Firmware wait_for_connection, fake_with_ophyd_sim, type="Cividec Diamond XBPM", @@ -192,6 +172,7 @@ def it( ) +@skip_device def vfm( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, @@ -199,12 +180,13 @@ def vfm( return device_instantiation( FocusingMirror, "vfm", - "-OP-KBM-01:VFM:", + "-OP-VFM-01:", wait_for_connection, fake_with_ophyd_sim, ) +@skip_device def hfm( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, @@ -212,7 +194,7 @@ def hfm( return device_instantiation( FocusingMirror, "hfm", - "-OP-KBM-01:HFM:", + "-OP-HFM-01:", wait_for_connection, fake_with_ophyd_sim, ) diff --git a/src/dodal/devices/i22/dcm.py b/src/dodal/devices/i22/dcm.py index 58180ad9b6..3c84c3e678 100644 --- a/src/dodal/devices/i22/dcm.py +++ b/src/dodal/devices/i22/dcm.py @@ -1,4 +1,5 @@ import time +from collections.abc import Sequence import numpy as np from bluesky.protocols import Reading From 4fb6a763d4b8ffe3e4e6dcf04ecb5505f0b29c75 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Thu, 1 Aug 2024 16:52:28 +0100 Subject: [PATCH 03/93] adapt the tests --- src/dodal/beamlines/p38.py | 5 +++++ tests/devices/i22/test_dcm.py | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/dodal/beamlines/p38.py b/src/dodal/beamlines/p38.py index 30cb2e5a99..2141e720ff 100644 --- a/src/dodal/beamlines/p38.py +++ b/src/dodal/beamlines/p38.py @@ -233,12 +233,17 @@ def dcm( fake_with_ophyd_sim, bl_prefix=False, temperature_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-DI-DCM-01:", +<<<<<<< HEAD crystal_1_metadata=make_crystal_metadata_from_material( MaterialsEnum.Si, (1, 1, 1) ), crystal_2_metadata=make_crystal_metadata_from_material( MaterialsEnum.Si, (1, 1, 1) ), +======= + crystal_1_metadata=CrystalMetadata("Si111"), + crystal_2_metadata=CrystalMetadata("Si111"), +>>>>>>> 7c02f1411 (adapt the tests) ) diff --git a/tests/devices/i22/test_dcm.py b/tests/devices/i22/test_dcm.py index cd8dd0ebbc..789206f6f6 100644 --- a/tests/devices/i22/test_dcm.py +++ b/tests/devices/i22/test_dcm.py @@ -49,6 +49,32 @@ def test_count_dcm( ) +<<<<<<< HEAD +======= +async def test_crystal_metadata_not_propagated_when_not_supplied(): + async with DeviceCollector(mock=True): + dcm = DoubleCrystalMonochromator( + prefix="FOO-MO", + temperature_prefix="FOO-DI", + crystal_1_metadata=CrystalMetadata("Si111"), + crystal_2_metadata=CrystalMetadata("Si111"), + ) + + configuration = await dcm.read_configuration() + expected_absent_keys = { + "crystal-1-usage", + "crystal-1-type", + "crystal-1-reflection", + "crystal-1-d_spacing", + "crystal-2-usage", + "crystal-2-type", + "crystal-2-reflection", + "crystal-2-d_spacing", + } + assert expected_absent_keys.isdisjoint(configuration) + + +>>>>>>> 7c02f1411 (adapt the tests) @pytest.mark.parametrize( "energy,wavelength", [ From 7311a8efee11eb766ec571f3c556f8b7419071ef Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Fri, 2 Aug 2024 10:22:04 +0000 Subject: [PATCH 04/93] udpate fixtures --- tests/devices/i22/test_dcm.py | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/tests/devices/i22/test_dcm.py b/tests/devices/i22/test_dcm.py index 789206f6f6..cd8dd0ebbc 100644 --- a/tests/devices/i22/test_dcm.py +++ b/tests/devices/i22/test_dcm.py @@ -49,32 +49,6 @@ def test_count_dcm( ) -<<<<<<< HEAD -======= -async def test_crystal_metadata_not_propagated_when_not_supplied(): - async with DeviceCollector(mock=True): - dcm = DoubleCrystalMonochromator( - prefix="FOO-MO", - temperature_prefix="FOO-DI", - crystal_1_metadata=CrystalMetadata("Si111"), - crystal_2_metadata=CrystalMetadata("Si111"), - ) - - configuration = await dcm.read_configuration() - expected_absent_keys = { - "crystal-1-usage", - "crystal-1-type", - "crystal-1-reflection", - "crystal-1-d_spacing", - "crystal-2-usage", - "crystal-2-type", - "crystal-2-reflection", - "crystal-2-d_spacing", - } - assert expected_absent_keys.isdisjoint(configuration) - - ->>>>>>> 7c02f1411 (adapt the tests) @pytest.mark.parametrize( "energy,wavelength", [ From 9c520870a3ddcf06ea5c2050ccb3703580a84c8e Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Fri, 2 Aug 2024 10:48:08 +0000 Subject: [PATCH 05/93] move devices from i18-bluesky --- src/dodal/beamlines/i18.py | 22 +++++++ src/dodal/devices/i18/diode.py | 20 ++++++ src/dodal/devices/i18/sim_motor_set.py | 88 ++++++++++++++++++++++++++ src/dodal/devices/i18/table.py | 14 ++++ 4 files changed, 144 insertions(+) create mode 100644 src/dodal/devices/i18/diode.py create mode 100644 src/dodal/devices/i18/sim_motor_set.py create mode 100644 src/dodal/devices/i18/table.py diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index dd81d52fbd..77f387a9b5 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -12,6 +12,8 @@ from dodal.common.crystal_metadata import CrystalMetadata from dodal.common.visit import DirectoryServiceClient, StaticVisitDirectoryProvider from dodal.devices.focusing_mirror import FocusingMirror +from dodal.devices.i18.diode import Diode +from dodal.devices.i18.table import Table from dodal.devices.i22.dcm import DoubleCrystalMonochromator from dodal.devices.slits import Slits from dodal.devices.synchrotron import Synchrotron @@ -198,3 +200,23 @@ def hfm( wait_for_connection, fake_with_ophyd_sim, ) + + +def diode(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -> Diode: + return device_instantiation( + Diode, + "diodad7bdiode", + "-DI-PHDGN-07:", + wait_for_connection, + fake_with_ophyd_sim, + ) + + +def table(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -> Table: + return device_instantiation( + Table, + "table", + "-MO-TABLE-01:", + wait_for_connection, + fake_with_ophyd_sim, + ) diff --git a/src/dodal/devices/i18/diode.py b/src/dodal/devices/i18/diode.py new file mode 100644 index 0000000000..55d7648fad --- /dev/null +++ b/src/dodal/devices/i18/diode.py @@ -0,0 +1,20 @@ +from ophyd_async.core import ( + StandardReadable, +) +from ophyd_async.epics.signal import epics_signal_r + + +class Diode(StandardReadable): + def __init__( + self, + prefix: str, + name: str = "", + ): + self._prefix = prefix + with self.add_children_as_readables(): + self.signal = epics_signal_r(float, prefix + "B:DIODE:I") + + super().__init__(name=name) + + +# todo should add 'read' override? diff --git a/src/dodal/devices/i18/sim_motor_set.py b/src/dodal/devices/i18/sim_motor_set.py new file mode 100644 index 0000000000..e829cb5d33 --- /dev/null +++ b/src/dodal/devices/i18/sim_motor_set.py @@ -0,0 +1,88 @@ +import os + +from ophyd import EpicsMotor +from ophyd.sim import Syn2DGauss, SynAxis, SynGauss + + +def create_epics_motor(motor_name="epics_motor", motor_base_pv="ws416-MO-SIM-01:M1"): + print(f"Creating Epics motor for {motor_base_pv}") + epics_motor = EpicsMotor(motor_base_pv, name=motor_name) + # epics_motor.wait_for_connection(timeout=5) # blueapi fails to connect any PVs! + return epics_motor + + +def create_dummy_motor(motor_name="dummy_motor"): + print(f"Creating dummy motor {motor_name}") + return SynAxis(name=motor_name, labels={"motors"}) + + +def create_syn_gaussian(det_name, motor, motor_field, noise="none", noise_multiplier=1): + print(f"Creating synthetic Gaussian detector {det_name}") + syn_gauss = SynGauss( + det_name, motor, motor_field, center=0, Imax=5, sigma=0.5, labels={"detectors"} + ) + syn_gauss.noise.put(noise) + syn_gauss.noise_multiplier.put(noise_multiplier) + return syn_gauss + + +def create_syn_2d_gaussian( + det_name, + motor1, + motor1_field, + motor2, + motor2_field, + noise="none", + noise_multiplier=1, +): + print(f"Creating synthetic 2d Gaussian detector {det_name}") + + syn_gauss = Syn2DGauss( + det_name, + motor1, + motor1_field, + motor2, + motor2_field, + center=0, + Imax=1, + labels={"detectors"}, + ) + syn_gauss.noise.put(noise) + syn_gauss.noise_multiplier.put(noise_multiplier) + return syn_gauss + + +dummy_mot1 = create_dummy_motor("dummy_motor1") +dummy_mot2 = create_dummy_motor("dummy_motor2") +dummy_mot1.delay = 0.05 + + +def dummy_motor1(name: str = "dummy_motor1") -> SynAxis: + return dummy_mot1 + + +def dummy_motor2(name: str = "dummy_motor2") -> SynAxis: + return dummy_mot2 + + +def sim_gauss_det(name: str = "sim_gauss_det") -> SynGauss: + return create_syn_gaussian(name, dummy_mot1, "dummy_motor1") + + +def sim_2d_gauss_det(name: str = "sim_2d_gauss_det") -> Syn2DGauss: + return create_syn_2d_gaussian( + name, dummy_mot1, "dummy_motor1", dummy_mot2, "dummy_motor2" + ) + + +# Make sure EPICS_CA_SERVER_PORT is set to correct value (6064 for DLS sim area detector and motors, 5064 on beamlines) +os.environ["EPICS_CA_SERVER_PORT"] = "6064" + + +# def sim_x(name : str, pv_name : str) -> EpicsMotor: +def sim_x(name: str = "sim_x", pv_name: str = "ws416-MO-SIM-01:M1") -> EpicsMotor: + return create_epics_motor(name, pv_name) + + +def sim_y(name: str = "sim_y", pv_name: str = "ws416-MO-SIM-01:M2") -> EpicsMotor: + return create_epics_motor(name, pv_name) diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py new file mode 100644 index 0000000000..b29134c66a --- /dev/null +++ b/src/dodal/devices/i18/table.py @@ -0,0 +1,14 @@ +from bluesky.protocols import Movable +from ophyd_async.core import ( + StandardReadable, +) +from ophyd_async.epics.motion import Motor + + +class Table(StandardReadable, Movable): + def __init__(self, motion_prefix: str, prefix: str = "", name: str = "") -> None: + with self.add_children_as_readables(): + self.x = Motor(motion_prefix + "X") + self.y = Motor(motion_prefix + "Y") + self.z = Motor(motion_prefix + "Z") + self.theta = Motor(motion_prefix + "THETA") From d902185d47d40c79fcdf4c0e9bfe51809c2b44e0 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 5 Aug 2024 11:20:14 +0100 Subject: [PATCH 06/93] fix some pv values --- src/dodal/beamlines/i18.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 77f387a9b5..92aa10417e 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -99,6 +99,7 @@ def panda1( ) +# NOTE: the reason for skipping is that the odin detectors are not yet supported @skip_device() def xspress3( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False @@ -109,7 +110,7 @@ def xspress3( return device_instantiation( Xspress3, - prefix="-EA-XPS-02:", + prefix="-EA-XSP-02:", name="Xspress3", num_channels=16, wait=wait_for_connection, @@ -150,7 +151,7 @@ def i0( return device_instantiation( TetrammDetector, "i0", - "-EA-XBPM-02:", + "-DI-XBPM-02:", wait_for_connection, fake_with_ophyd_sim, type="Cividec Diamond XBPM", @@ -166,10 +167,10 @@ def it( return device_instantiation( TetrammDetector, "it", - "-EA-TTRM-02:", + "-DI-XBPM-01:", wait_for_connection, fake_with_ophyd_sim, - type="PIN Diode", + type="Tetramm", directory_provider=get_directory_provider(), ) From d77441349536dd5b0a49dd1cfa2ad4c0755544da Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 5 Aug 2024 16:37:49 +0000 Subject: [PATCH 07/93] move away from ophyd devices --- src/dodal/beamlines/i18.py | 28 ++++++++ src/dodal/devices/i18/sim_detector.py | 18 +++++ src/dodal/devices/i18/sim_motor_set.py | 88 ----------------------- src/dodal/devices/i18/sim_raster_stage.py | 11 +++ 4 files changed, 57 insertions(+), 88 deletions(-) create mode 100644 src/dodal/devices/i18/sim_detector.py delete mode 100644 src/dodal/devices/i18/sim_motor_set.py create mode 100644 src/dodal/devices/i18/sim_raster_stage.py diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 92aa10417e..337a5632a2 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -1,3 +1,4 @@ +import os from pathlib import Path from ophyd_async.panda import HDFPanda @@ -13,6 +14,8 @@ from dodal.common.visit import DirectoryServiceClient, StaticVisitDirectoryProvider from dodal.devices.focusing_mirror import FocusingMirror from dodal.devices.i18.diode import Diode +from dodal.devices.i18.sim_detector import SimDetector +from dodal.devices.i18.sim_raster_stage import RasterStage from dodal.devices.i18.table import Table from dodal.devices.i22.dcm import DoubleCrystalMonochromator from dodal.devices.slits import Slits @@ -23,6 +26,9 @@ from dodal.log import set_beamline as set_log_beamline from dodal.utils import BeamlinePrefix, get_beamline_name, skip_device +# Make sure EPICS_CA_SERVER_PORT is set to correct value (6064 for DLS sim area detector and motors, 5064 on beamlines) +os.environ["EPICS_CA_SERVER_PORT"] = "6064" + BL = get_beamline_name("i18") set_log_beamline(BL) set_utils_beamline(BL) @@ -221,3 +227,25 @@ def table(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) - wait_for_connection, fake_with_ophyd_sim, ) + + +def raster_stage( + wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False +) -> RasterStage: + return device_instantiation( + RasterStage, + "raster_stage", + "-MO-SIM-01:", + wait_for_connection, + fake_with_ophyd_sim, + ) + + +def sim_detector(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False): + return device_instantiation( + SimDetector, + "sim_detector", + "-MO-SIM-01:", + wait_for_connection, + fake_with_ophyd_sim, + ) diff --git a/src/dodal/devices/i18/sim_detector.py b/src/dodal/devices/i18/sim_detector.py new file mode 100644 index 0000000000..2e5d074814 --- /dev/null +++ b/src/dodal/devices/i18/sim_detector.py @@ -0,0 +1,18 @@ +from ophyd_async.epics.motion import Motor +from ophyd_async.sim import PatternGenerator + + +class SimDetector: + def __init__(self, name: str, motor: Motor, motor_field: str): + self.name = name + self.motor = motor + self.motor_field = motor_field + self.pattern_generator = PatternGenerator( + saturation_exposure_time=0.1, detector_height=100, detector_width=100 + ) + + def read(self): + return {self.name: {"value": self.pattern_generator}} + + def describe(self): + return {self.name: {"source": "synthetic", "dtype": "number"}} diff --git a/src/dodal/devices/i18/sim_motor_set.py b/src/dodal/devices/i18/sim_motor_set.py deleted file mode 100644 index e829cb5d33..0000000000 --- a/src/dodal/devices/i18/sim_motor_set.py +++ /dev/null @@ -1,88 +0,0 @@ -import os - -from ophyd import EpicsMotor -from ophyd.sim import Syn2DGauss, SynAxis, SynGauss - - -def create_epics_motor(motor_name="epics_motor", motor_base_pv="ws416-MO-SIM-01:M1"): - print(f"Creating Epics motor for {motor_base_pv}") - epics_motor = EpicsMotor(motor_base_pv, name=motor_name) - # epics_motor.wait_for_connection(timeout=5) # blueapi fails to connect any PVs! - return epics_motor - - -def create_dummy_motor(motor_name="dummy_motor"): - print(f"Creating dummy motor {motor_name}") - return SynAxis(name=motor_name, labels={"motors"}) - - -def create_syn_gaussian(det_name, motor, motor_field, noise="none", noise_multiplier=1): - print(f"Creating synthetic Gaussian detector {det_name}") - syn_gauss = SynGauss( - det_name, motor, motor_field, center=0, Imax=5, sigma=0.5, labels={"detectors"} - ) - syn_gauss.noise.put(noise) - syn_gauss.noise_multiplier.put(noise_multiplier) - return syn_gauss - - -def create_syn_2d_gaussian( - det_name, - motor1, - motor1_field, - motor2, - motor2_field, - noise="none", - noise_multiplier=1, -): - print(f"Creating synthetic 2d Gaussian detector {det_name}") - - syn_gauss = Syn2DGauss( - det_name, - motor1, - motor1_field, - motor2, - motor2_field, - center=0, - Imax=1, - labels={"detectors"}, - ) - syn_gauss.noise.put(noise) - syn_gauss.noise_multiplier.put(noise_multiplier) - return syn_gauss - - -dummy_mot1 = create_dummy_motor("dummy_motor1") -dummy_mot2 = create_dummy_motor("dummy_motor2") -dummy_mot1.delay = 0.05 - - -def dummy_motor1(name: str = "dummy_motor1") -> SynAxis: - return dummy_mot1 - - -def dummy_motor2(name: str = "dummy_motor2") -> SynAxis: - return dummy_mot2 - - -def sim_gauss_det(name: str = "sim_gauss_det") -> SynGauss: - return create_syn_gaussian(name, dummy_mot1, "dummy_motor1") - - -def sim_2d_gauss_det(name: str = "sim_2d_gauss_det") -> Syn2DGauss: - return create_syn_2d_gaussian( - name, dummy_mot1, "dummy_motor1", dummy_mot2, "dummy_motor2" - ) - - -# Make sure EPICS_CA_SERVER_PORT is set to correct value (6064 for DLS sim area detector and motors, 5064 on beamlines) -os.environ["EPICS_CA_SERVER_PORT"] = "6064" - - -# def sim_x(name : str, pv_name : str) -> EpicsMotor: -def sim_x(name: str = "sim_x", pv_name: str = "ws416-MO-SIM-01:M1") -> EpicsMotor: - return create_epics_motor(name, pv_name) - - -def sim_y(name: str = "sim_y", pv_name: str = "ws416-MO-SIM-01:M2") -> EpicsMotor: - return create_epics_motor(name, pv_name) diff --git a/src/dodal/devices/i18/sim_raster_stage.py b/src/dodal/devices/i18/sim_raster_stage.py new file mode 100644 index 0000000000..c2fc598bbe --- /dev/null +++ b/src/dodal/devices/i18/sim_raster_stage.py @@ -0,0 +1,11 @@ +from bluesky.protocols import Movable +from ophyd_async.core import StandardReadable +from ophyd_async.epics.motion import Motor + + +class RasterStage(StandardReadable, Movable): + def __init__(self, prefix: str, name: str = "") -> None: + with self.add_children_as_readables(): + self.offset_in_mm = Motor(prefix + "M1") + self.perp_in_mm = Motor(prefix + "M2") + super().__init__(name) From ed4f4c82f16dd83be12ca166ab64decf23732c95 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 19 Aug 2024 16:02:14 +0100 Subject: [PATCH 08/93] add a kb mirror device@ --- src/dodal/beamlines/i18.py | 11 +++++------ src/dodal/devices/i18/KBMirror.py | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 src/dodal/devices/i18/KBMirror.py diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 337a5632a2..efe0363969 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -12,8 +12,8 @@ from dodal.common.beamlines.device_helpers import numbered_slits from dodal.common.crystal_metadata import CrystalMetadata from dodal.common.visit import DirectoryServiceClient, StaticVisitDirectoryProvider -from dodal.devices.focusing_mirror import FocusingMirror from dodal.devices.i18.diode import Diode +from dodal.devices.i18.KBMirror import KBMirror from dodal.devices.i18.sim_detector import SimDetector from dodal.devices.i18.sim_raster_stage import RasterStage from dodal.devices.i18.table import Table @@ -185,9 +185,9 @@ def it( def vfm( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, -) -> FocusingMirror: +) -> KBMirror: return device_instantiation( - FocusingMirror, + KBMirror, "vfm", "-OP-VFM-01:", wait_for_connection, @@ -195,13 +195,12 @@ def vfm( ) -@skip_device def hfm( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, -) -> FocusingMirror: +) -> KBMirror: return device_instantiation( - FocusingMirror, + KBMirror, "hfm", "-OP-HFM-01:", wait_for_connection, diff --git a/src/dodal/devices/i18/KBMirror.py b/src/dodal/devices/i18/KBMirror.py new file mode 100644 index 0000000000..36bf002489 --- /dev/null +++ b/src/dodal/devices/i18/KBMirror.py @@ -0,0 +1,22 @@ +from bluesky.protocols import Movable +from ophyd_async.core import ( + StandardReadable, +) +from ophyd_async.epics.signal import epics_signal_rw + + +class KBMirror(StandardReadable, Movable): + def __init__( + self, + prefix: str, + name: str = "", + ): + self._prefix = prefix + with self.add_children_as_readables(): + self.x = epics_signal_rw(float, prefix + "X") + self.y = epics_signal_rw(float, prefix + "Y") + self.bend1 = epics_signal_rw(float, prefix + "BEND1") + self.bend2 = epics_signal_rw(float, prefix + "BEND2") + self.curve = epics_signal_rw(float, prefix + "CURVE") + self.ellip = epics_signal_rw(float, prefix + "ELLIP") + super().__init__(name=name) From 52d6ffe45ba142c101956f3d88258dc1f43f7185 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Wed, 21 Aug 2024 11:59:01 +0100 Subject: [PATCH 09/93] test lookup table --- src/dodal/devices/i18/IdGapLookupTable.py | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/dodal/devices/i18/IdGapLookupTable.py diff --git a/src/dodal/devices/i18/IdGapLookupTable.py b/src/dodal/devices/i18/IdGapLookupTable.py new file mode 100644 index 0000000000..f82e7135d0 --- /dev/null +++ b/src/dodal/devices/i18/IdGapLookupTable.py @@ -0,0 +1,25 @@ +from bluesky.protocols import Movable +from ophyd_async.core import ( + AsyncStatus, + StandardReadable, +) + +from dodal.log import LOGGER + + +class LookupTable(StandardReadable, Movable): + def __init__(self, prefix: str, name: str = ""): + with self.add_children_as_readables(): + # self.actual_transmission = epics_signal_r(float, prefix + "MATCH") + print("test") + + super().__init__(name) + + @AsyncStatus.wrap + async def set(self, transmission: float): + LOGGER.debug("Updating the lookup table ") + await self._use_current_energy.trigger() + LOGGER.info(f"Setting desired transmission to {transmission}") + await self._desired_transmission.set(transmission) + LOGGER.debug("Sending change filter command") + await self._change.trigger() From 42f16186ca4c67a1109ff821deafb5e44eb63e89 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Wed, 21 Aug 2024 15:05:10 +0100 Subject: [PATCH 10/93] readjust the value param in the set method --- src/dodal/devices/i18/IdGapLookupTable.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/dodal/devices/i18/IdGapLookupTable.py b/src/dodal/devices/i18/IdGapLookupTable.py index f82e7135d0..a765f3b397 100644 --- a/src/dodal/devices/i18/IdGapLookupTable.py +++ b/src/dodal/devices/i18/IdGapLookupTable.py @@ -16,10 +16,9 @@ def __init__(self, prefix: str, name: str = ""): super().__init__(name) @AsyncStatus.wrap - async def set(self, transmission: float): + async def set(self, value: float): + """ + value: transmission + """ LOGGER.debug("Updating the lookup table ") - await self._use_current_energy.trigger() - LOGGER.info(f"Setting desired transmission to {transmission}") - await self._desired_transmission.set(transmission) - LOGGER.debug("Sending change filter command") - await self._change.trigger() + pass From ba01d19537070baf4c78f76c07d161c70d10cdda Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Fri, 30 Aug 2024 10:01:52 +0100 Subject: [PATCH 11/93] trying to add the sim extra for ophyd-async --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 28093ed942..fecfef9202 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ dependencies = [ "click", "ophyd", "ophyd-async >= 0.9.0a1", + "ophyd-async[sim]", "bluesky", "pyepics", "dataclasses-json", From 80ac2e82017141fd8e4c3fe59fa0e7fedfa60af0 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Thu, 12 Sep 2024 13:39:01 +0100 Subject: [PATCH 12/93] adapt to the ophyd-async update --- src/dodal/beamlines/i18.py | 24 ++++++++++++++---------- src/dodal/devices/i18/sim_detector.py | 4 ++-- src/dodal/devices/i18/table.py | 2 +- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index efe0363969..9e9aee8c1b 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -1,23 +1,27 @@ import os from pathlib import Path +from ophyd_async.fastcs.panda import HDFPanda from ophyd_async.panda import HDFPanda from dodal.common.beamlines.beamline_utils import ( device_instantiation, - get_directory_provider, - set_directory_provider, + get_path_provider, + set_path_provider, ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.common.beamlines.device_helpers import numbered_slits from dodal.common.crystal_metadata import CrystalMetadata -from dodal.common.visit import DirectoryServiceClient, StaticVisitDirectoryProvider +from dodal.common.visit import ( + LocalDirectoryServiceClient, + StaticVisitPathProvider, +) from dodal.devices.i18.diode import Diode from dodal.devices.i18.KBMirror import KBMirror from dodal.devices.i18.sim_detector import SimDetector from dodal.devices.i18.sim_raster_stage import RasterStage from dodal.devices.i18.table import Table -from dodal.devices.i22.dcm import DoubleCrystalMonochromator +from dodal.devices.i22.dcm import CrystalMetadata, DoubleCrystalMonochromator from dodal.devices.slits import Slits from dodal.devices.synchrotron import Synchrotron from dodal.devices.tetramm import TetrammDetector @@ -39,11 +43,11 @@ # Communication with GDA is also WIP so for now we determine an arbitrary scan number # locally and write the commissioning directory. The scan number is not guaranteed to # be unique and the data is at risk - this configuration is for testing only. -set_directory_provider( - StaticVisitDirectoryProvider( +set_path_provider( + StaticVisitPathProvider( BL, Path("/dls/i18/data/2024/cm37264-2/bluesky"), - client=DirectoryServiceClient("http://i18-control:8088/api"), + client=LocalDirectoryServiceClient(), ) ) @@ -101,7 +105,7 @@ def panda1( "-MO-PANDA-01:", wait_for_connection, fake_with_ophyd_sim, - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -161,7 +165,7 @@ def i0( wait_for_connection, fake_with_ophyd_sim, type="Cividec Diamond XBPM", - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -177,7 +181,7 @@ def it( wait_for_connection, fake_with_ophyd_sim, type="Tetramm", - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) diff --git a/src/dodal/devices/i18/sim_detector.py b/src/dodal/devices/i18/sim_detector.py index 2e5d074814..2053cbca5f 100644 --- a/src/dodal/devices/i18/sim_detector.py +++ b/src/dodal/devices/i18/sim_detector.py @@ -1,5 +1,5 @@ -from ophyd_async.epics.motion import Motor -from ophyd_async.sim import PatternGenerator +from ophyd_async.epics import Motor +from ophyd_async.sim.demo import PatternGenerator class SimDetector: diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index b29134c66a..5c5c21c1a6 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -2,7 +2,7 @@ from ophyd_async.core import ( StandardReadable, ) -from ophyd_async.epics.motion import Motor +from ophyd_async.epics import Motor class Table(StandardReadable, Movable): From 2aa12554a1dc0b7cb5e394211caba8e52f50d763 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Fri, 13 Sep 2024 09:02:30 +0000 Subject: [PATCH 13/93] remove the wrong panda import --- src/dodal/beamlines/i18.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 9e9aee8c1b..22bf99492e 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -2,7 +2,6 @@ from pathlib import Path from ophyd_async.fastcs.panda import HDFPanda -from ophyd_async.panda import HDFPanda from dodal.common.beamlines.beamline_utils import ( device_instantiation, From 378b978477920798ca463e88c160ffd186a3dad7 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Fri, 13 Sep 2024 13:24:47 +0000 Subject: [PATCH 14/93] fix epics imports --- src/dodal/devices/i18/sim_detector.py | 2 +- src/dodal/devices/i18/table.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dodal/devices/i18/sim_detector.py b/src/dodal/devices/i18/sim_detector.py index 2053cbca5f..69ccbeead7 100644 --- a/src/dodal/devices/i18/sim_detector.py +++ b/src/dodal/devices/i18/sim_detector.py @@ -1,4 +1,4 @@ -from ophyd_async.epics import Motor +from ophyd_async.epics.motor import Motor from ophyd_async.sim.demo import PatternGenerator diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index 5c5c21c1a6..1782b441bd 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -2,7 +2,7 @@ from ophyd_async.core import ( StandardReadable, ) -from ophyd_async.epics import Motor +from ophyd_async.epics.motor import Motor class Table(StandardReadable, Movable): From d2a5c141aa61f6361364b29776e4f14eaebd9651 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Fri, 13 Sep 2024 13:26:55 +0000 Subject: [PATCH 15/93] fix motor --- src/dodal/devices/i18/sim_raster_stage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dodal/devices/i18/sim_raster_stage.py b/src/dodal/devices/i18/sim_raster_stage.py index c2fc598bbe..a763c1144f 100644 --- a/src/dodal/devices/i18/sim_raster_stage.py +++ b/src/dodal/devices/i18/sim_raster_stage.py @@ -1,6 +1,6 @@ from bluesky.protocols import Movable from ophyd_async.core import StandardReadable -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor class RasterStage(StandardReadable, Movable): From a01e7f587b0586fc2b53c378853b4256a4cc20a5 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 23 Sep 2024 15:26:48 +0100 Subject: [PATCH 16/93] add a set method --- src/dodal/devices/i18/KBMirror.py | 3 +++ src/dodal/devices/i18/sim_raster_stage.py | 7 ++++++- src/dodal/devices/i18/table.py | 3 +++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/dodal/devices/i18/KBMirror.py b/src/dodal/devices/i18/KBMirror.py index 36bf002489..be6705b2f4 100644 --- a/src/dodal/devices/i18/KBMirror.py +++ b/src/dodal/devices/i18/KBMirror.py @@ -20,3 +20,6 @@ def __init__( self.curve = epics_signal_rw(float, prefix + "CURVE") self.ellip = epics_signal_rw(float, prefix + "ELLIP") super().__init__(name=name) + + async def set(self, value: float): + await self.x.set(value) diff --git a/src/dodal/devices/i18/sim_raster_stage.py b/src/dodal/devices/i18/sim_raster_stage.py index a763c1144f..19200a1719 100644 --- a/src/dodal/devices/i18/sim_raster_stage.py +++ b/src/dodal/devices/i18/sim_raster_stage.py @@ -1,5 +1,7 @@ from bluesky.protocols import Movable -from ophyd_async.core import StandardReadable +from ophyd_async.core import ( + StandardReadable, +) from ophyd_async.epics.motor import Motor @@ -9,3 +11,6 @@ def __init__(self, prefix: str, name: str = "") -> None: self.offset_in_mm = Motor(prefix + "M1") self.perp_in_mm = Motor(prefix + "M2") super().__init__(name) + + async def set(self, value: float): + await self.offset_in_mm.set(value) diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index 1782b441bd..71b17798fa 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -12,3 +12,6 @@ def __init__(self, motion_prefix: str, prefix: str = "", name: str = "") -> None self.y = Motor(motion_prefix + "Y") self.z = Motor(motion_prefix + "Z") self.theta = Motor(motion_prefix + "THETA") + + async def set(self, value: float, wait: bool = False): + await self.x.set(value) From e97bbaeec3edd6b58f1d463507d015f9d44d8d26 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 23 Sep 2024 15:02:59 +0000 Subject: [PATCH 17/93] add set wrap asyncstatus --- src/dodal/devices/i18/KBMirror.py | 19 ++++++++++++++----- src/dodal/devices/i18/sim_raster_stage.py | 2 ++ src/dodal/devices/i18/table.py | 4 +++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/dodal/devices/i18/KBMirror.py b/src/dodal/devices/i18/KBMirror.py index be6705b2f4..640f2c3366 100644 --- a/src/dodal/devices/i18/KBMirror.py +++ b/src/dodal/devices/i18/KBMirror.py @@ -1,10 +1,17 @@ +from dataclasses import dataclass + from bluesky.protocols import Movable -from ophyd_async.core import ( - StandardReadable, -) +from ophyd_async.core import AsyncStatus, StandardReadable from ophyd_async.epics.signal import epics_signal_rw +# todo might make a class for common types of movement and use it as a value type +@dataclass +class XYPosition: + x: float + y: float + + class KBMirror(StandardReadable, Movable): def __init__( self, @@ -21,5 +28,7 @@ def __init__( self.ellip = epics_signal_rw(float, prefix + "ELLIP") super().__init__(name=name) - async def set(self, value: float): - await self.x.set(value) + @AsyncStatus.wrap + async def set(self, value: XYPosition): + self.x.set(value.x) + self.y.set(value.y) diff --git a/src/dodal/devices/i18/sim_raster_stage.py b/src/dodal/devices/i18/sim_raster_stage.py index 19200a1719..1b7016be45 100644 --- a/src/dodal/devices/i18/sim_raster_stage.py +++ b/src/dodal/devices/i18/sim_raster_stage.py @@ -1,5 +1,6 @@ from bluesky.protocols import Movable from ophyd_async.core import ( + AsyncStatus, StandardReadable, ) from ophyd_async.epics.motor import Motor @@ -12,5 +13,6 @@ def __init__(self, prefix: str, name: str = "") -> None: self.perp_in_mm = Motor(prefix + "M2") super().__init__(name) + @AsyncStatus.wrap async def set(self, value: float): await self.offset_in_mm.set(value) diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index 71b17798fa..81b19abb1e 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -1,5 +1,6 @@ from bluesky.protocols import Movable from ophyd_async.core import ( + AsyncStatus, StandardReadable, ) from ophyd_async.epics.motor import Motor @@ -13,5 +14,6 @@ def __init__(self, motion_prefix: str, prefix: str = "", name: str = "") -> None self.z = Motor(motion_prefix + "Z") self.theta = Motor(motion_prefix + "THETA") - async def set(self, value: float, wait: bool = False): + @AsyncStatus.wrap + async def set(self, value: float): await self.x.set(value) From 17924825e8a3a68ff67c4e7c046002dfe4ec9e09 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 23 Sep 2024 15:31:25 +0000 Subject: [PATCH 18/93] remove the Movable interface --- src/dodal/devices/i18/KBMirror.py | 5 ++--- src/dodal/devices/i18/sim_raster_stage.py | 20 ++++++++++++++------ src/dodal/devices/i18/table.py | 16 ++++++++++++---- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/dodal/devices/i18/KBMirror.py b/src/dodal/devices/i18/KBMirror.py index 640f2c3366..ad71dd7bf3 100644 --- a/src/dodal/devices/i18/KBMirror.py +++ b/src/dodal/devices/i18/KBMirror.py @@ -1,6 +1,5 @@ from dataclasses import dataclass -from bluesky.protocols import Movable from ophyd_async.core import AsyncStatus, StandardReadable from ophyd_async.epics.signal import epics_signal_rw @@ -12,7 +11,7 @@ class XYPosition: y: float -class KBMirror(StandardReadable, Movable): +class KBMirror(StandardReadable): def __init__( self, prefix: str, @@ -29,6 +28,6 @@ def __init__( super().__init__(name=name) @AsyncStatus.wrap - async def set(self, value: XYPosition): + async def set_xy(self, value: XYPosition): self.x.set(value.x) self.y.set(value.y) diff --git a/src/dodal/devices/i18/sim_raster_stage.py b/src/dodal/devices/i18/sim_raster_stage.py index 1b7016be45..c25e2243df 100644 --- a/src/dodal/devices/i18/sim_raster_stage.py +++ b/src/dodal/devices/i18/sim_raster_stage.py @@ -1,4 +1,5 @@ -from bluesky.protocols import Movable +from dataclasses import dataclass + from ophyd_async.core import ( AsyncStatus, StandardReadable, @@ -6,13 +7,20 @@ from ophyd_async.epics.motor import Motor -class RasterStage(StandardReadable, Movable): +@dataclass +class XYPosition: + x: float + y: float + + +class RasterStage(StandardReadable): def __init__(self, prefix: str, name: str = "") -> None: with self.add_children_as_readables(): - self.offset_in_mm = Motor(prefix + "M1") - self.perp_in_mm = Motor(prefix + "M2") + self.x = Motor(prefix + "M1") + self.y = Motor(prefix + "M2") super().__init__(name) @AsyncStatus.wrap - async def set(self, value: float): - await self.offset_in_mm.set(value) + async def set_xy(self, value: XYPosition): + self.x.set(value.x) + self.y.set(value.y) diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index 81b19abb1e..c61c1e7c99 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -1,4 +1,5 @@ -from bluesky.protocols import Movable +from dataclasses import dataclass + from ophyd_async.core import ( AsyncStatus, StandardReadable, @@ -6,7 +7,13 @@ from ophyd_async.epics.motor import Motor -class Table(StandardReadable, Movable): +@dataclass +class XYPosition: + x: float + y: float + + +class Table(StandardReadable): def __init__(self, motion_prefix: str, prefix: str = "", name: str = "") -> None: with self.add_children_as_readables(): self.x = Motor(motion_prefix + "X") @@ -15,5 +22,6 @@ def __init__(self, motion_prefix: str, prefix: str = "", name: str = "") -> None self.theta = Motor(motion_prefix + "THETA") @AsyncStatus.wrap - async def set(self, value: float): - await self.x.set(value) + async def set_xy(self, value: XYPosition): + self.x.set(value.x) + self.y.set(value.y) From 1502f899acdd9c555da7edc4d837df1b9a358d3a Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 30 Sep 2024 13:17:33 +0100 Subject: [PATCH 19/93] table error still --- src/dodal/devices/i18/table.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index c61c1e7c99..1995d35a82 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -14,12 +14,12 @@ class XYPosition: class Table(StandardReadable): - def __init__(self, motion_prefix: str, prefix: str = "", name: str = "") -> None: + def __init__(self, prefix: str = "", name: str = "") -> None: with self.add_children_as_readables(): - self.x = Motor(motion_prefix + "X") - self.y = Motor(motion_prefix + "Y") - self.z = Motor(motion_prefix + "Z") - self.theta = Motor(motion_prefix + "THETA") + self.x = Motor(prefix + "X") + self.y = Motor(prefix + "Y") + self.z = Motor(prefix + "Z") + self.theta = Motor(prefix + "THETA") @AsyncStatus.wrap async def set_xy(self, value: XYPosition): From 5b760eba4eb1405d3f887eb74ff12fcae17b5a90 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 30 Sep 2024 15:21:14 +0100 Subject: [PATCH 20/93] devices cleared up --- src/dodal/beamlines/i18.py | 4 ++++ src/dodal/devices/i18/table.py | 1 + 2 files changed, 5 insertions(+) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 22bf99492e..360f96ca22 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -63,6 +63,7 @@ def synchrotron( ) +@skip_device() def undulator( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, @@ -231,6 +232,9 @@ def table(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) - ) +# note this is a mock table, not sure how does it relate to the real Table, +# maybe just a fake option in instantiation is needed +@skip_device() def raster_stage( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False ) -> RasterStage: diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index 1995d35a82..f86dbc0530 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -20,6 +20,7 @@ def __init__(self, prefix: str = "", name: str = "") -> None: self.y = Motor(prefix + "Y") self.z = Motor(prefix + "Z") self.theta = Motor(prefix + "THETA") + super().__init__(name=name) @AsyncStatus.wrap async def set_xy(self, value: XYPosition): From a7120089f365a3573c3462c6204d112495467154 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Wed, 2 Oct 2024 15:20:57 +0100 Subject: [PATCH 21/93] add testing to the i18 devices --- tests/devices/i18/test_kb_mirror.py | 65 ++++++++++++++++++++++++++ tests/devices/i18/test_sim_detector.py | 35 ++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 tests/devices/i18/test_kb_mirror.py create mode 100644 tests/devices/i18/test_sim_detector.py diff --git a/tests/devices/i18/test_kb_mirror.py b/tests/devices/i18/test_kb_mirror.py new file mode 100644 index 0000000000..d0dc3d5e7b --- /dev/null +++ b/tests/devices/i18/test_kb_mirror.py @@ -0,0 +1,65 @@ +from unittest.mock import ANY + +import pytest +from ophyd_async.core import DeviceCollector, set_mock_value + +from dodal.devices.i18.KBMirror import KBMirror, XYPosition + + +@pytest.fixture +async def kbmirror() -> KBMirror: + """Fixture to set up a mock KBMirror device using DeviceCollector.""" + async with DeviceCollector(mock=True): + kbmirror = KBMirror(prefix="MIRROR:") + return kbmirror + + +async def test_setting_xy_position_kbmirror(kbmirror: KBMirror): + """ + Test setting x and y positions on the KBMirror using the ophyd_async mock tools. + """ + # Mock the initial values of the x and y signals + set_mock_value(kbmirror.x, 0.0) + set_mock_value(kbmirror.y, 0.0) + + # Create a position object + position = XYPosition(x=1.23, y=4.56) + + # Call set_xy to update the position + await kbmirror.set_xy(position) + + reading = await kbmirror.read() + expected_reading = { + "kbmirror-y": { + "value": 4.56, + "timestamp": ANY, + "alarm_severity": 0, + }, + "kbmirror-bend1": { + "value": 0.0, + "timestamp": ANY, + "alarm_severity": 0, + }, + "kbmirror-ellip": { + "value": 0.0, + "timestamp": ANY, + "alarm_severity": 0, + }, + "kbmirror-x": { + "value": 1.23, + "timestamp": ANY, + "alarm_severity": 0, + }, + "kbmirror-bend2": { + "value": 0.0, + "timestamp": ANY, + "alarm_severity": 0, + }, + "kbmirror-curve": { + "value": 0.0, + "timestamp": ANY, + "alarm_severity": 0, + }, + } + + assert reading == expected_reading diff --git a/tests/devices/i18/test_sim_detector.py b/tests/devices/i18/test_sim_detector.py new file mode 100644 index 0000000000..684f80f903 --- /dev/null +++ b/tests/devices/i18/test_sim_detector.py @@ -0,0 +1,35 @@ +from unittest.mock import Mock + +from dodal.devices.i18.sim_detector import SimDetector + + +def test_simdetector_read(): + """ + Test SimDetector.read() returns a pattern generator value in the correct structure. + """ + motor_mock = Mock() + pattern_generator_mock = Mock() + pattern_generator_mock.return_value = {"value": pattern_generator_mock} + + detector = SimDetector(name="detector1", motor=motor_mock, motor_field="position") + detector.pattern_generator = ( + pattern_generator_mock # Replace actual generator with mock for testing + ) + + result = detector.read() + + # Check the structure and value + assert result == {"detector1": {"value": pattern_generator_mock}} + + +def test_simdetector_describe(): + """ + Test SimDetector.describe() returns correct metadata. + """ + motor_mock = Mock() + detector = SimDetector(name="detector1", motor=motor_mock, motor_field="position") + + result = detector.describe() + + # Check the structure and value + assert result == {"detector1": {"source": "synthetic", "dtype": "number"}} From b0583a8f105c38e618af4396b9eeb74d5e54f49a Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 7 Oct 2024 18:02:05 +0100 Subject: [PATCH 22/93] respond to feedback --- src/dodal/beamlines/i18.py | 27 ++++++++++++++--------- src/dodal/devices/i18/IdGapLookupTable.py | 24 -------------------- src/dodal/devices/i18/KBMirror.py | 1 - 3 files changed, 17 insertions(+), 35 deletions(-) delete mode 100644 src/dodal/devices/i18/IdGapLookupTable.py diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 360f96ca22..8387420b70 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -29,9 +29,6 @@ from dodal.log import set_beamline as set_log_beamline from dodal.utils import BeamlinePrefix, get_beamline_name, skip_device -# Make sure EPICS_CA_SERVER_PORT is set to correct value (6064 for DLS sim area detector and motors, 5064 on beamlines) -os.environ["EPICS_CA_SERVER_PORT"] = "6064" - BL = get_beamline_name("i18") set_log_beamline(BL) set_utils_beamline(BL) @@ -109,15 +106,9 @@ def panda1( ) -# NOTE: the reason for skipping is that the odin detectors are not yet supported -@skip_device() -def xspress3( +def old_xspress3( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False ) -> Xspress3: - """ - 16 channels Xspress3 detector - """ - return device_instantiation( Xspress3, prefix="-EA-XSP-02:", @@ -128,6 +119,22 @@ def xspress3( ) +# NOTE: the reason for skipping is that the odin detectors are not yet supported +# There is a controls project in the works, not ready anytime soon +@skip_device() +def xspress3_odin( + wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False +) -> Xspress3: + return device_instantiation( + Xspress3, + prefix="-EA-XSP-03:", + name="Xspress3", + num_channels=4, + wait=wait_for_connection, + fake=fake_with_ophyd_sim, + ) + + crystal_1_metadata = CrystalMetadata("Si111") crystal_2_metadata = CrystalMetadata("Si111") diff --git a/src/dodal/devices/i18/IdGapLookupTable.py b/src/dodal/devices/i18/IdGapLookupTable.py deleted file mode 100644 index a765f3b397..0000000000 --- a/src/dodal/devices/i18/IdGapLookupTable.py +++ /dev/null @@ -1,24 +0,0 @@ -from bluesky.protocols import Movable -from ophyd_async.core import ( - AsyncStatus, - StandardReadable, -) - -from dodal.log import LOGGER - - -class LookupTable(StandardReadable, Movable): - def __init__(self, prefix: str, name: str = ""): - with self.add_children_as_readables(): - # self.actual_transmission = epics_signal_r(float, prefix + "MATCH") - print("test") - - super().__init__(name) - - @AsyncStatus.wrap - async def set(self, value: float): - """ - value: transmission - """ - LOGGER.debug("Updating the lookup table ") - pass diff --git a/src/dodal/devices/i18/KBMirror.py b/src/dodal/devices/i18/KBMirror.py index ad71dd7bf3..1c78939dad 100644 --- a/src/dodal/devices/i18/KBMirror.py +++ b/src/dodal/devices/i18/KBMirror.py @@ -4,7 +4,6 @@ from ophyd_async.epics.signal import epics_signal_rw -# todo might make a class for common types of movement and use it as a value type @dataclass class XYPosition: x: float From 52f7b71ad7de9c3b0a3a7350c66770e1439179b2 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 7 Oct 2024 18:06:26 +0100 Subject: [PATCH 23/93] add testing for the crystal metadata --- src/dodal/beamlines/i18.py | 1 - src/dodal/devices/i18/diode.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 8387420b70..863acc83e7 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -1,4 +1,3 @@ -import os from pathlib import Path from ophyd_async.fastcs.panda import HDFPanda diff --git a/src/dodal/devices/i18/diode.py b/src/dodal/devices/i18/diode.py index 55d7648fad..42cd6a392d 100644 --- a/src/dodal/devices/i18/diode.py +++ b/src/dodal/devices/i18/diode.py @@ -16,5 +16,3 @@ def __init__( super().__init__(name=name) - -# todo should add 'read' override? From ccd69e5b7d697f29dc9476575115d9eb931b27e8 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 7 Oct 2024 17:18:00 +0000 Subject: [PATCH 24/93] ruff fix --- src/dodal/devices/i18/diode.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dodal/devices/i18/diode.py b/src/dodal/devices/i18/diode.py index 42cd6a392d..85bbd35843 100644 --- a/src/dodal/devices/i18/diode.py +++ b/src/dodal/devices/i18/diode.py @@ -15,4 +15,3 @@ def __init__( self.signal = epics_signal_r(float, prefix + "B:DIODE:I") super().__init__(name=name) - From 0f53ba606e470c8227050d80049021fd54c316fb Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 15 Oct 2024 12:44:35 +0100 Subject: [PATCH 25/93] respond to feedback --- src/dodal/beamlines/i18.py | 42 +++++++++++++++++------ src/dodal/devices/i18/KBMirror.py | 14 +++++--- src/dodal/devices/i18/sim_raster_stage.py | 26 -------------- src/dodal/devices/i18/table.py | 10 +++--- src/dodal/devices/i18/thor_labs_stag.py | 24 +++++++++++++ tests/devices/i18/test_kb_mirror.py | 2 +- 6 files changed, 70 insertions(+), 48 deletions(-) delete mode 100644 src/dodal/devices/i18/sim_raster_stage.py create mode 100644 src/dodal/devices/i18/thor_labs_stag.py diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 863acc83e7..a6f4169e5a 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -17,7 +17,6 @@ from dodal.devices.i18.diode import Diode from dodal.devices.i18.KBMirror import KBMirror from dodal.devices.i18.sim_detector import SimDetector -from dodal.devices.i18.sim_raster_stage import RasterStage from dodal.devices.i18.table import Table from dodal.devices.i22.dcm import CrystalMetadata, DoubleCrystalMonochromator from dodal.devices.slits import Slits @@ -59,7 +58,7 @@ def synchrotron( ) -@skip_device() +@skip_device("not ready yet") def undulator( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, @@ -118,9 +117,11 @@ def old_xspress3( ) -# NOTE: the reason for skipping is that the odin detectors are not yet supported -# There is a controls project in the works, not ready anytime soon -@skip_device() +@skip_device(""" + odin detectors are not yet supported. + There is a controls project in the works, + not ready anytime soon + """) def xspress3_odin( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False ) -> Xspress3: @@ -228,7 +229,9 @@ def diode(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) - ) -def table(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -> Table: +def main_table( + wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False +) -> Table: return device_instantiation( Table, "table", @@ -238,14 +241,31 @@ def table(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) - ) -# note this is a mock table, not sure how does it relate to the real Table, -# maybe just a fake option in instantiation is needed -@skip_device() +def thor_labs_table( + wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False +) -> Table: + return device_instantiation( + Table, + "table", + "-MO-TABLE-02:", + wait_for_connection, + fake_with_ophyd_sim, + ) + + +# SIMULATED DEVICES + + +@skip_device( + """ + this is a mock table, not sure how does it relate to the real Table, maybe just a fake option in instantiation is needed + """ +) def raster_stage( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False -) -> RasterStage: +) -> Table: return device_instantiation( - RasterStage, + Table, "raster_stage", "-MO-SIM-01:", wait_for_connection, diff --git a/src/dodal/devices/i18/KBMirror.py b/src/dodal/devices/i18/KBMirror.py index 1c78939dad..1903a1232b 100644 --- a/src/dodal/devices/i18/KBMirror.py +++ b/src/dodal/devices/i18/KBMirror.py @@ -1,16 +1,15 @@ -from dataclasses import dataclass - +from bluesky.protocols import Movable from ophyd_async.core import AsyncStatus, StandardReadable from ophyd_async.epics.signal import epics_signal_rw +from pydantic import BaseModel -@dataclass -class XYPosition: +class XYPosition(BaseModel): x: float y: float -class KBMirror(StandardReadable): +class KBMirror(StandardReadable, Movable): def __init__( self, prefix: str, @@ -30,3 +29,8 @@ def __init__( async def set_xy(self, value: XYPosition): self.x.set(value.x) self.y.set(value.y) + + @AsyncStatus.wrap + async def set(self, value: XYPosition): + self.x.set(value.x) + self.y.set(value.y) diff --git a/src/dodal/devices/i18/sim_raster_stage.py b/src/dodal/devices/i18/sim_raster_stage.py deleted file mode 100644 index c25e2243df..0000000000 --- a/src/dodal/devices/i18/sim_raster_stage.py +++ /dev/null @@ -1,26 +0,0 @@ -from dataclasses import dataclass - -from ophyd_async.core import ( - AsyncStatus, - StandardReadable, -) -from ophyd_async.epics.motor import Motor - - -@dataclass -class XYPosition: - x: float - y: float - - -class RasterStage(StandardReadable): - def __init__(self, prefix: str, name: str = "") -> None: - with self.add_children_as_readables(): - self.x = Motor(prefix + "M1") - self.y = Motor(prefix + "M2") - super().__init__(name) - - @AsyncStatus.wrap - async def set_xy(self, value: XYPosition): - self.x.set(value.x) - self.y.set(value.y) diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index f86dbc0530..1ebb40fa34 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -1,16 +1,16 @@ -from dataclasses import dataclass - from ophyd_async.core import ( AsyncStatus, StandardReadable, ) from ophyd_async.epics.motor import Motor +from pydantic import BaseModel -@dataclass -class XYPosition: +class Four_D_Position(BaseModel): x: float y: float + z: float + theta: float class Table(StandardReadable): @@ -23,6 +23,6 @@ def __init__(self, prefix: str = "", name: str = "") -> None: super().__init__(name=name) @AsyncStatus.wrap - async def set_xy(self, value: XYPosition): + async def set(self, value: Four_D_Position): self.x.set(value.x) self.y.set(value.y) diff --git a/src/dodal/devices/i18/thor_labs_stag.py b/src/dodal/devices/i18/thor_labs_stag.py new file mode 100644 index 0000000000..b27aec477a --- /dev/null +++ b/src/dodal/devices/i18/thor_labs_stag.py @@ -0,0 +1,24 @@ +from ophyd_async.core import ( + AsyncStatus, + StandardReadable, +) +from ophyd_async.epics.motor import Motor +from pydantic import BaseModel + + +class XYPosition(BaseModel): + x: float + y: float + + +class Table(StandardReadable): + def __init__(self, prefix: str = "", name: str = "") -> None: + with self.add_children_as_readables(): + self.x = Motor(prefix + "X") + self.y = Motor(prefix + "Y") + super().__init__(name=name) + + @AsyncStatus.wrap + async def set(self, value: XYPosition): + self.x.set(value.x) + self.y.set(value.y) diff --git a/tests/devices/i18/test_kb_mirror.py b/tests/devices/i18/test_kb_mirror.py index d0dc3d5e7b..4b2ed5a801 100644 --- a/tests/devices/i18/test_kb_mirror.py +++ b/tests/devices/i18/test_kb_mirror.py @@ -26,7 +26,7 @@ async def test_setting_xy_position_kbmirror(kbmirror: KBMirror): position = XYPosition(x=1.23, y=4.56) # Call set_xy to update the position - await kbmirror.set_xy(position) + await kbmirror.set(position) reading = await kbmirror.read() expected_reading = { From 68c0a7a871b91fc822725071b043bffe60f53dcc Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Wed, 16 Oct 2024 10:44:41 +0000 Subject: [PATCH 26/93] cancel the skip_device comment --- src/dodal/beamlines/i18.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index a6f4169e5a..7bb3bb0cd4 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -58,7 +58,8 @@ def synchrotron( ) -@skip_device("not ready yet") +# not ready yet +@skip_device() def undulator( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, @@ -117,11 +118,10 @@ def old_xspress3( ) -@skip_device(""" - odin detectors are not yet supported. - There is a controls project in the works, - not ready anytime soon - """) +# odin detectors are not yet supported. +# There is a controls project in the works, +# not ready anytime soon +@skip_device() def xspress3_odin( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False ) -> Xspress3: @@ -256,11 +256,8 @@ def thor_labs_table( # SIMULATED DEVICES -@skip_device( - """ - this is a mock table, not sure how does it relate to the real Table, maybe just a fake option in instantiation is needed - """ -) +# this is a mock table, not sure how does it relate to the real Table, maybe just a fake option in instantiation is needed +@skip_device() def raster_stage( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False ) -> Table: From a8a020b0c2cf3ff8720ab36a309ce34766e184fc Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Wed, 16 Oct 2024 12:02:55 +0000 Subject: [PATCH 27/93] cancel the crystal metadata --- src/dodal/beamlines/i18.py | 1 - src/dodal/beamlines/p38.py | 5 ----- src/dodal/devices/i22/dcm.py | 15 +++++++++++++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 7bb3bb0cd4..d8c5214430 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -9,7 +9,6 @@ ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.common.beamlines.device_helpers import numbered_slits -from dodal.common.crystal_metadata import CrystalMetadata from dodal.common.visit import ( LocalDirectoryServiceClient, StaticVisitPathProvider, diff --git a/src/dodal/beamlines/p38.py b/src/dodal/beamlines/p38.py index 2141e720ff..30cb2e5a99 100644 --- a/src/dodal/beamlines/p38.py +++ b/src/dodal/beamlines/p38.py @@ -233,17 +233,12 @@ def dcm( fake_with_ophyd_sim, bl_prefix=False, temperature_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-DI-DCM-01:", -<<<<<<< HEAD crystal_1_metadata=make_crystal_metadata_from_material( MaterialsEnum.Si, (1, 1, 1) ), crystal_2_metadata=make_crystal_metadata_from_material( MaterialsEnum.Si, (1, 1, 1) ), -======= - crystal_1_metadata=CrystalMetadata("Si111"), - crystal_2_metadata=CrystalMetadata("Si111"), ->>>>>>> 7c02f1411 (adapt the tests) ) diff --git a/src/dodal/devices/i22/dcm.py b/src/dodal/devices/i22/dcm.py index 3c84c3e678..e6d5fa3c89 100644 --- a/src/dodal/devices/i22/dcm.py +++ b/src/dodal/devices/i22/dcm.py @@ -1,5 +1,7 @@ import time from collections.abc import Sequence +from dataclasses import dataclass +from typing import Literal import numpy as np from bluesky.protocols import Reading @@ -20,6 +22,19 @@ _CONVERSION_CONSTANT = 12.3984 +@dataclass(frozen=True, unsafe_hash=True) +class CrystalMetadata: + """ + Metadata used in the NeXus format, + see https://manual.nexusformat.org/classes/base_classes/NXcrystal.html + """ + + usage: Literal["Bragg", "Laue"] | None = None + type: str | None = None + reflection: tuple[int, int, int] | None = None + d_spacing: tuple[float, str] | None = None + + class DoubleCrystalMonochromator(StandardReadable): """ A double crystal monochromator (DCM), used to select the energy of the beam. From 314edda1180f569cfd47ba6aa3d26ae5796e186d Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Wed, 16 Oct 2024 12:14:38 +0000 Subject: [PATCH 28/93] fix imports again --- src/dodal/beamlines/i18.py | 21 ++++++++++++++++----- src/dodal/devices/i22/dcm.py | 2 -- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index d8c5214430..5ecbdd80be 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -134,11 +134,22 @@ def xspress3_odin( ) -crystal_1_metadata = CrystalMetadata("Si111") -crystal_2_metadata = CrystalMetadata("Si111") - -_unused_crystal_metadata_1 = CrystalMetadata("Si311") -_unused_crystal_metadata_2 = CrystalMetadata("Si333") +crystal_1_metadata = ( + CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), + ), +) +crystal_2_metadata = ( + CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), + ), +) def dcm( diff --git a/src/dodal/devices/i22/dcm.py b/src/dodal/devices/i22/dcm.py index e6d5fa3c89..65c1383729 100644 --- a/src/dodal/devices/i22/dcm.py +++ b/src/dodal/devices/i22/dcm.py @@ -15,8 +15,6 @@ from ophyd_async.epics.core import epics_signal_r from ophyd_async.epics.motor import Motor -from dodal.common.crystal_metadata import CrystalMetadata - # Conversion constant for energy and wavelength, taken from the X-Ray data booklet # Converts between energy in KeV and wavelength in angstrom _CONVERSION_CONSTANT = 12.3984 From 9d522caf0b0c0ebae7ce5486247e4f8505d47edc Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 29 Oct 2024 09:55:18 +0000 Subject: [PATCH 29/93] fix dcm i18 test --- src/dodal/beamlines/i18.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 5ecbdd80be..a9bcefb381 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -134,21 +134,18 @@ def xspress3_odin( ) -crystal_1_metadata = ( - CrystalMetadata( - usage="Bragg", - type="silicon", - reflection=(1, 1, 1), - d_spacing=(3.13475, "nm"), - ), +crystal_1_metadata = CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), ) -crystal_2_metadata = ( - CrystalMetadata( - usage="Bragg", - type="silicon", - reflection=(1, 1, 1), - d_spacing=(3.13475, "nm"), - ), + +crystal_2_metadata = CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), ) @@ -159,11 +156,10 @@ def dcm( return device_instantiation( DoubleCrystalMonochromator, "dcm", - "", + f"{BeamlinePrefix(BL).beamline_prefix}-MO-DCM-01:", wait_for_connection, fake_with_ophyd_sim, bl_prefix=False, - motion_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-MO-DCM-01:", temperature_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-DI-DCM-01:", crystal_1_metadata=crystal_1_metadata, crystal_2_metadata=crystal_2_metadata, From 9376dd1bb538dab9f0b0698e5798d95e17d7fb92 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 11 Nov 2024 12:10:21 +0000 Subject: [PATCH 30/93] fix lint --- src/dodal/devices/i22/dcm.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dodal/devices/i22/dcm.py b/src/dodal/devices/i22/dcm.py index 65c1383729..8f00d42037 100644 --- a/src/dodal/devices/i22/dcm.py +++ b/src/dodal/devices/i22/dcm.py @@ -1,5 +1,4 @@ import time -from collections.abc import Sequence from dataclasses import dataclass from typing import Literal From 67b43cdbb90d32e6ee30bbbc5e48296ca650c352 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 11 Nov 2024 12:17:50 +0000 Subject: [PATCH 31/93] fix import --- src/dodal/devices/i22/dcm.py | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/dodal/devices/i22/dcm.py b/src/dodal/devices/i22/dcm.py index 8f00d42037..58180ad9b6 100644 --- a/src/dodal/devices/i22/dcm.py +++ b/src/dodal/devices/i22/dcm.py @@ -1,6 +1,4 @@ import time -from dataclasses import dataclass -from typing import Literal import numpy as np from bluesky.protocols import Reading @@ -14,24 +12,13 @@ from ophyd_async.epics.core import epics_signal_r from ophyd_async.epics.motor import Motor +from dodal.common.crystal_metadata import CrystalMetadata + # Conversion constant for energy and wavelength, taken from the X-Ray data booklet # Converts between energy in KeV and wavelength in angstrom _CONVERSION_CONSTANT = 12.3984 -@dataclass(frozen=True, unsafe_hash=True) -class CrystalMetadata: - """ - Metadata used in the NeXus format, - see https://manual.nexusformat.org/classes/base_classes/NXcrystal.html - """ - - usage: Literal["Bragg", "Laue"] | None = None - type: str | None = None - reflection: tuple[int, int, int] | None = None - d_spacing: tuple[float, str] | None = None - - class DoubleCrystalMonochromator(StandardReadable): """ A double crystal monochromator (DCM), used to select the energy of the beam. From f5f0618d19536b7119bd09a1115cf5adb5e5df92 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 11 Nov 2024 12:33:14 +0000 Subject: [PATCH 32/93] add test to increase covereage --- src/dodal/devices/i18/KBMirror.py | 5 --- tests/devices/i18/test_kb_mirror.py | 2 +- tests/devices/i18/test_table.py | 50 +++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 tests/devices/i18/test_table.py diff --git a/src/dodal/devices/i18/KBMirror.py b/src/dodal/devices/i18/KBMirror.py index 1903a1232b..55d95e6018 100644 --- a/src/dodal/devices/i18/KBMirror.py +++ b/src/dodal/devices/i18/KBMirror.py @@ -25,11 +25,6 @@ def __init__( self.ellip = epics_signal_rw(float, prefix + "ELLIP") super().__init__(name=name) - @AsyncStatus.wrap - async def set_xy(self, value: XYPosition): - self.x.set(value.x) - self.y.set(value.y) - @AsyncStatus.wrap async def set(self, value: XYPosition): self.x.set(value.x) diff --git a/tests/devices/i18/test_kb_mirror.py b/tests/devices/i18/test_kb_mirror.py index 4b2ed5a801..1eff890564 100644 --- a/tests/devices/i18/test_kb_mirror.py +++ b/tests/devices/i18/test_kb_mirror.py @@ -25,7 +25,7 @@ async def test_setting_xy_position_kbmirror(kbmirror: KBMirror): # Create a position object position = XYPosition(x=1.23, y=4.56) - # Call set_xy to update the position + # Call set to update the position await kbmirror.set(position) reading = await kbmirror.read() diff --git a/tests/devices/i18/test_table.py b/tests/devices/i18/test_table.py new file mode 100644 index 0000000000..900339894a --- /dev/null +++ b/tests/devices/i18/test_table.py @@ -0,0 +1,50 @@ +from unittest.mock import ANY + +import pytest +from ophyd_async.core import DeviceCollector, set_mock_value + +from dodal.devices.i18.table import Four_D_Position, Table + + +@pytest.fixture +async def table() -> Table: + """Fixture to set up a mock Table device using DeviceCollector.""" + async with DeviceCollector(mock=True): + table = Table(prefix="MIRROR:") + return table + + +async def test_setting_xy_position_table(table: Table): + """ + Test setting x and y positions on the Table using the ophyd_async mock tools. + """ + set_mock_value(table.x.user_readback, 1.23) + set_mock_value(table.y.user_readback, 4.56) + + # Create a position object + position = Four_D_Position(x=1.23, y=4.56, z=0.0, theta=0.0) + + # Call set to update the position + await table.set(position) + + reading = await table.read() + expected_reading = { + "table-y": { + "value": 4.56, + "timestamp": ANY, + "alarm_severity": 0, + }, + "table-x": { + "value": 1.23, + "timestamp": ANY, + "alarm_severity": 0, + }, + "table-theta": { + "alarm_severity": 0, + "timestamp": ANY, + "value": 0.0, + }, + "table-z": {"alarm_severity": 0, "timestamp": ANY, "value": 0.0}, + } + + assert reading == expected_reading From b808dd1aa42bd4f28c7c50c6ccc09811bc5e34e7 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Fri, 22 Nov 2024 14:44:56 +0000 Subject: [PATCH 33/93] update the imports --- src/dodal/devices/i18/KBMirror.py | 2 +- src/dodal/devices/i18/diode.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dodal/devices/i18/KBMirror.py b/src/dodal/devices/i18/KBMirror.py index 55d95e6018..baf69f67a0 100644 --- a/src/dodal/devices/i18/KBMirror.py +++ b/src/dodal/devices/i18/KBMirror.py @@ -1,6 +1,6 @@ from bluesky.protocols import Movable from ophyd_async.core import AsyncStatus, StandardReadable -from ophyd_async.epics.signal import epics_signal_rw +from ophyd_async.epics.core import epics_signal_rw from pydantic import BaseModel diff --git a/src/dodal/devices/i18/diode.py b/src/dodal/devices/i18/diode.py index 85bbd35843..f0c9526adc 100644 --- a/src/dodal/devices/i18/diode.py +++ b/src/dodal/devices/i18/diode.py @@ -1,7 +1,7 @@ from ophyd_async.core import ( StandardReadable, ) -from ophyd_async.epics.signal import epics_signal_r +from ophyd_async.epics.core import epics_signal_r class Diode(StandardReadable): From 9f2369e489dc29abbf167c4004ec0fe5aeef981e Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 25 Nov 2024 17:24:57 +0000 Subject: [PATCH 34/93] respond to feedback --- src/dodal/beamlines/i18.py | 40 ++++++++++++------------- src/dodal/beamlines/i22.py | 4 +-- src/dodal/devices/i18/sim_detector.py | 18 ----------- src/dodal/devices/i18/thor_labs_stag.py | 24 --------------- 4 files changed, 22 insertions(+), 64 deletions(-) delete mode 100644 src/dodal/devices/i18/sim_detector.py delete mode 100644 src/dodal/devices/i18/thor_labs_stag.py diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index a9bcefb381..d48b935248 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -104,13 +104,13 @@ def panda1( ) -def old_xspress3( +def xspress3( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False ) -> Xspress3: return device_instantiation( Xspress3, prefix="-EA-XSP-02:", - name="Xspress3", + name="xspress3", num_channels=16, wait=wait_for_connection, fake=fake_with_ophyd_sim, @@ -127,32 +127,31 @@ def xspress3_odin( return device_instantiation( Xspress3, prefix="-EA-XSP-03:", - name="Xspress3", + name="xspress3_odin", num_channels=4, wait=wait_for_connection, fake=fake_with_ophyd_sim, ) -crystal_1_metadata = CrystalMetadata( - usage="Bragg", - type="silicon", - reflection=(1, 1, 1), - d_spacing=(3.13475, "nm"), -) - -crystal_2_metadata = CrystalMetadata( - usage="Bragg", - type="silicon", - reflection=(1, 1, 1), - d_spacing=(3.13475, "nm"), -) - - def dcm( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, ) -> DoubleCrystalMonochromator: + crystal_1_metadata = CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), + ) + + crystal_2_metadata = CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), + ) + return device_instantiation( DoubleCrystalMonochromator, "dcm", @@ -225,10 +224,11 @@ def hfm( ) -def diode(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -> Diode: +def d7diode( + wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False +) -> Diode: return device_instantiation( Diode, - "diodad7bdiode", "-DI-PHDGN-07:", wait_for_connection, fake_with_ophyd_sim, diff --git a/src/dodal/beamlines/i22.py b/src/dodal/beamlines/i22.py index b80c12e03e..da158488f2 100644 --- a/src/dodal/beamlines/i22.py +++ b/src/dodal/beamlines/i22.py @@ -159,8 +159,8 @@ def undulator() -> Undulator: return Undulator( prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:", id_gap_lookup_table_path="/dls_sw/i22/software/daq_configuration/lookup/BeamLine_Undulator_toGap.txt", - poles=80, - length=2.0, + poles=None, # todo need to double check for the metadata to be valid + length=None, # todo need to double check for the metadata to be valid ) diff --git a/src/dodal/devices/i18/sim_detector.py b/src/dodal/devices/i18/sim_detector.py deleted file mode 100644 index 69ccbeead7..0000000000 --- a/src/dodal/devices/i18/sim_detector.py +++ /dev/null @@ -1,18 +0,0 @@ -from ophyd_async.epics.motor import Motor -from ophyd_async.sim.demo import PatternGenerator - - -class SimDetector: - def __init__(self, name: str, motor: Motor, motor_field: str): - self.name = name - self.motor = motor - self.motor_field = motor_field - self.pattern_generator = PatternGenerator( - saturation_exposure_time=0.1, detector_height=100, detector_width=100 - ) - - def read(self): - return {self.name: {"value": self.pattern_generator}} - - def describe(self): - return {self.name: {"source": "synthetic", "dtype": "number"}} diff --git a/src/dodal/devices/i18/thor_labs_stag.py b/src/dodal/devices/i18/thor_labs_stag.py deleted file mode 100644 index b27aec477a..0000000000 --- a/src/dodal/devices/i18/thor_labs_stag.py +++ /dev/null @@ -1,24 +0,0 @@ -from ophyd_async.core import ( - AsyncStatus, - StandardReadable, -) -from ophyd_async.epics.motor import Motor -from pydantic import BaseModel - - -class XYPosition(BaseModel): - x: float - y: float - - -class Table(StandardReadable): - def __init__(self, prefix: str = "", name: str = "") -> None: - with self.add_children_as_readables(): - self.x = Motor(prefix + "X") - self.y = Motor(prefix + "Y") - super().__init__(name=name) - - @AsyncStatus.wrap - async def set(self, value: XYPosition): - self.x.set(value.x) - self.y.set(value.y) From 6526c6140fcf6fe370fc64c7c6adfc15c6d9c70e Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 26 Nov 2024 15:21:30 +0000 Subject: [PATCH 35/93] delete test for sim detector (old) --- tests/devices/i18/test_sim_detector.py | 35 -------------------------- 1 file changed, 35 deletions(-) delete mode 100644 tests/devices/i18/test_sim_detector.py diff --git a/tests/devices/i18/test_sim_detector.py b/tests/devices/i18/test_sim_detector.py deleted file mode 100644 index 684f80f903..0000000000 --- a/tests/devices/i18/test_sim_detector.py +++ /dev/null @@ -1,35 +0,0 @@ -from unittest.mock import Mock - -from dodal.devices.i18.sim_detector import SimDetector - - -def test_simdetector_read(): - """ - Test SimDetector.read() returns a pattern generator value in the correct structure. - """ - motor_mock = Mock() - pattern_generator_mock = Mock() - pattern_generator_mock.return_value = {"value": pattern_generator_mock} - - detector = SimDetector(name="detector1", motor=motor_mock, motor_field="position") - detector.pattern_generator = ( - pattern_generator_mock # Replace actual generator with mock for testing - ) - - result = detector.read() - - # Check the structure and value - assert result == {"detector1": {"value": pattern_generator_mock}} - - -def test_simdetector_describe(): - """ - Test SimDetector.describe() returns correct metadata. - """ - motor_mock = Mock() - detector = SimDetector(name="detector1", motor=motor_mock, motor_field="position") - - result = detector.describe() - - # Check the structure and value - assert result == {"detector1": {"source": "synthetic", "dtype": "number"}} From 01de9714e80045c4126887a7a2f0cf1a90129264 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 26 Nov 2024 15:22:21 +0000 Subject: [PATCH 36/93] delete sim detector --- src/dodal/beamlines/i18.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index d48b935248..8b1cdc9cc1 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -15,7 +15,6 @@ ) from dodal.devices.i18.diode import Diode from dodal.devices.i18.KBMirror import KBMirror -from dodal.devices.i18.sim_detector import SimDetector from dodal.devices.i18.table import Table from dodal.devices.i22.dcm import CrystalMetadata, DoubleCrystalMonochromator from dodal.devices.slits import Slits @@ -274,13 +273,3 @@ def raster_stage( wait_for_connection, fake_with_ophyd_sim, ) - - -def sim_detector(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False): - return device_instantiation( - SimDetector, - "sim_detector", - "-MO-SIM-01:", - wait_for_connection, - fake_with_ophyd_sim, - ) From 70df1ebd984f5bd92c5a0bc7acc2b216e8a38cca Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 26 Nov 2024 16:02:51 +0000 Subject: [PATCH 37/93] add name to diode --- src/dodal/beamlines/i18.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 8b1cdc9cc1..d6336fd7e2 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -228,6 +228,7 @@ def d7diode( ) -> Diode: return device_instantiation( Diode, + "d7diode", "-DI-PHDGN-07:", wait_for_connection, fake_with_ophyd_sim, From 78fdc272ec4954d36c7e943c3abaccc0c08da081 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 26 Nov 2024 17:07:29 +0000 Subject: [PATCH 38/93] restore the thor labs stage and cancel the xpress3 not working one --- src/dodal/beamlines/i18.py | 28 ++++++++++++------------ src/dodal/devices/i18/thor_labs_stage.py | 24 ++++++++++++++++++++ 2 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 src/dodal/devices/i18/thor_labs_stage.py diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index d6336fd7e2..5260e746fe 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -16,6 +16,7 @@ from dodal.devices.i18.diode import Diode from dodal.devices.i18.KBMirror import KBMirror from dodal.devices.i18.table import Table +from dodal.devices.i18.thor_labs_stage import ThorLabsStage from dodal.devices.i22.dcm import CrystalMetadata, DoubleCrystalMonochromator from dodal.devices.slits import Slits from dodal.devices.synchrotron import Synchrotron @@ -103,19 +104,6 @@ def panda1( ) -def xspress3( - wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False -) -> Xspress3: - return device_instantiation( - Xspress3, - prefix="-EA-XSP-02:", - name="xspress3", - num_channels=16, - wait=wait_for_connection, - fake=fake_with_ophyd_sim, - ) - - # odin detectors are not yet supported. # There is a controls project in the works, # not ready anytime soon @@ -125,7 +113,7 @@ def xspress3_odin( ) -> Xspress3: return device_instantiation( Xspress3, - prefix="-EA-XSP-03:", + prefix="-EA-XSP-02:", name="xspress3_odin", num_channels=4, wait=wait_for_connection, @@ -274,3 +262,15 @@ def raster_stage( wait_for_connection, fake_with_ophyd_sim, ) + + +def thor_labs_table( + wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False +) -> ThorLabsStage: + return device_instantiation( + ThorLabsStage, + "table", + "-MO-TABLE-02:", + wait_for_connection, + fake_with_ophyd_sim, + ) diff --git a/src/dodal/devices/i18/thor_labs_stage.py b/src/dodal/devices/i18/thor_labs_stage.py new file mode 100644 index 0000000000..6577464b13 --- /dev/null +++ b/src/dodal/devices/i18/thor_labs_stage.py @@ -0,0 +1,24 @@ +from ophyd_async.core import ( + AsyncStatus, + StandardReadable, +) +from ophyd_async.epics.motor import Motor +from pydantic import BaseModel + + +class XYPosition(BaseModel): + x: float + y: float + + +class ThorLabsStage(StandardReadable): + def __init__(self, prefix: str = "", name: str = "") -> None: + with self.add_children_as_readables(): + self.x = Motor(prefix + "X") + self.y = Motor(prefix + "Y") + super().__init__(name=name) + + @AsyncStatus.wrap + async def set(self, value: XYPosition): + self.x.set(value.x) + self.y.set(value.y) From 5224b9677590bd3714cbee7cc55a654521d97c4a Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 26 Nov 2024 17:08:46 +0000 Subject: [PATCH 39/93] make all connect --- src/dodal/beamlines/i18.py | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 5260e746fe..e5cf5e8940 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -237,10 +237,10 @@ def main_table( def thor_labs_table( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False -) -> Table: +) -> ThorLabsStage: return device_instantiation( - Table, - "table", + ThorLabsStage, + "thor_labs_stage", "-MO-TABLE-02:", wait_for_connection, fake_with_ophyd_sim, @@ -262,15 +262,3 @@ def raster_stage( wait_for_connection, fake_with_ophyd_sim, ) - - -def thor_labs_table( - wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False -) -> ThorLabsStage: - return device_instantiation( - ThorLabsStage, - "table", - "-MO-TABLE-02:", - wait_for_connection, - fake_with_ophyd_sim, - ) From 61007bdbe342403dbe9f7fb25d935a6ab88b4db6 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 14 Jan 2025 17:02:44 +0000 Subject: [PATCH 40/93] fix the tests imports --- tests/devices/i18/test_kb_mirror.py | 3 ++- tests/devices/i18/test_table.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/devices/i18/test_kb_mirror.py b/tests/devices/i18/test_kb_mirror.py index 1eff890564..c66486cf40 100644 --- a/tests/devices/i18/test_kb_mirror.py +++ b/tests/devices/i18/test_kb_mirror.py @@ -1,7 +1,8 @@ from unittest.mock import ANY import pytest -from ophyd_async.core import DeviceCollector, set_mock_value +from ophyd_async.core import DeviceCollector +from ophyd_async.testing import set_mock_value from dodal.devices.i18.KBMirror import KBMirror, XYPosition diff --git a/tests/devices/i18/test_table.py b/tests/devices/i18/test_table.py index 900339894a..80f816312c 100644 --- a/tests/devices/i18/test_table.py +++ b/tests/devices/i18/test_table.py @@ -1,7 +1,8 @@ from unittest.mock import ANY import pytest -from ophyd_async.core import DeviceCollector, set_mock_value +from ophyd_async.core import DeviceCollector +from ophyd_async.testing import set_mock_value from dodal.devices.i18.table import Four_D_Position, Table From e8c8bcd48fe92589a886737239106ab0c1d9ed4f Mon Sep 17 00:00:00 2001 From: "Ware, Joseph (DLSLtd,RAL,LSCI)" Date: Fri, 24 Jan 2025 11:29:13 +0000 Subject: [PATCH 41/93] Add device_factory --- src/dodal/beamlines/i18.py | 209 ++++++++------------------------ src/dodal/devices/i18/table.py | 14 ++- tests/devices/i18/test_table.py | 4 +- 3 files changed, 61 insertions(+), 166 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index e5cf5e8940..8e309c0de5 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -3,12 +3,11 @@ from ophyd_async.fastcs.panda import HDFPanda from dodal.common.beamlines.beamline_utils import ( - device_instantiation, + device_factory, get_path_provider, set_path_provider, ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.common.beamlines.device_helpers import numbered_slits from dodal.common.visit import ( LocalDirectoryServiceClient, StaticVisitPathProvider, @@ -24,9 +23,10 @@ from dodal.devices.undulator import Undulator from dodal.devices.xspress3.xspress3 import Xspress3 from dodal.log import set_beamline as set_log_beamline -from dodal.utils import BeamlinePrefix, get_beamline_name, skip_device +from dodal.utils import BeamlinePrefix, get_beamline_name BL = get_beamline_name("i18") +PREFIX = BeamlinePrefix(BL) set_log_beamline(BL) set_utils_beamline(BL) @@ -45,61 +45,28 @@ ) -def synchrotron( - wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False -) -> Synchrotron: - return device_instantiation( - Synchrotron, - "synchrotron", - "", - wait_for_connection, - fake_with_ophyd_sim, - ) +@device_factory() +def synchrotron() -> Synchrotron: + return Synchrotron() # not ready yet -@skip_device() -def undulator( - wait_for_connection: bool = True, - fake_with_ophyd_sim: bool = False, -) -> Undulator: - return device_instantiation( - Undulator, - "undulator", - f"{BeamlinePrefix(BL).insertion_prefix}-MO-SERVC-01:", - wait_for_connection, - fake_with_ophyd_sim, - bl_prefix=False, - poles=80, - length=2.0, - ) +@device_factory() +def undulator() -> Undulator: + return Undulator(f"{PREFIX.insertion_prefix}-MO-SERVC-01:") -@skip_device() -def slits_1( - wait_for_connection: bool = True, - fake_with_ophyd_sim: bool = False, -) -> Slits: - return numbered_slits( - 1, - wait_for_connection, - fake_with_ophyd_sim, - ) +@device_factory() +def slits_1() -> Slits: + return Slits(f"{PREFIX.beamline_prefix}-AL-SLITS-01:") # Must document what PandAs are physically connected to # See: https://github.com/bluesky/ophyd-async/issues/284 -@skip_device() -def panda1( - wait_for_connection: bool = True, - fake_with_ophyd_sim: bool = False, -) -> HDFPanda: - return device_instantiation( - HDFPanda, - "panda1", - "-MO-PANDA-01:", - wait_for_connection, - fake_with_ophyd_sim, +@device_factory() +def panda1() -> HDFPanda: + return HDFPanda( + f"{PREFIX.beamline_prefix}-MO-PANDA-01:", path_provider=get_path_provider(), ) @@ -107,24 +74,16 @@ def panda1( # odin detectors are not yet supported. # There is a controls project in the works, # not ready anytime soon -@skip_device() -def xspress3_odin( - wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False -) -> Xspress3: - return device_instantiation( - Xspress3, - prefix="-EA-XSP-02:", - name="xspress3_odin", +@device_factory() +def xspress3_odin() -> Xspress3: + return Xspress3( + f"{PREFIX.beamline_prefix}-EA-XSP-02:", num_channels=4, - wait=wait_for_connection, - fake=fake_with_ophyd_sim, ) -def dcm( - wait_for_connection: bool = True, - fake_with_ophyd_sim: bool = False, -) -> DoubleCrystalMonochromator: +@device_factory() +def dcm() -> DoubleCrystalMonochromator: crystal_1_metadata = CrystalMetadata( usage="Bragg", type="silicon", @@ -139,126 +98,58 @@ def dcm( d_spacing=(3.13475, "nm"), ) - return device_instantiation( - DoubleCrystalMonochromator, - "dcm", - f"{BeamlinePrefix(BL).beamline_prefix}-MO-DCM-01:", - wait_for_connection, - fake_with_ophyd_sim, - bl_prefix=False, + return DoubleCrystalMonochromator( temperature_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-DI-DCM-01:", crystal_1_metadata=crystal_1_metadata, crystal_2_metadata=crystal_2_metadata, ) -@skip_device() -def i0( - wait_for_connection: bool = True, - fake_with_ophyd_sim: bool = False, -) -> TetrammDetector: - return device_instantiation( - TetrammDetector, - "i0", - "-DI-XBPM-02:", - wait_for_connection, - fake_with_ophyd_sim, +@device_factory() +def i0() -> TetrammDetector: + return TetrammDetector( + f"{PREFIX.beamline_prefix}-DI-XBPM-02:", type="Cividec Diamond XBPM", path_provider=get_path_provider(), ) -@skip_device() -def it( - wait_for_connection: bool = True, - fake_with_ophyd_sim: bool = False, -) -> TetrammDetector: - return device_instantiation( - TetrammDetector, - "it", - "-DI-XBPM-01:", - wait_for_connection, - fake_with_ophyd_sim, +@device_factory() +def it() -> TetrammDetector: + return TetrammDetector( + f"{PREFIX.beamline_prefix}-DI-XBPM-01:", type="Tetramm", path_provider=get_path_provider(), ) -@skip_device -def vfm( - wait_for_connection: bool = True, - fake_with_ophyd_sim: bool = False, -) -> KBMirror: - return device_instantiation( - KBMirror, - "vfm", - "-OP-VFM-01:", - wait_for_connection, - fake_with_ophyd_sim, - ) - - -def hfm( - wait_for_connection: bool = True, - fake_with_ophyd_sim: bool = False, -) -> KBMirror: - return device_instantiation( - KBMirror, - "hfm", - "-OP-HFM-01:", - wait_for_connection, - fake_with_ophyd_sim, - ) +@device_factory() +def vfm() -> KBMirror: + return KBMirror(f"{PREFIX.beamline_prefix}-OP-VFM-01:") -def d7diode( - wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False -) -> Diode: - return device_instantiation( - Diode, - "d7diode", - "-DI-PHDGN-07:", - wait_for_connection, - fake_with_ophyd_sim, - ) +@device_factory() +def hfm() -> KBMirror: + return KBMirror(f"{PREFIX.beamline_prefix}-OP-HFM-01:") -def main_table( - wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False -) -> Table: - return device_instantiation( - Table, - "table", - "-MO-TABLE-01:", - wait_for_connection, - fake_with_ophyd_sim, - ) +@device_factory() +def d7diode() -> Diode: + return Diode(f"{PREFIX.beamline_prefix}-DI-PHDGN-07:") -def thor_labs_table( - wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False -) -> ThorLabsStage: - return device_instantiation( - ThorLabsStage, - "thor_labs_stage", - "-MO-TABLE-02:", - wait_for_connection, - fake_with_ophyd_sim, +@device_factory() +def main_table() -> Table: + return Table( + f"{PREFIX.beamline_prefix}-MO-TABLE-01:", ) -# SIMULATED DEVICES +@device_factory() +def thor_labs_stage() -> ThorLabsStage: + return ThorLabsStage(f"{PREFIX.beamline_prefix}-MO-TABLE-02:") -# this is a mock table, not sure how does it relate to the real Table, maybe just a fake option in instantiation is needed -@skip_device() -def raster_stage( - wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False -) -> Table: - return device_instantiation( - Table, - "raster_stage", - "-MO-SIM-01:", - wait_for_connection, - fake_with_ophyd_sim, - ) +@device_factory() +def raster_stage() -> Table: + return Table(f"{PREFIX.beamline_prefix}-MO-SIM-01:") diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index 1ebb40fa34..27a307ea28 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -6,15 +6,15 @@ from pydantic import BaseModel -class Four_D_Position(BaseModel): +class TablePosition(BaseModel): x: float y: float - z: float - theta: float + z: float | None = None + theta: float | None = None class Table(StandardReadable): - def __init__(self, prefix: str = "", name: str = "") -> None: + def __init__(self, prefix: str, name: str = "") -> None: with self.add_children_as_readables(): self.x = Motor(prefix + "X") self.y = Motor(prefix + "Y") @@ -23,6 +23,10 @@ def __init__(self, prefix: str = "", name: str = "") -> None: super().__init__(name=name) @AsyncStatus.wrap - async def set(self, value: Four_D_Position): + async def set(self, value: TablePosition): self.x.set(value.x) self.y.set(value.y) + if value.z: + self.z.set(value.z) + if value.theta: + self.theta.set(value.theta) diff --git a/tests/devices/i18/test_table.py b/tests/devices/i18/test_table.py index 80f816312c..7879ca4b66 100644 --- a/tests/devices/i18/test_table.py +++ b/tests/devices/i18/test_table.py @@ -4,7 +4,7 @@ from ophyd_async.core import DeviceCollector from ophyd_async.testing import set_mock_value -from dodal.devices.i18.table import Four_D_Position, Table +from dodal.devices.i18.table import Table, TablePosition @pytest.fixture @@ -23,7 +23,7 @@ async def test_setting_xy_position_table(table: Table): set_mock_value(table.y.user_readback, 4.56) # Create a position object - position = Four_D_Position(x=1.23, y=4.56, z=0.0, theta=0.0) + position = TablePosition(x=1.23, y=4.56, z=0.0, theta=0.0) # Call set to update the position await table.set(position) From e0398855828205bad71d771cf88b51288344d2a1 Mon Sep 17 00:00:00 2001 From: "Ware, Joseph (DLSLtd,RAL,LSCI)" Date: Fri, 24 Jan 2025 11:37:38 +0000 Subject: [PATCH 42/93] remove unknown values and simulated device --- pyproject.toml | 1 - src/dodal/beamlines/i18.py | 17 ++++------------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fecfef9202..28093ed942 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,6 @@ dependencies = [ "click", "ophyd", "ophyd-async >= 0.9.0a1", - "ophyd-async[sim]", "bluesky", "pyepics", "dataclasses-json", diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 8e309c0de5..f01f0f7557 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -51,7 +51,7 @@ def synchrotron() -> Synchrotron: # not ready yet -@device_factory() +@device_factory(skip=True) def undulator() -> Undulator: return Undulator(f"{PREFIX.insertion_prefix}-MO-SERVC-01:") @@ -74,7 +74,7 @@ def panda1() -> HDFPanda: # odin detectors are not yet supported. # There is a controls project in the works, # not ready anytime soon -@device_factory() +@device_factory(skip=True) def xspress3_odin() -> Xspress3: return Xspress3( f"{PREFIX.beamline_prefix}-EA-XSP-02:", @@ -82,7 +82,7 @@ def xspress3_odin() -> Xspress3: ) -@device_factory() +@device_factory(skip=True) def dcm() -> DoubleCrystalMonochromator: crystal_1_metadata = CrystalMetadata( usage="Bragg", @@ -109,7 +109,6 @@ def dcm() -> DoubleCrystalMonochromator: def i0() -> TetrammDetector: return TetrammDetector( f"{PREFIX.beamline_prefix}-DI-XBPM-02:", - type="Cividec Diamond XBPM", path_provider=get_path_provider(), ) @@ -118,7 +117,6 @@ def i0() -> TetrammDetector: def it() -> TetrammDetector: return TetrammDetector( f"{PREFIX.beamline_prefix}-DI-XBPM-01:", - type="Tetramm", path_provider=get_path_provider(), ) @@ -140,16 +138,9 @@ def d7diode() -> Diode: @device_factory() def main_table() -> Table: - return Table( - f"{PREFIX.beamline_prefix}-MO-TABLE-01:", - ) + return Table(f"{PREFIX.beamline_prefix}-MO-TABLE-01:") @device_factory() def thor_labs_stage() -> ThorLabsStage: return ThorLabsStage(f"{PREFIX.beamline_prefix}-MO-TABLE-02:") - - -@device_factory() -def raster_stage() -> Table: - return Table(f"{PREFIX.beamline_prefix}-MO-SIM-01:") From d94415f58a093585837226797965bed693ec0fdd Mon Sep 17 00:00:00 2001 From: "Ware, Joseph (DLSLtd,RAL,LSCI)" Date: Fri, 24 Jan 2025 11:42:31 +0000 Subject: [PATCH 43/93] i18 slits centre spelling --- src/dodal/beamlines/i18.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index f01f0f7557..0f0cb1a51a 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -58,7 +58,11 @@ def undulator() -> Undulator: @device_factory() def slits_1() -> Slits: - return Slits(f"{PREFIX.beamline_prefix}-AL-SLITS-01:") + return Slits( + f"{PREFIX.beamline_prefix}-AL-SLITS-01:", + x_centre="X:CENTER", + y_centre="Y:CENTER", + ) # Must document what PandAs are physically connected to From 5972984e50f29f4a243bd4fa266cc4f9c56da7e0 Mon Sep 17 00:00:00 2001 From: "Ware, Joseph (DLSLtd,RAL,LSCI)" Date: Fri, 24 Jan 2025 11:47:03 +0000 Subject: [PATCH 44/93] Skip devices that require device classes --- src/dodal/beamlines/i18.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 0f0cb1a51a..9b2e360606 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -67,7 +67,7 @@ def slits_1() -> Slits: # Must document what PandAs are physically connected to # See: https://github.com/bluesky/ophyd-async/issues/284 -@device_factory() +@device_factory(skip=True) def panda1() -> HDFPanda: return HDFPanda( f"{PREFIX.beamline_prefix}-MO-PANDA-01:", @@ -125,7 +125,7 @@ def it() -> TetrammDetector: ) -@device_factory() +@device_factory(skip=True) def vfm() -> KBMirror: return KBMirror(f"{PREFIX.beamline_prefix}-OP-VFM-01:") From ebb5175f871237ccc8b99a5f5882cf3149b8f51b Mon Sep 17 00:00:00 2001 From: "Ware, Joseph (DLSLtd,RAL,LSCI)" Date: Fri, 24 Jan 2025 11:53:09 +0000 Subject: [PATCH 45/93] Remove unused devices --- src/dodal/beamlines/i18.py | 43 +++----------------------------------- 1 file changed, 3 insertions(+), 40 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 9b2e360606..992dc4d9c0 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -16,12 +16,10 @@ from dodal.devices.i18.KBMirror import KBMirror from dodal.devices.i18.table import Table from dodal.devices.i18.thor_labs_stage import ThorLabsStage -from dodal.devices.i22.dcm import CrystalMetadata, DoubleCrystalMonochromator from dodal.devices.slits import Slits from dodal.devices.synchrotron import Synchrotron from dodal.devices.tetramm import TetrammDetector from dodal.devices.undulator import Undulator -from dodal.devices.xspress3.xspress3 import Xspress3 from dodal.log import set_beamline as set_log_beamline from dodal.utils import BeamlinePrefix, get_beamline_name @@ -50,8 +48,7 @@ def synchrotron() -> Synchrotron: return Synchrotron() -# not ready yet -@device_factory(skip=True) +@device_factory() def undulator() -> Undulator: return Undulator(f"{PREFIX.insertion_prefix}-MO-SERVC-01:") @@ -65,8 +62,7 @@ def slits_1() -> Slits: ) -# Must document what PandAs are physically connected to -# See: https://github.com/bluesky/ophyd-async/issues/284 +# PandA IOC needs to be updated to support PVI @device_factory(skip=True) def panda1() -> HDFPanda: return HDFPanda( @@ -75,40 +71,6 @@ def panda1() -> HDFPanda: ) -# odin detectors are not yet supported. -# There is a controls project in the works, -# not ready anytime soon -@device_factory(skip=True) -def xspress3_odin() -> Xspress3: - return Xspress3( - f"{PREFIX.beamline_prefix}-EA-XSP-02:", - num_channels=4, - ) - - -@device_factory(skip=True) -def dcm() -> DoubleCrystalMonochromator: - crystal_1_metadata = CrystalMetadata( - usage="Bragg", - type="silicon", - reflection=(1, 1, 1), - d_spacing=(3.13475, "nm"), - ) - - crystal_2_metadata = CrystalMetadata( - usage="Bragg", - type="silicon", - reflection=(1, 1, 1), - d_spacing=(3.13475, "nm"), - ) - - return DoubleCrystalMonochromator( - temperature_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-DI-DCM-01:", - crystal_1_metadata=crystal_1_metadata, - crystal_2_metadata=crystal_2_metadata, - ) - - @device_factory() def i0() -> TetrammDetector: return TetrammDetector( @@ -126,6 +88,7 @@ def it() -> TetrammDetector: @device_factory(skip=True) +# VFM uses different IOC than HFM def vfm() -> KBMirror: return KBMirror(f"{PREFIX.beamline_prefix}-OP-VFM-01:") From 1068b44489e287190cd5fdc0df15b8b4f20d012e Mon Sep 17 00:00:00 2001 From: "Ware, Joseph (DLSLtd,RAL,LSCI)" Date: Fri, 24 Jan 2025 12:19:06 +0000 Subject: [PATCH 46/93] Restore i18 dcm --- src/dodal/beamlines/i18.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 992dc4d9c0..64dbb17ad3 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -8,6 +8,7 @@ set_path_provider, ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.common.crystal_metadata import CrystalMetadata from dodal.common.visit import ( LocalDirectoryServiceClient, StaticVisitPathProvider, @@ -16,6 +17,7 @@ from dodal.devices.i18.KBMirror import KBMirror from dodal.devices.i18.table import Table from dodal.devices.i18.thor_labs_stage import ThorLabsStage +from dodal.devices.i22.dcm import DoubleCrystalMonochromator from dodal.devices.slits import Slits from dodal.devices.synchrotron import Synchrotron from dodal.devices.tetramm import TetrammDetector @@ -53,6 +55,30 @@ def undulator() -> Undulator: return Undulator(f"{PREFIX.insertion_prefix}-MO-SERVC-01:") +@device_factory() +def dcm() -> DoubleCrystalMonochromator: + crystal_1_metadata = CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), + ) + + crystal_2_metadata = CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), + ) + + return DoubleCrystalMonochromator( + prefix=f"{PREFIX.beamline_prefix}-MO-DCM-01:", + temperature_prefix=f"{PREFIX.beamline_prefix}-DI-DCM-01:", + crystal_1_metadata=crystal_1_metadata, + crystal_2_metadata=crystal_2_metadata, + ) + + @device_factory() def slits_1() -> Slits: return Slits( From 9201e5ba4b1a50ca35ebd1ec0dbdef5c7e769080 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Thu, 1 Aug 2024 11:50:09 +0100 Subject: [PATCH 47/93] add i18 beamline definition --- src/dodal/beamlines/i18.py | 218 +++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 src/dodal/beamlines/i18.py diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py new file mode 100644 index 0000000000..e5a53cde0b --- /dev/null +++ b/src/dodal/beamlines/i18.py @@ -0,0 +1,218 @@ +from pathlib import Path + +from ophyd_async.panda import HDFPanda + +from dodal.common.beamlines.beamline_utils import ( + device_instantiation, + get_directory_provider, + set_directory_provider, +) +from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.common.beamlines.device_helpers import numbered_slits +from dodal.common.visit import DirectoryServiceClient, StaticVisitDirectoryProvider +from dodal.devices.focusing_mirror import FocusingMirror +from dodal.devices.i22.dcm import CrystalMetadata, DoubleCrystalMonochromator +from dodal.devices.slits import Slits +from dodal.devices.synchrotron import Synchrotron +from dodal.devices.tetramm import TetrammDetector +from dodal.devices.undulator import Undulator +from dodal.devices.xspress3.xspress3 import Xspress3 +from dodal.log import set_beamline as set_log_beamline +from dodal.utils import BeamlinePrefix, get_beamline_name, skip_device + +BL = get_beamline_name("i18") +set_log_beamline(BL) +set_utils_beamline(BL) + +# +# table again, t1 theta and d7bdiode +# MO table is ok +# +# pinhole PVs? +# -AL-APTR-01:TEMP1 +# +# diode reading and also DRAIN +# diode is ok, there are A and B variants +# motors A and B +# camera not used + +# Currently we must hard-code the visit, determining the visit at runtime requires +# infrastructure that is still WIP. +# Communication with GDA is also WIP so for now we determine an arbitrary scan number +# locally and write the commissioning directory. The scan number is not guaranteed to +# be unique and the data is at risk - this configuration is for testing only. +set_directory_provider( + StaticVisitDirectoryProvider( + BL, + Path("/dls/i18/data/2024/cm37264-2/bluesky"), + client=DirectoryServiceClient("http://i18-control:8088/api"), + ) +) + + +def synchrotron( + wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False +) -> Synchrotron: + return device_instantiation( + Synchrotron, + "synchrotron", + "", + wait_for_connection, + fake_with_ophyd_sim, + ) + + +def undulator( + wait_for_connection: bool = True, + fake_with_ophyd_sim: bool = False, +) -> Undulator: + return device_instantiation( + Undulator, + "undulator", + f"{BeamlinePrefix(BL).insertion_prefix}-MO-SERVC-01:", + # add CURRGAPD + wait_for_connection, + fake_with_ophyd_sim, + bl_prefix=False, + poles=80, + length=2.0, + ) + + +@skip_device() +def slits_1( + wait_for_connection: bool = True, + fake_with_ophyd_sim: bool = False, +) -> Slits: + return numbered_slits( + 1, + # BL18I-AL-SLITS-01 + wait_for_connection, + fake_with_ophyd_sim, + ) + + +# Must document what PandAs are physically connected to +# See: https://github.com/bluesky/ophyd-async/issues/284 +@skip_device() +def panda1( + wait_for_connection: bool = True, + fake_with_ophyd_sim: bool = False, +) -> HDFPanda: + return device_instantiation( + HDFPanda, + "panda1", + "-MO-PANDA-01:", + wait_for_connection, + fake_with_ophyd_sim, + directory_provider=get_directory_provider(), + ) + + +@skip_device() +def xspress3( + wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False +) -> Xspress3: + """ + 16 channels Xspress3 detector + -EA-XPS-02:CAM:MaxSizeX_RBV + also ArraySize + also :CONNECTED + """ + + return device_instantiation( + Xspress3, + # prefix="-EA-DET-03:", + prefix="-EA-XPS-02:", + name="Xspress3", + num_channels=16, + wait=wait_for_connection, + fake=fake_with_ophyd_sim, + ) + + +def dcm( + wait_for_connection: bool = True, + fake_with_ophyd_sim: bool = False, +) -> DoubleCrystalMonochromator: + return device_instantiation( + DoubleCrystalMonochromator, + "dcm", + "", + wait_for_connection, + fake_with_ophyd_sim, + bl_prefix=False, + motion_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-MO-DCM-01:", + temperature_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-DI-DCM-01:", + crystal_1_metadata=CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), + ), + crystal_2_metadata=CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), + ), + ) + + +@skip_device() +def i0( + wait_for_connection: bool = True, + fake_with_ophyd_sim: bool = False, +) -> TetrammDetector: + return device_instantiation( + TetrammDetector, + "i0", + "-EA-XBPM-02:", + # -DI-XBPM-02:DEV:Firmware + wait_for_connection, + fake_with_ophyd_sim, + type="Cividec Diamond XBPM", + directory_provider=get_directory_provider(), + ) + + +@skip_device() +def it( + wait_for_connection: bool = True, + fake_with_ophyd_sim: bool = False, +) -> TetrammDetector: + return device_instantiation( + TetrammDetector, + "it", + "-EA-TTRM-02:", + wait_for_connection, + fake_with_ophyd_sim, + type="PIN Diode", + directory_provider=get_directory_provider(), + ) + + +def vfm( + wait_for_connection: bool = True, + fake_with_ophyd_sim: bool = False, +) -> FocusingMirror: + return device_instantiation( + FocusingMirror, + "vfm", + "-OP-KBM-01:VFM:", + wait_for_connection, + fake_with_ophyd_sim, + ) + + +def hfm( + wait_for_connection: bool = True, + fake_with_ophyd_sim: bool = False, +) -> FocusingMirror: + return device_instantiation( + FocusingMirror, + "hfm", + "-OP-KBM-01:HFM:", + wait_for_connection, + fake_with_ophyd_sim, + ) From b916855812fbb98e94f242b35707bb07af7fd1f2 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Thu, 1 Aug 2024 15:52:42 +0100 Subject: [PATCH 48/93] add metadata to dcm crystals --- src/dodal/beamlines/i18.py | 48 +++++++++++------------------------- src/dodal/devices/i22/dcm.py | 1 + 2 files changed, 16 insertions(+), 33 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index e5a53cde0b..dd81d52fbd 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -9,9 +9,10 @@ ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.common.beamlines.device_helpers import numbered_slits +from dodal.common.crystal_metadata import CrystalMetadata from dodal.common.visit import DirectoryServiceClient, StaticVisitDirectoryProvider from dodal.devices.focusing_mirror import FocusingMirror -from dodal.devices.i22.dcm import CrystalMetadata, DoubleCrystalMonochromator +from dodal.devices.i22.dcm import DoubleCrystalMonochromator from dodal.devices.slits import Slits from dodal.devices.synchrotron import Synchrotron from dodal.devices.tetramm import TetrammDetector @@ -24,17 +25,6 @@ set_log_beamline(BL) set_utils_beamline(BL) -# -# table again, t1 theta and d7bdiode -# MO table is ok -# -# pinhole PVs? -# -AL-APTR-01:TEMP1 -# -# diode reading and also DRAIN -# diode is ok, there are A and B variants -# motors A and B -# camera not used # Currently we must hard-code the visit, determining the visit at runtime requires # infrastructure that is still WIP. @@ -70,7 +60,6 @@ def undulator( Undulator, "undulator", f"{BeamlinePrefix(BL).insertion_prefix}-MO-SERVC-01:", - # add CURRGAPD wait_for_connection, fake_with_ophyd_sim, bl_prefix=False, @@ -86,7 +75,6 @@ def slits_1( ) -> Slits: return numbered_slits( 1, - # BL18I-AL-SLITS-01 wait_for_connection, fake_with_ophyd_sim, ) @@ -115,14 +103,10 @@ def xspress3( ) -> Xspress3: """ 16 channels Xspress3 detector - -EA-XPS-02:CAM:MaxSizeX_RBV - also ArraySize - also :CONNECTED """ return device_instantiation( Xspress3, - # prefix="-EA-DET-03:", prefix="-EA-XPS-02:", name="Xspress3", num_channels=16, @@ -131,6 +115,13 @@ def xspress3( ) +crystal_1_metadata = CrystalMetadata("Si111") +crystal_2_metadata = CrystalMetadata("Si111") + +_unused_crystal_metadata_1 = CrystalMetadata("Si311") +_unused_crystal_metadata_2 = CrystalMetadata("Si333") + + def dcm( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, @@ -144,18 +135,8 @@ def dcm( bl_prefix=False, motion_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-MO-DCM-01:", temperature_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-DI-DCM-01:", - crystal_1_metadata=CrystalMetadata( - usage="Bragg", - type="silicon", - reflection=(1, 1, 1), - d_spacing=(3.13475, "nm"), - ), - crystal_2_metadata=CrystalMetadata( - usage="Bragg", - type="silicon", - reflection=(1, 1, 1), - d_spacing=(3.13475, "nm"), - ), + crystal_1_metadata=crystal_1_metadata, + crystal_2_metadata=crystal_2_metadata, ) @@ -168,7 +149,6 @@ def i0( TetrammDetector, "i0", "-EA-XBPM-02:", - # -DI-XBPM-02:DEV:Firmware wait_for_connection, fake_with_ophyd_sim, type="Cividec Diamond XBPM", @@ -192,6 +172,7 @@ def it( ) +@skip_device def vfm( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, @@ -199,12 +180,13 @@ def vfm( return device_instantiation( FocusingMirror, "vfm", - "-OP-KBM-01:VFM:", + "-OP-VFM-01:", wait_for_connection, fake_with_ophyd_sim, ) +@skip_device def hfm( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, @@ -212,7 +194,7 @@ def hfm( return device_instantiation( FocusingMirror, "hfm", - "-OP-KBM-01:HFM:", + "-OP-HFM-01:", wait_for_connection, fake_with_ophyd_sim, ) diff --git a/src/dodal/devices/i22/dcm.py b/src/dodal/devices/i22/dcm.py index 58180ad9b6..3c84c3e678 100644 --- a/src/dodal/devices/i22/dcm.py +++ b/src/dodal/devices/i22/dcm.py @@ -1,4 +1,5 @@ import time +from collections.abc import Sequence import numpy as np from bluesky.protocols import Reading From 216728c9352cbaff91b2ce7175d439ed12c371b7 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Thu, 1 Aug 2024 16:52:28 +0100 Subject: [PATCH 49/93] adapt the tests --- src/dodal/beamlines/p38.py | 5 +++++ tests/devices/i22/test_dcm.py | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/dodal/beamlines/p38.py b/src/dodal/beamlines/p38.py index 30cb2e5a99..2141e720ff 100644 --- a/src/dodal/beamlines/p38.py +++ b/src/dodal/beamlines/p38.py @@ -233,12 +233,17 @@ def dcm( fake_with_ophyd_sim, bl_prefix=False, temperature_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-DI-DCM-01:", +<<<<<<< HEAD crystal_1_metadata=make_crystal_metadata_from_material( MaterialsEnum.Si, (1, 1, 1) ), crystal_2_metadata=make_crystal_metadata_from_material( MaterialsEnum.Si, (1, 1, 1) ), +======= + crystal_1_metadata=CrystalMetadata("Si111"), + crystal_2_metadata=CrystalMetadata("Si111"), +>>>>>>> 7c02f1411 (adapt the tests) ) diff --git a/tests/devices/i22/test_dcm.py b/tests/devices/i22/test_dcm.py index cd8dd0ebbc..789206f6f6 100644 --- a/tests/devices/i22/test_dcm.py +++ b/tests/devices/i22/test_dcm.py @@ -49,6 +49,32 @@ def test_count_dcm( ) +<<<<<<< HEAD +======= +async def test_crystal_metadata_not_propagated_when_not_supplied(): + async with DeviceCollector(mock=True): + dcm = DoubleCrystalMonochromator( + prefix="FOO-MO", + temperature_prefix="FOO-DI", + crystal_1_metadata=CrystalMetadata("Si111"), + crystal_2_metadata=CrystalMetadata("Si111"), + ) + + configuration = await dcm.read_configuration() + expected_absent_keys = { + "crystal-1-usage", + "crystal-1-type", + "crystal-1-reflection", + "crystal-1-d_spacing", + "crystal-2-usage", + "crystal-2-type", + "crystal-2-reflection", + "crystal-2-d_spacing", + } + assert expected_absent_keys.isdisjoint(configuration) + + +>>>>>>> 7c02f1411 (adapt the tests) @pytest.mark.parametrize( "energy,wavelength", [ From 1ba4df56537c9266f73ca2843b9f9e9c2bd696d9 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Fri, 2 Aug 2024 10:22:04 +0000 Subject: [PATCH 50/93] udpate fixtures --- tests/devices/i22/test_dcm.py | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/tests/devices/i22/test_dcm.py b/tests/devices/i22/test_dcm.py index 789206f6f6..cd8dd0ebbc 100644 --- a/tests/devices/i22/test_dcm.py +++ b/tests/devices/i22/test_dcm.py @@ -49,32 +49,6 @@ def test_count_dcm( ) -<<<<<<< HEAD -======= -async def test_crystal_metadata_not_propagated_when_not_supplied(): - async with DeviceCollector(mock=True): - dcm = DoubleCrystalMonochromator( - prefix="FOO-MO", - temperature_prefix="FOO-DI", - crystal_1_metadata=CrystalMetadata("Si111"), - crystal_2_metadata=CrystalMetadata("Si111"), - ) - - configuration = await dcm.read_configuration() - expected_absent_keys = { - "crystal-1-usage", - "crystal-1-type", - "crystal-1-reflection", - "crystal-1-d_spacing", - "crystal-2-usage", - "crystal-2-type", - "crystal-2-reflection", - "crystal-2-d_spacing", - } - assert expected_absent_keys.isdisjoint(configuration) - - ->>>>>>> 7c02f1411 (adapt the tests) @pytest.mark.parametrize( "energy,wavelength", [ From cdaaf2500a553dde23215223069f114c5a8f9a1a Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Fri, 2 Aug 2024 10:48:08 +0000 Subject: [PATCH 51/93] move devices from i18-bluesky --- src/dodal/beamlines/i18.py | 22 +++++++ src/dodal/devices/i18/diode.py | 20 ++++++ src/dodal/devices/i18/sim_motor_set.py | 88 ++++++++++++++++++++++++++ src/dodal/devices/i18/table.py | 14 ++++ 4 files changed, 144 insertions(+) create mode 100644 src/dodal/devices/i18/diode.py create mode 100644 src/dodal/devices/i18/sim_motor_set.py create mode 100644 src/dodal/devices/i18/table.py diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index dd81d52fbd..77f387a9b5 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -12,6 +12,8 @@ from dodal.common.crystal_metadata import CrystalMetadata from dodal.common.visit import DirectoryServiceClient, StaticVisitDirectoryProvider from dodal.devices.focusing_mirror import FocusingMirror +from dodal.devices.i18.diode import Diode +from dodal.devices.i18.table import Table from dodal.devices.i22.dcm import DoubleCrystalMonochromator from dodal.devices.slits import Slits from dodal.devices.synchrotron import Synchrotron @@ -198,3 +200,23 @@ def hfm( wait_for_connection, fake_with_ophyd_sim, ) + + +def diode(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -> Diode: + return device_instantiation( + Diode, + "diodad7bdiode", + "-DI-PHDGN-07:", + wait_for_connection, + fake_with_ophyd_sim, + ) + + +def table(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -> Table: + return device_instantiation( + Table, + "table", + "-MO-TABLE-01:", + wait_for_connection, + fake_with_ophyd_sim, + ) diff --git a/src/dodal/devices/i18/diode.py b/src/dodal/devices/i18/diode.py new file mode 100644 index 0000000000..55d7648fad --- /dev/null +++ b/src/dodal/devices/i18/diode.py @@ -0,0 +1,20 @@ +from ophyd_async.core import ( + StandardReadable, +) +from ophyd_async.epics.signal import epics_signal_r + + +class Diode(StandardReadable): + def __init__( + self, + prefix: str, + name: str = "", + ): + self._prefix = prefix + with self.add_children_as_readables(): + self.signal = epics_signal_r(float, prefix + "B:DIODE:I") + + super().__init__(name=name) + + +# todo should add 'read' override? diff --git a/src/dodal/devices/i18/sim_motor_set.py b/src/dodal/devices/i18/sim_motor_set.py new file mode 100644 index 0000000000..e829cb5d33 --- /dev/null +++ b/src/dodal/devices/i18/sim_motor_set.py @@ -0,0 +1,88 @@ +import os + +from ophyd import EpicsMotor +from ophyd.sim import Syn2DGauss, SynAxis, SynGauss + + +def create_epics_motor(motor_name="epics_motor", motor_base_pv="ws416-MO-SIM-01:M1"): + print(f"Creating Epics motor for {motor_base_pv}") + epics_motor = EpicsMotor(motor_base_pv, name=motor_name) + # epics_motor.wait_for_connection(timeout=5) # blueapi fails to connect any PVs! + return epics_motor + + +def create_dummy_motor(motor_name="dummy_motor"): + print(f"Creating dummy motor {motor_name}") + return SynAxis(name=motor_name, labels={"motors"}) + + +def create_syn_gaussian(det_name, motor, motor_field, noise="none", noise_multiplier=1): + print(f"Creating synthetic Gaussian detector {det_name}") + syn_gauss = SynGauss( + det_name, motor, motor_field, center=0, Imax=5, sigma=0.5, labels={"detectors"} + ) + syn_gauss.noise.put(noise) + syn_gauss.noise_multiplier.put(noise_multiplier) + return syn_gauss + + +def create_syn_2d_gaussian( + det_name, + motor1, + motor1_field, + motor2, + motor2_field, + noise="none", + noise_multiplier=1, +): + print(f"Creating synthetic 2d Gaussian detector {det_name}") + + syn_gauss = Syn2DGauss( + det_name, + motor1, + motor1_field, + motor2, + motor2_field, + center=0, + Imax=1, + labels={"detectors"}, + ) + syn_gauss.noise.put(noise) + syn_gauss.noise_multiplier.put(noise_multiplier) + return syn_gauss + + +dummy_mot1 = create_dummy_motor("dummy_motor1") +dummy_mot2 = create_dummy_motor("dummy_motor2") +dummy_mot1.delay = 0.05 + + +def dummy_motor1(name: str = "dummy_motor1") -> SynAxis: + return dummy_mot1 + + +def dummy_motor2(name: str = "dummy_motor2") -> SynAxis: + return dummy_mot2 + + +def sim_gauss_det(name: str = "sim_gauss_det") -> SynGauss: + return create_syn_gaussian(name, dummy_mot1, "dummy_motor1") + + +def sim_2d_gauss_det(name: str = "sim_2d_gauss_det") -> Syn2DGauss: + return create_syn_2d_gaussian( + name, dummy_mot1, "dummy_motor1", dummy_mot2, "dummy_motor2" + ) + + +# Make sure EPICS_CA_SERVER_PORT is set to correct value (6064 for DLS sim area detector and motors, 5064 on beamlines) +os.environ["EPICS_CA_SERVER_PORT"] = "6064" + + +# def sim_x(name : str, pv_name : str) -> EpicsMotor: +def sim_x(name: str = "sim_x", pv_name: str = "ws416-MO-SIM-01:M1") -> EpicsMotor: + return create_epics_motor(name, pv_name) + + +def sim_y(name: str = "sim_y", pv_name: str = "ws416-MO-SIM-01:M2") -> EpicsMotor: + return create_epics_motor(name, pv_name) diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py new file mode 100644 index 0000000000..b29134c66a --- /dev/null +++ b/src/dodal/devices/i18/table.py @@ -0,0 +1,14 @@ +from bluesky.protocols import Movable +from ophyd_async.core import ( + StandardReadable, +) +from ophyd_async.epics.motion import Motor + + +class Table(StandardReadable, Movable): + def __init__(self, motion_prefix: str, prefix: str = "", name: str = "") -> None: + with self.add_children_as_readables(): + self.x = Motor(motion_prefix + "X") + self.y = Motor(motion_prefix + "Y") + self.z = Motor(motion_prefix + "Z") + self.theta = Motor(motion_prefix + "THETA") From 7d2ddd3f1b878f776190949f35a89f5a9f7d4476 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 5 Aug 2024 11:20:14 +0100 Subject: [PATCH 52/93] fix some pv values --- src/dodal/beamlines/i18.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 77f387a9b5..92aa10417e 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -99,6 +99,7 @@ def panda1( ) +# NOTE: the reason for skipping is that the odin detectors are not yet supported @skip_device() def xspress3( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False @@ -109,7 +110,7 @@ def xspress3( return device_instantiation( Xspress3, - prefix="-EA-XPS-02:", + prefix="-EA-XSP-02:", name="Xspress3", num_channels=16, wait=wait_for_connection, @@ -150,7 +151,7 @@ def i0( return device_instantiation( TetrammDetector, "i0", - "-EA-XBPM-02:", + "-DI-XBPM-02:", wait_for_connection, fake_with_ophyd_sim, type="Cividec Diamond XBPM", @@ -166,10 +167,10 @@ def it( return device_instantiation( TetrammDetector, "it", - "-EA-TTRM-02:", + "-DI-XBPM-01:", wait_for_connection, fake_with_ophyd_sim, - type="PIN Diode", + type="Tetramm", directory_provider=get_directory_provider(), ) From c19a1479fb74269cdb63d9872624e87a5f456c57 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 5 Aug 2024 16:37:49 +0000 Subject: [PATCH 53/93] move away from ophyd devices --- src/dodal/beamlines/i18.py | 28 ++++++++ src/dodal/devices/i18/sim_detector.py | 18 +++++ src/dodal/devices/i18/sim_motor_set.py | 88 ----------------------- src/dodal/devices/i18/sim_raster_stage.py | 11 +++ 4 files changed, 57 insertions(+), 88 deletions(-) create mode 100644 src/dodal/devices/i18/sim_detector.py delete mode 100644 src/dodal/devices/i18/sim_motor_set.py create mode 100644 src/dodal/devices/i18/sim_raster_stage.py diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 92aa10417e..337a5632a2 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -1,3 +1,4 @@ +import os from pathlib import Path from ophyd_async.panda import HDFPanda @@ -13,6 +14,8 @@ from dodal.common.visit import DirectoryServiceClient, StaticVisitDirectoryProvider from dodal.devices.focusing_mirror import FocusingMirror from dodal.devices.i18.diode import Diode +from dodal.devices.i18.sim_detector import SimDetector +from dodal.devices.i18.sim_raster_stage import RasterStage from dodal.devices.i18.table import Table from dodal.devices.i22.dcm import DoubleCrystalMonochromator from dodal.devices.slits import Slits @@ -23,6 +26,9 @@ from dodal.log import set_beamline as set_log_beamline from dodal.utils import BeamlinePrefix, get_beamline_name, skip_device +# Make sure EPICS_CA_SERVER_PORT is set to correct value (6064 for DLS sim area detector and motors, 5064 on beamlines) +os.environ["EPICS_CA_SERVER_PORT"] = "6064" + BL = get_beamline_name("i18") set_log_beamline(BL) set_utils_beamline(BL) @@ -221,3 +227,25 @@ def table(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) - wait_for_connection, fake_with_ophyd_sim, ) + + +def raster_stage( + wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False +) -> RasterStage: + return device_instantiation( + RasterStage, + "raster_stage", + "-MO-SIM-01:", + wait_for_connection, + fake_with_ophyd_sim, + ) + + +def sim_detector(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False): + return device_instantiation( + SimDetector, + "sim_detector", + "-MO-SIM-01:", + wait_for_connection, + fake_with_ophyd_sim, + ) diff --git a/src/dodal/devices/i18/sim_detector.py b/src/dodal/devices/i18/sim_detector.py new file mode 100644 index 0000000000..2e5d074814 --- /dev/null +++ b/src/dodal/devices/i18/sim_detector.py @@ -0,0 +1,18 @@ +from ophyd_async.epics.motion import Motor +from ophyd_async.sim import PatternGenerator + + +class SimDetector: + def __init__(self, name: str, motor: Motor, motor_field: str): + self.name = name + self.motor = motor + self.motor_field = motor_field + self.pattern_generator = PatternGenerator( + saturation_exposure_time=0.1, detector_height=100, detector_width=100 + ) + + def read(self): + return {self.name: {"value": self.pattern_generator}} + + def describe(self): + return {self.name: {"source": "synthetic", "dtype": "number"}} diff --git a/src/dodal/devices/i18/sim_motor_set.py b/src/dodal/devices/i18/sim_motor_set.py deleted file mode 100644 index e829cb5d33..0000000000 --- a/src/dodal/devices/i18/sim_motor_set.py +++ /dev/null @@ -1,88 +0,0 @@ -import os - -from ophyd import EpicsMotor -from ophyd.sim import Syn2DGauss, SynAxis, SynGauss - - -def create_epics_motor(motor_name="epics_motor", motor_base_pv="ws416-MO-SIM-01:M1"): - print(f"Creating Epics motor for {motor_base_pv}") - epics_motor = EpicsMotor(motor_base_pv, name=motor_name) - # epics_motor.wait_for_connection(timeout=5) # blueapi fails to connect any PVs! - return epics_motor - - -def create_dummy_motor(motor_name="dummy_motor"): - print(f"Creating dummy motor {motor_name}") - return SynAxis(name=motor_name, labels={"motors"}) - - -def create_syn_gaussian(det_name, motor, motor_field, noise="none", noise_multiplier=1): - print(f"Creating synthetic Gaussian detector {det_name}") - syn_gauss = SynGauss( - det_name, motor, motor_field, center=0, Imax=5, sigma=0.5, labels={"detectors"} - ) - syn_gauss.noise.put(noise) - syn_gauss.noise_multiplier.put(noise_multiplier) - return syn_gauss - - -def create_syn_2d_gaussian( - det_name, - motor1, - motor1_field, - motor2, - motor2_field, - noise="none", - noise_multiplier=1, -): - print(f"Creating synthetic 2d Gaussian detector {det_name}") - - syn_gauss = Syn2DGauss( - det_name, - motor1, - motor1_field, - motor2, - motor2_field, - center=0, - Imax=1, - labels={"detectors"}, - ) - syn_gauss.noise.put(noise) - syn_gauss.noise_multiplier.put(noise_multiplier) - return syn_gauss - - -dummy_mot1 = create_dummy_motor("dummy_motor1") -dummy_mot2 = create_dummy_motor("dummy_motor2") -dummy_mot1.delay = 0.05 - - -def dummy_motor1(name: str = "dummy_motor1") -> SynAxis: - return dummy_mot1 - - -def dummy_motor2(name: str = "dummy_motor2") -> SynAxis: - return dummy_mot2 - - -def sim_gauss_det(name: str = "sim_gauss_det") -> SynGauss: - return create_syn_gaussian(name, dummy_mot1, "dummy_motor1") - - -def sim_2d_gauss_det(name: str = "sim_2d_gauss_det") -> Syn2DGauss: - return create_syn_2d_gaussian( - name, dummy_mot1, "dummy_motor1", dummy_mot2, "dummy_motor2" - ) - - -# Make sure EPICS_CA_SERVER_PORT is set to correct value (6064 for DLS sim area detector and motors, 5064 on beamlines) -os.environ["EPICS_CA_SERVER_PORT"] = "6064" - - -# def sim_x(name : str, pv_name : str) -> EpicsMotor: -def sim_x(name: str = "sim_x", pv_name: str = "ws416-MO-SIM-01:M1") -> EpicsMotor: - return create_epics_motor(name, pv_name) - - -def sim_y(name: str = "sim_y", pv_name: str = "ws416-MO-SIM-01:M2") -> EpicsMotor: - return create_epics_motor(name, pv_name) diff --git a/src/dodal/devices/i18/sim_raster_stage.py b/src/dodal/devices/i18/sim_raster_stage.py new file mode 100644 index 0000000000..c2fc598bbe --- /dev/null +++ b/src/dodal/devices/i18/sim_raster_stage.py @@ -0,0 +1,11 @@ +from bluesky.protocols import Movable +from ophyd_async.core import StandardReadable +from ophyd_async.epics.motion import Motor + + +class RasterStage(StandardReadable, Movable): + def __init__(self, prefix: str, name: str = "") -> None: + with self.add_children_as_readables(): + self.offset_in_mm = Motor(prefix + "M1") + self.perp_in_mm = Motor(prefix + "M2") + super().__init__(name) From 9faa28eba457dbd93fd2925cdd40a498cd7117eb Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 19 Aug 2024 16:02:14 +0100 Subject: [PATCH 54/93] add a kb mirror device@ --- src/dodal/beamlines/i18.py | 11 +++++------ src/dodal/devices/i18/KBMirror.py | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 src/dodal/devices/i18/KBMirror.py diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 337a5632a2..efe0363969 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -12,8 +12,8 @@ from dodal.common.beamlines.device_helpers import numbered_slits from dodal.common.crystal_metadata import CrystalMetadata from dodal.common.visit import DirectoryServiceClient, StaticVisitDirectoryProvider -from dodal.devices.focusing_mirror import FocusingMirror from dodal.devices.i18.diode import Diode +from dodal.devices.i18.KBMirror import KBMirror from dodal.devices.i18.sim_detector import SimDetector from dodal.devices.i18.sim_raster_stage import RasterStage from dodal.devices.i18.table import Table @@ -185,9 +185,9 @@ def it( def vfm( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, -) -> FocusingMirror: +) -> KBMirror: return device_instantiation( - FocusingMirror, + KBMirror, "vfm", "-OP-VFM-01:", wait_for_connection, @@ -195,13 +195,12 @@ def vfm( ) -@skip_device def hfm( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, -) -> FocusingMirror: +) -> KBMirror: return device_instantiation( - FocusingMirror, + KBMirror, "hfm", "-OP-HFM-01:", wait_for_connection, diff --git a/src/dodal/devices/i18/KBMirror.py b/src/dodal/devices/i18/KBMirror.py new file mode 100644 index 0000000000..36bf002489 --- /dev/null +++ b/src/dodal/devices/i18/KBMirror.py @@ -0,0 +1,22 @@ +from bluesky.protocols import Movable +from ophyd_async.core import ( + StandardReadable, +) +from ophyd_async.epics.signal import epics_signal_rw + + +class KBMirror(StandardReadable, Movable): + def __init__( + self, + prefix: str, + name: str = "", + ): + self._prefix = prefix + with self.add_children_as_readables(): + self.x = epics_signal_rw(float, prefix + "X") + self.y = epics_signal_rw(float, prefix + "Y") + self.bend1 = epics_signal_rw(float, prefix + "BEND1") + self.bend2 = epics_signal_rw(float, prefix + "BEND2") + self.curve = epics_signal_rw(float, prefix + "CURVE") + self.ellip = epics_signal_rw(float, prefix + "ELLIP") + super().__init__(name=name) From eb132454d83befec04b39e0252cd3a85270fbcc5 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Wed, 21 Aug 2024 11:59:01 +0100 Subject: [PATCH 55/93] test lookup table --- src/dodal/devices/i18/IdGapLookupTable.py | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/dodal/devices/i18/IdGapLookupTable.py diff --git a/src/dodal/devices/i18/IdGapLookupTable.py b/src/dodal/devices/i18/IdGapLookupTable.py new file mode 100644 index 0000000000..f82e7135d0 --- /dev/null +++ b/src/dodal/devices/i18/IdGapLookupTable.py @@ -0,0 +1,25 @@ +from bluesky.protocols import Movable +from ophyd_async.core import ( + AsyncStatus, + StandardReadable, +) + +from dodal.log import LOGGER + + +class LookupTable(StandardReadable, Movable): + def __init__(self, prefix: str, name: str = ""): + with self.add_children_as_readables(): + # self.actual_transmission = epics_signal_r(float, prefix + "MATCH") + print("test") + + super().__init__(name) + + @AsyncStatus.wrap + async def set(self, transmission: float): + LOGGER.debug("Updating the lookup table ") + await self._use_current_energy.trigger() + LOGGER.info(f"Setting desired transmission to {transmission}") + await self._desired_transmission.set(transmission) + LOGGER.debug("Sending change filter command") + await self._change.trigger() From 02a94dd10c652c054ee24da9b1deb18971db8c9d Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Wed, 21 Aug 2024 15:05:10 +0100 Subject: [PATCH 56/93] readjust the value param in the set method --- src/dodal/devices/i18/IdGapLookupTable.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/dodal/devices/i18/IdGapLookupTable.py b/src/dodal/devices/i18/IdGapLookupTable.py index f82e7135d0..a765f3b397 100644 --- a/src/dodal/devices/i18/IdGapLookupTable.py +++ b/src/dodal/devices/i18/IdGapLookupTable.py @@ -16,10 +16,9 @@ def __init__(self, prefix: str, name: str = ""): super().__init__(name) @AsyncStatus.wrap - async def set(self, transmission: float): + async def set(self, value: float): + """ + value: transmission + """ LOGGER.debug("Updating the lookup table ") - await self._use_current_energy.trigger() - LOGGER.info(f"Setting desired transmission to {transmission}") - await self._desired_transmission.set(transmission) - LOGGER.debug("Sending change filter command") - await self._change.trigger() + pass From fd7adc217a3cb6163dbed6350044e5418743720a Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Thu, 12 Sep 2024 13:39:01 +0100 Subject: [PATCH 57/93] adapt to the ophyd-async update --- src/dodal/beamlines/i18.py | 24 ++++++++++++++---------- src/dodal/devices/i18/sim_detector.py | 4 ++-- src/dodal/devices/i18/table.py | 2 +- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index efe0363969..9e9aee8c1b 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -1,23 +1,27 @@ import os from pathlib import Path +from ophyd_async.fastcs.panda import HDFPanda from ophyd_async.panda import HDFPanda from dodal.common.beamlines.beamline_utils import ( device_instantiation, - get_directory_provider, - set_directory_provider, + get_path_provider, + set_path_provider, ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.common.beamlines.device_helpers import numbered_slits from dodal.common.crystal_metadata import CrystalMetadata -from dodal.common.visit import DirectoryServiceClient, StaticVisitDirectoryProvider +from dodal.common.visit import ( + LocalDirectoryServiceClient, + StaticVisitPathProvider, +) from dodal.devices.i18.diode import Diode from dodal.devices.i18.KBMirror import KBMirror from dodal.devices.i18.sim_detector import SimDetector from dodal.devices.i18.sim_raster_stage import RasterStage from dodal.devices.i18.table import Table -from dodal.devices.i22.dcm import DoubleCrystalMonochromator +from dodal.devices.i22.dcm import CrystalMetadata, DoubleCrystalMonochromator from dodal.devices.slits import Slits from dodal.devices.synchrotron import Synchrotron from dodal.devices.tetramm import TetrammDetector @@ -39,11 +43,11 @@ # Communication with GDA is also WIP so for now we determine an arbitrary scan number # locally and write the commissioning directory. The scan number is not guaranteed to # be unique and the data is at risk - this configuration is for testing only. -set_directory_provider( - StaticVisitDirectoryProvider( +set_path_provider( + StaticVisitPathProvider( BL, Path("/dls/i18/data/2024/cm37264-2/bluesky"), - client=DirectoryServiceClient("http://i18-control:8088/api"), + client=LocalDirectoryServiceClient(), ) ) @@ -101,7 +105,7 @@ def panda1( "-MO-PANDA-01:", wait_for_connection, fake_with_ophyd_sim, - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -161,7 +165,7 @@ def i0( wait_for_connection, fake_with_ophyd_sim, type="Cividec Diamond XBPM", - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) @@ -177,7 +181,7 @@ def it( wait_for_connection, fake_with_ophyd_sim, type="Tetramm", - directory_provider=get_directory_provider(), + path_provider=get_path_provider(), ) diff --git a/src/dodal/devices/i18/sim_detector.py b/src/dodal/devices/i18/sim_detector.py index 2e5d074814..2053cbca5f 100644 --- a/src/dodal/devices/i18/sim_detector.py +++ b/src/dodal/devices/i18/sim_detector.py @@ -1,5 +1,5 @@ -from ophyd_async.epics.motion import Motor -from ophyd_async.sim import PatternGenerator +from ophyd_async.epics import Motor +from ophyd_async.sim.demo import PatternGenerator class SimDetector: diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index b29134c66a..5c5c21c1a6 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -2,7 +2,7 @@ from ophyd_async.core import ( StandardReadable, ) -from ophyd_async.epics.motion import Motor +from ophyd_async.epics import Motor class Table(StandardReadable, Movable): From 66161684a665a80a64585c109e89d0c98dbca163 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Fri, 13 Sep 2024 09:02:30 +0000 Subject: [PATCH 58/93] remove the wrong panda import --- src/dodal/beamlines/i18.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 9e9aee8c1b..22bf99492e 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -2,7 +2,6 @@ from pathlib import Path from ophyd_async.fastcs.panda import HDFPanda -from ophyd_async.panda import HDFPanda from dodal.common.beamlines.beamline_utils import ( device_instantiation, From 2dccb097d2f819050fff9586a247bc1bca2cc3b9 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Fri, 13 Sep 2024 13:24:47 +0000 Subject: [PATCH 59/93] fix epics imports --- src/dodal/devices/i18/sim_detector.py | 2 +- src/dodal/devices/i18/table.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dodal/devices/i18/sim_detector.py b/src/dodal/devices/i18/sim_detector.py index 2053cbca5f..69ccbeead7 100644 --- a/src/dodal/devices/i18/sim_detector.py +++ b/src/dodal/devices/i18/sim_detector.py @@ -1,4 +1,4 @@ -from ophyd_async.epics import Motor +from ophyd_async.epics.motor import Motor from ophyd_async.sim.demo import PatternGenerator diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index 5c5c21c1a6..1782b441bd 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -2,7 +2,7 @@ from ophyd_async.core import ( StandardReadable, ) -from ophyd_async.epics import Motor +from ophyd_async.epics.motor import Motor class Table(StandardReadable, Movable): From f9bf3e589082fb6969dc94dc2f2b4a0d892da3ca Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Fri, 13 Sep 2024 13:26:55 +0000 Subject: [PATCH 60/93] fix motor --- src/dodal/devices/i18/sim_raster_stage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dodal/devices/i18/sim_raster_stage.py b/src/dodal/devices/i18/sim_raster_stage.py index c2fc598bbe..a763c1144f 100644 --- a/src/dodal/devices/i18/sim_raster_stage.py +++ b/src/dodal/devices/i18/sim_raster_stage.py @@ -1,6 +1,6 @@ from bluesky.protocols import Movable from ophyd_async.core import StandardReadable -from ophyd_async.epics.motion import Motor +from ophyd_async.epics.motor import Motor class RasterStage(StandardReadable, Movable): From 3f4cbf2d8c08ab701d6185e89495782ad8793168 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 23 Sep 2024 15:26:48 +0100 Subject: [PATCH 61/93] add a set method --- src/dodal/devices/i18/KBMirror.py | 3 +++ src/dodal/devices/i18/sim_raster_stage.py | 7 ++++++- src/dodal/devices/i18/table.py | 3 +++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/dodal/devices/i18/KBMirror.py b/src/dodal/devices/i18/KBMirror.py index 36bf002489..be6705b2f4 100644 --- a/src/dodal/devices/i18/KBMirror.py +++ b/src/dodal/devices/i18/KBMirror.py @@ -20,3 +20,6 @@ def __init__( self.curve = epics_signal_rw(float, prefix + "CURVE") self.ellip = epics_signal_rw(float, prefix + "ELLIP") super().__init__(name=name) + + async def set(self, value: float): + await self.x.set(value) diff --git a/src/dodal/devices/i18/sim_raster_stage.py b/src/dodal/devices/i18/sim_raster_stage.py index a763c1144f..19200a1719 100644 --- a/src/dodal/devices/i18/sim_raster_stage.py +++ b/src/dodal/devices/i18/sim_raster_stage.py @@ -1,5 +1,7 @@ from bluesky.protocols import Movable -from ophyd_async.core import StandardReadable +from ophyd_async.core import ( + StandardReadable, +) from ophyd_async.epics.motor import Motor @@ -9,3 +11,6 @@ def __init__(self, prefix: str, name: str = "") -> None: self.offset_in_mm = Motor(prefix + "M1") self.perp_in_mm = Motor(prefix + "M2") super().__init__(name) + + async def set(self, value: float): + await self.offset_in_mm.set(value) diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index 1782b441bd..71b17798fa 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -12,3 +12,6 @@ def __init__(self, motion_prefix: str, prefix: str = "", name: str = "") -> None self.y = Motor(motion_prefix + "Y") self.z = Motor(motion_prefix + "Z") self.theta = Motor(motion_prefix + "THETA") + + async def set(self, value: float, wait: bool = False): + await self.x.set(value) From 18515b62832a8cf7f40ba9c0eb60cb0da6587823 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 23 Sep 2024 15:02:59 +0000 Subject: [PATCH 62/93] add set wrap asyncstatus --- src/dodal/devices/i18/KBMirror.py | 19 ++++++++++++++----- src/dodal/devices/i18/sim_raster_stage.py | 2 ++ src/dodal/devices/i18/table.py | 4 +++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/dodal/devices/i18/KBMirror.py b/src/dodal/devices/i18/KBMirror.py index be6705b2f4..640f2c3366 100644 --- a/src/dodal/devices/i18/KBMirror.py +++ b/src/dodal/devices/i18/KBMirror.py @@ -1,10 +1,17 @@ +from dataclasses import dataclass + from bluesky.protocols import Movable -from ophyd_async.core import ( - StandardReadable, -) +from ophyd_async.core import AsyncStatus, StandardReadable from ophyd_async.epics.signal import epics_signal_rw +# todo might make a class for common types of movement and use it as a value type +@dataclass +class XYPosition: + x: float + y: float + + class KBMirror(StandardReadable, Movable): def __init__( self, @@ -21,5 +28,7 @@ def __init__( self.ellip = epics_signal_rw(float, prefix + "ELLIP") super().__init__(name=name) - async def set(self, value: float): - await self.x.set(value) + @AsyncStatus.wrap + async def set(self, value: XYPosition): + self.x.set(value.x) + self.y.set(value.y) diff --git a/src/dodal/devices/i18/sim_raster_stage.py b/src/dodal/devices/i18/sim_raster_stage.py index 19200a1719..1b7016be45 100644 --- a/src/dodal/devices/i18/sim_raster_stage.py +++ b/src/dodal/devices/i18/sim_raster_stage.py @@ -1,5 +1,6 @@ from bluesky.protocols import Movable from ophyd_async.core import ( + AsyncStatus, StandardReadable, ) from ophyd_async.epics.motor import Motor @@ -12,5 +13,6 @@ def __init__(self, prefix: str, name: str = "") -> None: self.perp_in_mm = Motor(prefix + "M2") super().__init__(name) + @AsyncStatus.wrap async def set(self, value: float): await self.offset_in_mm.set(value) diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index 71b17798fa..81b19abb1e 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -1,5 +1,6 @@ from bluesky.protocols import Movable from ophyd_async.core import ( + AsyncStatus, StandardReadable, ) from ophyd_async.epics.motor import Motor @@ -13,5 +14,6 @@ def __init__(self, motion_prefix: str, prefix: str = "", name: str = "") -> None self.z = Motor(motion_prefix + "Z") self.theta = Motor(motion_prefix + "THETA") - async def set(self, value: float, wait: bool = False): + @AsyncStatus.wrap + async def set(self, value: float): await self.x.set(value) From f1e03f9d7c38724a02bd5eb63721d5955ae188aa Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 23 Sep 2024 15:31:25 +0000 Subject: [PATCH 63/93] remove the Movable interface --- src/dodal/devices/i18/KBMirror.py | 5 ++--- src/dodal/devices/i18/sim_raster_stage.py | 20 ++++++++++++++------ src/dodal/devices/i18/table.py | 16 ++++++++++++---- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/dodal/devices/i18/KBMirror.py b/src/dodal/devices/i18/KBMirror.py index 640f2c3366..ad71dd7bf3 100644 --- a/src/dodal/devices/i18/KBMirror.py +++ b/src/dodal/devices/i18/KBMirror.py @@ -1,6 +1,5 @@ from dataclasses import dataclass -from bluesky.protocols import Movable from ophyd_async.core import AsyncStatus, StandardReadable from ophyd_async.epics.signal import epics_signal_rw @@ -12,7 +11,7 @@ class XYPosition: y: float -class KBMirror(StandardReadable, Movable): +class KBMirror(StandardReadable): def __init__( self, prefix: str, @@ -29,6 +28,6 @@ def __init__( super().__init__(name=name) @AsyncStatus.wrap - async def set(self, value: XYPosition): + async def set_xy(self, value: XYPosition): self.x.set(value.x) self.y.set(value.y) diff --git a/src/dodal/devices/i18/sim_raster_stage.py b/src/dodal/devices/i18/sim_raster_stage.py index 1b7016be45..c25e2243df 100644 --- a/src/dodal/devices/i18/sim_raster_stage.py +++ b/src/dodal/devices/i18/sim_raster_stage.py @@ -1,4 +1,5 @@ -from bluesky.protocols import Movable +from dataclasses import dataclass + from ophyd_async.core import ( AsyncStatus, StandardReadable, @@ -6,13 +7,20 @@ from ophyd_async.epics.motor import Motor -class RasterStage(StandardReadable, Movable): +@dataclass +class XYPosition: + x: float + y: float + + +class RasterStage(StandardReadable): def __init__(self, prefix: str, name: str = "") -> None: with self.add_children_as_readables(): - self.offset_in_mm = Motor(prefix + "M1") - self.perp_in_mm = Motor(prefix + "M2") + self.x = Motor(prefix + "M1") + self.y = Motor(prefix + "M2") super().__init__(name) @AsyncStatus.wrap - async def set(self, value: float): - await self.offset_in_mm.set(value) + async def set_xy(self, value: XYPosition): + self.x.set(value.x) + self.y.set(value.y) diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index 81b19abb1e..c61c1e7c99 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -1,4 +1,5 @@ -from bluesky.protocols import Movable +from dataclasses import dataclass + from ophyd_async.core import ( AsyncStatus, StandardReadable, @@ -6,7 +7,13 @@ from ophyd_async.epics.motor import Motor -class Table(StandardReadable, Movable): +@dataclass +class XYPosition: + x: float + y: float + + +class Table(StandardReadable): def __init__(self, motion_prefix: str, prefix: str = "", name: str = "") -> None: with self.add_children_as_readables(): self.x = Motor(motion_prefix + "X") @@ -15,5 +22,6 @@ def __init__(self, motion_prefix: str, prefix: str = "", name: str = "") -> None self.theta = Motor(motion_prefix + "THETA") @AsyncStatus.wrap - async def set(self, value: float): - await self.x.set(value) + async def set_xy(self, value: XYPosition): + self.x.set(value.x) + self.y.set(value.y) From 87c9c730a3a7a820bd6da5cc4ad3ac62b6a93be6 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 30 Sep 2024 13:17:33 +0100 Subject: [PATCH 64/93] table error still --- src/dodal/devices/i18/table.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index c61c1e7c99..1995d35a82 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -14,12 +14,12 @@ class XYPosition: class Table(StandardReadable): - def __init__(self, motion_prefix: str, prefix: str = "", name: str = "") -> None: + def __init__(self, prefix: str = "", name: str = "") -> None: with self.add_children_as_readables(): - self.x = Motor(motion_prefix + "X") - self.y = Motor(motion_prefix + "Y") - self.z = Motor(motion_prefix + "Z") - self.theta = Motor(motion_prefix + "THETA") + self.x = Motor(prefix + "X") + self.y = Motor(prefix + "Y") + self.z = Motor(prefix + "Z") + self.theta = Motor(prefix + "THETA") @AsyncStatus.wrap async def set_xy(self, value: XYPosition): From b79b76d37d66f39aed625a1c998106c2a652bb8e Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 30 Sep 2024 15:21:14 +0100 Subject: [PATCH 65/93] devices cleared up --- src/dodal/beamlines/i18.py | 4 ++++ src/dodal/devices/i18/table.py | 1 + 2 files changed, 5 insertions(+) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 22bf99492e..360f96ca22 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -63,6 +63,7 @@ def synchrotron( ) +@skip_device() def undulator( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, @@ -231,6 +232,9 @@ def table(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) - ) +# note this is a mock table, not sure how does it relate to the real Table, +# maybe just a fake option in instantiation is needed +@skip_device() def raster_stage( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False ) -> RasterStage: diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index 1995d35a82..f86dbc0530 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -20,6 +20,7 @@ def __init__(self, prefix: str = "", name: str = "") -> None: self.y = Motor(prefix + "Y") self.z = Motor(prefix + "Z") self.theta = Motor(prefix + "THETA") + super().__init__(name=name) @AsyncStatus.wrap async def set_xy(self, value: XYPosition): From 11b08e38294975d7f524c7224de4e76ac001e665 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Wed, 2 Oct 2024 15:20:57 +0100 Subject: [PATCH 66/93] add testing to the i18 devices --- tests/devices/i18/test_kb_mirror.py | 65 ++++++++++++++++++++++++++ tests/devices/i18/test_sim_detector.py | 35 ++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 tests/devices/i18/test_kb_mirror.py create mode 100644 tests/devices/i18/test_sim_detector.py diff --git a/tests/devices/i18/test_kb_mirror.py b/tests/devices/i18/test_kb_mirror.py new file mode 100644 index 0000000000..d0dc3d5e7b --- /dev/null +++ b/tests/devices/i18/test_kb_mirror.py @@ -0,0 +1,65 @@ +from unittest.mock import ANY + +import pytest +from ophyd_async.core import DeviceCollector, set_mock_value + +from dodal.devices.i18.KBMirror import KBMirror, XYPosition + + +@pytest.fixture +async def kbmirror() -> KBMirror: + """Fixture to set up a mock KBMirror device using DeviceCollector.""" + async with DeviceCollector(mock=True): + kbmirror = KBMirror(prefix="MIRROR:") + return kbmirror + + +async def test_setting_xy_position_kbmirror(kbmirror: KBMirror): + """ + Test setting x and y positions on the KBMirror using the ophyd_async mock tools. + """ + # Mock the initial values of the x and y signals + set_mock_value(kbmirror.x, 0.0) + set_mock_value(kbmirror.y, 0.0) + + # Create a position object + position = XYPosition(x=1.23, y=4.56) + + # Call set_xy to update the position + await kbmirror.set_xy(position) + + reading = await kbmirror.read() + expected_reading = { + "kbmirror-y": { + "value": 4.56, + "timestamp": ANY, + "alarm_severity": 0, + }, + "kbmirror-bend1": { + "value": 0.0, + "timestamp": ANY, + "alarm_severity": 0, + }, + "kbmirror-ellip": { + "value": 0.0, + "timestamp": ANY, + "alarm_severity": 0, + }, + "kbmirror-x": { + "value": 1.23, + "timestamp": ANY, + "alarm_severity": 0, + }, + "kbmirror-bend2": { + "value": 0.0, + "timestamp": ANY, + "alarm_severity": 0, + }, + "kbmirror-curve": { + "value": 0.0, + "timestamp": ANY, + "alarm_severity": 0, + }, + } + + assert reading == expected_reading diff --git a/tests/devices/i18/test_sim_detector.py b/tests/devices/i18/test_sim_detector.py new file mode 100644 index 0000000000..684f80f903 --- /dev/null +++ b/tests/devices/i18/test_sim_detector.py @@ -0,0 +1,35 @@ +from unittest.mock import Mock + +from dodal.devices.i18.sim_detector import SimDetector + + +def test_simdetector_read(): + """ + Test SimDetector.read() returns a pattern generator value in the correct structure. + """ + motor_mock = Mock() + pattern_generator_mock = Mock() + pattern_generator_mock.return_value = {"value": pattern_generator_mock} + + detector = SimDetector(name="detector1", motor=motor_mock, motor_field="position") + detector.pattern_generator = ( + pattern_generator_mock # Replace actual generator with mock for testing + ) + + result = detector.read() + + # Check the structure and value + assert result == {"detector1": {"value": pattern_generator_mock}} + + +def test_simdetector_describe(): + """ + Test SimDetector.describe() returns correct metadata. + """ + motor_mock = Mock() + detector = SimDetector(name="detector1", motor=motor_mock, motor_field="position") + + result = detector.describe() + + # Check the structure and value + assert result == {"detector1": {"source": "synthetic", "dtype": "number"}} From f74346232f5f0dba5467cb5acbf5afe88d280a66 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 7 Oct 2024 18:02:05 +0100 Subject: [PATCH 67/93] respond to feedback --- src/dodal/beamlines/i18.py | 27 ++++++++++++++--------- src/dodal/devices/i18/IdGapLookupTable.py | 24 -------------------- src/dodal/devices/i18/KBMirror.py | 1 - 3 files changed, 17 insertions(+), 35 deletions(-) delete mode 100644 src/dodal/devices/i18/IdGapLookupTable.py diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 360f96ca22..8387420b70 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -29,9 +29,6 @@ from dodal.log import set_beamline as set_log_beamline from dodal.utils import BeamlinePrefix, get_beamline_name, skip_device -# Make sure EPICS_CA_SERVER_PORT is set to correct value (6064 for DLS sim area detector and motors, 5064 on beamlines) -os.environ["EPICS_CA_SERVER_PORT"] = "6064" - BL = get_beamline_name("i18") set_log_beamline(BL) set_utils_beamline(BL) @@ -109,15 +106,9 @@ def panda1( ) -# NOTE: the reason for skipping is that the odin detectors are not yet supported -@skip_device() -def xspress3( +def old_xspress3( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False ) -> Xspress3: - """ - 16 channels Xspress3 detector - """ - return device_instantiation( Xspress3, prefix="-EA-XSP-02:", @@ -128,6 +119,22 @@ def xspress3( ) +# NOTE: the reason for skipping is that the odin detectors are not yet supported +# There is a controls project in the works, not ready anytime soon +@skip_device() +def xspress3_odin( + wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False +) -> Xspress3: + return device_instantiation( + Xspress3, + prefix="-EA-XSP-03:", + name="Xspress3", + num_channels=4, + wait=wait_for_connection, + fake=fake_with_ophyd_sim, + ) + + crystal_1_metadata = CrystalMetadata("Si111") crystal_2_metadata = CrystalMetadata("Si111") diff --git a/src/dodal/devices/i18/IdGapLookupTable.py b/src/dodal/devices/i18/IdGapLookupTable.py deleted file mode 100644 index a765f3b397..0000000000 --- a/src/dodal/devices/i18/IdGapLookupTable.py +++ /dev/null @@ -1,24 +0,0 @@ -from bluesky.protocols import Movable -from ophyd_async.core import ( - AsyncStatus, - StandardReadable, -) - -from dodal.log import LOGGER - - -class LookupTable(StandardReadable, Movable): - def __init__(self, prefix: str, name: str = ""): - with self.add_children_as_readables(): - # self.actual_transmission = epics_signal_r(float, prefix + "MATCH") - print("test") - - super().__init__(name) - - @AsyncStatus.wrap - async def set(self, value: float): - """ - value: transmission - """ - LOGGER.debug("Updating the lookup table ") - pass diff --git a/src/dodal/devices/i18/KBMirror.py b/src/dodal/devices/i18/KBMirror.py index ad71dd7bf3..1c78939dad 100644 --- a/src/dodal/devices/i18/KBMirror.py +++ b/src/dodal/devices/i18/KBMirror.py @@ -4,7 +4,6 @@ from ophyd_async.epics.signal import epics_signal_rw -# todo might make a class for common types of movement and use it as a value type @dataclass class XYPosition: x: float From e753e40eac158e051ade4b097f1fa5f24d633056 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 7 Oct 2024 18:06:26 +0100 Subject: [PATCH 68/93] add testing for the crystal metadata --- src/dodal/beamlines/i18.py | 1 - src/dodal/devices/i18/diode.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 8387420b70..863acc83e7 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -1,4 +1,3 @@ -import os from pathlib import Path from ophyd_async.fastcs.panda import HDFPanda diff --git a/src/dodal/devices/i18/diode.py b/src/dodal/devices/i18/diode.py index 55d7648fad..42cd6a392d 100644 --- a/src/dodal/devices/i18/diode.py +++ b/src/dodal/devices/i18/diode.py @@ -16,5 +16,3 @@ def __init__( super().__init__(name=name) - -# todo should add 'read' override? From 17fa5d3f8a92384efefae2b7b3d4dfda6af8afb3 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 7 Oct 2024 17:18:00 +0000 Subject: [PATCH 69/93] ruff fix --- src/dodal/devices/i18/diode.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dodal/devices/i18/diode.py b/src/dodal/devices/i18/diode.py index 42cd6a392d..85bbd35843 100644 --- a/src/dodal/devices/i18/diode.py +++ b/src/dodal/devices/i18/diode.py @@ -15,4 +15,3 @@ def __init__( self.signal = epics_signal_r(float, prefix + "B:DIODE:I") super().__init__(name=name) - From 7c651ccb5d79f36696e99feca94f320020b6d9c2 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 15 Oct 2024 12:44:35 +0100 Subject: [PATCH 70/93] respond to feedback --- src/dodal/beamlines/i18.py | 42 +++++++++++++++++------ src/dodal/devices/i18/KBMirror.py | 14 +++++--- src/dodal/devices/i18/sim_raster_stage.py | 26 -------------- src/dodal/devices/i18/table.py | 10 +++--- src/dodal/devices/i18/thor_labs_stag.py | 24 +++++++++++++ tests/devices/i18/test_kb_mirror.py | 2 +- 6 files changed, 70 insertions(+), 48 deletions(-) delete mode 100644 src/dodal/devices/i18/sim_raster_stage.py create mode 100644 src/dodal/devices/i18/thor_labs_stag.py diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 863acc83e7..a6f4169e5a 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -17,7 +17,6 @@ from dodal.devices.i18.diode import Diode from dodal.devices.i18.KBMirror import KBMirror from dodal.devices.i18.sim_detector import SimDetector -from dodal.devices.i18.sim_raster_stage import RasterStage from dodal.devices.i18.table import Table from dodal.devices.i22.dcm import CrystalMetadata, DoubleCrystalMonochromator from dodal.devices.slits import Slits @@ -59,7 +58,7 @@ def synchrotron( ) -@skip_device() +@skip_device("not ready yet") def undulator( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, @@ -118,9 +117,11 @@ def old_xspress3( ) -# NOTE: the reason for skipping is that the odin detectors are not yet supported -# There is a controls project in the works, not ready anytime soon -@skip_device() +@skip_device(""" + odin detectors are not yet supported. + There is a controls project in the works, + not ready anytime soon + """) def xspress3_odin( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False ) -> Xspress3: @@ -228,7 +229,9 @@ def diode(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) - ) -def table(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -> Table: +def main_table( + wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False +) -> Table: return device_instantiation( Table, "table", @@ -238,14 +241,31 @@ def table(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) - ) -# note this is a mock table, not sure how does it relate to the real Table, -# maybe just a fake option in instantiation is needed -@skip_device() +def thor_labs_table( + wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False +) -> Table: + return device_instantiation( + Table, + "table", + "-MO-TABLE-02:", + wait_for_connection, + fake_with_ophyd_sim, + ) + + +# SIMULATED DEVICES + + +@skip_device( + """ + this is a mock table, not sure how does it relate to the real Table, maybe just a fake option in instantiation is needed + """ +) def raster_stage( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False -) -> RasterStage: +) -> Table: return device_instantiation( - RasterStage, + Table, "raster_stage", "-MO-SIM-01:", wait_for_connection, diff --git a/src/dodal/devices/i18/KBMirror.py b/src/dodal/devices/i18/KBMirror.py index 1c78939dad..1903a1232b 100644 --- a/src/dodal/devices/i18/KBMirror.py +++ b/src/dodal/devices/i18/KBMirror.py @@ -1,16 +1,15 @@ -from dataclasses import dataclass - +from bluesky.protocols import Movable from ophyd_async.core import AsyncStatus, StandardReadable from ophyd_async.epics.signal import epics_signal_rw +from pydantic import BaseModel -@dataclass -class XYPosition: +class XYPosition(BaseModel): x: float y: float -class KBMirror(StandardReadable): +class KBMirror(StandardReadable, Movable): def __init__( self, prefix: str, @@ -30,3 +29,8 @@ def __init__( async def set_xy(self, value: XYPosition): self.x.set(value.x) self.y.set(value.y) + + @AsyncStatus.wrap + async def set(self, value: XYPosition): + self.x.set(value.x) + self.y.set(value.y) diff --git a/src/dodal/devices/i18/sim_raster_stage.py b/src/dodal/devices/i18/sim_raster_stage.py deleted file mode 100644 index c25e2243df..0000000000 --- a/src/dodal/devices/i18/sim_raster_stage.py +++ /dev/null @@ -1,26 +0,0 @@ -from dataclasses import dataclass - -from ophyd_async.core import ( - AsyncStatus, - StandardReadable, -) -from ophyd_async.epics.motor import Motor - - -@dataclass -class XYPosition: - x: float - y: float - - -class RasterStage(StandardReadable): - def __init__(self, prefix: str, name: str = "") -> None: - with self.add_children_as_readables(): - self.x = Motor(prefix + "M1") - self.y = Motor(prefix + "M2") - super().__init__(name) - - @AsyncStatus.wrap - async def set_xy(self, value: XYPosition): - self.x.set(value.x) - self.y.set(value.y) diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index f86dbc0530..1ebb40fa34 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -1,16 +1,16 @@ -from dataclasses import dataclass - from ophyd_async.core import ( AsyncStatus, StandardReadable, ) from ophyd_async.epics.motor import Motor +from pydantic import BaseModel -@dataclass -class XYPosition: +class Four_D_Position(BaseModel): x: float y: float + z: float + theta: float class Table(StandardReadable): @@ -23,6 +23,6 @@ def __init__(self, prefix: str = "", name: str = "") -> None: super().__init__(name=name) @AsyncStatus.wrap - async def set_xy(self, value: XYPosition): + async def set(self, value: Four_D_Position): self.x.set(value.x) self.y.set(value.y) diff --git a/src/dodal/devices/i18/thor_labs_stag.py b/src/dodal/devices/i18/thor_labs_stag.py new file mode 100644 index 0000000000..b27aec477a --- /dev/null +++ b/src/dodal/devices/i18/thor_labs_stag.py @@ -0,0 +1,24 @@ +from ophyd_async.core import ( + AsyncStatus, + StandardReadable, +) +from ophyd_async.epics.motor import Motor +from pydantic import BaseModel + + +class XYPosition(BaseModel): + x: float + y: float + + +class Table(StandardReadable): + def __init__(self, prefix: str = "", name: str = "") -> None: + with self.add_children_as_readables(): + self.x = Motor(prefix + "X") + self.y = Motor(prefix + "Y") + super().__init__(name=name) + + @AsyncStatus.wrap + async def set(self, value: XYPosition): + self.x.set(value.x) + self.y.set(value.y) diff --git a/tests/devices/i18/test_kb_mirror.py b/tests/devices/i18/test_kb_mirror.py index d0dc3d5e7b..4b2ed5a801 100644 --- a/tests/devices/i18/test_kb_mirror.py +++ b/tests/devices/i18/test_kb_mirror.py @@ -26,7 +26,7 @@ async def test_setting_xy_position_kbmirror(kbmirror: KBMirror): position = XYPosition(x=1.23, y=4.56) # Call set_xy to update the position - await kbmirror.set_xy(position) + await kbmirror.set(position) reading = await kbmirror.read() expected_reading = { From e3a2e0e6302387d56551282b6717e327322c4bed Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Wed, 16 Oct 2024 10:44:41 +0000 Subject: [PATCH 71/93] cancel the skip_device comment --- src/dodal/beamlines/i18.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index a6f4169e5a..7bb3bb0cd4 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -58,7 +58,8 @@ def synchrotron( ) -@skip_device("not ready yet") +# not ready yet +@skip_device() def undulator( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, @@ -117,11 +118,10 @@ def old_xspress3( ) -@skip_device(""" - odin detectors are not yet supported. - There is a controls project in the works, - not ready anytime soon - """) +# odin detectors are not yet supported. +# There is a controls project in the works, +# not ready anytime soon +@skip_device() def xspress3_odin( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False ) -> Xspress3: @@ -256,11 +256,8 @@ def thor_labs_table( # SIMULATED DEVICES -@skip_device( - """ - this is a mock table, not sure how does it relate to the real Table, maybe just a fake option in instantiation is needed - """ -) +# this is a mock table, not sure how does it relate to the real Table, maybe just a fake option in instantiation is needed +@skip_device() def raster_stage( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False ) -> Table: From 3b3fc280a5f0ff11829ca9ea86a3d1c5f2a06f50 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Wed, 16 Oct 2024 12:02:55 +0000 Subject: [PATCH 72/93] cancel the crystal metadata --- src/dodal/beamlines/i18.py | 1 - src/dodal/beamlines/p38.py | 5 ----- src/dodal/devices/i22/dcm.py | 15 +++++++++++++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 7bb3bb0cd4..d8c5214430 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -9,7 +9,6 @@ ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.common.beamlines.device_helpers import numbered_slits -from dodal.common.crystal_metadata import CrystalMetadata from dodal.common.visit import ( LocalDirectoryServiceClient, StaticVisitPathProvider, diff --git a/src/dodal/beamlines/p38.py b/src/dodal/beamlines/p38.py index 2141e720ff..30cb2e5a99 100644 --- a/src/dodal/beamlines/p38.py +++ b/src/dodal/beamlines/p38.py @@ -233,17 +233,12 @@ def dcm( fake_with_ophyd_sim, bl_prefix=False, temperature_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-DI-DCM-01:", -<<<<<<< HEAD crystal_1_metadata=make_crystal_metadata_from_material( MaterialsEnum.Si, (1, 1, 1) ), crystal_2_metadata=make_crystal_metadata_from_material( MaterialsEnum.Si, (1, 1, 1) ), -======= - crystal_1_metadata=CrystalMetadata("Si111"), - crystal_2_metadata=CrystalMetadata("Si111"), ->>>>>>> 7c02f1411 (adapt the tests) ) diff --git a/src/dodal/devices/i22/dcm.py b/src/dodal/devices/i22/dcm.py index 3c84c3e678..e6d5fa3c89 100644 --- a/src/dodal/devices/i22/dcm.py +++ b/src/dodal/devices/i22/dcm.py @@ -1,5 +1,7 @@ import time from collections.abc import Sequence +from dataclasses import dataclass +from typing import Literal import numpy as np from bluesky.protocols import Reading @@ -20,6 +22,19 @@ _CONVERSION_CONSTANT = 12.3984 +@dataclass(frozen=True, unsafe_hash=True) +class CrystalMetadata: + """ + Metadata used in the NeXus format, + see https://manual.nexusformat.org/classes/base_classes/NXcrystal.html + """ + + usage: Literal["Bragg", "Laue"] | None = None + type: str | None = None + reflection: tuple[int, int, int] | None = None + d_spacing: tuple[float, str] | None = None + + class DoubleCrystalMonochromator(StandardReadable): """ A double crystal monochromator (DCM), used to select the energy of the beam. From 553662679d80d6abb6806ae9e617d7e01e08b859 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Wed, 16 Oct 2024 12:14:38 +0000 Subject: [PATCH 73/93] fix imports again --- src/dodal/beamlines/i18.py | 21 ++++++++++++++++----- src/dodal/devices/i22/dcm.py | 2 -- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index d8c5214430..5ecbdd80be 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -134,11 +134,22 @@ def xspress3_odin( ) -crystal_1_metadata = CrystalMetadata("Si111") -crystal_2_metadata = CrystalMetadata("Si111") - -_unused_crystal_metadata_1 = CrystalMetadata("Si311") -_unused_crystal_metadata_2 = CrystalMetadata("Si333") +crystal_1_metadata = ( + CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), + ), +) +crystal_2_metadata = ( + CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), + ), +) def dcm( diff --git a/src/dodal/devices/i22/dcm.py b/src/dodal/devices/i22/dcm.py index e6d5fa3c89..65c1383729 100644 --- a/src/dodal/devices/i22/dcm.py +++ b/src/dodal/devices/i22/dcm.py @@ -15,8 +15,6 @@ from ophyd_async.epics.core import epics_signal_r from ophyd_async.epics.motor import Motor -from dodal.common.crystal_metadata import CrystalMetadata - # Conversion constant for energy and wavelength, taken from the X-Ray data booklet # Converts between energy in KeV and wavelength in angstrom _CONVERSION_CONSTANT = 12.3984 From 568828dc2334035d43a452cb81f4f887f26b8fe2 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 29 Oct 2024 09:55:18 +0000 Subject: [PATCH 74/93] fix dcm i18 test --- src/dodal/beamlines/i18.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 5ecbdd80be..a9bcefb381 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -134,21 +134,18 @@ def xspress3_odin( ) -crystal_1_metadata = ( - CrystalMetadata( - usage="Bragg", - type="silicon", - reflection=(1, 1, 1), - d_spacing=(3.13475, "nm"), - ), +crystal_1_metadata = CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), ) -crystal_2_metadata = ( - CrystalMetadata( - usage="Bragg", - type="silicon", - reflection=(1, 1, 1), - d_spacing=(3.13475, "nm"), - ), + +crystal_2_metadata = CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), ) @@ -159,11 +156,10 @@ def dcm( return device_instantiation( DoubleCrystalMonochromator, "dcm", - "", + f"{BeamlinePrefix(BL).beamline_prefix}-MO-DCM-01:", wait_for_connection, fake_with_ophyd_sim, bl_prefix=False, - motion_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-MO-DCM-01:", temperature_prefix=f"{BeamlinePrefix(BL).beamline_prefix}-DI-DCM-01:", crystal_1_metadata=crystal_1_metadata, crystal_2_metadata=crystal_2_metadata, From 8e42ad98dcf12cef9c83e2aa7fefae3431b3c6fc Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 11 Nov 2024 12:10:21 +0000 Subject: [PATCH 75/93] fix lint --- src/dodal/devices/i22/dcm.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dodal/devices/i22/dcm.py b/src/dodal/devices/i22/dcm.py index 65c1383729..8f00d42037 100644 --- a/src/dodal/devices/i22/dcm.py +++ b/src/dodal/devices/i22/dcm.py @@ -1,5 +1,4 @@ import time -from collections.abc import Sequence from dataclasses import dataclass from typing import Literal From 8e17e2f3160bc980698786ebec9b18b7d5b2c618 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 11 Nov 2024 12:17:50 +0000 Subject: [PATCH 76/93] fix import --- src/dodal/devices/i22/dcm.py | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/dodal/devices/i22/dcm.py b/src/dodal/devices/i22/dcm.py index 8f00d42037..58180ad9b6 100644 --- a/src/dodal/devices/i22/dcm.py +++ b/src/dodal/devices/i22/dcm.py @@ -1,6 +1,4 @@ import time -from dataclasses import dataclass -from typing import Literal import numpy as np from bluesky.protocols import Reading @@ -14,24 +12,13 @@ from ophyd_async.epics.core import epics_signal_r from ophyd_async.epics.motor import Motor +from dodal.common.crystal_metadata import CrystalMetadata + # Conversion constant for energy and wavelength, taken from the X-Ray data booklet # Converts between energy in KeV and wavelength in angstrom _CONVERSION_CONSTANT = 12.3984 -@dataclass(frozen=True, unsafe_hash=True) -class CrystalMetadata: - """ - Metadata used in the NeXus format, - see https://manual.nexusformat.org/classes/base_classes/NXcrystal.html - """ - - usage: Literal["Bragg", "Laue"] | None = None - type: str | None = None - reflection: tuple[int, int, int] | None = None - d_spacing: tuple[float, str] | None = None - - class DoubleCrystalMonochromator(StandardReadable): """ A double crystal monochromator (DCM), used to select the energy of the beam. From c239359401b00b5485b59414399a19fcffb45309 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 11 Nov 2024 12:33:14 +0000 Subject: [PATCH 77/93] add test to increase covereage --- src/dodal/devices/i18/KBMirror.py | 5 --- tests/devices/i18/test_kb_mirror.py | 2 +- tests/devices/i18/test_table.py | 50 +++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 tests/devices/i18/test_table.py diff --git a/src/dodal/devices/i18/KBMirror.py b/src/dodal/devices/i18/KBMirror.py index 1903a1232b..55d95e6018 100644 --- a/src/dodal/devices/i18/KBMirror.py +++ b/src/dodal/devices/i18/KBMirror.py @@ -25,11 +25,6 @@ def __init__( self.ellip = epics_signal_rw(float, prefix + "ELLIP") super().__init__(name=name) - @AsyncStatus.wrap - async def set_xy(self, value: XYPosition): - self.x.set(value.x) - self.y.set(value.y) - @AsyncStatus.wrap async def set(self, value: XYPosition): self.x.set(value.x) diff --git a/tests/devices/i18/test_kb_mirror.py b/tests/devices/i18/test_kb_mirror.py index 4b2ed5a801..1eff890564 100644 --- a/tests/devices/i18/test_kb_mirror.py +++ b/tests/devices/i18/test_kb_mirror.py @@ -25,7 +25,7 @@ async def test_setting_xy_position_kbmirror(kbmirror: KBMirror): # Create a position object position = XYPosition(x=1.23, y=4.56) - # Call set_xy to update the position + # Call set to update the position await kbmirror.set(position) reading = await kbmirror.read() diff --git a/tests/devices/i18/test_table.py b/tests/devices/i18/test_table.py new file mode 100644 index 0000000000..900339894a --- /dev/null +++ b/tests/devices/i18/test_table.py @@ -0,0 +1,50 @@ +from unittest.mock import ANY + +import pytest +from ophyd_async.core import DeviceCollector, set_mock_value + +from dodal.devices.i18.table import Four_D_Position, Table + + +@pytest.fixture +async def table() -> Table: + """Fixture to set up a mock Table device using DeviceCollector.""" + async with DeviceCollector(mock=True): + table = Table(prefix="MIRROR:") + return table + + +async def test_setting_xy_position_table(table: Table): + """ + Test setting x and y positions on the Table using the ophyd_async mock tools. + """ + set_mock_value(table.x.user_readback, 1.23) + set_mock_value(table.y.user_readback, 4.56) + + # Create a position object + position = Four_D_Position(x=1.23, y=4.56, z=0.0, theta=0.0) + + # Call set to update the position + await table.set(position) + + reading = await table.read() + expected_reading = { + "table-y": { + "value": 4.56, + "timestamp": ANY, + "alarm_severity": 0, + }, + "table-x": { + "value": 1.23, + "timestamp": ANY, + "alarm_severity": 0, + }, + "table-theta": { + "alarm_severity": 0, + "timestamp": ANY, + "value": 0.0, + }, + "table-z": {"alarm_severity": 0, "timestamp": ANY, "value": 0.0}, + } + + assert reading == expected_reading From 1e7f4a26579b6d704e6b4e8a65b961b9066c7e6c Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Fri, 22 Nov 2024 14:44:56 +0000 Subject: [PATCH 78/93] update the imports --- src/dodal/devices/i18/KBMirror.py | 2 +- src/dodal/devices/i18/diode.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dodal/devices/i18/KBMirror.py b/src/dodal/devices/i18/KBMirror.py index 55d95e6018..baf69f67a0 100644 --- a/src/dodal/devices/i18/KBMirror.py +++ b/src/dodal/devices/i18/KBMirror.py @@ -1,6 +1,6 @@ from bluesky.protocols import Movable from ophyd_async.core import AsyncStatus, StandardReadable -from ophyd_async.epics.signal import epics_signal_rw +from ophyd_async.epics.core import epics_signal_rw from pydantic import BaseModel diff --git a/src/dodal/devices/i18/diode.py b/src/dodal/devices/i18/diode.py index 85bbd35843..f0c9526adc 100644 --- a/src/dodal/devices/i18/diode.py +++ b/src/dodal/devices/i18/diode.py @@ -1,7 +1,7 @@ from ophyd_async.core import ( StandardReadable, ) -from ophyd_async.epics.signal import epics_signal_r +from ophyd_async.epics.core import epics_signal_r class Diode(StandardReadable): From 5fe2ca22b6f1265c83167a5892ae4d5b08f6e2d5 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 25 Nov 2024 17:24:57 +0000 Subject: [PATCH 79/93] respond to feedback --- src/dodal/beamlines/i18.py | 40 ++++++++++++------------- src/dodal/beamlines/i22.py | 4 +-- src/dodal/devices/i18/sim_detector.py | 18 ----------- src/dodal/devices/i18/thor_labs_stag.py | 24 --------------- 4 files changed, 22 insertions(+), 64 deletions(-) delete mode 100644 src/dodal/devices/i18/sim_detector.py delete mode 100644 src/dodal/devices/i18/thor_labs_stag.py diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index a9bcefb381..d48b935248 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -104,13 +104,13 @@ def panda1( ) -def old_xspress3( +def xspress3( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False ) -> Xspress3: return device_instantiation( Xspress3, prefix="-EA-XSP-02:", - name="Xspress3", + name="xspress3", num_channels=16, wait=wait_for_connection, fake=fake_with_ophyd_sim, @@ -127,32 +127,31 @@ def xspress3_odin( return device_instantiation( Xspress3, prefix="-EA-XSP-03:", - name="Xspress3", + name="xspress3_odin", num_channels=4, wait=wait_for_connection, fake=fake_with_ophyd_sim, ) -crystal_1_metadata = CrystalMetadata( - usage="Bragg", - type="silicon", - reflection=(1, 1, 1), - d_spacing=(3.13475, "nm"), -) - -crystal_2_metadata = CrystalMetadata( - usage="Bragg", - type="silicon", - reflection=(1, 1, 1), - d_spacing=(3.13475, "nm"), -) - - def dcm( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, ) -> DoubleCrystalMonochromator: + crystal_1_metadata = CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), + ) + + crystal_2_metadata = CrystalMetadata( + usage="Bragg", + type="silicon", + reflection=(1, 1, 1), + d_spacing=(3.13475, "nm"), + ) + return device_instantiation( DoubleCrystalMonochromator, "dcm", @@ -225,10 +224,11 @@ def hfm( ) -def diode(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) -> Diode: +def d7diode( + wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False +) -> Diode: return device_instantiation( Diode, - "diodad7bdiode", "-DI-PHDGN-07:", wait_for_connection, fake_with_ophyd_sim, diff --git a/src/dodal/beamlines/i22.py b/src/dodal/beamlines/i22.py index b80c12e03e..da158488f2 100644 --- a/src/dodal/beamlines/i22.py +++ b/src/dodal/beamlines/i22.py @@ -159,8 +159,8 @@ def undulator() -> Undulator: return Undulator( prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:", id_gap_lookup_table_path="/dls_sw/i22/software/daq_configuration/lookup/BeamLine_Undulator_toGap.txt", - poles=80, - length=2.0, + poles=None, # todo need to double check for the metadata to be valid + length=None, # todo need to double check for the metadata to be valid ) diff --git a/src/dodal/devices/i18/sim_detector.py b/src/dodal/devices/i18/sim_detector.py deleted file mode 100644 index 69ccbeead7..0000000000 --- a/src/dodal/devices/i18/sim_detector.py +++ /dev/null @@ -1,18 +0,0 @@ -from ophyd_async.epics.motor import Motor -from ophyd_async.sim.demo import PatternGenerator - - -class SimDetector: - def __init__(self, name: str, motor: Motor, motor_field: str): - self.name = name - self.motor = motor - self.motor_field = motor_field - self.pattern_generator = PatternGenerator( - saturation_exposure_time=0.1, detector_height=100, detector_width=100 - ) - - def read(self): - return {self.name: {"value": self.pattern_generator}} - - def describe(self): - return {self.name: {"source": "synthetic", "dtype": "number"}} diff --git a/src/dodal/devices/i18/thor_labs_stag.py b/src/dodal/devices/i18/thor_labs_stag.py deleted file mode 100644 index b27aec477a..0000000000 --- a/src/dodal/devices/i18/thor_labs_stag.py +++ /dev/null @@ -1,24 +0,0 @@ -from ophyd_async.core import ( - AsyncStatus, - StandardReadable, -) -from ophyd_async.epics.motor import Motor -from pydantic import BaseModel - - -class XYPosition(BaseModel): - x: float - y: float - - -class Table(StandardReadable): - def __init__(self, prefix: str = "", name: str = "") -> None: - with self.add_children_as_readables(): - self.x = Motor(prefix + "X") - self.y = Motor(prefix + "Y") - super().__init__(name=name) - - @AsyncStatus.wrap - async def set(self, value: XYPosition): - self.x.set(value.x) - self.y.set(value.y) From fe7ff5e0bad8a1418394037749ff79621fc901cf Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 26 Nov 2024 15:21:30 +0000 Subject: [PATCH 80/93] delete test for sim detector (old) --- tests/devices/i18/test_sim_detector.py | 35 -------------------------- 1 file changed, 35 deletions(-) delete mode 100644 tests/devices/i18/test_sim_detector.py diff --git a/tests/devices/i18/test_sim_detector.py b/tests/devices/i18/test_sim_detector.py deleted file mode 100644 index 684f80f903..0000000000 --- a/tests/devices/i18/test_sim_detector.py +++ /dev/null @@ -1,35 +0,0 @@ -from unittest.mock import Mock - -from dodal.devices.i18.sim_detector import SimDetector - - -def test_simdetector_read(): - """ - Test SimDetector.read() returns a pattern generator value in the correct structure. - """ - motor_mock = Mock() - pattern_generator_mock = Mock() - pattern_generator_mock.return_value = {"value": pattern_generator_mock} - - detector = SimDetector(name="detector1", motor=motor_mock, motor_field="position") - detector.pattern_generator = ( - pattern_generator_mock # Replace actual generator with mock for testing - ) - - result = detector.read() - - # Check the structure and value - assert result == {"detector1": {"value": pattern_generator_mock}} - - -def test_simdetector_describe(): - """ - Test SimDetector.describe() returns correct metadata. - """ - motor_mock = Mock() - detector = SimDetector(name="detector1", motor=motor_mock, motor_field="position") - - result = detector.describe() - - # Check the structure and value - assert result == {"detector1": {"source": "synthetic", "dtype": "number"}} From a3f8b1ad407d6994daa9fb6fbe315e58fbae9d41 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 26 Nov 2024 15:22:21 +0000 Subject: [PATCH 81/93] delete sim detector --- src/dodal/beamlines/i18.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index d48b935248..8b1cdc9cc1 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -15,7 +15,6 @@ ) from dodal.devices.i18.diode import Diode from dodal.devices.i18.KBMirror import KBMirror -from dodal.devices.i18.sim_detector import SimDetector from dodal.devices.i18.table import Table from dodal.devices.i22.dcm import CrystalMetadata, DoubleCrystalMonochromator from dodal.devices.slits import Slits @@ -274,13 +273,3 @@ def raster_stage( wait_for_connection, fake_with_ophyd_sim, ) - - -def sim_detector(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False): - return device_instantiation( - SimDetector, - "sim_detector", - "-MO-SIM-01:", - wait_for_connection, - fake_with_ophyd_sim, - ) From b5fef2dd2a77dacf8d4e89f7f276e06a41c625d2 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 26 Nov 2024 16:02:51 +0000 Subject: [PATCH 82/93] add name to diode --- src/dodal/beamlines/i18.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 8b1cdc9cc1..d6336fd7e2 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -228,6 +228,7 @@ def d7diode( ) -> Diode: return device_instantiation( Diode, + "d7diode", "-DI-PHDGN-07:", wait_for_connection, fake_with_ophyd_sim, From 05f2f508c4d15f344f3896b37315894c325a2656 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 26 Nov 2024 17:07:29 +0000 Subject: [PATCH 83/93] restore the thor labs stage and cancel the xpress3 not working one --- src/dodal/beamlines/i18.py | 28 ++++++++++++------------ src/dodal/devices/i18/thor_labs_stage.py | 24 ++++++++++++++++++++ 2 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 src/dodal/devices/i18/thor_labs_stage.py diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index d6336fd7e2..5260e746fe 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -16,6 +16,7 @@ from dodal.devices.i18.diode import Diode from dodal.devices.i18.KBMirror import KBMirror from dodal.devices.i18.table import Table +from dodal.devices.i18.thor_labs_stage import ThorLabsStage from dodal.devices.i22.dcm import CrystalMetadata, DoubleCrystalMonochromator from dodal.devices.slits import Slits from dodal.devices.synchrotron import Synchrotron @@ -103,19 +104,6 @@ def panda1( ) -def xspress3( - wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False -) -> Xspress3: - return device_instantiation( - Xspress3, - prefix="-EA-XSP-02:", - name="xspress3", - num_channels=16, - wait=wait_for_connection, - fake=fake_with_ophyd_sim, - ) - - # odin detectors are not yet supported. # There is a controls project in the works, # not ready anytime soon @@ -125,7 +113,7 @@ def xspress3_odin( ) -> Xspress3: return device_instantiation( Xspress3, - prefix="-EA-XSP-03:", + prefix="-EA-XSP-02:", name="xspress3_odin", num_channels=4, wait=wait_for_connection, @@ -274,3 +262,15 @@ def raster_stage( wait_for_connection, fake_with_ophyd_sim, ) + + +def thor_labs_table( + wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False +) -> ThorLabsStage: + return device_instantiation( + ThorLabsStage, + "table", + "-MO-TABLE-02:", + wait_for_connection, + fake_with_ophyd_sim, + ) diff --git a/src/dodal/devices/i18/thor_labs_stage.py b/src/dodal/devices/i18/thor_labs_stage.py new file mode 100644 index 0000000000..6577464b13 --- /dev/null +++ b/src/dodal/devices/i18/thor_labs_stage.py @@ -0,0 +1,24 @@ +from ophyd_async.core import ( + AsyncStatus, + StandardReadable, +) +from ophyd_async.epics.motor import Motor +from pydantic import BaseModel + + +class XYPosition(BaseModel): + x: float + y: float + + +class ThorLabsStage(StandardReadable): + def __init__(self, prefix: str = "", name: str = "") -> None: + with self.add_children_as_readables(): + self.x = Motor(prefix + "X") + self.y = Motor(prefix + "Y") + super().__init__(name=name) + + @AsyncStatus.wrap + async def set(self, value: XYPosition): + self.x.set(value.x) + self.y.set(value.y) From 5b8fec430ebad9686712c3a2ba9d50591c6507b2 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 26 Nov 2024 17:08:46 +0000 Subject: [PATCH 84/93] make all connect --- src/dodal/beamlines/i18.py | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 5260e746fe..e5cf5e8940 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -237,10 +237,10 @@ def main_table( def thor_labs_table( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False -) -> Table: +) -> ThorLabsStage: return device_instantiation( - Table, - "table", + ThorLabsStage, + "thor_labs_stage", "-MO-TABLE-02:", wait_for_connection, fake_with_ophyd_sim, @@ -262,15 +262,3 @@ def raster_stage( wait_for_connection, fake_with_ophyd_sim, ) - - -def thor_labs_table( - wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False -) -> ThorLabsStage: - return device_instantiation( - ThorLabsStage, - "table", - "-MO-TABLE-02:", - wait_for_connection, - fake_with_ophyd_sim, - ) From 3f2a7c3f62cc6f621325f27c255d7850ad498c83 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 14 Jan 2025 17:02:44 +0000 Subject: [PATCH 85/93] fix the tests imports --- tests/devices/i18/test_kb_mirror.py | 3 ++- tests/devices/i18/test_table.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/devices/i18/test_kb_mirror.py b/tests/devices/i18/test_kb_mirror.py index 1eff890564..c66486cf40 100644 --- a/tests/devices/i18/test_kb_mirror.py +++ b/tests/devices/i18/test_kb_mirror.py @@ -1,7 +1,8 @@ from unittest.mock import ANY import pytest -from ophyd_async.core import DeviceCollector, set_mock_value +from ophyd_async.core import DeviceCollector +from ophyd_async.testing import set_mock_value from dodal.devices.i18.KBMirror import KBMirror, XYPosition diff --git a/tests/devices/i18/test_table.py b/tests/devices/i18/test_table.py index 900339894a..80f816312c 100644 --- a/tests/devices/i18/test_table.py +++ b/tests/devices/i18/test_table.py @@ -1,7 +1,8 @@ from unittest.mock import ANY import pytest -from ophyd_async.core import DeviceCollector, set_mock_value +from ophyd_async.core import DeviceCollector +from ophyd_async.testing import set_mock_value from dodal.devices.i18.table import Four_D_Position, Table From 7d665227ffd574c045ad1d2bd9652091859deb51 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Fri, 24 Jan 2025 11:50:55 +0000 Subject: [PATCH 86/93] respond to feedback --- src/dodal/beamlines/i18.py | 6 ++++-- src/dodal/beamlines/i22.py | 4 ++-- src/dodal/devices/i18/table.py | 10 +++++----- tests/devices/i18/test_table.py | 4 ++-- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index e5cf5e8940..a3cfee29dd 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -70,8 +70,8 @@ def undulator( wait_for_connection, fake_with_ophyd_sim, bl_prefix=False, - poles=80, - length=2.0, + poles=None, # todo check the real values + length=None, # todo check the real values ) @@ -121,6 +121,7 @@ def xspress3_odin( ) +@skip_device def dcm( wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False, @@ -138,6 +139,7 @@ def dcm( reflection=(1, 1, 1), d_spacing=(3.13475, "nm"), ) + # todo double check if crystal metdata is correct return device_instantiation( DoubleCrystalMonochromator, diff --git a/src/dodal/beamlines/i22.py b/src/dodal/beamlines/i22.py index da158488f2..1e6548dd88 100644 --- a/src/dodal/beamlines/i22.py +++ b/src/dodal/beamlines/i22.py @@ -159,8 +159,8 @@ def undulator() -> Undulator: return Undulator( prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:", id_gap_lookup_table_path="/dls_sw/i22/software/daq_configuration/lookup/BeamLine_Undulator_toGap.txt", - poles=None, # todo need to double check for the metadata to be valid - length=None, # todo need to double check for the metadata to be valid + poles=80, + length=2.0 ) diff --git a/src/dodal/devices/i18/table.py b/src/dodal/devices/i18/table.py index 1ebb40fa34..1ecc0e1728 100644 --- a/src/dodal/devices/i18/table.py +++ b/src/dodal/devices/i18/table.py @@ -6,15 +6,15 @@ from pydantic import BaseModel -class Four_D_Position(BaseModel): +class TablePosition(BaseModel): x: float y: float - z: float - theta: float + z: float | None = None + theta: float | None = None class Table(StandardReadable): - def __init__(self, prefix: str = "", name: str = "") -> None: + def __init__(self, prefix: str, name: str = "") -> None: with self.add_children_as_readables(): self.x = Motor(prefix + "X") self.y = Motor(prefix + "Y") @@ -23,6 +23,6 @@ def __init__(self, prefix: str = "", name: str = "") -> None: super().__init__(name=name) @AsyncStatus.wrap - async def set(self, value: Four_D_Position): + async def set(self, value: TablePosition): self.x.set(value.x) self.y.set(value.y) diff --git a/tests/devices/i18/test_table.py b/tests/devices/i18/test_table.py index 80f816312c..7879ca4b66 100644 --- a/tests/devices/i18/test_table.py +++ b/tests/devices/i18/test_table.py @@ -4,7 +4,7 @@ from ophyd_async.core import DeviceCollector from ophyd_async.testing import set_mock_value -from dodal.devices.i18.table import Four_D_Position, Table +from dodal.devices.i18.table import Table, TablePosition @pytest.fixture @@ -23,7 +23,7 @@ async def test_setting_xy_position_table(table: Table): set_mock_value(table.y.user_readback, 4.56) # Create a position object - position = Four_D_Position(x=1.23, y=4.56, z=0.0, theta=0.0) + position = TablePosition(x=1.23, y=4.56, z=0.0, theta=0.0) # Call set to update the position await table.set(position) From cfc9f78ebcbe78641874cdd6979632218172de8c Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 27 Jan 2025 11:39:19 +0000 Subject: [PATCH 87/93] fix lint --- src/dodal/beamlines/i22.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dodal/beamlines/i22.py b/src/dodal/beamlines/i22.py index 1e6548dd88..b80c12e03e 100644 --- a/src/dodal/beamlines/i22.py +++ b/src/dodal/beamlines/i22.py @@ -160,7 +160,7 @@ def undulator() -> Undulator: prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:", id_gap_lookup_table_path="/dls_sw/i22/software/daq_configuration/lookup/BeamLine_Undulator_toGap.txt", poles=80, - length=2.0 + length=2.0, ) From f111987de9d7d9e39e06e704e6c23fb5a57259ec Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 27 Jan 2025 15:09:28 +0000 Subject: [PATCH 88/93] apply some feedback --- src/dodal/devices/i18/thor_labs_stage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dodal/devices/i18/thor_labs_stage.py b/src/dodal/devices/i18/thor_labs_stage.py index 6577464b13..9684ab5425 100644 --- a/src/dodal/devices/i18/thor_labs_stage.py +++ b/src/dodal/devices/i18/thor_labs_stage.py @@ -12,7 +12,7 @@ class XYPosition(BaseModel): class ThorLabsStage(StandardReadable): - def __init__(self, prefix: str = "", name: str = "") -> None: + def __init__(self, prefix: str, name: str = "") -> None: with self.add_children_as_readables(): self.x = Motor(prefix + "X") self.y = Motor(prefix + "Y") From 5fbf51e768081dec2cbc02ca5d3e551518dab8f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Malinowski?= <56644812+stan-dot@users.noreply.github.com> Date: Mon, 27 Jan 2025 16:26:30 +0000 Subject: [PATCH 89/93] Update src/dodal/beamlines/i22.py Co-authored-by: Joseph Ware <53935796+DiamondJoseph@users.noreply.github.com> --- src/dodal/beamlines/i22.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dodal/beamlines/i22.py b/src/dodal/beamlines/i22.py index da158488f2..b80c12e03e 100644 --- a/src/dodal/beamlines/i22.py +++ b/src/dodal/beamlines/i22.py @@ -159,8 +159,8 @@ def undulator() -> Undulator: return Undulator( prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:", id_gap_lookup_table_path="/dls_sw/i22/software/daq_configuration/lookup/BeamLine_Undulator_toGap.txt", - poles=None, # todo need to double check for the metadata to be valid - length=None, # todo need to double check for the metadata to be valid + poles=80, + length=2.0, ) From 10b54a71d1fde36d79669306735581258d583fcb Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 27 Jan 2025 16:37:27 +0000 Subject: [PATCH 90/93] add issue link to vfm error --- src/dodal/beamlines/i18.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 80adc377d1..2a644f8226 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -115,7 +115,7 @@ def it() -> TetrammDetector: @device_factory(skip=True) -# VFM uses different IOC than HFM +# VFM uses different IOC than HFM https://github.com/DiamondLightSource/dodal/issues/1009 def vfm() -> KBMirror: return KBMirror(f"{PREFIX.beamline_prefix}-OP-VFM-01:") From 8171e9621460b482dfb686594bae5aa0dbc10a80 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Mon, 27 Jan 2025 17:05:15 +0000 Subject: [PATCH 91/93] add the tests for i18 --- .../unit_tests/i18/test_thor_labs_stage.py | 17 ++++++++++++++ tests/devices/unit_tests/test_table.py | 22 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/devices/unit_tests/i18/test_thor_labs_stage.py create mode 100644 tests/devices/unit_tests/test_table.py diff --git a/tests/devices/unit_tests/i18/test_thor_labs_stage.py b/tests/devices/unit_tests/i18/test_thor_labs_stage.py new file mode 100644 index 0000000000..38dc4a0b0a --- /dev/null +++ b/tests/devices/unit_tests/i18/test_thor_labs_stage.py @@ -0,0 +1,17 @@ +import pytest +from ophyd_async.core import DeviceCollector + +from dodal.devices.i18.thor_labs_stage import ThorLabsStage, XYPosition + + +@pytest.fixture +async def fake_thor_labs_stage(): + async with DeviceCollector(mock=True): + fake_thor_labs_stage = ThorLabsStage("", "thor_labs_stage") + + return fake_thor_labs_stage + + +async def test_setting(fake_thor_labs_stage: ThorLabsStage): + pos = XYPosition(x=5, y=5) + await fake_thor_labs_stage.set(pos) diff --git a/tests/devices/unit_tests/test_table.py b/tests/devices/unit_tests/test_table.py new file mode 100644 index 0000000000..cf345c24dd --- /dev/null +++ b/tests/devices/unit_tests/test_table.py @@ -0,0 +1,22 @@ +import pytest +from ophyd_async.core import DeviceCollector + +from dodal.devices.i18.table import Table, TablePosition + + +@pytest.fixture +async def fake_table(): + async with DeviceCollector(mock=True): + fake_thor_labs_stage = Table("", "thor_labs_stage") + + return fake_thor_labs_stage + + +async def test_setting_xy(fake_table: Table): + pos = TablePosition(x=5, y=5) + await fake_table.set(pos) + + +async def test_setting_xyztheta(fake_table: Table): + pos = TablePosition(x=5, y=5, z=5, theta=5) + await fake_table.set(pos) From de2d3c62b47fd48311f50cab18b0ba261de41bd5 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 28 Jan 2025 11:06:36 +0000 Subject: [PATCH 92/93] adjust the dcm for i18 to have no metadata --- src/dodal/beamlines/i18.py | 16 --------- src/dodal/devices/i22/dcm.py | 66 +++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 48 deletions(-) diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 2a644f8226..9e07cd4c95 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -57,25 +57,9 @@ def undulator() -> Undulator: @device_factory() def dcm() -> DoubleCrystalMonochromator: - crystal_1_metadata = CrystalMetadata( - usage="Bragg", - type="silicon", - reflection=(1, 1, 1), - d_spacing=(3.13475, "nm"), - ) - - crystal_2_metadata = CrystalMetadata( - usage="Bragg", - type="silicon", - reflection=(1, 1, 1), - d_spacing=(3.13475, "nm"), - ) - return DoubleCrystalMonochromator( prefix=f"{PREFIX.beamline_prefix}-MO-DCM-01:", temperature_prefix=f"{PREFIX.beamline_prefix}-DI-DCM-01:", - crystal_1_metadata=crystal_1_metadata, - crystal_2_metadata=crystal_2_metadata, ) diff --git a/src/dodal/devices/i22/dcm.py b/src/dodal/devices/i22/dcm.py index 58180ad9b6..b26847777c 100644 --- a/src/dodal/devices/i22/dcm.py +++ b/src/dodal/devices/i22/dcm.py @@ -32,8 +32,8 @@ class DoubleCrystalMonochromator(StandardReadable): def __init__( self, temperature_prefix: str, - crystal_1_metadata: CrystalMetadata, - crystal_2_metadata: CrystalMetadata, + crystal_1_metadata: CrystalMetadata | None = None, + crystal_2_metadata: CrystalMetadata | None = None, prefix: str = "", name: str = "", ) -> None: @@ -62,36 +62,38 @@ def __init__( # Soft metadata # If supplied include crystal details in output of read_configuration with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL): - self.crystal_1_usage, _ = soft_signal_r_and_setter( - str, initial_value=crystal_1_metadata.usage - ) - self.crystal_1_type, _ = soft_signal_r_and_setter( - str, initial_value=crystal_1_metadata.type - ) - self.crystal_1_reflection, _ = soft_signal_r_and_setter( - Array1D[np.int32], - initial_value=np.array(crystal_1_metadata.reflection), - ) - self.crystal_1_d_spacing, _ = soft_signal_r_and_setter( - float, - initial_value=crystal_1_metadata.d_spacing[0], - units=crystal_1_metadata.d_spacing[1], - ) - self.crystal_2_usage, _ = soft_signal_r_and_setter( - str, initial_value=crystal_2_metadata.usage - ) - self.crystal_2_type, _ = soft_signal_r_and_setter( - str, initial_value=crystal_2_metadata.type - ) - self.crystal_2_reflection, _ = soft_signal_r_and_setter( - Array1D[np.int32], - initial_value=np.array(crystal_2_metadata.reflection), - ) - self.crystal_2_d_spacing, _ = soft_signal_r_and_setter( - float, - initial_value=crystal_2_metadata.d_spacing[0], - units=crystal_2_metadata.d_spacing[1], - ) + if crystal_1_metadata is not None: + self.crystal_1_usage, _ = soft_signal_r_and_setter( + str, initial_value=crystal_1_metadata.usage + ) + self.crystal_1_type, _ = soft_signal_r_and_setter( + str, initial_value=crystal_1_metadata.type + ) + self.crystal_1_reflection, _ = soft_signal_r_and_setter( + Array1D[np.int32], + initial_value=np.array(crystal_1_metadata.reflection), + ) + self.crystal_1_d_spacing, _ = soft_signal_r_and_setter( + float, + initial_value=crystal_1_metadata.d_spacing[0], + units=crystal_1_metadata.d_spacing[1], + ) + if crystal_2_metadata is not None: + self.crystal_2_usage, _ = soft_signal_r_and_setter( + str, initial_value=crystal_2_metadata.usage + ) + self.crystal_2_type, _ = soft_signal_r_and_setter( + str, initial_value=crystal_2_metadata.type + ) + self.crystal_2_reflection, _ = soft_signal_r_and_setter( + Array1D[np.int32], + initial_value=np.array(crystal_2_metadata.reflection), + ) + self.crystal_2_d_spacing, _ = soft_signal_r_and_setter( + float, + initial_value=crystal_2_metadata.d_spacing[0], + units=crystal_2_metadata.d_spacing[1], + ) super().__init__(name) From 5f4220e0994a095be97ea35b0da90844a567cdc8 Mon Sep 17 00:00:00 2001 From: Stanislaw Malinowski Date: Tue, 28 Jan 2025 11:20:50 +0000 Subject: [PATCH 93/93] fix the tests --- src/dodal/beamlines/i18.py | 1 - tests/devices/i18/test_table.py | 38 ++++++++++++++ tests/devices/i18/test_thor_labs_stage.py | 50 +++++++++++++++++++ .../unit_tests/i18/test_thor_labs_stage.py | 17 ------- tests/devices/unit_tests/test_table.py | 22 -------- 5 files changed, 88 insertions(+), 40 deletions(-) create mode 100644 tests/devices/i18/test_thor_labs_stage.py delete mode 100644 tests/devices/unit_tests/i18/test_thor_labs_stage.py delete mode 100644 tests/devices/unit_tests/test_table.py diff --git a/src/dodal/beamlines/i18.py b/src/dodal/beamlines/i18.py index 9e07cd4c95..e7f70b4b54 100644 --- a/src/dodal/beamlines/i18.py +++ b/src/dodal/beamlines/i18.py @@ -8,7 +8,6 @@ set_path_provider, ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.common.crystal_metadata import CrystalMetadata from dodal.common.visit import ( LocalDirectoryServiceClient, StaticVisitPathProvider, diff --git a/tests/devices/i18/test_table.py b/tests/devices/i18/test_table.py index 7879ca4b66..875a0803df 100644 --- a/tests/devices/i18/test_table.py +++ b/tests/devices/i18/test_table.py @@ -49,3 +49,41 @@ async def test_setting_xy_position_table(table: Table): } assert reading == expected_reading + + +async def test_setting_xyztheta_position_table(table: Table): + """ + Test setting x and y positions on the Table using the ophyd_async mock tools. + """ + set_mock_value(table.x.user_readback, 1.23) + set_mock_value(table.y.user_readback, 4.56) + set_mock_value(table.z.user_readback, 7.89) + set_mock_value(table.theta.user_readback, 10.11) + + # Create a position object + position = TablePosition(x=1.23, y=4.56, z=7.89, theta=10.11) + + # Call set to update the position + await table.set(position) + + reading = await table.read() + expected_reading = { + "table-y": { + "value": 4.56, + "timestamp": ANY, + "alarm_severity": 0, + }, + "table-x": { + "value": 1.23, + "timestamp": ANY, + "alarm_severity": 0, + }, + "table-theta": { + "alarm_severity": 0, + "timestamp": ANY, + "value": 10.11, + }, + "table-z": {"alarm_severity": 0, "timestamp": ANY, "value": 7.89}, + } + + assert reading == expected_reading diff --git a/tests/devices/i18/test_thor_labs_stage.py b/tests/devices/i18/test_thor_labs_stage.py new file mode 100644 index 0000000000..ff0fee38af --- /dev/null +++ b/tests/devices/i18/test_thor_labs_stage.py @@ -0,0 +1,50 @@ +from unittest.mock import ANY + +import pytest +from ophyd_async.core import DeviceCollector +from ophyd_async.testing import set_mock_value + +from dodal.devices.i18.thor_labs_stage import ThorLabsStage, XYPosition + + +@pytest.fixture +async def thor_labs_stage(): + async with DeviceCollector(mock=True): + fake_thor_labs_stage = ThorLabsStage("", "thor_labs_stage") + + return fake_thor_labs_stage + + +async def test_setting(thor_labs_stage: ThorLabsStage): + """ + Test setting x and y positions on the ThorLabsStage using ophyd_async mock tools. + """ + # Set initial mock values for the stage's position readbacks + set_mock_value(thor_labs_stage.x.user_readback, 5.0) + set_mock_value(thor_labs_stage.y.user_readback, 5.0) + + # Define the new position to be set + pos = XYPosition(x=5, y=5) + + # Call set to update the position + await thor_labs_stage.set(pos) + + # Read the stage's current position + reading = await thor_labs_stage.read() + + # Define the expected position values after the set operation + expected_reading = { + "stage-x": { + "value": 5.0, + "timestamp": ANY, + "alarm_severity": 0, + }, + "stage-y": { + "value": 5.0, + "timestamp": ANY, + "alarm_severity": 0, + }, + } + + # Assert the actual reading matches the expected reading + assert reading == expected_reading diff --git a/tests/devices/unit_tests/i18/test_thor_labs_stage.py b/tests/devices/unit_tests/i18/test_thor_labs_stage.py deleted file mode 100644 index 38dc4a0b0a..0000000000 --- a/tests/devices/unit_tests/i18/test_thor_labs_stage.py +++ /dev/null @@ -1,17 +0,0 @@ -import pytest -from ophyd_async.core import DeviceCollector - -from dodal.devices.i18.thor_labs_stage import ThorLabsStage, XYPosition - - -@pytest.fixture -async def fake_thor_labs_stage(): - async with DeviceCollector(mock=True): - fake_thor_labs_stage = ThorLabsStage("", "thor_labs_stage") - - return fake_thor_labs_stage - - -async def test_setting(fake_thor_labs_stage: ThorLabsStage): - pos = XYPosition(x=5, y=5) - await fake_thor_labs_stage.set(pos) diff --git a/tests/devices/unit_tests/test_table.py b/tests/devices/unit_tests/test_table.py deleted file mode 100644 index cf345c24dd..0000000000 --- a/tests/devices/unit_tests/test_table.py +++ /dev/null @@ -1,22 +0,0 @@ -import pytest -from ophyd_async.core import DeviceCollector - -from dodal.devices.i18.table import Table, TablePosition - - -@pytest.fixture -async def fake_table(): - async with DeviceCollector(mock=True): - fake_thor_labs_stage = Table("", "thor_labs_stage") - - return fake_thor_labs_stage - - -async def test_setting_xy(fake_table: Table): - pos = TablePosition(x=5, y=5) - await fake_table.set(pos) - - -async def test_setting_xyztheta(fake_table: Table): - pos = TablePosition(x=5, y=5, z=5, theta=5) - await fake_table.set(pos)