mx-bluesky 0.3.1__py3-none-any.whl → 1.2.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 (142) hide show
  1. mx_bluesky/_version.py +2 -2
  2. mx_bluesky/beamlines/i04/__init__.py +3 -0
  3. mx_bluesky/{i04 → beamlines/i04}/thawing_plan.py +5 -4
  4. mx_bluesky/{i24 → beamlines/i24}/serial/blueapi_config.yaml +1 -1
  5. mx_bluesky/{i24 → beamlines/i24}/serial/dcid.py +2 -2
  6. mx_bluesky/{i24 → beamlines/i24}/serial/extruder/EX-gui-edm/DetStage.edl +3 -3
  7. mx_bluesky/{i24 → beamlines/i24}/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +7 -7
  8. mx_bluesky/{i24 → beamlines/i24}/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +12 -9
  9. mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/CustomChip_py3v1.edl +3 -3
  10. mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/DetStage.edl +3 -3
  11. mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +245 -200
  12. mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/MappingLite-oxford_py3v1.edl +4 -4
  13. mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +8 -8
  14. mx_bluesky/beamlines/i24/serial/fixed_target/__init__.py +0 -0
  15. mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +80 -70
  16. mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +20 -21
  17. mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +5 -5
  18. mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +7 -4
  19. mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_moveonclick.py +59 -39
  20. mx_bluesky/{i24 → beamlines/i24}/serial/log.py +1 -9
  21. mx_bluesky/beamlines/i24/serial/parameters/__init__.py +15 -0
  22. mx_bluesky/{i24 → beamlines/i24}/serial/parameters/constants.py +1 -1
  23. mx_bluesky/{i24 → beamlines/i24}/serial/parameters/experiment_parameters.py +4 -25
  24. mx_bluesky/{i24 → beamlines/i24}/serial/parameters/utils.py +5 -3
  25. mx_bluesky/{i24 → beamlines/i24}/serial/run_serial.py +1 -1
  26. mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/pv_abstract.py +1 -1
  27. mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/setup_beamline.py +2 -2
  28. mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/setup_detector.py +5 -5
  29. mx_bluesky/{i24 → beamlines/i24}/serial/write_nexus.py +6 -3
  30. mx_bluesky/hyperion/__init__.py +1 -0
  31. mx_bluesky/hyperion/__main__.py +374 -0
  32. mx_bluesky/hyperion/device_setup_plans/__init__.py +0 -0
  33. mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +134 -0
  34. mx_bluesky/hyperion/device_setup_plans/manipulate_sample.py +110 -0
  35. mx_bluesky/hyperion/device_setup_plans/position_detector.py +16 -0
  36. mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +60 -0
  37. mx_bluesky/hyperion/device_setup_plans/setup_oav.py +87 -0
  38. mx_bluesky/hyperion/device_setup_plans/setup_panda.py +210 -0
  39. mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +214 -0
  40. mx_bluesky/hyperion/device_setup_plans/smargon.py +25 -0
  41. mx_bluesky/hyperion/device_setup_plans/utils.py +55 -0
  42. mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +93 -0
  43. mx_bluesky/hyperion/exceptions.py +47 -0
  44. mx_bluesky/hyperion/experiment_plans/__init__.py +30 -0
  45. mx_bluesky/hyperion/experiment_plans/experiment_registry.py +93 -0
  46. mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +537 -0
  47. mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +209 -0
  48. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +46 -0
  49. mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +173 -0
  50. mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +81 -0
  51. mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +463 -0
  52. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +119 -0
  53. mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +164 -0
  54. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +237 -0
  55. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +162 -0
  56. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +436 -0
  57. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +60 -0
  58. mx_bluesky/hyperion/external_interaction/__init__.py +9 -0
  59. mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +10 -0
  60. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +148 -0
  61. mx_bluesky/hyperion/external_interaction/callbacks/aperture_change_callback.py +22 -0
  62. mx_bluesky/hyperion/external_interaction/callbacks/common/__init__.py +0 -0
  63. mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +64 -0
  64. mx_bluesky/hyperion/external_interaction/callbacks/common/ispyb_mapping.py +62 -0
  65. mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +88 -0
  66. mx_bluesky/hyperion/external_interaction/callbacks/ispyb_callback_base.py +203 -0
  67. mx_bluesky/hyperion/external_interaction/callbacks/log_uid_tag_callback.py +20 -0
  68. mx_bluesky/hyperion/external_interaction/callbacks/logging_callback.py +29 -0
  69. mx_bluesky/hyperion/external_interaction/callbacks/plan_reactive_callback.py +101 -0
  70. mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +86 -0
  71. mx_bluesky/hyperion/external_interaction/callbacks/rotation/__init__.py +0 -0
  72. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +174 -0
  73. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +17 -0
  74. mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +102 -0
  75. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/__init__.py +0 -0
  76. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +269 -0
  77. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_mapping.py +53 -0
  78. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +95 -0
  79. mx_bluesky/hyperion/external_interaction/callbacks/zocalo_callback.py +92 -0
  80. mx_bluesky/hyperion/external_interaction/config_server.py +35 -0
  81. mx_bluesky/hyperion/external_interaction/exceptions.py +13 -0
  82. mx_bluesky/hyperion/external_interaction/ispyb/__init__.py +0 -0
  83. mx_bluesky/hyperion/external_interaction/ispyb/data_model.py +95 -0
  84. mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +125 -0
  85. mx_bluesky/hyperion/external_interaction/ispyb/ispyb_store.py +276 -0
  86. mx_bluesky/hyperion/external_interaction/ispyb/ispyb_utils.py +27 -0
  87. mx_bluesky/hyperion/external_interaction/nexus/__init__.py +0 -0
  88. mx_bluesky/hyperion/external_interaction/nexus/nexus_utils.py +148 -0
  89. mx_bluesky/hyperion/external_interaction/nexus/write_nexus.py +114 -0
  90. mx_bluesky/hyperion/log.py +99 -0
  91. mx_bluesky/hyperion/parameters/__init__.py +2 -0
  92. mx_bluesky/hyperion/parameters/cli.py +68 -0
  93. mx_bluesky/{parameters → hyperion/parameters}/components.py +80 -26
  94. mx_bluesky/hyperion/parameters/constants.py +158 -0
  95. mx_bluesky/hyperion/parameters/gridscan.py +221 -0
  96. mx_bluesky/hyperion/parameters/load_centre_collect.py +50 -0
  97. mx_bluesky/hyperion/parameters/robot_load.py +16 -0
  98. mx_bluesky/hyperion/parameters/rotation.py +160 -0
  99. mx_bluesky/hyperion/resources/panda/panda-gridscan.yaml +964 -0
  100. mx_bluesky/hyperion/tracing.py +28 -0
  101. mx_bluesky/hyperion/utils/context.py +84 -0
  102. mx_bluesky/hyperion/utils/utils.py +25 -0
  103. mx_bluesky/hyperion/utils/validation.py +196 -0
  104. mx_bluesky/jupyter_example.ipynb +3 -2
  105. {mx_bluesky-0.3.1.dist-info → mx_bluesky-1.2.0.dist-info}/METADATA +26 -11
  106. mx_bluesky-1.2.0.dist-info/RECORD +140 -0
  107. {mx_bluesky-0.3.1.dist-info → mx_bluesky-1.2.0.dist-info}/WHEEL +1 -1
  108. mx_bluesky-1.2.0.dist-info/entry_points.txt +8 -0
  109. mx_bluesky/i04/__init__.py +0 -3
  110. mx_bluesky/i24/serial/parameters/__init__.py +0 -15
  111. mx_bluesky/parameters/__init__.py +0 -31
  112. mx_bluesky-0.3.1.dist-info/RECORD +0 -67
  113. mx_bluesky-0.3.1.dist-info/entry_points.txt +0 -4
  114. /mx_bluesky/{i24 → beamlines}/__init__.py +0 -0
  115. /mx_bluesky/{i04 → beamlines/i04}/callbacks/murko_callback.py +0 -0
  116. /mx_bluesky/{i24/serial/extruder → beamlines/i24}/__init__.py +0 -0
  117. /mx_bluesky/{i24 → beamlines/i24}/serial/__init__.py +0 -0
  118. /mx_bluesky/{i24 → beamlines/i24}/serial/extruder/EX-gui-edm/microdrop_alignment.edl +0 -0
  119. /mx_bluesky/{i24/serial/fixed_target → beamlines/i24/serial/extruder}/__init__.py +0 -0
  120. /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/ME14E-GeneralPurpose.edl +0 -0
  121. /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/PMAC_Command.edl +0 -0
  122. /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/Shutter_Control.edl +0 -0
  123. /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/microdrop_alignment.edl +0 -0
  124. /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/nudgechip.edl +0 -0
  125. /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/short1-laser.png +0 -0
  126. /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/short2-laser.png +0 -0
  127. /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/ft_utils.py +0 -0
  128. /mx_bluesky/{i24 → beamlines/i24}/serial/parameters/fixed_target/cs/cs_maker.json +0 -0
  129. /mx_bluesky/{i24 → beamlines/i24}/serial/parameters/fixed_target/cs/motor_direction.txt +0 -0
  130. /mx_bluesky/{i24 → beamlines/i24}/serial/parameters/fixed_target/pvar_files/minichip-oxford.pvar +0 -0
  131. /mx_bluesky/{i24 → beamlines/i24}/serial/parameters/fixed_target/pvar_files/oxford.pvar +0 -0
  132. /mx_bluesky/{i24 → beamlines/i24}/serial/run_extruder.sh +0 -0
  133. /mx_bluesky/{i24 → beamlines/i24}/serial/run_fixed_target.sh +0 -0
  134. /mx_bluesky/{i24 → beamlines/i24}/serial/run_ssx.sh +0 -0
  135. /mx_bluesky/{i24 → beamlines/i24}/serial/set_visit_directory.sh +0 -0
  136. /mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/__init__.py +0 -0
  137. /mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/ca.py +0 -0
  138. /mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/pv.py +0 -0
  139. /mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/setup_zebra_plans.py +0 -0
  140. /mx_bluesky/{i24 → beamlines/i24}/serial/start_blueapi.sh +0 -0
  141. {mx_bluesky-0.3.1.dist-info → mx_bluesky-1.2.0.dist-info}/LICENSE +0 -0
  142. {mx_bluesky-0.3.1.dist-info → mx_bluesky-1.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,537 @@
1
+ from __future__ import annotations
2
+
3
+ import dataclasses
4
+ from collections.abc import Callable
5
+ from functools import partial
6
+ from pathlib import Path
7
+ from time import time
8
+ from typing import Protocol
9
+
10
+ import bluesky.plan_stubs as bps
11
+ import bluesky.preprocessors as bpp
12
+ import numpy as np
13
+ from blueapi.core import BlueskyContext, MsgGenerator
14
+ from dodal.devices.aperturescatterguard import (
15
+ ApertureScatterguard,
16
+ ApertureValue,
17
+ )
18
+ from dodal.devices.attenuator import Attenuator
19
+ from dodal.devices.backlight import Backlight
20
+ from dodal.devices.dcm import DCM
21
+ from dodal.devices.eiger import EigerDetector
22
+ from dodal.devices.fast_grid_scan import (
23
+ FastGridScanCommon,
24
+ PandAFastGridScan,
25
+ ZebraFastGridScan,
26
+ )
27
+ from dodal.devices.fast_grid_scan import (
28
+ set_fast_grid_scan_params as set_flyscan_params,
29
+ )
30
+ from dodal.devices.flux import Flux
31
+ from dodal.devices.robot import BartRobot
32
+ from dodal.devices.s4_slit_gaps import S4SlitGaps
33
+ from dodal.devices.smargon import Smargon, StubPosition
34
+ from dodal.devices.synchrotron import Synchrotron
35
+ from dodal.devices.undulator import Undulator
36
+ from dodal.devices.xbpm_feedback import XBPMFeedback
37
+ from dodal.devices.zebra import Zebra
38
+ from dodal.devices.zebra_controlled_shutter import ZebraShutter
39
+ from dodal.devices.zocalo.zocalo_results import (
40
+ ZOCALO_READING_PLAN_NAME,
41
+ ZOCALO_STAGE_GROUP,
42
+ ZocaloResults,
43
+ get_processing_result,
44
+ )
45
+ from dodal.plans.check_topup import check_topup_and_wait_if_necessary
46
+ from ophyd_async.fastcs.panda import HDFPanda
47
+ from scanspec.core import AxesPoints, Axis
48
+
49
+ from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import move_x_y_z
50
+ from mx_bluesky.hyperion.device_setup_plans.read_hardware_for_setup import (
51
+ read_hardware_during_collection,
52
+ read_hardware_for_zocalo,
53
+ read_hardware_pre_collection,
54
+ )
55
+ from mx_bluesky.hyperion.device_setup_plans.setup_panda import (
56
+ disarm_panda_for_gridscan,
57
+ set_panda_directory,
58
+ setup_panda_for_flyscan,
59
+ )
60
+ from mx_bluesky.hyperion.device_setup_plans.setup_zebra import (
61
+ setup_zebra_for_gridscan,
62
+ setup_zebra_for_panda_flyscan,
63
+ tidy_up_zebra_after_gridscan,
64
+ )
65
+ from mx_bluesky.hyperion.device_setup_plans.xbpm_feedback import (
66
+ transmission_and_xbpm_feedback_for_collection_decorator,
67
+ )
68
+ from mx_bluesky.hyperion.exceptions import WarningException
69
+ from mx_bluesky.hyperion.log import LOGGER
70
+ from mx_bluesky.hyperion.parameters.constants import CONST
71
+ from mx_bluesky.hyperion.parameters.gridscan import ThreeDGridScan
72
+ from mx_bluesky.hyperion.tracing import TRACER
73
+ from mx_bluesky.hyperion.utils.context import device_composite_from_context
74
+
75
+
76
+ class SmargonSpeedException(Exception):
77
+ pass
78
+
79
+
80
+ class CrystalNotFoundException(WarningException):
81
+ """Raised if grid detection completed normally but no crystal was found."""
82
+
83
+ pass
84
+
85
+
86
+ @dataclasses.dataclass
87
+ class FlyScanXRayCentreComposite:
88
+ """All devices which are directly or indirectly required by this plan"""
89
+
90
+ aperture_scatterguard: ApertureScatterguard
91
+ attenuator: Attenuator
92
+ backlight: Backlight
93
+ dcm: DCM
94
+ eiger: EigerDetector
95
+ zebra_fast_grid_scan: ZebraFastGridScan
96
+ flux: Flux
97
+ s4_slit_gaps: S4SlitGaps
98
+ smargon: Smargon
99
+ undulator: Undulator
100
+ synchrotron: Synchrotron
101
+ xbpm_feedback: XBPMFeedback
102
+ zebra: Zebra
103
+ zocalo: ZocaloResults
104
+ panda: HDFPanda
105
+ panda_fast_grid_scan: PandAFastGridScan
106
+ robot: BartRobot
107
+ sample_shutter: ZebraShutter
108
+
109
+ @property
110
+ def sample_motors(self) -> Smargon:
111
+ """Convenience alias with a more user-friendly name"""
112
+ return self.smargon
113
+
114
+
115
+ def create_devices(context: BlueskyContext) -> FlyScanXRayCentreComposite:
116
+ """Creates the devices required for the plan and connect to them"""
117
+ return device_composite_from_context(context, FlyScanXRayCentreComposite)
118
+
119
+
120
+ def flyscan_xray_centre(
121
+ composite: FlyScanXRayCentreComposite,
122
+ parameters: ThreeDGridScan,
123
+ ) -> MsgGenerator:
124
+ """Create the plan to run the grid scan based on provided parameters.
125
+
126
+ The ispyb handler should be added to the whole gridscan as we want to capture errors
127
+ at any point in it.
128
+
129
+ Args:
130
+ parameters (ThreeDGridScan): The parameters to run the scan.
131
+
132
+ Returns:
133
+ Generator: The plan for the gridscan
134
+ """
135
+ parameters.features.update_self_from_server()
136
+ composite.eiger.set_detector_parameters(parameters.detector_params)
137
+ composite.zocalo.zocalo_environment = parameters.zocalo_environment
138
+ composite.zocalo.use_cpu_and_gpu = parameters.use_cpu_and_gpu_zocalo
139
+
140
+ feature_controlled = _get_feature_controlled(composite, parameters)
141
+
142
+ @bpp.set_run_key_decorator(CONST.PLAN.GRIDSCAN_OUTER)
143
+ @bpp.run_decorator( # attach experiment metadata to the start document
144
+ md={
145
+ "subplan_name": CONST.PLAN.GRIDSCAN_OUTER,
146
+ CONST.TRIGGER.ZOCALO: CONST.PLAN.DO_FGS,
147
+ "zocalo_environment": parameters.zocalo_environment,
148
+ "hyperion_parameters": parameters.model_dump_json(),
149
+ "activate_callbacks": [
150
+ "GridscanNexusFileCallback",
151
+ ],
152
+ }
153
+ )
154
+ @bpp.finalize_decorator(lambda: feature_controlled.tidy_plan(composite))
155
+ @transmission_and_xbpm_feedback_for_collection_decorator(
156
+ composite.xbpm_feedback,
157
+ composite.attenuator,
158
+ parameters.transmission_frac,
159
+ )
160
+ def run_gridscan_and_move_and_tidy(
161
+ fgs_composite: FlyScanXRayCentreComposite,
162
+ params: ThreeDGridScan,
163
+ feature_controlled: _FeatureControlled,
164
+ ):
165
+ yield from run_gridscan_and_move(fgs_composite, params, feature_controlled)
166
+
167
+ return run_gridscan_and_move_and_tidy(composite, parameters, feature_controlled)
168
+
169
+
170
+ @bpp.set_run_key_decorator(CONST.PLAN.GRIDSCAN_AND_MOVE)
171
+ @bpp.run_decorator(md={"subplan_name": CONST.PLAN.GRIDSCAN_AND_MOVE})
172
+ def run_gridscan_and_move(
173
+ fgs_composite: FlyScanXRayCentreComposite,
174
+ parameters: ThreeDGridScan,
175
+ feature_controlled: _FeatureControlled,
176
+ ) -> MsgGenerator:
177
+ """A multi-run plan which runs a gridscan, gets the results from zocalo
178
+ and moves to the centre of mass determined by zocalo"""
179
+
180
+ # We get the initial motor positions so we can return to them on zocalo failure
181
+ initial_xyz = np.array(
182
+ [
183
+ (yield from bps.rd(fgs_composite.sample_motors.x)),
184
+ (yield from bps.rd(fgs_composite.sample_motors.y)),
185
+ (yield from bps.rd(fgs_composite.sample_motors.z)),
186
+ ]
187
+ )
188
+
189
+ yield from feature_controlled.setup_trigger(fgs_composite, parameters, initial_xyz)
190
+
191
+ LOGGER.info("Starting grid scan")
192
+ yield from bps.stage(
193
+ fgs_composite.zocalo, group=ZOCALO_STAGE_GROUP
194
+ ) # connect to zocalo and make sure the queue is clear
195
+ yield from run_gridscan(fgs_composite, parameters, feature_controlled)
196
+
197
+ LOGGER.info("Grid scan finished, getting results.")
198
+
199
+ try:
200
+ with TRACER.start_span("wait_for_zocalo"):
201
+ yield from bps.trigger_and_read(
202
+ [fgs_composite.zocalo], name=ZOCALO_READING_PLAN_NAME
203
+ )
204
+ LOGGER.info("Zocalo triggered and read, interpreting results.")
205
+ xray_centre, bbox_size = yield from get_processing_result(
206
+ fgs_composite.zocalo
207
+ )
208
+ LOGGER.info(f"Got xray centre: {xray_centre}, bbox size: {bbox_size}")
209
+ if xray_centre is not None:
210
+ xray_centre = parameters.FGS_params.grid_position_to_motor_position(
211
+ xray_centre
212
+ )
213
+ else:
214
+ LOGGER.warning("No X-ray centre received")
215
+ raise CrystalNotFoundException()
216
+ if bbox_size is not None:
217
+ with TRACER.start_span("change_aperture"):
218
+ yield from set_aperture_for_bbox_size(
219
+ fgs_composite.aperture_scatterguard, bbox_size
220
+ )
221
+ else:
222
+ LOGGER.warning("No bounding box size received")
223
+
224
+ # once we have the results, go to the appropriate position
225
+ LOGGER.info("Moving to centre of mass.")
226
+ with TRACER.start_span("move_to_result"):
227
+ x, y, z = xray_centre
228
+ yield from move_x_y_z(fgs_composite.sample_motors, x, y, z, wait=True)
229
+
230
+ if parameters.FGS_params.set_stub_offsets:
231
+ LOGGER.info("Recentring smargon co-ordinate system to this point.")
232
+ yield from bps.mv(
233
+ fgs_composite.sample_motors.stub_offsets, StubPosition.CURRENT_AS_CENTER
234
+ )
235
+ finally:
236
+ # Turn off dev/shm streaming to avoid filling disk, see https://github.com/DiamondLightSource/hyperion/issues/1395
237
+ LOGGER.info("Turning off Eiger dev/shm streaming")
238
+ yield from bps.abs_set(fgs_composite.eiger.odin.fan.dev_shm_enable, 0)
239
+
240
+ # Wait on everything before returning to GDA (particularly apertures), can be removed
241
+ # when we do not return to GDA here
242
+ yield from bps.wait()
243
+
244
+
245
+ @bpp.set_run_key_decorator(CONST.PLAN.GRIDSCAN_MAIN)
246
+ @bpp.run_decorator(md={"subplan_name": CONST.PLAN.GRIDSCAN_MAIN})
247
+ def run_gridscan(
248
+ fgs_composite: FlyScanXRayCentreComposite,
249
+ parameters: ThreeDGridScan,
250
+ feature_controlled: _FeatureControlled,
251
+ md={ # noqa
252
+ "plan_name": CONST.PLAN.GRIDSCAN_MAIN,
253
+ },
254
+ ):
255
+ sample_motors = fgs_composite.sample_motors
256
+
257
+ # Currently gridscan only works for omega 0, see #
258
+ with TRACER.start_span("moving_omega_to_0"):
259
+ yield from bps.abs_set(sample_motors.omega, 0)
260
+
261
+ # We only subscribe to the communicator callback for run_gridscan, so this is where
262
+ # we should generate an event reading the values which need to be included in the
263
+ # ispyb deposition
264
+ with TRACER.start_span("ispyb_hardware_readings"):
265
+ yield from read_hardware_pre_collection(
266
+ fgs_composite.undulator,
267
+ fgs_composite.synchrotron,
268
+ fgs_composite.s4_slit_gaps,
269
+ fgs_composite.robot,
270
+ fgs_composite.smargon,
271
+ )
272
+
273
+ read_during_collection = partial(
274
+ read_hardware_during_collection,
275
+ fgs_composite.aperture_scatterguard,
276
+ fgs_composite.attenuator,
277
+ fgs_composite.flux,
278
+ fgs_composite.dcm,
279
+ fgs_composite.eiger,
280
+ )
281
+
282
+ LOGGER.info("Setting fgs params")
283
+ yield from feature_controlled.set_flyscan_params()
284
+
285
+ LOGGER.info("Waiting for gridscan validity check")
286
+ yield from wait_for_gridscan_valid(feature_controlled.fgs_motors)
287
+
288
+ LOGGER.info("Waiting for arming to finish")
289
+ yield from bps.wait(CONST.WAIT.GRID_READY_FOR_DC)
290
+ yield from bps.stage(fgs_composite.eiger)
291
+
292
+ yield from kickoff_and_complete_gridscan(
293
+ feature_controlled.fgs_motors,
294
+ fgs_composite.eiger,
295
+ fgs_composite.synchrotron,
296
+ [parameters.scan_points_first_grid, parameters.scan_points_second_grid],
297
+ parameters.scan_indices,
298
+ do_during_run=read_during_collection,
299
+ )
300
+ yield from bps.abs_set(feature_controlled.fgs_motors.z_steps, 0, wait=False)
301
+
302
+
303
+ def kickoff_and_complete_gridscan(
304
+ gridscan: FastGridScanCommon,
305
+ eiger: EigerDetector,
306
+ synchrotron: Synchrotron,
307
+ scan_points: list[AxesPoints[Axis]],
308
+ scan_start_indices: list[int],
309
+ do_during_run: Callable[[], MsgGenerator] | None = None,
310
+ ):
311
+ @TRACER.start_as_current_span(CONST.PLAN.DO_FGS)
312
+ @bpp.set_run_key_decorator(CONST.PLAN.DO_FGS)
313
+ @bpp.run_decorator(
314
+ md={
315
+ "subplan_name": CONST.PLAN.DO_FGS,
316
+ "scan_points": scan_points,
317
+ "scan_start_indices": scan_start_indices,
318
+ }
319
+ )
320
+ @bpp.contingency_decorator(
321
+ except_plan=lambda e: (yield from bps.stop(eiger)),
322
+ else_plan=lambda: (yield from bps.unstage(eiger)),
323
+ )
324
+ def do_fgs():
325
+ # Check topup gate
326
+ expected_images = yield from bps.rd(gridscan.expected_images)
327
+ exposure_sec_per_image = yield from bps.rd(eiger.cam.acquire_time)
328
+ LOGGER.info("waiting for topup if necessary...")
329
+ yield from check_topup_and_wait_if_necessary(
330
+ synchrotron,
331
+ expected_images * exposure_sec_per_image,
332
+ 30.0,
333
+ )
334
+ yield from read_hardware_for_zocalo(eiger)
335
+ LOGGER.info("Wait for all moves with no assigned group")
336
+ yield from bps.wait()
337
+ LOGGER.info("kicking off FGS")
338
+ yield from bps.kickoff(gridscan, wait=True)
339
+ gridscan_start_time = time()
340
+ LOGGER.info("Waiting for Zocalo device queue to have been cleared...")
341
+ yield from bps.wait(
342
+ ZOCALO_STAGE_GROUP
343
+ ) # Make sure ZocaloResults queue is clear and ready to accept our new data
344
+ if do_during_run:
345
+ LOGGER.info(f"Running {do_during_run} during FGS")
346
+ yield from do_during_run()
347
+ LOGGER.info("completing FGS")
348
+ yield from bps.complete(gridscan, wait=True)
349
+
350
+ # Remove this logging statement once metrics have been added
351
+ LOGGER.info(
352
+ f"Gridscan motion program took {round(time()-gridscan_start_time,2)} to complete"
353
+ )
354
+
355
+ yield from do_fgs()
356
+
357
+
358
+ def wait_for_gridscan_valid(fgs_motors: FastGridScanCommon, timeout=0.5):
359
+ LOGGER.info("Waiting for valid fgs_params")
360
+ SLEEP_PER_CHECK = 0.1
361
+ times_to_check = int(timeout / SLEEP_PER_CHECK)
362
+ for _ in range(times_to_check):
363
+ scan_invalid = yield from bps.rd(fgs_motors.scan_invalid)
364
+ pos_counter = yield from bps.rd(fgs_motors.position_counter)
365
+ LOGGER.debug(
366
+ f"Scan invalid: {scan_invalid} and position counter: {pos_counter}"
367
+ )
368
+ if not scan_invalid and pos_counter == 0:
369
+ LOGGER.info("Gridscan scan valid and position counter reset")
370
+ return
371
+ yield from bps.sleep(SLEEP_PER_CHECK)
372
+ raise WarningException("Scan invalid - pin too long/short/bent and out of range")
373
+
374
+
375
+ def set_aperture_for_bbox_size(
376
+ aperture_device: ApertureScatterguard,
377
+ bbox_size: list[int] | np.ndarray,
378
+ ):
379
+ # bbox_size is [x,y,z], for i03 we only care about x
380
+ new_selected_aperture = (
381
+ ApertureValue.MEDIUM if bbox_size[0] < 2 else ApertureValue.LARGE
382
+ )
383
+ LOGGER.info(
384
+ f"Setting aperture to {new_selected_aperture} based on bounding box size {bbox_size}."
385
+ )
386
+
387
+ @bpp.set_run_key_decorator("change_aperture")
388
+ @bpp.run_decorator(
389
+ md={
390
+ "subplan_name": "change_aperture",
391
+ "aperture_size": new_selected_aperture.value,
392
+ }
393
+ )
394
+ def set_aperture():
395
+ yield from bps.abs_set(aperture_device, new_selected_aperture)
396
+
397
+ yield from set_aperture()
398
+
399
+
400
+ @dataclasses.dataclass
401
+ class _FeatureControlled:
402
+ class _ZebraSetup(Protocol):
403
+ def __call__(
404
+ self, zebra: Zebra, group="setup_zebra_for_gridscan", wait=True
405
+ ) -> MsgGenerator: ...
406
+
407
+ class _ExtraSetup(Protocol):
408
+ def __call__(
409
+ self,
410
+ fgs_composite: FlyScanXRayCentreComposite,
411
+ parameters: ThreeDGridScan,
412
+ initial_xyz: np.ndarray,
413
+ ) -> MsgGenerator: ...
414
+
415
+ setup_trigger: _ExtraSetup
416
+ tidy_plan: Callable[[FlyScanXRayCentreComposite], MsgGenerator]
417
+ set_flyscan_params: Callable[[], MsgGenerator]
418
+ fgs_motors: FastGridScanCommon
419
+
420
+
421
+ def _get_feature_controlled(
422
+ fgs_composite: FlyScanXRayCentreComposite,
423
+ parameters: ThreeDGridScan,
424
+ ):
425
+ if parameters.features.use_panda_for_gridscan:
426
+ return _FeatureControlled(
427
+ setup_trigger=_panda_triggering_setup,
428
+ tidy_plan=_panda_tidy,
429
+ set_flyscan_params=partial(
430
+ set_flyscan_params,
431
+ fgs_composite.panda_fast_grid_scan,
432
+ parameters.panda_FGS_params,
433
+ ),
434
+ fgs_motors=fgs_composite.panda_fast_grid_scan,
435
+ )
436
+ else:
437
+ return _FeatureControlled(
438
+ setup_trigger=_zebra_triggering_setup,
439
+ tidy_plan=partial(_generic_tidy, group="flyscan_zebra_tidy", wait=True),
440
+ set_flyscan_params=partial(
441
+ set_flyscan_params,
442
+ fgs_composite.zebra_fast_grid_scan,
443
+ parameters.FGS_params,
444
+ ),
445
+ fgs_motors=fgs_composite.zebra_fast_grid_scan,
446
+ )
447
+
448
+
449
+ def _generic_tidy(
450
+ fgs_composite: FlyScanXRayCentreComposite, group, wait=True
451
+ ) -> MsgGenerator:
452
+ LOGGER.info("Tidying up Zebra")
453
+ yield from tidy_up_zebra_after_gridscan(
454
+ fgs_composite.zebra, fgs_composite.sample_shutter, group=group, wait=wait
455
+ )
456
+ LOGGER.info("Tidying up Zocalo")
457
+ # make sure we don't consume any other results
458
+ yield from bps.unstage(fgs_composite.zocalo, group=group, wait=wait)
459
+
460
+
461
+ def _panda_tidy(fgs_composite: FlyScanXRayCentreComposite):
462
+ group = "panda_flyscan_tidy"
463
+ LOGGER.info("Disabling panda blocks")
464
+ yield from disarm_panda_for_gridscan(fgs_composite.panda, group)
465
+ yield from _generic_tidy(fgs_composite, group, False)
466
+ yield from bps.wait(group, timeout=10)
467
+ yield from bps.unstage(fgs_composite.panda)
468
+
469
+
470
+ def _zebra_triggering_setup(
471
+ fgs_composite: FlyScanXRayCentreComposite,
472
+ parameters: ThreeDGridScan,
473
+ initial_xyz: np.ndarray,
474
+ ):
475
+ yield from setup_zebra_for_gridscan(
476
+ fgs_composite.zebra, fgs_composite.sample_shutter, wait=True
477
+ )
478
+
479
+
480
+ def _panda_triggering_setup(
481
+ fgs_composite: FlyScanXRayCentreComposite,
482
+ parameters: ThreeDGridScan,
483
+ initial_xyz: np.ndarray,
484
+ ):
485
+ LOGGER.info("Setting up Panda for flyscan")
486
+
487
+ run_up_distance_mm = yield from bps.rd(
488
+ fgs_composite.panda_fast_grid_scan.run_up_distance_mm
489
+ )
490
+
491
+ # Set the time between x steps pv
492
+ DEADTIME_S = 1e-6 # according to https://www.dectris.com/en/detectors/x-ray-detectors/eiger2/eiger2-for-synchrotrons/eiger2-x/
493
+
494
+ time_between_x_steps_ms = (DEADTIME_S + parameters.exposure_time_s) * 1e3
495
+
496
+ smargon_speed_limit_mm_per_s = yield from bps.rd(
497
+ fgs_composite.smargon.x.max_velocity
498
+ )
499
+
500
+ sample_velocity_mm_per_s = (
501
+ parameters.panda_FGS_params.x_step_size * 1e3 / time_between_x_steps_ms
502
+ )
503
+ if sample_velocity_mm_per_s > smargon_speed_limit_mm_per_s:
504
+ raise SmargonSpeedException(
505
+ f"Smargon speed was calculated from x step size\
506
+ {parameters.panda_FGS_params.x_step_size} and\
507
+ time_between_x_steps_ms {time_between_x_steps_ms} as\
508
+ {sample_velocity_mm_per_s}. The smargon's speed limit is\
509
+ {smargon_speed_limit_mm_per_s} mm/s."
510
+ )
511
+ else:
512
+ LOGGER.info(
513
+ f"Panda grid scan: Smargon speed set to {smargon_speed_limit_mm_per_s} mm/s"
514
+ f" and using a run-up distance of {run_up_distance_mm}"
515
+ )
516
+
517
+ yield from bps.mv(
518
+ fgs_composite.panda_fast_grid_scan.time_between_x_steps_ms,
519
+ time_between_x_steps_ms,
520
+ )
521
+
522
+ directory_provider_root = Path(parameters.storage_directory)
523
+ yield from set_panda_directory(directory_provider_root)
524
+
525
+ yield from setup_panda_for_flyscan(
526
+ fgs_composite.panda,
527
+ parameters.panda_FGS_params,
528
+ initial_xyz[0],
529
+ parameters.exposure_time_s,
530
+ time_between_x_steps_ms,
531
+ sample_velocity_mm_per_s,
532
+ )
533
+
534
+ LOGGER.info("Setting up Zebra for panda flyscan")
535
+ yield from setup_zebra_for_panda_flyscan(
536
+ fgs_composite.zebra, fgs_composite.sample_shutter, wait=True
537
+ )