mx-bluesky 1.4.8__py3-none-any.whl → 1.5.0__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 (67) 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/i24/serial/__init__.py +4 -2
  6. mx_bluesky/beamlines/i24/serial/blueapi_config.yaml +6 -1
  7. mx_bluesky/beamlines/i24/serial/dcid.py +5 -5
  8. mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DetStage.edl +2 -2
  9. mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +9 -9
  10. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +18 -3
  11. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DetStage.edl +2 -2
  12. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +14 -14
  13. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +2 -2
  14. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +55 -4
  15. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +9 -2
  16. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +0 -8
  17. mx_bluesky/beamlines/i24/serial/web_gui_plans/general_plans.py +77 -8
  18. mx_bluesky/common/device_setup_plans/manipulate_sample.py +9 -14
  19. mx_bluesky/common/device_setup_plans/utils.py +49 -0
  20. mx_bluesky/common/{plans → experiment_plans}/common_flyscan_xray_centre_plan.py +11 -19
  21. mx_bluesky/{hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py → common/experiment_plans/common_grid_detect_then_xray_centre_plan.py} +108 -126
  22. mx_bluesky/common/{plans → experiment_plans}/inner_plans/do_fgs.py +1 -1
  23. mx_bluesky/common/experiment_plans/oav_grid_detection_plan.py +5 -13
  24. mx_bluesky/{hyperion → common}/experiment_plans/oav_snapshot_plan.py +8 -2
  25. mx_bluesky/common/external_interaction/nexus/nexus_utils.py +2 -2
  26. mx_bluesky/common/parameters/components.py +1 -1
  27. mx_bluesky/common/parameters/device_composites.py +65 -0
  28. mx_bluesky/common/utils/__init__.py +0 -0
  29. mx_bluesky/common/utils/log.py +12 -11
  30. mx_bluesky/hyperion/__main__.py +6 -11
  31. mx_bluesky/hyperion/baton_handler.py +8 -3
  32. mx_bluesky/hyperion/device_setup_plans/smargon.py +2 -7
  33. mx_bluesky/hyperion/device_setup_plans/utils.py +0 -47
  34. mx_bluesky/hyperion/experiment_plans/__init__.py +5 -5
  35. mx_bluesky/hyperion/experiment_plans/experiment_registry.py +6 -7
  36. mx_bluesky/hyperion/experiment_plans/hyperion_flyscan_xray_centre_plan.py +40 -41
  37. mx_bluesky/hyperion/experiment_plans/hyperion_grid_detect_then_xray_centre_plan.py +60 -0
  38. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +40 -10
  39. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +26 -15
  40. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +2 -11
  41. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +8 -6
  42. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +33 -36
  43. mx_bluesky/hyperion/external_interaction/agamemnon.py +68 -62
  44. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +1 -1
  45. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +3 -3
  46. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +6 -3
  47. mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +2 -2
  48. mx_bluesky/hyperion/external_interaction/config_server.py +4 -1
  49. mx_bluesky/hyperion/parameters/cli.py +3 -10
  50. mx_bluesky/hyperion/parameters/constants.py +1 -1
  51. mx_bluesky/hyperion/parameters/device_composites.py +5 -27
  52. mx_bluesky/hyperion/parameters/load_centre_collect.py +4 -4
  53. mx_bluesky/hyperion/parameters/rotation.py +9 -8
  54. mx_bluesky/hyperion/utils/context.py +5 -2
  55. mx_bluesky/hyperion/utils/validation.py +11 -18
  56. {mx_bluesky-1.4.8.dist-info → mx_bluesky-1.5.0.dist-info}/METADATA +5 -5
  57. {mx_bluesky-1.4.8.dist-info → mx_bluesky-1.5.0.dist-info}/RECORD +65 -62
  58. {mx_bluesky-1.4.8.dist-info → mx_bluesky-1.5.0.dist-info}/WHEEL +1 -1
  59. mx_bluesky/common/device_setup_plans/check_beamstop.py +0 -27
  60. mx_bluesky/common/external_interaction/test_config_server.py +0 -38
  61. /mx_bluesky/common/{plans → experiment_plans}/__init__.py +0 -0
  62. /mx_bluesky/common/{plans → experiment_plans}/inner_plans/__init__ .py +0 -0
  63. /mx_bluesky/common/{plans → experiment_plans}/read_hardware.py +0 -0
  64. /mx_bluesky/common/{plans → experiment_plans}/write_sample_status.py +0 -0
  65. {mx_bluesky-1.4.8.dist-info → mx_bluesky-1.5.0.dist-info}/entry_points.txt +0 -0
  66. {mx_bluesky-1.4.8.dist-info → mx_bluesky-1.5.0.dist-info}/licenses/LICENSE +0 -0
  67. {mx_bluesky-1.4.8.dist-info → mx_bluesky-1.5.0.dist-info}/top_level.txt +0 -0
