mx-bluesky 1.4.2__py3-none-any.whl → 1.4.4__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 (92) hide show
  1. mx_bluesky/_version.py +2 -2
  2. mx_bluesky/beamlines/i24/serial/dcid.py +3 -3
  3. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +24 -9
  4. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +13 -4
  5. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +1 -1
  6. mx_bluesky/beamlines/i24/serial/parameters/__init__.py +2 -1
  7. mx_bluesky/beamlines/i24/serial/parameters/constants.py +13 -5
  8. mx_bluesky/beamlines/i24/serial/parameters/experiment_parameters.py +20 -4
  9. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +40 -11
  10. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +81 -40
  11. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/aperture_change_callback.py +1 -1
  12. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/grid_detection_callback.py +19 -1
  13. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/ispyb_callback_base.py +40 -34
  14. mx_bluesky/{hyperion → common}/external_interaction/callbacks/common/ispyb_mapping.py +4 -4
  15. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/logging_callback.py +1 -1
  16. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/zocalo_callback.py +13 -19
  17. mx_bluesky/{hyperion → common}/external_interaction/callbacks/xray_centre/ispyb_callback.py +39 -34
  18. mx_bluesky/{hyperion → common}/external_interaction/callbacks/xray_centre/ispyb_mapping.py +2 -2
  19. mx_bluesky/{hyperion → common}/external_interaction/callbacks/xray_centre/nexus_callback.py +20 -15
  20. mx_bluesky/common/external_interaction/config_server.py +11 -0
  21. mx_bluesky/common/external_interaction/ispyb/__init__.py +0 -0
  22. mx_bluesky/{hyperion → common}/external_interaction/ispyb/data_model.py +2 -0
  23. mx_bluesky/{hyperion → common}/external_interaction/ispyb/exp_eye_store.py +5 -5
  24. mx_bluesky/{hyperion → common}/external_interaction/ispyb/ispyb_store.py +20 -18
  25. mx_bluesky/{hyperion → common}/external_interaction/ispyb/ispyb_utils.py +2 -2
  26. mx_bluesky/common/external_interaction/nexus/__init__.py +0 -0
  27. mx_bluesky/{hyperion → common}/external_interaction/nexus/nexus_utils.py +21 -6
  28. mx_bluesky/{hyperion → common}/external_interaction/nexus/write_nexus.py +5 -5
  29. mx_bluesky/common/external_interaction/test_config_server.py +38 -0
  30. mx_bluesky/common/parameters/components.py +9 -8
  31. mx_bluesky/common/parameters/constants.py +1 -0
  32. mx_bluesky/common/parameters/gridscan.py +107 -53
  33. mx_bluesky/common/plans/do_fgs.py +4 -10
  34. mx_bluesky/{hyperion → common/utils}/exceptions.py +15 -1
  35. mx_bluesky/common/utils/log.py +17 -7
  36. mx_bluesky/hyperion/__main__.py +15 -14
  37. mx_bluesky/hyperion/device_setup_plans/check_beamstop.py +27 -0
  38. mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +13 -6
  39. mx_bluesky/hyperion/device_setup_plans/manipulate_sample.py +1 -1
  40. mx_bluesky/hyperion/device_setup_plans/position_detector.py +1 -1
  41. mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +3 -3
  42. mx_bluesky/hyperion/device_setup_plans/setup_panda.py +21 -4
  43. mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +62 -36
  44. mx_bluesky/hyperion/device_setup_plans/smargon.py +1 -1
  45. mx_bluesky/hyperion/device_setup_plans/utils.py +4 -0
  46. mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +8 -8
  47. mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +28 -17
  48. mx_bluesky/hyperion/experiment_plans/common/xrc_result.py +10 -1
  49. mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -9
  50. mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +39 -49
  51. mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +22 -23
  52. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +4 -11
  53. mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +3 -3
  54. mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +6 -14
  55. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +12 -11
  56. mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +2 -2
  57. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +9 -4
  58. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +10 -11
  59. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +33 -17
  60. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +2 -2
  61. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +32 -23
  62. mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +60 -34
  63. mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +22 -15
  64. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +25 -24
  65. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +1 -1
  66. mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +13 -9
  67. mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/sample_handling_callback.py +12 -46
  68. mx_bluesky/hyperion/external_interaction/config_server.py +15 -1
  69. mx_bluesky/hyperion/parameters/components.py +3 -2
  70. mx_bluesky/hyperion/parameters/constants.py +1 -0
  71. mx_bluesky/hyperion/parameters/gridscan.py +54 -89
  72. mx_bluesky/hyperion/parameters/load_centre_collect.py +51 -6
  73. mx_bluesky/hyperion/parameters/robot_load.py +40 -0
  74. mx_bluesky/hyperion/parameters/rotation.py +28 -3
  75. mx_bluesky/hyperion/utils/context.py +1 -1
  76. mx_bluesky/hyperion/utils/validation.py +4 -2
  77. {mx_bluesky-1.4.2.dist-info → mx_bluesky-1.4.4.dist-info}/METADATA +6 -6
  78. {mx_bluesky-1.4.2.dist-info → mx_bluesky-1.4.4.dist-info}/RECORD +89 -87
  79. {mx_bluesky-1.4.2.dist-info → mx_bluesky-1.4.4.dist-info}/WHEEL +1 -1
  80. mx_bluesky/common/parameters/robot_load.py +0 -16
  81. mx_bluesky/hyperion/external_interaction/exceptions.py +0 -4
  82. mx_bluesky/hyperion/log.py +0 -15
  83. /mx_bluesky/{hyperion/external_interaction/callbacks/xray_centre → common/external_interaction}/__init__.py +0 -0
  84. /mx_bluesky/{hyperion/external_interaction/ispyb → common/external_interaction/callbacks/common}/__init__.py +0 -0
  85. /mx_bluesky/{hyperion → common}/external_interaction/callbacks/common/abstract_event.py +0 -0
  86. /mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/log_uid_tag_callback.py +0 -0
  87. /mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/plan_reactive_callback.py +0 -0
  88. /mx_bluesky/{hyperion/external_interaction/nexus → common/external_interaction/callbacks/xray_centre}/__init__.py +0 -0
  89. /mx_bluesky/{hyperion → common}/utils/utils.py +0 -0
  90. {mx_bluesky-1.4.2.dist-info → mx_bluesky-1.4.4.dist-info}/LICENSE +0 -0
  91. {mx_bluesky-1.4.2.dist-info → mx_bluesky-1.4.4.dist-info}/entry_points.txt +0 -0
  92. {mx_bluesky-1.4.2.dist-info → mx_bluesky-1.4.4.dist-info}/top_level.txt +0 -0
