mx-bluesky 1.4.0__py3-none-any.whl → 1.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mx_bluesky/_version.py +2 -2
- mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +178 -0
- mx_bluesky/beamlines/i04/thawing_plan.py +1 -1
- mx_bluesky/beamlines/i24/serial/dcid.py +143 -171
- mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +1 -1
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +54 -21
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +2 -5
- mx_bluesky/beamlines/i24/serial/fixed_target/ft_utils.py +0 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +67 -50
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +26 -79
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +0 -199
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +4 -6
- mx_bluesky/beamlines/i24/serial/log.py +1 -1
- mx_bluesky/beamlines/i24/serial/parameters/__init__.py +4 -0
- mx_bluesky/beamlines/i24/serial/parameters/constants.py +6 -1
- mx_bluesky/beamlines/i24/serial/parameters/experiment_parameters.py +42 -15
- mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +4 -3
- mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +2 -0
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +103 -81
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +1 -2
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +24 -26
- mx_bluesky/beamlines/i24/serial/write_nexus.py +74 -72
- mx_bluesky/common/external_interaction/config_server.py +46 -0
- mx_bluesky/common/parameters/components.py +52 -15
- mx_bluesky/common/parameters/constants.py +11 -1
- mx_bluesky/common/parameters/gridscan.py +94 -0
- mx_bluesky/{hyperion → common}/parameters/robot_load.py +2 -2
- mx_bluesky/common/plans/do_fgs.py +2 -2
- mx_bluesky/common/utils/log.py +2 -0
- mx_bluesky/hyperion/__main__.py +2 -1
- mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +21 -31
- mx_bluesky/hyperion/device_setup_plans/setup_panda.py +4 -4
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +1 -1
- mx_bluesky/hyperion/device_setup_plans/smargon.py +3 -3
- mx_bluesky/hyperion/exceptions.py +13 -1
- mx_bluesky/hyperion/experiment_plans/__init__.py +4 -0
- mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +83 -0
- mx_bluesky/hyperion/experiment_plans/common/xrc_result.py +47 -0
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -9
- mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +133 -97
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +42 -18
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +75 -9
- mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +2 -2
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +1 -1
- mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +2 -2
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +36 -17
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +5 -5
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +28 -28
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +64 -16
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +11 -3
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +10 -10
- mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +0 -4
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +4 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/abstract_event.py +66 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +5 -0
- mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +15 -15
- mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +18 -10
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +3 -1
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +5 -3
- mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/sample_handling_callback.py +84 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +15 -9
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +5 -4
- mx_bluesky/hyperion/external_interaction/config_server.py +8 -37
- mx_bluesky/hyperion/external_interaction/exceptions.py +0 -9
- mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +65 -15
- mx_bluesky/hyperion/parameters/components.py +4 -9
- mx_bluesky/hyperion/parameters/constants.py +0 -1
- mx_bluesky/hyperion/parameters/gridscan.py +33 -76
- mx_bluesky/hyperion/parameters/load_centre_collect.py +14 -9
- mx_bluesky/hyperion/parameters/rotation.py +15 -6
- {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1.dist-info}/METADATA +35 -34
- {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1.dist-info}/RECORD +77 -70
- {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1.dist-info}/WHEEL +1 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +0 -150
- {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1.dist-info}/LICENSE +0 -0
- {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1.dist-info}/top_level.txt +0 -0
|
@@ -1,32 +1,27 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import json
|
|
3
|
-
import logging
|
|
4
3
|
import math
|
|
5
4
|
import os
|
|
6
|
-
import re
|
|
7
5
|
import subprocess
|
|
8
|
-
import warnings
|
|
9
6
|
from functools import lru_cache
|
|
7
|
+
from typing import Literal
|
|
10
8
|
|
|
9
|
+
import bluesky.plan_stubs as bps
|
|
11
10
|
import requests
|
|
12
|
-
|
|
13
|
-
from
|
|
14
|
-
from
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
11
|
+
from dodal.beamlines import i24
|
|
12
|
+
from dodal.devices.i24.beam_center import DetectorBeamCenter
|
|
13
|
+
from dodal.devices.i24.dcm import DCM
|
|
14
|
+
from dodal.devices.i24.focus_mirrors import FocusMirrorsMode
|
|
15
|
+
from dodal.devices.i24.pilatus_metadata import PilatusMetadata
|
|
16
|
+
|
|
17
|
+
from mx_bluesky.beamlines.i24.serial.fixed_target.ft_utils import PumpProbeSetting
|
|
18
|
+
from mx_bluesky.beamlines.i24.serial.log import SSX_LOGGER
|
|
19
|
+
from mx_bluesky.beamlines.i24.serial.parameters import (
|
|
20
|
+
BeamSettings,
|
|
21
|
+
ExtruderParameters,
|
|
22
|
+
FixedTargetParameters,
|
|
21
23
|
)
|
|
22
|
-
|
|
23
|
-
try:
|
|
24
|
-
from typing import Literal
|
|
25
|
-
except ImportError:
|
|
26
|
-
pass
|
|
27
|
-
|
|
28
|
-
logger = logging.getLogger("I24ssx.DCID")
|
|
29
|
-
|
|
24
|
+
from mx_bluesky.beamlines.i24.serial.setup_beamline import Detector, Eiger, Pilatus
|
|
30
25
|
|
|
31
26
|
# Collection start/end script to kick off analysis
|
|
32
27
|
COLLECTION_START_SCRIPT = "/dls_sw/i24/scripts/RunAtStartOfCollect-i24-ssx.sh"
|
|
@@ -41,7 +36,7 @@ CREDENTIALS_LOCATION = "/scratch/ssx_dcserver.key"
|
|
|
41
36
|
def get_auth_header() -> dict:
|
|
42
37
|
"""Read the credentials file and build the Authorisation header"""
|
|
43
38
|
if not os.path.isfile(CREDENTIALS_LOCATION):
|
|
44
|
-
|
|
39
|
+
SSX_LOGGER.warning(
|
|
45
40
|
"Could not read %s; attempting to proceed without credentials",
|
|
46
41
|
CREDENTIALS_LOCATION,
|
|
47
42
|
)
|
|
@@ -51,19 +46,54 @@ def get_auth_header() -> dict:
|
|
|
51
46
|
return {"Authorization": "Bearer " + token}
|
|
52
47
|
|
|
53
48
|
|
|
54
|
-
|
|
49
|
+
def read_beam_info_from_hardware(
|
|
50
|
+
dcm: DCM,
|
|
51
|
+
mirrors: FocusMirrorsMode,
|
|
52
|
+
beam_center: DetectorBeamCenter,
|
|
53
|
+
detector_name: Literal["eiger", "pilatus"],
|
|
54
|
+
):
|
|
55
|
+
""" Read the beam information from hardware.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
dcm (DCM): The decm device.
|
|
59
|
+
mirrors (FocusMirrorMode): The device describing the focus mirror mode settings.
|
|
60
|
+
beam_center (DetectorBeamCenter): A device to set and read the beam center on \
|
|
61
|
+
the detector.
|
|
62
|
+
detector_name (Literal["eiger", "pilatus"]): The detector currently in use.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
BeamSettings parameter model.
|
|
55
66
|
"""
|
|
56
|
-
|
|
67
|
+
wavelength = yield from bps.rd(dcm.wavelength_in_a)
|
|
68
|
+
beamsize_x = yield from bps.rd(mirrors.beam_size_x)
|
|
69
|
+
beamsize_y = yield from bps.rd(mirrors.beam_size_y)
|
|
70
|
+
pixel_size = (
|
|
71
|
+
Eiger().pixel_size_mm if detector_name == "eiger" else Pilatus().pixel_size_mm
|
|
72
|
+
)
|
|
73
|
+
beam_center_x = yield from bps.rd(beam_center.beam_x)
|
|
74
|
+
beam_center_y = yield from bps.rd(beam_center.beam_y)
|
|
75
|
+
return BeamSettings(
|
|
76
|
+
wavelength_in_a=wavelength,
|
|
77
|
+
beam_size_in_um=(beamsize_x, beamsize_y),
|
|
78
|
+
beam_center_in_mm=(
|
|
79
|
+
beam_center_x * pixel_size[0],
|
|
80
|
+
beam_center_y * pixel_size[1],
|
|
81
|
+
),
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class DCID:
|
|
86
|
+
""" Interfaces with ISPyB to allow ssx DCID/synchweb interaction.
|
|
57
87
|
|
|
58
88
|
Args:
|
|
59
|
-
server: The URL for the bridge server, if not the default.
|
|
60
|
-
emit_errors:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
89
|
+
server (str, optional): The URL for the bridge server, if not the default.
|
|
90
|
+
emit_errors (bool, optional): If False, errors while interacting with the DCID \
|
|
91
|
+
server will not be propagated to the caller. This decides if you want to \
|
|
92
|
+
stop collection if you can't get a DCID. Defaults to True.
|
|
93
|
+
timeout (float, optional): Length of time in s to wait for the DB server before \
|
|
94
|
+
giving up. Defaults to 10 s.
|
|
95
|
+
expt_parameters (ExtruderParameters | FixedTargetParameters): Collection \
|
|
96
|
+
parameters input by user.
|
|
67
97
|
|
|
68
98
|
|
|
69
99
|
Attributes:
|
|
@@ -77,47 +107,44 @@ class DCID:
|
|
|
77
107
|
server: str | None = None,
|
|
78
108
|
emit_errors: bool = True,
|
|
79
109
|
timeout: float = 10,
|
|
80
|
-
|
|
81
|
-
detector: Detector | Literal["eiger", "pilatus"] | None = None,
|
|
110
|
+
expt_params: ExtruderParameters | FixedTargetParameters,
|
|
82
111
|
):
|
|
112
|
+
self.parameters = expt_params
|
|
83
113
|
self.detector: Detector
|
|
84
114
|
# Handle case of string literal
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
self.detector = Pilatus()
|
|
91
|
-
warnings.warn(
|
|
92
|
-
"Please pass detector= to DCID. Pilatus assumed, this will be removed in the future.",
|
|
93
|
-
UserWarning,
|
|
94
|
-
stacklevel=5,
|
|
95
|
-
)
|
|
115
|
+
match expt_params.detector_name:
|
|
116
|
+
case "eiger":
|
|
117
|
+
self.detector = Eiger()
|
|
118
|
+
case "pilatus":
|
|
119
|
+
self.detector = Pilatus()
|
|
96
120
|
|
|
97
121
|
self.server = server or DEFAULT_ISPYB_SERVER
|
|
98
122
|
self.emit_errors = emit_errors
|
|
99
123
|
self.error = False
|
|
100
124
|
self.timeout = timeout
|
|
101
|
-
self.ssx_type = SSXType(ssx_type)
|
|
102
125
|
self.dcid = None
|
|
103
126
|
|
|
104
127
|
def generate_dcid(
|
|
105
128
|
self,
|
|
106
|
-
|
|
129
|
+
beam_settings: BeamSettings,
|
|
107
130
|
image_dir: str,
|
|
131
|
+
file_template: str,
|
|
108
132
|
num_images: int,
|
|
109
|
-
exposure_time: float,
|
|
110
|
-
start_time: datetime.datetime | None = None,
|
|
111
133
|
shots_per_position: int = 1,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
pump_status: int = 0,
|
|
134
|
+
start_time: datetime.datetime | None = None,
|
|
135
|
+
pump_probe: bool = False,
|
|
115
136
|
):
|
|
116
137
|
"""Generate an ispyb DCID.
|
|
117
138
|
|
|
118
139
|
Args:
|
|
119
|
-
|
|
120
|
-
image_dir: The location the images will be written
|
|
140
|
+
beam_settings (BeamSettings): Information about the beam read from hardware.
|
|
141
|
+
image_dir (str): The location the images will be written to.
|
|
142
|
+
num_images (int): Total number of images to be collected.
|
|
143
|
+
shots_per_position (int, optional): Number of exposures per position in a \
|
|
144
|
+
chip. Defaults to 1, which works for extruder.
|
|
145
|
+
start_time(datetime, optional): Collection start time. Defaults to None.
|
|
146
|
+
pump_probe (bool, optional): If True, a pump probe collection is running. \
|
|
147
|
+
Defaults to False.
|
|
121
148
|
"""
|
|
122
149
|
try:
|
|
123
150
|
if not start_time:
|
|
@@ -125,22 +152,18 @@ class DCID:
|
|
|
125
152
|
elif not start_time.timetz:
|
|
126
153
|
start_time = start_time.astimezone()
|
|
127
154
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
155
|
+
resolution = get_resolution(
|
|
156
|
+
self.detector,
|
|
157
|
+
self.parameters.detector_distance_mm,
|
|
158
|
+
beam_settings.wavelength_in_a,
|
|
159
|
+
)
|
|
160
|
+
beamsize_x, beamsize_y = beam_settings.beam_size_in_um
|
|
161
|
+
transmission = self.parameters.transmission * 100
|
|
162
|
+
xbeam, ybeam = beam_settings.beam_center_in_mm
|
|
135
163
|
|
|
136
164
|
if isinstance(self.detector, Pilatus):
|
|
137
|
-
# Mirror the construction that the PPU does
|
|
138
|
-
fileTemplate = get_pilatus_filename_template_from_pvs()
|
|
139
165
|
startImageNumber = 0
|
|
140
166
|
elif isinstance(self.detector, Eiger):
|
|
141
|
-
# Eiger base filename is directly written to the PV
|
|
142
|
-
# Nexgen then uses this to write the .nxs file
|
|
143
|
-
fileTemplate = str(cagetstring(self.detector.pv.file_name)) + ".nxs"
|
|
144
167
|
startImageNumber = 1
|
|
145
168
|
else:
|
|
146
169
|
raise ValueError("Unknown detector:", self.detector)
|
|
@@ -149,48 +172,48 @@ class DCID:
|
|
|
149
172
|
{
|
|
150
173
|
"name": "Xray probe",
|
|
151
174
|
"offset": 0,
|
|
152
|
-
"duration":
|
|
153
|
-
"period":
|
|
175
|
+
"duration": self.parameters.exposure_time_s,
|
|
176
|
+
"period": self.parameters.exposure_time_s,
|
|
154
177
|
"repetition": shots_per_position,
|
|
155
178
|
"eventType": "XrayDetection",
|
|
156
179
|
}
|
|
157
180
|
]
|
|
158
|
-
if
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
181
|
+
if pump_probe:
|
|
182
|
+
match self.parameters:
|
|
183
|
+
case FixedTargetParameters():
|
|
184
|
+
# pump then probe - pump_delay corresponds to time *before* first image
|
|
185
|
+
pump_delay = (
|
|
186
|
+
-self.parameters.laser_delay_s
|
|
187
|
+
if self.parameters.pump_repeat
|
|
188
|
+
is not PumpProbeSetting.Short2
|
|
189
|
+
else self.parameters.laser_delay_s
|
|
190
|
+
)
|
|
191
|
+
case ExtruderParameters():
|
|
192
|
+
pump_delay = self.parameters.laser_delay_s
|
|
169
193
|
events.append(
|
|
170
194
|
{
|
|
171
195
|
"name": "Laser probe",
|
|
172
196
|
"offset": pump_delay,
|
|
173
|
-
"duration":
|
|
174
|
-
# "period": None,
|
|
197
|
+
"duration": self.parameters.laser_dwell_s,
|
|
175
198
|
"repetition": 1,
|
|
176
199
|
"eventType": "LaserExcitation",
|
|
177
200
|
},
|
|
178
201
|
)
|
|
179
202
|
|
|
180
203
|
data = {
|
|
181
|
-
"detectorDistance":
|
|
204
|
+
"detectorDistance": self.parameters.detector_distance_mm,
|
|
182
205
|
"detectorId": self.detector.id,
|
|
183
|
-
"exposureTime":
|
|
184
|
-
"fileTemplate":
|
|
185
|
-
"imageDirectory":
|
|
186
|
-
"numberOfImages":
|
|
187
|
-
"resolution":
|
|
206
|
+
"exposureTime": self.parameters.exposure_time_s,
|
|
207
|
+
"fileTemplate": file_template,
|
|
208
|
+
"imageDirectory": image_dir,
|
|
209
|
+
"numberOfImages": num_images,
|
|
210
|
+
"resolution": resolution,
|
|
188
211
|
"startImageNumber": startImageNumber,
|
|
189
212
|
"startTime": start_time.isoformat(),
|
|
190
|
-
"transmission":
|
|
191
|
-
"visit": visit,
|
|
192
|
-
"wavelength":
|
|
193
|
-
"group": {"experimentType": self.
|
|
213
|
+
"transmission": transmission,
|
|
214
|
+
"visit": self.parameters.visit.name,
|
|
215
|
+
"wavelength": beam_settings.wavelength_in_a,
|
|
216
|
+
"group": {"experimentType": self.parameters.ispyb_experiment_type},
|
|
194
217
|
"xBeam": xbeam,
|
|
195
218
|
"yBeam": ybeam,
|
|
196
219
|
"ssx": {
|
|
@@ -205,12 +228,12 @@ class DCID:
|
|
|
205
228
|
|
|
206
229
|
# Log what we are doing here
|
|
207
230
|
try:
|
|
208
|
-
|
|
231
|
+
SSX_LOGGER.info(
|
|
209
232
|
"BRIDGE: POST /dc --data %s",
|
|
210
233
|
repr(json.dumps(data)),
|
|
211
234
|
)
|
|
212
235
|
except Exception:
|
|
213
|
-
|
|
236
|
+
SSX_LOGGER.info(
|
|
214
237
|
"Caught exception converting data to JSON. Data:\n%s\nVERBOSE:\n%s",
|
|
215
238
|
str({k: type(v) for k, v in data.items()}),
|
|
216
239
|
)
|
|
@@ -224,20 +247,20 @@ class DCID:
|
|
|
224
247
|
)
|
|
225
248
|
resp.raise_for_status()
|
|
226
249
|
self.dcid = resp.json()["dataCollectionId"]
|
|
227
|
-
|
|
250
|
+
SSX_LOGGER.info("Generated DCID %s", self.dcid)
|
|
228
251
|
except requests.HTTPError as e:
|
|
229
252
|
self.error = True
|
|
230
|
-
|
|
253
|
+
SSX_LOGGER.error(
|
|
231
254
|
"DCID generation Failed; Reason from server: %s", e.response.text
|
|
232
255
|
)
|
|
233
256
|
if self.emit_errors:
|
|
234
257
|
raise
|
|
235
|
-
|
|
258
|
+
SSX_LOGGER.exception("Error generating DCID: %s", e)
|
|
236
259
|
except Exception as e:
|
|
237
260
|
self.error = True
|
|
238
261
|
if self.emit_errors:
|
|
239
262
|
raise
|
|
240
|
-
|
|
263
|
+
SSX_LOGGER.exception("Error generating DCID: %s", e)
|
|
241
264
|
|
|
242
265
|
def __int__(self):
|
|
243
266
|
return self.dcid
|
|
@@ -248,13 +271,13 @@ class DCID:
|
|
|
248
271
|
return None
|
|
249
272
|
try:
|
|
250
273
|
command = [COLLECTION_START_SCRIPT, str(self.dcid)]
|
|
251
|
-
|
|
274
|
+
SSX_LOGGER.info("Running %s", " ".join(command))
|
|
252
275
|
subprocess.Popen(command)
|
|
253
276
|
except Exception as e:
|
|
254
277
|
self.error = True
|
|
255
278
|
if self.emit_errors:
|
|
256
279
|
raise
|
|
257
|
-
|
|
280
|
+
SSX_LOGGER.warning("Error starting start of collect script: %s", e)
|
|
258
281
|
|
|
259
282
|
def notify_end(self):
|
|
260
283
|
"""Send notifications that the collection has now ended"""
|
|
@@ -262,13 +285,13 @@ class DCID:
|
|
|
262
285
|
return
|
|
263
286
|
try:
|
|
264
287
|
command = [COLLECTION_END_SCRIPT, str(self.dcid)]
|
|
265
|
-
|
|
288
|
+
SSX_LOGGER.info("Running %s", " ".join(command))
|
|
266
289
|
subprocess.Popen(command)
|
|
267
290
|
except Exception as e:
|
|
268
291
|
self.error = True
|
|
269
292
|
if self.emit_errors:
|
|
270
293
|
raise
|
|
271
|
-
|
|
294
|
+
SSX_LOGGER.warning("Error running end of collect notification: %s", e)
|
|
272
295
|
|
|
273
296
|
def collection_complete(
|
|
274
297
|
self, end_time: str | datetime.datetime | None = None, aborted: bool = False
|
|
@@ -285,7 +308,7 @@ class DCID:
|
|
|
285
308
|
# end_time might be a string from time.ctime
|
|
286
309
|
if isinstance(end_time, str):
|
|
287
310
|
end_time = datetime.datetime.strptime(end_time, "%a %b %d %H:%M:%S %Y")
|
|
288
|
-
|
|
311
|
+
SSX_LOGGER.debug("Parsed end time: %s", end_time)
|
|
289
312
|
|
|
290
313
|
if not end_time:
|
|
291
314
|
end_time = datetime.datetime.now().astimezone()
|
|
@@ -302,13 +325,13 @@ class DCID:
|
|
|
302
325
|
if self.dcid is None:
|
|
303
326
|
# Print what we would have sent. This means that if something is failing,
|
|
304
327
|
# we still have the data to upload in the log files.
|
|
305
|
-
|
|
328
|
+
SSX_LOGGER.info(
|
|
306
329
|
'BRIDGE: No DCID but Would PATCH "/dc/XXXX" --data=%s',
|
|
307
330
|
repr(json.dumps(data)),
|
|
308
331
|
)
|
|
309
332
|
return
|
|
310
333
|
|
|
311
|
-
|
|
334
|
+
SSX_LOGGER.info(
|
|
312
335
|
'BRIDGE: PATCH "/dc/%s" --data=%s', self.dcid, repr(json.dumps(data))
|
|
313
336
|
)
|
|
314
337
|
response = requests.patch(
|
|
@@ -318,7 +341,7 @@ class DCID:
|
|
|
318
341
|
headers=get_auth_header(),
|
|
319
342
|
)
|
|
320
343
|
response.raise_for_status()
|
|
321
|
-
|
|
344
|
+
SSX_LOGGER.info("Successfully updated end time for DCID %d", self.dcid)
|
|
322
345
|
except Exception as e:
|
|
323
346
|
resp_obj = getattr(e, "response", None)
|
|
324
347
|
try:
|
|
@@ -333,87 +356,36 @@ class DCID:
|
|
|
333
356
|
self.error = True
|
|
334
357
|
if self.emit_errors:
|
|
335
358
|
raise
|
|
336
|
-
|
|
359
|
+
SSX_LOGGER.warning("Error completing DCID: %s (%s)", e, resp_str)
|
|
337
360
|
|
|
338
361
|
|
|
339
|
-
def
|
|
340
|
-
"""
|
|
341
|
-
Get the template file path by querying the detector PVs.
|
|
342
|
-
|
|
343
|
-
Returns: A template string, with the image numbers replaced with '#'
|
|
362
|
+
def get_pilatus_filename_template_from_device():
|
|
344
363
|
"""
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
filename_template = cagetstring(pv.pilat_filetemplate)
|
|
348
|
-
file_number = int(caget(pv.pilat_filenumber))
|
|
349
|
-
# Exploit fact that passing negative numbers will put the - before the 0's
|
|
350
|
-
expected_filename = str(filename_template % (filename, f"{file_number:05d}_", -9))
|
|
351
|
-
# Now, find the -09 part of this
|
|
352
|
-
numberpart = re.search(r"(-0+9)", expected_filename)
|
|
353
|
-
# Make sure this was the only one
|
|
354
|
-
if numberpart is not None:
|
|
355
|
-
assert re.search(r"(-0+9)", expected_filename[numberpart.end() :]) is None
|
|
356
|
-
template_fill = "#" * len(numberpart.group(0))
|
|
357
|
-
return (
|
|
358
|
-
expected_filename[: numberpart.start()]
|
|
359
|
-
+ template_fill
|
|
360
|
-
+ expected_filename[numberpart.end() :]
|
|
361
|
-
)
|
|
362
|
-
else:
|
|
363
|
-
raise ValueError(f"{filename=} did not contain the numbers for templating")
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
def get_beamsize() -> tuple[float | None, float | None]:
|
|
367
|
-
"""
|
|
368
|
-
Read the PVs to get the current beamsize.
|
|
364
|
+
Get the template file path by querying the detector PVs, mirror the construction \
|
|
365
|
+
that the PPU does.
|
|
369
366
|
|
|
370
367
|
Returns:
|
|
371
|
-
A
|
|
372
|
-
if the focus mode was unrecognised.
|
|
368
|
+
A template string, with the image numbers replaced with '#'
|
|
373
369
|
"""
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
"focus30d": ("30x30", 30, 30),
|
|
379
|
-
"focus50d": ("50x50", 50, 50),
|
|
380
|
-
"focus1050d": ("10x50", 10, 50),
|
|
381
|
-
"focus5010d": ("50x10", 50, 10),
|
|
382
|
-
"focus3010d": ("30x10", 30, 10),
|
|
383
|
-
}
|
|
384
|
-
v_mode = caget("BL24I-OP-MFM-01:G0:TARGETAPPLY")
|
|
385
|
-
h_mode = caget("BL24I-OP-MFM-01:G1:TARGETAPPLY")
|
|
386
|
-
# Validate these and note an error otherwise
|
|
387
|
-
if not v_mode.startswith("VMFM") or v_mode[4:] not in focus_modes:
|
|
388
|
-
logger.error("Unrecognised vertical beam mode %s", v_mode)
|
|
389
|
-
if not h_mode.startswith("HMFM") or h_mode[4:] not in focus_modes:
|
|
390
|
-
logger.error("Unrecognised horizontal beam mode %s", h_mode)
|
|
391
|
-
_, h, _ = focus_modes.get(h_mode[4:], (None, None, None))
|
|
392
|
-
_, _, v = focus_modes.get(v_mode[4:], (None, None, None))
|
|
393
|
-
|
|
394
|
-
return (h, v)
|
|
370
|
+
pilatus_metadata: PilatusMetadata = i24.pilatus_metadata()
|
|
371
|
+
|
|
372
|
+
filename_template = yield from bps.rd(pilatus_metadata.filename_template)
|
|
373
|
+
return filename_template
|
|
395
374
|
|
|
396
375
|
|
|
397
376
|
def get_resolution(detector: Detector, distance: float, wavelength: float) -> float:
|
|
398
|
-
"""
|
|
399
|
-
Calculate the inscribed resolution for detector.
|
|
377
|
+
""" Calculate the inscribed resolution for detector.
|
|
400
378
|
|
|
401
|
-
This assumes perfectly centered beam as I don't know where to
|
|
402
|
-
|
|
379
|
+
This assumes perfectly centered beam as I don't know where to extract the beam \
|
|
380
|
+
position parameters yet.
|
|
403
381
|
|
|
404
382
|
Args:
|
|
405
|
-
|
|
406
|
-
|
|
383
|
+
detector (Detector): Detector instance, Eiger() or Pilatus().
|
|
384
|
+
distance (float): Distance to detector, in mm.
|
|
385
|
+
wavelength (float): Beam wavelength, in Å.
|
|
407
386
|
|
|
408
387
|
Returns:
|
|
409
|
-
Maximum resolution
|
|
388
|
+
Maximum resolution, in Å.
|
|
410
389
|
"""
|
|
411
390
|
width = detector.image_size_mm[0]
|
|
412
391
|
return round(wavelength / (2 * math.sin(math.atan(width / (2 * distance)) / 2)), 2)
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
def get_beam_center(detector: Detector) -> tuple[float, float]:
|
|
416
|
-
"""Get the detector beam center, in mm"""
|
|
417
|
-
beamX = float(caget(detector.pv.beamx)) * detector.pixel_size_mm[0]
|
|
418
|
-
beamY = float(caget(detector.pv.beamy)) * detector.pixel_size_mm[1]
|
|
419
|
-
return (beamX, beamY)
|
|
@@ -370,7 +370,7 @@ font "arial-medium-r-24.0"
|
|
|
370
370
|
buttonLabel "Start"
|
|
371
371
|
numCmds 1
|
|
372
372
|
command {
|
|
373
|
-
0 "blueapi -c CONFIG_LOCATION controller run run_extruder_plan '\{\"zebra\":\"zebra\",\"aperture\":\"aperture\",\"backlight\":\"backlight\",\"beamstop\":\"beamstop\",\"detector_stage\":\"detector_motion\",\"shutter\":\"shutter\",\"dcm\":\"dcm\"\}'"
|
|
373
|
+
0 "blueapi -c CONFIG_LOCATION controller run run_extruder_plan '\{\"zebra\":\"zebra\",\"aperture\":\"aperture\",\"backlight\":\"backlight\",\"beamstop\":\"beamstop\",\"detector_stage\":\"detector_motion\",\"shutter\":\"shutter\",\"dcm\":\"dcm\",\"mirrors\":\"focus_mirrors\",\"attenuator\":\"attenuator\"\}'"
|
|
374
374
|
}
|
|
375
375
|
endObjectProperties
|
|
376
376
|
|