mx-bluesky 1.4.6__py3-none-any.whl → 1.4.8__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 (95) hide show
  1. mx_bluesky/_version.py +2 -2
  2. mx_bluesky/beamlines/aithre_lasershaping/__init__.py +13 -0
  3. mx_bluesky/beamlines/aithre_lasershaping/check_goniometer_performance.py +29 -0
  4. mx_bluesky/beamlines/aithre_lasershaping/goniometer_controls.py +18 -0
  5. mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +35 -29
  6. mx_bluesky/beamlines/i04/thawing_plan.py +18 -3
  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/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +12 -12
  11. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +36 -30
  12. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +3 -3
  13. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +15 -66
  14. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +8 -10
  15. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +10 -3
  16. mx_bluesky/beamlines/i24/serial/log.py +9 -9
  17. mx_bluesky/beamlines/i24/serial/parameters/utils.py +36 -7
  18. mx_bluesky/beamlines/i24/serial/set_visit_directory.sh +1 -1
  19. mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +16 -17
  20. mx_bluesky/beamlines/i24/serial/setup_beamline/pv_abstract.py +4 -4
  21. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +51 -52
  22. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +3 -2
  23. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +9 -7
  24. mx_bluesky/beamlines/i24/serial/web_gui_plans/general_plans.py +71 -11
  25. mx_bluesky/beamlines/i24/serial/write_nexus.py +6 -5
  26. mx_bluesky/{hyperion → common}/device_setup_plans/check_beamstop.py +1 -1
  27. mx_bluesky/{hyperion → common}/device_setup_plans/manipulate_sample.py +1 -1
  28. mx_bluesky/{hyperion → common}/device_setup_plans/setup_oav.py +12 -6
  29. mx_bluesky/common/device_setup_plans/xbpm_feedback.py +45 -0
  30. mx_bluesky/{hyperion → common}/experiment_plans/change_aperture_then_move_plan.py +13 -29
  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 +8 -9
  33. mx_bluesky/common/external_interaction/callbacks/common/ispyb_mapping.py +1 -1
  34. mx_bluesky/common/external_interaction/callbacks/common/zocalo_callback.py +18 -15
  35. mx_bluesky/{hyperion → common}/external_interaction/callbacks/sample_handling/sample_handling_callback.py +16 -4
  36. mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py +50 -45
  37. mx_bluesky/common/external_interaction/callbacks/xray_centre/nexus_callback.py +2 -1
  38. mx_bluesky/common/external_interaction/ispyb/data_model.py +1 -0
  39. mx_bluesky/common/external_interaction/ispyb/ispyb_store.py +18 -2
  40. mx_bluesky/common/external_interaction/ispyb/ispyb_utils.py +4 -4
  41. mx_bluesky/common/external_interaction/nexus/nexus_utils.py +1 -1
  42. mx_bluesky/common/parameters/components.py +22 -2
  43. mx_bluesky/common/parameters/constants.py +6 -16
  44. mx_bluesky/common/parameters/gridscan.py +36 -32
  45. mx_bluesky/common/plans/common_flyscan_xray_centre_plan.py +316 -0
  46. mx_bluesky/common/plans/inner_plans/__init__ .py +0 -0
  47. mx_bluesky/common/plans/read_hardware.py +3 -3
  48. mx_bluesky/common/plans/write_sample_status.py +46 -0
  49. mx_bluesky/common/preprocessors/__init__.py +0 -0
  50. mx_bluesky/common/preprocessors/preprocessors.py +105 -0
  51. mx_bluesky/common/protocols/__init__.py +0 -0
  52. mx_bluesky/common/protocols/protocols.py +10 -0
  53. mx_bluesky/common/utils/log.py +15 -12
  54. mx_bluesky/hyperion/__main__.py +5 -24
  55. mx_bluesky/hyperion/baton_handler.py +84 -0
  56. mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +4 -4
  57. mx_bluesky/hyperion/device_setup_plans/setup_panda.py +5 -1
  58. mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +0 -33
  59. mx_bluesky/hyperion/device_setup_plans/utils.py +4 -4
  60. mx_bluesky/hyperion/experiment_plans/__init__.py +0 -10
  61. mx_bluesky/hyperion/experiment_plans/experiment_registry.py +0 -16
  62. mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +71 -88
  63. mx_bluesky/hyperion/experiment_plans/hyperion_flyscan_xray_centre_plan.py +183 -0
  64. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +17 -8
  65. mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +29 -8
  66. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +4 -4
  67. mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +6 -4
  68. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +11 -3
  69. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +9 -34
  70. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +35 -68
  71. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +27 -8
  72. mx_bluesky/hyperion/external_interaction/agamemnon.py +140 -10
  73. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +17 -9
  74. mx_bluesky/hyperion/external_interaction/callbacks/snapshot_callback.py +259 -0
  75. mx_bluesky/hyperion/parameters/cli.py +2 -10
  76. mx_bluesky/hyperion/parameters/constants.py +0 -5
  77. mx_bluesky/hyperion/parameters/device_composites.py +40 -5
  78. mx_bluesky/hyperion/parameters/gridscan.py +9 -58
  79. mx_bluesky/hyperion/parameters/rotation.py +1 -5
  80. mx_bluesky/hyperion/utils/context.py +2 -5
  81. mx_bluesky/hyperion/utils/validation.py +13 -10
  82. {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.8.dist-info}/METADATA +10 -9
  83. {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.8.dist-info}/RECORD +92 -79
  84. {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.8.dist-info}/WHEEL +1 -1
  85. mx_bluesky/common/external_interaction/callbacks/common/aperture_change_callback.py +0 -22
  86. mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +0 -103
  87. mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +0 -466
  88. /mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/{short1-laser.png → s1l.png} +0 -0
  89. /mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/{short2-laser.png → s2l.png} +0 -0
  90. /mx_bluesky/{hyperion → common}/device_setup_plans/position_detector.py +0 -0
  91. /mx_bluesky/{hyperion → common}/external_interaction/callbacks/sample_handling/__init__.py +0 -0
  92. /mx_bluesky/common/plans/{do_fgs.py → inner_plans/do_fgs.py} +0 -0
  93. {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.8.dist-info}/entry_points.txt +0 -0
  94. {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.8.dist-info/licenses}/LICENSE +0 -0
  95. {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,259 @@
1
+ import dataclasses
2
+ import re
3
+ from collections.abc import Iterator
4
+ from datetime import datetime
5
+ from math import cos, radians, sin
6
+ from pathlib import Path
7
+
8
+ from dodal.devices.oav.snapshots.snapshot_image_processing import (
9
+ compute_beam_centre_pixel_xy_for_mm_position,
10
+ draw_crosshair,
11
+ )
12
+ from event_model import Event, EventDescriptor, RunStart
13
+ from PIL import Image
14
+
15
+ from mx_bluesky.common.external_interaction.callbacks.common.plan_reactive_callback import (
16
+ PlanReactiveCallback,
17
+ )
18
+ from mx_bluesky.common.parameters.components import WithSnapshot
19
+ from mx_bluesky.common.parameters.constants import DocDescriptorNames, PlanNameConstants
20
+ from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER as CALLBACK_LOGGER
21
+
22
+ COMPRESSION_LEVEL = 6 # 6 is the default compression level for PIL if not specified
23
+
24
+
25
+ @dataclasses.dataclass
26
+ class _SnapshotInfo:
27
+ beam_centre: tuple[int, int]
28
+ microns_per_pixel: tuple[float, float]
29
+ snapshot_path: str
30
+ omega: int
31
+ sample_pos_mm: tuple[float, float, float]
32
+
33
+ @property
34
+ def snapshot_basename(self) -> str:
35
+ match = re.match("(.*)\\.png", self.snapshot_path)
36
+ assert match, f"Snapshot {self.snapshot_path} was not a .png file"
37
+ return match.groups()[0]
38
+
39
+
40
+ class BeamDrawingCallback(PlanReactiveCallback):
41
+ """
42
+ Callback that monitors for OAV_ROTATION_SNAPSHOT_TRIGGERED events and
43
+ draws a crosshair at the beam centre, saving the snapshot to a file.
44
+ The callback assumes an OAV device "oav" and Smargon "smargon"
45
+ Examples:
46
+ Take a rotation snapshot at the current location
47
+ >>> from bluesky.run_engine import RunEngine
48
+ >>> import bluesky.preprocessors as bpp
49
+ >>> import bluesky.plan_stubs as bps
50
+ >>> from dodal.devices.oav.oav_detector import OAV
51
+ >>> from mx_bluesky.common.parameters.components import WithSnapshot
52
+ >>> def take_snapshot(params: WithSnapshot, oav: OAV, run_engine: RunEngine):
53
+ ... run_engine.subscribe(BeamDrawingCallback())
54
+ ... @bpp.run_decorator(md={
55
+ ... "activate_callbacks": ["BeamDrawingCallback"],
56
+ ... "with_snapshot": params.model_dump_json(),
57
+ ... })
58
+ ... def inner_plan():
59
+ ... yield from bps.abs_set(oav.snapshot.directory, "/path/to/snapshot_folder", wait=True)
60
+ ... yield from bps.abs_set(oav.snapshot.filename, "my_snapshot_prefix", wait=True)
61
+ ... yield from bps.trigger(oav.snapshot, wait=True)
62
+ ... yield from bps.create(DocDescriptorNames.OAV_ROTATION_SNAPSHOT_TRIGGERED)
63
+ ... yield from bps.read(oav)
64
+ ... yield from bps.save()
65
+
66
+ Generate rotation snapshots from a previously taken base gridscan snapshot.
67
+ WithSnapshot.snapshot_omegas_deg is ignored and snapshots are generated for the previously captured
68
+ 0, 90 base images named "my_snapshot_prefix_0" and "my_snapshot_prefix_90"
69
+ >>> from dodal.devices.smargon import Smargon
70
+ >>> def take_snapshot(params: WithSnapshot, oav: OAV, smargon: Smargon, run_engine: RunEngine):
71
+ ... run_engine.subscribe(BeamDrawingCallback())
72
+ ... @bpp.run_decorator(md={
73
+ ... "activate_callbacks": ["BeamDrawingCallback"],
74
+ ... "with_snapshot": params.model_dump_json(),
75
+ ... })
76
+ ... def inner_plan():
77
+ ... for omega in (0, 90,):
78
+ ... yield from bps.abs_set(smargon.omega, omega, wait=True)
79
+ ... yield from bps.abs_set(oav.grid_snapshot.directory, "/path/to/grid_snapshot_folder", wait=True)
80
+ ... yield from bps.abs_set(oav.grid_snapshot.filename, f"my_grid_snapshot_prefix_{omega}", wait=True)
81
+ ... yield from bps.trigger(oav.grid_snapshot, wait=True)
82
+ ... yield from bps.create(DocDescriptorNames.OAV_GRID_SNAPSHOT_TRIGGERED)
83
+ ... yield from bps.read(oav) # Capture base image path
84
+ ... yield from bps.read(smargon) # Capture base image sample x, y, z, omega
85
+ ... yield from bps.save()
86
+ ... # Rest of gridscan here...
87
+ ... # Later on...
88
+ ... for omega in (0, 90,):
89
+ ... yield from bps.abs_set(oav.snapshot.last_saved_path,
90
+ ... f"/path/to/snapshot_folder/my_snapshot_prefix_{omega}.png", wait=True)
91
+ ... yield from bps.create(DocDescriptorNames.OAV_ROTATION_SNAPSHOT_TRIGGERED)
92
+ ... yield from bps.read(oav) # Capture path info for generated snapshot
93
+ ... yield from bps.read(smargon) # Capture the current sample x, y, z
94
+ ... yield from bps.save()
95
+ """
96
+
97
+ def __init__(self, *args, **kwargs):
98
+ super().__init__(*args, log=CALLBACK_LOGGER, **kwargs)
99
+ self._base_snapshots: list[_SnapshotInfo] = []
100
+ self._rotation_snapshot_descriptor: str = ""
101
+ self._grid_snapshot_descriptor: str = ""
102
+ self._next_snapshot_info: Iterator | None = None
103
+ self._use_grid_snapshots: bool = False
104
+
105
+ def _reset(self):
106
+ self._base_snapshots = []
107
+
108
+ def activity_gated_start(self, doc: RunStart):
109
+ if self.activity_uid == doc.get("uid"):
110
+ self._reset()
111
+ with_snapshot = WithSnapshot.model_validate_json(doc.get("with_snapshot")) # type: ignore
112
+ self._use_grid_snapshots = with_snapshot.use_grid_snapshots
113
+ CALLBACK_LOGGER.info(f"Snapshot callback initialised with {with_snapshot}")
114
+ elif doc.get("subplan_name") == PlanNameConstants.ROTATION_MAIN:
115
+ self._next_snapshot_info = None
116
+ CALLBACK_LOGGER.info("Snapshot callback start rotation")
117
+ return doc
118
+
119
+ def activity_gated_descriptor(self, doc: EventDescriptor) -> EventDescriptor | None:
120
+ if doc.get("name") == DocDescriptorNames.OAV_ROTATION_SNAPSHOT_TRIGGERED:
121
+ self._rotation_snapshot_descriptor = doc["uid"]
122
+ elif doc.get("name") == DocDescriptorNames.OAV_GRID_SNAPSHOT_TRIGGERED:
123
+ self._grid_snapshot_descriptor = doc["uid"]
124
+ return doc
125
+
126
+ def activity_gated_event(self, doc: Event) -> Event:
127
+ if doc["descriptor"] == self._rotation_snapshot_descriptor:
128
+ self._handle_rotation_snapshot(doc)
129
+ elif doc["descriptor"] == self._grid_snapshot_descriptor:
130
+ self._handle_grid_snapshot(doc)
131
+ return doc
132
+
133
+ def _extract_base_snapshot_params(
134
+ self, snapshot_device_prefix: str, doc: Event
135
+ ) -> _SnapshotInfo:
136
+ data = doc["data"]
137
+ base_snapshot_path = data[f"oav-{snapshot_device_prefix}-last_saved_path"]
138
+ return _SnapshotInfo(
139
+ beam_centre=(data["oav-beam_centre_i"], data["oav-beam_centre_j"]),
140
+ microns_per_pixel=(
141
+ data["oav-microns_per_pixel_x"],
142
+ data["oav-microns_per_pixel_y"],
143
+ ),
144
+ snapshot_path=base_snapshot_path,
145
+ sample_pos_mm=(
146
+ data.get("smargon-x", 0.0),
147
+ data.get("smargon-y", 0.0),
148
+ data.get("smargon-z", 0.0),
149
+ ),
150
+ omega=round(data.get("smargon-omega", 0.0)),
151
+ )
152
+
153
+ def _handle_grid_snapshot(self, doc: Event):
154
+ snapshot_info = self._extract_base_snapshot_params("grid_snapshot", doc)
155
+ self._base_snapshots.append(snapshot_info)
156
+
157
+ def _handle_rotation_snapshot(self, doc: Event) -> Event:
158
+ data = doc["data"]
159
+ if self._use_grid_snapshots:
160
+ if not self._next_snapshot_info:
161
+ self._next_snapshot_info = iter(self._base_snapshots)
162
+ snapshot_info = next(self._next_snapshot_info, None)
163
+ assert snapshot_info, (
164
+ "Insufficient base gridscan snapshots to generate required rotation snapshots"
165
+ )
166
+ current_sample_pos_mm = (
167
+ data["smargon-x"],
168
+ data["smargon-y"],
169
+ data["smargon-z"],
170
+ )
171
+ CALLBACK_LOGGER.info(
172
+ f"Generating snapshot at {current_sample_pos_mm} from base snapshot {snapshot_info}"
173
+ )
174
+ output_snapshot_directory = data["oav-snapshot-directory"]
175
+ base_file_stem = Path(snapshot_info.snapshot_path).stem
176
+ output_snapshot_filename = _snapshot_filename(base_file_stem)
177
+ output_snapshot_path = (
178
+ f"{output_snapshot_directory}/{output_snapshot_filename}.png"
179
+ )
180
+ self._generate_snapshot_at(
181
+ snapshot_info,
182
+ output_snapshot_path,
183
+ *self._image_plane_offset_mm(snapshot_info, current_sample_pos_mm),
184
+ )
185
+ else:
186
+ snapshot_info = self._extract_base_snapshot_params("snapshot", doc)
187
+ output_snapshot_path = (
188
+ f"{snapshot_info.snapshot_basename}_with_beam_centre.png"
189
+ )
190
+ CALLBACK_LOGGER.info(
191
+ f"Annotating snapshot {output_snapshot_path} from base snapshot {snapshot_info}"
192
+ )
193
+ self._generate_snapshot_zero_offset(
194
+ snapshot_info,
195
+ output_snapshot_path,
196
+ )
197
+ data["oav-snapshot-last_saved_path"] = output_snapshot_path
198
+ return doc
199
+
200
+ def _image_plane_offset_mm(
201
+ self,
202
+ snapshot_info: _SnapshotInfo,
203
+ current_sample_pos_mm: tuple[float, float, float],
204
+ ) -> tuple[float, float]:
205
+ return self._project_xyz_to_xy(
206
+ (
207
+ (current_sample_pos_mm[0] - snapshot_info.sample_pos_mm[0]),
208
+ (current_sample_pos_mm[1] - snapshot_info.sample_pos_mm[1]),
209
+ (current_sample_pos_mm[2] - snapshot_info.sample_pos_mm[2]),
210
+ ),
211
+ snapshot_info.omega,
212
+ )
213
+
214
+ def _project_xyz_to_xy(
215
+ self, xyz: tuple[float, float, float], omega_deg: float
216
+ ) -> tuple[float, float]:
217
+ return (
218
+ xyz[0],
219
+ xyz[1] * cos(-radians(omega_deg)) + xyz[2] * sin(-radians(omega_deg)),
220
+ )
221
+
222
+ def _generate_snapshot_zero_offset(
223
+ self,
224
+ base_snapshot_info: _SnapshotInfo,
225
+ output_snapshot_path: str,
226
+ ):
227
+ self._generate_snapshot_at(base_snapshot_info, output_snapshot_path, 0, 0)
228
+
229
+ def _generate_snapshot_at(
230
+ self,
231
+ base_snapshot_info: _SnapshotInfo,
232
+ output_snapshot_path: str,
233
+ image_plane_dx_mm: float,
234
+ image_plane_dy_mm: float,
235
+ ):
236
+ """
237
+ Save a snapshot to the specified path, with an annotated crosshair at the specified
238
+ position
239
+ Args:
240
+ base_snapshot_info: Metadata about the base snapshot image from which the annotated
241
+ image will be derived.
242
+ output_snapshot_path: The path to the image that will be annotated.
243
+ image_plane_dx_mm: Relative x location of the sample to the original image in the image plane (mm)
244
+ image_plane_dy_mm: Relative y location of the sample to the original image in the image plane (mm)
245
+ """
246
+ image = Image.open(base_snapshot_info.snapshot_path)
247
+ x_px, y_px = compute_beam_centre_pixel_xy_for_mm_position(
248
+ (image_plane_dx_mm, image_plane_dy_mm),
249
+ base_snapshot_info.beam_centre,
250
+ base_snapshot_info.microns_per_pixel,
251
+ )
252
+ draw_crosshair(image, x_px, y_px)
253
+ image.save(output_snapshot_path, format="png", compress_level=COMPRESSION_LEVEL)
254
+
255
+
256
+ def _snapshot_filename(grid_snapshot_name):
257
+ time_now = datetime.now()
258
+ filename = f"{time_now.strftime('%H%M%S%f')[:8]}_oav_snapshot_{grid_snapshot_name}"
259
+ return filename
@@ -9,7 +9,6 @@ from mx_bluesky._version import version
9
9
  class HyperionArgs:
10
10
  dev_mode: bool = False
11
11
  verbose_event_logging: bool = False
12
- skip_startup_connection: bool = False
13
12
 
14
13
 
15
14
  def _add_callback_relevant_args(parser: argparse.ArgumentParser) -> None:
@@ -17,7 +16,7 @@ def _add_callback_relevant_args(parser: argparse.ArgumentParser) -> None:
17
16
  parser.add_argument(
18
17
  "--dev",
19
18
  action="store_true",
20
- help="Use dev options, such as local graylog instances and S03",
19
+ help="Use dev options, such as local graylog instances",
21
20
  )
22
21
 
23
22
 
@@ -32,8 +31,7 @@ def parse_callback_dev_mode_arg() -> bool:
32
31
  def parse_cli_args() -> HyperionArgs:
33
32
  """Parses all arguments relevant to hyperion. Returns an HyperionArgs dataclass with
34
33
  the fields: (verbose_event_logging: bool,
35
- dev_mode: bool,
36
- skip_startup_connection: bool)"""
34
+ dev_mode: bool)"""
37
35
  parser = argparse.ArgumentParser()
38
36
  _add_callback_relevant_args(parser)
39
37
  parser.add_argument(
@@ -41,11 +39,6 @@ def parse_cli_args() -> HyperionArgs:
41
39
  action="store_true",
42
40
  help="Log all bluesky event documents to graylog",
43
41
  )
44
- parser.add_argument(
45
- "--skip-startup-connection",
46
- action="store_true",
47
- help="Skip connecting to EPICS PVs on startup",
48
- )
49
42
  parser.add_argument(
50
43
  "--version",
51
44
  help="Print hyperion version string",
@@ -56,5 +49,4 @@ def parse_cli_args() -> HyperionArgs:
56
49
  return HyperionArgs(
57
50
  verbose_event_logging=args.verbose_event_logging or False,
58
51
  dev_mode=args.dev or False,
59
- skip_startup_connection=args.skip_startup_connection or False,
60
52
  )
@@ -12,8 +12,6 @@ from mx_bluesky.common.parameters.constants import (
12
12
  OavConstants,
13
13
  PlanGroupCheckpointConstants,
14
14
  PlanNameConstants,
15
- SimConstants,
16
- TriggerConstants,
17
15
  )
18
16
 
19
17
  TEST_MODE = os.environ.get("HYPERION_TEST_MODE")
@@ -42,15 +40,12 @@ class I03Constants:
42
40
  @dataclass(frozen=True)
43
41
  class HyperionConstants:
44
42
  DESCRIPTORS = DocDescriptorNames()
45
- TRIGGER = TriggerConstants()
46
43
  ZOCALO_ENV = EnvironmentConstants.ZOCALO_ENV
47
44
  HARDWARE = HardwareConstants()
48
45
  I03 = I03Constants()
49
46
  PARAM = ExperimentParamConstants()
50
47
  PLAN = PlanNameConstants()
51
48
  WAIT = PlanGroupCheckpointConstants()
52
- SIM = SimConstants()
53
- TRIGGER = TriggerConstants()
54
49
  CALLBACK_0MQ_PROXY_PORTS = (5577, 5578)
55
50
  DESCRIPTORS = DocDescriptorNames()
56
51
  CONFIG_SERVER_URL = (
@@ -6,13 +6,17 @@ from dodal.devices.aperturescatterguard import (
6
6
  )
7
7
  from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
8
8
  from dodal.devices.backlight import Backlight
9
- from dodal.devices.dcm import DCM
9
+ from dodal.devices.common_dcm import BaseDCM
10
+ from dodal.devices.detector.detector_motion import DetectorMotion
10
11
  from dodal.devices.eiger import EigerDetector
11
12
  from dodal.devices.fast_grid_scan import (
12
13
  PandAFastGridScan,
13
14
  ZebraFastGridScan,
14
15
  )
15
16
  from dodal.devices.flux import Flux
17
+ from dodal.devices.i03 import Beamstop
18
+ from dodal.devices.oav.oav_detector import OAV
19
+ from dodal.devices.oav.pin_image_recognition import PinTipDetection
16
20
  from dodal.devices.robot import BartRobot
17
21
  from dodal.devices.s4_slit_gaps import S4SlitGaps
18
22
  from dodal.devices.smargon import Smargon
@@ -24,22 +28,53 @@ from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
24
28
  from dodal.devices.zocalo import ZocaloResults
25
29
  from ophyd_async.fastcs.panda import HDFPanda
26
30
 
31
+ from mx_bluesky.common.plans.common_flyscan_xray_centre_plan import (
32
+ FlyScanEssentialDevices,
33
+ )
34
+
27
35
 
28
36
  @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
29
- class HyperionFlyScanXRayCentreComposite:
37
+ class HyperionFlyScanXRayCentreComposite(FlyScanEssentialDevices):
30
38
  """All devices which are directly or indirectly required by this plan"""
31
39
 
32
40
  aperture_scatterguard: ApertureScatterguard
33
41
  attenuator: BinaryFilterAttenuator
42
+ dcm: BaseDCM
43
+ eiger: EigerDetector
44
+ flux: Flux
45
+ s4_slit_gaps: S4SlitGaps
46
+ undulator: Undulator
47
+ synchrotron: Synchrotron
48
+ zebra: Zebra
49
+ zocalo: ZocaloResults
50
+ panda: HDFPanda
51
+ panda_fast_grid_scan: PandAFastGridScan
52
+ robot: BartRobot
53
+ sample_shutter: ZebraShutter
34
54
  backlight: Backlight
35
- dcm: DCM
55
+ xbpm_feedback: XBPMFeedback
56
+ zebra_fast_grid_scan: ZebraFastGridScan
57
+
58
+
59
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
60
+ class GridDetectThenXRayCentreComposite:
61
+ """All devices which are directly or indirectly required by this plan"""
62
+
63
+ aperture_scatterguard: ApertureScatterguard
64
+ attenuator: BinaryFilterAttenuator
65
+ backlight: Backlight
66
+ beamstop: Beamstop
67
+ dcm: BaseDCM
68
+ detector_motion: DetectorMotion
36
69
  eiger: EigerDetector
37
70
  zebra_fast_grid_scan: ZebraFastGridScan
38
71
  flux: Flux
39
- s4_slit_gaps: S4SlitGaps
72
+ oav: OAV
73
+ pin_tip_detection: PinTipDetection
40
74
  smargon: Smargon
41
- undulator: Undulator
42
75
  synchrotron: Synchrotron
76
+ s4_slit_gaps: S4SlitGaps
77
+ undulator: Undulator
43
78
  xbpm_feedback: XBPMFeedback
44
79
  zebra: Zebra
45
80
  zocalo: ZocaloResults
@@ -1,8 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- from dodal.devices.detector import (
4
- DetectorParams,
5
- )
6
3
  from dodal.devices.fast_grid_scan import (
7
4
  PandAGridScanParams,
8
5
  ZebraGridScanParams,
@@ -13,7 +10,6 @@ from mx_bluesky.common.parameters.gridscan import (
13
10
  SpecifiedThreeDGridScan,
14
11
  )
15
12
  from mx_bluesky.hyperion.parameters.components import WithHyperionUDCFeatures
16
- from mx_bluesky.hyperion.parameters.constants import CONST, I03Constants
17
13
 
18
14
 
19
15
  class GridCommonWithHyperionDetectorParams(GridCommon, WithHyperionUDCFeatures):
@@ -23,34 +19,11 @@ class GridCommonWithHyperionDetectorParams(GridCommon, WithHyperionUDCFeatures):
23
19
  # https://github.com/DiamondLightSource/hyperion/issues/1395"""
