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
|
@@ -1,25 +1,43 @@
|
|
|
1
|
-
import
|
|
1
|
+
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
|
|
5
|
+
import pydantic
|
|
3
6
|
from blueapi.core import BlueskyContext
|
|
7
|
+
from bluesky.preprocessors import run_decorator, set_run_key_decorator, subs_wrapper
|
|
8
|
+
from bluesky.utils import MsgGenerator
|
|
4
9
|
from dodal.devices.oav.oav_parameters import OAVParameters
|
|
10
|
+
from dodal.devices.smargon import Smargon
|
|
5
11
|
|
|
12
|
+
import mx_bluesky.hyperion.experiment_plans.common.xrc_result as flyscan_result
|
|
13
|
+
from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
|
|
14
|
+
XRayCentreEventHandler,
|
|
15
|
+
)
|
|
6
16
|
from mx_bluesky.hyperion.experiment_plans.robot_load_then_centre_plan import (
|
|
7
17
|
RobotLoadThenCentreComposite,
|
|
8
|
-
|
|
18
|
+
robot_load_then_xray_centre,
|
|
9
19
|
)
|
|
10
20
|
from mx_bluesky.hyperion.experiment_plans.rotation_scan_plan import (
|
|
21
|
+
MultiRotationScan,
|
|
11
22
|
RotationScanComposite,
|
|
12
23
|
multi_rotation_scan,
|
|
13
24
|
)
|
|
25
|
+
from mx_bluesky.hyperion.external_interaction.callbacks.sample_handling.sample_handling_callback import (
|
|
26
|
+
sample_handling_callback_decorator,
|
|
27
|
+
)
|
|
28
|
+
from mx_bluesky.hyperion.log import LOGGER
|
|
29
|
+
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
14
30
|
from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect
|
|
15
31
|
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
16
32
|
|
|
17
33
|
|
|
18
|
-
@dataclasses.dataclass
|
|
34
|
+
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
19
35
|
class LoadCentreCollectComposite(RobotLoadThenCentreComposite, RotationScanComposite):
|
|
20
36
|
"""Composite that provides access to the required devices."""
|
|
21
37
|
|
|
22
|
-
|
|
38
|
+
@property
|
|
39
|
+
def sample_motors(self) -> Smargon:
|
|
40
|
+
return self.smargon
|
|
23
41
|
|
|
24
42
|
|
|
25
43
|
def create_devices(context: BlueskyContext) -> LoadCentreCollectComposite:
|
|
@@ -27,11 +45,11 @@ def create_devices(context: BlueskyContext) -> LoadCentreCollectComposite:
|
|
|
27
45
|
return device_composite_from_context(context, LoadCentreCollectComposite)
|
|
28
46
|
|
|
29
47
|
|
|
30
|
-
def
|
|
48
|
+
def load_centre_collect_full(
|
|
31
49
|
composite: LoadCentreCollectComposite,
|
|
32
|
-
|
|
50
|
+
parameters: LoadCentreCollect,
|
|
33
51
|
oav_params: OAVParameters | None = None,
|
|
34
|
-
):
|
|
52
|
+
) -> MsgGenerator:
|
|
35
53
|
"""Attempt a complete data collection experiment, consisting of the following:
|
|
36
54
|
* Load the sample if necessary
|
|
37
55
|
* Move to the specified goniometer start angles
|
|
@@ -41,6 +59,54 @@ def load_centre_collect_full_plan(
|
|
|
41
59
|
"""
|
|
42
60
|
if not oav_params:
|
|
43
61
|
oav_params = OAVParameters(context="xrayCentring")
|
|
44
|
-
yield from robot_load_then_centre(composite, params.robot_load_then_centre)
|
|
45
62
|
|
|
46
|
-
|
|
63
|
+
@set_run_key_decorator(CONST.PLAN.LOAD_CENTRE_COLLECT)
|
|
64
|
+
@run_decorator(
|
|
65
|
+
md={
|
|
66
|
+
"metadata": {"sample_id": parameters.sample_id},
|
|
67
|
+
"activate_callbacks": ["SampleHandlingCallback"],
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
@sample_handling_callback_decorator()
|
|
71
|
+
def plan_with_callback_subs():
|
|
72
|
+
flyscan_event_handler = XRayCentreEventHandler()
|
|
73
|
+
yield from subs_wrapper(
|
|
74
|
+
robot_load_then_xray_centre(composite, parameters.robot_load_then_centre),
|
|
75
|
+
flyscan_event_handler,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
assert flyscan_event_handler.xray_centre_results, "Flyscan result event not received or no crystal found and exception not raised"
|
|
79
|
+
|
|
80
|
+
selection_func = flyscan_result.resolve_selection_fn(
|
|
81
|
+
parameters.selection_params
|
|
82
|
+
)
|
|
83
|
+
hits: Sequence[flyscan_result.XRayCentreResult] = selection_func(
|
|
84
|
+
flyscan_event_handler.xray_centre_results
|
|
85
|
+
)
|
|
86
|
+
LOGGER.info(
|
|
87
|
+
f"Selected hits {hits} using {selection_func}, args={parameters.selection_params}"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
multi_rotation = parameters.multi_rotation_scan
|
|
91
|
+
rotation_template = multi_rotation.rotation_scans.copy()
|
|
92
|
+
|
|
93
|
+
multi_rotation.rotation_scans.clear()
|
|
94
|
+
|
|
95
|
+
for hit in hits:
|
|
96
|
+
for rot in rotation_template:
|
|
97
|
+
combination = rot.model_copy()
|
|
98
|
+
(
|
|
99
|
+
combination.x_start_um,
|
|
100
|
+
combination.y_start_um,
|
|
101
|
+
combination.z_start_um,
|
|
102
|
+
) = (axis * 1000 for axis in hit.centre_of_mass_mm)
|
|
103
|
+
multi_rotation.rotation_scans.append(combination)
|
|
104
|
+
multi_rotation = MultiRotationScan.model_validate(multi_rotation)
|
|
105
|
+
|
|
106
|
+
assert (
|
|
107
|
+
multi_rotation.demand_energy_ev
|
|
108
|
+
== parameters.robot_load_then_centre.demand_energy_ev
|
|
109
|
+
), "Setting a different energy for gridscan and rotation is not supported"
|
|
110
|
+
yield from multi_rotation_scan(composite, multi_rotation, oav_params)
|
|
111
|
+
|
|
112
|
+
yield from plan_with_callback_subs()
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import dataclasses
|
|
4
3
|
import math
|
|
5
4
|
from typing import TYPE_CHECKING
|
|
6
5
|
|
|
7
6
|
import bluesky.plan_stubs as bps
|
|
8
7
|
import numpy as np
|
|
8
|
+
import pydantic
|
|
9
9
|
from blueapi.core import BlueskyContext
|
|
10
10
|
from dodal.devices.backlight import Backlight
|
|
11
11
|
from dodal.devices.oav.oav_detector import OAV
|
|
@@ -26,7 +26,7 @@ if TYPE_CHECKING:
|
|
|
26
26
|
from dodal.devices.oav.oav_parameters import OAVParameters
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
@dataclasses.dataclass
|
|
29
|
+
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
30
30
|
class OavGridDetectionComposite:
|
|
31
31
|
"""All devices which are directly or indirectly required by this plan"""
|
|
32
32
|
|
|
@@ -63,7 +63,7 @@ def grid_detection_plan(
|
|
|
63
63
|
snapshot_template: str,
|
|
64
64
|
snapshot_dir: str,
|
|
65
65
|
grid_width_microns: float,
|
|
66
|
-
box_size_um: float
|
|
66
|
+
box_size_um: float,
|
|
67
67
|
):
|
|
68
68
|
"""
|
|
69
69
|
Creates the parameters for two grids that are 90 degrees from each other and
|
|
@@ -74,7 +74,7 @@ def grid_detection_plan(
|
|
|
74
74
|
parameters (OAVParameters): Object containing parameters for setting up the OAV
|
|
75
75
|
snapshot_template (str): A template for the name of the snapshots, expected to be filled in with an angle
|
|
76
76
|
snapshot_dir (str): The location to save snapshots
|
|
77
|
-
grid_width_microns (
|
|
77
|
+
grid_width_microns (float): The width of the grid to scan in microns
|
|
78
78
|
box_size_um (float): The size of each box of the grid in microns
|
|
79
79
|
"""
|
|
80
80
|
oav: OAV = composite.oav
|
|
@@ -90,16 +90,17 @@ def grid_detection_plan(
|
|
|
90
90
|
|
|
91
91
|
LOGGER.info("OAV Centring: Camera set up")
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
assert isinstance(oav.parameters.micronsPerYPixel, float)
|
|
96
|
-
box_size_y_pixels = box_size_um / oav.parameters.micronsPerYPixel
|
|
93
|
+
microns_per_pixel_x = yield from bps.rd(oav.microns_per_pixel_x)
|
|
94
|
+
microns_per_pixel_y = yield from bps.rd(oav.microns_per_pixel_y)
|
|
97
95
|
|
|
98
|
-
|
|
96
|
+
box_size_x_pixels = box_size_um / microns_per_pixel_x
|
|
97
|
+
box_size_y_pixels = box_size_um / microns_per_pixel_y
|
|
98
|
+
|
|
99
|
+
grid_width_pixels = int(grid_width_microns / microns_per_pixel_x)
|
|
99
100
|
|
|
100
101
|
# The FGS uses -90 so we need to match it
|
|
101
102
|
for angle in [0, -90]:
|
|
102
|
-
yield from bps.mv(smargon.omega, angle)
|
|
103
|
+
yield from bps.mv(smargon.omega, angle) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
103
104
|
# need to wait for the OAV image to update
|
|
104
105
|
# See #673 for improvements
|
|
105
106
|
yield from bps.sleep(CONST.HARDWARE.OAV_REFRESH_DELAY)
|
|
@@ -115,7 +116,7 @@ def grid_detection_plan(
|
|
|
115
116
|
(yield from bps.rd(pin_tip_detection.triggered_bottom_edge))
|
|
116
117
|
)
|
|
117
118
|
|
|
118
|
-
full_image_height_px = yield from bps.rd(oav.cam.
|
|
119
|
+
full_image_height_px = yield from bps.rd(oav.cam.array_size_y)
|
|
119
120
|
|
|
120
121
|
# only use the area from the start of the pin onwards
|
|
121
122
|
top_edge = top_edge[tip_x_px : tip_x_px + grid_width_pixels]
|
|
@@ -151,20 +152,20 @@ def grid_detection_plan(
|
|
|
151
152
|
|
|
152
153
|
upper_left = (tip_x_px, min_y)
|
|
153
154
|
|
|
154
|
-
yield from bps.abs_set(oav.grid_snapshot.top_left_x, upper_left[0])
|
|
155
|
-
yield from bps.abs_set(oav.grid_snapshot.top_left_y, upper_left[1])
|
|
156
|
-
yield from bps.abs_set(oav.grid_snapshot.box_width, box_size_x_pixels)
|
|
157
|
-
yield from bps.abs_set(oav.grid_snapshot.num_boxes_x, x_steps)
|
|
158
|
-
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]) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
156
|
+
yield from bps.abs_set(oav.grid_snapshot.top_left_y, upper_left[1]) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
157
|
+
yield from bps.abs_set(oav.grid_snapshot.box_width, box_size_x_pixels) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
158
|
+
yield from bps.abs_set(oav.grid_snapshot.num_boxes_x, x_steps) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
159
|
+
yield from bps.abs_set(oav.grid_snapshot.num_boxes_y, y_steps) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
159
160
|
|
|
160
161
|
snapshot_filename = snapshot_template.format(angle=abs(angle))
|
|
161
162
|
|
|
162
|
-
yield from bps.abs_set(oav.grid_snapshot.filename, snapshot_filename)
|
|
163
|
-
yield from bps.abs_set(oav.grid_snapshot.directory, snapshot_dir)
|
|
164
|
-
yield from bps.trigger(oav.grid_snapshot, wait=True)
|
|
163
|
+
yield from bps.abs_set(oav.grid_snapshot.filename, snapshot_filename) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
164
|
+
yield from bps.abs_set(oav.grid_snapshot.directory, snapshot_dir) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
165
|
+
yield from bps.trigger(oav.grid_snapshot, wait=True) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
165
166
|
yield from bps.create(CONST.DESCRIPTORS.OAV_GRID_SNAPSHOT_TRIGGERED)
|
|
166
167
|
|
|
167
|
-
yield from bps.read(oav.
|
|
168
|
+
yield from bps.read(oav) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
168
169
|
yield from bps.read(smargon)
|
|
169
170
|
yield from bps.save()
|
|
170
171
|
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from typing import Protocol
|
|
3
3
|
|
|
4
|
-
from blueapi.core import MsgGenerator
|
|
5
4
|
from bluesky import plan_stubs as bps
|
|
5
|
+
from bluesky.utils import MsgGenerator
|
|
6
6
|
from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureValue
|
|
7
7
|
from dodal.devices.backlight import Backlight, BacklightPosition
|
|
8
8
|
from dodal.devices.oav.oav_detector import OAV
|
|
9
9
|
from dodal.devices.oav.oav_parameters import OAVParameters
|
|
10
10
|
from dodal.devices.smargon import Smargon
|
|
11
11
|
|
|
12
|
+
from mx_bluesky.common.parameters.components import WithSnapshot
|
|
12
13
|
from mx_bluesky.hyperion.device_setup_plans.setup_oav import setup_general_oav_params
|
|
13
|
-
from mx_bluesky.hyperion.parameters.components import WithSnapshot
|
|
14
14
|
from mx_bluesky.hyperion.parameters.constants import CONST, DocDescriptorNames
|
|
15
15
|
|
|
16
16
|
OAV_SNAPSHOT_SETUP_SHOT = "oav_snapshot_setup_shot"
|
|
@@ -61,7 +61,8 @@ def _setup_oav(
|
|
|
61
61
|
):
|
|
62
62
|
yield from setup_general_oav_params(composite.oav, oav_parameters)
|
|
63
63
|
yield from bps.abs_set(
|
|
64
|
-
composite.oav.snapshot.directory,
|
|
64
|
+
composite.oav.snapshot.directory, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
65
|
+
str(parameters.snapshot_directory),
|
|
65
66
|
)
|
|
66
67
|
|
|
67
68
|
|
|
@@ -72,10 +73,12 @@ def _take_oav_snapshot(composite: OavSnapshotComposite, omega: float):
|
|
|
72
73
|
time_now = datetime.now()
|
|
73
74
|
filename = f"{time_now.strftime('%H%M%S')}_oav_snapshot_{omega:.0f}"
|
|
74
75
|
yield from bps.abs_set(
|
|
75
|
-
composite.oav.snapshot.filename,
|
|
76
|
+
composite.oav.snapshot.filename, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
77
|
+
filename,
|
|
78
|
+
group=OAV_SNAPSHOT_SETUP_SHOT,
|
|
76
79
|
)
|
|
77
80
|
yield from bps.wait(group=OAV_SNAPSHOT_SETUP_SHOT)
|
|
78
|
-
yield from bps.trigger(composite.oav.snapshot, wait=True)
|
|
81
|
+
yield from bps.trigger(composite.oav.snapshot, wait=True) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
79
82
|
yield from bps.create(DocDescriptorNames.OAV_ROTATION_SNAPSHOT_TRIGGERED)
|
|
80
|
-
yield from bps.read(composite.oav.snapshot)
|
|
83
|
+
yield from bps.read(composite.oav.snapshot) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
81
84
|
yield from bps.save()
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import dataclasses
|
|
2
1
|
from enum import Enum
|
|
3
2
|
|
|
4
3
|
import bluesky.plan_stubs as bps
|
|
5
4
|
import bluesky.preprocessors as bpp
|
|
6
5
|
import numpy as np
|
|
6
|
+
import pydantic
|
|
7
7
|
from blueapi.core import BlueskyContext
|
|
8
8
|
from dodal.devices.attenuator import Attenuator
|
|
9
9
|
from dodal.devices.xspress3.xspress3 import Xspress3
|
|
@@ -22,7 +22,7 @@ class Direction(Enum):
|
|
|
22
22
|
NEGATIVE = "negative"
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
@dataclasses.dataclass
|
|
25
|
+
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
26
26
|
class OptimizeAttenuationComposite:
|
|
27
27
|
"""All devices which are directly or indirectly required by this plan"""
|
|
28
28
|
|
|
@@ -2,14 +2,27 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import bluesky.preprocessors as bpp
|
|
6
|
+
from blueapi.core import BlueskyContext
|
|
7
|
+
from bluesky.utils import MsgGenerator
|
|
6
8
|
from dodal.devices.eiger import EigerDetector
|
|
7
|
-
from dodal.devices.oav.oav_parameters import
|
|
9
|
+
from dodal.devices.oav.oav_parameters import OAVParameters
|
|
8
10
|
|
|
11
|
+
from mx_bluesky.common.parameters.constants import OavConstants
|
|
12
|
+
from mx_bluesky.common.parameters.gridscan import (
|
|
13
|
+
GridScanWithEdgeDetect,
|
|
14
|
+
PinTipCentreThenXrayCentre,
|
|
15
|
+
)
|
|
9
16
|
from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import move_phi_chi_omega
|
|
10
17
|
from mx_bluesky.hyperion.device_setup_plans.utils import (
|
|
11
18
|
start_preparing_data_collection_then_do_plan,
|
|
12
19
|
)
|
|
20
|
+
from mx_bluesky.hyperion.experiment_plans.change_aperture_then_move_plan import (
|
|
21
|
+
change_aperture_then_move_to_xtal,
|
|
22
|
+
)
|
|
23
|
+
from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
|
|
24
|
+
XRayCentreEventHandler,
|
|
25
|
+
)
|
|
13
26
|
from mx_bluesky.hyperion.experiment_plans.grid_detect_then_xray_centre_plan import (
|
|
14
27
|
GridDetectThenXRayCentreComposite,
|
|
15
28
|
detect_grid_and_do_gridscan,
|
|
@@ -26,10 +39,6 @@ from mx_bluesky.hyperion.external_interaction.callbacks.xray_centre.ispyb_callba
|
|
|
26
39
|
)
|
|
27
40
|
from mx_bluesky.hyperion.log import LOGGER
|
|
28
41
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
29
|
-
from mx_bluesky.hyperion.parameters.gridscan import (
|
|
30
|
-
GridScanWithEdgeDetect,
|
|
31
|
-
PinTipCentreThenXrayCentre,
|
|
32
|
-
)
|
|
33
42
|
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
34
43
|
|
|
35
44
|
|
|
@@ -52,14 +61,12 @@ def create_parameters_for_grid_detection(
|
|
|
52
61
|
return grid_detect_and_xray_centre
|
|
53
62
|
|
|
54
63
|
|
|
55
|
-
def
|
|
64
|
+
def pin_centre_then_flyscan_plan(
|
|
56
65
|
composite: GridDetectThenXRayCentreComposite,
|
|
57
66
|
parameters: PinTipCentreThenXrayCentre,
|
|
58
|
-
oav_config_file: str = OAV_CONFIG_JSON,
|
|
67
|
+
oav_config_file: str = OavConstants.OAV_CONFIG_JSON,
|
|
59
68
|
):
|
|
60
|
-
"""Plan that
|
|
61
|
-
centre the sample"""
|
|
62
|
-
oav_config_file = parameters.oav_centring_file
|
|
69
|
+
"""Plan that performs a pin tip centre followed by a flyscan to determine the centres of interest"""
|
|
63
70
|
|
|
64
71
|
pin_tip_centring_composite = PinTipCentringComposite(
|
|
65
72
|
oav=composite.oav,
|
|
@@ -68,7 +75,7 @@ def pin_centre_then_xray_centre_plan(
|
|
|
68
75
|
pin_tip_detection=composite.pin_tip_detection,
|
|
69
76
|
)
|
|
70
77
|
|
|
71
|
-
def
|
|
78
|
+
def _pin_centre_then_flyscan_plan():
|
|
72
79
|
yield from setup_beamline_for_OAV(
|
|
73
80
|
composite.smargon, composite.backlight, composite.aperture_scatterguard
|
|
74
81
|
)
|
|
@@ -96,24 +103,36 @@ def pin_centre_then_xray_centre_plan(
|
|
|
96
103
|
oav_params,
|
|
97
104
|
)
|
|
98
105
|
|
|
99
|
-
yield from ispyb_activation_wrapper(
|
|
106
|
+
yield from ispyb_activation_wrapper(_pin_centre_then_flyscan_plan(), parameters)
|
|
100
107
|
|
|
101
108
|
|
|
102
109
|
def pin_tip_centre_then_xray_centre(
|
|
103
110
|
composite: GridDetectThenXRayCentreComposite,
|
|
104
111
|
parameters: PinTipCentreThenXrayCentre,
|
|
105
|
-
oav_config_file: str = OAV_CONFIG_JSON,
|
|
112
|
+
oav_config_file: str = OavConstants.OAV_CONFIG_JSON,
|
|
106
113
|
) -> MsgGenerator:
|
|
107
114
|
"""Starts preparing for collection then performs the pin tip centre and xray centre"""
|
|
108
|
-
|
|
109
115
|
eiger: EigerDetector = composite.eiger
|
|
110
116
|
|
|
111
117
|
eiger.set_detector_parameters(parameters.detector_params)
|
|
112
118
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
+
flyscan_event_handler = XRayCentreEventHandler()
|
|
120
|
+
|
|
121
|
+
@bpp.subs_decorator(flyscan_event_handler)
|
|
122
|
+
def pin_centre_flyscan_then_fetch_results() -> MsgGenerator:
|
|
123
|
+
yield from start_preparing_data_collection_then_do_plan(
|
|
124
|
+
eiger,
|
|
125
|
+
composite.detector_motion,
|
|
126
|
+
parameters.detector_params.detector_distance,
|
|
127
|
+
pin_centre_then_flyscan_plan(composite, parameters, oav_config_file),
|
|
128
|
+
group=CONST.WAIT.GRID_READY_FOR_DC,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
yield from pin_centre_flyscan_then_fetch_results()
|
|
132
|
+
flyscan_results = flyscan_event_handler.xray_centre_results
|
|
133
|
+
assert (
|
|
134
|
+
flyscan_results
|
|
135
|
+
), "Flyscan result event not received or no crystal found and exception not raised"
|
|
136
|
+
yield from change_aperture_then_move_to_xtal(
|
|
137
|
+
flyscan_results[0], composite.smargon, composite.aperture_scatterguard
|
|
119
138
|
)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import dataclasses
|
|
2
1
|
from collections.abc import Generator
|
|
3
2
|
|
|
4
3
|
import bluesky.plan_stubs as bps
|
|
4
|
+
import pydantic
|
|
5
5
|
from blueapi.core import BlueskyContext
|
|
6
6
|
from bluesky.utils import Msg
|
|
7
7
|
from dodal.devices.backlight import Backlight
|
|
8
8
|
from dodal.devices.oav.oav_detector import OAV
|
|
9
9
|
from dodal.devices.oav.oav_parameters import OAV_CONFIG_JSON, OAVParameters
|
|
10
|
-
from dodal.devices.oav.pin_image_recognition import PinTipDetection
|
|
10
|
+
from dodal.devices.oav.pin_image_recognition import PinTipDetection, Tip
|
|
11
11
|
from dodal.devices.oav.utils import (
|
|
12
12
|
Pixel,
|
|
13
13
|
get_move_required_so_that_beam_is_at_pixel,
|
|
@@ -19,7 +19,7 @@ from mx_bluesky.hyperion.device_setup_plans.setup_oav import pre_centring_setup_
|
|
|
19
19
|
from mx_bluesky.hyperion.device_setup_plans.smargon import (
|
|
20
20
|
move_smargon_warn_on_out_of_range,
|
|
21
21
|
)
|
|
22
|
-
from mx_bluesky.hyperion.exceptions import
|
|
22
|
+
from mx_bluesky.hyperion.exceptions import SampleException
|
|
23
23
|
from mx_bluesky.hyperion.log import LOGGER
|
|
24
24
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
25
25
|
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
@@ -27,7 +27,7 @@ from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
|
27
27
|
DEFAULT_STEP_SIZE = 0.5
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
@dataclasses.dataclass
|
|
30
|
+
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
31
31
|
class PinTipCentringComposite:
|
|
32
32
|
"""All devices which are directly or indirectly required by this plan"""
|
|
33
33
|
|
|
@@ -43,11 +43,11 @@ def create_devices(context: BlueskyContext) -> PinTipCentringComposite:
|
|
|
43
43
|
|
|
44
44
|
def trigger_and_return_pin_tip(
|
|
45
45
|
pin_tip: PinTipDetection,
|
|
46
|
-
) -> Generator[Msg, None,
|
|
46
|
+
) -> Generator[Msg, None, Tip]:
|
|
47
47
|
yield from bps.trigger(pin_tip, wait=True)
|
|
48
48
|
tip_x_y_px = yield from bps.rd(pin_tip.triggered_tip)
|
|
49
49
|
LOGGER.info(f"Pin tip found at {tip_x_y_px}")
|
|
50
|
-
return tip_x_y_px
|
|
50
|
+
return tip_x_y_px
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
def move_pin_into_view(
|
|
@@ -68,22 +68,22 @@ def move_pin_into_view(
|
|
|
68
68
|
max_steps (int, optional): The number of steps to search with. Defaults to 2.
|
|
69
69
|
|
|
70
70
|
Raises:
|
|
71
|
-
|
|
71
|
+
SampleException: Error if the pin tip is never found
|
|
72
72
|
|
|
73
73
|
Returns:
|
|
74
74
|
Tuple[int, int]: The location of the pin tip in pixels
|
|
75
75
|
"""
|
|
76
76
|
|
|
77
|
-
def pin_tip_valid(
|
|
78
|
-
return
|
|
77
|
+
def pin_tip_valid(pin_xy: Tip):
|
|
78
|
+
return not all(pin_xy == pin_tip_device.INVALID_POSITION) and pin_xy[0] != 0
|
|
79
79
|
|
|
80
80
|
for _ in range(max_steps):
|
|
81
|
-
|
|
81
|
+
tip_xy_px = yield from trigger_and_return_pin_tip(pin_tip_device)
|
|
82
82
|
|
|
83
|
-
if pin_tip_valid(
|
|
84
|
-
return (
|
|
83
|
+
if pin_tip_valid(tip_xy_px):
|
|
84
|
+
return (int(tip_xy_px[0]), int(tip_xy_px[1]))
|
|
85
85
|
|
|
86
|
-
if
|
|
86
|
+
if tip_xy_px[0] == 0:
|
|
87
87
|
# Pin is off in the -ve direction
|
|
88
88
|
step_size_mm = -step_size_mm
|
|
89
89
|
|
|
@@ -97,19 +97,19 @@ def move_pin_into_view(
|
|
|
97
97
|
f"Pin tip is off screen, and moving {step_size_mm} mm would cross limits, "
|
|
98
98
|
f"moving to {move_within_limits} instead"
|
|
99
99
|
)
|
|
100
|
-
yield from bps.mv(smargon.x, move_within_limits)
|
|
100
|
+
yield from bps.mv(smargon.x, move_within_limits) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
101
101
|
|
|
102
102
|
# Some time for the view to settle after the move
|
|
103
103
|
yield from bps.sleep(CONST.HARDWARE.OAV_REFRESH_DELAY)
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
tip_xy_px = yield from trigger_and_return_pin_tip(pin_tip_device)
|
|
106
106
|
|
|
107
|
-
if not pin_tip_valid(
|
|
108
|
-
raise
|
|
107
|
+
if not pin_tip_valid(tip_xy_px):
|
|
108
|
+
raise SampleException(
|
|
109
109
|
"Pin tip centring failed - pin too long/short/bent and out of range"
|
|
110
110
|
)
|
|
111
111
|
else:
|
|
112
|
-
return (
|
|
112
|
+
return (int(tip_xy_px[0]), int(tip_xy_px[1]))
|
|
113
113
|
|
|
114
114
|
|
|
115
115
|
def pin_tip_centre_plan(
|
|
@@ -132,13 +132,13 @@ def pin_tip_centre_plan(
|
|
|
132
132
|
pin_tip_setup = composite.pin_tip_detection
|
|
133
133
|
pin_tip_detect = composite.pin_tip_detection
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
tip_offset_px = int(tip_offset_microns /
|
|
135
|
+
microns_per_pixel_x = yield from bps.rd(oav.microns_per_pixel_x)
|
|
136
|
+
tip_offset_px = int(tip_offset_microns / microns_per_pixel_x)
|
|
137
137
|
|
|
138
138
|
def offset_and_move(tip: Pixel):
|
|
139
139
|
pixel_to_move_to = (tip[0] + tip_offset_px, tip[1])
|
|
140
140
|
position_mm = yield from get_move_required_so_that_beam_is_at_pixel(
|
|
141
|
-
smargon, pixel_to_move_to, oav
|
|
141
|
+
smargon, pixel_to_move_to, oav
|
|
142
142
|
)
|
|
143
143
|
LOGGER.info(f"Tip centring moving to : {position_mm}")
|
|
144
144
|
yield from move_smargon_warn_on_out_of_range(smargon, position_mm)
|
|
@@ -154,7 +154,7 @@ def pin_tip_centre_plan(
|
|
|
154
154
|
tip = yield from move_pin_into_view(pin_tip_detect, smargon)
|
|
155
155
|
yield from offset_and_move(tip)
|
|
156
156
|
|
|
157
|
-
yield from bps.mvr(smargon.omega, 90)
|
|
157
|
+
yield from bps.mvr(smargon.omega, 90) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
158
158
|
|
|
159
159
|
# need to wait for the OAV image to update
|
|
160
160
|
# See #673 for improvements
|