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
|
@@ -11,13 +11,6 @@ from mx_bluesky.hyperion.experiment_plans import (
|
|
|
11
11
|
pin_centre_then_xray_centre_plan,
|
|
12
12
|
robot_load_then_centre_plan,
|
|
13
13
|
)
|
|
14
|
-
from mx_bluesky.hyperion.external_interaction.callbacks.common.callback_util import (
|
|
15
|
-
CallbacksFactory,
|
|
16
|
-
create_gridscan_callbacks,
|
|
17
|
-
create_load_centre_collect_callbacks,
|
|
18
|
-
create_robot_load_and_centre_callbacks,
|
|
19
|
-
create_rotation_callbacks,
|
|
20
|
-
)
|
|
21
14
|
from mx_bluesky.hyperion.parameters.gridscan import (
|
|
22
15
|
GridScanWithEdgeDetect,
|
|
23
16
|
HyperionSpecifiedThreeDGridScan,
|
|
@@ -47,44 +40,36 @@ class ExperimentRegistryEntry(TypedDict):
|
|
|
47
40
|
| LoadCentreCollect
|
|
48
41
|
| RobotLoadThenCentre
|
|
49
42
|
]
|
|
50
|
-
callbacks_factory: CallbacksFactory
|
|
51
43
|
|
|
52
44
|
|
|
53
45
|
PLAN_REGISTRY: dict[str, ExperimentRegistryEntry] = {
|
|
54
46
|
"flyscan_xray_centre": {
|
|
55
47
|
"setup": flyscan_xray_centre_plan.create_devices,
|
|
56
48
|
"param_type": HyperionSpecifiedThreeDGridScan,
|
|
57
|
-
"callbacks_factory": create_gridscan_callbacks,
|
|
58
49
|
},
|
|
59
50
|
"grid_detect_then_xray_centre": {
|
|
60
51
|
"setup": grid_detect_then_xray_centre_plan.create_devices,
|
|
61
52
|
"param_type": GridScanWithEdgeDetect,
|
|
62
|
-
"callbacks_factory": create_gridscan_callbacks,
|
|
63
53
|
},
|
|
64
54
|
"rotation_scan": {
|
|
65
55
|
"setup": rotation_scan_plan.create_devices,
|
|
66
56
|
"param_type": RotationScan,
|
|
67
|
-
"callbacks_factory": create_rotation_callbacks,
|
|
68
57
|
},
|
|
69
58
|
"pin_tip_centre_then_xray_centre": {
|
|
70
59
|
"setup": pin_centre_then_xray_centre_plan.create_devices,
|
|
71
60
|
"param_type": PinTipCentreThenXrayCentre,
|
|
72
|
-
"callbacks_factory": create_gridscan_callbacks,
|
|
73
61
|
},
|
|
74
62
|
"robot_load_then_centre": {
|
|
75
63
|
"setup": robot_load_then_centre_plan.create_devices,
|
|
76
64
|
"param_type": RobotLoadThenCentre,
|
|
77
|
-
"callbacks_factory": create_robot_load_and_centre_callbacks,
|
|
78
65
|
},
|
|
79
66
|
"multi_rotation_scan": {
|
|
80
67
|
"setup": rotation_scan_plan.create_devices,
|
|
81
68
|
"param_type": MultiRotationScan,
|
|
82
|
-
"callbacks_factory": create_rotation_callbacks,
|
|
83
69
|
},
|
|
84
70
|
"load_centre_collect_full": {
|
|
85
71
|
"setup": load_centre_collect_full_plan.create_devices,
|
|
86
72
|
"param_type": LoadCentreCollect,
|
|
87
|
-
"callbacks_factory": create_load_centre_collect_callbacks,
|
|
88
73
|
},
|
|
89
74
|
}
|
|
90
75
|
|
|
@@ -9,58 +9,39 @@ from typing import Protocol
|
|
|
9
9
|
import bluesky.plan_stubs as bps
|
|
10
10
|
import bluesky.preprocessors as bpp
|
|
11
11
|
import numpy as np
|
|
12
|
-
import pydantic
|
|
13
12
|
from blueapi.core import BlueskyContext
|
|
14
|
-
from bluesky.callbacks import CallbackBase
|
|
15
13
|
from bluesky.utils import MsgGenerator
|
|
16
|
-
from dodal.devices.aperturescatterguard import (
|
|
17
|
-
ApertureScatterguard,
|
|
18
|
-
)
|
|
19
|
-
from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
|
|
20
|
-
from dodal.devices.backlight import Backlight
|
|
21
|
-
from dodal.devices.dcm import DCM
|
|
22
|
-
from dodal.devices.eiger import EigerDetector
|
|
23
14
|
from dodal.devices.fast_grid_scan import (
|
|
24
15
|
FastGridScanCommon,
|
|
25
|
-
PandAFastGridScan,
|
|
26
|
-
ZebraFastGridScan,
|
|
27
16
|
)
|
|
28
17
|
from dodal.devices.fast_grid_scan import (
|
|
29
18
|
set_fast_grid_scan_params as set_flyscan_params,
|
|
30
19
|
)
|
|
31
|
-
from dodal.devices.flux import Flux
|
|
32
|
-
from dodal.devices.robot import BartRobot
|
|
33
|
-
from dodal.devices.s4_slit_gaps import S4SlitGaps
|
|
34
|
-
from dodal.devices.smargon import Smargon
|
|
35
|
-
from dodal.devices.synchrotron import Synchrotron
|
|
36
|
-
from dodal.devices.undulator import Undulator
|
|
37
|
-
from dodal.devices.xbpm_feedback import XBPMFeedback
|
|
38
20
|
from dodal.devices.zebra.zebra import Zebra
|
|
39
|
-
from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
|
|
40
21
|
from dodal.devices.zocalo.zocalo_results import (
|
|
41
22
|
ZOCALO_READING_PLAN_NAME,
|
|
42
23
|
ZOCALO_STAGE_GROUP,
|
|
43
24
|
XrcResult,
|
|
44
|
-
ZocaloResults,
|
|
45
25
|
get_full_processing_results,
|
|
46
26
|
)
|
|
47
|
-
from event_model import RunStart
|
|
48
|
-
from ophyd_async.fastcs.panda import HDFPanda
|
|
49
27
|
|
|
50
28
|
from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback import (
|
|
51
29
|
ispyb_activation_wrapper,
|
|
52
30
|
)
|
|
31
|
+
from mx_bluesky.common.parameters.constants import HardwareConstants
|
|
53
32
|
from mx_bluesky.common.plans.do_fgs import kickoff_and_complete_gridscan
|
|
33
|
+
from mx_bluesky.common.plans.read_hardware import (
|
|
34
|
+
standard_read_hardware_during_collection,
|
|
35
|
+
standard_read_hardware_pre_collection,
|
|
36
|
+
)
|
|
37
|
+
from mx_bluesky.common.utils.context import device_composite_from_context
|
|
54
38
|
from mx_bluesky.common.utils.exceptions import (
|
|
55
39
|
CrystalNotFoundException,
|
|
56
40
|
SampleException,
|
|
57
41
|
)
|
|
58
42
|
from mx_bluesky.common.utils.log import LOGGER
|
|
59
43
|
from mx_bluesky.common.utils.tracing import TRACER
|
|
60
|
-
from mx_bluesky.
|
|
61
|
-
read_hardware_during_collection,
|
|
62
|
-
read_hardware_pre_collection,
|
|
63
|
-
)
|
|
44
|
+
from mx_bluesky.common.xrc_result import XRayCentreEventHandler, XRayCentreResult
|
|
64
45
|
from mx_bluesky.hyperion.device_setup_plans.setup_panda import (
|
|
65
46
|
disarm_panda_for_gridscan,
|
|
66
47
|
set_panda_directory,
|
|
@@ -77,10 +58,11 @@ from mx_bluesky.hyperion.device_setup_plans.xbpm_feedback import (
|
|
|
77
58
|
from mx_bluesky.hyperion.experiment_plans.change_aperture_then_move_plan import (
|
|
78
59
|
change_aperture_then_move_to_xtal,
|
|
79
60
|
)
|
|
80
|
-
from mx_bluesky.hyperion.experiment_plans.common.xrc_result import XRayCentreResult
|
|
81
61
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
62
|
+
from mx_bluesky.hyperion.parameters.device_composites import (
|
|
63
|
+
HyperionFlyScanXRayCentreComposite,
|
|
64
|
+
)
|
|
82
65
|
from mx_bluesky.hyperion.parameters.gridscan import HyperionSpecifiedThreeDGridScan
|
|
83
|
-
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
84
66
|
|
|
85
67
|
ZOCALO_MIN_TOTAL_COUNT_THRESHOLD = 3
|
|
86
68
|
|
|
@@ -89,57 +71,23 @@ class SmargonSpeedException(Exception):
|
|
|
89
71
|
pass
|
|
90
72
|
|
|
91
73
|
|
|
92
|
-
|
|
93
|
-
class FlyScanXRayCentreComposite:
|
|
94
|
-
"""All devices which are directly or indirectly required by this plan"""
|
|
95
|
-
|
|
96
|
-
aperture_scatterguard: ApertureScatterguard
|
|
97
|
-
attenuator: BinaryFilterAttenuator
|
|
98
|
-
backlight: Backlight
|
|
99
|
-
dcm: DCM
|
|
100
|
-
eiger: EigerDetector
|
|
101
|
-
zebra_fast_grid_scan: ZebraFastGridScan
|
|
102
|
-
flux: Flux
|
|
103
|
-
s4_slit_gaps: S4SlitGaps
|
|
104
|
-
smargon: Smargon
|
|
105
|
-
undulator: Undulator
|
|
106
|
-
synchrotron: Synchrotron
|
|
107
|
-
xbpm_feedback: XBPMFeedback
|
|
108
|
-
zebra: Zebra
|
|
109
|
-
zocalo: ZocaloResults
|
|
110
|
-
panda: HDFPanda
|
|
111
|
-
panda_fast_grid_scan: PandAFastGridScan
|
|
112
|
-
robot: BartRobot
|
|
113
|
-
sample_shutter: ZebraShutter
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
class XRayCentreEventHandler(CallbackBase):
|
|
117
|
-
def __init__(self):
|
|
118
|
-
super().__init__()
|
|
119
|
-
self.xray_centre_results: Sequence[XRayCentreResult] | None = None
|
|
120
|
-
|
|
121
|
-
def start(self, doc: RunStart) -> RunStart | None:
|
|
122
|
-
if "xray_centre_results" in doc:
|
|
123
|
-
self.xray_centre_results = [
|
|
124
|
-
XRayCentreResult(**result_dict)
|
|
125
|
-
for result_dict in doc["xray_centre_results"] # type: ignore
|
|
126
|
-
]
|
|
127
|
-
return doc
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def create_devices(context: BlueskyContext) -> FlyScanXRayCentreComposite:
|
|
74
|
+
def create_devices(context: BlueskyContext) -> HyperionFlyScanXRayCentreComposite:
|
|
131
75
|
"""Creates the devices required for the plan and connect to them"""
|
|
132
|
-
return device_composite_from_context(context,
|
|
76
|
+
return device_composite_from_context(context, HyperionFlyScanXRayCentreComposite)
|
|
133
77
|
|
|
134
78
|
|
|
135
79
|
def flyscan_xray_centre_no_move(
|
|
136
|
-
composite:
|
|
80
|
+
composite: HyperionFlyScanXRayCentreComposite,
|
|
81
|
+
parameters: HyperionSpecifiedThreeDGridScan,
|
|
137
82
|
) -> MsgGenerator:
|
|
138
83
|
"""Perform a flyscan and determine the centres of interest"""
|
|
139
|
-
|
|
84
|
+
|
|
140
85
|
composite.eiger.set_detector_parameters(parameters.detector_params)
|
|
141
86
|
composite.zocalo.zocalo_environment = CONST.ZOCALO_ENV
|
|
87
|
+
|
|
88
|
+
parameters.features.update_self_from_server()
|
|
142
89
|
composite.zocalo.use_cpu_and_gpu = parameters.features.compare_cpu_and_gpu_zocalo
|
|
90
|
+
composite.zocalo.use_gpu = parameters.features.use_gpu_results
|
|
143
91
|
|
|
144
92
|
feature_controlled = _get_feature_controlled(composite, parameters)
|
|
145
93
|
|
|
@@ -155,12 +103,14 @@ def flyscan_xray_centre_no_move(
|
|
|
155
103
|
)
|
|
156
104
|
@bpp.finalize_decorator(lambda: feature_controlled.tidy_plan(composite))
|
|
157
105
|
@transmission_and_xbpm_feedback_for_collection_decorator(
|
|
106
|
+
composite.undulator,
|
|
158
107
|
composite.xbpm_feedback,
|
|
159
108
|
composite.attenuator,
|
|
109
|
+
composite.dcm,
|
|
160
110
|
parameters.transmission_frac,
|
|
161
111
|
)
|
|
162
112
|
def run_gridscan_and_fetch_and_tidy(
|
|
163
|
-
fgs_composite:
|
|
113
|
+
fgs_composite: HyperionFlyScanXRayCentreComposite,
|
|
164
114
|
params: HyperionSpecifiedThreeDGridScan,
|
|
165
115
|
feature_controlled: _FeatureControlled,
|
|
166
116
|
) -> MsgGenerator:
|
|
@@ -174,7 +124,7 @@ def flyscan_xray_centre_no_move(
|
|
|
174
124
|
|
|
175
125
|
|
|
176
126
|
def flyscan_xray_centre(
|
|
177
|
-
composite:
|
|
127
|
+
composite: HyperionFlyScanXRayCentreComposite,
|
|
178
128
|
parameters: HyperionSpecifiedThreeDGridScan,
|
|
179
129
|
) -> MsgGenerator:
|
|
180
130
|
"""Create the plan to run the grid scan based on provided parameters.
|
|
@@ -213,7 +163,7 @@ def flyscan_xray_centre(
|
|
|
213
163
|
@bpp.set_run_key_decorator(CONST.PLAN.GRIDSCAN_AND_MOVE)
|
|
214
164
|
@bpp.run_decorator(md={"subplan_name": CONST.PLAN.GRIDSCAN_AND_MOVE})
|
|
215
165
|
def run_gridscan_and_fetch_results(
|
|
216
|
-
fgs_composite:
|
|
166
|
+
fgs_composite: HyperionFlyScanXRayCentreComposite,
|
|
217
167
|
parameters: HyperionSpecifiedThreeDGridScan,
|
|
218
168
|
feature_controlled: _FeatureControlled,
|
|
219
169
|
) -> MsgGenerator:
|
|
@@ -261,7 +211,7 @@ def run_gridscan_and_fetch_results(
|
|
|
261
211
|
finally:
|
|
262
212
|
# Turn off dev/shm streaming to avoid filling disk, see https://github.com/DiamondLightSource/hyperion/issues/1395
|
|
263
213
|
LOGGER.info("Turning off Eiger dev/shm streaming")
|
|
264
|
-
yield from bps.abs_set(fgs_composite.eiger.odin.fan.dev_shm_enable, 0) # type: ignore #
|
|
214
|
+
yield from bps.abs_set(fgs_composite.eiger.odin.fan.dev_shm_enable, 0) # type: ignore # Fix types in ophyd-async (https://github.com/DiamondLightSource/mx-bluesky/issues/855)
|
|
265
215
|
|
|
266
216
|
# Wait on everything before returning to GDA (particularly apertures), can be removed
|
|
267
217
|
# when we do not return to GDA here
|
|
@@ -303,14 +253,14 @@ def _fire_xray_centre_result_event(results: Sequence[XRayCentreResult]):
|
|
|
303
253
|
|
|
304
254
|
yield from bpp.run_wrapper(
|
|
305
255
|
empty_plan(),
|
|
306
|
-
md={
|
|
256
|
+
md={CONST.PLAN.FLYSCAN_RESULTS: [dataclasses.asdict(r) for r in results]},
|
|
307
257
|
)
|
|
308
258
|
|
|
309
259
|
|
|
310
260
|
@bpp.set_run_key_decorator(CONST.PLAN.GRIDSCAN_MAIN)
|
|
311
261
|
@bpp.run_decorator(md={"subplan_name": CONST.PLAN.GRIDSCAN_MAIN})
|
|
312
262
|
def run_gridscan(
|
|
313
|
-
fgs_composite:
|
|
263
|
+
fgs_composite: HyperionFlyScanXRayCentreComposite,
|
|
314
264
|
parameters: HyperionSpecifiedThreeDGridScan,
|
|
315
265
|
feature_controlled: _FeatureControlled,
|
|
316
266
|
md={ # noqa
|
|
@@ -325,7 +275,7 @@ def run_gridscan(
|
|
|
325
275
|
# we should generate an event reading the values which need to be included in the
|
|
326
276
|
# ispyb deposition
|
|
327
277
|
with TRACER.start_span("ispyb_hardware_readings"):
|
|
328
|
-
yield from
|
|
278
|
+
yield from standard_read_hardware_pre_collection(
|
|
329
279
|
fgs_composite.undulator,
|
|
330
280
|
fgs_composite.synchrotron,
|
|
331
281
|
fgs_composite.s4_slit_gaps,
|
|
@@ -334,7 +284,7 @@ def run_gridscan(
|
|
|
334
284
|
)
|
|
335
285
|
|
|
336
286
|
read_during_collection = partial(
|
|
337
|
-
|
|
287
|
+
standard_read_hardware_during_collection,
|
|
338
288
|
fgs_composite.aperture_scatterguard,
|
|
339
289
|
fgs_composite.attenuator,
|
|
340
290
|
fgs_composite.flux,
|
|
@@ -350,7 +300,7 @@ def run_gridscan(
|
|
|
350
300
|
|
|
351
301
|
LOGGER.info("Waiting for arming to finish")
|
|
352
302
|
yield from bps.wait(CONST.WAIT.GRID_READY_FOR_DC)
|
|
353
|
-
yield from bps.stage(fgs_composite.eiger)
|
|
303
|
+
yield from bps.stage(fgs_composite.eiger)
|
|
354
304
|
|
|
355
305
|
yield from kickoff_and_complete_gridscan(
|
|
356
306
|
feature_controlled.fgs_motors,
|
|
@@ -390,18 +340,18 @@ class _FeatureControlled:
|
|
|
390
340
|
class _ExtraSetup(Protocol):
|
|
391
341
|
def __call__(
|
|
392
342
|
self,
|
|
393
|
-
fgs_composite:
|
|
343
|
+
fgs_composite: HyperionFlyScanXRayCentreComposite,
|
|
394
344
|
parameters: HyperionSpecifiedThreeDGridScan,
|
|
395
345
|
) -> MsgGenerator: ...
|
|
396
346
|
|
|
397
347
|
setup_trigger: _ExtraSetup
|
|
398
|
-
tidy_plan: Callable[[
|
|
348
|
+
tidy_plan: Callable[[HyperionFlyScanXRayCentreComposite], MsgGenerator]
|
|
399
349
|
set_flyscan_params: Callable[[], MsgGenerator]
|
|
400
350
|
fgs_motors: FastGridScanCommon
|
|
401
351
|
|
|
402
352
|
|
|
403
353
|
def _get_feature_controlled(
|
|
404
|
-
fgs_composite:
|
|
354
|
+
fgs_composite: HyperionFlyScanXRayCentreComposite,
|
|
405
355
|
parameters: HyperionSpecifiedThreeDGridScan,
|
|
406
356
|
):
|
|
407
357
|
if parameters.features.use_panda_for_gridscan:
|
|
@@ -429,7 +379,7 @@ def _get_feature_controlled(
|
|
|
429
379
|
|
|
430
380
|
|
|
431
381
|
def _generic_tidy(
|
|
432
|
-
fgs_composite:
|
|
382
|
+
fgs_composite: HyperionFlyScanXRayCentreComposite, group, wait=True
|
|
433
383
|
) -> MsgGenerator:
|
|
434
384
|
LOGGER.info("Tidying up Zebra")
|
|
435
385
|
yield from tidy_up_zebra_after_gridscan(
|
|
@@ -440,7 +390,7 @@ def _generic_tidy(
|
|
|
440
390
|
yield from bps.unstage(fgs_composite.zocalo, group=group, wait=wait)
|
|
441
391
|
|
|
442
392
|
|
|
443
|
-
def _panda_tidy(fgs_composite:
|
|
393
|
+
def _panda_tidy(fgs_composite: HyperionFlyScanXRayCentreComposite):
|
|
444
394
|
group = "panda_flyscan_tidy"
|
|
445
395
|
LOGGER.info("Disabling panda blocks")
|
|
446
396
|
yield from disarm_panda_for_gridscan(fgs_composite.panda, group)
|
|
@@ -450,7 +400,7 @@ def _panda_tidy(fgs_composite: FlyScanXRayCentreComposite):
|
|
|
450
400
|
|
|
451
401
|
|
|
452
402
|
def _zebra_triggering_setup(
|
|
453
|
-
fgs_composite:
|
|
403
|
+
fgs_composite: HyperionFlyScanXRayCentreComposite,
|
|
454
404
|
parameters: HyperionSpecifiedThreeDGridScan,
|
|
455
405
|
):
|
|
456
406
|
yield from setup_zebra_for_gridscan(
|
|
@@ -459,7 +409,7 @@ def _zebra_triggering_setup(
|
|
|
459
409
|
|
|
460
410
|
|
|
461
411
|
def _panda_triggering_setup(
|
|
462
|
-
fgs_composite:
|
|
412
|
+
fgs_composite: HyperionFlyScanXRayCentreComposite,
|
|
463
413
|
parameters: HyperionSpecifiedThreeDGridScan,
|
|
464
414
|
):
|
|
465
415
|
LOGGER.info("Setting up Panda for flyscan")
|
|
@@ -468,10 +418,9 @@ def _panda_triggering_setup(
|
|
|
468
418
|
fgs_composite.panda_fast_grid_scan.run_up_distance_mm
|
|
469
419
|
)
|
|
470
420
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
time_between_x_steps_ms = (DEADTIME_S + parameters.exposure_time_s) * 1e3
|
|
421
|
+
time_between_x_steps_ms = (
|
|
422
|
+
HardwareConstants.PANDA_FGS_EIGER_DEADTIME_S + parameters.exposure_time_s
|
|
423
|
+
) * 1e3
|
|
475
424
|
|
|
476
425
|
smargon_speed_limit_mm_per_s = yield from bps.rd(
|
|
477
426
|
fgs_composite.smargon.x.max_velocity
|
|
@@ -490,13 +439,13 @@ def _panda_triggering_setup(
|
|
|
490
439
|
)
|
|
491
440
|
else:
|
|
492
441
|
LOGGER.info(
|
|
493
|
-
f"Panda grid scan: Smargon speed set to {
|
|
442
|
+
f"Panda grid scan: Smargon speed set to {sample_velocity_mm_per_s} mm/s"
|
|
494
443
|
f" and using a run-up distance of {run_up_distance_mm}"
|
|
495
444
|
)
|
|
496
445
|
|
|
497
446
|
yield from bps.mv(
|
|
498
|
-
fgs_composite.panda_fast_grid_scan.time_between_x_steps_ms,
|
|
499
|
-
time_between_x_steps_ms,
|
|
447
|
+
fgs_composite.panda_fast_grid_scan.time_between_x_steps_ms,
|
|
448
|
+
time_between_x_steps_ms,
|
|
500
449
|
)
|
|
501
450
|
|
|
502
451
|
directory_provider_root = Path(parameters.storage_directory)
|
|
@@ -39,7 +39,9 @@ from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback
|
|
|
39
39
|
ispyb_activation_wrapper,
|
|
40
40
|
)
|
|
41
41
|
from mx_bluesky.common.parameters.constants import OavConstants
|
|
42
|
+
from mx_bluesky.common.utils.context import device_composite_from_context
|
|
42
43
|
from mx_bluesky.common.utils.log import LOGGER
|
|
44
|
+
from mx_bluesky.common.xrc_result import XRayCentreEventHandler
|
|
43
45
|
from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import (
|
|
44
46
|
move_aperture_if_required,
|
|
45
47
|
)
|
|
@@ -50,10 +52,6 @@ from mx_bluesky.hyperion.experiment_plans.change_aperture_then_move_plan import
|
|
|
50
52
|
change_aperture_then_move_to_xtal,
|
|
51
53
|
)
|
|
52
54
|
from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
|
|
53
|
-
FlyScanXRayCentreComposite as FlyScanXRayCentreComposite,
|
|
54
|
-
)
|
|
55
|
-
from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
|
|
56
|
-
XRayCentreEventHandler,
|
|
57
55
|
flyscan_xray_centre_no_move,
|
|
58
56
|
)
|
|
59
57
|
from mx_bluesky.hyperion.experiment_plans.oav_grid_detection_plan import (
|
|
@@ -61,11 +59,13 @@ from mx_bluesky.hyperion.experiment_plans.oav_grid_detection_plan import (
|
|
|
61
59
|
grid_detection_plan,
|
|
62
60
|
)
|
|
63
61
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
62
|
+
from mx_bluesky.hyperion.parameters.device_composites import (
|
|
63
|
+
HyperionFlyScanXRayCentreComposite,
|
|
64
|
+
)
|
|
64
65
|
from mx_bluesky.hyperion.parameters.gridscan import (
|
|
65
66
|
GridScanWithEdgeDetect,
|
|
66
67
|
HyperionSpecifiedThreeDGridScan,
|
|
67
68
|
)
|
|
68
|
-
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
@@ -142,6 +142,14 @@ def detect_grid_and_do_gridscan(
|
|
|
142
142
|
parameters.box_size_um,
|
|
143
143
|
)
|
|
144
144
|
|
|
145
|
+
if parameters.selected_aperture:
|
|
146
|
+
# Start moving the aperture/scatterguard into position without moving it in
|
|
147
|
+
yield from bps.prepare(
|
|
148
|
+
composite.aperture_scatterguard,
|
|
149
|
+
parameters.selected_aperture,
|
|
150
|
+
group=CONST.WAIT.GRID_READY_FOR_DC,
|
|
151
|
+
)
|
|
152
|
+
|
|
145
153
|
yield from run_grid_detection_plan(
|
|
146
154
|
oav_params,
|
|
147
155
|
snapshot_template,
|
|
@@ -159,7 +167,7 @@ def detect_grid_and_do_gridscan(
|
|
|
159
167
|
)
|
|
160
168
|
|
|
161
169
|
yield from flyscan_xray_centre_no_move(
|
|
162
|
-
|
|
170
|
+
HyperionFlyScanXRayCentreComposite(
|
|
163
171
|
aperture_scatterguard=composite.aperture_scatterguard,
|
|
164
172
|
attenuator=composite.attenuator,
|
|
165
173
|
backlight=composite.backlight,
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import bluesky.plan_stubs as bps
|
|
4
|
+
import numpy as np
|
|
5
5
|
import pydantic
|
|
6
6
|
from blueapi.core import BlueskyContext
|
|
7
7
|
from bluesky.preprocessors import run_decorator, set_run_key_decorator, subs_wrapper
|
|
8
8
|
from bluesky.utils import MsgGenerator
|
|
9
9
|
from dodal.devices.oav.oav_parameters import OAVParameters
|
|
10
10
|
|
|
11
|
-
import mx_bluesky.
|
|
11
|
+
import mx_bluesky.common.xrc_result as flyscan_result
|
|
12
|
+
from mx_bluesky.common.utils.context import device_composite_from_context
|
|
12
13
|
from mx_bluesky.common.utils.log import LOGGER
|
|
13
|
-
from mx_bluesky.
|
|
14
|
-
XRayCentreEventHandler,
|
|
15
|
-
)
|
|
14
|
+
from mx_bluesky.common.xrc_result import XRayCentreEventHandler
|
|
16
15
|
from mx_bluesky.hyperion.experiment_plans.robot_load_then_centre_plan import (
|
|
17
16
|
RobotLoadThenCentreComposite,
|
|
18
17
|
robot_load_then_xray_centre,
|
|
@@ -24,7 +23,6 @@ from mx_bluesky.hyperion.experiment_plans.rotation_scan_plan import (
|
|
|
24
23
|
)
|
|
25
24
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
26
25
|
from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect
|
|
27
|
-
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
28
26
|
|
|
29
27
|
|
|
30
28
|
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
@@ -66,33 +64,40 @@ def load_centre_collect_full(
|
|
|
66
64
|
flyscan_event_handler,
|
|
67
65
|
)
|
|
68
66
|
|
|
69
|
-
|
|
70
|
-
"Flyscan result event not received or no crystal found and exception not raised"
|
|
71
|
-
)
|
|
67
|
+
locations_to_collect_um: list[np.ndarray] = []
|
|
72
68
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
flyscan_event_handler.xray_centre_results
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
69
|
+
if flyscan_event_handler.xray_centre_results:
|
|
70
|
+
selection_func = flyscan_result.resolve_selection_fn(
|
|
71
|
+
parameters.selection_params
|
|
72
|
+
)
|
|
73
|
+
hits = selection_func(flyscan_event_handler.xray_centre_results)
|
|
74
|
+
locations_to_collect_um = [hit.centre_of_mass_mm * 1000 for hit in hits]
|
|
75
|
+
|
|
76
|
+
LOGGER.info(
|
|
77
|
+
f"Selected hits {hits} using {selection_func}, args={parameters.selection_params}"
|
|
78
|
+
)
|
|
79
|
+
else:
|
|
80
|
+
# If the xray centring hasn't found a result but has not thrown an error it
|
|
81
|
+
# means that we do not need to recentre and can collect where we are
|
|
82
|
+
initial_x = yield from bps.rd(composite.smargon.x.user_readback)
|
|
83
|
+
initial_y = yield from bps.rd(composite.smargon.y.user_readback)
|
|
84
|
+
initial_z = yield from bps.rd(composite.smargon.z.user_readback)
|
|
85
|
+
|
|
86
|
+
locations_to_collect_um = [np.array([initial_x, initial_y, initial_z])]
|
|
82
87
|
|
|
83
88
|
multi_rotation = parameters.multi_rotation_scan
|
|
84
89
|
rotation_template = multi_rotation.rotation_scans.copy()
|
|
85
90
|
|
|
86
91
|
multi_rotation.rotation_scans.clear()
|
|
87
92
|
|
|
88
|
-
for
|
|
93
|
+
for location in locations_to_collect_um:
|
|
89
94
|
for rot in rotation_template:
|
|
90
95
|
combination = rot.model_copy()
|
|
91
96
|
(
|
|
92
97
|
combination.x_start_um,
|
|
93
98
|
combination.y_start_um,
|
|
94
99
|
combination.z_start_um,
|
|
95
|
-
) =
|
|
100
|
+
) = location
|
|
96
101
|
multi_rotation.rotation_scans.append(combination)
|
|
97
102
|
multi_rotation = MultiRotationScan.model_validate(multi_rotation)
|
|
98
103
|
|
|
@@ -14,13 +14,13 @@ from dodal.devices.oav.pin_image_recognition.utils import NONE_VALUE
|
|
|
14
14
|
from dodal.devices.oav.utils import PinNotFoundException, wait_for_tip_to_be_found
|
|
15
15
|
from dodal.devices.smargon import Smargon
|
|
16
16
|
|
|
17
|
+
from mx_bluesky.common.utils.context import device_composite_from_context
|
|
17
18
|
from mx_bluesky.common.utils.exceptions import catch_exception_and_warn
|
|
18
19
|
from mx_bluesky.common.utils.log import LOGGER
|
|
19
20
|
from mx_bluesky.hyperion.device_setup_plans.setup_oav import (
|
|
20
21
|
pre_centring_setup_oav,
|
|
21
22
|
)
|
|
22
23
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
23
|
-
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
24
24
|
|
|
25
25
|
if TYPE_CHECKING:
|
|
26
26
|
from dodal.devices.oav.oav_parameters import OAVParameters
|
|
@@ -100,7 +100,7 @@ def grid_detection_plan(
|
|
|
100
100
|
|
|
101
101
|
# The FGS uses -90 so we need to match it
|
|
102
102
|
for angle in [0, -90]:
|
|
103
|
-
yield from bps.mv(smargon.omega, angle)
|
|
103
|
+
yield from bps.mv(smargon.omega, angle)
|
|
104
104
|
# need to wait for the OAV image to update
|
|
105
105
|
# See #673 for improvements
|
|
106
106
|
yield from bps.sleep(CONST.HARDWARE.OAV_REFRESH_DELAY)
|
|
@@ -152,20 +152,20 @@ def grid_detection_plan(
|
|
|
152
152
|
|
|
153
153
|
upper_left = (tip_x_px, min_y)
|
|
154
154
|
|
|
155
|
-
yield from bps.abs_set(oav.grid_snapshot.top_left_x, upper_left[0])
|
|
156
|
-
yield from bps.abs_set(oav.grid_snapshot.top_left_y, upper_left[1])
|
|
157
|
-
yield from bps.abs_set(oav.grid_snapshot.box_width, box_size_x_pixels)
|
|
158
|
-
yield from bps.abs_set(oav.grid_snapshot.num_boxes_x, x_steps)
|
|
159
|
-
yield from bps.abs_set(oav.grid_snapshot.num_boxes_y, y_steps)
|
|
155
|
+
yield from bps.abs_set(oav.grid_snapshot.top_left_x, upper_left[0])
|
|
156
|
+
yield from bps.abs_set(oav.grid_snapshot.top_left_y, upper_left[1])
|
|
157
|
+
yield from bps.abs_set(oav.grid_snapshot.box_width, box_size_x_pixels)
|
|
158
|
+
yield from bps.abs_set(oav.grid_snapshot.num_boxes_x, x_steps)
|
|
159
|
+
yield from bps.abs_set(oav.grid_snapshot.num_boxes_y, y_steps)
|
|
160
160
|
|
|
161
161
|
snapshot_filename = snapshot_template.format(angle=abs(angle))
|
|
162
162
|
|
|
163
|
-
yield from bps.abs_set(oav.grid_snapshot.filename, snapshot_filename)
|
|
164
|
-
yield from bps.abs_set(oav.grid_snapshot.directory, snapshot_dir)
|
|
165
|
-
yield from bps.trigger(oav.grid_snapshot, wait=True)
|
|
163
|
+
yield from bps.abs_set(oav.grid_snapshot.filename, snapshot_filename)
|
|
164
|
+
yield from bps.abs_set(oav.grid_snapshot.directory, snapshot_dir)
|
|
165
|
+
yield from bps.trigger(oav.grid_snapshot, wait=True)
|
|
166
166
|
yield from bps.create(CONST.DESCRIPTORS.OAV_GRID_SNAPSHOT_TRIGGERED)
|
|
167
167
|
|
|
168
|
-
yield from bps.read(oav)
|
|
168
|
+
yield from bps.read(oav)
|
|
169
169
|
yield from bps.read(smargon)
|
|
170
170
|
yield from bps.save()
|
|
171
171
|
|
|
@@ -34,9 +34,7 @@ def setup_beamline_for_OAV(
|
|
|
34
34
|
yield from bps.abs_set(smargon.omega.velocity, max_vel, group=group)
|
|
35
35
|
yield from bps.abs_set(backlight, BacklightPosition.IN, group=group)
|
|
36
36
|
yield from bps.abs_set(
|
|
37
|
-
aperture_scatterguard,
|
|
38
|
-
ApertureValue.ROBOT_LOAD,
|
|
39
|
-
group=group,
|
|
37
|
+
aperture_scatterguard, ApertureValue.OUT_OF_BEAM, group=group
|
|
40
38
|
)
|
|
41
39
|
|
|
42
40
|
|
|
@@ -44,11 +42,9 @@ def oav_snapshot_plan(
|
|
|
44
42
|
composite: OavSnapshotComposite,
|
|
45
43
|
parameters: WithSnapshot,
|
|
46
44
|
oav_parameters: OAVParameters,
|
|
47
|
-
wait: bool = True,
|
|
48
45
|
) -> MsgGenerator:
|
|
49
46
|
if not parameters.take_snapshots:
|
|
50
47
|
return
|
|
51
|
-
yield from bps.wait(group=CONST.WAIT.READY_FOR_OAV)
|
|
52
48
|
yield from _setup_oav(composite, parameters, oav_parameters)
|
|
53
49
|
for omega in parameters.snapshot_omegas_deg or []:
|
|
54
50
|
yield from _take_oav_snapshot(composite, omega)
|
|
@@ -61,7 +57,7 @@ def _setup_oav(
|
|
|
61
57
|
):
|
|
62
58
|
yield from setup_general_oav_params(composite.oav, oav_parameters)
|
|
63
59
|
yield from bps.abs_set(
|
|
64
|
-
composite.oav.snapshot.directory,
|
|
60
|
+
composite.oav.snapshot.directory,
|
|
65
61
|
str(parameters.snapshot_directory),
|
|
66
62
|
)
|
|
67
63
|
|
|
@@ -73,12 +69,12 @@ def _take_oav_snapshot(composite: OavSnapshotComposite, omega: float):
|
|
|
73
69
|
time_now = datetime.now()
|
|
74
70
|
filename = f"{time_now.strftime('%H%M%S')}_oav_snapshot_{omega:.0f}"
|
|
75
71
|
yield from bps.abs_set(
|
|
76
|
-
composite.oav.snapshot.filename,
|
|
72
|
+
composite.oav.snapshot.filename,
|
|
77
73
|
filename,
|
|
78
74
|
group=OAV_SNAPSHOT_SETUP_SHOT,
|
|
79
75
|
)
|
|
80
76
|
yield from bps.wait(group=OAV_SNAPSHOT_SETUP_SHOT)
|
|
81
|
-
yield from bps.trigger(composite.oav.snapshot, wait=True)
|
|
77
|
+
yield from bps.trigger(composite.oav.snapshot, wait=True)
|
|
82
78
|
yield from bps.create(DocDescriptorNames.OAV_ROTATION_SNAPSHOT_TRIGGERED)
|
|
83
|
-
yield from bps.read(composite.oav.snapshot)
|
|
79
|
+
yield from bps.read(composite.oav.snapshot)
|
|
84
80
|
yield from bps.save()
|
|
@@ -9,8 +9,8 @@ from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
|
|
|
9
9
|
from dodal.devices.xspress3.xspress3 import Xspress3
|
|
10
10
|
from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter, ZebraShutterState
|
|
11
11
|
|
|
12
|
+
from mx_bluesky.common.utils.context import device_composite_from_context
|
|
12
13
|
from mx_bluesky.common.utils.log import LOGGER
|
|
13
|
-
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class AttenuationOptimisationFailedException(Exception):
|
|
@@ -12,7 +12,9 @@ from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback
|
|
|
12
12
|
ispyb_activation_wrapper,
|
|
13
13
|
)
|
|
14
14
|
from mx_bluesky.common.parameters.constants import OavConstants
|
|
15
|
+
from mx_bluesky.common.utils.context import device_composite_from_context
|
|
15
16
|
from mx_bluesky.common.utils.log import LOGGER
|
|
17
|
+
from mx_bluesky.common.xrc_result import XRayCentreEventHandler
|
|
16
18
|
from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import move_phi_chi_omega
|
|
17
19
|
from mx_bluesky.hyperion.device_setup_plans.utils import (
|
|
18
20
|
start_preparing_data_collection_then_do_plan,
|
|
@@ -20,9 +22,6 @@ from mx_bluesky.hyperion.device_setup_plans.utils import (
|
|
|
20
22
|
from mx_bluesky.hyperion.experiment_plans.change_aperture_then_move_plan import (
|
|
21
23
|
change_aperture_then_move_to_xtal,
|
|
22
24
|
)
|
|
23
|
-
from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
|
|
24
|
-
XRayCentreEventHandler,
|
|
25
|
-
)
|
|
26
25
|
from mx_bluesky.hyperion.experiment_plans.grid_detect_then_xray_centre_plan import (
|
|
27
26
|
GridDetectThenXRayCentreComposite,
|
|
28
27
|
detect_grid_and_do_gridscan,
|
|
@@ -39,7 +38,6 @@ from mx_bluesky.hyperion.parameters.gridscan import (
|
|
|
39
38
|
GridScanWithEdgeDetect,
|
|
40
39
|
PinTipCentreThenXrayCentre,
|
|
41
40
|
)
|
|
42
|
-
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
43
41
|
|
|
44
42
|
|
|
45
43
|
def create_devices(context: BlueskyContext) -> GridDetectThenXRayCentreComposite:
|