dls-dodal 1.55.1__py3-none-any.whl → 1.57.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.55.1.dist-info → dls_dodal-1.57.0.dist-info}/METADATA +3 -3
- {dls_dodal-1.55.1.dist-info → dls_dodal-1.57.0.dist-info}/RECORD +101 -87
- dodal/_version.py +16 -3
- dodal/beamlines/b01_1.py +6 -1
- dodal/beamlines/b07.py +2 -1
- dodal/beamlines/b07_1.py +2 -1
- dodal/beamlines/b21.py +4 -24
- dodal/beamlines/i03.py +53 -53
- dodal/beamlines/i04.py +16 -38
- dodal/beamlines/i09.py +3 -2
- dodal/beamlines/i09_1.py +2 -1
- dodal/beamlines/i11.py +143 -0
- dodal/beamlines/i17.py +37 -0
- dodal/beamlines/i19_1.py +1 -0
- dodal/beamlines/i19_2.py +7 -0
- dodal/beamlines/i22.py +5 -5
- dodal/beamlines/i23.py +3 -3
- dodal/beamlines/i24.py +6 -33
- dodal/beamlines/p38.py +1 -0
- dodal/beamlines/p60.py +3 -2
- dodal/cli.py +11 -1
- dodal/common/__init__.py +4 -0
- dodal/common/beamlines/beamline_parameters.py +1 -1
- dodal/common/beamlines/beamline_utils.py +5 -1
- dodal/common/enums.py +19 -0
- dodal/common/watcher_utils.py +83 -0
- dodal/devices/aithre_lasershaping/laser_robot.py +4 -9
- dodal/devices/aperturescatterguard.py +52 -12
- dodal/devices/apple2_undulator.py +0 -1
- dodal/devices/b16/detector.py +1 -10
- dodal/devices/backlight.py +8 -20
- dodal/devices/bimorph_mirror.py +4 -7
- dodal/devices/collimation_table.py +36 -0
- dodal/devices/controllers.py +21 -0
- dodal/devices/cryostream.py +97 -7
- dodal/devices/current_amplifiers/femto.py +1 -1
- dodal/devices/detector/detector_motion.py +1 -7
- dodal/devices/eiger.py +22 -8
- dodal/devices/eiger_odin.py +2 -0
- dodal/devices/electron_analyser/__init__.py +2 -1
- dodal/devices/electron_analyser/abstract/__init__.py +0 -1
- dodal/devices/electron_analyser/abstract/base_detector.py +3 -25
- dodal/devices/electron_analyser/abstract/base_driver_io.py +18 -9
- dodal/devices/electron_analyser/abstract/base_region.py +34 -3
- dodal/devices/electron_analyser/detector.py +24 -0
- dodal/devices/electron_analyser/enums.py +5 -0
- dodal/devices/electron_analyser/specs/detector.py +2 -1
- dodal/devices/electron_analyser/specs/driver_io.py +21 -26
- dodal/devices/electron_analyser/specs/region.py +1 -1
- dodal/devices/electron_analyser/util.py +20 -0
- dodal/devices/electron_analyser/vgscienta/__init__.py +3 -3
- dodal/devices/electron_analyser/vgscienta/detector.py +2 -1
- dodal/devices/electron_analyser/vgscienta/driver_io.py +24 -32
- dodal/devices/electron_analyser/vgscienta/enums.py +0 -8
- dodal/devices/electron_analyser/vgscienta/region.py +2 -31
- dodal/devices/eurotherm.py +126 -0
- dodal/devices/fluorescence_detector_motion.py +3 -10
- dodal/devices/focusing_mirror.py +1 -1
- dodal/devices/i03/undulator_dcm.py +0 -1
- dodal/devices/i09/enums.py +8 -8
- dodal/devices/i10/diagnostics.py +4 -4
- dodal/devices/i10/i10_apple2.py +3 -6
- dodal/devices/i11/cyberstar_blower.py +34 -0
- dodal/devices/i11/diff_stages.py +55 -0
- dodal/devices/i11/mythen.py +165 -0
- dodal/devices/i11/nx100robot.py +153 -0
- dodal/devices/i11/spinner.py +30 -0
- dodal/devices/i13_1/merlin_controller.py +4 -4
- dodal/devices/i19/diffractometer.py +34 -0
- dodal/devices/i19/shutter.py +11 -1
- dodal/devices/i22/dcm.py +1 -1
- dodal/devices/i22/fswitch.py +3 -12
- dodal/devices/i24/aperture.py +3 -3
- dodal/devices/i24/beam_center.py +1 -2
- dodal/devices/i24/dcm.py +11 -15
- dodal/devices/i24/dual_backlight.py +11 -12
- dodal/devices/i24/pmac.py +8 -7
- dodal/devices/mx_phase1/beamstop.py +10 -11
- dodal/devices/oav/pin_image_recognition/__init__.py +0 -3
- dodal/devices/p60/enums.py +8 -8
- dodal/devices/p60/lab_xray_source.py +3 -2
- dodal/devices/pressure_jump_cell.py +77 -123
- dodal/devices/scintillator.py +76 -4
- dodal/devices/smargon.py +35 -18
- dodal/devices/synchrotron.py +1 -2
- dodal/devices/thawer.py +22 -15
- dodal/devices/undulator.py +3 -8
- dodal/devices/util/epics_util.py +1 -1
- dodal/devices/watsonmarlow323_pump.py +7 -7
- dodal/devices/webcam.py +1 -0
- dodal/devices/xbpm_feedback.py +4 -6
- dodal/devices/xspress3/xspress3.py +0 -5
- dodal/devices/zocalo/zocalo_results.py +1 -3
- dodal/testing/__init__.py +3 -0
- dodal/testing/electron_analyser/__init__.py +6 -0
- dodal/testing/electron_analyser/device_factory.py +59 -0
- dodal/testing/setup.py +67 -0
- dodal/devices/CTAB.py +0 -41
- dodal/devices/i24/pilatus_metadata.py +0 -44
- dodal/devices/util/test_utils.py +0 -37
- {dls_dodal-1.55.1.dist-info → dls_dodal-1.57.0.dist-info}/WHEEL +0 -0
- {dls_dodal-1.55.1.dist-info → dls_dodal-1.57.0.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.55.1.dist-info → dls_dodal-1.57.0.dist-info}/licenses/LICENSE +0 -0
- {dls_dodal-1.55.1.dist-info → dls_dodal-1.57.0.dist-info}/top_level.txt +0 -0
dodal/devices/undulator.py
CHANGED
|
@@ -7,12 +7,12 @@ from ophyd_async.core import (
|
|
|
7
7
|
AsyncStatus,
|
|
8
8
|
StandardReadable,
|
|
9
9
|
StandardReadableFormat,
|
|
10
|
-
StrictEnum,
|
|
11
10
|
soft_signal_r_and_setter,
|
|
12
11
|
)
|
|
13
12
|
from ophyd_async.epics.core import epics_signal_r
|
|
14
13
|
from ophyd_async.epics.motor import Motor
|
|
15
14
|
|
|
15
|
+
from dodal.common.enums import EnabledDisabledUpper
|
|
16
16
|
from dodal.log import LOGGER
|
|
17
17
|
|
|
18
18
|
from .util.lookup_tables import energy_distance_table
|
|
@@ -33,11 +33,6 @@ UNDULATOR_DISCREPANCY_THRESHOLD_MM = 2e-3
|
|
|
33
33
|
STATUS_TIMEOUT_S: float = 10.0
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
class UndulatorGapAccess(StrictEnum):
|
|
37
|
-
ENABLED = "ENABLED"
|
|
38
|
-
DISABLED = "DISABLED"
|
|
39
|
-
|
|
40
|
-
|
|
41
36
|
def _get_gap_for_energy(
|
|
42
37
|
dcm_energy_ev: float, energy_to_distance_table: ndarray
|
|
43
38
|
) -> float:
|
|
@@ -73,7 +68,7 @@ class Undulator(StandardReadable, Movable[float]):
|
|
|
73
68
|
with self.add_children_as_readables():
|
|
74
69
|
self.gap_motor = Motor(prefix + "BLGAPMTR")
|
|
75
70
|
self.current_gap = epics_signal_r(float, prefix + "CURRGAPD")
|
|
76
|
-
self.gap_access = epics_signal_r(
|
|
71
|
+
self.gap_access = epics_signal_r(EnabledDisabledUpper, prefix + "IDBLENA")
|
|
77
72
|
|
|
78
73
|
with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
|
|
79
74
|
self.gap_discrepancy_tolerance_mm, _ = soft_signal_r_and_setter(
|
|
@@ -110,7 +105,7 @@ class Undulator(StandardReadable, Movable[float]):
|
|
|
110
105
|
|
|
111
106
|
async def raise_if_not_enabled(self):
|
|
112
107
|
access_level = await self.gap_access.get_value()
|
|
113
|
-
if access_level is
|
|
108
|
+
if access_level is EnabledDisabledUpper.DISABLED and not TEST_MODE:
|
|
114
109
|
raise AccessError("Undulator gap access is disabled. Contact Control Room")
|
|
115
110
|
|
|
116
111
|
async def _set_undulator_gap(self, energy_kev: float) -> None:
|
dodal/devices/util/epics_util.py
CHANGED
|
@@ -117,7 +117,7 @@ def call_func(func: Callable[[], StatusBase]) -> StatusBase:
|
|
|
117
117
|
class SetWhenEnabled(OphydAsyncDevice, Movable[int]):
|
|
118
118
|
"""A device that sets the proc field of a PV when it becomes enabled."""
|
|
119
119
|
|
|
120
|
-
def __init__(self,
|
|
120
|
+
def __init__(self, prefix: str, name: str = ""):
|
|
121
121
|
self.proc = epics_signal_rw(int, prefix + ".PROC")
|
|
122
122
|
self.disp = epics_signal_r(int, prefix + ".DISP")
|
|
123
123
|
super().__init__(name)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
from ophyd_async.core import
|
|
1
|
+
from ophyd_async.core import (
|
|
2
|
+
EnabledDisabled,
|
|
3
|
+
StandardReadable,
|
|
4
|
+
StandardReadableFormat,
|
|
5
|
+
StrictEnum,
|
|
6
|
+
)
|
|
2
7
|
from ophyd_async.epics.core import epics_signal_rw
|
|
3
8
|
|
|
4
9
|
|
|
5
|
-
class WatsonMarlow323PumpEnable(StrictEnum):
|
|
6
|
-
DISABLED = "Disabled"
|
|
7
|
-
ENABLED = "Enabled"
|
|
8
|
-
|
|
9
|
-
|
|
10
10
|
class WatsonMarlow323PumpDirection(StrictEnum):
|
|
11
11
|
CLOCKWISE = "CW"
|
|
12
12
|
COUNTER_CLOCKWISE = "CCW"
|
|
@@ -38,7 +38,7 @@ class WatsonMarlow323Pump(StandardReadable):
|
|
|
38
38
|
|
|
39
39
|
with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
|
|
40
40
|
self.enabled = epics_signal_rw(
|
|
41
|
-
|
|
41
|
+
EnabledDisabled,
|
|
42
42
|
prefix + "DISABLE",
|
|
43
43
|
)
|
|
44
44
|
|
dodal/devices/webcam.py
CHANGED
dodal/devices/xbpm_feedback.py
CHANGED
|
@@ -14,12 +14,10 @@ class XBPMFeedback(Device, Triggerable):
|
|
|
14
14
|
"""The XBPM feedback device is an IOC that moves the DCM, HFM and VFM to automatically
|
|
15
15
|
hold the beam into place, as measured by the XBPM sensor."""
|
|
16
16
|
|
|
17
|
-
def __init__(self, prefix: str
|
|
18
|
-
self.pos_ok = epics_signal_r(float, prefix + "
|
|
19
|
-
self.pos_stable = epics_signal_r(float, prefix + "
|
|
20
|
-
self.pause_feedback = epics_signal_rw(Pause, prefix + "
|
|
21
|
-
self.x = epics_signal_r(float, prefix + "-EA-XBPM-02:PosX:MeanValue_RBV")
|
|
22
|
-
self.y = epics_signal_r(float, prefix + "-EA-XBPM-02:PosY:MeanValue_RBV")
|
|
17
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
18
|
+
self.pos_ok = epics_signal_r(float, prefix + "XBPM2POSITION_OK")
|
|
19
|
+
self.pos_stable = epics_signal_r(float, prefix + "XBPM2_STABLE")
|
|
20
|
+
self.pause_feedback = epics_signal_rw(Pause, prefix + "FB_PAUSE")
|
|
23
21
|
super().__init__(name=name)
|
|
24
22
|
|
|
25
23
|
@AsyncStatus.wrap
|
|
@@ -125,19 +125,17 @@ class ZocaloResults(StandardReadable, Triggerable):
|
|
|
125
125
|
|
|
126
126
|
def __init__(
|
|
127
127
|
self,
|
|
128
|
-
name: str = "
|
|
128
|
+
name: str = "",
|
|
129
129
|
zocalo_environment: str = ZOCALO_ENV,
|
|
130
130
|
channel: str = "xrc.i03",
|
|
131
131
|
sort_key: str = DEFAULT_SORT_KEY.value,
|
|
132
132
|
timeout_s: float = DEFAULT_TIMEOUT,
|
|
133
|
-
prefix: str = "",
|
|
134
133
|
results_source: ZocaloSource = ZocaloSource.CPU,
|
|
135
134
|
) -> None:
|
|
136
135
|
self.zocalo_environment = zocalo_environment
|
|
137
136
|
self.sort_key = SortKeys[sort_key]
|
|
138
137
|
self.channel = channel
|
|
139
138
|
self.timeout_s = timeout_s
|
|
140
|
-
self._prefix = prefix
|
|
141
139
|
self._raw_results_received: Queue = Queue()
|
|
142
140
|
self.transport: CommonTransport | None = None
|
|
143
141
|
self.results_source = results_source
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from typing import Any, get_args, get_origin
|
|
2
|
+
|
|
3
|
+
from dodal.devices.electron_analyser.abstract import (
|
|
4
|
+
TAbstractAnalyserDriverIO,
|
|
5
|
+
)
|
|
6
|
+
from dodal.devices.electron_analyser.detector import TElectronAnalyserDetector
|
|
7
|
+
from dodal.devices.electron_analyser.vgscienta import (
|
|
8
|
+
VGScientaAnalyserDriverIO,
|
|
9
|
+
VGScientaDetector,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def create_driver(
|
|
14
|
+
driver_class: type[TAbstractAnalyserDriverIO],
|
|
15
|
+
**kwargs: Any,
|
|
16
|
+
) -> TAbstractAnalyserDriverIO:
|
|
17
|
+
"""
|
|
18
|
+
Helper function that helps to reduce the code to setup an analyser driver. The
|
|
19
|
+
parameters used for the enum types are taken directly from the subscripts of the
|
|
20
|
+
class so the user only needs to provide it in one place.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
driver_class: The class for the driver which must include the enums in the
|
|
24
|
+
subscript, for example MyDriverClass[MyLensMode, ...]
|
|
25
|
+
kwargs: Additional key worded arguments that the driver needs for initalisation.
|
|
26
|
+
"""
|
|
27
|
+
parameters = {
|
|
28
|
+
"lens_mode_type": get_args(driver_class)[0],
|
|
29
|
+
"psu_mode_type": get_args(driver_class)[1],
|
|
30
|
+
}
|
|
31
|
+
if get_origin(driver_class) is VGScientaAnalyserDriverIO:
|
|
32
|
+
parameters["pass_energy_type"] = get_args(driver_class)[2]
|
|
33
|
+
|
|
34
|
+
return driver_class(**(parameters | kwargs))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
async def create_detector(
|
|
38
|
+
detector_class: type[TElectronAnalyserDetector],
|
|
39
|
+
**kwargs: Any,
|
|
40
|
+
) -> TElectronAnalyserDetector:
|
|
41
|
+
"""
|
|
42
|
+
Helper function that helps to reduce the code to setup an analyser detector. The
|
|
43
|
+
parameters used for the enum types are taken directly from the subscripts of the
|
|
44
|
+
class so the user only needs to provide it in one place.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
detector_class: The class for the detector which must include the enums in the
|
|
48
|
+
subscript, for example MyDetectorClass[MyLensMode, ...]
|
|
49
|
+
kwargs: Additional key worded arguments that the detector needs for
|
|
50
|
+
initalisation.
|
|
51
|
+
"""
|
|
52
|
+
parameters = {
|
|
53
|
+
"lens_mode_type": get_args(detector_class)[0],
|
|
54
|
+
"psu_mode_type": get_args(detector_class)[1],
|
|
55
|
+
}
|
|
56
|
+
if get_origin(detector_class) is VGScientaDetector:
|
|
57
|
+
parameters["pass_energy_type"] = get_args(detector_class)[2]
|
|
58
|
+
|
|
59
|
+
return detector_class(**(parameters | kwargs))
|
dodal/testing/setup.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from contextlib import ExitStack
|
|
2
|
+
|
|
3
|
+
from ophyd_async.core import Device
|
|
4
|
+
from ophyd_async.epics.motor import Motor
|
|
5
|
+
from ophyd_async.testing import (
|
|
6
|
+
callback_on_mock_put,
|
|
7
|
+
set_mock_value,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def patch_motor(
|
|
12
|
+
motor: Motor,
|
|
13
|
+
initial_position: float = 0,
|
|
14
|
+
deadband: float = 0.001,
|
|
15
|
+
velocity: float = 3,
|
|
16
|
+
max_velocity: float = 5,
|
|
17
|
+
low_limit_travel: float = float("-inf"),
|
|
18
|
+
high_limit_travel: float = float("inf"),
|
|
19
|
+
):
|
|
20
|
+
"""
|
|
21
|
+
Patch a mock motor with sensible default values so that it can still be used in
|
|
22
|
+
tests and plans without running into errors as default values are zero.
|
|
23
|
+
|
|
24
|
+
Parameters:
|
|
25
|
+
motor: The mock motor to set mock values with.
|
|
26
|
+
initial_position: The default initial position of the motor to be set.
|
|
27
|
+
deadband: The tolerance between readback value and demand setpoint which the
|
|
28
|
+
motor is considered at position.
|
|
29
|
+
velocity: Requested move speed when the mock motor moves.
|
|
30
|
+
max_velocity: The maximum allowable velocity that can be set for the motor.
|
|
31
|
+
low_limit_travel: The lower limit that the motor can move to.
|
|
32
|
+
high_limit_travel: The higher limit that the motor can move to.
|
|
33
|
+
"""
|
|
34
|
+
set_mock_value(motor.user_setpoint, initial_position)
|
|
35
|
+
set_mock_value(motor.user_readback, initial_position)
|
|
36
|
+
set_mock_value(motor.deadband, deadband)
|
|
37
|
+
set_mock_value(motor.motor_done_move, 1)
|
|
38
|
+
set_mock_value(motor.velocity, velocity)
|
|
39
|
+
set_mock_value(motor.max_velocity, max_velocity)
|
|
40
|
+
set_mock_value(motor.low_limit_travel, low_limit_travel)
|
|
41
|
+
set_mock_value(motor.high_limit_travel, high_limit_travel)
|
|
42
|
+
return callback_on_mock_put(
|
|
43
|
+
motor.user_setpoint,
|
|
44
|
+
lambda pos, *args, **kwargs: set_mock_value(motor.user_readback, pos),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def patch_all_motors(parent_device: Device):
|
|
49
|
+
"""
|
|
50
|
+
Check all children of a device and patch any motors with mock values.
|
|
51
|
+
|
|
52
|
+
Parameters:
|
|
53
|
+
parent_device: The device that hold motor(s) as children.
|
|
54
|
+
"""
|
|
55
|
+
motors = []
|
|
56
|
+
|
|
57
|
+
def recursively_find_motors(device: Device):
|
|
58
|
+
for _, child_device in device.children():
|
|
59
|
+
if isinstance(child_device, Motor):
|
|
60
|
+
motors.append(child_device)
|
|
61
|
+
recursively_find_motors(child_device)
|
|
62
|
+
|
|
63
|
+
recursively_find_motors(parent_device)
|
|
64
|
+
motor_patch_stack = ExitStack()
|
|
65
|
+
for motor in motors:
|
|
66
|
+
motor_patch_stack.enter_context(patch_motor(motor))
|
|
67
|
+
return motor_patch_stack
|
dodal/devices/CTAB.py
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
from ophyd_async.core import StandardReadable
|
|
2
|
-
from ophyd_async.epics.core import epics_signal_r
|
|
3
|
-
from ophyd_async.epics.motor import Motor
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class CTAB(StandardReadable):
|
|
7
|
-
"""Basic collimantion table (CTAB) device for motion plus the motion disable signal
|
|
8
|
-
when laser curtain triggered and hutch not locked.
|
|
9
|
-
|
|
10
|
-
CTAB has 3 physical vertical motors, the jacks. 1 upstream and 2 downstream.
|
|
11
|
-
The two downstream jacks are labelled as outboard (away from the ring) and
|
|
12
|
-
inboard (towards the ring).
|
|
13
|
-
Together these 3 jacks provide compound motion for vertical motion and pitch/roll.
|
|
14
|
-
There are 2 physical horizontal motors 1 upstream, 1 downstream. These provide yaw.
|
|
15
|
-
|
|
16
|
-
CTAB motion is disabled by an object being within the laser curtain area and can be
|
|
17
|
-
overriden by use of the dead man's handle device or locking the hutch. The effect of
|
|
18
|
-
these disabling systems is to cut power to the motors - signal for this is crate_power
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
def __init__(self, prefix: str, name: str = ""):
|
|
22
|
-
with self.add_children_as_readables():
|
|
23
|
-
self.inboard_y = Motor(prefix + "-MO-TABLE-01:INBOARDY")
|
|
24
|
-
self.outboard_y = Motor(prefix + "-MO-TABLE-01:OUTBOARDY")
|
|
25
|
-
self.upstream_y = Motor(prefix + "-MO-TABLE-01:UPSTREAMY")
|
|
26
|
-
self.combined_downstream_y = Motor(prefix + "-MO-TABLE-01:DOWNSTREAMY")
|
|
27
|
-
self.combined_all_y = Motor(prefix + "-MO-TABLE-01:Y")
|
|
28
|
-
|
|
29
|
-
self.downstream_x = Motor(prefix + "-MO-TABLE-01:DOWNSTREAMX")
|
|
30
|
-
self.upstream_x = Motor(prefix + "-MO-TABLE-01:UPSTREAMX")
|
|
31
|
-
self.combined_all_x = Motor(prefix + "-MO-TABLE-01:X")
|
|
32
|
-
|
|
33
|
-
self.pitch = Motor(prefix + "-MO-TABLE-01:PITCH")
|
|
34
|
-
self.roll = Motor(prefix + "-MO-TABLE-01:ROLL")
|
|
35
|
-
self.yaw = Motor(prefix + "-MO-TABLE-01:YAW")
|
|
36
|
-
|
|
37
|
-
self.crate_power = epics_signal_r(
|
|
38
|
-
int, prefix + "-MO-PMAC-02:CRATE2_HEALTHY"
|
|
39
|
-
) # returns 0 if no power
|
|
40
|
-
|
|
41
|
-
super().__init__(name)
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"""A small temporary device to set and read the filename template from the pilatus"""
|
|
2
|
-
|
|
3
|
-
import re
|
|
4
|
-
|
|
5
|
-
from ophyd_async.core import StandardReadable, derived_signal_r
|
|
6
|
-
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class PilatusMetadata(StandardReadable):
|
|
10
|
-
def __init__(self, prefix: str, name: str = "") -> None:
|
|
11
|
-
self.filename = epics_signal_rw(str, prefix + "cam1:FileName")
|
|
12
|
-
self.template = epics_signal_r(str, prefix + "cam1:FileTemplate_RBV")
|
|
13
|
-
self.filenumber = epics_signal_r(int, prefix + "cam1:FileNumber_RBV")
|
|
14
|
-
with self.add_children_as_readables():
|
|
15
|
-
self.filename_template = derived_signal_r(
|
|
16
|
-
self._get_full_filename_template,
|
|
17
|
-
filename=self.filename,
|
|
18
|
-
filename_template=self.template,
|
|
19
|
-
file_number=self.filenumber,
|
|
20
|
-
)
|
|
21
|
-
super().__init__(name)
|
|
22
|
-
|
|
23
|
-
def _get_full_filename_template(
|
|
24
|
-
self, filename: str, filename_template: str, file_number: int
|
|
25
|
-
) -> str:
|
|
26
|
-
"""
|
|
27
|
-
Get the template file path by querying the detector PVs.
|
|
28
|
-
Mirror the construction that the PPU does.
|
|
29
|
-
|
|
30
|
-
Returns: A template string, with the image numbers replaced with '#'
|
|
31
|
-
"""
|
|
32
|
-
# Exploit fact that passing negative numbers will put the - before the 0's
|
|
33
|
-
expected_filename = str(
|
|
34
|
-
filename_template % (filename, f"{file_number:05d}_", -9)
|
|
35
|
-
)
|
|
36
|
-
# Now, find the -09 part of this
|
|
37
|
-
numberpart = re.search(r"(-0+9)", expected_filename)
|
|
38
|
-
assert numberpart is not None
|
|
39
|
-
template_fill = "#" * len(numberpart.group(0))
|
|
40
|
-
return (
|
|
41
|
-
expected_filename[: numberpart.start()]
|
|
42
|
-
+ template_fill
|
|
43
|
-
+ expected_filename[numberpart.end() :]
|
|
44
|
-
)
|
dodal/devices/util/test_utils.py
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
from contextlib import ExitStack
|
|
2
|
-
|
|
3
|
-
from ophyd_async.core import Device
|
|
4
|
-
from ophyd_async.epics.motor import Motor
|
|
5
|
-
from ophyd_async.testing import (
|
|
6
|
-
callback_on_mock_put,
|
|
7
|
-
set_mock_value,
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def patch_motor(motor: Motor, initial_position=0):
|
|
12
|
-
set_mock_value(motor.user_setpoint, initial_position)
|
|
13
|
-
set_mock_value(motor.user_readback, initial_position)
|
|
14
|
-
set_mock_value(motor.deadband, 0.001)
|
|
15
|
-
set_mock_value(motor.motor_done_move, 1)
|
|
16
|
-
set_mock_value(motor.velocity, 3)
|
|
17
|
-
set_mock_value(motor.max_velocity, 5)
|
|
18
|
-
return callback_on_mock_put(
|
|
19
|
-
motor.user_setpoint,
|
|
20
|
-
lambda pos, *args, **kwargs: set_mock_value(motor.user_readback, pos),
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def patch_all_motors(parent_device: Device):
|
|
25
|
-
motors = []
|
|
26
|
-
|
|
27
|
-
def recursively_find_motors(device: Device):
|
|
28
|
-
for _, child_device in device.children():
|
|
29
|
-
if isinstance(child_device, Motor):
|
|
30
|
-
motors.append(child_device)
|
|
31
|
-
recursively_find_motors(child_device)
|
|
32
|
-
|
|
33
|
-
recursively_find_motors(parent_device)
|
|
34
|
-
motor_patch_stack = ExitStack()
|
|
35
|
-
for motor in motors:
|
|
36
|
-
motor_patch_stack.enter_context(patch_motor(motor))
|
|
37
|
-
return motor_patch_stack
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|