@@ -9,26 +9,26 @@ from dodal.devices.detector import DetectorParams
9
9
  from dodal.devices.detector.det_resolution import resolution
10
10
  from dodal.devices.synchrotron import SynchrotronMode
11
11
 
12
- from mx_bluesky.common.parameters.components import DiffractionExperimentWithSample
13
- from mx_bluesky.common.utils.log import set_dcgid_tag
14
- from mx_bluesky.hyperion.external_interaction.callbacks.plan_reactive_callback import (
12
+ from mx_bluesky.common.external_interaction.callbacks.common.logging_callback import (
13
+ format_doc_for_log,
14
+ )
15
+ from mx_bluesky.common.external_interaction.callbacks.common.plan_reactive_callback import (
15
16
  PlanReactiveCallback,
16
17
  )
17
- from mx_bluesky.hyperion.external_interaction.ispyb.data_model import (
18
+ from mx_bluesky.common.external_interaction.ispyb.data_model import (
18
19
  DataCollectionInfo,
19
20
  DataCollectionPositionInfo,
20
21
  ScanDataInfo,
21
22
  )
22
- from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_store import (
23
+ from mx_bluesky.common.external_interaction.ispyb.ispyb_store import (
23
24
  IspybIds,
24
25
  StoreInIspyb,
25
26
  )
26
- from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_utils import get_ispyb_config
27
- from mx_bluesky.hyperion.log import ISPYB_LOGGER
28
- from mx_bluesky.hyperion.parameters.constants import CONST
29
- from mx_bluesky.hyperion.utils.utils import convert_eV_to_angstrom
30
-
31
- from .logging_callback import format_doc_for_log
27
+ from mx_bluesky.common.external_interaction.ispyb.ispyb_utils import get_ispyb_config
28
+ from mx_bluesky.common.parameters.components import DiffractionExperimentWithSample
29
+ from mx_bluesky.common.parameters.constants import DocDescriptorNames, SimConstants
30
+ from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER, set_dcgid_tag
31
+ from mx_bluesky.common.utils.utils import convert_eV_to_angstrom
32
32
 
33
33
  D = TypeVar("D")
34
34
  if TYPE_CHECKING:
@@ -63,25 +63,25 @@ class BaseISPyBCallback(PlanReactiveCallback):
63
63
  """Subclasses should run super().__init__() with parameters, then set
64
64
  self.ispyb to the type of ispyb relevant to the experiment and define the type
65
65
  for self.ispyb_ids."""
66
- ISPYB_LOGGER.debug("Initialising ISPyB callback")
67
- super().__init__(log=ISPYB_LOGGER, emit=emit)
66
+ ISPYB_ZOCALO_CALLBACK_LOGGER.debug("Initialising ISPyB callback")
67
+ super().__init__(log=ISPYB_ZOCALO_CALLBACK_LOGGER, emit=emit)
68
68
  self._oav_snapshot_event_idx: int = 0
69
69
  self.params: DiffractionExperimentWithSample | None = None
70
70
  self.ispyb: StoreInIspyb
71
71
  self.descriptors: dict[str, EventDescriptor] = {}
72
72
  self.ispyb_config = get_ispyb_config()
73
73
  if (
74
- self.ispyb_config == CONST.SIM.ISPYB_CONFIG
75
- or self.ispyb_config == CONST.SIM.DEV_ISPYB_DATABASE_CFG
74
+ self.ispyb_config == SimConstants.ISPYB_CONFIG
75
+ or self.ispyb_config == SimConstants.DEV_ISPYB_DATABASE_CFG
76
76
  ):
77
- ISPYB_LOGGER.warning(
77
+ ISPYB_ZOCALO_CALLBACK_LOGGER.warning(
78
78
  f"{self.__class__} using dev ISPyB config: {self.ispyb_config}. If you"
79
79
  "want to use the real database, please set the ISPYB_CONFIG_PATH "
80
80
  "environment variable."
81
81
  )
82
82
  self.uid_to_finalize_on: str | None = None
83
83
  self.ispyb_ids: IspybIds = IspybIds()
84
- self.log = ISPYB_LOGGER
84
+ self.log = ISPYB_ZOCALO_CALLBACK_LOGGER
85
85
 
86
86
  def activity_gated_start(self, doc: RunStart):
87
87
  self._oav_snapshot_event_idx = 0
@@ -94,31 +94,33 @@ class BaseISPyBCallback(PlanReactiveCallback):
94
94
  def activity_gated_event(self, doc: Event) -> Event:
95
95
  """Subclasses should extend this to add a call to set_dcig_tag from
96
96
  hyperion.log"""
97
- ISPYB_LOGGER.debug("ISPyB handler received event document.")
97
+ ISPYB_ZOCALO_CALLBACK_LOGGER.debug("ISPyB handler received event document.")
98
98
  assert self.ispyb is not None, "ISPyB deposition wasn't initialised!"
99
99
  assert self.params is not None, "ISPyB handler didn't receive parameters!"
100
100
 
101
101
  event_descriptor = self.descriptors.get(doc["descriptor"])
102
102
  if event_descriptor is None:
103
- ISPYB_LOGGER.warning(
103
+ ISPYB_ZOCALO_CALLBACK_LOGGER.warning(
104
104
  f"Ispyb handler {self} received event doc {format_doc_for_log(doc)} and "
105
105
  "has no corresponding descriptor record"
106
106
  )
107
107
  return doc
108
108
  match event_descriptor.get("name"):
109
- case CONST.DESCRIPTORS.HARDWARE_READ_PRE:
109
+ case DocDescriptorNames.HARDWARE_READ_PRE:
110
110
  scan_data_infos = self._handle_ispyb_hardware_read(doc)
111
- case CONST.DESCRIPTORS.HARDWARE_READ_DURING:
111
+ case DocDescriptorNames.HARDWARE_READ_DURING:
112
112
  scan_data_infos = self._handle_ispyb_transmission_flux_read(doc)
113
113
  case _:
114
114
  return self._tag_doc(doc)
115
115
  self.ispyb_ids = self.ispyb.update_deposition(self.ispyb_ids, scan_data_infos)
116
- ISPYB_LOGGER.info(f"Received ISPYB IDs: {self.ispyb_ids}")
116
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info(f"Received ISPYB IDs: {self.ispyb_ids}")
117
117
  return self._tag_doc(doc)
118
118
 
119
119
  def _handle_ispyb_hardware_read(self, doc) -> Sequence[ScanDataInfo]:
120
120
  assert self.params, "Event handled before activity_gated_start received params"
121
- ISPYB_LOGGER.info("ISPyB handler received event from read hardware")
121
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info(
122
+ "ISPyB handler received event from read hardware"
123
+ )
122
124
  assert isinstance(
123
125
  synchrotron_mode := doc["data"]["synchrotron-synchrotron_mode"],
124
126
  SynchrotronMode,
@@ -127,8 +129,8 @@ class BaseISPyBCallback(PlanReactiveCallback):
127
129
  hwscan_data_collection_info = DataCollectionInfo(
128
130
  undulator_gap1=doc["data"]["undulator-current_gap"],
129
131
  synchrotron_mode=synchrotron_mode.value,
130
- slitgap_horizontal=doc["data"]["s4_slit_gaps_xgap"],
131
- slitgap_vertical=doc["data"]["s4_slit_gaps_ygap"],
132
+ slitgap_horizontal=doc["data"]["s4_slit_gaps-xgap"],
133
+ slitgap_vertical=doc["data"]["s4_slit_gaps-ygap"],
132
134
  )
133
135
  hwscan_data_collection_info = _update_based_on_energy(
134
136
  doc, self.params.detector_params, hwscan_data_collection_info
@@ -141,7 +143,9 @@ class BaseISPyBCallback(PlanReactiveCallback):
141
143
  scan_data_infos = self.populate_info_for_update(
142
144
  hwscan_data_collection_info, hwscan_position_info, self.params
143
145
  )
144
- ISPYB_LOGGER.info("Updating ispyb data collection after hardware read.")
146
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info(
147
+ "Updating ispyb data collection after hardware read."
148
+ )
145
149
  return scan_data_infos
146
150
 
147
151
  def _handle_ispyb_transmission_flux_read(self, doc) -> Sequence[ScanDataInfo]:
@@ -156,7 +160,7 @@ class BaseISPyBCallback(PlanReactiveCallback):
156
160
  beamsize_at_sampley=beamsize_y_mm,
157
161
  focal_spot_size_at_samplex=beamsize_x_mm,
158
162
  focal_spot_size_at_sampley=beamsize_y_mm,
159
- flux=doc["data"]["flux_flux_reading"],
163
+ flux=doc["data"]["flux-flux_reading"],
160
164
  )
161
165
  if transmission := doc["data"]["attenuator-actual_transmission"]:
162
166
  # Ispyb wants the transmission in a percentage, we use fractions
@@ -167,7 +171,9 @@ class BaseISPyBCallback(PlanReactiveCallback):
167
171
  scan_data_infos = self.populate_info_for_update(
168
172
  hwscan_data_collection_info, None, self.params
169
173
  )
170
- ISPYB_LOGGER.info("Updating ispyb data collection after flux read.")
174
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info(
175
+ "Updating ispyb data collection after flux read."
176
+ )
171
177
  self.append_to_comment(f"Aperture: {aperture}. ")
172
178
  return scan_data_infos
173
179
 
@@ -183,10 +189,10 @@ class BaseISPyBCallback(PlanReactiveCallback):
183
189
  def activity_gated_stop(self, doc: RunStop) -> RunStop:
184
190
  """Subclasses must check that they are recieving a stop document for the correct
185
191
  uid to use this method!"""
186
- assert (
187
- self.ispyb is not None
188
- ), "ISPyB handler received stop document, but deposition object doesn't exist!"
189
- ISPYB_LOGGER.debug("ISPyB handler received stop document.")
192
+ assert self.ispyb is not None, (
193
+ "ISPyB handler received stop document, but deposition object doesn't exist!"
194
+ )
195
+ ISPYB_ZOCALO_CALLBACK_LOGGER.debug("ISPyB handler received stop document.")
190
196
  exit_status = (
191
197
  doc.get("exit_status") or "Exit status not available in stop document!"
192
198
  )
@@ -195,7 +201,7 @@ class BaseISPyBCallback(PlanReactiveCallback):
195
201
  try:
196
202
  self.ispyb.end_deposition(self.ispyb_ids, exit_status, reason)
197
203
  except Exception as e:
198
- ISPYB_LOGGER.warning(
204
+ ISPYB_ZOCALO_CALLBACK_LOGGER.warning(
199
205
  f"Failed to finalise ISPyB deposition on stop document: {format_doc_for_log(doc)} with exception: {e}"
200
206
  )
201
207
  return self._tag_doc(doc)
@@ -205,7 +211,7 @@ class BaseISPyBCallback(PlanReactiveCallback):
205
211
  try:
206
212
  self.ispyb.append_to_comment(id, comment)
207
213
  except TypeError:
208
- ISPYB_LOGGER.warning(
214
+ ISPYB_ZOCALO_CALLBACK_LOGGER.warning(
209
215
  "ISPyB deposition not initialised, can't update comment."
210
216
  )
211
217
 
@@ -1,17 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
- from mx_bluesky.common.parameters.components import DiffractionExperimentWithSample
4
- from mx_bluesky.hyperion.external_interaction.ispyb.data_model import (
3
+ from mx_bluesky.common.external_interaction.ispyb.data_model import (
5
4
  DataCollectionGroupInfo,
6
5
  DataCollectionInfo,
7
6
  )
8
- from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_store import (
7
+ from mx_bluesky.common.external_interaction.ispyb.ispyb_store import (
9
8
  EIGER_FILE_SUFFIX,
10
9
  I03_EIGER_DETECTOR,
11
10
  )
12
- from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_utils import (
11
+ from mx_bluesky.common.external_interaction.ispyb.ispyb_utils import (
13
12
  get_current_time_string,
14
13
  )
14
+ from mx_bluesky.common.parameters.components import DiffractionExperimentWithSample
15
15
 
16
16
 
17
17
  def populate_data_collection_group(params: DiffractionExperimentWithSample):
@@ -2,7 +2,7 @@ import json
2
2
 
3
3
  from bluesky.callbacks import CallbackBase
4
4
 
5
- from mx_bluesky.hyperion.log import LOGGER
5
+ from mx_bluesky.common.utils.log import LOGGER
6
6
 
7
7
 
8
8
  class _BestEffortEncoder(json.JSONEncoder):
@@ -5,10 +5,12 @@ from typing import TYPE_CHECKING
5
5
  from bluesky.callbacks import CallbackBase
6
6
  from dodal.devices.zocalo import ZocaloStartInfo, ZocaloTrigger
7
7
 
8
- from mx_bluesky.hyperion.external_interaction.exceptions import ISPyBDepositionNotMade
9
- from mx_bluesky.hyperion.log import ISPYB_LOGGER
10
- from mx_bluesky.hyperion.parameters.constants import CONST
11
- from mx_bluesky.hyperion.utils.utils import number_of_frames_from_scan_spec
8
+ from mx_bluesky.common.parameters.constants import (
9
+ DocDescriptorNames,
10
+ )
11
+ from mx_bluesky.common.utils.exceptions import ISPyBDepositionNotMade
12
+ from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER
13
+ from mx_bluesky.common.utils.utils import number_of_frames_from_scan_spec
12
14
 
13
15
  if TYPE_CHECKING:
14
16
  from event_model.documents import Event, EventDescriptor, RunStart, RunStop
@@ -27,25 +29,17 @@ class ZocaloCallback(CallbackBase):
27
29
 
28
30
  def _reset_state(self):
29
31
  self.run_uid: str | None = None
30
- self.triggering_plan: str | None = None
31
- self.zocalo_interactor: ZocaloTrigger | None = None
32
32
  self.zocalo_info: list[ZocaloStartInfo] = []
33
33
  self.descriptors: dict[str, EventDescriptor] = {}
34
34
 
35
- def __init__(
36
- self,
37
- ):
35
+ def __init__(self, triggering_plan: str, zocalo_environment: str):
38
36
  super().__init__()
37
+ self.triggering_plan = triggering_plan
38
+ self.zocalo_interactor = ZocaloTrigger(zocalo_environment)
39
39
  self._reset_state()
40
40
 
41
41
  def start(self, doc: RunStart):
42
- ISPYB_LOGGER.info("Zocalo handler received start document.")
43
- if triggering_plan := doc.get(CONST.TRIGGER.ZOCALO):
44
- self.triggering_plan = triggering_plan
45
- assert isinstance(zocalo_environment := doc.get("zocalo_environment"), str)
46
- ISPYB_LOGGER.info(f"Zocalo environment set to {zocalo_environment}.")
47
- self.zocalo_interactor = ZocaloTrigger(zocalo_environment)
48
-
42
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info("Zocalo handler received start document.")
49
43
  if self.triggering_plan and doc.get("subplan_name") == self.triggering_plan:
50
44
  self.run_uid = doc.get("uid")
51
45
  assert isinstance(scan_points := doc.get("scan_points"), list)
@@ -53,6 +47,7 @@ class ZocaloCallback(CallbackBase):
53
47
  isinstance(ispyb_ids := doc.get("ispyb_dcids"), tuple)
54
48
  and len(ispyb_ids) > 0
55
49
  ):
50
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info(f"Zocalo triggering for {ispyb_ids}")
56
51
  ids_and_shape = list(zip(ispyb_ids, scan_points, strict=False))
57
52
  start_frame = 0
58
53
  self.zocalo_info = []
@@ -73,17 +68,16 @@ class ZocaloCallback(CallbackBase):
73
68
 
74
69
  def event(self, doc: Event) -> Event:
75
70
  event_descriptor = self.descriptors[doc["descriptor"]]
76
- if event_descriptor.get("name") == CONST.DESCRIPTORS.ZOCALO_HW_READ:
71
+ if event_descriptor.get("name") == DocDescriptorNames.ZOCALO_HW_READ:
77
72
  filename = doc["data"]["eiger_odin_file_writer_id"]
78
73
  for start_info in self.zocalo_info:
79
74
  start_info.filename = filename
80
- assert self.zocalo_interactor is not None
81
75
  self.zocalo_interactor.run_start(start_info)
82
76
  return doc
83
77
 
84
78
  def stop(self, doc: RunStop):
85
79
  if doc.get("run_start") == self.run_uid:
86
- ISPYB_LOGGER.info(
80
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info(
87
81
  f"Zocalo handler received stop document, for run {doc.get('run_start')}."
88
82
  )
89
83
  assert self.zocalo_interactor is not None
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from collections.abc import Callable, Sequence
4
4
  from time import time
5
- from typing import TYPE_CHECKING, Any
5
+ from typing import TYPE_CHECKING, Any, TypeVar
6
6
 
7
7
  import numpy as np
8
8
  from bluesky import preprocessors as bpp
@@ -12,41 +12,39 @@ from dodal.devices.zocalo.zocalo_results import (
12
12
  get_processing_results_from_event,
13
13
  )
14
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
18
- from mx_bluesky.hyperion.external_interaction.callbacks.common.ispyb_mapping import (
15
+ from mx_bluesky.common.external_interaction.callbacks.common.ispyb_callback_base import (
16
+ BaseISPyBCallback,
17
+ )
18
+ from mx_bluesky.common.external_interaction.callbacks.common.ispyb_mapping import (
19
19
  populate_data_collection_group,
20
20
  populate_remaining_data_collection_info,
21
21
  )
22
- from mx_bluesky.hyperion.external_interaction.callbacks.ispyb_callback_base import (
23
- BaseISPyBCallback,
24
- )
25
- from mx_bluesky.hyperion.external_interaction.callbacks.logging_callback import (
22
+ from mx_bluesky.common.external_interaction.callbacks.common.logging_callback import (
26
23
  format_doc_for_log,
27
24
  )
28
- from mx_bluesky.hyperion.external_interaction.callbacks.xray_centre.ispyb_mapping import (
25
+ from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_mapping import (
29
26
  construct_comment_for_gridscan,
30
27
  populate_xy_data_collection_info,
31
28
  populate_xz_data_collection_info,
32
29
  )
33
- from mx_bluesky.hyperion.external_interaction.exceptions import ISPyBDepositionNotMade
34
- from mx_bluesky.hyperion.external_interaction.ispyb.data_model import (
30
+ from mx_bluesky.common.external_interaction.ispyb.data_model import (
35
31
  DataCollectionGridInfo,
36
32
  DataCollectionInfo,
37
33
  DataCollectionPositionInfo,
38
34
  Orientation,
39
35
  ScanDataInfo,
40
36
  )
41
- from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_store import (
37
+ from mx_bluesky.common.external_interaction.ispyb.ispyb_store import (
42
38
  IspybIds,
43
39
  StoreInIspyb,
44
40
  )
45
- from mx_bluesky.hyperion.log import ISPYB_LOGGER
46
- from mx_bluesky.hyperion.parameters.constants import CONST
47
- from mx_bluesky.hyperion.parameters.gridscan import (
41
+ from mx_bluesky.common.parameters.components import DiffractionExperimentWithSample
42
+ from mx_bluesky.common.parameters.constants import DocDescriptorNames, PlanNameConstants
43
+ from mx_bluesky.common.parameters.gridscan import (
48
44
  GridCommon,
49
45
  )
46
+ from mx_bluesky.common.utils.exceptions import ISPyBDepositionNotMade
47
+ from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER, set_dcgid_tag
50
48
 
51
49
  if TYPE_CHECKING:
52
50
  from event_model import Event, RunStart, RunStop
@@ -58,14 +56,17 @@ def ispyb_activation_wrapper(plan_generator: MsgGenerator, parameters):
58
56
  plan_generator,
59
57
  md={
60
58
  "activate_callbacks": ["GridscanISPyBCallback"],
61
- "subplan_name": CONST.PLAN.GRID_DETECT_AND_DO_GRIDSCAN,
62
- "hyperion_parameters": parameters.model_dump_json(),
59
+ "subplan_name": PlanNameConstants.GRID_DETECT_AND_DO_GRIDSCAN,
60
+ "mx_bluesky_parameters": parameters.model_dump_json(),
63
61
  },
64
62
  ),
65
- CONST.PLAN.ISPYB_ACTIVATION,
63
+ PlanNameConstants.ISPYB_ACTIVATION,
66
64
  )
67
65
 
68
66
 
67
+ T = TypeVar("T", bound="GridCommon")
68
+
69
+
69
70
  class GridscanISPyBCallback(BaseISPyBCallback):
70
71
  """Callback class to handle the deposition of experiment parameters into the ISPyB
71
72
  database. Listens for 'event' and 'descriptor' documents. Creates the ISpyB entry on
@@ -83,27 +84,29 @@ class GridscanISPyBCallback(BaseISPyBCallback):
83
84
 
84
85
  def __init__(
85
86
  self,
87
+ param_type: type[T],
86
88
  *,
87
89
  emit: Callable[..., Any] | None = None,
88
90
  ) -> None:
89
91
  super().__init__(emit=emit)
90
92
  self.ispyb: StoreInIspyb
91
93
  self.ispyb_ids: IspybIds = IspybIds()
94
+ self.param_type = param_type
92
95
  self._start_of_fgs_uid: str | None = None
93
96
  self._processing_start_time: float | None = None
94
97
 
95
98
  def activity_gated_start(self, doc: RunStart):
96
99
  if doc.get("subplan_name") == PlanNameConstants.DO_FGS:
97
100
  self._start_of_fgs_uid = doc.get("uid")
98
- if doc.get("subplan_name") == CONST.PLAN.GRID_DETECT_AND_DO_GRIDSCAN:
101
+ if doc.get("subplan_name") == PlanNameConstants.GRID_DETECT_AND_DO_GRIDSCAN:
99
102
  self.uid_to_finalize_on = doc.get("uid")
100
- ISPYB_LOGGER.info(
103
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info(
101
104
  "ISPyB callback received start document with experiment parameters and "
102
105
  f"uid: {self.uid_to_finalize_on}"
103
106
  )
104
- hyperion_params = doc.get("hyperion_parameters")
105
- assert isinstance(hyperion_params, str)
106
- self.params = GridCommon.model_validate_json(hyperion_params)
107
+ mx_bluesky_parameters = doc.get("mx_bluesky_parameters")
108
+ assert isinstance(mx_bluesky_parameters, str)
109
+ self.params = self.param_type.model_validate_json(mx_bluesky_parameters)
107
110
  self.ispyb = StoreInIspyb(self.ispyb_config)
108
111
  data_collection_group_info = populate_data_collection_group(self.params)
109
112
 
@@ -140,7 +143,7 @@ class GridscanISPyBCallback(BaseISPyBCallback):
140
143
  descriptor_name = self.descriptors[doc["descriptor"]].get("name")
141
144
  if descriptor_name == ZOCALO_READING_PLAN_NAME:
142
145
  self._handle_zocalo_read_event(doc)
143
- elif descriptor_name == CONST.DESCRIPTORS.OAV_GRID_SNAPSHOT_TRIGGERED:
146
+ elif descriptor_name == DocDescriptorNames.OAV_GRID_SNAPSHOT_TRIGGERED:
144
147
  scan_data_infos = self._handle_oav_grid_snapshot_triggered(doc)
145
148
  self.ispyb_ids = self.ispyb.update_deposition(
146
149
  self.ispyb_ids, scan_data_infos
@@ -154,7 +157,7 @@ class GridscanISPyBCallback(BaseISPyBCallback):
154
157
  proc_time = time() - self._processing_start_time
155
158
  crystal_summary = f"Zocalo processing took {proc_time:.2f} s. "
156
159
  bboxes: list[np.ndarray] = []
157
- ISPYB_LOGGER.info(
160
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info(
158
161
  f"Amending comment based on Zocalo reading doc: {format_doc_for_log(doc)}"
159
162
  )
160
163
 
@@ -176,9 +179,9 @@ class GridscanISPyBCallback(BaseISPyBCallback):
176
179
  )
177
180
  else:
178
181
  crystal_summary += "Zocalo found no crystals in this gridscan."
179
- assert (
180
- self.ispyb_ids.data_collection_ids
181
- ), "No data collection to add results to"
182
+ assert self.ispyb_ids.data_collection_ids, (
183
+ "No data collection to add results to"
184
+ )
182
185
  self.ispyb.append_to_comment(
183
186
  self.ispyb_ids.data_collection_ids[0], crystal_summary
184
187
  )
@@ -225,7 +228,9 @@ class GridscanISPyBCallback(BaseISPyBCallback):
225
228
  data_collection_id=data_collection_id,
226
229
  data_collection_grid_info=data_collection_grid_info,
227
230
  )
228
- ISPYB_LOGGER.info("Updating ispyb data collection after oav snapshot.")
231
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info(
232
+ "Updating ispyb data collection after oav snapshot."
233
+ )
229
234
  self._oav_snapshot_event_idx += 1
230
235
  return [scan_data_info]
231
236
 
@@ -245,9 +250,9 @@ class GridscanISPyBCallback(BaseISPyBCallback):
245
250
  event_sourced_position_info: DataCollectionPositionInfo | None,
246
251
  params: DiffractionExperimentWithSample,
247
252
  ) -> Sequence[ScanDataInfo]:
248
- assert (
249
- self.ispyb_ids.data_collection_ids
250
- ), "Expect at least one valid data collection to record scan data"
253
+ assert self.ispyb_ids.data_collection_ids, (
254
+ "Expect at least one valid data collection to record scan data"
255
+ )
251
256
  xy_scan_data_info = ScanDataInfo(
252
257
  data_collection_info=event_sourced_data_collection_info,
253
258
  data_collection_id=self.ispyb_ids.data_collection_ids[0],
@@ -270,7 +275,7 @@ class GridscanISPyBCallback(BaseISPyBCallback):
270
275
  if doc.get("run_start") == self._start_of_fgs_uid:
271
276
  self._processing_start_time = time()
272
277
  if doc.get("run_start") == self.uid_to_finalize_on:
273
- ISPYB_LOGGER.info(
278
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info(
274
279
  "ISPyB callback received stop document corresponding to start document "
275
280
  f"with uid: {self.uid_to_finalize_on}."
276
281
  )
@@ -4,7 +4,7 @@ import numpy
4
4
  from dodal.devices.detector import DetectorParams
5
5
  from dodal.devices.oav import utils as oav_utils
6
6
 
7
- from mx_bluesky.hyperion.external_interaction.ispyb.data_model import (
7
+ from mx_bluesky.common.external_interaction.ispyb.data_model import (
8
8
  DataCollectionGridInfo,
9
9
  DataCollectionInfo,
10
10
  )
@@ -43,7 +43,7 @@ def construct_comment_for_gridscan(grid_info: DataCollectionGridInfo) -> str:
43
43
  grid_info.microns_per_pixel_y,
44
44
  )
45
45
  return (
46
- "Hyperion: Xray centring - Diffraction grid scan of "
46
+ "MX-Bluesky: Xray centring - Diffraction grid scan of "
47
47
  f"{grid_info.steps_x} by "
48
48
  f"{grid_info.steps_y} images in "
49
49
  f"{(grid_info.dx_in_mm * 1e3):.1f} um by "
@@ -1,22 +1,26 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING
3
+ from typing import TYPE_CHECKING, TypeVar
4
4
 
5
- from mx_bluesky.hyperion.external_interaction.callbacks.plan_reactive_callback import (
5
+ from mx_bluesky.common.external_interaction.callbacks.common.plan_reactive_callback import (
6
6
  PlanReactiveCallback,
7
7
  )
8
- from mx_bluesky.hyperion.external_interaction.nexus.nexus_utils import (
8
+ from mx_bluesky.common.external_interaction.nexus.nexus_utils import (
9
9
  create_beam_and_attenuator_parameters,
10
10
  vds_type_based_on_bit_depth,
11
11
  )
12
- from mx_bluesky.hyperion.external_interaction.nexus.write_nexus import NexusWriter
13
- from mx_bluesky.hyperion.log import NEXUS_LOGGER
14
- from mx_bluesky.hyperion.parameters.constants import CONST
15
- from mx_bluesky.hyperion.parameters.gridscan import HyperionThreeDGridScan
12
+ from mx_bluesky.common.external_interaction.nexus.write_nexus import NexusWriter
13
+ from mx_bluesky.common.parameters.constants import DocDescriptorNames, PlanNameConstants
14
+ from mx_bluesky.common.parameters.gridscan import (
15
+ SpecifiedThreeDGridScan,
16
+ )
17
+ from mx_bluesky.common.utils.log import NEXUS_LOGGER
16
18
 
17
19
  if TYPE_CHECKING:
18
20
  from event_model.documents import Event, EventDescriptor, RunStart
19
21
 
22
+ T = TypeVar("T", bound="SpecifiedThreeDGridScan")
23
+
20
24
 
21
25
  class GridscanNexusFileCallback(PlanReactiveCallback):
22
26
  """Callback class to handle the creation of Nexus files based on experiment \
@@ -35,8 +39,9 @@ class GridscanNexusFileCallback(PlanReactiveCallback):
35
39
  See: https://blueskyproject.io/bluesky/callbacks.html#ways-to-invoke-callbacks
36
40
  """
37
41
 
38
- def __init__(self) -> None:
42
+ def __init__(self, param_type: type[T]) -> None:
39
43
  super().__init__(NEXUS_LOGGER)
44
+ self.param_type = param_type
40
45
  self.run_start_uid: str | None = None
41
46
  self.nexus_writer_1: NexusWriter | None = None
42
47
  self.nexus_writer_2: NexusWriter | None = None
@@ -44,13 +49,13 @@ class GridscanNexusFileCallback(PlanReactiveCallback):
44
49
  self.log = NEXUS_LOGGER
45
50
 
46
51
  def activity_gated_start(self, doc: RunStart):
47
- if doc.get("subplan_name") == CONST.PLAN.GRIDSCAN_OUTER:
48
- hyperion_params = doc.get("hyperion_parameters")
49
- assert isinstance(hyperion_params, str)
52
+ if doc.get("subplan_name") == PlanNameConstants.GRIDSCAN_OUTER:
53
+ mx_bluesky_parameters = doc.get("mx_bluesky_parameters")
54
+ assert isinstance(mx_bluesky_parameters, str)
50
55
  NEXUS_LOGGER.info(
51
- f"Nexus writer received start document with experiment parameters {hyperion_params}"
56
+ f"Nexus writer received start document with experiment parameters {mx_bluesky_parameters}"
52
57
  )
53
- parameters = HyperionThreeDGridScan.model_validate_json(hyperion_params)
58
+ parameters = self.param_type.model_validate_json(mx_bluesky_parameters)
54
59
  d_size = parameters.detector_params.detector_size_constants.det_size_pixels
55
60
  grid_n_img_1 = parameters.scan_indices[1]
56
61
  grid_n_img_2 = parameters.num_images - grid_n_img_1
@@ -75,7 +80,7 @@ class GridscanNexusFileCallback(PlanReactiveCallback):
75
80
 
76
81
  def activity_gated_event(self, doc: Event) -> Event | None:
77
82
  assert (event_descriptor := self.descriptors.get(doc["descriptor"])) is not None
78
- if event_descriptor.get("name") == CONST.DESCRIPTORS.HARDWARE_READ_DURING:
83
+ if event_descriptor.get("name") == DocDescriptorNames.HARDWARE_READ_DURING:
79
84
  data = doc["data"]
80
85
  for nexus_writer in [self.nexus_writer_1, self.nexus_writer_2]:
81
86
  assert nexus_writer, "Nexus callback did not receive start doc"
@@ -84,7 +89,7 @@ class GridscanNexusFileCallback(PlanReactiveCallback):
84
89
  nexus_writer.attenuator,
85
90
  ) = create_beam_and_attenuator_parameters(
86
91
  data["dcm-energy_in_kev"],
87
- data["flux_flux_reading"],
92
+ data["flux-flux_reading"],
88
93
  data["attenuator-actual_transmission"],
89
94
  )
90
95
  vds_data_type = vds_type_based_on_bit_depth(
@@ -29,8 +29,19 @@ class FeatureFlags(BaseModel, ABC):
29
29
  def mark_overridden_features(cls, values):
30
30
  assert isinstance(values, dict)
31
31
  values["overriden_features"] = values.copy()
32
+ cls._validate_overridden_features(values)
32
33
  return values
33
34
 
35
+ @classmethod
36
+ def _validate_overridden_features(cls, values: dict):
37
+ """Validates overridden features to ensure they are defined in the model fields."""
38
+ defined_fields = cls.model_fields.keys()
39
+ invalid_features = [key for key in values.keys() if key not in defined_fields]
40
+
41
+ if invalid_features:
42
+ message = f"Invalid feature toggle(s) supplied: {invalid_features}. "
43
+ raise ValueError(message)
44
+
34
45
  def _get_flags(self):
35
46
  flags = type(self).get_config_server().best_effort_get_all_feature_flags()
36
47
  return {f: flags[f] for f in flags if f in self.model_fields.keys()}
@@ -70,6 +70,8 @@ class DataCollectionPositionInfo:
70
70
 
71
71
  @dataclass
72
72
  class DataCollectionGridInfo:
73
+ """This information is used by Zocalo gridscan per-image-analysis"""
74
+
73
75
  dx_in_mm: float
74
76
  dy_in_mm: float
75
77
  steps_x: int
@@ -5,11 +5,11 @@ from enum import StrEnum
5
5
  from requests import patch, post
6
6
  from requests.auth import AuthBase
7
7
 
8
- from mx_bluesky.hyperion.external_interaction.exceptions import ISPyBDepositionNotMade
9
- from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_utils import (
8
+ from mx_bluesky.common.external_interaction.ispyb.ispyb_utils import (
10
9
  get_current_time_string,
11
10
  get_ispyb_config,
12
11
  )
12
+ from mx_bluesky.common.utils.exceptions import ISPyBDepositionNotMade
13
13
 
14
14
  RobotActionID = int
15
15
 
@@ -54,9 +54,9 @@ class BLSampleStatus(StrEnum):
54
54
  ERROR_BEAMLINE = "ERROR - beamline"
55
55
 
56
56
 
57
- assert all(
58
- len(value) <= 20 for value in BLSampleStatus
59
- ), "Column size limit of 20 for BLSampleStatus"
57
+ assert all(len(value) <= 20 for value in BLSampleStatus), (
58
+ "Column size limit of 20 for BLSampleStatus"
59
+ )
60
60
 
61
61
 
62
62
  class ExpeyeInteraction: