mx-bluesky 1.2.0__py3-none-any.whl → 1.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mx_bluesky/__init__.py +8 -3
- mx_bluesky/__main__.py +12 -7
- mx_bluesky/_version.py +2 -2
- mx_bluesky/beamlines/i04/callbacks/murko_callback.py +14 -4
- mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +178 -0
- mx_bluesky/beamlines/i04/thawing_plan.py +49 -11
- mx_bluesky/beamlines/i24/serial/__init__.py +3 -0
- mx_bluesky/beamlines/i24/serial/dcid.py +143 -171
- mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +1 -1
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +121 -110
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +3 -6
- mx_bluesky/beamlines/i24/serial/fixed_target/ft_utils.py +0 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +164 -169
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +149 -225
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +7 -216
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +18 -17
- mx_bluesky/beamlines/i24/serial/log.py +58 -49
- mx_bluesky/beamlines/i24/serial/parameters/__init__.py +4 -0
- mx_bluesky/beamlines/i24/serial/parameters/constants.py +6 -1
- mx_bluesky/beamlines/i24/serial/parameters/experiment_parameters.py +42 -15
- mx_bluesky/beamlines/i24/serial/parameters/fixed_target/cs/cs_maker.json +3 -3
- mx_bluesky/beamlines/i24/serial/run_extruder.sh +30 -5
- mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +30 -5
- mx_bluesky/beamlines/i24/serial/run_serial.py +24 -8
- mx_bluesky/beamlines/i24/serial/setup_beamline/ca.py +0 -2
- mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +2 -0
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +104 -82
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +9 -20
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +26 -28
- mx_bluesky/beamlines/i24/serial/write_nexus.py +74 -72
- mx_bluesky/common/__init__.py +0 -0
- mx_bluesky/common/device_setup_plans/read_hardware_for_setup.py +14 -0
- mx_bluesky/common/external_interaction/config_server.py +46 -0
- mx_bluesky/common/parameters/components.py +258 -0
- mx_bluesky/common/parameters/constants.py +143 -0
- mx_bluesky/common/parameters/gridscan.py +94 -0
- mx_bluesky/common/parameters/robot_load.py +16 -0
- mx_bluesky/common/plans/__init__.py +1 -0
- mx_bluesky/common/plans/do_fgs.py +121 -0
- mx_bluesky/common/utils/log.py +118 -0
- mx_bluesky/{hyperion → common/utils}/tracing.py +2 -2
- mx_bluesky/hyperion/__main__.py +13 -10
- mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +47 -52
- mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +6 -12
- mx_bluesky/hyperion/device_setup_plans/setup_oav.py +6 -12
- mx_bluesky/hyperion/device_setup_plans/setup_panda.py +5 -6
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +49 -18
- mx_bluesky/hyperion/device_setup_plans/smargon.py +9 -9
- mx_bluesky/hyperion/device_setup_plans/utils.py +2 -2
- mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +4 -4
- mx_bluesky/hyperion/exceptions.py +13 -1
- mx_bluesky/hyperion/experiment_plans/__init__.py +4 -0
- mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +83 -0
- mx_bluesky/hyperion/experiment_plans/common/xrc_result.py +47 -0
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -9
- mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +147 -169
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +48 -22
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +75 -9
- mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +21 -20
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +9 -6
- mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +2 -2
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +40 -21
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +22 -22
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +43 -39
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +69 -18
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +17 -7
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +13 -13
- mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +0 -4
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +5 -2
- mx_bluesky/hyperion/external_interaction/callbacks/common/abstract_event.py +66 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +5 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/ispyb_mapping.py +1 -1
- mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +30 -25
- mx_bluesky/hyperion/external_interaction/callbacks/ispyb_callback_base.py +29 -12
- mx_bluesky/hyperion/external_interaction/callbacks/log_uid_tag_callback.py +1 -1
- mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +19 -11
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +7 -4
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +5 -3
- mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/sample_handling_callback.py +84 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +38 -27
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +5 -4
- mx_bluesky/hyperion/external_interaction/config_server.py +11 -28
- mx_bluesky/hyperion/external_interaction/exceptions.py +0 -9
- mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +65 -15
- mx_bluesky/hyperion/external_interaction/ispyb/ispyb_store.py +1 -1
- mx_bluesky/hyperion/external_interaction/nexus/nexus_utils.py +2 -2
- mx_bluesky/hyperion/external_interaction/nexus/write_nexus.py +1 -1
- mx_bluesky/hyperion/log.py +0 -84
- mx_bluesky/hyperion/parameters/components.py +4 -251
- mx_bluesky/hyperion/parameters/constants.py +22 -119
- mx_bluesky/hyperion/parameters/gridscan.py +35 -74
- mx_bluesky/hyperion/parameters/load_centre_collect.py +16 -11
- mx_bluesky/hyperion/parameters/rotation.py +23 -10
- mx_bluesky/hyperion/utils/utils.py +17 -0
- mx_bluesky/hyperion/utils/validation.py +5 -6
- {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/METADATA +36 -33
- {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/RECORD +102 -89
- {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/WHEEL +1 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +0 -161
- mx_bluesky/example.py +0 -19
- mx_bluesky/hyperion/parameters/robot_load.py +0 -16
- {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/LICENSE +0 -0
- {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/top_level.txt +0 -0
|
@@ -2,7 +2,6 @@ from typing import TypedDict
|
|
|
2
2
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
from bluesky.callbacks import CallbackBase
|
|
5
|
-
from dodal.devices.oav.oav_detector import OAVConfigParams
|
|
6
5
|
from dodal.devices.oav.utils import calculate_x_y_z_of_pixel
|
|
7
6
|
from event_model.documents import Event
|
|
8
7
|
|
|
@@ -26,21 +25,19 @@ class GridParamUpdate(TypedDict):
|
|
|
26
25
|
class GridDetectionCallback(CallbackBase):
|
|
27
26
|
def __init__(
|
|
28
27
|
self,
|
|
29
|
-
oav_params: OAVConfigParams,
|
|
30
28
|
*args,
|
|
31
29
|
) -> None:
|
|
32
30
|
super().__init__(*args)
|
|
33
|
-
self.
|
|
34
|
-
self.start_positions: list = []
|
|
31
|
+
self.start_positions_mm: list = []
|
|
35
32
|
self.box_numbers: list = []
|
|
36
33
|
|
|
37
34
|
def event(self, doc: Event):
|
|
38
35
|
data = doc.get("data")
|
|
39
|
-
top_left_x_px = data["
|
|
40
|
-
box_width_px = data["
|
|
36
|
+
top_left_x_px = data["oav-grid_snapshot-top_left_x"]
|
|
37
|
+
box_width_px = data["oav-grid_snapshot-box_width"]
|
|
41
38
|
x_of_centre_of_first_box_px = top_left_x_px + box_width_px / 2
|
|
42
39
|
|
|
43
|
-
top_left_y_px = data["
|
|
40
|
+
top_left_y_px = data["oav-grid_snapshot-top_left_y"]
|
|
44
41
|
y_of_centre_of_first_box_px = top_left_y_px + box_width_px / 2
|
|
45
42
|
|
|
46
43
|
smargon_omega = data["smargon-omega"]
|
|
@@ -53,36 +50,44 @@ class GridDetectionCallback(CallbackBase):
|
|
|
53
50
|
y_of_centre_of_first_box_px,
|
|
54
51
|
)
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
microns_per_pixel_x = data["oav-microns_per_pixel_x"]
|
|
54
|
+
microns_per_pixel_y = data["oav-microns_per_pixel_y"]
|
|
55
|
+
beam_x = data["oav-beam_centre_i"]
|
|
56
|
+
beam_y = data["oav-beam_centre_j"]
|
|
59
57
|
|
|
60
|
-
|
|
58
|
+
position_grid_start_mm = calculate_x_y_z_of_pixel(
|
|
59
|
+
current_xyz,
|
|
60
|
+
smargon_omega,
|
|
61
|
+
centre_of_first_box,
|
|
62
|
+
(beam_x, beam_y),
|
|
63
|
+
(microns_per_pixel_x, microns_per_pixel_y),
|
|
64
|
+
)
|
|
65
|
+
LOGGER.info(f"Calculated start position {position_grid_start_mm}")
|
|
61
66
|
|
|
62
|
-
self.
|
|
67
|
+
self.start_positions_mm.append(position_grid_start_mm)
|
|
63
68
|
self.box_numbers.append(
|
|
64
69
|
(
|
|
65
|
-
data["
|
|
66
|
-
data["
|
|
70
|
+
data["oav-grid_snapshot-num_boxes_x"],
|
|
71
|
+
data["oav-grid_snapshot-num_boxes_y"],
|
|
67
72
|
)
|
|
68
73
|
)
|
|
69
74
|
|
|
70
|
-
self.
|
|
71
|
-
self.
|
|
72
|
-
self.
|
|
75
|
+
self.x_step_size_um = box_width_px * microns_per_pixel_x
|
|
76
|
+
self.y_step_size_um = box_width_px * microns_per_pixel_y
|
|
77
|
+
self.z_step_size_um = box_width_px * microns_per_pixel_y
|
|
73
78
|
return doc
|
|
74
79
|
|
|
75
80
|
def get_grid_parameters(self) -> GridParamUpdate:
|
|
76
81
|
return {
|
|
77
|
-
"x_start_um": self.
|
|
78
|
-
"y_start_um": self.
|
|
79
|
-
"y2_start_um": self.
|
|
80
|
-
"z_start_um": self.
|
|
81
|
-
"z2_start_um": self.
|
|
82
|
+
"x_start_um": self.start_positions_mm[0][0] * 1000,
|
|
83
|
+
"y_start_um": self.start_positions_mm[0][1] * 1000,
|
|
84
|
+
"y2_start_um": self.start_positions_mm[0][1] * 1000,
|
|
85
|
+
"z_start_um": self.start_positions_mm[1][2] * 1000,
|
|
86
|
+
"z2_start_um": self.start_positions_mm[1][2] * 1000,
|
|
82
87
|
"x_steps": self.box_numbers[0][0],
|
|
83
88
|
"y_steps": self.box_numbers[0][1],
|
|
84
89
|
"z_steps": self.box_numbers[1][1],
|
|
85
|
-
"x_step_size_um": self.
|
|
86
|
-
"y_step_size_um": self.
|
|
87
|
-
"z_step_size_um": self.
|
|
90
|
+
"x_step_size_um": self.x_step_size_um,
|
|
91
|
+
"y_step_size_um": self.y_step_size_um,
|
|
92
|
+
"z_step_size_um": self.z_step_size_um,
|
|
88
93
|
}
|
|
@@ -5,9 +5,12 @@ from collections.abc import Callable, Sequence
|
|
|
5
5
|
from typing import TYPE_CHECKING, Any, TypeVar, cast
|
|
6
6
|
|
|
7
7
|
from dodal.beamline_specific_utils.i03 import beam_size_from_aperture
|
|
8
|
+
from dodal.devices.detector import DetectorParams
|
|
8
9
|
from dodal.devices.detector.det_resolution import resolution
|
|
9
10
|
from dodal.devices.synchrotron import SynchrotronMode
|
|
10
11
|
|
|
12
|
+
from mx_bluesky.common.parameters.components import DiffractionExperimentWithSample
|
|
13
|
+
from mx_bluesky.common.utils.log import set_dcgid_tag
|
|
11
14
|
from mx_bluesky.hyperion.external_interaction.callbacks.plan_reactive_callback import (
|
|
12
15
|
PlanReactiveCallback,
|
|
13
16
|
)
|
|
@@ -21,8 +24,7 @@ from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_store import (
|
|
|
21
24
|
StoreInIspyb,
|
|
22
25
|
)
|
|
23
26
|
from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_utils import get_ispyb_config
|
|
24
|
-
from mx_bluesky.hyperion.log import ISPYB_LOGGER
|
|
25
|
-
from mx_bluesky.hyperion.parameters.components import DiffractionExperimentWithSample
|
|
27
|
+
from mx_bluesky.hyperion.log import ISPYB_LOGGER
|
|
26
28
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
27
29
|
from mx_bluesky.hyperion.utils.utils import convert_eV_to_angstrom
|
|
28
30
|
|
|
@@ -33,6 +35,25 @@ if TYPE_CHECKING:
|
|
|
33
35
|
from event_model.documents import Event, EventDescriptor, RunStart, RunStop
|
|
34
36
|
|
|
35
37
|
|
|
38
|
+
def _update_based_on_energy(
|
|
39
|
+
doc: Event,
|
|
40
|
+
detector_params: DetectorParams,
|
|
41
|
+
data_collection_info: DataCollectionInfo,
|
|
42
|
+
):
|
|
43
|
+
"""If energy has been read as part of this reading then add it into the data
|
|
44
|
+
collection info along with the other fields that depend on it."""
|
|
45
|
+
if energy_kev := doc["data"].get("dcm-energy_in_kev", None):
|
|
46
|
+
energy_ev = energy_kev * 1000
|
|
47
|
+
wavelength_angstroms = convert_eV_to_angstrom(energy_ev)
|
|
48
|
+
data_collection_info.wavelength = wavelength_angstroms
|
|
49
|
+
data_collection_info.resolution = resolution(
|
|
50
|
+
detector_params,
|
|
51
|
+
wavelength_angstroms,
|
|
52
|
+
detector_params.detector_distance,
|
|
53
|
+
)
|
|
54
|
+
return data_collection_info
|
|
55
|
+
|
|
56
|
+
|
|
36
57
|
class BaseISPyBCallback(PlanReactiveCallback):
|
|
37
58
|
def __init__(
|
|
38
59
|
self,
|
|
@@ -109,6 +130,9 @@ class BaseISPyBCallback(PlanReactiveCallback):
|
|
|
109
130
|
slitgap_horizontal=doc["data"]["s4_slit_gaps_xgap"],
|
|
110
131
|
slitgap_vertical=doc["data"]["s4_slit_gaps_ygap"],
|
|
111
132
|
)
|
|
133
|
+
hwscan_data_collection_info = _update_based_on_energy(
|
|
134
|
+
doc, self.params.detector_params, hwscan_data_collection_info
|
|
135
|
+
)
|
|
112
136
|
hwscan_position_info = DataCollectionPositionInfo(
|
|
113
137
|
pos_x=float(doc["data"]["smargon-x"]),
|
|
114
138
|
pos_y=float(doc["data"]["smargon-y"]),
|
|
@@ -137,16 +161,9 @@ class BaseISPyBCallback(PlanReactiveCallback):
|
|
|
137
161
|
if transmission := doc["data"]["attenuator-actual_transmission"]:
|
|
138
162
|
# Ispyb wants the transmission in a percentage, we use fractions
|
|
139
163
|
hwscan_data_collection_info.transmission = transmission * 100
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
wavelength_angstroms = convert_eV_to_angstrom(energy_ev)
|
|
144
|
-
hwscan_data_collection_info.wavelength = wavelength_angstroms
|
|
145
|
-
hwscan_data_collection_info.resolution = resolution(
|
|
146
|
-
self.params.detector_params,
|
|
147
|
-
wavelength_angstroms,
|
|
148
|
-
self.params.detector_params.detector_distance,
|
|
149
|
-
)
|
|
164
|
+
hwscan_data_collection_info = _update_based_on_energy(
|
|
165
|
+
doc, self.params.detector_params, hwscan_data_collection_info
|
|
166
|
+
)
|
|
150
167
|
scan_data_infos = self.populate_info_for_update(
|
|
151
168
|
hwscan_data_collection_info, None, self.params
|
|
152
169
|
)
|
|
@@ -2,8 +2,6 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
|
-
from event_model.documents import EventDescriptor
|
|
6
|
-
|
|
7
5
|
from mx_bluesky.hyperion.external_interaction.callbacks.common.ispyb_mapping import (
|
|
8
6
|
get_proposal_and_session_from_visit_string,
|
|
9
7
|
)
|
|
@@ -11,6 +9,7 @@ from mx_bluesky.hyperion.external_interaction.callbacks.plan_reactive_callback i
|
|
|
11
9
|
PlanReactiveCallback,
|
|
12
10
|
)
|
|
13
11
|
from mx_bluesky.hyperion.external_interaction.ispyb.exp_eye_store import (
|
|
12
|
+
BLSampleStatus,
|
|
14
13
|
ExpeyeInteraction,
|
|
15
14
|
RobotActionID,
|
|
16
15
|
)
|
|
@@ -25,6 +24,7 @@ class RobotLoadISPyBCallback(PlanReactiveCallback):
|
|
|
25
24
|
def __init__(self) -> None:
|
|
26
25
|
ISPYB_LOGGER.debug("Initialising ISPyB Robot Load Callback")
|
|
27
26
|
super().__init__(log=ISPYB_LOGGER)
|
|
27
|
+
self._metadata: dict | None = None
|
|
28
28
|
self.run_uid: str | None = None
|
|
29
29
|
self.descriptors: dict[str, EventDescriptor] = {}
|
|
30
30
|
self.action_id: RobotActionID | None = None
|
|
@@ -35,16 +35,17 @@ class RobotLoadISPyBCallback(PlanReactiveCallback):
|
|
|
35
35
|
if doc.get("subplan_name") == CONST.PLAN.ROBOT_LOAD:
|
|
36
36
|
ISPYB_LOGGER.debug(f"ISPyB robot load callback received: {doc}")
|
|
37
37
|
self.run_uid = doc.get("uid")
|
|
38
|
-
|
|
38
|
+
self._metadata = doc.get("metadata")
|
|
39
|
+
assert isinstance(self._metadata, dict)
|
|
39
40
|
proposal, session = get_proposal_and_session_from_visit_string(
|
|
40
|
-
|
|
41
|
+
self._metadata["visit"]
|
|
41
42
|
)
|
|
42
43
|
self.action_id = self.expeye.start_load(
|
|
43
44
|
proposal,
|
|
44
45
|
session,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
self._metadata["sample_id"],
|
|
47
|
+
self._metadata["sample_puck"],
|
|
48
|
+
self._metadata["sample_pin"],
|
|
48
49
|
)
|
|
49
50
|
return super().activity_gated_start(doc)
|
|
50
51
|
|
|
@@ -62,7 +63,7 @@ class RobotLoadISPyBCallback(PlanReactiveCallback):
|
|
|
62
63
|
self.action_id is not None
|
|
63
64
|
), "ISPyB Robot load callback event called unexpectedly"
|
|
64
65
|
barcode = doc["data"]["robot-barcode"]
|
|
65
|
-
oav_snapshot = doc["data"]["
|
|
66
|
+
oav_snapshot = doc["data"]["oav-snapshot-last_saved_path"]
|
|
66
67
|
webcam_snapshot = doc["data"]["webcam-last_saved_path"]
|
|
67
68
|
# I03 uses webcam/oav snapshots in place of before/after snapshots
|
|
68
69
|
self.expeye.update_barcode_and_snapshots(
|
|
@@ -77,10 +78,17 @@ class RobotLoadISPyBCallback(PlanReactiveCallback):
|
|
|
77
78
|
assert (
|
|
78
79
|
self.action_id is not None
|
|
79
80
|
), "ISPyB Robot load callback stop called unexpectedly"
|
|
80
|
-
exit_status = (
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
exit_status = doc.get("exit_status")
|
|
82
|
+
assert exit_status, "Exit status not available in stop document!"
|
|
83
|
+
assert self._metadata, "Metadata not received before stop document."
|
|
83
84
|
reason = doc.get("reason") or "OK"
|
|
85
|
+
|
|
84
86
|
self.expeye.end_load(self.action_id, exit_status, reason)
|
|
87
|
+
self.expeye.update_sample_status(
|
|
88
|
+
self._metadata["sample_id"],
|
|
89
|
+
BLSampleStatus.LOADED
|
|
90
|
+
if exit_status == "success"
|
|
91
|
+
else BLSampleStatus.ERROR_BEAMLINE,
|
|
92
|
+
)
|
|
85
93
|
self.action_id = None
|
|
86
94
|
return super().activity_gated_stop(doc)
|
|
@@ -3,6 +3,8 @@ from __future__ import annotations
|
|
|
3
3
|
from collections.abc import Callable, Sequence
|
|
4
4
|
from typing import TYPE_CHECKING, Any, cast
|
|
5
5
|
|
|
6
|
+
from mx_bluesky.common.parameters.components import IspybExperimentType
|
|
7
|
+
from mx_bluesky.common.utils.log import set_dcgid_tag
|
|
6
8
|
from mx_bluesky.hyperion.external_interaction.callbacks.common.ispyb_mapping import (
|
|
7
9
|
populate_data_collection_group,
|
|
8
10
|
populate_remaining_data_collection_info,
|
|
@@ -22,8 +24,7 @@ from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_store import (
|
|
|
22
24
|
IspybIds,
|
|
23
25
|
StoreInIspyb,
|
|
24
26
|
)
|
|
25
|
-
from mx_bluesky.hyperion.log import ISPYB_LOGGER
|
|
26
|
-
from mx_bluesky.hyperion.parameters.components import IspybExperimentType
|
|
27
|
+
from mx_bluesky.hyperion.log import ISPYB_LOGGER
|
|
27
28
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
28
29
|
from mx_bluesky.hyperion.parameters.rotation import RotationScan
|
|
29
30
|
|
|
@@ -60,7 +61,9 @@ class RotationISPyBCallback(BaseISPyBCallback):
|
|
|
60
61
|
ISPYB_LOGGER.info(
|
|
61
62
|
"ISPyB callback received start document with experiment parameters."
|
|
62
63
|
)
|
|
63
|
-
|
|
64
|
+
hyperion_params = doc.get("hyperion_parameters")
|
|
65
|
+
assert isinstance(hyperion_params, str)
|
|
66
|
+
self.params = RotationScan.model_validate_json(hyperion_params)
|
|
64
67
|
dcgid = (
|
|
65
68
|
self.ispyb_ids.data_collection_group_id
|
|
66
69
|
if (self.params.sample_id == self.last_sample_id)
|
|
@@ -157,7 +160,7 @@ class RotationISPyBCallback(BaseISPyBCallback):
|
|
|
157
160
|
data_collection_info = DataCollectionInfo(
|
|
158
161
|
**{
|
|
159
162
|
f"xtal_snapshot{self._oav_snapshot_event_idx}": data.get(
|
|
160
|
-
"
|
|
163
|
+
"oav-snapshot-last_saved_path"
|
|
161
164
|
)
|
|
162
165
|
}
|
|
163
166
|
)
|
|
@@ -78,12 +78,14 @@ class RotationNexusFileCallback(PlanReactiveCallback):
|
|
|
78
78
|
self.meta_data_run_number = doc.get("meta_data_run_number")
|
|
79
79
|
if doc.get("subplan_name") == CONST.PLAN.ROTATION_OUTER:
|
|
80
80
|
self.run_uid = doc.get("uid")
|
|
81
|
-
|
|
81
|
+
hyperion_params = doc.get("hyperion_parameters")
|
|
82
|
+
assert isinstance(hyperion_params, str)
|
|
82
83
|
NEXUS_LOGGER.info(
|
|
83
|
-
f"Nexus writer received start document with experiment parameters {
|
|
84
|
+
f"Nexus writer received start document with experiment parameters {hyperion_params}"
|
|
84
85
|
)
|
|
85
|
-
parameters = RotationScan.
|
|
86
|
+
parameters = RotationScan.model_validate_json(hyperion_params)
|
|
86
87
|
NEXUS_LOGGER.info("Setting up nexus file...")
|
|
88
|
+
|
|
87
89
|
det_size = (
|
|
88
90
|
parameters.detector_params.detector_size_constants.det_size_pixels
|
|
89
91
|
)
|
|
File without changes
|
mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/sample_handling_callback.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from collections.abc import Generator
|
|
3
|
+
from functools import partial
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import bluesky.plan_stubs as bps
|
|
7
|
+
from bluesky.preprocessors import contingency_wrapper
|
|
8
|
+
from bluesky.utils import Msg, make_decorator
|
|
9
|
+
from event_model import Event, EventDescriptor, RunStart
|
|
10
|
+
|
|
11
|
+
from mx_bluesky.hyperion.exceptions import CrystalNotFoundException, SampleException
|
|
12
|
+
from mx_bluesky.hyperion.external_interaction.callbacks.common.abstract_event import (
|
|
13
|
+
AbstractEvent,
|
|
14
|
+
)
|
|
15
|
+
from mx_bluesky.hyperion.external_interaction.callbacks.plan_reactive_callback import (
|
|
16
|
+
PlanReactiveCallback,
|
|
17
|
+
)
|
|
18
|
+
from mx_bluesky.hyperion.external_interaction.ispyb.exp_eye_store import (
|
|
19
|
+
BLSampleStatus,
|
|
20
|
+
ExpeyeInteraction,
|
|
21
|
+
)
|
|
22
|
+
from mx_bluesky.hyperion.log import ISPYB_LOGGER
|
|
23
|
+
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
24
|
+
|
|
25
|
+
# TODO remove this event-raising shenanigans once
|
|
26
|
+
# https://github.com/bluesky/bluesky/issues/1829 is addressed
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclasses.dataclass(frozen=True)
|
|
30
|
+
class _ExceptionEvent(AbstractEvent):
|
|
31
|
+
exception_type: str
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _exception_interceptor(exception: Exception) -> Generator[Msg, Any, Any]:
|
|
35
|
+
yield from bps.create(CONST.DESCRIPTORS.SAMPLE_HANDLING_EXCEPTION)
|
|
36
|
+
yield from bps.read(_ExceptionEvent(type(exception).__name__))
|
|
37
|
+
yield from bps.save()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
sample_handling_callback_decorator = make_decorator(
|
|
41
|
+
partial(contingency_wrapper, except_plan=_exception_interceptor)
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class SampleHandlingCallback(PlanReactiveCallback):
|
|
46
|
+
"""Intercepts exceptions from experiment plans and updates the ISPyB BLSampleStatus
|
|
47
|
+
field according to the type of exception raised."""
|
|
48
|
+
|
|
49
|
+
def __init__(self):
|
|
50
|
+
super().__init__(log=ISPYB_LOGGER)
|
|
51
|
+
self._sample_id: int | None = None
|
|
52
|
+
self._descriptor: str | None = None
|
|
53
|
+
|
|
54
|
+
def activity_gated_start(self, doc: RunStart):
|
|
55
|
+
if not self._sample_id:
|
|
56
|
+
sample_id = doc.get("metadata", {}).get("sample_id")
|
|
57
|
+
self.log.info(f"Recording sample ID at run start {sample_id}")
|
|
58
|
+
self._sample_id = sample_id
|
|
59
|
+
|
|
60
|
+
def activity_gated_descriptor(self, doc: EventDescriptor) -> EventDescriptor | None:
|
|
61
|
+
if doc.get("name") == CONST.DESCRIPTORS.SAMPLE_HANDLING_EXCEPTION:
|
|
62
|
+
self._descriptor = doc["uid"]
|
|
63
|
+
return super().activity_gated_descriptor(doc)
|
|
64
|
+
|
|
65
|
+
def activity_gated_event(self, doc: Event) -> Event | None:
|
|
66
|
+
if doc["descriptor"] == self._descriptor:
|
|
67
|
+
exception_type = doc["data"]["exception_type"]
|
|
68
|
+
self.log.info(
|
|
69
|
+
f"Sample handling callback intercepted exception of type {exception_type}"
|
|
70
|
+
)
|
|
71
|
+
self._record_exception(exception_type)
|
|
72
|
+
return doc
|
|
73
|
+
|
|
74
|
+
def _record_exception(self, exception_type: str):
|
|
75
|
+
expeye = ExpeyeInteraction()
|
|
76
|
+
assert self._sample_id, "Unable to record exception due to no sample ID"
|
|
77
|
+
sample_status = self._decode_sample_status(exception_type)
|
|
78
|
+
expeye.update_sample_status(self._sample_id, sample_status)
|
|
79
|
+
|
|
80
|
+
def _decode_sample_status(self, exception_type: str) -> BLSampleStatus:
|
|
81
|
+
match exception_type:
|
|
82
|
+
case SampleException.__name__ | CrystalNotFoundException.__name__:
|
|
83
|
+
return BLSampleStatus.ERROR_SAMPLE
|
|
84
|
+
return BLSampleStatus.ERROR_BEAMLINE
|
|
@@ -5,10 +5,16 @@ from time import time
|
|
|
5
5
|
from typing import TYPE_CHECKING, Any
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
|
-
from blueapi.core import MsgGenerator
|
|
9
8
|
from bluesky import preprocessors as bpp
|
|
10
|
-
from
|
|
9
|
+
from bluesky.utils import MsgGenerator
|
|
10
|
+
from dodal.devices.zocalo.zocalo_results import (
|
|
11
|
+
ZOCALO_READING_PLAN_NAME,
|
|
12
|
+
get_processing_results_from_event,
|
|
13
|
+
)
|
|
11
14
|
|
|
15
|
+
from mx_bluesky.common.parameters.components import DiffractionExperimentWithSample
|
|
16
|
+
from mx_bluesky.common.parameters.constants import PlanNameConstants
|
|
17
|
+
from mx_bluesky.common.utils.log import set_dcgid_tag
|
|
12
18
|
from mx_bluesky.hyperion.external_interaction.callbacks.common.ispyb_mapping import (
|
|
13
19
|
populate_data_collection_group,
|
|
14
20
|
populate_remaining_data_collection_info,
|
|
@@ -36,8 +42,7 @@ from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_store import (
|
|
|
36
42
|
IspybIds,
|
|
37
43
|
StoreInIspyb,
|
|
38
44
|
)
|
|
39
|
-
from mx_bluesky.hyperion.log import ISPYB_LOGGER
|
|
40
|
-
from mx_bluesky.hyperion.parameters.components import DiffractionExperimentWithSample
|
|
45
|
+
from mx_bluesky.hyperion.log import ISPYB_LOGGER
|
|
41
46
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
42
47
|
from mx_bluesky.hyperion.parameters.gridscan import (
|
|
43
48
|
GridCommon,
|
|
@@ -48,13 +53,16 @@ if TYPE_CHECKING:
|
|
|
48
53
|
|
|
49
54
|
|
|
50
55
|
def ispyb_activation_wrapper(plan_generator: MsgGenerator, parameters):
|
|
51
|
-
return bpp.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
return bpp.set_run_key_wrapper(
|
|
57
|
+
bpp.run_wrapper(
|
|
58
|
+
plan_generator,
|
|
59
|
+
md={
|
|
60
|
+
"activate_callbacks": ["GridscanISPyBCallback"],
|
|
61
|
+
"subplan_name": CONST.PLAN.GRID_DETECT_AND_DO_GRIDSCAN,
|
|
62
|
+
"hyperion_parameters": parameters.model_dump_json(),
|
|
63
|
+
},
|
|
64
|
+
),
|
|
65
|
+
CONST.PLAN.ISPYB_ACTIVATION,
|
|
58
66
|
)
|
|
59
67
|
|
|
60
68
|
|
|
@@ -85,7 +93,7 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
85
93
|
self._processing_start_time: float | None = None
|
|
86
94
|
|
|
87
95
|
def activity_gated_start(self, doc: RunStart):
|
|
88
|
-
if doc.get("subplan_name") ==
|
|
96
|
+
if doc.get("subplan_name") == PlanNameConstants.DO_FGS:
|
|
89
97
|
self._start_of_fgs_uid = doc.get("uid")
|
|
90
98
|
if doc.get("subplan_name") == CONST.PLAN.GRID_DETECT_AND_DO_GRIDSCAN:
|
|
91
99
|
self.uid_to_finalize_on = doc.get("uid")
|
|
@@ -93,7 +101,9 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
93
101
|
"ISPyB callback received start document with experiment parameters and "
|
|
94
102
|
f"uid: {self.uid_to_finalize_on}"
|
|
95
103
|
)
|
|
96
|
-
|
|
104
|
+
hyperion_params = doc.get("hyperion_parameters")
|
|
105
|
+
assert isinstance(hyperion_params, str)
|
|
106
|
+
self.params = GridCommon.model_validate_json(hyperion_params)
|
|
97
107
|
self.ispyb = StoreInIspyb(self.ispyb_config)
|
|
98
108
|
data_collection_group_info = populate_data_collection_group(self.params)
|
|
99
109
|
|
|
@@ -147,7 +157,8 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
147
157
|
ISPYB_LOGGER.info(
|
|
148
158
|
f"Amending comment based on Zocalo reading doc: {format_doc_for_log(doc)}"
|
|
149
159
|
)
|
|
150
|
-
|
|
160
|
+
|
|
161
|
+
raw_results = get_processing_results_from_event("zocalo", doc)
|
|
151
162
|
if len(raw_results) > 0:
|
|
152
163
|
for n, res in enumerate(raw_results):
|
|
153
164
|
bb = res["bounding_box"]
|
|
@@ -178,25 +189,25 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
178
189
|
data = doc["data"]
|
|
179
190
|
data_collection_id = None
|
|
180
191
|
data_collection_info = DataCollectionInfo(
|
|
181
|
-
xtal_snapshot1=data.get("
|
|
182
|
-
xtal_snapshot2=data.get("
|
|
183
|
-
xtal_snapshot3=data.get("
|
|
192
|
+
xtal_snapshot1=data.get("oav-grid_snapshot-last_path_full_overlay"),
|
|
193
|
+
xtal_snapshot2=data.get("oav-grid_snapshot-last_path_outer"),
|
|
194
|
+
xtal_snapshot3=data.get("oav-grid_snapshot-last_saved_path"),
|
|
184
195
|
n_images=(
|
|
185
|
-
data["
|
|
186
|
-
* data["
|
|
196
|
+
data["oav-grid_snapshot-num_boxes_x"]
|
|
197
|
+
* data["oav-grid_snapshot-num_boxes_y"]
|
|
187
198
|
),
|
|
188
199
|
)
|
|
189
|
-
microns_per_pixel_x = data["
|
|
190
|
-
microns_per_pixel_y = data["
|
|
200
|
+
microns_per_pixel_x = data["oav-microns_per_pixel_x"]
|
|
201
|
+
microns_per_pixel_y = data["oav-microns_per_pixel_y"]
|
|
191
202
|
data_collection_grid_info = DataCollectionGridInfo(
|
|
192
|
-
dx_in_mm=data["
|
|
193
|
-
dy_in_mm=data["
|
|
194
|
-
steps_x=data["
|
|
195
|
-
steps_y=data["
|
|
203
|
+
dx_in_mm=data["oav-grid_snapshot-box_width"] * microns_per_pixel_x / 1000,
|
|
204
|
+
dy_in_mm=data["oav-grid_snapshot-box_width"] * microns_per_pixel_y / 1000,
|
|
205
|
+
steps_x=data["oav-grid_snapshot-num_boxes_x"],
|
|
206
|
+
steps_y=data["oav-grid_snapshot-num_boxes_y"],
|
|
196
207
|
microns_per_pixel_x=microns_per_pixel_x,
|
|
197
208
|
microns_per_pixel_y=microns_per_pixel_y,
|
|
198
|
-
snapshot_offset_x_pixel=int(data["
|
|
199
|
-
snapshot_offset_y_pixel=int(data["
|
|
209
|
+
snapshot_offset_x_pixel=int(data["oav-grid_snapshot-top_left_x"]),
|
|
210
|
+
snapshot_offset_y_pixel=int(data["oav-grid_snapshot-top_left_y"]),
|
|
200
211
|
orientation=Orientation.HORIZONTAL,
|
|
201
212
|
snaked=True,
|
|
202
213
|
)
|
|
@@ -12,7 +12,7 @@ from mx_bluesky.hyperion.external_interaction.nexus.nexus_utils import (
|
|
|
12
12
|
from mx_bluesky.hyperion.external_interaction.nexus.write_nexus import NexusWriter
|
|
13
13
|
from mx_bluesky.hyperion.log import NEXUS_LOGGER
|
|
14
14
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
15
|
-
from mx_bluesky.hyperion.parameters.gridscan import
|
|
15
|
+
from mx_bluesky.hyperion.parameters.gridscan import HyperionThreeDGridScan
|
|
16
16
|
|
|
17
17
|
if TYPE_CHECKING:
|
|
18
18
|
from event_model.documents import Event, EventDescriptor, RunStart
|
|
@@ -45,11 +45,12 @@ class GridscanNexusFileCallback(PlanReactiveCallback):
|
|
|
45
45
|
|
|
46
46
|
def activity_gated_start(self, doc: RunStart):
|
|
47
47
|
if doc.get("subplan_name") == CONST.PLAN.GRIDSCAN_OUTER:
|
|
48
|
-
|
|
48
|
+
hyperion_params = doc.get("hyperion_parameters")
|
|
49
|
+
assert isinstance(hyperion_params, str)
|
|
49
50
|
NEXUS_LOGGER.info(
|
|
50
|
-
f"Nexus writer received start document with experiment parameters {
|
|
51
|
+
f"Nexus writer received start document with experiment parameters {hyperion_params}"
|
|
51
52
|
)
|
|
52
|
-
parameters =
|
|
53
|
+
parameters = HyperionThreeDGridScan.model_validate_json(hyperion_params)
|
|
53
54
|
d_size = parameters.detector_params.detector_size_constants.det_size_pixels
|
|
54
55
|
grid_n_img_1 = parameters.scan_indices[1]
|
|
55
56
|
grid_n_img_2 = parameters.num_images - grid_n_img_1
|
|
@@ -1,35 +1,18 @@
|
|
|
1
|
+
from functools import cache
|
|
2
|
+
|
|
1
3
|
from daq_config_server.client import ConfigServer
|
|
2
|
-
from pydantic import BaseModel
|
|
3
4
|
|
|
5
|
+
from mx_bluesky.common.external_interaction.config_server import FeatureFlags
|
|
4
6
|
from mx_bluesky.hyperion.log import LOGGER
|
|
5
7
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
6
8
|
|
|
7
|
-
_CONFIG_SERVER: ConfigServer | None = None
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def config_server() -> ConfigServer:
|
|
11
|
-
global _CONFIG_SERVER
|
|
12
|
-
if _CONFIG_SERVER is None:
|
|
13
|
-
_CONFIG_SERVER = ConfigServer(CONST.CONFIG_SERVER_URL, LOGGER)
|
|
14
|
-
return _CONFIG_SERVER
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class FeatureFlags(BaseModel):
|
|
18
|
-
# The default value will be used as the fallback when doing a best-effort fetch
|
|
19
|
-
# from the service
|
|
20
|
-
use_panda_for_gridscan: bool = False
|
|
21
|
-
use_gpu_for_gridscan: bool = False
|
|
22
|
-
set_stub_offsets: bool = False
|
|
23
|
-
|
|
24
|
-
@classmethod
|
|
25
|
-
def _get_flags(cls):
|
|
26
|
-
flags = config_server().best_effort_get_all_feature_flags()
|
|
27
|
-
return {f: flags[f] for f in flags if f in cls.__fields__.keys()}
|
|
28
9
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
10
|
+
class HyperionFeatureFlags(FeatureFlags):
|
|
11
|
+
@staticmethod
|
|
12
|
+
@cache
|
|
13
|
+
def get_config_server() -> ConfigServer:
|
|
14
|
+
return ConfigServer(CONST.CONFIG_SERVER_URL, LOGGER)
|
|
32
15
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
16
|
+
use_panda_for_gridscan: bool = CONST.I03.USE_PANDA_FOR_GRIDSCAN
|
|
17
|
+
compare_cpu_and_gpu_zocalo: bool = CONST.I03.COMPARE_CPU_AND_GPU_ZOCALO
|
|
18
|
+
set_stub_offsets: bool = CONST.I03.SET_STUB_OFFSETS
|
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
from mx_bluesky.hyperion.exceptions import WarningException
|
|
2
|
-
|
|
3
|
-
|
|
4
1
|
class ISPyBDepositionNotMade(Exception):
|
|
5
2
|
"""Raised when the ISPyB or Zocalo callbacks can't access ISPyB deposition numbers."""
|
|
6
3
|
|
|
7
4
|
pass
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class NoCentreFoundException(WarningException):
|
|
11
|
-
"""Error for if zocalo is unable to find the centre during a gridscan."""
|
|
12
|
-
|
|
13
|
-
pass
|