@@ -1,59 +1,12 @@
1
- from collections.abc import Generator
2
-
3
1
  from bluesky import plan_stubs as bps
4
- from bluesky import preprocessors as bpp
5
- from bluesky.utils import Msg
6
2
  from dodal.devices.detector import (
7
3
  DetectorParams,
8
4
  )
9
- from dodal.devices.detector.detector_motion import DetectorMotion, ShutterState
10
- from dodal.devices.eiger import EigerDetector
11
- from dodal.devices.i03 import Beamstop
12
5
  from dodal.devices.i03.dcm import DCM
13
6
 
14
- from mx_bluesky.common.device_setup_plans.check_beamstop import check_beamstop
15
- from mx_bluesky.common.device_setup_plans.position_detector import (
16
- set_detector_z_position,
17
- set_shutter,
18
- )
19
-
20
7
 
21
8
  def fill_in_energy_if_not_supplied(dcm: DCM, detector_params: DetectorParams):
22
9
  if not detector_params.expected_energy_ev:
23
10
  actual_energy_ev = 1000 * (yield from bps.rd(dcm.energy_in_kev))
24
11
  detector_params.expected_energy_ev = actual_energy_ev
25
12
  return detector_params
26
-
27
-
28
- def start_preparing_data_collection_then_do_plan(
29
- beamstop: Beamstop,
30
- eiger: EigerDetector,
31
- detector_motion: DetectorMotion,
32
- detector_distance_mm: float | None,
33
- plan_to_run: Generator[Msg, None, None],
34
- group="ready_for_data_collection",
35
- ) -> Generator[Msg, None, None]:
36
- """Starts preparing for the next data collection and then runs the
37
- given plan.
38
-
39
- Preparation consists of:
40
- * Arming the Eiger
41
- * Moving the detector to the specified position
42
- * Opening the detect shutter
43
- If the plan fails it will disarm the eiger.
44
- """
45
-
46
- def wrapped_plan():
47
- yield from bps.abs_set(eiger.do_arm, 1, group=group) # type: ignore # Fix types in ophyd-async (https://github.com/DiamondLightSource/mx-bluesky/issues/855)
48
- if detector_distance_mm:
49
- yield from set_detector_z_position(
50
- detector_motion, detector_distance_mm, group
51
- )
52
- yield from set_shutter(detector_motion, ShutterState.OPEN, group)
53
- yield from plan_to_run
54
-
55
- yield from check_beamstop(beamstop)
56
- yield from bpp.contingency_wrapper(
57
- wrapped_plan(),
58
- except_plan=lambda e: (yield from bps.stop(eiger)), # type: ignore # Fix types in ophyd-async (https://github.com/DiamondLightSource/mx-bluesky/issues/855)
59
- )
@@ -3,8 +3,8 @@
3
3
  The __all__ list in here are the plans that are externally available from outside Hyperion.
