mx-bluesky 1.5.0__py3-none-any.whl → 1.5.2__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/__init__.py +4 -1
- mx_bluesky/beamlines/i04/callbacks/murko_callback.py +56 -1
- mx_bluesky/beamlines/i04/experiment_plans/__init__.py +0 -0
- mx_bluesky/beamlines/i04/experiment_plans/i04_grid_detect_then_xray_centre_plan.py +259 -0
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +8 -8
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +8 -6
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +4 -4
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +2 -2
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +5 -5
- mx_bluesky/beamlines/i24/serial/web_gui_plans/general_plans.py +3 -3
- mx_bluesky/common/device_setup_plans/robot_load_unload.py +123 -0
- mx_bluesky/common/experiment_plans/change_aperture_then_move_plan.py +5 -1
- mx_bluesky/common/experiment_plans/common_flyscan_xray_centre_plan.py +27 -3
- mx_bluesky/common/experiment_plans/common_grid_detect_then_xray_centre_plan.py +1 -0
- mx_bluesky/common/experiment_plans/inner_plans/do_fgs.py +3 -1
- mx_bluesky/common/external_interaction/ispyb/exp_eye_store.py +26 -24
- mx_bluesky/common/external_interaction/ispyb/ispyb_store.py +11 -7
- mx_bluesky/common/external_interaction/nexus/write_nexus.py +2 -2
- mx_bluesky/common/parameters/__init__.py +0 -0
- mx_bluesky/common/parameters/components.py +7 -2
- mx_bluesky/common/parameters/constants.py +5 -3
- mx_bluesky/common/parameters/device_composites.py +1 -1
- mx_bluesky/common/parameters/gridscan.py +1 -0
- mx_bluesky/common/xrc_result.py +25 -2
- mx_bluesky/hyperion/__main__.py +1 -1
- mx_bluesky/hyperion/baton_handler.py +36 -4
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +4 -93
- mx_bluesky/hyperion/experiment_plans/hyperion_flyscan_xray_centre_plan.py +19 -31
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +26 -8
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +21 -75
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +2 -2
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +14 -9
- mx_bluesky/hyperion/external_interaction/agamemnon.py +4 -4
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +1 -1
- mx_bluesky/hyperion/external_interaction/callbacks/{robot_load → robot_actions}/ispyb_callback.py +28 -19
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +1 -1
- mx_bluesky/hyperion/external_interaction/callbacks/snapshot_callback.py +3 -0
- mx_bluesky/hyperion/external_interaction/config_server.py +0 -11
- mx_bluesky/hyperion/parameters/constants.py +2 -7
- mx_bluesky/hyperion/parameters/gridscan.py +2 -6
- mx_bluesky/hyperion/parameters/load_centre_collect.py +15 -0
- mx_bluesky/hyperion/parameters/rotation.py +7 -3
- mx_bluesky/hyperion/utils/context.py +19 -5
- mx_bluesky/phase1_zebra/__init__.py +1 -0
- mx_bluesky/phase1_zebra/device_setup_plans/__init__.py +0 -0
- mx_bluesky/phase1_zebra/device_setup_plans/setup_zebra.py +112 -0
- {mx_bluesky-1.5.0.dist-info → mx_bluesky-1.5.2.dist-info}/METADATA +5 -4
- {mx_bluesky-1.5.0.dist-info → mx_bluesky-1.5.2.dist-info}/RECORD +55 -49
- mx_bluesky/hyperion/utils/validation.py +0 -196
- /mx_bluesky/common/experiment_plans/{read_hardware.py → inner_plans/read_hardware.py} +0 -0
- /mx_bluesky/common/experiment_plans/{write_sample_status.py → inner_plans/write_sample_status.py} +0 -0
- {mx_bluesky-1.5.0.dist-info → mx_bluesky-1.5.2.dist-info}/WHEEL +0 -0
- {mx_bluesky-1.5.0.dist-info → mx_bluesky-1.5.2.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.5.0.dist-info → mx_bluesky-1.5.2.dist-info}/licenses/LICENSE +0 -0
- {mx_bluesky-1.5.0.dist-info → mx_bluesky-1.5.2.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import bluesky.plan_stubs as bps
|
|
2
|
-
from bluesky.utils import MsgGenerator
|
|
3
2
|
from dodal.devices.zebra.zebra import (
|
|
4
3
|
ArmDemand,
|
|
5
4
|
EncEnum,
|
|
@@ -12,9 +11,11 @@ from dodal.devices.zebra.zebra_controlled_shutter import (
|
|
|
12
11
|
ZebraShutterControl,
|
|
13
12
|
)
|
|
14
13
|
|
|
14
|
+
from mx_bluesky.common.parameters.constants import ZEBRA_STATUS_TIMEOUT
|
|
15
15
|
from mx_bluesky.common.utils.log import LOGGER
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
from mx_bluesky.phase1_zebra.device_setup_plans.setup_zebra import (
|
|
17
|
+
configure_zebra_and_shutter_for_auto_shutter,
|
|
18
|
+
)
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
def arm_zebra(zebra: Zebra):
|
|
@@ -35,47 +36,6 @@ def tidy_up_zebra_after_rotation_scan(
|
|
|
35
36
|
yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
|
|
36
37
|
|
|
37
38
|
|
|
38
|
-
def set_shutter_auto_input(zebra: Zebra, input: int, group="set_shutter_trigger"):
|
|
39
|
-
"""Set the signal that controls the shutter. We use the second input to the
|
|
40
|
-
Zebra's AND2 gate for this input. ZebraShutter control mode must be in auto for this input to take control
|
|
41
|
-
|
|
42
|
-
For more details see the ZebraShutter device."""
|
|
43
|
-
auto_gate = zebra.mapping.AND_GATE_FOR_AUTO_SHUTTER
|
|
44
|
-
auto_shutter_control = zebra.logic_gates.and_gates[auto_gate]
|
|
45
|
-
yield from bps.abs_set(auto_shutter_control.sources[2], input, group)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def configure_zebra_and_shutter_for_auto_shutter(
|
|
49
|
-
zebra: Zebra, zebra_shutter: ZebraShutter, input: int, group="use_automatic_shutter"
|
|
50
|
-
):
|
|
51
|
-
"""Set the shutter to auto mode, and configure the zebra to trigger the shutter on
|
|
52
|
-
an input source. For the input, use one of the source constants in zebra.py
|
|
53
|
-
|
|
54
|
-
When the shutter is in auto/manual, logic in EPICS sets the Zebra's
|
|
55
|
-
SOFT_IN1 to low/high respectively. The Zebra's AND2 gate should be used to control the shutter while in auto mode.
|
|
56
|
-
To do this, we need (AND2 = SOFT_IN1 AND input), where input is the zebra signal we want to control the shutter when in auto mode.
|
|
57
|
-
"""
|
|
58
|
-
# See https://github.com/DiamondLightSource/dodal/issues/813 for better typing here.
|
|
59
|
-
|
|
60
|
-
# Set shutter to auto mode
|
|
61
|
-
yield from bps.abs_set(
|
|
62
|
-
zebra_shutter.control_mode, ZebraShutterControl.AUTO, group=group
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
auto_gate = zebra.mapping.AND_GATE_FOR_AUTO_SHUTTER
|
|
66
|
-
|
|
67
|
-
# Set first input of AND2 gate to SOFT_IN1, which is high when shutter is in auto mode
|
|
68
|
-
# Note the Zebra should ALWAYS be setup this way. See https://github.com/DiamondLightSource/mx-bluesky/issues/551
|
|
69
|
-
yield from bps.abs_set(
|
|
70
|
-
zebra.logic_gates.and_gates[auto_gate].sources[1],
|
|
71
|
-
zebra.mapping.sources.SOFT_IN1,
|
|
72
|
-
group=group,
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
# Set the second input of AND2 gate to the requested zebra input source
|
|
76
|
-
yield from set_shutter_auto_input(zebra, input, group=group)
|
|
77
|
-
|
|
78
|
-
|
|
79
39
|
def setup_zebra_for_rotation(
|
|
80
40
|
zebra: Zebra,
|
|
81
41
|
zebra_shutter: ZebraShutter,
|
|
@@ -155,55 +115,6 @@ def setup_zebra_for_rotation(
|
|
|
155
115
|
yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
|
|
156
116
|
|
|
157
117
|
|
|
158
|
-
def setup_zebra_for_gridscan(
|
|
159
|
-
zebra: Zebra,
|
|
160
|
-
zebra_shutter: ZebraShutter,
|
|
161
|
-
group="setup_zebra_for_gridscan",
|
|
162
|
-
wait=True,
|
|
163
|
-
):
|
|
164
|
-
# Set shutter to automatic and to trigger via motion controller GPIO signal (IN4_TTL)
|
|
165
|
-
yield from configure_zebra_and_shutter_for_auto_shutter(
|
|
166
|
-
zebra, zebra_shutter, zebra.mapping.sources.IN4_TTL, group=group
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
yield from bps.abs_set(
|
|
170
|
-
zebra.output.out_pvs[zebra.mapping.outputs.TTL_DETECTOR],
|
|
171
|
-
zebra.mapping.sources.IN3_TTL,
|
|
172
|
-
group=group,
|
|
173
|
-
)
|
|
174
|
-
yield from bps.abs_set(
|
|
175
|
-
zebra.output.out_pvs[zebra.mapping.outputs.TTL_XSPRESS3],
|
|
176
|
-
zebra.mapping.sources.DISCONNECT,
|
|
177
|
-
group=group,
|
|
178
|
-
)
|
|
179
|
-
yield from bps.abs_set(
|
|
180
|
-
zebra.output.pulse_1.input, zebra.mapping.sources.DISCONNECT, group=group
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
if wait:
|
|
184
|
-
yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
def tidy_up_zebra_after_gridscan(
|
|
188
|
-
zebra: Zebra,
|
|
189
|
-
zebra_shutter: ZebraShutter,
|
|
190
|
-
group="tidy_up_zebra_after_gridscan",
|
|
191
|
-
wait=True,
|
|
192
|
-
) -> MsgGenerator:
|
|
193
|
-
yield from bps.abs_set(
|
|
194
|
-
zebra.output.out_pvs[zebra.mapping.outputs.TTL_DETECTOR],
|
|
195
|
-
zebra.mapping.sources.PC_PULSE,
|
|
196
|
-
group=group,
|
|
197
|
-
)
|
|
198
|
-
yield from bps.abs_set(
|
|
199
|
-
zebra_shutter.control_mode, ZebraShutterControl.MANUAL, group=group
|
|
200
|
-
)
|
|
201
|
-
yield from set_shutter_auto_input(zebra, zebra.mapping.sources.PC_GATE, group=group)
|
|
202
|
-
|
|
203
|
-
if wait:
|
|
204
|
-
yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
|
|
205
|
-
|
|
206
|
-
|
|
207
118
|
def setup_zebra_for_panda_flyscan(
|
|
208
119
|
zebra: Zebra,
|
|
209
120
|
zebra_shutter: ZebraShutter,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from collections.abc import Callable
|
|
3
4
|
from functools import partial
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
@@ -19,14 +20,16 @@ from mx_bluesky.hyperion.device_setup_plans.setup_panda import (
|
|
|
19
20
|
setup_panda_for_flyscan,
|
|
20
21
|
)
|
|
21
22
|
from mx_bluesky.hyperion.device_setup_plans.setup_zebra import (
|
|
22
|
-
setup_zebra_for_gridscan,
|
|
23
23
|
setup_zebra_for_panda_flyscan,
|
|
24
|
-
tidy_up_zebra_after_gridscan,
|
|
25
24
|
)
|
|
26
25
|
from mx_bluesky.hyperion.parameters.device_composites import (
|
|
27
26
|
HyperionFlyScanXRayCentreComposite,
|
|
28
27
|
)
|
|
29
28
|
from mx_bluesky.hyperion.parameters.gridscan import HyperionSpecifiedThreeDGridScan
|
|
29
|
+
from mx_bluesky.phase1_zebra.device_setup_plans.setup_zebra import (
|
|
30
|
+
setup_zebra_for_gridscan,
|
|
31
|
+
tidy_up_zebra_after_gridscan,
|
|
32
|
+
)
|
|
30
33
|
|
|
31
34
|
|
|
32
35
|
class SmargonSpeedException(Exception):
|
|
@@ -59,9 +62,11 @@ def construct_hyperion_specific_features(
|
|
|
59
62
|
xrc_composite.eiger.bit_depth,
|
|
60
63
|
]
|
|
61
64
|
|
|
65
|
+
setup_trigger_plan: Callable[..., MsgGenerator]
|
|
66
|
+
|
|
62
67
|
if xrc_parameters.features.use_panda_for_gridscan:
|
|
63
68
|
setup_trigger_plan = _panda_triggering_setup
|
|
64
|
-
tidy_plan = _panda_tidy
|
|
69
|
+
tidy_plan = partial(_panda_tidy, xrc_composite)
|
|
65
70
|
set_flyscan_params_plan = partial(
|
|
66
71
|
set_fast_grid_scan_params,
|
|
67
72
|
xrc_composite.panda_fast_grid_scan,
|
|
@@ -70,8 +75,14 @@ def construct_hyperion_specific_features(
|
|
|
70
75
|
fgs_motors = xrc_composite.panda_fast_grid_scan
|
|
71
76
|
|
|
72
77
|
else:
|
|
73
|
-
setup_trigger_plan =
|
|
74
|
-
tidy_plan = partial(
|
|
78
|
+
setup_trigger_plan = setup_zebra_for_gridscan
|
|
79
|
+
tidy_plan = partial(
|
|
80
|
+
tidy_up_zebra_after_gridscan,
|
|
81
|
+
xrc_composite.zebra,
|
|
82
|
+
xrc_composite.sample_shutter,
|
|
83
|
+
group="flyscan_zebra_tidy",
|
|
84
|
+
wait=True,
|
|
85
|
+
)
|
|
75
86
|
set_flyscan_params_plan = partial(
|
|
76
87
|
set_fast_grid_scan_params,
|
|
77
88
|
xrc_composite.zebra_fast_grid_scan,
|
|
@@ -89,40 +100,17 @@ def construct_hyperion_specific_features(
|
|
|
89
100
|
)
|
|
90
101
|
|
|
91
102
|
|
|
92
|
-
def _generic_tidy(
|
|
93
|
-
xrc_composite: HyperionFlyScanXRayCentreComposite, group, wait=True
|
|
94
|
-
) -> MsgGenerator:
|
|
95
|
-
LOGGER.info("Tidying up Zebra")
|
|
96
|
-
yield from tidy_up_zebra_after_gridscan(
|
|
97
|
-
xrc_composite.zebra, xrc_composite.sample_shutter, group=group, wait=wait
|
|
98
|
-
)
|
|
99
|
-
LOGGER.info("Tidying up Zocalo")
|
|
100
|
-
# make sure we don't consume any other results
|
|
101
|
-
yield from bps.unstage(xrc_composite.zocalo, group=group, wait=wait)
|
|
102
|
-
|
|
103
|
-
# Turn off dev/shm streaming to avoid filling disk, see https://github.com/DiamondLightSource/hyperion/issues/1395
|
|
104
|
-
LOGGER.info("Turning off Eiger dev/shm streaming")
|
|
105
|
-
yield from bps.abs_set(xrc_composite.eiger.odin.fan.dev_shm_enable, 0) # type: ignore # Fix types in ophyd-async (https://github.com/DiamondLightSource/mx-bluesky/issues/855)
|
|
106
|
-
|
|
107
|
-
|
|
108
103
|
def _panda_tidy(xrc_composite: HyperionFlyScanXRayCentreComposite):
|
|
109
104
|
group = "panda_flyscan_tidy"
|
|
110
105
|
LOGGER.info("Disabling panda blocks")
|
|
111
106
|
yield from disarm_panda_for_gridscan(xrc_composite.panda, group)
|
|
112
|
-
yield from
|
|
107
|
+
yield from tidy_up_zebra_after_gridscan(
|
|
108
|
+
xrc_composite.zebra, xrc_composite.sample_shutter, group=group, wait=False
|
|
109
|
+
)
|
|
113
110
|
yield from bps.wait(group, timeout=10)
|
|
114
111
|
yield from bps.unstage(xrc_composite.panda)
|
|
115
112
|
|
|
116
113
|
|
|
117
|
-
def _zebra_triggering_setup(
|
|
118
|
-
xrc_composite: HyperionFlyScanXRayCentreComposite,
|
|
119
|
-
parameters: HyperionSpecifiedThreeDGridScan,
|
|
120
|
-
) -> MsgGenerator:
|
|
121
|
-
yield from setup_zebra_for_gridscan(
|
|
122
|
-
xrc_composite.zebra, xrc_composite.sample_shutter, wait=True
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
|
|
126
114
|
def _panda_triggering_setup(
|
|
127
115
|
xrc_composite: HyperionFlyScanXRayCentreComposite,
|
|
128
116
|
parameters: HyperionSpecifiedThreeDGridScan,
|
|
@@ -76,17 +76,29 @@ def load_centre_collect_full(
|
|
|
76
76
|
flyscan_event_handler,
|
|
77
77
|
)
|
|
78
78
|
|
|
79
|
-
locations_to_collect_um: list[np.ndarray]
|
|
79
|
+
locations_to_collect_um: list[np.ndarray]
|
|
80
|
+
samples_to_collect: list[int]
|
|
80
81
|
|
|
81
82
|
if flyscan_event_handler.xray_centre_results:
|
|
82
83
|
selection_func = flyscan_result.resolve_selection_fn(
|
|
83
84
|
parameters.selection_params
|
|
84
85
|
)
|
|
85
86
|
hits = selection_func(flyscan_event_handler.xray_centre_results)
|
|
86
|
-
|
|
87
|
+
hits_to_collect = []
|
|
88
|
+
for hit in hits:
|
|
89
|
+
if hit.sample_id is None:
|
|
90
|
+
LOGGER.warning(
|
|
91
|
+
f"Diffracting centre {hit} not collected because no sample id was assigned."
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
hits_to_collect.append(hit)
|
|
87
95
|
|
|
96
|
+
locations_to_collect_um = [
|
|
97
|
+
hit.centre_of_mass_mm * 1000 for hit in hits_to_collect
|
|
98
|
+
]
|
|
99
|
+
samples_to_collect = [hit.sample_id for hit in hits_to_collect]
|
|
88
100
|
LOGGER.info(
|
|
89
|
-
f"Selected hits {
|
|
101
|
+
f"Selected hits {hits_to_collect} using {selection_func}, args={parameters.selection_params}"
|
|
90
102
|
)
|
|
91
103
|
else:
|
|
92
104
|
# If the xray centring hasn't found a result but has not thrown an error it
|
|
@@ -98,6 +110,7 @@ def load_centre_collect_full(
|
|
|
98
110
|
locations_to_collect_um = [
|
|
99
111
|
np.array([initial_x_mm, initial_y_mm, initial_z_mm]) * 1000
|
|
100
112
|
]
|
|
113
|
+
samples_to_collect = [parameters.sample_id]
|
|
101
114
|
|
|
102
115
|
multi_rotation = parameters.multi_rotation_scan
|
|
103
116
|
rotation_template = multi_rotation.rotation_scans.copy()
|
|
@@ -108,9 +121,11 @@ def load_centre_collect_full(
|
|
|
108
121
|
|
|
109
122
|
generator = rotation_scan_generator(is_alternating)
|
|
110
123
|
next(generator)
|
|
111
|
-
for location in
|
|
124
|
+
for location, sample_id in zip(
|
|
125
|
+
locations_to_collect_um, samples_to_collect, strict=True
|
|
126
|
+
):
|
|
112
127
|
for rot in rotation_template:
|
|
113
|
-
combination = generator.send((rot, location))
|
|
128
|
+
combination = generator.send((rot, location, sample_id))
|
|
114
129
|
multi_rotation.rotation_scans.append(combination)
|
|
115
130
|
multi_rotation = RotationScan.model_validate(multi_rotation)
|
|
116
131
|
|
|
@@ -125,8 +140,10 @@ def load_centre_collect_full(
|
|
|
125
140
|
|
|
126
141
|
def rotation_scan_generator(
|
|
127
142
|
is_alternating: bool,
|
|
128
|
-
) -> Generator[
|
|
129
|
-
|
|
143
|
+
) -> Generator[
|
|
144
|
+
RotationScanPerSweep, tuple[RotationScanPerSweep, np.ndarray, int], None
|
|
145
|
+
]:
|
|
146
|
+
scan_template, location, sample_id = yield # type: ignore
|
|
130
147
|
next_rotation_direction = scan_template.rotation_direction
|
|
131
148
|
while True:
|
|
132
149
|
scan = scan_template.model_copy()
|
|
@@ -135,6 +152,7 @@ def rotation_scan_generator(
|
|
|
135
152
|
scan.y_start_um,
|
|
136
153
|
scan.z_start_um,
|
|
137
154
|
) = location
|
|
155
|
+
scan.sample_id = sample_id
|
|
138
156
|
if is_alternating:
|
|
139
157
|
if next_rotation_direction != scan.rotation_direction:
|
|
140
158
|
# If originally specified direction of the current scan is different
|
|
@@ -146,4 +164,4 @@ def rotation_scan_generator(
|
|
|
146
164
|
scan.rotation_direction = next_rotation_direction
|
|
147
165
|
next_rotation_direction = next_rotation_direction.opposite
|
|
148
166
|
|
|
149
|
-
scan_template, location = yield scan
|
|
167
|
+
scan_template, location, sample_id = yield scan
|
|
@@ -10,22 +10,25 @@ import bluesky.preprocessors as bpp
|
|
|
10
10
|
import pydantic
|
|
11
11
|
from blueapi.core import BlueskyContext
|
|
12
12
|
from bluesky.utils import Msg
|
|
13
|
-
from dodal.devices.aperturescatterguard import ApertureScatterguard
|
|
13
|
+
from dodal.devices.aperturescatterguard import ApertureScatterguard
|
|
14
14
|
from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
|
|
15
15
|
from dodal.devices.backlight import Backlight, BacklightPosition
|
|
16
16
|
from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, MirrorVoltages
|
|
17
17
|
from dodal.devices.i03.dcm import DCM
|
|
18
18
|
from dodal.devices.i03.undulator_dcm import UndulatorDCM
|
|
19
|
-
from dodal.devices.motors import
|
|
19
|
+
from dodal.devices.motors import XYZStage
|
|
20
20
|
from dodal.devices.oav.oav_detector import OAV
|
|
21
21
|
from dodal.devices.robot import BartRobot, SampleLocation
|
|
22
|
-
from dodal.devices.smargon import
|
|
22
|
+
from dodal.devices.smargon import Smargon
|
|
23
23
|
from dodal.devices.thawer import Thawer
|
|
24
24
|
from dodal.devices.webcam import Webcam
|
|
25
25
|
from dodal.devices.xbpm_feedback import XBPMFeedback
|
|
26
|
-
from dodal.plan_stubs.motor_utils import MoveTooLarge, home_and_reset_wrapper
|
|
27
26
|
|
|
28
|
-
from mx_bluesky.common.
|
|
27
|
+
from mx_bluesky.common.device_setup_plans.robot_load_unload import (
|
|
28
|
+
do_plan_while_lower_gonio_at_home,
|
|
29
|
+
prepare_for_robot_load,
|
|
30
|
+
wait_for_smargon_not_disabled,
|
|
31
|
+
)
|
|
29
32
|
from mx_bluesky.hyperion.experiment_plans.set_energy_plan import (
|
|
30
33
|
SetEnergyComposite,
|
|
31
34
|
set_energy_plan,
|
|
@@ -47,7 +50,7 @@ class RobotLoadAndEnergyChangeComposite:
|
|
|
47
50
|
# RobotLoad fields
|
|
48
51
|
robot: BartRobot
|
|
49
52
|
webcam: Webcam
|
|
50
|
-
lower_gonio:
|
|
53
|
+
lower_gonio: XYZStage
|
|
51
54
|
thawer: Thawer
|
|
52
55
|
oav: OAV
|
|
53
56
|
smargon: Smargon
|
|
@@ -61,25 +64,6 @@ def create_devices(context: BlueskyContext) -> RobotLoadAndEnergyChangeComposite
|
|
|
61
64
|
return device_composite_from_context(context, RobotLoadAndEnergyChangeComposite)
|
|
62
65
|
|
|
63
66
|
|
|
64
|
-
def wait_for_smargon_not_disabled(smargon: Smargon, timeout=60):
|
|
65
|
-
"""Waits for the smargon disabled flag to go low. The robot hardware is responsible
|
|
66
|
-
for setting this to low when it is safe to move. It does this through a physical
|
|
67
|
-
connection between the robot and the smargon.
|
|
68
|
-
"""
|
|
69
|
-
LOGGER.info("Waiting for smargon enabled")
|
|
70
|
-
SLEEP_PER_CHECK = 0.1
|
|
71
|
-
times_to_check = int(timeout / SLEEP_PER_CHECK)
|
|
72
|
-
for _ in range(times_to_check):
|
|
73
|
-
smargon_disabled = yield from bps.rd(smargon.disabled)
|
|
74
|
-
if not smargon_disabled:
|
|
75
|
-
LOGGER.info("Smargon now enabled")
|
|
76
|
-
return
|
|
77
|
-
yield from bps.sleep(SLEEP_PER_CHECK)
|
|
78
|
-
raise TimeoutError(
|
|
79
|
-
"Timed out waiting for smargon to become enabled after robot load"
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
|
|
83
67
|
def take_robot_snapshots(oav: OAV, webcam: Webcam, directory: Path):
|
|
84
68
|
time_now = datetime.now()
|
|
85
69
|
snapshot_format = f"{time_now.strftime('%H%M%S')}_{{device}}_after_load"
|
|
@@ -93,28 +77,15 @@ def take_robot_snapshots(oav: OAV, webcam: Webcam, directory: Path):
|
|
|
93
77
|
yield from bps.wait("snapshots")
|
|
94
78
|
|
|
95
79
|
|
|
96
|
-
def prepare_for_robot_load(
|
|
97
|
-
aperture_scatterguard: ApertureScatterguard, smargon: Smargon
|
|
98
|
-
):
|
|
99
|
-
yield from bps.abs_set(
|
|
100
|
-
aperture_scatterguard.selected_aperture,
|
|
101
|
-
ApertureValue.OUT_OF_BEAM,
|
|
102
|
-
group="prepare_robot_load",
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
yield from bps.mv(smargon.stub_offsets, StubPosition.RESET_TO_ROBOT_LOAD)
|
|
106
|
-
|
|
107
|
-
yield from bps.mv(smargon, CombinedMove(x=0, y=0, z=0, chi=0, phi=0, omega=0))
|
|
108
|
-
|
|
109
|
-
yield from bps.wait("prepare_robot_load")
|
|
110
|
-
|
|
111
|
-
|
|
112
80
|
def do_robot_load(
|
|
113
81
|
composite: RobotLoadAndEnergyChangeComposite,
|
|
114
82
|
sample_location: SampleLocation,
|
|
83
|
+
sample_id: int,
|
|
115
84
|
demand_energy_ev: float | None,
|
|
116
85
|
thawing_time: float,
|
|
117
86
|
):
|
|
87
|
+
yield from bps.abs_set(composite.robot.next_sample_id, sample_id, wait=True)
|
|
88
|
+
|
|
118
89
|
yield from bps.abs_set(
|
|
119
90
|
composite.robot,
|
|
120
91
|
sample_location,
|
|
@@ -133,16 +104,6 @@ def do_robot_load(
|
|
|
133
104
|
yield from wait_for_smargon_not_disabled(composite.smargon)
|
|
134
105
|
|
|
135
106
|
|
|
136
|
-
def raise_exception_if_moved_out_of_cryojet(exception):
|
|
137
|
-
yield from bps.null()
|
|
138
|
-
if isinstance(exception, MoveTooLarge):
|
|
139
|
-
raise Exception(
|
|
140
|
-
f"Moving {exception.axis} back to {exception.position} after \
|
|
141
|
-
robot load would move it out of the cryojet. The max safe \
|
|
142
|
-
distance is {exception.maximum_move}"
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
|
|
146
107
|
def pin_already_loaded(
|
|
147
108
|
robot: BartRobot, sample_location: SampleLocation
|
|
148
109
|
) -> Generator[Msg, None, bool]:
|
|
@@ -158,6 +119,7 @@ def robot_load_and_snapshots(
|
|
|
158
119
|
composite: RobotLoadAndEnergyChangeComposite,
|
|
159
120
|
location: SampleLocation,
|
|
160
121
|
snapshot_directory: Path,
|
|
122
|
+
sample_id: int,
|
|
161
123
|
thawing_time: float,
|
|
162
124
|
demand_energy_ev: float | None,
|
|
163
125
|
):
|
|
@@ -166,37 +128,25 @@ def robot_load_and_snapshots(
|
|
|
166
128
|
robot_load_plan = do_robot_load(
|
|
167
129
|
composite,
|
|
168
130
|
location,
|
|
131
|
+
sample_id,
|
|
169
132
|
demand_energy_ev,
|
|
170
133
|
thawing_time,
|
|
171
134
|
)
|
|
172
135
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
# to the lower gonio and the move is quicker than the robot takes to get to the
|
|
176
|
-
# load position.
|
|
177
|
-
yield from bpp.contingency_wrapper(
|
|
178
|
-
home_and_reset_wrapper(
|
|
179
|
-
robot_load_plan,
|
|
180
|
-
composite.lower_gonio,
|
|
181
|
-
BartRobot.LOAD_TOLERANCE_MM,
|
|
182
|
-
CONST.HARDWARE.CRYOJET_MARGIN_MM,
|
|
183
|
-
"lower_gonio",
|
|
184
|
-
wait_for_all=False,
|
|
185
|
-
),
|
|
186
|
-
except_plan=raise_exception_if_moved_out_of_cryojet,
|
|
136
|
+
gonio_finished = yield from do_plan_while_lower_gonio_at_home(
|
|
137
|
+
robot_load_plan, composite.lower_gonio
|
|
187
138
|
)
|
|
188
|
-
|
|
189
139
|
yield from bps.wait(group="snapshot")
|
|
190
140
|
|
|
191
141
|
yield from take_robot_snapshots(composite.oav, composite.webcam, snapshot_directory)
|
|
192
142
|
|
|
193
|
-
yield from bps.create(name=CONST.DESCRIPTORS.
|
|
194
|
-
yield from bps.read(composite.robot
|
|
143
|
+
yield from bps.create(name=CONST.DESCRIPTORS.ROBOT_UPDATE)
|
|
144
|
+
yield from bps.read(composite.robot)
|
|
195
145
|
yield from bps.read(composite.oav.snapshot)
|
|
196
146
|
yield from bps.read(composite.webcam)
|
|
197
147
|
yield from bps.save()
|
|
198
148
|
|
|
199
|
-
yield from bps.wait(
|
|
149
|
+
yield from bps.wait(gonio_finished)
|
|
200
150
|
|
|
201
151
|
|
|
202
152
|
def robot_load_and_change_energy_plan(
|
|
@@ -218,17 +168,13 @@ def robot_load_and_change_energy_plan(
|
|
|
218
168
|
composite,
|
|
219
169
|
sample_location,
|
|
220
170
|
params.snapshot_directory,
|
|
171
|
+
params.sample_id,
|
|
221
172
|
params.thawing_time,
|
|
222
173
|
params.demand_energy_ev,
|
|
223
174
|
),
|
|
224
175
|
md={
|
|
225
176
|
"subplan_name": CONST.PLAN.ROBOT_LOAD,
|
|
226
|
-
"metadata": {
|
|
227
|
-
"visit": params.visit,
|
|
228
|
-
"sample_id": params.sample_id,
|
|
229
|
-
"sample_puck": sample_location.puck,
|
|
230
|
-
"sample_pin": sample_location.pin,
|
|
231
|
-
},
|
|
177
|
+
"metadata": {"visit": params.visit, "sample_id": params.sample_id},
|
|
232
178
|
"activate_callbacks": [
|
|
233
179
|
"RobotLoadISPyBCallback",
|
|
234
180
|
],
|
|
@@ -18,7 +18,7 @@ from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, MirrorVolta
|
|
|
18
18
|
from dodal.devices.i03 import Beamstop
|
|
19
19
|
from dodal.devices.i03.dcm import DCM
|
|
20
20
|
from dodal.devices.i03.undulator_dcm import UndulatorDCM
|
|
21
|
-
from dodal.devices.motors import
|
|
21
|
+
from dodal.devices.motors import XYZStage
|
|
22
22
|
from dodal.devices.oav.oav_detector import OAV
|
|
23
23
|
from dodal.devices.oav.pin_image_recognition import PinTipDetection
|
|
24
24
|
from dodal.devices.robot import BartRobot, SampleLocation
|
|
@@ -96,7 +96,7 @@ class RobotLoadThenCentreComposite:
|
|
|
96
96
|
# RobotLoad fields
|
|
97
97
|
robot: BartRobot
|
|
98
98
|
webcam: Webcam
|
|
99
|
-
lower_gonio:
|
|
99
|
+
lower_gonio: XYZStage
|
|
100
100
|
beamstop: Beamstop
|
|
101
101
|
|
|
102
102
|
|
|
@@ -37,16 +37,16 @@ from mx_bluesky.common.device_setup_plans.manipulate_sample import (
|
|
|
37
37
|
from mx_bluesky.common.device_setup_plans.utils import (
|
|
38
38
|
start_preparing_data_collection_then_do_plan,
|
|
39
39
|
)
|
|
40
|
+
from mx_bluesky.common.experiment_plans.inner_plans.read_hardware import (
|
|
41
|
+
read_hardware_for_zocalo,
|
|
42
|
+
standard_read_hardware_during_collection,
|
|
43
|
+
standard_read_hardware_pre_collection,
|
|
44
|
+
)
|
|
40
45
|
from mx_bluesky.common.experiment_plans.oav_snapshot_plan import (
|
|
41
46
|
OavSnapshotComposite,
|
|
42
47
|
oav_snapshot_plan,
|
|
43
48
|
setup_beamline_for_OAV,
|
|
44
49
|
)
|
|
45
|
-
from mx_bluesky.common.experiment_plans.read_hardware import (
|
|
46
|
-
read_hardware_for_zocalo,
|
|
47
|
-
standard_read_hardware_during_collection,
|
|
48
|
-
standard_read_hardware_pre_collection,
|
|
49
|
-
)
|
|
50
50
|
from mx_bluesky.common.parameters.components import WithSnapshot
|
|
51
51
|
from mx_bluesky.common.preprocessors.preprocessors import (
|
|
52
52
|
transmission_and_xbpm_feedback_for_collection_decorator,
|
|
@@ -342,10 +342,15 @@ def _move_and_rotation(
|
|
|
342
342
|
|
|
343
343
|
if params.take_snapshots:
|
|
344
344
|
yield from bps.wait(CONST.WAIT.MOVE_GONIO_TO_START)
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
345
|
+
|
|
346
|
+
if not params.use_grid_snapshots:
|
|
347
|
+
yield from setup_beamline_for_OAV(
|
|
348
|
+
composite.smargon,
|
|
349
|
+
composite.backlight,
|
|
350
|
+
composite.aperture_scatterguard,
|
|
351
|
+
wait=True,
|
|
352
|
+
)
|
|
353
|
+
|
|
349
354
|
if params.selected_aperture:
|
|
350
355
|
yield from bps.prepare(
|
|
351
356
|
composite.aperture_scatterguard,
|
|
@@ -148,6 +148,7 @@ def populate_parameters_from_agamemnon(agamemnon_params) -> Sequence[LoadCentreC
|
|
|
148
148
|
"name": "TopNByMaxCount",
|
|
149
149
|
"n": pin_type.expected_number_of_crystals,
|
|
150
150
|
},
|
|
151
|
+
"features": {"use_gpu_results": True},
|
|
151
152
|
"robot_load_then_centre": {
|
|
152
153
|
"storage_directory": str(visit_directory) + "/xraycentring",
|
|
153
154
|
"file_name": file_name,
|
|
@@ -156,7 +157,6 @@ def populate_parameters_from_agamemnon(agamemnon_params) -> Sequence[LoadCentreC
|
|
|
156
157
|
"omega_start_deg": 0.0,
|
|
157
158
|
"chi_start_deg": collection["chi"],
|
|
158
159
|
"transmission_frac": 1.0,
|
|
159
|
-
"features": {"use_gpu_results": True},
|
|
160
160
|
**with_energy_params,
|
|
161
161
|
},
|
|
162
162
|
"multi_rotation_scan": {
|
|
@@ -228,10 +228,10 @@ def update_params_from_agamemnon(parameters: T) -> T:
|
|
|
228
228
|
parameters.robot_load_then_centre.grid_width_um = pin_type.full_width
|
|
229
229
|
parameters.select_centres.n = pin_type.expected_number_of_crystals
|
|
230
230
|
if pin_type != SinglePin():
|
|
231
|
-
#
|
|
232
|
-
#
|
|
233
|
-
# this will give no snapshots but that's preferable
|
|
231
|
+
# Rotation snapshots will be generated from the gridscan snapshots,
|
|
232
|
+
# no need to specify snapshot omega.
|
|
234
233
|
parameters.multi_rotation_scan.snapshot_omegas_deg = []
|
|
234
|
+
parameters.multi_rotation_scan.use_grid_snapshots = True
|
|
235
235
|
except (ValueError, ValidationError) as e:
|
|
236
236
|
LOGGER.warning(f"Failed to update parameters: {e}")
|
|
237
237
|
except Exception as e:
|
|
@@ -29,7 +29,7 @@ from mx_bluesky.common.utils.log import (
|
|
|
29
29
|
_get_logging_dirs,
|
|
30
30
|
tag_filter,
|
|
31
31
|
)
|
|
32
|
-
from mx_bluesky.hyperion.external_interaction.callbacks.
|
|
32
|
+
from mx_bluesky.hyperion.external_interaction.callbacks.robot_actions.ispyb_callback import (
|
|
33
33
|
RobotLoadISPyBCallback,
|
|
34
34
|
)
|
|
35
35
|
from mx_bluesky.hyperion.external_interaction.callbacks.rotation.ispyb_callback import (
|