mx-bluesky 1.5.8__py3-none-any.whl → 1.5.10__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.
- mx_bluesky/_version.py +2 -2
- mx_bluesky/beamlines/i04/experiment_plans/i04_grid_detect_then_xray_centre_plan.py +35 -12
- mx_bluesky/beamlines/i24/jungfrau_commissioning/experiment_plans/__init__.py +0 -0
- mx_bluesky/beamlines/i24/jungfrau_commissioning/experiment_plans/do_darks.py +78 -0
- mx_bluesky/beamlines/i24/jungfrau_commissioning/plan_stubs/__init__.py +1 -0
- mx_bluesky/beamlines/i24/jungfrau_commissioning/{do_external_acquisition.py → plan_stubs/do_external_acquisition.py} +7 -6
- mx_bluesky/beamlines/i24/jungfrau_commissioning/{do_internal_acquisition.py → plan_stubs/do_internal_acquisition.py} +4 -3
- mx_bluesky/beamlines/i24/jungfrau_commissioning/{plan_utils.py → plan_stubs/plan_utils.py} +21 -28
- mx_bluesky/common/device_setup_plans/__init__.py +0 -0
- mx_bluesky/common/device_setup_plans/manipulate_sample.py +2 -2
- mx_bluesky/common/device_setup_plans/robot_load_unload.py +10 -6
- mx_bluesky/common/device_setup_plans/setup_oav.py +1 -1
- mx_bluesky/common/device_setup_plans/setup_zebra_and_shutter.py +245 -0
- mx_bluesky/common/device_setup_plans/xbpm_feedback.py +4 -4
- mx_bluesky/common/experiment_plans/common_flyscan_xray_centre_plan.py +10 -22
- mx_bluesky/common/experiment_plans/inner_plans/__init__.py +0 -0
- mx_bluesky/common/experiment_plans/inner_plans/do_fgs.py +5 -3
- mx_bluesky/common/experiment_plans/inner_plans/read_hardware.py +3 -3
- mx_bluesky/common/experiment_plans/oav_grid_detection_plan.py +2 -2
- mx_bluesky/common/external_interaction/callbacks/common/ispyb_mapping.py +4 -3
- mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py +34 -25
- mx_bluesky/common/parameters/device_composites.py +2 -2
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +2 -100
- mx_bluesky/hyperion/experiment_plans/hyperion_flyscan_xray_centre_plan.py +13 -12
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +52 -38
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +2 -2
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +5 -3
- mx_bluesky/hyperion/external_interaction/callbacks/alert_on_container_change.py +14 -5
- mx_bluesky/hyperion/external_interaction/callbacks/robot_actions/__init__.py +0 -0
- mx_bluesky/hyperion/parameters/device_composites.py +2 -2
- mx_bluesky/hyperion/parameters/rotation.py +4 -6
- mx_bluesky/hyperion/utils/context.py +6 -1
- {mx_bluesky-1.5.8.dist-info → mx_bluesky-1.5.10.dist-info}/METADATA +4 -4
- {mx_bluesky-1.5.8.dist-info → mx_bluesky-1.5.10.dist-info}/RECORD +39 -33
- mx_bluesky/phase1_zebra/device_setup_plans/setup_zebra.py +0 -112
- /mx_bluesky/{common/experiment_plans/inner_plans/__init__ .py → beamlines/i04/callbacks/__init__.py} +0 -0
- {mx_bluesky-1.5.8.dist-info → mx_bluesky-1.5.10.dist-info}/WHEEL +0 -0
- {mx_bluesky-1.5.8.dist-info → mx_bluesky-1.5.10.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.5.8.dist-info → mx_bluesky-1.5.10.dist-info}/licenses/LICENSE +0 -0
- {mx_bluesky-1.5.8.dist-info → mx_bluesky-1.5.10.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
from typing import Protocol, runtime_checkable
|
|
2
|
+
|
|
3
|
+
import bluesky.plan_stubs as bps
|
|
4
|
+
from bluesky.utils import MsgGenerator
|
|
5
|
+
from dodal.devices.zebra.zebra import (
|
|
6
|
+
ArmDemand,
|
|
7
|
+
EncEnum,
|
|
8
|
+
I03Axes,
|
|
9
|
+
RotationDirection,
|
|
10
|
+
Zebra,
|
|
11
|
+
)
|
|
12
|
+
from dodal.devices.zebra.zebra_controlled_shutter import (
|
|
13
|
+
ZebraShutter,
|
|
14
|
+
ZebraShutterControl,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from mx_bluesky.common.parameters.constants import ZEBRA_STATUS_TIMEOUT
|
|
18
|
+
from mx_bluesky.common.utils.log import LOGGER
|
|
19
|
+
|
|
20
|
+
"""Plans in this file will work as intended if the zebra has the following configuration:
|
|
21
|
+
- A fast shutter is connected through TTL inputs from the Zebra.
|
|
22
|
+
- When the zebra shutter is set to auto mode, the IOC sets the Zebra's SOFT_IN1 signal high.
|
|
23
|
+
- When the zebra shutter is set to manual mode, the IOC sets the Zebra's SOFT_IN1 signal low.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@runtime_checkable
|
|
28
|
+
class GridscanSetupDevices(Protocol):
|
|
29
|
+
zebra: Zebra
|
|
30
|
+
sample_shutter: ZebraShutter
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def setup_zebra_for_gridscan(
|
|
34
|
+
composite: GridscanSetupDevices, # XRC gridscan's generic trigger setup expects a composite rather than individual devices
|
|
35
|
+
group="setup_zebra_for_gridscan",
|
|
36
|
+
wait=True,
|
|
37
|
+
ttl_input_for_detector_to_use: None | int = None,
|
|
38
|
+
) -> MsgGenerator:
|
|
39
|
+
"""
|
|
40
|
+
Configure the zebra for an MX XRC gridscan by allowing the zebra to trigger the fast shutter and detector via signals
|
|
41
|
+
sent from the motion controller.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
composite: Composite device containing a zebra and zebra shutter
|
|
45
|
+
group: Bluesky group to use when waiting on completion
|
|
46
|
+
wait: If true, block until completion
|
|
47
|
+
ttl_input_for_detector_to_use: If the zebra isn't using the TTL_DETECTOR zebra input, manually
|
|
48
|
+
specify which TTL input is being used for the desired detector
|
|
49
|
+
|
|
50
|
+
This plan assumes that the motion controller, as part of its gridscan PLC, will send triggers as required to the zebra's
|
|
51
|
+
IN4_TTL and IN3_TTL to control the fast_shutter and detector respectively
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
zebra = composite.zebra
|
|
55
|
+
ttl_detector = ttl_input_for_detector_to_use or zebra.mapping.outputs.TTL_DETECTOR
|
|
56
|
+
# Set shutter to automatic and to trigger via motion controller GPIO signal (IN4_TTL)
|
|
57
|
+
yield from configure_zebra_and_shutter_for_auto_shutter(
|
|
58
|
+
zebra, composite.sample_shutter, zebra.mapping.sources.IN4_TTL, group=group
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
yield from bps.abs_set(
|
|
62
|
+
zebra.output.out_pvs[ttl_detector],
|
|
63
|
+
zebra.mapping.sources.IN3_TTL,
|
|
64
|
+
group=group,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if wait:
|
|
68
|
+
yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def set_shutter_auto_input(zebra: Zebra, input: int, group="set_shutter_trigger"):
|
|
72
|
+
"""Set the signal that controls the shutter. We use the second input to the
|
|
73
|
+
Zebra's AND_GATE_FOR_AUTO_SHUTTER for this input. ZebraShutter control mode must be in auto for this input to take control
|
|
74
|
+
|
|
75
|
+
For more details see the ZebraShutter device."""
|
|
76
|
+
auto_gate = zebra.mapping.AND_GATE_FOR_AUTO_SHUTTER
|
|
77
|
+
auto_shutter_control = zebra.logic_gates.and_gates[auto_gate]
|
|
78
|
+
yield from bps.abs_set(auto_shutter_control.sources[2], input, group)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def configure_zebra_and_shutter_for_auto_shutter(
|
|
82
|
+
zebra: Zebra, zebra_shutter: ZebraShutter, input: int, group="use_automatic_shutter"
|
|
83
|
+
):
|
|
84
|
+
"""Set the shutter to auto mode, and configure the zebra to trigger the shutter on
|
|
85
|
+
an input source. For the input, use one of the source constants in zebra.py
|
|
86
|
+
|
|
87
|
+
When the shutter is in auto/manual, logic in EPICS sets the Zebra's
|
|
88
|
+
SOFT_IN1 to low/high respectively. The Zebra's AND_GATE_FOR_AUTO_SHUTTER should be used to control the shutter while in auto mode.
|
|
89
|
+
To do this, we need (AND_GATE_FOR_AUTO_SHUTTER = SOFT_IN1 AND input), where input is the zebra signal we want to control the shutter when in auto mode.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
# Set shutter to auto mode
|
|
93
|
+
yield from bps.abs_set(
|
|
94
|
+
zebra_shutter.control_mode, ZebraShutterControl.AUTO, group=group
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
auto_gate = zebra.mapping.AND_GATE_FOR_AUTO_SHUTTER
|
|
98
|
+
|
|
99
|
+
# Set first input of AND_GATE_FOR_AUTO_SHUTTER to SOFT_IN1, which is high when shutter is in auto mode
|
|
100
|
+
# Note the Zebra should ALWAYS be setup this way. See https://github.com/DiamondLightSource/mx-bluesky/issues/551
|
|
101
|
+
yield from bps.abs_set(
|
|
102
|
+
zebra.logic_gates.and_gates[auto_gate].sources[1],
|
|
103
|
+
zebra.mapping.sources.SOFT_IN1,
|
|
104
|
+
group=group,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Set the second input of AND_GATE_FOR_AUTO_SHUTTER to the requested zebra input source
|
|
108
|
+
yield from set_shutter_auto_input(zebra, input, group=group)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def tidy_up_zebra_after_gridscan(
|
|
112
|
+
zebra: Zebra,
|
|
113
|
+
zebra_shutter: ZebraShutter,
|
|
114
|
+
group="tidy_up_zebra_after_gridscan",
|
|
115
|
+
wait=True,
|
|
116
|
+
ttl_input_for_detector_to_use: int | None = None,
|
|
117
|
+
) -> MsgGenerator:
|
|
118
|
+
"""
|
|
119
|
+
Set the zebra back to a state which is expected by GDA.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
zebra: Zebra device.
|
|
123
|
+
zebra_shutter: Zebra shutter device.
|
|
124
|
+
group: Bluesky group to use when waiting on completion.
|
|
125
|
+
wait: If true, block until completion.
|
|
126
|
+
ttl_input_for_detector_to_use: If the zebra isn't using the TTL_DETECTOR zebra input, manually
|
|
127
|
+
specify which TTL input is being used for the desired detector.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
LOGGER.info("Tidying up Zebra")
|
|
131
|
+
|
|
132
|
+
ttl_detector = ttl_input_for_detector_to_use or zebra.mapping.outputs.TTL_DETECTOR
|
|
133
|
+
|
|
134
|
+
yield from bps.abs_set(
|
|
135
|
+
zebra.output.out_pvs[ttl_detector],
|
|
136
|
+
zebra.mapping.sources.PC_PULSE,
|
|
137
|
+
group=group,
|
|
138
|
+
)
|
|
139
|
+
yield from bps.abs_set(
|
|
140
|
+
zebra_shutter.control_mode, ZebraShutterControl.MANUAL, group=group
|
|
141
|
+
)
|
|
142
|
+
yield from set_shutter_auto_input(zebra, zebra.mapping.sources.PC_GATE, group=group)
|
|
143
|
+
|
|
144
|
+
if wait:
|
|
145
|
+
yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def setup_zebra_for_rotation(
|
|
149
|
+
zebra: Zebra,
|
|
150
|
+
zebra_shutter: ZebraShutter,
|
|
151
|
+
axis: EncEnum = I03Axes.OMEGA,
|
|
152
|
+
start_angle: float = 0,
|
|
153
|
+
scan_width: float = 360,
|
|
154
|
+
shutter_opening_deg: float = 2.5,
|
|
155
|
+
shutter_opening_s: float = 0.04,
|
|
156
|
+
direction: RotationDirection = RotationDirection.POSITIVE,
|
|
157
|
+
group: str = "setup_zebra_for_rotation",
|
|
158
|
+
wait: bool = True,
|
|
159
|
+
ttl_input_for_detector_to_use: int | None = None,
|
|
160
|
+
):
|
|
161
|
+
"""Set up the Zebra to collect a rotation dataset. Any plan using this is
|
|
162
|
+
responsible for setting the smargon velocity appropriately so that the desired
|
|
163
|
+
image width is achieved with the exposure time given here.
|
|
164
|
+
|
|
165
|
+
Parameters:
|
|
166
|
+
zebra: The zebra device to use
|
|
167
|
+
axis: Encoder enum representing which axis to use for position
|
|
168
|
+
compare. Currently always omega.
|
|
169
|
+
start_angle: Position at which the scan should begin, in degrees.
|
|
170
|
+
scan_width: Total angle through which to collect, in degrees.
|
|
171
|
+
shutter_opening_deg:How many degrees of rotation it takes for the fast shutter
|
|
172
|
+
to open. Increases the gate width.
|
|
173
|
+
shutter_opening_s: How many seconds it takes for the fast shutter to open. The
|
|
174
|
+
detector pulse is delayed after the shutter signal by this
|
|
175
|
+
amount.
|
|
176
|
+
direction: RotationDirection enum for positive or negative.
|
|
177
|
+
Defaults to Positive.
|
|
178
|
+
group: A name for the group of statuses generated
|
|
179
|
+
wait: Block until all the settings have completed
|
|
180
|
+
ttl_input_for_detector_to_use: If the zebra isn't using the TTL_DETECTOR zebra input,
|
|
181
|
+
manually specify which TTL input is being used for the desired detector
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
ttl_detector = ttl_input_for_detector_to_use or zebra.mapping.outputs.TTL_DETECTOR
|
|
185
|
+
|
|
186
|
+
if not isinstance(direction, RotationDirection):
|
|
187
|
+
raise ValueError(
|
|
188
|
+
"Disallowed rotation direction provided to Zebra setup plan. "
|
|
189
|
+
"Use RotationDirection.POSITIVE or RotationDirection.NEGATIVE."
|
|
190
|
+
)
|
|
191
|
+
yield from bps.abs_set(zebra.pc.dir, direction.value, group=group)
|
|
192
|
+
LOGGER.info("ZEBRA SETUP: START")
|
|
193
|
+
# Set gate start, adjust for shutter opening time if necessary
|
|
194
|
+
LOGGER.info(f"ZEBRA SETUP: degrees to adjust for shutter = {shutter_opening_deg}")
|
|
195
|
+
LOGGER.info(f"ZEBRA SETUP: start angle start: {start_angle}")
|
|
196
|
+
LOGGER.info(f"ZEBRA SETUP: start angle adjusted, gate start set to: {start_angle}")
|
|
197
|
+
yield from bps.abs_set(zebra.pc.gate_start, start_angle, group=group)
|
|
198
|
+
# set gate width to total width
|
|
199
|
+
yield from bps.abs_set(
|
|
200
|
+
zebra.pc.gate_width, scan_width + shutter_opening_deg, group=group
|
|
201
|
+
)
|
|
202
|
+
LOGGER.info(
|
|
203
|
+
f"Pulse start set to shutter open time, set to: {abs(shutter_opening_s)}"
|
|
204
|
+
)
|
|
205
|
+
yield from bps.abs_set(zebra.pc.pulse_start, abs(shutter_opening_s), group=group)
|
|
206
|
+
# Set gate position to be angle of interest
|
|
207
|
+
yield from bps.abs_set(zebra.pc.gate_trigger, axis.value, group=group)
|
|
208
|
+
# Set shutter to automatic and to trigger via PC_GATE
|
|
209
|
+
yield from configure_zebra_and_shutter_for_auto_shutter(
|
|
210
|
+
zebra, zebra_shutter, zebra.mapping.sources.PC_GATE, group=group
|
|
211
|
+
)
|
|
212
|
+
# Trigger the detector with a pulse
|
|
213
|
+
yield from bps.abs_set(
|
|
214
|
+
zebra.output.out_pvs[ttl_detector],
|
|
215
|
+
zebra.mapping.sources.PC_PULSE,
|
|
216
|
+
group=group,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
LOGGER.info(f"ZEBRA SETUP: END - {'' if wait else 'not'} waiting for completion")
|
|
220
|
+
if wait:
|
|
221
|
+
yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def tidy_up_zebra_after_rotation_scan(
|
|
225
|
+
zebra: Zebra,
|
|
226
|
+
zebra_shutter: ZebraShutter,
|
|
227
|
+
group="tidy_up_zebra_after_rotation",
|
|
228
|
+
wait=True,
|
|
229
|
+
):
|
|
230
|
+
"""
|
|
231
|
+
Set the zebra back to a state which is expected by GDA.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
zebra: Zebra device.
|
|
235
|
+
zebra_shutter: Zebra shutter device.
|
|
236
|
+
group: Bluesky group to use when waiting on completion.
|
|
237
|
+
wait: If true, block until completion.
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
yield from bps.abs_set(zebra.pc.arm, ArmDemand.DISARM, group=group)
|
|
241
|
+
yield from bps.abs_set(
|
|
242
|
+
zebra_shutter.control_mode, ZebraShutterControl.MANUAL, group=group
|
|
243
|
+
)
|
|
244
|
+
if wait:
|
|
245
|
+
yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
|
|
@@ -16,7 +16,7 @@ def unpause_xbpm_feedback_and_set_transmission_to_1(
|
|
|
16
16
|
the beam in position
|
|
17
17
|
attenuator (BinaryFilterAttenuator): The attenuator used to set transmission
|
|
18
18
|
"""
|
|
19
|
-
yield from bps.mv(xbpm_feedback.pause_feedback, Pause.RUN, attenuator, 1.0)
|
|
19
|
+
yield from bps.mv(xbpm_feedback.pause_feedback, Pause.RUN, attenuator, 1.0)
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def check_and_pause_feedback(
|
|
@@ -35,11 +35,11 @@ def check_and_pause_feedback(
|
|
|
35
35
|
turning XBPM feedback off.
|
|
36
36
|
|
|
37
37
|
"""
|
|
38
|
-
yield from bps.mv(attenuator, 1.0)
|
|
38
|
+
yield from bps.mv(attenuator, 1.0)
|
|
39
39
|
LOGGER.info("Waiting for XBPM feedback to be stable")
|
|
40
40
|
yield from bps.trigger(xbpm_feedback, wait=True)
|
|
41
41
|
LOGGER.info(
|
|
42
42
|
f"XPBM feedback in position, pausing and setting transmission to {desired_transmission_fraction}"
|
|
43
43
|
)
|
|
44
|
-
yield from bps.mv(xbpm_feedback.pause_feedback, Pause.PAUSE)
|
|
45
|
-
yield from bps.mv(attenuator, desired_transmission_fraction)
|
|
44
|
+
yield from bps.mv(xbpm_feedback.pause_feedback, Pause.PAUSE)
|
|
45
|
+
yield from bps.mv(attenuator, desired_transmission_fraction)
|
|
@@ -8,11 +8,12 @@ import bluesky.plan_stubs as bps
|
|
|
8
8
|
import bluesky.preprocessors as bpp
|
|
9
9
|
import numpy as np
|
|
10
10
|
from bluesky.protocols import Readable
|
|
11
|
-
from bluesky.utils import MsgGenerator
|
|
11
|
+
from bluesky.utils import FailedStatus, MsgGenerator
|
|
12
12
|
from dodal.common.beamlines.commissioning_mode import read_commissioning_mode
|
|
13
13
|
from dodal.devices.fast_grid_scan import (
|
|
14
14
|
FastGridScanCommon,
|
|
15
15
|
FastGridScanThreeD,
|
|
16
|
+
GridScanInvalidError,
|
|
16
17
|
)
|
|
17
18
|
from dodal.devices.zocalo import ZocaloResults
|
|
18
19
|
from dodal.devices.zocalo.zocalo_results import (
|
|
@@ -270,14 +271,18 @@ def run_gridscan(
|
|
|
270
271
|
yield from beamline_specific.read_pre_flyscan_plan()
|
|
271
272
|
|
|
272
273
|
LOGGER.info("Setting fgs params")
|
|
273
|
-
yield from beamline_specific.set_flyscan_params_plan()
|
|
274
274
|
|
|
275
|
-
|
|
276
|
-
|
|
275
|
+
try:
|
|
276
|
+
yield from beamline_specific.set_flyscan_params_plan()
|
|
277
|
+
except FailedStatus as e:
|
|
278
|
+
if isinstance(e.__cause__, GridScanInvalidError):
|
|
279
|
+
raise SampleException(
|
|
280
|
+
"Scan invalid - gridscan not valid for detected pin position"
|
|
281
|
+
) from e
|
|
277
282
|
|
|
278
283
|
LOGGER.info("Waiting for arming to finish")
|
|
279
284
|
yield from bps.wait(PlanGroupCheckpointConstants.GRID_READY_FOR_DC)
|
|
280
|
-
yield from bps.stage(fgs_composite.eiger)
|
|
285
|
+
yield from bps.stage(fgs_composite.eiger, wait=True)
|
|
281
286
|
|
|
282
287
|
yield from kickoff_and_complete_gridscan(
|
|
283
288
|
beamline_specific.fgs_motors,
|
|
@@ -293,23 +298,6 @@ def run_gridscan(
|
|
|
293
298
|
yield from bps.abs_set(beamline_specific.fgs_motors.z_steps, 0, wait=False)
|
|
294
299
|
|
|
295
300
|
|
|
296
|
-
def wait_for_gridscan_valid(fgs_motors: FastGridScanCommon, timeout=0.5):
|
|
297
|
-
LOGGER.info("Waiting for valid fgs_params")
|
|
298
|
-
SLEEP_PER_CHECK = 0.1
|
|
299
|
-
times_to_check = int(timeout / SLEEP_PER_CHECK)
|
|
300
|
-
for _ in range(times_to_check):
|
|
301
|
-
scan_invalid = yield from bps.rd(fgs_motors.scan_invalid)
|
|
302
|
-
pos_counter = yield from bps.rd(fgs_motors.position_counter)
|
|
303
|
-
LOGGER.debug(
|
|
304
|
-
f"Scan invalid: {scan_invalid} and position counter: {pos_counter}"
|
|
305
|
-
)
|
|
306
|
-
if not scan_invalid and pos_counter == 0:
|
|
307
|
-
LOGGER.info("Gridscan scan valid and position counter reset")
|
|
308
|
-
return
|
|
309
|
-
yield from bps.sleep(SLEEP_PER_CHECK)
|
|
310
|
-
raise SampleException("Scan invalid - pin too long/short/bent and out of range")
|
|
311
|
-
|
|
312
|
-
|
|
313
301
|
def _xrc_result_in_boxes_to_result_in_mm(
|
|
314
302
|
xrc_result: XrcResult, parameters: SpecifiedThreeDGridScan
|
|
315
303
|
) -> XRayCentreResult:
|
|
File without changes
|
|
@@ -94,14 +94,16 @@ def kickoff_and_complete_gridscan(
|
|
|
94
94
|
md={
|
|
95
95
|
"subplan_name": plan_name,
|
|
96
96
|
"omega_to_scan_spec": {
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
# These have to be cast to strings due to a bug in orsjon. See
|
|
98
|
+
# https://github.com/ijl/orjson/issues/414
|
|
99
|
+
str(GridscanPlane.OMEGA_XY): scan_points[0],
|
|
100
|
+
str(GridscanPlane.OMEGA_XZ): scan_points[1],
|
|
99
101
|
},
|
|
100
102
|
}
|
|
101
103
|
)
|
|
102
104
|
@bpp.contingency_decorator(
|
|
103
105
|
except_plan=lambda e: (yield from bps.stop(detector)), # type: ignore # Fix types in ophyd-async (https://github.com/DiamondLightSource/mx-bluesky/issues/855)
|
|
104
|
-
else_plan=lambda: (yield from bps.unstage(detector)),
|
|
106
|
+
else_plan=lambda: (yield from bps.unstage(detector, wait=True)),
|
|
105
107
|
)
|
|
106
108
|
def _decorated_do_fgs():
|
|
107
109
|
yield from _wait_for_zocalo_to_stage_then_do_fgs(
|
|
@@ -4,7 +4,7 @@ import bluesky.plan_stubs as bps
|
|
|
4
4
|
from bluesky.protocols import Readable
|
|
5
5
|
from dodal.devices.aperturescatterguard import ApertureScatterguard
|
|
6
6
|
from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
|
|
7
|
-
from dodal.devices.common_dcm import
|
|
7
|
+
from dodal.devices.common_dcm import DoubleCrystalMonochromator
|
|
8
8
|
from dodal.devices.eiger import EigerDetector
|
|
9
9
|
from dodal.devices.flux import Flux
|
|
10
10
|
from dodal.devices.s4_slit_gaps import S4SlitGaps
|
|
@@ -43,7 +43,7 @@ def standard_read_hardware_pre_collection(
|
|
|
43
43
|
undulator: Undulator,
|
|
44
44
|
synchrotron: Synchrotron,
|
|
45
45
|
s4_slit_gaps: S4SlitGaps,
|
|
46
|
-
dcm:
|
|
46
|
+
dcm: DoubleCrystalMonochromator,
|
|
47
47
|
smargon: Smargon,
|
|
48
48
|
):
|
|
49
49
|
LOGGER.info("Reading status of beamline for callbacks, pre collection.")
|
|
@@ -63,7 +63,7 @@ def standard_read_hardware_during_collection(
|
|
|
63
63
|
aperture_scatterguard: ApertureScatterguard,
|
|
64
64
|
attenuator: BinaryFilterAttenuator,
|
|
65
65
|
flux: Flux,
|
|
66
|
-
dcm:
|
|
66
|
+
dcm: DoubleCrystalMonochromator,
|
|
67
67
|
detector: EigerDetector,
|
|
68
68
|
):
|
|
69
69
|
signals_to_read_during_collection = [
|
|
@@ -10,7 +10,7 @@ from bluesky.utils import MsgGenerator
|
|
|
10
10
|
from dodal.devices.oav.oav_detector import OAV
|
|
11
11
|
from dodal.devices.oav.pin_image_recognition import PinTipDetection
|
|
12
12
|
from dodal.devices.oav.pin_image_recognition.utils import NONE_VALUE
|
|
13
|
-
from dodal.devices.oav.utils import
|
|
13
|
+
from dodal.devices.oav.utils import PinNotFoundError, wait_for_tip_to_be_found
|
|
14
14
|
from dodal.devices.smargon import Smargon
|
|
15
15
|
|
|
16
16
|
from mx_bluesky.common.device_setup_plans.setup_oav import (
|
|
@@ -108,7 +108,7 @@ def grid_detection_plan(
|
|
|
108
108
|
yield from bps.sleep(HardwareConstants.OAV_REFRESH_DELAY)
|
|
109
109
|
|
|
110
110
|
tip_x_px, tip_y_px = yield from catch_exception_and_warn(
|
|
111
|
-
|
|
111
|
+
PinNotFoundError, wait_for_tip_to_be_found, pin_tip_detection
|
|
112
112
|
)
|
|
113
113
|
|
|
114
114
|
LOGGER.info(f"Tip is at x,y: {tip_x_px},{tip_y_px}")
|
|
@@ -50,9 +50,10 @@ def populate_remaining_data_collection_info(
|
|
|
50
50
|
data_collection_info.xbeam = beam_position[0]
|
|
51
51
|
data_collection_info.ybeam = beam_position[1]
|
|
52
52
|
data_collection_info.start_time = get_current_time_string()
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
if data_collection_info.data_collection_number is not None:
|
|
54
|
+
# Do not write the file template if we don't have sufficient information - for gridscans we may not
|
|
55
|
+
# know the data collection number until later
|
|
56
|
+
data_collection_info.file_template = f"{params.detector_params.prefix}_{data_collection_info.data_collection_number}_master.h5"
|
|
56
57
|
return data_collection_info
|
|
57
58
|
|
|
58
59
|
|
|
@@ -105,6 +105,7 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
105
105
|
self._start_of_fgs_uid: str | None = None
|
|
106
106
|
self._processing_start_time: float | None = None
|
|
107
107
|
self._grid_plane_to_id_map: dict[GridscanPlane, int] = {}
|
|
108
|
+
self._grid_plane_to_width_map: dict[GridscanPlane, int] = {}
|
|
108
109
|
self.data_collection_group_info: DataCollectionGroupInfo | None
|
|
109
110
|
|
|
110
111
|
def activity_gated_start(self, doc: RunStart):
|
|
@@ -131,9 +132,7 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
131
132
|
data_collection_info=populate_remaining_data_collection_info(
|
|
132
133
|
"MX-Bluesky: Xray centring 1 -",
|
|
133
134
|
None,
|
|
134
|
-
DataCollectionInfo(
|
|
135
|
-
data_collection_number=self.params.detector_params.run_number,
|
|
136
|
-
),
|
|
135
|
+
DataCollectionInfo(),
|
|
137
136
|
self.params,
|
|
138
137
|
),
|
|
139
138
|
),
|
|
@@ -141,11 +140,7 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
141
140
|
data_collection_info=populate_remaining_data_collection_info(
|
|
142
141
|
"MX-Bluesky: Xray centring 2 -",
|
|
143
142
|
None,
|
|
144
|
-
DataCollectionInfo(
|
|
145
|
-
data_collection_number=(
|
|
146
|
-
self.params.detector_params.run_number + 1
|
|
147
|
-
),
|
|
148
|
-
),
|
|
143
|
+
DataCollectionInfo(),
|
|
149
144
|
self.params,
|
|
150
145
|
)
|
|
151
146
|
),
|
|
@@ -192,6 +187,15 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
192
187
|
assert self.params, "ISPyB handler didn't receive parameters!"
|
|
193
188
|
assert self.data_collection_group_info, "No data collection group"
|
|
194
189
|
data = doc["data"]
|
|
190
|
+
omega = doc["data"]["smargon-omega"]
|
|
191
|
+
grid_plane = _smargon_omega_to_xyxz_plane(omega)
|
|
192
|
+
ISPYB_ZOCALO_CALLBACK_LOGGER.info(
|
|
193
|
+
f"Generating dc info for gridplane {grid_plane}, omega {omega}"
|
|
194
|
+
)
|
|
195
|
+
data_collection_number = self.data_collection_number_from_gridplane(grid_plane)
|
|
196
|
+
file_template = (
|
|
197
|
+
f"{self.params.detector_params.prefix}_{data_collection_number}_master.h5"
|
|
198
|
+
)
|
|
195
199
|
data_collection_info = DataCollectionInfo(
|
|
196
200
|
xtal_snapshot1=data.get("oav-grid_snapshot-last_path_full_overlay"),
|
|
197
201
|
xtal_snapshot2=data.get("oav-grid_snapshot-last_path_outer"),
|
|
@@ -200,6 +204,8 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
200
204
|
data["oav-grid_snapshot-num_boxes_x"]
|
|
201
205
|
* data["oav-grid_snapshot-num_boxes_y"]
|
|
202
206
|
),
|
|
207
|
+
data_collection_number=data_collection_number,
|
|
208
|
+
file_template=file_template,
|
|
203
209
|
)
|
|
204
210
|
microns_per_pixel_x = data["oav-microns_per_pixel_x"]
|
|
205
211
|
microns_per_pixel_y = data["oav-microns_per_pixel_y"]
|
|
@@ -219,21 +225,22 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
219
225
|
data_collection_grid_info
|
|
220
226
|
)
|
|
221
227
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
f"by {data_collection_grid_info.steps_y}."
|
|
225
|
-
)
|
|
226
|
-
else:
|
|
227
|
-
self.data_collection_group_info.comments = (
|
|
228
|
-
f"Diffraction grid scan of "
|
|
229
|
-
f"{data_collection_grid_info.steps_x} "
|
|
230
|
-
f"by {data_collection_grid_info.steps_y} "
|
|
231
|
-
)
|
|
232
|
-
|
|
228
|
+
# Snapshots may be triggered in a different order to gridscans, so save
|
|
229
|
+
# the mapping to the data collection id in order to trigger Zocalo correctly.
|
|
233
230
|
data_collection_id = self.ispyb_ids.data_collection_ids[
|
|
234
|
-
|
|
231
|
+
0 if grid_plane == GridscanPlane.OMEGA_XY else 1
|
|
235
232
|
]
|
|
236
|
-
self.
|
|
233
|
+
self._grid_plane_to_id_map[grid_plane] = data_collection_id
|
|
234
|
+
self._grid_plane_to_width_map[grid_plane] = data_collection_grid_info.steps_y
|
|
235
|
+
|
|
236
|
+
y_steps = self._grid_plane_to_width_map.get(GridscanPlane.OMEGA_XY, "_")
|
|
237
|
+
z_steps = self._grid_plane_to_width_map.get(GridscanPlane.OMEGA_XZ, "_")
|
|
238
|
+
self.data_collection_group_info.comments = (
|
|
239
|
+
f"Diffraction grid scan of {data_collection_grid_info.steps_x} by "
|
|
240
|
+
f"{y_steps} by {z_steps}."
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
self._populate_axis_info(data_collection_info, omega)
|
|
237
244
|
|
|
238
245
|
scan_data_info = ScanDataInfo(
|
|
239
246
|
data_collection_info=data_collection_info,
|
|
@@ -243,10 +250,6 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
243
250
|
ISPYB_ZOCALO_CALLBACK_LOGGER.info(
|
|
244
251
|
"Updating ispyb data collection after oav snapshot."
|
|
245
252
|
)
|
|
246
|
-
grid_plane = _smargon_omega_to_xyxz_plane(doc["data"]["smargon-omega"])
|
|
247
|
-
# Snapshots may be triggered in a different order to gridscans, so save
|
|
248
|
-
# the mapping to the data collection id in order to trigger Zocalo correctly.
|
|
249
|
-
self._grid_plane_to_id_map[grid_plane] = data_collection_id
|
|
250
253
|
|
|
251
254
|
self._oav_snapshot_event_idx += 1
|
|
252
255
|
return [scan_data_info]
|
|
@@ -315,6 +318,7 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
315
318
|
)
|
|
316
319
|
self.data_collection_group_info = None
|
|
317
320
|
self._grid_plane_to_id_map.clear()
|
|
321
|
+
self._grid_plane_to_width_map.clear()
|
|
318
322
|
return super().activity_gated_stop(doc)
|
|
319
323
|
return self.tag_doc(doc)
|
|
320
324
|
|
|
@@ -325,6 +329,11 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
325
329
|
doc["grid_plane_to_id_map"] = self._grid_plane_to_id_map
|
|
326
330
|
return doc # type: ignore
|
|
327
331
|
|
|
332
|
+
def data_collection_number_from_gridplane(self, plane) -> int:
|
|
333
|
+
assert self.params
|
|
334
|
+
base_number = self.params.detector_params.run_number
|
|
335
|
+
return base_number if plane == GridscanPlane.OMEGA_XY else base_number + 1
|
|
336
|
+
|
|
328
337
|
|
|
329
338
|
def generate_start_info_from_omega_map() -> ZocaloInfoGenerator:
|
|
330
339
|
"""
|
|
@@ -4,7 +4,7 @@ from dodal.devices.aperturescatterguard import (
|
|
|
4
4
|
)
|
|
5
5
|
from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
|
|
6
6
|
from dodal.devices.backlight import Backlight
|
|
7
|
-
from dodal.devices.common_dcm import
|
|
7
|
+
from dodal.devices.common_dcm import DoubleCrystalMonochromator
|
|
8
8
|
from dodal.devices.detector.detector_motion import DetectorMotion
|
|
9
9
|
from dodal.devices.eiger import EigerDetector
|
|
10
10
|
from dodal.devices.fast_grid_scan import (
|
|
@@ -51,7 +51,7 @@ class GridDetectThenXRayCentreComposite(FlyScanEssentialDevices):
|
|
|
51
51
|
attenuator: BinaryFilterAttenuator
|
|
52
52
|
backlight: Backlight
|
|
53
53
|
beamstop: Beamstop
|
|
54
|
-
dcm:
|
|
54
|
+
dcm: DoubleCrystalMonochromator
|
|
55
55
|
detector_motion: DetectorMotion
|
|
56
56
|
zebra_fast_grid_scan: ZebraFastGridScanThreeD
|
|
57
57
|
flux: Flux
|
|
@@ -1,120 +1,22 @@
|
|
|
1
1
|
import bluesky.plan_stubs as bps
|
|
2
2
|
from dodal.devices.zebra.zebra import (
|
|
3
3
|
ArmDemand,
|
|
4
|
-
EncEnum,
|
|
5
|
-
I03Axes,
|
|
6
|
-
RotationDirection,
|
|
7
4
|
Zebra,
|
|
8
5
|
)
|
|
9
6
|
from dodal.devices.zebra.zebra_controlled_shutter import (
|
|
10
7
|
ZebraShutter,
|
|
11
|
-
ZebraShutterControl,
|
|
12
8
|
)
|
|
13
9
|
|
|
14
|
-
from mx_bluesky.common.
|
|
15
|
-
from mx_bluesky.common.utils.log import LOGGER
|
|
16
|
-
from mx_bluesky.phase1_zebra.device_setup_plans.setup_zebra import (
|
|
10
|
+
from mx_bluesky.common.device_setup_plans.setup_zebra_and_shutter import (
|
|
17
11
|
configure_zebra_and_shutter_for_auto_shutter,
|
|
18
12
|
)
|
|
13
|
+
from mx_bluesky.common.parameters.constants import ZEBRA_STATUS_TIMEOUT
|
|
19
14
|
|
|
20
15
|
|
|
21
16
|
def arm_zebra(zebra: Zebra):
|
|
22
17
|
yield from bps.abs_set(zebra.pc.arm, ArmDemand.ARM, wait=True)
|
|
23
18
|
|
|
24
19
|
|
|
25
|
-
def tidy_up_zebra_after_rotation_scan(
|
|
26
|
-
zebra: Zebra,
|
|
27
|
-
zebra_shutter: ZebraShutter,
|
|
28
|
-
group="tidy_up_zebra_after_rotation",
|
|
29
|
-
wait=True,
|
|
30
|
-
):
|
|
31
|
-
yield from bps.abs_set(zebra.pc.arm, ArmDemand.DISARM, group=group)
|
|
32
|
-
yield from bps.abs_set(
|
|
33
|
-
zebra_shutter.control_mode, ZebraShutterControl.MANUAL, group=group
|
|
34
|
-
)
|
|
35
|
-
if wait:
|
|
36
|
-
yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def setup_zebra_for_rotation(
|
|
40
|
-
zebra: Zebra,
|
|
41
|
-
zebra_shutter: ZebraShutter,
|
|
42
|
-
axis: EncEnum = I03Axes.OMEGA,
|
|
43
|
-
start_angle: float = 0,
|
|
44
|
-
scan_width: float = 360,
|
|
45
|
-
shutter_opening_deg: float = 2.5,
|
|
46
|
-
shutter_opening_s: float = 0.04,
|
|
47
|
-
direction: RotationDirection = RotationDirection.POSITIVE,
|
|
48
|
-
group: str = "setup_zebra_for_rotation",
|
|
49
|
-
wait: bool = True,
|
|
50
|
-
):
|
|
51
|
-
"""Set up the Zebra to collect a rotation dataset. Any plan using this is
|
|
52
|
-
responsible for setting the smargon velocity appropriately so that the desired
|
|
53
|
-
image width is achieved with the exposure time given here.
|
|
54
|
-
|
|
55
|
-
Parameters:
|
|
56
|
-
zebra: The zebra device to use
|
|
57
|
-
axis: I03 axes enum representing which axis to use for position
|
|
58
|
-
compare. Currently always omega.
|
|
59
|
-
start_angle: Position at which the scan should begin, in degrees.
|
|
60
|
-
scan_width: Total angle through which to collect, in degrees.
|
|
61
|
-
shutter_opening_deg:How many degrees of rotation it takes for the fast shutter
|
|
62
|
-
to open. Increases the gate width.
|
|
63
|
-
shutter_opening_s: How many seconds it takes for the fast shutter to open. The
|
|
64
|
-
detector pulse is delayed after the shutter signal by this
|
|
65
|
-
amount.
|
|
66
|
-
direction: RotationDirection enum for positive or negative.
|
|
67
|
-
Defaults to Positive.
|
|
68
|
-
group: A name for the group of statuses generated
|
|
69
|
-
wait: Block until all the settings have completed
|
|
70
|
-
"""
|
|
71
|
-
|
|
72
|
-
if not isinstance(direction, RotationDirection):
|
|
73
|
-
raise ValueError(
|
|
74
|
-
"Disallowed rotation direction provided to Zebra setup plan. "
|
|
75
|
-
"Use RotationDirection.POSITIVE or RotationDirection.NEGATIVE."
|
|
76
|
-
)
|
|
77
|
-
yield from bps.abs_set(zebra.pc.dir, direction.value, group=group)
|
|
78
|
-
LOGGER.info("ZEBRA SETUP: START")
|
|
79
|
-
# Set gate start, adjust for shutter opening time if necessary
|
|
80
|
-
LOGGER.info(f"ZEBRA SETUP: degrees to adjust for shutter = {shutter_opening_deg}")
|
|
81
|
-
LOGGER.info(f"ZEBRA SETUP: start angle start: {start_angle}")
|
|
82
|
-
LOGGER.info(f"ZEBRA SETUP: start angle adjusted, gate start set to: {start_angle}")
|
|
83
|
-
yield from bps.abs_set(zebra.pc.gate_start, start_angle, group=group)
|
|
84
|
-
# set gate width to total width
|
|
85
|
-
yield from bps.abs_set(
|
|
86
|
-
zebra.pc.gate_width, scan_width + shutter_opening_deg, group=group
|
|
87
|
-
)
|
|
88
|
-
LOGGER.info(
|
|
89
|
-
f"Pulse start set to shutter open time, set to: {abs(shutter_opening_s)}"
|
|
90
|
-
)
|
|
91
|
-
yield from bps.abs_set(zebra.pc.pulse_start, abs(shutter_opening_s), group=group)
|
|
92
|
-
# Set gate position to be angle of interest
|
|
93
|
-
yield from bps.abs_set(zebra.pc.gate_trigger, axis.value, group=group)
|
|
94
|
-
# Set shutter to automatic and to trigger via PC_GATE
|
|
95
|
-
yield from configure_zebra_and_shutter_for_auto_shutter(
|
|
96
|
-
zebra, zebra_shutter, zebra.mapping.sources.PC_GATE, group=group
|
|
97
|
-
)
|
|
98
|
-
# Trigger the detector with a pulse
|
|
99
|
-
yield from bps.abs_set(
|
|
100
|
-
zebra.output.out_pvs[zebra.mapping.outputs.TTL_DETECTOR],
|
|
101
|
-
zebra.mapping.sources.PC_PULSE,
|
|
102
|
-
group=group,
|
|
103
|
-
)
|
|
104
|
-
# Don't use the fluorescence detector
|
|
105
|
-
yield from bps.abs_set(
|
|
106
|
-
zebra.output.out_pvs[zebra.mapping.outputs.TTL_XSPRESS3],
|
|
107
|
-
zebra.mapping.sources.DISCONNECT,
|
|
108
|
-
group=group,
|
|
109
|
-
)
|
|
110
|
-
yield from bps.abs_set(
|
|
111
|
-
zebra.output.pulse_1.input, zebra.mapping.sources.DISCONNECT, group=group
|
|
112
|
-
)
|
|
113
|
-
LOGGER.info(f"ZEBRA SETUP: END - {'' if wait else 'not'} waiting for completion")
|
|
114
|
-
if wait:
|
|
115
|
-
yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
|
|
116
|
-
|
|
117
|
-
|
|
118
20
|
def setup_zebra_for_panda_flyscan(
|
|
119
21
|
zebra: Zebra,
|
|
120
22
|
zebra_shutter: ZebraShutter,
|