mx-bluesky 1.2.0__py3-none-any.whl → 1.4.1a0__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.
Files changed (94) hide show
  1. mx_bluesky/__init__.py +8 -3
  2. mx_bluesky/__main__.py +12 -7
  3. mx_bluesky/_version.py +2 -2
  4. mx_bluesky/beamlines/i04/callbacks/murko_callback.py +14 -4
  5. mx_bluesky/beamlines/i04/thawing_plan.py +49 -11
  6. mx_bluesky/beamlines/i24/serial/__init__.py +3 -0
  7. mx_bluesky/beamlines/i24/serial/dcid.py +19 -21
  8. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +69 -91
  9. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +2 -5
  10. mx_bluesky/beamlines/i24/serial/fixed_target/ft_utils.py +0 -1
  11. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +111 -143
  12. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +141 -222
  13. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +7 -216
  14. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +18 -17
  15. mx_bluesky/beamlines/i24/serial/log.py +58 -49
  16. mx_bluesky/beamlines/i24/serial/parameters/constants.py +0 -1
  17. mx_bluesky/beamlines/i24/serial/parameters/fixed_target/cs/cs_maker.json +3 -3
  18. mx_bluesky/beamlines/i24/serial/run_extruder.sh +30 -5
  19. mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +30 -5
  20. mx_bluesky/beamlines/i24/serial/run_serial.py +24 -8
  21. mx_bluesky/beamlines/i24/serial/setup_beamline/ca.py +0 -2
  22. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +79 -81
  23. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +9 -20
  24. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +26 -28
  25. mx_bluesky/beamlines/i24/serial/write_nexus.py +11 -11
  26. mx_bluesky/common/__init__.py +0 -0
  27. mx_bluesky/common/device_setup_plans/read_hardware_for_setup.py +14 -0
  28. mx_bluesky/common/external_interaction/config_server.py +46 -0
  29. mx_bluesky/common/parameters/components.py +258 -0
  30. mx_bluesky/common/parameters/constants.py +138 -0
  31. mx_bluesky/common/parameters/gridscan.py +94 -0
  32. mx_bluesky/common/parameters/robot_load.py +16 -0
  33. mx_bluesky/common/plans/__init__.py +1 -0
  34. mx_bluesky/common/plans/do_fgs.py +121 -0
  35. mx_bluesky/common/utils/log.py +118 -0
  36. mx_bluesky/{hyperion → common/utils}/tracing.py +2 -2
  37. mx_bluesky/hyperion/__main__.py +13 -10
  38. mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +31 -26
  39. mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +6 -12
  40. mx_bluesky/hyperion/device_setup_plans/setup_oav.py +6 -12
  41. mx_bluesky/hyperion/device_setup_plans/setup_panda.py +5 -6
  42. mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +49 -18
  43. mx_bluesky/hyperion/device_setup_plans/smargon.py +6 -6
  44. mx_bluesky/hyperion/device_setup_plans/utils.py +2 -2
  45. mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +4 -4
  46. mx_bluesky/hyperion/experiment_plans/__init__.py +4 -0
  47. mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +83 -0
  48. mx_bluesky/hyperion/experiment_plans/common/xrc_result.py +47 -0
  49. mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -9
  50. mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +145 -161
  51. mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +56 -22
  52. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +52 -10
  53. mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +21 -20
  54. mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +11 -14
  55. mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +2 -2
  56. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +40 -21
  57. mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +19 -19
  58. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +21 -21
  59. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +51 -13
  60. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +24 -7
  61. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +5 -6
  62. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +1 -2
  63. mx_bluesky/hyperion/external_interaction/callbacks/common/abstract_event.py +66 -0
  64. mx_bluesky/hyperion/external_interaction/callbacks/common/ispyb_mapping.py +1 -1
  65. mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +30 -25
  66. mx_bluesky/hyperion/external_interaction/callbacks/ispyb_callback_base.py +29 -12
  67. mx_bluesky/hyperion/external_interaction/callbacks/log_uid_tag_callback.py +1 -1
  68. mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +1 -1
  69. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +7 -4
  70. mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +5 -3
  71. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +28 -20
  72. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +5 -4
  73. mx_bluesky/hyperion/external_interaction/config_server.py +11 -28
  74. mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +1 -1
  75. mx_bluesky/hyperion/external_interaction/ispyb/ispyb_store.py +1 -1
  76. mx_bluesky/hyperion/external_interaction/nexus/nexus_utils.py +2 -2
  77. mx_bluesky/hyperion/external_interaction/nexus/write_nexus.py +1 -1
  78. mx_bluesky/hyperion/log.py +0 -84
  79. mx_bluesky/hyperion/parameters/components.py +4 -251
  80. mx_bluesky/hyperion/parameters/constants.py +22 -119
  81. mx_bluesky/hyperion/parameters/gridscan.py +35 -74
  82. mx_bluesky/hyperion/parameters/load_centre_collect.py +16 -11
  83. mx_bluesky/hyperion/parameters/rotation.py +23 -10
  84. mx_bluesky/hyperion/utils/utils.py +17 -0
  85. mx_bluesky/hyperion/utils/validation.py +5 -6
  86. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/METADATA +36 -33
  87. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/RECORD +91 -81
  88. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/WHEEL +1 -1
  89. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +0 -161
  90. mx_bluesky/example.py +0 -19
  91. mx_bluesky/hyperion/parameters/robot_load.py +0 -16
  92. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/LICENSE +0 -0
  93. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/entry_points.txt +0 -0
  94. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/top_level.txt +0 -0
