mx-bluesky 1.5.11__py3-none-any.whl → 1.5.14__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/Getting started.ipynb +170 -0
- mx_bluesky/_version.py +2 -2
- mx_bluesky/beamlines/aithre_lasershaping/experiment_plans/__init__.py +0 -0
- mx_bluesky/beamlines/aithre_lasershaping/experiment_plans/robot_load_plan.py +198 -0
- mx_bluesky/beamlines/aithre_lasershaping/parameters/__init__.py +0 -0
- mx_bluesky/beamlines/aithre_lasershaping/parameters/constants.py +17 -0
- mx_bluesky/beamlines/aithre_lasershaping/parameters/robot_load_parameters.py +13 -0
- mx_bluesky/beamlines/aithre_lasershaping/pin_tip_centring.py +31 -0
- mx_bluesky/beamlines/aithre_lasershaping/robot_load.py +74 -0
- mx_bluesky/beamlines/i04/__init__.py +6 -2
- mx_bluesky/beamlines/i04/callbacks/murko_callback.py +27 -12
- mx_bluesky/beamlines/i04/experiment_plans/i04_grid_detect_then_xray_centre_plan.py +88 -13
- mx_bluesky/beamlines/i04/external_interaction/__init__.py +0 -0
- mx_bluesky/beamlines/i04/external_interaction/config_server.py +15 -0
- mx_bluesky/beamlines/i04/oav_centering_plans/__init__.py +0 -0
- mx_bluesky/beamlines/i04/oav_centering_plans/oav_imaging.py +115 -0
- mx_bluesky/beamlines/i04/parameters/__init__.py +0 -0
- mx_bluesky/beamlines/i04/parameters/constants.py +21 -0
- mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +24 -1
- mx_bluesky/beamlines/i04/thawing_plan.py +147 -152
- mx_bluesky/beamlines/i24/serial/dcid.py +4 -5
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_extruder_collect_py3v2.py +5 -2
- 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 +3 -3
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +142 -142
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/MappingLite-oxford_py3v1.edl +135 -135
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/PMAC_Command.edl +8 -8
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +13 -13
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_chip_collect_py3v1.py +7 -4
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_chip_manager_py3v1.py +35 -32
- mx_bluesky/beamlines/i24/serial/parameters/utils.py +5 -5
- mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +113 -306
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +8 -2
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +1 -1
- mx_bluesky/beamlines/i24/serial/web_gui_plans/general_plans.py +6 -6
- mx_bluesky/beamlines/i24/serial/web_gui_plans/oav_plans.py +64 -0
- mx_bluesky/{hyperion/device_setup_plans/smargon.py → common/device_setup_plans/gonio.py} +9 -6
- mx_bluesky/common/device_setup_plans/manipulate_sample.py +8 -1
- mx_bluesky/common/device_setup_plans/robot_load_unload.py +1 -1
- mx_bluesky/common/device_setup_plans/setup_oav.py +8 -0
- mx_bluesky/common/device_setup_plans/setup_zebra_and_shutter.py +0 -5
- mx_bluesky/common/device_setup_plans/xbpm_feedback.py +8 -1
- mx_bluesky/common/experiment_plans/beamstop_check.py +229 -0
- mx_bluesky/common/experiment_plans/common_flyscan_xray_centre_plan.py +2 -0
- mx_bluesky/common/experiment_plans/inner_plans/read_hardware.py +5 -2
- mx_bluesky/common/experiment_plans/oav_snapshot_plan.py +0 -1
- mx_bluesky/{hyperion → common}/experiment_plans/pin_tip_centring_plan.py +20 -21
- mx_bluesky/common/external_interaction/callbacks/common/grid_detection_callback.py +5 -0
- mx_bluesky/common/external_interaction/callbacks/common/ispyb_callback_base.py +10 -12
- mx_bluesky/common/external_interaction/callbacks/common/ispyb_mapping.py +3 -5
- mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py +5 -5
- mx_bluesky/common/external_interaction/config_server.py +2 -2
- mx_bluesky/common/external_interaction/ispyb/data_model.py +11 -4
- mx_bluesky/common/external_interaction/ispyb/exp_eye_store.py +159 -2
- mx_bluesky/common/external_interaction/ispyb/ispyb_store.py +76 -166
- mx_bluesky/common/external_interaction/ispyb/ispyb_utils.py +0 -14
- mx_bluesky/common/parameters/components.py +1 -0
- mx_bluesky/common/parameters/constants.py +5 -2
- mx_bluesky/common/parameters/device_composites.py +4 -2
- mx_bluesky/common/utils/exceptions.py +15 -0
- mx_bluesky/common/utils/log.py +9 -0
- mx_bluesky/common/utils/utils.py +48 -0
- mx_bluesky/hyperion/__main__.py +3 -13
- mx_bluesky/hyperion/baton_handler.py +23 -6
- mx_bluesky/hyperion/experiment_plans/hyperion_flyscan_xray_centre_plan.py +1 -0
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +5 -6
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +3 -10
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +4 -2
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +8 -2
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +2 -2
- mx_bluesky/hyperion/experiment_plans/udc_default_state.py +166 -0
- mx_bluesky/hyperion/external_interaction/agamemnon.py +1 -1
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +48 -21
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +2 -2
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +1 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +1 -4
- mx_bluesky/hyperion/external_interaction/config_server.py +5 -5
- mx_bluesky/hyperion/parameters/constants.py +10 -3
- mx_bluesky/hyperion/parameters/device_composites.py +4 -2
- mx_bluesky/hyperion/parameters/robot_load.py +1 -9
- mx_bluesky/hyperion/plan_runner.py +31 -0
- mx_bluesky/hyperion/plan_runner_api.py +14 -1
- mx_bluesky/hyperion/utils/context.py +2 -2
- mx_bluesky/jupyter_example.ipynb +9 -1
- {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.14.dist-info}/METADATA +7 -6
- {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.14.dist-info}/RECORD +90 -75
- mx_bluesky/common/experiment_plans/inner_plans/udc_default_state.py +0 -86
- mx_bluesky/common/external_interaction/callbacks/common/logging_callback.py +0 -29
- {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.14.dist-info}/WHEEL +0 -0
- {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.14.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.14.dist-info}/licenses/LICENSE +0 -0
- {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.14.dist-info}/top_level.txt +0 -0
|
@@ -7,6 +7,12 @@ from event_model.documents import Event
|
|
|
7
7
|
from requests import JSONDecodeError, patch, post
|
|
8
8
|
from requests.auth import AuthBase
|
|
9
9
|
|
|
10
|
+
from mx_bluesky.common.external_interaction.ispyb.data_model import (
|
|
11
|
+
DataCollectionGridInfo,
|
|
12
|
+
DataCollectionGroupInfo,
|
|
13
|
+
DataCollectionInfo,
|
|
14
|
+
DataCollectionPositionInfo,
|
|
15
|
+
)
|
|
10
16
|
from mx_bluesky.common.external_interaction.ispyb.ispyb_utils import (
|
|
11
17
|
get_current_time_string,
|
|
12
18
|
get_ispyb_config,
|
|
@@ -33,8 +39,8 @@ def _get_base_url_and_token() -> tuple[str, str]:
|
|
|
33
39
|
return expeye_config["url"], expeye_config["token"]
|
|
34
40
|
|
|
35
41
|
|
|
36
|
-
def _send_and_get_response(auth, url, data, send_func) -> dict:
|
|
37
|
-
response = send_func(url, auth=auth, json=data)
|
|
42
|
+
def _send_and_get_response(auth, url, data, send_func, query_params=None) -> dict:
|
|
43
|
+
response = send_func(url, auth=auth, json=data, params=query_params)
|
|
38
44
|
if not response.ok:
|
|
39
45
|
try:
|
|
40
46
|
resp_txt = str(response.json())
|
|
@@ -181,3 +187,154 @@ class ExpeyeInteraction:
|
|
|
181
187
|
bl_sample_status=response["blSampleStatus"],
|
|
182
188
|
container_id=response["containerId"],
|
|
183
189
|
)
|
|
190
|
+
|
|
191
|
+
def create_data_group(
|
|
192
|
+
self, proposal_reference: str, visit_number: int, data: DataCollectionGroupInfo
|
|
193
|
+
) -> int:
|
|
194
|
+
response = _send_and_get_response(
|
|
195
|
+
self._auth,
|
|
196
|
+
self._base_url + f"/proposals/{proposal_reference}/sessions/"
|
|
197
|
+
f"{visit_number}/data-groups",
|
|
198
|
+
_data_collection_group_info_to_json(data),
|
|
199
|
+
post,
|
|
200
|
+
)
|
|
201
|
+
return response["dataCollectionGroupId"]
|
|
202
|
+
|
|
203
|
+
def update_data_group(self, group_id: int, data: DataCollectionGroupInfo):
|
|
204
|
+
_send_and_get_response(
|
|
205
|
+
self._auth,
|
|
206
|
+
self._base_url + f"/data-groups/{group_id}",
|
|
207
|
+
_data_collection_group_info_to_json(data),
|
|
208
|
+
patch,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
def create_data_collection(self, group_id: int, data: DataCollectionInfo) -> int:
|
|
212
|
+
response = _send_and_get_response(
|
|
213
|
+
self._auth,
|
|
214
|
+
self._base_url + f"/data-groups/{group_id}/data-collections",
|
|
215
|
+
_data_collection_info_to_json(data),
|
|
216
|
+
post,
|
|
217
|
+
)
|
|
218
|
+
return response["dataCollectionId"]
|
|
219
|
+
|
|
220
|
+
def update_data_collection(
|
|
221
|
+
self,
|
|
222
|
+
data_collection_id: int,
|
|
223
|
+
data: DataCollectionInfo,
|
|
224
|
+
append_comment: bool = False,
|
|
225
|
+
):
|
|
226
|
+
_send_and_get_response(
|
|
227
|
+
self._auth,
|
|
228
|
+
self._base_url + f"/data-collections/{data_collection_id}",
|
|
229
|
+
_data_collection_info_to_json(data),
|
|
230
|
+
patch,
|
|
231
|
+
{"appendComment": "true"} if append_comment else None,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
def create_position(
|
|
235
|
+
self, data_collection_id: int, data: DataCollectionPositionInfo
|
|
236
|
+
):
|
|
237
|
+
_send_and_get_response(
|
|
238
|
+
self._auth,
|
|
239
|
+
self._base_url + f"/data-collections/{data_collection_id}/position",
|
|
240
|
+
_position_info_to_json(data),
|
|
241
|
+
post,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
def create_grid(self, data_collection_id: int, data: DataCollectionGridInfo) -> int:
|
|
245
|
+
response = _send_and_get_response(
|
|
246
|
+
self._auth,
|
|
247
|
+
self._base_url + f"/data-collections/{data_collection_id}/grids",
|
|
248
|
+
_grid_info_to_json(data),
|
|
249
|
+
post,
|
|
250
|
+
)
|
|
251
|
+
return response["gridInfoId"]
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _none_to_absent(json: dict) -> dict:
|
|
255
|
+
for key in [key for key in json if json[key] is None]:
|
|
256
|
+
del json[key]
|
|
257
|
+
return json
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _data_collection_group_info_to_json(data: DataCollectionGroupInfo) -> dict:
|
|
261
|
+
return _none_to_absent(
|
|
262
|
+
{
|
|
263
|
+
"experimentType": data.experiment_type,
|
|
264
|
+
"sampleId": data.sample_id,
|
|
265
|
+
"actualSampleBarcode": data.sample_barcode,
|
|
266
|
+
"comments": data.comments,
|
|
267
|
+
}
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def _data_collection_info_to_json(data: DataCollectionInfo) -> dict:
|
|
272
|
+
return _none_to_absent(
|
|
273
|
+
{
|
|
274
|
+
"omegaStart": data.omega_start,
|
|
275
|
+
"dataCollectionNumber": data.data_collection_number,
|
|
276
|
+
"xtalSnapshotFullPath1": data.xtal_snapshot1,
|
|
277
|
+
"xtalSnapshotFullPath2": data.xtal_snapshot2,
|
|
278
|
+
"xtalSnapshotFullPath3": data.xtal_snapshot3,
|
|
279
|
+
"xtalSnapshotFullPath4": data.xtal_snapshot4,
|
|
280
|
+
"numberOfImages": data.n_images,
|
|
281
|
+
"axisRange": data.axis_range,
|
|
282
|
+
"axisEnd": data.axis_end,
|
|
283
|
+
"chiStart": data.chi_start,
|
|
284
|
+
"kappaStart": data.kappa_start,
|
|
285
|
+
"detectorId": data.detector_id,
|
|
286
|
+
"axisStart": data.axis_start,
|
|
287
|
+
"slitGapVertical": data.slitgap_vertical,
|
|
288
|
+
"slitGapHorizontal": data.slitgap_horizontal,
|
|
289
|
+
"beamSizeAtSampleX": data.beamsize_at_samplex,
|
|
290
|
+
"beamSizeAtSampleY": data.beamsize_at_sampley,
|
|
291
|
+
"transmission": data.transmission,
|
|
292
|
+
"comments": data.comments,
|
|
293
|
+
"detectorDistance": data.detector_distance,
|
|
294
|
+
"exposureTime": data.exp_time,
|
|
295
|
+
"imageDirectory": data.imgdir,
|
|
296
|
+
"fileTemplate": data.file_template,
|
|
297
|
+
"imagePrefix": data.imgprefix,
|
|
298
|
+
"imageSuffix": data.imgsuffix,
|
|
299
|
+
"numberOfPasses": data.n_passes,
|
|
300
|
+
"overlap": data.overlap,
|
|
301
|
+
"flux": data.flux,
|
|
302
|
+
"startImageNumber": data.start_image_number,
|
|
303
|
+
"resolution": data.resolution,
|
|
304
|
+
"wavelength": data.wavelength,
|
|
305
|
+
"xBeam": data.xbeam,
|
|
306
|
+
"yBeam": data.ybeam,
|
|
307
|
+
"synchrotronMode": data.synchrotron_mode,
|
|
308
|
+
"undulatorGap1": data.undulator_gap1,
|
|
309
|
+
"startTime": data.start_time,
|
|
310
|
+
"endTime": data.end_time,
|
|
311
|
+
"runStatus": data.run_status,
|
|
312
|
+
}
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def _position_info_to_json(data: DataCollectionPositionInfo) -> dict:
|
|
317
|
+
return _none_to_absent(
|
|
318
|
+
{
|
|
319
|
+
"posX": data.pos_x,
|
|
320
|
+
"posY": data.pos_y,
|
|
321
|
+
"posZ": data.pos_z,
|
|
322
|
+
}
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def _grid_info_to_json(data: DataCollectionGridInfo) -> dict:
|
|
327
|
+
return _none_to_absent(
|
|
328
|
+
{
|
|
329
|
+
"snapshotOffsetXPixel": data.snapshot_offset_x_pixel,
|
|
330
|
+
"snapshotOffsetYPixel": data.snapshot_offset_y_pixel,
|
|
331
|
+
"dx": data.dx_in_mm,
|
|
332
|
+
"dy": data.dy_in_mm,
|
|
333
|
+
"stepsX": data.steps_x,
|
|
334
|
+
"stepsY": data.steps_y,
|
|
335
|
+
"orientation": data.orientation.value,
|
|
336
|
+
"micronsPerPixelX": data.microns_per_pixel_x,
|
|
337
|
+
"micronsPerPixelY": data.microns_per_pixel_y,
|
|
338
|
+
"snaked": data.snaked,
|
|
339
|
+
}
|
|
340
|
+
)
|
|
@@ -1,35 +1,28 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from collections.abc import Sequence
|
|
4
|
-
from dataclasses import asdict
|
|
5
4
|
from typing import TYPE_CHECKING
|
|
6
5
|
|
|
7
|
-
import ispyb
|
|
8
|
-
import numpy as np
|
|
9
|
-
from ispyb.connector.mysqlsp.main import ISPyBMySQLSPConnector as Connector
|
|
10
|
-
from ispyb.sp.mxacquisition import MXAcquisition
|
|
11
|
-
from ispyb.strictordereddict import StrictOrderedDict
|
|
12
6
|
from pydantic import BaseModel
|
|
13
7
|
|
|
8
|
+
from mx_bluesky.common.external_interaction.callbacks.common.ispyb_mapping import (
|
|
9
|
+
get_proposal_and_session_from_visit_string,
|
|
10
|
+
)
|
|
14
11
|
from mx_bluesky.common.external_interaction.ispyb.data_model import (
|
|
15
|
-
DataCollectionGridInfo,
|
|
16
12
|
DataCollectionGroupInfo,
|
|
17
13
|
DataCollectionInfo,
|
|
18
14
|
ScanDataInfo,
|
|
19
15
|
)
|
|
16
|
+
from mx_bluesky.common.external_interaction.ispyb.exp_eye_store import ExpeyeInteraction
|
|
20
17
|
from mx_bluesky.common.external_interaction.ispyb.ispyb_utils import (
|
|
21
18
|
get_current_time_string,
|
|
22
|
-
get_session_id_from_visit,
|
|
23
19
|
)
|
|
20
|
+
from mx_bluesky.common.utils.exceptions import ISPyBDepositionNotMadeError
|
|
24
21
|
from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER
|
|
25
|
-
from mx_bluesky.common.utils.tracing import TRACER
|
|
26
22
|
|
|
27
23
|
if TYPE_CHECKING:
|
|
28
24
|
pass
|
|
29
25
|
|
|
30
|
-
I03_EIGER_DETECTOR = 78
|
|
31
|
-
EIGER_FILE_SUFFIX = "h5"
|
|
32
|
-
|
|
33
26
|
|
|
34
27
|
class IspybIds(BaseModel):
|
|
35
28
|
data_collection_ids: tuple[int, ...] = ()
|
|
@@ -40,6 +33,7 @@ class IspybIds(BaseModel):
|
|
|
40
33
|
class StoreInIspyb:
|
|
41
34
|
def __init__(self, ispyb_config: str) -> None:
|
|
42
35
|
self.ISPYB_CONFIG_PATH: str = ispyb_config
|
|
36
|
+
self._expeye = ExpeyeInteraction()
|
|
43
37
|
|
|
44
38
|
def begin_deposition(
|
|
45
39
|
self,
|
|
@@ -75,45 +69,41 @@ class StoreInIspyb:
|
|
|
75
69
|
data_collection_group_info: DataCollectionGroupInfo | None,
|
|
76
70
|
scan_data_infos,
|
|
77
71
|
) -> IspybIds:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
self._store_data_collection_group_table(
|
|
83
|
-
conn,
|
|
84
|
-
data_collection_group_info,
|
|
85
|
-
ispyb_ids.data_collection_group_id,
|
|
86
|
-
)
|
|
87
|
-
)
|
|
88
|
-
else:
|
|
89
|
-
assert ispyb_ids.data_collection_group_id, (
|
|
90
|
-
"Attempt to update data collection without a data collection group ID"
|
|
72
|
+
if data_collection_group_info:
|
|
73
|
+
ispyb_ids.data_collection_group_id = (
|
|
74
|
+
self._store_data_collection_group_table(
|
|
75
|
+
data_collection_group_info, ispyb_ids.data_collection_group_id
|
|
91
76
|
)
|
|
77
|
+
)
|
|
78
|
+
else:
|
|
79
|
+
assert ispyb_ids.data_collection_group_id, (
|
|
80
|
+
"Attempt to update data collection without a data collection group ID"
|
|
81
|
+
)
|
|
92
82
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
new_data_collection_id, grid_id = self._store_single_scan_data(
|
|
106
|
-
conn, scan_data_info, data_collection_id
|
|
83
|
+
grid_ids = list(ispyb_ids.grid_ids)
|
|
84
|
+
data_collection_ids_out = list(ispyb_ids.data_collection_ids)
|
|
85
|
+
for scan_data_info in scan_data_infos:
|
|
86
|
+
data_collection_id = scan_data_info.data_collection_id
|
|
87
|
+
if (
|
|
88
|
+
scan_data_info.data_collection_info
|
|
89
|
+
and not scan_data_info.data_collection_info.parent_id
|
|
90
|
+
):
|
|
91
|
+
scan_data_info.data_collection_info.parent_id = (
|
|
92
|
+
ispyb_ids.data_collection_group_id
|
|
107
93
|
)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
grid_ids.append(grid_id)
|
|
112
|
-
ispyb_ids = IspybIds(
|
|
113
|
-
data_collection_ids=tuple(data_collection_ids_out),
|
|
114
|
-
grid_ids=tuple(grid_ids),
|
|
115
|
-
data_collection_group_id=ispyb_ids.data_collection_group_id,
|
|
94
|
+
|
|
95
|
+
new_data_collection_id, grid_id = self._store_single_scan_data(
|
|
96
|
+
scan_data_info, data_collection_id
|
|
116
97
|
)
|
|
98
|
+
if not data_collection_id:
|
|
99
|
+
data_collection_ids_out.append(new_data_collection_id)
|
|
100
|
+
if grid_id:
|
|
101
|
+
grid_ids.append(grid_id)
|
|
102
|
+
ispyb_ids = IspybIds(
|
|
103
|
+
data_collection_ids=tuple(data_collection_ids_out),
|
|
104
|
+
grid_ids=tuple(grid_ids),
|
|
105
|
+
data_collection_group_id=ispyb_ids.data_collection_group_id,
|
|
106
|
+
)
|
|
117
107
|
return ispyb_ids
|
|
118
108
|
|
|
119
109
|
def end_deposition(self, ispyb_ids: IspybIds, success: str, reason: str):
|
|
@@ -134,24 +124,19 @@ class StoreInIspyb:
|
|
|
134
124
|
run_status = "DataCollection Successful"
|
|
135
125
|
current_time = get_current_time_string()
|
|
136
126
|
self._update_scan_with_end_time_and_status(
|
|
137
|
-
current_time,
|
|
138
|
-
run_status,
|
|
139
|
-
reason,
|
|
140
|
-
id_,
|
|
141
|
-
ispyb_ids.data_collection_group_id,
|
|
127
|
+
current_time, run_status, reason, id_
|
|
142
128
|
)
|
|
143
129
|
|
|
144
130
|
def append_to_comment(
|
|
145
131
|
self, data_collection_id: int, comment: str, delimiter: str = " "
|
|
146
132
|
) -> None:
|
|
147
133
|
try:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
except ispyb.ReadWriteError as e:
|
|
134
|
+
self._expeye.update_data_collection(
|
|
135
|
+
data_collection_id,
|
|
136
|
+
DataCollectionInfo(comments=delimiter + comment),
|
|
137
|
+
True,
|
|
138
|
+
)
|
|
139
|
+
except ISPyBDepositionNotMadeError as e:
|
|
155
140
|
ISPYB_ZOCALO_CALLBACK_LOGGER.warning(
|
|
156
141
|
f"Unable to log comment, comment probably exceeded column length: {comment}",
|
|
157
142
|
exc_info=e,
|
|
@@ -162,143 +147,68 @@ class StoreInIspyb:
|
|
|
162
147
|
dcg_info: DataCollectionGroupInfo,
|
|
163
148
|
data_collection_group_id: int | None = None,
|
|
164
149
|
) -> None:
|
|
165
|
-
|
|
166
|
-
assert conn is not None, "Failed to connect to ISPyB!"
|
|
167
|
-
self._store_data_collection_group_table(
|
|
168
|
-
conn,
|
|
169
|
-
dcg_info,
|
|
170
|
-
data_collection_group_id,
|
|
171
|
-
)
|
|
150
|
+
self._store_data_collection_group_table(dcg_info, data_collection_group_id)
|
|
172
151
|
|
|
173
152
|
def _update_scan_with_end_time_and_status(
|
|
174
|
-
self,
|
|
175
|
-
end_time: str,
|
|
176
|
-
run_status: str,
|
|
177
|
-
reason: str,
|
|
178
|
-
data_collection_id: int,
|
|
179
|
-
data_collection_group_id: int,
|
|
153
|
+
self, end_time: str, run_status: str, reason: str, data_collection_id: int
|
|
180
154
|
) -> None:
|
|
181
|
-
if reason
|
|
155
|
+
if reason != "":
|
|
182
156
|
self.append_to_comment(data_collection_id, f"{run_status} reason: {reason}")
|
|
183
157
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
mx_acquisition: MXAcquisition = conn.mx_acquisition
|
|
188
|
-
|
|
189
|
-
params = mx_acquisition.get_data_collection_params()
|
|
190
|
-
params["id"] = data_collection_id
|
|
191
|
-
params["parentid"] = data_collection_group_id
|
|
192
|
-
params["endtime"] = end_time
|
|
193
|
-
params["run_status"] = run_status
|
|
194
|
-
mx_acquisition.upsert_data_collection(list(params.values()))
|
|
195
|
-
|
|
196
|
-
def _store_position_table(
|
|
197
|
-
self, conn: Connector, dc_pos_info, data_collection_id
|
|
198
|
-
) -> int:
|
|
199
|
-
mx_acquisition: MXAcquisition = conn.mx_acquisition
|
|
200
|
-
|
|
201
|
-
params = mx_acquisition.get_dc_position_params()
|
|
202
|
-
params["id"] = data_collection_id
|
|
203
|
-
params |= asdict(dc_pos_info)
|
|
204
|
-
|
|
205
|
-
return mx_acquisition.update_dc_position(list(params.values()))
|
|
158
|
+
info = DataCollectionInfo(end_time=end_time, run_status=run_status)
|
|
159
|
+
self._expeye.update_data_collection(data_collection_id, info)
|
|
206
160
|
|
|
207
161
|
def _store_data_collection_group_table(
|
|
208
162
|
self,
|
|
209
|
-
conn: Connector,
|
|
210
163
|
dcg_info: DataCollectionGroupInfo,
|
|
211
164
|
data_collection_group_id: int | None = None,
|
|
212
165
|
) -> int:
|
|
213
|
-
mx_acquisition: MXAcquisition = conn.mx_acquisition
|
|
214
|
-
|
|
215
|
-
params = mx_acquisition.get_data_collection_group_params()
|
|
216
166
|
if data_collection_group_id:
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
167
|
+
self._expeye.update_data_group(data_collection_group_id, dcg_info)
|
|
168
|
+
return data_collection_group_id
|
|
169
|
+
else:
|
|
170
|
+
proposal, session = get_proposal_and_session_from_visit_string(
|
|
171
|
+
dcg_info.visit_string
|
|
172
|
+
)
|
|
173
|
+
return self._expeye.create_data_group(proposal, session, dcg_info)
|
|
222
174
|
|
|
223
175
|
def _store_data_collection_table(
|
|
224
|
-
self,
|
|
225
|
-
):
|
|
176
|
+
self, data_collection_id, data_collection_info: DataCollectionInfo
|
|
177
|
+
) -> int:
|
|
226
178
|
if data_collection_id and data_collection_info.comments:
|
|
227
179
|
self.append_to_comment(
|
|
228
180
|
data_collection_id, data_collection_info.comments, " "
|
|
229
181
|
)
|
|
230
182
|
data_collection_info.comments = None
|
|
231
183
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
184
|
+
if data_collection_id:
|
|
185
|
+
self._expeye.update_data_collection(
|
|
186
|
+
data_collection_id, data_collection_info
|
|
187
|
+
)
|
|
188
|
+
return data_collection_id
|
|
189
|
+
else:
|
|
190
|
+
assert data_collection_info.parent_id, (
|
|
191
|
+
"Data Collection must have a Data Collection Group"
|
|
192
|
+
)
|
|
193
|
+
return self._expeye.create_data_collection(
|
|
194
|
+
data_collection_info.parent_id, data_collection_info
|
|
195
|
+
)
|
|
237
196
|
|
|
238
197
|
def _store_single_scan_data(
|
|
239
|
-
self,
|
|
198
|
+
self, scan_data_info, data_collection_id=None
|
|
240
199
|
) -> tuple[int, int | None]:
|
|
241
200
|
data_collection_id = self._store_data_collection_table(
|
|
242
|
-
|
|
201
|
+
data_collection_id, scan_data_info.data_collection_info
|
|
243
202
|
)
|
|
244
203
|
|
|
245
204
|
if scan_data_info.data_collection_position_info:
|
|
246
|
-
self.
|
|
247
|
-
|
|
248
|
-
scan_data_info.data_collection_position_info,
|
|
249
|
-
data_collection_id,
|
|
205
|
+
self._expeye.create_position(
|
|
206
|
+
data_collection_id, scan_data_info.data_collection_position_info
|
|
250
207
|
)
|
|
251
208
|
|
|
252
209
|
grid_id = None
|
|
253
210
|
if scan_data_info.data_collection_grid_info:
|
|
254
|
-
grid_id = self.
|
|
255
|
-
|
|
256
|
-
data_collection_id,
|
|
257
|
-
scan_data_info.data_collection_grid_info,
|
|
211
|
+
grid_id = self._expeye.create_grid(
|
|
212
|
+
data_collection_id, scan_data_info.data_collection_grid_info
|
|
258
213
|
)
|
|
259
214
|
return data_collection_id, grid_id
|
|
260
|
-
|
|
261
|
-
def _store_grid_info_table(
|
|
262
|
-
self,
|
|
263
|
-
conn: Connector,
|
|
264
|
-
ispyb_data_collection_id: int,
|
|
265
|
-
dc_grid_info: DataCollectionGridInfo,
|
|
266
|
-
) -> int:
|
|
267
|
-
mx_acquisition: MXAcquisition = conn.mx_acquisition
|
|
268
|
-
params = mx_acquisition.get_dc_grid_params()
|
|
269
|
-
params |= dc_grid_info.as_dict()
|
|
270
|
-
params["parentid"] = ispyb_data_collection_id
|
|
271
|
-
return mx_acquisition.upsert_dc_grid(list(params.values()))
|
|
272
|
-
|
|
273
|
-
def _fill_common_data_collection_params(
|
|
274
|
-
self, conn, data_collection_id, data_collection_info: DataCollectionInfo
|
|
275
|
-
) -> StrictOrderedDict:
|
|
276
|
-
mx_acquisition: MXAcquisition = conn.mx_acquisition
|
|
277
|
-
params = mx_acquisition.get_data_collection_params()
|
|
278
|
-
|
|
279
|
-
if data_collection_id:
|
|
280
|
-
params["id"] = data_collection_id
|
|
281
|
-
if data_collection_info.visit_string:
|
|
282
|
-
# This is only needed for populating the DataCollectionGroup
|
|
283
|
-
params["visit_id"] = get_session_id_from_visit(
|
|
284
|
-
conn, data_collection_info.visit_string
|
|
285
|
-
)
|
|
286
|
-
params |= {
|
|
287
|
-
k: v.item() if isinstance(v, np.generic) else v # Convert to native types
|
|
288
|
-
for k, v in asdict(data_collection_info).items()
|
|
289
|
-
if k != "visit_string"
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
return params
|
|
293
|
-
|
|
294
|
-
@staticmethod
|
|
295
|
-
@TRACER.start_as_current_span("_upsert_data_collection_group")
|
|
296
|
-
def _upsert_data_collection_group(
|
|
297
|
-
conn: Connector, params: StrictOrderedDict
|
|
298
|
-
) -> int:
|
|
299
|
-
return conn.mx_acquisition.upsert_data_collection_group(list(params.values()))
|
|
300
|
-
|
|
301
|
-
@staticmethod
|
|
302
|
-
@TRACER.start_as_current_span("_upsert_data_collection")
|
|
303
|
-
def _upsert_data_collection(conn: Connector, params: StrictOrderedDict) -> int:
|
|
304
|
-
return conn.mx_acquisition.upsert_data_collection(list(params.values()))
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
import datetime
|
|
4
2
|
import os
|
|
5
3
|
|
|
6
|
-
from ispyb import NoResult
|
|
7
|
-
from ispyb.connector.mysqlsp.main import ISPyBMySQLSPConnector as Connector
|
|
8
|
-
from ispyb.sp.core import Core
|
|
9
|
-
|
|
10
4
|
|
|
11
5
|
def get_ispyb_config() -> str:
|
|
12
6
|
ispyb_config = os.environ.get("ISPYB_CONFIG_PATH")
|
|
@@ -14,14 +8,6 @@ def get_ispyb_config() -> str:
|
|
|
14
8
|
return ispyb_config
|
|
15
9
|
|
|
16
10
|
|
|
17
|
-
def get_session_id_from_visit(conn: Connector, visit: str):
|
|
18
|
-
try:
|
|
19
|
-
core: Core = conn.core
|
|
20
|
-
return core.retrieve_visit_id(visit)
|
|
21
|
-
except NoResult as e:
|
|
22
|
-
raise NoResult(f"No session ID found in ispyb for visit {visit}") from e
|
|
23
|
-
|
|
24
|
-
|
|
25
11
|
def get_current_time_string():
|
|
26
12
|
now = datetime.datetime.now()
|
|
27
13
|
return now.strftime("%Y-%m-%d %H:%M:%S")
|
|
@@ -171,6 +171,7 @@ class DiffractionExperiment(
|
|
|
171
171
|
ispyb_experiment_type: IspybExperimentType
|
|
172
172
|
storage_directory: str
|
|
173
173
|
use_roi_mode: bool = Field(default=GridscanParamConstants.USE_ROI)
|
|
174
|
+
snapshot_directory: Path = None # type:ignore # filled in on validation
|
|
174
175
|
|
|
175
176
|
@model_validator(mode="before")
|
|
176
177
|
@classmethod
|
|
@@ -40,6 +40,8 @@ def _get_oav_config_json_path():
|
|
|
40
40
|
return "tests/test_data/test_OAVCentring.json"
|
|
41
41
|
elif BEAMLINE == "i03":
|
|
42
42
|
return f"/dls_sw/{BEAMLINE}/software/daq_configuration/json/OAVCentring_hyperion.json"
|
|
43
|
+
elif BEAMLINE == "aithre":
|
|
44
|
+
return "/dls/science/groups/i23/aithre/daq_configuration/json/OAVCentring_aithre.json"
|
|
43
45
|
else:
|
|
44
46
|
return f"/dls_sw/{BEAMLINE}/software/daq_configuration/json/OAVCentring.json"
|
|
45
47
|
|
|
@@ -85,8 +87,9 @@ class HardwareConstants:
|
|
|
85
87
|
OAV_REFRESH_DELAY = 0.3
|
|
86
88
|
PANDA_FGS_RUN_UP_DEFAULT = 0.17
|
|
87
89
|
CRYOJET_MARGIN_MM = 0.2
|
|
88
|
-
THAWING_TIME = 40
|
|
89
90
|
TIP_OFFSET_UM = 0
|
|
91
|
+
MAX_CRYO_TEMP_K = 110
|
|
92
|
+
MAX_CRYO_PRESSURE_BAR = 0.1
|
|
90
93
|
|
|
91
94
|
# Value quoted in https://www.dectris.com/en/detectors/x-ray-detectors/eiger2/eiger2-for-synchrotrons/eiger2-x/,
|
|
92
95
|
# causes dropped frames, so increase value for safety
|
|
@@ -163,7 +166,7 @@ class Status(Enum):
|
|
|
163
166
|
|
|
164
167
|
|
|
165
168
|
@dataclass
|
|
166
|
-
class
|
|
169
|
+
class FeatureSettings: ... # List of features and their default values. Subclasses must also be a pydantic dataclass
|
|
167
170
|
|
|
168
171
|
|
|
169
172
|
class FeatureSettingSources(
|
|
@@ -4,6 +4,7 @@ from dodal.devices.aperturescatterguard import (
|
|
|
4
4
|
)
|
|
5
5
|
from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
|
|
6
6
|
from dodal.devices.backlight import Backlight
|
|
7
|
+
from dodal.devices.beamsize.beamsize import BeamsizeBase
|
|
7
8
|
from dodal.devices.common_dcm import DoubleCrystalMonochromator
|
|
8
9
|
from dodal.devices.detector.detector_motion import DetectorMotion
|
|
9
10
|
from dodal.devices.eiger import EigerDetector
|
|
@@ -18,7 +19,7 @@ from dodal.devices.robot import BartRobot
|
|
|
18
19
|
from dodal.devices.s4_slit_gaps import S4SlitGaps
|
|
19
20
|
from dodal.devices.smargon import Smargon
|
|
20
21
|
from dodal.devices.synchrotron import Synchrotron
|
|
21
|
-
from dodal.devices.undulator import
|
|
22
|
+
from dodal.devices.undulator import UndulatorInKeV
|
|
22
23
|
from dodal.devices.xbpm_feedback import XBPMFeedback
|
|
23
24
|
from dodal.devices.zebra.zebra import Zebra
|
|
24
25
|
from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
|
|
@@ -51,6 +52,7 @@ class GridDetectThenXRayCentreComposite(FlyScanEssentialDevices):
|
|
|
51
52
|
attenuator: BinaryFilterAttenuator
|
|
52
53
|
backlight: Backlight
|
|
53
54
|
beamstop: Beamstop
|
|
55
|
+
beamsize: BeamsizeBase
|
|
54
56
|
dcm: DoubleCrystalMonochromator
|
|
55
57
|
detector_motion: DetectorMotion
|
|
56
58
|
zebra_fast_grid_scan: ZebraFastGridScanThreeD
|
|
@@ -58,7 +60,7 @@ class GridDetectThenXRayCentreComposite(FlyScanEssentialDevices):
|
|
|
58
60
|
oav: OAV
|
|
59
61
|
pin_tip_detection: PinTipDetection
|
|
60
62
|
s4_slit_gaps: S4SlitGaps
|
|
61
|
-
undulator:
|
|
63
|
+
undulator: UndulatorInKeV
|
|
62
64
|
xbpm_feedback: XBPMFeedback
|
|
63
65
|
zebra: Zebra
|
|
64
66
|
robot: BartRobot
|
|
@@ -16,12 +16,27 @@ class WarningError(
|
|
|
16
16
|
pass
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
class BeamlineCheckFailureError(Exception):
|
|
20
|
+
"""
|
|
21
|
+
An error which is raised during a beamline check to indicate that the check did
|
|
22
|
+
not pass.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
|
|
19
28
|
class ISPyBDepositionNotMadeError(Exception):
|
|
20
29
|
"""Raised when the ISPyB or Zocalo callbacks can't access ISPyB deposition numbers."""
|
|
21
30
|
|
|
22
31
|
pass
|
|
23
32
|
|
|
24
33
|
|
|
34
|
+
class BeamlineStateError(Exception):
|
|
35
|
+
"""Exception raised when the beamline is in the incorrect state"""
|
|
36
|
+
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
25
40
|
class SampleError(WarningError):
|
|
26
41
|
"""An exception which identifies an issue relating to the sample."""
|
|
27
42
|
|
mx_bluesky/common/utils/log.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import logging
|
|
2
3
|
from logging.handlers import TimedRotatingFileHandler
|
|
3
4
|
from os import environ
|
|
@@ -27,6 +28,14 @@ ALL_LOGGERS = [LOGGER, ISPYB_ZOCALO_CALLBACK_LOGGER, NEXUS_LOGGER]
|
|
|
27
28
|
__logger_handlers: DodalLogHandlers | None = None
|
|
28
29
|
|
|
29
30
|
|
|
31
|
+
def format_doc_for_log(doc):
|
|
32
|
+
class _BestEffortEncoder(json.JSONEncoder):
|
|
33
|
+
def default(self, o):
|
|
34
|
+
return repr(o)
|
|
35
|
+
|
|
36
|
+
return json.dumps(doc, indent=2, cls=_BestEffortEncoder)
|
|
37
|
+
|
|
38
|
+
|
|
30
39
|
class ExperimentMetadataTagFilter(logging.Filter):
|
|
31
40
|
"""When an instance of this custom filter is added to a logging handler, dc_group_id
|
|
32
41
|
and run_id will be tagged in that handlers' log messages."""
|