4
4
  """
5
5
 
6
- from mx_bluesky.hyperion.experiment_plans.grid_detect_then_xray_centre_plan import (
7
- grid_detect_then_xray_centre,
6
+ from mx_bluesky.hyperion.experiment_plans.hyperion_grid_detect_then_xray_centre_plan import (
7
+ hyperion_grid_detect_then_xray_centre,
8
8
  )
9
9
  from mx_bluesky.hyperion.experiment_plans.load_centre_collect_full_plan import (
10
10
  load_centre_collect_full,
@@ -13,12 +13,12 @@ from mx_bluesky.hyperion.experiment_plans.pin_centre_then_xray_centre_plan impor
13
13
  pin_tip_centre_then_xray_centre,
14
14
  )
15
15
  from mx_bluesky.hyperion.experiment_plans.rotation_scan_plan import (
16
- multi_rotation_scan,
16
+ rotation_scan,
17
17
  )
18
18
 
19
19
  __all__ = [
20
- "grid_detect_then_xray_centre",
20
+ "hyperion_grid_detect_then_xray_centre",
21
21
  "pin_tip_centre_then_xray_centre",
22
- "multi_rotation_scan",
22
+ "rotation_scan",
23
23
  "load_centre_collect_full",
24
24
  ]
@@ -5,7 +5,7 @@ from typing import TypedDict
5
5
 
6
6
  import mx_bluesky.hyperion.experiment_plans.rotation_scan_plan as rotation_scan_plan
7
7
  from mx_bluesky.hyperion.experiment_plans import (
8
- grid_detect_then_xray_centre_plan,
8
+ hyperion_grid_detect_then_xray_centre_plan,
9
9
  load_centre_collect_full_plan,
10
10
  pin_centre_then_xray_centre_plan,
11
11
  )
@@ -15,7 +15,7 @@ from mx_bluesky.hyperion.parameters.gridscan import (
15
15
  PinTipCentreThenXrayCentre,
16
16
  )
17
17
  from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect
18
- from mx_bluesky.hyperion.parameters.rotation import MultiRotationScan, RotationScan
18
+ from mx_bluesky.hyperion.parameters.rotation import RotationScan
19
19
 
20
20
 
21
21
  def not_implemented():
@@ -32,24 +32,23 @@ class ExperimentRegistryEntry(TypedDict):
32
32
  HyperionSpecifiedThreeDGridScan
33
33
  | GridScanWithEdgeDetect
34
34
  | RotationScan
35
- | MultiRotationScan
36
35
  | PinTipCentreThenXrayCentre
37
36
  | LoadCentreCollect
38
37
  ]
39
38
 
40
39
 
41
40
  PLAN_REGISTRY: dict[str, ExperimentRegistryEntry] = {
42
- "grid_detect_then_xray_centre": {
43
- "setup": grid_detect_then_xray_centre_plan.create_devices,
41
+ "hyperion_grid_detect_then_xray_centre": {
42
+ "setup": hyperion_grid_detect_then_xray_centre_plan.create_devices,
44
43
  "param_type": GridScanWithEdgeDetect,
45
44
  },
46
45
  "pin_tip_centre_then_xray_centre": {
47
46
  "setup": pin_centre_then_xray_centre_plan.create_devices,
48
47
  "param_type": PinTipCentreThenXrayCentre,
49
48
  },
50
- "multi_rotation_scan": {
49
+ "rotation_scan": {
51
50
  "setup": rotation_scan_plan.create_devices,
52
- "param_type": MultiRotationScan,
51
+ "param_type": RotationScan,
53
52
  },
54
53
  "load_centre_collect_full": {
55
54
  "setup": load_centre_collect_full_plan.create_devices,
@@ -9,7 +9,7 @@ from dodal.devices.fast_grid_scan import (
9
9
  set_fast_grid_scan_params,
10
10
  )
11
11
 
12
- from mx_bluesky.common.plans.common_flyscan_xray_centre_plan import (
12
+ from mx_bluesky.common.experiment_plans.common_flyscan_xray_centre_plan import (
13
13
  construct_beamline_specific_FGS_features,
14
14
  )
15
15
  from mx_bluesky.common.utils.log import LOGGER
@@ -34,51 +34,50 @@ class SmargonSpeedException(Exception):
34
34
 
35
35
 
36
36
  def construct_hyperion_specific_features(
37
- fgs_composite: HyperionFlyScanXRayCentreComposite,
38
- parameters: HyperionSpecifiedThreeDGridScan,
37
+ xrc_composite: HyperionFlyScanXRayCentreComposite,
38
+ xrc_parameters: HyperionSpecifiedThreeDGridScan,
39
39
  ):
40
40
  """
41
41
  Get all the information needed to do the Hyperion-specific parts of the XRC flyscan.
