mx-bluesky 1.4.6__py3-none-any.whl → 1.4.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mx_bluesky/_version.py +2 -2
- mx_bluesky/beamlines/aithre_lasershaping/__init__.py +13 -0
- mx_bluesky/beamlines/aithre_lasershaping/check_goniometer_performance.py +29 -0
- mx_bluesky/beamlines/aithre_lasershaping/goniometer_controls.py +18 -0
- mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +31 -25
- mx_bluesky/beamlines/i04/thawing_plan.py +10 -1
- mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +12 -12
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +30 -29
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +10 -11
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +8 -10
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +10 -3
- mx_bluesky/beamlines/i24/serial/log.py +1 -0
- mx_bluesky/beamlines/i24/serial/set_visit_directory.sh +1 -1
- mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +16 -16
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +47 -48
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +1 -1
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +9 -7
- mx_bluesky/beamlines/i24/serial/write_nexus.py +3 -2
- mx_bluesky/common/device_setup_plans/xbpm_feedback.py +45 -0
- mx_bluesky/common/external_interaction/callbacks/common/ispyb_callback_base.py +2 -4
- mx_bluesky/common/external_interaction/callbacks/common/ispyb_mapping.py +1 -1
- mx_bluesky/common/external_interaction/callbacks/common/zocalo_callback.py +18 -15
- mx_bluesky/{hyperion → common}/external_interaction/callbacks/sample_handling/sample_handling_callback.py +16 -4
- mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py +41 -5
- mx_bluesky/common/external_interaction/callbacks/xray_centre/nexus_callback.py +2 -1
- mx_bluesky/common/external_interaction/ispyb/data_model.py +1 -0
- mx_bluesky/common/external_interaction/ispyb/ispyb_store.py +14 -1
- mx_bluesky/common/external_interaction/nexus/nexus_utils.py +1 -1
- mx_bluesky/common/parameters/constants.py +2 -0
- mx_bluesky/common/parameters/gridscan.py +1 -1
- mx_bluesky/common/plans/write_sample_status.py +46 -0
- mx_bluesky/common/preprocessors/__init__.py +0 -0
- mx_bluesky/common/preprocessors/preprocessors.py +105 -0
- mx_bluesky/common/protocols/__init__.py +0 -0
- mx_bluesky/common/protocols/protocols.py +10 -0
- mx_bluesky/hyperion/__main__.py +3 -9
- mx_bluesky/hyperion/baton_handler.py +84 -0
- mx_bluesky/hyperion/device_setup_plans/setup_panda.py +5 -1
- mx_bluesky/hyperion/experiment_plans/__init__.py +0 -4
- mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +10 -25
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +0 -7
- mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +11 -10
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +5 -1
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +2 -2
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +5 -3
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +0 -26
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +23 -18
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +25 -6
- mx_bluesky/hyperion/external_interaction/agamemnon.py +148 -10
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +12 -6
- mx_bluesky/hyperion/external_interaction/callbacks/snapshot_callback.py +107 -0
- mx_bluesky/hyperion/parameters/gridscan.py +2 -2
- mx_bluesky/hyperion/parameters/rotation.py +1 -1
- {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.7.dist-info}/METADATA +7 -7
- {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.7.dist-info}/RECORD +60 -51
- {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.7.dist-info}/WHEEL +1 -1
- mx_bluesky/common/external_interaction/callbacks/common/aperture_change_callback.py +0 -22
- mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +0 -103
- /mx_bluesky/{hyperion → common}/external_interaction/callbacks/sample_handling/__init__.py +0 -0
- {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.7.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.7.dist-info/licenses}/LICENSE +0 -0
- {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.7.dist-info}/top_level.txt +0 -0
|
@@ -24,6 +24,9 @@ from dodal.devices.zocalo.zocalo_results import (
|
|
|
24
24
|
XrcResult,
|
|
25
25
|
get_full_processing_results,
|
|
26
26
|
)
|
|
27
|
+
from dodal.plans.preprocessors.verify_undulator_gap import (
|
|
28
|
+
verify_undulator_gap_before_run_decorator,
|
|
29
|
+
)
|
|
27
30
|
|
|
28
31
|
from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback import (
|
|
29
32
|
ispyb_activation_wrapper,
|
|
@@ -34,6 +37,9 @@ from mx_bluesky.common.plans.read_hardware import (
|
|
|
34
37
|
standard_read_hardware_during_collection,
|
|
35
38
|
standard_read_hardware_pre_collection,
|
|
36
39
|
)
|
|
40
|
+
from mx_bluesky.common.preprocessors.preprocessors import (
|
|
41
|
+
transmission_and_xbpm_feedback_for_collection_decorator,
|
|
42
|
+
)
|
|
37
43
|
from mx_bluesky.common.utils.context import device_composite_from_context
|
|
38
44
|
from mx_bluesky.common.utils.exceptions import (
|
|
39
45
|
CrystalNotFoundException,
|
|
@@ -52,9 +58,6 @@ from mx_bluesky.hyperion.device_setup_plans.setup_zebra import (
|
|
|
52
58
|
setup_zebra_for_panda_flyscan,
|
|
53
59
|
tidy_up_zebra_after_gridscan,
|
|
54
60
|
)
|
|
55
|
-
from mx_bluesky.hyperion.device_setup_plans.xbpm_feedback import (
|
|
56
|
-
transmission_and_xbpm_feedback_for_collection_decorator,
|
|
57
|
-
)
|
|
58
61
|
from mx_bluesky.hyperion.experiment_plans.change_aperture_then_move_plan import (
|
|
59
62
|
change_aperture_then_move_to_xtal,
|
|
60
63
|
)
|
|
@@ -102,13 +105,6 @@ def flyscan_xray_centre_no_move(
|
|
|
102
105
|
}
|
|
103
106
|
)
|
|
104
107
|
@bpp.finalize_decorator(lambda: feature_controlled.tidy_plan(composite))
|
|
105
|
-
@transmission_and_xbpm_feedback_for_collection_decorator(
|
|
106
|
-
composite.undulator,
|
|
107
|
-
composite.xbpm_feedback,
|
|
108
|
-
composite.attenuator,
|
|
109
|
-
composite.dcm,
|
|
110
|
-
parameters.transmission_frac,
|
|
111
|
-
)
|
|
112
108
|
def run_gridscan_and_fetch_and_tidy(
|
|
113
109
|
fgs_composite: HyperionFlyScanXRayCentreComposite,
|
|
114
110
|
params: HyperionSpecifiedThreeDGridScan,
|
|
@@ -140,6 +136,11 @@ def flyscan_xray_centre(
|
|
|
140
136
|
"""
|
|
141
137
|
xrc_event_handler = XRayCentreEventHandler()
|
|
142
138
|
|
|
139
|
+
@transmission_and_xbpm_feedback_for_collection_decorator(
|
|
140
|
+
composite,
|
|
141
|
+
parameters.transmission_frac,
|
|
142
|
+
)
|
|
143
|
+
@verify_undulator_gap_before_run_decorator(composite)
|
|
143
144
|
@bpp.subs_decorator(xrc_event_handler)
|
|
144
145
|
def flyscan_and_fetch_results() -> MsgGenerator:
|
|
145
146
|
yield from ispyb_activation_wrapper(
|
|
@@ -9,6 +9,7 @@ from bluesky.utils import MsgGenerator
|
|
|
9
9
|
from dodal.devices.oav.oav_parameters import OAVParameters
|
|
10
10
|
|
|
11
11
|
import mx_bluesky.common.xrc_result as flyscan_result
|
|
12
|
+
from mx_bluesky.common.parameters.components import WithSnapshot
|
|
12
13
|
from mx_bluesky.common.utils.context import device_composite_from_context
|
|
13
14
|
from mx_bluesky.common.utils.log import LOGGER
|
|
14
15
|
from mx_bluesky.common.xrc_result import XRayCentreEventHandler
|
|
@@ -54,7 +55,10 @@ def load_centre_collect_full(
|
|
|
54
55
|
@run_decorator(
|
|
55
56
|
md={
|
|
56
57
|
"metadata": {"sample_id": parameters.sample_id},
|
|
57
|
-
"activate_callbacks": ["SampleHandlingCallback"],
|
|
58
|
+
"activate_callbacks": ["BeamDrawingCallback", "SampleHandlingCallback"],
|
|
59
|
+
"with_snapshot": parameters.multi_rotation_scan.model_dump_json(
|
|
60
|
+
include=WithSnapshot.model_fields.keys() # type: ignore
|
|
61
|
+
),
|
|
58
62
|
}
|
|
59
63
|
)
|
|
60
64
|
def plan_with_callback_subs():
|
|
@@ -67,7 +67,7 @@ def _take_oav_snapshot(composite: OavSnapshotComposite, omega: float):
|
|
|
67
67
|
composite.smargon.omega, omega, group=OAV_SNAPSHOT_SETUP_SHOT
|
|
68
68
|
)
|
|
69
69
|
time_now = datetime.now()
|
|
70
|
-
filename = f"{time_now.strftime('%H%M%S')}_oav_snapshot_{omega:.0f}"
|
|
70
|
+
filename = f"{time_now.strftime('%H%M%S%f')[:8]}_oav_snapshot_{omega:.0f}"
|
|
71
71
|
yield from bps.abs_set(
|
|
72
72
|
composite.oav.snapshot.filename,
|
|
73
73
|
filename,
|
|
@@ -76,5 +76,5 @@ def _take_oav_snapshot(composite: OavSnapshotComposite, omega: float):
|
|
|
76
76
|
yield from bps.wait(group=OAV_SNAPSHOT_SETUP_SHOT)
|
|
77
77
|
yield from bps.trigger(composite.oav.snapshot, wait=True)
|
|
78
78
|
yield from bps.create(DocDescriptorNames.OAV_ROTATION_SNAPSHOT_TRIGGERED)
|
|
79
|
-
yield from bps.read(composite.oav
|
|
79
|
+
yield from bps.read(composite.oav)
|
|
80
80
|
yield from bps.save()
|
|
@@ -9,6 +9,7 @@ from dodal.devices.oav.oav_detector import OAV
|
|
|
9
9
|
from dodal.devices.oav.oav_parameters import OAV_CONFIG_JSON, OAVParameters
|
|
10
10
|
from dodal.devices.oav.pin_image_recognition import PinTipDetection, Tip
|
|
11
11
|
from dodal.devices.oav.utils import (
|
|
12
|
+
PinNotFoundException,
|
|
12
13
|
Pixel,
|
|
13
14
|
get_move_required_so_that_beam_is_at_pixel,
|
|
14
15
|
wait_for_tip_to_be_found,
|
|
@@ -16,7 +17,7 @@ from dodal.devices.oav.utils import (
|
|
|
16
17
|
from dodal.devices.smargon import Smargon
|
|
17
18
|
|
|
18
19
|
from mx_bluesky.common.utils.context import device_composite_from_context
|
|
19
|
-
from mx_bluesky.common.utils.exceptions import SampleException
|
|
20
|
+
from mx_bluesky.common.utils.exceptions import SampleException, catch_exception_and_warn
|
|
20
21
|
from mx_bluesky.common.utils.log import LOGGER
|
|
21
22
|
from mx_bluesky.hyperion.device_setup_plans.setup_oav import pre_centring_setup_oav
|
|
22
23
|
from mx_bluesky.hyperion.device_setup_plans.smargon import (
|
|
@@ -159,6 +160,7 @@ def pin_tip_centre_plan(
|
|
|
159
160
|
# need to wait for the OAV image to update
|
|
160
161
|
# See #673 for improvements
|
|
161
162
|
yield from bps.sleep(0.3)
|
|
162
|
-
|
|
163
|
-
|
|
163
|
+
tip = yield from catch_exception_and_warn(
|
|
164
|
+
PinNotFoundException, wait_for_tip_to_be_found, pin_tip_detect
|
|
165
|
+
)
|
|
164
166
|
yield from offset_and_move(tip)
|
|
@@ -3,7 +3,6 @@ from __future__ import annotations
|
|
|
3
3
|
from math import isclose
|
|
4
4
|
from typing import cast
|
|
5
5
|
|
|
6
|
-
import bluesky.preprocessors as bpp
|
|
7
6
|
import pydantic
|
|
8
7
|
from blueapi.core import BlueskyContext
|
|
9
8
|
from bluesky import plan_stubs as bps
|
|
@@ -37,14 +36,10 @@ from dodal.log import LOGGER
|
|
|
37
36
|
from ophyd_async.fastcs.panda import HDFPanda
|
|
38
37
|
|
|
39
38
|
from mx_bluesky.common.parameters.constants import OavConstants
|
|
40
|
-
from mx_bluesky.common.xrc_result import XRayCentreEventHandler
|
|
41
39
|
from mx_bluesky.hyperion.device_setup_plans.utils import (
|
|
42
40
|
fill_in_energy_if_not_supplied,
|
|
43
41
|
start_preparing_data_collection_then_do_plan,
|
|
44
42
|
)
|
|
45
|
-
from mx_bluesky.hyperion.experiment_plans.change_aperture_then_move_plan import (
|
|
46
|
-
change_aperture_then_move_to_xtal,
|
|
47
|
-
)
|
|
48
43
|
from mx_bluesky.hyperion.experiment_plans.grid_detect_then_xray_centre_plan import (
|
|
49
44
|
GridDetectThenXRayCentreComposite,
|
|
50
45
|
)
|
|
@@ -133,27 +128,6 @@ def _robot_load_then_flyscan_plan(
|
|
|
133
128
|
yield from _flyscan_plan_from_robot_load_params(composite, params, oav_config_file)
|
|
134
129
|
|
|
135
130
|
|
|
136
|
-
def robot_load_then_centre(
|
|
137
|
-
composite: RobotLoadThenCentreComposite,
|
|
138
|
-
parameters: RobotLoadThenCentre,
|
|
139
|
-
) -> MsgGenerator:
|
|
140
|
-
"""Perform pin-tip detection followed by a flyscan to determine centres of interest.
|
|
141
|
-
Performs a robot load if necessary. Centre on the best diffracting centre.
|
|
142
|
-
"""
|
|
143
|
-
|
|
144
|
-
xray_centre_event_handler = XRayCentreEventHandler()
|
|
145
|
-
|
|
146
|
-
yield from bpp.subs_wrapper(
|
|
147
|
-
robot_load_then_xray_centre(composite, parameters), xray_centre_event_handler
|
|
148
|
-
)
|
|
149
|
-
flyscan_results = xray_centre_event_handler.xray_centre_results
|
|
150
|
-
if flyscan_results is not None:
|
|
151
|
-
yield from change_aperture_then_move_to_xtal(
|
|
152
|
-
flyscan_results[0], composite.smargon, composite.aperture_scatterguard
|
|
153
|
-
)
|
|
154
|
-
# else no chi change, no need to recentre.
|
|
155
|
-
|
|
156
|
-
|
|
157
131
|
def robot_load_then_xray_centre(
|
|
158
132
|
composite: RobotLoadThenCentreComposite,
|
|
159
133
|
parameters: RobotLoadThenCentre,
|
|
@@ -26,12 +26,19 @@ from dodal.devices.xbpm_feedback import XBPMFeedback
|
|
|
26
26
|
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
|
+
from dodal.plans.preprocessors.verify_undulator_gap import (
|
|
30
|
+
verify_undulator_gap_before_run_decorator,
|
|
31
|
+
)
|
|
29
32
|
|
|
33
|
+
from mx_bluesky.common.parameters.components import WithSnapshot
|
|
30
34
|
from mx_bluesky.common.plans.read_hardware import (
|
|
31
35
|
read_hardware_for_zocalo,
|
|
32
36
|
standard_read_hardware_during_collection,
|
|
33
37
|
standard_read_hardware_pre_collection,
|
|
34
38
|
)
|
|
39
|
+
from mx_bluesky.common.preprocessors.preprocessors import (
|
|
40
|
+
transmission_and_xbpm_feedback_for_collection_decorator,
|
|
41
|
+
)
|
|
35
42
|
from mx_bluesky.common.utils.context import device_composite_from_context
|
|
36
43
|
from mx_bluesky.common.utils.log import LOGGER
|
|
37
44
|
from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import (
|
|
@@ -48,9 +55,6 @@ from mx_bluesky.hyperion.device_setup_plans.setup_zebra import (
|
|
|
48
55
|
from mx_bluesky.hyperion.device_setup_plans.utils import (
|
|
49
56
|
start_preparing_data_collection_then_do_plan,
|
|
50
57
|
)
|
|
51
|
-
from mx_bluesky.hyperion.device_setup_plans.xbpm_feedback import (
|
|
52
|
-
transmission_and_xbpm_feedback_for_collection_decorator,
|
|
53
|
-
)
|
|
54
58
|
from mx_bluesky.hyperion.experiment_plans.oav_snapshot_plan import (
|
|
55
59
|
OavSnapshotComposite,
|
|
56
60
|
oav_snapshot_plan,
|
|
@@ -369,24 +373,26 @@ def rotation_scan(
|
|
|
369
373
|
if not oav_params:
|
|
370
374
|
oav_params = OAVParameters(context="xrayCentring")
|
|
371
375
|
|
|
376
|
+
@transmission_and_xbpm_feedback_for_collection_decorator(
|
|
377
|
+
composite,
|
|
378
|
+
parameters.transmission_frac,
|
|
379
|
+
)
|
|
380
|
+
@verify_undulator_gap_before_run_decorator(composite)
|
|
372
381
|
@bpp.set_run_key_decorator("rotation_scan")
|
|
373
382
|
@bpp.run_decorator( # attach experiment metadata to the start document
|
|
374
383
|
md={
|
|
375
384
|
"subplan_name": CONST.PLAN.ROTATION_OUTER,
|
|
376
385
|
"mx_bluesky_parameters": parameters.model_dump_json(),
|
|
386
|
+
"with_snapshot": parameters.model_dump_json(
|
|
387
|
+
include=WithSnapshot.model_fields.keys() # type: ignore
|
|
388
|
+
),
|
|
377
389
|
"activate_callbacks": [
|
|
390
|
+
"BeamDrawingCallback",
|
|
378
391
|
"RotationISPyBCallback",
|
|
379
392
|
"RotationNexusFileCallback",
|
|
380
393
|
],
|
|
381
394
|
}
|
|
382
395
|
)
|
|
383
|
-
@transmission_and_xbpm_feedback_for_collection_decorator(
|
|
384
|
-
composite.undulator,
|
|
385
|
-
composite.xbpm_feedback,
|
|
386
|
-
composite.attenuator,
|
|
387
|
-
composite.dcm,
|
|
388
|
-
parameters.transmission_frac,
|
|
389
|
-
)
|
|
390
396
|
def rotation_scan_plan_with_stage_and_cleanup(
|
|
391
397
|
params: RotationScan,
|
|
392
398
|
):
|
|
@@ -422,6 +428,10 @@ def multi_rotation_scan(
|
|
|
422
428
|
eiger: EigerDetector = composite.eiger
|
|
423
429
|
eiger.set_detector_parameters(parameters.detector_params)
|
|
424
430
|
|
|
431
|
+
@transmission_and_xbpm_feedback_for_collection_decorator(
|
|
432
|
+
composite,
|
|
433
|
+
parameters.transmission_frac,
|
|
434
|
+
)
|
|
425
435
|
@bpp.set_run_key_decorator("multi_rotation_scan")
|
|
426
436
|
@bpp.run_decorator(
|
|
427
437
|
md={
|
|
@@ -434,17 +444,11 @@ def multi_rotation_scan(
|
|
|
434
444
|
],
|
|
435
445
|
}
|
|
436
446
|
)
|
|
437
|
-
@transmission_and_xbpm_feedback_for_collection_decorator(
|
|
438
|
-
composite.undulator,
|
|
439
|
-
composite.xbpm_feedback,
|
|
440
|
-
composite.attenuator,
|
|
441
|
-
composite.dcm,
|
|
442
|
-
parameters.transmission_frac,
|
|
443
|
-
)
|
|
444
447
|
@bpp.finalize_decorator(lambda: _cleanup_plan(composite))
|
|
445
448
|
def _multi_rotation_scan():
|
|
446
449
|
for single_scan in parameters.single_rotation_scans:
|
|
447
450
|
|
|
451
|
+
@verify_undulator_gap_before_run_decorator(composite)
|
|
448
452
|
@bpp.set_run_key_decorator("rotation_scan")
|
|
449
453
|
@bpp.run_decorator( # attach experiment metadata to the start document
|
|
450
454
|
md={
|
|
@@ -459,6 +463,8 @@ def multi_rotation_scan(
|
|
|
459
463
|
|
|
460
464
|
yield from rotation_scan_core(single_scan)
|
|
461
465
|
|
|
466
|
+
yield from bps.unstage(eiger)
|
|
467
|
+
|
|
462
468
|
LOGGER.info("setting up and staging eiger...")
|
|
463
469
|
yield from start_preparing_data_collection_then_do_plan(
|
|
464
470
|
composite.beamstop,
|
|
@@ -468,4 +474,3 @@ def multi_rotation_scan(
|
|
|
468
474
|
_multi_rotation_scan(),
|
|
469
475
|
group=CONST.WAIT.ROTATION_READY_FOR_DC,
|
|
470
476
|
)
|
|
471
|
-
yield from bps.unstage(eiger)
|
|
@@ -5,18 +5,21 @@
|
|
|
5
5
|
* reenable feedback
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import bluesky.preprocessors as bpp
|
|
8
9
|
import pydantic
|
|
9
10
|
from bluesky import plan_stubs as bps
|
|
10
11
|
from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
|
|
11
12
|
from dodal.devices.dcm import DCM
|
|
12
13
|
from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, MirrorVoltages
|
|
14
|
+
from dodal.devices.undulator import Undulator
|
|
13
15
|
from dodal.devices.undulator_dcm import UndulatorDCM
|
|
14
16
|
from dodal.devices.xbpm_feedback import XBPMFeedback
|
|
15
17
|
|
|
16
|
-
from mx_bluesky.
|
|
17
|
-
from mx_bluesky.
|
|
18
|
+
from mx_bluesky.common.parameters.constants import PlanNameConstants
|
|
19
|
+
from mx_bluesky.common.preprocessors.preprocessors import (
|
|
18
20
|
transmission_and_xbpm_feedback_for_collection_wrapper,
|
|
19
21
|
)
|
|
22
|
+
from mx_bluesky.hyperion.device_setup_plans import dcm_pitch_roll_mirror_adjuster
|
|
20
23
|
|
|
21
24
|
DESIRED_TRANSMISSION_FRACTION = 0.1
|
|
22
25
|
|
|
@@ -33,6 +36,17 @@ class SetEnergyComposite:
|
|
|
33
36
|
attenuator: BinaryFilterAttenuator
|
|
34
37
|
|
|
35
38
|
|
|
39
|
+
# Remove composite after https://github.com/DiamondLightSource/dodal/issues/1092
|
|
40
|
+
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
41
|
+
class XBPMWrapperComposite:
|
|
42
|
+
undulator: Undulator
|
|
43
|
+
xbpm_feedback: XBPMFeedback
|
|
44
|
+
attenuator: BinaryFilterAttenuator
|
|
45
|
+
dcm: DCM
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@bpp.set_run_key_decorator(PlanNameConstants.SET_ENERGY)
|
|
49
|
+
@bpp.run_decorator()
|
|
36
50
|
def _set_energy_plan(
|
|
37
51
|
energy_kev,
|
|
38
52
|
composite: SetEnergyComposite,
|
|
@@ -51,12 +65,17 @@ def set_energy_plan(
|
|
|
51
65
|
energy_ev: float | None,
|
|
52
66
|
composite: SetEnergyComposite,
|
|
53
67
|
):
|
|
68
|
+
# Remove conversion after https://github.com/DiamondLightSource/dodal/issues/1092
|
|
69
|
+
composite_for_wrapper = XBPMWrapperComposite(
|
|
70
|
+
composite.undulator_dcm.undulator_ref._obj, # noqa: SLF001
|
|
71
|
+
composite.xbpm_feedback,
|
|
72
|
+
composite.attenuator,
|
|
73
|
+
composite.dcm,
|
|
74
|
+
)
|
|
75
|
+
|
|
54
76
|
if energy_ev:
|
|
55
77
|
yield from transmission_and_xbpm_feedback_for_collection_wrapper(
|
|
56
78
|
_set_energy_plan(energy_ev / 1000, composite),
|
|
57
|
-
|
|
58
|
-
composite.xbpm_feedback,
|
|
59
|
-
composite.attenuator,
|
|
60
|
-
composite.dcm,
|
|
79
|
+
composite_for_wrapper,
|
|
61
80
|
DESIRED_TRANSMISSION_FRACTION,
|
|
62
81
|
)
|
|
@@ -1,21 +1,52 @@
|
|
|
1
1
|
import dataclasses
|
|
2
2
|
import json
|
|
3
3
|
import re
|
|
4
|
-
from
|
|
4
|
+
from os import path
|
|
5
|
+
from typing import Any, TypeVar
|
|
5
6
|
|
|
6
7
|
import requests
|
|
8
|
+
from deepdiff.diff import DeepDiff
|
|
7
9
|
from dodal.utils import get_beamline_name
|
|
8
|
-
|
|
9
|
-
from
|
|
10
|
-
|
|
10
|
+
from jsonschema import ValidationError
|
|
11
|
+
from pydantic_extra_types.semantic_version import SemanticVersion
|
|
12
|
+
|
|
13
|
+
from mx_bluesky.common.parameters.components import (
|
|
14
|
+
PARAMETER_VERSION,
|
|
15
|
+
MxBlueskyParameters,
|
|
16
|
+
TopNByMaxCountSelection,
|
|
17
|
+
WithCentreSelection,
|
|
18
|
+
WithOptionalEnergyChange,
|
|
19
|
+
WithSample,
|
|
20
|
+
WithVisit,
|
|
21
|
+
)
|
|
22
|
+
from mx_bluesky.common.parameters.constants import (
|
|
23
|
+
GridscanParamConstants,
|
|
24
|
+
)
|
|
11
25
|
from mx_bluesky.common.utils.log import LOGGER
|
|
26
|
+
from mx_bluesky.common.utils.utils import convert_angstrom_to_eV
|
|
27
|
+
from mx_bluesky.hyperion.parameters.components import WithHyperionUDCFeatures
|
|
12
28
|
from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect
|
|
29
|
+
from mx_bluesky.hyperion.parameters.robot_load import RobotLoadThenCentre
|
|
13
30
|
|
|
14
31
|
T = TypeVar("T", bound=WithVisit)
|
|
15
32
|
AGAMEMNON_URL = "http://agamemnon.diamond.ac.uk/"
|
|
16
33
|
MULTIPIN_PREFIX = "multipin"
|
|
17
34
|
MULTIPIN_FORMAT_DESC = "Expected multipin format is multipin_{number_of_wells}x{well_size}+{distance_between_tip_and_first_well}"
|
|
18
35
|
MULTIPIN_REGEX = rf"^{MULTIPIN_PREFIX}_(\d+)x(\d+(?:\.\d+)?)\+(\d+(?:\.\d+)?)$"
|
|
36
|
+
MX_GENERAL_ROOT_REGEX = r"^/dls/(?P<beamline>[^/]+)/data/[^/]*/(?P<visit>[^/]+)(?:/|$)"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class AgamemnonLoadCentreCollect(
|
|
40
|
+
MxBlueskyParameters,
|
|
41
|
+
WithVisit,
|
|
42
|
+
WithSample,
|
|
43
|
+
WithCentreSelection,
|
|
44
|
+
WithHyperionUDCFeatures,
|
|
45
|
+
WithOptionalEnergyChange,
|
|
46
|
+
):
|
|
47
|
+
"""Experiment parameters to compare against GDA populated LoadCentreCollect."""
|
|
48
|
+
|
|
49
|
+
robot_load_then_centre: RobotLoadThenCentre
|
|
19
50
|
|
|
20
51
|
|
|
21
52
|
@dataclasses.dataclass
|
|
@@ -60,7 +91,7 @@ def _get_parameters_from_url(url: str) -> dict:
|
|
|
60
91
|
raise KeyError(f"Unexpected json from agamemnon: {response_json}") from e
|
|
61
92
|
|
|
62
93
|
|
|
63
|
-
def
|
|
94
|
+
def get_pin_type_from_agamemnon_parameters(parameters: dict) -> PinType:
|
|
64
95
|
loop_type_name: str | None = parameters["sample"]["loopType"]
|
|
65
96
|
if loop_type_name:
|
|
66
97
|
regex_search = re.search(MULTIPIN_REGEX, loop_type_name)
|
|
@@ -81,15 +112,119 @@ def get_next_instruction(beamline: str) -> dict:
|
|
|
81
112
|
return _get_parameters_from_url(AGAMEMNON_URL + f"getnextcollect/{beamline}")
|
|
82
113
|
|
|
83
114
|
|
|
84
|
-
def
|
|
85
|
-
|
|
86
|
-
|
|
115
|
+
def get_withvisit_parameters_from_agamemnon(parameters: dict) -> tuple:
|
|
116
|
+
try:
|
|
117
|
+
prefix = parameters["prefix"]
|
|
118
|
+
collection = parameters["collection"]
|
|
119
|
+
# Assuming distance is identical for multiple collections. Remove after https://github.com/DiamondLightSource/mx-bluesky/issues/773
|
|
120
|
+
detector_distance = collection[0]["distance"]
|
|
121
|
+
except KeyError as e:
|
|
122
|
+
raise KeyError("Unexpected json from agamemnon") from e
|
|
123
|
+
|
|
124
|
+
match = re.match(MX_GENERAL_ROOT_REGEX, prefix) if prefix else None
|
|
125
|
+
|
|
126
|
+
if match:
|
|
127
|
+
return (match.group("visit"), detector_distance)
|
|
128
|
+
|
|
129
|
+
raise ValueError(
|
|
130
|
+
f"Agamemnon prefix '{prefix}' does not match MX-General root structure"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def get_withsample_parameters_from_agamemnon(parameters: dict) -> dict[str, Any]:
|
|
135
|
+
assert parameters.get("sample"), "instruction does not have a sample"
|
|
136
|
+
return {
|
|
137
|
+
"sample_id": parameters["sample"]["id"],
|
|
138
|
+
"sample_puck": parameters["sample"]["container"],
|
|
139
|
+
"sample_pin": parameters["sample"]["position"],
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def get_withenergy_parameters_from_agamemnon(parameters: dict) -> dict[str, Any]:
|
|
144
|
+
try:
|
|
145
|
+
first_collection: dict = parameters["collection"][0]
|
|
146
|
+
wavelength = first_collection.get("wavelength")
|
|
147
|
+
assert isinstance(wavelength, float)
|
|
148
|
+
demand_energy_ev = convert_angstrom_to_eV(wavelength)
|
|
149
|
+
return {"demand_energy_ev": demand_energy_ev}
|
|
150
|
+
except (KeyError, IndexError, AttributeError, TypeError):
|
|
151
|
+
return {"demand_energy_ev": None}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def get_param_version() -> SemanticVersion:
|
|
155
|
+
return SemanticVersion.validate_from_str(str(PARAMETER_VERSION))
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def create_robot_load_then_centre_params_from_agamemnon(
|
|
159
|
+
parameters: dict,
|
|
160
|
+
) -> RobotLoadThenCentre:
|
|
161
|
+
visit, detector_distance = get_withvisit_parameters_from_agamemnon(parameters)
|
|
162
|
+
with_sample_params = get_withsample_parameters_from_agamemnon(parameters)
|
|
163
|
+
with_energy_params = get_withenergy_parameters_from_agamemnon(parameters)
|
|
164
|
+
visit_directory, file_name = path.split(parameters["prefix"])
|
|
165
|
+
return RobotLoadThenCentre(
|
|
166
|
+
parameter_model_version=get_param_version(),
|
|
167
|
+
storage_directory=visit_directory + "/xraycentring",
|
|
168
|
+
visit=visit,
|
|
169
|
+
detector_distance_mm=detector_distance,
|
|
170
|
+
snapshot_directory=visit_directory + "/snapshots",
|
|
171
|
+
file_name=file_name,
|
|
172
|
+
**with_energy_params,
|
|
173
|
+
**with_sample_params,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def populate_parameters_from_agamemnon(agamemnon_params):
|
|
178
|
+
visit, detector_distance = get_withvisit_parameters_from_agamemnon(agamemnon_params)
|
|
179
|
+
with_sample_params = get_withsample_parameters_from_agamemnon(agamemnon_params)
|
|
180
|
+
with_energy_params = get_withenergy_parameters_from_agamemnon(agamemnon_params)
|
|
181
|
+
pin_type = get_pin_type_from_agamemnon_parameters(agamemnon_params)
|
|
182
|
+
robot_load_params = create_robot_load_then_centre_params_from_agamemnon(
|
|
183
|
+
agamemnon_params
|
|
184
|
+
)
|
|
185
|
+
return AgamemnonLoadCentreCollect(
|
|
186
|
+
parameter_model_version=SemanticVersion.validate_from_str(
|
|
187
|
+
str(PARAMETER_VERSION)
|
|
188
|
+
),
|
|
189
|
+
visit=visit,
|
|
190
|
+
detector_distance_mm=detector_distance,
|
|
191
|
+
select_centres=TopNByMaxCountSelection(n=pin_type.expected_number_of_crystals),
|
|
192
|
+
robot_load_then_centre=robot_load_params,
|
|
193
|
+
**with_sample_params,
|
|
194
|
+
**with_energy_params,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def create_parameters_from_agamemnon() -> AgamemnonLoadCentreCollect:
|
|
199
|
+
beamline_name = get_beamline_name("i03")
|
|
200
|
+
agamemnon_params = get_next_instruction(beamline_name)
|
|
201
|
+
|
|
202
|
+
return populate_parameters_from_agamemnon(agamemnon_params)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def compare_params(load_centre_collect_params):
|
|
206
|
+
try:
|
|
207
|
+
parameters = create_parameters_from_agamemnon()
|
|
208
|
+
|
|
209
|
+
# Log differences against GDA populated parameters
|
|
210
|
+
differences = DeepDiff(
|
|
211
|
+
parameters, load_centre_collect_params, math_epsilon=1e-5
|
|
212
|
+
)
|
|
213
|
+
if differences:
|
|
214
|
+
LOGGER.info(
|
|
215
|
+
f"Different parameters found when directly reading from Hyperion: {differences}"
|
|
216
|
+
)
|
|
217
|
+
except (ValueError, KeyError) as e:
|
|
218
|
+
LOGGER.warning(f"Failed to compare parameters: {e}")
|
|
219
|
+
except Exception as e:
|
|
220
|
+
LOGGER.warning(f"Unexpected error occurred. Failed to compare parameters: {e}")
|
|
87
221
|
|
|
88
222
|
|
|
89
223
|
def update_params_from_agamemnon(parameters: T) -> T:
|
|
90
224
|
try:
|
|
91
225
|
beamline_name = get_beamline_name("i03")
|
|
92
|
-
|
|
226
|
+
agamemnon_params = get_next_instruction(beamline_name)
|
|
227
|
+
pin_type = get_pin_type_from_agamemnon_parameters(agamemnon_params)
|
|
93
228
|
if isinstance(parameters, LoadCentreCollect):
|
|
94
229
|
parameters.robot_load_then_centre.tip_offset_um = pin_type.full_width / 2
|
|
95
230
|
parameters.robot_load_then_centre.grid_width_um = pin_type.full_width
|
|
@@ -99,6 +234,9 @@ def update_params_from_agamemnon(parameters: T) -> T:
|
|
|
99
234
|
# Before we do https://github.com/DiamondLightSource/mx-bluesky/issues/226
|
|
100
235
|
# this will give no snapshots but that's preferable
|
|
101
236
|
parameters.multi_rotation_scan.snapshot_omegas_deg = []
|
|
237
|
+
except (ValueError, ValidationError) as e:
|
|
238
|
+
LOGGER.warning(f"Failed to update parameters: {e}")
|
|
102
239
|
except Exception as e:
|
|
103
|
-
LOGGER.warning(f"
|
|
240
|
+
LOGGER.warning(f"Unexpected error occurred. Failed to update parameters: {e}")
|
|
241
|
+
|
|
104
242
|
return parameters
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from collections.abc import Callable, Sequence
|
|
3
3
|
from threading import Thread
|
|
4
|
-
from time import sleep
|
|
5
4
|
|
|
5
|
+
import bluesky.plan_stubs as bps
|
|
6
6
|
from bluesky.callbacks import CallbackBase
|
|
7
7
|
from bluesky.callbacks.zmq import Proxy, RemoteDispatcher
|
|
8
8
|
from dodal.log import LOGGER as dodal_logger
|
|
@@ -14,6 +14,9 @@ from mx_bluesky.common.external_interaction.callbacks.common.log_uid_tag_callbac
|
|
|
14
14
|
from mx_bluesky.common.external_interaction.callbacks.common.zocalo_callback import (
|
|
15
15
|
ZocaloCallback,
|
|
16
16
|
)
|
|
17
|
+
from mx_bluesky.common.external_interaction.callbacks.sample_handling.sample_handling_callback import (
|
|
18
|
+
SampleHandlingCallback,
|
|
19
|
+
)
|
|
17
20
|
from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback import (
|
|
18
21
|
GridscanISPyBCallback,
|
|
19
22
|
)
|
|
@@ -35,8 +38,8 @@ from mx_bluesky.hyperion.external_interaction.callbacks.rotation.ispyb_callback
|
|
|
35
38
|
from mx_bluesky.hyperion.external_interaction.callbacks.rotation.nexus_callback import (
|
|
36
39
|
RotationNexusFileCallback,
|
|
37
40
|
)
|
|
38
|
-
from mx_bluesky.hyperion.external_interaction.callbacks.
|
|
39
|
-
|
|
41
|
+
from mx_bluesky.hyperion.external_interaction.callbacks.snapshot_callback import (
|
|
42
|
+
BeamDrawingCallback,
|
|
40
43
|
)
|
|
41
44
|
from mx_bluesky.hyperion.parameters.cli import parse_callback_dev_mode_arg
|
|
42
45
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
@@ -67,15 +70,18 @@ def create_rotation_callbacks() -> tuple[
|
|
|
67
70
|
return (
|
|
68
71
|
RotationNexusFileCallback(),
|
|
69
72
|
RotationISPyBCallback(
|
|
70
|
-
emit=ZocaloCallback(CONST.PLAN.
|
|
73
|
+
emit=ZocaloCallback(CONST.PLAN.ROTATION_MULTI, CONST.ZOCALO_ENV)
|
|
71
74
|
),
|
|
72
75
|
)
|
|
73
76
|
|
|
74
77
|
|
|
75
78
|
def setup_callbacks() -> list[CallbackBase]:
|
|
79
|
+
rot_nexus_cb, rot_ispyb_cb = create_rotation_callbacks()
|
|
80
|
+
snapshot_cb = BeamDrawingCallback(emit=rot_ispyb_cb)
|
|
76
81
|
return [
|
|
77
82
|
*create_gridscan_callbacks(),
|
|
78
|
-
|
|
83
|
+
rot_nexus_cb,
|
|
84
|
+
snapshot_cb,
|
|
79
85
|
LogUidTaggingCallback(),
|
|
80
86
|
RobotLoadISPyBCallback(),
|
|
81
87
|
SampleHandlingCallback(),
|
|
@@ -134,7 +140,7 @@ def wait_for_threads_forever(threads: Sequence[Thread]):
|
|
|
134
140
|
try:
|
|
135
141
|
log_debug("Trying to wait forever on callback and dispatcher threads")
|
|
136
142
|
while all(alive):
|
|
137
|
-
sleep(LIVENESS_POLL_SECONDS)
|
|
143
|
+
yield from bps.sleep(LIVENESS_POLL_SECONDS)
|
|
138
144
|
alive = [t.is_alive() for t in threads]
|
|
139
145
|
except KeyboardInterrupt:
|
|
140
146
|
log_info("Main thread received interrupt - exiting.")
|