mx-bluesky 1.4.1a0__py3-none-any.whl → 1.4.2__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/i04/redis_to_murko_forwarder.py +178 -0
- mx_bluesky/beamlines/i24/serial/__init__.py +0 -6
- mx_bluesky/beamlines/i24/serial/dcid.py +125 -151
- 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 +66 -36
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +1 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/MappingLite-oxford_py3v1.edl +2 -46
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +74 -120
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +58 -66
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +1 -19
- mx_bluesky/beamlines/i24/serial/parameters/__init__.py +9 -1
- mx_bluesky/beamlines/i24/serial/parameters/constants.py +6 -0
- mx_bluesky/beamlines/i24/serial/parameters/experiment_parameters.py +75 -16
- mx_bluesky/beamlines/i24/serial/parameters/utils.py +19 -0
- mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +2 -0
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +32 -8
- mx_bluesky/beamlines/i24/serial/write_nexus.py +66 -67
- mx_bluesky/common/parameters/components.py +3 -3
- mx_bluesky/common/parameters/constants.py +5 -0
- mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +21 -31
- mx_bluesky/hyperion/device_setup_plans/manipulate_sample.py +6 -6
- mx_bluesky/hyperion/device_setup_plans/smargon.py +3 -3
- mx_bluesky/hyperion/exceptions.py +13 -1
- mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +16 -10
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +0 -8
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +58 -34
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +8 -2
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +3 -3
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +30 -26
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +26 -7
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +0 -7
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +8 -7
- mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +0 -4
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +4 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +5 -0
- mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +18 -10
- 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 +10 -7
- mx_bluesky/hyperion/external_interaction/exceptions.py +0 -9
- mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +65 -15
- mx_bluesky/hyperion/utils/validation.py +1 -1
- {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.2.dist-info}/METADATA +2 -2
- {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.2.dist-info}/RECORD +49 -46
- {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.2.dist-info}/LICENSE +0 -0
- {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.2.dist-info}/WHEEL +0 -0
- {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.2.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.2.dist-info}/top_level.txt +0 -0
|
@@ -3,7 +3,6 @@ import pathlib
|
|
|
3
3
|
import pprint
|
|
4
4
|
import time
|
|
5
5
|
from datetime import datetime
|
|
6
|
-
from typing import Literal
|
|
7
6
|
|
|
8
7
|
import requests
|
|
9
8
|
|
|
@@ -13,43 +12,49 @@ from mx_bluesky.beamlines.i24.serial.parameters import (
|
|
|
13
12
|
ExtruderParameters,
|
|
14
13
|
FixedTargetParameters,
|
|
15
14
|
)
|
|
16
|
-
from mx_bluesky.beamlines.i24.serial.setup_beamline import Eiger, caget
|
|
15
|
+
from mx_bluesky.beamlines.i24.serial.setup_beamline import Eiger, caget
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
def call_nexgen(
|
|
20
19
|
chip_prog_dict: dict | None,
|
|
21
|
-
start_time: datetime,
|
|
22
20
|
parameters: ExtruderParameters | FixedTargetParameters,
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
wavelength_in_a: float,
|
|
22
|
+
beam_center_in_pix: tuple[float, float],
|
|
23
|
+
start_time: datetime | None = None,
|
|
25
24
|
):
|
|
26
|
-
|
|
27
|
-
print(f"det_type: {det_type}")
|
|
25
|
+
"""Call the nexus writer by sending a request to nexgen-server.
|
|
28
26
|
|
|
27
|
+
Args:
|
|
28
|
+
chip_prog_dict (dict | None): Dictionary containing most of the information \
|
|
29
|
+
passed to the program runner for the collection. Only used for fixed target.
|
|
30
|
+
start_time
|
|
31
|
+
parameters (SerialAndLaserExperiment): Collection parameters.
|
|
32
|
+
wavelength_in_a (float): Wavelength, in A.
|
|
33
|
+
beam_center_in_pix (list[float]): Beam center position on detector, in pixels.
|
|
34
|
+
start_time (datetime, optional): Collection start time.
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
ValueError: For a wrong experiment type passed (either unknwon or not matched \
|
|
38
|
+
to parameter model).
|
|
39
|
+
|
|
40
|
+
"""
|
|
29
41
|
current_chip_map = None
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
pump_status = parameters.pump_status
|
|
44
|
-
else:
|
|
45
|
-
raise ValueError(f"{expt_type=} not recognised")
|
|
42
|
+
match parameters:
|
|
43
|
+
case FixedTargetParameters():
|
|
44
|
+
if not (
|
|
45
|
+
parameters.map_type == MappingType.NoMap
|
|
46
|
+
or parameters.chip.chip_type == ChipType.Custom
|
|
47
|
+
):
|
|
48
|
+
# For nexgen >= 0.9.10
|
|
49
|
+
current_chip_map = parameters.chip_map
|
|
50
|
+
pump_status = bool(parameters.pump_repeat)
|
|
51
|
+
total_numb_imgs = parameters.total_num_images
|
|
52
|
+
case ExtruderParameters():
|
|
53
|
+
total_numb_imgs = parameters.num_images
|
|
54
|
+
pump_status = parameters.pump_status
|
|
46
55
|
|
|
47
|
-
filename_prefix =
|
|
48
|
-
meta_h5 =
|
|
49
|
-
pathlib.Path(parameters.visit)
|
|
50
|
-
/ parameters.directory
|
|
51
|
-
/ f"{filename_prefix}_meta.h5"
|
|
52
|
-
)
|
|
56
|
+
filename_prefix = parameters.filename
|
|
57
|
+
meta_h5 = parameters.visit / parameters.directory / f"{filename_prefix}_meta.h5"
|
|
53
58
|
t0 = time.time()
|
|
54
59
|
max_wait = 60 # seconds
|
|
55
60
|
SSX_LOGGER.info(f"Watching for {meta_h5}")
|
|
@@ -62,44 +67,38 @@ def call_nexgen(
|
|
|
62
67
|
time.sleep(1)
|
|
63
68
|
if not meta_h5.exists():
|
|
64
69
|
SSX_LOGGER.warning(f"Giving up waiting for {meta_h5} after {max_wait} seconds")
|
|
65
|
-
return
|
|
70
|
+
return
|
|
66
71
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
SSX_LOGGER.debug(
|
|
72
|
-
f"Call to nexgen server with the following chip definition: \n{chip_prog_dict}"
|
|
73
|
-
)
|
|
72
|
+
bit_depth = int(caget(Eiger.pv.bit_depth))
|
|
73
|
+
SSX_LOGGER.debug(
|
|
74
|
+
f"Call to nexgen server with the following chip definition: \n{chip_prog_dict}"
|
|
75
|
+
)
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
access_token = pathlib.Path("/scratch/ssx_nexgen.key").read_text().strip()
|
|
78
|
+
url = "https://ssx-nexgen.diamond.ac.uk/ssx_eiger/write"
|
|
79
|
+
headers = {"Authorization": f"Bearer {access_token}"}
|
|
78
80
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
# the following will raise an error if the request was unsuccessful
|
|
104
|
-
return response.status_code == requests.codes.ok
|
|
105
|
-
return False
|
|
81
|
+
payload = {
|
|
82
|
+
"beamline": "i24",
|
|
83
|
+
"beam_center": beam_center_in_pix,
|
|
84
|
+
"chipmap": current_chip_map,
|
|
85
|
+
"chip_info": chip_prog_dict,
|
|
86
|
+
"det_dist": parameters.detector_distance_mm,
|
|
87
|
+
"exp_time": parameters.exposure_time_s,
|
|
88
|
+
"expt_type": parameters.nexgen_experiment_type,
|
|
89
|
+
"filename": filename_prefix,
|
|
90
|
+
"num_imgs": total_numb_imgs,
|
|
91
|
+
"pump_status": pump_status,
|
|
92
|
+
"pump_exp": parameters.laser_dwell_s,
|
|
93
|
+
"pump_delay": parameters.laser_delay_s,
|
|
94
|
+
"transmission": parameters.transmission,
|
|
95
|
+
"visitpath": os.fspath(meta_h5.parent),
|
|
96
|
+
"wavelength": wavelength_in_a,
|
|
97
|
+
"bit_depth": bit_depth,
|
|
98
|
+
"start_time": start_time,
|
|
99
|
+
}
|
|
100
|
+
SSX_LOGGER.info(f"Sending POST request to {url} with payload:")
|
|
101
|
+
SSX_LOGGER.info(pprint.pformat(payload))
|
|
102
|
+
response = requests.post(url, headers=headers, json=payload)
|
|
103
|
+
response.raise_for_status()
|
|
104
|
+
SSX_LOGGER.info(f"Response: {response.text} (status code: {response.status_code})")
|
|
@@ -103,12 +103,12 @@ class MxBlueskyParameters(BaseModel):
|
|
|
103
103
|
|
|
104
104
|
@field_validator("parameter_model_version")
|
|
105
105
|
@classmethod
|
|
106
|
-
def _validate_version(cls, version:
|
|
106
|
+
def _validate_version(cls, version: SemanticVersion):
|
|
107
107
|
assert (
|
|
108
|
-
version >=
|
|
108
|
+
version >= SemanticVersion(major=PARAMETER_VERSION.major)
|
|
109
109
|
), f"Parameter version too old! This version of hyperion uses {PARAMETER_VERSION}"
|
|
110
110
|
assert (
|
|
111
|
-
version <=
|
|
111
|
+
version <= SemanticVersion(major=PARAMETER_VERSION.major + 1)
|
|
112
112
|
), f"Parameter version too new! This version of hyperion uses {PARAMETER_VERSION}"
|
|
113
113
|
return version
|
|
114
114
|
|
|
@@ -19,6 +19,7 @@ class DocDescriptorNames:
|
|
|
19
19
|
OAV_GRID_SNAPSHOT_TRIGGERED = "snapshot_to_ispyb"
|
|
20
20
|
HARDWARE_READ_PRE = "read_hardware_for_callbacks_pre_collection"
|
|
21
21
|
HARDWARE_READ_DURING = "read_hardware_for_callbacks_during_collection"
|
|
22
|
+
SAMPLE_HANDLING_EXCEPTION = "sample_handling_exception"
|
|
22
23
|
ZOCALO_HW_READ = "zocalo_read_hardware_plan"
|
|
23
24
|
FLYSCAN_RESULTS = "flyscan_results_obtained"
|
|
24
25
|
|
|
@@ -36,6 +37,7 @@ class OavConstants:
|
|
|
36
37
|
|
|
37
38
|
@dataclass(frozen=True)
|
|
38
39
|
class PlanNameConstants:
|
|
40
|
+
LOAD_CENTRE_COLLECT = "load_centre_collect"
|
|
39
41
|
# Robot load subplan
|
|
40
42
|
ROBOT_LOAD = "robot_load"
|
|
41
43
|
# Gridscan
|
|
@@ -45,6 +47,9 @@ class PlanNameConstants:
|
|
|
45
47
|
GRIDSCAN_AND_MOVE = "run_gridscan_and_move"
|
|
46
48
|
GRIDSCAN_MAIN = "run_gridscan"
|
|
47
49
|
DO_FGS = "do_fgs"
|
|
50
|
+
# IspyB callback activation
|
|
51
|
+
ISPYB_ACTIVATION = "ispyb_activation"
|
|
52
|
+
ROBOT_LOAD_AND_SNAPSHOTS = "robot_load_and_snapshots"
|
|
48
53
|
# Rotation scan
|
|
49
54
|
ROTATION_MULTI = "multi_rotation_wrapper"
|
|
50
55
|
ROTATION_OUTER = "rotation_scan_with_cleanup"
|
|
@@ -19,6 +19,7 @@ from mx_bluesky.hyperion.utils.utils import (
|
|
|
19
19
|
|
|
20
20
|
MIRROR_VOLTAGE_GROUP = "MIRROR_VOLTAGE_GROUP"
|
|
21
21
|
DCM_GROUP = "DCM_GROUP"
|
|
22
|
+
YAW_LAT_TIMEOUT_S = 30
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
def _apply_and_wait_for_voltages_to_settle(
|
|
@@ -46,31 +47,42 @@ def _apply_and_wait_for_voltages_to_settle(
|
|
|
46
47
|
for voltage_channel, required_voltage in zip(
|
|
47
48
|
channels.values(), required_voltages, strict=True
|
|
48
49
|
):
|
|
49
|
-
LOGGER.
|
|
50
|
+
LOGGER.info(
|
|
50
51
|
f"Applying and waiting for voltage {voltage_channel.name} = {required_voltage}"
|
|
51
52
|
)
|
|
52
53
|
yield from bps.abs_set(
|
|
53
|
-
voltage_channel, required_voltage, group=MIRROR_VOLTAGE_GROUP
|
|
54
|
+
voltage_channel, required_voltage, group=MIRROR_VOLTAGE_GROUP, wait=True
|
|
54
55
|
)
|
|
55
56
|
|
|
56
|
-
yield from bps.wait(group=MIRROR_VOLTAGE_GROUP)
|
|
57
|
-
|
|
58
57
|
|
|
59
58
|
def adjust_mirror_stripe(
|
|
60
59
|
energy_kev, mirror: FocusingMirrorWithStripes, mirror_voltages: MirrorVoltages
|
|
61
60
|
):
|
|
62
61
|
"""Feedback should be OFF prior to entry, in order to prevent
|
|
63
62
|
feedback from making unnecessary corrections while beam is being adjusted."""
|
|
64
|
-
|
|
63
|
+
mirror_config = mirror.energy_to_stripe(energy_kev)
|
|
65
64
|
|
|
66
65
|
LOGGER.info(
|
|
67
|
-
f"Adjusting mirror stripe for {energy_kev}keV selecting {stripe} stripe"
|
|
66
|
+
f"Adjusting mirror stripe for {energy_kev}keV selecting {mirror_config['stripe']} stripe"
|
|
68
67
|
)
|
|
69
|
-
yield from bps.abs_set(mirror.stripe, stripe, wait=True)
|
|
68
|
+
yield from bps.abs_set(mirror.stripe, mirror_config["stripe"], wait=True)
|
|
70
69
|
yield from bps.trigger(mirror.apply_stripe)
|
|
71
70
|
|
|
71
|
+
# yaw, lat cannot be done simultaneously
|
|
72
|
+
LOGGER.info(f"Adjusting {mirror.name} lat to {mirror_config['lat_mm']}")
|
|
73
|
+
yield from bps.abs_set(
|
|
74
|
+
mirror.x_mm, mirror_config["lat_mm"], wait=True, timeout=YAW_LAT_TIMEOUT_S
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
LOGGER.info(f"Adjusting {mirror.name} yaw to {mirror_config['yaw_mrad']}")
|
|
78
|
+
yield from bps.abs_set(
|
|
79
|
+
mirror.yaw_mrad, mirror_config["yaw_mrad"], wait=True, timeout=YAW_LAT_TIMEOUT_S
|
|
80
|
+
)
|
|
81
|
+
|
|
72
82
|
LOGGER.info("Adjusting mirror voltages...")
|
|
73
|
-
yield from _apply_and_wait_for_voltages_to_settle(
|
|
83
|
+
yield from _apply_and_wait_for_voltages_to_settle(
|
|
84
|
+
mirror_config["stripe"], mirror_voltages
|
|
85
|
+
)
|
|
74
86
|
|
|
75
87
|
|
|
76
88
|
def adjust_dcm_pitch_roll_vfm_from_lut(
|
|
@@ -109,31 +121,9 @@ def adjust_dcm_pitch_roll_vfm_from_lut(
|
|
|
109
121
|
yield from dcm_roll_adjuster(DCM_GROUP)
|
|
110
122
|
LOGGER.info("Waiting for DCM roll adjust to complete...")
|
|
111
123
|
|
|
112
|
-
# DCM Perp pitch
|
|
113
|
-
offset_mm = undulator_dcm.dcm_fixed_offset_mm
|
|
114
|
-
LOGGER.info(f"Adjusting DCM offset to {offset_mm} mm")
|
|
115
|
-
yield from bps.abs_set(dcm.offset_in_mm, offset_mm, group=DCM_GROUP)
|
|
116
|
-
|
|
117
124
|
#
|
|
118
|
-
# Adjust
|
|
125
|
+
# Adjust vfm mirror stripe and mirror voltages
|
|
119
126
|
#
|
|
120
127
|
|
|
121
|
-
# No need to change HFM
|
|
122
|
-
|
|
123
|
-
# Assumption is focus mode is already set to "sample"
|
|
124
|
-
# not sure how we check this
|
|
125
|
-
|
|
126
128
|
# VFM Stripe selection
|
|
127
129
|
yield from adjust_mirror_stripe(energy_kev, vfm, mirror_voltages)
|
|
128
|
-
yield from bps.wait(DCM_GROUP)
|
|
129
|
-
|
|
130
|
-
# VFM Adjust - for I03 this table always returns the same value
|
|
131
|
-
vfm_lut = vfm.bragg_to_lat_lookup_table_path
|
|
132
|
-
assert vfm_lut is not None
|
|
133
|
-
vfm_x_adjuster = lookup_table_adjuster(
|
|
134
|
-
linear_interpolation_lut(vfm_lut),
|
|
135
|
-
vfm.x_mm,
|
|
136
|
-
bragg_deg,
|
|
137
|
-
)
|
|
138
|
-
LOGGER.info("Waiting for VFM Lat (Horizontal Translation) to complete...")
|
|
139
|
-
yield from vfm_x_adjuster()
|
|
@@ -78,11 +78,11 @@ def move_x_y_z(
|
|
|
78
78
|
axes are optional."""
|
|
79
79
|
|
|
80
80
|
LOGGER.info(f"Moving smargon to x, y, z: {(x_mm, y_mm, z_mm)}")
|
|
81
|
-
if x_mm:
|
|
81
|
+
if x_mm is not None:
|
|
82
82
|
yield from bps.abs_set(smargon.x, x_mm, group=group)
|
|
83
|
-
if y_mm:
|
|
83
|
+
if y_mm is not None:
|
|
84
84
|
yield from bps.abs_set(smargon.y, y_mm, group=group)
|
|
85
|
-
if z_mm:
|
|
85
|
+
if z_mm is not None:
|
|
86
86
|
yield from bps.abs_set(smargon.z, z_mm, group=group)
|
|
87
87
|
if wait:
|
|
88
88
|
yield from bps.wait(group)
|
|
@@ -100,11 +100,11 @@ def move_phi_chi_omega(
|
|
|
100
100
|
axes are optional."""
|
|
101
101
|
|
|
102
102
|
LOGGER.info(f"Moving smargon to phi, chi, omega: {(phi, chi, omega)}")
|
|
103
|
-
if phi:
|
|
103
|
+
if phi is not None:
|
|
104
104
|
yield from bps.abs_set(smargon.phi, phi, group=group)
|
|
105
|
-
if chi:
|
|
105
|
+
if chi is not None:
|
|
106
106
|
yield from bps.abs_set(smargon.chi, chi, group=group)
|
|
107
|
-
if omega:
|
|
107
|
+
if omega is not None:
|
|
108
108
|
yield from bps.abs_set(smargon.omega, omega, group=group)
|
|
109
109
|
if wait:
|
|
110
110
|
yield from bps.wait(group)
|
|
@@ -2,17 +2,17 @@ import numpy as np
|
|
|
2
2
|
from bluesky import plan_stubs as bps
|
|
3
3
|
from dodal.devices.smargon import Smargon
|
|
4
4
|
|
|
5
|
-
from mx_bluesky.hyperion.exceptions import
|
|
5
|
+
from mx_bluesky.hyperion.exceptions import SampleException
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def move_smargon_warn_on_out_of_range(
|
|
9
9
|
smargon: Smargon, position: np.ndarray | list[float] | tuple[float, float, float]
|
|
10
10
|
):
|
|
11
|
-
"""Throws a
|
|
11
|
+
"""Throws a SampleException if the specified position is out of range for the
|
|
12
12
|
smargon. Otherwise moves to that position."""
|
|
13
13
|
limits = yield from smargon.get_xyz_limits()
|
|
14
14
|
if not limits.position_valid(position):
|
|
15
|
-
raise
|
|
15
|
+
raise SampleException(
|
|
16
16
|
"Pin tip centring failed - pin too long/short/bent and out of range"
|
|
17
17
|
)
|
|
18
18
|
yield from bps.mv(
|
|
@@ -13,9 +13,21 @@ class WarningException(Exception):
|
|
|
13
13
|
pass
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
class SampleException(WarningException):
|
|
17
|
+
"""An exception which identifies an issue relating to the sample."""
|
|
18
|
+
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
16
22
|
T = TypeVar("T")
|
|
17
23
|
|
|
18
24
|
|
|
25
|
+
class CrystalNotFoundException(SampleException):
|
|
26
|
+
"""Raised if grid detection completed normally but no crystal was found."""
|
|
27
|
+
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
19
31
|
def catch_exception_and_warn(
|
|
20
32
|
exception_to_catch: type[Exception],
|
|
21
33
|
func: Callable[..., Generator[Msg, None, T]],
|
|
@@ -36,7 +48,7 @@ def catch_exception_and_warn(
|
|
|
36
48
|
|
|
37
49
|
def warn_if_exception_matches(exception: Exception):
|
|
38
50
|
if isinstance(exception, exception_to_catch):
|
|
39
|
-
raise
|
|
51
|
+
raise SampleException(str(exception)) from exception
|
|
40
52
|
yield from null()
|
|
41
53
|
|
|
42
54
|
return (
|
|
@@ -66,7 +66,7 @@ from mx_bluesky.hyperion.device_setup_plans.setup_zebra import (
|
|
|
66
66
|
from mx_bluesky.hyperion.device_setup_plans.xbpm_feedback import (
|
|
67
67
|
transmission_and_xbpm_feedback_for_collection_decorator,
|
|
68
68
|
)
|
|
69
|
-
from mx_bluesky.hyperion.exceptions import
|
|
69
|
+
from mx_bluesky.hyperion.exceptions import CrystalNotFoundException, SampleException
|
|
70
70
|
from mx_bluesky.hyperion.experiment_plans.change_aperture_then_move_plan import (
|
|
71
71
|
change_aperture_then_move_to_xtal,
|
|
72
72
|
)
|
|
@@ -79,14 +79,10 @@ from mx_bluesky.hyperion.parameters.constants import CONST
|
|
|
79
79
|
from mx_bluesky.hyperion.parameters.gridscan import HyperionThreeDGridScan
|
|
80
80
|
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
class SmargonSpeedException(Exception):
|
|
84
|
-
pass
|
|
82
|
+
ZOCALO_MIN_TOTAL_COUNT_THRESHOLD = 3
|
|
85
83
|
|
|
86
84
|
|
|
87
|
-
class
|
|
88
|
-
"""Raised if grid detection completed normally but no crystal was found."""
|
|
89
|
-
|
|
85
|
+
class SmargonSpeedException(Exception):
|
|
90
86
|
pass
|
|
91
87
|
|
|
92
88
|
|
|
@@ -253,10 +249,20 @@ def run_gridscan_and_fetch_results(
|
|
|
253
249
|
LOGGER.info("Zocalo triggered and read, interpreting results.")
|
|
254
250
|
xrc_results = yield from get_full_processing_results(fgs_composite.zocalo)
|
|
255
251
|
LOGGER.info(f"Got xray centres, top 5: {xrc_results[:5]}")
|
|
256
|
-
|
|
252
|
+
filtered_results = [
|
|
253
|
+
result
|
|
254
|
+
for result in xrc_results
|
|
255
|
+
if result["total_count"] >= ZOCALO_MIN_TOTAL_COUNT_THRESHOLD
|
|
256
|
+
]
|
|
257
|
+
discarded_count = len(xrc_results) - len(filtered_results)
|
|
258
|
+
if discarded_count > 0:
|
|
259
|
+
LOGGER.info(
|
|
260
|
+
f"Removed {discarded_count} results because below threshold"
|
|
261
|
+
)
|
|
262
|
+
if filtered_results:
|
|
257
263
|
flyscan_results = [
|
|
258
264
|
_xrc_result_in_boxes_to_result_in_mm(xr, parameters)
|
|
259
|
-
for xr in
|
|
265
|
+
for xr in filtered_results
|
|
260
266
|
]
|
|
261
267
|
else:
|
|
262
268
|
LOGGER.warning("No X-ray centre received")
|
|
@@ -378,7 +384,7 @@ def wait_for_gridscan_valid(fgs_motors: FastGridScanCommon, timeout=0.5):
|
|
|
378
384
|
LOGGER.info("Gridscan scan valid and position counter reset")
|
|
379
385
|
return
|
|
380
386
|
yield from bps.sleep(SLEEP_PER_CHECK)
|
|
381
|
-
raise
|
|
387
|
+
raise SampleException("Scan invalid - pin too long/short/bent and out of range")
|
|
382
388
|
|
|
383
389
|
|
|
384
390
|
@dataclasses.dataclass
|
|
@@ -144,14 +144,6 @@ def detect_grid_and_do_gridscan(
|
|
|
144
144
|
parameters.box_size_um,
|
|
145
145
|
)
|
|
146
146
|
|
|
147
|
-
if parameters.selected_aperture:
|
|
148
|
-
# Start moving the aperture/scatterguard into position without moving it in
|
|
149
|
-
yield from bps.abs_set(
|
|
150
|
-
composite.aperture_scatterguard.aperture_outside_beam,
|
|
151
|
-
parameters.selected_aperture,
|
|
152
|
-
group=CONST.WAIT.GRID_READY_FOR_DC,
|
|
153
|
-
)
|
|
154
|
-
|
|
155
147
|
yield from run_grid_detection_plan(
|
|
156
148
|
oav_params,
|
|
157
149
|
snapshot_template,
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from collections.abc import Sequence
|
|
2
4
|
|
|
3
5
|
import pydantic
|
|
4
|
-
from blueapi.core import BlueskyContext
|
|
5
|
-
from bluesky.preprocessors import subs_wrapper
|
|
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
|
|
6
9
|
from dodal.devices.oav.oav_parameters import OAVParameters
|
|
7
10
|
from dodal.devices.smargon import Smargon
|
|
8
11
|
|
|
@@ -19,7 +22,11 @@ from mx_bluesky.hyperion.experiment_plans.rotation_scan_plan import (
|
|
|
19
22
|
RotationScanComposite,
|
|
20
23
|
multi_rotation_scan,
|
|
21
24
|
)
|
|
25
|
+
from mx_bluesky.hyperion.external_interaction.callbacks.sample_handling.sample_handling_callback import (
|
|
26
|
+
sample_handling_callback_decorator,
|
|
27
|
+
)
|
|
22
28
|
from mx_bluesky.hyperion.log import LOGGER
|
|
29
|
+
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
23
30
|
from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect
|
|
24
31
|
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
25
32
|
|
|
@@ -53,36 +60,53 @@ def load_centre_collect_full(
|
|
|
53
60
|
if not oav_params:
|
|
54
61
|
oav_params = OAVParameters(context="xrayCentring")
|
|
55
62
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
assert (
|
|
63
|
-
flyscan_event_handler.xray_centre_results
|
|
64
|
-
), "Flyscan result event not received or no crystal found and exception not raised"
|
|
65
|
-
|
|
66
|
-
selection_func = flyscan_result.resolve_selection_fn(parameters.selection_params)
|
|
67
|
-
hits: Sequence[flyscan_result.XRayCentreResult] = selection_func(
|
|
68
|
-
flyscan_event_handler.xray_centre_results
|
|
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
69
|
)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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()
|
|
@@ -3,7 +3,7 @@ from typing import Protocol
|
|
|
3
3
|
|
|
4
4
|
from bluesky import plan_stubs as bps
|
|
5
5
|
from bluesky.utils import MsgGenerator
|
|
6
|
-
from dodal.devices.aperturescatterguard import ApertureScatterguard
|
|
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
|
|
@@ -33,16 +33,22 @@ def setup_beamline_for_OAV(
|
|
|
33
33
|
max_vel = yield from bps.rd(smargon.omega.max_velocity)
|
|
34
34
|
yield from bps.abs_set(smargon.omega.velocity, max_vel, group=group)
|
|
35
35
|
yield from bps.abs_set(backlight, BacklightPosition.IN, group=group)
|
|
36
|
-
yield from bps.
|
|
36
|
+
yield from bps.abs_set(
|
|
37
|
+
aperture_scatterguard,
|
|
38
|
+
ApertureValue.ROBOT_LOAD,
|
|
39
|
+
group=group,
|
|
40
|
+
)
|
|
37
41
|
|
|
38
42
|
|
|
39
43
|
def oav_snapshot_plan(
|
|
40
44
|
composite: OavSnapshotComposite,
|
|
41
45
|
parameters: WithSnapshot,
|
|
42
46
|
oav_parameters: OAVParameters,
|
|
47
|
+
wait: bool = True,
|
|
43
48
|
) -> MsgGenerator:
|
|
44
49
|
if not parameters.take_snapshots:
|
|
45
50
|
return
|
|
51
|
+
yield from bps.wait(group=CONST.WAIT.READY_FOR_OAV)
|
|
46
52
|
yield from _setup_oav(composite, parameters, oav_parameters)
|
|
47
53
|
for omega in parameters.snapshot_omegas_deg or []:
|
|
48
54
|
yield from _take_oav_snapshot(composite, omega)
|
|
@@ -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
|
|
@@ -68,7 +68,7 @@ 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
|
|
@@ -105,7 +105,7 @@ def move_pin_into_view(
|
|
|
105
105
|
tip_xy_px = yield from trigger_and_return_pin_tip(pin_tip_device)
|
|
106
106
|
|
|
107
107
|
if not pin_tip_valid(tip_xy_px):
|
|
108
|
-
raise
|
|
108
|
+
raise SampleException(
|
|
109
109
|
"Pin tip centring failed - pin too long/short/bent and out of range"
|
|
110
110
|
)
|
|
111
111
|
else:
|