42
42
  """
43
-
44
43
  signals_to_read_pre_flyscan = [
45
- fgs_composite.undulator.current_gap,
46
- fgs_composite.synchrotron.synchrotron_mode,
47
- fgs_composite.s4_slit_gaps.xgap,
48
- fgs_composite.s4_slit_gaps.ygap,
49
- fgs_composite.smargon.x,
50
- fgs_composite.smargon.y,
51
- fgs_composite.smargon.z,
52
- fgs_composite.dcm.energy_in_kev,
44
+ xrc_composite.undulator.current_gap,
45
+ xrc_composite.synchrotron.synchrotron_mode,
46
+ xrc_composite.s4_slit_gaps.xgap,
47
+ xrc_composite.s4_slit_gaps.ygap,
48
+ xrc_composite.smargon.x,
49
+ xrc_composite.smargon.y,
50
+ xrc_composite.smargon.z,
51
+ xrc_composite.dcm.energy_in_kev,
53
52
  ]
54
53
 
55
54
  signals_to_read_during_collection = [
56
- fgs_composite.aperture_scatterguard,
57
- fgs_composite.attenuator.actual_transmission,
58
- fgs_composite.flux.flux_reading,
59
- fgs_composite.dcm.energy_in_kev,
60
- fgs_composite.eiger.bit_depth,
55
+ xrc_composite.aperture_scatterguard,
56
+ xrc_composite.attenuator.actual_transmission,
57
+ xrc_composite.flux.flux_reading,
58
+ xrc_composite.dcm.energy_in_kev,
59
+ xrc_composite.eiger.bit_depth,
61
60
  ]
62
61
 
63
- if parameters.features.use_panda_for_gridscan:
62
+ if xrc_parameters.features.use_panda_for_gridscan:
64
63
  setup_trigger_plan = _panda_triggering_setup
65
64
  tidy_plan = _panda_tidy
66
65
  set_flyscan_params_plan = partial(
67
66
  set_fast_grid_scan_params,
68
- fgs_composite.panda_fast_grid_scan,
69
- parameters.panda_FGS_params,
67
+ xrc_composite.panda_fast_grid_scan,
68
+ xrc_parameters.panda_FGS_params,
70
69
  )
71
- fgs_motors = fgs_composite.panda_fast_grid_scan
70
+ fgs_motors = xrc_composite.panda_fast_grid_scan
72
71
 
73
72
  else:
74
73
  setup_trigger_plan = _zebra_triggering_setup
75
74
  tidy_plan = partial(_generic_tidy, group="flyscan_zebra_tidy", wait=True)
76
75
  set_flyscan_params_plan = partial(
77
76
  set_fast_grid_scan_params,
78
- fgs_composite.zebra_fast_grid_scan,
79
- parameters.FGS_params,
77
+ xrc_composite.zebra_fast_grid_scan,
78
+ xrc_parameters.FGS_params,
80
79
  )
81
- fgs_motors = fgs_composite.zebra_fast_grid_scan
80
+ fgs_motors = xrc_composite.zebra_fast_grid_scan
82
81
  return construct_beamline_specific_FGS_features(
83
82
  setup_trigger_plan,
84
83
  tidy_plan,
@@ -91,47 +90,47 @@ def construct_hyperion_specific_features(
91
90
 
92
91
 
93
92
  def _generic_tidy(
94
- fgs_composite: HyperionFlyScanXRayCentreComposite, group, wait=True
93
+ xrc_composite: HyperionFlyScanXRayCentreComposite, group, wait=True
95
94
  ) -> MsgGenerator:
96
95
  LOGGER.info("Tidying up Zebra")
97
96
  yield from tidy_up_zebra_after_gridscan(
98
- fgs_composite.zebra, fgs_composite.sample_shutter, group=group, wait=wait
97
+ xrc_composite.zebra, xrc_composite.sample_shutter, group=group, wait=wait
99
98
  )
100
99
  LOGGER.info("Tidying up Zocalo")
101
100
  # make sure we don't consume any other results
102
- yield from bps.unstage(fgs_composite.zocalo, group=group, wait=wait)
101
+ yield from bps.unstage(xrc_composite.zocalo, group=group, wait=wait)
103
102
 
104
103
  # Turn off dev/shm streaming to avoid filling disk, see https://github.com/DiamondLightSource/hyperion/issues/1395
105
104
  LOGGER.info("Turning off Eiger dev/shm streaming")
106
- yield from bps.abs_set(fgs_composite.eiger.odin.fan.dev_shm_enable, 0) # type: ignore # Fix types in ophyd-async (https://github.com/DiamondLightSource/mx-bluesky/issues/855)
105
+ yield from bps.abs_set(xrc_composite.eiger.odin.fan.dev_shm_enable, 0) # type: ignore # Fix types in ophyd-async (https://github.com/DiamondLightSource/mx-bluesky/issues/855)
107
106
 
108
107
 
109
- def _panda_tidy(fgs_composite: HyperionFlyScanXRayCentreComposite):
108
+ def _panda_tidy(xrc_composite: HyperionFlyScanXRayCentreComposite):
110
109
  group = "panda_flyscan_tidy"
111
110
  LOGGER.info("Disabling panda blocks")
112
- yield from disarm_panda_for_gridscan(fgs_composite.panda, group)
113
- yield from _generic_tidy(fgs_composite, group, False)
111
+ yield from disarm_panda_for_gridscan(xrc_composite.panda, group)
112
+ yield from _generic_tidy(xrc_composite, group, False)
114
113
  yield from bps.wait(group, timeout=10)
115
- yield from bps.unstage(fgs_composite.panda)
114
+ yield from bps.unstage(xrc_composite.panda)
116
115
 
117
116
 
118
117
  def _zebra_triggering_setup(
119
- fgs_composite: HyperionFlyScanXRayCentreComposite,
118
+ xrc_composite: HyperionFlyScanXRayCentreComposite,
120
119
  parameters: HyperionSpecifiedThreeDGridScan,
121
120
  ) -> MsgGenerator:
122
121
  yield from setup_zebra_for_gridscan(
123
- fgs_composite.zebra, fgs_composite.sample_shutter, wait=True
122
+ xrc_composite.zebra, xrc_composite.sample_shutter, wait=True
124
123
  )
125
124
 
126
125
 
127
126
  def _panda_triggering_setup(
128
- fgs_composite: HyperionFlyScanXRayCentreComposite,
127
+ xrc_composite: HyperionFlyScanXRayCentreComposite,
129
128
  parameters: HyperionSpecifiedThreeDGridScan,
130
129
  ) -> MsgGenerator:
131
130
  LOGGER.info("Setting up Panda for flyscan")
132
131
 
133
132
  run_up_distance_mm = yield from bps.rd(
134
- fgs_composite.panda_fast_grid_scan.run_up_distance_mm
133
+ xrc_composite.panda_fast_grid_scan.run_up_distance_mm
135
134
  )
136
135
 
137
136
  # Set the time between x steps pv
@@ -140,7 +139,7 @@ def _panda_triggering_setup(
140
139
  time_between_x_steps_ms = (DEADTIME_S + parameters.exposure_time_s) * 1e3
141
140
 
142
141
  smargon_speed_limit_mm_per_s = yield from bps.rd(
143
- fgs_composite.smargon.x.max_velocity
142
+ xrc_composite.smargon.x.max_velocity
144
143
  )
145
144
 
146
145
  sample_velocity_mm_per_s = (
@@ -161,7 +160,7 @@ def _panda_triggering_setup(
161
160
  )
162
161
 
163
162
  yield from bps.mv(
164
- fgs_composite.panda_fast_grid_scan.time_between_x_steps_ms, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
163
+ xrc_composite.panda_fast_grid_scan.time_between_x_steps_ms, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
165
164
  time_between_x_steps_ms, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
166
165
  )
167
166
 
@@ -169,9 +168,9 @@ def _panda_triggering_setup(
169
168
  yield from set_panda_directory(directory_provider_root)
170
169
 
171
170
  yield from setup_panda_for_flyscan(
172
- fgs_composite.panda,
171
+ xrc_composite.panda,
173
172
  parameters.panda_FGS_params,
174
- fgs_composite.smargon,
173
+ xrc_composite.smargon,
175
174
  parameters.exposure_time_s,
176
175
  time_between_x_steps_ms,
177
176
  sample_velocity_mm_per_s,
@@ -179,5 +178,5 @@ def _panda_triggering_setup(
179
178
 
180
179
  LOGGER.info("Setting up Zebra for panda flyscan")
181
180
  yield from setup_zebra_for_panda_flyscan(
182
- fgs_composite.zebra, fgs_composite.sample_shutter, wait=True
181
+ xrc_composite.zebra, xrc_composite.sample_shutter, wait=True
183
182
  )
@@ -0,0 +1,60 @@
1
+ from __future__ import annotations
2
+
3
+ from blueapi.core import BlueskyContext
4
+ from bluesky.utils import MsgGenerator
5
+ from dodal.plans.preprocessors.verify_undulator_gap import (
6
+ verify_undulator_gap_before_run_decorator,
7
+ )
8
+
9
+ from mx_bluesky.common.experiment_plans.common_grid_detect_then_xray_centre_plan import (
10
+ grid_detect_then_xray_centre,
11
+ )
12
+ from mx_bluesky.common.parameters.constants import OavConstants, PlanNameConstants
13
+ from mx_bluesky.common.preprocessors.preprocessors import (
14
+ transmission_and_xbpm_feedback_for_collection_decorator,
15
+ )
16
+ from mx_bluesky.common.utils.context import device_composite_from_context
17
+ from mx_bluesky.hyperion.experiment_plans.hyperion_flyscan_xray_centre_plan import (
18
+ construct_hyperion_specific_features,
19
+ )
20
+ from mx_bluesky.hyperion.parameters.device_composites import (
21
+ HyperionGridDetectThenXRayCentreComposite,
22
+ )
23
+ from mx_bluesky.hyperion.parameters.gridscan import (
24
+ GridScanWithEdgeDetect,
25
+ HyperionSpecifiedThreeDGridScan,
26
+ )
27
+
28
+
29
+ def create_devices(
30
+ context: BlueskyContext,
31
+ ) -> HyperionGridDetectThenXRayCentreComposite:
32
+ return device_composite_from_context(
33
+ context, HyperionGridDetectThenXRayCentreComposite
34
+ )
35
+
36
+
37
+ def hyperion_grid_detect_then_xray_centre(
38
+ composite: HyperionGridDetectThenXRayCentreComposite,
39
+ parameters: GridScanWithEdgeDetect,
40
+ oav_config: str = OavConstants.OAV_CONFIG_JSON,
41
+ ) -> MsgGenerator:
42
+ """
43
+ A plan which combines the collection of snapshots from the OAV and the determination
44
+ of the grid dimensions to use for the following grid scan.
45
+ """
46
+
47
+ @verify_undulator_gap_before_run_decorator(composite)
48
+ @transmission_and_xbpm_feedback_for_collection_decorator(
49
+ composite, parameters.transmission_frac, PlanNameConstants.GRIDSCAN_OUTER
50
+ )
51
+ def plan_to_perform():
52
+ yield from grid_detect_then_xray_centre(
53
+ composite=composite,
54
+ parameters=parameters,
55
+ xrc_params_type=HyperionSpecifiedThreeDGridScan,
56
+ construct_beamline_specific=construct_hyperion_specific_features,
57
+ oav_config=oav_config,
58
+ )
59
+
60
+ yield from plan_to_perform()
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Generator
4
+
3
5
  import bluesky.plan_stubs as bps
4
6
  import numpy as np
5
7
  import pydantic
@@ -18,12 +20,13 @@ from mx_bluesky.hyperion.experiment_plans.robot_load_then_centre_plan import (
18
20
  robot_load_then_xray_centre,
19
21
  )
20
22
  from mx_bluesky.hyperion.experiment_plans.rotation_scan_plan import (
21
- MultiRotationScan,
23
+ RotationScan,
22
24
  RotationScanComposite,
23
- multi_rotation_scan_internal,
25
+ rotation_scan_internal,
24
26
  )
25
27
  from mx_bluesky.hyperion.parameters.constants import CONST
26
28
  from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect
29
+ from mx_bluesky.hyperion.parameters.rotation import RotationScanPerSweep
27
30
 
28
31
 
29
32
  @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
@@ -48,6 +51,8 @@ def load_centre_collect_full(
48
51
  * If X-ray centring finds a diffracting centre then move to that centre and
49
52
  * do a collection with the specified parameters.
50
53
  """