24
20
  @property
25
21
  def detector_params(self):
26
- self.det_dist_to_beam_converter_path = (
27
- self.det_dist_to_beam_converter_path
28
- or CONST.PARAM.DETECTOR.BEAM_XY_LUT_PATH
29
- )
30
- optional_args = {}
31
- if self.run_number:
32
- optional_args["run_number"] = self.run_number
33
- assert self.detector_distance_mm is not None, (
34
- "Detector distance must be filled before generating DetectorParams"
35
- )
36
- return DetectorParams(
37
- detector_size_constants=I03Constants.DETECTOR,
38
- expected_energy_ev=self.demand_energy_ev,
39
- exposure_time=self.exposure_time_s,
40
- directory=self.storage_directory,
41
- prefix=self.file_name,
42
- detector_distance=self.detector_distance_mm,
43
- omega_start=self.omega_start_deg or 0,
44
- omega_increment=0,
45
- num_images_per_trigger=1,
46
- num_triggers=self.num_images,
47
- use_roi_mode=self.use_roi_mode,
48
- det_dist_to_beam_converter_path=self.det_dist_to_beam_converter_path,
49
- trigger_mode=self.trigger_mode,
50
- enable_dev_shm=self.features.compare_cpu_and_gpu_zocalo
51
- or self.features.use_gpu_results,
52
- **optional_args,
22
+ params = super().detector_params
23
+ params.enable_dev_shm = (
24
+ self.features.compare_cpu_and_gpu_zocalo or self.features.use_gpu_results
53
25
  )
26
+ return params
54
27
 
55
28
 
56
29
  class HyperionSpecifiedThreeDGridScan(WithHyperionUDCFeatures, SpecifiedThreeDGridScan):
@@ -58,36 +31,14 @@ class HyperionSpecifiedThreeDGridScan(WithHyperionUDCFeatures, SpecifiedThreeDGr
58
31
 
59
32
  # These detector params only exist so that we can properly select enable_dev_shm. Remove in
60
33
  # https://github.com/DiamondLightSource/hyperion/issues/1395"""
34
+
61
35
  @property
62
36
  def detector_params(self):
63
- self.det_dist_to_beam_converter_path = (
64
- self.det_dist_to_beam_converter_path
65
- or CONST.PARAM.DETECTOR.BEAM_XY_LUT_PATH
66
- )
67
- optional_args = {}
68
- if self.run_number:
69
- optional_args["run_number"] = self.run_number
70
- assert self.detector_distance_mm is not None, (
71
- "Detector distance must be filled before generating DetectorParams"
72
- )
73
- return DetectorParams(
74
- detector_size_constants=I03Constants.DETECTOR,
75
- expected_energy_ev=self.demand_energy_ev,
76
- exposure_time=self.exposure_time_s,
77
- directory=self.storage_directory,
78
- prefix=self.file_name,
79
- detector_distance=self.detector_distance_mm,
80
- omega_start=self.omega_start_deg or 0,
81
- omega_increment=0,
82
- num_images_per_trigger=1,
83
- num_triggers=self.num_images,
84
- use_roi_mode=self.use_roi_mode,
85
- det_dist_to_beam_converter_path=self.det_dist_to_beam_converter_path,
86
- trigger_mode=self.trigger_mode,
87
- enable_dev_shm=self.features.compare_cpu_and_gpu_zocalo
88
- or self.features.use_gpu_results,
89
- **optional_args,
37
+ params = super().detector_params
38
+ params.enable_dev_shm = (
39
+ self.features.compare_cpu_and_gpu_zocalo or self.features.use_gpu_results
90
40
  )
41
+ return params
91
42
 
92
43
  # Relative to common grid scan, stub offsets are defined by config server
93
44
  @property
@@ -75,7 +75,7 @@ class RotationExperiment(DiffractionExperimentWithSample, WithHyperionUDCFeature
75
75
  return DetectorParams(
76
76
  detector_size_constants=I03Constants.DETECTOR,
77
77
  expected_energy_ev=self.demand_energy_ev,
78
- exposure_time=self.exposure_time_s,
78
+ exposure_time_s=self.exposure_time_s,
79
79
  directory=self.storage_directory,
80
80
  prefix=self.file_name,
81
81
  detector_distance=self.detector_distance_mm,
@@ -192,7 +192,3 @@ class MultiRotationScan(RotationExperiment, SplitScan):
192
192
  self._num_images_per_scan()[0],
193
193
  len(self._num_images_per_scan()),
194
194
  )
195
-
196
- @property
197
- def ispyb_params(self): # pyright: ignore
198
- raise ValueError("Please get ispyb params from one of the individual scans")
@@ -5,14 +5,11 @@ import mx_bluesky.hyperion.experiment_plans as hyperion_plans
5
5
  from mx_bluesky.common.utils.log import LOGGER
6
6
 
7
7
 
8
- def setup_context(wait_for_connection: bool = True) -> BlueskyContext:
8
+ def setup_context() -> BlueskyContext:
9
9
  context = BlueskyContext()
10
10
  context.with_plan_module(hyperion_plans)
11
11
 
12
- context.with_dodal_module(
13
- get_beamline_based_on_environment_variable(),
14
- wait_for_connection=wait_for_connection,
15
- )
12
+ context.with_dodal_module(get_beamline_based_on_environment_variable())
16
13
 
17
14
  LOGGER.info(f"Plans found in context: {context.plan_functions.keys()}")
18
15
 
@@ -21,7 +21,7 @@ from mx_bluesky.hyperion.external_interaction.callbacks.rotation.nexus_callback
21
21
  RotationNexusFileCallback,
22
22
  )
23
23
  from mx_bluesky.hyperion.parameters.constants import CONST
24
- from mx_bluesky.hyperion.parameters.rotation import RotationScan
24
+ from mx_bluesky.hyperion.parameters.rotation import MultiRotationScan
25
25
 
26
26
  DISPLAY_CONFIGURATION = "tests/test_data/test_display.configuration"
27
27
  ZOOM_LEVELS_XML = "tests/test_data/test_jCameraManZoomLevels.xml"
@@ -36,33 +36,36 @@ def test_params(filename_stub, dir):
36
36
  with open(filename) as f:
37
37
  return json.loads(f.read())
38
38
 
39
- params = RotationScan(
39
+ params = MultiRotationScan(
40
40
  **get_params(
41
- "tests/test_data/parameter_json_files/good_test_rotation_scan_parameters.json"
41
+ "tests/test_data/parameter_json_files/good_test_one_multi_rotation_scan_parameters.json"
42
42
  )
43
43
  )
44
+ for scan_params in params.rotation_scans:
45
+ scan_params.x_start_um = 0
46
+ scan_params.y_start_um = 0
47
+ scan_params.z_start_um = 0
48
+ scan_params.scan_width_deg = 360
44
49
  params.file_name = filename_stub
45
- params.scan_width_deg = 360
46
50
  params.demand_energy_ev = 12700
47
51
  params.storage_directory = str(dir)
48
- params.x_start_um = 0
49
- params.y_start_um = 0
50
- params.z_start_um = 0
51
52
  params.exposure_time_s = 0.004
52
53
  return params
53
54
 
54
55
 
55
56
  def fake_rotation_scan(
56
- parameters: RotationScan,
57
+ parameters: MultiRotationScan,
57
58
  subscription: RotationNexusFileCallback,
58
59
  rotation_devices: RotationScanComposite,
59
60
  ):
61
+ single_scan_parameters = next(parameters.single_rotation_scans)
62
+
60
63
  @bpp.subs_decorator(subscription)
61
64
  @bpp.set_run_key_decorator("rotation_scan_with_cleanup_and_subs")
62
65
  @bpp.run_decorator( # attach experiment metadata to the start document
63
66
  md={
64
67
  "subplan_name": CONST.PLAN.ROTATION_OUTER,
65
- "mx_bluesky_parameters": parameters.model_dump_json(),
68
+ "mx_bluesky_parameters": single_scan_parameters.model_dump_json(),
66
69
  "activate_callbacks": "RotationNexusFileCallback",
67
70
  }
68
71
  )
@@ -129,7 +132,7 @@ def fake_create_rotation_devices():
129
132
 
130
133
 
131
134
  def sim_rotation_scan_to_create_nexus(
132
- test_params: RotationScan,
135
+ test_params: MultiRotationScan,
133
136
  fake_create_rotation_devices: RotationScanComposite,
134
137
  filename_stub,
135
138
  RE,
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: mx-bluesky
3
- Version: 1.4.6
3
+ Version: 1.4.8
4
4
  Summary: Bluesky tools for MX Beamlines at DLS
5
5
  Author-email: Dominic Oram <dominic.oram@diamond.ac.uk>
6
6
  License: Apache License
@@ -215,7 +215,6 @@ Description-Content-Type: text/x-rst
215
215
  License-File: LICENSE
216
216
  Requires-Dist: annotated_types
217
217
  Requires-Dist: caproto
218
- Requires-Dist: confluent-kafka==2.8.0
219
218
  Requires-Dist: fastapi[all]
220
219
  Requires-Dist: flask-restful
221
220
  Requires-Dist: ispyb
@@ -237,10 +236,10 @@ Requires-Dist: semver
237
236
  Requires-Dist: matplotlib
238
237
  Requires-Dist: blueapi>=0.5.0
239
238
  Requires-Dist: daq-config-server>=0.1.1
240
- Requires-Dist: ophyd==1.9.0
241
- Requires-Dist: ophyd-async>=0.9.0a2
242
- Requires-Dist: bluesky>=1.13
243
- Requires-Dist: dls-dodal==1.41.0
239
+ Requires-Dist: ophyd>=1.10.5
240
+ Requires-Dist: ophyd-async>=0.10.0a2
241
+ Requires-Dist: bluesky>=1.13.1
242
+ Requires-Dist: dls-dodal==1.47.0
244
243
  Provides-Extra: dev
245
244
  Requires-Dist: black; extra == "dev"
246
245
  Requires-Dist: build; extra == "dev"
@@ -254,10 +253,11 @@ Requires-Dist: pipdeptree; extra == "dev"
254
253
  Requires-Dist: plantweb; extra == "dev"
255
254
  Requires-Dist: pre-commit; extra == "dev"
256
255
  Requires-Dist: pydata-sphinx-theme>=0.12; extra == "dev"
257
- Requires-Dist: pyright==1.1.394; extra == "dev"
256
+ Requires-Dist: pyright; extra == "dev"
258
257
  Requires-Dist: pytest-asyncio; extra == "dev"
259
258
  Requires-Dist: pytest-cov; extra == "dev"
260
259
  Requires-Dist: pytest-random-order; extra == "dev"
260
+ Requires-Dist: pytest-timeout; extra == "dev"
261
261
  Requires-Dist: pytest; extra == "dev"
262
262
  Requires-Dist: ruff; extra == "dev"
263
263
  Requires-Dist: sphinx-autobuild; extra == "dev"
@@ -267,6 +267,7 @@ Requires-Dist: tox-direct; extra == "dev"
267
267
  Requires-Dist: tox; extra == "dev"
268
268
  Requires-Dist: types-mock; extra == "dev"
269
269
  Requires-Dist: types-requests; extra == "dev"
270
+ Dynamic: license-file
270
271
 
271
272
  mx-bluesky
272
273
  ===========================
@@ -285,7 +286,7 @@ Releases https://github.com/DiamondLightSource/mx-bluesky/releases
285
286
  Getting Started
286
287
  ===============
287
288
 
288
- To get started with developing this repo at DLS run ``dls_dev_setup.sh``.
289
+ To get started with developing this repo at DLS run ``./utility_scripts/dls_dev_env.sh``.
289
290
 
290
291
  If you want to develop interactively at the beamline we recommend using jupyter notebooks. You can get started with this by running::
291
292