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
@@ -0,0 +1,316 @@
1
+ from __future__ import annotations
2
+
3
+ import dataclasses
4
+ from collections.abc import Callable, Sequence
5
+ from functools import partial
6
+
7
+ import bluesky.plan_stubs as bps
8
+ import bluesky.preprocessors as bpp
9
+ import numpy as np
10
+ import pydantic
11
+ from bluesky.protocols import Readable
12
+ from bluesky.utils import MsgGenerator
13
+ from dodal.devices.eiger import EigerDetector
14
+ from dodal.devices.fast_grid_scan import (
15
+ FastGridScanCommon,
16
+ )
17
+ from dodal.devices.smargon import Smargon
18
+ from dodal.devices.synchrotron import Synchrotron
19
+ from dodal.devices.zocalo import ZocaloResults
20
+ from dodal.devices.zocalo.zocalo_results import (
21
+ XrcResult,
22
+ get_full_processing_results,
23
+ )
24
+
25
+ from mx_bluesky.common.parameters.constants import (
26
+ DocDescriptorNames,
27
+ GridscanParamConstants,
28
+ PlanGroupCheckpointConstants,
29
+ PlanNameConstants,
30
+ )
31
+ from mx_bluesky.common.parameters.gridscan import SpecifiedThreeDGridScan
32
+ from mx_bluesky.common.plans.inner_plans.do_fgs import (
33
+ ZOCALO_STAGE_GROUP,
34
+ kickoff_and_complete_gridscan,
35
+ )
36
+ from mx_bluesky.common.plans.read_hardware import (
37
+ read_hardware_plan,
38
+ )
39
+ from mx_bluesky.common.utils.exceptions import (
40
+ CrystalNotFoundException,
41
+ SampleException,
42
+ )
43
+ from mx_bluesky.common.utils.log import LOGGER
44
+ from mx_bluesky.common.utils.tracing import TRACER
45
+ from mx_bluesky.common.xrc_result import XRayCentreResult
46
+
47
+
48
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
49
+ class FlyScanEssentialDevices:
50
+ eiger: EigerDetector
51
+ synchrotron: Synchrotron
52
+ zocalo: ZocaloResults
53
+ smargon: Smargon
54
+
55
+
56
+ @dataclasses.dataclass
57
+ class BeamlineSpecificFGSFeatures:
58
+ setup_trigger_plan: Callable[..., MsgGenerator]
59
+ tidy_plan: Callable[..., MsgGenerator]
60
+ set_flyscan_params_plan: Callable[..., MsgGenerator]
61
+ fgs_motors: FastGridScanCommon
62
+ read_pre_flyscan_plan: Callable[
63
+ ..., MsgGenerator
64
+ ] # Eventually replace with https://github.com/DiamondLightSource/mx-bluesky/issues/819
65
+ read_during_collection_plan: Callable[..., MsgGenerator]
66
+ get_xrc_results_from_zocalo: bool
67
+
68
+
69
+ def construct_beamline_specific_FGS_features(
70
+ setup_trigger_plan: Callable[..., MsgGenerator],
71
+ tidy_plan: Callable[..., MsgGenerator],
72
+ set_flyscan_params_plan: Callable[..., MsgGenerator],
73
+ fgs_motors: FastGridScanCommon,
74
+ signals_to_read_pre_flyscan: list[Readable],
75
+ signals_to_read_during_collection: list[Readable],
76
+ get_xrc_results_from_zocalo: bool = False,
77
+ ) -> BeamlineSpecificFGSFeatures:
78
+ """Construct the class needed to do beamline-specific parts of the XRC FGS
79
+
80
+ Args:
81
+ setup_trigger_plan (Callable): Configure triggering, for example with the Zebra or PandA device.
82
+ Ran directly before kicking off the gridscan.
83
+
84
+ tidy_plan (Callable): Tidy up states of devices. Ran at the end of the flyscan, regardless of
85
+ whether or not it finished successfully.
86
+
87
+ set_flyscan_params_plan (Callable): Set PV's for the relevant Fast Grid Scan dodal device
88
+
89
+ fgs_motors (Callable): Composite device representing the fast grid scan's motion program parameters.
90
+
91
+ signals_to_read_pre_flyscan (Callable): Signals which will be read and saved as a bluesky event document
92
+ after all configuration, but before the gridscan.
93
+
94
+ signals_to_read_during_collection (Callable): Signals which will be read and saved as a bluesky event
95
+ document whilst the gridscan motion is in progress
96
+
97
+ get_xrc_results_from_zocalo (bool): If true, fetch grid scan results from zocalo after completion, as well as
98
+ update the ispyb comment field with information about the results. See _fetch_xrc_results_from_zocalo
99
+ """
100
+ read_pre_flyscan_plan = partial(
101
+ read_hardware_plan,
102
+ signals_to_read_pre_flyscan,
103
+ DocDescriptorNames.HARDWARE_READ_PRE,
104
+ )
105
+
106
+ read_during_collection_plan = partial(
107
+ read_hardware_plan,
108
+ signals_to_read_during_collection,
109
+ DocDescriptorNames.HARDWARE_READ_DURING,
110
+ )
111
+
112
+ return BeamlineSpecificFGSFeatures(
113
+ setup_trigger_plan,
114
+ tidy_plan,
115
+ set_flyscan_params_plan,
116
+ fgs_motors,
117
+ read_pre_flyscan_plan,
118
+ read_during_collection_plan,
119
+ get_xrc_results_from_zocalo,
120
+ )
121
+
122
+
123
+ def common_flyscan_xray_centre(
124
+ composite: FlyScanEssentialDevices,
125
+ parameters: SpecifiedThreeDGridScan,
126
+ beamline_specific: BeamlineSpecificFGSFeatures,
127
+ ) -> MsgGenerator:
128
+ """Main entry point of the MX-Bluesky x-ray centering flyscan
129
+
130
+ Args:
131
+ composite (FlyScanEssentialDevices): Devices required to perform this plan.
132
+
133
+ parameters (SpecifiedThreeDGridScan): Parameters required to perform this plan.
134
+
135
+ beamline_specific (BeamlineSpecificFGSFeatures): Configure the beamline-specific version
136
+ of this plan: For example triggering setup and tidy up plans, as well as what to do with the
137
+ centering results.
138
+
139
+ With a minimum set of devices and parameters, prepares for; performs; and tidies up a flyscan
140
+ x-ray-center plan. This includes: Configuring desired triggering; writing nexus files; triggering zocalo;
141
+ reading hardware before and during the scan; and tidying up devices after
142
+ the plan is complete. Optionally fetch results from zocalo after completing the grid scan.
143
+
144
+ This plan will also push data to ispyb when used with the ispyb_activation_decorator.
145
+
146
+ There are a few other useful decorators to use with this plan, see: verify_undulator_gap_before_run_decorator, transmission_and_xbpm_feedback_for_collection_decorator
147
+ """
148
+
149
+ def _decorated_flyscan():
150
+ @bpp.set_run_key_decorator(PlanNameConstants.GRIDSCAN_OUTER)
151
+ @bpp.run_decorator( # attach experiment metadata to the start document
152
+ md={
153
+ "subplan_name": PlanNameConstants.GRIDSCAN_OUTER,
154
+ "mx_bluesky_parameters": parameters.model_dump_json(),
155
+ "activate_callbacks": [
156
+ "GridscanNexusFileCallback",
157
+ ],
158
+ }
159
+ )
160
+ @bpp.finalize_decorator(lambda: beamline_specific.tidy_plan(composite))
161
+ def run_gridscan_and_tidy(
162
+ fgs_composite: FlyScanEssentialDevices,
163
+ params: SpecifiedThreeDGridScan,
164
+ beamline_specific: BeamlineSpecificFGSFeatures,
165
+ ) -> MsgGenerator:
166
+ yield from beamline_specific.setup_trigger_plan(fgs_composite, parameters)
167
+
168
+ LOGGER.info("Starting grid scan")
169
+ yield from bps.stage(
170
+ fgs_composite.zocalo, group=ZOCALO_STAGE_GROUP
171
+ ) # connect to zocalo and make sure the queue is clear
172
+ yield from run_gridscan(fgs_composite, params, beamline_specific)
173
+
174
+ LOGGER.info("Grid scan finished")
175
+
176
+ if beamline_specific.get_xrc_results_from_zocalo:
177
+ yield from _fetch_xrc_results_from_zocalo(composite.zocalo, parameters)
178
+
179
+ yield from run_gridscan_and_tidy(composite, parameters, beamline_specific)
180
+
181
+ composite.eiger.set_detector_parameters(parameters.detector_params)
182
+ yield from _decorated_flyscan()
183
+
184
+
185
+ def _fetch_xrc_results_from_zocalo(
186
+ zocalo_results: ZocaloResults,
187
+ parameters: SpecifiedThreeDGridScan,
188
+ ) -> MsgGenerator:
189
+ """
190
+ Get XRC results from the ZocaloResults device which was staged during a grid scan,
191
+ and store them in XRayCentreEventHandler.xray_centre_results by firing an event.
192
+
193
+ The RunEngine must be subscribed to XRayCentreEventHandler for this plan to work.
194
+ """
195
+
196
+ LOGGER.info("Getting X-ray center Zocalo results...")
197
+
198
+ yield from bps.trigger(zocalo_results)
199
+ LOGGER.info("Zocalo triggered and read, interpreting results.")
200
+ xrc_results = yield from get_full_processing_results(zocalo_results)
201
+ LOGGER.info(f"Got xray centres, top 5: {xrc_results[:5]}")
202
+ filtered_results = [
203
+ result
204
+ for result in xrc_results
205
+ if result["total_count"]
206
+ >= GridscanParamConstants.ZOCALO_MIN_TOTAL_COUNT_THRESHOLD
207
+ ]
208
+ discarded_count = len(xrc_results) - len(filtered_results)
209
+ if discarded_count > 0:
210
+ LOGGER.info(f"Removed {discarded_count} results because below threshold")
211
+ if filtered_results:
212
+ flyscan_results = [
213
+ _xrc_result_in_boxes_to_result_in_mm(xr, parameters)
214
+ for xr in filtered_results
215
+ ]
216
+ else:
217
+ LOGGER.warning("No X-ray centre received")
218
+ raise CrystalNotFoundException()
219
+ yield from _fire_xray_centre_result_event(flyscan_results)
220
+
221
+
222
+ @bpp.set_run_key_decorator(PlanNameConstants.GRIDSCAN_MAIN)
223
+ @bpp.run_decorator(md={"subplan_name": PlanNameConstants.GRIDSCAN_MAIN})
224
+ def run_gridscan(
225
+ fgs_composite: FlyScanEssentialDevices,
226
+ parameters: SpecifiedThreeDGridScan,
227
+ beamline_specific: BeamlineSpecificFGSFeatures,
228
+ ):
229
+ # Currently gridscan only works for omega 0, see https://github.com/DiamondLightSource/mx-bluesky/issues/410
230
+ with TRACER.start_span("moving_omega_to_0"):
231
+ yield from bps.abs_set(fgs_composite.smargon.omega, 0)
232
+
233
+ with TRACER.start_span("ispyb_hardware_readings"):
234
+ yield from beamline_specific.read_pre_flyscan_plan()
235
+
236
+ LOGGER.info("Setting fgs params")
237
+ yield from beamline_specific.set_flyscan_params_plan()
238
+
239
+ LOGGER.info("Waiting for gridscan validity check")
240
+ yield from wait_for_gridscan_valid(beamline_specific.fgs_motors)
241
+
242
+ LOGGER.info("Waiting for arming to finish")
243
+ yield from bps.wait(PlanGroupCheckpointConstants.GRID_READY_FOR_DC)
244
+ yield from bps.stage(fgs_composite.eiger) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
245
+
246
+ yield from kickoff_and_complete_gridscan(
247
+ beamline_specific.fgs_motors,
248
+ fgs_composite.eiger,
249
+ fgs_composite.synchrotron,
250
+ [parameters.scan_points_first_grid, parameters.scan_points_second_grid],
251
+ parameters.scan_indices,
252
+ plan_during_collection=beamline_specific.read_during_collection_plan,
253
+ )
254
+ yield from bps.abs_set(beamline_specific.fgs_motors.z_steps, 0, wait=False)
255
+
256
+
257
+ def wait_for_gridscan_valid(fgs_motors: FastGridScanCommon, timeout=0.5):
258
+ LOGGER.info("Waiting for valid fgs_params")
259
+ SLEEP_PER_CHECK = 0.1
260
+ times_to_check = int(timeout / SLEEP_PER_CHECK)
261
+ for _ in range(times_to_check):
262
+ scan_invalid = yield from bps.rd(fgs_motors.scan_invalid)
263
+ pos_counter = yield from bps.rd(fgs_motors.position_counter)
264
+ LOGGER.debug(
265
+ f"Scan invalid: {scan_invalid} and position counter: {pos_counter}"
266
+ )
267
+ if not scan_invalid and pos_counter == 0:
268
+ LOGGER.info("Gridscan scan valid and position counter reset")
269
+ return
270
+ yield from bps.sleep(SLEEP_PER_CHECK)
271
+ raise SampleException("Scan invalid - pin too long/short/bent and out of range")
272
+
273
+
274
+ def _xrc_result_in_boxes_to_result_in_mm(
275
+ xrc_result: XrcResult, parameters: SpecifiedThreeDGridScan
276
+ ) -> XRayCentreResult:
277
+ fgs_params = parameters.FGS_params
278
+ xray_centre = fgs_params.grid_position_to_motor_position(
279
+ np.array(xrc_result["centre_of_mass"])
280
+ )
281
+ # A correction is applied to the bounding box to map discrete grid coordinates to
282
+ # the corners of the box in motor-space; we do not apply this correction
283
+ # to the xray-centre as it is already in continuous space and the conversion has
284
+ # been performed already
285
+ # In other words, xrc_result["bounding_box"] contains the position of the box centre,
286
+ # so we subtract half a box to get the corner of the box
287
+ return XRayCentreResult(
288
+ centre_of_mass_mm=xray_centre,
289
+ bounding_box_mm=(
290
+ fgs_params.grid_position_to_motor_position(
291
+ np.array(xrc_result["bounding_box"][0]) - 0.5
292
+ ),
293
+ fgs_params.grid_position_to_motor_position(
294
+ np.array(xrc_result["bounding_box"][1]) - 0.5
295
+ ),
296
+ ),
297
+ max_count=xrc_result["max_count"],
298
+ total_count=xrc_result["total_count"],
299
+ )
300
+
301
+
302
+ def _fire_xray_centre_result_event(results: Sequence[XRayCentreResult]):
303
+ def empty_plan():
304
+ return iter([])
305
+
306
+ yield from bpp.set_run_key_wrapper(
307
+ bpp.run_wrapper(
308
+ empty_plan(),
309
+ md={
310
+ PlanNameConstants.FLYSCAN_RESULTS: [
311
+ dataclasses.asdict(r) for r in results
312
+ ]
313
+ },
314
+ ),
315
+ PlanNameConstants.FLYSCAN_RESULTS,
316
+ )
File without changes
@@ -4,7 +4,7 @@ import bluesky.plan_stubs as bps
4
4
  from bluesky.protocols import Readable