54
+ parameters.features.update_self_from_server()
55
+
51
56
  if not oav_params:
52
57
  oav_params = OAVParameters(context="xrayCentring")
53
58
  oav_config_file = oav_params.oav_config_json
@@ -99,21 +104,46 @@ def load_centre_collect_full(
99
104
 
100
105
  multi_rotation.rotation_scans.clear()
101
106
 
107
+ is_alternating = parameters.features.alternate_rotation_direction
108
+
109
+ generator = rotation_scan_generator(is_alternating)
110
+ next(generator)
102
111
  for location in locations_to_collect_um:
103
112
  for rot in rotation_template:
104
- combination = rot.model_copy()
105
- (
106
- combination.x_start_um,
107
- combination.y_start_um,
108
- combination.z_start_um,
109
- ) = location
113
+ combination = generator.send((rot, location))
110
114
  multi_rotation.rotation_scans.append(combination)
111
- multi_rotation = MultiRotationScan.model_validate(multi_rotation)
115
+ multi_rotation = RotationScan.model_validate(multi_rotation)
112
116
 
113
117
  assert (
114
118
  multi_rotation.demand_energy_ev
115
119
  == parameters.robot_load_then_centre.demand_energy_ev
116
120
  ), "Setting a different energy for gridscan and rotation is not supported"
117
- yield from multi_rotation_scan_internal(composite, multi_rotation, oav_params)
121
+ yield from rotation_scan_internal(composite, multi_rotation, oav_params)
118
122
 
119
123
  yield from plan_with_callback_subs()
124
+
125
+
126
+ def rotation_scan_generator(
127
+ is_alternating: bool,
128
+ ) -> Generator[RotationScanPerSweep, tuple[RotationScanPerSweep, np.ndarray], None]:
129
+ scan_template, location = yield # type: ignore
130
+ next_rotation_direction = scan_template.rotation_direction
131
+ while True:
132
+ scan = scan_template.model_copy()
133
+ (
134
+ scan.x_start_um,
135
+ scan.y_start_um,
136
+ scan.z_start_um,
137
+ ) = location
138
+ if is_alternating:
139
+ if next_rotation_direction != scan.rotation_direction:
140
+ # If originally specified direction of the current scan is different
141
+ # from that required, swap the start and ends.
142
+ start = scan.omega_start_deg
143
+ rotation_sign = scan.rotation_direction.multiplier
144
+ end = start + rotation_sign * scan.scan_width_deg
145
+ scan.omega_start_deg = end
146
+ scan.rotation_direction = next_rotation_direction
147
+ next_rotation_direction = next_rotation_direction.opposite
148
+
149
+ scan_template, location = yield scan
@@ -9,9 +9,18 @@ from dodal.devices.eiger import EigerDetector
9
9
  from dodal.devices.oav.oav_parameters import OAVParameters
10
10
 
11
11
  from mx_bluesky.common.device_setup_plans.manipulate_sample import move_phi_chi_omega
12
+ from mx_bluesky.common.device_setup_plans.utils import (
13
+ start_preparing_data_collection_then_do_plan,
14
+ )
12
15
  from mx_bluesky.common.experiment_plans.change_aperture_then_move_plan import (
13
16
  change_aperture_then_move_to_xtal,
14
17
  )
18
+ from mx_bluesky.common.experiment_plans.common_grid_detect_then_xray_centre_plan import (
19
+ detect_grid_and_do_gridscan,
20
+ )
21
+ from mx_bluesky.common.experiment_plans.oav_snapshot_plan import (
22
+ setup_beamline_for_OAV,
23
+ )
15
24
  from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback import (
16
25
  ispyb_activation_wrapper,
17
26
  )
@@ -19,32 +28,33 @@ from mx_bluesky.common.parameters.constants import OavConstants
19
28
  from mx_bluesky.common.utils.context import device_composite_from_context
20
29
  from mx_bluesky.common.utils.log import LOGGER
21
30
  from mx_bluesky.common.xrc_result import XRayCentreEventHandler
22
- from mx_bluesky.hyperion.device_setup_plans.utils import (
23
- start_preparing_data_collection_then_do_plan,
24
- )
25
- from mx_bluesky.hyperion.experiment_plans.grid_detect_then_xray_centre_plan import (
26
- GridDetectThenXRayCentreComposite,
27
- detect_grid_and_do_gridscan,
28
- )
29
- from mx_bluesky.hyperion.experiment_plans.oav_snapshot_plan import (
30
- setup_beamline_for_OAV,
31
+ from mx_bluesky.hyperion.experiment_plans.hyperion_flyscan_xray_centre_plan import (
32
+ construct_hyperion_specific_features,
31
33
  )
32
34
  from mx_bluesky.hyperion.experiment_plans.pin_tip_centring_plan import (
33
35
  PinTipCentringComposite,
34
36
  pin_tip_centre_plan,
35
37
  )
36
38
  from mx_bluesky.hyperion.parameters.constants import CONST
39
+ from mx_bluesky.hyperion.parameters.device_composites import (
40
+ HyperionGridDetectThenXRayCentreComposite,
41
+ )
37
42
  from mx_bluesky.hyperion.parameters.gridscan import (
38
43
  GridScanWithEdgeDetect,
44
+ HyperionSpecifiedThreeDGridScan,
39
45
  PinTipCentreThenXrayCentre,
40
46
  )
41
47
 
42
48
 
43
- def create_devices(context: BlueskyContext) -> GridDetectThenXRayCentreComposite:
49
+ def create_devices(
50
+ context: BlueskyContext,
51
+ ) -> HyperionGridDetectThenXRayCentreComposite:
44
52
  """
45
- GridDetectThenXRayCentreComposite contains all the devices we need, reuse that.
53
+ HyperionGridDetectThenXRayCentreComposite contains all the devices we need, reuse that.
46
54
  """
47
- return device_composite_from_context(context, GridDetectThenXRayCentreComposite)
55
+ return device_composite_from_context(
56
+ context, HyperionGridDetectThenXRayCentreComposite
57
+ )
48
58
 
49
59
 
50
60
  def create_parameters_for_grid_detection(
@@ -60,7 +70,7 @@ def create_parameters_for_grid_detection(
60
70
 
61
71
 
62
72
  def pin_centre_then_flyscan_plan(
63
- composite: GridDetectThenXRayCentreComposite,
73
+ composite: HyperionGridDetectThenXRayCentreComposite,
64
74
  parameters: PinTipCentreThenXrayCentre,
65
75
  oav_config_file: str = OavConstants.OAV_CONFIG_JSON,
66
76
  ):
@@ -92,20 +102,21 @@ def pin_centre_then_flyscan_plan(
92
102
  )
93
103
 
94
104
  grid_detect_params = create_parameters_for_grid_detection(parameters)
95
-
96
105
  oav_params = OAVParameters("xrayCentring", oav_config_file)
97
106
 
98
107
  yield from detect_grid_and_do_gridscan(
99
108
  composite,
100
109
  grid_detect_params,
101
110
  oav_params,
111
+ HyperionSpecifiedThreeDGridScan,
112
+ construct_hyperion_specific_features,
102
113
  )
103
114
 
104
115
  yield from ispyb_activation_wrapper(_pin_centre_then_flyscan_plan(), parameters)
105
116
 
106
117
 
107
118
  def pin_tip_centre_then_xray_centre(
108
- composite: GridDetectThenXRayCentreComposite,
119
+ composite: HyperionGridDetectThenXRayCentreComposite,
109
120
  parameters: PinTipCentreThenXrayCentre,
110
121
  oav_config_file: str = OavConstants.OAV_CONFIG_JSON,
111
122
  ) -> MsgGenerator:
@@ -19,7 +19,7 @@ from dodal.devices.i03.undulator_dcm import UndulatorDCM
19
19
  from dodal.devices.motors import XYZPositioner
20
20
  from dodal.devices.oav.oav_detector import OAV
21
21
  from dodal.devices.robot import BartRobot, SampleLocation
22
- from dodal.devices.smargon import Smargon, StubPosition
22
+ from dodal.devices.smargon import CombinedMove, Smargon, StubPosition
23
23
  from dodal.devices.thawer import Thawer
24
24
  from dodal.devices.webcam import Webcam
25
25
  from dodal.devices.xbpm_feedback import XBPMFeedback
@@ -104,16 +104,7 @@ def prepare_for_robot_load(
104
104
 
105
105
  yield from bps.mv(smargon.stub_offsets, StubPosition.RESET_TO_ROBOT_LOAD)
106
106
 
107
- # fmt: off
108
- yield from bps.mv(
109
- smargon.x, 0,
110
- smargon.y, 0,
111
- smargon.z, 0,
112
- smargon.omega, 0,
113
- smargon.chi, 0,
114
- smargon.phi, 0
115
- )
116
- # fmt: on
107
+ yield from bps.mv(smargon, CombinedMove(x=0, y=0, z=0, chi=0, phi=0, omega=0))
117
108
 
118
109
  yield from bps.wait("prepare_robot_load")
119
110
 
@@ -35,13 +35,12 @@ from dodal.devices.zocalo import ZocaloResults
35
35
  from dodal.log import LOGGER
36
36
  from ophyd_async.fastcs.panda import HDFPanda
37
37
 
38
+ from mx_bluesky.common.device_setup_plans.utils import (
39
+ start_preparing_data_collection_then_do_plan,
40
+ )
38
41
  from mx_bluesky.common.parameters.constants import OavConstants
39
42
  from mx_bluesky.hyperion.device_setup_plans.utils import (
40
43
  fill_in_energy_if_not_supplied,
41
- start_preparing_data_collection_then_do_plan,
42
- )
43
- from mx_bluesky.hyperion.experiment_plans.grid_detect_then_xray_centre_plan import (
44
- GridDetectThenXRayCentreComposite,
45
44
  )
46
45
  from mx_bluesky.hyperion.experiment_plans.pin_centre_then_xray_centre_plan import (
47
46
  pin_centre_then_flyscan_plan,
@@ -56,6 +55,9 @@ from mx_bluesky.hyperion.experiment_plans.set_energy_plan import (
56
55
  set_energy_plan,
57
56
  )
58
57
  from mx_bluesky.hyperion.parameters.constants import CONST
58
+ from mx_bluesky.hyperion.parameters.device_composites import (
59
+ HyperionGridDetectThenXRayCentreComposite,
60
+ )
59
61
  from mx_bluesky.hyperion.parameters.robot_load import RobotLoadThenCentre
60
62
 
61
63
 
@@ -65,7 +67,7 @@ class RobotLoadThenCentreComposite:
65
67
  xbpm_feedback: XBPMFeedback
66
68
  attenuator: BinaryFilterAttenuator
67
69
 
68
- # GridDetectThenXRayCentreComposite fields
70
+ # HyperionGridDetectThenXRayCentreComposite fields
69
71
  aperture_scatterguard: ApertureScatterguard
70
72
  backlight: Backlight
71
73
  detector_motion: DetectorMotion
@@ -110,7 +112,7 @@ def _flyscan_plan_from_robot_load_params(
110
112
  oav_config_file: str = OavConstants.OAV_CONFIG_JSON,
111
113
  ):
112
114
  yield from pin_centre_then_flyscan_plan(
113
- cast(GridDetectThenXRayCentreComposite, composite),
115
+ cast(HyperionGridDetectThenXRayCentreComposite, composite),
114
116
  params.pin_centre_then_xray_centre_params,
115
117
  oav_config_file,
116
118
  )