mx-bluesky 1.1.0__py3-none-any.whl → 1.4.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.
- mx_bluesky/__init__.py +8 -3
- mx_bluesky/__main__.py +12 -7
- mx_bluesky/_version.py +2 -2
- mx_bluesky/beamlines/i04/callbacks/murko_callback.py +14 -4
- mx_bluesky/beamlines/i04/thawing_plan.py +48 -10
- mx_bluesky/beamlines/i24/serial/__init__.py +3 -0
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +68 -90
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +1 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +104 -126
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +139 -162
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +25 -36
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +24 -34
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +14 -11
- mx_bluesky/beamlines/i24/serial/log.py +58 -49
- mx_bluesky/beamlines/i24/serial/parameters/fixed_target/cs/cs_maker.json +3 -3
- mx_bluesky/beamlines/i24/serial/run_extruder.sh +30 -5
- mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +31 -7
- mx_bluesky/beamlines/i24/serial/run_serial.py +24 -8
- mx_bluesky/beamlines/i24/serial/setup_beamline/ca.py +0 -2
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +1 -1
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +8 -18
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +2 -2
- mx_bluesky/common/__init__.py +0 -0
- mx_bluesky/common/device_setup_plans/read_hardware_for_setup.py +14 -0
- mx_bluesky/common/parameters/components.py +221 -0
- mx_bluesky/common/parameters/constants.py +133 -0
- mx_bluesky/common/plans/__init__.py +1 -0
- mx_bluesky/common/plans/do_fgs.py +121 -0
- mx_bluesky/common/utils/log.py +116 -0
- mx_bluesky/{hyperion → common/utils}/tracing.py +2 -2
- mx_bluesky/hyperion/__main__.py +11 -9
- mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +31 -26
- mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +6 -12
- mx_bluesky/hyperion/device_setup_plans/setup_oav.py +6 -12
- mx_bluesky/hyperion/device_setup_plans/setup_panda.py +1 -2
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +48 -17
- mx_bluesky/hyperion/device_setup_plans/smargon.py +6 -6
- mx_bluesky/hyperion/device_setup_plans/utils.py +13 -2
- mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +4 -4
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -0
- mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +59 -108
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +7 -5
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +46 -0
- mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +19 -18
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +8 -5
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +4 -4
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +17 -17
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +241 -0
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +24 -181
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +6 -4
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +3 -11
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +1 -2
- mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +18 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/ispyb_mapping.py +1 -9
- mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +18 -13
- mx_bluesky/hyperion/external_interaction/callbacks/ispyb_callback_base.py +32 -15
- mx_bluesky/hyperion/external_interaction/callbacks/log_uid_tag_callback.py +1 -1
- mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +3 -5
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +4 -3
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +23 -18
- mx_bluesky/hyperion/external_interaction/config_server.py +22 -10
- mx_bluesky/hyperion/external_interaction/ispyb/ispyb_store.py +1 -1
- mx_bluesky/hyperion/external_interaction/ispyb/ispyb_utils.py +0 -2
- mx_bluesky/hyperion/external_interaction/nexus/nexus_utils.py +2 -2
- mx_bluesky/hyperion/external_interaction/nexus/write_nexus.py +1 -1
- mx_bluesky/hyperion/log.py +0 -84
- mx_bluesky/hyperion/parameters/components.py +1 -242
- mx_bluesky/hyperion/parameters/constants.py +22 -118
- mx_bluesky/hyperion/parameters/gridscan.py +20 -11
- mx_bluesky/hyperion/parameters/load_centre_collect.py +50 -0
- mx_bluesky/hyperion/parameters/robot_load.py +16 -0
- mx_bluesky/hyperion/parameters/rotation.py +9 -5
- mx_bluesky/hyperion/utils/utils.py +17 -0
- mx_bluesky/hyperion/utils/validation.py +5 -6
- {mx_bluesky-1.1.0.dist-info → mx_bluesky-1.4.0.dist-info}/METADATA +4 -2
- {mx_bluesky-1.1.0.dist-info → mx_bluesky-1.4.0.dist-info}/RECORD +80 -70
- {mx_bluesky-1.1.0.dist-info → mx_bluesky-1.4.0.dist-info}/WHEEL +1 -1
- mx_bluesky/example.py +0 -19
- {mx_bluesky-1.1.0.dist-info → mx_bluesky-1.4.0.dist-info}/LICENSE +0 -0
- {mx_bluesky-1.1.0.dist-info → mx_bluesky-1.4.0.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.1.0.dist-info → mx_bluesky-1.4.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
from collections.abc import Generator
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import cast
|
|
8
|
+
|
|
9
|
+
import bluesky.plan_stubs as bps
|
|
10
|
+
import bluesky.preprocessors as bpp
|
|
11
|
+
from blueapi.core import BlueskyContext
|
|
12
|
+
from bluesky.utils import Msg
|
|
13
|
+
from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureValue
|
|
14
|
+
from dodal.devices.attenuator import Attenuator
|
|
15
|
+
from dodal.devices.dcm import DCM
|
|
16
|
+
from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, MirrorVoltages
|
|
17
|
+
from dodal.devices.motors import XYZPositioner
|
|
18
|
+
from dodal.devices.oav.oav_detector import OAV
|
|
19
|
+
from dodal.devices.robot import BartRobot, SampleLocation
|
|
20
|
+
from dodal.devices.smargon import Smargon, StubPosition
|
|
21
|
+
from dodal.devices.thawer import Thawer
|
|
22
|
+
from dodal.devices.undulator_dcm import UndulatorDCM
|
|
23
|
+
from dodal.devices.webcam import Webcam
|
|
24
|
+
from dodal.devices.xbpm_feedback import XBPMFeedback
|
|
25
|
+
from dodal.plans.motor_util_plans import MoveTooLarge, home_and_reset_wrapper
|
|
26
|
+
|
|
27
|
+
from mx_bluesky.hyperion.experiment_plans.set_energy_plan import (
|
|
28
|
+
SetEnergyComposite,
|
|
29
|
+
set_energy_plan,
|
|
30
|
+
)
|
|
31
|
+
from mx_bluesky.hyperion.log import LOGGER
|
|
32
|
+
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
33
|
+
from mx_bluesky.hyperion.parameters.robot_load import RobotLoadAndEnergyChange
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclasses.dataclass
|
|
37
|
+
class RobotLoadAndEnergyChangeComposite:
|
|
38
|
+
# SetEnergyComposite fields
|
|
39
|
+
vfm: FocusingMirrorWithStripes
|
|
40
|
+
mirror_voltages: MirrorVoltages
|
|
41
|
+
dcm: DCM
|
|
42
|
+
undulator_dcm: UndulatorDCM
|
|
43
|
+
xbpm_feedback: XBPMFeedback
|
|
44
|
+
attenuator: Attenuator
|
|
45
|
+
|
|
46
|
+
# RobotLoad fields
|
|
47
|
+
robot: BartRobot
|
|
48
|
+
webcam: Webcam
|
|
49
|
+
lower_gonio: XYZPositioner
|
|
50
|
+
thawer: Thawer
|
|
51
|
+
oav: OAV
|
|
52
|
+
smargon: Smargon
|
|
53
|
+
aperture_scatterguard: ApertureScatterguard
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def create_devices(context: BlueskyContext) -> RobotLoadAndEnergyChangeComposite:
|
|
57
|
+
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
58
|
+
|
|
59
|
+
return device_composite_from_context(context, RobotLoadAndEnergyChangeComposite)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def wait_for_smargon_not_disabled(smargon: Smargon, timeout=60):
|
|
63
|
+
"""Waits for the smargon disabled flag to go low. The robot hardware is responsible
|
|
64
|
+
for setting this to low when it is safe to move. It does this through a physical
|
|
65
|
+
connection between the robot and the smargon.
|
|
66
|
+
"""
|
|
67
|
+
LOGGER.info("Waiting for smargon enabled")
|
|
68
|
+
SLEEP_PER_CHECK = 0.1
|
|
69
|
+
times_to_check = int(timeout / SLEEP_PER_CHECK)
|
|
70
|
+
for _ in range(times_to_check):
|
|
71
|
+
smargon_disabled = yield from bps.rd(smargon.disabled)
|
|
72
|
+
if not smargon_disabled:
|
|
73
|
+
LOGGER.info("Smargon now enabled")
|
|
74
|
+
return
|
|
75
|
+
yield from bps.sleep(SLEEP_PER_CHECK)
|
|
76
|
+
raise TimeoutError(
|
|
77
|
+
"Timed out waiting for smargon to become enabled after robot load"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def take_robot_snapshots(oav: OAV, webcam: Webcam, directory: Path):
|
|
82
|
+
time_now = datetime.now()
|
|
83
|
+
snapshot_format = f"{time_now.strftime('%H%M%S')}_{{device}}_after_load"
|
|
84
|
+
for device in [oav.snapshot, webcam]:
|
|
85
|
+
yield from bps.abs_set(
|
|
86
|
+
device.filename, snapshot_format.format(device=device.name)
|
|
87
|
+
)
|
|
88
|
+
yield from bps.abs_set(device.directory, str(directory))
|
|
89
|
+
# Note: should be able to use `wait=True` after https://github.com/bluesky/bluesky/issues/1795
|
|
90
|
+
yield from bps.trigger(device, group="snapshots")
|
|
91
|
+
yield from bps.wait("snapshots")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def prepare_for_robot_load(
|
|
95
|
+
aperture_scatterguard: ApertureScatterguard, smargon: Smargon
|
|
96
|
+
):
|
|
97
|
+
yield from bps.abs_set(
|
|
98
|
+
aperture_scatterguard,
|
|
99
|
+
ApertureValue.ROBOT_LOAD,
|
|
100
|
+
group="prepare_robot_load",
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
yield from bps.mv(smargon.stub_offsets, StubPosition.RESET_TO_ROBOT_LOAD) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
104
|
+
|
|
105
|
+
# fmt: off
|
|
106
|
+
yield from bps.mv(
|
|
107
|
+
smargon.x, 0, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
108
|
+
smargon.y, 0, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
109
|
+
smargon.z, 0, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
110
|
+
smargon.omega, 0, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
111
|
+
smargon.chi, 0, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
112
|
+
smargon.phi, 0 # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
113
|
+
)
|
|
114
|
+
# fmt: on
|
|
115
|
+
|
|
116
|
+
yield from bps.wait("prepare_robot_load")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def do_robot_load(
|
|
120
|
+
composite: RobotLoadAndEnergyChangeComposite,
|
|
121
|
+
sample_location: SampleLocation,
|
|
122
|
+
demand_energy_ev: float | None,
|
|
123
|
+
thawing_time: float,
|
|
124
|
+
):
|
|
125
|
+
yield from bps.abs_set(
|
|
126
|
+
composite.robot,
|
|
127
|
+
sample_location,
|
|
128
|
+
group="robot_load",
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
if demand_energy_ev:
|
|
132
|
+
yield from set_energy_plan(
|
|
133
|
+
demand_energy_ev / 1000,
|
|
134
|
+
cast(SetEnergyComposite, composite),
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
yield from bps.wait("robot_load")
|
|
138
|
+
|
|
139
|
+
yield from bps.abs_set(
|
|
140
|
+
composite.thawer.thaw_for_time_s, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
141
|
+
thawing_time,
|
|
142
|
+
group="thawing_finished",
|
|
143
|
+
)
|
|
144
|
+
yield from wait_for_smargon_not_disabled(composite.smargon)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def raise_exception_if_moved_out_of_cryojet(exception):
|
|
148
|
+
yield from bps.null()
|
|
149
|
+
if isinstance(exception, MoveTooLarge):
|
|
150
|
+
raise Exception(
|
|
151
|
+
f"Moving {exception.axis} back to {exception.position} after \
|
|
152
|
+
robot load would move it out of the cryojet. The max safe \
|
|
153
|
+
distance is {exception.maximum_move}"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def pin_already_loaded(
|
|
158
|
+
robot: BartRobot, sample_location: SampleLocation
|
|
159
|
+
) -> Generator[Msg, None, bool]:
|
|
160
|
+
current_puck = yield from bps.rd(robot.current_puck)
|
|
161
|
+
current_pin = yield from bps.rd(robot.current_pin)
|
|
162
|
+
return (
|
|
163
|
+
int(current_puck) == sample_location.puck
|
|
164
|
+
and int(current_pin) == sample_location.pin
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def robot_load_and_snapshots(
|
|
169
|
+
composite: RobotLoadAndEnergyChangeComposite,
|
|
170
|
+
location: SampleLocation,
|
|
171
|
+
snapshot_directory: Path,
|
|
172
|
+
thawing_time: float,
|
|
173
|
+
demand_energy_ev: float | None,
|
|
174
|
+
):
|
|
175
|
+
robot_load_plan = do_robot_load(
|
|
176
|
+
composite,
|
|
177
|
+
location,
|
|
178
|
+
demand_energy_ev,
|
|
179
|
+
thawing_time,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# The lower gonio must be in the correct position for the robot load and we
|
|
183
|
+
# want to put it back afterwards. Note we don't wait the robot is interlocked
|
|
184
|
+
# to the lower gonio and the move is quicker than the robot takes to get to the
|
|
185
|
+
# load position.
|
|
186
|
+
yield from bpp.contingency_wrapper(
|
|
187
|
+
home_and_reset_wrapper(
|
|
188
|
+
robot_load_plan,
|
|
189
|
+
composite.lower_gonio,
|
|
190
|
+
BartRobot.LOAD_TOLERANCE_MM,
|
|
191
|
+
CONST.HARDWARE.CRYOJET_MARGIN_MM,
|
|
192
|
+
"lower_gonio",
|
|
193
|
+
wait_for_all=False,
|
|
194
|
+
),
|
|
195
|
+
except_plan=raise_exception_if_moved_out_of_cryojet,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
yield from take_robot_snapshots(composite.oav, composite.webcam, snapshot_directory)
|
|
199
|
+
|
|
200
|
+
yield from bps.create(name=CONST.DESCRIPTORS.ROBOT_LOAD)
|
|
201
|
+
yield from bps.read(composite.robot.barcode)
|
|
202
|
+
yield from bps.read(composite.oav.snapshot) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
203
|
+
yield from bps.read(composite.webcam)
|
|
204
|
+
yield from bps.save()
|
|
205
|
+
|
|
206
|
+
yield from bps.wait("reset-lower_gonio")
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def robot_load_and_change_energy_plan(
|
|
210
|
+
composite: RobotLoadAndEnergyChangeComposite,
|
|
211
|
+
params: RobotLoadAndEnergyChange,
|
|
212
|
+
):
|
|
213
|
+
assert params.sample_puck is not None
|
|
214
|
+
assert params.sample_pin is not None
|
|
215
|
+
|
|
216
|
+
sample_location = SampleLocation(params.sample_puck, params.sample_pin)
|
|
217
|
+
|
|
218
|
+
yield from prepare_for_robot_load(
|
|
219
|
+
composite.aperture_scatterguard, composite.smargon
|
|
220
|
+
)
|
|
221
|
+
yield from bpp.run_wrapper(
|
|
222
|
+
robot_load_and_snapshots(
|
|
223
|
+
composite,
|
|
224
|
+
sample_location,
|
|
225
|
+
params.snapshot_directory,
|
|
226
|
+
params.thawing_time,
|
|
227
|
+
params.demand_energy_ev,
|
|
228
|
+
),
|
|
229
|
+
md={
|
|
230
|
+
"subplan_name": CONST.PLAN.ROBOT_LOAD,
|
|
231
|
+
"metadata": {
|
|
232
|
+
"visit": params.visit,
|
|
233
|
+
"sample_id": params.sample_id,
|
|
234
|
+
"sample_puck": sample_location.puck,
|
|
235
|
+
"sample_pin": sample_location.pin,
|
|
236
|
+
},
|
|
237
|
+
"activate_callbacks": [
|
|
238
|
+
"RobotLoadISPyBCallback",
|
|
239
|
+
],
|
|
240
|
+
},
|
|
241
|
+
)
|
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import dataclasses
|
|
4
|
-
from collections.abc import Generator
|
|
5
|
-
from datetime import datetime
|
|
6
|
-
from pathlib import Path
|
|
7
4
|
from typing import cast
|
|
8
5
|
|
|
9
|
-
import bluesky.plan_stubs as bps
|
|
10
|
-
import bluesky.preprocessors as bpp
|
|
11
6
|
from blueapi.core import BlueskyContext, MsgGenerator
|
|
12
|
-
from
|
|
13
|
-
from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureValue
|
|
7
|
+
from dodal.devices.aperturescatterguard import ApertureScatterguard
|
|
14
8
|
from dodal.devices.attenuator import Attenuator
|
|
15
9
|
from dodal.devices.backlight import Backlight
|
|
16
10
|
from dodal.devices.dcm import DCM
|
|
@@ -18,13 +12,13 @@ from dodal.devices.detector.detector_motion import DetectorMotion
|
|
|
18
12
|
from dodal.devices.eiger import EigerDetector
|
|
19
13
|
from dodal.devices.fast_grid_scan import PandAFastGridScan, ZebraFastGridScan
|
|
20
14
|
from dodal.devices.flux import Flux
|
|
21
|
-
from dodal.devices.focusing_mirror import FocusingMirrorWithStripes,
|
|
15
|
+
from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, MirrorVoltages
|
|
22
16
|
from dodal.devices.motors import XYZPositioner
|
|
23
17
|
from dodal.devices.oav.oav_detector import OAV
|
|
24
18
|
from dodal.devices.oav.pin_image_recognition import PinTipDetection
|
|
25
19
|
from dodal.devices.robot import BartRobot, SampleLocation
|
|
26
20
|
from dodal.devices.s4_slit_gaps import S4SlitGaps
|
|
27
|
-
from dodal.devices.smargon import Smargon
|
|
21
|
+
from dodal.devices.smargon import Smargon
|
|
28
22
|
from dodal.devices.synchrotron import Synchrotron
|
|
29
23
|
from dodal.devices.thawer import Thawer
|
|
30
24
|
from dodal.devices.undulator import Undulator
|
|
@@ -34,10 +28,12 @@ from dodal.devices.xbpm_feedback import XBPMFeedback
|
|
|
34
28
|
from dodal.devices.zebra import Zebra
|
|
35
29
|
from dodal.devices.zebra_controlled_shutter import ZebraShutter
|
|
36
30
|
from dodal.devices.zocalo import ZocaloResults
|
|
37
|
-
from dodal.
|
|
31
|
+
from dodal.log import LOGGER
|
|
38
32
|
from ophyd_async.fastcs.panda import HDFPanda
|
|
39
33
|
|
|
34
|
+
from mx_bluesky.common.parameters.constants import OavConstants
|
|
40
35
|
from mx_bluesky.hyperion.device_setup_plans.utils import (
|
|
36
|
+
fill_in_energy_if_not_supplied,
|
|
41
37
|
start_preparing_data_collection_then_do_plan,
|
|
42
38
|
)
|
|
43
39
|
from mx_bluesky.hyperion.experiment_plans.grid_detect_then_xray_centre_plan import (
|
|
@@ -46,12 +42,11 @@ from mx_bluesky.hyperion.experiment_plans.grid_detect_then_xray_centre_plan impo
|
|
|
46
42
|
from mx_bluesky.hyperion.experiment_plans.pin_centre_then_xray_centre_plan import (
|
|
47
43
|
pin_centre_then_xray_centre_plan,
|
|
48
44
|
)
|
|
49
|
-
from mx_bluesky.hyperion.experiment_plans.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
from mx_bluesky.hyperion.experiment_plans.robot_load_and_change_energy import (
|
|
46
|
+
RobotLoadAndEnergyChangeComposite,
|
|
47
|
+
pin_already_loaded,
|
|
48
|
+
robot_load_and_change_energy_plan,
|
|
53
49
|
)
|
|
54
|
-
from mx_bluesky.hyperion.log import LOGGER
|
|
55
50
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
56
51
|
from mx_bluesky.hyperion.parameters.gridscan import RobotLoadThenCentre
|
|
57
52
|
|
|
@@ -84,7 +79,7 @@ class RobotLoadThenCentreComposite:
|
|
|
84
79
|
|
|
85
80
|
# SetEnergyComposite fields
|
|
86
81
|
vfm: FocusingMirrorWithStripes
|
|
87
|
-
|
|
82
|
+
mirror_voltages: MirrorVoltages
|
|
88
83
|
dcm: DCM
|
|
89
84
|
undulator_dcm: UndulatorDCM
|
|
90
85
|
|
|
@@ -100,147 +95,10 @@ def create_devices(context: BlueskyContext) -> RobotLoadThenCentreComposite:
|
|
|
100
95
|
return device_composite_from_context(context, RobotLoadThenCentreComposite)
|
|
101
96
|
|
|
102
97
|
|
|
103
|
-
def wait_for_smargon_not_disabled(smargon: Smargon, timeout=60):
|
|
104
|
-
"""Waits for the smargon disabled flag to go low. The robot hardware is responsible
|
|
105
|
-
for setting this to low when it is safe to move. It does this through a physical
|
|
106
|
-
connection between the robot and the smargon.
|
|
107
|
-
"""
|
|
108
|
-
LOGGER.info("Waiting for smargon enabled")
|
|
109
|
-
SLEEP_PER_CHECK = 0.1
|
|
110
|
-
times_to_check = int(timeout / SLEEP_PER_CHECK)
|
|
111
|
-
for _ in range(times_to_check):
|
|
112
|
-
smargon_disabled = yield from bps.rd(smargon.disabled)
|
|
113
|
-
if not smargon_disabled:
|
|
114
|
-
LOGGER.info("Smargon now enabled")
|
|
115
|
-
return
|
|
116
|
-
yield from bps.sleep(SLEEP_PER_CHECK)
|
|
117
|
-
raise TimeoutError(
|
|
118
|
-
"Timed out waiting for smargon to become enabled after robot load"
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def take_robot_snapshots(oav: OAV, webcam: Webcam, directory: Path):
|
|
123
|
-
time_now = datetime.now()
|
|
124
|
-
snapshot_format = f"{time_now.strftime('%H%M%S')}_{{device}}_after_load"
|
|
125
|
-
for device in [oav.snapshot, webcam]:
|
|
126
|
-
yield from bps.abs_set(
|
|
127
|
-
device.filename, snapshot_format.format(device=device.name)
|
|
128
|
-
)
|
|
129
|
-
yield from bps.abs_set(device.directory, str(directory))
|
|
130
|
-
# Note: should be able to use `wait=True` after https://github.com/bluesky/bluesky/issues/1795
|
|
131
|
-
yield from bps.trigger(device, group="snapshots")
|
|
132
|
-
yield from bps.wait("snapshots")
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
def prepare_for_robot_load(composite: RobotLoadThenCentreComposite):
|
|
136
|
-
yield from bps.abs_set(
|
|
137
|
-
composite.aperture_scatterguard,
|
|
138
|
-
ApertureValue.ROBOT_LOAD,
|
|
139
|
-
group="prepare_robot_load",
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
yield from bps.mv(composite.smargon.stub_offsets, StubPosition.RESET_TO_ROBOT_LOAD)
|
|
143
|
-
|
|
144
|
-
# fmt: off
|
|
145
|
-
yield from bps.mv(composite.smargon.x, 0,
|
|
146
|
-
composite.smargon.y, 0,
|
|
147
|
-
composite.smargon.z, 0,
|
|
148
|
-
composite.smargon.omega, 0,
|
|
149
|
-
composite.smargon.chi, 0,
|
|
150
|
-
composite.smargon.phi, 0)
|
|
151
|
-
# fmt: on
|
|
152
|
-
|
|
153
|
-
yield from bps.wait("prepare_robot_load")
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
def do_robot_load(
|
|
157
|
-
composite: RobotLoadThenCentreComposite,
|
|
158
|
-
sample_location: SampleLocation,
|
|
159
|
-
demand_energy_ev: float | None,
|
|
160
|
-
thawing_time: float,
|
|
161
|
-
):
|
|
162
|
-
yield from bps.abs_set(
|
|
163
|
-
composite.robot,
|
|
164
|
-
sample_location,
|
|
165
|
-
group="robot_load",
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
if demand_energy_ev:
|
|
169
|
-
yield from set_energy_plan(
|
|
170
|
-
demand_energy_ev / 1000,
|
|
171
|
-
cast(SetEnergyComposite, composite),
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
yield from bps.wait("robot_load")
|
|
175
|
-
|
|
176
|
-
yield from bps.abs_set(
|
|
177
|
-
composite.thawer.thaw_for_time_s, thawing_time, group="thawing_finished"
|
|
178
|
-
)
|
|
179
|
-
yield from wait_for_smargon_not_disabled(composite.smargon)
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
def raise_exception_if_moved_out_of_cryojet(exception):
|
|
183
|
-
yield from bps.null()
|
|
184
|
-
if isinstance(exception, MoveTooLarge):
|
|
185
|
-
raise Exception(
|
|
186
|
-
f"Moving {exception.axis} back to {exception.position} after \
|
|
187
|
-
robot load would move it out of the cryojet. The max safe \
|
|
188
|
-
distance is {exception.maximum_move}"
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
def _pin_already_loaded(
|
|
193
|
-
robot: BartRobot, pin_to_load: int, puck_to_load: int
|
|
194
|
-
) -> Generator[Msg, None, bool]:
|
|
195
|
-
current_puck = yield from bps.rd(robot.current_puck)
|
|
196
|
-
current_pin = yield from bps.rd(robot.current_pin)
|
|
197
|
-
return int(current_puck) == puck_to_load and int(current_pin) == pin_to_load
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
def robot_load_and_snapshots(
|
|
201
|
-
composite: RobotLoadThenCentreComposite,
|
|
202
|
-
params: RobotLoadThenCentre,
|
|
203
|
-
location: SampleLocation,
|
|
204
|
-
):
|
|
205
|
-
robot_load_plan = do_robot_load(
|
|
206
|
-
composite,
|
|
207
|
-
location,
|
|
208
|
-
params.demand_energy_ev,
|
|
209
|
-
params.thawing_time,
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
# The lower gonio must be in the correct position for the robot load and we
|
|
213
|
-
# want to put it back afterwards. Note we don't wait the robot is interlocked
|
|
214
|
-
# to the lower gonio and the move is quicker than the robot takes to get to the
|
|
215
|
-
# load position.
|
|
216
|
-
yield from bpp.contingency_wrapper(
|
|
217
|
-
home_and_reset_wrapper(
|
|
218
|
-
robot_load_plan,
|
|
219
|
-
composite.lower_gonio,
|
|
220
|
-
BartRobot.LOAD_TOLERANCE_MM,
|
|
221
|
-
CONST.HARDWARE.CRYOJET_MARGIN_MM,
|
|
222
|
-
"lower_gonio",
|
|
223
|
-
wait_for_all=False,
|
|
224
|
-
),
|
|
225
|
-
except_plan=raise_exception_if_moved_out_of_cryojet,
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
yield from take_robot_snapshots(
|
|
229
|
-
composite.oav, composite.webcam, params.snapshot_directory
|
|
230
|
-
)
|
|
231
|
-
|
|
232
|
-
yield from bps.create(name=CONST.DESCRIPTORS.ROBOT_LOAD)
|
|
233
|
-
yield from bps.read(composite.robot.barcode)
|
|
234
|
-
yield from bps.read(composite.oav.snapshot)
|
|
235
|
-
yield from bps.read(composite.webcam)
|
|
236
|
-
yield from bps.save()
|
|
237
|
-
|
|
238
|
-
yield from bps.wait("reset-lower_gonio")
|
|
239
|
-
|
|
240
|
-
|
|
241
98
|
def centring_plan_from_robot_load_params(
|
|
242
99
|
composite: RobotLoadThenCentreComposite,
|
|
243
100
|
params: RobotLoadThenCentre,
|
|
101
|
+
oav_config_file: str = OavConstants.OAV_CONFIG_JSON,
|
|
244
102
|
):
|
|
245
103
|
yield from pin_centre_then_xray_centre_plan(
|
|
246
104
|
cast(GridDetectThenXRayCentreComposite, composite),
|
|
@@ -251,26 +109,14 @@ def centring_plan_from_robot_load_params(
|
|
|
251
109
|
def robot_load_then_centre_plan(
|
|
252
110
|
composite: RobotLoadThenCentreComposite,
|
|
253
111
|
params: RobotLoadThenCentre,
|
|
254
|
-
|
|
112
|
+
oav_config_file: str = OavConstants.OAV_CONFIG_JSON,
|
|
255
113
|
):
|
|
256
|
-
yield from
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
md={
|
|
260
|
-
"subplan_name": CONST.PLAN.ROBOT_LOAD,
|
|
261
|
-
"metadata": {
|
|
262
|
-
"visit_path": str(params.visit_directory),
|
|
263
|
-
"sample_id": params.sample_id,
|
|
264
|
-
"sample_puck": params.sample_puck,
|
|
265
|
-
"sample_pin": params.sample_pin,
|
|
266
|
-
},
|
|
267
|
-
"activate_callbacks": [
|
|
268
|
-
"RobotLoadISPyBCallback",
|
|
269
|
-
],
|
|
270
|
-
},
|
|
114
|
+
yield from robot_load_and_change_energy_plan(
|
|
115
|
+
cast(RobotLoadAndEnergyChangeComposite, composite),
|
|
116
|
+
params.robot_load_params(),
|
|
271
117
|
)
|
|
272
118
|
|
|
273
|
-
yield from centring_plan_from_robot_load_params(composite, params)
|
|
119
|
+
yield from centring_plan_from_robot_load_params(composite, params, oav_config_file)
|
|
274
120
|
|
|
275
121
|
|
|
276
122
|
def robot_load_then_centre(
|
|
@@ -283,10 +129,10 @@ def robot_load_then_centre(
|
|
|
283
129
|
assert parameters.sample_puck is not None
|
|
284
130
|
assert parameters.sample_pin is not None
|
|
285
131
|
|
|
132
|
+
sample_location = SampleLocation(parameters.sample_puck, parameters.sample_pin)
|
|
133
|
+
|
|
286
134
|
doing_sample_load = not (
|
|
287
|
-
yield from
|
|
288
|
-
composite.robot, parameters.sample_pin, parameters.sample_puck
|
|
289
|
-
)
|
|
135
|
+
yield from pin_already_loaded(composite.robot, sample_location)
|
|
290
136
|
)
|
|
291
137
|
|
|
292
138
|
doing_chi_change = parameters.chi_start_deg is not None
|
|
@@ -295,7 +141,6 @@ def robot_load_then_centre(
|
|
|
295
141
|
plan = robot_load_then_centre_plan(
|
|
296
142
|
composite,
|
|
297
143
|
parameters,
|
|
298
|
-
SampleLocation(parameters.sample_puck, parameters.sample_pin),
|
|
299
144
|
)
|
|
300
145
|
LOGGER.info("Pin not loaded, loading and centring")
|
|
301
146
|
elif doing_chi_change:
|
|
@@ -305,12 +150,10 @@ def robot_load_then_centre(
|
|
|
305
150
|
LOGGER.info("Pin already loaded and chi not changed so doing nothing")
|
|
306
151
|
return
|
|
307
152
|
|
|
308
|
-
detector_params =
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
)
|
|
313
|
-
detector_params.expected_energy_ev = actual_energy_ev
|
|
153
|
+
detector_params = yield from fill_in_energy_if_not_supplied(
|
|
154
|
+
composite.dcm, parameters.detector_params
|
|
155
|
+
)
|
|
156
|
+
|
|
314
157
|
eiger.set_detector_parameters(detector_params)
|
|
315
158
|
|
|
316
159
|
yield from start_preparing_data_collection_then_do_plan(
|
|
@@ -24,6 +24,9 @@ from dodal.devices.zebra import RotationDirection, Zebra
|
|
|
24
24
|
from dodal.devices.zebra_controlled_shutter import ZebraShutter
|
|
25
25
|
from dodal.plans.check_topup import check_topup_and_wait_if_necessary
|
|
26
26
|
|
|
27
|
+
from mx_bluesky.common.device_setup_plans.read_hardware_for_setup import (
|
|
28
|
+
read_hardware_for_zocalo,
|
|
29
|
+
)
|
|
27
30
|
from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import (
|
|
28
31
|
cleanup_sample_environment,
|
|
29
32
|
move_phi_chi_omega,
|
|
@@ -32,7 +35,6 @@ from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import (
|
|
|
32
35
|
)
|
|
33
36
|
from mx_bluesky.hyperion.device_setup_plans.read_hardware_for_setup import (
|
|
34
37
|
read_hardware_during_collection,
|
|
35
|
-
read_hardware_for_zocalo,
|
|
36
38
|
read_hardware_pre_collection,
|
|
37
39
|
)
|
|
38
40
|
from mx_bluesky.hyperion.device_setup_plans.setup_zebra import (
|
|
@@ -252,7 +254,7 @@ def rotation_scan_plan(
|
|
|
252
254
|
composite.undulator,
|
|
253
255
|
composite.synchrotron,
|
|
254
256
|
composite.s4_slit_gaps,
|
|
255
|
-
composite.
|
|
257
|
+
composite.dcm,
|
|
256
258
|
composite.smargon,
|
|
257
259
|
)
|
|
258
260
|
|
|
@@ -348,7 +350,7 @@ def rotation_scan(
|
|
|
348
350
|
md={
|
|
349
351
|
"subplan_name": CONST.PLAN.ROTATION_OUTER,
|
|
350
352
|
CONST.TRIGGER.ZOCALO: CONST.PLAN.ROTATION_MAIN,
|
|
351
|
-
"zocalo_environment":
|
|
353
|
+
"zocalo_environment": CONST.ZOCALO_ENV,
|
|
352
354
|
"hyperion_parameters": parameters.model_dump_json(),
|
|
353
355
|
"activate_callbacks": [
|
|
354
356
|
"RotationISPyBCallback",
|
|
@@ -379,7 +381,7 @@ def rotation_scan(
|
|
|
379
381
|
rotation_with_cleanup_and_stage(params),
|
|
380
382
|
group=CONST.WAIT.ROTATION_READY_FOR_DC,
|
|
381
383
|
)
|
|
382
|
-
yield from bps.unstage(eiger)
|
|
384
|
+
yield from bps.unstage(eiger) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
383
385
|
|
|
384
386
|
yield from rotation_scan_plan_with_stage_and_cleanup(parameters)
|
|
385
387
|
|
|
@@ -6,14 +6,11 @@
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import dataclasses
|
|
9
|
-
from collections.abc import Generator
|
|
10
|
-
from typing import Any
|
|
11
9
|
|
|
12
10
|
from bluesky import plan_stubs as bps
|
|
13
|
-
from bluesky.utils import Msg
|
|
14
11
|
from dodal.devices.attenuator import Attenuator
|
|
15
12
|
from dodal.devices.dcm import DCM
|
|
16
|
-
from dodal.devices.focusing_mirror import FocusingMirrorWithStripes,
|
|
13
|
+
from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, MirrorVoltages
|
|
17
14
|
from dodal.devices.undulator_dcm import UndulatorDCM
|
|
18
15
|
from dodal.devices.xbpm_feedback import XBPMFeedback
|
|
19
16
|
|
|
@@ -30,7 +27,7 @@ UNDULATOR_GROUP = "UNDULATOR_GROUP"
|
|
|
30
27
|
@dataclasses.dataclass
|
|
31
28
|
class SetEnergyComposite:
|
|
32
29
|
vfm: FocusingMirrorWithStripes
|
|
33
|
-
|
|
30
|
+
mirror_voltages: MirrorVoltages
|
|
34
31
|
dcm: DCM
|
|
35
32
|
undulator_dcm: UndulatorDCM
|
|
36
33
|
xbpm_feedback: XBPMFeedback
|
|
@@ -45,17 +42,12 @@ def _set_energy_plan(
|
|
|
45
42
|
yield from dcm_pitch_roll_mirror_adjuster.adjust_dcm_pitch_roll_vfm_from_lut(
|
|
46
43
|
composite.undulator_dcm,
|
|
47
44
|
composite.vfm,
|
|
48
|
-
composite.
|
|
45
|
+
composite.mirror_voltages,
|
|
49
46
|
energy_kev,
|
|
50
47
|
)
|
|
51
48
|
yield from bps.wait(group=UNDULATOR_GROUP)
|
|
52
49
|
|
|
53
50
|
|
|
54
|
-
def read_energy(composite: SetEnergyComposite) -> Generator[Msg, Any, float]:
|
|
55
|
-
"""Obtain the energy in kev"""
|
|
56
|
-
return (yield from bps.rd(composite.dcm.energy_in_kev)) # type: ignore
|
|
57
|
-
|
|
58
|
-
|
|
59
51
|
def set_energy_plan(
|
|
60
52
|
energy_kev,
|
|
61
53
|
composite: SetEnergyComposite,
|
|
@@ -7,6 +7,7 @@ from bluesky.callbacks.zmq import Proxy, RemoteDispatcher
|
|
|
7
7
|
from dodal.log import LOGGER as dodal_logger
|
|
8
8
|
from dodal.log import set_up_all_logging_handlers
|
|
9
9
|
|
|
10
|
+
from mx_bluesky.common.utils.log import _get_logging_dir, tag_filter
|
|
10
11
|
from mx_bluesky.hyperion.external_interaction.callbacks.log_uid_tag_callback import (
|
|
11
12
|
LogUidTaggingCallback,
|
|
12
13
|
)
|
|
@@ -31,8 +32,6 @@ from mx_bluesky.hyperion.external_interaction.callbacks.zocalo_callback import (
|
|
|
31
32
|
from mx_bluesky.hyperion.log import (
|
|
32
33
|
ISPYB_LOGGER,
|
|
33
34
|
NEXUS_LOGGER,
|
|
34
|
-
_get_logging_dir,
|
|
35
|
-
tag_filter,
|
|
36
35
|
)
|
|
37
36
|
from mx_bluesky.hyperion.parameters.cli import parse_callback_dev_mode_arg
|
|
38
37
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
@@ -44,3 +44,21 @@ def create_rotation_callbacks() -> (
|
|
|
44
44
|
tuple[RotationNexusFileCallback, RotationISPyBCallback]
|
|
45
45
|
):
|
|
46
46
|
return (RotationNexusFileCallback(), RotationISPyBCallback(emit=ZocaloCallback()))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def create_load_centre_collect_callbacks() -> (
|
|
50
|
+
tuple[
|
|
51
|
+
GridscanNexusFileCallback,
|
|
52
|
+
GridscanISPyBCallback,
|
|
53
|
+
RobotLoadISPyBCallback,
|
|
54
|
+
RotationNexusFileCallback,
|
|
55
|
+
RotationISPyBCallback,
|
|
56
|
+
]
|
|
57
|
+
):
|
|
58
|
+
return (
|
|
59
|
+
GridscanNexusFileCallback(),
|
|
60
|
+
GridscanISPyBCallback(emit=ZocaloCallback()),
|
|
61
|
+
RobotLoadISPyBCallback(),
|
|
62
|
+
RotationNexusFileCallback(),
|
|
63
|
+
RotationISPyBCallback(emit=ZocaloCallback()),
|
|
64
|
+
)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
|
|
3
|
+
from mx_bluesky.common.parameters.components import DiffractionExperimentWithSample
|
|
5
4
|
from mx_bluesky.hyperion.external_interaction.ispyb.data_model import (
|
|
6
5
|
DataCollectionGroupInfo,
|
|
7
6
|
DataCollectionInfo,
|
|
@@ -11,10 +10,8 @@ from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_store import (
|
|
|
11
10
|
I03_EIGER_DETECTOR,
|
|
12
11
|
)
|
|
13
12
|
from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_utils import (
|
|
14
|
-
VISIT_PATH_REGEX,
|
|
15
13
|
get_current_time_string,
|
|
16
14
|
)
|
|
17
|
-
from mx_bluesky.hyperion.parameters.components import DiffractionExperimentWithSample
|
|
18
15
|
|
|
19
16
|
|
|
20
17
|
def populate_data_collection_group(params: DiffractionExperimentWithSample):
|
|
@@ -63,8 +60,3 @@ def get_proposal_and_session_from_visit_string(visit_string: str) -> tuple[str,
|
|
|
63
60
|
visit_parts = visit_string.split("-")
|
|
64
61
|
assert len(visit_parts) == 2, f"Unexpected visit string {visit_string}"
|
|
65
62
|
return visit_parts[0], int(visit_parts[1])
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def get_visit_string_from_path(path: str | None) -> str | None:
|
|
69
|
-
match = re.search(VISIT_PATH_REGEX, path) if path else None
|
|
70
|
-
return str(match.group(1)) if match else None
|