dls-dodal 1.29.4__py3-none-any.whl → 1.30.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dls_dodal-1.29.4.dist-info → dls_dodal-1.30.0.dist-info}/METADATA +27 -42
- dls_dodal-1.30.0.dist-info/RECORD +132 -0
- {dls_dodal-1.29.4.dist-info → dls_dodal-1.30.0.dist-info}/WHEEL +1 -1
- dls_dodal-1.30.0.dist-info/entry_points.txt +3 -0
- dodal/__init__.py +1 -4
- dodal/_version.py +2 -2
- dodal/beamlines/__init__.py +3 -1
- dodal/beamlines/i03.py +28 -23
- dodal/beamlines/i04.py +34 -12
- dodal/beamlines/i13_1.py +66 -0
- dodal/beamlines/i22.py +5 -5
- dodal/beamlines/i24.py +1 -1
- dodal/beamlines/p38.py +7 -7
- dodal/beamlines/p45.py +7 -5
- dodal/beamlines/p99.py +61 -0
- dodal/cli.py +6 -3
- dodal/common/beamlines/beamline_parameters.py +2 -2
- dodal/common/beamlines/beamline_utils.py +6 -5
- dodal/common/maths.py +1 -3
- dodal/common/types.py +2 -3
- dodal/common/udc_directory_provider.py +14 -3
- dodal/common/visit.py +2 -3
- dodal/devices/CTAB.py +22 -17
- dodal/devices/aperturescatterguard.py +114 -136
- dodal/devices/areadetector/adaravis.py +8 -6
- dodal/devices/areadetector/adsim.py +2 -3
- dodal/devices/areadetector/adutils.py +20 -12
- dodal/devices/cryostream.py +19 -7
- dodal/devices/detector/__init__.py +13 -2
- dodal/devices/detector/det_dim_constants.py +2 -2
- dodal/devices/detector/det_dist_to_beam_converter.py +1 -1
- dodal/devices/detector/detector.py +5 -5
- dodal/devices/detector/detector_motion.py +38 -31
- dodal/devices/eiger.py +11 -15
- dodal/devices/eiger_odin.py +9 -10
- dodal/devices/fast_grid_scan.py +4 -3
- dodal/devices/fluorescence_detector_motion.py +13 -4
- dodal/devices/focusing_mirror.py +4 -4
- dodal/devices/hutch_shutter.py +4 -4
- dodal/devices/i22/dcm.py +4 -3
- dodal/devices/i22/fswitch.py +4 -4
- dodal/devices/i22/nxsas.py +23 -32
- dodal/devices/i24/pmac.py +47 -8
- dodal/devices/ipin.py +7 -4
- dodal/devices/linkam3.py +11 -5
- dodal/devices/logging_ophyd_device.py +1 -1
- dodal/devices/motors.py +31 -5
- dodal/devices/oav/grid_overlay.py +1 -0
- dodal/devices/oav/microns_for_zoom_levels.json +1 -1
- dodal/devices/oav/oav_detector.py +2 -1
- dodal/devices/oav/oav_parameters.py +18 -10
- dodal/devices/oav/oav_to_redis_forwarder.py +100 -0
- dodal/devices/oav/pin_image_recognition/__init__.py +6 -6
- dodal/devices/oav/pin_image_recognition/utils.py +5 -6
- dodal/devices/oav/utils.py +2 -2
- dodal/devices/p99/__init__.py +0 -0
- dodal/devices/p99/sample_stage.py +43 -0
- dodal/devices/robot.py +30 -18
- dodal/devices/scintillator.py +8 -5
- dodal/devices/smargon.py +3 -3
- dodal/devices/status.py +2 -31
- dodal/devices/tetramm.py +4 -4
- dodal/devices/thawer.py +5 -3
- dodal/devices/undulator_dcm.py +6 -8
- dodal/devices/util/adjuster_plans.py +2 -2
- dodal/devices/util/epics_util.py +5 -7
- dodal/devices/util/lookup_tables.py +2 -3
- dodal/devices/util/save_panda.py +87 -0
- dodal/devices/util/test_utils.py +17 -0
- dodal/devices/webcam.py +3 -3
- dodal/devices/xbpm_feedback.py +0 -23
- dodal/devices/zebra.py +10 -10
- dodal/devices/zebra_controlled_shutter.py +3 -3
- dodal/devices/zocalo/zocalo_interaction.py +10 -2
- dodal/devices/zocalo/zocalo_results.py +31 -18
- dodal/log.py +14 -5
- dodal/plans/data_session_metadata.py +1 -0
- dodal/plans/motor_util_plans.py +117 -0
- dodal/utils.py +65 -22
- dls_dodal-1.29.4.dist-info/RECORD +0 -125
- dls_dodal-1.29.4.dist-info/entry_points.txt +0 -2
- dodal/devices/qbpm1.py +0 -8
- {dls_dodal-1.29.4.dist-info → dls_dodal-1.30.0.dist-info}/LICENSE +0 -0
- {dls_dodal-1.29.4.dist-info → dls_dodal-1.30.0.dist-info}/top_level.txt +0 -0
- /dodal/devices/i24/{I24_detector_motion.py → i24_detector_motion.py} +0 -0
|
@@ -1,37 +1,44 @@
|
|
|
1
|
-
from enum import
|
|
1
|
+
from enum import Enum
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
from
|
|
3
|
+
from ophyd_async.core import Device
|
|
4
|
+
from ophyd_async.epics.motion import Motor
|
|
5
|
+
from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
|
|
5
6
|
|
|
6
7
|
|
|
7
|
-
class ShutterState(
|
|
8
|
-
CLOSED =
|
|
9
|
-
OPEN =
|
|
8
|
+
class ShutterState(str, Enum):
|
|
9
|
+
CLOSED = "Closed"
|
|
10
|
+
OPEN = "Open"
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class DetectorMotion(Device):
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
14
|
+
def __init__(self, prefix: str, name: str = ""):
|
|
15
|
+
device_prefix = "-MO-DET-01:"
|
|
16
|
+
pmac_prefix = "-MO-PMAC-02:"
|
|
17
|
+
|
|
18
|
+
self.upstream_x = Motor(f"{prefix}{device_prefix}UPSTREAMX")
|
|
19
|
+
self.downstream_x = Motor(f"{prefix}{device_prefix}DOWNSTREAMX")
|
|
20
|
+
self.x = Motor(f"{prefix}{device_prefix}X")
|
|
21
|
+
self.y = Motor(f"{prefix}{device_prefix}Y")
|
|
22
|
+
self.z = Motor(f"{prefix}{device_prefix}Z")
|
|
23
|
+
self.yaw = Motor(f"{prefix}{device_prefix}YAW")
|
|
24
|
+
|
|
25
|
+
self.shutter = epics_signal_rw(
|
|
26
|
+
ShutterState, f"{prefix}{device_prefix}SET_SHUTTER_STATE"
|
|
27
|
+
)
|
|
28
|
+
self.shutter_closed_lim = epics_signal_r(
|
|
29
|
+
float, f"{prefix}{device_prefix}CLOSE_LIMIT"
|
|
30
|
+
) # on limit = 1, off = 0
|
|
31
|
+
self.shutter_open_lim = epics_signal_r(
|
|
32
|
+
float, f"{prefix}{device_prefix}OPEN_LIMIT"
|
|
33
|
+
) # on limit = 1, off = 0
|
|
34
|
+
self.z_disabled = epics_signal_r(
|
|
35
|
+
float, f"{prefix}{device_prefix}Z:DISABLED"
|
|
36
|
+
) # robot interlock, 0=ok to move, 1=blocked
|
|
37
|
+
self.crate_power = epics_signal_r(
|
|
38
|
+
float, f"{prefix}{pmac_prefix}CRATE2_HEALTHY"
|
|
39
|
+
) # returns 0 if no power
|
|
40
|
+
self.in_robot_load_safe_position = epics_signal_r(
|
|
41
|
+
int, f"{prefix}{pmac_prefix}GPIO_INP_BITS.B2"
|
|
42
|
+
) # returns 1 if safe
|
|
43
|
+
|
|
44
|
+
super().__init__(name)
|
dodal/devices/eiger.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# type: ignore # Eiger will soon be ophyd-async https://github.com/DiamondLightSource/dodal/issues/700
|
|
1
2
|
from enum import Enum
|
|
2
3
|
|
|
3
4
|
from ophyd import Component, Device, EpicsSignalRO, Signal
|
|
@@ -12,10 +13,6 @@ from dodal.log import LOGGER
|
|
|
12
13
|
|
|
13
14
|
FREE_RUN_MAX_IMAGES = 1000000
|
|
14
15
|
|
|
15
|
-
# TODO present for testing purposes, remove
|
|
16
|
-
TEST_1169_FIX = True
|
|
17
|
-
TEST_1169_INJECT = False
|
|
18
|
-
|
|
19
16
|
|
|
20
17
|
class InternalEigerTriggerMode(Enum):
|
|
21
18
|
INTERNAL_SERIES = 0
|
|
@@ -56,17 +53,15 @@ class EigerDetector(Device):
|
|
|
56
53
|
cls,
|
|
57
54
|
params: DetectorParams,
|
|
58
55
|
name: str = "EigerDetector",
|
|
59
|
-
*args,
|
|
60
|
-
**kwargs,
|
|
61
56
|
):
|
|
62
|
-
det = cls(name=name
|
|
57
|
+
det = cls(name=name)
|
|
63
58
|
det.set_detector_parameters(params)
|
|
64
59
|
return det
|
|
65
60
|
|
|
66
61
|
def set_detector_parameters(self, detector_params: DetectorParams):
|
|
67
62
|
self.detector_params = detector_params
|
|
68
63
|
if self.detector_params is None:
|
|
69
|
-
raise
|
|
64
|
+
raise ValueError("Parameters for scan must be specified")
|
|
70
65
|
|
|
71
66
|
to_check = [
|
|
72
67
|
(
|
|
@@ -103,7 +98,7 @@ class EigerDetector(Device):
|
|
|
103
98
|
|
|
104
99
|
def stage(self):
|
|
105
100
|
self.wait_on_arming_if_started()
|
|
106
|
-
if
|
|
101
|
+
if not self.is_armed():
|
|
107
102
|
LOGGER.info("Eiger not armed, arming")
|
|
108
103
|
|
|
109
104
|
self.async_stage().wait(timeout=self.ARMING_TIMEOUT)
|
|
@@ -318,7 +313,9 @@ class EigerDetector(Device):
|
|
|
318
313
|
|
|
319
314
|
def _finish_arm(self) -> Status:
|
|
320
315
|
LOGGER.info("Eiger staging: Finishing arming")
|
|
321
|
-
|
|
316
|
+
status = Status()
|
|
317
|
+
status.set_finished()
|
|
318
|
+
return status
|
|
322
319
|
|
|
323
320
|
def forward_bit_depth_to_filewriter(self):
|
|
324
321
|
bit_depth = self.bit_depth.get()
|
|
@@ -341,6 +338,10 @@ class EigerDetector(Device):
|
|
|
341
338
|
functions_to_do_arm.append(self.enable_roi_mode)
|
|
342
339
|
|
|
343
340
|
arming_sequence_funcs = [
|
|
341
|
+
# If a beam dump occurs after arming the eiger but prior to eiger staging,
|
|
342
|
+
# the odin may timeout which will cause the arming sequence to be retried;
|
|
343
|
+
# if this previously completed successfully we must reset the odin first
|
|
344
|
+
self.odin.stop,
|
|
344
345
|
lambda: self.change_dev_shm(detector_params.enable_dev_shm),
|
|
345
346
|
lambda: self.set_detector_threshold(detector_params.expected_energy_ev),
|
|
346
347
|
self.set_cam_pvs,
|
|
@@ -354,11 +355,6 @@ class EigerDetector(Device):
|
|
|
354
355
|
self._wait_fan_ready,
|
|
355
356
|
self._finish_arm,
|
|
356
357
|
]
|
|
357
|
-
if TEST_1169_FIX:
|
|
358
|
-
# If a beam dump occurs after arming the eiger but prior to eiger staging,
|
|
359
|
-
# the odin may timeout which will cause the arming sequence to be retried;
|
|
360
|
-
# if this previously completed successfully we must reset the odin first
|
|
361
|
-
arming_sequence_funcs.insert(0, self.odin.stop)
|
|
362
358
|
|
|
363
359
|
functions_to_do_arm.extend(arming_sequence_funcs)
|
|
364
360
|
|
dodal/devices/eiger_odin.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# type: ignore # Eiger will soon be ophyd-async https://github.com/DiamondLightSource/dodal/issues/700
|
|
3
2
|
from ophyd import Component, Device, EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV
|
|
4
3
|
from ophyd.areadetector.plugins import HDF5Plugin_V22
|
|
5
4
|
from ophyd.sim import NullStatus
|
|
@@ -59,12 +58,12 @@ class OdinNodesStatus(Device):
|
|
|
59
58
|
node_3 = Component(OdinNode, "OD4:")
|
|
60
59
|
|
|
61
60
|
@property
|
|
62
|
-
def nodes(self) ->
|
|
61
|
+
def nodes(self) -> list[OdinNode]:
|
|
63
62
|
return [self.node_0, self.node_1, self.node_2, self.node_3]
|
|
64
63
|
|
|
65
64
|
def check_node_frames_from_attr(
|
|
66
65
|
self, node_get_func, error_message_verb: str
|
|
67
|
-
) ->
|
|
66
|
+
) -> tuple[bool, str]:
|
|
68
67
|
nodes_frames_values = [0] * len(self.nodes)
|
|
69
68
|
frames_details = []
|
|
70
69
|
for node_number, node_pv in enumerate(self.nodes):
|
|
@@ -75,17 +74,17 @@ class OdinNodesStatus(Device):
|
|
|
75
74
|
bad_frames = any(v != 0 for v in nodes_frames_values)
|
|
76
75
|
return bad_frames, "\n".join(frames_details)
|
|
77
76
|
|
|
78
|
-
def check_frames_timed_out(self) ->
|
|
77
|
+
def check_frames_timed_out(self) -> tuple[bool, str]:
|
|
79
78
|
return self.check_node_frames_from_attr(
|
|
80
79
|
lambda node: node.frames_timed_out.get(), "timed out"
|
|
81
80
|
)
|
|
82
81
|
|
|
83
|
-
def check_frames_dropped(self) ->
|
|
82
|
+
def check_frames_dropped(self) -> tuple[bool, str]:
|
|
84
83
|
return self.check_node_frames_from_attr(
|
|
85
84
|
lambda node: node.frames_dropped.get(), "dropped"
|
|
86
85
|
)
|
|
87
86
|
|
|
88
|
-
def get_error_state(self) ->
|
|
87
|
+
def get_error_state(self) -> tuple[bool, str]:
|
|
89
88
|
is_error = []
|
|
90
89
|
error_messages = []
|
|
91
90
|
for node_number, node_pv in enumerate(self.nodes):
|
|
@@ -99,7 +98,7 @@ class OdinNodesStatus(Device):
|
|
|
99
98
|
|
|
100
99
|
def get_init_state(self) -> bool:
|
|
101
100
|
is_initialised = []
|
|
102
|
-
for
|
|
101
|
+
for node_pv in self.nodes:
|
|
103
102
|
is_initialised.append(node_pv.fr_initialised.get())
|
|
104
103
|
is_initialised.append(node_pv.fp_initialised.get())
|
|
105
104
|
return all(is_initialised)
|
|
@@ -132,7 +131,7 @@ class EigerOdin(Device):
|
|
|
132
131
|
frames_timed_out, frames_timed_out_details = self.nodes.check_frames_timed_out()
|
|
133
132
|
|
|
134
133
|
if not is_initialised:
|
|
135
|
-
raise
|
|
134
|
+
raise RuntimeError(error_message)
|
|
136
135
|
if frames_dropped:
|
|
137
136
|
self.log.error(f"Frames dropped: {frames_dropped_details}")
|
|
138
137
|
if frames_timed_out:
|
|
@@ -140,7 +139,7 @@ class EigerOdin(Device):
|
|
|
140
139
|
|
|
141
140
|
return is_initialised and not frames_dropped and not frames_timed_out
|
|
142
141
|
|
|
143
|
-
def check_odin_initialised(self) ->
|
|
142
|
+
def check_odin_initialised(self) -> tuple[bool, str]:
|
|
144
143
|
is_error_state, error_messages = self.nodes.get_error_state()
|
|
145
144
|
to_check = [
|
|
146
145
|
(not self.fan.consumers_connected.get(), "EigerFan is not connected"),
|
dodal/devices/fast_grid_scan.py
CHANGED
|
@@ -126,7 +126,7 @@ class GridScanParamsCommon(AbstractExperimentWithBeamParams):
|
|
|
126
126
|
:return: The motor position this corresponds to.
|
|
127
127
|
:raises: IndexError if the desired position is outside the grid."""
|
|
128
128
|
for position, axis in zip(
|
|
129
|
-
grid_position, [self.x_axis, self.y_axis, self.z_axis]
|
|
129
|
+
grid_position, [self.x_axis, self.y_axis, self.z_axis], strict=False
|
|
130
130
|
):
|
|
131
131
|
if not axis.is_within(position):
|
|
132
132
|
raise IndexError(f"{grid_position} is outside the bounds of the grid")
|
|
@@ -191,9 +191,10 @@ class MotionProgram(Device):
|
|
|
191
191
|
class ExpectedImages(SignalR[int]):
|
|
192
192
|
def __init__(self, parent: "FastGridScanCommon") -> None:
|
|
193
193
|
super().__init__(SoftSignalBackend(int))
|
|
194
|
-
self.parent
|
|
194
|
+
self.parent = parent
|
|
195
195
|
|
|
196
|
-
async def get_value(self):
|
|
196
|
+
async def get_value(self, cached: bool | None = None):
|
|
197
|
+
assert isinstance(self.parent, FastGridScanCommon)
|
|
197
198
|
x = await self.parent.x_steps.get_value()
|
|
198
199
|
y = await self.parent.y_steps.get_value()
|
|
199
200
|
z = await self.parent.z_steps.get_value()
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
from
|
|
2
|
-
from ophyd import Device, EpicsSignal
|
|
1
|
+
from enum import Enum
|
|
3
2
|
|
|
3
|
+
from ophyd_async.core import StandardReadable
|
|
4
|
+
from ophyd_async.epics.signal import epics_signal_r
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
|
|
7
|
+
class FluorescenceDetectorControlState(Enum):
|
|
6
8
|
OUT = 0
|
|
7
9
|
IN = 1
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
|
|
12
|
+
class FluorescenceDetector(StandardReadable):
|
|
13
|
+
def __init__(self, prefix: str, name: str = ""):
|
|
14
|
+
with self.add_children_as_readables():
|
|
15
|
+
self.pos = epics_signal_r(
|
|
16
|
+
FluorescenceDetectorControlState, prefix + "-EA-FLU-01:CTRL"
|
|
17
|
+
)
|
|
18
|
+
super().__init__(name)
|
dodal/devices/focusing_mirror.py
CHANGED
|
@@ -2,8 +2,10 @@ from enum import Enum
|
|
|
2
2
|
|
|
3
3
|
from ophyd_async.core import (
|
|
4
4
|
AsyncStatus,
|
|
5
|
+
ConfigSignal,
|
|
5
6
|
Device,
|
|
6
7
|
DeviceVector,
|
|
8
|
+
HintedSignal,
|
|
7
9
|
StandardReadable,
|
|
8
10
|
observe_value,
|
|
9
11
|
)
|
|
@@ -144,10 +146,8 @@ class FocusingMirror(StandardReadable):
|
|
|
144
146
|
# regardless of orientation of the mirror
|
|
145
147
|
self.incident_angle = Motor(prefix + "PITCH")
|
|
146
148
|
|
|
147
|
-
self.
|
|
148
|
-
|
|
149
|
-
config=[self.type],
|
|
150
|
-
)
|
|
149
|
+
self.add_readables([self.incident_angle.user_readback], wrapper=HintedSignal)
|
|
150
|
+
self.add_readables([self.type], wrapper=ConfigSignal)
|
|
151
151
|
super().__init__(name)
|
|
152
152
|
|
|
153
153
|
|
dodal/devices/hutch_shutter.py
CHANGED
|
@@ -74,20 +74,20 @@ class HutchShutter(StandardReadable, Movable):
|
|
|
74
74
|
super().__init__(name)
|
|
75
75
|
|
|
76
76
|
@AsyncStatus.wrap
|
|
77
|
-
async def set(self,
|
|
77
|
+
async def set(self, value: ShutterDemand):
|
|
78
78
|
interlock_state = await self.interlock.shutter_safe_to_operate()
|
|
79
79
|
if not interlock_state:
|
|
80
80
|
raise ShutterNotSafeToOperateError(
|
|
81
81
|
"The hutch has not been locked, not operating shutter."
|
|
82
82
|
)
|
|
83
|
-
if
|
|
83
|
+
if value == ShutterDemand.OPEN:
|
|
84
84
|
await self.control.set(ShutterDemand.RESET, wait=True)
|
|
85
|
-
await self.control.set(
|
|
85
|
+
await self.control.set(value, wait=True)
|
|
86
86
|
return await wait_for_value(
|
|
87
87
|
self.status, match=ShutterState.OPEN, timeout=DEFAULT_TIMEOUT
|
|
88
88
|
)
|
|
89
89
|
else:
|
|
90
|
-
await self.control.set(
|
|
90
|
+
await self.control.set(value, wait=True)
|
|
91
91
|
return await wait_for_value(
|
|
92
92
|
self.status, match=ShutterState.CLOSED, timeout=DEFAULT_TIMEOUT
|
|
93
93
|
)
|
dodal/devices/i22/dcm.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import time
|
|
2
|
+
from collections.abc import Sequence
|
|
2
3
|
from dataclasses import dataclass
|
|
3
|
-
from typing import
|
|
4
|
+
from typing import Literal
|
|
4
5
|
|
|
5
6
|
from bluesky.protocols import Reading
|
|
6
7
|
from event_model.documents.event_descriptor import DataKey
|
|
@@ -127,7 +128,7 @@ class DoubleCrystalMonochromator(StandardReadable):
|
|
|
127
128
|
|
|
128
129
|
super().__init__(name)
|
|
129
130
|
|
|
130
|
-
async def describe(self) ->
|
|
131
|
+
async def describe(self) -> dict[str, DataKey]:
|
|
131
132
|
default_describe = await super().describe()
|
|
132
133
|
return {
|
|
133
134
|
f"{self.name}-wavelength": DataKey(
|
|
@@ -139,7 +140,7 @@ class DoubleCrystalMonochromator(StandardReadable):
|
|
|
139
140
|
**default_describe,
|
|
140
141
|
}
|
|
141
142
|
|
|
142
|
-
async def read(self) ->
|
|
143
|
+
async def read(self) -> dict[str, Reading]:
|
|
143
144
|
default_reading = await super().read()
|
|
144
145
|
energy: float = default_reading[f"{self.name}-energy"]["value"]
|
|
145
146
|
if energy > 0.0:
|
dodal/devices/i22/fswitch.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import time
|
|
3
3
|
from enum import Enum
|
|
4
|
-
from typing import Dict
|
|
5
4
|
|
|
6
|
-
from bluesky.protocols import
|
|
5
|
+
from bluesky.protocols import Reading
|
|
6
|
+
from event_model import DataKey
|
|
7
7
|
from ophyd_async.core import ConfigSignal, StandardReadable, soft_signal_r_and_setter
|
|
8
8
|
from ophyd_async.core.device import DeviceVector
|
|
9
9
|
from ophyd_async.epics.signal import epics_signal_r
|
|
@@ -74,7 +74,7 @@ class FSwitch(StandardReadable):
|
|
|
74
74
|
|
|
75
75
|
super().__init__(name)
|
|
76
76
|
|
|
77
|
-
async def describe(self) ->
|
|
77
|
+
async def describe(self) -> dict[str, DataKey]:
|
|
78
78
|
default_describe = await super().describe()
|
|
79
79
|
return {
|
|
80
80
|
FSwitch.NUM_LENSES_FIELD_NAME: DataKey(
|
|
@@ -83,7 +83,7 @@ class FSwitch(StandardReadable):
|
|
|
83
83
|
**default_describe,
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
async def read(self) ->
|
|
86
|
+
async def read(self) -> dict[str, Reading]:
|
|
87
87
|
result = await asyncio.gather(
|
|
88
88
|
*(filter.get_value() for filter in self.filters.values())
|
|
89
89
|
)
|
dodal/devices/i22/nxsas.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from dataclasses import dataclass, fields
|
|
2
|
-
from typing import Dict
|
|
3
2
|
|
|
4
3
|
from bluesky.protocols import Reading
|
|
5
4
|
from event_model.documents.event_descriptor import DataKey
|
|
@@ -13,7 +12,7 @@ ValueAndUnits = tuple[float, str]
|
|
|
13
12
|
@dataclass
|
|
14
13
|
class MetadataHolder:
|
|
15
14
|
# TODO: just in case this is useful more widely...
|
|
16
|
-
async def describe(self, parent_name: str) ->
|
|
15
|
+
async def describe(self, parent_name: str) -> dict[str, DataKey]:
|
|
17
16
|
def datakey(value) -> DataKey:
|
|
18
17
|
if isinstance(value, tuple):
|
|
19
18
|
return {"units": value[1], **datakey(value[0])}
|
|
@@ -40,8 +39,8 @@ class MetadataHolder:
|
|
|
40
39
|
if getattr(self, field.name, None) is not None
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
async def read(self, parent_name: str) ->
|
|
44
|
-
def reading(value):
|
|
42
|
+
async def read(self, parent_name: str) -> dict[str, Reading]:
|
|
43
|
+
def reading(value) -> Reading:
|
|
45
44
|
if isinstance(value, tuple):
|
|
46
45
|
return reading(value[0])
|
|
47
46
|
return {"timestamp": -1, "value": value}
|
|
@@ -101,25 +100,21 @@ class NXSasPilatus(PilatusDetector):
|
|
|
101
100
|
)
|
|
102
101
|
self._metadata_holder = metadata_holder
|
|
103
102
|
|
|
104
|
-
async def read_configuration(self) ->
|
|
103
|
+
async def read_configuration(self) -> dict[str, Reading]:
|
|
105
104
|
return await merge_gathered_dicts(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
self._metadata_holder.read(self.name),
|
|
111
|
-
)
|
|
105
|
+
r
|
|
106
|
+
for r in (
|
|
107
|
+
super().read_configuration(),
|
|
108
|
+
self._metadata_holder.read(self.name),
|
|
112
109
|
)
|
|
113
110
|
)
|
|
114
111
|
|
|
115
|
-
async def describe_configuration(self) ->
|
|
112
|
+
async def describe_configuration(self) -> dict[str, DataKey]:
|
|
116
113
|
return await merge_gathered_dicts(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
self._metadata_holder.describe(self.name),
|
|
122
|
-
)
|
|
114
|
+
r
|
|
115
|
+
for r in (
|
|
116
|
+
super().describe_configuration(),
|
|
117
|
+
self._metadata_holder.describe(self.name),
|
|
123
118
|
)
|
|
124
119
|
)
|
|
125
120
|
|
|
@@ -150,24 +145,20 @@ class NXSasOAV(AravisDetector):
|
|
|
150
145
|
)
|
|
151
146
|
self._metadata_holder = metadata_holder
|
|
152
147
|
|
|
153
|
-
async def read_configuration(self) ->
|
|
148
|
+
async def read_configuration(self) -> dict[str, Reading]:
|
|
154
149
|
return await merge_gathered_dicts(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
self._metadata_holder.read(self.name),
|
|
160
|
-
)
|
|
150
|
+
r
|
|
151
|
+
for r in (
|
|
152
|
+
super().read_configuration(),
|
|
153
|
+
self._metadata_holder.read(self.name),
|
|
161
154
|
)
|
|
162
155
|
)
|
|
163
156
|
|
|
164
|
-
async def describe_configuration(self) ->
|
|
157
|
+
async def describe_configuration(self) -> dict[str, DataKey]:
|
|
165
158
|
return await merge_gathered_dicts(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
self._metadata_holder.describe(self.name),
|
|
171
|
-
)
|
|
159
|
+
r
|
|
160
|
+
for r in (
|
|
161
|
+
super().describe_configuration(),
|
|
162
|
+
self._metadata_holder.describe(self.name),
|
|
172
163
|
)
|
|
173
164
|
)
|
dodal/devices/i24/pmac.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
from enum import Enum
|
|
1
|
+
from enum import Enum, IntEnum
|
|
2
|
+
from typing import SupportsFloat
|
|
2
3
|
|
|
3
4
|
from bluesky.protocols import Triggerable
|
|
4
|
-
from ophyd_async.core import AsyncStatus, StandardReadable
|
|
5
|
-
from ophyd_async.core.signal import SignalRW
|
|
5
|
+
from ophyd_async.core import AsyncStatus, StandardReadable, wait_for_value
|
|
6
|
+
from ophyd_async.core.signal import CalculateTimeout, SignalR, SignalRW
|
|
6
7
|
from ophyd_async.core.signal_backend import SignalBackend
|
|
7
8
|
from ophyd_async.core.soft_signal_backend import SoftSignalBackend
|
|
8
9
|
from ophyd_async.core.utils import DEFAULT_TIMEOUT
|
|
@@ -13,6 +14,11 @@ HOME_STR = r"\#1hmz\#2hmz\#3hmz" # Command to home the PMAC motors
|
|
|
13
14
|
ZERO_STR = "!x0y0z0" # Command to blend any ongoing move into new position
|
|
14
15
|
|
|
15
16
|
|
|
17
|
+
class ScanState(IntEnum):
|
|
18
|
+
RUNNING = 1
|
|
19
|
+
DONE = 0
|
|
20
|
+
|
|
21
|
+
|
|
16
22
|
class LaserSettings(str, Enum):
|
|
17
23
|
"""PMAC strings to switch laser on and off.
|
|
18
24
|
Note. On the PMAC, M-variables usually have to do with position compare
|
|
@@ -73,26 +79,55 @@ class PMACStringLaser(SignalRW):
|
|
|
73
79
|
super().__init__(backend, timeout, name)
|
|
74
80
|
|
|
75
81
|
@AsyncStatus.wrap
|
|
76
|
-
async def set(self,
|
|
77
|
-
await self.signal.set(
|
|
82
|
+
async def set(self, value: LaserSettings, wait=True, timeout=CalculateTimeout):
|
|
83
|
+
await self.signal.set(value.value, wait, timeout)
|
|
78
84
|
|
|
79
85
|
|
|
80
86
|
class PMACStringEncReset(SignalRW):
|
|
81
|
-
""""""
|
|
87
|
+
"""Set a pmac_string to control the encoder channels in the controller."""
|
|
88
|
+
|
|
89
|
+
def __init__(
|
|
90
|
+
self,
|
|
91
|
+
pmac_str_sig: SignalRW,
|
|
92
|
+
backend: SignalBackend,
|
|
93
|
+
timeout: float | None = DEFAULT_TIMEOUT,
|
|
94
|
+
name: str = "",
|
|
95
|
+
) -> None:
|
|
96
|
+
self.signal = pmac_str_sig
|
|
97
|
+
super().__init__(backend, timeout, name)
|
|
98
|
+
|
|
99
|
+
@AsyncStatus.wrap
|
|
100
|
+
async def set(self, value: EncReset, wait=True, timeout=CalculateTimeout):
|
|
101
|
+
await self.signal.set(value.value, wait, timeout)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class ProgramRunner(SignalRW):
|
|
105
|
+
"""Trigger the collection by setting the program number on the PMAC string.
|
|
106
|
+
|
|
107
|
+
Once the program number has been set, wait for the collection to be complete.
|
|
108
|
+
This will only be true when the status becomes 0.
|
|
109
|
+
"""
|
|
82
110
|
|
|
83
111
|
def __init__(
|
|
84
112
|
self,
|
|
85
113
|
pmac_str_sig: SignalRW,
|
|
114
|
+
status_sig: SignalR,
|
|
86
115
|
backend: SignalBackend,
|
|
87
116
|
timeout: float | None = DEFAULT_TIMEOUT,
|
|
88
117
|
name: str = "",
|
|
89
118
|
) -> None:
|
|
90
119
|
self.signal = pmac_str_sig
|
|
120
|
+
self.status = status_sig
|
|
91
121
|
super().__init__(backend, timeout, name)
|
|
92
122
|
|
|
93
123
|
@AsyncStatus.wrap
|
|
94
|
-
async def set(self,
|
|
95
|
-
|
|
124
|
+
async def set(self, value: int, wait=True, timeout=None):
|
|
125
|
+
prog_str = f"&2b{value}r"
|
|
126
|
+
assert isinstance(timeout, SupportsFloat) or (
|
|
127
|
+
timeout is None
|
|
128
|
+
), f"ProgramRunner does not support calculating timeout itself, {timeout=}"
|
|
129
|
+
await self.signal.set(prog_str, wait=wait)
|
|
130
|
+
await wait_for_value(self.status, ScanState.DONE, timeout)
|
|
96
131
|
|
|
97
132
|
|
|
98
133
|
class PMAC(StandardReadable):
|
|
@@ -121,4 +156,8 @@ class PMAC(StandardReadable):
|
|
|
121
156
|
self.scanstatus = epics_signal_r(float, "BL24I-MO-STEP-14:signal:P2401")
|
|
122
157
|
self.counter = epics_signal_r(float, "BL24I-MO-STEP-14:signal:P2402")
|
|
123
158
|
|
|
159
|
+
self.run_program = ProgramRunner(
|
|
160
|
+
self.pmac_string, self.scanstatus, backend=SoftSignalBackend(str)
|
|
161
|
+
)
|
|
162
|
+
|
|
124
163
|
super().__init__(name)
|
dodal/devices/ipin.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
from
|
|
2
|
-
from
|
|
1
|
+
from ophyd_async.core import HintedSignal, StandardReadable
|
|
2
|
+
from ophyd_async.epics.signal import epics_signal_r
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
class IPin(
|
|
5
|
+
class IPin(StandardReadable):
|
|
6
6
|
"""Simple device to get the ipin reading"""
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
9
|
+
with self.add_children_as_readables(wrapper=HintedSignal):
|
|
10
|
+
self.pin_readback = epics_signal_r(float, prefix + "I")
|
|
11
|
+
super().__init__(name)
|
dodal/devices/linkam3.py
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import time
|
|
3
3
|
from enum import Enum
|
|
4
|
-
from typing import Optional
|
|
5
4
|
|
|
6
5
|
from bluesky.protocols import Location
|
|
7
|
-
from ophyd_async.core import
|
|
6
|
+
from ophyd_async.core import (
|
|
7
|
+
ConfigSignal,
|
|
8
|
+
HintedSignal,
|
|
9
|
+
StandardReadable,
|
|
10
|
+
WatchableAsyncStatus,
|
|
11
|
+
observe_value,
|
|
12
|
+
)
|
|
8
13
|
from ophyd_async.core.utils import WatcherUpdate
|
|
9
14
|
from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
|
|
10
15
|
|
|
@@ -57,14 +62,15 @@ class Linkam3(StandardReadable):
|
|
|
57
62
|
# status is a bitfield stored in a double?
|
|
58
63
|
self.status = epics_signal_r(float, prefix + "STATUS:")
|
|
59
64
|
|
|
60
|
-
self.
|
|
61
|
-
|
|
65
|
+
self.add_readables((self.temp,), wrapper=HintedSignal)
|
|
66
|
+
self.add_readables(
|
|
67
|
+
(self.ramp_rate, self.speed, self.set_point), wrapper=ConfigSignal
|
|
62
68
|
)
|
|
63
69
|
|
|
64
70
|
super().__init__(name=name)
|
|
65
71
|
|
|
66
72
|
@WatchableAsyncStatus.wrap
|
|
67
|
-
async def set(self, new_position: float, timeout:
|
|
73
|
+
async def set(self, new_position: float, timeout: float | None = None):
|
|
68
74
|
# time.monotonic won't go backwards in case of NTP corrections
|
|
69
75
|
start = time.monotonic()
|
|
70
76
|
old_position = await self.set_point.get_value()
|
|
@@ -3,7 +3,7 @@ from ophyd.log import logger as ophyd_logger
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class InfoLoggingDevice(Device):
|
|
6
|
-
def wait_for_connection(self, all_signals=False, timeout=2):
|
|
6
|
+
def wait_for_connection(self, all_signals=False, timeout=2.0):
|
|
7
7
|
class_name = self.__class__.__name__
|
|
8
8
|
ophyd_logger.info(
|
|
9
9
|
f"{class_name} waiting for connection, {'not' if all_signals else ''} waiting for all signals, timeout = {timeout}s.",
|