mx-bluesky 1.5.3__py3-none-any.whl → 1.5.5__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 +2 -0
- mx_bluesky/beamlines/aithre_lasershaping/beamline_safe.py +17 -0
- mx_bluesky/beamlines/i04/__init__.py +6 -1
- mx_bluesky/beamlines/i04/experiment_plans/i04_grid_detect_then_xray_centre_plan.py +0 -8
- mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +2 -3
- mx_bluesky/beamlines/i04/thawing_plan.py +174 -60
- mx_bluesky/beamlines/i24/serial/blueapi_config.yaml +1 -1
- mx_bluesky/beamlines/i24/serial/dcid.py +4 -25
- mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DetStage.edl +4 -7
- mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +5 -5
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +18 -107
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/CustomChip_py3v1.edl +11 -11
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DetStage.edl +2 -5
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +80 -80
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/ME14E-GeneralPurpose.edl +120 -120
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/MappingLite-oxford_py3v1.edl +143 -143
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/PMAC_Command.edl +2 -2
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/Shutter_Control.edl +3 -3
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/nudgechip.edl +24 -24
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +19 -19
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +8 -92
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +15 -30
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +10 -10
- mx_bluesky/beamlines/i24/serial/parameters/constants.py +0 -2
- mx_bluesky/beamlines/i24/serial/parameters/experiment_parameters.py +1 -6
- mx_bluesky/beamlines/i24/serial/parameters/fixed_target/cs/cs_maker.json +3 -3
- mx_bluesky/beamlines/i24/serial/parameters/utils.py +1 -1
- mx_bluesky/beamlines/i24/serial/run_extruder.sh +15 -0
- mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +17 -0
- mx_bluesky/beamlines/i24/serial/set_visit_directory.sh +1 -1
- mx_bluesky/beamlines/i24/serial/setup_beamline/__init__.py +1 -2
- mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +142 -160
- mx_bluesky/beamlines/i24/serial/setup_beamline/pv_abstract.py +1 -30
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +0 -94
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +4 -10
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +12 -20
- mx_bluesky/beamlines/i24/serial/web_gui_plans/general_plans.py +4 -13
- mx_bluesky/beamlines/i24/serial/write_nexus.py +34 -9
- mx_bluesky/common/device_setup_plans/manipulate_sample.py +2 -2
- mx_bluesky/common/experiment_plans/common_grid_detect_then_xray_centre_plan.py +2 -2
- mx_bluesky/common/experiment_plans/inner_plans/udc_default_state.py +65 -0
- mx_bluesky/common/experiment_plans/oav_snapshot_plan.py +2 -2
- mx_bluesky/common/external_interaction/callbacks/common/grid_detection_callback.py +35 -17
- mx_bluesky/common/external_interaction/callbacks/common/ispyb_callback_base.py +1 -1
- mx_bluesky/common/external_interaction/callbacks/common/plan_reactive_callback.py +2 -2
- mx_bluesky/common/external_interaction/callbacks/sample_handling/sample_handling_callback.py +3 -2
- mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py +2 -2
- mx_bluesky/common/external_interaction/callbacks/xray_centre/nexus_callback.py +2 -2
- mx_bluesky/common/parameters/constants.py +1 -0
- mx_bluesky/hyperion/baton_handler.py +50 -8
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +5 -1
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +6 -2
- mx_bluesky/hyperion/external_interaction/alerting/constants.py +2 -7
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +4 -0
- mx_bluesky/hyperion/external_interaction/callbacks/alert_on_container_change.py +54 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +2 -2
- {mx_bluesky-1.5.3.dist-info → mx_bluesky-1.5.5.dist-info}/METADATA +2 -2
- {mx_bluesky-1.5.3.dist-info → mx_bluesky-1.5.5.dist-info}/RECORD +63 -61
- {mx_bluesky-1.5.3.dist-info → mx_bluesky-1.5.5.dist-info}/WHEEL +0 -0
- {mx_bluesky-1.5.3.dist-info → mx_bluesky-1.5.5.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.5.3.dist-info → mx_bluesky-1.5.5.dist-info}/licenses/LICENSE +0 -0
- {mx_bluesky-1.5.3.dist-info → mx_bluesky-1.5.5.dist-info}/top_level.txt +0 -0
|
@@ -13,7 +13,6 @@ from dodal.devices.i24.beamstop import Beamstop
|
|
|
13
13
|
from dodal.devices.i24.dcm import DCM
|
|
14
14
|
from dodal.devices.i24.dual_backlight import BacklightPositions, DualBacklight
|
|
15
15
|
from dodal.devices.i24.focus_mirrors import FocusMirrorsMode
|
|
16
|
-
from dodal.devices.i24.pilatus_metadata import PilatusMetadata
|
|
17
16
|
from dodal.devices.i24.pmac import PMAC
|
|
18
17
|
from dodal.devices.motors import YZStage
|
|
19
18
|
from dodal.devices.oav.oav_detector import OAVBeamCentreFile
|
|
@@ -39,14 +38,13 @@ from mx_bluesky.beamlines.i24.serial.log import (
|
|
|
39
38
|
_read_visit_directory_from_file,
|
|
40
39
|
)
|
|
41
40
|
from mx_bluesky.beamlines.i24.serial.parameters import (
|
|
42
|
-
DetectorName,
|
|
43
41
|
FixedTargetParameters,
|
|
44
42
|
get_chip_format,
|
|
45
43
|
)
|
|
46
44
|
from mx_bluesky.beamlines.i24.serial.parameters.utils import EmptyMapError
|
|
47
45
|
from mx_bluesky.beamlines.i24.serial.setup_beamline import pv
|
|
48
46
|
from mx_bluesky.beamlines.i24.serial.setup_beamline.ca import caput
|
|
49
|
-
from mx_bluesky.beamlines.i24.serial.setup_beamline.pv_abstract import Eiger
|
|
47
|
+
from mx_bluesky.beamlines.i24.serial.setup_beamline.pv_abstract import Eiger
|
|
50
48
|
from mx_bluesky.beamlines.i24.serial.setup_beamline.setup_detector import (
|
|
51
49
|
_move_detector_stage,
|
|
52
50
|
get_detector_type,
|
|
@@ -103,10 +101,10 @@ def gui_sleep(sec: int) -> MsgGenerator:
|
|
|
103
101
|
|
|
104
102
|
@bpp.run_decorator()
|
|
105
103
|
def gui_move_detector(
|
|
106
|
-
det: Literal["eiger"
|
|
104
|
+
det: Literal["eiger"],
|
|
107
105
|
detector_stage: YZStage = inject("detector_motion"),
|
|
108
106
|
) -> MsgGenerator:
|
|
109
|
-
det_y_target = Eiger.det_y_target
|
|
107
|
+
det_y_target = Eiger.det_y_target
|
|
110
108
|
yield from _move_detector_stage(detector_stage, det_y_target)
|
|
111
109
|
# Make the output readable
|
|
112
110
|
SSX_LOGGER.debug(f"Detector move done, resetting general PV to {det}")
|
|
@@ -138,9 +136,7 @@ def gui_run_chip_collection(
|
|
|
138
136
|
shutter: HutchShutter = inject("shutter"),
|
|
139
137
|
dcm: DCM = inject("dcm"),
|
|
140
138
|
mirrors: FocusMirrorsMode = inject("focus_mirrors"),
|
|
141
|
-
beam_center_pilatus: DetectorBeamCenter = inject("pilatus_bc"),
|
|
142
139
|
beam_center_eiger: DetectorBeamCenter = inject("eiger_bc"),
|
|
143
|
-
pilatus_metadata: PilatusMetadata = inject("pilatus_meta"),
|
|
144
140
|
) -> MsgGenerator:
|
|
145
141
|
"""Set the parameter model for the data collection.
|
|
146
142
|
|
|
@@ -210,11 +206,7 @@ def gui_run_chip_collection(
|
|
|
210
206
|
if parameters.chip_map:
|
|
211
207
|
yield from upload_chip_map_to_geobrick(pmac, parameters.chip_map)
|
|
212
208
|
|
|
213
|
-
beam_center_device =
|
|
214
|
-
beam_center_eiger
|
|
215
|
-
if parameters.detector_name is DetectorName.EIGER
|
|
216
|
-
else beam_center_pilatus
|
|
217
|
-
)
|
|
209
|
+
beam_center_device = beam_center_eiger
|
|
218
210
|
SSX_LOGGER.info("Beam center device ready")
|
|
219
211
|
|
|
220
212
|
# DCID instance - do not create yet
|
|
@@ -234,5 +226,4 @@ def gui_run_chip_collection(
|
|
|
234
226
|
beam_center_device,
|
|
235
227
|
parameters,
|
|
236
228
|
dcid,
|
|
237
|
-
pilatus_metadata,
|
|
238
229
|
)
|
|
@@ -35,8 +35,9 @@ def call_nexgen(
|
|
|
35
35
|
start_time (datetime): Collection start time.
|
|
36
36
|
|
|
37
37
|
Raises:
|
|
38
|
-
ValueError: For a wrong experiment type passed (either
|
|
38
|
+
ValueError: For a wrong experiment type passed (either unknown or not matched \
|
|
39
39
|
to parameter model).
|
|
40
|
+
HTTPError: For a problem with reponse from server
|
|
40
41
|
|
|
41
42
|
"""
|
|
42
43
|
current_chip_map = None
|
|
@@ -75,10 +76,6 @@ def call_nexgen(
|
|
|
75
76
|
f"Call to nexgen server with the following chip definition: \n{chip_prog_dict}"
|
|
76
77
|
)
|
|
77
78
|
|
|
78
|
-
access_token = pathlib.Path("/scratch/ssx_nexgen.key").read_text().strip()
|
|
79
|
-
url = "https://ssx-nexgen.diamond.ac.uk/ssx_eiger/write"
|
|
80
|
-
headers = {"Authorization": f"Bearer {access_token}"}
|
|
81
|
-
|
|
82
79
|
payload = {
|
|
83
80
|
"beamline": "i24",
|
|
84
81
|
"beam_center": beam_center_in_pix,
|
|
@@ -98,8 +95,36 @@ def call_nexgen(
|
|
|
98
95
|
"bit_depth": bit_depth,
|
|
99
96
|
"start_time": start_time.isoformat(),
|
|
100
97
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
submit_to_server(payload)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def submit_to_server(
|
|
102
|
+
payload: dict | None,
|
|
103
|
+
):
|
|
104
|
+
"""Submit the payload to nexgen-server.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
payload (dict): Dictionary of parameters to send to nex-gen server
|
|
108
|
+
|
|
109
|
+
Raises:
|
|
110
|
+
ValueError: For a wrong experiment type passed (either unknown or not matched \
|
|
111
|
+
to parameter model).
|
|
112
|
+
HTTPError: For a problem with reponse from server
|
|
113
|
+
|
|
114
|
+
"""
|
|
115
|
+
access_token = pathlib.Path("/scratch/ssx_nexgen.key").read_text().strip()
|
|
116
|
+
url = "https://ssx-nexgen.diamond.ac.uk/ssx_eiger/write"
|
|
117
|
+
headers = {"Authorization": f"Bearer {access_token}"}
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
SSX_LOGGER.info(f"Sending POST request to {url} with payload:")
|
|
121
|
+
SSX_LOGGER.info(pprint.pformat(payload))
|
|
122
|
+
response = requests.post(url, headers=headers, json=payload)
|
|
123
|
+
response.raise_for_status()
|
|
124
|
+
except requests.HTTPError as e:
|
|
125
|
+
SSX_LOGGER.error(f"Nexus writer failed. Reason from server {e}")
|
|
126
|
+
raise
|
|
127
|
+
except Exception as e:
|
|
128
|
+
SSX_LOGGER.exception(f"Error generating nexus file: {e}")
|
|
129
|
+
raise
|
|
105
130
|
SSX_LOGGER.info(f"Response: {response.text} (status code: {response.status_code})")
|
|
@@ -5,7 +5,7 @@ from dodal.devices.aperturescatterguard import (
|
|
|
5
5
|
ApertureScatterguard,
|
|
6
6
|
ApertureValue,
|
|
7
7
|
)
|
|
8
|
-
from dodal.devices.backlight import Backlight,
|
|
8
|
+
from dodal.devices.backlight import Backlight, InOut
|
|
9
9
|
from dodal.devices.detector.detector_motion import DetectorMotion
|
|
10
10
|
from dodal.devices.smargon import CombinedMove, Smargon
|
|
11
11
|
|
|
@@ -22,7 +22,7 @@ def setup_sample_environment(
|
|
|
22
22
|
group="setup_senv",
|
|
23
23
|
):
|
|
24
24
|
"""Move the aperture into required position, move out the backlight."""
|
|
25
|
-
yield from bps.abs_set(backlight,
|
|
25
|
+
yield from bps.abs_set(backlight, InOut.OUT, group=group)
|
|
26
26
|
|
|
27
27
|
aperture_value = (
|
|
28
28
|
None
|
|
@@ -7,7 +7,7 @@ from bluesky import plan_stubs as bps
|
|
|
7
7
|
from bluesky import preprocessors as bpp
|
|
8
8
|
from bluesky.preprocessors import subs_decorator
|
|
9
9
|
from bluesky.utils import MsgGenerator
|
|
10
|
-
from dodal.devices.backlight import
|
|
10
|
+
from dodal.devices.backlight import InOut
|
|
11
11
|
from dodal.devices.eiger import EigerDetector
|
|
12
12
|
from dodal.devices.oav.oav_parameters import OAVParameters
|
|
13
13
|
|
|
@@ -168,7 +168,7 @@ def detect_grid_and_do_gridscan(
|
|
|
168
168
|
|
|
169
169
|
yield from bps.abs_set(
|
|
170
170
|
composite.backlight,
|
|
171
|
-
|
|
171
|
+
InOut.OUT,
|
|
172
172
|
group=PlanGroupCheckpointConstants.GRID_READY_FOR_DC,
|
|
173
173
|
)
|
|
174
174
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import bluesky.plan_stubs as bps
|
|
2
|
+
import pydantic
|
|
3
|
+
from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureValue
|
|
4
|
+
from dodal.devices.collimation_table import CollimationTable
|
|
5
|
+
from dodal.devices.cryostream import CryoStream
|
|
6
|
+
from dodal.devices.cryostream import InOut as CryoInOut
|
|
7
|
+
from dodal.devices.fluorescence_detector_motion import (
|
|
8
|
+
FluorescenceDetector,
|
|
9
|
+
)
|
|
10
|
+
from dodal.devices.fluorescence_detector_motion import InOut as FlouInOut
|
|
11
|
+
from dodal.devices.mx_phase1.beamstop import Beamstop, BeamstopPositions
|
|
12
|
+
from dodal.devices.scintillator import InOut as ScinInOut
|
|
13
|
+
from dodal.devices.scintillator import Scintillator
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
17
|
+
class UDCDefaultDevices:
|
|
18
|
+
cryostream: CryoStream
|
|
19
|
+
fluorescence_det_motion: FluorescenceDetector
|
|
20
|
+
beamstop: Beamstop
|
|
21
|
+
scintillator: Scintillator
|
|
22
|
+
aperture_scatterguard: ApertureScatterguard
|
|
23
|
+
collimation_table: CollimationTable
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def move_to_udc_default_state(devices: UDCDefaultDevices):
|
|
27
|
+
"""Moves beamline to known positions prior to UDC start"""
|
|
28
|
+
|
|
29
|
+
cryostream_temp = yield from bps.rd(devices.cryostream.temperature_k)
|
|
30
|
+
cryostream_pressure = yield from bps.rd(devices.cryostream.back_pressure_bar)
|
|
31
|
+
if cryostream_temp > devices.cryostream.MAX_TEMP_K:
|
|
32
|
+
raise ValueError("Cryostream temperature is too high, not starting UDC")
|
|
33
|
+
if cryostream_pressure > devices.cryostream.MAX_PRESSURE_BAR:
|
|
34
|
+
raise ValueError("Cryostream back pressure is too high, not starting UDC")
|
|
35
|
+
|
|
36
|
+
yield from bps.abs_set(devices.scintillator.selected_pos, ScinInOut.OUT, wait=True)
|
|
37
|
+
|
|
38
|
+
yield from bps.abs_set(
|
|
39
|
+
devices.fluorescence_det_motion.pos, FlouInOut.OUT, group="udc_default"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
yield from bps.abs_set(devices.collimation_table.inboard_y, 0, group="udc_default")
|
|
43
|
+
yield from bps.abs_set(devices.collimation_table.outboard_y, 0, group="udc_default")
|
|
44
|
+
yield from bps.abs_set(devices.collimation_table.upstream_y, 0, group="udc_default")
|
|
45
|
+
yield from bps.abs_set(devices.collimation_table.upstream_x, 0, group="udc_default")
|
|
46
|
+
yield from bps.abs_set(
|
|
47
|
+
devices.collimation_table.downstream_x, 0, group="udc_default"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
yield from bps.abs_set(
|
|
51
|
+
devices.beamstop.selected_pos,
|
|
52
|
+
BeamstopPositions.DATA_COLLECTION,
|
|
53
|
+
group="udc_default",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
yield from bps.abs_set(
|
|
57
|
+
devices.aperture_scatterguard.selected_aperture,
|
|
58
|
+
ApertureValue.SMALL,
|
|
59
|
+
group="udc_default",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
yield from bps.abs_set(devices.cryostream.course, CryoInOut.IN, group="udc_default")
|
|
63
|
+
yield from bps.abs_set(devices.cryostream.fine, CryoInOut.IN, group="udc_default")
|
|
64
|
+
|
|
65
|
+
yield from bps.wait("udc_default")
|
|
@@ -4,7 +4,7 @@ from typing import Protocol
|
|
|
4
4
|
from bluesky import plan_stubs as bps
|
|
5
5
|
from bluesky.utils import MsgGenerator
|
|
6
6
|
from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureValue
|
|
7
|
-
from dodal.devices.backlight import Backlight,
|
|
7
|
+
from dodal.devices.backlight import Backlight, InOut
|
|
8
8
|
from dodal.devices.oav.oav_detector import OAV
|
|
9
9
|
from dodal.devices.oav.oav_parameters import OAVParameters
|
|
10
10
|
from dodal.devices.smargon import Smargon
|
|
@@ -36,7 +36,7 @@ def setup_beamline_for_OAV(
|
|
|
36
36
|
):
|
|
37
37
|
max_vel = yield from bps.rd(smargon.omega.max_velocity)
|
|
38
38
|
yield from bps.abs_set(smargon.omega.velocity, max_vel, group=group)
|
|
39
|
-
yield from bps.abs_set(backlight,
|
|
39
|
+
yield from bps.abs_set(backlight, InOut.IN, group=group)
|
|
40
40
|
yield from bps.abs_set(
|
|
41
41
|
aperture_scatterguard.selected_aperture, ApertureValue.OUT_OF_BEAM, group=group
|
|
42
42
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import TypedDict
|
|
1
|
+
from typing import Generic, TypedDict, TypeVar
|
|
2
2
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
from bluesky.callbacks import CallbackBase
|
|
@@ -7,6 +7,8 @@ from event_model.documents import Event
|
|
|
7
7
|
|
|
8
8
|
from mx_bluesky.common.utils.log import LOGGER
|
|
9
9
|
|
|
10
|
+
T = TypeVar("T", int, float)
|
|
11
|
+
|
|
10
12
|
|
|
11
13
|
class GridParamUpdate(TypedDict):
|
|
12
14
|
"""
|
|
@@ -40,14 +42,22 @@ class GridParamUpdate(TypedDict):
|
|
|
40
42
|
z_step_size_um: float
|
|
41
43
|
|
|
42
44
|
|
|
45
|
+
class XYZParams(TypedDict, Generic[T]):
|
|
46
|
+
x: T
|
|
47
|
+
y: T
|
|
48
|
+
z: T
|
|
49
|
+
|
|
50
|
+
|
|
43
51
|
class GridDetectionCallback(CallbackBase):
|
|
52
|
+
OMEGA_TOLERANCE = 1
|
|
53
|
+
|
|
44
54
|
def __init__(
|
|
45
55
|
self,
|
|
46
56
|
*args,
|
|
47
57
|
) -> None:
|
|
48
58
|
super().__init__(*args)
|
|
49
|
-
self.
|
|
50
|
-
self.box_numbers:
|
|
59
|
+
self.start_positions_um: XYZParams[float] = XYZParams(x=0, y=0, z=0)
|
|
60
|
+
self.box_numbers: XYZParams[int] = XYZParams(x=0, y=0, z=0)
|
|
51
61
|
|
|
52
62
|
def event(self, doc: Event):
|
|
53
63
|
data = doc.get("data")
|
|
@@ -82,13 +92,21 @@ class GridDetectionCallback(CallbackBase):
|
|
|
82
92
|
)
|
|
83
93
|
LOGGER.info(f"Calculated start position {position_grid_start_mm}")
|
|
84
94
|
|
|
85
|
-
|
|
86
|
-
self.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
95
|
+
# If data is taken at omega=~0 then it gives us x-y info, at omega=~-90 it is x-z
|
|
96
|
+
if abs(smargon_omega) < self.OMEGA_TOLERANCE:
|
|
97
|
+
self.start_positions_um["x"] = position_grid_start_mm[0] * 1000
|
|
98
|
+
self.start_positions_um["y"] = position_grid_start_mm[1] * 1000
|
|
99
|
+
self.box_numbers["x"] = data["oav-grid_snapshot-num_boxes_x"]
|
|
100
|
+
self.box_numbers["y"] = data["oav-grid_snapshot-num_boxes_y"]
|
|
101
|
+
elif abs(smargon_omega + 90) < self.OMEGA_TOLERANCE:
|
|
102
|
+
self.start_positions_um["x"] = position_grid_start_mm[0] * 1000
|
|
103
|
+
self.start_positions_um["z"] = position_grid_start_mm[2] * 1000
|
|
104
|
+
self.box_numbers["x"] = data["oav-grid_snapshot-num_boxes_x"]
|
|
105
|
+
self.box_numbers["z"] = data["oav-grid_snapshot-num_boxes_y"]
|
|
106
|
+
else:
|
|
107
|
+
raise ValueError(
|
|
108
|
+
f"Grid detection only works at omegas of 0 or -90, omega of {smargon_omega} given."
|
|
90
109
|
)
|
|
91
|
-
)
|
|
92
110
|
|
|
93
111
|
self.x_step_size_um = box_width_px * microns_per_pixel_x
|
|
94
112
|
self.y_step_size_um = box_width_px * microns_per_pixel_y
|
|
@@ -97,14 +115,14 @@ class GridDetectionCallback(CallbackBase):
|
|
|
97
115
|
|
|
98
116
|
def get_grid_parameters(self) -> GridParamUpdate:
|
|
99
117
|
return {
|
|
100
|
-
"x_start_um": self.
|
|
101
|
-
"y_start_um": self.
|
|
102
|
-
"y2_start_um": self.
|
|
103
|
-
"z_start_um": self.
|
|
104
|
-
"z2_start_um": self.
|
|
105
|
-
"x_steps": self.box_numbers[
|
|
106
|
-
"y_steps": self.box_numbers[
|
|
107
|
-
"z_steps": self.box_numbers[
|
|
118
|
+
"x_start_um": self.start_positions_um["x"],
|
|
119
|
+
"y_start_um": self.start_positions_um["y"],
|
|
120
|
+
"y2_start_um": self.start_positions_um["y"],
|
|
121
|
+
"z_start_um": self.start_positions_um["z"],
|
|
122
|
+
"z2_start_um": self.start_positions_um["z"],
|
|
123
|
+
"x_steps": self.box_numbers["x"],
|
|
124
|
+
"y_steps": self.box_numbers["y"],
|
|
125
|
+
"z_steps": self.box_numbers["z"],
|
|
108
126
|
"x_step_size_um": self.x_step_size_um,
|
|
109
127
|
"y_step_size_um": self.y_step_size_um,
|
|
110
128
|
"z_step_size_um": self.z_step_size_um,
|
|
@@ -186,7 +186,7 @@ class BaseISPyBCallback(PlanReactiveCallback):
|
|
|
186
186
|
pass
|
|
187
187
|
|
|
188
188
|
def activity_gated_stop(self, doc: RunStop) -> RunStop:
|
|
189
|
-
"""Subclasses must check that they are
|
|
189
|
+
"""Subclasses must check that they are receiving a stop document for the correct
|
|
190
190
|
uid to use this method!"""
|
|
191
191
|
assert self.ispyb is not None, (
|
|
192
192
|
"ISPyB handler received stop document, but deposition object doesn't exist!"
|
|
@@ -24,8 +24,8 @@ class PlanReactiveCallback(CallbackBase):
|
|
|
24
24
|
metadata to trigger this.
|
|
25
25
|
The run_decorator of the plan should include in its metadata dictionary the key
|
|
26
26
|
'activate callbacks', with a list of strings of the callback class(es) to
|
|
27
|
-
activate or deactivate. On a
|
|
28
|
-
class will be activated, and on
|
|
27
|
+
activate or deactivate. On a receiving a start doc which specifies this, this
|
|
28
|
+
class will be activated, and on receiving the stop document for the
|
|
29
29
|
corresponding uid it will deactivate. The ordinary 'start', 'descriptor',
|
|
30
30
|
'event' and 'stop' methods will be triggered as normal, and will in turn trigger
|
|
31
31
|
'activity_gated_' methods - to preserve this functionality, subclasses which
|
mx_bluesky/common/external_interaction/callbacks/sample_handling/sample_handling_callback.py
CHANGED
|
@@ -17,8 +17,9 @@ from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class SampleHandlingCallback(PlanReactiveCallback):
|
|
20
|
-
"""Intercepts exceptions from experiment plans and
|
|
21
|
-
field according to the type of exception raised.
|
|
20
|
+
"""Intercepts exceptions from experiment plans and:
|
|
21
|
+
* Updates the ISPyB BLSampleStatus field according to the type of exception raised.
|
|
22
|
+
* Triggers an alert with details of the error."""
|
|
22
23
|
|
|
23
24
|
def __init__(self, record_loaded_on_success=False):
|
|
24
25
|
super().__init__(log=ISPYB_ZOCALO_CALLBACK_LOGGER)
|
|
@@ -69,8 +69,8 @@ ispyb_activation_decorator = make_decorator(ispyb_activation_wrapper)
|
|
|
69
69
|
class GridscanISPyBCallback(BaseISPyBCallback):
|
|
70
70
|
"""Callback class to handle the deposition of experiment parameters into the ISPyB
|
|
71
71
|
database. Listens for 'event' and 'descriptor' documents. Creates the ISpyB entry on
|
|
72
|
-
|
|
73
|
-
deposition on
|
|
72
|
+
receiving an 'event' document for the 'ispyb_reading_hardware' event, and updates the
|
|
73
|
+
deposition on receiving its final 'stop' document.
|
|
74
74
|
|
|
75
75
|
To use, subscribe the Bluesky RunEngine to an instance of this class.
|
|
76
76
|
E.g.:
|
|
@@ -24,10 +24,10 @@ T = TypeVar("T", bound="SpecifiedThreeDGridScan")
|
|
|
24
24
|
|
|
25
25
|
class GridscanNexusFileCallback(PlanReactiveCallback):
|
|
26
26
|
"""Callback class to handle the creation of Nexus files based on experiment \
|
|
27
|
-
parameters. Initialises on
|
|
27
|
+
parameters. Initialises on receiving a 'start' document for the \
|
|
28
28
|
'run_gridscan_move_and_tidy' sub plan, which must also contain the run parameters, \
|
|
29
29
|
as metadata under the 'hyperion_internal_parameters' key. Actually writes the \
|
|
30
|
-
nexus files on updates the timestamps on
|
|
30
|
+
nexus files on updates the timestamps on receiving the 'ispyb_reading_hardware' event \
|
|
31
31
|
document, and finalises the files on getting a 'stop' document for the whole run.
|
|
32
32
|
|
|
33
33
|
To use, subscribe the Bluesky RunEngine to an instance of this class.
|
|
@@ -23,6 +23,7 @@ GDA_DOMAIN_PROPERTIES_PATH = (
|
|
|
23
23
|
@dataclass(frozen=True)
|
|
24
24
|
class DocDescriptorNames:
|
|
25
25
|
# Robot load/unload event descriptor
|
|
26
|
+
ROBOT_PRE_LOAD = "robot_update_pre_load"
|
|
26
27
|
ROBOT_UPDATE = "robot_update"
|
|
27
28
|
# For callbacks to use
|
|
28
29
|
OAV_ROTATION_SNAPSHOT_TRIGGERED = "rotation_snapshot_triggered"
|
|
@@ -8,8 +8,17 @@ from bluesky import preprocessors as bpp
|
|
|
8
8
|
from bluesky.utils import MsgGenerator, RunEngineInterrupted
|
|
9
9
|
from dodal.devices.baton import Baton
|
|
10
10
|
|
|
11
|
+
from mx_bluesky.common.experiment_plans.inner_plans.udc_default_state import (
|
|
12
|
+
UDCDefaultDevices,
|
|
13
|
+
move_to_udc_default_state,
|
|
14
|
+
)
|
|
15
|
+
from mx_bluesky.common.external_interaction.alerting import (
|
|
16
|
+
AlertService,
|
|
17
|
+
get_alerting_service,
|
|
18
|
+
)
|
|
11
19
|
from mx_bluesky.common.parameters.components import MxBlueskyParameters
|
|
12
20
|
from mx_bluesky.common.utils.context import (
|
|
21
|
+
device_composite_from_context,
|
|
13
22
|
find_device_in_context,
|
|
14
23
|
)
|
|
15
24
|
from mx_bluesky.common.utils.log import LOGGER
|
|
@@ -20,6 +29,7 @@ from mx_bluesky.hyperion.experiment_plans.load_centre_collect_full_plan import (
|
|
|
20
29
|
from mx_bluesky.hyperion.external_interaction.agamemnon import (
|
|
21
30
|
create_parameters_from_agamemnon,
|
|
22
31
|
)
|
|
32
|
+
from mx_bluesky.hyperion.external_interaction.alerting.constants import Subjects
|
|
23
33
|
from mx_bluesky.hyperion.parameters.components import Wait
|
|
24
34
|
from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect
|
|
25
35
|
from mx_bluesky.hyperion.plan_runner import PlanException, PlanRunner
|
|
@@ -81,7 +91,8 @@ def run_udc_when_requested(context: BlueskyContext, runner: PlanRunner):
|
|
|
81
91
|
baton: The baton device
|
|
82
92
|
runner: The runner
|
|
83
93
|
"""
|
|
84
|
-
|
|
94
|
+
_raise_udc_start_alert(get_alerting_service())
|
|
95
|
+
yield from _move_to_udc_default_state(context)
|
|
85
96
|
|
|
86
97
|
# re-fetch the baton because the device has been reinstantiated
|
|
87
98
|
baton = _get_baton(context)
|
|
@@ -92,8 +103,9 @@ def run_udc_when_requested(context: BlueskyContext, runner: PlanRunner):
|
|
|
92
103
|
# If hyperion has given up the baton itself we need to also release requested
|
|
93
104
|
# user so that hyperion doesn't think we're requested again
|
|
94
105
|
baton = _get_baton(context)
|
|
95
|
-
yield from _safely_release_baton(baton)
|
|
96
|
-
yield from bps.abs_set(baton.current_user, NO_USER)
|
|
106
|
+
previous_requested_user = yield from _safely_release_baton(baton)
|
|
107
|
+
yield from bps.abs_set(baton.current_user, NO_USER, wait=True)
|
|
108
|
+
_raise_baton_released_alert(get_alerting_service(), previous_requested_user)
|
|
97
109
|
|
|
98
110
|
def collect_then_release() -> MsgGenerator:
|
|
99
111
|
yield from bpp.contingency_wrapper(collect(), final_plan=release_baton)
|
|
@@ -147,10 +159,34 @@ def _fetch_and_process_agamemnon_instruction(
|
|
|
147
159
|
f"Unsupported instruction decoded from agamemnon {type(parameters)}"
|
|
148
160
|
)
|
|
149
161
|
else:
|
|
162
|
+
_raise_udc_completed_alert(get_alerting_service())
|
|
150
163
|
# Release the baton for orderly exit from the instruction loop
|
|
151
164
|
yield from _safely_release_baton(baton)
|
|
152
165
|
|
|
153
166
|
|
|
167
|
+
def _raise_udc_start_alert(alert_service: AlertService):
|
|
168
|
+
alert_service.raise_alert(
|
|
169
|
+
Subjects.UDC_STARTED, "Unattended Data Collection has started.", {}
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _raise_baton_released_alert(alert_service: AlertService, baton_requester: str):
|
|
174
|
+
alert_service.raise_alert(
|
|
175
|
+
Subjects.UDC_BATON_RELEASED,
|
|
176
|
+
f"Hyperion has released the baton. The baton is currently requested by:"
|
|
177
|
+
f" {baton_requester}",
|
|
178
|
+
{},
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _raise_udc_completed_alert(alert_service: AlertService):
|
|
183
|
+
alert_service.raise_alert(
|
|
184
|
+
Subjects.UDC_COMPLETED,
|
|
185
|
+
"Hyperion UDC has completed all pending Agamemnon requests.",
|
|
186
|
+
{},
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
154
190
|
def _runner_sleep(parameters: Wait) -> MsgGenerator:
|
|
155
191
|
yield from bps.sleep(parameters.duration_s)
|
|
156
192
|
|
|
@@ -160,18 +196,24 @@ def _is_requesting_baton(baton: Baton) -> MsgGenerator:
|
|
|
160
196
|
return requested_user == HYPERION_USER
|
|
161
197
|
|
|
162
198
|
|
|
163
|
-
def
|
|
164
|
-
|
|
165
|
-
yield from
|
|
199
|
+
def _move_to_udc_default_state(context: BlueskyContext):
|
|
200
|
+
udc_default_devices = device_composite_from_context(context, UDCDefaultDevices)
|
|
201
|
+
yield from move_to_udc_default_state(udc_default_devices)
|
|
166
202
|
|
|
167
203
|
|
|
168
204
|
def _get_baton(context: BlueskyContext) -> Baton:
|
|
169
205
|
return find_device_in_context(context, "baton", Baton)
|
|
170
206
|
|
|
171
207
|
|
|
172
|
-
def _safely_release_baton(baton: Baton) -> MsgGenerator:
|
|
208
|
+
def _safely_release_baton(baton: Baton) -> MsgGenerator[str]:
|
|
173
209
|
"""Relinquish the requested user of the baton if it is not already requested
|
|
174
|
-
by another user.
|
|
210
|
+
by another user.
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
The previously requested user, or NO_USER if no user was already requested.
|
|
214
|
+
"""
|
|
175
215
|
requested_user = yield from bps.rd(baton.requested_user)
|
|
176
216
|
if requested_user == HYPERION_USER:
|
|
177
217
|
yield from bps.abs_set(baton.requested_user, NO_USER)
|
|
218
|
+
return NO_USER
|
|
219
|
+
return requested_user
|
|
@@ -69,7 +69,11 @@ def load_centre_collect_full(
|
|
|
69
69
|
"visit": parameters.visit,
|
|
70
70
|
"container": parameters.sample_puck,
|
|
71
71
|
},
|
|
72
|
-
"activate_callbacks": [
|
|
72
|
+
"activate_callbacks": [
|
|
73
|
+
"BeamDrawingCallback",
|
|
74
|
+
"SampleHandlingCallback",
|
|
75
|
+
"AlertOnContainerChange",
|
|
76
|
+
],
|
|
73
77
|
"with_snapshot": parameters.multi_rotation_scan.model_dump_json(
|
|
74
78
|
include=WithSnapshot.model_fields.keys() # type: ignore
|
|
75
79
|
),
|
|
@@ -12,7 +12,7 @@ from blueapi.core import BlueskyContext
|
|
|
12
12
|
from bluesky.utils import Msg
|
|
13
13
|
from dodal.devices.aperturescatterguard import ApertureScatterguard
|
|
14
14
|
from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
|
|
15
|
-
from dodal.devices.backlight import Backlight,
|
|
15
|
+
from dodal.devices.backlight import Backlight, InOut
|
|
16
16
|
from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, MirrorVoltages
|
|
17
17
|
from dodal.devices.i03.dcm import DCM
|
|
18
18
|
from dodal.devices.i03.undulator_dcm import UndulatorDCM
|
|
@@ -123,7 +123,11 @@ def robot_load_and_snapshots(
|
|
|
123
123
|
thawing_time: float,
|
|
124
124
|
demand_energy_ev: float | None,
|
|
125
125
|
):
|
|
126
|
-
yield from bps.abs_set(composite.backlight,
|
|
126
|
+
yield from bps.abs_set(composite.backlight, InOut.IN, group="snapshot")
|
|
127
|
+
|
|
128
|
+
yield from bps.create(name=CONST.DESCRIPTORS.ROBOT_PRE_LOAD)
|
|
129
|
+
yield from bps.read(composite.robot)
|
|
130
|
+
yield from bps.save()
|
|
127
131
|
|
|
128
132
|
robot_load_plan = do_robot_load(
|
|
129
133
|
composite,
|
|
@@ -3,10 +3,5 @@ from enum import StrEnum
|
|
|
3
3
|
|
|
4
4
|
class Subjects(StrEnum):
|
|
5
5
|
UDC_STARTED = "UDC Started"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
UDC_SUSPENDED_OPERATION = "UDC Suspended operation"
|
|
9
|
-
NEW_CONTAINER = "Hyperion is collecting from a new container"
|
|
10
|
-
NEW_VISIT = "Hyperion has changed visit"
|
|
11
|
-
SAMPLE_ERROR = "Hyperion has encountered a sample error"
|
|
12
|
-
BEAMLINE_ERROR = "Hyperion has encountered a beamline error"
|
|
6
|
+
UDC_BATON_RELEASED = "UDC Baton was released"
|
|
7
|
+
UDC_COMPLETED = "UDC Completed"
|
|
@@ -33,6 +33,9 @@ from mx_bluesky.common.utils.log import (
|
|
|
33
33
|
_get_logging_dirs,
|
|
34
34
|
tag_filter,
|
|
35
35
|
)
|
|
36
|
+
from mx_bluesky.hyperion.external_interaction.callbacks.alert_on_container_change import (
|
|
37
|
+
AlertOnContainerChange,
|
|
38
|
+
)
|
|
36
39
|
from mx_bluesky.hyperion.external_interaction.callbacks.robot_actions.ispyb_callback import (
|
|
37
40
|
RobotLoadISPyBCallback,
|
|
38
41
|
)
|
|
@@ -89,6 +92,7 @@ def setup_callbacks() -> list[CallbackBase]:
|
|
|
89
92
|
LogUidTaggingCallback(),
|
|
90
93
|
RobotLoadISPyBCallback(),
|
|
91
94
|
SampleHandlingCallback(),
|
|
95
|
+
AlertOnContainerChange(),
|
|
92
96
|
]
|
|
93
97
|
|
|
94
98
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from dodal.utils import get_beamline_name
|
|
2
|
+
from event_model import Event, EventDescriptor, RunStart
|
|
3
|
+
|
|
4
|
+
from mx_bluesky.common.external_interaction.alerting import (
|
|
5
|
+
Metadata,
|
|
6
|
+
get_alerting_service,
|
|
7
|
+
)
|
|
8
|
+
from mx_bluesky.common.external_interaction.callbacks.common.plan_reactive_callback import (
|
|
9
|
+
PlanReactiveCallback,
|
|
10
|
+
)
|
|
11
|
+
from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER
|
|
12
|
+
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AlertOnContainerChange(PlanReactiveCallback):
|
|
16
|
+
"""Sends an alert to beamline staff when a pin from a new puck has been loaded.
|
|
17
|
+
This tends to be used as a heartbeat so we know that UDC is running."""
|
|
18
|
+
|
|
19
|
+
def __init__(self):
|
|
20
|
+
super().__init__(log=ISPYB_ZOCALO_CALLBACK_LOGGER)
|
|
21
|
+
self._new_container = None
|
|
22
|
+
self._visit = None
|
|
23
|
+
self._sample_id = None
|
|
24
|
+
self.descriptors: dict[str, EventDescriptor] = {}
|
|
25
|
+
|
|
26
|
+
def activity_gated_descriptor(self, doc: EventDescriptor) -> EventDescriptor | None:
|
|
27
|
+
self.descriptors[doc["uid"]] = doc
|
|
28
|
+
return super().activity_gated_descriptor(doc)
|
|
29
|
+
|
|
30
|
+
def activity_gated_event(self, doc: Event) -> Event | None:
|
|
31
|
+
event_descriptor = self.descriptors.get(doc["descriptor"])
|
|
32
|
+
if (
|
|
33
|
+
event_descriptor
|
|
34
|
+
and event_descriptor.get("name") == CONST.DESCRIPTORS.ROBOT_PRE_LOAD
|
|
35
|
+
):
|
|
36
|
+
current_container = int(doc["data"]["robot-current_puck"])
|
|
37
|
+
if self._new_container != current_container:
|
|
38
|
+
beamline = get_beamline_name("")
|
|
39
|
+
get_alerting_service().raise_alert(
|
|
40
|
+
f"UDC moved on to puck {self._new_container} on {beamline}",
|
|
41
|
+
f"Hyperion finished container {current_container} and moved on to {self._new_container}",
|
|
42
|
+
{
|
|
43
|
+
Metadata.SAMPLE_ID: str(self._sample_id),
|
|
44
|
+
Metadata.VISIT: self._visit or "",
|
|
45
|
+
Metadata.CONTAINER: str(self._new_container),
|
|
46
|
+
},
|
|
47
|
+
)
|
|
48
|
+
return doc
|
|
49
|
+
|
|
50
|
+
def activity_gated_start(self, doc: RunStart):
|
|
51
|
+
metadata = doc.get("metadata", {})
|
|
52
|
+
self._new_container = metadata.get("container")
|
|
53
|
+
self._sample_id = metadata.get("sample_id")
|
|
54
|
+
self._visit = metadata.get("visit")
|
|
@@ -34,8 +34,8 @@ if TYPE_CHECKING:
|
|
|
34
34
|
class RotationISPyBCallback(BaseISPyBCallback):
|
|
35
35
|
"""Callback class to handle the deposition of experiment parameters into the ISPyB
|
|
36
36
|
database. Listens for 'event' and 'descriptor' documents. Creates the ISpyB entry on
|
|
37
|
-
|
|
38
|
-
deposition on
|
|
37
|
+
receiving an 'event' document for the 'ispyb_reading_hardware' event, and updates the
|
|
38
|
+
deposition on receiving its final 'stop' document.
|
|
39
39
|
|
|
40
40
|
To use, subscribe the Bluesky RunEngine to an instance of this class.
|
|
41
41
|
E.g.:
|