5
5
  from dodal.devices.aperturescatterguard import ApertureScatterguard
6
6
  from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
7
- from dodal.devices.dcm import DCM
7
+ from dodal.devices.common_dcm import BaseDCM
8
8
  from dodal.devices.eiger import EigerDetector
9
9
  from dodal.devices.flux import Flux
10
10
  from dodal.devices.s4_slit_gaps import S4SlitGaps
@@ -43,7 +43,7 @@ def standard_read_hardware_pre_collection(
43
43
  undulator: Undulator,
44
44
  synchrotron: Synchrotron,
45
45
  s4_slit_gaps: S4SlitGaps,
46
- dcm: DCM,
46
+ dcm: BaseDCM,
47
47
  smargon: Smargon,
48
48
  ):
49
49
  LOGGER.info("Reading status of beamline for callbacks, pre collection.")
@@ -63,7 +63,7 @@ def standard_read_hardware_during_collection(
63
63
  aperture_scatterguard: ApertureScatterguard,
64
64
  attenuator: BinaryFilterAttenuator,
65
65
  flux: Flux,
66
- dcm: DCM,
66
+ dcm: BaseDCM,
67
67
  detector: EigerDetector,
68
68
  ):
69
69
  signals_to_read_during_collection = [
@@ -66,14 +66,15 @@ def do_default_logging_setup(
66
66
  """Configures dodal logger so that separate debug and info log files are created,
67
67
  info logs are sent to Graylog, info logs are streamed to sys.sterr, and logs from ophyd
68
68
  and bluesky and ophyd-async are optionally included."""
69
-
69
+ logging_path, debug_logging_path = _get_logging_dirs(dev_mode)
70
70
  handlers = set_up_all_logging_handlers(
71
71
  dodal_logger,
72
- _get_logging_dir(),
72
+ logging_path,
73
73
  file_name,
74
74
  dev_mode,
75
75
  ERROR_LOG_BUFFER_LINES,
76
76
  graylog_port,
77
+ debug_logging_path,
77
78
  )
78
79
 
79
80
  if integrate_all_logs:
@@ -102,8 +103,8 @@ def flush_debug_handler() -> str:
102
103
  return handler.target.baseFilename
103
104
 
104
105
 
105
- def _get_logging_dir() -> Path:
106
- """Get the path to write the mx_bluesky log files to.
106
+ def _get_logging_dirs(dev_mode: bool) -> tuple[Path, Path]:
107
+ """Get the paths to write the mx_bluesky log files to.
107
108
 
108
109
  Log location can be specified in the LOG_DIR environment variable, otherwise MX bluesky logs are written to 'dls_sw/ixx/logs/bluesky'.
109
110
  This directory will be created if it is not found
@@ -111,18 +112,21 @@ def _get_logging_dir() -> Path:
111
112
  Logs are written to ./tmp/logs/bluesky if BEAMLINE environment variable is not found
112
113
 
113
114
  Returns:
114
- logging_path (Path): Path to the log file for the file handler to write to.
115
+ tuple[Path, Path]: Paths to the standard log file and to the debug log file, for the file handlers to write to
115
116
  """
116
117
 
117
- logging_str = environ.get("LOG_DIR")
118
- if logging_str:
119
- logging_path = Path(logging_str)
118
+ beamline = environ.get("BEAMLINE")
119
+
120
+ if beamline and not dev_mode:
121
+ default_logging_str = f"/dls_sw/{beamline}/logs/bluesky/"
122
+ default_debug_logging_str = f"/dls/tmp/{beamline}/logs/bluesky/"
120
123
  else:
121
- beamline = environ.get("BEAMLINE")
122
- logging_path = (
123
- Path(f"/dls_sw/{beamline}/logs/bluesky/")
124
- if beamline
125
- else Path("/tmp/logs/bluesky")
126
- )
124
+ default_logging_str = "/tmp/logs/bluesky"
125
+ default_debug_logging_str = default_logging_str
126
+
127
+ logging_path = Path(environ.get("LOG_DIR", default_logging_str))
128
+ debug_logging_path = Path(environ.get("DEBUG_LOG_DIR", default_debug_logging_str))
129
+
127
130
  Path.mkdir(logging_path, exist_ok=True, parents=True)
128
- return logging_path
131
+ Path.mkdir(debug_logging_path, exist_ok=True, parents=True)
132
+ return logging_path, debug_logging_path
@@ -19,9 +19,6 @@ from pydantic.dataclasses import dataclass
19
19
  from mx_bluesky.common.external_interaction.callbacks.common.log_uid_tag_callback import (
20
20
  LogUidTaggingCallback,
21
21
  )
22
- from mx_bluesky.common.external_interaction.callbacks.common.logging_callback import (
23
- VerbosePlanExecutionLoggingCallback,
24
- )
25
22
  from mx_bluesky.common.parameters.components import MxBlueskyParameters
26
23
  from mx_bluesky.common.parameters.constants import Actions, Status
27
24
  from mx_bluesky.common.utils.exceptions import WarningException
@@ -41,10 +38,9 @@ from mx_bluesky.hyperion.external_interaction.agamemnon import (
41
38
  )
42
39
  from mx_bluesky.hyperion.parameters.cli import parse_cli_args
43
40
  from mx_bluesky.hyperion.parameters.constants import CONST
41
+ from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect
44
42
  from mx_bluesky.hyperion.utils.context import setup_context
45
43
 
46
- VERBOSE_EVENT_LOGGING: bool | None = None
47
-
48
44
 
49
45
  @dataclass
50
46
  class Command:
@@ -82,7 +78,6 @@ class BlueskyRunner:
82
78
  self,
83
79
  RE: RunEngine,
84
80
  context: BlueskyContext,
85
- skip_startup_connection=False,
86
81
  ) -> None:
87
82
  self.command_queue: Queue[Command] = Queue()
88
83
  self.current_status: StatusAndMessage = StatusAndMessage(Status.IDLE)
@@ -98,15 +93,6 @@ class BlueskyRunner:
98
93
  self.publisher = Publisher(f"localhost:{CONST.CALLBACK_0MQ_PROXY_PORTS[0]}")
99
94
  RE.subscribe(self.publisher)
100
95
 
101
- if VERBOSE_EVENT_LOGGING:
102
- RE.subscribe(VerbosePlanExecutionLoggingCallback())
103
-
104
- self.skip_startup_connection = skip_startup_connection
105
- if not self.skip_startup_connection:
106
- LOGGER.info("Initialising dodal devices...")
107
- for plan_name in PLAN_REGISTRY:
108
- PLAN_REGISTRY[plan_name]["setup"](context)
109
-
110
96
  def start(
111
97
  self,
112
98
  experiment: Callable,
@@ -205,7 +191,8 @@ def compose_start_args(context: BlueskyContext, plan_name: str, action: Actions)
205
191
  try:
206
192
  parameters = experiment_internal_param_type(**json.loads(request.data))
207
193
  parameters = update_params_from_agamemnon(parameters)
208
- compare_params(parameters)
194
+ if isinstance(parameters, LoadCentreCollect):
195
+ compare_params(parameters)
209
196
  if parameters.model_extra:
210
197
  raise ValueError(f"Extra fields not allowed {parameters.model_extra}")
211
198
  except Exception as e:
@@ -278,15 +265,12 @@ class FlushLogs(Resource):
278
265
  def create_app(
279
266
  test_config=None,
280
267
  RE: RunEngine = RunEngine({}),
281
- skip_startup_connection: bool = False,
268
+ dev_mode: bool = False,
282
269
  ) -> tuple[Flask, BlueskyRunner]:
283
- context = setup_context(
284
- wait_for_connection=not skip_startup_connection,
285
- )
270
+ context = setup_context(dev_mode=dev_mode)
286
271
  runner = BlueskyRunner(
287
272
  RE,
288
273
  context=context,
289
- skip_startup_connection=skip_startup_connection,
290
274
  )
291
275
  app = Flask(__name__)
292
276
  if test_config:
@@ -316,9 +300,7 @@ def create_targets():
316
300
  CONST.LOG_FILE_NAME, CONST.GRAYLOG_PORT, dev_mode=args.dev_mode
317
301
  )
318
302
  LOGGER.info(f"Hyperion launched with args:{argv}")
319
- app, runner = create_app(
320
- skip_startup_connection=args.skip_startup_connection,
321
- )
303
+ app, runner = create_app(dev_mode=args.dev_mode)
322
304
  return app, runner, hyperion_port, args.dev_mode
323
305
 
324
306
 
@@ -1,3 +1,5 @@
1
+ from collections.abc import Sequence
2
+
1
3
  from bluesky import plan_stubs as bps
2
4
  from bluesky import preprocessors as bpp
3
5
  from dodal.devices.baton import Baton
@@ -37,9 +39,12 @@ def main_hyperion_loop(baton: Baton, composite: LoadCentreCollectComposite):
37
39
  while requested_user == HYPERION_USER:
38
40
 
39
41
  def inner_loop():
40
- parameters: LoadCentreCollect | None = create_parameters_from_agamemnon() # type: ignore # not complete until https://github.com/DiamondLightSource/mx-bluesky/issues/773
41
- if parameters:
42
- yield from load_centre_collect_full(composite, parameters)
42
+ parameter_list: Sequence[LoadCentreCollect] = (
43
+ create_parameters_from_agamemnon()
44
+ )
45
+ if parameter_list:
46
+ for parameters in parameter_list:
47
+ yield from load_centre_collect_full(composite, parameters)
43
48
  else:
44
49
  yield from bps.mv(baton.requested_user, NO_USER)
45
50
 
@@ -6,7 +6,7 @@ from dodal.devices.focusing_mirror import (
6
6
  MirrorStripe,
7
7
  MirrorVoltages,
8
8
  )
9
- from dodal.devices.undulator_dcm import UndulatorDCM
9
+ from dodal.devices.i03.undulator_dcm import UndulatorDCM
10
10
  from dodal.devices.util.adjuster_plans import lookup_table_adjuster
11
11
  from dodal.devices.util.lookup_tables import (
12
12
  linear_interpolation_lut,
@@ -102,7 +102,7 @@ def adjust_dcm_pitch_roll_vfm_from_lut(
102
102
  dcm = undulator_dcm.dcm_ref()
103
103
  LOGGER.info(f"Adjusting DCM and VFM for {energy_kev} keV")
104
104
  d_spacing_a: float = yield from bps.rd(
105
- undulator_dcm.dcm_ref().crystal_metadata_d_spacing
105
+ undulator_dcm.dcm_ref().crystal_metadata_d_spacing_a
106
106
  )
107
107
  bragg_deg = energy_to_bragg_angle(energy_kev, d_spacing_a)
108
108
  LOGGER.info(f"Target Bragg angle = {bragg_deg} degrees")
@@ -110,7 +110,7 @@ def adjust_dcm_pitch_roll_vfm_from_lut(
110
110
  linear_interpolation_lut(
111
111
  *parse_lookup_table(undulator_dcm.pitch_energy_table_path)
112
112
  ),
113
- dcm.pitch_in_mrad,
113
+ dcm.xtal_1.pitch_in_mrad,
114
114
  bragg_deg,
115
115
  )
116
116
  yield from dcm_pitch_adjuster(DCM_GROUP)
@@ -122,7 +122,7 @@ def adjust_dcm_pitch_roll_vfm_from_lut(
122
122
  linear_interpolation_lut(
123
123
  *parse_lookup_table(undulator_dcm.roll_energy_table_path)
124
124
  ),
125
- dcm.roll_in_mrad,
125
+ dcm.xtal_1.roll_in_mrad,
126
126
  bragg_deg,
127
127
  )
128
128
  yield from dcm_roll_adjuster(DCM_GROUP)
@@ -1,8 +1,4 @@
1
- from collections.abc import Callable
2
- from functools import wraps
3
-
4
1
  import bluesky.plan_stubs as bps
5
- import bluesky.preprocessors as bpp
6
2
  from bluesky.utils import MsgGenerator
7
3
  from dodal.devices.zebra.zebra import (
8
4
  ArmDemand,
@@ -21,31 +17,6 @@ from mx_bluesky.common.utils.log import LOGGER
21
17
  ZEBRA_STATUS_TIMEOUT = 30
22
18
 
23
19
 
24
- def bluesky_retry(func: Callable):
25
- """Decorator that will retry the decorated plan if it fails.
26
-
27
- Use this with care as it knows nothing about the state of the world when things fail.
28
- If it is possible that your plan fails when the beamline is in a transient state that
29
- the plan could not act on do not use this decorator without doing some more intelligent
30
- clean up.
31
-
32
- You should avoid using this decorator often in general production as it hides errors,
33
- instead it should be used only for debugging these underlying errors.
34
- """
35
-
36
- @wraps(func)
37
- def newfunc(*args, **kwargs):
38
- def log_and_retry(exception):
39
- LOGGER.error(f"Function {func.__name__} failed with {exception}, retrying")
40
- yield from func(*args, **kwargs)
41
-
42
- yield from bpp.contingency_wrapper(
43
- func(*args, **kwargs), except_plan=log_and_retry, auto_raise=False
44
- )
45
-
46
- return newfunc
47
-
48
-
49
20
  def arm_zebra(zebra: Zebra):
50
21
  yield from bps.abs_set(zebra.pc.arm, ArmDemand.ARM, wait=True)
51
22
 
@@ -105,7 +76,6 @@ def configure_zebra_and_shutter_for_auto_shutter(
105
76
  yield from set_shutter_auto_input(zebra, input, group=group)
106
77
 
107
78
 
108
- @bluesky_retry
109
79
  def setup_zebra_for_rotation(
110
80
  zebra: Zebra,
111
81
  zebra_shutter: ZebraShutter,
@@ -185,7 +155,6 @@ def setup_zebra_for_rotation(
185
155
  yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
186
156
 
187
157
 
188
- @bluesky_retry
189
158
  def setup_zebra_for_gridscan(
190
159
  zebra: Zebra,
191
160
  zebra_shutter: ZebraShutter,
@@ -215,7 +184,6 @@ def setup_zebra_for_gridscan(
215
184
  yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
216
185
 
217
186
 
218
- @bluesky_retry
219
187
  def tidy_up_zebra_after_gridscan(
220
188
  zebra: Zebra,
221
189
  zebra_shutter: ZebraShutter,
@@ -236,7 +204,6 @@ def tidy_up_zebra_after_gridscan(
236
204
  yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
237
205
 
238
206
 
239
- @bluesky_retry
240
207
  def setup_zebra_for_panda_flyscan(
241
208
  zebra: Zebra,
242
209
  zebra_shutter: ZebraShutter,
@@ -1,6 +1,6 @@
1
1
  import numpy as np
2
2
  from bluesky import plan_stubs as bps
3
- from dodal.devices.smargon import Smargon
3
+ from dodal.devices.smargon import CombinedMove, Smargon
4
4
 
5
5
  from mx_bluesky.common.utils.exceptions import SampleException
6
6
 
@@ -16,10 +16,5 @@ def move_smargon_warn_on_out_of_range(
16
16
  "Pin tip centring failed - pin too long/short/bent and out of range"
17
17
  )
18
18
  yield from bps.mv(
19
- smargon.x,
20
- position[0],
21
- smargon.y,
22
- position[1],
23
- smargon.z,
24
- position[2],
19
+ smargon, CombinedMove(x=position[0], y=position[1], z=position[2])
25
20
  )
@@ -3,16 +3,15 @@ from collections.abc import Generator
3
3
  from bluesky import plan_stubs as bps
4
4
  from bluesky import preprocessors as bpp
5
5
  from bluesky.utils import Msg
6
- from dodal.devices.dcm import DCM
7
6
  from dodal.devices.detector import (
8
7
  DetectorParams,
9
8
  )
10
9
  from dodal.devices.detector.detector_motion import DetectorMotion, ShutterState
11
10
  from dodal.devices.eiger import EigerDetector
12
- from dodal.devices.i03.beamstop import Beamstop
11
+ from dodal.devices.i03.dcm import DCM
12
+ from dodal.devices.mx_phase1.beamstop import Beamstop, BeamstopPositions
13
13
 
14
- from mx_bluesky.hyperion.device_setup_plans.check_beamstop import check_beamstop
15
- from mx_bluesky.hyperion.device_setup_plans.position_detector import (
14
+ from mx_bluesky.common.device_setup_plans.position_detector import (
16
15
  set_detector_z_position,
17
16
  set_shutter,
18
17
  )
@@ -45,6 +44,9 @@ def start_preparing_data_collection_then_do_plan(
45
44
 
46
45
  def wrapped_plan():
47
46
  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)
47
+ yield from bps.abs_set(
48
+ beamstop.selected_pos, BeamstopPositions.DATA_COLLECTION, group=group
49
+ )
48
50
  if detector_distance_mm:
49
51
  yield from set_detector_z_position(
50
52
  detector_motion, detector_distance_mm, group
@@ -52,7 +54,6 @@ def start_preparing_data_collection_then_do_plan(
52
54
  yield from set_shutter(detector_motion, ShutterState.OPEN, group)
53
55
  yield from plan_to_run
54
56
 
55
- yield from check_beamstop(beamstop)
56
57
  yield from bpp.contingency_wrapper(
57
58
  wrapped_plan(),
58
59
  except_plan=lambda e: (yield from bps.stop(eiger)), # type: ignore # Fix types in ophyd-async (https://github.com/DiamondLightSource/mx-bluesky/issues/855)