mx-bluesky 1.4.4__py3-none-any.whl → 1.4.6__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 +9 -4
- mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +14 -3
- mx_bluesky/beamlines/i04/thawing_plan.py +9 -13
- mx_bluesky/beamlines/i24/serial/__init__.py +14 -0
- mx_bluesky/beamlines/i24/serial/dcid.py +3 -1
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +1 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +6 -3
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +11 -11
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +3 -3
- mx_bluesky/beamlines/i24/serial/log.py +0 -1
- mx_bluesky/beamlines/i24/serial/parameters/constants.py +1 -1
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +3 -3
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +1 -1
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +2 -2
- mx_bluesky/beamlines/i24/serial/web_gui_plans/general_plans.py +109 -0
- mx_bluesky/beamlines/i24/serial/write_nexus.py +2 -2
- mx_bluesky/common/device_setup_plans/setup_panda.py +9 -0
- mx_bluesky/common/external_interaction/callbacks/common/plan_reactive_callback.py +2 -2
- mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py +11 -3
- mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_mapping.py +1 -1
- mx_bluesky/common/external_interaction/ispyb/exp_eye_store.py +6 -2
- mx_bluesky/common/external_interaction/ispyb/ispyb_store.py +7 -0
- mx_bluesky/common/parameters/constants.py +16 -0
- mx_bluesky/common/parameters/gridscan.py +36 -1
- mx_bluesky/common/plans/do_fgs.py +4 -6
- mx_bluesky/common/plans/read_hardware.py +78 -0
- mx_bluesky/common/utils/context.py +68 -0
- mx_bluesky/common/utils/exceptions.py +2 -1
- mx_bluesky/{hyperion/experiment_plans/common → common}/xrc_result.py +16 -0
- mx_bluesky/definitions.py +4 -0
- mx_bluesky/hyperion/__main__.py +11 -42
- mx_bluesky/hyperion/device_setup_plans/setup_oav.py +5 -5
- mx_bluesky/hyperion/device_setup_plans/setup_panda.py +9 -8
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +2 -2
- mx_bluesky/hyperion/device_setup_plans/smargon.py +6 -6
- mx_bluesky/hyperion/device_setup_plans/utils.py +2 -2
- mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +14 -4
- mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +2 -6
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +0 -15
- mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +42 -93
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +14 -6
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +26 -21
- mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +11 -11
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +5 -9
- mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +1 -1
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +2 -4
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +10 -10
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +11 -18
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +2 -4
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +19 -10
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +2 -0
- mx_bluesky/hyperion/external_interaction/agamemnon.py +104 -0
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +19 -2
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +1 -1
- mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/sample_handling_callback.py +14 -9
- mx_bluesky/hyperion/external_interaction/config_server.py +13 -2
- mx_bluesky/hyperion/parameters/cli.py +1 -9
- mx_bluesky/hyperion/parameters/constants.py +6 -1
- mx_bluesky/hyperion/parameters/device_composites.py +49 -0
- mx_bluesky/hyperion/parameters/gridscan.py +5 -3
- mx_bluesky/hyperion/resources/panda/panda-gridscan.yaml +1006 -964
- mx_bluesky/hyperion/utils/__init__.py +1 -0
- mx_bluesky/hyperion/utils/context.py +0 -65
- mx_bluesky/hyperion/utils/validation.py +23 -20
- {mx_bluesky-1.4.4.dist-info → mx_bluesky-1.4.6.dist-info}/METADATA +5 -4
- {mx_bluesky-1.4.4.dist-info → mx_bluesky-1.4.6.dist-info}/RECORD +71 -66
- {mx_bluesky-1.4.4.dist-info → mx_bluesky-1.4.6.dist-info}/WHEEL +1 -1
- {mx_bluesky-1.4.4.dist-info → mx_bluesky-1.4.6.dist-info}/entry_points.txt +1 -0
- mx_bluesky/common/device_setup_plans/read_hardware_for_setup.py +0 -14
- mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +0 -54
- mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +0 -95
- /mx_bluesky/{hyperion/external_interaction/callbacks/common → beamlines/i24/serial/web_gui_plans}/__init__.py +0 -0
- {mx_bluesky-1.4.4.dist-info → mx_bluesky-1.4.6.dist-info}/LICENSE +0 -0
- {mx_bluesky-1.4.4.dist-info → mx_bluesky-1.4.6.dist-info}/top_level.txt +0 -0
|
@@ -15,6 +15,7 @@ from dodal.devices.oav.utils import (
|
|
|
15
15
|
)
|
|
16
16
|
from dodal.devices.smargon import Smargon
|
|
17
17
|
|
|
18
|
+
from mx_bluesky.common.utils.context import device_composite_from_context
|
|
18
19
|
from mx_bluesky.common.utils.exceptions import SampleException
|
|
19
20
|
from mx_bluesky.common.utils.log import LOGGER
|
|
20
21
|
from mx_bluesky.hyperion.device_setup_plans.setup_oav import pre_centring_setup_oav
|
|
@@ -22,7 +23,6 @@ from mx_bluesky.hyperion.device_setup_plans.smargon import (
|
|
|
22
23
|
move_smargon_warn_on_out_of_range,
|
|
23
24
|
)
|
|
24
25
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
25
|
-
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
26
26
|
|
|
27
27
|
DEFAULT_STEP_SIZE = 0.5
|
|
28
28
|
|
|
@@ -53,7 +53,7 @@ def trigger_and_return_pin_tip(
|
|
|
53
53
|
def move_pin_into_view(
|
|
54
54
|
pin_tip_device: PinTipDetection,
|
|
55
55
|
smargon: Smargon,
|
|
56
|
-
|
|
56
|
+
step_magnitude_mm: float = DEFAULT_STEP_SIZE,
|
|
57
57
|
max_steps: int = 2,
|
|
58
58
|
) -> Generator[Msg, None, Pixel]:
|
|
59
59
|
"""Attempt to move the pin into view and return the tip location in pixels if found.
|
|
@@ -63,7 +63,7 @@ def move_pin_into_view(
|
|
|
63
63
|
Args:
|
|
64
64
|
pin_tip_device (PinTipDetection): The device being used to detect the pin
|
|
65
65
|
smargon (Smargon): The gonio to move the tip
|
|
66
|
-
|
|
66
|
+
step_magnitude_mm (float, optional): Distance to move the gonio (in mm) for each
|
|
67
67
|
step of the search. Defaults to 0.5.
|
|
68
68
|
max_steps (int, optional): The number of steps to search with. Defaults to 2.
|
|
69
69
|
|
|
@@ -83,21 +83,21 @@ def move_pin_into_view(
|
|
|
83
83
|
if pin_tip_valid(tip_xy_px):
|
|
84
84
|
return (int(tip_xy_px[0]), int(tip_xy_px[1]))
|
|
85
85
|
|
|
86
|
-
if
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
# Pin is off in the -ve direction if the returned tip x pixel value is 0
|
|
87
|
+
direction_multiple = -1 if tip_xy_px[0] == 0 else 1
|
|
88
|
+
step_vector_mm = step_magnitude_mm * direction_multiple
|
|
89
89
|
|
|
90
90
|
smargon_x = yield from bps.rd(smargon.x.user_readback)
|
|
91
|
-
ideal_move_to_find_pin = float(smargon_x) +
|
|
91
|
+
ideal_move_to_find_pin = float(smargon_x) + step_vector_mm
|
|
92
92
|
high_limit = yield from bps.rd(smargon.x.high_limit_travel)
|
|
93
93
|
low_limit = yield from bps.rd(smargon.x.low_limit_travel)
|
|
94
94
|
move_within_limits = max(min(ideal_move_to_find_pin, high_limit), low_limit)
|
|
95
95
|
if move_within_limits != ideal_move_to_find_pin:
|
|
96
96
|
LOGGER.warning(
|
|
97
|
-
f"Pin tip is off screen, and moving {
|
|
97
|
+
f"Pin tip is off screen, and moving {step_vector_mm}mm would cross limits, "
|
|
98
98
|
f"moving to {move_within_limits} instead"
|
|
99
99
|
)
|
|
100
|
-
yield from bps.mv(smargon.x, move_within_limits)
|
|
100
|
+
yield from bps.mv(smargon.x, move_within_limits)
|
|
101
101
|
|
|
102
102
|
# Some time for the view to settle after the move
|
|
103
103
|
yield from bps.sleep(CONST.HARDWARE.OAV_REFRESH_DELAY)
|
|
@@ -154,7 +154,7 @@ def pin_tip_centre_plan(
|
|
|
154
154
|
tip = yield from move_pin_into_view(pin_tip_detect, smargon)
|
|
155
155
|
yield from offset_and_move(tip)
|
|
156
156
|
|
|
157
|
-
yield from bps.mvr(smargon.omega, 90)
|
|
157
|
+
yield from bps.mvr(smargon.omega, 90)
|
|
158
158
|
|
|
159
159
|
# need to wait for the OAV image to update
|
|
160
160
|
# See #673 for improvements
|
|
@@ -54,7 +54,7 @@ class RobotLoadAndEnergyChangeComposite:
|
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
def create_devices(context: BlueskyContext) -> RobotLoadAndEnergyChangeComposite:
|
|
57
|
-
from mx_bluesky.
|
|
57
|
+
from mx_bluesky.common.utils.context import device_composite_from_context
|
|
58
58
|
|
|
59
59
|
return device_composite_from_context(context, RobotLoadAndEnergyChangeComposite)
|
|
60
60
|
|
|
@@ -95,21 +95,19 @@ def prepare_for_robot_load(
|
|
|
95
95
|
aperture_scatterguard: ApertureScatterguard, smargon: Smargon
|
|
96
96
|
):
|
|
97
97
|
yield from bps.abs_set(
|
|
98
|
-
aperture_scatterguard,
|
|
99
|
-
ApertureValue.ROBOT_LOAD,
|
|
100
|
-
group="prepare_robot_load",
|
|
98
|
+
aperture_scatterguard, ApertureValue.OUT_OF_BEAM, group="prepare_robot_load"
|
|
101
99
|
)
|
|
102
100
|
|
|
103
|
-
yield from bps.mv(smargon.stub_offsets, StubPosition.RESET_TO_ROBOT_LOAD)
|
|
101
|
+
yield from bps.mv(smargon.stub_offsets, StubPosition.RESET_TO_ROBOT_LOAD)
|
|
104
102
|
|
|
105
103
|
# fmt: off
|
|
106
104
|
yield from bps.mv(
|
|
107
|
-
smargon.x, 0,
|
|
108
|
-
smargon.y, 0,
|
|
109
|
-
smargon.z, 0,
|
|
110
|
-
smargon.omega, 0,
|
|
111
|
-
smargon.chi, 0,
|
|
112
|
-
smargon.phi, 0
|
|
105
|
+
smargon.x, 0,
|
|
106
|
+
smargon.y, 0,
|
|
107
|
+
smargon.z, 0,
|
|
108
|
+
smargon.omega, 0,
|
|
109
|
+
smargon.chi, 0,
|
|
110
|
+
smargon.phi, 0
|
|
113
111
|
)
|
|
114
112
|
# fmt: on
|
|
115
113
|
|
|
@@ -122,11 +120,6 @@ def do_robot_load(
|
|
|
122
120
|
demand_energy_ev: float | None,
|
|
123
121
|
thawing_time: float,
|
|
124
122
|
):
|
|
125
|
-
error_code = yield from bps.rd(composite.robot.error_code)
|
|
126
|
-
# Reset robot if light curtains were tripped
|
|
127
|
-
if error_code == 40:
|
|
128
|
-
yield from bps.trigger(composite.robot.reset, wait=True)
|
|
129
|
-
|
|
130
123
|
yield from bps.abs_set(
|
|
131
124
|
composite.robot,
|
|
132
125
|
sample_location,
|
|
@@ -138,7 +131,7 @@ def do_robot_load(
|
|
|
138
131
|
yield from bps.wait("robot_load")
|
|
139
132
|
|
|
140
133
|
yield from bps.abs_set(
|
|
141
|
-
composite.thawer.thaw_for_time_s,
|
|
134
|
+
composite.thawer.thaw_for_time_s,
|
|
142
135
|
thawing_time,
|
|
143
136
|
group="thawing_finished",
|
|
144
137
|
)
|
|
@@ -200,7 +193,7 @@ def robot_load_and_snapshots(
|
|
|
200
193
|
|
|
201
194
|
yield from bps.create(name=CONST.DESCRIPTORS.ROBOT_LOAD)
|
|
202
195
|
yield from bps.read(composite.robot.barcode)
|
|
203
|
-
yield from bps.read(composite.oav.snapshot)
|
|
196
|
+
yield from bps.read(composite.oav.snapshot)
|
|
204
197
|
yield from bps.read(composite.webcam)
|
|
205
198
|
yield from bps.save()
|
|
206
199
|
|
|
@@ -37,6 +37,7 @@ from dodal.log import LOGGER
|
|
|
37
37
|
from ophyd_async.fastcs.panda import HDFPanda
|
|
38
38
|
|
|
39
39
|
from mx_bluesky.common.parameters.constants import OavConstants
|
|
40
|
+
from mx_bluesky.common.xrc_result import XRayCentreEventHandler
|
|
40
41
|
from mx_bluesky.hyperion.device_setup_plans.utils import (
|
|
41
42
|
fill_in_energy_if_not_supplied,
|
|
42
43
|
start_preparing_data_collection_then_do_plan,
|
|
@@ -44,9 +45,6 @@ from mx_bluesky.hyperion.device_setup_plans.utils import (
|
|
|
44
45
|
from mx_bluesky.hyperion.experiment_plans.change_aperture_then_move_plan import (
|
|
45
46
|
change_aperture_then_move_to_xtal,
|
|
46
47
|
)
|
|
47
|
-
from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
|
|
48
|
-
XRayCentreEventHandler,
|
|
49
|
-
)
|
|
50
48
|
from mx_bluesky.hyperion.experiment_plans.grid_detect_then_xray_centre_plan import (
|
|
51
49
|
GridDetectThenXRayCentreComposite,
|
|
52
50
|
)
|
|
@@ -106,7 +104,7 @@ class RobotLoadThenCentreComposite:
|
|
|
106
104
|
|
|
107
105
|
|
|
108
106
|
def create_devices(context: BlueskyContext) -> RobotLoadThenCentreComposite:
|
|
109
|
-
from mx_bluesky.
|
|
107
|
+
from mx_bluesky.common.utils.context import device_composite_from_context
|
|
110
108
|
|
|
111
109
|
return device_composite_from_context(context, RobotLoadThenCentreComposite)
|
|
112
110
|
|
|
@@ -27,9 +27,12 @@ from dodal.devices.zebra.zebra import RotationDirection, Zebra
|
|
|
27
27
|
from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
|
|
28
28
|
from dodal.plan_stubs.check_topup import check_topup_and_wait_if_necessary
|
|
29
29
|
|
|
30
|
-
from mx_bluesky.common.
|
|
30
|
+
from mx_bluesky.common.plans.read_hardware import (
|
|
31
31
|
read_hardware_for_zocalo,
|
|
32
|
+
standard_read_hardware_during_collection,
|
|
33
|
+
standard_read_hardware_pre_collection,
|
|
32
34
|
)
|
|
35
|
+
from mx_bluesky.common.utils.context import device_composite_from_context
|
|
33
36
|
from mx_bluesky.common.utils.log import LOGGER
|
|
34
37
|
from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import (
|
|
35
38
|
cleanup_sample_environment,
|
|
@@ -37,10 +40,6 @@ from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import (
|
|
|
37
40
|
move_x_y_z,
|
|
38
41
|
setup_sample_environment,
|
|
39
42
|
)
|
|
40
|
-
from mx_bluesky.hyperion.device_setup_plans.read_hardware_for_setup import (
|
|
41
|
-
read_hardware_during_collection,
|
|
42
|
-
read_hardware_pre_collection,
|
|
43
|
-
)
|
|
44
43
|
from mx_bluesky.hyperion.device_setup_plans.setup_zebra import (
|
|
45
44
|
arm_zebra,
|
|
46
45
|
setup_zebra_for_rotation,
|
|
@@ -62,7 +61,6 @@ from mx_bluesky.hyperion.parameters.rotation import (
|
|
|
62
61
|
MultiRotationScan,
|
|
63
62
|
RotationScan,
|
|
64
63
|
)
|
|
65
|
-
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
66
64
|
|
|
67
65
|
|
|
68
66
|
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
@@ -267,7 +265,7 @@ def rotation_scan_plan(
|
|
|
267
265
|
# get some information for the ispyb deposition and trigger the callback
|
|
268
266
|
yield from read_hardware_for_zocalo(composite.eiger)
|
|
269
267
|
|
|
270
|
-
yield from
|
|
268
|
+
yield from standard_read_hardware_pre_collection(
|
|
271
269
|
composite.undulator,
|
|
272
270
|
composite.synchrotron,
|
|
273
271
|
composite.s4_slit_gaps,
|
|
@@ -293,7 +291,7 @@ def rotation_scan_plan(
|
|
|
293
291
|
LOGGER.info("Executing rotation scan")
|
|
294
292
|
yield from bps.rel_set(axis, motion_values.distance_to_move_deg, wait=True)
|
|
295
293
|
|
|
296
|
-
yield from
|
|
294
|
+
yield from standard_read_hardware_during_collection(
|
|
297
295
|
composite.aperture_scatterguard,
|
|
298
296
|
composite.attenuator,
|
|
299
297
|
composite.flux,
|
|
@@ -346,6 +344,13 @@ def _move_and_rotation(
|
|
|
346
344
|
yield from setup_beamline_for_OAV(
|
|
347
345
|
composite.smargon, composite.backlight, composite.aperture_scatterguard
|
|
348
346
|
)
|
|
347
|
+
yield from bps.wait(group=CONST.WAIT.READY_FOR_OAV)
|
|
348
|
+
if params.selected_aperture:
|
|
349
|
+
yield from bps.prepare(
|
|
350
|
+
composite.aperture_scatterguard,
|
|
351
|
+
params.selected_aperture,
|
|
352
|
+
group=CONST.WAIT.ROTATION_READY_FOR_DC,
|
|
353
|
+
)
|
|
349
354
|
yield from oav_snapshot_plan(composite, params, oav_params)
|
|
350
355
|
yield from rotation_scan_plan(
|
|
351
356
|
composite,
|
|
@@ -376,8 +381,10 @@ def rotation_scan(
|
|
|
376
381
|
}
|
|
377
382
|
)
|
|
378
383
|
@transmission_and_xbpm_feedback_for_collection_decorator(
|
|
384
|
+
composite.undulator,
|
|
379
385
|
composite.xbpm_feedback,
|
|
380
386
|
composite.attenuator,
|
|
387
|
+
composite.dcm,
|
|
381
388
|
parameters.transmission_frac,
|
|
382
389
|
)
|
|
383
390
|
def rotation_scan_plan_with_stage_and_cleanup(
|
|
@@ -399,7 +406,7 @@ def rotation_scan(
|
|
|
399
406
|
rotation_with_cleanup_and_stage(params),
|
|
400
407
|
group=CONST.WAIT.ROTATION_READY_FOR_DC,
|
|
401
408
|
)
|
|
402
|
-
yield from bps.unstage(eiger)
|
|
409
|
+
yield from bps.unstage(eiger)
|
|
403
410
|
|
|
404
411
|
yield from rotation_scan_plan_with_stage_and_cleanup(parameters)
|
|
405
412
|
|
|
@@ -427,10 +434,11 @@ def multi_rotation_scan(
|
|
|
427
434
|
],
|
|
428
435
|
}
|
|
429
436
|
)
|
|
430
|
-
@bpp.stage_decorator([eiger])
|
|
431
437
|
@transmission_and_xbpm_feedback_for_collection_decorator(
|
|
438
|
+
composite.undulator,
|
|
432
439
|
composite.xbpm_feedback,
|
|
433
440
|
composite.attenuator,
|
|
441
|
+
composite.dcm,
|
|
434
442
|
parameters.transmission_frac,
|
|
435
443
|
)
|
|
436
444
|
@bpp.finalize_decorator(lambda: _cleanup_plan(composite))
|
|
@@ -460,3 +468,4 @@ def multi_rotation_scan(
|
|
|
460
468
|
_multi_rotation_scan(),
|
|
461
469
|
group=CONST.WAIT.ROTATION_READY_FOR_DC,
|
|
462
470
|
)
|
|
471
|
+
yield from bps.unstage(eiger)
|
|
@@ -54,7 +54,9 @@ def set_energy_plan(
|
|
|
54
54
|
if energy_ev:
|
|
55
55
|
yield from transmission_and_xbpm_feedback_for_collection_wrapper(
|
|
56
56
|
_set_energy_plan(energy_ev / 1000, composite),
|
|
57
|
+
composite.undulator_dcm.undulator_ref(),
|
|
57
58
|
composite.xbpm_feedback,
|
|
58
59
|
composite.attenuator,
|
|
60
|
+
composite.dcm,
|
|
59
61
|
DESIRED_TRANSMISSION_FRACTION,
|
|
60
62
|
)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import json
|
|
3
|
+
import re
|
|
4
|
+
from typing import TypeVar
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
from dodal.utils import get_beamline_name
|
|
8
|
+
|
|
9
|
+
from mx_bluesky.common.parameters.components import WithVisit
|
|
10
|
+
from mx_bluesky.common.parameters.constants import GridscanParamConstants
|
|
11
|
+
from mx_bluesky.common.utils.log import LOGGER
|
|
12
|
+
from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect
|
|
13
|
+
|
|
14
|
+
T = TypeVar("T", bound=WithVisit)
|
|
15
|
+
AGAMEMNON_URL = "http://agamemnon.diamond.ac.uk/"
|
|
16
|
+
MULTIPIN_PREFIX = "multipin"
|
|
17
|
+
MULTIPIN_FORMAT_DESC = "Expected multipin format is multipin_{number_of_wells}x{well_size}+{distance_between_tip_and_first_well}"
|
|
18
|
+
MULTIPIN_REGEX = rf"^{MULTIPIN_PREFIX}_(\d+)x(\d+(?:\.\d+)?)\+(\d+(?:\.\d+)?)$"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclasses.dataclass
|
|
22
|
+
class PinType:
|
|
23
|
+
expected_number_of_crystals: int
|
|
24
|
+
single_well_width_um: float
|
|
25
|
+
tip_to_first_well_um: float = 0
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def full_width(self) -> float:
|
|
29
|
+
"""This is the "width" of the area where there may be samples.
|
|
30
|
+
|
|
31
|
+
From a pin perspective this is along the length of the pin but we use width here as
|
|
32
|
+
we mount the sample at 90 deg to the optical camera.
|
|
33
|
+
|
|
34
|
+
We calculate the full width by adding all the gaps between wells then assuming
|
|
35
|
+
there is a buffer of {tip_to_first_well_um} either side too. In reality the
|
|
36
|
+
calculation does not need to be very exact as long as we get a width that's good
|
|
37
|
+
enough to use for optical centring and XRC grid size.
|
|
38
|
+
"""
|
|
39
|
+
return (self.expected_number_of_crystals - 1) * self.single_well_width_um + (
|
|
40
|
+
2 * self.tip_to_first_well_um
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class SinglePin(PinType):
|
|
45
|
+
def __init__(self):
|
|
46
|
+
super().__init__(1, GridscanParamConstants.WIDTH_UM)
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def full_width(self) -> float:
|
|
50
|
+
return self.single_well_width_um
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _get_parameters_from_url(url: str) -> dict:
|
|
54
|
+
response = requests.get(url, headers={"Accept": "application/json"})
|
|
55
|
+
response.raise_for_status()
|
|
56
|
+
response_json = json.loads(response.content)
|
|
57
|
+
try:
|
|
58
|
+
return response_json["collect"]
|
|
59
|
+
except KeyError as e:
|
|
60
|
+
raise KeyError(f"Unexpected json from agamemnon: {response_json}") from e
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _get_pin_type_from_agamemnon_parameters(parameters: dict) -> PinType:
|
|
64
|
+
loop_type_name: str | None = parameters["sample"]["loopType"]
|
|
65
|
+
if loop_type_name:
|
|
66
|
+
regex_search = re.search(MULTIPIN_REGEX, loop_type_name)
|
|
67
|
+
if regex_search:
|
|
68
|
+
wells, well_size, tip_to_first_well = regex_search.groups()
|
|
69
|
+
return PinType(int(wells), float(well_size), float(tip_to_first_well))
|
|
70
|
+
else:
|
|
71
|
+
loop_type_message = (
|
|
72
|
+
f"Agamemnon loop type of {loop_type_name} not recognised"
|
|
73
|
+
)
|
|
74
|
+
if loop_type_name.startswith(MULTIPIN_PREFIX):
|
|
75
|
+
raise ValueError(f"{loop_type_message}. {MULTIPIN_FORMAT_DESC}")
|
|
76
|
+
LOGGER.warning(f"{loop_type_message}, assuming single pin")
|
|
77
|
+
return SinglePin()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def get_next_instruction(beamline: str) -> dict:
|
|
81
|
+
return _get_parameters_from_url(AGAMEMNON_URL + f"getnextcollect/{beamline}")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_pin_type_from_agamemnon(beamline: str) -> PinType:
|
|
85
|
+
params = get_next_instruction(beamline)
|
|
86
|
+
return _get_pin_type_from_agamemnon_parameters(params)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def update_params_from_agamemnon(parameters: T) -> T:
|
|
90
|
+
try:
|
|
91
|
+
beamline_name = get_beamline_name("i03")
|
|
92
|
+
pin_type = get_pin_type_from_agamemnon(beamline_name)
|
|
93
|
+
if isinstance(parameters, LoadCentreCollect):
|
|
94
|
+
parameters.robot_load_then_centre.tip_offset_um = pin_type.full_width / 2
|
|
95
|
+
parameters.robot_load_then_centre.grid_width_um = pin_type.full_width
|
|
96
|
+
parameters.select_centres.n = pin_type.expected_number_of_crystals
|
|
97
|
+
if pin_type != SinglePin():
|
|
98
|
+
# Snapshots between each collection take a lot of time.
|
|
99
|
+
# Before we do https://github.com/DiamondLightSource/mx-bluesky/issues/226
|
|
100
|
+
# this will give no snapshots but that's preferable
|
|
101
|
+
parameters.multi_rotation_scan.snapshot_omegas_deg = []
|
|
102
|
+
except Exception as e:
|
|
103
|
+
LOGGER.warning(f"Failed to get pin type from agamemnon, using single pin {e}")
|
|
104
|
+
return parameters
|
|
@@ -3,6 +3,7 @@ from collections.abc import Callable, Sequence
|
|
|
3
3
|
from threading import Thread
|
|
4
4
|
from time import sleep
|
|
5
5
|
|
|
6
|
+
from bluesky.callbacks import CallbackBase
|
|
6
7
|
from bluesky.callbacks.zmq import Proxy, RemoteDispatcher
|
|
7
8
|
from dodal.log import LOGGER as dodal_logger
|
|
8
9
|
from dodal.log import set_up_all_logging_handlers
|
|
@@ -48,17 +49,33 @@ LIVENESS_POLL_SECONDS = 1
|
|
|
48
49
|
ERROR_LOG_BUFFER_LINES = 5000
|
|
49
50
|
|
|
50
51
|
|
|
51
|
-
def
|
|
52
|
-
|
|
52
|
+
def create_gridscan_callbacks() -> tuple[
|
|
53
|
+
GridscanNexusFileCallback, GridscanISPyBCallback
|
|
54
|
+
]:
|
|
55
|
+
return (
|
|
53
56
|
GridscanNexusFileCallback(param_type=HyperionSpecifiedThreeDGridScan),
|
|
54
57
|
GridscanISPyBCallback(
|
|
55
58
|
param_type=GridCommonWithHyperionDetectorParams,
|
|
56
59
|
emit=ZocaloCallback(CONST.PLAN.DO_FGS, CONST.ZOCALO_ENV),
|
|
57
60
|
),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def create_rotation_callbacks() -> tuple[
|
|
65
|
+
RotationNexusFileCallback, RotationISPyBCallback
|
|
66
|
+
]:
|
|
67
|
+
return (
|
|
58
68
|
RotationNexusFileCallback(),
|
|
59
69
|
RotationISPyBCallback(
|
|
60
70
|
emit=ZocaloCallback(CONST.PLAN.ROTATION_MAIN, CONST.ZOCALO_ENV)
|
|
61
71
|
),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def setup_callbacks() -> list[CallbackBase]:
|
|
76
|
+
return [
|
|
77
|
+
*create_gridscan_callbacks(),
|
|
78
|
+
*create_rotation_callbacks(),
|
|
62
79
|
LogUidTaggingCallback(),
|
|
63
80
|
RobotLoadISPyBCallback(),
|
|
64
81
|
SampleHandlingCallback(),
|
|
@@ -136,7 +136,7 @@ class RotationISPyBCallback(BaseISPyBCallback):
|
|
|
136
136
|
"handle_ispyb_hardware_read triggered before activity_gated_start"
|
|
137
137
|
)
|
|
138
138
|
motor_positions_um = [position * 1000 for position in motor_positions_mm]
|
|
139
|
-
comment = f"Sample position (µm): ({motor_positions_um[0]:.0f}, {motor_positions_um[1]:.0f}, {motor_positions_um[2]:.0f})
|
|
139
|
+
comment = f"Sample position (µm): ({motor_positions_um[0]:.0f}, {motor_positions_um[1]:.0f}, {motor_positions_um[2]:.0f})"
|
|
140
140
|
scan_data_infos[0].data_collection_info.comments = comment
|
|
141
141
|
return scan_data_infos
|
|
142
142
|
|
mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/sample_handling_callback.py
CHANGED
|
@@ -19,22 +19,27 @@ class SampleHandlingCallback(PlanReactiveCallback):
|
|
|
19
19
|
super().__init__(log=ISPYB_ZOCALO_CALLBACK_LOGGER)
|
|
20
20
|
self._sample_id: int | None = None
|
|
21
21
|
self._descriptor: str | None = None
|
|
22
|
+
self._run_id: str | None = None
|
|
22
23
|
|
|
23
24
|
def activity_gated_start(self, doc: RunStart):
|
|
24
|
-
if not self._sample_id:
|
|
25
|
+
if not self._sample_id and self.active:
|
|
25
26
|
sample_id = doc.get("metadata", {}).get("sample_id")
|
|
26
27
|
self.log.info(f"Recording sample ID at run start {sample_id}")
|
|
27
28
|
self._sample_id = sample_id
|
|
29
|
+
self._run_id = self.activity_uid
|
|
28
30
|
|
|
29
31
|
def activity_gated_stop(self, doc: RunStop) -> RunStop:
|
|
30
|
-
if
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
if self._run_id == doc.get("run_start"):
|
|
33
|
+
if doc["exit_status"] != "success":
|
|
34
|
+
exception_type, message = SampleException.type_and_message_from_reason(
|
|
35
|
+
doc.get("reason", "")
|
|
36
|
+
)
|
|
37
|
+
self.log.info(
|
|
38
|
+
f"Sample handling callback intercepted exception of type {exception_type}: {message}"
|
|
39
|
+
)
|
|
40
|
+
self._record_exception(exception_type)
|
|
41
|
+
self._sample_id = None
|
|
42
|
+
self._run_id = None
|
|
38
43
|
return doc
|
|
39
44
|
|
|
40
45
|
def _record_exception(self, exception_type: str):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from functools import cache
|
|
2
2
|
|
|
3
3
|
from daq_config_server.client import ConfigServer
|
|
4
|
+
from pydantic import model_validator
|
|
4
5
|
|
|
5
6
|
from mx_bluesky.common.external_interaction.config_server import FeatureFlags
|
|
6
7
|
from mx_bluesky.common.utils.log import LOGGER
|
|
@@ -13,8 +14,10 @@ class HyperionFeatureFlags(FeatureFlags):
|
|
|
13
14
|
|
|
14
15
|
Attributes:
|
|
15
16
|
use_panda_for_gridscan: If True then the PandA is used for gridscans, otherwise the zebra is used
|
|
16
|
-
compare_cpu_and_gpu_zocalo: If True then GPU result processing is enabled
|
|
17
|
-
CPU
|
|
17
|
+
compare_cpu_and_gpu_zocalo: If True then GPU result processing is enabled
|
|
18
|
+
alongside CPU and the results are compared. The CPU result is still take.n
|
|
19
|
+
use_gpu_results: If True then GPU result processing is enabled
|
|
20
|
+
and the GPU result is taken.
|
|
18
21
|
set_stub_offsets: If True then set the stub offsets after moving to the crystal (ignored for
|
|
19
22
|
multi-centre)
|
|
20
23
|
omega_flip: If True then invert the smargon omega motor rotation commands with respect to
|
|
@@ -26,7 +29,15 @@ class HyperionFeatureFlags(FeatureFlags):
|
|
|
26
29
|
def get_config_server() -> ConfigServer:
|
|
27
30
|
return ConfigServer(CONST.CONFIG_SERVER_URL, LOGGER)
|
|
28
31
|
|
|
32
|
+
@model_validator(mode="after")
|
|
33
|
+
def use_gpu_and_compare_cannot_both_be_true(self):
|
|
34
|
+
assert not (self.use_gpu_results and self.compare_cpu_and_gpu_zocalo), (
|
|
35
|
+
"Cannot both use GPU results and compare them to CPU"
|
|
36
|
+
)
|
|
37
|
+
return self
|
|
38
|
+
|
|
29
39
|
use_panda_for_gridscan: bool = CONST.I03.USE_PANDA_FOR_GRIDSCAN
|
|
30
40
|
compare_cpu_and_gpu_zocalo: bool = CONST.I03.COMPARE_CPU_AND_GPU_ZOCALO
|
|
41
|
+
use_gpu_results: bool = CONST.I03.USE_GPU_RESULTS
|
|
31
42
|
set_stub_offsets: bool = CONST.I03.SET_STUB_OFFSETS
|
|
32
43
|
omega_flip: bool = CONST.I03.OMEGA_FLIP
|
|
@@ -8,7 +8,6 @@ from mx_bluesky._version import version
|
|
|
8
8
|
@dataclass
|
|
9
9
|
class HyperionArgs:
|
|
10
10
|
dev_mode: bool = False
|
|
11
|
-
use_external_callbacks: bool = False
|
|
12
11
|
verbose_event_logging: bool = False
|
|
13
12
|
skip_startup_connection: bool = False
|
|
14
13
|
|
|
@@ -34,8 +33,7 @@ def parse_cli_args() -> HyperionArgs:
|
|
|
34
33
|
"""Parses all arguments relevant to hyperion. Returns an HyperionArgs dataclass with
|
|
35
34
|
the fields: (verbose_event_logging: bool,
|
|
36
35
|
dev_mode: bool,
|
|
37
|
-
skip_startup_connection: bool
|
|
38
|
-
external_callbacks: bool)"""
|
|
36
|
+
skip_startup_connection: bool)"""
|
|
39
37
|
parser = argparse.ArgumentParser()
|
|
40
38
|
_add_callback_relevant_args(parser)
|
|
41
39
|
parser.add_argument(
|
|
@@ -48,11 +46,6 @@ def parse_cli_args() -> HyperionArgs:
|
|
|
48
46
|
action="store_true",
|
|
49
47
|
help="Skip connecting to EPICS PVs on startup",
|
|
50
48
|
)
|
|
51
|
-
parser.add_argument(
|
|
52
|
-
"--external-callbacks",
|
|
53
|
-
action="store_true",
|
|
54
|
-
help="Run the external hyperion-callbacks service and publish events over ZMQ",
|
|
55
|
-
)
|
|
56
49
|
parser.add_argument(
|
|
57
50
|
"--version",
|
|
58
51
|
help="Print hyperion version string",
|
|
@@ -64,5 +57,4 @@ def parse_cli_args() -> HyperionArgs:
|
|
|
64
57
|
verbose_event_logging=args.verbose_event_logging or False,
|
|
65
58
|
dev_mode=args.dev or False,
|
|
66
59
|
skip_startup_connection=args.skip_startup_connection or False,
|
|
67
|
-
use_external_callbacks=args.external_callbacks or False,
|
|
68
60
|
)
|
|
@@ -4,6 +4,7 @@ from dodal.devices.detector import EIGER2_X_16M_SIZE
|
|
|
4
4
|
from pydantic.dataclasses import dataclass
|
|
5
5
|
|
|
6
6
|
from mx_bluesky.common.parameters.constants import (
|
|
7
|
+
DeviceSettingsConstants,
|
|
7
8
|
DocDescriptorNames,
|
|
8
9
|
EnvironmentConstants,
|
|
9
10
|
ExperimentParamConstants,
|
|
@@ -31,9 +32,12 @@ class I03Constants:
|
|
|
31
32
|
OMEGA_FLIP = True
|
|
32
33
|
|
|
33
34
|
# Turns on GPU processing for zocalo and logs a comparison between GPU and CPU-
|
|
34
|
-
# processed results.
|
|
35
|
+
# processed results.
|
|
35
36
|
COMPARE_CPU_AND_GPU_ZOCALO = False
|
|
36
37
|
|
|
38
|
+
# Turns on GPU processing for zocalo and uses the results that come back
|
|
39
|
+
USE_GPU_RESULTS = False
|
|
40
|
+
|
|
37
41
|
|
|
38
42
|
@dataclass(frozen=True)
|
|
39
43
|
class HyperionConstants:
|
|
@@ -57,6 +61,7 @@ class HyperionConstants:
|
|
|
57
61
|
GRAYLOG_PORT = 12232
|
|
58
62
|
PARAMETER_SCHEMA_DIRECTORY = "src/hyperion/parameters/schemas/"
|
|
59
63
|
LOG_FILE_NAME = "hyperion.log"
|
|
64
|
+
DEVICE_SETTINGS_CONSTANTS = DeviceSettingsConstants()
|
|
60
65
|
|
|
61
66
|
|
|
62
67
|
CONST = HyperionConstants()
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import pydantic
|
|
4
|
+
from dodal.devices.aperturescatterguard import (
|
|
5
|
+
ApertureScatterguard,
|
|
6
|
+
)
|
|
7
|
+
from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
|
|
8
|
+
from dodal.devices.backlight import Backlight
|
|
9
|
+
from dodal.devices.dcm import DCM
|
|
10
|
+
from dodal.devices.eiger import EigerDetector
|
|
11
|
+
from dodal.devices.fast_grid_scan import (
|
|
12
|
+
PandAFastGridScan,
|
|
13
|
+
ZebraFastGridScan,
|
|
14
|
+
)
|
|
15
|
+
from dodal.devices.flux import Flux
|
|
16
|
+
from dodal.devices.robot import BartRobot
|
|
17
|
+
from dodal.devices.s4_slit_gaps import S4SlitGaps
|
|
18
|
+
from dodal.devices.smargon import Smargon
|
|
19
|
+
from dodal.devices.synchrotron import Synchrotron
|
|
20
|
+
from dodal.devices.undulator import Undulator
|
|
21
|
+
from dodal.devices.xbpm_feedback import XBPMFeedback
|
|
22
|
+
from dodal.devices.zebra.zebra import Zebra
|
|
23
|
+
from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
|
|
24
|
+
from dodal.devices.zocalo import ZocaloResults
|
|
25
|
+
from ophyd_async.fastcs.panda import HDFPanda
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
29
|
+
class HyperionFlyScanXRayCentreComposite:
|
|
30
|
+
"""All devices which are directly or indirectly required by this plan"""
|
|
31
|
+
|
|
32
|
+
aperture_scatterguard: ApertureScatterguard
|
|
33
|
+
attenuator: BinaryFilterAttenuator
|
|
34
|
+
backlight: Backlight
|
|
35
|
+
dcm: DCM
|
|
36
|
+
eiger: EigerDetector
|
|
37
|
+
zebra_fast_grid_scan: ZebraFastGridScan
|
|
38
|
+
flux: Flux
|
|
39
|
+
s4_slit_gaps: S4SlitGaps
|
|
40
|
+
smargon: Smargon
|
|
41
|
+
undulator: Undulator
|
|
42
|
+
synchrotron: Synchrotron
|
|
43
|
+
xbpm_feedback: XBPMFeedback
|
|
44
|
+
zebra: Zebra
|
|
45
|
+
zocalo: ZocaloResults
|
|
46
|
+
panda: HDFPanda
|
|
47
|
+
panda_fast_grid_scan: PandAFastGridScan
|
|
48
|
+
robot: BartRobot
|
|
49
|
+
sample_shutter: ZebraShutter
|
|
@@ -47,12 +47,13 @@ class GridCommonWithHyperionDetectorParams(GridCommon, WithHyperionUDCFeatures):
|
|
|
47
47
|
use_roi_mode=self.use_roi_mode,
|
|
48
48
|
det_dist_to_beam_converter_path=self.det_dist_to_beam_converter_path,
|
|
49
49
|
trigger_mode=self.trigger_mode,
|
|
50
|
-
enable_dev_shm=self.features.compare_cpu_and_gpu_zocalo
|
|
50
|
+
enable_dev_shm=self.features.compare_cpu_and_gpu_zocalo
|
|
51
|
+
or self.features.use_gpu_results,
|
|
51
52
|
**optional_args,
|
|
52
53
|
)
|
|
53
54
|
|
|
54
55
|
|
|
55
|
-
class HyperionSpecifiedThreeDGridScan(
|
|
56
|
+
class HyperionSpecifiedThreeDGridScan(WithHyperionUDCFeatures, SpecifiedThreeDGridScan):
|
|
56
57
|
"""Hyperion's 3D grid scan deviates from the common class due to: optionally using a PandA, optionally using dev_shm for GPU analysis, and using a config server for features"""
|
|
57
58
|
|
|
58
59
|
# These detector params only exist so that we can properly select enable_dev_shm. Remove in
|
|
@@ -83,7 +84,8 @@ class HyperionSpecifiedThreeDGridScan(SpecifiedThreeDGridScan, WithHyperionUDCFe
|
|
|
83
84
|
use_roi_mode=self.use_roi_mode,
|
|
84
85
|
det_dist_to_beam_converter_path=self.det_dist_to_beam_converter_path,
|
|
85
86
|
trigger_mode=self.trigger_mode,
|
|
86
|
-
enable_dev_shm=self.features.compare_cpu_and_gpu_zocalo
|
|
87
|
+
enable_dev_shm=self.features.compare_cpu_and_gpu_zocalo
|
|
88
|
+
or self.features.use_gpu_results,
|
|
87
89
|
**optional_args,
|
|
88
90
|
)
|
|
89
91
|
|