From e6eebf67494a0f732fa74accc8f3bcc921ccf06f Mon Sep 17 00:00:00 2001 From: Tamoor Shahid Date: Thu, 18 Dec 2025 11:13:19 +0000 Subject: [PATCH 1/9] Partially Converted easy changes in I04 to DeviceManager --- src/dodal/beamlines/i04.py | 191 +++++++++---------------------------- 1 file changed, 44 insertions(+), 147 deletions(-) diff --git a/src/dodal/beamlines/i04.py b/src/dodal/beamlines/i04.py index 1367b45a1ba..74ed0dc2590 100644 --- a/src/dodal/beamlines/i04.py +++ b/src/dodal/beamlines/i04.py @@ -6,6 +6,7 @@ device_instantiation, ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.device_manager import DeviceManager from dodal.devices.aperturescatterguard import ( AperturePosition, ApertureScatterguard, @@ -70,126 +71,85 @@ PREFIX = BeamlinePrefix(BL) +devices = DeviceManager() -@device_factory() + +@devices.factory() def smargon() -> Smargon: - """Get the i04 Smargon device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return Smargon(f"{PREFIX.beamline_prefix}-MO-SGON-01:") -@device_factory() +@devices.factory() def gonio_positioner() -> XYZStage: - """Get the i04 lower_gonio_stages device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return XYZStage( f"{PREFIX.beamline_prefix}-MO-GONIO-01:", "lower_gonio_stages", ) -@device_factory() +@devices.factory() def sample_delivery_system() -> XYZStage: - """Get the i04 sample_delivery_system device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return XYZStage(f"{PREFIX.beamline_prefix}-MO-SDE-01:") -@device_factory() +@devices.factory() def ipin() -> IPin: - """Get the i04 ipin device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return IPin(f"{PREFIX.beamline_prefix}-EA-PIN-01:") -@device_factory() +@devices.factory() def beamstop() -> Beamstop: - """Get the i04 beamstop device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return Beamstop( f"{PREFIX.beamline_prefix}-MO-BS-01:", beamline_parameters=get_beamline_parameters(), ) -@device_factory() +@devices.factory() def sample_shutter() -> ZebraShutter: - """Get the i04 sample shutter device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return ZebraShutter(f"{PREFIX.beamline_prefix}-EA-SHTR-01:") -@device_factory() +@devices.factory() def attenuator() -> BinaryFilterAttenuator: - """Get the i04 attenuator device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return BinaryFilterAttenuator( prefix=f"{PREFIX.beamline_prefix}-EA-ATTN-01:", num_filters=16, ) -@device_factory() +@devices.factory() def transfocator() -> Transfocator: - """Get the i04 transfocator device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return Transfocator(f"{PREFIX.beamline_prefix}-MO-FSWT-01:") -@device_factory() +@devices.factory() def baton() -> Baton: - """Get the i04 baton device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return Baton(f"{PREFIX.beamline_prefix}-CS-BATON-01:") -@device_factory() -def xbpm_feedback() -> XBPMFeedback: - """Get the i04 xbpm_feedback device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ - return XBPMFeedback(f"{PREFIX.beamline_prefix}-EA-FDBK-01:", baton=baton()) +@devices.factory() +def xbpm_feedback(baton: Baton) -> XBPMFeedback: + return XBPMFeedback(f"{PREFIX.beamline_prefix}-EA-FDBK-01:", baton=baton) -@device_factory() +@devices.factory() def flux() -> Flux: - """Get the i04 flux device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return Flux(f"{PREFIX.beamline_prefix}-MO-FLUX-01:") -@device_factory() +@devices.factory() def dcm() -> DCM: - """Get the i04 DCM device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return DCM(f"{PREFIX.beamline_prefix}-MO-DCM-01:") -@device_factory() +@devices.factory() def backlight() -> Backlight: - """Get the i04 backlight device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return Backlight(PREFIX.beamline_prefix) -@device_factory() +@devices.factory() def aperture_scatterguard() -> ApertureScatterguard: - """Get the i04 aperture and scatterguard device, instantiate it if it hasn't already - been. If this is called when already instantiated in i04, it will return the existing - object. - """ params = get_beamline_parameters() return ApertureScatterguard( aperture_prefix=f"{PREFIX.beamline_prefix}-MO-MAPT-01:", @@ -199,13 +159,8 @@ def aperture_scatterguard() -> ApertureScatterguard: ) -@device_factory(skip=BL == "s04") +@device_factory() def eiger(mock: bool = False, params: DetectorParams | None = None) -> EigerDetector: - """Get the i04 Eiger device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - If called with params, will update those params to the Eiger object. - """ - def set_params(eiger: EigerDetector): if params is not None: eiger.set_detector_parameters(params) @@ -220,29 +175,20 @@ def set_params(eiger: EigerDetector): ) -@device_factory() +@devices.factory() def zebra_fast_grid_scan() -> ZebraFastGridScanThreeD: - """Get the i04 zebra_fast_grid_scan device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return ZebraFastGridScanThreeD( prefix=f"{PREFIX.beamline_prefix}-MO-SGON-01:", ) -@device_factory() +@devices.factory() def s4_slit_gaps() -> S4SlitGaps: - """Get the i04 s4_slit_gaps device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return S4SlitGaps(f"{PREFIX.beamline_prefix}-AL-SLITS-04:") @device_factory() def undulator() -> UndulatorInKeV: - """Get the i04 undulator device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return UndulatorInKeV( prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:", id_gap_lookup_table_path="/dls_sw/i04/software/daq_configuration/lookup/BeamLine_Undulator_toGap.txt", @@ -250,45 +196,29 @@ def undulator() -> UndulatorInKeV: ) -@device_factory(skip=BL == "s04") +@devices.factory() def synchrotron() -> Synchrotron: - """Get the i04 synchrotron device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return Synchrotron() -@device_factory() +@devices.factory() def zebra() -> Zebra: - """Get the i04 zebra device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return Zebra( prefix=f"{PREFIX.beamline_prefix}-EA-ZEBRA-01:", mapping=I04_ZEBRA_MAPPING, ) -@device_factory( - skip=BL == "s04", -) +@device_factory() def oav(params: OAVConfig | None = None) -> OAVBeamCentrePV: - """Get the i04 OAV device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return OAVBeamCentrePV( prefix=f"{PREFIX.beamline_prefix}-DI-OAV-01:", config=params or OAVConfig(ZOOM_PARAMS_FILE), ) -@device_factory( - skip=BL == "s04", -) +@devices.factory() def oav_full_screen(params: OAVConfig | None = None) -> OAVBeamCentrePV: - """Get the i04 OAV device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return OAVBeamCentrePV( prefix=f"{PREFIX.beamline_prefix}-DI-OAV-01:", config=params or OAVConfig(ZOOM_PARAMS_FILE), @@ -297,40 +227,26 @@ def oav_full_screen(params: OAVConfig | None = None) -> OAVBeamCentrePV: ) -@device_factory( - skip=BL == "s04", -) +@devices.factory() def detector_motion() -> DetectorMotion: - """Get the i04 detector motion device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return DetectorMotion( device_prefix=f"{PREFIX.beamline_prefix}-MO-DET-01:", pmac_prefix=f"{PREFIX.beamline_prefix}-MO-PMAC-02:", ) -@device_factory() +@devices.factory() def thawer() -> Thawer: - """Get the i04 thawer, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return Thawer(f"{PREFIX.beamline_prefix}-EA-THAW-01") -@device_factory() +@devices.factory() def robot() -> BartRobot: - """Get the i04 robot device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return BartRobot(f"{PREFIX.beamline_prefix}-MO-ROBOT-01:") -@device_factory() +@devices.factory() def oav_to_redis_forwarder() -> OAVToRedisForwarder: - """Get the i04 OAV to redis forwarder, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return OAVToRedisForwarder( f"{PREFIX.beamline_prefix}-DI-OAV-01:", oav_roi=oav(), @@ -341,11 +257,8 @@ def oav_to_redis_forwarder() -> OAVToRedisForwarder: ) -@device_factory() +@devices.factory() def murko_results() -> MurkoResultsDevice: - """Get the i04 OAV to redis forwarder, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return MurkoResultsDevice( redis_host=RedisConstants.REDIS_HOST, redis_password=RedisConstants.REDIS_PASSWORD, @@ -353,60 +266,44 @@ def murko_results() -> MurkoResultsDevice: ) -@device_factory() +@devices.factory() def diamond_filter() -> DiamondFilter[I04Filters]: - """Get the i04 diamond filter device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return DiamondFilter[I04Filters]( f"{PREFIX.beamline_prefix}-MO-FLTR-01:Y", I04Filters ) -@device_factory() +@devices.factory() def zocalo() -> ZocaloResults: - """Get the i04 ZocaloResults device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return ZocaloResults(channel="xrc.i04", results_source=ZocaloSource.CPU) -@device_factory() +@devices.factory() def pin_tip_detection() -> PinTipDetection: - """Get the i04 pin tip detection device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return PinTipDetection(f"{PREFIX.beamline_prefix}-DI-OAV-01:") -@device_factory() -def scintillator() -> Scintillator: - """Get the i04 scintillator device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ +@devices.factory() +def scintillator(aperture_scatterguard: ApertureScatterguard) -> Scintillator: return Scintillator( f"{PREFIX.beamline_prefix}-MO-SCIN-01:", - Reference(aperture_scatterguard()), + Reference(aperture_scatterguard), get_beamline_parameters(), ) -@device_factory() +@devices.factory() def max_pixel() -> MaxPixel: - """Get the i04 max pixel device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ return MaxPixel( f"{PREFIX.beamline_prefix}-DI-OAV-01:", ) -@device_factory() -def beamsize() -> Beamsize: - """Get the i04 beamsize device, instantiate it if it hasn't already been. - If this is called when already instantiated in i04, it will return the existing object. - """ +@devices.factory() +def beamsize( + transfocator: Transfocator, aperture_scatterguard: ApertureScatterguard +) -> Beamsize: return Beamsize( - transfocator=transfocator(), - aperture_scatterguard=aperture_scatterguard(), + transfocator, + aperture_scatterguard, ) From b01fceea9467baaafea3d512c0071c8c422aa260 Mon Sep 17 00:00:00 2001 From: Tamoor Shahid Date: Thu, 18 Dec 2025 11:58:23 +0000 Subject: [PATCH 2/9] Changed I04 Eiger to use DeviceManager --- src/dodal/beamlines/i04.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/dodal/beamlines/i04.py b/src/dodal/beamlines/i04.py index 74ed0dc2590..4aedd7f459d 100644 --- a/src/dodal/beamlines/i04.py +++ b/src/dodal/beamlines/i04.py @@ -159,20 +159,9 @@ def aperture_scatterguard() -> ApertureScatterguard: ) -@device_factory() -def eiger(mock: bool = False, params: DetectorParams | None = None) -> EigerDetector: - def set_params(eiger: EigerDetector): - if params is not None: - eiger.set_detector_parameters(params) - - return device_instantiation( - device_factory=EigerDetector, - name="eiger", - prefix="-EA-EIGER-01:", - wait=False, - fake=mock, - post_create=set_params, - ) +@devices.v1_init(EigerDetector, prefix="BL03I-EA-EIGER-01:", wait=False) +def eiger(eiger: EigerDetector) -> EigerDetector: + return eiger @devices.factory() @@ -187,6 +176,7 @@ def s4_slit_gaps() -> S4SlitGaps: return S4SlitGaps(f"{PREFIX.beamline_prefix}-AL-SLITS-04:") +# TO DO: FIX UNDULATOR TO USE DEVICE MANAGER @device_factory() def undulator() -> UndulatorInKeV: return UndulatorInKeV( @@ -209,6 +199,7 @@ def zebra() -> Zebra: ) +# TO DO: FIX OAV TO USE DEVICE MANAGER @device_factory() def oav(params: OAVConfig | None = None) -> OAVBeamCentrePV: return OAVBeamCentrePV( From 9b12b3bd7b408409b4e45a9dac5a09c191d7bbe9 Mon Sep 17 00:00:00 2001 From: Tamoor Shahid Date: Thu, 18 Dec 2025 13:40:42 +0000 Subject: [PATCH 3/9] Converted I02-1 to Device Manager --- src/dodal/beamlines/i02_1.py | 50 +++++++++--------------------------- 1 file changed, 12 insertions(+), 38 deletions(-) diff --git a/src/dodal/beamlines/i02_1.py b/src/dodal/beamlines/i02_1.py index 427dad19d7c..c05c1a10a89 100644 --- a/src/dodal/beamlines/i02_1.py +++ b/src/dodal/beamlines/i02_1.py @@ -5,6 +5,7 @@ device_instantiation, ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.device_manager import DeviceManager from dodal.devices.attenuator.attenuator import EnumFilterAttenuator from dodal.devices.attenuator.filter_selections import ( I02_1FilterFourSelections, @@ -37,46 +38,29 @@ sources=ZebraSources(), ) +devices = DeviceManager() -@device_factory() -def eiger(mock: bool = False) -> EigerDetector: - """Get the i02-1 Eiger device, instantiate it if it hasn't already been. - If this is called when already instantiated in i02-1, it will return the existing object. - """ - return device_instantiation( - device_factory=EigerDetector, - prefix=f"{PREFIX.beamline_prefix}-EA-EIGER-01:", - bl_prefix=False, - wait=False, - fake=mock, - name="eiger", - ) + +@devices.v1_init(EigerDetector, prefix="BL03I-EA-EIGER-01:", wait=False) +def eiger(eiger: EigerDetector) -> EigerDetector: + return eiger -@device_factory() +@devices.factory() def zebra_fast_grid_scan() -> ZebraFastGridScanTwoD: - """Get the i02-1 zebra_fast_grid_scan device, instantiate it if it hasn't already been. - If this is called when already instantiated in i02-1, it will return the existing object. - """ return ZebraFastGridScanTwoD( prefix=f"{PREFIX.beamline_prefix}-MO-SAMP-11:", motion_controller_prefix="BL02J-MO-STEP-11:", ) -@device_factory() +@devices.factory() def synchrotron() -> Synchrotron: - """Get the i02-1 synchrotron device, instantiate it if it hasn't already been. - If this is called when already instantiated in i02-1, it will return the existing object. - """ return Synchrotron() -@device_factory() +@devices.factory() def zebra() -> Zebra: - """Get the i02-1 zebra device, instantiate it if it hasn't already been. - If this is called when already instantiated in i02-1, it will return the existing object. - """ return Zebra( prefix=f"{PREFIX.beamline_prefix}-EA-ZEBRA-01:", mapping=I02_1_ZEBRA_MAPPING, @@ -84,28 +68,18 @@ def zebra() -> Zebra: # Device not needed after https://github.com/DiamondLightSource/mx-bluesky/issues/1299 -@device_factory() +@devices.factory() def zocalo() -> ZocaloResults: - """Get the i02-1 ZocaloResults device, instantiate it if it hasn't already been. - If this is called when already instantiated in i02-1, it will return the existing object. - """ return ZocaloResults() -@device_factory() +@devices.factory() def goniometer() -> SampleMotors: - """Get the i02-1 goniometer device, instantiate it if it hasn't already been. - If this is called when already instantiated in i02-1, it will return the existing object. - """ return SampleMotors(f"{PREFIX.beamline_prefix}-MO-SAMP-01:") -@device_factory() +@devices.factory() def attenuator() -> EnumFilterAttenuator: - """Get the i02-1 attenuator device, instantiate it if it hasn't already been. - If this is called when already instantiated in i02-1, it will return the existing object. - """ - return EnumFilterAttenuator( f"{PREFIX.beamline_prefix}-OP-ATTN-01:", ( From a07a000a16829bf1e97796b5bc869cdfeb8e5395 Mon Sep 17 00:00:00 2001 From: Tamoor Shahid Date: Thu, 18 Dec 2025 13:45:21 +0000 Subject: [PATCH 4/9] Converted I02-2 to Device Manager --- src/dodal/beamlines/i02_1.py | 4 ---- src/dodal/beamlines/i02_2.py | 10 +++++----- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/dodal/beamlines/i02_1.py b/src/dodal/beamlines/i02_1.py index c05c1a10a89..6adff9825ac 100644 --- a/src/dodal/beamlines/i02_1.py +++ b/src/dodal/beamlines/i02_1.py @@ -1,9 +1,5 @@ """Beamline i02-1 is also known as VMXm, or I02J""" -from dodal.common.beamlines.beamline_utils import ( - device_factory, - device_instantiation, -) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.device_manager import DeviceManager from dodal.devices.attenuator.attenuator import EnumFilterAttenuator diff --git a/src/dodal/beamlines/i02_2.py b/src/dodal/beamlines/i02_2.py index 96289234d51..3d583b54c89 100644 --- a/src/dodal/beamlines/i02_2.py +++ b/src/dodal/beamlines/i02_2.py @@ -1,9 +1,7 @@ """Beamline i02-2 is also known as VMXi, or I02I""" -from dodal.common.beamlines.beamline_utils import ( - device_factory, -) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.device_manager import DeviceManager from dodal.devices.motors import XYStage from dodal.devices.synchrotron import Synchrotron from dodal.log import set_beamline as set_log_beamline @@ -15,8 +13,10 @@ set_utils_beamline(BL) DAQ_CONFIGURATION_PATH = "/dls_sw/i02-2/software/daq_configuration" +devices = DeviceManager() -@device_factory() + +@devices.factory() def synchrotron() -> Synchrotron: """Get the i02-2 synchrotron device, instantiate it if it hasn't already been. If this is called when already instantiated in i02-2, it will return the existing object. @@ -24,7 +24,7 @@ def synchrotron() -> Synchrotron: return Synchrotron() -@device_factory() +@devices.factory() def sample_motors() -> XYStage: """Get the i02-2 goniometer device, instantiate it if it hasn't already been. If this is called when already instantiated in i02-2, it will return the existing object. From 35b4680ac98463f581c51d75a64f8058fd09648c Mon Sep 17 00:00:00 2001 From: Tamoor Shahid Date: Thu, 18 Dec 2025 14:00:41 +0000 Subject: [PATCH 5/9] Converted I23 to Device Manager --- src/dodal/beamlines/i23.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/dodal/beamlines/i23.py b/src/dodal/beamlines/i23.py index 1b7f6ae8f99..a78911802dd 100644 --- a/src/dodal/beamlines/i23.py +++ b/src/dodal/beamlines/i23.py @@ -4,13 +4,13 @@ from ophyd_async.epics.adpilatus import PilatusDetector from dodal.common.beamlines.beamline_utils import ( - 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 HDF5_SUFFIX from dodal.common.visit import LocalDirectoryServiceClient, StaticVisitPathProvider +from dodal.device_manager import DeviceManager from dodal.devices.motors import SixAxisGonio from dodal.devices.oav.pin_image_recognition import PinTipDetection from dodal.devices.positioner import Positioner1D @@ -43,6 +43,8 @@ sources=ZebraSources(), ) +devices = DeviceManager() + class I23DetectorPositions(StrictEnum): IN = InOut.IN.value @@ -59,7 +61,7 @@ def _is_i23_machine(): return hostname.startswith("i23-ws") or hostname.startswith("i23-control") -@device_factory(skip=lambda: not _is_i23_machine()) +@devices.factory(skip=lambda: not _is_i23_machine()) def oav_pin_tip_detection() -> PinTipDetection: """Get the i23 OAV pin-tip detection device.""" @@ -69,19 +71,19 @@ def oav_pin_tip_detection() -> PinTipDetection: ) -@device_factory() +@devices.factory() def shutter() -> ZebraShutter: """Get the i23 zebra controlled shutter.""" return ZebraShutter(f"{PREFIX.beamline_prefix}-EA-SHTR-01:") -@device_factory() +@devices.factory() def gonio() -> SixAxisGonio: """Get the i23 goniometer""" return SixAxisGonio(f"{PREFIX.beamline_prefix}-MO-GONIO-01:") -@device_factory() +@devices.factory() def zebra() -> Zebra: """Get the i23 zebra""" return Zebra( @@ -90,7 +92,7 @@ def zebra() -> Zebra: ) -@device_factory() +@devices.factory() def pilatus() -> PilatusDetector: """Get the i23 pilatus""" return PilatusDetector( @@ -101,7 +103,7 @@ def pilatus() -> PilatusDetector: ) -@device_factory() +@devices.factory() def detector_motion() -> Positioner1D[I23DetectorPositions]: """Get the i23 detector""" return Positioner1D[I23DetectorPositions]( From 662aa5445361b91d215a11e2306adad33c44b566 Mon Sep 17 00:00:00 2001 From: Tamoor Shahid Date: Thu, 18 Dec 2025 14:07:05 +0000 Subject: [PATCH 6/9] Converted I24 to Device Manager --- src/dodal/beamlines/i24.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/dodal/beamlines/i24.py b/src/dodal/beamlines/i24.py index 4b75f127cd5..37ad148d80f 100644 --- a/src/dodal/beamlines/i24.py +++ b/src/dodal/beamlines/i24.py @@ -2,11 +2,9 @@ from ophyd_async.core import AutoIncrementingPathProvider, StaticFilenameProvider -from dodal.common.beamlines.beamline_utils import ( - BL, - device_factory, -) +from dodal.common.beamlines.beamline_utils import BL from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.device_manager import DeviceManager from dodal.devices.attenuator.attenuator import EnumFilterAttenuator from dodal.devices.attenuator.filter_selections import ( I24FilterOneSelections, @@ -51,8 +49,10 @@ PREFIX = BeamlinePrefix(BL) +devices = DeviceManager() + -@device_factory() +@devices.factory() def attenuator() -> EnumFilterAttenuator: """Get a read-only attenuator device for i24, instantiate it if it hasn't already been. If this is called when already instantiated in i24, it will return the @@ -63,7 +63,7 @@ def attenuator() -> EnumFilterAttenuator: ) -@device_factory() +@devices.factory() def aperture() -> Aperture: """Get the i24 aperture device, instantiate it if it hasn't already been. If this is called when already instantiated in i24, it will return the existing object. @@ -73,7 +73,7 @@ def aperture() -> Aperture: ) -@device_factory() +@devices.factory() def beamstop() -> Beamstop: """Get the i24 beamstop device, instantiate it if it hasn't already been. If this is called when already instantiated in i24, it will return the existing object. @@ -83,7 +83,7 @@ def beamstop() -> Beamstop: ) -@device_factory() +@devices.factory() def backlight() -> DualBacklight: """Get the i24 backlight device, instantiate it if it hasn't already been. If this is called when already instantiated in i24, it will return the existing object. @@ -93,7 +93,7 @@ def backlight() -> DualBacklight: ) -@device_factory() +@devices.factory() def detector_motion() -> YZStage: """Get the i24 detector motion device, instantiate it if it hasn't already been. If this is called when already instantiated in i24, it will return the existing object. @@ -103,7 +103,7 @@ def detector_motion() -> YZStage: ) -@device_factory() +@devices.factory() def dcm() -> DCM: """Get the i24 DCM device, instantiate it if it hasn't already been. If this is called when already instantiated in i24, it will return the existing object. @@ -141,7 +141,7 @@ def dcm() -> DCM: # ) -@device_factory() +@devices.factory() def pmac() -> PMAC: """Get the i24 PMAC device, instantiate it if it hasn't already been. If this is called when already instantiated in i24, it will return the existing object. @@ -149,7 +149,7 @@ def pmac() -> PMAC: return PMAC(PREFIX.beamline_prefix) -@device_factory() +@devices.factory() def oav() -> OAVBeamCentreFile: return OAVBeamCentreFile( prefix=f"{PREFIX.beamline_prefix}-DI-OAV-01:", @@ -157,7 +157,7 @@ def oav() -> OAVBeamCentreFile: ) -@device_factory() +@devices.factory() def vgonio() -> VerticalGoniometer: """Get the i24 vertical goniometer device, instantiate it if it hasn't already been. If this is called when already instantiated, it will return the existing object. @@ -165,7 +165,7 @@ def vgonio() -> VerticalGoniometer: return VerticalGoniometer(f"{PREFIX.beamline_prefix}-MO-VGON-01:") -@device_factory() +@devices.factory() def zebra() -> Zebra: """Get the i24 zebra device, instantiate it if it hasn't already been. If this is called when already instantiated in i24, it will return the existing object. @@ -176,7 +176,7 @@ def zebra() -> Zebra: ) -@device_factory() +@devices.factory() def shutter() -> HutchShutter: """Get the i24 hutch shutter device, instantiate it if it hasn't already been. If this is called when already instantiated, it will return the existing object. @@ -184,13 +184,13 @@ def shutter() -> HutchShutter: return HutchShutter(f"{PREFIX.beamline_prefix}-PS-SHTR-01:") -@device_factory() +@devices.factory() def focus_mirrors() -> FocusMirrorsMode: """Get the i24 focus mirror devise to find the beam size.""" return FocusMirrorsMode(f"{PREFIX.beamline_prefix}-OP-MFM-01:") -@device_factory() +@devices.factory() def eiger_beam_center() -> DetectorBeamCenter: """A device for setting/reading the beamcenter from the eiger on i24.""" return DetectorBeamCenter( @@ -199,7 +199,7 @@ def eiger_beam_center() -> DetectorBeamCenter: ) -@device_factory() +@devices.factory() def commissioning_jungfrau( path_to_dir: str = "/tmp/jf", # Device factory doesn't allow for required args, filename: str = "jf_output", # but these should be manually entered when commissioning From 9bfbbc4e242581b4a2f640f40ede0ab1319c7afc Mon Sep 17 00:00:00 2001 From: Tamoor Shahid Date: Thu, 18 Dec 2025 16:15:58 +0000 Subject: [PATCH 7/9] Converted i04 to Device Manager --- src/dodal/beamlines/i04.py | 17 ++++++++++------- tests/test_utils.py | 24 ------------------------ 2 files changed, 10 insertions(+), 31 deletions(-) diff --git a/src/dodal/beamlines/i04.py b/src/dodal/beamlines/i04.py index 4aedd7f459d..a736fb97dd5 100644 --- a/src/dodal/beamlines/i04.py +++ b/src/dodal/beamlines/i04.py @@ -176,13 +176,17 @@ def s4_slit_gaps() -> S4SlitGaps: return S4SlitGaps(f"{PREFIX.beamline_prefix}-AL-SLITS-04:") -# TO DO: FIX UNDULATOR TO USE DEVICE MANAGER -@device_factory() -def undulator() -> UndulatorInKeV: +@devices.fixture +def daq_configuration_path() -> str: + return DAQ_CONFIGURATION_PATH + + +@devices.factory() +def undulator(baton: Baton, daq_configuration_path: str) -> UndulatorInKeV: return UndulatorInKeV( prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:", - id_gap_lookup_table_path="/dls_sw/i04/software/daq_configuration/lookup/BeamLine_Undulator_toGap.txt", - baton=baton(), + id_gap_lookup_table_path=f"{daq_configuration_path}/lookup/BeamLine_Undulator_toGap.txt", + baton=baton, ) @@ -199,8 +203,7 @@ def zebra() -> Zebra: ) -# TO DO: FIX OAV TO USE DEVICE MANAGER -@device_factory() +@devices.factory() def oav(params: OAVConfig | None = None) -> OAVBeamCentrePV: return OAVBeamCentrePV( prefix=f"{PREFIX.beamline_prefix}-DI-OAV-01:", diff --git a/tests/test_utils.py b/tests/test_utils.py index d01a87a48e3..abcf568f855 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -485,27 +485,3 @@ def test_filter_ophyd_devices_raises_for_extra_types(): ) def test_is_v2_device_type(input: Any, expected_result: bool): assert is_v2_device_type(input) == expected_result - - -def test_calling_factory_with_different_args_raises_an_exception(): - i04.oav(params=MagicMock()) - with pytest.raises( - RuntimeError, - match="Device factory method called multiple times with different parameters", - ): - i04.oav(params=MagicMock()) - i04.oav.cache_clear() - - -def test_calling_factory_with_different_args_does_not_raise_an_exception_after_cache_clear(): - i04.oav(params=MagicMock()) - i04.oav.cache_clear() - i04.oav(params=MagicMock()) - i04.oav.cache_clear() - - -def test_factory_calls_are_cached(): - undulator1 = i04.undulator() - undulator2 = i04.undulator() - assert undulator1 is undulator2 - i04.undulator.cache_clear() From d07eb00e4aecc93713323269ee8142a3b0444801 Mon Sep 17 00:00:00 2001 From: Tamoor Shahid Date: Mon, 5 Jan 2026 14:18:46 +0000 Subject: [PATCH 8/9] Removed unused imports --- src/dodal/beamlines/i04.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/dodal/beamlines/i04.py b/src/dodal/beamlines/i04.py index a736fb97dd5..c3087586dc2 100644 --- a/src/dodal/beamlines/i04.py +++ b/src/dodal/beamlines/i04.py @@ -1,10 +1,6 @@ from ophyd_async.core import Reference from dodal.common.beamlines.beamline_parameters import get_beamline_parameters -from dodal.common.beamlines.beamline_utils import ( - device_factory, - device_instantiation, -) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.device_manager import DeviceManager from dodal.devices.aperturescatterguard import ( @@ -15,7 +11,6 @@ from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator from dodal.devices.backlight import Backlight from dodal.devices.baton import Baton -from dodal.devices.detector import DetectorParams from dodal.devices.detector.detector_motion import DetectorMotion from dodal.devices.diamond_filter import DiamondFilter, I04Filters from dodal.devices.eiger import EigerDetector From 55d03feeed8ace58fa8b961abebc00f885ccca3c Mon Sep 17 00:00:00 2001 From: Tamoor Shahid Date: Tue, 6 Jan 2026 16:13:01 +0000 Subject: [PATCH 9/9] Added fixture changes to path_provider --- src/dodal/beamlines/i23.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/dodal/beamlines/i23.py b/src/dodal/beamlines/i23.py index a78911802dd..34a1e91ad4a 100644 --- a/src/dodal/beamlines/i23.py +++ b/src/dodal/beamlines/i23.py @@ -1,12 +1,9 @@ +from functools import cache from pathlib import Path -from ophyd_async.core import InOut, StrictEnum +from ophyd_async.core import InOut, PathProvider, StrictEnum from ophyd_async.epics.adpilatus import PilatusDetector -from dodal.common.beamlines.beamline_utils import ( - 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 HDF5_SUFFIX from dodal.common.visit import LocalDirectoryServiceClient, StaticVisitPathProvider @@ -25,26 +22,29 @@ from dodal.utils import BeamlinePrefix, get_beamline_name, get_hostname BL = get_beamline_name("i23") +PREFIX = BeamlinePrefix(BL) set_log_beamline(BL) set_utils_beamline(BL) -set_path_provider( - StaticVisitPathProvider( +devices = DeviceManager() + + +@devices.fixture +@cache +def path_provider() -> PathProvider: + """Path provider for i23 Pilatus detector.""" + return StaticVisitPathProvider( BL, Path("/tmp"), client=LocalDirectoryServiceClient(), ) -) -PREFIX = BeamlinePrefix(BL) I23_ZEBRA_MAPPING = ZebraMapping( outputs=ZebraTTLOutputs(TTL_DETECTOR=1, TTL_SHUTTER=4), sources=ZebraSources(), ) -devices = DeviceManager() - class I23DetectorPositions(StrictEnum): IN = InOut.IN.value @@ -93,11 +93,11 @@ def zebra() -> Zebra: @devices.factory() -def pilatus() -> PilatusDetector: +def pilatus(path_provider: PathProvider) -> PilatusDetector: """Get the i23 pilatus""" return PilatusDetector( prefix=f"{PREFIX.beamline_prefix}-EA-PILAT-01:", - path_provider=get_path_provider(), + path_provider=path_provider, drv_suffix="cam1:", fileio_suffix=HDF5_SUFFIX, )