mx-bluesky 1.4.7__py3-none-any.whl → 1.4.9__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 (89) hide show
  1. mx_bluesky/_version.py +2 -2
  2. mx_bluesky/beamlines/aithre_lasershaping/__init__.py +8 -0
  3. mx_bluesky/beamlines/aithre_lasershaping/beamline_safe.py +36 -0
  4. mx_bluesky/beamlines/aithre_lasershaping/goniometer_controls.py +43 -0
  5. mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +4 -4
  6. mx_bluesky/beamlines/i04/thawing_plan.py +8 -2
  7. mx_bluesky/beamlines/i23/__init__.py +3 -0
  8. mx_bluesky/beamlines/i23/serial.py +71 -0
  9. mx_bluesky/beamlines/i24/serial/__init__.py +2 -0
  10. mx_bluesky/beamlines/i24/serial/blueapi_config.yaml +2 -1
  11. mx_bluesky/beamlines/i24/serial/dcid.py +5 -5
  12. mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DetStage.edl +2 -2
  13. mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +9 -9
  14. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +25 -5
  15. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DetStage.edl +2 -2
  16. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +14 -14
  17. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +5 -5
  18. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +29 -60
  19. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +7 -1
  20. mx_bluesky/beamlines/i24/serial/log.py +9 -10
  21. mx_bluesky/beamlines/i24/serial/parameters/utils.py +36 -7
  22. mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +0 -1
  23. mx_bluesky/beamlines/i24/serial/setup_beamline/pv_abstract.py +4 -4
  24. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +4 -12
  25. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +2 -1
  26. mx_bluesky/beamlines/i24/serial/web_gui_plans/general_plans.py +71 -11
  27. mx_bluesky/beamlines/i24/serial/write_nexus.py +3 -3
  28. mx_bluesky/{hyperion → common}/device_setup_plans/manipulate_sample.py +6 -14
  29. mx_bluesky/{hyperion → common}/device_setup_plans/setup_oav.py +12 -6
  30. mx_bluesky/{hyperion → common}/experiment_plans/change_aperture_then_move_plan.py +4 -5
  31. mx_bluesky/{hyperion → common}/experiment_plans/oav_grid_detection_plan.py +6 -6
  32. mx_bluesky/common/external_interaction/callbacks/common/ispyb_callback_base.py +6 -5
  33. mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py +16 -47
  34. mx_bluesky/common/external_interaction/ispyb/ispyb_store.py +4 -1
  35. mx_bluesky/common/external_interaction/ispyb/ispyb_utils.py +4 -4
  36. mx_bluesky/common/external_interaction/nexus/nexus_utils.py +2 -2
  37. mx_bluesky/common/parameters/components.py +22 -2
  38. mx_bluesky/common/parameters/constants.py +4 -16
  39. mx_bluesky/common/parameters/gridscan.py +36 -32
  40. mx_bluesky/common/plans/common_flyscan_xray_centre_plan.py +316 -0
  41. mx_bluesky/common/plans/inner_plans/__init__ .py +0 -0
  42. mx_bluesky/common/plans/read_hardware.py +3 -3
  43. mx_bluesky/common/utils/log.py +19 -15
  44. mx_bluesky/hyperion/__main__.py +6 -24
  45. mx_bluesky/hyperion/baton_handler.py +8 -3
  46. mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +4 -4
  47. mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +0 -33
  48. mx_bluesky/hyperion/device_setup_plans/smargon.py +2 -7
  49. mx_bluesky/hyperion/device_setup_plans/utils.py +6 -5
  50. mx_bluesky/hyperion/experiment_plans/__init__.py +1 -7
  51. mx_bluesky/hyperion/experiment_plans/experiment_registry.py +3 -13
  52. mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +80 -87
  53. mx_bluesky/hyperion/experiment_plans/hyperion_flyscan_xray_centre_plan.py +183 -0
  54. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +50 -15
  55. mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +31 -7
  56. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +4 -4
  57. mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +1 -1
  58. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +13 -14
  59. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +9 -8
  60. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +30 -71
  61. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +2 -2
  62. mx_bluesky/hyperion/external_interaction/agamemnon.py +78 -80
  63. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +8 -6
  64. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +3 -3
  65. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +6 -3
  66. mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +2 -2
  67. mx_bluesky/hyperion/external_interaction/callbacks/snapshot_callback.py +183 -31
  68. mx_bluesky/hyperion/external_interaction/config_server.py +4 -1
  69. mx_bluesky/hyperion/parameters/cli.py +4 -19
  70. mx_bluesky/hyperion/parameters/constants.py +1 -5
  71. mx_bluesky/hyperion/parameters/device_composites.py +40 -5
  72. mx_bluesky/hyperion/parameters/gridscan.py +9 -58
  73. mx_bluesky/hyperion/parameters/load_centre_collect.py +4 -4
  74. mx_bluesky/hyperion/parameters/rotation.py +9 -12
  75. mx_bluesky/hyperion/utils/context.py +2 -2
  76. mx_bluesky/hyperion/utils/validation.py +15 -19
  77. {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/METADATA +7 -6
  78. {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/RECORD +86 -83
  79. {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/WHEEL +1 -1
  80. mx_bluesky/common/external_interaction/test_config_server.py +0 -38
  81. mx_bluesky/hyperion/device_setup_plans/check_beamstop.py +0 -27
  82. mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +0 -467
  83. /mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/{short1-laser.png → s1l.png} +0 -0
  84. /mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/{short2-laser.png → s2l.png} +0 -0
  85. /mx_bluesky/{hyperion → common}/device_setup_plans/position_detector.py +0 -0
  86. /mx_bluesky/common/plans/{do_fgs.py → inner_plans/do_fgs.py} +0 -0
  87. {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/entry_points.txt +0 -0
  88. {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/licenses/LICENSE +0 -0
  89. {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/top_level.txt +0 -0
@@ -7,7 +7,7 @@ from dodal.devices.aperturescatterguard import (
7
7
  )
8
8
  from dodal.devices.backlight import Backlight, BacklightPosition
9
9
  from dodal.devices.detector.detector_motion import DetectorMotion
10
- from dodal.devices.smargon import Smargon
10
+ from dodal.devices.smargon import CombinedMove, Smargon
11
11
 
12
12
  from mx_bluesky.common.utils.log import LOGGER
13
13
 
@@ -47,7 +47,7 @@ def move_aperture_if_required(
47
47
  else:
48
48
  LOGGER.info(f"Setting aperture position to {aperture_value}")
49
49
  yield from bps.abs_set(
50
- aperture_scatterguard,
50
+ aperture_scatterguard.selected_aperture,
51
51
  aperture_value,
52
52
  group=group,
53
53
  )
@@ -78,12 +78,7 @@ def move_x_y_z(
78
78
  axes are optional."""
79
79
 
80
80
  LOGGER.info(f"Moving smargon to x, y, z: {(x_mm, y_mm, z_mm)}")
81
- if x_mm is not None:
82
- yield from bps.abs_set(smargon.x, x_mm, group=group)
83
- if y_mm is not None:
84
- yield from bps.abs_set(smargon.y, y_mm, group=group)
85
- if z_mm is not None:
86
- yield from bps.abs_set(smargon.z, z_mm, group=group)
81
+ yield from bps.abs_set(smargon, CombinedMove(x=x_mm, y=y_mm, z=z_mm), group=group)
87
82
  if wait:
88
83
  yield from bps.wait(group)
89
84
 
@@ -100,11 +95,8 @@ def move_phi_chi_omega(
100
95
  axes are optional."""
101
96
 
102
97
  LOGGER.info(f"Moving smargon to phi, chi, omega: {(phi, chi, omega)}")
103
- if phi is not None:
104
- yield from bps.abs_set(smargon.phi, phi, group=group)
105
- if chi is not None:
106
- yield from bps.abs_set(smargon.chi, chi, group=group)
107
- if omega is not None:
108
- yield from bps.abs_set(smargon.omega, omega, group=group)
98
+ yield from bps.abs_set(
99
+ smargon, CombinedMove(phi=phi, chi=chi, omega=omega), group=group
100
+ )
109
101
  if wait:
110
102
  yield from bps.wait(group)
@@ -6,15 +6,18 @@ from dodal.devices.oav.oav_detector import OAV
6
6
  from dodal.devices.oav.oav_parameters import OAVParameters
7
7
  from dodal.devices.oav.pin_image_recognition import PinTipDetection
8
8
 
9
- from mx_bluesky.hyperion.parameters.constants import CONST
10
-
11
- # Helper function to make sure we set the waiting groups correctly
12
- set_using_group = partial(bps.abs_set, group=CONST.WAIT.READY_FOR_OAV)
9
+ from mx_bluesky.common.parameters.constants import (
10
+ PlanGroupCheckpointConstants,
11
+ )
13
12
 
14
13
 
15
14
  def setup_pin_tip_detection_params(
16
- pin_tip_detect_device: PinTipDetection, parameters: OAVParameters
15
+ pin_tip_detect_device: PinTipDetection,
16
+ parameters: OAVParameters,
17
17
  ):
18
+ set_using_group = partial(
19
+ bps.abs_set, group=PlanGroupCheckpointConstants.READY_FOR_OAV
20
+ )
18
21
  # select which blur to apply to image
19
22
  yield from set_using_group(
20
23
  pin_tip_detect_device.preprocess_operation, parameters.preprocess
@@ -55,6 +58,9 @@ def setup_pin_tip_detection_params(
55
58
 
56
59
 
57
60
  def setup_general_oav_params(oav: OAV, parameters: OAVParameters):
61
+ set_using_group = partial(
62
+ bps.abs_set, group=PlanGroupCheckpointConstants.READY_FOR_OAV
63
+ )
58
64
  yield from set_using_group(oav.cam.color_mode, ColorMode.RGB1)
59
65
  yield from set_using_group(oav.cam.acquire_period, parameters.acquire_period)
60
66
  yield from set_using_group(oav.cam.acquire_time, parameters.exposure)
@@ -78,4 +84,4 @@ def pre_centring_setup_oav(
78
84
  """
79
85
  yield from setup_general_oav_params(oav, parameters)
80
86
  yield from setup_pin_tip_detection_params(pin_tip_detection_device, parameters)
81
- yield from bps.wait(CONST.WAIT.READY_FOR_OAV)
87
+ yield from bps.wait(PlanGroupCheckpointConstants.READY_FOR_OAV)
@@ -3,18 +3,17 @@ import numpy
3
3
  from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureValue
4
4
  from dodal.devices.smargon import Smargon, StubPosition
5
5
 
6
+ from mx_bluesky.common.device_setup_plans.manipulate_sample import move_x_y_z
6
7
  from mx_bluesky.common.utils.log import LOGGER
7
8
  from mx_bluesky.common.utils.tracing import TRACER
8
9
  from mx_bluesky.common.xrc_result import XRayCentreResult
9
- from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import move_x_y_z
10
- from mx_bluesky.hyperion.parameters.gridscan import HyperionSpecifiedThreeDGridScan
11
10
 
12
11
 
13
12
  def change_aperture_then_move_to_xtal(
14
13
  best_hit: XRayCentreResult,
15
14
  smargon: Smargon,
16
15
  aperture_scatterguard: ApertureScatterguard,
17
- parameters: HyperionSpecifiedThreeDGridScan | None = None,
16
+ set_stub_offsets: bool | None = None,
18
17
  ):
19
18
  """For the given x-ray centring result,
20
19
  * Change the aperture so that the beam size is comparable to the crystal size
@@ -36,7 +35,7 @@ def change_aperture_then_move_to_xtal(
36
35
 
37
36
  # TODO support for setting stub offsets in multipin
38
37
  # https://github.com/DiamondLightSource/mx-bluesky/issues/552
39
- if parameters and parameters.FGS_params.set_stub_offsets:
38
+ if set_stub_offsets:
40
39
  LOGGER.info("Recentring smargon co-ordinate system to this point.")
41
40
  yield from bps.mv(smargon.stub_offsets, StubPosition.CURRENT_AS_CENTER)
42
41
 
@@ -72,4 +71,4 @@ def set_aperture_for_bbox_mm(
72
71
  f"Setting aperture to {new_selected_aperture} based on bounding box size {bbox_size_mm}."
73
72
  )
74
73
 
75
- yield from bps.abs_set(aperture_device, new_selected_aperture)
74
+ yield from bps.abs_set(aperture_device.selected_aperture, new_selected_aperture)
@@ -14,13 +14,13 @@ from dodal.devices.oav.pin_image_recognition.utils import NONE_VALUE
14
14
  from dodal.devices.oav.utils import PinNotFoundException, wait_for_tip_to_be_found
15
15
  from dodal.devices.smargon import Smargon
16
16
 
17
+ from mx_bluesky.common.device_setup_plans.setup_oav import (
18
+ pre_centring_setup_oav,
19
+ )
20
+ from mx_bluesky.common.parameters.constants import DocDescriptorNames, HardwareConstants
17
21
  from mx_bluesky.common.utils.context import device_composite_from_context
18
22
  from mx_bluesky.common.utils.exceptions import catch_exception_and_warn
19
23
  from mx_bluesky.common.utils.log import LOGGER
20
- from mx_bluesky.hyperion.device_setup_plans.setup_oav import (
21
- pre_centring_setup_oav,
22
- )
23
- from mx_bluesky.hyperion.parameters.constants import CONST
24
24
 
25
25
  if TYPE_CHECKING:
26
26
  from dodal.devices.oav.oav_parameters import OAVParameters
@@ -103,7 +103,7 @@ def grid_detection_plan(
103
103
  yield from bps.mv(smargon.omega, angle)
104
104
  # need to wait for the OAV image to update
105
105
  # See #673 for improvements
106
- yield from bps.sleep(CONST.HARDWARE.OAV_REFRESH_DELAY)
106
+ yield from bps.sleep(HardwareConstants.OAV_REFRESH_DELAY)
107
107
 
108
108
  tip_x_px, tip_y_px = yield from catch_exception_and_warn(
109
109
  PinNotFoundException, wait_for_tip_to_be_found, pin_tip_detection
@@ -163,7 +163,7 @@ def grid_detection_plan(
163
163
  yield from bps.abs_set(oav.grid_snapshot.filename, snapshot_filename)
164
164
  yield from bps.abs_set(oav.grid_snapshot.directory, snapshot_dir)
165
165
  yield from bps.trigger(oav.grid_snapshot, wait=True)
166
- yield from bps.create(CONST.DESCRIPTORS.OAV_GRID_SNAPSHOT_TRIGGERED)
166
+ yield from bps.create(DocDescriptorNames.OAV_GRID_SNAPSHOT_TRIGGERED)
167
167
 
168
168
  yield from bps.read(oav)
169
169
  yield from bps.read(smargon)
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from abc import abstractmethod
4
4
  from collections.abc import Callable, Sequence
5
+ from pathlib import Path
5
6
  from typing import TYPE_CHECKING, Any, TypeVar, cast
6
7
 
7
8
  from dodal.beamline_specific_utils.i03 import beam_size_from_aperture
@@ -26,7 +27,7 @@ from mx_bluesky.common.external_interaction.ispyb.ispyb_store import (
26
27
  )
27
28
  from mx_bluesky.common.external_interaction.ispyb.ispyb_utils import get_ispyb_config
28
29
  from mx_bluesky.common.parameters.components import DiffractionExperimentWithSample
29
- from mx_bluesky.common.parameters.constants import DocDescriptorNames, SimConstants
30
+ from mx_bluesky.common.parameters.constants import DocDescriptorNames
30
31
  from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER, set_dcgid_tag
31
32
  from mx_bluesky.common.utils.utils import convert_eV_to_angstrom
32
33
 
@@ -70,10 +71,10 @@ class BaseISPyBCallback(PlanReactiveCallback):
70
71
  self.ispyb: StoreInIspyb
71
72
  self.descriptors: dict[str, EventDescriptor] = {}
72
73
  self.ispyb_config = get_ispyb_config()
73
- if (
74
- self.ispyb_config == SimConstants.ISPYB_CONFIG
75
- or self.ispyb_config == SimConstants.DEV_ISPYB_DATABASE_CFG
76
- ):
74
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info(
75
+ f"Using ISPyB configuration from {self.ispyb_config}"
76
+ )
77
+ if not self.ispyb_config or not Path(self.ispyb_config).is_absolute():
77
78
  ISPYB_ZOCALO_CALLBACK_LOGGER.warning(
78
79
  f"{self.__class__} using dev ISPyB config: {self.ispyb_config}. If you"
79
80
  "want to use the real database, please set the ISPYB_CONFIG_PATH "
@@ -4,13 +4,8 @@ from collections.abc import Callable, Sequence
4
4
  from time import time
5
5
  from typing import TYPE_CHECKING, Any, TypeVar
6
6
 
7
- import numpy as np
8
7
  from bluesky import preprocessors as bpp
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
- )
8
+ from bluesky.utils import MsgGenerator, make_decorator
14
9
 
15
10
  from mx_bluesky.common.external_interaction.callbacks.common.ispyb_callback_base import (
16
11
  BaseISPyBCallback,
@@ -19,9 +14,6 @@ from mx_bluesky.common.external_interaction.callbacks.common.ispyb_mapping impor
19
14
  populate_data_collection_group,
20
15
  populate_remaining_data_collection_info,
21
16
  )
22
- from mx_bluesky.common.external_interaction.callbacks.common.logging_callback import (
23
- format_doc_for_log,
24
- )
25
17
  from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_mapping import (
26
18
  construct_comment_for_gridscan,
27
19
  populate_xy_data_collection_info,
@@ -71,6 +63,9 @@ def ispyb_activation_wrapper(plan_generator: MsgGenerator, parameters):
71
63
  )
72
64
 
73
65
 
66
+ ispyb_activation_decorator = make_decorator(ispyb_activation_wrapper)
67
+
68
+
74
69
  class GridscanISPyBCallback(BaseISPyBCallback):
75
70
  """Callback class to handle the deposition of experiment parameters into the ISPyB
76
71
  database. Listens for 'event' and 'descriptor' documents. Creates the ISpyB entry on
@@ -103,6 +98,7 @@ class GridscanISPyBCallback(BaseISPyBCallback):
103
98
  def activity_gated_start(self, doc: RunStart):
104
99
  if doc.get("subplan_name") == PlanNameConstants.DO_FGS:
105
100
  self._start_of_fgs_uid = doc.get("uid")
101
+
106
102
  if doc.get("subplan_name") == PlanNameConstants.GRID_DETECT_AND_DO_GRIDSCAN:
107
103
  self.uid_to_finalize_on = doc.get("uid")
108
104
  ISPYB_ZOCALO_CALLBACK_LOGGER.info(
@@ -146,12 +142,11 @@ class GridscanISPyBCallback(BaseISPyBCallback):
146
142
 
147
143
  def activity_gated_event(self, doc: Event):
148
144
  assert self.data_collection_group_info, ASSERT_START_BEFORE_EVENT_DOC_MESSAGE
145
+
149
146
  doc = super().activity_gated_event(doc)
150
147
 
151
148
  descriptor_name = self.descriptors[doc["descriptor"]].get("name")
152
- if descriptor_name == ZOCALO_READING_PLAN_NAME:
153
- self._handle_zocalo_read_event(doc)
154
- elif descriptor_name == DocDescriptorNames.OAV_GRID_SNAPSHOT_TRIGGERED:
149
+ if descriptor_name == DocDescriptorNames.OAV_GRID_SNAPSHOT_TRIGGERED:
155
150
  scan_data_infos = self._handle_oav_grid_snapshot_triggered(doc)
156
151
  self.ispyb_ids = self.ispyb.update_deposition(
157
152
  self.ispyb_ids, scan_data_infos
@@ -162,38 +157,10 @@ class GridscanISPyBCallback(BaseISPyBCallback):
162
157
 
163
158
  return doc
164
159
 
165
- def _handle_zocalo_read_event(self, doc):
160
+ def _add_processing_time_to_comment(self, processing_start_time: float):
166
161
  assert self.data_collection_group_info, ASSERT_START_BEFORE_EVENT_DOC_MESSAGE
167
- crystal_summary = ""
168
- if self._processing_start_time is not None:
169
- proc_time = time() - self._processing_start_time
170
- crystal_summary = f"Zocalo processing took {proc_time:.2f} s. "
171
- bboxes: list[np.ndarray] = []
172
- ISPYB_ZOCALO_CALLBACK_LOGGER.info(
173
- f"Amending comment based on Zocalo reading doc: {format_doc_for_log(doc)}"
174
- )
175
-
176
- raw_results = get_processing_results_from_event("zocalo", doc)
177
- if len(raw_results) > 0:
178
- for n, res in enumerate(raw_results):
179
- bb = res["bounding_box"]
180
- diff = np.array(bb[1]) - np.array(bb[0])
181
- bboxes.append(diff)
182
-
183
- nicely_formatted_com = [
184
- f"{np.round(com, 2)}" for com in res["centre_of_mass"]
185
- ]
186
- crystal_summary += (
187
- f"Crystal {n + 1}: "
188
- f"Strength {res['total_count']}; "
189
- f"Position (grid boxes) {nicely_formatted_com}; "
190
- f"Size (grid boxes) {bboxes[n]}; "
191
- )
192
- else:
193
- crystal_summary += "Zocalo found no crystals in this gridscan."
194
- assert self.ispyb_ids.data_collection_ids, (
195
- "No data collection to add results to"
196
- )
162
+ proc_time = time() - processing_start_time
163
+ crystal_summary = f"Zocalo processing took {proc_time:.2f} s."
197
164
 
198
165
  self.data_collection_group_info.comments = (
199
166
  self.data_collection_group_info.comments or ""
@@ -320,10 +287,12 @@ class GridscanISPyBCallback(BaseISPyBCallback):
320
287
  if exception_type:
321
288
  doc["reason"] = message
322
289
  self.data_collection_group_info.comments = message
323
- self.ispyb.update_data_collection_group_table(
324
- self.data_collection_group_info,
325
- self.ispyb_ids.data_collection_group_id,
326
- )
290
+ elif self._processing_start_time:
291
+ self._add_processing_time_to_comment(self._processing_start_time)
292
+ self.ispyb.update_data_collection_group_table(
293
+ self.data_collection_group_info,
294
+ self.ispyb_ids.data_collection_group_id,
295
+ )
327
296
  self.data_collection_group_info = None
328
297
  return super().activity_gated_stop(doc)
329
298
  return self._tag_doc(doc)
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING
6
6
 
7
7
  import ispyb
8
8
  import ispyb.sqlalchemy
9
+ import numpy as np
9
10
  from ispyb.connector.mysqlsp.main import ISPyBMySQLSPConnector as Connector
10
11
  from ispyb.sp.mxacquisition import MXAcquisition
11
12
  from ispyb.strictordereddict import StrictOrderedDict
@@ -280,7 +281,9 @@ class StoreInIspyb:
280
281
  conn, data_collection_info.visit_string
281
282
  )
282
283
  params |= {
283
- k: v for k, v in asdict(data_collection_info).items() if k != "visit_string"
284
+ k: v.item() if isinstance(v, np.generic) else v # Convert to native types
285
+ for k, v in asdict(data_collection_info).items()
286
+ if k != "visit_string"
284
287
  }
285
288
 
286
289
  return params
@@ -7,11 +7,11 @@ from ispyb import NoResult
7
7
  from ispyb.connector.mysqlsp.main import ISPyBMySQLSPConnector as Connector
8
8
  from ispyb.sp.core import Core
9
9
 
10
- from mx_bluesky.common.parameters.constants import SimConstants
11
10
 
12
-
13
- def get_ispyb_config():
14
- return os.environ.get("ISPYB_CONFIG_PATH", SimConstants.ISPYB_CONFIG)
11
+ def get_ispyb_config() -> str:
12
+ ispyb_config = os.environ.get("ISPYB_CONFIG_PATH")
13
+ assert ispyb_config, "ISPYB_CONFIG_PATH must be set"
14
+ return ispyb_config
15
15
 
16
16
 
17
17
  def get_session_id_from_visit(conn: Connector, visit: str):
@@ -158,6 +158,6 @@ def create_beam_and_attenuator_parameters(
158
158
  tuple[Beam, Attenuator]: Descriptions of the beam and attenuator for nexgen.
159
159
  """
160
160
  return (
161
- Beam(convert_eV_to_angstrom(energy_kev * 1000), flux), # pyright: ignore
162
- Attenuator(transmission_fraction), # pyright: ignore
161
+ Beam(wavelength=convert_eV_to_angstrom(energy_kev * 1000), flux=flux),
162
+ Attenuator(transmission=transmission_fraction),
163
163
  )
@@ -5,7 +5,7 @@ from abc import abstractmethod
5
5
  from collections.abc import Sequence
6
6
  from enum import StrEnum
7
7
  from pathlib import Path
8
- from typing import Literal, SupportsInt, cast
8
+ from typing import Literal, Self, SupportsInt, cast
9
9
 
10
10
  from dodal.devices.aperturescatterguard import ApertureValue
11
11
  from dodal.devices.detector import (
@@ -114,12 +114,32 @@ class MxBlueskyParameters(BaseModel):
114
114
 
115
115
 
116
116
  class WithSnapshot(BaseModel):
117
+ """
118
+ Configures how snapshot images are created.
119
+ Attributes:
120
+ snapshot_directory: Path to the directory where snapshot images will be stored
121
+ snapshot_omegas_deg: list of omega values at which snapshots will be taken. For
122
+ gridscans, this attribute is ignored.
123
+ use_grid_snapshots: This may be specified for rotation snapshots to speed up rotation
124
+ execution. If set to True then rotation snapshots are generated from the
125
+ previously captured grid snapshots. Otherwise they are captured using
126
+ freshly captured snapshots during the rotation plan.
127
+ """
128
+
117
129
  snapshot_directory: Path
118
130
  snapshot_omegas_deg: list[float] | None = None
131
+ use_grid_snapshots: bool = False
119
132
 
120
133
  @property
121
134
  def take_snapshots(self) -> bool:
122
- return bool(self.snapshot_omegas_deg)
135
+ return bool(self.snapshot_omegas_deg) or self.use_grid_snapshots
136
+
137
+ @model_validator(mode="after")
138
+ def _validate_omegas_with_grid_snapshots(self) -> Self:
139
+ assert not self.use_grid_snapshots or self.snapshot_omegas_deg is None, (
140
+ "snapshot_omegas may not be specified with use_grid_snapshots"
141
+ )
142
+ return self
123
143
 
124
144
 
125
145
  class WithOptionalEnergyChange(BaseModel):
@@ -55,6 +55,7 @@ class PlanNameConstants:
55
55
  ROBOT_LOAD_AND_SNAPSHOTS = "robot_load_and_snapshots"
56
56
  # Rotation scan
57
57
  ROTATION_MULTI = "multi_rotation_wrapper"
58
+ ROTATION_MULTI_OUTER = "multi_rotation_outer"
58
59
  ROTATION_OUTER = "rotation_scan_with_cleanup"
59
60
  ROTATION_MAIN = "rotation_scan_main"
60
61
  FLYSCAN_RESULTS = "xray_centre_results"
@@ -67,17 +68,12 @@ class EnvironmentConstants:
67
68
  ZOCALO_ENV = ZOCALO_ENV_FROM_DODAL
68
69
 
69
70
 
70
- @dataclass(frozen=True)
71
- class TriggerConstants:
72
- ZOCALO = "trigger_zocalo_on"
73
-
74
-
75
71
  @dataclass(frozen=True)
76
72
  class HardwareConstants:
77
73
  OAV_REFRESH_DELAY = 0.3
78
74
  PANDA_FGS_RUN_UP_DEFAULT = 0.17
79
75
  CRYOJET_MARGIN_MM = 0.2
80
- THAWING_TIME = 20
76
+ THAWING_TIME = 40
81
77
  TIP_OFFSET_UM = 0
82
78
 
83
79
  # Value quoted in https://www.dectris.com/en/detectors/x-ray-detectors/eiger2/eiger2-for-synchrotrons/eiger2-x/,
@@ -94,6 +90,7 @@ class GridscanParamConstants:
94
90
  OMEGA_1 = 0.0
95
91
  OMEGA_2 = 90.0
96
92
  PANDA_RUN_UP_DISTANCE_MM = 0.2
93
+ ZOCALO_MIN_TOTAL_COUNT_THRESHOLD = 3
97
94
 
98
95
 
99
96
  @dataclass(frozen=True)
@@ -125,6 +122,7 @@ class PlanGroupCheckpointConstants:
125
122
  ROTATION_READY_FOR_DC = "rotation_ready_for_data_collection"
126
123
  MOVE_GONIO_TO_START = "move_gonio_to_start"
127
124
  READY_FOR_OAV = "ready_for_oav"
125
+ PREPARE_APERTURE = "prepare_aperture"
128
126
 
129
127
 
130
128
  # Eventually replace below with https://github.com/DiamondLightSource/mx-bluesky/issues/798
@@ -136,16 +134,6 @@ class DeviceSettingsConstants:
136
134
  )
137
135
 
138
136
 
139
- @dataclass(frozen=True)
140
- class SimConstants:
141
- BEAMLINE = "BL03S"
142
- INSERTION_PREFIX = "SR03S"
143
- # this one is for unit tests
144
- ISPYB_CONFIG = "tests/test_data/test_config.cfg"
145
- # this one is for system tests
146
- DEV_ISPYB_DATABASE_CFG = "/dls_sw/dasc/mariadb/credentials/ispyb-hyperion-dev.cfg"
147
-
148
-
149
137
  class Actions(Enum):
150
138
  START = "start"
151
139
  STOP = "stop"
@@ -26,7 +26,11 @@ from mx_bluesky.common.parameters.constants import (
26
26
  HardwareConstants,
27
27
  )
28
28
 
29
- DETECTOR_SIZE_PER_BEAMLINE = {"i02-1": EIGER2_X_9M_SIZE, "dev": EIGER2_X_16M_SIZE}
29
+ DETECTOR_SIZE_PER_BEAMLINE = {
30
+ "i02-1": EIGER2_X_9M_SIZE,
31
+ "dev": EIGER2_X_16M_SIZE,
32
+ "i03": EIGER2_X_16M_SIZE,
33
+ }
30
34
 
31
35
 
32
36
  class GridCommon(
@@ -50,6 +54,37 @@ class GridCommon(
50
54
 
51
55
  tip_offset_um: float = Field(default=HardwareConstants.TIP_OFFSET_UM)
52
56
 
57
+ @property
58
+ def detector_params(self):
59
+ self.det_dist_to_beam_converter_path = (
60
+ self.det_dist_to_beam_converter_path
61
+ or DetectorParamConstants.BEAM_XY_LUT_PATH
62
+ )
63
+ optional_args = {}
64
+ if self.run_number:
65
+ optional_args["run_number"] = self.run_number
66
+ assert self.detector_distance_mm is not None, (
67
+ "Detector distance must be filled before generating DetectorParams"
68
+ )
69
+ return DetectorParams(
70
+ detector_size_constants=DETECTOR_SIZE_PER_BEAMLINE[
71
+ get_beamline_name("dev")
72
+ ],
73
+ expected_energy_ev=self.demand_energy_ev,
74
+ exposure_time_s=self.exposure_time_s,
75
+ directory=self.storage_directory,
76
+ prefix=self.file_name,
77
+ detector_distance=self.detector_distance_mm,
78
+ omega_start=self.omega_start_deg or 0,
79
+ omega_increment=0,
80
+ num_images_per_trigger=1,
81
+ num_triggers=self.num_images,
82
+ use_roi_mode=self.use_roi_mode,
83
+ det_dist_to_beam_converter_path=self.det_dist_to_beam_converter_path,
84
+ trigger_mode=self.trigger_mode,
85
+ **optional_args,
86
+ )
87
+
53
88
 
54
89
  class SpecifiedGrid(XyzStarts, WithScan):
55
90
  """A specified grid is one which has defined values for the start position,
@@ -150,34 +185,3 @@ class SpecifiedThreeDGridScan(
150
185
  @property
151
186
  def num_images(self) -> int:
152
187
  return len(self.scan_points["sam_x"])
153
-
154
- @property
155
- def detector_params(self):
156
- self.det_dist_to_beam_converter_path = (
157
- self.det_dist_to_beam_converter_path
158
- or DetectorParamConstants.BEAM_XY_LUT_PATH
159
- )
160
- optional_args = {}
161
- if self.run_number:
162
- optional_args["run_number"] = self.run_number
163
- assert self.detector_distance_mm is not None, (
164
- "Detector distance must be filled before generating DetectorParams"
165
- )
166
- return DetectorParams(
167
- detector_size_constants=DETECTOR_SIZE_PER_BEAMLINE[
168
- get_beamline_name("dev")
169
- ],
170
- expected_energy_ev=self.demand_energy_ev,
171
- exposure_time_s=self.exposure_time_s,
172
- directory=self.storage_directory,
173
- prefix=self.file_name,
174
- detector_distance=self.detector_distance_mm,
175
- omega_start=self.omega_start_deg or 0,
176
- omega_increment=0,
177
- num_images_per_trigger=1,
178
- num_triggers=self.num_images,
179
- use_roi_mode=self.use_roi_mode,
180
- det_dist_to_beam_converter_path=self.det_dist_to_beam_converter_path,
181
- trigger_mode=self.trigger_mode,
182
- **optional_args,
183
- )