mx-bluesky 1.4.0__py3-none-any.whl → 1.4.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) 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/i04/thawing_plan.py +1 -1
  4. mx_bluesky/beamlines/i24/serial/dcid.py +143 -171
  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 +54 -21
  7. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +2 -5
  8. mx_bluesky/beamlines/i24/serial/fixed_target/ft_utils.py +0 -1
  9. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +67 -50
  10. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +26 -79
  11. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +0 -199
  12. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +4 -6
  13. mx_bluesky/beamlines/i24/serial/log.py +1 -1
  14. mx_bluesky/beamlines/i24/serial/parameters/__init__.py +4 -0
  15. mx_bluesky/beamlines/i24/serial/parameters/constants.py +6 -1
  16. mx_bluesky/beamlines/i24/serial/parameters/experiment_parameters.py +42 -15
  17. mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +4 -3
  18. mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +2 -0
  19. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +103 -81
  20. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +1 -2
  21. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +24 -26
  22. mx_bluesky/beamlines/i24/serial/write_nexus.py +74 -72
  23. mx_bluesky/common/external_interaction/config_server.py +46 -0
  24. mx_bluesky/common/parameters/components.py +52 -15
  25. mx_bluesky/common/parameters/constants.py +11 -1
  26. mx_bluesky/common/parameters/gridscan.py +94 -0
  27. mx_bluesky/{hyperion → common}/parameters/robot_load.py +2 -2
  28. mx_bluesky/common/plans/do_fgs.py +2 -2
  29. mx_bluesky/common/utils/log.py +2 -0
  30. mx_bluesky/hyperion/__main__.py +2 -1
  31. mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +21 -31
  32. mx_bluesky/hyperion/device_setup_plans/setup_panda.py +4 -4
  33. mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +1 -1
  34. mx_bluesky/hyperion/device_setup_plans/smargon.py +3 -3
  35. mx_bluesky/hyperion/exceptions.py +13 -1
  36. mx_bluesky/hyperion/experiment_plans/__init__.py +4 -0
  37. mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +83 -0
  38. mx_bluesky/hyperion/experiment_plans/common/xrc_result.py +47 -0
  39. mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -9
  40. mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +133 -97
  41. mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +42 -18
  42. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +75 -9
  43. mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +2 -2
  44. mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +1 -1
  45. mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +2 -2
  46. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +36 -17
  47. mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +5 -5
  48. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +28 -28
  49. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +64 -16
  50. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +11 -3
  51. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +10 -10
  52. mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +0 -4
  53. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +4 -0
  54. mx_bluesky/hyperion/external_interaction/callbacks/common/abstract_event.py +66 -0
  55. mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +5 -0
  56. mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +15 -15
  57. mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +18 -10
  58. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +3 -1
  59. mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +5 -3
  60. mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/__init__.py +0 -0
  61. mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/sample_handling_callback.py +84 -0
  62. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +15 -9
  63. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +5 -4
  64. mx_bluesky/hyperion/external_interaction/config_server.py +8 -37
  65. mx_bluesky/hyperion/external_interaction/exceptions.py +0 -9
  66. mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +65 -15
  67. mx_bluesky/hyperion/parameters/components.py +4 -9
  68. mx_bluesky/hyperion/parameters/constants.py +0 -1
  69. mx_bluesky/hyperion/parameters/gridscan.py +33 -76
  70. mx_bluesky/hyperion/parameters/load_centre_collect.py +14 -9
  71. mx_bluesky/hyperion/parameters/rotation.py +15 -6
  72. {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1.dist-info}/METADATA +35 -34
  73. {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1.dist-info}/RECORD +77 -70
  74. {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1.dist-info}/WHEEL +1 -1
  75. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +0 -150
  76. {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1.dist-info}/LICENSE +0 -0
  77. {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1.dist-info}/entry_points.txt +0 -0
  78. {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,13 @@
1
1
  from __future__ import annotations
2
2
 
3
- import dataclasses
4
3
  from pathlib import Path
5
4
 
6
- from blueapi.core import BlueskyContext, MsgGenerator
5
+ import pydantic
6
+ from blueapi.core import BlueskyContext
7
7
  from bluesky import plan_stubs as bps
8
8
  from bluesky import preprocessors as bpp
9
+ from bluesky.preprocessors import subs_decorator
10
+ from bluesky.utils import MsgGenerator
9
11
  from dodal.devices.aperturescatterguard import ApertureScatterguard
10
12
  from dodal.devices.attenuator import Attenuator
11
13
  from dodal.devices.backlight import Backlight, BacklightPosition
@@ -29,17 +31,22 @@ from dodal.devices.zocalo import ZocaloResults
29
31
  from ophyd_async.fastcs.panda import HDFPanda
30
32
 
31
33
  from mx_bluesky.common.parameters.constants import OavConstants
34
+ from mx_bluesky.common.parameters.gridscan import GridScanWithEdgeDetect
32
35
  from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import (
33
36
  move_aperture_if_required,
34
37
  )
35
38
  from mx_bluesky.hyperion.device_setup_plans.utils import (
36
39
  start_preparing_data_collection_then_do_plan,
37
40
  )
41
+ from mx_bluesky.hyperion.experiment_plans.change_aperture_then_move_plan import (
42
+ change_aperture_then_move_to_xtal,
43
+ )
38
44
  from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
39
45
  FlyScanXRayCentreComposite as FlyScanXRayCentreComposite,
40
46
  )
