mx-bluesky 1.4.1a0__py3-none-any.whl → 1.4.3__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 (105) hide show
  1. mx_bluesky/_version.py +2 -2
  2. mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +178 -0
  3. mx_bluesky/beamlines/i24/serial/__init__.py +0 -6
  4. mx_bluesky/beamlines/i24/serial/dcid.py +125 -151
  5. mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +1 -1
  6. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +88 -43
  7. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +1 -1
  8. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/MappingLite-oxford_py3v1.edl +2 -46
  9. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +85 -122
  10. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +58 -66
  11. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +1 -19
  12. mx_bluesky/beamlines/i24/serial/parameters/__init__.py +11 -2
  13. mx_bluesky/beamlines/i24/serial/parameters/constants.py +16 -2
  14. mx_bluesky/beamlines/i24/serial/parameters/experiment_parameters.py +94 -19
  15. mx_bluesky/beamlines/i24/serial/parameters/utils.py +19 -0
  16. mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +2 -0
  17. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +61 -8
  18. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +81 -40
  19. mx_bluesky/beamlines/i24/serial/write_nexus.py +66 -67
  20. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/aperture_change_callback.py +1 -1
  21. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/grid_detection_callback.py +19 -1
  22. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/ispyb_callback_base.py +40 -34
  23. mx_bluesky/{hyperion → common}/external_interaction/callbacks/common/ispyb_mapping.py +4 -4
  24. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/logging_callback.py +1 -1
  25. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/zocalo_callback.py +14 -9
  26. mx_bluesky/{hyperion → common}/external_interaction/callbacks/xray_centre/ispyb_callback.py +46 -38
  27. mx_bluesky/{hyperion → common}/external_interaction/callbacks/xray_centre/ispyb_mapping.py +2 -2
  28. mx_bluesky/{hyperion → common}/external_interaction/callbacks/xray_centre/nexus_callback.py +20 -15
  29. mx_bluesky/common/external_interaction/config_server.py +11 -0
  30. mx_bluesky/common/external_interaction/ispyb/__init__.py +0 -0
  31. mx_bluesky/{hyperion → common}/external_interaction/ispyb/data_model.py +2 -0
  32. mx_bluesky/{hyperion → common}/external_interaction/ispyb/exp_eye_store.py +67 -17
  33. mx_bluesky/{hyperion → common}/external_interaction/ispyb/ispyb_store.py +20 -18
  34. mx_bluesky/{hyperion → common}/external_interaction/ispyb/ispyb_utils.py +2 -2
  35. mx_bluesky/common/external_interaction/nexus/__init__.py +0 -0
  36. mx_bluesky/{hyperion → common}/external_interaction/nexus/nexus_utils.py +21 -6
  37. mx_bluesky/{hyperion → common}/external_interaction/nexus/write_nexus.py +5 -5
  38. mx_bluesky/common/external_interaction/test_config_server.py +38 -0
  39. mx_bluesky/common/parameters/components.py +10 -8
  40. mx_bluesky/common/parameters/constants.py +6 -0
  41. mx_bluesky/common/parameters/gridscan.py +102 -53
  42. mx_bluesky/common/plans/do_fgs.py +4 -4
  43. mx_bluesky/{hyperion → common/utils}/exceptions.py +27 -1
  44. mx_bluesky/common/utils/log.py +17 -7
  45. mx_bluesky/hyperion/__main__.py +15 -14
  46. mx_bluesky/hyperion/device_setup_plans/check_beamstop.py +27 -0
  47. mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +34 -37
  48. mx_bluesky/hyperion/device_setup_plans/manipulate_sample.py +7 -7
  49. mx_bluesky/hyperion/device_setup_plans/position_detector.py +1 -1
  50. mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +3 -3
  51. mx_bluesky/hyperion/device_setup_plans/setup_panda.py +21 -4
  52. mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +62 -36
  53. mx_bluesky/hyperion/device_setup_plans/smargon.py +3 -3
  54. mx_bluesky/hyperion/device_setup_plans/utils.py +4 -0
  55. mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +8 -8
  56. mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +28 -17
  57. mx_bluesky/hyperion/experiment_plans/common/xrc_result.py +10 -1
  58. mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -9
  59. mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +54 -58
  60. mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +22 -31
  61. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +57 -40
  62. mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +3 -3
  63. mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +8 -2
  64. mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +6 -14
  65. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +12 -11
  66. mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +4 -4
  67. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +39 -30
  68. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +36 -18
  69. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +33 -21
  70. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +10 -9
  71. mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +0 -4
  72. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +31 -20
  73. mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +46 -30
  74. mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +39 -24
  75. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +25 -24
  76. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +1 -1
  77. mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +13 -9
  78. mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/__init__.py +0 -0
  79. mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/sample_handling_callback.py +50 -0
  80. mx_bluesky/hyperion/external_interaction/config_server.py +15 -1
  81. mx_bluesky/hyperion/parameters/components.py +3 -2
  82. mx_bluesky/hyperion/parameters/constants.py +1 -0
  83. mx_bluesky/hyperion/parameters/gridscan.py +56 -89
  84. mx_bluesky/hyperion/parameters/load_centre_collect.py +51 -6
  85. mx_bluesky/hyperion/parameters/robot_load.py +40 -0
  86. mx_bluesky/hyperion/parameters/rotation.py +28 -3
  87. mx_bluesky/hyperion/utils/context.py +1 -1
  88. mx_bluesky/hyperion/utils/validation.py +5 -3
  89. {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/METADATA +6 -6
  90. mx_bluesky-1.4.3.dist-info/RECORD +155 -0
  91. {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/WHEEL +1 -1
  92. mx_bluesky/common/parameters/robot_load.py +0 -16
  93. mx_bluesky/hyperion/external_interaction/exceptions.py +0 -13
  94. mx_bluesky/hyperion/log.py +0 -15
  95. mx_bluesky-1.4.1a0.dist-info/RECORD +0 -150
  96. /mx_bluesky/{hyperion/external_interaction/callbacks/xray_centre → common/external_interaction}/__init__.py +0 -0
  97. /mx_bluesky/{hyperion/external_interaction/ispyb → common/external_interaction/callbacks/common}/__init__.py +0 -0
  98. /mx_bluesky/{hyperion → common}/external_interaction/callbacks/common/abstract_event.py +0 -0
  99. /mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/log_uid_tag_callback.py +0 -0
  100. /mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/plan_reactive_callback.py +0 -0
  101. /mx_bluesky/{hyperion/external_interaction/nexus → common/external_interaction/callbacks/xray_centre}/__init__.py +0 -0
  102. /mx_bluesky/{hyperion → common}/utils/utils.py +0 -0
  103. {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/LICENSE +0 -0
  104. {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/entry_points.txt +0 -0
  105. {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/top_level.txt +0 -0
@@ -5,11 +5,6 @@ from typing import TypedDict
5
5
 
6
6
  import mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan as flyscan_xray_centre_plan
7
7
  import mx_bluesky.hyperion.experiment_plans.rotation_scan_plan as rotation_scan_plan
8
- from mx_bluesky.common.parameters.gridscan import (
9
- GridScanWithEdgeDetect,
10
- PinTipCentreThenXrayCentre,
11
- RobotLoadThenCentre,
12
- )
13
8
  from mx_bluesky.hyperion.experiment_plans import (
14
9
  grid_detect_then_xray_centre_plan,
15
10
  load_centre_collect_full_plan,
@@ -23,8 +18,13 @@ from mx_bluesky.hyperion.external_interaction.callbacks.common.callback_util imp
23
18
  create_robot_load_and_centre_callbacks,
24
19
  create_rotation_callbacks,
25
20
  )
26
- from mx_bluesky.hyperion.parameters.gridscan import HyperionThreeDGridScan
21
+ from mx_bluesky.hyperion.parameters.gridscan import (
22
+ GridScanWithEdgeDetect,
23
+ HyperionSpecifiedThreeDGridScan,
24
+ PinTipCentreThenXrayCentre,
25
+ )
27
26
  from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect
27
+ from mx_bluesky.hyperion.parameters.robot_load import RobotLoadThenCentre
28
28
  from mx_bluesky.hyperion.parameters.rotation import MultiRotationScan, RotationScan
29
29
 
30
30
 
@@ -39,13 +39,13 @@ def do_nothing():
39
39
  class ExperimentRegistryEntry(TypedDict):
40
40
  setup: Callable
41
41
  param_type: type[
42
- HyperionThreeDGridScan
42
+ HyperionSpecifiedThreeDGridScan
43
43
  | GridScanWithEdgeDetect
44
44
  | RotationScan
45
45
  | MultiRotationScan
46
46
  | PinTipCentreThenXrayCentre
47
- | RobotLoadThenCentre
48
47
  | LoadCentreCollect
48
+ | RobotLoadThenCentre
49
49
  ]
50
50
  callbacks_factory: CallbacksFactory
51
51
 
@@ -53,7 +53,7 @@ class ExperimentRegistryEntry(TypedDict):
53
53
  PLAN_REGISTRY: dict[str, ExperimentRegistryEntry] = {
54
54
  "flyscan_xray_centre": {
55
55
  "setup": flyscan_xray_centre_plan.create_devices,
56
- "param_type": HyperionThreeDGridScan,
56
+ "param_type": HyperionSpecifiedThreeDGridScan,
57
57
  "callbacks_factory": create_gridscan_callbacks,
58
58
  },
59
59
  "grid_detect_then_xray_centre": {
@@ -16,7 +16,7 @@ from bluesky.utils import MsgGenerator
16
16
  from dodal.devices.aperturescatterguard import (
17
17
  ApertureScatterguard,
18
18
  )
19
- from dodal.devices.attenuator import Attenuator
19
+ from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
20
20
  from dodal.devices.backlight import Backlight
21
21
  from dodal.devices.dcm import DCM
22
22
  from dodal.devices.eiger import EigerDetector
@@ -35,8 +35,8 @@ from dodal.devices.smargon import Smargon
35
35
  from dodal.devices.synchrotron import Synchrotron
36
36
  from dodal.devices.undulator import Undulator
37
37
  from dodal.devices.xbpm_feedback import XBPMFeedback
38
- from dodal.devices.zebra import Zebra
39
- from dodal.devices.zebra_controlled_shutter import ZebraShutter
38
+ from dodal.devices.zebra.zebra import Zebra
39
+ from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
40
40
  from dodal.devices.zocalo.zocalo_results import (
41
41
  ZOCALO_READING_PLAN_NAME,
42
42
  ZOCALO_STAGE_GROUP,
@@ -47,7 +47,15 @@ from dodal.devices.zocalo.zocalo_results import (
47
47
  from event_model import RunStart
48
48
  from ophyd_async.fastcs.panda import HDFPanda
49
49
 
50
+ from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback import (
51
+ ispyb_activation_wrapper,
52
+ )
50
53
  from mx_bluesky.common.plans.do_fgs import kickoff_and_complete_gridscan
54
+ from mx_bluesky.common.utils.exceptions import (
55
+ CrystalNotFoundException,
56
+ SampleException,
57
+ )
58
+ from mx_bluesky.common.utils.log import LOGGER
51
59
  from mx_bluesky.common.utils.tracing import TRACER
52
60
  from mx_bluesky.hyperion.device_setup_plans.read_hardware_for_setup import (
53
61
  read_hardware_during_collection,
@@ -66,27 +74,18 @@ from mx_bluesky.hyperion.device_setup_plans.setup_zebra import (
66
74
  from mx_bluesky.hyperion.device_setup_plans.xbpm_feedback import (
67
75
  transmission_and_xbpm_feedback_for_collection_decorator,
68
76
  )
69
- from mx_bluesky.hyperion.exceptions import WarningException
70
77
  from mx_bluesky.hyperion.experiment_plans.change_aperture_then_move_plan import (
71
78
  change_aperture_then_move_to_xtal,
72
79
  )
73
80
  from mx_bluesky.hyperion.experiment_plans.common.xrc_result import XRayCentreResult
74
- from mx_bluesky.hyperion.external_interaction.callbacks.xray_centre.ispyb_callback import (
75
- ispyb_activation_wrapper,
76
- )
77
- from mx_bluesky.hyperion.log import LOGGER
78
81
  from mx_bluesky.hyperion.parameters.constants import CONST
79
- from mx_bluesky.hyperion.parameters.gridscan import HyperionThreeDGridScan
82
+ from mx_bluesky.hyperion.parameters.gridscan import HyperionSpecifiedThreeDGridScan
80
83
  from mx_bluesky.hyperion.utils.context import device_composite_from_context
81
84
 
85
+ ZOCALO_MIN_TOTAL_COUNT_THRESHOLD = 3
82
86
 
83
- class SmargonSpeedException(Exception):
84
- pass
85
-
86
-
87
- class CrystalNotFoundException(WarningException):
88
- """Raised if grid detection completed normally but no crystal was found."""
89
87
 
88
+ class SmargonSpeedException(Exception):
90
89
  pass
91
90
 
92
91
 
@@ -95,7 +94,7 @@ class FlyScanXRayCentreComposite:
95
94
  """All devices which are directly or indirectly required by this plan"""
96
95
 
97
96
  aperture_scatterguard: ApertureScatterguard
98
- attenuator: Attenuator
97
+ attenuator: BinaryFilterAttenuator
99
98
  backlight: Backlight
100
99
  dcm: DCM
101
100
  eiger: EigerDetector
@@ -113,11 +112,6 @@ class FlyScanXRayCentreComposite:
113
112
  robot: BartRobot
114
113
  sample_shutter: ZebraShutter
115
114
 
116
- @property
117
- def sample_motors(self) -> Smargon:
118
- """Convenience alias with a more user-friendly name"""
119
- return self.smargon
120
-
121
115
 
122
116
  class XRayCentreEventHandler(CallbackBase):
123
117
  def __init__(self):
@@ -139,7 +133,7 @@ def create_devices(context: BlueskyContext) -> FlyScanXRayCentreComposite:
139
133
 
140
134
 
141
135
  def flyscan_xray_centre_no_move(
142
- composite: FlyScanXRayCentreComposite, parameters: HyperionThreeDGridScan
136
+ composite: FlyScanXRayCentreComposite, parameters: HyperionSpecifiedThreeDGridScan
143
137
  ) -> MsgGenerator:
144
138
  """Perform a flyscan and determine the centres of interest"""
145
139
  parameters.features.update_self_from_server()
@@ -153,7 +147,7 @@ def flyscan_xray_centre_no_move(
153
147
  @bpp.run_decorator( # attach experiment metadata to the start document
154
148
  md={
155
149
  "subplan_name": CONST.PLAN.GRIDSCAN_OUTER,
156
- "hyperion_parameters": parameters.model_dump_json(),
150
+ "mx_bluesky_parameters": parameters.model_dump_json(),
157
151
  "activate_callbacks": [
158
152
  "GridscanNexusFileCallback",
159
153
  ],
@@ -167,7 +161,7 @@ def flyscan_xray_centre_no_move(
167
161
  )
168
162
  def run_gridscan_and_fetch_and_tidy(
169
163
  fgs_composite: FlyScanXRayCentreComposite,
170
- params: HyperionThreeDGridScan,
164
+ params: HyperionSpecifiedThreeDGridScan,
171
165
  feature_controlled: _FeatureControlled,
172
166
  ) -> MsgGenerator:
173
167
  yield from run_gridscan_and_fetch_results(
@@ -181,7 +175,7 @@ def flyscan_xray_centre_no_move(
181
175
 
182
176
  def flyscan_xray_centre(
183
177
  composite: FlyScanXRayCentreComposite,
184
- parameters: HyperionThreeDGridScan,
178
+ parameters: HyperionSpecifiedThreeDGridScan,
185
179
  ) -> MsgGenerator:
186
180
  """Create the plan to run the grid scan based on provided parameters.
187
181
 
@@ -189,7 +183,7 @@ def flyscan_xray_centre(
189
183
  at any point in it.
190
184
 
191
185
  Args:
192
- parameters (ThreeDGridScan): The parameters to run the scan.
186
+ parameters (HyperionSpecifiedThreeDGridScan): The parameters to run the scan.
193
187
 
194
188
  Returns:
195
189
  Generator: The plan for the gridscan
@@ -205,9 +199,9 @@ def flyscan_xray_centre(
205
199
  yield from flyscan_and_fetch_results()
206
200
 
207
201
  xray_centre_results = xrc_event_handler.xray_centre_results
208
- assert (
209
- xray_centre_results
210
- ), "Flyscan result event not received or no crystal found and exception not raised"
202
+ assert xray_centre_results, (
203
+ "Flyscan result event not received or no crystal found and exception not raised"
204
+ )
211
205
  yield from change_aperture_then_move_to_xtal(
212
206
  xray_centre_results[0],
213
207
  composite.smargon,
@@ -220,22 +214,13 @@ def flyscan_xray_centre(
220
214
  @bpp.run_decorator(md={"subplan_name": CONST.PLAN.GRIDSCAN_AND_MOVE})
221
215
  def run_gridscan_and_fetch_results(
222
216
  fgs_composite: FlyScanXRayCentreComposite,
223
- parameters: HyperionThreeDGridScan,
217
+ parameters: HyperionSpecifiedThreeDGridScan,
224
218
  feature_controlled: _FeatureControlled,
225
219
  ) -> MsgGenerator:
226
220
  """A multi-run plan which runs a gridscan, gets the results from zocalo
227
221
  and fires an event with the centres of mass determined by zocalo"""
228
222
 
229
- # We get the initial motor positions so we can return to them on zocalo failure
230
- initial_xyz = np.array(
231
- [
232
- (yield from bps.rd(fgs_composite.sample_motors.x)),
233
- (yield from bps.rd(fgs_composite.sample_motors.y)),
234
- (yield from bps.rd(fgs_composite.sample_motors.z)),
235
- ]
236
- )
237
-
238
- yield from feature_controlled.setup_trigger(fgs_composite, parameters, initial_xyz)
223
+ yield from feature_controlled.setup_trigger(fgs_composite, parameters)
239
224
 
240
225
  LOGGER.info("Starting grid scan")
241
226
  yield from bps.stage(
@@ -253,10 +238,20 @@ def run_gridscan_and_fetch_results(
253
238
  LOGGER.info("Zocalo triggered and read, interpreting results.")
254
239
  xrc_results = yield from get_full_processing_results(fgs_composite.zocalo)
255
240
  LOGGER.info(f"Got xray centres, top 5: {xrc_results[:5]}")
256
- if xrc_results:
241
+ filtered_results = [
242
+ result
243
+ for result in xrc_results
244
+ if result["total_count"] >= ZOCALO_MIN_TOTAL_COUNT_THRESHOLD
245
+ ]
246
+ discarded_count = len(xrc_results) - len(filtered_results)
247
+ if discarded_count > 0:
248
+ LOGGER.info(
249
+ f"Removed {discarded_count} results because below threshold"
250
+ )
251
+ if filtered_results:
257
252
  flyscan_results = [
258
253
  _xrc_result_in_boxes_to_result_in_mm(xr, parameters)
259
- for xr in xrc_results
254
+ for xr in filtered_results
260
255
  ]
261
256
  else:
262
257
  LOGGER.warning("No X-ray centre received")
@@ -274,20 +269,26 @@ def run_gridscan_and_fetch_results(
274
269
 
275
270
 
276
271
  def _xrc_result_in_boxes_to_result_in_mm(
277
- xrc_result: XrcResult, parameters: HyperionThreeDGridScan
272
+ xrc_result: XrcResult, parameters: HyperionSpecifiedThreeDGridScan
278
273
  ) -> XRayCentreResult:
279
274
  fgs_params = parameters.FGS_params
280
275
  xray_centre = fgs_params.grid_position_to_motor_position(
281
276
  np.array(xrc_result["centre_of_mass"])
282
277
  )
278
+ # A correction is applied to the bounding box to map discrete grid coordinates to
279
+ # the corners of the box in motor-space; we do not apply this correction
280
+ # to the xray-centre as it is already in continuous space and the conversion has
281
+ # been performed already
282
+ # In other words, xrc_result["bounding_box"] contains the position of the box centre,
283
+ # so we subtract half a box to get the corner of the box
283
284
  return XRayCentreResult(
284
285
  centre_of_mass_mm=xray_centre,
285
286
  bounding_box_mm=(
286
287
  fgs_params.grid_position_to_motor_position(
287
- np.array(xrc_result["bounding_box"][0])
288
+ np.array(xrc_result["bounding_box"][0]) - 0.5
288
289
  ),
289
290
  fgs_params.grid_position_to_motor_position(
290
- np.array(xrc_result["bounding_box"][1])
291
+ np.array(xrc_result["bounding_box"][1]) - 0.5
291
292
  ),
292
293
  ),
293
294
  max_count=xrc_result["max_count"],
@@ -310,17 +311,15 @@ def _fire_xray_centre_result_event(results: Sequence[XRayCentreResult]):
310
311
  @bpp.run_decorator(md={"subplan_name": CONST.PLAN.GRIDSCAN_MAIN})
311
312
  def run_gridscan(
312
313
  fgs_composite: FlyScanXRayCentreComposite,
313
- parameters: HyperionThreeDGridScan,
314
+ parameters: HyperionSpecifiedThreeDGridScan,
314
315
  feature_controlled: _FeatureControlled,
315
316
  md={ # noqa
316
317
  "plan_name": CONST.PLAN.GRIDSCAN_MAIN,
317
318
  },
318
319
  ):
319
- sample_motors = fgs_composite.sample_motors
320
-
321
320
  # Currently gridscan only works for omega 0, see #
322
321
  with TRACER.start_span("moving_omega_to_0"):
323
- yield from bps.abs_set(sample_motors.omega, 0)
322
+ yield from bps.abs_set(fgs_composite.smargon.omega, 0)
324
323
 
325
324
  # We only subscribe to the communicator callback for run_gridscan, so this is where
326
325
  # we should generate an event reading the values which need to be included in the
@@ -378,7 +377,7 @@ def wait_for_gridscan_valid(fgs_motors: FastGridScanCommon, timeout=0.5):
378
377
  LOGGER.info("Gridscan scan valid and position counter reset")
379
378
  return
380
379
  yield from bps.sleep(SLEEP_PER_CHECK)
381
- raise WarningException("Scan invalid - pin too long/short/bent and out of range")
380
+ raise SampleException("Scan invalid - pin too long/short/bent and out of range")
382
381
 
383
382
 
384
383
  @dataclasses.dataclass
@@ -392,8 +391,7 @@ class _FeatureControlled:
392
391
  def __call__(
393
392
  self,
394
393
  fgs_composite: FlyScanXRayCentreComposite,
395
- parameters: HyperionThreeDGridScan,
396
- initial_xyz: np.ndarray,
394
+ parameters: HyperionSpecifiedThreeDGridScan,
397
395
  ) -> MsgGenerator: ...
398
396
 
399
397
  setup_trigger: _ExtraSetup
@@ -404,7 +402,7 @@ class _FeatureControlled:
404
402
 
405
403
  def _get_feature_controlled(
406
404
  fgs_composite: FlyScanXRayCentreComposite,
407
- parameters: HyperionThreeDGridScan,
405
+ parameters: HyperionSpecifiedThreeDGridScan,
408
406
  ):
409
407
  if parameters.features.use_panda_for_gridscan:
410
408
  return _FeatureControlled(
@@ -453,8 +451,7 @@ def _panda_tidy(fgs_composite: FlyScanXRayCentreComposite):
453
451
 
454
452
  def _zebra_triggering_setup(
455
453
  fgs_composite: FlyScanXRayCentreComposite,
456
- parameters: HyperionThreeDGridScan,
457
- initial_xyz: np.ndarray,
454
+ parameters: HyperionSpecifiedThreeDGridScan,
458
455
  ):
459
456
  yield from setup_zebra_for_gridscan(
460
457
  fgs_composite.zebra, fgs_composite.sample_shutter, wait=True
@@ -463,8 +460,7 @@ def _zebra_triggering_setup(
463
460
 
464
461
  def _panda_triggering_setup(
465
462
  fgs_composite: FlyScanXRayCentreComposite,
466
- parameters: HyperionThreeDGridScan,
467
- initial_xyz: np.ndarray,
463
+ parameters: HyperionSpecifiedThreeDGridScan,
468
464
  ):
469
465
  LOGGER.info("Setting up Panda for flyscan")
470
466
 
@@ -509,7 +505,7 @@ def _panda_triggering_setup(
509
505
  yield from setup_panda_for_flyscan(
510
506
  fgs_composite.panda,
511
507
  parameters.panda_FGS_params,
512
- initial_xyz[0],
508
+ fgs_composite.smargon,
513
509
  parameters.exposure_time_s,
514
510
  time_between_x_steps_ms,
515
511
  sample_velocity_mm_per_s,
@@ -9,13 +9,14 @@ from bluesky import preprocessors as bpp
9
9
  from bluesky.preprocessors import subs_decorator
10
10
  from bluesky.utils import MsgGenerator
11
11
  from dodal.devices.aperturescatterguard import ApertureScatterguard
12
- from dodal.devices.attenuator import Attenuator
12
+ from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
13
13
  from dodal.devices.backlight import Backlight, BacklightPosition
14
14
  from dodal.devices.dcm import DCM
15
15
  from dodal.devices.detector.detector_motion import DetectorMotion
16
16
  from dodal.devices.eiger import EigerDetector
17
17
  from dodal.devices.fast_grid_scan import PandAFastGridScan, ZebraFastGridScan
18
18
  from dodal.devices.flux import Flux
19
+ from dodal.devices.i03.beamstop import Beamstop
19
20
  from dodal.devices.oav.oav_detector import OAV
20
21
  from dodal.devices.oav.oav_parameters import OAVParameters
21
22
  from dodal.devices.oav.pin_image_recognition import PinTipDetection
@@ -25,13 +26,20 @@ from dodal.devices.smargon import Smargon
25
26
  from dodal.devices.synchrotron import Synchrotron
26
27
  from dodal.devices.undulator import Undulator
27
28
  from dodal.devices.xbpm_feedback import XBPMFeedback
28
- from dodal.devices.zebra import Zebra
29
- from dodal.devices.zebra_controlled_shutter import ZebraShutter
29
+ from dodal.devices.zebra.zebra import Zebra
30
+ from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
30
31
  from dodal.devices.zocalo import ZocaloResults
31
32
  from ophyd_async.fastcs.panda import HDFPanda
32
33
 
34
+ from mx_bluesky.common.external_interaction.callbacks.common.grid_detection_callback import (
35
+ GridDetectionCallback,
36
+ GridParamUpdate,
37
+ )
38
+ from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback import (
39
+ ispyb_activation_wrapper,
40
+ )
33
41
  from mx_bluesky.common.parameters.constants import OavConstants
34
- from mx_bluesky.common.parameters.gridscan import GridScanWithEdgeDetect
42
+ from mx_bluesky.common.utils.log import LOGGER
35
43
  from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import (
36
44
  move_aperture_if_required,
37
45
  )
@@ -52,17 +60,10 @@ from mx_bluesky.hyperion.experiment_plans.oav_grid_detection_plan import (
52
60
  OavGridDetectionComposite,
53
61
  grid_detection_plan,
54
62
  )
55
- from mx_bluesky.hyperion.external_interaction.callbacks.grid_detection_callback import (
56
- GridDetectionCallback,
57
- GridParamUpdate,
58
- )
59
- from mx_bluesky.hyperion.external_interaction.callbacks.xray_centre.ispyb_callback import (
60
- ispyb_activation_wrapper,
61
- )
62
- from mx_bluesky.hyperion.log import LOGGER
63
63
  from mx_bluesky.hyperion.parameters.constants import CONST
64
64
  from mx_bluesky.hyperion.parameters.gridscan import (
65
- HyperionThreeDGridScan,
65
+ GridScanWithEdgeDetect,
66
+ HyperionSpecifiedThreeDGridScan,
66
67
  )
67
68
  from mx_bluesky.hyperion.utils.context import device_composite_from_context
68
69
 
@@ -72,8 +73,9 @@ class GridDetectThenXRayCentreComposite:
72
73
  """All devices which are directly or indirectly required by this plan"""
73
74
 
74
75
  aperture_scatterguard: ApertureScatterguard
75
- attenuator: Attenuator
76
+ attenuator: BinaryFilterAttenuator
76
77
  backlight: Backlight
78
+ beamstop: Beamstop
77
79
  dcm: DCM
78
80
  detector_motion: DetectorMotion
79
81
  eiger: EigerDetector
@@ -93,10 +95,6 @@ class GridDetectThenXRayCentreComposite:
93
95
  robot: BartRobot
94
96
  sample_shutter: ZebraShutter
95
97
 
96
- @property
97
- def sample_motors(self):
98
- return self.smargon
99
-
100
98
 
101
99
  def create_devices(context: BlueskyContext) -> GridDetectThenXRayCentreComposite:
102
100
  return device_composite_from_context(context, GridDetectThenXRayCentreComposite)
@@ -105,10 +103,10 @@ def create_devices(context: BlueskyContext) -> GridDetectThenXRayCentreComposite
105
103
  def create_parameters_for_flyscan_xray_centre(
106
104
  grid_scan_with_edge_params: GridScanWithEdgeDetect,
107
105
  grid_parameters: GridParamUpdate,
108
- ) -> HyperionThreeDGridScan:
106
+ ) -> HyperionSpecifiedThreeDGridScan:
109
107
  params_json = grid_scan_with_edge_params.model_dump()
110
108
  params_json.update(grid_parameters)
111
- flyscan_xray_centre_parameters = HyperionThreeDGridScan(**params_json)
109
+ flyscan_xray_centre_parameters = HyperionSpecifiedThreeDGridScan(**params_json)
112
110
  LOGGER.info(f"Parameters for FGS: {flyscan_xray_centre_parameters}")
113
111
  return flyscan_xray_centre_parameters
114
112
 
@@ -144,14 +142,6 @@ def detect_grid_and_do_gridscan(
144
142
  parameters.box_size_um,
145
143
  )
146
144
 
147
- if parameters.selected_aperture:
148
- # Start moving the aperture/scatterguard into position without moving it in
149
- yield from bps.abs_set(
150
- composite.aperture_scatterguard.aperture_outside_beam,
151
- parameters.selected_aperture,
152
- group=CONST.WAIT.GRID_READY_FOR_DC,
153
- )
154
-
155
145
  yield from run_grid_detection_plan(
156
146
  oav_params,
157
147
  snapshot_template,
@@ -225,6 +215,7 @@ def grid_detect_then_xray_centre(
225
215
  )
226
216
 
227
217
  yield from start_preparing_data_collection_then_do_plan(
218
+ composite.beamstop,
228
219
  eiger,
229
220
  composite.detector_motion,
230
221
  parameters.detector_params.detector_distance,
@@ -232,9 +223,9 @@ def grid_detect_then_xray_centre(
232
223
  group=CONST.WAIT.GRID_READY_FOR_DC,
233
224
  )
234
225
 
235
- assert (
236
- flyscan_event_handler.xray_centre_results
237
- ), "Flyscan result event not received or no crystal found and exception not raised"
226
+ assert flyscan_event_handler.xray_centre_results, (
227
+ "Flyscan result event not received or no crystal found and exception not raised"
228
+ )
238
229
 
239
230
  yield from change_aperture_then_move_to_xtal(
240
231
  flyscan_event_handler.xray_centre_results[0],
@@ -1,12 +1,15 @@
1
+ from __future__ import annotations
2
+
1
3
  from collections.abc import Sequence
2
4
 
3
5
  import pydantic
4
- from blueapi.core import BlueskyContext, MsgGenerator
5
- from bluesky.preprocessors import subs_wrapper
6
+ from blueapi.core import BlueskyContext
7
+ from bluesky.preprocessors import run_decorator, set_run_key_decorator, subs_wrapper
8
+ from bluesky.utils import MsgGenerator
6
9
  from dodal.devices.oav.oav_parameters import OAVParameters
7
- from dodal.devices.smargon import Smargon
8
10
 
9
11
  import mx_bluesky.hyperion.experiment_plans.common.xrc_result as flyscan_result
12
+ from mx_bluesky.common.utils.log import LOGGER
10
13
  from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
11
14
  XRayCentreEventHandler,
12
15
  )
@@ -19,7 +22,7 @@ from mx_bluesky.hyperion.experiment_plans.rotation_scan_plan import (
19
22
  RotationScanComposite,
20
23
  multi_rotation_scan,
21
24
  )
22
- from mx_bluesky.hyperion.log import LOGGER
25
+ from mx_bluesky.hyperion.parameters.constants import CONST
23
26
  from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect
24
27
  from mx_bluesky.hyperion.utils.context import device_composite_from_context
25
28
 
@@ -28,10 +31,6 @@ from mx_bluesky.hyperion.utils.context import device_composite_from_context
28
31
  class LoadCentreCollectComposite(RobotLoadThenCentreComposite, RotationScanComposite):
29
32
  """Composite that provides access to the required devices."""
30
33
 
31
- @property
32
- def sample_motors(self) -> Smargon:
33
- return self.smargon
34
-
35
34
 
36
35
  def create_devices(context: BlueskyContext) -> LoadCentreCollectComposite:
37
36
  """Create the necessary devices for the plan."""
@@ -53,36 +52,54 @@ def load_centre_collect_full(
53
52
  if not oav_params:
54
53
  oav_params = OAVParameters(context="xrayCentring")
55
54
 
56
- flyscan_event_handler = XRayCentreEventHandler()
57
- yield from subs_wrapper(
58
- robot_load_then_xray_centre(composite, parameters.robot_load_then_centre),
59
- flyscan_event_handler,
60
- )
61
-
62
- assert (
63
- flyscan_event_handler.xray_centre_results
64
- ), "Flyscan result event not received or no crystal found and exception not raised"
65
-
66
- selection_func = flyscan_result.resolve_selection_fn(parameters.selection_params)
67
- hits: Sequence[flyscan_result.XRayCentreResult] = selection_func(
68
- flyscan_event_handler.xray_centre_results
55
+ @set_run_key_decorator(CONST.PLAN.LOAD_CENTRE_COLLECT)
56
+ @run_decorator(
57
+ md={
58
+ "metadata": {"sample_id": parameters.sample_id},
59
+ "activate_callbacks": ["SampleHandlingCallback"],
60
+ }
69
61
  )
70
- LOGGER.info(
71
- f"Selected hits {hits} using {selection_func}, args={parameters.selection_params}"
72
- )
73
-
74
- multi_rotation = parameters.multi_rotation_scan
75
- rotation_template = multi_rotation.rotation_scans.copy()
76
-
77
- multi_rotation.rotation_scans.clear()
78
-
79
- for hit in hits:
80
- for rot in rotation_template:
81
- combination = rot.model_copy()
82
- combination.x_start_um, combination.y_start_um, combination.z_start_um = (
83
- axis * 1000 for axis in hit.centre_of_mass_mm
84
- )
85
- multi_rotation.rotation_scans.append(combination)
86
- multi_rotation = MultiRotationScan.model_validate(multi_rotation)
87
-
88
- yield from multi_rotation_scan(composite, multi_rotation, oav_params)
62
+ def plan_with_callback_subs():
63
+ flyscan_event_handler = XRayCentreEventHandler()
64
+ yield from subs_wrapper(
65
+ robot_load_then_xray_centre(composite, parameters.robot_load_then_centre),
66
+ flyscan_event_handler,
67
+ )
68
+
69
+ assert flyscan_event_handler.xray_centre_results, (
70
+ "Flyscan result event not received or no crystal found and exception not raised"
71
+ )
72
+
73
+ selection_func = flyscan_result.resolve_selection_fn(
74
+ parameters.selection_params
75
+ )
76
+ hits: Sequence[flyscan_result.XRayCentreResult] = selection_func(
77
+ flyscan_event_handler.xray_centre_results
78
+ )
79
+ LOGGER.info(
80
+ f"Selected hits {hits} using {selection_func}, args={parameters.selection_params}"
81
+ )
82
+
83
+ multi_rotation = parameters.multi_rotation_scan
84
+ rotation_template = multi_rotation.rotation_scans.copy()
85
+
86
+ multi_rotation.rotation_scans.clear()
87
+
88
+ for hit in hits:
89
+ for rot in rotation_template:
90
+ combination = rot.model_copy()
91
+ (
92
+ combination.x_start_um,
93
+ combination.y_start_um,
94
+ combination.z_start_um,
95
+ ) = (axis * 1000 for axis in hit.centre_of_mass_mm)
96
+ multi_rotation.rotation_scans.append(combination)
97
+ multi_rotation = MultiRotationScan.model_validate(multi_rotation)
98
+
99
+ assert (
100
+ multi_rotation.demand_energy_ev
101
+ == parameters.robot_load_then_centre.demand_energy_ev
102
+ ), "Setting a different energy for gridscan and rotation is not supported"
103
+ yield from multi_rotation_scan(composite, multi_rotation, oav_params)
104
+
105
+ yield from plan_with_callback_subs()
@@ -14,11 +14,11 @@ 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.utils.exceptions import catch_exception_and_warn
18
+ from mx_bluesky.common.utils.log import LOGGER
17
19
  from mx_bluesky.hyperion.device_setup_plans.setup_oav import (
18
20
  pre_centring_setup_oav,
19
21
  )
20
- from mx_bluesky.hyperion.exceptions import catch_exception_and_warn
21
- from mx_bluesky.hyperion.log import LOGGER
22
22
  from mx_bluesky.hyperion.parameters.constants import CONST
23
23
  from mx_bluesky.hyperion.utils.context import device_composite_from_context
24
24
 
@@ -139,7 +139,7 @@ def grid_detection_plan(
139
139
  # See https://github.com/DiamondLightSource/hyperion/wiki/PandA-constant%E2%80%90motion-scanning#motion-program-summary
140
140
  if y_steps % 2 and angle == 0:
141
141
  LOGGER.debug(
142
- f"Forcing number of rows in first grid to be even: Adding an extra row onto bottom of first grid and shifting grid upwards by {box_size_y_pixels/2}"
142
+ f"Forcing number of rows in first grid to be even: Adding an extra row onto bottom of first grid and shifting grid upwards by {box_size_y_pixels / 2}"
143
143
  )
144
144
  y_steps += 1
145
145
  min_y -= box_size_y_pixels / 2
@@ -3,7 +3,7 @@ from typing import Protocol
3
3
 
4
4
  from bluesky import plan_stubs as bps
5
5
  from bluesky.utils import MsgGenerator
6
- from dodal.devices.aperturescatterguard import ApertureScatterguard
6
+ from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureValue
7
7
  from dodal.devices.backlight import Backlight, BacklightPosition
8
8
  from dodal.devices.oav.oav_detector import OAV
9
9
  from dodal.devices.oav.oav_parameters import OAVParameters
@@ -33,16 +33,22 @@ def setup_beamline_for_OAV(
33
33
  max_vel = yield from bps.rd(smargon.omega.max_velocity)
34
34
  yield from bps.abs_set(smargon.omega.velocity, max_vel, group=group)
35
35
  yield from bps.abs_set(backlight, BacklightPosition.IN, group=group)
36
- yield from bps.trigger(aperture_scatterguard.move_out, group=group)
36
+ yield from bps.abs_set(
37
+ aperture_scatterguard,
38
+ ApertureValue.ROBOT_LOAD,
39
+ group=group,
40
+ )
37
41
 
38
42
 
39
43
  def oav_snapshot_plan(
40
44
  composite: OavSnapshotComposite,
41
45
  parameters: WithSnapshot,
42
46
  oav_parameters: OAVParameters,
47
+ wait: bool = True,
43
48
  ) -> MsgGenerator:
44
49
  if not parameters.take_snapshots:
45
50
  return
51
+ yield from bps.wait(group=CONST.WAIT.READY_FOR_OAV)
46
52
  yield from _setup_oav(composite, parameters, oav_parameters)
47
53
  for omega in parameters.snapshot_omegas_deg or []:
48
54
  yield from _take_oav_snapshot(composite, omega)