mx-bluesky 1.4.7__py3-none-any.whl → 1.4.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. mx_bluesky/_version.py +2 -2
  2. mx_bluesky/beamlines/aithre_lasershaping/__init__.py +8 -0
  3. mx_bluesky/beamlines/aithre_lasershaping/beamline_safe.py +36 -0
  4. mx_bluesky/beamlines/aithre_lasershaping/goniometer_controls.py +43 -0
  5. mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +4 -4
  6. mx_bluesky/beamlines/i04/thawing_plan.py +8 -2
  7. mx_bluesky/beamlines/i23/__init__.py +3 -0
  8. mx_bluesky/beamlines/i23/serial.py +71 -0
  9. mx_bluesky/beamlines/i24/serial/__init__.py +2 -0
  10. mx_bluesky/beamlines/i24/serial/blueapi_config.yaml +2 -1
  11. mx_bluesky/beamlines/i24/serial/dcid.py +5 -5
  12. mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DetStage.edl +2 -2
  13. mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +9 -9
  14. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +25 -5
  15. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DetStage.edl +2 -2
  16. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +14 -14
  17. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +5 -5
  18. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +29 -60
  19. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +7 -1
  20. mx_bluesky/beamlines/i24/serial/log.py +9 -10
  21. mx_bluesky/beamlines/i24/serial/parameters/utils.py +36 -7
  22. mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +0 -1
  23. mx_bluesky/beamlines/i24/serial/setup_beamline/pv_abstract.py +4 -4
  24. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +4 -12
  25. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +2 -1
  26. mx_bluesky/beamlines/i24/serial/web_gui_plans/general_plans.py +71 -11
  27. mx_bluesky/beamlines/i24/serial/write_nexus.py +3 -3
  28. mx_bluesky/{hyperion → common}/device_setup_plans/manipulate_sample.py +6 -14
  29. mx_bluesky/{hyperion → common}/device_setup_plans/setup_oav.py +12 -6
  30. mx_bluesky/{hyperion → common}/experiment_plans/change_aperture_then_move_plan.py +4 -5
  31. mx_bluesky/{hyperion → common}/experiment_plans/oav_grid_detection_plan.py +6 -6
  32. mx_bluesky/common/external_interaction/callbacks/common/ispyb_callback_base.py +6 -5
  33. mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py +16 -47
  34. mx_bluesky/common/external_interaction/ispyb/ispyb_store.py +4 -1
  35. mx_bluesky/common/external_interaction/ispyb/ispyb_utils.py +4 -4
  36. mx_bluesky/common/external_interaction/nexus/nexus_utils.py +2 -2
  37. mx_bluesky/common/parameters/components.py +22 -2
  38. mx_bluesky/common/parameters/constants.py +4 -16
  39. mx_bluesky/common/parameters/gridscan.py +36 -32
  40. mx_bluesky/common/plans/common_flyscan_xray_centre_plan.py +316 -0
  41. mx_bluesky/common/plans/inner_plans/__init__ .py +0 -0
  42. mx_bluesky/common/plans/read_hardware.py +3 -3
  43. mx_bluesky/common/utils/log.py +19 -15
  44. mx_bluesky/hyperion/__main__.py +6 -24
  45. mx_bluesky/hyperion/baton_handler.py +8 -3
  46. mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +4 -4
  47. mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +0 -33
  48. mx_bluesky/hyperion/device_setup_plans/smargon.py +2 -7
  49. mx_bluesky/hyperion/device_setup_plans/utils.py +6 -5
  50. mx_bluesky/hyperion/experiment_plans/__init__.py +1 -7
  51. mx_bluesky/hyperion/experiment_plans/experiment_registry.py +3 -13
  52. mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +80 -87
  53. mx_bluesky/hyperion/experiment_plans/hyperion_flyscan_xray_centre_plan.py +183 -0
  54. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +50 -15
  55. mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +31 -7
  56. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +4 -4
  57. mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +1 -1
  58. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +13 -14
  59. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +9 -8
  60. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +30 -71
  61. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +2 -2
  62. mx_bluesky/hyperion/external_interaction/agamemnon.py +78 -80
  63. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +8 -6
  64. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +3 -3
  65. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +6 -3
  66. mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +2 -2
  67. mx_bluesky/hyperion/external_interaction/callbacks/snapshot_callback.py +183 -31
  68. mx_bluesky/hyperion/external_interaction/config_server.py +4 -1
  69. mx_bluesky/hyperion/parameters/cli.py +4 -19
  70. mx_bluesky/hyperion/parameters/constants.py +1 -5
  71. mx_bluesky/hyperion/parameters/device_composites.py +40 -5
  72. mx_bluesky/hyperion/parameters/gridscan.py +9 -58
  73. mx_bluesky/hyperion/parameters/load_centre_collect.py +4 -4
  74. mx_bluesky/hyperion/parameters/rotation.py +9 -12
  75. mx_bluesky/hyperion/utils/context.py +2 -2
  76. mx_bluesky/hyperion/utils/validation.py +15 -19
  77. {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/METADATA +7 -6
  78. {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/RECORD +86 -83
  79. {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/WHEEL +1 -1
  80. mx_bluesky/common/external_interaction/test_config_server.py +0 -38
  81. mx_bluesky/hyperion/device_setup_plans/check_beamstop.py +0 -27
  82. mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +0 -467
  83. /mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/{short1-laser.png → s1l.png} +0 -0
  84. /mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/{short2-laser.png → s2l.png} +0 -0
  85. /mx_bluesky/{hyperion → common}/device_setup_plans/position_detector.py +0 -0
  86. /mx_bluesky/common/plans/{do_fgs.py → inner_plans/do_fgs.py} +0 -0
  87. {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/entry_points.txt +0 -0
  88. {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/licenses/LICENSE +0 -0
  89. {mx_bluesky-1.4.7.dist-info → mx_bluesky-1.4.9.dist-info}/top_level.txt +0 -0
@@ -3,9 +3,6 @@
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.flyscan_xray_centre_plan import (
7
- flyscan_xray_centre,
8
- )
9
6
  from mx_bluesky.hyperion.experiment_plans.grid_detect_then_xray_centre_plan import (
10
7
  grid_detect_then_xray_centre,
11
8
  )
@@ -16,15 +13,12 @@ from mx_bluesky.hyperion.experiment_plans.pin_centre_then_xray_centre_plan impor
16
13
  pin_tip_centre_then_xray_centre,
17
14
  )
18
15
  from mx_bluesky.hyperion.experiment_plans.rotation_scan_plan import (
19
- multi_rotation_scan,
20
16
  rotation_scan,
21
17
  )
22
18
 
23
19
  __all__ = [
24
- "flyscan_xray_centre",
25
20
  "grid_detect_then_xray_centre",
26
- "rotation_scan",
27
21
  "pin_tip_centre_then_xray_centre",
28
- "multi_rotation_scan",
22
+ "rotation_scan",
29
23
  "load_centre_collect_full",
30
24
  ]
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  from collections.abc import Callable
4
4
  from typing import TypedDict
5
5
 
6
- import mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan as flyscan_xray_centre_plan
7
6
  import mx_bluesky.hyperion.experiment_plans.rotation_scan_plan as rotation_scan_plan
8
7
  from mx_bluesky.hyperion.experiment_plans import (
9
8
  grid_detect_then_xray_centre_plan,
@@ -16,7 +15,7 @@ from mx_bluesky.hyperion.parameters.gridscan import (
16
15
  PinTipCentreThenXrayCentre,
17
16
  )
18
17
  from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect
19
- from mx_bluesky.hyperion.parameters.rotation import MultiRotationScan, RotationScan
18
+ from mx_bluesky.hyperion.parameters.rotation import RotationScan
20
19
 
21
20
 
22
21
  def not_implemented():
@@ -33,32 +32,23 @@ class ExperimentRegistryEntry(TypedDict):
33
32
  HyperionSpecifiedThreeDGridScan
34
33
  | GridScanWithEdgeDetect
35
34
  | RotationScan
36
- | MultiRotationScan
37
35
  | PinTipCentreThenXrayCentre
38
36
  | LoadCentreCollect
39
37
  ]
40
38
 
41
39
 
42
40
  PLAN_REGISTRY: dict[str, ExperimentRegistryEntry] = {
43
- "flyscan_xray_centre": {
44
- "setup": flyscan_xray_centre_plan.create_devices,
45
- "param_type": HyperionSpecifiedThreeDGridScan,
46
- },
47
41
  "grid_detect_then_xray_centre": {
48
42
  "setup": grid_detect_then_xray_centre_plan.create_devices,
49
43
  "param_type": GridScanWithEdgeDetect,
50
44
  },
51
- "rotation_scan": {
52
- "setup": rotation_scan_plan.create_devices,
53
- "param_type": RotationScan,
54
- },
55
45
  "pin_tip_centre_then_xray_centre": {
56
46
  "setup": pin_centre_then_xray_centre_plan.create_devices,
57
47
  "param_type": PinTipCentreThenXrayCentre,
58
48
  },
59
- "multi_rotation_scan": {
49
+ "rotation_scan": {
60
50
  "setup": rotation_scan_plan.create_devices,
61
- "param_type": MultiRotationScan,
51
+ "param_type": RotationScan,
62
52
  },
63
53
  "load_centre_collect_full": {
64
54
  "setup": load_centre_collect_full_plan.create_devices,
@@ -2,35 +2,28 @@ from __future__ import annotations
2
2
 
3
3
  from pathlib import Path
4
4
 
5
- import pydantic
6
5
  from blueapi.core import BlueskyContext
7
6
  from bluesky import plan_stubs as bps
8
7
  from bluesky import preprocessors as bpp
9
8
  from bluesky.preprocessors import subs_decorator
10
9
  from bluesky.utils import MsgGenerator
11
- from dodal.devices.aperturescatterguard import ApertureScatterguard
12
- from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
13
- from dodal.devices.backlight import Backlight, BacklightPosition
14
- from dodal.devices.dcm import DCM
15
- from dodal.devices.detector.detector_motion import DetectorMotion
10
+ from dodal.devices.backlight import BacklightPosition
16
11
  from dodal.devices.eiger import EigerDetector
17
- from dodal.devices.fast_grid_scan import PandAFastGridScan, ZebraFastGridScan
18
- from dodal.devices.flux import Flux
19
- from dodal.devices.i03.beamstop import Beamstop
20
- from dodal.devices.oav.oav_detector import OAV
21
12
  from dodal.devices.oav.oav_parameters import OAVParameters
22
- from dodal.devices.oav.pin_image_recognition import PinTipDetection
23
- from dodal.devices.robot import BartRobot
24
- from dodal.devices.s4_slit_gaps import S4SlitGaps
25
- from dodal.devices.smargon import Smargon
26
- from dodal.devices.synchrotron import Synchrotron
27
- from dodal.devices.undulator import Undulator
28
- from dodal.devices.xbpm_feedback import XBPMFeedback
29
- from dodal.devices.zebra.zebra import Zebra
30
- from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
31
- from dodal.devices.zocalo import ZocaloResults
32
- from ophyd_async.fastcs.panda import HDFPanda
13
+ from dodal.plans.preprocessors.verify_undulator_gap import (
14
+ verify_undulator_gap_before_run_decorator,
15
+ )
33
16
 
17
+ from mx_bluesky.common.device_setup_plans.manipulate_sample import (
18
+ move_aperture_if_required,
19
+ )
20
+ from mx_bluesky.common.experiment_plans.change_aperture_then_move_plan import (
21
+ change_aperture_then_move_to_xtal,
22
+ )
23
+ from mx_bluesky.common.experiment_plans.oav_grid_detection_plan import (
24
+ OavGridDetectionComposite,
25
+ grid_detection_plan,
26
+ )
34
27
  from mx_bluesky.common.external_interaction.callbacks.common.grid_detection_callback import (
35
28
  GridDetectionCallback,
36
29
  GridParamUpdate,
@@ -39,27 +32,29 @@ from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback
39
32
  ispyb_activation_wrapper,
40
33
  )
41
34
  from mx_bluesky.common.parameters.constants import OavConstants
35
+ from mx_bluesky.common.parameters.gridscan import GridCommon
36
+ from mx_bluesky.common.plans.common_flyscan_xray_centre_plan import (
37
+ BeamlineSpecificFGSFeatures,
38
+ common_flyscan_xray_centre,
39
+ )
40
+ from mx_bluesky.common.preprocessors.preprocessors import (
41
+ transmission_and_xbpm_feedback_for_collection_decorator,
42
+ )
42
43
  from mx_bluesky.common.utils.context import device_composite_from_context
43
44
  from mx_bluesky.common.utils.log import LOGGER
44
45
  from mx_bluesky.common.xrc_result import XRayCentreEventHandler
45
- from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import (
46
- move_aperture_if_required,
47
- )
48
46
  from mx_bluesky.hyperion.device_setup_plans.utils import (
49
47
  start_preparing_data_collection_then_do_plan,
50
48
  )
51
- from mx_bluesky.hyperion.experiment_plans.change_aperture_then_move_plan import (
52
- change_aperture_then_move_to_xtal,
53
- )
54
- from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
55
- flyscan_xray_centre_no_move,
49
+ from mx_bluesky.hyperion.experiment_plans.hyperion_flyscan_xray_centre_plan import (
50
+ construct_hyperion_specific_features,
56
51
  )
57
- from mx_bluesky.hyperion.experiment_plans.oav_grid_detection_plan import (
58
- OavGridDetectionComposite,
59
- grid_detection_plan,
52
+ from mx_bluesky.hyperion.experiment_plans.oav_snapshot_plan import (
53
+ setup_beamline_for_OAV,
60
54
  )
61
55
  from mx_bluesky.hyperion.parameters.constants import CONST
62
56
  from mx_bluesky.hyperion.parameters.device_composites import (
57
+ GridDetectThenXRayCentreComposite,
63
58
  HyperionFlyScanXRayCentreComposite,
64
59
  )
65
60
  from mx_bluesky.hyperion.parameters.gridscan import (
@@ -68,40 +63,12 @@ from mx_bluesky.hyperion.parameters.gridscan import (
68
63
  )
69
64
 
70
65
 
71
- @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
72
- class GridDetectThenXRayCentreComposite:
73
- """All devices which are directly or indirectly required by this plan"""
74
-
75
- aperture_scatterguard: ApertureScatterguard
76
- attenuator: BinaryFilterAttenuator
77
- backlight: Backlight
78
- beamstop: Beamstop
79
- dcm: DCM
80
- detector_motion: DetectorMotion
81
- eiger: EigerDetector
82
- zebra_fast_grid_scan: ZebraFastGridScan
83
- flux: Flux
84
- oav: OAV
85
- pin_tip_detection: PinTipDetection
86
- smargon: Smargon
87
- synchrotron: Synchrotron
88
- s4_slit_gaps: S4SlitGaps
89
- undulator: Undulator
90
- xbpm_feedback: XBPMFeedback
91
- zebra: Zebra
92
- zocalo: ZocaloResults
93
- panda: HDFPanda
94
- panda_fast_grid_scan: PandAFastGridScan
95
- robot: BartRobot
96
- sample_shutter: ZebraShutter
97
-
98
-
99
66
  def create_devices(context: BlueskyContext) -> GridDetectThenXRayCentreComposite:
100
67
  return device_composite_from_context(context, GridDetectThenXRayCentreComposite)
101
68
 
102
69
 
103
70
  def create_parameters_for_flyscan_xray_centre(
104
- grid_scan_with_edge_params: GridScanWithEdgeDetect,
71
+ grid_scan_with_edge_params: GridCommon,
105
72
  grid_parameters: GridParamUpdate,
106
73
  ) -> HyperionSpecifiedThreeDGridScan:
107
74
  params_json = grid_scan_with_edge_params.model_dump()
@@ -113,7 +80,7 @@ def create_parameters_for_flyscan_xray_centre(
113
80
 
114
81
  def detect_grid_and_do_gridscan(
115
82
  composite: GridDetectThenXRayCentreComposite,
116
- parameters: GridScanWithEdgeDetect,
83
+ parameters: GridCommon,
117
84
  oav_params: OAVParameters,
118
85
  ):
119
86
  snapshot_template = f"{parameters.detector_params.prefix}_{parameters.detector_params.run_number}_{{angle}}"
@@ -142,12 +109,19 @@ def detect_grid_and_do_gridscan(
142
109
  parameters.box_size_um,
143
110
  )
144
111
 
112
+ yield from setup_beamline_for_OAV(
113
+ composite.smargon,
114
+ composite.backlight,
115
+ composite.aperture_scatterguard,
116
+ wait=True,
117
+ )
118
+
145
119
  if parameters.selected_aperture:
146
120
  # Start moving the aperture/scatterguard into position without moving it in
147
121
  yield from bps.prepare(
148
122
  composite.aperture_scatterguard,
149
123
  parameters.selected_aperture,
150
- group=CONST.WAIT.GRID_READY_FOR_DC,
124
+ group=CONST.WAIT.PREPARE_APERTURE,
151
125
  )
152
126
 
153
127
  yield from run_grid_detection_plan(
@@ -160,38 +134,57 @@ def detect_grid_and_do_gridscan(
160
134
  composite.backlight, BacklightPosition.OUT, group=CONST.WAIT.GRID_READY_FOR_DC
161
135
  )
162
136
 
137
+ yield from bps.wait(CONST.WAIT.PREPARE_APERTURE)
163
138
  yield from move_aperture_if_required(
164
139
  composite.aperture_scatterguard,
165
140
  parameters.selected_aperture,
166
141
  group=CONST.WAIT.GRID_READY_FOR_DC,
167
142
  )
168
143
 
169
- yield from flyscan_xray_centre_no_move(
170
- HyperionFlyScanXRayCentreComposite(
171
- aperture_scatterguard=composite.aperture_scatterguard,
172
- attenuator=composite.attenuator,
173
- backlight=composite.backlight,
174
- eiger=composite.eiger,
175
- panda_fast_grid_scan=composite.panda_fast_grid_scan,
176
- flux=composite.flux,
177
- s4_slit_gaps=composite.s4_slit_gaps,
178
- smargon=composite.smargon,
179
- undulator=composite.undulator,
180
- synchrotron=composite.synchrotron,
181
- xbpm_feedback=composite.xbpm_feedback,
182
- zebra=composite.zebra,
183
- zocalo=composite.zocalo,
184
- panda=composite.panda,
185
- zebra_fast_grid_scan=composite.zebra_fast_grid_scan,
186
- dcm=composite.dcm,
187
- robot=composite.robot,
188
- sample_shutter=composite.sample_shutter,
189
- ),
190
- create_parameters_for_flyscan_xray_centre(
191
- parameters, grid_params_callback.get_grid_parameters()
192
- ),
144
+ xrc_composite = HyperionFlyScanXRayCentreComposite(
145
+ aperture_scatterguard=composite.aperture_scatterguard,
146
+ attenuator=composite.attenuator,
147
+ backlight=composite.backlight,
148
+ eiger=composite.eiger,
149
+ panda_fast_grid_scan=composite.panda_fast_grid_scan,
150
+ flux=composite.flux,
151
+ s4_slit_gaps=composite.s4_slit_gaps,
152
+ smargon=composite.smargon,
153
+ undulator=composite.undulator,
154
+ synchrotron=composite.synchrotron,
155
+ xbpm_feedback=composite.xbpm_feedback,
156
+ zebra=composite.zebra,
157
+ zocalo=composite.zocalo,
158
+ panda=composite.panda,
159
+ zebra_fast_grid_scan=composite.zebra_fast_grid_scan,
160
+ dcm=composite.dcm,
161
+ robot=composite.robot,
162
+ sample_shutter=composite.sample_shutter,
193
163
  )
194
164
 
165
+ params = create_parameters_for_flyscan_xray_centre(
166
+ parameters, grid_params_callback.get_grid_parameters()
167
+ )
168
+
169
+ beamline_specific = construct_hyperion_specific_features(xrc_composite, params)
170
+
171
+ yield from _gridscan_with_undulator_checks(xrc_composite, params, beamline_specific)
172
+
173
+
174
+ def _gridscan_with_undulator_checks(
175
+ composite: HyperionFlyScanXRayCentreComposite,
176
+ params: HyperionSpecifiedThreeDGridScan,
177
+ beamline_specific: BeamlineSpecificFGSFeatures,
178
+ ):
179
+ @transmission_and_xbpm_feedback_for_collection_decorator(
180
+ composite, params.transmission_frac
181
+ )
182
+ @verify_undulator_gap_before_run_decorator(composite)
183
+ def _inner():
184
+ yield from common_flyscan_xray_centre(composite, params, beamline_specific)
185
+
186
+ yield from _inner()
187
+
195
188
 
196
189
  def grid_detect_then_xray_centre(
197
190
  composite: GridDetectThenXRayCentreComposite,
@@ -0,0 +1,183 @@
1
+ from __future__ import annotations
2
+
3
+ from functools import partial
4
+ from pathlib import Path
5
+
6
+ import bluesky.plan_stubs as bps
7
+ from bluesky.utils import MsgGenerator
8
+ from dodal.devices.fast_grid_scan import (
9
+ set_fast_grid_scan_params,
10
+ )
11
+
12
+ from mx_bluesky.common.plans.common_flyscan_xray_centre_plan import (
13
+ construct_beamline_specific_FGS_features,
14
+ )
15
+ from mx_bluesky.common.utils.log import LOGGER
16
+ from mx_bluesky.hyperion.device_setup_plans.setup_panda import (
17
+ disarm_panda_for_gridscan,
18
+ set_panda_directory,
19
+ setup_panda_for_flyscan,
20
+ )
21
+ from mx_bluesky.hyperion.device_setup_plans.setup_zebra import (
22
+ setup_zebra_for_gridscan,
23
+ setup_zebra_for_panda_flyscan,
24
+ tidy_up_zebra_after_gridscan,
25
+ )
26
+ from mx_bluesky.hyperion.parameters.device_composites import (
27
+ HyperionFlyScanXRayCentreComposite,
28
+ )
29
+ from mx_bluesky.hyperion.parameters.gridscan import HyperionSpecifiedThreeDGridScan
30
+
31
+
32
+ class SmargonSpeedException(Exception):
33
+ pass
34
+
35
+
36
+ def construct_hyperion_specific_features(
37
+ fgs_composite: HyperionFlyScanXRayCentreComposite,
38
+ parameters: HyperionSpecifiedThreeDGridScan,
39
+ ):
40
+ """
41
+ Get all the information needed to do the Hyperion-specific parts of the XRC flyscan.
42
+ """
43
+
44
+ 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,
53
+ ]
54
+
55
+ 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,
61
+ ]
62
+
63
+ if parameters.features.use_panda_for_gridscan:
64
+ setup_trigger_plan = _panda_triggering_setup
65
+ tidy_plan = _panda_tidy
66
+ set_flyscan_params_plan = partial(
67
+ set_fast_grid_scan_params,
68
+ fgs_composite.panda_fast_grid_scan,
69
+ parameters.panda_FGS_params,
70
+ )
71
+ fgs_motors = fgs_composite.panda_fast_grid_scan
72
+
73
+ else:
74
+ setup_trigger_plan = _zebra_triggering_setup
75
+ tidy_plan = partial(_generic_tidy, group="flyscan_zebra_tidy", wait=True)
76
+ set_flyscan_params_plan = partial(
77
+ set_fast_grid_scan_params,
78
+ fgs_composite.zebra_fast_grid_scan,
79
+ parameters.FGS_params,
80
+ )
81
+ fgs_motors = fgs_composite.zebra_fast_grid_scan
82
+ return construct_beamline_specific_FGS_features(
83
+ setup_trigger_plan,
84
+ tidy_plan,
85
+ set_flyscan_params_plan,
86
+ fgs_motors,
87
+ signals_to_read_pre_flyscan,
88
+ signals_to_read_during_collection,
89
+ get_xrc_results_from_zocalo=True,
90
+ )
91
+
92
+
93
+ def _generic_tidy(
94
+ fgs_composite: HyperionFlyScanXRayCentreComposite, group, wait=True
95
+ ) -> MsgGenerator:
96
+ LOGGER.info("Tidying up Zebra")
97
+ yield from tidy_up_zebra_after_gridscan(
98
+ fgs_composite.zebra, fgs_composite.sample_shutter, group=group, wait=wait
99
+ )
100
+ LOGGER.info("Tidying up Zocalo")
101
+ # make sure we don't consume any other results
102
+ yield from bps.unstage(fgs_composite.zocalo, group=group, wait=wait)
103
+
104
+ # Turn off dev/shm streaming to avoid filling disk, see https://github.com/DiamondLightSource/hyperion/issues/1395
105
+ 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)
107
+
108
+
109
+ def _panda_tidy(fgs_composite: HyperionFlyScanXRayCentreComposite):
110
+ group = "panda_flyscan_tidy"
111
+ 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)
114
+ yield from bps.wait(group, timeout=10)
115
+ yield from bps.unstage(fgs_composite.panda)
116
+
117
+
118
+ def _zebra_triggering_setup(
119
+ fgs_composite: HyperionFlyScanXRayCentreComposite,
120
+ parameters: HyperionSpecifiedThreeDGridScan,
121
+ ) -> MsgGenerator:
122
+ yield from setup_zebra_for_gridscan(
123
+ fgs_composite.zebra, fgs_composite.sample_shutter, wait=True
124
+ )
125
+
126
+
127
+ def _panda_triggering_setup(
128
+ fgs_composite: HyperionFlyScanXRayCentreComposite,
129
+ parameters: HyperionSpecifiedThreeDGridScan,
130
+ ) -> MsgGenerator:
131
+ LOGGER.info("Setting up Panda for flyscan")
132
+
133
+ run_up_distance_mm = yield from bps.rd(
134
+ fgs_composite.panda_fast_grid_scan.run_up_distance_mm
135
+ )
136
+
137
+ # Set the time between x steps pv
138
+ DEADTIME_S = 1e-6 # according to https://www.dectris.com/en/detectors/x-ray-detectors/eiger2/eiger2-for-synchrotrons/eiger2-x/
139
+
140
+ time_between_x_steps_ms = (DEADTIME_S + parameters.exposure_time_s) * 1e3
141
+
142
+ smargon_speed_limit_mm_per_s = yield from bps.rd(
143
+ fgs_composite.smargon.x.max_velocity
144
+ )
145
+
146
+ sample_velocity_mm_per_s = (
147
+ parameters.panda_FGS_params.x_step_size_mm * 1e3 / time_between_x_steps_ms
148
+ )
149
+ if sample_velocity_mm_per_s > smargon_speed_limit_mm_per_s:
150
+ raise SmargonSpeedException(
151
+ f"Smargon speed was calculated from x step size\
152
+ {parameters.panda_FGS_params.x_step_size_mm}mm and\
153
+ time_between_x_steps_ms {time_between_x_steps_ms} as\
154
+ {sample_velocity_mm_per_s}mm/s. The smargon's speed limit is\
155
+ {smargon_speed_limit_mm_per_s}mm/s."
156
+ )
157
+ else:
158
+ LOGGER.info(
159
+ f"Panda grid scan: Smargon speed set to {smargon_speed_limit_mm_per_s} mm/s"
160
+ f" and using a run-up distance of {run_up_distance_mm}"
161
+ )
162
+
163
+ 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
165
+ time_between_x_steps_ms, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
166
+ )
167
+
168
+ directory_provider_root = Path(parameters.storage_directory)
169
+ yield from set_panda_directory(directory_provider_root)
170
+
171
+ yield from setup_panda_for_flyscan(
172
+ fgs_composite.panda,
173
+ parameters.panda_FGS_params,
174
+ fgs_composite.smargon,
175
+ parameters.exposure_time_s,
176
+ time_between_x_steps_ms,
177
+ sample_velocity_mm_per_s,
178
+ )
179
+
180
+ LOGGER.info("Setting up Zebra for panda flyscan")
181
+ yield from setup_zebra_for_panda_flyscan(
182
+ fgs_composite.zebra, fgs_composite.sample_shutter, wait=True
183
+ )
@@ -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,
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,8 +51,11 @@ 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")
58
+ oav_config_file = oav_params.oav_config_json
53
59
 
54
60
  @set_run_key_decorator(CONST.PLAN.LOAD_CENTRE_COLLECT)
55
61
  @run_decorator(
@@ -64,7 +70,9 @@ def load_centre_collect_full(
64
70
  def plan_with_callback_subs():
65
71
  flyscan_event_handler = XRayCentreEventHandler()
66
72
  yield from subs_wrapper(
67
- robot_load_then_xray_centre(composite, parameters.robot_load_then_centre),
73
+ robot_load_then_xray_centre(
74
+ composite, parameters.robot_load_then_centre, oav_config_file
75
+ ),
68
76
  flyscan_event_handler,
69
77
  )
70
78
 
@@ -83,32 +91,59 @@ def load_centre_collect_full(
83
91
  else:
84
92
  # If the xray centring hasn't found a result but has not thrown an error it
85
93
  # means that we do not need to recentre and can collect where we are
86
- initial_x = yield from bps.rd(composite.smargon.x.user_readback)
87
- initial_y = yield from bps.rd(composite.smargon.y.user_readback)
88
- initial_z = yield from bps.rd(composite.smargon.z.user_readback)
94
+ initial_x_mm = yield from bps.rd(composite.smargon.x.user_readback)
95
+ initial_y_mm = yield from bps.rd(composite.smargon.y.user_readback)
96
+ initial_z_mm = yield from bps.rd(composite.smargon.z.user_readback)
89
97
 
90
- locations_to_collect_um = [np.array([initial_x, initial_y, initial_z])]
98
+ locations_to_collect_um = [
99
+ np.array([initial_x_mm, initial_y_mm, initial_z_mm]) * 1000
100
+ ]
91
101
 
92
102
  multi_rotation = parameters.multi_rotation_scan
93
103
  rotation_template = multi_rotation.rotation_scans.copy()
94
104
 
95
105
  multi_rotation.rotation_scans.clear()
96
106
 
107
+ is_alternating = parameters.features.alternate_rotation_direction
108
+
109
+ generator = rotation_scan_generator(is_alternating)
110
+ next(generator)
97
111
  for location in locations_to_collect_um:
98
112
  for rot in rotation_template:
99
- combination = rot.model_copy()
100
- (
101
- combination.x_start_um,
102
- combination.y_start_um,
103
- combination.z_start_um,
104
- ) = location
113
+ combination = generator.send((rot, location))
105
114
  multi_rotation.rotation_scans.append(combination)
106
- multi_rotation = MultiRotationScan.model_validate(multi_rotation)
115
+ multi_rotation = RotationScan.model_validate(multi_rotation)
107
116
 
108
117
  assert (
109
118
  multi_rotation.demand_energy_ev
110
119
  == parameters.robot_load_then_centre.demand_energy_ev
111
120
  ), "Setting a different energy for gridscan and rotation is not supported"
112
- yield from multi_rotation_scan(composite, multi_rotation, oav_params)
121
+ yield from rotation_scan_internal(composite, multi_rotation, oav_params)
113
122
 
114
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