mx-bluesky 1.2.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/__init__.py +8 -3
- mx_bluesky/__main__.py +12 -7
- mx_bluesky/_version.py +2 -2
- mx_bluesky/beamlines/i04/callbacks/murko_callback.py +14 -4
- mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +178 -0
- mx_bluesky/beamlines/i04/thawing_plan.py +49 -11
- mx_bluesky/beamlines/i24/serial/__init__.py +3 -0
- 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 +121 -110
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +3 -6
- mx_bluesky/beamlines/i24/serial/fixed_target/ft_utils.py +0 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +164 -169
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +149 -225
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +7 -216
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +18 -17
- mx_bluesky/beamlines/i24/serial/log.py +58 -49
- 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/parameters/fixed_target/cs/cs_maker.json +3 -3
- mx_bluesky/beamlines/i24/serial/run_extruder.sh +30 -5
- mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +30 -5
- mx_bluesky/beamlines/i24/serial/run_serial.py +24 -8
- mx_bluesky/beamlines/i24/serial/setup_beamline/ca.py +0 -2
- mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +2 -0
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +104 -82
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +9 -20
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +26 -28
- mx_bluesky/beamlines/i24/serial/write_nexus.py +74 -72
- mx_bluesky/common/__init__.py +0 -0
- mx_bluesky/common/device_setup_plans/read_hardware_for_setup.py +14 -0
- mx_bluesky/common/external_interaction/config_server.py +46 -0
- mx_bluesky/common/parameters/components.py +258 -0
- mx_bluesky/common/parameters/constants.py +143 -0
- mx_bluesky/common/parameters/gridscan.py +94 -0
- mx_bluesky/common/parameters/robot_load.py +16 -0
- mx_bluesky/common/plans/__init__.py +1 -0
- mx_bluesky/common/plans/do_fgs.py +121 -0
- mx_bluesky/common/utils/log.py +118 -0
- mx_bluesky/{hyperion → common/utils}/tracing.py +2 -2
- mx_bluesky/hyperion/__main__.py +13 -10
- mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +47 -52
- mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +6 -12
- mx_bluesky/hyperion/device_setup_plans/setup_oav.py +6 -12
- mx_bluesky/hyperion/device_setup_plans/setup_panda.py +5 -6
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +49 -18
- mx_bluesky/hyperion/device_setup_plans/smargon.py +9 -9
- mx_bluesky/hyperion/device_setup_plans/utils.py +2 -2
- mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +4 -4
- 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 +147 -169
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +48 -22
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +75 -9
- mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +21 -20
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +9 -6
- mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +2 -2
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +40 -21
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +22 -22
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +43 -39
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +69 -18
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +17 -7
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +13 -13
- mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +0 -4
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +5 -2
- 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/common/ispyb_mapping.py +1 -1
- mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +30 -25
- mx_bluesky/hyperion/external_interaction/callbacks/ispyb_callback_base.py +29 -12
- mx_bluesky/hyperion/external_interaction/callbacks/log_uid_tag_callback.py +1 -1
- mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +19 -11
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +7 -4
- 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 +38 -27
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +5 -4
- mx_bluesky/hyperion/external_interaction/config_server.py +11 -28
- mx_bluesky/hyperion/external_interaction/exceptions.py +0 -9
- mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +65 -15
- mx_bluesky/hyperion/external_interaction/ispyb/ispyb_store.py +1 -1
- mx_bluesky/hyperion/external_interaction/nexus/nexus_utils.py +2 -2
- mx_bluesky/hyperion/external_interaction/nexus/write_nexus.py +1 -1
- mx_bluesky/hyperion/log.py +0 -84
- mx_bluesky/hyperion/parameters/components.py +4 -251
- mx_bluesky/hyperion/parameters/constants.py +22 -119
- mx_bluesky/hyperion/parameters/gridscan.py +35 -74
- mx_bluesky/hyperion/parameters/load_centre_collect.py +16 -11
- mx_bluesky/hyperion/parameters/rotation.py +23 -10
- mx_bluesky/hyperion/utils/utils.py +17 -0
- mx_bluesky/hyperion/utils/validation.py +5 -6
- {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/METADATA +36 -33
- {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/RECORD +102 -89
- {mx_bluesky-1.2.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 -161
- mx_bluesky/example.py +0 -19
- mx_bluesky/hyperion/parameters/robot_load.py +0 -16
- {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/LICENSE +0 -0
- {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import configparser
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from enum import StrEnum
|
|
2
4
|
|
|
3
5
|
from requests import patch, post
|
|
4
6
|
from requests.auth import AuthBase
|
|
@@ -29,20 +31,44 @@ def _get_base_url_and_token() -> tuple[str, str]:
|
|
|
29
31
|
return expeye_config["url"], expeye_config["token"]
|
|
30
32
|
|
|
31
33
|
|
|
34
|
+
def _send_and_get_response(auth, url, data, send_func) -> dict:
|
|
35
|
+
response = send_func(url, auth=auth, json=data)
|
|
36
|
+
if not response.ok:
|
|
37
|
+
raise ISPyBDepositionNotMade(f"Could not write {data} to {url}: {response}")
|
|
38
|
+
return response.json()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class BLSample:
|
|
43
|
+
container_id: int
|
|
44
|
+
bl_sample_id: int
|
|
45
|
+
bl_sample_status: str | None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class BLSampleStatus(StrEnum):
|
|
49
|
+
# The sample has been loaded
|
|
50
|
+
LOADED = "LOADED"
|
|
51
|
+
# Problem with the sample e.g. pin too long/short
|
|
52
|
+
ERROR_SAMPLE = "ERROR - sample"
|
|
53
|
+
# Any other general error
|
|
54
|
+
ERROR_BEAMLINE = "ERROR - beamline"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
assert all(
|
|
58
|
+
len(value) <= 20 for value in BLSampleStatus
|
|
59
|
+
), "Column size limit of 20 for BLSampleStatus"
|
|
60
|
+
|
|
61
|
+
|
|
32
62
|
class ExpeyeInteraction:
|
|
63
|
+
"""Exposes functionality from the Expeye core API"""
|
|
64
|
+
|
|
33
65
|
CREATE_ROBOT_ACTION = "/proposals/{proposal}/sessions/{visit_number}/robot-actions"
|
|
34
66
|
UPDATE_ROBOT_ACTION = "/robot-actions/{action_id}"
|
|
35
67
|
|
|
36
68
|
def __init__(self) -> None:
|
|
37
69
|
url, token = _get_base_url_and_token()
|
|
38
|
-
self.
|
|
39
|
-
self.
|
|
40
|
-
|
|
41
|
-
def _send_and_get_response(self, url, data, send_func) -> dict:
|
|
42
|
-
response = send_func(url, auth=self.auth, json=data)
|
|
43
|
-
if not response.ok:
|
|
44
|
-
raise ISPyBDepositionNotMade(f"Could not write {data} to {url}: {response}")
|
|
45
|
-
return response.json()
|
|
70
|
+
self._base_url = url
|
|
71
|
+
self._auth = BearerAuth(token)
|
|
46
72
|
|
|
47
73
|
def start_load(
|
|
48
74
|
self,
|
|
@@ -66,7 +92,7 @@ class ExpeyeInteraction:
|
|
|
66
92
|
Returns:
|
|
67
93
|
RobotActionID: The id of the robot load action that is created
|
|
68
94
|
"""
|
|
69
|
-
url = self.
|
|
95
|
+
url = self._base_url + self.CREATE_ROBOT_ACTION.format(
|
|
70
96
|
proposal=proposal_reference, visit_number=visit_number
|
|
71
97
|
)
|
|
72
98
|
|
|
@@ -77,7 +103,7 @@ class ExpeyeInteraction:
|
|
|
77
103
|
"containerLocation": container_location,
|
|
78
104
|
"dewarLocation": dewar_location,
|
|
79
105
|
}
|
|
80
|
-
response = self.
|
|
106
|
+
response = _send_and_get_response(self._auth, url, data, post)
|
|
81
107
|
return response["robotActionId"]
|
|
82
108
|
|
|
83
109
|
def update_barcode_and_snapshots(
|
|
@@ -95,14 +121,14 @@ class ExpeyeInteraction:
|
|
|
95
121
|
snapshot_before_path (str): Path to the snapshot before robot load
|
|
96
122
|
snapshot_after_path (str): Path to the snapshot after robot load
|
|
97
123
|
"""
|
|
98
|
-
url = self.
|
|
124
|
+
url = self._base_url + self.UPDATE_ROBOT_ACTION.format(action_id=action_id)
|
|
99
125
|
|
|
100
126
|
data = {
|
|
101
127
|
"sampleBarcode": barcode,
|
|
102
128
|
"xtalSnapshotBefore": snapshot_before_path,
|
|
103
129
|
"xtalSnapshotAfter": snapshot_after_path,
|
|
104
130
|
}
|
|
105
|
-
self.
|
|
131
|
+
_send_and_get_response(self._auth, url, data, patch)
|
|
106
132
|
|
|
107
133
|
def end_load(self, action_id: RobotActionID, status: str, reason: str):
|
|
108
134
|
"""Finish an existing robot action, providing final information about how it went
|
|
@@ -113,13 +139,37 @@ class ExpeyeInteraction:
|
|
|
113
139
|
otherwise error
|
|
114
140
|
reason (str): If the status is in error than the reason for that error
|
|
115
141
|
"""
|
|
116
|
-
url = self.
|
|
142
|
+
url = self._base_url + self.UPDATE_ROBOT_ACTION.format(action_id=action_id)
|
|
117
143
|
|
|
118
144
|
run_status = "SUCCESS" if status == "success" else "ERROR"
|
|
119
145
|
|
|
120
146
|
data = {
|
|
121
147
|
"endTimestamp": get_current_time_string(),
|
|
122
148
|
"status": run_status,
|
|
123
|
-
"message": reason,
|
|
149
|
+
"message": reason[:255] if reason else "",
|
|
124
150
|
}
|
|
125
|
-
self.
|
|
151
|
+
_send_and_get_response(self._auth, url, data, patch)
|
|
152
|
+
|
|
153
|
+
def update_sample_status(
|
|
154
|
+
self, bl_sample_id: int, bl_sample_status: BLSampleStatus
|
|
155
|
+
) -> BLSample:
|
|
156
|
+
"""Update the blSampleStatus of a sample.
|
|
157
|
+
Args:
|
|
158
|
+
bl_sample_id: The sample ID
|
|
159
|
+
bl_sample_status: The sample status
|
|
160
|
+
status_message: An optional message
|
|
161
|
+
Returns:
|
|
162
|
+
The updated sample
|
|
163
|
+
"""
|
|
164
|
+
data = {"blSampleStatus": (str(bl_sample_status))}
|
|
165
|
+
response = _send_and_get_response(
|
|
166
|
+
self._auth, self._base_url + f"/samples/{bl_sample_id}", data, patch
|
|
167
|
+
)
|
|
168
|
+
return self._sample_from_json(response)
|
|
169
|
+
|
|
170
|
+
def _sample_from_json(self, response) -> BLSample:
|
|
171
|
+
return BLSample(
|
|
172
|
+
bl_sample_id=response["blSampleId"],
|
|
173
|
+
bl_sample_status=response["blSampleStatus"],
|
|
174
|
+
container_id=response["containerId"],
|
|
175
|
+
)
|
|
@@ -11,6 +11,7 @@ from ispyb.sp.mxacquisition import MXAcquisition
|
|
|
11
11
|
from ispyb.strictordereddict import StrictOrderedDict
|
|
12
12
|
from pydantic import BaseModel
|
|
13
13
|
|
|
14
|
+
from mx_bluesky.common.utils.tracing import TRACER
|
|
14
15
|
from mx_bluesky.hyperion.external_interaction.ispyb.data_model import (
|
|
15
16
|
DataCollectionGridInfo,
|
|
16
17
|
DataCollectionGroupInfo,
|
|
@@ -22,7 +23,6 @@ from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_utils import (
|
|
|
22
23
|
get_session_id_from_visit,
|
|
23
24
|
)
|
|
24
25
|
from mx_bluesky.hyperion.log import ISPYB_LOGGER
|
|
25
|
-
from mx_bluesky.hyperion.tracing import TRACER
|
|
26
26
|
|
|
27
27
|
if TYPE_CHECKING:
|
|
28
28
|
pass
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import time
|
|
4
|
-
from datetime import datetime, timedelta
|
|
4
|
+
from datetime import UTC, datetime, timedelta
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
from dodal.devices.detector import DetectorParams
|
|
@@ -93,7 +93,7 @@ def create_goniometer_axes(
|
|
|
93
93
|
|
|
94
94
|
def get_start_and_predicted_end_time(time_expected: float) -> tuple[str, str]:
|
|
95
95
|
time_format = r"%Y-%m-%dT%H:%M:%SZ"
|
|
96
|
-
start = datetime.
|
|
96
|
+
start = datetime.fromtimestamp(time.time(), tz=UTC)
|
|
97
97
|
end_est = start + timedelta(seconds=time_expected)
|
|
98
98
|
return start.strftime(time_format), end_est.strftime(time_format)
|
|
99
99
|
|
|
@@ -15,12 +15,12 @@ from nexgen.nxs_write.nxmx_writer import NXmxFileWriter
|
|
|
15
15
|
from numpy.typing import DTypeLike
|
|
16
16
|
from scanspec.core import AxesPoints
|
|
17
17
|
|
|
18
|
+
from mx_bluesky.common.parameters.components import DiffractionExperimentWithSample
|
|
18
19
|
from mx_bluesky.hyperion.external_interaction.nexus.nexus_utils import (
|
|
19
20
|
create_detector_parameters,
|
|
20
21
|
create_goniometer_axes,
|
|
21
22
|
get_start_and_predicted_end_time,
|
|
22
23
|
)
|
|
23
|
-
from mx_bluesky.hyperion.parameters.components import DiffractionExperimentWithSample
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class NexusWriter:
|
mx_bluesky/hyperion/log.py
CHANGED
|
@@ -1,23 +1,10 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from logging.handlers import TimedRotatingFileHandler
|
|
3
|
-
from os import environ
|
|
4
|
-
from pathlib import Path
|
|
5
2
|
|
|
6
|
-
from dodal.log import (
|
|
7
|
-
ERROR_LOG_BUFFER_LINES,
|
|
8
|
-
CircularMemoryHandler,
|
|
9
|
-
DodalLogHandlers,
|
|
10
|
-
integrate_bluesky_and_ophyd_logging,
|
|
11
|
-
set_up_all_logging_handlers,
|
|
12
|
-
)
|
|
13
3
|
from dodal.log import LOGGER as dodal_logger
|
|
14
4
|
|
|
15
|
-
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
16
|
-
|
|
17
5
|
LOGGER = logging.getLogger("Hyperion")
|
|
18
6
|
LOGGER.setLevel("DEBUG")
|
|
19
7
|
LOGGER.parent = dodal_logger
|
|
20
|
-
__logger_handlers: DodalLogHandlers | None = None
|
|
21
8
|
|
|
22
9
|
ISPYB_LOGGER = logging.getLogger("Hyperion ISPyB and Zocalo callbacks")
|
|
23
10
|
ISPYB_LOGGER.setLevel(logging.DEBUG)
|
|
@@ -26,74 +13,3 @@ NEXUS_LOGGER = logging.getLogger("Hyperion NeXus callbacks")
|
|
|
26
13
|
NEXUS_LOGGER.setLevel(logging.DEBUG)
|
|
27
14
|
|
|
28
15
|
ALL_LOGGERS = [LOGGER, ISPYB_LOGGER, NEXUS_LOGGER]
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class ExperimentMetadataTagFilter(logging.Filter):
|
|
32
|
-
dc_group_id: str | None = None
|
|
33
|
-
run_uid: str | None = None
|
|
34
|
-
|
|
35
|
-
def filter(self, record):
|
|
36
|
-
if self.dc_group_id:
|
|
37
|
-
record.dc_group_id = self.dc_group_id
|
|
38
|
-
if self.run_uid:
|
|
39
|
-
record.run_uid = self.run_uid
|
|
40
|
-
return True
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
tag_filter = ExperimentMetadataTagFilter()
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def set_dcgid_tag(dcgid):
|
|
47
|
-
"""Set the datacollection group id as a tag on all subsequent log messages.
|
|
48
|
-
Setting to None will remove the tag."""
|
|
49
|
-
tag_filter.dc_group_id = dcgid
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def set_uid_tag(uid):
|
|
53
|
-
tag_filter.run_uid = uid
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def do_default_logging_setup(dev_mode=False):
|
|
57
|
-
handlers = set_up_all_logging_handlers(
|
|
58
|
-
dodal_logger,
|
|
59
|
-
_get_logging_dir(),
|
|
60
|
-
"hyperion.log",
|
|
61
|
-
dev_mode,
|
|
62
|
-
ERROR_LOG_BUFFER_LINES,
|
|
63
|
-
CONST.GRAYLOG_PORT,
|
|
64
|
-
)
|
|
65
|
-
integrate_bluesky_and_ophyd_logging(dodal_logger)
|
|
66
|
-
handlers["graylog_handler"].addFilter(tag_filter)
|
|
67
|
-
|
|
68
|
-
global __logger_handlers
|
|
69
|
-
__logger_handlers = handlers
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def _get_debug_handler() -> CircularMemoryHandler:
|
|
73
|
-
assert (
|
|
74
|
-
__logger_handlers is not None
|
|
75
|
-
), "You can only use this after running the default logging setup"
|
|
76
|
-
return __logger_handlers["debug_memory_handler"]
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def flush_debug_handler() -> str:
|
|
80
|
-
"""Writes the contents of the circular debug log buffer to disk and returns the written filename"""
|
|
81
|
-
handler = _get_debug_handler()
|
|
82
|
-
assert isinstance(
|
|
83
|
-
handler.target, TimedRotatingFileHandler
|
|
84
|
-
), "Circular memory handler doesn't have an appropriate fileHandler target"
|
|
85
|
-
handler.flush()
|
|
86
|
-
return handler.target.baseFilename
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def _get_logging_dir() -> Path:
|
|
90
|
-
"""Get the path to write the hyperion log files to.
|
|
91
|
-
|
|
92
|
-
If the HYPERION_LOG_DIR environment variable exists then logs will be put in here.
|
|
93
|
-
If no environment variable is found it will default it to the ./tmp/dev directory.
|
|
94
|
-
|
|
95
|
-
Returns:
|
|
96
|
-
logging_path (Path): Path to the log file for the file handler to write to.
|
|
97
|
-
"""
|
|
98
|
-
logging_path = Path(environ.get("HYPERION_LOG_DIR") or "./tmp/dev/")
|
|
99
|
-
return logging_path
|
|
@@ -1,254 +1,7 @@
|
|
|
1
|
-
from
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
from abc import abstractmethod
|
|
5
|
-
from collections.abc import Sequence
|
|
6
|
-
from enum import StrEnum
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from typing import SupportsInt, TypeVar
|
|
3
|
+
from mx_bluesky.hyperion.external_interaction.config_server import HyperionFeatureFlags
|
|
9
4
|
|
|
10
|
-
from dodal.devices.aperturescatterguard import ApertureValue
|
|
11
|
-
from dodal.devices.detector import (
|
|
12
|
-
DetectorParams,
|
|
13
|
-
TriggerMode,
|
|
14
|
-
)
|
|
15
|
-
from pydantic import (
|
|
16
|
-
BaseModel,
|
|
17
|
-
ConfigDict,
|
|
18
|
-
Field,
|
|
19
|
-
field_serializer,
|
|
20
|
-
field_validator,
|
|
21
|
-
model_validator,
|
|
22
|
-
)
|
|
23
|
-
from scanspec.core import AxesPoints
|
|
24
|
-
from semver import Version
|
|
25
5
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
T = TypeVar("T")
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class ParameterVersion(Version):
|
|
33
|
-
@classmethod
|
|
34
|
-
def _parse(cls, version):
|
|
35
|
-
if isinstance(version, cls):
|
|
36
|
-
return version
|
|
37
|
-
return cls.parse(version)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
PARAMETER_VERSION = ParameterVersion.parse("5.1.0")
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class RotationAxis(StrEnum):
|
|
44
|
-
OMEGA = "omega"
|
|
45
|
-
PHI = "phi"
|
|
46
|
-
CHI = "chi"
|
|
47
|
-
KAPPA = "kappa"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class XyzAxis(StrEnum):
|
|
51
|
-
X = "sam_x"
|
|
52
|
-
Y = "sam_y"
|
|
53
|
-
Z = "sam_z"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
class IspybExperimentType(StrEnum):
|
|
57
|
-
# Enum values from ispyb column data type
|
|
58
|
-
SAD = "SAD" # at or slightly above the peak
|
|
59
|
-
SAD_INVERSE_BEAM = "SAD - Inverse Beam"
|
|
60
|
-
OSC = "OSC" # "native" (in the absence of a heavy atom)
|
|
61
|
-
COLLECT_MULTIWEDGE = (
|
|
62
|
-
"Collect - Multiwedge" # "poorly determined" ~ EDNA complex strategy???
|
|
63
|
-
)
|
|
64
|
-
MAD = "MAD"
|
|
65
|
-
HELICAL = "Helical"
|
|
66
|
-
MULTI_POSITIONAL = "Multi-positional"
|
|
67
|
-
MESH = "Mesh"
|
|
68
|
-
BURN = "Burn"
|
|
69
|
-
MAD_INVERSE_BEAM = "MAD - Inverse Beam"
|
|
70
|
-
CHARACTERIZATION = "Characterization"
|
|
71
|
-
DEHYDRATION = "Dehydration"
|
|
72
|
-
TOMO = "tomo"
|
|
73
|
-
EXPERIMENT = "experiment"
|
|
74
|
-
EM = "EM"
|
|
75
|
-
PDF = "PDF"
|
|
76
|
-
PDF_BRAGG = "PDF+Bragg"
|
|
77
|
-
BRAGG = "Bragg"
|
|
78
|
-
SINGLE_PARTICLE = "single particle"
|
|
79
|
-
SERIAL_FIXED = "Serial Fixed"
|
|
80
|
-
SERIAL_JET = "Serial Jet"
|
|
81
|
-
STANDARD = "Standard" # Routine structure determination experiment
|
|
82
|
-
TIME_RESOLVED = "Time Resolved" # Investigate the change of a system over time
|
|
83
|
-
DLS_ANVIL_HP = "Diamond Anvil High Pressure" # HP sample environment pressure cell
|
|
84
|
-
CUSTOM = "Custom" # Special or non-standard data collection
|
|
85
|
-
XRF_MAP = "XRF map"
|
|
86
|
-
ENERGY_SCAN = "Energy scan"
|
|
87
|
-
XRF_SPECTRUM = "XRF spectrum"
|
|
88
|
-
XRF_MAP_XAS = "XRF map xas"
|
|
89
|
-
MESH_3D = "Mesh3D"
|
|
90
|
-
SCREENING = "Screening"
|
|
91
|
-
STILL = "Still"
|
|
92
|
-
SSX_CHIP = "SSX-Chip"
|
|
93
|
-
SSX_JET = "SSX-Jet"
|
|
94
|
-
|
|
95
|
-
# Aliases for historic hyperion experiment type mapping
|
|
96
|
-
ROTATION = "SAD"
|
|
97
|
-
GRIDSCAN_2D = "mesh"
|
|
98
|
-
GRIDSCAN_3D = "Mesh3D"
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
class HyperionParameters(BaseModel):
|
|
102
|
-
model_config = ConfigDict(
|
|
103
|
-
arbitrary_types_allowed=True,
|
|
104
|
-
extra="allow",
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
def __hash__(self) -> int:
|
|
108
|
-
return self.json().__hash__()
|
|
109
|
-
|
|
110
|
-
features: FeatureFlags = Field(default=FeatureFlags())
|
|
111
|
-
parameter_model_version: ParameterVersion
|
|
112
|
-
|
|
113
|
-
@field_serializer("parameter_model_version")
|
|
114
|
-
def serialize_parameter_version(self, version: ParameterVersion):
|
|
115
|
-
return str(version)
|
|
116
|
-
|
|
117
|
-
@field_validator("parameter_model_version", mode="before")
|
|
118
|
-
@classmethod
|
|
119
|
-
def _validate_version(cls, version_str: str):
|
|
120
|
-
version = ParameterVersion.parse(version_str)
|
|
121
|
-
assert (
|
|
122
|
-
version >= ParameterVersion(major=PARAMETER_VERSION.major)
|
|
123
|
-
), f"Parameter version too old! This version of hyperion uses {PARAMETER_VERSION}"
|
|
124
|
-
assert (
|
|
125
|
-
version <= ParameterVersion(major=PARAMETER_VERSION.major + 1)
|
|
126
|
-
), f"Parameter version too new! This version of hyperion uses {PARAMETER_VERSION}"
|
|
127
|
-
return version
|
|
128
|
-
|
|
129
|
-
@classmethod
|
|
130
|
-
def from_json(cls, input: str | None):
|
|
131
|
-
assert input is not None
|
|
132
|
-
return cls(**json.loads(input))
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
class WithSnapshot(BaseModel):
|
|
136
|
-
snapshot_directory: Path
|
|
137
|
-
snapshot_omegas_deg: list[float] | None = None
|
|
138
|
-
|
|
139
|
-
@property
|
|
140
|
-
def take_snapshots(self) -> bool:
|
|
141
|
-
return bool(self.snapshot_omegas_deg)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
class WithOptionalEnergyChange(BaseModel):
|
|
145
|
-
demand_energy_ev: float | None = Field(default=None, gt=0)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
class WithVisit(BaseModel):
|
|
149
|
-
visit: str = Field(min_length=1)
|
|
150
|
-
zocalo_environment: str = Field(default=CONST.ZOCALO_ENV)
|
|
151
|
-
beamline: str = Field(default=CONST.I03.BEAMLINE, pattern=r"BL\d{2}[BIJS]")
|
|
152
|
-
det_dist_to_beam_converter_path: str = Field(
|
|
153
|
-
default=CONST.PARAM.DETECTOR.BEAM_XY_LUT_PATH
|
|
154
|
-
)
|
|
155
|
-
insertion_prefix: str = Field(
|
|
156
|
-
default=CONST.I03.INSERTION_PREFIX, pattern=r"SR\d{2}[BIJS]"
|
|
157
|
-
)
|
|
158
|
-
detector_distance_mm: float | None = Field(default=None, gt=0)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
class DiffractionExperiment(
|
|
162
|
-
HyperionParameters, WithSnapshot, WithOptionalEnergyChange, WithVisit
|
|
163
|
-
):
|
|
164
|
-
"""For all experiments which use beam"""
|
|
165
|
-
|
|
166
|
-
file_name: str
|
|
167
|
-
exposure_time_s: float = Field(gt=0)
|
|
168
|
-
comment: str = Field(default="")
|
|
169
|
-
trigger_mode: TriggerMode = Field(default=TriggerMode.FREE_RUN)
|
|
170
|
-
run_number: int | None = Field(default=None, ge=0)
|
|
171
|
-
selected_aperture: ApertureValue | None = Field(default=None)
|
|
172
|
-
transmission_frac: float = Field(default=0.1)
|
|
173
|
-
ispyb_experiment_type: IspybExperimentType
|
|
174
|
-
storage_directory: str
|
|
175
|
-
|
|
176
|
-
@model_validator(mode="before")
|
|
177
|
-
@classmethod
|
|
178
|
-
def validate_snapshot_directory(cls, values):
|
|
179
|
-
snapshot_dir = values.get(
|
|
180
|
-
"snapshot_directory", Path(values["storage_directory"], "snapshots")
|
|
181
|
-
)
|
|
182
|
-
values["snapshot_directory"] = (
|
|
183
|
-
snapshot_dir if isinstance(snapshot_dir, Path) else Path(snapshot_dir)
|
|
184
|
-
)
|
|
185
|
-
return values
|
|
186
|
-
|
|
187
|
-
@property
|
|
188
|
-
def num_images(self) -> int:
|
|
189
|
-
return 0
|
|
190
|
-
|
|
191
|
-
@property
|
|
192
|
-
@abstractmethod
|
|
193
|
-
def detector_params(self) -> DetectorParams: ...
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
class WithScan(BaseModel):
|
|
197
|
-
"""For experiments where the scan is known"""
|
|
198
|
-
|
|
199
|
-
@property
|
|
200
|
-
@abstractmethod
|
|
201
|
-
def scan_points(self) -> AxesPoints: ...
|
|
202
|
-
|
|
203
|
-
@property
|
|
204
|
-
@abstractmethod
|
|
205
|
-
def num_images(self) -> int: ...
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
class SplitScan(BaseModel):
|
|
209
|
-
@property
|
|
210
|
-
@abstractmethod
|
|
211
|
-
def scan_indices(self) -> Sequence[SupportsInt]:
|
|
212
|
-
"""Should return the first index of each scan (i.e. for each nexus file)"""
|
|
213
|
-
...
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
class WithSample(BaseModel):
|
|
217
|
-
sample_id: int
|
|
218
|
-
sample_puck: int | None = None
|
|
219
|
-
sample_pin: int | None = None
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
class DiffractionExperimentWithSample(DiffractionExperiment, WithSample): ...
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
class WithOavCentring(BaseModel):
|
|
226
|
-
oav_centring_file: str = Field(default=CONST.I03.OAV_CENTRING_FILE)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
class OptionalXyzStarts(BaseModel):
|
|
230
|
-
x_start_um: float | None = None
|
|
231
|
-
y_start_um: float | None = None
|
|
232
|
-
z_start_um: float | None = None
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
class XyzStarts(BaseModel):
|
|
236
|
-
x_start_um: float
|
|
237
|
-
y_start_um: float
|
|
238
|
-
z_start_um: float
|
|
239
|
-
|
|
240
|
-
def _start_for_axis(self, axis: XyzAxis) -> float:
|
|
241
|
-
match axis:
|
|
242
|
-
case XyzAxis.X:
|
|
243
|
-
return self.x_start_um
|
|
244
|
-
case XyzAxis.Y:
|
|
245
|
-
return self.y_start_um
|
|
246
|
-
case XyzAxis.Z:
|
|
247
|
-
return self.z_start_um
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
class OptionalGonioAngleStarts(BaseModel):
|
|
251
|
-
omega_start_deg: float | None = None
|
|
252
|
-
phi_start_deg: float | None = None
|
|
253
|
-
chi_start_deg: float | None = None
|
|
254
|
-
kappa_start_deg: float | None = None
|
|
6
|
+
class WithHyperionFeatures(BaseModel):
|
|
7
|
+
features: HyperionFeatureFlags = Field(default=HyperionFeatureFlags())
|