dls-dodal 1.46.0__py3-none-any.whl → 1.47.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.46.0.dist-info → dls_dodal-1.47.0.dist-info}/METADATA +1 -1
- {dls_dodal-1.46.0.dist-info → dls_dodal-1.47.0.dist-info}/RECORD +62 -51
- {dls_dodal-1.46.0.dist-info → dls_dodal-1.47.0.dist-info}/WHEEL +1 -1
- dodal/_version.py +2 -2
- dodal/beamlines/__init__.py +0 -1
- dodal/beamlines/b07.py +2 -6
- dodal/beamlines/b07_1.py +1 -3
- dodal/beamlines/i03.py +12 -15
- dodal/beamlines/i04.py +48 -16
- dodal/beamlines/i09.py +1 -3
- dodal/beamlines/i09_1.py +1 -3
- dodal/beamlines/i23.py +17 -1
- dodal/beamlines/p38.py +1 -1
- dodal/beamlines/p60.py +2 -6
- dodal/beamlines/p99.py +48 -4
- dodal/common/beamlines/beamline_parameters.py +1 -2
- dodal/common/data_util.py +4 -0
- dodal/devices/aperturescatterguard.py +47 -47
- dodal/devices/current_amplifiers/struck_scaler_counter.py +1 -1
- dodal/devices/diamond_filter.py +5 -17
- dodal/devices/eiger.py +1 -1
- dodal/devices/electron_analyser/__init__.py +8 -0
- dodal/devices/electron_analyser/abstract/__init__.py +28 -0
- dodal/devices/electron_analyser/abstract/base_detector.py +210 -0
- dodal/devices/electron_analyser/abstract/base_driver_io.py +121 -0
- dodal/devices/electron_analyser/{abstract_region.py → abstract/base_region.py} +2 -9
- dodal/devices/electron_analyser/specs/__init__.py +11 -0
- dodal/devices/electron_analyser/specs/detector.py +29 -0
- dodal/devices/electron_analyser/specs/driver_io.py +64 -0
- dodal/devices/electron_analyser/{specs_region.py → specs/region.py} +1 -1
- dodal/devices/electron_analyser/types.py +6 -0
- dodal/devices/electron_analyser/util.py +13 -0
- dodal/devices/electron_analyser/vgscienta/__init__.py +12 -0
- dodal/devices/electron_analyser/vgscienta/detector.py +36 -0
- dodal/devices/electron_analyser/vgscienta/driver_io.py +39 -0
- dodal/devices/electron_analyser/{vgscienta_region.py → vgscienta/region.py} +1 -1
- dodal/devices/fast_grid_scan.py +7 -9
- dodal/devices/i03/__init__.py +3 -0
- dodal/devices/i04/__init__.py +3 -0
- dodal/devices/i04/constants.py +9 -0
- dodal/devices/i04/murko_results.py +195 -0
- dodal/devices/i10/diagnostics.py +9 -61
- dodal/devices/i24/focus_mirrors.py +9 -13
- dodal/devices/i24/pilatus_metadata.py +9 -9
- dodal/devices/i24/pmac.py +19 -14
- dodal/devices/{i03 → mx_phase1}/beamstop.py +6 -12
- dodal/devices/oav/oav_calculations.py +2 -2
- dodal/devices/oav/oav_detector.py +32 -22
- dodal/devices/oav/utils.py +2 -2
- dodal/devices/p99/andor2_point.py +41 -0
- dodal/devices/positioner.py +49 -0
- dodal/devices/tetramm.py +5 -2
- dodal/devices/util/adjuster_plans.py +1 -1
- dodal/devices/zebra/zebra_constants_mapping.py +1 -1
- dodal/devices/zocalo/__init__.py +0 -3
- dodal/devices/zocalo/zocalo_results.py +6 -32
- dodal/log.py +14 -14
- dodal/plan_stubs/electron_analyser/__init__.py +3 -0
- dodal/plan_stubs/electron_analyser/{configure_controller.py → configure_driver.py} +30 -18
- dodal/common/signal_utils.py +0 -88
- dodal/devices/electron_analyser/abstract_analyser_io.py +0 -47
- dodal/devices/electron_analyser/specs_analyser_io.py +0 -19
- dodal/devices/electron_analyser/vgscienta_analyser_io.py +0 -26
- dodal/devices/logging_ophyd_device.py +0 -17
- {dls_dodal-1.46.0.dist-info → dls_dodal-1.47.0.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.46.0.dist-info → dls_dodal-1.47.0.dist-info}/licenses/LICENSE +0 -0
- {dls_dodal-1.46.0.dist-info → dls_dodal-1.47.0.dist-info}/top_level.txt +0 -0
dodal/devices/i10/diagnostics.py
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
from bluesky.protocols import Movable
|
|
2
1
|
from ophyd_async.core import (
|
|
3
|
-
AsyncStatus,
|
|
4
2
|
Device,
|
|
5
3
|
StandardReadable,
|
|
6
4
|
StrictEnum,
|
|
@@ -13,7 +11,6 @@ from ophyd_async.epics.core import (
|
|
|
13
11
|
epics_signal_r,
|
|
14
12
|
epics_signal_rw,
|
|
15
13
|
)
|
|
16
|
-
from ophyd_async.epics.motor import Motor
|
|
17
14
|
|
|
18
15
|
from dodal.devices.current_amplifiers import (
|
|
19
16
|
CurrentAmpDet,
|
|
@@ -23,6 +20,7 @@ from dodal.devices.current_amplifiers import (
|
|
|
23
20
|
FemtoDDPCA,
|
|
24
21
|
StruckScaler,
|
|
25
22
|
)
|
|
23
|
+
from dodal.devices.positioner import create_positioner
|
|
26
24
|
|
|
27
25
|
|
|
28
26
|
class D3Position(StrictEnum):
|
|
@@ -70,36 +68,6 @@ class InOutReadBackTable(StrictEnum):
|
|
|
70
68
|
OUT_OF_BEAM = "Out of Beam"
|
|
71
69
|
|
|
72
70
|
|
|
73
|
-
class Positioner(StandardReadable, Movable):
|
|
74
|
-
"""1D stage with a enum table to select positions."""
|
|
75
|
-
|
|
76
|
-
def __init__(
|
|
77
|
-
self,
|
|
78
|
-
prefix: str,
|
|
79
|
-
positioner_enum: type[StrictEnum],
|
|
80
|
-
positioner_suffix: str = "",
|
|
81
|
-
Positioner_pv_suffix: str = ":MP:SELECT",
|
|
82
|
-
name: str = "",
|
|
83
|
-
) -> None:
|
|
84
|
-
self._stage_motion = Motor(prefix=prefix + positioner_suffix)
|
|
85
|
-
with self.add_children_as_readables(Format.CONFIG_SIGNAL):
|
|
86
|
-
self.stage_position = epics_signal_rw(
|
|
87
|
-
positioner_enum,
|
|
88
|
-
read_pv=prefix + positioner_suffix + Positioner_pv_suffix,
|
|
89
|
-
)
|
|
90
|
-
super().__init__(name=name)
|
|
91
|
-
self.positioner_enum = positioner_enum
|
|
92
|
-
|
|
93
|
-
@AsyncStatus.wrap
|
|
94
|
-
async def set(self, value: StrictEnum) -> None:
|
|
95
|
-
if value in self.positioner_enum:
|
|
96
|
-
await self.stage_position.set(value=value)
|
|
97
|
-
else:
|
|
98
|
-
raise ValueError(
|
|
99
|
-
f"{value} is not an allow position. Position must be: {self.positioner_enum}"
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
|
|
103
71
|
class I10PneumaticStage(StandardReadable):
|
|
104
72
|
"""Pneumatic stage only has two real positions in or out.
|
|
105
73
|
Use for fluorescent screen which can be insert into the x-ray beam.
|
|
@@ -154,16 +122,13 @@ class FullDiagnostic(Device):
|
|
|
154
122
|
self,
|
|
155
123
|
prefix: str,
|
|
156
124
|
positioner_enum: type[StrictEnum],
|
|
157
|
-
positioner_suffix: str
|
|
158
|
-
Positioner_pv_suffix: str = ":MP:SELECT",
|
|
125
|
+
positioner_suffix: str,
|
|
159
126
|
cam_infix: str = "DCAM:",
|
|
160
127
|
name: str = "",
|
|
161
128
|
) -> None:
|
|
162
|
-
self.positioner =
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
positioner_suffix=positioner_suffix,
|
|
166
|
-
Positioner_pv_suffix=Positioner_pv_suffix,
|
|
129
|
+
self.positioner = create_positioner(
|
|
130
|
+
positioner_enum,
|
|
131
|
+
prefix + positioner_suffix,
|
|
167
132
|
)
|
|
168
133
|
self.screen = ScreenCam(
|
|
169
134
|
prefix,
|
|
@@ -185,28 +150,11 @@ class I10Diagnostic(Device):
|
|
|
185
150
|
positioner_suffix="DET:X",
|
|
186
151
|
)
|
|
187
152
|
self.d4 = ScreenCam(prefix=prefix + "PHDGN-04:")
|
|
188
|
-
self.d5 =
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
self.d5A = Positioner(
|
|
195
|
-
prefix=prefix + "PHDGN-06:",
|
|
196
|
-
positioner_enum=D5APosition,
|
|
197
|
-
positioner_suffix="DET:X",
|
|
198
|
-
)
|
|
153
|
+
self.d5 = create_positioner(D5Position, f"{prefix}IONC-01:Y")
|
|
154
|
+
self.d5A = create_positioner(D5APosition, f"{prefix}PHDGN-06:DET:X")
|
|
155
|
+
self.d6 = FullDiagnostic(f"{prefix}PHDGN-05:", D6Position, "DET:X")
|
|
156
|
+
self.d7 = create_positioner(D7Position, f"{prefix}PHDGN-07:Y")
|
|
199
157
|
|
|
200
|
-
self.d6 = FullDiagnostic(
|
|
201
|
-
prefix=prefix + "PHDGN-05:",
|
|
202
|
-
positioner_enum=D6Position,
|
|
203
|
-
positioner_suffix="DET:X",
|
|
204
|
-
)
|
|
205
|
-
self.d7 = Positioner(
|
|
206
|
-
prefix=prefix + "PHDGN-07:",
|
|
207
|
-
positioner_enum=D7Position,
|
|
208
|
-
positioner_suffix="Y",
|
|
209
|
-
)
|
|
210
158
|
super().__init__(name)
|
|
211
159
|
|
|
212
160
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
from ophyd_async.core import StandardReadable, StrictEnum
|
|
1
|
+
from ophyd_async.core import StandardReadable, StrictEnum, derived_signal_r
|
|
2
2
|
from ophyd_async.epics.core import epics_signal_rw
|
|
3
3
|
|
|
4
|
-
from dodal.common.signal_utils import create_r_hardware_backed_soft_signal
|
|
5
|
-
|
|
6
4
|
|
|
7
5
|
class HFocusMode(StrictEnum):
|
|
8
6
|
FOCUS_10 = "HMFMfocus10"
|
|
@@ -40,21 +38,19 @@ class FocusMirrorsMode(StandardReadable):
|
|
|
40
38
|
self.vertical = epics_signal_rw(VFocusMode, prefix + "G0:TARGETAPPLY")
|
|
41
39
|
|
|
42
40
|
with self.add_children_as_readables():
|
|
43
|
-
self.beam_size_x =
|
|
44
|
-
|
|
41
|
+
self.beam_size_x = derived_signal_r(
|
|
42
|
+
self._get_beam_size_x, horizontal=self.horizontal, derived_units="um"
|
|
45
43
|
)
|
|
46
|
-
self.beam_size_y =
|
|
47
|
-
|
|
44
|
+
self.beam_size_y = derived_signal_r(
|
|
45
|
+
self._get_beam_size_y, vertical=self.vertical, derived_units="um"
|
|
48
46
|
)
|
|
49
47
|
|
|
50
48
|
super().__init__(name)
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
beam_x = BEAM_SIZES[h_mode.removeprefix("HMFM")][0]
|
|
50
|
+
def _get_beam_size_x(self, horizontal: HFocusMode) -> int:
|
|
51
|
+
beam_x = BEAM_SIZES[horizontal.removeprefix("HMFM")][0]
|
|
55
52
|
return beam_x
|
|
56
53
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
beam_y = BEAM_SIZES[v_mode.removeprefix("VMFM")][1]
|
|
54
|
+
def _get_beam_size_y(self, vertical: VFocusMode) -> int:
|
|
55
|
+
beam_y = BEAM_SIZES[vertical.removeprefix("VMFM")][1]
|
|
60
56
|
return beam_y
|
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
|
|
5
|
-
from ophyd_async.core import StandardReadable
|
|
5
|
+
from ophyd_async.core import StandardReadable, derived_signal_r
|
|
6
6
|
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
|
|
7
7
|
|
|
8
|
-
from dodal.common.signal_utils import create_r_hardware_backed_soft_signal
|
|
9
|
-
|
|
10
8
|
|
|
11
9
|
class PilatusMetadata(StandardReadable):
|
|
12
10
|
def __init__(self, prefix: str, name: str = "") -> None:
|
|
@@ -14,21 +12,23 @@ class PilatusMetadata(StandardReadable):
|
|
|
14
12
|
self.template = epics_signal_r(str, prefix + "cam1:FileTemplate_RBV")
|
|
15
13
|
self.filenumber = epics_signal_r(int, prefix + "cam1:FileNumber_RBV")
|
|
16
14
|
with self.add_children_as_readables():
|
|
17
|
-
self.filename_template =
|
|
18
|
-
|
|
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,
|
|
19
20
|
)
|
|
20
21
|
super().__init__(name)
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
def _get_full_filename_template(
|
|
24
|
+
self, filename: str, filename_template: str, file_number: int
|
|
25
|
+
) -> str:
|
|
23
26
|
"""
|
|
24
27
|
Get the template file path by querying the detector PVs.
|
|
25
28
|
Mirror the construction that the PPU does.
|
|
26
29
|
|
|
27
30
|
Returns: A template string, with the image numbers replaced with '#'
|
|
28
31
|
"""
|
|
29
|
-
filename = await self.filename.get_value()
|
|
30
|
-
filename_template = await self.template.get_value()
|
|
31
|
-
file_number = await self.filenumber.get_value()
|
|
32
32
|
# Exploit fact that passing negative numbers will put the - before the 0's
|
|
33
33
|
expected_filename = str(
|
|
34
34
|
filename_template % (filename, f"{file_number:05d}_", -9)
|
dodal/devices/i24/pmac.py
CHANGED
|
@@ -10,6 +10,7 @@ from ophyd_async.core import (
|
|
|
10
10
|
SignalR,
|
|
11
11
|
SignalRW,
|
|
12
12
|
StandardReadable,
|
|
13
|
+
observe_signals_value,
|
|
13
14
|
soft_signal_rw,
|
|
14
15
|
wait_for_value,
|
|
15
16
|
)
|
|
@@ -120,15 +121,16 @@ class ProgramRunner(Device, Flyable):
|
|
|
120
121
|
self,
|
|
121
122
|
pmac_str_sig: SignalRW,
|
|
122
123
|
status_sig: SignalR,
|
|
124
|
+
counter_sig: SignalR,
|
|
123
125
|
prog_num_sig: SignalRW,
|
|
124
|
-
|
|
126
|
+
counter_time_sig: SignalRW,
|
|
125
127
|
name: str = "",
|
|
126
128
|
) -> None:
|
|
127
129
|
self._signal_ref = Reference(pmac_str_sig)
|
|
128
130
|
self._status_ref = Reference(status_sig)
|
|
131
|
+
self._counter_ref = Reference(counter_sig)
|
|
129
132
|
self._prog_num_ref = Reference(prog_num_sig)
|
|
130
|
-
|
|
131
|
-
self._collection_time_ref = Reference(collection_time_sig)
|
|
133
|
+
self._counter_time_ref = Reference(counter_time_sig)
|
|
132
134
|
|
|
133
135
|
super().__init__(name)
|
|
134
136
|
|
|
@@ -151,16 +153,18 @@ class ProgramRunner(Device, Flyable):
|
|
|
151
153
|
|
|
152
154
|
@AsyncStatus.wrap
|
|
153
155
|
async def complete(self):
|
|
154
|
-
"""Stop collecting when the scan status PV goes to 0
|
|
155
|
-
|
|
156
|
-
Args:
|
|
157
|
-
complete_time (float): total time required by the collection to \
|
|
158
|
-
finish correctly.
|
|
156
|
+
"""Stop collecting when the scan status PV goes to 0 or when counter PV hasn't \
|
|
157
|
+
updated for 30 seconds.
|
|
159
158
|
"""
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
self._status_ref(),
|
|
163
|
-
|
|
159
|
+
counter_time = await self._counter_time_ref().get_value()
|
|
160
|
+
async for signal, value in observe_signals_value(
|
|
161
|
+
self._status_ref(),
|
|
162
|
+
self._counter_ref(),
|
|
163
|
+
timeout=counter_time,
|
|
164
|
+
):
|
|
165
|
+
if signal is self._status_ref():
|
|
166
|
+
if value == ScanState.DONE:
|
|
167
|
+
break
|
|
164
168
|
|
|
165
169
|
|
|
166
170
|
class ProgramAbort(Triggerable):
|
|
@@ -217,13 +221,14 @@ class PMAC(StandardReadable):
|
|
|
217
221
|
# A couple of soft signals for running a collection: program number to send to
|
|
218
222
|
# the PMAC_STRING and expected collection time.
|
|
219
223
|
self.program_number = soft_signal_rw(int)
|
|
220
|
-
self.
|
|
224
|
+
self.counter_time = soft_signal_rw(float, initial_value=30.0, units="s")
|
|
221
225
|
|
|
222
226
|
self.run_program = ProgramRunner(
|
|
223
227
|
self.pmac_string,
|
|
224
228
|
self.scanstatus,
|
|
229
|
+
self.counter,
|
|
225
230
|
self.program_number,
|
|
226
|
-
self.
|
|
231
|
+
self.counter_time,
|
|
227
232
|
)
|
|
228
233
|
self.abort_program = ProgramAbort(self.pmac_string, self.scanstatus)
|
|
229
234
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
from asyncio import gather
|
|
2
1
|
from math import isclose
|
|
3
2
|
|
|
4
|
-
from ophyd_async.core import StandardReadable, StrictEnum
|
|
3
|
+
from ophyd_async.core import StandardReadable, StrictEnum, derived_signal_r
|
|
5
4
|
from ophyd_async.epics.motor import Motor
|
|
6
5
|
|
|
7
6
|
from dodal.common.beamlines.beamline_parameters import GDABeamlineParameters
|
|
8
|
-
from dodal.common.signal_utils import create_r_hardware_backed_soft_signal
|
|
9
7
|
|
|
10
8
|
|
|
11
9
|
class BeamstopPositions(StrictEnum):
|
|
@@ -33,7 +31,7 @@ class BeamstopPositions(StrictEnum):
|
|
|
33
31
|
|
|
34
32
|
class Beamstop(StandardReadable):
|
|
35
33
|
"""
|
|
36
|
-
Beamstop for I03.
|
|
34
|
+
Beamstop for I03 and I04.
|
|
37
35
|
|
|
38
36
|
Attributes:
|
|
39
37
|
x: beamstop x position in mm
|
|
@@ -53,8 +51,8 @@ class Beamstop(StandardReadable):
|
|
|
53
51
|
self.x_mm = Motor(prefix + "X")
|
|
54
52
|
self.y_mm = Motor(prefix + "Y")
|
|
55
53
|
self.z_mm = Motor(prefix + "Z")
|
|
56
|
-
self.selected_pos =
|
|
57
|
-
|
|
54
|
+
self.selected_pos = derived_signal_r(
|
|
55
|
+
self._get_selected_position, x=self.x_mm, y=self.y_mm, z=self.z_mm
|
|
58
56
|
)
|
|
59
57
|
|
|
60
58
|
self._in_beam_xyz_mm = [
|
|
@@ -68,12 +66,8 @@ class Beamstop(StandardReadable):
|
|
|
68
66
|
|
|
69
67
|
super().__init__(name)
|
|
70
68
|
|
|
71
|
-
|
|
72
|
-
current_pos =
|
|
73
|
-
self.x_mm.user_readback.get_value(),
|
|
74
|
-
self.y_mm.user_readback.get_value(),
|
|
75
|
-
self.z_mm.user_readback.get_value(),
|
|
76
|
-
)
|
|
69
|
+
def _get_selected_position(self, x: float, y: float, z: float) -> BeamstopPositions:
|
|
70
|
+
current_pos = [x, y, z]
|
|
77
71
|
if all(
|
|
78
72
|
isclose(axis_pos, axis_in_beam, abs_tol=axis_tolerance)
|
|
79
73
|
for axis_pos, axis_in_beam, axis_tolerance in zip(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
def
|
|
4
|
+
def camera_coordinates_to_xyz_mm(
|
|
5
5
|
horizontal: float,
|
|
6
6
|
vertical: float,
|
|
7
7
|
omega: float,
|
|
@@ -9,7 +9,7 @@ def camera_coordinates_to_xyz(
|
|
|
9
9
|
microns_per_j_pixel: float,
|
|
10
10
|
) -> np.ndarray:
|
|
11
11
|
"""
|
|
12
|
-
Converts from (horizontal,vertical) pixel measurements from the OAV camera into to (x, y, z) motor coordinates in
|
|
12
|
+
Converts from (horizontal,vertical) pixel measurements from the OAV camera into to (x, y, z) motor coordinates in millimetres.
|
|
13
13
|
For an overview of the coordinate system for I03 see https://github.com/DiamondLightSource/hyperion/wiki/Gridscan-Coordinate-System.
|
|
14
14
|
|
|
15
15
|
Args:
|
|
@@ -6,10 +6,11 @@ from ophyd_async.core import (
|
|
|
6
6
|
AsyncStatus,
|
|
7
7
|
LazyMock,
|
|
8
8
|
StandardReadable,
|
|
9
|
+
derived_signal_r,
|
|
10
|
+
soft_signal_rw,
|
|
9
11
|
)
|
|
10
12
|
from ophyd_async.epics.core import epics_signal_rw
|
|
11
13
|
|
|
12
|
-
from dodal.common.signal_utils import create_r_hardware_backed_soft_signal
|
|
13
14
|
from dodal.devices.areadetector.plugins.CAM import Cam
|
|
14
15
|
from dodal.devices.oav.oav_parameters import DEFAULT_OAV_WINDOW, OAVConfig
|
|
15
16
|
from dodal.devices.oav.snapshots.snapshot import Snapshot
|
|
@@ -60,49 +61,58 @@ class OAV(StandardReadable):
|
|
|
60
61
|
self.zoom_controller = ZoomController(f"{_bl_prefix}-EA-OAV-01:FZOOM:", name)
|
|
61
62
|
|
|
62
63
|
self.cam = Cam(f"{prefix}CAM:", name=name)
|
|
63
|
-
|
|
64
64
|
with self.add_children_as_readables():
|
|
65
65
|
self.grid_snapshot = SnapshotWithGrid(f"{prefix}MJPG:", name)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
|
|
67
|
+
self.sizes = [self.grid_snapshot.x_size, self.grid_snapshot.y_size]
|
|
68
|
+
|
|
69
|
+
with self.add_children_as_readables():
|
|
70
|
+
self.microns_per_pixel_x = derived_signal_r(
|
|
71
|
+
self._get_microns_per_pixel,
|
|
72
|
+
zoom_level=self.zoom_controller.level,
|
|
73
|
+
size=self.sizes[Coords.X],
|
|
74
|
+
coord=soft_signal_rw(datatype=int, initial_value=Coords.X.value),
|
|
69
75
|
)
|
|
70
|
-
self.microns_per_pixel_y =
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
self.microns_per_pixel_y = derived_signal_r(
|
|
77
|
+
self._get_microns_per_pixel,
|
|
78
|
+
zoom_level=self.zoom_controller.level,
|
|
79
|
+
size=self.sizes[Coords.Y],
|
|
80
|
+
coord=soft_signal_rw(datatype=int, initial_value=Coords.Y.value),
|
|
73
81
|
)
|
|
74
|
-
self.beam_centre_i =
|
|
75
|
-
|
|
82
|
+
self.beam_centre_i = derived_signal_r(
|
|
83
|
+
self._get_beam_position,
|
|
84
|
+
zoom_level=self.zoom_controller.level,
|
|
85
|
+
size=self.sizes[Coords.X],
|
|
86
|
+
coord=soft_signal_rw(datatype=int, initial_value=Coords.X.value),
|
|
76
87
|
)
|
|
77
|
-
self.beam_centre_j =
|
|
78
|
-
|
|
88
|
+
self.beam_centre_j = derived_signal_r(
|
|
89
|
+
self._get_beam_position,
|
|
90
|
+
zoom_level=self.zoom_controller.level,
|
|
91
|
+
size=self.sizes[Coords.Y],
|
|
92
|
+
coord=soft_signal_rw(datatype=int, initial_value=Coords.Y.value),
|
|
79
93
|
)
|
|
80
94
|
self.snapshot = Snapshot(
|
|
81
95
|
f"{self._prefix}MJPG:",
|
|
82
96
|
self._name,
|
|
83
97
|
)
|
|
84
98
|
|
|
85
|
-
self.sizes = [self.grid_snapshot.x_size, self.grid_snapshot.y_size]
|
|
86
|
-
|
|
87
99
|
super().__init__(name)
|
|
88
100
|
|
|
89
|
-
|
|
90
|
-
_zoom = await self.zoom_controller.level.get_value()
|
|
101
|
+
def _read_current_zoom(self, _zoom: str) -> str:
|
|
91
102
|
return _get_correct_zoom_string(_zoom)
|
|
92
103
|
|
|
93
|
-
|
|
104
|
+
def _get_microns_per_pixel(self, zoom_level: str, size: int, coord: int) -> float:
|
|
94
105
|
"""Extracts the microns per x pixel and y pixel for a given zoom level."""
|
|
95
|
-
_zoom =
|
|
106
|
+
_zoom = self._read_current_zoom(zoom_level)
|
|
96
107
|
value = self.parameters[_zoom].microns_per_pixel[coord]
|
|
97
|
-
size = await self.sizes[coord].get_value()
|
|
98
108
|
return value * DEFAULT_OAV_WINDOW[coord] / size
|
|
99
109
|
|
|
100
|
-
|
|
110
|
+
def _get_beam_position(self, zoom_level: str, size: int, coord: int) -> int:
|
|
101
111
|
"""Extracts the beam location in pixels `xCentre` `yCentre`, for a requested \
|
|
102
112
|
zoom level. """
|
|
103
|
-
_zoom =
|
|
113
|
+
_zoom = self._read_current_zoom(zoom_level)
|
|
104
114
|
value = self.parameters[_zoom].crosshair[coord]
|
|
105
|
-
|
|
115
|
+
|
|
106
116
|
return int(value * size / DEFAULT_OAV_WINDOW[coord])
|
|
107
117
|
|
|
108
118
|
async def connect(
|
dodal/devices/oav/utils.py
CHANGED
|
@@ -7,7 +7,7 @@ from bluesky.utils import Msg
|
|
|
7
7
|
|
|
8
8
|
from dodal.devices.oav.oav_calculations import (
|
|
9
9
|
calculate_beam_distance,
|
|
10
|
-
|
|
10
|
+
camera_coordinates_to_xyz_mm,
|
|
11
11
|
)
|
|
12
12
|
from dodal.devices.oav.oav_detector import OAV
|
|
13
13
|
from dodal.devices.oav.pin_image_recognition import PinTipDetection
|
|
@@ -90,7 +90,7 @@ def calculate_x_y_z_of_pixel(
|
|
|
90
90
|
"""Get the x, y, z position of a pixel in mm"""
|
|
91
91
|
beam_distance_px: Pixel = calculate_beam_distance(beam_centre, *pixel)
|
|
92
92
|
|
|
93
|
-
return current_x_y_z +
|
|
93
|
+
return current_x_y_z + camera_coordinates_to_xyz_mm(
|
|
94
94
|
beam_distance_px[0],
|
|
95
95
|
beam_distance_px[1],
|
|
96
96
|
current_omega,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from ophyd_async.core import (
|
|
2
|
+
StandardReadableFormat,
|
|
3
|
+
)
|
|
4
|
+
from ophyd_async.epics.adandor import Andor2DriverIO
|
|
5
|
+
from ophyd_async.epics.adcore import NDPluginBaseIO, SingleTriggerDetector
|
|
6
|
+
from ophyd_async.epics.core import epics_signal_r
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Andor2Point(SingleTriggerDetector):
|
|
10
|
+
"""Using the andor2 as if it is a massive point detector, read the read uncached
|
|
11
|
+
value after a picture is taken."""
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
prefix: str,
|
|
16
|
+
drv_suffix: str,
|
|
17
|
+
read_uncached: dict[str, str],
|
|
18
|
+
name: str = "",
|
|
19
|
+
plugins: dict[str, NDPluginBaseIO] | None = None,
|
|
20
|
+
) -> None:
|
|
21
|
+
"""
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
prefix: str,
|
|
25
|
+
Beamline camera pv
|
|
26
|
+
drv_suffix : str,
|
|
27
|
+
Camera pv suffix
|
|
28
|
+
read_uncached: dict[str,str]
|
|
29
|
+
A dictionary contains the name and the pv suffix for the statistic plugin.
|
|
30
|
+
name: str:
|
|
31
|
+
Name of the device.
|
|
32
|
+
plugins:: Optional[dict[str, NDPluginBaseIO] | None
|
|
33
|
+
Dictionary containing plugin that are forward to the base class.
|
|
34
|
+
"""
|
|
35
|
+
with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
|
|
36
|
+
for k, v in read_uncached.items():
|
|
37
|
+
setattr(self, k, epics_signal_r(float, prefix + v))
|
|
38
|
+
|
|
39
|
+
super().__init__(
|
|
40
|
+
drv=Andor2DriverIO(prefix + drv_suffix), name=name, plugins=plugins
|
|
41
|
+
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from typing import TypeVar
|
|
2
|
+
|
|
3
|
+
from bluesky.protocols import Movable
|
|
4
|
+
from ophyd_async.core import (
|
|
5
|
+
AsyncStatus,
|
|
6
|
+
StandardReadable,
|
|
7
|
+
StrictEnum,
|
|
8
|
+
)
|
|
9
|
+
from ophyd_async.epics.core import (
|
|
10
|
+
epics_signal_rw,
|
|
11
|
+
)
|
|
12
|
+
from ophyd_async.epics.motor import Motor
|
|
13
|
+
|
|
14
|
+
T = TypeVar("T", bound=StrictEnum)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Positioner1D(StandardReadable, Movable[T]):
|
|
18
|
+
"""1D stage with a enum table to select positions.
|
|
19
|
+
|
|
20
|
+
Use this when writing a device with an EPICS positioner on a single axis.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
prefix: str,
|
|
26
|
+
datatype: type[StrictEnum],
|
|
27
|
+
positioner_pv_suffix: str = ":MP:SELECT",
|
|
28
|
+
name: str = "",
|
|
29
|
+
) -> None:
|
|
30
|
+
self._stage_motion = Motor(prefix=prefix)
|
|
31
|
+
with self.add_children_as_readables():
|
|
32
|
+
self.stage_position = epics_signal_rw(
|
|
33
|
+
datatype,
|
|
34
|
+
read_pv=prefix + positioner_pv_suffix,
|
|
35
|
+
)
|
|
36
|
+
super().__init__(name=name)
|
|
37
|
+
|
|
38
|
+
@AsyncStatus.wrap
|
|
39
|
+
async def set(self, value: T) -> None:
|
|
40
|
+
await self.stage_position.set(value=value)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def create_positioner(
|
|
44
|
+
datatype: type[T],
|
|
45
|
+
prefix,
|
|
46
|
+
positioner_pv_suffix: str = ":MP:SELECT",
|
|
47
|
+
name: str = "",
|
|
48
|
+
) -> Positioner1D[T]:
|
|
49
|
+
return Positioner1D[datatype](prefix, datatype, positioner_pv_suffix, name)
|
dodal/devices/tetramm.py
CHANGED
|
@@ -131,11 +131,14 @@ class TetrammController(DetectorController):
|
|
|
131
131
|
)
|
|
132
132
|
|
|
133
133
|
async def arm(self):
|
|
134
|
-
self._arm_status = await set_and_wait_for_value(
|
|
134
|
+
self._arm_status = await set_and_wait_for_value(
|
|
135
|
+
self._drv.acquire, True, wait_for_set_completion=False
|
|
136
|
+
)
|
|
135
137
|
|
|
136
138
|
async def wait_for_idle(self):
|
|
137
|
-
if self._arm_status:
|
|
139
|
+
if self._arm_status and not self._arm_status.done:
|
|
138
140
|
await self._arm_status
|
|
141
|
+
self._arm_status = None
|
|
139
142
|
|
|
140
143
|
def _validate_trigger(self, trigger: DetectorTrigger) -> None:
|
|
141
144
|
supported_trigger_types = {
|
|
@@ -20,6 +20,6 @@ def lookup_table_adjuster(
|
|
|
20
20
|
def adjust(group=None) -> Generator[Msg, None, None]:
|
|
21
21
|
setpoint = lookup_table(input)
|
|
22
22
|
LOGGER.info(f"lookup_table_adjuster setting {output_device.name} to {setpoint}")
|
|
23
|
-
yield from bps.abs_set(output_device, setpoint, group=group)
|
|
23
|
+
yield from bps.abs_set(output_device, setpoint, group=group)
|
|
24
24
|
|
|
25
25
|
return adjust
|
|
@@ -89,7 +89,7 @@ class ZebraMapping(ZebraMappingValidations):
|
|
|
89
89
|
# Which of the Zebra's four AND gates is used to control the automatic shutter, if it's being used.
|
|
90
90
|
# After defining, the correct GateControl device can be accessed with, eg,
|
|
91
91
|
# zebra.logic_gates.and_gates[zebra.mapping.AND_GATE_FOR_AUTO_SHUTTER]. Set to -1 if not being used.
|
|
92
|
-
AND_GATE_FOR_AUTO_SHUTTER: int = Field(default
|
|
92
|
+
AND_GATE_FOR_AUTO_SHUTTER: int = Field(default=2, ge=-1, le=4)
|
|
93
93
|
|
|
94
94
|
|
|
95
95
|
class UnmappedZebraException(Exception):
|
dodal/devices/zocalo/__init__.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from collections.abc import Generator, Sequence
|
|
3
3
|
from enum import Enum
|
|
4
|
-
from inspect import get_annotations
|
|
5
4
|
from queue import Empty, Queue
|
|
6
5
|
from typing import Any, TypedDict
|
|
7
6
|
|
|
@@ -47,7 +46,6 @@ class ZocaloSource(str, Enum):
|
|
|
47
46
|
|
|
48
47
|
DEFAULT_TIMEOUT = 180
|
|
49
48
|
DEFAULT_SORT_KEY = SortKeys.max_count
|
|
50
|
-
ZOCALO_READING_PLAN_NAME = "zocalo reading"
|
|
51
49
|
CLEAR_QUEUE_WAIT_S = 2.0
|
|
52
50
|
ZOCALO_STAGE_GROUP = "clear zocalo queue"
|
|
53
51
|
|
|
@@ -389,12 +387,12 @@ def get_full_processing_results(
|
|
|
389
387
|
"""A plan that will return the raw zocalo results, ranked in descending order according to the sort key.
|
|
390
388
|
Returns empty list in the event no results found."""
|
|
391
389
|
LOGGER.info("Retrieving raw zocalo processing results")
|
|
392
|
-
com = yield from bps.rd(zocalo.centre_of_mass, default_value=[])
|
|
393
|
-
max_voxel = yield from bps.rd(zocalo.max_voxel, default_value=[])
|
|
394
|
-
max_count = yield from bps.rd(zocalo.max_count, default_value=[])
|
|
395
|
-
n_voxels = yield from bps.rd(zocalo.n_voxels, default_value=[])
|
|
396
|
-
total_count = yield from bps.rd(zocalo.total_count, default_value=[])
|
|
397
|
-
bounding_box = yield from bps.rd(zocalo.bounding_box, default_value=[])
|
|
390
|
+
com = yield from bps.rd(zocalo.centre_of_mass, default_value=[])
|
|
391
|
+
max_voxel = yield from bps.rd(zocalo.max_voxel, default_value=[])
|
|
392
|
+
max_count = yield from bps.rd(zocalo.max_count, default_value=[])
|
|
393
|
+
n_voxels = yield from bps.rd(zocalo.n_voxels, default_value=[])
|
|
394
|
+
total_count = yield from bps.rd(zocalo.total_count, default_value=[])
|
|
395
|
+
bounding_box = yield from bps.rd(zocalo.bounding_box, default_value=[])
|
|
398
396
|
return [
|
|
399
397
|
_corrected_xrc_result(
|
|
400
398
|
XrcResult(
|
|
@@ -410,27 +408,3 @@ def get_full_processing_results(
|
|
|
410
408
|
com, max_voxel, max_count, n_voxels, total_count, bounding_box, strict=True
|
|
411
409
|
)
|
|
412
410
|
]
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
def get_processing_results_from_event(
|
|
416
|
-
device_name: str, doc: dict
|
|
417
|
-
) -> Sequence[XrcResult]:
|
|
418
|
-
"""
|
|
419
|
-
Decode an event document into the corresponding x-ray centring results
|
|
420
|
-
|
|
421
|
-
Args:
|
|
422
|
-
doc A bluesky event document containing the signals read from the ZocaloResults
|
|
423
|
-
device_name The device name prefix to prepend to the document keys
|
|
424
|
-
|
|
425
|
-
Returns:
|
|
426
|
-
The list of XrcResults decoded from the event document
|
|
427
|
-
"""
|
|
428
|
-
results_keys = get_annotations(XrcResult).keys()
|
|
429
|
-
results_dict = {k: doc["data"][f"{device_name}-{k}"] for k in results_keys}
|
|
430
|
-
results_values = [results_dict[k].tolist() for k in results_keys]
|
|
431
|
-
|
|
432
|
-
def create_result(*argv):
|
|
433
|
-
kwargs = dict(zip(results_keys, argv, strict=False))
|
|
434
|
-
return XrcResult(**kwargs)
|
|
435
|
-
|
|
436
|
-
return list(map(create_result, *results_values))
|