dls-dodal 1.35.0__py3-none-any.whl → 1.36.1a0__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.35.0.dist-info → dls_dodal-1.36.1a0.dist-info}/METADATA +33 -31
- {dls_dodal-1.35.0.dist-info → dls_dodal-1.36.1a0.dist-info}/RECORD +46 -37
- {dls_dodal-1.35.0.dist-info → dls_dodal-1.36.1a0.dist-info}/WHEEL +1 -1
- dodal/_version.py +2 -2
- dodal/beamlines/b01_1.py +16 -31
- dodal/beamlines/i22.py +124 -265
- dodal/beamlines/i24.py +56 -7
- dodal/beamlines/p38.py +16 -1
- dodal/beamlines/p99.py +22 -53
- dodal/beamlines/training_rig.py +16 -26
- dodal/cli.py +54 -8
- dodal/common/beamlines/beamline_utils.py +32 -2
- dodal/common/beamlines/device_helpers.py +2 -0
- dodal/devices/aperture.py +7 -0
- dodal/devices/aperturescatterguard.py +195 -79
- dodal/devices/dcm.py +5 -4
- dodal/devices/eiger.py +88 -49
- dodal/devices/fast_grid_scan.py +21 -46
- dodal/devices/focusing_mirror.py +8 -3
- dodal/devices/i24/beam_center.py +12 -0
- dodal/devices/i24/focus_mirrors.py +60 -0
- dodal/devices/i24/pilatus_metadata.py +44 -0
- dodal/devices/linkam3.py +1 -1
- dodal/devices/motors.py +14 -10
- dodal/devices/oav/oav_detector.py +2 -2
- dodal/devices/oav/pin_image_recognition/__init__.py +4 -5
- dodal/devices/oav/utils.py +1 -0
- dodal/devices/p99/sample_stage.py +12 -16
- dodal/devices/pressure_jump_cell.py +299 -0
- dodal/devices/robot.py +1 -1
- dodal/devices/tetramm.py +1 -1
- dodal/devices/undulator.py +4 -1
- dodal/devices/undulator_dcm.py +3 -19
- dodal/devices/zocalo/zocalo_results.py +7 -7
- dodal/plan_stubs/__init__.py +0 -0
- dodal/{plans/data_session_metadata.py → plan_stubs/data_session.py} +2 -2
- dodal/{plans/motor_util_plans.py → plan_stubs/motor_utils.py} +2 -2
- dodal/plan_stubs/wrapped.py +150 -0
- dodal/plans/__init__.py +4 -0
- dodal/plans/scanspec.py +66 -0
- dodal/plans/wrapped.py +57 -0
- dodal/utils.py +151 -2
- {dls_dodal-1.35.0.dist-info → dls_dodal-1.36.1a0.dist-info}/LICENSE +0 -0
- {dls_dodal-1.35.0.dist-info → dls_dodal-1.36.1a0.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.35.0.dist-info → dls_dodal-1.36.1a0.dist-info}/top_level.txt +0 -0
- /dodal/{plans → plan_stubs}/check_topup.py +0 -0
|
@@ -40,15 +40,15 @@ class ZoomController(StandardReadable):
|
|
|
40
40
|
"""
|
|
41
41
|
|
|
42
42
|
def __init__(self, prefix: str, name: str = "") -> None:
|
|
43
|
-
super().__init__(name=name)
|
|
44
43
|
self.percentage = epics_signal_rw(float, f"{prefix}ZOOMPOSCMD")
|
|
45
44
|
|
|
46
45
|
# Level is the string description of the zoom level e.g. "1.0x" or "1.0"
|
|
47
46
|
self.level = epics_signal_rw(str, f"{prefix}MP:SELECT")
|
|
47
|
+
super().__init__(name=name)
|
|
48
48
|
|
|
49
49
|
async def _get_allowed_zoom_levels(self) -> list:
|
|
50
50
|
zoom_levels = await self.level.describe()
|
|
51
|
-
return zoom_levels[
|
|
51
|
+
return zoom_levels[self.level.name]["choices"] # type: ignore
|
|
52
52
|
|
|
53
53
|
@AsyncStatus.wrap
|
|
54
54
|
async def set(self, level_to_set: str):
|
|
@@ -2,7 +2,6 @@ import asyncio
|
|
|
2
2
|
import time
|
|
3
3
|
|
|
4
4
|
import numpy as np
|
|
5
|
-
from numpy.typing import NDArray
|
|
6
5
|
from ophyd_async.core import (
|
|
7
6
|
Array1D,
|
|
8
7
|
AsyncStatus,
|
|
@@ -57,12 +56,12 @@ class PinTipDetection(StandardReadable):
|
|
|
57
56
|
Tip, name="triggered_tip"
|
|
58
57
|
)
|
|
59
58
|
self.triggered_top_edge, self._top_edge_setter = soft_signal_r_and_setter(
|
|
60
|
-
|
|
59
|
+
Array1D[np.int32], name="triggered_top_edge"
|
|
61
60
|
)
|
|
62
61
|
self.triggered_bottom_edge, self._bottom_edge_setter = soft_signal_r_and_setter(
|
|
63
|
-
|
|
62
|
+
Array1D[np.int32], name="triggered_bottom_edge"
|
|
64
63
|
)
|
|
65
|
-
self.array_data = epics_signal_r(
|
|
64
|
+
self.array_data = epics_signal_r(Array1D[np.uint8], f"pva://{prefix}PVA:ARRAY")
|
|
66
65
|
|
|
67
66
|
# Soft parameters for pin-tip detection.
|
|
68
67
|
self.preprocess_operation = soft_signal_rw(int, 10, name="preprocess")
|
|
@@ -101,7 +100,7 @@ class PinTipDetection(StandardReadable):
|
|
|
101
100
|
self._bottom_edge_setter(results.edge_bottom)
|
|
102
101
|
|
|
103
102
|
async def _get_tip_and_edge_data(
|
|
104
|
-
self, array_data:
|
|
103
|
+
self, array_data: Array1D[np.uint8]
|
|
105
104
|
) -> SampleLocation:
|
|
106
105
|
"""
|
|
107
106
|
Gets the location of the pin tip and the top and bottom edges.
|
dodal/devices/oav/utils.py
CHANGED
|
@@ -87,6 +87,7 @@ def calculate_x_y_z_of_pixel(
|
|
|
87
87
|
beam_centre: tuple[int, int],
|
|
88
88
|
microns_per_pixel: tuple[float, float],
|
|
89
89
|
) -> np.ndarray:
|
|
90
|
+
"""Get the x, y, z position of a pixel in mm"""
|
|
90
91
|
beam_distance_px: Pixel = calculate_beam_distance(beam_centre, *pixel)
|
|
91
92
|
|
|
92
93
|
return current_x_y_z + camera_coordinates_to_xyz(
|
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
from ophyd_async.core import
|
|
2
|
-
from ophyd_async.epics.core import epics_signal_rw
|
|
1
|
+
from ophyd_async.core import StandardReadable, SubsetEnum
|
|
2
|
+
from ophyd_async.epics.core import epics_signal_rw, epics_signal_rw_rbv
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
class SampleAngleStage(
|
|
6
|
-
def __init__(self, prefix: str, name: str):
|
|
7
|
-
self.
|
|
8
|
-
float, prefix + "WRITETHETA
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
float, prefix + "WRITEROLL:RBV", prefix + "WRITEROLL"
|
|
12
|
-
)
|
|
13
|
-
self.pitch = epics_signal_rw(
|
|
14
|
-
float, prefix + "WRITEPITCH:RBV", prefix + "WRITEPITCH"
|
|
15
|
-
)
|
|
5
|
+
class SampleAngleStage(StandardReadable):
|
|
6
|
+
def __init__(self, prefix: str, name: str = ""):
|
|
7
|
+
with self.add_children_as_readables():
|
|
8
|
+
self.theta = epics_signal_rw_rbv(float, prefix + "WRITETHETA", ":RBV")
|
|
9
|
+
self.roll = epics_signal_rw_rbv(float, prefix + "WRITEROLL", ":RBV")
|
|
10
|
+
self.pitch = epics_signal_rw_rbv(float, prefix + "WRITEPITCH", ":RBV")
|
|
16
11
|
super().__init__(name=name)
|
|
17
12
|
|
|
18
13
|
|
|
@@ -35,7 +30,8 @@ class p99StageSelections(SubsetEnum):
|
|
|
35
30
|
User = "User"
|
|
36
31
|
|
|
37
32
|
|
|
38
|
-
class FilterMotor(
|
|
39
|
-
def __init__(self, prefix: str, name: str):
|
|
40
|
-
self.
|
|
33
|
+
class FilterMotor(StandardReadable):
|
|
34
|
+
def __init__(self, prefix: str, name: str = ""):
|
|
35
|
+
with self.add_children_as_readables():
|
|
36
|
+
self.user_setpoint = epics_signal_rw(p99StageSelections, prefix)
|
|
41
37
|
super().__init__(name=name)
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
from bluesky.protocols import HasName, Movable
|
|
5
|
+
from ophyd_async.core import (
|
|
6
|
+
AsyncStatus,
|
|
7
|
+
DeviceVector,
|
|
8
|
+
SignalR,
|
|
9
|
+
StandardReadable,
|
|
10
|
+
StandardReadableFormat,
|
|
11
|
+
StrictEnum,
|
|
12
|
+
)
|
|
13
|
+
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
14
|
+
|
|
15
|
+
OPENSEQ_PULSE_LENGTH = 0.2
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class PumpState(StrictEnum):
|
|
19
|
+
MANUAL = "Manual"
|
|
20
|
+
AUTO_PRESSURE = "Auto Pressure"
|
|
21
|
+
AUTO_POSITION = "Auto Position"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class StopState(StrictEnum):
|
|
25
|
+
CONTINUE = "CONTINUE"
|
|
26
|
+
STOP = "STOP"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class FastValveControlRequest(StrictEnum):
|
|
30
|
+
OPEN = "Open"
|
|
31
|
+
CLOSE = "Close"
|
|
32
|
+
RESET = "Reset"
|
|
33
|
+
ARM = "Arm"
|
|
34
|
+
DISARM = "Disarm"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ValveControlRequest(StrictEnum):
|
|
38
|
+
OPEN = "Open"
|
|
39
|
+
CLOSE = "Close"
|
|
40
|
+
RESET = "Reset"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ValveOpenSeqRequest(StrictEnum):
|
|
44
|
+
INACTIVE = 0
|
|
45
|
+
OPEN_SEQ = 1
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class PumpMotorDirectionState(StrictEnum):
|
|
49
|
+
EMPTY = ""
|
|
50
|
+
FORWARD = "Forward"
|
|
51
|
+
REVERSE = "Reverse"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ValveState(StrictEnum):
|
|
55
|
+
FAULT = "Fault"
|
|
56
|
+
OPEN = "Open"
|
|
57
|
+
OPENING = "Opening"
|
|
58
|
+
CLOSED = "Closed"
|
|
59
|
+
CLOSING = "Closing"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class FastValveState(StrictEnum):
|
|
63
|
+
FAULT = "Fault"
|
|
64
|
+
OPEN = "Open"
|
|
65
|
+
OPEN_ARMED = "Open Armed"
|
|
66
|
+
CLOSED = "Closed"
|
|
67
|
+
CLOSED_ARMED = "Closed Armed"
|
|
68
|
+
NONE = "Unused"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class AllValvesControlState:
|
|
73
|
+
valve_1: ValveControlRequest | None = None
|
|
74
|
+
valve_3: ValveControlRequest | None = None
|
|
75
|
+
valve_5: FastValveControlRequest | None = None
|
|
76
|
+
valve_6: FastValveControlRequest | None = None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class AllValvesControl(StandardReadable, Movable):
|
|
80
|
+
"""
|
|
81
|
+
valves 2, 4, 7, 8 are not controlled by the IOC,
|
|
82
|
+
as they are under manual control.
|
|
83
|
+
fast_valves: tuple[int, ...] = (5, 6)
|
|
84
|
+
slow_valves: tuple[int, ...] = (1, 3)
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
def __init__(
|
|
88
|
+
self,
|
|
89
|
+
prefix: str,
|
|
90
|
+
name: str = "",
|
|
91
|
+
fast_valves: tuple[int, ...] = (5, 6),
|
|
92
|
+
slow_valves: tuple[int, ...] = (1, 3),
|
|
93
|
+
) -> None:
|
|
94
|
+
self.fast_valves = fast_valves
|
|
95
|
+
self.slow_valves = slow_valves
|
|
96
|
+
with self.add_children_as_readables():
|
|
97
|
+
self.valve_states: DeviceVector[SignalR[ValveState]] = DeviceVector(
|
|
98
|
+
{
|
|
99
|
+
i: epics_signal_r(ValveState, f"{prefix}V{i}:STA")
|
|
100
|
+
for i in self.slow_valves
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
self.fast_valve_states: DeviceVector[SignalR[FastValveState]] = (
|
|
104
|
+
DeviceVector(
|
|
105
|
+
{
|
|
106
|
+
i: epics_signal_r(FastValveState, f"{prefix}V{i}:STA")
|
|
107
|
+
for i in self.fast_valves
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
self.fast_valve_control: DeviceVector[FastValveControl] = DeviceVector(
|
|
113
|
+
{i: FastValveControl(f"{prefix}V{i}") for i in self.fast_valves}
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
self.valve_control: DeviceVector[ValveControl] = DeviceVector(
|
|
117
|
+
{i: ValveControl(f"{prefix}V{i}") for i in self.slow_valves}
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
super().__init__(name)
|
|
121
|
+
|
|
122
|
+
async def set_valve(
|
|
123
|
+
self,
|
|
124
|
+
valve: int,
|
|
125
|
+
value: ValveControlRequest | FastValveControlRequest,
|
|
126
|
+
):
|
|
127
|
+
if valve in self.slow_valves and (isinstance(value, ValveControlRequest)):
|
|
128
|
+
if value == ValveControlRequest.OPEN:
|
|
129
|
+
await self.valve_control[valve].set(ValveOpenSeqRequest.OPEN_SEQ)
|
|
130
|
+
await asyncio.sleep(OPENSEQ_PULSE_LENGTH)
|
|
131
|
+
await self.valve_control[valve].set(ValveOpenSeqRequest.INACTIVE)
|
|
132
|
+
else:
|
|
133
|
+
await self.valve_control[valve].set(value)
|
|
134
|
+
|
|
135
|
+
elif valve in self.fast_valves and (isinstance(value, FastValveControlRequest)):
|
|
136
|
+
if value == FastValveControlRequest.OPEN:
|
|
137
|
+
await self.fast_valve_control[valve].set(ValveOpenSeqRequest.OPEN_SEQ)
|
|
138
|
+
await asyncio.sleep(OPENSEQ_PULSE_LENGTH)
|
|
139
|
+
await self.fast_valve_control[valve].set(ValveOpenSeqRequest.INACTIVE)
|
|
140
|
+
else:
|
|
141
|
+
await self.fast_valve_control[valve].set(value)
|
|
142
|
+
|
|
143
|
+
@AsyncStatus.wrap
|
|
144
|
+
async def set(self, value: AllValvesControlState):
|
|
145
|
+
await asyncio.gather(
|
|
146
|
+
*(
|
|
147
|
+
self.set_valve(int(i[-1]), value)
|
|
148
|
+
for i, value in value.__dict__.items()
|
|
149
|
+
if value is not None
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class ValveControl(StandardReadable):
|
|
155
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
156
|
+
with self.add_children_as_readables():
|
|
157
|
+
self.close = epics_signal_rw(ValveControlRequest, prefix + ":CON")
|
|
158
|
+
self.open = epics_signal_rw(ValveOpenSeqRequest, prefix + ":OPENSEQ")
|
|
159
|
+
|
|
160
|
+
super().__init__(name)
|
|
161
|
+
|
|
162
|
+
def set(self, value: ValveControlRequest | ValveOpenSeqRequest) -> AsyncStatus:
|
|
163
|
+
set_status = None
|
|
164
|
+
|
|
165
|
+
if isinstance(value, ValveControlRequest):
|
|
166
|
+
set_status = self.close.set(value)
|
|
167
|
+
elif isinstance(value, ValveOpenSeqRequest):
|
|
168
|
+
set_status = self.open.set(value)
|
|
169
|
+
|
|
170
|
+
return set_status
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class FastValveControl(StandardReadable):
|
|
174
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
175
|
+
with self.add_children_as_readables():
|
|
176
|
+
self.close = epics_signal_rw(FastValveControlRequest, prefix + ":CON")
|
|
177
|
+
self.open = epics_signal_rw(ValveOpenSeqRequest, prefix + ":OPENSEQ")
|
|
178
|
+
|
|
179
|
+
super().__init__(name)
|
|
180
|
+
|
|
181
|
+
def set(self, value: FastValveControlRequest | ValveOpenSeqRequest) -> AsyncStatus:
|
|
182
|
+
set_status = None
|
|
183
|
+
|
|
184
|
+
if isinstance(value, FastValveControlRequest):
|
|
185
|
+
set_status = self.close.set(value)
|
|
186
|
+
elif isinstance(value, ValveOpenSeqRequest):
|
|
187
|
+
set_status = self.open.set(value)
|
|
188
|
+
|
|
189
|
+
return set_status
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class Pump(StandardReadable):
|
|
193
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
194
|
+
with self.add_children_as_readables():
|
|
195
|
+
self.pump_position = epics_signal_r(float, prefix + "POS")
|
|
196
|
+
self.pump_motor_direction = epics_signal_r(
|
|
197
|
+
PumpMotorDirectionState, prefix + "MTRDIR"
|
|
198
|
+
)
|
|
199
|
+
self.pump_speed = epics_signal_rw(
|
|
200
|
+
float, write_pv=prefix + "MSPEED", read_pv=prefix + "MSPEED_RBV"
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
|
|
204
|
+
self.pump_mode = epics_signal_rw(PumpState, prefix + "SP:AUTO")
|
|
205
|
+
|
|
206
|
+
super().__init__(name)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class PressureTransducer(StandardReadable):
|
|
210
|
+
"""
|
|
211
|
+
Pressure transducer for a high pressure X-ray cell.
|
|
212
|
+
This is the chamber and there are three of them.
|
|
213
|
+
1 is the start, 3 is where the sample is.
|
|
214
|
+
NOTE: the distinction between the adc prefix and the cell prefix is kept here.
|
|
215
|
+
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
def __init__(
|
|
219
|
+
self,
|
|
220
|
+
prefix: str,
|
|
221
|
+
cell_prefix: str,
|
|
222
|
+
number: int,
|
|
223
|
+
name: str = "",
|
|
224
|
+
full_different_prefix_adc: str = "",
|
|
225
|
+
) -> None:
|
|
226
|
+
final_prefix = f"{prefix}{cell_prefix}"
|
|
227
|
+
with self.add_children_as_readables():
|
|
228
|
+
self.omron_pressure = epics_signal_r(
|
|
229
|
+
float, f"{final_prefix}PP{number}:PRES"
|
|
230
|
+
)
|
|
231
|
+
self.omron_voltage = epics_signal_r(float, f"{final_prefix}PP{number}:RAW")
|
|
232
|
+
self.beckhoff_pressure = epics_signal_r(
|
|
233
|
+
float, f"{final_prefix}STATP{number}:MeanValue_RBV"
|
|
234
|
+
)
|
|
235
|
+
self.slow_beckhoff_voltage_readout = epics_signal_r(
|
|
236
|
+
float, f"{full_different_prefix_adc}CH1"
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
super().__init__(name)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class PressureJumpCellController(HasName):
|
|
243
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
244
|
+
self.stop = epics_signal_rw(StopState, f"{prefix}STOP")
|
|
245
|
+
|
|
246
|
+
self.target_pressure = epics_signal_rw(float, f"{prefix}TARGET")
|
|
247
|
+
self.timeout = epics_signal_rw(float, f"{prefix}TIMER.HIGH")
|
|
248
|
+
self.go = epics_signal_rw(bool, f"{prefix}GO")
|
|
249
|
+
|
|
250
|
+
## Jump logic ##
|
|
251
|
+
self.start_pressure = epics_signal_rw(float, f"{prefix}JUMPF")
|
|
252
|
+
self.target_pressure = epics_signal_rw(float, f"{prefix}JUMPT")
|
|
253
|
+
self.jump_ready = epics_signal_rw(bool, f"{prefix}SETJUMP")
|
|
254
|
+
|
|
255
|
+
self._name = name
|
|
256
|
+
super().__init__()
|
|
257
|
+
|
|
258
|
+
@property
|
|
259
|
+
def name(self):
|
|
260
|
+
return self._name
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
class PressureJumpCell(StandardReadable):
|
|
264
|
+
"""
|
|
265
|
+
High pressure X-ray cell, used to apply pressure or pressure jumps to a sample.
|
|
266
|
+
prefix: str
|
|
267
|
+
The prefix of beamline - SPECIAL - unusual that the cell prefix is computed separately
|
|
268
|
+
"""
|
|
269
|
+
|
|
270
|
+
def __init__(
|
|
271
|
+
self,
|
|
272
|
+
prefix: str,
|
|
273
|
+
cell_prefix: str = "-HPXC-01:",
|
|
274
|
+
adc_prefix: str = "-ADC",
|
|
275
|
+
name: str = "",
|
|
276
|
+
):
|
|
277
|
+
self.all_valves_control = AllValvesControl(f"{prefix}{cell_prefix}", name)
|
|
278
|
+
self.pump = Pump(f"{prefix}{cell_prefix}", name)
|
|
279
|
+
|
|
280
|
+
self.controller = PressureJumpCellController(
|
|
281
|
+
f"{prefix}{cell_prefix}CTRL:", name
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
with self.add_children_as_readables():
|
|
285
|
+
self.pressure_transducers: DeviceVector[PressureTransducer] = DeviceVector(
|
|
286
|
+
{
|
|
287
|
+
i: PressureTransducer(
|
|
288
|
+
prefix=prefix,
|
|
289
|
+
number=i,
|
|
290
|
+
cell_prefix=cell_prefix,
|
|
291
|
+
full_different_prefix_adc=f"{prefix}{adc_prefix}-0{i}:",
|
|
292
|
+
)
|
|
293
|
+
for i in [1, 2, 3]
|
|
294
|
+
}
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
self.cell_temperature = epics_signal_r(float, f"{prefix}{cell_prefix}TEMP")
|
|
298
|
+
|
|
299
|
+
super().__init__(name)
|
dodal/devices/robot.py
CHANGED
|
@@ -42,7 +42,7 @@ class BartRobot(StandardReadable, Movable):
|
|
|
42
42
|
"""The sample changing robot."""
|
|
43
43
|
|
|
44
44
|
# How long to wait for the robot if it is busy soaking/drying
|
|
45
|
-
NOT_BUSY_TIMEOUT = 60
|
|
45
|
+
NOT_BUSY_TIMEOUT = 5 * 60
|
|
46
46
|
# How long to wait for the actual load to happen
|
|
47
47
|
LOAD_TIMEOUT = 60
|
|
48
48
|
NO_PIN_ERROR_CODE = 25
|
dodal/devices/tetramm.py
CHANGED
dodal/devices/undulator.py
CHANGED
|
@@ -108,10 +108,13 @@ class Undulator(StandardReadable, Movable):
|
|
|
108
108
|
"""
|
|
109
109
|
await self._set_undulator_gap(value)
|
|
110
110
|
|
|
111
|
-
async def
|
|
111
|
+
async def raise_if_not_enabled(self):
|
|
112
112
|
access_level = await self.gap_access.get_value()
|
|
113
113
|
if access_level is UndulatorGapAccess.DISABLED and not TEST_MODE:
|
|
114
114
|
raise AccessError("Undulator gap access is disabled. Contact Control Room")
|
|
115
|
+
|
|
116
|
+
async def _set_undulator_gap(self, energy_kev: float) -> None:
|
|
117
|
+
await self.raise_if_not_enabled()
|
|
115
118
|
LOGGER.info(f"Setting undulator gap to {energy_kev:.2f} kev")
|
|
116
119
|
target_gap = await self._get_gap_to_match_energy(energy_kev)
|
|
117
120
|
|
dodal/devices/undulator_dcm.py
CHANGED
|
@@ -6,17 +6,10 @@ from ophyd_async.core import AsyncStatus, StandardReadable
|
|
|
6
6
|
from dodal.common.beamlines.beamline_parameters import get_beamline_parameters
|
|
7
7
|
|
|
8
8
|
from .dcm import DCM
|
|
9
|
-
from .undulator import Undulator
|
|
9
|
+
from .undulator import Undulator
|
|
10
10
|
|
|
11
11
|
ENERGY_TIMEOUT_S: float = 30.0
|
|
12
12
|
|
|
13
|
-
# Enable to allow testing when the beamline is down, do not change in production!
|
|
14
|
-
TEST_MODE = False
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class AccessError(Exception):
|
|
18
|
-
pass
|
|
19
|
-
|
|
20
13
|
|
|
21
14
|
class UndulatorDCM(StandardReadable, Movable):
|
|
22
15
|
"""
|
|
@@ -61,17 +54,8 @@ class UndulatorDCM(StandardReadable, Movable):
|
|
|
61
54
|
|
|
62
55
|
@AsyncStatus.wrap
|
|
63
56
|
async def set(self, value: float):
|
|
57
|
+
await self.undulator.raise_if_not_enabled()
|
|
64
58
|
await asyncio.gather(
|
|
65
|
-
self.
|
|
59
|
+
self.dcm.energy_in_kev.set(value, timeout=ENERGY_TIMEOUT_S),
|
|
66
60
|
self.undulator.set(value),
|
|
67
61
|
)
|
|
68
|
-
|
|
69
|
-
async def _set_dcm_energy(self, energy_kev: float) -> None:
|
|
70
|
-
access_level = await self.undulator.gap_access.get_value()
|
|
71
|
-
if access_level is UndulatorGapAccess.DISABLED and not TEST_MODE:
|
|
72
|
-
raise AccessError("Undulator gap access is disabled. Contact Control Room")
|
|
73
|
-
|
|
74
|
-
await self.dcm.energy_in_kev.set(
|
|
75
|
-
energy_kev,
|
|
76
|
-
timeout=ENERGY_TIMEOUT_S,
|
|
77
|
-
)
|
|
@@ -12,8 +12,8 @@ import workflows.transport
|
|
|
12
12
|
from bluesky.protocols import Triggerable
|
|
13
13
|
from bluesky.utils import Msg
|
|
14
14
|
from deepdiff import DeepDiff
|
|
15
|
-
from numpy.typing import NDArray
|
|
16
15
|
from ophyd_async.core import (
|
|
16
|
+
Array1D,
|
|
17
17
|
AsyncStatus,
|
|
18
18
|
StandardReadable,
|
|
19
19
|
StandardReadableFormat,
|
|
@@ -133,22 +133,22 @@ class ZocaloResults(StandardReadable, Triggerable):
|
|
|
133
133
|
self.use_cpu_and_gpu = use_cpu_and_gpu
|
|
134
134
|
|
|
135
135
|
self.centre_of_mass, self._com_setter = soft_signal_r_and_setter(
|
|
136
|
-
|
|
136
|
+
Array1D[np.uint64], name="centre_of_mass"
|
|
137
137
|
)
|
|
138
138
|
self.bounding_box, self._bounding_box_setter = soft_signal_r_and_setter(
|
|
139
|
-
|
|
139
|
+
Array1D[np.uint64], name="bounding_box"
|
|
140
140
|
)
|
|
141
141
|
self.max_voxel, self._max_voxel_setter = soft_signal_r_and_setter(
|
|
142
|
-
|
|
142
|
+
Array1D[np.uint64], name="max_voxel"
|
|
143
143
|
)
|
|
144
144
|
self.max_count, self._max_count_setter = soft_signal_r_and_setter(
|
|
145
|
-
|
|
145
|
+
Array1D[np.uint64], name="max_count"
|
|
146
146
|
)
|
|
147
147
|
self.n_voxels, self._n_voxels_setter = soft_signal_r_and_setter(
|
|
148
|
-
|
|
148
|
+
Array1D[np.uint64], name="n_voxels"
|
|
149
149
|
)
|
|
150
150
|
self.total_count, self._total_count_setter = soft_signal_r_and_setter(
|
|
151
|
-
|
|
151
|
+
Array1D[np.uint64], name="total_count"
|
|
152
152
|
)
|
|
153
153
|
self.ispyb_dcid, self._ispyb_dcid_setter = soft_signal_r_and_setter(
|
|
154
154
|
int, name="ispyb_dcid"
|
|
File without changes
|
|
@@ -2,7 +2,7 @@ from bluesky import plan_stubs as bps
|
|
|
2
2
|
from bluesky import preprocessors as bpp
|
|
3
3
|
from bluesky.utils import MsgGenerator, make_decorator
|
|
4
4
|
|
|
5
|
-
from dodal.common.beamlines import
|
|
5
|
+
from dodal.common.beamlines.beamline_utils import get_path_provider
|
|
6
6
|
from dodal.common.types import UpdatingPathProvider
|
|
7
7
|
|
|
8
8
|
DATA_SESSION = "data_session"
|
|
@@ -31,7 +31,7 @@ def attach_data_session_metadata_wrapper(
|
|
|
31
31
|
Iterator[Msg]: Plan messages
|
|
32
32
|
"""
|
|
33
33
|
if provider is None:
|
|
34
|
-
provider =
|
|
34
|
+
provider = get_path_provider()
|
|
35
35
|
yield from bps.wait_for([provider.update])
|
|
36
36
|
ress = yield from bps.wait_for([provider.data_session])
|
|
37
37
|
data_session = ress[0].result()
|
|
@@ -23,7 +23,7 @@ class MoveTooLarge(Exception):
|
|
|
23
23
|
super().__init__(*args)
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
def
|
|
26
|
+
def check_and_cache_values(
|
|
27
27
|
devices_and_positions: dict[MovableReadableDevice, float],
|
|
28
28
|
smallest_move: float,
|
|
29
29
|
maximum_move: float,
|
|
@@ -89,7 +89,7 @@ def move_and_reset_wrapper(
|
|
|
89
89
|
on. If false it is left up to the caller to wait on
|
|
90
90
|
them. Defaults to True.
|
|
91
91
|
"""
|
|
92
|
-
initial_positions = yield from
|
|
92
|
+
initial_positions = yield from check_and_cache_values(
|
|
93
93
|
device_and_positions, smallest_move, maximum_move
|
|
94
94
|
)
|
|
95
95
|
|