@@ -5,10 +5,16 @@ from time import time
5
5
  from typing import TYPE_CHECKING, Any
6
6
 
7
7
  import numpy as np
8
- from blueapi.core import MsgGenerator
9
8
  from bluesky import preprocessors as bpp
10
- from dodal.devices.zocalo.zocalo_results import ZOCALO_READING_PLAN_NAME
9
+ from bluesky.utils import MsgGenerator
10
+ from dodal.devices.zocalo.zocalo_results import (
11
+ ZOCALO_READING_PLAN_NAME,
12
+ get_processing_results_from_event,
13
+ )
11
14
 
15
+ from mx_bluesky.common.parameters.components import DiffractionExperimentWithSample
16
+ from mx_bluesky.common.parameters.constants import PlanNameConstants
17
+ from mx_bluesky.common.utils.log import set_dcgid_tag
12
18
  from mx_bluesky.hyperion.external_interaction.callbacks.common.ispyb_mapping import (
13
19
  populate_data_collection_group,
14
20
  populate_remaining_data_collection_info,
@@ -36,8 +42,7 @@ from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_store import (
36
42
  IspybIds,
37
43
  StoreInIspyb,
38
44
  )
39
- from mx_bluesky.hyperion.log import ISPYB_LOGGER, set_dcgid_tag
40
- from mx_bluesky.hyperion.parameters.components import DiffractionExperimentWithSample
45
+ from mx_bluesky.hyperion.log import ISPYB_LOGGER
41
46
  from mx_bluesky.hyperion.parameters.constants import CONST
42
47
  from mx_bluesky.hyperion.parameters.gridscan import (
43
48
  GridCommon,
@@ -85,7 +90,7 @@ class GridscanISPyBCallback(BaseISPyBCallback):
85
90
  self._processing_start_time: float | None = None
86
91
 
87
92
  def activity_gated_start(self, doc: RunStart):
88
- if doc.get("subplan_name") == CONST.PLAN.DO_FGS:
93
+ if doc.get("subplan_name") == PlanNameConstants.DO_FGS:
89
94
  self._start_of_fgs_uid = doc.get("uid")
90
95
  if doc.get("subplan_name") == CONST.PLAN.GRID_DETECT_AND_DO_GRIDSCAN:
91
96
  self.uid_to_finalize_on = doc.get("uid")
@@ -93,7 +98,9 @@ class GridscanISPyBCallback(BaseISPyBCallback):
93
98
  "ISPyB callback received start document with experiment parameters and "
94
99
  f"uid: {self.uid_to_finalize_on}"
95
100
  )
96
- self.params = GridCommon.from_json(doc.get("hyperion_parameters"))
101
+ hyperion_params = doc.get("hyperion_parameters")
102
+ assert isinstance(hyperion_params, str)
103
+ self.params = GridCommon.model_validate_json(hyperion_params)
97
104
  self.ispyb = StoreInIspyb(self.ispyb_config)
98
105
  data_collection_group_info = populate_data_collection_group(self.params)
99
106
 
@@ -147,7 +154,8 @@ class GridscanISPyBCallback(BaseISPyBCallback):
147
154
  ISPYB_LOGGER.info(
148
155
  f"Amending comment based on Zocalo reading doc: {format_doc_for_log(doc)}"
149
156
  )
150
- raw_results = doc["data"]["zocalo-results"]
157
+
158
+ raw_results = get_processing_results_from_event("zocalo", doc)
151
159
  if len(raw_results) > 0:
152
160
  for n, res in enumerate(raw_results):
153
161
  bb = res["bounding_box"]
@@ -178,25 +186,25 @@ class GridscanISPyBCallback(BaseISPyBCallback):
178
186
  data = doc["data"]
179
187
  data_collection_id = None
180
188
  data_collection_info = DataCollectionInfo(
181
- xtal_snapshot1=data.get("oav_grid_snapshot_last_path_full_overlay"),
182
- xtal_snapshot2=data.get("oav_grid_snapshot_last_path_outer"),
183
- xtal_snapshot3=data.get("oav_grid_snapshot_last_saved_path"),
189
+ xtal_snapshot1=data.get("oav-grid_snapshot-last_path_full_overlay"),
190
+ xtal_snapshot2=data.get("oav-grid_snapshot-last_path_outer"),
191
+ xtal_snapshot3=data.get("oav-grid_snapshot-last_saved_path"),
184
192
  n_images=(
185
- data["oav_grid_snapshot_num_boxes_x"]
186
- * data["oav_grid_snapshot_num_boxes_y"]
193
+ data["oav-grid_snapshot-num_boxes_x"]
194
+ * data["oav-grid_snapshot-num_boxes_y"]
187
195
  ),
188
196
  )
189
- microns_per_pixel_x = data["oav_grid_snapshot_microns_per_pixel_x"]
190
- microns_per_pixel_y = data["oav_grid_snapshot_microns_per_pixel_y"]
197
+ microns_per_pixel_x = data["oav-microns_per_pixel_x"]
198
+ microns_per_pixel_y = data["oav-microns_per_pixel_y"]
191
199
  data_collection_grid_info = DataCollectionGridInfo(
192
- dx_in_mm=data["oav_grid_snapshot_box_width"] * microns_per_pixel_x / 1000,
193
- dy_in_mm=data["oav_grid_snapshot_box_width"] * microns_per_pixel_y / 1000,
194
- steps_x=data["oav_grid_snapshot_num_boxes_x"],
195
- steps_y=data["oav_grid_snapshot_num_boxes_y"],
200
+ dx_in_mm=data["oav-grid_snapshot-box_width"] * microns_per_pixel_x / 1000,
201
+ dy_in_mm=data["oav-grid_snapshot-box_width"] * microns_per_pixel_y / 1000,
202
+ steps_x=data["oav-grid_snapshot-num_boxes_x"],
203
+ steps_y=data["oav-grid_snapshot-num_boxes_y"],
196
204
  microns_per_pixel_x=microns_per_pixel_x,
197
205
  microns_per_pixel_y=microns_per_pixel_y,
198
- snapshot_offset_x_pixel=int(data["oav_grid_snapshot_top_left_x"]),
199
- snapshot_offset_y_pixel=int(data["oav_grid_snapshot_top_left_y"]),
206
+ snapshot_offset_x_pixel=int(data["oav-grid_snapshot-top_left_x"]),
207
+ snapshot_offset_y_pixel=int(data["oav-grid_snapshot-top_left_y"]),
200
208
  orientation=Orientation.HORIZONTAL,
201
209
  snaked=True,
202
210
  )
@@ -12,7 +12,7 @@ from mx_bluesky.hyperion.external_interaction.nexus.nexus_utils import (
12
12
  from mx_bluesky.hyperion.external_interaction.nexus.write_nexus import NexusWriter
13
13
  from mx_bluesky.hyperion.log import NEXUS_LOGGER
14
14
  from mx_bluesky.hyperion.parameters.constants import CONST
15
- from mx_bluesky.hyperion.parameters.gridscan import ThreeDGridScan
15
+ from mx_bluesky.hyperion.parameters.gridscan import HyperionThreeDGridScan
16
16
 
17
17
  if TYPE_CHECKING:
18
18
  from event_model.documents import Event, EventDescriptor, RunStart
@@ -45,11 +45,12 @@ class GridscanNexusFileCallback(PlanReactiveCallback):
45
45
 
46
46
  def activity_gated_start(self, doc: RunStart):
47
47
  if doc.get("subplan_name") == CONST.PLAN.GRIDSCAN_OUTER:
48
- json_params = doc.get("hyperion_parameters")
48
+ hyperion_params = doc.get("hyperion_parameters")
49
+ assert isinstance(hyperion_params, str)
49
50
  NEXUS_LOGGER.info(
50
- f"Nexus writer received start document with experiment parameters {json_params}"
51
+ f"Nexus writer received start document with experiment parameters {hyperion_params}"
51
52
  )
52
- parameters = ThreeDGridScan.from_json(json_params)
53
+ parameters = HyperionThreeDGridScan.model_validate_json(hyperion_params)
53
54
  d_size = parameters.detector_params.detector_size_constants.det_size_pixels
54
55
  grid_n_img_1 = parameters.scan_indices[1]
55
56
  grid_n_img_2 = parameters.num_images - grid_n_img_1
@@ -1,35 +1,18 @@
1
+ from functools import cache
2
+
1
3
  from daq_config_server.client import ConfigServer
2
- from pydantic import BaseModel
3
4
 
5
+ from mx_bluesky.common.external_interaction.config_server import FeatureFlags
4
6
  from mx_bluesky.hyperion.log import LOGGER
5
7
  from mx_bluesky.hyperion.parameters.constants import CONST
6
8
 
7
- _CONFIG_SERVER: ConfigServer | None = None
8
-
9
-
10
- def config_server() -> ConfigServer:
11
- global _CONFIG_SERVER
12
- if _CONFIG_SERVER is None:
13
- _CONFIG_SERVER = ConfigServer(CONST.CONFIG_SERVER_URL, LOGGER)
14
- return _CONFIG_SERVER
15
-
16
-
17
- class FeatureFlags(BaseModel):
18
- # The default value will be used as the fallback when doing a best-effort fetch
19
- # from the service
20
- use_panda_for_gridscan: bool = False
21
- use_gpu_for_gridscan: bool = False
22
- set_stub_offsets: bool = False
23
-
24
- @classmethod
25
- def _get_flags(cls):
26
- flags = config_server().best_effort_get_all_feature_flags()
27
- return {f: flags[f] for f in flags if f in cls.__fields__.keys()}
28
9
 
29
- @classmethod
30
- def best_effort(cls):
31
- return cls(**cls._get_flags())
10
+ class HyperionFeatureFlags(FeatureFlags):
11
+ @staticmethod
12
+ @cache
13
+ def get_config_server() -> ConfigServer:
14
+ return ConfigServer(CONST.CONFIG_SERVER_URL, LOGGER)
32
15
 
33
- def update_self_from_server(self):
34
- for flag, value in self._get_flags().items():
35
- setattr(self, flag, value)
16
+ use_panda_for_gridscan: bool = CONST.I03.USE_PANDA_FOR_GRIDSCAN
17
+ compare_cpu_and_gpu_zocalo: bool = CONST.I03.COMPARE_CPU_AND_GPU_ZOCALO
18
+ set_stub_offsets: bool = CONST.I03.SET_STUB_OFFSETS
@@ -35,7 +35,7 @@ class ExpeyeInteraction:
35
35
 
36
36
  def __init__(self) -> None:
37
37
  url, token = _get_base_url_and_token()
38
- self.base_url = url + "/core"
38
+ self.base_url = url
39
39
  self.auth = BearerAuth(token)
40
40
 
41
41
  def _send_and_get_response(self, url, data, send_func) -> dict:
@@ -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.utcfromtimestamp(time.time())
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:
@@ -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 __future__ import annotations
1
+ from pydantic import BaseModel, Field
2
2
 
3
- import json
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
- from mx_bluesky.hyperion.external_interaction.config_server import FeatureFlags
27
- from mx_bluesky.hyperion.parameters.constants import CONST
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())