From 06b0c4914dcf663ac04b074bd32f5632e89d8265 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Wed, 30 Jul 2025 13:48:42 +0000 Subject: [PATCH 01/27] create i05-shared and move pgm there --- src/dodal/beamlines/__init__.py | 1 + src/dodal/beamlines/i05.py | 4 ++-- src/dodal/beamlines/i05_1.py | 7 +++++++ src/dodal/beamlines/i05_shared.py | 16 ++++++++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 src/dodal/beamlines/i05_shared.py diff --git a/src/dodal/beamlines/__init__.py b/src/dodal/beamlines/__init__.py index e5d29c0706f..23a14f53d56 100644 --- a/src/dodal/beamlines/__init__.py +++ b/src/dodal/beamlines/__init__.py @@ -10,6 +10,7 @@ # dictionary, which maps ${BEAMLINE} to dodal.beamlines. _BEAMLINE_NAME_OVERRIDES = { "i05-1": "i05_1", + "i05-shared": "i05_shared", "b07-1": "b07_1", "i09-1": "i09_1", "i13-1": "i13_1", diff --git a/src/dodal/beamlines/i05.py b/src/dodal/beamlines/i05.py index e60bf2c99da..ab984983477 100644 --- a/src/dodal/beamlines/i05.py +++ b/src/dodal/beamlines/i05.py @@ -1,6 +1,6 @@ +from dodal.beamlines.i05_shared import pgm as i05pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.devices.i05 import Grating from dodal.devices.pgm import PGM from dodal.devices.synchrotron import Synchrotron from dodal.log import set_beamline as set_log_beamline @@ -19,4 +19,4 @@ def synchrotron() -> Synchrotron: @device_factory() def pgm() -> PGM: - return PGM(prefix=f"{PREFIX.beamline_prefix}-OP-PGM-01:", grating=Grating) + return i05pgm() diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index 03c364f5174..4571f48eab6 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -1,7 +1,9 @@ +from dodal.beamlines.i05_shared import pgm as i05pgm from dodal.common.beamlines.beamline_utils import ( device_factory, ) from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.devices.pgm import PGM from dodal.devices.synchrotron import Synchrotron from dodal.log import set_beamline as set_log_beamline from dodal.utils import BeamlinePrefix, get_beamline_name @@ -12,6 +14,11 @@ set_utils_beamline(BL) +@device_factory() +def pgm() -> PGM: + return i05pgm() + + @device_factory() def synchrotron() -> Synchrotron: return Synchrotron() diff --git a/src/dodal/beamlines/i05_shared.py b/src/dodal/beamlines/i05_shared.py new file mode 100644 index 00000000000..8a6258fae82 --- /dev/null +++ b/src/dodal/beamlines/i05_shared.py @@ -0,0 +1,16 @@ +from dodal.common.beamlines.beamline_utils import device_factory +from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.devices.i05.enums import Grating +from dodal.devices.pgm import PGM +from dodal.log import set_beamline as set_log_beamline +from dodal.utils import BeamlinePrefix, get_beamline_name + +BL = get_beamline_name("i05-shared") +PREFIX = BeamlinePrefix("i05", "I") +set_log_beamline(BL) +set_utils_beamline(BL) + + +@device_factory() +def pgm() -> PGM: + return PGM(prefix=f"{PREFIX.beamline_prefix}-OP-PGM-01:", grating=Grating) From db2bf55fa4d740e0be7cfccfecaafbc9c5f73140 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Wed, 30 Jul 2025 20:26:23 +0000 Subject: [PATCH 02/27] change names to snake case --- src/dodal/beamlines/i05.py | 4 ++-- src/dodal/beamlines/i05_1.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dodal/beamlines/i05.py b/src/dodal/beamlines/i05.py index ab984983477..11a972be31a 100644 --- a/src/dodal/beamlines/i05.py +++ b/src/dodal/beamlines/i05.py @@ -1,4 +1,4 @@ -from dodal.beamlines.i05_shared import pgm as i05pgm +from dodal.beamlines.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.devices.pgm import PGM @@ -19,4 +19,4 @@ def synchrotron() -> Synchrotron: @device_factory() def pgm() -> PGM: - return i05pgm() + return i05_pgm() diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index 4571f48eab6..1647d6a7fcb 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -1,4 +1,4 @@ -from dodal.beamlines.i05_shared import pgm as i05pgm +from dodal.beamlines.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import ( device_factory, ) @@ -16,7 +16,7 @@ @device_factory() def pgm() -> PGM: - return i05pgm() + return i05_pgm() @device_factory() From 7a22f71002949bcd623173d11e201322a3ffa92f Mon Sep 17 00:00:00 2001 From: eir17846 Date: Wed, 30 Jul 2025 20:27:54 +0000 Subject: [PATCH 03/27] minor import change --- src/dodal/beamlines/i05_1.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index 1647d6a7fcb..a1f89dbf294 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -1,7 +1,5 @@ from dodal.beamlines.i05_shared import pgm as i05_pgm -from dodal.common.beamlines.beamline_utils import ( - device_factory, -) +from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.devices.pgm import PGM from dodal.devices.synchrotron import Synchrotron From 49c2eb5cad55ecfb919e20e18ca62d9ee298141c Mon Sep 17 00:00:00 2001 From: eir17846 Date: Wed, 30 Jul 2025 21:28:00 +0000 Subject: [PATCH 04/27] create base mirror --- src/dodal/devices/common_mirror.py | 16 ++++++++++++++++ src/dodal/devices/i10/mirrors.py | 8 ++------ 2 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 src/dodal/devices/common_mirror.py diff --git a/src/dodal/devices/common_mirror.py b/src/dodal/devices/common_mirror.py new file mode 100644 index 00000000000..230caab386c --- /dev/null +++ b/src/dodal/devices/common_mirror.py @@ -0,0 +1,16 @@ +from ophyd_async.epics.motor import Motor + +from dodal.devices.motors import XYZStage + + +class BaseMirror(XYZStage): + def __init__( + self, + prefix: str, + name: str = "", + ): + with self.add_children_as_readables(): + self.yaw = Motor(prefix + "YAW") + self.pitch = Motor(prefix + "PITCH") + self.roll = Motor(prefix + "ROLL") + super().__init__(prefix, name) diff --git a/src/dodal/devices/i10/mirrors.py b/src/dodal/devices/i10/mirrors.py index e2a1eefc26a..5b9552e2176 100644 --- a/src/dodal/devices/i10/mirrors.py +++ b/src/dodal/devices/i10/mirrors.py @@ -1,19 +1,15 @@ from ophyd_async.epics.core import epics_signal_rw -from ophyd_async.epics.motor import Motor -from dodal.devices.motors import XYZStage +from dodal.devices.common_mirror import BaseMirror -class PiezoMirror(XYZStage): +class PiezoMirror(BaseMirror): def __init__( self, prefix: str, name: str = "", ): with self.add_children_as_readables(): - self.yaw = Motor(prefix + "YAW") - self.pitch = Motor(prefix + "PITCH") - self.roll = Motor(prefix + "ROLL") self.fine_pitch = epics_signal_rw( float, read_pv=prefix + "FPITCH:RBV:AI", From 81166cb213ab22129e73f9c2e209ce15dfc919f1 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Wed, 30 Jul 2025 21:41:06 +0000 Subject: [PATCH 05/27] add m1 mirror --- src/dodal/beamlines/i05.py | 7 +++++++ src/dodal/beamlines/i05_1.py | 7 +++++++ src/dodal/beamlines/i05_shared.py | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/src/dodal/beamlines/i05.py b/src/dodal/beamlines/i05.py index 11a972be31a..6aeb2eb0102 100644 --- a/src/dodal/beamlines/i05.py +++ b/src/dodal/beamlines/i05.py @@ -1,6 +1,8 @@ +from dodal.beamlines.i05_shared import m1 as i05_m1 from dodal.beamlines.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.devices.common_mirror import BaseMirror from dodal.devices.pgm import PGM from dodal.devices.synchrotron import Synchrotron from dodal.log import set_beamline as set_log_beamline @@ -20,3 +22,8 @@ def synchrotron() -> Synchrotron: @device_factory() def pgm() -> PGM: return i05_pgm() + + +@device_factory() +def m1() -> BaseMirror: + return i05_m1() diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index a1f89dbf294..5c0ca42c640 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -1,6 +1,8 @@ +from dodal.beamlines.i05_shared import m1 as i05_m1 from dodal.beamlines.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.devices.common_mirror import BaseMirror from dodal.devices.pgm import PGM from dodal.devices.synchrotron import Synchrotron from dodal.log import set_beamline as set_log_beamline @@ -20,3 +22,8 @@ def pgm() -> PGM: @device_factory() def synchrotron() -> Synchrotron: return Synchrotron() + + +@device_factory() +def m1() -> BaseMirror: + return i05_m1() diff --git a/src/dodal/beamlines/i05_shared.py b/src/dodal/beamlines/i05_shared.py index 8a6258fae82..001c5e7367c 100644 --- a/src/dodal/beamlines/i05_shared.py +++ b/src/dodal/beamlines/i05_shared.py @@ -1,5 +1,6 @@ from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline +from dodal.devices.common_mirror import BaseMirror from dodal.devices.i05.enums import Grating from dodal.devices.pgm import PGM from dodal.log import set_beamline as set_log_beamline @@ -14,3 +15,8 @@ @device_factory() def pgm() -> PGM: return PGM(prefix=f"{PREFIX.beamline_prefix}-OP-PGM-01:", grating=Grating) + + +@device_factory() +def m1() -> BaseMirror: + return BaseMirror(prefix=f"{PREFIX.beamline_prefix}-OP-COL-01:") From a6bb6f1d8dc5e697045311a4a46cb32f634c0a97 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Thu, 31 Jul 2025 10:06:53 +0000 Subject: [PATCH 06/27] add XY and XYZ collimating mirrrors --- src/dodal/beamlines/i05.py | 4 ++-- src/dodal/beamlines/i05_1.py | 4 ++-- src/dodal/beamlines/i05_shared.py | 6 ++--- src/dodal/devices/collimating_mirror.py | 29 +++++++++++++++++++++++++ src/dodal/devices/common_mirror.py | 16 -------------- src/dodal/devices/i10/mirrors.py | 2 +- 6 files changed, 37 insertions(+), 24 deletions(-) create mode 100644 src/dodal/devices/collimating_mirror.py delete mode 100644 src/dodal/devices/common_mirror.py diff --git a/src/dodal/beamlines/i05.py b/src/dodal/beamlines/i05.py index 6aeb2eb0102..1fbbf5d4472 100644 --- a/src/dodal/beamlines/i05.py +++ b/src/dodal/beamlines/i05.py @@ -2,7 +2,7 @@ from dodal.beamlines.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.devices.common_mirror import BaseMirror +from dodal.devices.collimating_mirror import CollMirrorXYZ from dodal.devices.pgm import PGM from dodal.devices.synchrotron import Synchrotron from dodal.log import set_beamline as set_log_beamline @@ -25,5 +25,5 @@ def pgm() -> PGM: @device_factory() -def m1() -> BaseMirror: +def m1() -> CollMirrorXYZ: return i05_m1() diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index 5c0ca42c640..d01a0562af3 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -2,7 +2,7 @@ from dodal.beamlines.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.devices.common_mirror import BaseMirror +from dodal.devices.collimating_mirror import CollMirrorXYZ from dodal.devices.pgm import PGM from dodal.devices.synchrotron import Synchrotron from dodal.log import set_beamline as set_log_beamline @@ -25,5 +25,5 @@ def synchrotron() -> Synchrotron: @device_factory() -def m1() -> BaseMirror: +def m1() -> CollMirrorXYZ: return i05_m1() diff --git a/src/dodal/beamlines/i05_shared.py b/src/dodal/beamlines/i05_shared.py index 001c5e7367c..817c2f86133 100644 --- a/src/dodal/beamlines/i05_shared.py +++ b/src/dodal/beamlines/i05_shared.py @@ -1,6 +1,6 @@ from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.devices.common_mirror import BaseMirror +from dodal.devices.collimating_mirror import CollMirrorXYZ from dodal.devices.i05.enums import Grating from dodal.devices.pgm import PGM from dodal.log import set_beamline as set_log_beamline @@ -18,5 +18,5 @@ def pgm() -> PGM: @device_factory() -def m1() -> BaseMirror: - return BaseMirror(prefix=f"{PREFIX.beamline_prefix}-OP-COL-01:") +def m1() -> CollMirrorXYZ: + return CollMirrorXYZ(prefix=f"{PREFIX.beamline_prefix}-OP-COL-01:") diff --git a/src/dodal/devices/collimating_mirror.py b/src/dodal/devices/collimating_mirror.py new file mode 100644 index 00000000000..5af20fbd44d --- /dev/null +++ b/src/dodal/devices/collimating_mirror.py @@ -0,0 +1,29 @@ +from ophyd_async.epics.motor import Motor + +from dodal.devices.motors import XYStage, XYZStage + + +class CollMirrorXY(XYStage): + def __init__( + self, + prefix: str, + name: str = "", + ): + with self.add_children_as_readables(): + self.yaw = Motor(prefix + "YAW") + self.pitch = Motor(prefix + "PITCH") + self.roll = Motor(prefix + "ROLL") + super().__init__(prefix, name) + + +class CollMirrorXYZ(XYZStage): + def __init__( + self, + prefix: str, + name: str = "", + ): + with self.add_children_as_readables(): + self.yaw = Motor(prefix + "YAW") + self.pitch = Motor(prefix + "PITCH") + self.roll = Motor(prefix + "ROLL") + super().__init__(prefix, name) diff --git a/src/dodal/devices/common_mirror.py b/src/dodal/devices/common_mirror.py deleted file mode 100644 index 230caab386c..00000000000 --- a/src/dodal/devices/common_mirror.py +++ /dev/null @@ -1,16 +0,0 @@ -from ophyd_async.epics.motor import Motor - -from dodal.devices.motors import XYZStage - - -class BaseMirror(XYZStage): - def __init__( - self, - prefix: str, - name: str = "", - ): - with self.add_children_as_readables(): - self.yaw = Motor(prefix + "YAW") - self.pitch = Motor(prefix + "PITCH") - self.roll = Motor(prefix + "ROLL") - super().__init__(prefix, name) diff --git a/src/dodal/devices/i10/mirrors.py b/src/dodal/devices/i10/mirrors.py index 5b9552e2176..f00dba8e6d3 100644 --- a/src/dodal/devices/i10/mirrors.py +++ b/src/dodal/devices/i10/mirrors.py @@ -1,6 +1,6 @@ from ophyd_async.epics.core import epics_signal_rw -from dodal.devices.common_mirror import BaseMirror +from dodal.devices.collimating_mirror import BaseMirror class PiezoMirror(BaseMirror): From 6e7a53302282eb69fe05df347aa4482965f206c3 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Thu, 31 Jul 2025 10:07:28 +0000 Subject: [PATCH 07/27] fix i10 --- src/dodal/devices/i10/mirrors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dodal/devices/i10/mirrors.py b/src/dodal/devices/i10/mirrors.py index f00dba8e6d3..8fe7010157d 100644 --- a/src/dodal/devices/i10/mirrors.py +++ b/src/dodal/devices/i10/mirrors.py @@ -1,9 +1,9 @@ from ophyd_async.epics.core import epics_signal_rw -from dodal.devices.collimating_mirror import BaseMirror +from dodal.devices.collimating_mirror import CollMirrorXYZ -class PiezoMirror(BaseMirror): +class PiezoMirror(CollMirrorXYZ): def __init__( self, prefix: str, From ed3592b51bcd83c3dc6b816e46cca7e102c4b093 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Thu, 31 Jul 2025 11:49:05 +0000 Subject: [PATCH 08/27] add piezo mirror --- src/dodal/beamlines/i05.py | 10 +++++++-- src/dodal/beamlines/i05_1.py | 10 +++++++-- src/dodal/beamlines/i05_shared.py | 11 +++++++--- ...collimating_mirror.py => common_mirror.py} | 22 +++++++++++++++++-- src/dodal/devices/i10/mirrors.py | 8 +++++-- 5 files changed, 50 insertions(+), 11 deletions(-) rename src/dodal/devices/{collimating_mirror.py => common_mirror.py} (54%) diff --git a/src/dodal/beamlines/i05.py b/src/dodal/beamlines/i05.py index 1fbbf5d4472..5bbf43f3d5a 100644 --- a/src/dodal/beamlines/i05.py +++ b/src/dodal/beamlines/i05.py @@ -1,8 +1,9 @@ from dodal.beamlines.i05_shared import m1 as i05_m1 +from dodal.beamlines.i05_shared import m3mj6 as i05_m3mj6 from dodal.beamlines.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.devices.collimating_mirror import CollMirrorXYZ +from dodal.devices.common_mirror import XYZCollMirror, XYZPiezoCollMirror from dodal.devices.pgm import PGM from dodal.devices.synchrotron import Synchrotron from dodal.log import set_beamline as set_log_beamline @@ -25,5 +26,10 @@ def pgm() -> PGM: @device_factory() -def m1() -> CollMirrorXYZ: +def m1() -> XYZCollMirror: return i05_m1() + + +@device_factory() +def m3mj6() -> XYZPiezoCollMirror: + return i05_m3mj6() diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index d01a0562af3..8dd488e4f77 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -1,8 +1,9 @@ from dodal.beamlines.i05_shared import m1 as i05_m1 +from dodal.beamlines.i05_shared import m3mj6 as i05_m3mj6 from dodal.beamlines.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.devices.collimating_mirror import CollMirrorXYZ +from dodal.devices.common_mirror import XYZCollMirror, XYZPiezoCollMirror from dodal.devices.pgm import PGM from dodal.devices.synchrotron import Synchrotron from dodal.log import set_beamline as set_log_beamline @@ -25,5 +26,10 @@ def synchrotron() -> Synchrotron: @device_factory() -def m1() -> CollMirrorXYZ: +def m1() -> XYZCollMirror: return i05_m1() + + +@device_factory() +def m3mj6() -> XYZPiezoCollMirror: + return i05_m3mj6() diff --git a/src/dodal/beamlines/i05_shared.py b/src/dodal/beamlines/i05_shared.py index 817c2f86133..f058cd2e968 100644 --- a/src/dodal/beamlines/i05_shared.py +++ b/src/dodal/beamlines/i05_shared.py @@ -1,6 +1,6 @@ from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.devices.collimating_mirror import CollMirrorXYZ +from dodal.devices.common_mirror import XYZCollMirror, XYZPiezoCollMirror from dodal.devices.i05.enums import Grating from dodal.devices.pgm import PGM from dodal.log import set_beamline as set_log_beamline @@ -18,5 +18,10 @@ def pgm() -> PGM: @device_factory() -def m1() -> CollMirrorXYZ: - return CollMirrorXYZ(prefix=f"{PREFIX.beamline_prefix}-OP-COL-01:") +def m1() -> XYZCollMirror: + return XYZCollMirror(prefix=f"{PREFIX.beamline_prefix}-OP-COL-01:") + + +@device_factory() +def m3mj6() -> XYZPiezoCollMirror: + return XYZPiezoCollMirror(prefix=f"{PREFIX.beamline_prefix}-OP-SWTCH-01:") diff --git a/src/dodal/devices/collimating_mirror.py b/src/dodal/devices/common_mirror.py similarity index 54% rename from src/dodal/devices/collimating_mirror.py rename to src/dodal/devices/common_mirror.py index 5af20fbd44d..ea709a7a04e 100644 --- a/src/dodal/devices/collimating_mirror.py +++ b/src/dodal/devices/common_mirror.py @@ -1,9 +1,10 @@ +from ophyd_async.epics.core import epics_signal_rw from ophyd_async.epics.motor import Motor from dodal.devices.motors import XYStage, XYZStage -class CollMirrorXY(XYStage): +class XYCollMirror(XYStage): def __init__( self, prefix: str, @@ -16,7 +17,7 @@ def __init__( super().__init__(prefix, name) -class CollMirrorXYZ(XYZStage): +class XYZCollMirror(XYZStage): def __init__( self, prefix: str, @@ -27,3 +28,20 @@ def __init__( self.pitch = Motor(prefix + "PITCH") self.roll = Motor(prefix + "ROLL") super().__init__(prefix, name) + + +class XYZPiezoCollMirror(XYZCollMirror): + def __init__( + self, + prefix: str, + fpitch_read_suffix: str = "FPITCH:RBV", + fpitch_write_suffix: str = "FPITCH:DMD", + name: str = "", + ): + with self.add_children_as_readables(): + self.fine_pitch = epics_signal_rw( + float, + read_pv=prefix + fpitch_read_suffix, + write_pv=prefix + fpitch_write_suffix, + ) + super().__init__(prefix, name) diff --git a/src/dodal/devices/i10/mirrors.py b/src/dodal/devices/i10/mirrors.py index 8fe7010157d..e2a1eefc26a 100644 --- a/src/dodal/devices/i10/mirrors.py +++ b/src/dodal/devices/i10/mirrors.py @@ -1,15 +1,19 @@ from ophyd_async.epics.core import epics_signal_rw +from ophyd_async.epics.motor import Motor -from dodal.devices.collimating_mirror import CollMirrorXYZ +from dodal.devices.motors import XYZStage -class PiezoMirror(CollMirrorXYZ): +class PiezoMirror(XYZStage): def __init__( self, prefix: str, name: str = "", ): with self.add_children_as_readables(): + self.yaw = Motor(prefix + "YAW") + self.pitch = Motor(prefix + "PITCH") + self.roll = Motor(prefix + "ROLL") self.fine_pitch = epics_signal_rw( float, read_pv=prefix + "FPITCH:RBV:AI", From 1c292b65eeb15d7ce44faf8c31891d363bf2d3c2 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Thu, 31 Jul 2025 12:00:50 +0000 Subject: [PATCH 09/27] add more mirrors --- src/dodal/beamlines/i05.py | 11 +++++++++++ src/dodal/beamlines/i05_1.py | 19 +++++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/dodal/beamlines/i05.py b/src/dodal/beamlines/i05.py index 5bbf43f3d5a..9fc4bfc24ba 100644 --- a/src/dodal/beamlines/i05.py +++ b/src/dodal/beamlines/i05.py @@ -20,6 +20,9 @@ def synchrotron() -> Synchrotron: return Synchrotron() +# BL05 shared devices + + @device_factory() def pgm() -> PGM: return i05_pgm() @@ -33,3 +36,11 @@ def m1() -> XYZCollMirror: @device_factory() def m3mj6() -> XYZPiezoCollMirror: return i05_m3mj6() + + +# beamline specific devices + + +@device_factory() +def m4m5() -> XYZCollMirror: + return XYZCollMirror(prefix=f"{PREFIX.beamline_prefix}-OP-RFM-01:") diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index 8dd488e4f77..b14b9341e4f 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -16,13 +16,16 @@ @device_factory() -def pgm() -> PGM: - return i05_pgm() +def synchrotron() -> Synchrotron: + return Synchrotron() + + +# BL05 shared devices @device_factory() -def synchrotron() -> Synchrotron: - return Synchrotron() +def pgm() -> PGM: + return i05_pgm() @device_factory() @@ -33,3 +36,11 @@ def m1() -> XYZCollMirror: @device_factory() def m3mj6() -> XYZPiezoCollMirror: return i05_m3mj6() + + +# beamline specific devices + + +@device_factory() +def mj7j8() -> XYZPiezoCollMirror: + return XYZPiezoCollMirror(prefix=f"{PREFIX.beamline_prefix}-OP-RFM-01:") From 56e074b6d10684db6377852348044d06e4450b36 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Thu, 31 Jul 2025 15:22:10 +0000 Subject: [PATCH 10/27] move i05-shared to beamline specific utils --- .../{beamlines => beamline_specific_utils}/i05_shared.py | 7 +------ src/dodal/beamlines/i05.py | 2 +- src/dodal/beamlines/i05_1.py | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) rename src/dodal/{beamlines => beamline_specific_utils}/i05_shared.py (51%) diff --git a/src/dodal/beamlines/i05_shared.py b/src/dodal/beamline_specific_utils/i05_shared.py similarity index 51% rename from src/dodal/beamlines/i05_shared.py rename to src/dodal/beamline_specific_utils/i05_shared.py index 8a6258fae82..c0b43915f16 100644 --- a/src/dodal/beamlines/i05_shared.py +++ b/src/dodal/beamline_specific_utils/i05_shared.py @@ -1,14 +1,9 @@ from dodal.common.beamlines.beamline_utils import device_factory -from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.devices.i05.enums import Grating from dodal.devices.pgm import PGM -from dodal.log import set_beamline as set_log_beamline -from dodal.utils import BeamlinePrefix, get_beamline_name +from dodal.utils import BeamlinePrefix -BL = get_beamline_name("i05-shared") PREFIX = BeamlinePrefix("i05", "I") -set_log_beamline(BL) -set_utils_beamline(BL) @device_factory() diff --git a/src/dodal/beamlines/i05.py b/src/dodal/beamlines/i05.py index 11a972be31a..85f6879d212 100644 --- a/src/dodal/beamlines/i05.py +++ b/src/dodal/beamlines/i05.py @@ -1,4 +1,4 @@ -from dodal.beamlines.i05_shared import pgm as i05_pgm +from dodal.beamline_specific_utils.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.devices.pgm import PGM diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index a1f89dbf294..fd9826d96c9 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -1,4 +1,4 @@ -from dodal.beamlines.i05_shared import pgm as i05_pgm +from dodal.beamline_specific_utils.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.devices.pgm import PGM From ba7f8194a7d3707012e805328b022f4c4980c244 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Thu, 31 Jul 2025 15:23:39 +0000 Subject: [PATCH 11/27] amend beamlines init file --- src/dodal/beamlines/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dodal/beamlines/__init__.py b/src/dodal/beamlines/__init__.py index 23a14f53d56..e5d29c0706f 100644 --- a/src/dodal/beamlines/__init__.py +++ b/src/dodal/beamlines/__init__.py @@ -10,7 +10,6 @@ # dictionary, which maps ${BEAMLINE} to dodal.beamlines. _BEAMLINE_NAME_OVERRIDES = { "i05-1": "i05_1", - "i05-shared": "i05_shared", "b07-1": "b07_1", "i09-1": "i09_1", "i13-1": "i13_1", From b89e69a9e8444b7e8bbc4163462d4e6ec7050294 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Thu, 31 Jul 2025 15:22:10 +0000 Subject: [PATCH 12/27] move i05-shared to beamline specific utils --- .../{beamlines => beamline_specific_utils}/i05_shared.py | 7 +------ src/dodal/beamlines/i05.py | 6 +++--- src/dodal/beamlines/i05_1.py | 6 +++--- 3 files changed, 7 insertions(+), 12 deletions(-) rename src/dodal/{beamlines => beamline_specific_utils}/i05_shared.py (69%) diff --git a/src/dodal/beamlines/i05_shared.py b/src/dodal/beamline_specific_utils/i05_shared.py similarity index 69% rename from src/dodal/beamlines/i05_shared.py rename to src/dodal/beamline_specific_utils/i05_shared.py index f058cd2e968..7f9d780937d 100644 --- a/src/dodal/beamlines/i05_shared.py +++ b/src/dodal/beamline_specific_utils/i05_shared.py @@ -1,15 +1,10 @@ from dodal.common.beamlines.beamline_utils import device_factory -from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.devices.common_mirror import XYZCollMirror, XYZPiezoCollMirror from dodal.devices.i05.enums import Grating from dodal.devices.pgm import PGM -from dodal.log import set_beamline as set_log_beamline -from dodal.utils import BeamlinePrefix, get_beamline_name +from dodal.utils import BeamlinePrefix -BL = get_beamline_name("i05-shared") PREFIX = BeamlinePrefix("i05", "I") -set_log_beamline(BL) -set_utils_beamline(BL) @device_factory() diff --git a/src/dodal/beamlines/i05.py b/src/dodal/beamlines/i05.py index 9fc4bfc24ba..6e50553c965 100644 --- a/src/dodal/beamlines/i05.py +++ b/src/dodal/beamlines/i05.py @@ -1,6 +1,6 @@ -from dodal.beamlines.i05_shared import m1 as i05_m1 -from dodal.beamlines.i05_shared import m3mj6 as i05_m3mj6 -from dodal.beamlines.i05_shared import pgm as i05_pgm +from dodal.beamline_specific_utils.i05_shared import m1 as i05_m1 +from dodal.beamline_specific_utils.i05_shared import m3mj6 as i05_m3mj6 +from dodal.beamline_specific_utils.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.devices.common_mirror import XYZCollMirror, XYZPiezoCollMirror diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index b14b9341e4f..d634e89ee3b 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -1,6 +1,6 @@ -from dodal.beamlines.i05_shared import m1 as i05_m1 -from dodal.beamlines.i05_shared import m3mj6 as i05_m3mj6 -from dodal.beamlines.i05_shared import pgm as i05_pgm +from dodal.beamline_specific_utils.i05_shared import m1 as i05_m1 +from dodal.beamline_specific_utils.i05_shared import m3mj6 as i05_m3mj6 +from dodal.beamline_specific_utils.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.devices.common_mirror import XYZCollMirror, XYZPiezoCollMirror From 0c889cb23e8409939c7a1bdd366b7fb04f3fc459 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Thu, 31 Jul 2025 15:23:39 +0000 Subject: [PATCH 13/27] amend beamlines init file --- src/dodal/beamlines/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dodal/beamlines/__init__.py b/src/dodal/beamlines/__init__.py index 23a14f53d56..e5d29c0706f 100644 --- a/src/dodal/beamlines/__init__.py +++ b/src/dodal/beamlines/__init__.py @@ -10,7 +10,6 @@ # dictionary, which maps ${BEAMLINE} to dodal.beamlines. _BEAMLINE_NAME_OVERRIDES = { "i05-1": "i05_1", - "i05-shared": "i05_shared", "b07-1": "b07_1", "i09-1": "i09_1", "i13-1": "i13_1", From 6b57679887358fbf0ee99f4546e1949bd1905c62 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Thu, 31 Jul 2025 15:44:58 +0000 Subject: [PATCH 14/27] remove XY coll mirror - will be a separate issue --- src/dodal/devices/common_mirror.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/dodal/devices/common_mirror.py b/src/dodal/devices/common_mirror.py index ea709a7a04e..9de0f8b7392 100644 --- a/src/dodal/devices/common_mirror.py +++ b/src/dodal/devices/common_mirror.py @@ -1,20 +1,7 @@ from ophyd_async.epics.core import epics_signal_rw from ophyd_async.epics.motor import Motor -from dodal.devices.motors import XYStage, XYZStage - - -class XYCollMirror(XYStage): - def __init__( - self, - prefix: str, - name: str = "", - ): - with self.add_children_as_readables(): - self.yaw = Motor(prefix + "YAW") - self.pitch = Motor(prefix + "PITCH") - self.roll = Motor(prefix + "ROLL") - super().__init__(prefix, name) +from dodal.devices.motors import XYZStage class XYZCollMirror(XYZStage): From ca52f00eeee589b3985fe06cc630294f4959f779 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Fri, 1 Aug 2025 13:46:19 +0000 Subject: [PATCH 15/27] rename mirror class --- src/dodal/beamline_specific_utils/i05_shared.py | 10 +++++----- src/dodal/beamlines/i05.py | 10 +++++----- src/dodal/beamlines/i05_1.py | 10 +++++----- src/dodal/devices/common_mirror.py | 4 ++-- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/dodal/beamline_specific_utils/i05_shared.py b/src/dodal/beamline_specific_utils/i05_shared.py index 7f9d780937d..476fe6e6bc0 100644 --- a/src/dodal/beamline_specific_utils/i05_shared.py +++ b/src/dodal/beamline_specific_utils/i05_shared.py @@ -1,5 +1,5 @@ from dodal.common.beamlines.beamline_utils import device_factory -from dodal.devices.common_mirror import XYZCollMirror, XYZPiezoCollMirror +from dodal.devices.common_mirror import XYZCollimatingMirror, XYZPiezoCollimatingMirror from dodal.devices.i05.enums import Grating from dodal.devices.pgm import PGM from dodal.utils import BeamlinePrefix @@ -13,10 +13,10 @@ def pgm() -> PGM: @device_factory() -def m1() -> XYZCollMirror: - return XYZCollMirror(prefix=f"{PREFIX.beamline_prefix}-OP-COL-01:") +def m1() -> XYZCollimatingMirror: + return XYZCollimatingMirror(prefix=f"{PREFIX.beamline_prefix}-OP-COL-01:") @device_factory() -def m3mj6() -> XYZPiezoCollMirror: - return XYZPiezoCollMirror(prefix=f"{PREFIX.beamline_prefix}-OP-SWTCH-01:") +def m3mj6() -> XYZPiezoCollimatingMirror: + return XYZPiezoCollimatingMirror(prefix=f"{PREFIX.beamline_prefix}-OP-SWTCH-01:") diff --git a/src/dodal/beamlines/i05.py b/src/dodal/beamlines/i05.py index 6e50553c965..833f3afcb57 100644 --- a/src/dodal/beamlines/i05.py +++ b/src/dodal/beamlines/i05.py @@ -3,7 +3,7 @@ from dodal.beamline_specific_utils.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.devices.common_mirror import XYZCollMirror, XYZPiezoCollMirror +from dodal.devices.common_mirror import XYZCollimatingMirror, XYZPiezoCollimatingMirror from dodal.devices.pgm import PGM from dodal.devices.synchrotron import Synchrotron from dodal.log import set_beamline as set_log_beamline @@ -29,12 +29,12 @@ def pgm() -> PGM: @device_factory() -def m1() -> XYZCollMirror: +def m1() -> XYZCollimatingMirror: return i05_m1() @device_factory() -def m3mj6() -> XYZPiezoCollMirror: +def m3mj6() -> XYZPiezoCollimatingMirror: return i05_m3mj6() @@ -42,5 +42,5 @@ def m3mj6() -> XYZPiezoCollMirror: @device_factory() -def m4m5() -> XYZCollMirror: - return XYZCollMirror(prefix=f"{PREFIX.beamline_prefix}-OP-RFM-01:") +def m4m5() -> XYZCollimatingMirror: + return XYZCollimatingMirror(prefix=f"{PREFIX.beamline_prefix}-OP-RFM-01:") diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index 4818bcfdc10..2118d733014 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -3,7 +3,7 @@ from dodal.beamline_specific_utils.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.devices.common_mirror import XYZCollMirror, XYZPiezoCollMirror +from dodal.devices.common_mirror import XYZCollimatingMirror, XYZPiezoCollimatingMirror from dodal.devices.pgm import PGM from dodal.devices.synchrotron import Synchrotron from dodal.log import set_beamline as set_log_beamline @@ -34,12 +34,12 @@ def pgm() -> PGM: @device_factory() -def m1() -> XYZCollMirror: +def m1() -> XYZCollimatingMirror: return i05_m1() @device_factory() -def m3mj6() -> XYZPiezoCollMirror: +def m3mj6() -> XYZPiezoCollimatingMirror: return i05_m3mj6() @@ -47,5 +47,5 @@ def m3mj6() -> XYZPiezoCollMirror: @device_factory() -def mj7j8() -> XYZPiezoCollMirror: - return XYZPiezoCollMirror(prefix=f"{PREFIX.beamline_prefix}-OP-RFM-01:") +def mj7j8() -> XYZPiezoCollimatingMirror: + return XYZPiezoCollimatingMirror(prefix=f"{PREFIX.beamline_prefix}-OP-RFM-01:") diff --git a/src/dodal/devices/common_mirror.py b/src/dodal/devices/common_mirror.py index 9de0f8b7392..4a3f57f6064 100644 --- a/src/dodal/devices/common_mirror.py +++ b/src/dodal/devices/common_mirror.py @@ -4,7 +4,7 @@ from dodal.devices.motors import XYZStage -class XYZCollMirror(XYZStage): +class XYZCollimatingMirror(XYZStage): def __init__( self, prefix: str, @@ -17,7 +17,7 @@ def __init__( super().__init__(prefix, name) -class XYZPiezoCollMirror(XYZCollMirror): +class XYZPiezoCollimatingMirror(XYZCollimatingMirror): def __init__( self, prefix: str, From fdce2214b1d240a71b71b418c05bfbfccc4d6128 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Fri, 1 Aug 2025 16:08:49 +0000 Subject: [PATCH 16/27] move xyzpitchyawroll to motors --- .../beamline_specific_utils/i05_shared.py | 11 ++--- src/dodal/beamlines/i05.py | 21 +++++----- src/dodal/beamlines/i05_1.py | 26 ++++++------ src/dodal/devices/common_mirror.py | 40 ++++++++++++------- src/dodal/devices/motors.py | 13 ++++++ 5 files changed, 68 insertions(+), 43 deletions(-) diff --git a/src/dodal/beamline_specific_utils/i05_shared.py b/src/dodal/beamline_specific_utils/i05_shared.py index 476fe6e6bc0..e06a1962bb7 100644 --- a/src/dodal/beamline_specific_utils/i05_shared.py +++ b/src/dodal/beamline_specific_utils/i05_shared.py @@ -1,6 +1,7 @@ from dodal.common.beamlines.beamline_utils import device_factory -from dodal.devices.common_mirror import XYZCollimatingMirror, XYZPiezoCollimatingMirror +from dodal.devices.common_mirror import XYZPiezoSwitchingMirror from dodal.devices.i05.enums import Grating +from dodal.devices.motors import XYZPitchYawRollStage from dodal.devices.pgm import PGM from dodal.utils import BeamlinePrefix @@ -13,10 +14,10 @@ def pgm() -> PGM: @device_factory() -def m1() -> XYZCollimatingMirror: - return XYZCollimatingMirror(prefix=f"{PREFIX.beamline_prefix}-OP-COL-01:") +def m1_collimating_mirror() -> XYZPitchYawRollStage: + return XYZPitchYawRollStage(prefix=f"{PREFIX.beamline_prefix}-OP-COL-01:") @device_factory() -def m3mj6() -> XYZPiezoCollimatingMirror: - return XYZPiezoCollimatingMirror(prefix=f"{PREFIX.beamline_prefix}-OP-SWTCH-01:") +def m3mj6_switching_mirror() -> XYZPiezoSwitchingMirror: + return XYZPiezoSwitchingMirror(prefix=f"{PREFIX.beamline_prefix}-OP-SWTCH-01:") diff --git a/src/dodal/beamlines/i05.py b/src/dodal/beamlines/i05.py index 833f3afcb57..f19430f8fca 100644 --- a/src/dodal/beamlines/i05.py +++ b/src/dodal/beamlines/i05.py @@ -1,9 +1,12 @@ -from dodal.beamline_specific_utils.i05_shared import m1 as i05_m1 -from dodal.beamline_specific_utils.i05_shared import m3mj6 as i05_m3mj6 +from dodal.beamline_specific_utils.i05_shared import ( + m1_collimating_mirror, + m3mj6_switching_mirror, +) from dodal.beamline_specific_utils.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.devices.common_mirror import XYZCollimatingMirror, XYZPiezoCollimatingMirror +from dodal.devices.common_mirror import XYZPiezoSwitchingMirror, XYZSwitchingMirror +from dodal.devices.motors import XYZPitchYawRollStage from dodal.devices.pgm import PGM from dodal.devices.synchrotron import Synchrotron from dodal.log import set_beamline as set_log_beamline @@ -29,18 +32,18 @@ def pgm() -> PGM: @device_factory() -def m1() -> XYZCollimatingMirror: - return i05_m1() +def m1() -> XYZPitchYawRollStage: + return m1_collimating_mirror() @device_factory() -def m3mj6() -> XYZPiezoCollimatingMirror: - return i05_m3mj6() +def m3mj6() -> XYZPiezoSwitchingMirror: + return m3mj6_switching_mirror() # beamline specific devices @device_factory() -def m4m5() -> XYZCollimatingMirror: - return XYZCollimatingMirror(prefix=f"{PREFIX.beamline_prefix}-OP-RFM-01:") +def m4m5() -> XYZSwitchingMirror: + return XYZSwitchingMirror(prefix=f"{PREFIX.beamline_prefix}-OP-RFM-01:") diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index 2118d733014..2aee694183b 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -1,9 +1,12 @@ -from dodal.beamline_specific_utils.i05_shared import m1 as i05_m1 -from dodal.beamline_specific_utils.i05_shared import m3mj6 as i05_m3mj6 +from dodal.beamline_specific_utils.i05_shared import ( + m1_collimating_mirror, + m3mj6_switching_mirror, +) from dodal.beamline_specific_utils.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.devices.common_mirror import XYZCollimatingMirror, XYZPiezoCollimatingMirror +from dodal.devices.common_mirror import XYZPiezoSwitchingMirror +from dodal.devices.motors import XYZPitchYawRollStage from dodal.devices.pgm import PGM from dodal.devices.synchrotron import Synchrotron from dodal.log import set_beamline as set_log_beamline @@ -15,11 +18,6 @@ set_utils_beamline(BL) -@device_factory() -def pgm() -> PGM: - return i05_pgm() - - @device_factory() def synchrotron() -> Synchrotron: return Synchrotron() @@ -34,18 +32,18 @@ def pgm() -> PGM: @device_factory() -def m1() -> XYZCollimatingMirror: - return i05_m1() +def m1() -> XYZPitchYawRollStage: + return m1_collimating_mirror() @device_factory() -def m3mj6() -> XYZPiezoCollimatingMirror: - return i05_m3mj6() +def m3mj6() -> XYZPiezoSwitchingMirror: + return m3mj6_switching_mirror() # beamline specific devices @device_factory() -def mj7j8() -> XYZPiezoCollimatingMirror: - return XYZPiezoCollimatingMirror(prefix=f"{PREFIX.beamline_prefix}-OP-RFM-01:") +def mj7j8() -> XYZPiezoSwitchingMirror: + return XYZPiezoSwitchingMirror(prefix=f"{PREFIX.beamline_prefix}-OP-RFM-01:") diff --git a/src/dodal/devices/common_mirror.py b/src/dodal/devices/common_mirror.py index 4a3f57f6064..04cc4f4f5d8 100644 --- a/src/dodal/devices/common_mirror.py +++ b/src/dodal/devices/common_mirror.py @@ -1,34 +1,44 @@ -from ophyd_async.epics.core import epics_signal_rw -from ophyd_async.epics.motor import Motor +from ophyd_async.epics.core import epics_signal_r, epics_signal_rw -from dodal.devices.motors import XYZStage +from dodal.devices.motors import XYZPitchYawRollStage -class XYZCollimatingMirror(XYZStage): +class XYZPiezoCollimatingMirror(XYZPitchYawRollStage): def __init__( self, prefix: str, + fpitch_read_suffix: str = "FPITCH:RBV", + fpitch_write_suffix: str = "FPITCH:DMD", name: str = "", ): with self.add_children_as_readables(): - self.yaw = Motor(prefix + "YAW") - self.pitch = Motor(prefix + "PITCH") - self.roll = Motor(prefix + "ROLL") + self.fine_pitch = epics_signal_rw( + float, + read_pv=prefix + fpitch_read_suffix, + write_pv=prefix + fpitch_write_suffix, + ) super().__init__(prefix, name) -class XYZPiezoCollimatingMirror(XYZCollimatingMirror): +class XYZSwitchingMirror(XYZPitchYawRollStage): def __init__( self, prefix: str, - fpitch_read_suffix: str = "FPITCH:RBV", - fpitch_write_suffix: str = "FPITCH:DMD", + mirror_read_suffix: str = "MIRCTRL:RBV:MIRROR", name: str = "", ): with self.add_children_as_readables(): - self.fine_pitch = epics_signal_rw( - float, - read_pv=prefix + fpitch_read_suffix, - write_pv=prefix + fpitch_write_suffix, - ) + self.mirror = epics_signal_r(str, read_pv=prefix + mirror_read_suffix) + super().__init__(prefix, name) + + +class XYZPiezoSwitchingMirror(XYZPiezoCollimatingMirror): + def __init__( + self, + prefix: str, + mirror_read_suffix: str = "MIRCTRL:RBV:MIRROR", + name: str = "", + ): + with self.add_children_as_readables(): + self.mirror = epics_signal_r(str, read_pv=prefix + mirror_read_suffix) super().__init__(prefix, name) diff --git a/src/dodal/devices/motors.py b/src/dodal/devices/motors.py index b0ab7dd0268..1a0dd059d88 100644 --- a/src/dodal/devices/motors.py +++ b/src/dodal/devices/motors.py @@ -89,6 +89,19 @@ def __init__( super().__init__(prefix, name, x_infix, y_infix) +class XYZPitchYawRollStage(XYZStage): + def __init__( + self, + prefix: str, + name: str = "", + ): + with self.add_children_as_readables(): + self.yaw = Motor(prefix + "YAW") + self.pitch = Motor(prefix + "PITCH") + self.roll = Motor(prefix + "ROLL") + super().__init__(prefix, name) + + class SixAxisGonio(XYZStage): def __init__( self, From ccf8b5aac81ee0ffbaac67483162c468fd169e1f Mon Sep 17 00:00:00 2001 From: eir17846 Date: Fri, 1 Aug 2025 16:10:17 +0000 Subject: [PATCH 17/27] rename pgm --- src/dodal/beamline_specific_utils/i05_shared.py | 2 +- src/dodal/beamlines/i05.py | 2 +- src/dodal/beamlines/i05_1.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dodal/beamline_specific_utils/i05_shared.py b/src/dodal/beamline_specific_utils/i05_shared.py index e06a1962bb7..da4fcb4ee3e 100644 --- a/src/dodal/beamline_specific_utils/i05_shared.py +++ b/src/dodal/beamline_specific_utils/i05_shared.py @@ -9,7 +9,7 @@ @device_factory() -def pgm() -> PGM: +def i05_pgm() -> PGM: return PGM(prefix=f"{PREFIX.beamline_prefix}-OP-PGM-01:", grating=Grating) diff --git a/src/dodal/beamlines/i05.py b/src/dodal/beamlines/i05.py index f19430f8fca..60db7f7a1a4 100644 --- a/src/dodal/beamlines/i05.py +++ b/src/dodal/beamlines/i05.py @@ -1,8 +1,8 @@ from dodal.beamline_specific_utils.i05_shared import ( + i05_pgm, m1_collimating_mirror, m3mj6_switching_mirror, ) -from dodal.beamline_specific_utils.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.devices.common_mirror import XYZPiezoSwitchingMirror, XYZSwitchingMirror diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index 2aee694183b..4f61382078e 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -1,8 +1,8 @@ from dodal.beamline_specific_utils.i05_shared import ( + i05_pgm, m1_collimating_mirror, m3mj6_switching_mirror, ) -from dodal.beamline_specific_utils.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.devices.common_mirror import XYZPiezoSwitchingMirror From 2a97a79c5eb1f571dd938adfceb3658664630101 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Fri, 1 Aug 2025 19:57:15 +0000 Subject: [PATCH 18/27] add explicit parameters to child classes --- .../beamline_specific_utils/i05_shared.py | 2 +- src/dodal/beamlines/i05.py | 6 +-- src/dodal/beamlines/i05_1.py | 6 +-- src/dodal/devices/common_mirror.py | 49 ++++++++++++++++--- src/dodal/devices/motors.py | 14 ++++-- 5 files changed, 59 insertions(+), 18 deletions(-) diff --git a/src/dodal/beamline_specific_utils/i05_shared.py b/src/dodal/beamline_specific_utils/i05_shared.py index da4fcb4ee3e..e06a1962bb7 100644 --- a/src/dodal/beamline_specific_utils/i05_shared.py +++ b/src/dodal/beamline_specific_utils/i05_shared.py @@ -9,7 +9,7 @@ @device_factory() -def i05_pgm() -> PGM: +def pgm() -> PGM: return PGM(prefix=f"{PREFIX.beamline_prefix}-OP-PGM-01:", grating=Grating) diff --git a/src/dodal/beamlines/i05.py b/src/dodal/beamlines/i05.py index 60db7f7a1a4..e4d7a05fa7a 100644 --- a/src/dodal/beamlines/i05.py +++ b/src/dodal/beamlines/i05.py @@ -1,7 +1,7 @@ from dodal.beamline_specific_utils.i05_shared import ( - i05_pgm, m1_collimating_mirror, m3mj6_switching_mirror, + pgm, ) from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline @@ -27,8 +27,8 @@ def synchrotron() -> Synchrotron: @device_factory() -def pgm() -> PGM: - return i05_pgm() +def pgm_i05() -> PGM: + return pgm() @device_factory() diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index 4f61382078e..7e54705d0e4 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -1,7 +1,7 @@ from dodal.beamline_specific_utils.i05_shared import ( - i05_pgm, m1_collimating_mirror, m3mj6_switching_mirror, + pgm, ) from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline @@ -27,8 +27,8 @@ def synchrotron() -> Synchrotron: @device_factory() -def pgm() -> PGM: - return i05_pgm() +def pgm_i05() -> PGM: + return pgm() @device_factory() diff --git a/src/dodal/devices/common_mirror.py b/src/dodal/devices/common_mirror.py index 04cc4f4f5d8..86e0c24f0f9 100644 --- a/src/dodal/devices/common_mirror.py +++ b/src/dodal/devices/common_mirror.py @@ -1,15 +1,21 @@ from ophyd_async.epics.core import epics_signal_r, epics_signal_rw -from dodal.devices.motors import XYZPitchYawRollStage +from dodal.devices.motors import _X, _Y, _Z, XYZPitchYawRollStage class XYZPiezoCollimatingMirror(XYZPitchYawRollStage): def __init__( self, prefix: str, + name: str = "", + x_infix: str = _X, + y_infix: str = _Y, + z_infix: str = _Z, + pitch_infix: str = "PITCH", + yaw_infix: str = "YAW", + roll_infix: str = "ROLL", fpitch_read_suffix: str = "FPITCH:RBV", fpitch_write_suffix: str = "FPITCH:DMD", - name: str = "", ): with self.add_children_as_readables(): self.fine_pitch = epics_signal_rw( @@ -17,28 +23,57 @@ def __init__( read_pv=prefix + fpitch_read_suffix, write_pv=prefix + fpitch_write_suffix, ) - super().__init__(prefix, name) + super().__init__( + prefix, name, x_infix, y_infix, z_infix, pitch_infix, yaw_infix, roll_infix + ) class XYZSwitchingMirror(XYZPitchYawRollStage): def __init__( self, prefix: str, - mirror_read_suffix: str = "MIRCTRL:RBV:MIRROR", name: str = "", + x_infix: str = _X, + y_infix: str = _Y, + z_infix: str = _Z, + pitch_infix: str = "PITCH", + yaw_infix: str = "YAW", + roll_infix: str = "ROLL", + mirror_read_suffix: str = "MIRCTRL:RBV:MIRROR", ): with self.add_children_as_readables(): self.mirror = epics_signal_r(str, read_pv=prefix + mirror_read_suffix) - super().__init__(prefix, name) + super().__init__( + prefix, name, x_infix, y_infix, z_infix, pitch_infix, yaw_infix, roll_infix + ) class XYZPiezoSwitchingMirror(XYZPiezoCollimatingMirror): def __init__( self, prefix: str, - mirror_read_suffix: str = "MIRCTRL:RBV:MIRROR", name: str = "", + x_infix: str = _X, + y_infix: str = _Y, + z_infix: str = _Z, + pitch_infix: str = "PITCH", + yaw_infix: str = "YAW", + roll_infix: str = "ROLL", + fpitch_read_suffix: str = "FPITCH:RBV", + fpitch_write_suffix: str = "FPITCH:DMD", + mirror_read_suffix: str = "MIRCTRL:RBV:MIRROR", ): with self.add_children_as_readables(): self.mirror = epics_signal_r(str, read_pv=prefix + mirror_read_suffix) - super().__init__(prefix, name) + super().__init__( + prefix, + name, + x_infix, + y_infix, + z_infix, + pitch_infix, + yaw_infix, + roll_infix, + fpitch_read_suffix, + fpitch_write_suffix, + ) diff --git a/src/dodal/devices/motors.py b/src/dodal/devices/motors.py index 1a0dd059d88..6e21bc99219 100644 --- a/src/dodal/devices/motors.py +++ b/src/dodal/devices/motors.py @@ -94,12 +94,18 @@ def __init__( self, prefix: str, name: str = "", + x_infix: str = _X, + y_infix: str = _Y, + z_infix: str = _Z, + pitch_infix: str = "PITCH", + yaw_infix: str = "YAW", + roll_infix: str = "ROLL", ): with self.add_children_as_readables(): - self.yaw = Motor(prefix + "YAW") - self.pitch = Motor(prefix + "PITCH") - self.roll = Motor(prefix + "ROLL") - super().__init__(prefix, name) + self.pitch = Motor(prefix + pitch_infix) + self.yaw = Motor(prefix + yaw_infix) + self.roll = Motor(prefix + roll_infix) + super().__init__(prefix, name, x_infix, y_infix, z_infix) class SixAxisGonio(XYZStage): From 80422f00c72ef202ca7fd36d4b7b56505e0f0893 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Mon, 4 Aug 2025 12:36:26 +0000 Subject: [PATCH 19/27] make common mirror classes generic of strictenum --- .../beamline_specific_utils/i05_shared.py | 22 ++- src/dodal/beamlines/i05.py | 14 +- src/dodal/beamlines/i05_1.py | 14 +- src/dodal/devices/common_mirror.py | 16 +- src/dodal/devices/i05/enums.py | 8 - tests/devices/test_common_mirrors.py | 166 ++++++++++++++++++ tests/devices/test_motors.py | 55 +++++- 7 files changed, 278 insertions(+), 17 deletions(-) delete mode 100644 src/dodal/devices/i05/enums.py create mode 100644 tests/devices/test_common_mirrors.py diff --git a/src/dodal/beamline_specific_utils/i05_shared.py b/src/dodal/beamline_specific_utils/i05_shared.py index e06a1962bb7..b4408c1e36e 100644 --- a/src/dodal/beamline_specific_utils/i05_shared.py +++ b/src/dodal/beamline_specific_utils/i05_shared.py @@ -1,6 +1,7 @@ +from ophyd_async.core import StrictEnum + from dodal.common.beamlines.beamline_utils import device_factory from dodal.devices.common_mirror import XYZPiezoSwitchingMirror -from dodal.devices.i05.enums import Grating from dodal.devices.motors import XYZPitchYawRollStage from dodal.devices.pgm import PGM from dodal.utils import BeamlinePrefix @@ -8,6 +9,20 @@ PREFIX = BeamlinePrefix("i05", "I") +class M3MJ6Mirror(StrictEnum): + UNKNOWN = "Unknown" + MJ6 = "MJ6" + M3 = "M3" + REFERENCE = "Reference" + + +class Grating(StrictEnum): + PT_400 = "400 lines/mm" + C_1600 = "C 1600 lines/mm" + RH_1600 = "Rh 1600 lines/mm" + PT_800 = "B 800 lines/mm" + + @device_factory() def pgm() -> PGM: return PGM(prefix=f"{PREFIX.beamline_prefix}-OP-PGM-01:", grating=Grating) @@ -20,4 +35,7 @@ def m1_collimating_mirror() -> XYZPitchYawRollStage: @device_factory() def m3mj6_switching_mirror() -> XYZPiezoSwitchingMirror: - return XYZPiezoSwitchingMirror(prefix=f"{PREFIX.beamline_prefix}-OP-SWTCH-01:") + return XYZPiezoSwitchingMirror( + prefix=f"{PREFIX.beamline_prefix}-OP-SWTCH-01:", + mirrors=M3MJ6Mirror, + ) diff --git a/src/dodal/beamlines/i05.py b/src/dodal/beamlines/i05.py index e4d7a05fa7a..008350cd18c 100644 --- a/src/dodal/beamlines/i05.py +++ b/src/dodal/beamlines/i05.py @@ -1,3 +1,5 @@ +from ophyd_async.core import StrictEnum + from dodal.beamline_specific_utils.i05_shared import ( m1_collimating_mirror, m3mj6_switching_mirror, @@ -44,6 +46,16 @@ def m3mj6() -> XYZPiezoSwitchingMirror: # beamline specific devices +class M4M5Mirror(StrictEnum): + UNKNOWN = "Unknown" + MJ6 = "M4" + M3 = "M5" + REFERENCE = "Reference" + + @device_factory() def m4m5() -> XYZSwitchingMirror: - return XYZSwitchingMirror(prefix=f"{PREFIX.beamline_prefix}-OP-RFM-01:") + return XYZSwitchingMirror( + prefix=f"{PREFIX.beamline_prefix}-OP-RFM-01:", + mirrors=M4M5Mirror, + ) diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index 7e54705d0e4..f85067dc1b0 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -1,3 +1,5 @@ +from ophyd_async.core import StrictEnum + from dodal.beamline_specific_utils.i05_shared import ( m1_collimating_mirror, m3mj6_switching_mirror, @@ -44,6 +46,16 @@ def m3mj6() -> XYZPiezoSwitchingMirror: # beamline specific devices +class Mj7j8Mirror(StrictEnum): + UNKNOWN = "Unknown" + MJ6 = "MJ8" + M3 = "MJ7" + REFERENCE = "Reference" + + @device_factory() def mj7j8() -> XYZPiezoSwitchingMirror: - return XYZPiezoSwitchingMirror(prefix=f"{PREFIX.beamline_prefix}-OP-RFM-01:") + return XYZPiezoSwitchingMirror( + prefix=f"{PREFIX.beamline_prefix}-OP-RFM-01:", + mirrors=Mj7j8Mirror, + ) diff --git a/src/dodal/devices/common_mirror.py b/src/dodal/devices/common_mirror.py index 86e0c24f0f9..b44e870f6f2 100644 --- a/src/dodal/devices/common_mirror.py +++ b/src/dodal/devices/common_mirror.py @@ -1,3 +1,6 @@ +from typing import Generic, TypeVar + +from ophyd_async.core import StrictEnum from ophyd_async.epics.core import epics_signal_r, epics_signal_rw from dodal.devices.motors import _X, _Y, _Z, XYZPitchYawRollStage @@ -28,10 +31,14 @@ def __init__( ) -class XYZSwitchingMirror(XYZPitchYawRollStage): +TMirror = TypeVar("TMirror", bound=StrictEnum) + + +class XYZSwitchingMirror(XYZPitchYawRollStage, Generic[TMirror]): def __init__( self, prefix: str, + mirrors: type[TMirror], name: str = "", x_infix: str = _X, y_infix: str = _Y, @@ -42,16 +49,17 @@ def __init__( mirror_read_suffix: str = "MIRCTRL:RBV:MIRROR", ): with self.add_children_as_readables(): - self.mirror = epics_signal_r(str, read_pv=prefix + mirror_read_suffix) + self.mirror = epics_signal_r(mirrors, read_pv=prefix + mirror_read_suffix) super().__init__( prefix, name, x_infix, y_infix, z_infix, pitch_infix, yaw_infix, roll_infix ) -class XYZPiezoSwitchingMirror(XYZPiezoCollimatingMirror): +class XYZPiezoSwitchingMirror(XYZPiezoCollimatingMirror, Generic[TMirror]): def __init__( self, prefix: str, + mirrors: type[TMirror], name: str = "", x_infix: str = _X, y_infix: str = _Y, @@ -64,7 +72,7 @@ def __init__( mirror_read_suffix: str = "MIRCTRL:RBV:MIRROR", ): with self.add_children_as_readables(): - self.mirror = epics_signal_r(str, read_pv=prefix + mirror_read_suffix) + self.mirror = epics_signal_r(mirrors, read_pv=prefix + mirror_read_suffix) super().__init__( prefix, name, diff --git a/src/dodal/devices/i05/enums.py b/src/dodal/devices/i05/enums.py deleted file mode 100644 index e49cbed77b2..00000000000 --- a/src/dodal/devices/i05/enums.py +++ /dev/null @@ -1,8 +0,0 @@ -from ophyd_async.core import StrictEnum - - -class Grating(StrictEnum): - PT_400 = "400 lines/mm" - C_1600 = "C 1600 lines/mm" - RH_1600 = "Rh 1600 lines/mm" - PT_800 = "B 800 lines/mm" diff --git a/tests/devices/test_common_mirrors.py b/tests/devices/test_common_mirrors.py new file mode 100644 index 00000000000..6d387bda5d6 --- /dev/null +++ b/tests/devices/test_common_mirrors.py @@ -0,0 +1,166 @@ +import pytest +from ophyd_async.core import init_devices +from ophyd_async.testing import assert_reading, partial_reading, set_mock_value + +from dodal.beamline_specific_utils.i05_shared import M3MJ6Mirror +from dodal.devices.common_mirror import ( + XYZPiezoCollimatingMirror, + XYZPiezoSwitchingMirror, + XYZSwitchingMirror, +) + + +@pytest.fixture +async def xyz_piezo_coll_mirror() -> XYZPiezoCollimatingMirror: + async with init_devices(mock=True): + xyz_piezo_coll_mirror = XYZPiezoCollimatingMirror("") + return xyz_piezo_coll_mirror + + +@pytest.fixture +async def xyz_switching_mirror() -> XYZSwitchingMirror[M3MJ6Mirror]: + async with init_devices(mock=True): + xyz_switching_mirror = XYZSwitchingMirror("", M3MJ6Mirror) + return xyz_switching_mirror + + +@pytest.fixture +async def xyz_piezo_switching_mirror() -> XYZPiezoSwitchingMirror[M3MJ6Mirror]: + async with init_devices(mock=True): + xyz_piezo_switching_mirror = XYZPiezoSwitchingMirror("", M3MJ6Mirror) + return xyz_piezo_switching_mirror + + +@pytest.mark.parametrize( + "x, y, z, pitch, yaw, roll, fpitch", + [ + (0, 0, 0, 0, 0, 0, 0), + (1.23, 2.40, 0.0, 0.0, 0.0, 0.0, 0.0), + (1.23, 2.40, 3.51, 24.06, 12.02, 3.56, 13.23), + ], +) +async def test_setting_xyz_piezo_coll_mirror_positions( + xyz_piezo_coll_mirror: XYZPiezoCollimatingMirror, + x: float, + y: float, + z: float, + pitch: float, + yaw: float, + roll: float, + fpitch: float, +): + """ + Test setting positions on the Table using the ophyd_async mock tools. + """ + # Call set to update the position + set_mock_value(xyz_piezo_coll_mirror.x.user_readback, x) + set_mock_value(xyz_piezo_coll_mirror.y.user_readback, y) + set_mock_value(xyz_piezo_coll_mirror.z.user_readback, z) + set_mock_value(xyz_piezo_coll_mirror.pitch.user_readback, pitch) + set_mock_value(xyz_piezo_coll_mirror.yaw.user_readback, yaw) + set_mock_value(xyz_piezo_coll_mirror.roll.user_readback, roll) + set_mock_value(xyz_piezo_coll_mirror.fine_pitch, fpitch) + + await assert_reading( + xyz_piezo_coll_mirror, + { + "xyz_piezo_coll_mirror-x": partial_reading(x), + "xyz_piezo_coll_mirror-y": partial_reading(y), + "xyz_piezo_coll_mirror-z": partial_reading(z), + "xyz_piezo_coll_mirror-pitch": partial_reading(pitch), + "xyz_piezo_coll_mirror-yaw": partial_reading(yaw), + "xyz_piezo_coll_mirror-roll": partial_reading(roll), + "xyz_piezo_coll_mirror-fine_pitch": partial_reading(fpitch), + }, + ) + + +@pytest.mark.parametrize( + "x, y, z, pitch, yaw, roll, mirror", + [ + (0, 0, 0, 0, 0, 0, M3MJ6Mirror.UNKNOWN), + (1.23, 2.40, 0.0, 0.0, 0.0, 0.0, M3MJ6Mirror.M3), + (1.23, 2.40, 3.51, 24.06, 12.02, 3.56, M3MJ6Mirror.REFERENCE), + ], +) +async def test_setting_xyz_switching_mirror_position_table( + xyz_switching_mirror: XYZSwitchingMirror, + x: float, + y: float, + z: float, + pitch: float, + yaw: float, + roll: float, + mirror: M3MJ6Mirror, +): + """ + Test setting positions on the Table using the ophyd_async mock tools. + """ + # Call set to update the position + set_mock_value(xyz_switching_mirror.x.user_readback, x) + set_mock_value(xyz_switching_mirror.y.user_readback, y) + set_mock_value(xyz_switching_mirror.z.user_readback, z) + set_mock_value(xyz_switching_mirror.pitch.user_readback, pitch) + set_mock_value(xyz_switching_mirror.yaw.user_readback, yaw) + set_mock_value(xyz_switching_mirror.roll.user_readback, roll) + set_mock_value(xyz_switching_mirror.mirror, mirror) + + await assert_reading( + xyz_switching_mirror, + { + "xyz_switching_mirror-x": partial_reading(x), + "xyz_switching_mirror-y": partial_reading(y), + "xyz_switching_mirror-z": partial_reading(z), + "xyz_switching_mirror-pitch": partial_reading(pitch), + "xyz_switching_mirror-yaw": partial_reading(yaw), + "xyz_switching_mirror-roll": partial_reading(roll), + "xyz_switching_mirror-mirror": partial_reading(mirror), + }, + ) + + +@pytest.mark.parametrize( + "x, y, z, pitch, yaw, roll,fpitch, mirror", + [ + (0, 0, 0, 0, 0, 0, 0, M3MJ6Mirror.UNKNOWN), + (1.23, 2.40, 0.21, 0.0, 0.0, 0.0, 0.0, M3MJ6Mirror.M3), + (1.23, 2.40, 3.51, 24.06, 12.02, 3.56, 1.81, M3MJ6Mirror.REFERENCE), + ], +) +async def test_setting_xyz_piezo_switching_mirror_positions( + xyz_piezo_switching_mirror: XYZPiezoSwitchingMirror, + x: float, + y: float, + z: float, + pitch: float, + yaw: float, + roll: float, + fpitch: float, + mirror: M3MJ6Mirror, +): + """ + Test setting positions on the Table using the ophyd_async mock tools. + """ + # Call set to update the position + set_mock_value(xyz_piezo_switching_mirror.x.user_readback, x) + set_mock_value(xyz_piezo_switching_mirror.y.user_readback, y) + set_mock_value(xyz_piezo_switching_mirror.z.user_readback, z) + set_mock_value(xyz_piezo_switching_mirror.pitch.user_readback, pitch) + set_mock_value(xyz_piezo_switching_mirror.yaw.user_readback, yaw) + set_mock_value(xyz_piezo_switching_mirror.roll.user_readback, roll) + set_mock_value(xyz_piezo_switching_mirror.fine_pitch, fpitch) + set_mock_value(xyz_piezo_switching_mirror.mirror, mirror) + + await assert_reading( + xyz_piezo_switching_mirror, + { + "xyz_piezo_switching_mirror-x": partial_reading(x), + "xyz_piezo_switching_mirror-y": partial_reading(y), + "xyz_piezo_switching_mirror-z": partial_reading(z), + "xyz_piezo_switching_mirror-pitch": partial_reading(pitch), + "xyz_piezo_switching_mirror-yaw": partial_reading(yaw), + "xyz_piezo_switching_mirror-roll": partial_reading(roll), + "xyz_piezo_switching_mirror-fine_pitch": partial_reading(fpitch), + "xyz_piezo_switching_mirror-mirror": partial_reading(mirror), + }, + ) diff --git a/tests/devices/test_motors.py b/tests/devices/test_motors.py index 34f375daae3..b11e6a6c8b0 100644 --- a/tests/devices/test_motors.py +++ b/tests/devices/test_motors.py @@ -2,7 +2,12 @@ from ophyd_async.core import init_devices from ophyd_async.testing import assert_reading, partial_reading, set_mock_value -from dodal.devices.motors import XThetaStage, XYStage, XYZThetaStage +from dodal.devices.motors import ( + XThetaStage, + XYStage, + XYZPitchYawRollStage, + XYZThetaStage, +) @pytest.fixture @@ -26,6 +31,13 @@ async def xtheta_stage() -> XThetaStage: return xtheta_stage +@pytest.fixture +async def xyzpyr_stage() -> XYZPitchYawRollStage: + async with init_devices(mock=True): + xyzpyr_stage = XYZPitchYawRollStage("") + return xyzpyr_stage + + async def test_setting_xy_position_table(xyzt_stage: XYZThetaStage): """ Test setting x and y positions on the Table using the ophyd_async mock tools. @@ -87,6 +99,47 @@ async def test_setting_xyztheta_position_table(xyzt_stage: XYZThetaStage): ) +@pytest.mark.parametrize( + "x, y, z, pitch, yaw, roll", + [ + (0, 0, 0, 0, 0, 0), + (1.23, 2.40, 0.0, 0.0, 0.0, 0.0), + (1.23, 2.40, 3.51, 24.0, 12.0, 3.56), + ], +) +async def test_setting_xyzpyr_position_table( + xyzpyr_stage: XYZPitchYawRollStage, + x: float, + y: float, + z: float, + pitch: float, + yaw: float, + roll: float, +): + """ + Test setting positions on the Table using the ophyd_async mock tools. + """ + # Call set to update the position + set_mock_value(xyzpyr_stage.x.user_readback, x) + set_mock_value(xyzpyr_stage.y.user_readback, y) + set_mock_value(xyzpyr_stage.z.user_readback, z) + set_mock_value(xyzpyr_stage.pitch.user_readback, pitch) + set_mock_value(xyzpyr_stage.yaw.user_readback, yaw) + set_mock_value(xyzpyr_stage.roll.user_readback, roll) + + await assert_reading( + xyzpyr_stage, + { + "xyzpyr_stage-x": partial_reading(x), + "xyzpyr_stage-y": partial_reading(y), + "xyzpyr_stage-z": partial_reading(z), + "xyzpyr_stage-pitch": partial_reading(pitch), + "xyzpyr_stage-yaw": partial_reading(yaw), + "xyzpyr_stage-roll": partial_reading(roll), + }, + ) + + async def test_setting(xy_stage: XYStage): """ Test setting x and y positions on the XYStage using ophyd_async mock tools. From c6046b716421f1d05cde7de599444a17f597aaf5 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Tue, 5 Aug 2025 13:42:40 +0000 Subject: [PATCH 20/27] add mirrors controls and docstring --- src/dodal/devices/common_mirror.py | 64 +++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/src/dodal/devices/common_mirror.py b/src/dodal/devices/common_mirror.py index b44e870f6f2..2068204c9ab 100644 --- a/src/dodal/devices/common_mirror.py +++ b/src/dodal/devices/common_mirror.py @@ -1,15 +1,22 @@ from typing import Generic, TypeVar from ophyd_async.core import StrictEnum -from ophyd_async.epics.core import epics_signal_r, epics_signal_rw +from ophyd_async.epics.core import epics_signal_rw, epics_signal_x from dodal.devices.motors import _X, _Y, _Z, XYZPitchYawRollStage +TMirror = TypeVar("TMirror", bound=StrictEnum) + + +class XYZSwitchingMirror(XYZPitchYawRollStage, Generic[TMirror]): + """ + Set of mirrors on a hexapod with x,y,z and yaw, pitch, roll motors. Mirrors is an stritcEnum class type of readback values, to change mirror one need to select and trigger mirror change. + """ -class XYZPiezoCollimatingMirror(XYZPitchYawRollStage): def __init__( self, prefix: str, + mirrors: type[TMirror], name: str = "", x_infix: str = _X, y_infix: str = _Y, @@ -17,28 +24,35 @@ def __init__( pitch_infix: str = "PITCH", yaw_infix: str = "YAW", roll_infix: str = "ROLL", - fpitch_read_suffix: str = "FPITCH:RBV", - fpitch_write_suffix: str = "FPITCH:DMD", + mirror_read_suffix: str = "MIRCTRL:RBV:MIRROR", + mirror_write_suffix: str = "MIRCTRL:DMD:MIRROR", + mirror_change_suffix: str = "MIRCTRL:SEQ:CHNG:MIRROR.PROC", + mirror_abort_suffix: str = "MIRCTRL:DMD:ABORT.PROC", ): with self.add_children_as_readables(): - self.fine_pitch = epics_signal_rw( - float, - read_pv=prefix + fpitch_read_suffix, - write_pv=prefix + fpitch_write_suffix, + self.mirror = epics_signal_rw( + mirrors, + read_pv=prefix + mirror_read_suffix, + write_pv=prefix + mirror_write_suffix, ) + + self.mirror_change = epics_signal_x(write_pv=prefix + mirror_change_suffix) + self.mirror_abort = epics_signal_x(write_pv=prefix + mirror_abort_suffix) + super().__init__( prefix, name, x_infix, y_infix, z_infix, pitch_infix, yaw_infix, roll_infix ) -TMirror = TypeVar("TMirror", bound=StrictEnum) - +class XYZPiezoCollimatingMirror(XYZPitchYawRollStage): + """ + Collimating mirror on a hexapod with x,y,z and yaw, pitch, roll motors. + In addition there is a fine pitch piezo motor. + """ -class XYZSwitchingMirror(XYZPitchYawRollStage, Generic[TMirror]): def __init__( self, prefix: str, - mirrors: type[TMirror], name: str = "", x_infix: str = _X, y_infix: str = _Y, @@ -46,16 +60,25 @@ def __init__( pitch_infix: str = "PITCH", yaw_infix: str = "YAW", roll_infix: str = "ROLL", - mirror_read_suffix: str = "MIRCTRL:RBV:MIRROR", + fpitch_read_suffix: str = "FPITCH:RBV", + fpitch_write_suffix: str = "FPITCH:DMD", ): with self.add_children_as_readables(): - self.mirror = epics_signal_r(mirrors, read_pv=prefix + mirror_read_suffix) + self.fine_pitch = epics_signal_rw( + float, + read_pv=prefix + fpitch_read_suffix, + write_pv=prefix + fpitch_write_suffix, + ) super().__init__( prefix, name, x_infix, y_infix, z_infix, pitch_infix, yaw_infix, roll_infix ) class XYZPiezoSwitchingMirror(XYZPiezoCollimatingMirror, Generic[TMirror]): + """ + Set of mirrors on a hexapod with x,y,z and yaw, pitch, roll motors. Mirrors is an stritcEnum class type of readback values, to change mirror one need to select and trigger mirror change. In addition there is a fine pitch piezo motor. + """ + def __init__( self, prefix: str, @@ -70,9 +93,20 @@ def __init__( fpitch_read_suffix: str = "FPITCH:RBV", fpitch_write_suffix: str = "FPITCH:DMD", mirror_read_suffix: str = "MIRCTRL:RBV:MIRROR", + mirror_write_suffix: str = "MIRCTRL:DMD:MIRROR", + mirror_change_suffix: str = "MIRCTRL:SEQ:CHNG:MIRROR.PROC", + mirror_abort_suffix: str = "MIRCTRL:DMD:ABORT.PROC", ): with self.add_children_as_readables(): - self.mirror = epics_signal_r(mirrors, read_pv=prefix + mirror_read_suffix) + self.mirror = epics_signal_rw( + mirrors, + read_pv=prefix + mirror_read_suffix, + write_pv=prefix + mirror_write_suffix, + ) + + self.mirror_change = epics_signal_x(write_pv=prefix + mirror_change_suffix) + self.mirror_abort = epics_signal_x(write_pv=prefix + mirror_abort_suffix) + super().__init__( prefix, name, From 0710b4783aa30b829368c879f74dd708d8952a91 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Tue, 19 Aug 2025 15:49:17 +0000 Subject: [PATCH 21/27] make all new classes depend on Stage only --- src/dodal/devices/common_mirror.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/dodal/devices/common_mirror.py b/src/dodal/devices/common_mirror.py index 2068204c9ab..c68d01ed319 100644 --- a/src/dodal/devices/common_mirror.py +++ b/src/dodal/devices/common_mirror.py @@ -10,7 +10,8 @@ class XYZSwitchingMirror(XYZPitchYawRollStage, Generic[TMirror]): """ - Set of mirrors on a hexapod with x,y,z and yaw, pitch, roll motors. Mirrors is an stritcEnum class type of readback values, to change mirror one need to select and trigger mirror change. + Set of mirrors on a hexapod with x,y,z and yaw, pitch, roll motors. + To change mirror one need to set mirror enum and trigger mirror change. """ def __init__( @@ -74,9 +75,11 @@ def __init__( ) -class XYZPiezoSwitchingMirror(XYZPiezoCollimatingMirror, Generic[TMirror]): +class XYZPiezoSwitchingMirror(XYZPitchYawRollStage, Generic[TMirror]): """ - Set of mirrors on a hexapod with x,y,z and yaw, pitch, roll motors. Mirrors is an stritcEnum class type of readback values, to change mirror one need to select and trigger mirror change. In addition there is a fine pitch piezo motor. + Set of mirrors on a hexapod with x,y,z and yaw, pitch, roll motors. + To change mirror one need to set mirror enum and trigger mirror change. + In addition there is a fine pitch piezo motor. """ def __init__( @@ -98,6 +101,11 @@ def __init__( mirror_abort_suffix: str = "MIRCTRL:DMD:ABORT.PROC", ): with self.add_children_as_readables(): + self.fine_pitch = epics_signal_rw( + float, + read_pv=prefix + fpitch_read_suffix, + write_pv=prefix + fpitch_write_suffix, + ) self.mirror = epics_signal_rw( mirrors, read_pv=prefix + mirror_read_suffix, @@ -108,14 +116,5 @@ def __init__( self.mirror_abort = epics_signal_x(write_pv=prefix + mirror_abort_suffix) super().__init__( - prefix, - name, - x_infix, - y_infix, - z_infix, - pitch_infix, - yaw_infix, - roll_infix, - fpitch_read_suffix, - fpitch_write_suffix, + prefix, name, x_infix, y_infix, z_infix, pitch_infix, yaw_infix, roll_infix ) From 9fdfcfd1e55341eaf0d184fd3552d211ea297f24 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Wed, 10 Sep 2025 16:10:24 +0000 Subject: [PATCH 22/27] relocate common_mirror to I05 devices --- src/dodal/beamline_specific_utils/i05_shared.py | 2 +- src/dodal/beamlines/i05.py | 2 +- src/dodal/beamlines/i05_1.py | 2 +- src/dodal/devices/i05/__init__.py | 8 ++++++-- src/dodal/devices/{ => i05}/common_mirror.py | 0 tests/devices/test_common_mirrors.py | 2 +- 6 files changed, 10 insertions(+), 6 deletions(-) rename src/dodal/devices/{ => i05}/common_mirror.py (100%) diff --git a/src/dodal/beamline_specific_utils/i05_shared.py b/src/dodal/beamline_specific_utils/i05_shared.py index b4408c1e36e..618f261aa05 100644 --- a/src/dodal/beamline_specific_utils/i05_shared.py +++ b/src/dodal/beamline_specific_utils/i05_shared.py @@ -1,7 +1,7 @@ from ophyd_async.core import StrictEnum from dodal.common.beamlines.beamline_utils import device_factory -from dodal.devices.common_mirror import XYZPiezoSwitchingMirror +from dodal.devices.i05.common_mirror import XYZPiezoSwitchingMirror from dodal.devices.motors import XYZPitchYawRollStage from dodal.devices.pgm import PGM from dodal.utils import BeamlinePrefix diff --git a/src/dodal/beamlines/i05.py b/src/dodal/beamlines/i05.py index 008350cd18c..4a9b209e251 100644 --- a/src/dodal/beamlines/i05.py +++ b/src/dodal/beamlines/i05.py @@ -7,7 +7,7 @@ ) from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.devices.common_mirror import XYZPiezoSwitchingMirror, XYZSwitchingMirror +from dodal.devices.i05.common_mirror import XYZPiezoSwitchingMirror, XYZSwitchingMirror from dodal.devices.motors import XYZPitchYawRollStage from dodal.devices.pgm import PGM from dodal.devices.synchrotron import Synchrotron diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index f85067dc1b0..78837a3dbcb 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -7,7 +7,7 @@ ) from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.devices.common_mirror import XYZPiezoSwitchingMirror +from dodal.devices.i05.common_mirror import XYZPiezoSwitchingMirror from dodal.devices.motors import XYZPitchYawRollStage from dodal.devices.pgm import PGM from dodal.devices.synchrotron import Synchrotron diff --git a/src/dodal/devices/i05/__init__.py b/src/dodal/devices/i05/__init__.py index 608b87cc86a..3be8b3fb037 100644 --- a/src/dodal/devices/i05/__init__.py +++ b/src/dodal/devices/i05/__init__.py @@ -1,3 +1,7 @@ -from dodal.devices.i05.enums import Grating +from dodal.devices.i05.common_mirror import ( + XYZPiezoCollimatingMirror, + XYZPiezoSwitchingMirror, + XYZSwitchingMirror, +) -__all__ = ["Grating"] +__all__ = ["XYZSwitchingMirror", "XYZPiezoCollimatingMirror", "XYZPiezoSwitchingMirror"] diff --git a/src/dodal/devices/common_mirror.py b/src/dodal/devices/i05/common_mirror.py similarity index 100% rename from src/dodal/devices/common_mirror.py rename to src/dodal/devices/i05/common_mirror.py diff --git a/tests/devices/test_common_mirrors.py b/tests/devices/test_common_mirrors.py index 6d387bda5d6..cdb890cade0 100644 --- a/tests/devices/test_common_mirrors.py +++ b/tests/devices/test_common_mirrors.py @@ -3,7 +3,7 @@ from ophyd_async.testing import assert_reading, partial_reading, set_mock_value from dodal.beamline_specific_utils.i05_shared import M3MJ6Mirror -from dodal.devices.common_mirror import ( +from dodal.devices.i05.common_mirror import ( XYZPiezoCollimatingMirror, XYZPiezoSwitchingMirror, XYZSwitchingMirror, From a0cfb59d7ed168cbd4b3bd45395a9afc6248e168 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Thu, 11 Sep 2025 10:49:14 +0000 Subject: [PATCH 23/27] add comment on mirrors connection --- src/dodal/beamlines/i05.py | 2 ++ src/dodal/beamlines/i05_1.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/dodal/beamlines/i05.py b/src/dodal/beamlines/i05.py index 4a9b209e251..6132cda9edb 100644 --- a/src/dodal/beamlines/i05.py +++ b/src/dodal/beamlines/i05.py @@ -38,6 +38,7 @@ def m1() -> XYZPitchYawRollStage: return m1_collimating_mirror() +# will connect after https://jira.diamond.ac.uk/browse/I05-731 @device_factory() def m3mj6() -> XYZPiezoSwitchingMirror: return m3mj6_switching_mirror() @@ -46,6 +47,7 @@ def m3mj6() -> XYZPiezoSwitchingMirror: # beamline specific devices +# will connect after https://jira.diamond.ac.uk/browse/I05-731 class M4M5Mirror(StrictEnum): UNKNOWN = "Unknown" MJ6 = "M4" diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index 78837a3dbcb..9b1dd08e192 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -46,6 +46,7 @@ def m3mj6() -> XYZPiezoSwitchingMirror: # beamline specific devices +# will connect after https://jira.diamond.ac.uk/browse/I05-731 class Mj7j8Mirror(StrictEnum): UNKNOWN = "Unknown" MJ6 = "MJ8" @@ -53,6 +54,7 @@ class Mj7j8Mirror(StrictEnum): REFERENCE = "Reference" +# will connect after https://jira.diamond.ac.uk/browse/I05-731 @device_factory() def mj7j8() -> XYZPiezoSwitchingMirror: return XYZPiezoSwitchingMirror( From 74045b970fba62a5a069be9ebcb7e3e100f3ea90 Mon Sep 17 00:00:00 2001 From: eir17846 Date: Fri, 19 Sep 2025 11:34:23 +0000 Subject: [PATCH 24/27] make mirrors locatable and stoppable, fix typos --- src/dodal/beamlines/i05.py | 4 +- src/dodal/beamlines/i05_1.py | 4 +- src/dodal/devices/i05/common_mirror.py | 79 +++++++++++++------------- tests/devices/test_common_mirrors.py | 67 ++++++++++++++++++++++ 4 files changed, 112 insertions(+), 42 deletions(-) diff --git a/src/dodal/beamlines/i05.py b/src/dodal/beamlines/i05.py index 6132cda9edb..3f368ad8d12 100644 --- a/src/dodal/beamlines/i05.py +++ b/src/dodal/beamlines/i05.py @@ -50,8 +50,8 @@ def m3mj6() -> XYZPiezoSwitchingMirror: # will connect after https://jira.diamond.ac.uk/browse/I05-731 class M4M5Mirror(StrictEnum): UNKNOWN = "Unknown" - MJ6 = "M4" - M3 = "M5" + M4 = "M4" + M5 = "M5" REFERENCE = "Reference" diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index 9b1dd08e192..85f889128a7 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -49,8 +49,8 @@ def m3mj6() -> XYZPiezoSwitchingMirror: # will connect after https://jira.diamond.ac.uk/browse/I05-731 class Mj7j8Mirror(StrictEnum): UNKNOWN = "Unknown" - MJ6 = "MJ8" - M3 = "MJ7" + MJ8 = "MJ8" + MJ7 = "MJ7" REFERENCE = "Reference" diff --git a/src/dodal/devices/i05/common_mirror.py b/src/dodal/devices/i05/common_mirror.py index c68d01ed319..a1e5dba033f 100644 --- a/src/dodal/devices/i05/common_mirror.py +++ b/src/dodal/devices/i05/common_mirror.py @@ -1,17 +1,20 @@ from typing import Generic, TypeVar -from ophyd_async.core import StrictEnum +from bluesky.protocols import Locatable, Location, Stoppable +from ophyd_async.core import AsyncStatus, StrictEnum from ophyd_async.epics.core import epics_signal_rw, epics_signal_x -from dodal.devices.motors import _X, _Y, _Z, XYZPitchYawRollStage +from dodal.devices.motors import XYZPitchYawRollStage TMirror = TypeVar("TMirror", bound=StrictEnum) -class XYZSwitchingMirror(XYZPitchYawRollStage, Generic[TMirror]): +class XYZSwitchingMirror( + XYZPitchYawRollStage, Generic[TMirror], Locatable[TMirror], Stoppable +): """ - Set of mirrors on a hexapod with x,y,z and yaw, pitch, roll motors. - To change mirror one need to set mirror enum and trigger mirror change. + A device represention set of mirrors on a hexapod stage with x,y,z and yaw, pitch, roll motors. + To change mirror set mirror enum and trigger mirror change. """ def __init__( @@ -19,12 +22,6 @@ def __init__( prefix: str, mirrors: type[TMirror], name: str = "", - x_infix: str = _X, - y_infix: str = _Y, - z_infix: str = _Z, - pitch_infix: str = "PITCH", - yaw_infix: str = "YAW", - roll_infix: str = "ROLL", mirror_read_suffix: str = "MIRCTRL:RBV:MIRROR", mirror_write_suffix: str = "MIRCTRL:DMD:MIRROR", mirror_change_suffix: str = "MIRCTRL:SEQ:CHNG:MIRROR.PROC", @@ -40,27 +37,30 @@ def __init__( self.mirror_change = epics_signal_x(write_pv=prefix + mirror_change_suffix) self.mirror_abort = epics_signal_x(write_pv=prefix + mirror_abort_suffix) - super().__init__( - prefix, name, x_infix, y_infix, z_infix, pitch_infix, yaw_infix, roll_infix - ) + super().__init__(prefix=prefix, name=name) + + @AsyncStatus.wrap + async def set(self, new_position: TMirror): + await self.mirror.set(new_position) + await self.mirror_change.trigger() + + async def locate(self) -> Location[TMirror]: + location = await self.mirror.locate() + return location + + async def stop(self, success=True) -> None: + await self.mirror_abort.trigger() class XYZPiezoCollimatingMirror(XYZPitchYawRollStage): """ - Collimating mirror on a hexapod with x,y,z and yaw, pitch, roll motors. - In addition there is a fine pitch piezo motor. + Collimating mirror on a hexapod stage with x,y,z and yaw, pitch, roll motors, including a fine pitch piezo motor. """ def __init__( self, prefix: str, name: str = "", - x_infix: str = _X, - y_infix: str = _Y, - z_infix: str = _Z, - pitch_infix: str = "PITCH", - yaw_infix: str = "YAW", - roll_infix: str = "ROLL", fpitch_read_suffix: str = "FPITCH:RBV", fpitch_write_suffix: str = "FPITCH:DMD", ): @@ -70,16 +70,15 @@ def __init__( read_pv=prefix + fpitch_read_suffix, write_pv=prefix + fpitch_write_suffix, ) - super().__init__( - prefix, name, x_infix, y_infix, z_infix, pitch_infix, yaw_infix, roll_infix - ) + super().__init__(prefix=prefix, name=name) -class XYZPiezoSwitchingMirror(XYZPitchYawRollStage, Generic[TMirror]): +class XYZPiezoSwitchingMirror( + XYZPitchYawRollStage, Generic[TMirror], Locatable[TMirror], Stoppable +): """ - Set of mirrors on a hexapod with x,y,z and yaw, pitch, roll motors. - To change mirror one need to set mirror enum and trigger mirror change. - In addition there is a fine pitch piezo motor. + A device represention set of mirrors on a hexapod stage with x,y,z and yaw, pitch, roll motors, including a fine pitch piezo motor. + To change mirror set mirror enum and trigger mirror change. """ def __init__( @@ -87,12 +86,6 @@ def __init__( prefix: str, mirrors: type[TMirror], name: str = "", - x_infix: str = _X, - y_infix: str = _Y, - z_infix: str = _Z, - pitch_infix: str = "PITCH", - yaw_infix: str = "YAW", - roll_infix: str = "ROLL", fpitch_read_suffix: str = "FPITCH:RBV", fpitch_write_suffix: str = "FPITCH:DMD", mirror_read_suffix: str = "MIRCTRL:RBV:MIRROR", @@ -115,6 +108,16 @@ def __init__( self.mirror_change = epics_signal_x(write_pv=prefix + mirror_change_suffix) self.mirror_abort = epics_signal_x(write_pv=prefix + mirror_abort_suffix) - super().__init__( - prefix, name, x_infix, y_infix, z_infix, pitch_infix, yaw_infix, roll_infix - ) + super().__init__(prefix=prefix, name=name) + + @AsyncStatus.wrap + async def set(self, new_position: TMirror): + await self.mirror.set(new_position) + await self.mirror_change.trigger() + + async def locate(self) -> Location[TMirror]: + location = await self.mirror.locate() + return location + + async def stop(self, success=True) -> None: + await self.mirror_abort.trigger() diff --git a/tests/devices/test_common_mirrors.py b/tests/devices/test_common_mirrors.py index cdb890cade0..9ec8666ae83 100644 --- a/tests/devices/test_common_mirrors.py +++ b/tests/devices/test_common_mirrors.py @@ -1,4 +1,9 @@ +from unittest.mock import AsyncMock + import pytest +from bluesky.plan_stubs import mv, stop +from bluesky.protocols import Location +from bluesky.run_engine import RunEngine from ophyd_async.core import init_devices from ophyd_async.testing import assert_reading, partial_reading, set_mock_value @@ -119,6 +124,37 @@ async def test_setting_xyz_switching_mirror_position_table( ) +async def test_move_xyz_switching_mirror( + xyz_switching_mirror: XYZSwitchingMirror, + RE: RunEngine, +): + assert await xyz_switching_mirror.mirror.get_value() == M3MJ6Mirror.UNKNOWN + RE(mv(xyz_switching_mirror, M3MJ6Mirror.M3)) + assert await xyz_switching_mirror.mirror.get_value() == M3MJ6Mirror.M3 + + +async def test_locate_xyz_switching_mirror( + xyz_switching_mirror: XYZSwitchingMirror, + RE: RunEngine, +): + assert await xyz_switching_mirror.locate() == Location( + setpoint=M3MJ6Mirror.UNKNOWN, readback=M3MJ6Mirror.UNKNOWN + ) + RE(mv(xyz_switching_mirror, M3MJ6Mirror.M3)) + assert await xyz_switching_mirror.locate() == Location( + setpoint=M3MJ6Mirror.M3, readback=M3MJ6Mirror.M3 + ) + + +async def test_stop_xyz_switching_mirror( + xyz_switching_mirror: XYZSwitchingMirror, + RE: RunEngine, +): + xyz_switching_mirror.mirror_abort.trigger = AsyncMock() + RE(stop(xyz_switching_mirror)) + xyz_switching_mirror.mirror_abort.trigger.assert_awaited_once() + + @pytest.mark.parametrize( "x, y, z, pitch, yaw, roll,fpitch, mirror", [ @@ -164,3 +200,34 @@ async def test_setting_xyz_piezo_switching_mirror_positions( "xyz_piezo_switching_mirror-mirror": partial_reading(mirror), }, ) + + +async def test_move_xyz_piezo_switching_mirror( + xyz_piezo_switching_mirror: XYZPiezoSwitchingMirror, + RE: RunEngine, +): + assert await xyz_piezo_switching_mirror.mirror.get_value() == M3MJ6Mirror.UNKNOWN + RE(mv(xyz_piezo_switching_mirror, M3MJ6Mirror.M3)) + assert await xyz_piezo_switching_mirror.mirror.get_value() == M3MJ6Mirror.M3 + + +async def test_locate_xyz_piezo_switching_mirror( + xyz_piezo_switching_mirror: XYZPiezoSwitchingMirror, + RE: RunEngine, +): + assert await xyz_piezo_switching_mirror.locate() == Location( + setpoint=M3MJ6Mirror.UNKNOWN, readback=M3MJ6Mirror.UNKNOWN + ) + RE(mv(xyz_piezo_switching_mirror, M3MJ6Mirror.M3)) + assert await xyz_piezo_switching_mirror.locate() == Location( + setpoint=M3MJ6Mirror.M3, readback=M3MJ6Mirror.M3 + ) + + +async def test_stop_xyz_piezo_switching_mirror( + xyz_piezo_switching_mirror: XYZPiezoSwitchingMirror, + RE: RunEngine, +): + xyz_piezo_switching_mirror.mirror_abort.trigger = AsyncMock() + RE(stop(xyz_piezo_switching_mirror)) + xyz_piezo_switching_mirror.mirror_abort.trigger.assert_awaited_once() From 8d00f21b2031d18698afd23287bea31b53f6553f Mon Sep 17 00:00:00 2001 From: eir17846 Date: Mon, 1 Dec 2025 14:55:16 +0000 Subject: [PATCH 25/27] fix merge --- src/dodal/beamline_specific_utils/i05_shared.py | 3 ++- src/dodal/beamlines/i05_1.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/dodal/beamline_specific_utils/i05_shared.py b/src/dodal/beamline_specific_utils/i05_shared.py index 1e890efb910..9da1daf9768 100644 --- a/src/dodal/beamline_specific_utils/i05_shared.py +++ b/src/dodal/beamline_specific_utils/i05_shared.py @@ -1,9 +1,9 @@ +from dodal.devices.i05.enums import Grating from ophyd_async.core import StrictEnum from dodal.common.beamlines.beamline_utils import device_factory from dodal.devices.i05.common_mirror import XYZPiezoSwitchingMirror from dodal.devices.motors import XYZPitchYawRollStage -from dodal.devices.i05.enums import Grating from dodal.devices.pgm import PlaneGratingMonochromator from dodal.utils import BeamlinePrefix @@ -34,6 +34,7 @@ def m3mj6_switching_mirror() -> XYZPiezoSwitchingMirror: return XYZPiezoSwitchingMirror( prefix=f"{PREFIX.beamline_prefix}-OP-SWTCH-01:", mirrors=M3MJ6Mirror, + ) @device_factory() diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index 5bd59b7f7c3..a25cd402fc6 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -3,14 +3,15 @@ from dodal.beamline_specific_utils.i05_shared import ( m1_collimating_mirror, m3mj6_switching_mirror, - pgm, +) +from dodal.beamline_specific_utils.i05_shared import ( + pgm as i05_pgm, ) from dodal.common.beamlines.beamline_utils import device_factory from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline from dodal.devices.i05.common_mirror import XYZPiezoSwitchingMirror from dodal.devices.motors import XYZPitchYawRollStage from dodal.devices.pgm import PlaneGratingMonochromator -from dodal.devices.synchrotron import Synchrotron from dodal.log import set_beamline as set_log_beamline from dodal.utils import BeamlinePrefix, get_beamline_name From b7529672dfd9209ccf591a38a2dab8293c364f9d Mon Sep 17 00:00:00 2001 From: eir17846 Date: Mon, 1 Dec 2025 15:11:26 +0000 Subject: [PATCH 26/27] fix merge --- src/dodal/beamline_specific_utils/i05_shared.py | 4 ++-- src/dodal/beamlines/i05.py | 13 +++++-------- src/dodal/beamlines/i05_1.py | 6 +++--- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/dodal/beamline_specific_utils/i05_shared.py b/src/dodal/beamline_specific_utils/i05_shared.py index 9da1daf9768..a0f598fcf77 100644 --- a/src/dodal/beamline_specific_utils/i05_shared.py +++ b/src/dodal/beamline_specific_utils/i05_shared.py @@ -1,4 +1,3 @@ -from dodal.devices.i05.enums import Grating from ophyd_async.core import StrictEnum from dodal.common.beamlines.beamline_utils import device_factory @@ -29,7 +28,8 @@ def m1_collimating_mirror() -> XYZPitchYawRollStage: return XYZPitchYawRollStage(prefix=f"{PREFIX.beamline_prefix}-OP-COL-01:") -@device_factory() +# will connect after https://jira.diamond.ac.uk/browse/I05-731 +@device_factory(skip=True) def m3mj6_switching_mirror() -> XYZPiezoSwitchingMirror: return XYZPiezoSwitchingMirror( prefix=f"{PREFIX.beamline_prefix}-OP-SWTCH-01:", diff --git a/src/dodal/beamlines/i05.py b/src/dodal/beamlines/i05.py index d1bb1967251..9d9cd827332 100644 --- a/src/dodal/beamlines/i05.py +++ b/src/dodal/beamlines/i05.py @@ -4,10 +4,6 @@ m1_collimating_mirror, m3mj6_switching_mirror, ) -from dodal.common.beamlines.beamline_utils import device_factory -from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline -from dodal.devices.i05.common_mirror import XYZPiezoSwitchingMirror, XYZSwitchingMirror -from dodal.devices.motors import XYZPitchYawRollStage from dodal.beamline_specific_utils.i05_shared import pgm as i05_pgm from dodal.common.beamlines.beamline_utils import ( device_factory, @@ -18,8 +14,9 @@ UndulatorGap, UndulatorLockedPhaseAxes, ) +from dodal.devices.i05.common_mirror import XYZPiezoSwitchingMirror, XYZSwitchingMirror +from dodal.devices.motors import XYZPitchYawRollStage from dodal.devices.pgm import PlaneGratingMonochromator - from dodal.devices.synchrotron import Synchrotron from dodal.log import set_beamline as set_log_beamline from dodal.utils import BeamlinePrefix, get_beamline_name @@ -44,7 +41,7 @@ def m1() -> XYZPitchYawRollStage: # will connect after https://jira.diamond.ac.uk/browse/I05-731 -@device_factory() +@device_factory(skip=True) def m3mj6() -> XYZPiezoSwitchingMirror: return m3mj6_switching_mirror() @@ -52,7 +49,6 @@ def m3mj6() -> XYZPiezoSwitchingMirror: # beamline specific devices -# will connect after https://jira.diamond.ac.uk/browse/I05-731 class M4M5Mirror(StrictEnum): UNKNOWN = "Unknown" M4 = "M4" @@ -60,7 +56,8 @@ class M4M5Mirror(StrictEnum): REFERENCE = "Reference" -@device_factory() +# will connect after https://jira.diamond.ac.uk/browse/I05-731 +@device_factory(skip=True) def m4m5() -> XYZSwitchingMirror: return XYZSwitchingMirror( prefix=f"{PREFIX.beamline_prefix}-OP-RFM-01:", diff --git a/src/dodal/beamlines/i05_1.py b/src/dodal/beamlines/i05_1.py index a25cd402fc6..911082e88bd 100644 --- a/src/dodal/beamlines/i05_1.py +++ b/src/dodal/beamlines/i05_1.py @@ -31,7 +31,8 @@ def m1() -> XYZPitchYawRollStage: return m1_collimating_mirror() -@device_factory() +# will connect after https://jira.diamond.ac.uk/browse/I05-731 +@device_factory(skip=True) def m3mj6() -> XYZPiezoSwitchingMirror: return m3mj6_switching_mirror() @@ -39,7 +40,6 @@ def m3mj6() -> XYZPiezoSwitchingMirror: # beamline specific devices -# will connect after https://jira.diamond.ac.uk/browse/I05-731 class Mj7j8Mirror(StrictEnum): UNKNOWN = "Unknown" MJ8 = "MJ8" @@ -48,7 +48,7 @@ class Mj7j8Mirror(StrictEnum): # will connect after https://jira.diamond.ac.uk/browse/I05-731 -@device_factory() +@device_factory(skip=True) def mj7j8() -> XYZPiezoSwitchingMirror: return XYZPiezoSwitchingMirror( prefix=f"{PREFIX.beamline_prefix}-OP-RFM-01:", From 5388298ccf63b42042d83ad0cd07f9e817bbd3bf Mon Sep 17 00:00:00 2001 From: eir17846 Date: Mon, 1 Dec 2025 16:19:04 +0000 Subject: [PATCH 27/27] fix test --- tests/devices/test_common_mirrors.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/devices/test_common_mirrors.py b/tests/devices/test_common_mirrors.py index 9ec8666ae83..b64584f453e 100644 --- a/tests/devices/test_common_mirrors.py +++ b/tests/devices/test_common_mirrors.py @@ -4,8 +4,8 @@ from bluesky.plan_stubs import mv, stop from bluesky.protocols import Location from bluesky.run_engine import RunEngine -from ophyd_async.core import init_devices -from ophyd_async.testing import assert_reading, partial_reading, set_mock_value +from ophyd_async.core import init_devices, set_mock_value +from ophyd_async.testing import assert_reading, partial_reading from dodal.beamline_specific_utils.i05_shared import M3MJ6Mirror from dodal.devices.i05.common_mirror import ( @@ -126,21 +126,21 @@ async def test_setting_xyz_switching_mirror_position_table( async def test_move_xyz_switching_mirror( xyz_switching_mirror: XYZSwitchingMirror, - RE: RunEngine, + run_engine: RunEngine, ): assert await xyz_switching_mirror.mirror.get_value() == M3MJ6Mirror.UNKNOWN - RE(mv(xyz_switching_mirror, M3MJ6Mirror.M3)) + run_engine(mv(xyz_switching_mirror, M3MJ6Mirror.M3)) assert await xyz_switching_mirror.mirror.get_value() == M3MJ6Mirror.M3 async def test_locate_xyz_switching_mirror( xyz_switching_mirror: XYZSwitchingMirror, - RE: RunEngine, + run_engine: RunEngine, ): assert await xyz_switching_mirror.locate() == Location( setpoint=M3MJ6Mirror.UNKNOWN, readback=M3MJ6Mirror.UNKNOWN ) - RE(mv(xyz_switching_mirror, M3MJ6Mirror.M3)) + run_engine(mv(xyz_switching_mirror, M3MJ6Mirror.M3)) assert await xyz_switching_mirror.locate() == Location( setpoint=M3MJ6Mirror.M3, readback=M3MJ6Mirror.M3 ) @@ -148,10 +148,10 @@ async def test_locate_xyz_switching_mirror( async def test_stop_xyz_switching_mirror( xyz_switching_mirror: XYZSwitchingMirror, - RE: RunEngine, + run_engine: RunEngine, ): xyz_switching_mirror.mirror_abort.trigger = AsyncMock() - RE(stop(xyz_switching_mirror)) + run_engine(stop(xyz_switching_mirror)) xyz_switching_mirror.mirror_abort.trigger.assert_awaited_once() @@ -204,21 +204,21 @@ async def test_setting_xyz_piezo_switching_mirror_positions( async def test_move_xyz_piezo_switching_mirror( xyz_piezo_switching_mirror: XYZPiezoSwitchingMirror, - RE: RunEngine, + run_engine: RunEngine, ): assert await xyz_piezo_switching_mirror.mirror.get_value() == M3MJ6Mirror.UNKNOWN - RE(mv(xyz_piezo_switching_mirror, M3MJ6Mirror.M3)) + run_engine(mv(xyz_piezo_switching_mirror, M3MJ6Mirror.M3)) assert await xyz_piezo_switching_mirror.mirror.get_value() == M3MJ6Mirror.M3 async def test_locate_xyz_piezo_switching_mirror( xyz_piezo_switching_mirror: XYZPiezoSwitchingMirror, - RE: RunEngine, + run_engine: RunEngine, ): assert await xyz_piezo_switching_mirror.locate() == Location( setpoint=M3MJ6Mirror.UNKNOWN, readback=M3MJ6Mirror.UNKNOWN ) - RE(mv(xyz_piezo_switching_mirror, M3MJ6Mirror.M3)) + run_engine(mv(xyz_piezo_switching_mirror, M3MJ6Mirror.M3)) assert await xyz_piezo_switching_mirror.locate() == Location( setpoint=M3MJ6Mirror.M3, readback=M3MJ6Mirror.M3 ) @@ -226,8 +226,8 @@ async def test_locate_xyz_piezo_switching_mirror( async def test_stop_xyz_piezo_switching_mirror( xyz_piezo_switching_mirror: XYZPiezoSwitchingMirror, - RE: RunEngine, + run_engine: RunEngine, ): xyz_piezo_switching_mirror.mirror_abort.trigger = AsyncMock() - RE(stop(xyz_piezo_switching_mirror)) + run_engine(stop(xyz_piezo_switching_mirror)) xyz_piezo_switching_mirror.mirror_abort.trigger.assert_awaited_once()