mx-bluesky 1.4.6__py3-none-any.whl → 1.4.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mx_bluesky/_version.py +2 -2
- mx_bluesky/beamlines/aithre_lasershaping/__init__.py +13 -0
- mx_bluesky/beamlines/aithre_lasershaping/check_goniometer_performance.py +29 -0
- mx_bluesky/beamlines/aithre_lasershaping/goniometer_controls.py +18 -0
- mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +35 -29
- mx_bluesky/beamlines/i04/thawing_plan.py +18 -3
- mx_bluesky/beamlines/i23/__init__.py +3 -0
- mx_bluesky/beamlines/i23/serial.py +71 -0
- mx_bluesky/beamlines/i24/serial/__init__.py +2 -0
- mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +12 -12
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +36 -30
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +3 -3
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +15 -66
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +8 -10
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +10 -3
- mx_bluesky/beamlines/i24/serial/log.py +9 -9
- mx_bluesky/beamlines/i24/serial/parameters/utils.py +36 -7
- mx_bluesky/beamlines/i24/serial/set_visit_directory.sh +1 -1
- mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +16 -17
- mx_bluesky/beamlines/i24/serial/setup_beamline/pv_abstract.py +4 -4
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +51 -52
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +3 -2
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +9 -7
- mx_bluesky/beamlines/i24/serial/web_gui_plans/general_plans.py +71 -11
- mx_bluesky/beamlines/i24/serial/write_nexus.py +6 -5
- mx_bluesky/{hyperion → common}/device_setup_plans/check_beamstop.py +1 -1
- mx_bluesky/{hyperion → common}/device_setup_plans/manipulate_sample.py +1 -1
- mx_bluesky/{hyperion → common}/device_setup_plans/setup_oav.py +12 -6
- mx_bluesky/common/device_setup_plans/xbpm_feedback.py +45 -0
- mx_bluesky/{hyperion → common}/experiment_plans/change_aperture_then_move_plan.py +13 -29
- mx_bluesky/{hyperion → common}/experiment_plans/oav_grid_detection_plan.py +6 -6
- mx_bluesky/common/external_interaction/callbacks/common/ispyb_callback_base.py +8 -9
- mx_bluesky/common/external_interaction/callbacks/common/ispyb_mapping.py +1 -1
- mx_bluesky/common/external_interaction/callbacks/common/zocalo_callback.py +18 -15
- mx_bluesky/{hyperion → common}/external_interaction/callbacks/sample_handling/sample_handling_callback.py +16 -4
- mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py +50 -45
- mx_bluesky/common/external_interaction/callbacks/xray_centre/nexus_callback.py +2 -1
- mx_bluesky/common/external_interaction/ispyb/data_model.py +1 -0
- mx_bluesky/common/external_interaction/ispyb/ispyb_store.py +18 -2
- mx_bluesky/common/external_interaction/ispyb/ispyb_utils.py +4 -4
- mx_bluesky/common/external_interaction/nexus/nexus_utils.py +1 -1
- mx_bluesky/common/parameters/components.py +22 -2
- mx_bluesky/common/parameters/constants.py +6 -16
- mx_bluesky/common/parameters/gridscan.py +36 -32
- mx_bluesky/common/plans/common_flyscan_xray_centre_plan.py +316 -0
- mx_bluesky/common/plans/inner_plans/__init__ .py +0 -0
- mx_bluesky/common/plans/read_hardware.py +3 -3
- mx_bluesky/common/plans/write_sample_status.py +46 -0
- mx_bluesky/common/preprocessors/__init__.py +0 -0
- mx_bluesky/common/preprocessors/preprocessors.py +105 -0
- mx_bluesky/common/protocols/__init__.py +0 -0
- mx_bluesky/common/protocols/protocols.py +10 -0
- mx_bluesky/common/utils/log.py +15 -12
- mx_bluesky/hyperion/__main__.py +5 -24
- mx_bluesky/hyperion/baton_handler.py +84 -0
- mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +4 -4
- mx_bluesky/hyperion/device_setup_plans/setup_panda.py +5 -1
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +0 -33
- mx_bluesky/hyperion/device_setup_plans/utils.py +4 -4
- mx_bluesky/hyperion/experiment_plans/__init__.py +0 -10
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +0 -16
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +71 -88
- mx_bluesky/hyperion/experiment_plans/hyperion_flyscan_xray_centre_plan.py +183 -0
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +17 -8
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +29 -8
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +4 -4
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +6 -4
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +11 -3
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +9 -34
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +35 -68
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +27 -8
- mx_bluesky/hyperion/external_interaction/agamemnon.py +140 -10
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +17 -9
- mx_bluesky/hyperion/external_interaction/callbacks/snapshot_callback.py +259 -0
- mx_bluesky/hyperion/parameters/cli.py +2 -10
- mx_bluesky/hyperion/parameters/constants.py +0 -5
- mx_bluesky/hyperion/parameters/device_composites.py +40 -5
- mx_bluesky/hyperion/parameters/gridscan.py +9 -58
- mx_bluesky/hyperion/parameters/rotation.py +1 -5
- mx_bluesky/hyperion/utils/context.py +2 -5
- mx_bluesky/hyperion/utils/validation.py +13 -10
- {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.8.dist-info}/METADATA +10 -9
- {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.8.dist-info}/RECORD +92 -79
- {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.8.dist-info}/WHEEL +1 -1
- mx_bluesky/common/external_interaction/callbacks/common/aperture_change_callback.py +0 -22
- mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +0 -103
- mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +0 -466
- /mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/{short1-laser.png → s1l.png} +0 -0
- /mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/{short2-laser.png → s2l.png} +0 -0
- /mx_bluesky/{hyperion → common}/device_setup_plans/position_detector.py +0 -0
- /mx_bluesky/{hyperion → common}/external_interaction/callbacks/sample_handling/__init__.py +0 -0
- /mx_bluesky/common/plans/{do_fgs.py → inner_plans/do_fgs.py} +0 -0
- {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.8.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.8.dist-info/licenses}/LICENSE +0 -0
- {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.8.dist-info}/top_level.txt +0 -0
|
@@ -5,7 +5,7 @@ from abc import abstractmethod
|
|
|
5
5
|
from collections.abc import Sequence
|
|
6
6
|
from enum import StrEnum
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Literal, SupportsInt, cast
|
|
8
|
+
from typing import Literal, Self, SupportsInt, cast
|
|
9
9
|
|
|
10
10
|
from dodal.devices.aperturescatterguard import ApertureValue
|
|
11
11
|
from dodal.devices.detector import (
|
|
@@ -114,12 +114,32 @@ class MxBlueskyParameters(BaseModel):
|
|
|
114
114
|
|
|
115
115
|
|
|
116
116
|
class WithSnapshot(BaseModel):
|
|
117
|
+
"""
|
|
118
|
+
Configures how snapshot images are created.
|
|
119
|
+
Attributes:
|
|
120
|
+
snapshot_directory: Path to the directory where snapshot images will be stored
|
|
121
|
+
snapshot_omegas_deg: list of omega values at which snapshots will be taken. For
|
|
122
|
+
gridscans, this attribute is ignored.
|
|
123
|
+
use_grid_snapshots: This may be specified for rotation snapshots to speed up rotation
|
|
124
|
+
execution. If set to True then rotation snapshots are generated from the
|
|
125
|
+
previously captured grid snapshots. Otherwise they are captured using
|
|
126
|
+
freshly captured snapshots during the rotation plan.
|
|
127
|
+
"""
|
|
128
|
+
|
|
117
129
|
snapshot_directory: Path
|
|
118
130
|
snapshot_omegas_deg: list[float] | None = None
|
|
131
|
+
use_grid_snapshots: bool = False
|
|
119
132
|
|
|
120
133
|
@property
|
|
121
134
|
def take_snapshots(self) -> bool:
|
|
122
|
-
return bool(self.snapshot_omegas_deg)
|
|
135
|
+
return bool(self.snapshot_omegas_deg) or self.use_grid_snapshots
|
|
136
|
+
|
|
137
|
+
@model_validator(mode="after")
|
|
138
|
+
def _validate_omegas_with_grid_snapshots(self) -> Self:
|
|
139
|
+
assert not self.use_grid_snapshots or self.snapshot_omegas_deg is None, (
|
|
140
|
+
"snapshot_omegas may not be specified with use_grid_snapshots"
|
|
141
|
+
)
|
|
142
|
+
return self
|
|
123
143
|
|
|
124
144
|
|
|
125
145
|
class WithOptionalEnergyChange(BaseModel):
|
|
@@ -55,9 +55,12 @@ class PlanNameConstants:
|
|
|
55
55
|
ROBOT_LOAD_AND_SNAPSHOTS = "robot_load_and_snapshots"
|
|
56
56
|
# Rotation scan
|
|
57
57
|
ROTATION_MULTI = "multi_rotation_wrapper"
|
|
58
|
+
ROTATION_MULTI_OUTER = "multi_rotation_outer"
|
|
58
59
|
ROTATION_OUTER = "rotation_scan_with_cleanup"
|
|
59
60
|
ROTATION_MAIN = "rotation_scan_main"
|
|
60
61
|
FLYSCAN_RESULTS = "xray_centre_results"
|
|
62
|
+
SET_ENERGY = "set_energy"
|
|
63
|
+
UNNAMED_RUN = "unnamed_run"
|
|
61
64
|
|
|
62
65
|
|
|
63
66
|
@dataclass(frozen=True)
|
|
@@ -65,17 +68,12 @@ class EnvironmentConstants:
|
|
|
65
68
|
ZOCALO_ENV = ZOCALO_ENV_FROM_DODAL
|
|
66
69
|
|
|
67
70
|
|
|
68
|
-
@dataclass(frozen=True)
|
|
69
|
-
class TriggerConstants:
|
|
70
|
-
ZOCALO = "trigger_zocalo_on"
|
|
71
|
-
|
|
72
|
-
|
|
73
71
|
@dataclass(frozen=True)
|
|
74
72
|
class HardwareConstants:
|
|
75
73
|
OAV_REFRESH_DELAY = 0.3
|
|
76
74
|
PANDA_FGS_RUN_UP_DEFAULT = 0.17
|
|
77
75
|
CRYOJET_MARGIN_MM = 0.2
|
|
78
|
-
THAWING_TIME =
|
|
76
|
+
THAWING_TIME = 40
|
|
79
77
|
TIP_OFFSET_UM = 0
|
|
80
78
|
|
|
81
79
|
# Value quoted in https://www.dectris.com/en/detectors/x-ray-detectors/eiger2/eiger2-for-synchrotrons/eiger2-x/,
|
|
@@ -92,6 +90,7 @@ class GridscanParamConstants:
|
|
|
92
90
|
OMEGA_1 = 0.0
|
|
93
91
|
OMEGA_2 = 90.0
|
|
94
92
|
PANDA_RUN_UP_DISTANCE_MM = 0.2
|
|
93
|
+
ZOCALO_MIN_TOTAL_COUNT_THRESHOLD = 3
|
|
95
94
|
|
|
96
95
|
|
|
97
96
|
@dataclass(frozen=True)
|
|
@@ -123,6 +122,7 @@ class PlanGroupCheckpointConstants:
|
|
|
123
122
|
ROTATION_READY_FOR_DC = "rotation_ready_for_data_collection"
|
|
124
123
|
MOVE_GONIO_TO_START = "move_gonio_to_start"
|
|
125
124
|
READY_FOR_OAV = "ready_for_oav"
|
|
125
|
+
PREPARE_APERTURE = "prepare_aperture"
|
|
126
126
|
|
|
127
127
|
|
|
128
128
|
# Eventually replace below with https://github.com/DiamondLightSource/mx-bluesky/issues/798
|
|
@@ -134,16 +134,6 @@ class DeviceSettingsConstants:
|
|
|
134
134
|
)
|
|
135
135
|
|
|
136
136
|
|
|
137
|
-
@dataclass(frozen=True)
|
|
138
|
-
class SimConstants:
|
|
139
|
-
BEAMLINE = "BL03S"
|
|
140
|
-
INSERTION_PREFIX = "SR03S"
|
|
141
|
-
# this one is for unit tests
|
|
142
|
-
ISPYB_CONFIG = "tests/test_data/test_config.cfg"
|
|
143
|
-
# this one is for system tests
|
|
144
|
-
DEV_ISPYB_DATABASE_CFG = "/dls_sw/dasc/mariadb/credentials/ispyb-hyperion-dev.cfg"
|
|
145
|
-
|
|
146
|
-
|
|
147
137
|
class Actions(Enum):
|
|
148
138
|
START = "start"
|
|
149
139
|
STOP = "stop"
|
|
@@ -26,7 +26,11 @@ from mx_bluesky.common.parameters.constants import (
|
|
|
26
26
|
HardwareConstants,
|
|
27
27
|
)
|
|
28
28
|
|
|
29
|
-
DETECTOR_SIZE_PER_BEAMLINE = {
|
|
29
|
+
DETECTOR_SIZE_PER_BEAMLINE = {
|
|
30
|
+
"i02-1": EIGER2_X_9M_SIZE,
|
|
31
|
+
"dev": EIGER2_X_16M_SIZE,
|
|
32
|
+
"i03": EIGER2_X_16M_SIZE,
|
|
33
|
+
}
|
|
30
34
|
|
|
31
35
|
|
|
32
36
|
class GridCommon(
|
|
@@ -50,6 +54,37 @@ class GridCommon(
|
|
|
50
54
|
|
|
51
55
|
tip_offset_um: float = Field(default=HardwareConstants.TIP_OFFSET_UM)
|
|
52
56
|
|
|
57
|
+
@property
|
|
58
|
+
def detector_params(self):
|
|
59
|
+
self.det_dist_to_beam_converter_path = (
|
|
60
|
+
self.det_dist_to_beam_converter_path
|
|
61
|
+
or DetectorParamConstants.BEAM_XY_LUT_PATH
|
|
62
|
+
)
|
|
63
|
+
optional_args = {}
|
|
64
|
+
if self.run_number:
|
|
65
|
+
optional_args["run_number"] = self.run_number
|
|
66
|
+
assert self.detector_distance_mm is not None, (
|
|
67
|
+
"Detector distance must be filled before generating DetectorParams"
|
|
68
|
+
)
|
|
69
|
+
return DetectorParams(
|
|
70
|
+
detector_size_constants=DETECTOR_SIZE_PER_BEAMLINE[
|
|
71
|
+
get_beamline_name("dev")
|
|
72
|
+
],
|
|
73
|
+
expected_energy_ev=self.demand_energy_ev,
|
|
74
|
+
exposure_time_s=self.exposure_time_s,
|
|
75
|
+
directory=self.storage_directory,
|
|
76
|
+
prefix=self.file_name,
|
|
77
|
+
detector_distance=self.detector_distance_mm,
|
|
78
|
+
omega_start=self.omega_start_deg or 0,
|
|
79
|
+
omega_increment=0,
|
|
80
|
+
num_images_per_trigger=1,
|
|
81
|
+
num_triggers=self.num_images,
|
|
82
|
+
use_roi_mode=self.use_roi_mode,
|
|
83
|
+
det_dist_to_beam_converter_path=self.det_dist_to_beam_converter_path,
|
|
84
|
+
trigger_mode=self.trigger_mode,
|
|
85
|
+
**optional_args,
|
|
86
|
+
)
|
|
87
|
+
|
|
53
88
|
|
|
54
89
|
class SpecifiedGrid(XyzStarts, WithScan):
|
|
55
90
|
"""A specified grid is one which has defined values for the start position,
|
|
@@ -150,34 +185,3 @@ class SpecifiedThreeDGridScan(
|
|
|
150
185
|
@property
|
|
151
186
|
def num_images(self) -> int:
|
|
152
187
|
return len(self.scan_points["sam_x"])
|
|
153
|
-
|
|
154
|
-
@property
|
|
155
|
-
def detector_params(self):
|
|
156
|
-
self.det_dist_to_beam_converter_path = (
|
|
157
|
-
self.det_dist_to_beam_converter_path
|
|
158
|
-
or DetectorParamConstants.BEAM_XY_LUT_PATH
|
|
159
|
-
)
|
|
160
|
-
optional_args = {}
|
|
161
|
-
if self.run_number:
|
|
162
|
-
optional_args["run_number"] = self.run_number
|
|
163
|
-
assert self.detector_distance_mm is not None, (
|
|
164
|
-
"Detector distance must be filled before generating DetectorParams"
|
|
165
|
-
)
|
|
166
|
-
return DetectorParams(
|
|
167
|
-
detector_size_constants=DETECTOR_SIZE_PER_BEAMLINE[
|
|
168
|
-
get_beamline_name("dev")
|
|
169
|
-
],
|
|
170
|
-
expected_energy_ev=self.demand_energy_ev,
|
|
171
|
-
exposure_time=self.exposure_time_s,
|
|
172
|
-
directory=self.storage_directory,
|
|
173
|
-
prefix=self.file_name,
|
|
174
|
-
detector_distance=self.detector_distance_mm,
|
|
175
|
-
omega_start=self.omega_start_deg or 0,
|
|
176
|
-
omega_increment=0,
|
|
177
|
-
num_images_per_trigger=1,
|
|
178
|
-
num_triggers=self.num_images,
|
|
179
|
-
use_roi_mode=self.use_roi_mode,
|
|
180
|
-
det_dist_to_beam_converter_path=self.det_dist_to_beam_converter_path,
|
|
181
|
-
trigger_mode=self.trigger_mode,
|
|
182
|
-
**optional_args,
|
|
183
|
-
)
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
from collections.abc import Callable, Sequence
|
|
5
|
+
from functools import partial
|
|
6
|
+
|
|
7
|
+
import bluesky.plan_stubs as bps
|
|
8
|
+
import bluesky.preprocessors as bpp
|
|
9
|
+
import numpy as np
|
|
10
|
+
import pydantic
|
|
11
|
+
from bluesky.protocols import Readable
|
|
12
|
+
from bluesky.utils import MsgGenerator
|
|
13
|
+
from dodal.devices.eiger import EigerDetector
|
|
14
|
+
from dodal.devices.fast_grid_scan import (
|
|
15
|
+
FastGridScanCommon,
|
|
16
|
+
)
|
|
17
|
+
from dodal.devices.smargon import Smargon
|
|
18
|
+
from dodal.devices.synchrotron import Synchrotron
|
|
19
|
+
from dodal.devices.zocalo import ZocaloResults
|
|
20
|
+
from dodal.devices.zocalo.zocalo_results import (
|
|
21
|
+
XrcResult,
|
|
22
|
+
get_full_processing_results,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
from mx_bluesky.common.parameters.constants import (
|
|
26
|
+
DocDescriptorNames,
|
|
27
|
+
GridscanParamConstants,
|
|
28
|
+
PlanGroupCheckpointConstants,
|
|
29
|
+
PlanNameConstants,
|
|
30
|
+
)
|
|
31
|
+
from mx_bluesky.common.parameters.gridscan import SpecifiedThreeDGridScan
|
|
32
|
+
from mx_bluesky.common.plans.inner_plans.do_fgs import (
|
|
33
|
+
ZOCALO_STAGE_GROUP,
|
|
34
|
+
kickoff_and_complete_gridscan,
|
|
35
|
+
)
|
|
36
|
+
from mx_bluesky.common.plans.read_hardware import (
|
|
37
|
+
read_hardware_plan,
|
|
38
|
+
)
|
|
39
|
+
from mx_bluesky.common.utils.exceptions import (
|
|
40
|
+
CrystalNotFoundException,
|
|
41
|
+
SampleException,
|
|
42
|
+
)
|
|
43
|
+
from mx_bluesky.common.utils.log import LOGGER
|
|
44
|
+
from mx_bluesky.common.utils.tracing import TRACER
|
|
45
|
+
from mx_bluesky.common.xrc_result import XRayCentreResult
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
49
|
+
class FlyScanEssentialDevices:
|
|
50
|
+
eiger: EigerDetector
|
|
51
|
+
synchrotron: Synchrotron
|
|
52
|
+
zocalo: ZocaloResults
|
|
53
|
+
smargon: Smargon
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclasses.dataclass
|
|
57
|
+
class BeamlineSpecificFGSFeatures:
|
|
58
|
+
setup_trigger_plan: Callable[..., MsgGenerator]
|
|
59
|
+
tidy_plan: Callable[..., MsgGenerator]
|
|
60
|
+
set_flyscan_params_plan: Callable[..., MsgGenerator]
|
|
61
|
+
fgs_motors: FastGridScanCommon
|
|
62
|
+
read_pre_flyscan_plan: Callable[
|
|
63
|
+
..., MsgGenerator
|
|
64
|
+
] # Eventually replace with https://github.com/DiamondLightSource/mx-bluesky/issues/819
|
|
65
|
+
read_during_collection_plan: Callable[..., MsgGenerator]
|
|
66
|
+
get_xrc_results_from_zocalo: bool
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def construct_beamline_specific_FGS_features(
|
|
70
|
+
setup_trigger_plan: Callable[..., MsgGenerator],
|
|
71
|
+
tidy_plan: Callable[..., MsgGenerator],
|
|
72
|
+
set_flyscan_params_plan: Callable[..., MsgGenerator],
|
|
73
|
+
fgs_motors: FastGridScanCommon,
|
|
74
|
+
signals_to_read_pre_flyscan: list[Readable],
|
|
75
|
+
signals_to_read_during_collection: list[Readable],
|
|
76
|
+
get_xrc_results_from_zocalo: bool = False,
|
|
77
|
+
) -> BeamlineSpecificFGSFeatures:
|
|
78
|
+
"""Construct the class needed to do beamline-specific parts of the XRC FGS
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
setup_trigger_plan (Callable): Configure triggering, for example with the Zebra or PandA device.
|
|
82
|
+
Ran directly before kicking off the gridscan.
|
|
83
|
+
|
|
84
|
+
tidy_plan (Callable): Tidy up states of devices. Ran at the end of the flyscan, regardless of
|
|
85
|
+
whether or not it finished successfully.
|
|
86
|
+
|
|
87
|
+
set_flyscan_params_plan (Callable): Set PV's for the relevant Fast Grid Scan dodal device
|
|
88
|
+
|
|
89
|
+
fgs_motors (Callable): Composite device representing the fast grid scan's motion program parameters.
|
|
90
|
+
|
|
91
|
+
signals_to_read_pre_flyscan (Callable): Signals which will be read and saved as a bluesky event document
|
|
92
|
+
after all configuration, but before the gridscan.
|
|
93
|
+
|
|
94
|
+
signals_to_read_during_collection (Callable): Signals which will be read and saved as a bluesky event
|
|
95
|
+
document whilst the gridscan motion is in progress
|
|
96
|
+
|
|
97
|
+
get_xrc_results_from_zocalo (bool): If true, fetch grid scan results from zocalo after completion, as well as
|
|
98
|
+
update the ispyb comment field with information about the results. See _fetch_xrc_results_from_zocalo
|
|
99
|
+
"""
|
|
100
|
+
read_pre_flyscan_plan = partial(
|
|
101
|
+
read_hardware_plan,
|
|
102
|
+
signals_to_read_pre_flyscan,
|
|
103
|
+
DocDescriptorNames.HARDWARE_READ_PRE,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
read_during_collection_plan = partial(
|
|
107
|
+
read_hardware_plan,
|
|
108
|
+
signals_to_read_during_collection,
|
|
109
|
+
DocDescriptorNames.HARDWARE_READ_DURING,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
return BeamlineSpecificFGSFeatures(
|
|
113
|
+
setup_trigger_plan,
|
|
114
|
+
tidy_plan,
|
|
115
|
+
set_flyscan_params_plan,
|
|
116
|
+
fgs_motors,
|
|
117
|
+
read_pre_flyscan_plan,
|
|
118
|
+
read_during_collection_plan,
|
|
119
|
+
get_xrc_results_from_zocalo,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def common_flyscan_xray_centre(
|
|
124
|
+
composite: FlyScanEssentialDevices,
|
|
125
|
+
parameters: SpecifiedThreeDGridScan,
|
|
126
|
+
beamline_specific: BeamlineSpecificFGSFeatures,
|
|
127
|
+
) -> MsgGenerator:
|
|
128
|
+
"""Main entry point of the MX-Bluesky x-ray centering flyscan
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
composite (FlyScanEssentialDevices): Devices required to perform this plan.
|
|
132
|
+
|
|
133
|
+
parameters (SpecifiedThreeDGridScan): Parameters required to perform this plan.
|
|
134
|
+
|
|
135
|
+
beamline_specific (BeamlineSpecificFGSFeatures): Configure the beamline-specific version
|
|
136
|
+
of this plan: For example triggering setup and tidy up plans, as well as what to do with the
|
|
137
|
+
centering results.
|
|
138
|
+
|
|
139
|
+
With a minimum set of devices and parameters, prepares for; performs; and tidies up a flyscan
|
|
140
|
+
x-ray-center plan. This includes: Configuring desired triggering; writing nexus files; triggering zocalo;
|
|
141
|
+
reading hardware before and during the scan; and tidying up devices after
|
|
142
|
+
the plan is complete. Optionally fetch results from zocalo after completing the grid scan.
|
|
143
|
+
|
|
144
|
+
This plan will also push data to ispyb when used with the ispyb_activation_decorator.
|
|
145
|
+
|
|
146
|
+
There are a few other useful decorators to use with this plan, see: verify_undulator_gap_before_run_decorator, transmission_and_xbpm_feedback_for_collection_decorator
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
def _decorated_flyscan():
|
|
150
|
+
@bpp.set_run_key_decorator(PlanNameConstants.GRIDSCAN_OUTER)
|
|
151
|
+
@bpp.run_decorator( # attach experiment metadata to the start document
|
|
152
|
+
md={
|
|
153
|
+
"subplan_name": PlanNameConstants.GRIDSCAN_OUTER,
|
|
154
|
+
"mx_bluesky_parameters": parameters.model_dump_json(),
|
|
155
|
+
"activate_callbacks": [
|
|
156
|
+
"GridscanNexusFileCallback",
|
|
157
|
+
],
|
|
158
|
+
}
|
|
159
|
+
)
|
|
160
|
+
@bpp.finalize_decorator(lambda: beamline_specific.tidy_plan(composite))
|
|
161
|
+
def run_gridscan_and_tidy(
|
|
162
|
+
fgs_composite: FlyScanEssentialDevices,
|
|
163
|
+
params: SpecifiedThreeDGridScan,
|
|
164
|
+
beamline_specific: BeamlineSpecificFGSFeatures,
|
|
165
|
+
) -> MsgGenerator:
|
|
166
|
+
yield from beamline_specific.setup_trigger_plan(fgs_composite, parameters)
|
|
167
|
+
|
|
168
|
+
LOGGER.info("Starting grid scan")
|
|
169
|
+
yield from bps.stage(
|
|
170
|
+
fgs_composite.zocalo, group=ZOCALO_STAGE_GROUP
|
|
171
|
+
) # connect to zocalo and make sure the queue is clear
|
|
172
|
+
yield from run_gridscan(fgs_composite, params, beamline_specific)
|
|
173
|
+
|
|
174
|
+
LOGGER.info("Grid scan finished")
|
|
175
|
+
|
|
176
|
+
if beamline_specific.get_xrc_results_from_zocalo:
|
|
177
|
+
yield from _fetch_xrc_results_from_zocalo(composite.zocalo, parameters)
|
|
178
|
+
|
|
179
|
+
yield from run_gridscan_and_tidy(composite, parameters, beamline_specific)
|
|
180
|
+
|
|
181
|
+
composite.eiger.set_detector_parameters(parameters.detector_params)
|
|
182
|
+
yield from _decorated_flyscan()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _fetch_xrc_results_from_zocalo(
|
|
186
|
+
zocalo_results: ZocaloResults,
|
|
187
|
+
parameters: SpecifiedThreeDGridScan,
|
|
188
|
+
) -> MsgGenerator:
|
|
189
|
+
"""
|
|
190
|
+
Get XRC results from the ZocaloResults device which was staged during a grid scan,
|
|
191
|
+
and store them in XRayCentreEventHandler.xray_centre_results by firing an event.
|
|
192
|
+
|
|
193
|
+
The RunEngine must be subscribed to XRayCentreEventHandler for this plan to work.
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
LOGGER.info("Getting X-ray center Zocalo results...")
|
|
197
|
+
|
|
198
|
+
yield from bps.trigger(zocalo_results)
|
|
199
|
+
LOGGER.info("Zocalo triggered and read, interpreting results.")
|
|
200
|
+
xrc_results = yield from get_full_processing_results(zocalo_results)
|
|
201
|
+
LOGGER.info(f"Got xray centres, top 5: {xrc_results[:5]}")
|
|
202
|
+
filtered_results = [
|
|
203
|
+
result
|
|
204
|
+
for result in xrc_results
|
|
205
|
+
if result["total_count"]
|
|
206
|
+
>= GridscanParamConstants.ZOCALO_MIN_TOTAL_COUNT_THRESHOLD
|
|
207
|
+
]
|
|
208
|
+
discarded_count = len(xrc_results) - len(filtered_results)
|
|
209
|
+
if discarded_count > 0:
|
|
210
|
+
LOGGER.info(f"Removed {discarded_count} results because below threshold")
|
|
211
|
+
if filtered_results:
|
|
212
|
+
flyscan_results = [
|
|
213
|
+
_xrc_result_in_boxes_to_result_in_mm(xr, parameters)
|
|
214
|
+
for xr in filtered_results
|
|
215
|
+
]
|
|
216
|
+
else:
|
|
217
|
+
LOGGER.warning("No X-ray centre received")
|
|
218
|
+
raise CrystalNotFoundException()
|
|
219
|
+
yield from _fire_xray_centre_result_event(flyscan_results)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
@bpp.set_run_key_decorator(PlanNameConstants.GRIDSCAN_MAIN)
|
|
223
|
+
@bpp.run_decorator(md={"subplan_name": PlanNameConstants.GRIDSCAN_MAIN})
|
|
224
|
+
def run_gridscan(
|
|
225
|
+
fgs_composite: FlyScanEssentialDevices,
|
|
226
|
+
parameters: SpecifiedThreeDGridScan,
|
|
227
|
+
beamline_specific: BeamlineSpecificFGSFeatures,
|
|
228
|
+
):
|
|
229
|
+
# Currently gridscan only works for omega 0, see https://github.com/DiamondLightSource/mx-bluesky/issues/410
|
|
230
|
+
with TRACER.start_span("moving_omega_to_0"):
|
|
231
|
+
yield from bps.abs_set(fgs_composite.smargon.omega, 0)
|
|
232
|
+
|
|
233
|
+
with TRACER.start_span("ispyb_hardware_readings"):
|
|
234
|
+
yield from beamline_specific.read_pre_flyscan_plan()
|
|
235
|
+
|
|
236
|
+
LOGGER.info("Setting fgs params")
|
|
237
|
+
yield from beamline_specific.set_flyscan_params_plan()
|
|
238
|
+
|
|
239
|
+
LOGGER.info("Waiting for gridscan validity check")
|
|
240
|
+
yield from wait_for_gridscan_valid(beamline_specific.fgs_motors)
|
|
241
|
+
|
|
242
|
+
LOGGER.info("Waiting for arming to finish")
|
|
243
|
+
yield from bps.wait(PlanGroupCheckpointConstants.GRID_READY_FOR_DC)
|
|
244
|
+
yield from bps.stage(fgs_composite.eiger) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
245
|
+
|
|
246
|
+
yield from kickoff_and_complete_gridscan(
|
|
247
|
+
beamline_specific.fgs_motors,
|
|
248
|
+
fgs_composite.eiger,
|
|
249
|
+
fgs_composite.synchrotron,
|
|
250
|
+
[parameters.scan_points_first_grid, parameters.scan_points_second_grid],
|
|
251
|
+
parameters.scan_indices,
|
|
252
|
+
plan_during_collection=beamline_specific.read_during_collection_plan,
|
|
253
|
+
)
|
|
254
|
+
yield from bps.abs_set(beamline_specific.fgs_motors.z_steps, 0, wait=False)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def wait_for_gridscan_valid(fgs_motors: FastGridScanCommon, timeout=0.5):
|
|
258
|
+
LOGGER.info("Waiting for valid fgs_params")
|
|
259
|
+
SLEEP_PER_CHECK = 0.1
|
|
260
|
+
times_to_check = int(timeout / SLEEP_PER_CHECK)
|
|
261
|
+
for _ in range(times_to_check):
|
|
262
|
+
scan_invalid = yield from bps.rd(fgs_motors.scan_invalid)
|
|
263
|
+
pos_counter = yield from bps.rd(fgs_motors.position_counter)
|
|
264
|
+
LOGGER.debug(
|
|
265
|
+
f"Scan invalid: {scan_invalid} and position counter: {pos_counter}"
|
|
266
|
+
)
|
|
267
|
+
if not scan_invalid and pos_counter == 0:
|
|
268
|
+
LOGGER.info("Gridscan scan valid and position counter reset")
|
|
269
|
+
return
|
|
270
|
+
yield from bps.sleep(SLEEP_PER_CHECK)
|
|
271
|
+
raise SampleException("Scan invalid - pin too long/short/bent and out of range")
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _xrc_result_in_boxes_to_result_in_mm(
|
|
275
|
+
xrc_result: XrcResult, parameters: SpecifiedThreeDGridScan
|
|
276
|
+
) -> XRayCentreResult:
|
|
277
|
+
fgs_params = parameters.FGS_params
|
|
278
|
+
xray_centre = fgs_params.grid_position_to_motor_position(
|
|
279
|
+
np.array(xrc_result["centre_of_mass"])
|
|
280
|
+
)
|
|
281
|
+
# A correction is applied to the bounding box to map discrete grid coordinates to
|
|
282
|
+
# the corners of the box in motor-space; we do not apply this correction
|
|
283
|
+
# to the xray-centre as it is already in continuous space and the conversion has
|
|
284
|
+
# been performed already
|
|
285
|
+
# In other words, xrc_result["bounding_box"] contains the position of the box centre,
|
|
286
|
+
# so we subtract half a box to get the corner of the box
|
|
287
|
+
return XRayCentreResult(
|
|
288
|
+
centre_of_mass_mm=xray_centre,
|
|
289
|
+
bounding_box_mm=(
|
|
290
|
+
fgs_params.grid_position_to_motor_position(
|
|
291
|
+
np.array(xrc_result["bounding_box"][0]) - 0.5
|
|
292
|
+
),
|
|
293
|
+
fgs_params.grid_position_to_motor_position(
|
|
294
|
+
np.array(xrc_result["bounding_box"][1]) - 0.5
|
|
295
|
+
),
|
|
296
|
+
),
|
|
297
|
+
max_count=xrc_result["max_count"],
|
|
298
|
+
total_count=xrc_result["total_count"],
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def _fire_xray_centre_result_event(results: Sequence[XRayCentreResult]):
|
|
303
|
+
def empty_plan():
|
|
304
|
+
return iter([])
|
|
305
|
+
|
|
306
|
+
yield from bpp.set_run_key_wrapper(
|
|
307
|
+
bpp.run_wrapper(
|
|
308
|
+
empty_plan(),
|
|
309
|
+
md={
|
|
310
|
+
PlanNameConstants.FLYSCAN_RESULTS: [
|
|
311
|
+
dataclasses.asdict(r) for r in results
|
|
312
|
+
]
|
|
313
|
+
},
|
|
314
|
+
),
|
|
315
|
+
PlanNameConstants.FLYSCAN_RESULTS,
|
|
316
|
+
)
|
|
File without changes
|
|
@@ -4,7 +4,7 @@ import bluesky.plan_stubs as bps
|
|
|
4
4
|
from bluesky.protocols import Readable
|
|
5
5
|
from dodal.devices.aperturescatterguard import ApertureScatterguard
|
|
6
6
|
from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
|
|
7
|
-
from dodal.devices.
|
|
7
|
+
from dodal.devices.common_dcm import BaseDCM
|
|
8
8
|
from dodal.devices.eiger import EigerDetector
|
|
9
9
|
from dodal.devices.flux import Flux
|
|
10
10
|
from dodal.devices.s4_slit_gaps import S4SlitGaps
|
|
@@ -43,7 +43,7 @@ def standard_read_hardware_pre_collection(
|
|
|
43
43
|
undulator: Undulator,
|
|
44
44
|
synchrotron: Synchrotron,
|
|
45
45
|
s4_slit_gaps: S4SlitGaps,
|
|
46
|
-
dcm:
|
|
46
|
+
dcm: BaseDCM,
|
|
47
47
|
smargon: Smargon,
|
|
48
48
|
):
|
|
49
49
|
LOGGER.info("Reading status of beamline for callbacks, pre collection.")
|
|
@@ -63,7 +63,7 @@ def standard_read_hardware_during_collection(
|
|
|
63
63
|
aperture_scatterguard: ApertureScatterguard,
|
|
64
64
|
attenuator: BinaryFilterAttenuator,
|
|
65
65
|
flux: Flux,
|
|
66
|
-
dcm:
|
|
66
|
+
dcm: BaseDCM,
|
|
67
67
|
detector: EigerDetector,
|
|
68
68
|
):
|
|
69
69
|
signals_to_read_during_collection = [
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
|
|
3
|
+
import bluesky.plan_stubs as bps
|
|
4
|
+
import bluesky.preprocessors as bpp
|
|
5
|
+
|
|
6
|
+
from mx_bluesky.common.external_interaction.callbacks.sample_handling.sample_handling_callback import (
|
|
7
|
+
SampleHandlingCallback,
|
|
8
|
+
)
|
|
9
|
+
from mx_bluesky.common.utils.exceptions import SampleException
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SampleStatusExceptionType(StrEnum):
|
|
13
|
+
BEAMLINE = "Beamline"
|
|
14
|
+
SAMPLE = "Sample"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@bpp.subs_decorator(SampleHandlingCallback())
|
|
18
|
+
def deposit_sample_error(exception_type: SampleStatusExceptionType, sample_id: int):
|
|
19
|
+
@bpp.run_decorator(
|
|
20
|
+
md={
|
|
21
|
+
"metadata": {"sample_id": sample_id},
|
|
22
|
+
"activate_callbacks": ["SampleHandlingCallback"],
|
|
23
|
+
}
|
|
24
|
+
)
|
|
25
|
+
def _inner():
|
|
26
|
+
yield from bps.null()
|
|
27
|
+
if exception_type == SampleStatusExceptionType.BEAMLINE:
|
|
28
|
+
raise AssertionError()
|
|
29
|
+
elif exception_type == SampleStatusExceptionType.SAMPLE:
|
|
30
|
+
raise SampleException
|
|
31
|
+
|
|
32
|
+
yield from _inner()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@bpp.subs_decorator(SampleHandlingCallback(record_loaded_on_success=True))
|
|
36
|
+
def deposit_loaded_sample(sample_id: int):
|
|
37
|
+
@bpp.run_decorator(
|
|
38
|
+
md={
|
|
39
|
+
"metadata": {"sample_id": sample_id},
|
|
40
|
+
"activate_callbacks": ["SampleHandlingCallback"],
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
def _inner():
|
|
44
|
+
yield from bps.null()
|
|
45
|
+
|
|
46
|
+
yield from _inner()
|
|
File without changes
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
from bluesky import preprocessors as bpp
|
|
2
|
+
from bluesky.preprocessors import plan_mutator
|
|
3
|
+
from bluesky.utils import Msg, MsgGenerator, make_decorator
|
|
4
|
+
|
|
5
|
+
from mx_bluesky.common.device_setup_plans.xbpm_feedback import (
|
|
6
|
+
check_and_pause_feedback,
|
|
7
|
+
unpause_xbpm_feedback_and_set_transmission_to_1,
|
|
8
|
+
)
|
|
9
|
+
from mx_bluesky.common.parameters.constants import PlanNameConstants
|
|
10
|
+
from mx_bluesky.common.protocols.protocols import (
|
|
11
|
+
XBPMPauseDevices,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def transmission_and_xbpm_feedback_for_collection_wrapper(
|
|
16
|
+
plan: MsgGenerator,
|
|
17
|
+
devices: XBPMPauseDevices,
|
|
18
|
+
desired_transmission_fraction: float,
|
|
19
|
+
run_key_to_wrap: PlanNameConstants | None = None,
|
|
20
|
+
):
|
|
21
|
+
"""
|
|
22
|
+
Sets the transmission for the data collection, ensuring the xbpm feedback is valid, then resets it immediately after
|
|
23
|
+
the run has finished.
|
|
24
|
+
|
|
25
|
+
This wrapper should be attached to the entry point of any beamline-specific plan that may disrupt the XBPM feedback,
|
|
26
|
+
such as a data collection or an x-ray center grid scan.
|
|
27
|
+
This wrapper will do nothing if no runs are seen.
|
|
28
|
+
|
|
29
|
+
XBPM feedback isn't reliable during collections due to:
|
|
30
|
+
* Objects (e.g. attenuator) crossing the beam can cause large (incorrect) feedback movements
|
|
31
|
+
* Lower transmissions/higher energies are less reliable for the xbpm
|
|
32
|
+
|
|
33
|
+
So we need to keep the transmission at 100% and the feedback on when not collecting
|
|
34
|
+
and then turn it off and set the correct transmission for collection. The feedback
|
|
35
|
+
mostly accounts for slow thermal drift so it is safe to assume that the beam is
|
|
36
|
+
stable during a collection.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
plan: The plan performing the data collection.
|
|
40
|
+
devices (XBPMPauseDevices): Composite device including The XBPM device that is responsible for keeping
|
|
41
|
+
the beam in position, and attenuator
|
|
42
|
+
desired_transmission_fraction (float): The desired transmission for the collection
|
|
43
|
+
run_key_to_wrap: (str | None): Pausing XBPM and setting transmission is inserted after the 'open_run' message is seen with
|
|
44
|
+
the matching run key, and unpausing and resetting transmission is inserted after the corresponding 'close_run' message is
|
|
45
|
+
seen. If not specified, instead wrap the first run encountered.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
_wrapped_run_name: None | str = None
|
|
49
|
+
|
|
50
|
+
def head(msg: Msg):
|
|
51
|
+
yield from check_and_pause_feedback(
|
|
52
|
+
devices.xbpm_feedback,
|
|
53
|
+
devices.attenuator,
|
|
54
|
+
desired_transmission_fraction,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Allow 'open_run' message to pass through
|
|
58
|
+
yield msg
|
|
59
|
+
|
|
60
|
+
def tail():
|
|
61
|
+
yield from unpause_xbpm_feedback_and_set_transmission_to_1(
|
|
62
|
+
devices.xbpm_feedback, devices.attenuator
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
def insert_plans(msg: Msg):
|
|
66
|
+
# Wrap the specified run, or, if none specified, wrap the first run encountered
|
|
67
|
+
nonlocal _wrapped_run_name
|
|
68
|
+
|
|
69
|
+
match msg.command:
|
|
70
|
+
case "open_run":
|
|
71
|
+
# If we specified a run key, did we encounter it
|
|
72
|
+
# If we didn't specify, then insert the plans and track the name of the run
|
|
73
|
+
if (
|
|
74
|
+
not (run_key_to_wrap or _wrapped_run_name)
|
|
75
|
+
or run_key_to_wrap is msg.run
|
|
76
|
+
):
|
|
77
|
+
_wrapped_run_name = msg.run if msg.run else "unnamed_run"
|
|
78
|
+
return head(msg), None
|
|
79
|
+
case "close_run":
|
|
80
|
+
# Check if the run tracked from above was closed
|
|
81
|
+
# An exception is raised in the RunEngine if two unnamed runs are opened
|
|
82
|
+
# at the same time, so we are safe from unpausing on the wrong run
|
|
83
|
+
if (_wrapped_run_name == "unnamed_run" and not msg.run) or (
|
|
84
|
+
msg.run and _wrapped_run_name and _wrapped_run_name is msg.run
|
|
85
|
+
):
|
|
86
|
+
return None, tail()
|
|
87
|
+
|
|
88
|
+
return None, None
|
|
89
|
+
|
|
90
|
+
# Contingency wrapper can cause unpausing to occur on exception and again on close_run.
|
|
91
|
+
# Not needed after https://github.com/bluesky/bluesky/issues/1891
|
|
92
|
+
return (
|
|
93
|
+
yield from bpp.contingency_wrapper(
|
|
94
|
+
plan_mutator(plan, insert_plans),
|
|
95
|
+
except_plan=lambda _: unpause_xbpm_feedback_and_set_transmission_to_1(
|
|
96
|
+
devices.xbpm_feedback,
|
|
97
|
+
devices.attenuator,
|
|
98
|
+
),
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
transmission_and_xbpm_feedback_for_collection_decorator = make_decorator(
|
|
104
|
+
transmission_and_xbpm_feedback_for_collection_wrapper
|
|
105
|
+
)
|
|
File without changes
|