41
47
  from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
42
- flyscan_xray_centre,
48
+ XRayCentreEventHandler,
49
+ flyscan_xray_centre_no_move,
43
50
  )
44
51
  from mx_bluesky.hyperion.experiment_plans.oav_grid_detection_plan import (
45
52
  OavGridDetectionComposite,
@@ -55,13 +62,12 @@ from mx_bluesky.hyperion.external_interaction.callbacks.xray_centre.ispyb_callba
55
62
  from mx_bluesky.hyperion.log import LOGGER
56
63
  from mx_bluesky.hyperion.parameters.constants import CONST
57
64
  from mx_bluesky.hyperion.parameters.gridscan import (
58
- GridScanWithEdgeDetect,
59
- ThreeDGridScan,
65
+ HyperionThreeDGridScan,
60
66
  )
61
67
  from mx_bluesky.hyperion.utils.context import device_composite_from_context
62
68
 
63
69
 
64
- @dataclasses.dataclass
70
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
65
71
  class GridDetectThenXRayCentreComposite:
66
72
  """All devices which are directly or indirectly required by this plan"""
67
73
 
@@ -87,6 +93,10 @@ class GridDetectThenXRayCentreComposite:
87
93
  robot: BartRobot
88
94
  sample_shutter: ZebraShutter
89
95
 
96
+ @property
97
+ def sample_motors(self):
98
+ return self.smargon
99
+
90
100
 
91
101
  def create_devices(context: BlueskyContext) -> GridDetectThenXRayCentreComposite:
92
102
  return device_composite_from_context(context, GridDetectThenXRayCentreComposite)
@@ -95,10 +105,10 @@ def create_devices(context: BlueskyContext) -> GridDetectThenXRayCentreComposite
95
105
  def create_parameters_for_flyscan_xray_centre(
96
106
  grid_scan_with_edge_params: GridScanWithEdgeDetect,
97
107
  grid_parameters: GridParamUpdate,
98
- ) -> ThreeDGridScan:
108
+ ) -> HyperionThreeDGridScan:
99
109
  params_json = grid_scan_with_edge_params.model_dump()
100
110
  params_json.update(grid_parameters)
101
- flyscan_xray_centre_parameters = ThreeDGridScan(**params_json)
111
+ flyscan_xray_centre_parameters = HyperionThreeDGridScan(**params_json)
102
112
  LOGGER.info(f"Parameters for FGS: {flyscan_xray_centre_parameters}")
103
113
  return flyscan_xray_centre_parameters
104
114
 
@@ -150,7 +160,7 @@ def detect_grid_and_do_gridscan(
150
160
  group=CONST.WAIT.GRID_READY_FOR_DC,
151
161
  )
152
162
 
