mx-bluesky 1.5.1__py3-none-any.whl → 1.5.3__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 +16 -3
- mx_bluesky/beamlines/i04/__init__.py +8 -1
- mx_bluesky/beamlines/i04/callbacks/murko_callback.py +56 -1
- mx_bluesky/beamlines/i04/experiment_plans/__init__.py +0 -0
- mx_bluesky/beamlines/i04/experiment_plans/i04_grid_detect_then_xray_centre_plan.py +262 -0
- mx_bluesky/beamlines/i24/serial/blueapi_config.yaml +2 -2
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +3 -1
- mx_bluesky/common/experiment_plans/change_aperture_then_move_plan.py +5 -1
- mx_bluesky/common/experiment_plans/common_flyscan_xray_centre_plan.py +26 -3
- mx_bluesky/common/experiment_plans/common_grid_detect_then_xray_centre_plan.py +1 -0
- mx_bluesky/common/experiment_plans/inner_plans/do_fgs.py +3 -1
- mx_bluesky/common/experiment_plans/oav_grid_detection_plan.py +12 -2
- mx_bluesky/common/external_interaction/alerting/__init__.py +13 -0
- mx_bluesky/common/external_interaction/alerting/_service.py +82 -0
- mx_bluesky/common/external_interaction/alerting/log_based_service.py +57 -0
- mx_bluesky/common/external_interaction/callbacks/sample_handling/sample_handling_callback.py +28 -4
- mx_bluesky/common/external_interaction/config_server.py +151 -54
- mx_bluesky/common/external_interaction/ispyb/ispyb_store.py +11 -6
- mx_bluesky/common/parameters/__init__.py +0 -0
- mx_bluesky/common/parameters/constants.py +27 -8
- mx_bluesky/common/parameters/device_composites.py +1 -1
- mx_bluesky/common/parameters/gridscan.py +2 -1
- mx_bluesky/hyperion/__main__.py +51 -179
- mx_bluesky/hyperion/baton_handler.py +142 -54
- mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +29 -24
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +4 -93
- mx_bluesky/hyperion/experiment_plans/hyperion_flyscan_xray_centre_plan.py +23 -38
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +12 -4
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +1 -1
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +7 -8
- mx_bluesky/hyperion/external_interaction/agamemnon.py +128 -73
- mx_bluesky/hyperion/external_interaction/alerting/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/alerting/constants.py +12 -0
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +5 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +2 -2
- mx_bluesky/hyperion/external_interaction/config_server.py +12 -31
- mx_bluesky/hyperion/parameters/cli.py +15 -3
- mx_bluesky/hyperion/parameters/components.py +7 -5
- mx_bluesky/hyperion/parameters/constants.py +21 -6
- mx_bluesky/hyperion/parameters/gridscan.py +22 -14
- mx_bluesky/hyperion/parameters/load_centre_collect.py +1 -14
- mx_bluesky/hyperion/parameters/robot_load.py +1 -4
- mx_bluesky/hyperion/parameters/rotation.py +1 -2
- mx_bluesky/hyperion/plan_runner.py +78 -0
- mx_bluesky/hyperion/runner.py +189 -0
- mx_bluesky/hyperion/utils/context.py +19 -5
- mx_bluesky/phase1_zebra/__init__.py +1 -0
- mx_bluesky/phase1_zebra/device_setup_plans/__init__.py +0 -0
- mx_bluesky/phase1_zebra/device_setup_plans/setup_zebra.py +112 -0
- {mx_bluesky-1.5.1.dist-info → mx_bluesky-1.5.3.dist-info}/METADATA +5 -4
- {mx_bluesky-1.5.1.dist-info → mx_bluesky-1.5.3.dist-info}/RECORD +57 -44
- {mx_bluesky-1.5.1.dist-info → mx_bluesky-1.5.3.dist-info}/entry_points.txt +0 -2
- /mx_bluesky/common/experiment_plans/{read_hardware.py → inner_plans/read_hardware.py} +0 -0
- /mx_bluesky/common/experiment_plans/{write_sample_status.py → inner_plans/write_sample_status.py} +0 -0
- {mx_bluesky-1.5.1.dist-info → mx_bluesky-1.5.3.dist-info}/WHEEL +0 -0
- {mx_bluesky-1.5.1.dist-info → mx_bluesky-1.5.3.dist-info}/licenses/LICENSE +0 -0
- {mx_bluesky-1.5.1.dist-info → mx_bluesky-1.5.3.dist-info}/top_level.txt +0 -0
mx_bluesky/_version.py
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
# file generated by setuptools-scm
|
|
2
2
|
# don't change, don't track in version control
|
|
3
3
|
|
|
4
|
-
__all__ = [
|
|
4
|
+
__all__ = [
|
|
5
|
+
"__version__",
|
|
6
|
+
"__version_tuple__",
|
|
7
|
+
"version",
|
|
8
|
+
"version_tuple",
|
|
9
|
+
"__commit_id__",
|
|
10
|
+
"commit_id",
|
|
11
|
+
]
|
|
5
12
|
|
|
6
13
|
TYPE_CHECKING = False
|
|
7
14
|
if TYPE_CHECKING:
|
|
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
|
|
|
9
16
|
from typing import Union
|
|
10
17
|
|
|
11
18
|
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
+
COMMIT_ID = Union[str, None]
|
|
12
20
|
else:
|
|
13
21
|
VERSION_TUPLE = object
|
|
22
|
+
COMMIT_ID = object
|
|
14
23
|
|
|
15
24
|
version: str
|
|
16
25
|
__version__: str
|
|
17
26
|
__version_tuple__: VERSION_TUPLE
|
|
18
27
|
version_tuple: VERSION_TUPLE
|
|
28
|
+
commit_id: COMMIT_ID
|
|
29
|
+
__commit_id__: COMMIT_ID
|
|
19
30
|
|
|
20
|
-
__version__ = version = '1.5.
|
|
21
|
-
__version_tuple__ = version_tuple = (1, 5,
|
|
31
|
+
__version__ = version = '1.5.3'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 5, 3)
|
|
33
|
+
|
|
34
|
+
__commit_id__ = commit_id = None
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
from mx_bluesky.beamlines.i04.experiment_plans.i04_grid_detect_then_xray_centre_plan import (
|
|
2
|
+
i04_grid_detect_then_xray_centre,
|
|
3
|
+
)
|
|
1
4
|
from mx_bluesky.beamlines.i04.thawing_plan import thaw, thaw_and_stream_to_redis
|
|
2
5
|
|
|
3
|
-
__all__ = [
|
|
6
|
+
__all__ = [
|
|
7
|
+
"thaw",
|
|
8
|
+
"thaw_and_stream_to_redis",
|
|
9
|
+
"i04_grid_detect_then_xray_centre",
|
|
10
|
+
]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
import json
|
|
3
3
|
from datetime import timedelta
|
|
4
|
+
from typing import TypedDict
|
|
4
5
|
|
|
5
6
|
from bluesky.callbacks import CallbackBase
|
|
6
7
|
from dodal.log import LOGGER
|
|
@@ -8,7 +9,43 @@ from event_model.documents import Event, RunStart, RunStop
|
|
|
8
9
|
from redis import StrictRedis
|
|
9
10
|
|
|
10
11
|
|
|
12
|
+
class OmegaReading(TypedDict):
|
|
13
|
+
value: float
|
|
14
|
+
timestamp: float
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def extrapolate_omega(
|
|
18
|
+
latest_omega: OmegaReading, previous_omega: OmegaReading, image_timestamp: float
|
|
19
|
+
) -> float:
|
|
20
|
+
"""Extrapolate an image omega from previous omegas.
|
|
21
|
+
|
|
22
|
+
There are a number of assumptions in this calculation:
|
|
23
|
+
* The speed of the smargon is fixed
|
|
24
|
+
* The timestamps from the two different devices are synchronised and match the data
|
|
25
|
+
exactly
|
|
26
|
+
|
|
27
|
+
These are accepted to be reasonable based on larger errors likely coming from murko
|
|
28
|
+
itself and that the results ultimately will be averaged out.
|
|
29
|
+
"""
|
|
30
|
+
omega_per_sec = (latest_omega["value"] - previous_omega["value"]) / (
|
|
31
|
+
latest_omega["timestamp"] - previous_omega["timestamp"]
|
|
32
|
+
)
|
|
33
|
+
time_elapsed = image_timestamp - latest_omega["timestamp"]
|
|
34
|
+
return latest_omega["value"] + time_elapsed * omega_per_sec
|
|
35
|
+
|
|
36
|
+
|
|
11
37
|
class MurkoCallback(CallbackBase):
|
|
38
|
+
"""A callback that triggers murko processing of images.
|
|
39
|
+
|
|
40
|
+
It combines metadata readings from e.g the goniometer rotation with the uuid's given
|
|
41
|
+
to us by an `OAVToRedisForwarder` (which describe the location of images in redis).
|
|
42
|
+
And writes these as a package to redis. A separate service then forwards this to murko.
|
|
43
|
+
|
|
44
|
+
The metadata and image data arrive independently, it is expected that the image data
|
|
45
|
+
is arriving at a faster rate than gonio metadata and so the value of omega for when
|
|
46
|
+
the image arrives is extrapolated based on previous omega readings.
|
|
47
|
+
"""
|
|
48
|
+
|
|
12
49
|
DATA_EXPIRY_DAYS = 7
|
|
13
50
|
|
|
14
51
|
def __init__(self, redis_host: str, redis_password: str, redis_db: int = 0):
|
|
@@ -16,6 +53,7 @@ class MurkoCallback(CallbackBase):
|
|
|
16
53
|
host=redis_host, password=redis_password, db=redis_db
|
|
17
54
|
)
|
|
18
55
|
self.last_uuid = None
|
|
56
|
+
self.previous_omegas: list[OmegaReading] = []
|
|
19
57
|
|
|
20
58
|
def start(self, doc: RunStart) -> RunStart | None:
|
|
21
59
|
self.sample_id = doc.get("sample_id")
|
|
@@ -28,14 +66,31 @@ class MurkoCallback(CallbackBase):
|
|
|
28
66
|
"sample_id": self.sample_id,
|
|
29
67
|
}
|
|
30
68
|
self.last_uuid = None
|
|
69
|
+
self.previous_omegas = []
|
|
31
70
|
LOGGER.info(f"Starting to stream metadata to murko under {self.sample_id}")
|
|
32
71
|
return doc
|
|
33
72
|
|
|
34
73
|
def event(self, doc: Event) -> Event:
|
|
35
74
|
if latest_omega := doc["data"].get("smargon-omega"):
|
|
36
|
-
if self.
|
|
75
|
+
if len(self.previous_omegas) <= 2 and self.last_uuid:
|
|
76
|
+
# For the first few images there's not enough data to extrapolate so we
|
|
77
|
+
# match them one to one
|
|
37
78
|
self.call_murko(self.last_uuid, latest_omega)
|
|
79
|
+
self.previous_omegas.append(
|
|
80
|
+
OmegaReading(
|
|
81
|
+
value=latest_omega,
|
|
82
|
+
timestamp=doc["timestamps"]["smargon-omega"],
|
|
83
|
+
)
|
|
84
|
+
)
|
|
38
85
|
elif (uuid := doc["data"].get("oav_to_redis_forwarder-uuid")) is not None:
|
|
86
|
+
if len(self.previous_omegas) >= 2:
|
|
87
|
+
omega = extrapolate_omega(
|
|
88
|
+
self.previous_omegas[-1],
|
|
89
|
+
self.previous_omegas[-2],
|
|
90
|
+
doc["timestamps"]["oav_to_redis_forwarder-uuid"],
|
|
91
|
+
)
|
|
92
|
+
LOGGER.info(f"Using extrapolated omega of {omega}")
|
|
93
|
+
self.call_murko(uuid, omega)
|
|
39
94
|
self.last_uuid = uuid
|
|
40
95
|
return doc
|
|
41
96
|
|
|
File without changes
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from functools import partial
|
|
4
|
+
|
|
5
|
+
import bluesky.plan_stubs as bps
|
|
6
|
+
import bluesky.preprocessors as bpp
|
|
7
|
+
from blueapi.core import BlueskyContext
|
|
8
|
+
from bluesky.utils import MsgGenerator
|
|
9
|
+
from dodal.common import inject
|
|
10
|
+
from dodal.devices.aperturescatterguard import ApertureScatterguard
|
|
11
|
+
from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
|
|
12
|
+
from dodal.devices.backlight import Backlight
|
|
13
|
+
from dodal.devices.common_dcm import BaseDCM
|
|
14
|
+
from dodal.devices.detector.detector_motion import DetectorMotion
|
|
15
|
+
from dodal.devices.eiger import EigerDetector
|
|
16
|
+
from dodal.devices.fast_grid_scan import (
|
|
17
|
+
ZebraFastGridScan,
|
|
18
|
+
set_fast_grid_scan_params,
|
|
19
|
+
)
|
|
20
|
+
from dodal.devices.flux import Flux
|
|
21
|
+
from dodal.devices.mx_phase1.beamstop import Beamstop
|
|
22
|
+
from dodal.devices.oav.oav_detector import OAV
|
|
23
|
+
from dodal.devices.oav.pin_image_recognition import PinTipDetection
|
|
24
|
+
from dodal.devices.robot import BartRobot
|
|
25
|
+
from dodal.devices.s4_slit_gaps import S4SlitGaps
|
|
26
|
+
from dodal.devices.smargon import Smargon
|
|
27
|
+
from dodal.devices.synchrotron import Synchrotron
|
|
28
|
+
from dodal.devices.undulator import Undulator
|
|
29
|
+
from dodal.devices.xbpm_feedback import XBPMFeedback
|
|
30
|
+
from dodal.devices.zebra.zebra import Zebra
|
|
31
|
+
from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
|
|
32
|
+
from dodal.devices.zocalo import ZocaloResults
|
|
33
|
+
from dodal.plans.preprocessors.verify_undulator_gap import (
|
|
34
|
+
verify_undulator_gap_before_run_decorator,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
from mx_bluesky.common.experiment_plans.common_flyscan_xray_centre_plan import (
|
|
38
|
+
BeamlineSpecificFGSFeatures,
|
|
39
|
+
construct_beamline_specific_FGS_features,
|
|
40
|
+
)
|
|
41
|
+
from mx_bluesky.common.experiment_plans.common_grid_detect_then_xray_centre_plan import (
|
|
42
|
+
grid_detect_then_xray_centre,
|
|
43
|
+
)
|
|
44
|
+
from mx_bluesky.common.experiment_plans.oav_snapshot_plan import (
|
|
45
|
+
setup_beamline_for_OAV,
|
|
46
|
+
)
|
|
47
|
+
from mx_bluesky.common.external_interaction.callbacks.common.zocalo_callback import (
|
|
48
|
+
ZocaloCallback,
|
|
49
|
+
)
|
|
50
|
+
from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback import (
|
|
51
|
+
GridscanISPyBCallback,
|
|
52
|
+
)
|
|
53
|
+
from mx_bluesky.common.external_interaction.callbacks.xray_centre.nexus_callback import (
|
|
54
|
+
GridscanNexusFileCallback,
|
|
55
|
+
)
|
|
56
|
+
from mx_bluesky.common.parameters.constants import (
|
|
57
|
+
EnvironmentConstants,
|
|
58
|
+
OavConstants,
|
|
59
|
+
PlanGroupCheckpointConstants,
|
|
60
|
+
PlanNameConstants,
|
|
61
|
+
)
|
|
62
|
+
from mx_bluesky.common.parameters.device_composites import (
|
|
63
|
+
GridDetectThenXRayCentreComposite,
|
|
64
|
+
)
|
|
65
|
+
from mx_bluesky.common.parameters.gridscan import GridCommon, SpecifiedThreeDGridScan
|
|
66
|
+
from mx_bluesky.common.preprocessors.preprocessors import (
|
|
67
|
+
transmission_and_xbpm_feedback_for_collection_decorator,
|
|
68
|
+
)
|
|
69
|
+
from mx_bluesky.common.utils.context import device_composite_from_context
|
|
70
|
+
from mx_bluesky.common.utils.log import LOGGER
|
|
71
|
+
from mx_bluesky.phase1_zebra.device_setup_plans.setup_zebra import (
|
|
72
|
+
setup_zebra_for_gridscan,
|
|
73
|
+
tidy_up_zebra_after_gridscan,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def create_devices(
|
|
78
|
+
context: BlueskyContext,
|
|
79
|
+
) -> GridDetectThenXRayCentreComposite:
|
|
80
|
+
return device_composite_from_context(context, GridDetectThenXRayCentreComposite)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# See https://github.com/DiamondLightSource/blueapi/issues/506 for using device composites
|
|
84
|
+
def i04_grid_detect_then_xray_centre(
|
|
85
|
+
parameters: GridCommon,
|
|
86
|
+
aperture_scatterguard: ApertureScatterguard = inject("aperture_scatterguard"),
|
|
87
|
+
attenuator: BinaryFilterAttenuator = inject("attenuator"),
|
|
88
|
+
backlight: Backlight = inject("backlight"),
|
|
89
|
+
beamstop: Beamstop = inject("beamstop"),
|
|
90
|
+
dcm: BaseDCM = inject("dcm"),
|
|
91
|
+
zebra_fast_grid_scan: ZebraFastGridScan = inject("zebra_fast_grid_scan"),
|
|
92
|
+
flux: Flux = inject("flux"),
|
|
93
|
+
oav: OAV = inject("oav"),
|
|
94
|
+
pin_tip_detection: PinTipDetection = inject("pin_tip_detection"),
|
|
95
|
+
s4_slit_gaps: S4SlitGaps = inject("s4_slit_gaps"),
|
|
96
|
+
undulator: Undulator = inject("undulator"),
|
|
97
|
+
xbpm_feedback: XBPMFeedback = inject("xbpm_feedback"),
|
|
98
|
+
zebra: Zebra = inject("zebra"),
|
|
99
|
+
robot: BartRobot = inject("robot"),
|
|
100
|
+
sample_shutter: ZebraShutter = inject("sample_shutter"),
|
|
101
|
+
eiger: EigerDetector = inject("eiger"),
|
|
102
|
+
synchrotron: Synchrotron = inject("synchrotron"),
|
|
103
|
+
zocalo: ZocaloResults = inject("zocalo"),
|
|
104
|
+
smargon: Smargon = inject("smargon"),
|
|
105
|
+
detector_motion: DetectorMotion = inject("detector_motion"),
|
|
106
|
+
oav_config: str = OavConstants.OAV_CONFIG_JSON,
|
|
107
|
+
udc: bool = False,
|
|
108
|
+
) -> MsgGenerator:
|
|
109
|
+
"""
|
|
110
|
+
A composite plan which:
|
|
111
|
+
- Uses the OAV to draw a virtual grid over the sample and to take snapshots of the sample
|
|
112
|
+
- Scans through the grid to identify the crystal centre
|
|
113
|
+
- Changes the aperture to match the beam size to the crystal size
|
|
114
|
+
- Moves the sample to the crystal centre of mass
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
i04's implementation of this plan is very similar to Hyperion. However, since i04
|
|
118
|
+
isn't running in a continious Bluesky UDC loop, we take additional steps in beamline
|
|
119
|
+
tidy-up.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
composite = GridDetectThenXRayCentreComposite(
|
|
123
|
+
eiger,
|
|
124
|
+
synchrotron,
|
|
125
|
+
zocalo,
|
|
126
|
+
smargon,
|
|
127
|
+
aperture_scatterguard,
|
|
128
|
+
attenuator,
|
|
129
|
+
backlight,
|
|
130
|
+
beamstop,
|
|
131
|
+
dcm,
|
|
132
|
+
detector_motion,
|
|
133
|
+
zebra_fast_grid_scan,
|
|
134
|
+
flux,
|
|
135
|
+
oav,
|
|
136
|
+
pin_tip_detection,
|
|
137
|
+
s4_slit_gaps,
|
|
138
|
+
undulator,
|
|
139
|
+
xbpm_feedback,
|
|
140
|
+
zebra,
|
|
141
|
+
robot,
|
|
142
|
+
sample_shutter,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def tidy_beamline_if_not_udc():
|
|
146
|
+
if not udc:
|
|
147
|
+
yield from get_ready_for_oav_and_close_shutter(
|
|
148
|
+
composite.smargon,
|
|
149
|
+
composite.backlight,
|
|
150
|
+
composite.aperture_scatterguard,
|
|
151
|
+
composite.detector_motion,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
@bpp.finalize_decorator(tidy_beamline_if_not_udc)
|
|
155
|
+
def _inner_grid_detect_then_xrc():
|
|
156
|
+
# These callbacks let us talk to ISPyB and Nexgen. They aren't included in the common plan because
|
|
157
|
+
# Hyperion handles its callbacks differently to BlueAPI-managed plans, see
|
|
158
|
+
# https://github.com/DiamondLightSource/mx-bluesky/issues/1117
|
|
159
|
+
callbacks = create_gridscan_callbacks()
|
|
160
|
+
|
|
161
|
+
@bpp.subs_decorator(callbacks)
|
|
162
|
+
@verify_undulator_gap_before_run_decorator(composite)
|
|
163
|
+
@transmission_and_xbpm_feedback_for_collection_decorator(
|
|
164
|
+
composite, parameters.transmission_frac, PlanNameConstants.GRIDSCAN_OUTER
|
|
165
|
+
)
|
|
166
|
+
def grid_detect_then_xray_centre_with_callbacks():
|
|
167
|
+
yield from grid_detect_then_xray_centre(
|
|
168
|
+
composite=composite,
|
|
169
|
+
parameters=parameters,
|
|
170
|
+
xrc_params_type=SpecifiedThreeDGridScan,
|
|
171
|
+
construct_beamline_specific=construct_i04_specific_features,
|
|
172
|
+
oav_config=oav_config,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
yield from grid_detect_then_xray_centre_with_callbacks()
|
|
176
|
+
|
|
177
|
+
yield from _inner_grid_detect_then_xrc()
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def get_ready_for_oav_and_close_shutter(
|
|
181
|
+
smargon: Smargon,
|
|
182
|
+
backlight: Backlight,
|
|
183
|
+
aperture_scatterguard: ApertureScatterguard,
|
|
184
|
+
detector_motion: DetectorMotion,
|
|
185
|
+
):
|
|
186
|
+
yield from bps.wait(PlanGroupCheckpointConstants.GRID_READY_FOR_DC)
|
|
187
|
+
group = "get_ready_for_oav_and_close_shutter"
|
|
188
|
+
LOGGER.info("Non-udc tidy: Seting up beamline for OAV")
|
|
189
|
+
yield from setup_beamline_for_OAV(
|
|
190
|
+
smargon, backlight, aperture_scatterguard, group=group
|
|
191
|
+
)
|
|
192
|
+
LOGGER.info("Non-udc tidy: Closing detector shutter")
|
|
193
|
+
yield from bps.abs_set(
|
|
194
|
+
detector_motion.shutter,
|
|
195
|
+
0,
|
|
196
|
+
group=group,
|
|
197
|
+
)
|
|
198
|
+
yield from bps.wait(group)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def create_gridscan_callbacks() -> tuple[
|
|
202
|
+
GridscanNexusFileCallback, GridscanISPyBCallback
|
|
203
|
+
]:
|
|
204
|
+
return (
|
|
205
|
+
GridscanNexusFileCallback(param_type=SpecifiedThreeDGridScan),
|
|
206
|
+
GridscanISPyBCallback(
|
|
207
|
+
param_type=GridCommon,
|
|
208
|
+
emit=ZocaloCallback(
|
|
209
|
+
PlanNameConstants.DO_FGS, EnvironmentConstants.ZOCALO_ENV
|
|
210
|
+
),
|
|
211
|
+
),
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def construct_i04_specific_features(
|
|
216
|
+
xrc_composite: GridDetectThenXRayCentreComposite,
|
|
217
|
+
xrc_parameters: SpecifiedThreeDGridScan,
|
|
218
|
+
) -> BeamlineSpecificFGSFeatures:
|
|
219
|
+
"""
|
|
220
|
+
Get all the information needed to do the i04 XRC flyscan.
|
|
221
|
+
"""
|
|
222
|
+
signals_to_read_pre_flyscan = [
|
|
223
|
+
xrc_composite.undulator.current_gap,
|
|
224
|
+
xrc_composite.synchrotron.synchrotron_mode,
|
|
225
|
+
xrc_composite.s4_slit_gaps.xgap,
|
|
226
|
+
xrc_composite.s4_slit_gaps.ygap,
|
|
227
|
+
xrc_composite.smargon.x,
|
|
228
|
+
xrc_composite.smargon.y,
|
|
229
|
+
xrc_composite.smargon.z,
|
|
230
|
+
xrc_composite.dcm.energy_in_kev,
|
|
231
|
+
]
|
|
232
|
+
|
|
233
|
+
signals_to_read_during_collection = [
|
|
234
|
+
xrc_composite.aperture_scatterguard,
|
|
235
|
+
xrc_composite.attenuator.actual_transmission,
|
|
236
|
+
xrc_composite.flux.flux_reading,
|
|
237
|
+
xrc_composite.dcm.energy_in_kev,
|
|
238
|
+
xrc_composite.eiger.bit_depth,
|
|
239
|
+
]
|
|
240
|
+
|
|
241
|
+
tidy_plan = partial(
|
|
242
|
+
tidy_up_zebra_after_gridscan,
|
|
243
|
+
xrc_composite.zebra,
|
|
244
|
+
xrc_composite.sample_shutter,
|
|
245
|
+
group="flyscan_zebra_tidy",
|
|
246
|
+
wait=True,
|
|
247
|
+
)
|
|
248
|
+
set_flyscan_params_plan = partial(
|
|
249
|
+
set_fast_grid_scan_params,
|
|
250
|
+
xrc_composite.zebra_fast_grid_scan,
|
|
251
|
+
xrc_parameters.FGS_params,
|
|
252
|
+
)
|
|
253
|
+
fgs_motors = xrc_composite.zebra_fast_grid_scan
|
|
254
|
+
return construct_beamline_specific_FGS_features(
|
|
255
|
+
setup_zebra_for_gridscan,
|
|
256
|
+
tidy_plan,
|
|
257
|
+
set_flyscan_params_plan,
|
|
258
|
+
fgs_motors,
|
|
259
|
+
signals_to_read_pre_flyscan,
|
|
260
|
+
signals_to_read_during_collection,
|
|
261
|
+
get_xrc_results_from_zocalo=True,
|
|
262
|
+
)
|
|
@@ -7,11 +7,11 @@ env:
|
|
|
7
7
|
events:
|
|
8
8
|
broadcast_status_events: false
|
|
9
9
|
api:
|
|
10
|
-
|
|
10
|
+
url: http://localhost:25565
|
|
11
11
|
cors:
|
|
12
12
|
allow_credentials: True
|
|
13
13
|
origins:
|
|
14
14
|
- "*"
|
|
15
15
|
stomp:
|
|
16
16
|
enabled: true
|
|
17
|
-
|
|
17
|
+
url: http://i24-control.diamond.ac.uk:61613
|
|
@@ -490,7 +490,9 @@ def run_aborted_plan(pmac: PMAC, dcid: DCID, exception: Exception):
|
|
|
490
490
|
either by pressing the Abort button or because of a timeout, and to reset the \
|
|
491
491
|
P variable.
|
|
492
492
|
"""
|
|
493
|
-
SSX_LOGGER.warning(
|
|
493
|
+
SSX_LOGGER.warning(
|
|
494
|
+
f"Data Collection Aborted: {''.join(format_exception(exception))}"
|
|
495
|
+
)
|
|
494
496
|
yield from bps.trigger(pmac.abort_program, wait=True)
|
|
495
497
|
|
|
496
498
|
end_time = datetime.now()
|
|
@@ -4,6 +4,7 @@ from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureVal
|
|
|
4
4
|
from dodal.devices.smargon import Smargon, StubPosition
|
|
5
5
|
|
|
6
6
|
from mx_bluesky.common.device_setup_plans.manipulate_sample import move_x_y_z
|
|
7
|
+
from mx_bluesky.common.parameters.constants import PlanGroupCheckpointConstants
|
|
7
8
|
from mx_bluesky.common.utils.log import LOGGER
|
|
8
9
|
from mx_bluesky.common.utils.tracing import TRACER
|
|
9
10
|
from mx_bluesky.common.xrc_result import XRayCentreResult
|
|
@@ -43,6 +44,7 @@ def change_aperture_then_move_to_xtal(
|
|
|
43
44
|
def set_aperture_for_bbox_mm(
|
|
44
45
|
aperture_device: ApertureScatterguard,
|
|
45
46
|
bbox_size_mm: list[float] | numpy.ndarray,
|
|
47
|
+
group=PlanGroupCheckpointConstants.GRID_READY_FOR_DC,
|
|
46
48
|
):
|
|
47
49
|
"""Sets aperture size based on bbox_size.
|
|
48
50
|
|
|
@@ -71,4 +73,6 @@ def set_aperture_for_bbox_mm(
|
|
|
71
73
|
f"Setting aperture to {new_selected_aperture} based on bounding box size {bbox_size_mm}."
|
|
72
74
|
)
|
|
73
75
|
|
|
74
|
-
yield from bps.abs_set(
|
|
76
|
+
yield from bps.abs_set(
|
|
77
|
+
aperture_device.selected_aperture, new_selected_aperture, group=group
|
|
78
|
+
)
|
|
@@ -22,7 +22,7 @@ from mx_bluesky.common.experiment_plans.inner_plans.do_fgs import (
|
|
|
22
22
|
ZOCALO_STAGE_GROUP,
|
|
23
23
|
kickoff_and_complete_gridscan,
|
|
24
24
|
)
|
|
25
|
-
from mx_bluesky.common.experiment_plans.read_hardware import (
|
|
25
|
+
from mx_bluesky.common.experiment_plans.inner_plans.read_hardware import (
|
|
26
26
|
read_hardware_plan,
|
|
27
27
|
)
|
|
28
28
|
from mx_bluesky.common.parameters.constants import (
|
|
@@ -55,6 +55,25 @@ class BeamlineSpecificFGSFeatures:
|
|
|
55
55
|
get_xrc_results_from_zocalo: bool
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
def generic_tidy(xrc_composite: FlyScanEssentialDevices, wait=True) -> MsgGenerator:
|
|
59
|
+
"""Tidy Zocalo and turn off Eiger dev/shm. Ran after the beamline-specific tidy plan"""
|
|
60
|
+
|
|
61
|
+
LOGGER.info("Tidying up Zocalo")
|
|
62
|
+
group = "generic_tidy"
|
|
63
|
+
# make sure we don't consume any other results
|
|
64
|
+
yield from bps.unstage(xrc_composite.zocalo, group=group)
|
|
65
|
+
|
|
66
|
+
# Turn off dev/shm streaming to avoid filling disk, see https://github.com/DiamondLightSource/hyperion/issues/1395
|
|
67
|
+
LOGGER.info("Turning off Eiger dev/shm streaming")
|
|
68
|
+
# Fix types in ophyd-async (https://github.com/DiamondLightSource/mx-bluesky/issues/855)
|
|
69
|
+
yield from bps.abs_set(
|
|
70
|
+
xrc_composite.eiger.odin.fan.dev_shm_enable, # type: ignore
|
|
71
|
+
0,
|
|
72
|
+
group=group,
|
|
73
|
+
)
|
|
74
|
+
yield from bps.wait(group)
|
|
75
|
+
|
|
76
|
+
|
|
58
77
|
def construct_beamline_specific_FGS_features(
|
|
59
78
|
setup_trigger_plan: Callable[..., MsgGenerator],
|
|
60
79
|
tidy_plan: Callable[..., MsgGenerator],
|
|
@@ -71,7 +90,7 @@ def construct_beamline_specific_FGS_features(
|
|
|
71
90
|
Ran directly before kicking off the gridscan.
|
|
72
91
|
|
|
73
92
|
tidy_plan (Callable): Tidy up states of devices. Ran at the end of the flyscan, regardless of
|
|
74
|
-
whether or not it finished successfully.
|
|
93
|
+
whether or not it finished successfully. Zocalo and Eiger are cleaned up separately
|
|
75
94
|
|
|
76
95
|
set_flyscan_params_plan (Callable): Set PV's for the relevant Fast Grid Scan dodal device
|
|
77
96
|
|
|
@@ -135,6 +154,10 @@ def common_flyscan_xray_centre(
|
|
|
135
154
|
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
|
|
136
155
|
"""
|
|
137
156
|
|
|
157
|
+
def _overall_tidy():
|
|
158
|
+
yield from beamline_specific.tidy_plan()
|
|
159
|
+
yield from generic_tidy(composite)
|
|
160
|
+
|
|
138
161
|
def _decorated_flyscan():
|
|
139
162
|
@bpp.set_run_key_decorator(PlanNameConstants.GRIDSCAN_OUTER)
|
|
140
163
|
@bpp.run_decorator( # attach experiment metadata to the start document
|
|
@@ -146,7 +169,7 @@ def common_flyscan_xray_centre(
|
|
|
146
169
|
],
|
|
147
170
|
}
|
|
148
171
|
)
|
|
149
|
-
@bpp.finalize_decorator(lambda:
|
|
172
|
+
@bpp.finalize_decorator(lambda: _overall_tidy())
|
|
150
173
|
def run_gridscan_and_tidy(
|
|
151
174
|
fgs_composite: FlyScanEssentialDevices,
|
|
152
175
|
params: SpecifiedThreeDGridScan,
|
|
@@ -111,6 +111,7 @@ def grid_detect_then_xray_centre(
|
|
|
111
111
|
)
|
|
112
112
|
|
|
113
113
|
|
|
114
|
+
# This function should be private but is currently called by Hyperion, see https://github.com/DiamondLightSource/mx-bluesky/issues/1148
|
|
114
115
|
def detect_grid_and_do_gridscan(
|
|
115
116
|
composite: GridDetectThenXRayCentreComposite,
|
|
116
117
|
parameters: GridCommon,
|
|
@@ -14,7 +14,9 @@ from dodal.log import LOGGER
|
|
|
14
14
|
from dodal.plan_stubs.check_topup import check_topup_and_wait_if_necessary
|
|
15
15
|
from scanspec.core import AxesPoints, Axis
|
|
16
16
|
|
|
17
|
-
from mx_bluesky.common.experiment_plans.read_hardware import
|
|
17
|
+
from mx_bluesky.common.experiment_plans.inner_plans.read_hardware import (
|
|
18
|
+
read_hardware_for_zocalo,
|
|
19
|
+
)
|
|
18
20
|
from mx_bluesky.common.parameters.constants import (
|
|
19
21
|
PlanNameConstants,
|
|
20
22
|
)
|
|
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING
|
|
|
6
6
|
import bluesky.plan_stubs as bps
|
|
7
7
|
import numpy as np
|
|
8
8
|
from blueapi.core import BlueskyContext
|
|
9
|
+
from bluesky.utils import MsgGenerator
|
|
9
10
|
from dodal.devices.oav.oav_detector import OAV
|
|
10
11
|
from dodal.devices.oav.pin_image_recognition import PinTipDetection
|
|
11
12
|
from dodal.devices.oav.pin_image_recognition.utils import NONE_VALUE
|
|
@@ -49,6 +50,16 @@ def get_min_and_max_y_of_pin(
|
|
|
49
50
|
return min_y, max_y
|
|
50
51
|
|
|
51
52
|
|
|
53
|
+
def optimum_grid_detect_angles(smargon: Smargon) -> MsgGenerator[list[float]]:
|
|
54
|
+
"""We need to match the 0 and -90 that the fast grid scan performs but the order in
|
|
55
|
+
which we do the grid detection does not matter so we do the closest angle first."""
|
|
56
|
+
current_omega = yield from bps.rd(smargon.omega)
|
|
57
|
+
if current_omega < -45:
|
|
58
|
+
return [-90, 0]
|
|
59
|
+
else:
|
|
60
|
+
return [0, -90]
|
|
61
|
+
|
|
62
|
+
|
|
52
63
|
def grid_detection_plan(
|
|
53
64
|
composite: OavGridDetectionComposite,
|
|
54
65
|
parameters: OAVParameters,
|
|
@@ -90,8 +101,7 @@ def grid_detection_plan(
|
|
|
90
101
|
|
|
91
102
|
grid_width_pixels = int(grid_width_microns / microns_per_pixel_x)
|
|
92
103
|
|
|
93
|
-
|
|
94
|
-
for angle in [0, -90]:
|
|
104
|
+
for angle in (yield from optimum_grid_detect_angles(smargon)):
|
|
95
105
|
yield from bps.mv(smargon.omega, angle)
|
|
96
106
|
# need to wait for the OAV image to update
|
|
97
107
|
# See #673 for improvements
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from mx_bluesky.common.external_interaction.alerting._service import (
|
|
2
|
+
AlertService,
|
|
3
|
+
Metadata,
|
|
4
|
+
get_alerting_service,
|
|
5
|
+
set_alerting_service,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"AlertService",
|
|
10
|
+
"Metadata",
|
|
11
|
+
"get_alerting_service",
|
|
12
|
+
"set_alerting_service",
|
|
13
|
+
]
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from datetime import UTC, datetime, timedelta
|
|
2
|
+
from enum import StrEnum
|
|
3
|
+
from typing import Protocol
|
|
4
|
+
from urllib.parse import quote, urlencode
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Metadata(StrEnum):
|
|
8
|
+
"""Metadata fields that can be specified by the caller when raising an alert."""
|
|
9
|
+
|
|
10
|
+
CONTAINER = "container"
|
|
11
|
+
SAMPLE_ID = "sample_id"
|
|
12
|
+
VISIT = "visit"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ExtraMetadata(StrEnum):
|
|
16
|
+
"""Additional metadata fields that are automatically appended by
|
|
17
|
+
the AlertService implementations."""
|
|
18
|
+
|
|
19
|
+
ALERT_CONTENT = "alert_content"
|
|
20
|
+
ALERT_SUMMARY = "alert_summary"
|
|
21
|
+
BEAMLINE = "beamline"
|
|
22
|
+
GRAYLOG_URL = "graylog_url"
|
|
23
|
+
ISPYB_URL = "ispyb_url"
|
|
24
|
+
PROPOSAL = "proposal"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AlertService(Protocol):
|
|
28
|
+
"""
|
|
29
|
+
Implemented by any backend that provides the ability to dispatch alerts to some
|
|
30
|
+
service that is capable of disseminating them via any of a variety of media such
|
|
31
|
+
as email, SMS, instant messaging, etc etc.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def raise_alert(self, summary: str, content: str, metadata: dict[Metadata, str]):
|
|
35
|
+
"""
|
|
36
|
+
Raise an alert that will be forwarded to beamline support staff, which might
|
|
37
|
+
for example be used as the basis for an incident in an incident reporting system.
|
|
38
|
+
Args:
|
|
39
|
+
summary: One line summary of the alert, that might for instance be used
|
|
40
|
+
in an email subject line.
|
|
41
|
+
content: Plain text content detailing the nature of the incident.
|
|
42
|
+
metadata: A dict of strings that can be included as metadata in the alert for
|
|
43
|
+
those backends that support it. The summary and content will be included
|
|
44
|
+
by default.
|
|
45
|
+
"""
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
_alert_service: AlertService
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def get_alerting_service() -> AlertService:
|
|
53
|
+
"""Get the alert service for this instance."""
|
|
54
|
+
return _alert_service
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def set_alerting_service(service: AlertService):
|
|
58
|
+
"""Set the alert service for this instance, call when the beamline is initialised."""
|
|
59
|
+
global _alert_service
|
|
60
|
+
_alert_service = service
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def ispyb_url(sample_id: str):
|
|
64
|
+
return f"https://ispyb.diamond.ac.uk/samples/sid/{quote(sample_id)}"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def graylog_url(stream_id: str):
|
|
68
|
+
now = datetime.now(UTC)
|
|
69
|
+
from_utc = now - timedelta(minutes=5)
|
|
70
|
+
from_timestamp = from_utc.isoformat()
|
|
71
|
+
# Add 1 second for graylog timing jitter
|
|
72
|
+
to_utc = now + timedelta(seconds=1)
|
|
73
|
+
to_timestamp = to_utc.isoformat()
|
|
74
|
+
query_string = urlencode(
|
|
75
|
+
{
|
|
76
|
+
"streams": stream_id,
|
|
77
|
+
"rangetype": "absolute",
|
|
78
|
+
"from": from_timestamp,
|
|
79
|
+
"to": to_timestamp,
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
return "https://graylog.diamond.ac.uk/search?" + query_string
|