153
- yield from flyscan_xray_centre(
163
+ yield from flyscan_xray_centre_no_move(
154
164
  FlyScanXRayCentreComposite(
155
165
  aperture_scatterguard=composite.aperture_scatterguard,
156
166
  attenuator=composite.attenuator,
@@ -193,19 +203,33 @@ def grid_detect_then_xray_centre(
193
203
 
194
204
  oav_params = OAVParameters("xrayCentring", oav_config)
195
205
 
196
- plan_to_perform = ispyb_activation_wrapper(
197
- detect_grid_and_do_gridscan(
198
- composite,
206
+ flyscan_event_handler = XRayCentreEventHandler()
207
+
208
+ @subs_decorator(flyscan_event_handler)
209
+ def plan_to_perform():
210
+ yield from ispyb_activation_wrapper(
211
+ detect_grid_and_do_gridscan(
212
+ composite,
213
+ parameters,
214
+ oav_params,
215
+ ),
199
216
  parameters,
200
- oav_params,
201
- ),
202
- parameters,
203
- )
217
+ )
204
218
 
205
- return start_preparing_data_collection_then_do_plan(
219
+ yield from start_preparing_data_collection_then_do_plan(
206
220
  eiger,
207
221
  composite.detector_motion,
208
222
  parameters.detector_params.detector_distance,
209
- plan_to_perform, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809 and MsgGenerator should allow for return values
223
+ plan_to_perform(),
210
224
  group=CONST.WAIT.GRID_READY_FOR_DC,
211
225
  )
226
+
227
+ assert (
228
+ flyscan_event_handler.xray_centre_results
229
+ ), "Flyscan result event not received or no crystal found and exception not raised"
230
+
231
+ yield from change_aperture_then_move_to_xtal(
232
+ flyscan_event_handler.xray_centre_results[0],
233
+ composite.smargon,
234
+ composite.aperture_scatterguard,
235
+ )
@@ -1,25 +1,43 @@
1
- import dataclasses
1
+ from __future__ import annotations
2
2
 
3
+ from collections.abc import Sequence
4
+
5
+ import pydantic
3
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
4
9
  from dodal.devices.oav.oav_parameters import OAVParameters
10
+ from dodal.devices.smargon import Smargon
5
11
 
12
+ import mx_bluesky.hyperion.experiment_plans.common.xrc_result as flyscan_result
13
+ from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
14
+ XRayCentreEventHandler,
15
+ )
6
16
  from mx_bluesky.hyperion.experiment_plans.robot_load_then_centre_plan import (
7
17
  RobotLoadThenCentreComposite,
8
- robot_load_then_centre,
18
+ robot_load_then_xray_centre,
9
19
  )
10
20
  from mx_bluesky.hyperion.experiment_plans.rotation_scan_plan import (
21
+ MultiRotationScan,
11
22
  RotationScanComposite,
12
23
  multi_rotation_scan,
13
24
  )
25
+ from mx_bluesky.hyperion.external_interaction.callbacks.sample_handling.sample_handling_callback import (
26
+ sample_handling_callback_decorator,
27
+ )
28
+ from mx_bluesky.hyperion.log import LOGGER
29
+ from mx_bluesky.hyperion.parameters.constants import CONST
14
30
  from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect
15
31
  from mx_bluesky.hyperion.utils.context import device_composite_from_context
16
32
 
17
33
 
18
- @dataclasses.dataclass
34
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
19
35
  class LoadCentreCollectComposite(RobotLoadThenCentreComposite, RotationScanComposite):
20
36
  """Composite that provides access to the required devices."""
21
37
 
22
- pass
38
+ @property
39
+ def sample_motors(self) -> Smargon:
40
+ return self.smargon
23
41
 
24
42
 
25
43
  def create_devices(context: BlueskyContext) -> LoadCentreCollectComposite:
@@ -27,11 +45,11 @@ def create_devices(context: BlueskyContext) -> LoadCentreCollectComposite:
27
45
  return device_composite_from_context(context, LoadCentreCollectComposite)
28
46
 
29
47
 
30
- def load_centre_collect_full_plan(
48
+ def load_centre_collect_full(
31
49
  composite: LoadCentreCollectComposite,
32
- params: LoadCentreCollect,
50
+ parameters: LoadCentreCollect,
33
51
  oav_params: OAVParameters | None = None,
34
- ):
52
+ ) -> MsgGenerator:
35
53
  """Attempt a complete data collection experiment, consisting of the following:
36
54
  * Load the sample if necessary
37
55
  * Move to the specified goniometer start angles
@@ -41,6 +59,54 @@ def load_centre_collect_full_plan(
41
59
  """
42
60
  if not oav_params:
43
61
  oav_params = OAVParameters(context="xrayCentring")
44
- yield from robot_load_then_centre(composite, params.robot_load_then_centre)
45
62
 
46
- yield from multi_rotation_scan(composite, params.multi_rotation_scan, oav_params)
63
+ @set_run_key_decorator(CONST.PLAN.LOAD_CENTRE_COLLECT)
64
+ @run_decorator(
65
+ md={
66
+ "metadata": {"sample_id": parameters.sample_id},
67
+ "activate_callbacks": ["SampleHandlingCallback"],
68
+ }
69
+ )
70
+ @sample_handling_callback_decorator()
71
+ def plan_with_callback_subs():
72
+ flyscan_event_handler = XRayCentreEventHandler()
73
+ yield from subs_wrapper(
74
+ robot_load_then_xray_centre(composite, parameters.robot_load_then_centre),
75
+ flyscan_event_handler,
76
+ )
77
+
78
+ assert flyscan_event_handler.xray_centre_results, "Flyscan result event not received or no crystal found and exception not raised"
79
+
80
+ selection_func = flyscan_result.resolve_selection_fn(
81
+ parameters.selection_params
82
+ )
83
+ hits: Sequence[flyscan_result.XRayCentreResult] = selection_func(
84
+ flyscan_event_handler.xray_centre_results
85
+ )
86
+ LOGGER.info(
87
+ f"Selected hits {hits} using {selection_func}, args={parameters.selection_params}"
88
+ )
89
+
90
+ multi_rotation = parameters.multi_rotation_scan
91
+ rotation_template = multi_rotation.rotation_scans.copy()
92
+
93
+ multi_rotation.rotation_scans.clear()
94
+
95
+ for hit in hits:
96
+ for rot in rotation_template:
97
+ combination = rot.model_copy()
98
+ (
99
+ combination.x_start_um,
100
+ combination.y_start_um,
101
+ combination.z_start_um,
102
+ ) = (axis * 1000 for axis in hit.centre_of_mass_mm)
103
+ multi_rotation.rotation_scans.append(combination)
104
+ multi_rotation = MultiRotationScan.model_validate(multi_rotation)
105
+
106
+ assert (
107
+ multi_rotation.demand_energy_ev
108
+ == parameters.robot_load_then_centre.demand_energy_ev
109
+ ), "Setting a different energy for gridscan and rotation is not supported"
110
+ yield from multi_rotation_scan(composite, multi_rotation, oav_params)
111
+
112
+ yield from plan_with_callback_subs()
@@ -1,11 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
- import dataclasses
4
3
  import math
5
4
  from typing import TYPE_CHECKING
6
5
 
7
6
  import bluesky.plan_stubs as bps
8
7
  import numpy as np
8
+ import pydantic
9
9
  from blueapi.core import BlueskyContext
10
10
  from dodal.devices.backlight import Backlight
11
11
  from dodal.devices.oav.oav_detector import OAV
@@ -26,7 +26,7 @@ if TYPE_CHECKING:
26
26
  from dodal.devices.oav.oav_parameters import OAVParameters
27
27
 
28
28
 
29
- @dataclasses.dataclass
29
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
30
30
  class OavGridDetectionComposite:
31
31
  """All devices which are directly or indirectly required by this plan"""
32
32
 
@@ -1,8 +1,8 @@
1
1
  from datetime import datetime
2
2
  from typing import Protocol
3
3
 
4
- from blueapi.core import MsgGenerator
5
4
  from bluesky import plan_stubs as bps
5
+ from bluesky.utils import MsgGenerator
6
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
@@ -1,9 +1,9 @@
1
- import dataclasses
2
1
  from enum import Enum
3
2
 
4
3
  import bluesky.plan_stubs as bps
5
4
  import bluesky.preprocessors as bpp
6
5
  import numpy as np
6
+ import pydantic
7
7
  from blueapi.core import BlueskyContext
8
8
  from dodal.devices.attenuator import Attenuator
9
9
  from dodal.devices.xspress3.xspress3 import Xspress3
@@ -22,7 +22,7 @@ class Direction(Enum):
22
22
  NEGATIVE = "negative"
23
23
 
24
24
 
25
- @dataclasses.dataclass
25
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
26
26
  class OptimizeAttenuationComposite:
27
27
  """All devices which are directly or indirectly required by this plan"""
28
28
 
@@ -2,15 +2,27 @@ from __future__ import annotations
2
2
 
3
3
  import json
4
4
 
5
- from blueapi.core import BlueskyContext, MsgGenerator
5
+ import bluesky.preprocessors as bpp
6
+ from blueapi.core import BlueskyContext
7
+ from bluesky.utils import MsgGenerator
6
8
  from dodal.devices.eiger import EigerDetector
7
9
  from dodal.devices.oav.oav_parameters import OAVParameters
8
10
 
9
11
  from mx_bluesky.common.parameters.constants import OavConstants
12
+ from mx_bluesky.common.parameters.gridscan import (
13
+ GridScanWithEdgeDetect,
14
+ PinTipCentreThenXrayCentre,
15
+ )
10
16
  from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import move_phi_chi_omega
11
17
  from mx_bluesky.hyperion.device_setup_plans.utils import (
12
18
  start_preparing_data_collection_then_do_plan,
13
19
  )
20
+ from mx_bluesky.hyperion.experiment_plans.change_aperture_then_move_plan import (
21
+ change_aperture_then_move_to_xtal,
22
+ )
23
+ from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
24
+ XRayCentreEventHandler,
25
+ )
14
26
  from mx_bluesky.hyperion.experiment_plans.grid_detect_then_xray_centre_plan import (
15
27
  GridDetectThenXRayCentreComposite,
16
28
  detect_grid_and_do_gridscan,
@@ -27,10 +39,6 @@ from mx_bluesky.hyperion.external_interaction.callbacks.xray_centre.ispyb_callba
27
39
  )
28
40
  from mx_bluesky.hyperion.log import LOGGER
29
41
  from mx_bluesky.hyperion.parameters.constants import CONST
30
- from mx_bluesky.hyperion.parameters.gridscan import (
31
- GridScanWithEdgeDetect,
32
- PinTipCentreThenXrayCentre,
33
- )
34
42
  from mx_bluesky.hyperion.utils.context import device_composite_from_context
35
43
 
36
44
 
@@ -53,13 +61,12 @@ def create_parameters_for_grid_detection(
53
61
  return grid_detect_and_xray_centre
54
62
 
55
63
 
56
- def pin_centre_then_xray_centre_plan(
64
+ def pin_centre_then_flyscan_plan(
57
65
  composite: GridDetectThenXRayCentreComposite,
58
66
  parameters: PinTipCentreThenXrayCentre,
59
67
  oav_config_file: str = OavConstants.OAV_CONFIG_JSON,
60
68
  ):
61
- """Plan that perfoms a pin tip centre followed by an xray centre to completely
62
- centre the sample"""
69
+ """Plan that performs a pin tip centre followed by a flyscan to determine the centres of interest"""
63
70
 
64
71
  pin_tip_centring_composite = PinTipCentringComposite(
65
72
  oav=composite.oav,
@@ -68,7 +75,7 @@ def pin_centre_then_xray_centre_plan(
68
75
  pin_tip_detection=composite.pin_tip_detection,
69
76
  )
70
77
 
71
- def _pin_centre_then_xray_centre_plan():
78
+ def _pin_centre_then_flyscan_plan():
72
79
  yield from setup_beamline_for_OAV(
73
80
  composite.smargon, composite.backlight, composite.aperture_scatterguard
74
81
  )
@@ -96,7 +103,7 @@ def pin_centre_then_xray_centre_plan(
96
103
  oav_params,
97
104
  )
98
105
 
99
- yield from ispyb_activation_wrapper(_pin_centre_then_xray_centre_plan(), parameters)
106
+ yield from ispyb_activation_wrapper(_pin_centre_then_flyscan_plan(), parameters)
100
107
 
101
108
 
102
109
  def pin_tip_centre_then_xray_centre(
@@ -105,15 +112,27 @@ def pin_tip_centre_then_xray_centre(
105
112
  oav_config_file: str = OavConstants.OAV_CONFIG_JSON,
106
113
  ) -> MsgGenerator:
107
114
  """Starts preparing for collection then performs the pin tip centre and xray centre"""
108
-
109
115
  eiger: EigerDetector = composite.eiger
110
116
 
111
117
  eiger.set_detector_parameters(parameters.detector_params)
112
118
 
113
- return start_preparing_data_collection_then_do_plan(
114
- eiger,
115
- composite.detector_motion,
116
- parameters.detector_params.detector_distance,
117
- pin_centre_then_xray_centre_plan(composite, parameters, oav_config_file),
118
- group=CONST.WAIT.GRID_READY_FOR_DC,
119
+ flyscan_event_handler = XRayCentreEventHandler()
120
+
121
+ @bpp.subs_decorator(flyscan_event_handler)
122
+ def pin_centre_flyscan_then_fetch_results() -> MsgGenerator:
123
+ yield from start_preparing_data_collection_then_do_plan(
124
+ eiger,
125
+ composite.detector_motion,
126
+ parameters.detector_params.detector_distance,
127
+ pin_centre_then_flyscan_plan(composite, parameters, oav_config_file),
128
+ group=CONST.WAIT.GRID_READY_FOR_DC,
129
+ )
130
+
131
+ yield from pin_centre_flyscan_then_fetch_results()
132
+ flyscan_results = flyscan_event_handler.xray_centre_results
133
+ assert (
134
+ flyscan_results
135
+ ), "Flyscan result event not received or no crystal found and exception not raised"
136
+ yield from change_aperture_then_move_to_xtal(
137
+ flyscan_results[0], composite.smargon, composite.aperture_scatterguard
119
138
  )
@@ -1,7 +1,7 @@
1
- import dataclasses
2
1
  from collections.abc import Generator
3
2
 
4
3
  import bluesky.plan_stubs as bps
4
+ import pydantic
5
5
  from blueapi.core import BlueskyContext
6
6
  from bluesky.utils import Msg
7
7
  from dodal.devices.backlight import Backlight
@@ -19,7 +19,7 @@ from mx_bluesky.hyperion.device_setup_plans.setup_oav import pre_centring_setup_
19
19
  from mx_bluesky.hyperion.device_setup_plans.smargon import (
20
20
  move_smargon_warn_on_out_of_range,
21
21
  )
22
- from mx_bluesky.hyperion.exceptions import WarningException
22
+ from mx_bluesky.hyperion.exceptions import SampleException
23
23
  from mx_bluesky.hyperion.log import LOGGER
24
24
  from mx_bluesky.hyperion.parameters.constants import CONST
25
25
  from mx_bluesky.hyperion.utils.context import device_composite_from_context
@@ -27,7 +27,7 @@ from mx_bluesky.hyperion.utils.context import device_composite_from_context
27
27
  DEFAULT_STEP_SIZE = 0.5
28
28
 
29
29
 
30
- @dataclasses.dataclass
30
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
31
31
  class PinTipCentringComposite:
32
32
  """All devices which are directly or indirectly required by this plan"""
33
33
 
@@ -68,7 +68,7 @@ def move_pin_into_view(
68
68
  max_steps (int, optional): The number of steps to search with. Defaults to 2.
69
69
 
70
70
  Raises:
71
- WarningException: Error if the pin tip is never found
71
+ SampleException: Error if the pin tip is never found
72
72
 
73
73
  Returns:
74
74
  Tuple[int, int]: The location of the pin tip in pixels
@@ -105,7 +105,7 @@ def move_pin_into_view(
105
105
  tip_xy_px = yield from trigger_and_return_pin_tip(pin_tip_device)
106
106
 
107
107
  if not pin_tip_valid(tip_xy_px):
108
- raise WarningException(
108
+ raise SampleException(
109
109
  "Pin tip centring failed - pin too long/short/bent and out of range"
110
110
  )
111
111
  else:
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import dataclasses
4
3
  from collections.abc import Generator
5
4
  from datetime import datetime
6
5
  from pathlib import Path
@@ -8,6 +7,7 @@ from typing import cast
8
7
 
9
8
  import bluesky.plan_stubs as bps
10
9
  import bluesky.preprocessors as bpp
10
+ import pydantic
11
11
  from blueapi.core import BlueskyContext
12
12
  from bluesky.utils import Msg
13
13
  from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureValue
@@ -22,18 +22,18 @@ from dodal.devices.thawer import Thawer
22
22
  from dodal.devices.undulator_dcm import UndulatorDCM
23
23
  from dodal.devices.webcam import Webcam
24
24
  from dodal.devices.xbpm_feedback import XBPMFeedback
25
- from dodal.plans.motor_util_plans import MoveTooLarge, home_and_reset_wrapper
25
+ from dodal.plan_stubs.motor_utils import MoveTooLarge, home_and_reset_wrapper
26
26
 
27
+ from mx_bluesky.common.parameters.robot_load import RobotLoadAndEnergyChange
27
28
  from mx_bluesky.hyperion.experiment_plans.set_energy_plan import (
28
29
  SetEnergyComposite,
29
30
  set_energy_plan,
30
31
  )
31
32
  from mx_bluesky.hyperion.log import LOGGER
32
33
  from mx_bluesky.hyperion.parameters.constants import CONST
33
- from mx_bluesky.hyperion.parameters.robot_load import RobotLoadAndEnergyChange
34
34
 
35
35
 
36
- @dataclasses.dataclass
36
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
37
37
  class RobotLoadAndEnergyChangeComposite:
38
38
  # SetEnergyComposite fields
39
39
  vfm: FocusingMirrorWithStripes
@@ -128,11 +128,7 @@ def do_robot_load(
128
128
  group="robot_load",
129
129
  )
130
130
 
131
- if demand_energy_ev:
132
- yield from set_energy_plan(
133
- demand_energy_ev / 1000,
134
- cast(SetEnergyComposite, composite),
135
- )
131
+ yield from set_energy_plan(demand_energy_ev, cast(SetEnergyComposite, composite))
136
132
 
137
133
  yield from bps.wait("robot_load")
138
134
 
@@ -218,24 +214,28 @@ def robot_load_and_change_energy_plan(
218
214
  yield from prepare_for_robot_load(
219
215
  composite.aperture_scatterguard, composite.smargon
220
216
  )
221
- yield from bpp.run_wrapper(
222
- robot_load_and_snapshots(
223
- composite,
224
- sample_location,
225
- params.snapshot_directory,
226
- params.thawing_time,
227
- params.demand_energy_ev,
228
- ),
229
- md={
230
- "subplan_name": CONST.PLAN.ROBOT_LOAD,
231
- "metadata": {
232
- "visit": params.visit,
233
- "sample_id": params.sample_id,
234
- "sample_puck": sample_location.puck,
235
- "sample_pin": sample_location.pin,
217
+
218
+ yield from bpp.set_run_key_wrapper(
219
+ bpp.run_wrapper(
220
+ robot_load_and_snapshots(
221
+ composite,
222
+ sample_location,
223
+ params.snapshot_directory,
224
+ params.thawing_time,
225
+ params.demand_energy_ev,
226
+ ),
227
+ md={
228
+ "subplan_name": CONST.PLAN.ROBOT_LOAD,
229
+ "metadata": {
230
+ "visit": params.visit,
231
+ "sample_id": params.sample_id,
232
+ "sample_puck": sample_location.puck,
233
+ "sample_pin": sample_location.pin,
234
+ },
235
+ "activate_callbacks": [
236
+ "RobotLoadISPyBCallback",
237
+ ],
236
238
  },
237
- "activate_callbacks": [
238
- "RobotLoadISPyBCallback",
239
- ],
240
- },
239
+ ),
240
+ CONST.PLAN.ROBOT_LOAD_AND_SNAPSHOTS,
241
241
  )