mx-bluesky 1.2.0__py3-none-any.whl → 1.4.1a0__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 (94) hide show
  1. mx_bluesky/__init__.py +8 -3
  2. mx_bluesky/__main__.py +12 -7
  3. mx_bluesky/_version.py +2 -2
  4. mx_bluesky/beamlines/i04/callbacks/murko_callback.py +14 -4
  5. mx_bluesky/beamlines/i04/thawing_plan.py +49 -11
  6. mx_bluesky/beamlines/i24/serial/__init__.py +3 -0
  7. mx_bluesky/beamlines/i24/serial/dcid.py +19 -21
  8. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +69 -91
  9. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +2 -5
  10. mx_bluesky/beamlines/i24/serial/fixed_target/ft_utils.py +0 -1
  11. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +111 -143
  12. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +141 -222
  13. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +7 -216
  14. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +18 -17
  15. mx_bluesky/beamlines/i24/serial/log.py +58 -49
  16. mx_bluesky/beamlines/i24/serial/parameters/constants.py +0 -1
  17. mx_bluesky/beamlines/i24/serial/parameters/fixed_target/cs/cs_maker.json +3 -3
  18. mx_bluesky/beamlines/i24/serial/run_extruder.sh +30 -5
  19. mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +30 -5
  20. mx_bluesky/beamlines/i24/serial/run_serial.py +24 -8
  21. mx_bluesky/beamlines/i24/serial/setup_beamline/ca.py +0 -2
  22. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +79 -81
  23. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +9 -20
  24. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +26 -28
  25. mx_bluesky/beamlines/i24/serial/write_nexus.py +11 -11
  26. mx_bluesky/common/__init__.py +0 -0
  27. mx_bluesky/common/device_setup_plans/read_hardware_for_setup.py +14 -0
  28. mx_bluesky/common/external_interaction/config_server.py +46 -0
  29. mx_bluesky/common/parameters/components.py +258 -0
  30. mx_bluesky/common/parameters/constants.py +138 -0
  31. mx_bluesky/common/parameters/gridscan.py +94 -0
  32. mx_bluesky/common/parameters/robot_load.py +16 -0
  33. mx_bluesky/common/plans/__init__.py +1 -0
  34. mx_bluesky/common/plans/do_fgs.py +121 -0
  35. mx_bluesky/common/utils/log.py +118 -0
  36. mx_bluesky/{hyperion → common/utils}/tracing.py +2 -2
  37. mx_bluesky/hyperion/__main__.py +13 -10
  38. mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +31 -26
  39. mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +6 -12
  40. mx_bluesky/hyperion/device_setup_plans/setup_oav.py +6 -12
  41. mx_bluesky/hyperion/device_setup_plans/setup_panda.py +5 -6
  42. mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +49 -18
  43. mx_bluesky/hyperion/device_setup_plans/smargon.py +6 -6
  44. mx_bluesky/hyperion/device_setup_plans/utils.py +2 -2
  45. mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +4 -4
  46. mx_bluesky/hyperion/experiment_plans/__init__.py +4 -0
  47. mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +83 -0
  48. mx_bluesky/hyperion/experiment_plans/common/xrc_result.py +47 -0
  49. mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -9
  50. mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +145 -161
  51. mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +56 -22
  52. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +52 -10
  53. mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +21 -20
  54. mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +11 -14
  55. mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +2 -2
  56. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +40 -21
  57. mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +19 -19
  58. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +21 -21
  59. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +51 -13
  60. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +24 -7
  61. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +5 -6
  62. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +1 -2
  63. mx_bluesky/hyperion/external_interaction/callbacks/common/abstract_event.py +66 -0
  64. mx_bluesky/hyperion/external_interaction/callbacks/common/ispyb_mapping.py +1 -1
  65. mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +30 -25
  66. mx_bluesky/hyperion/external_interaction/callbacks/ispyb_callback_base.py +29 -12
  67. mx_bluesky/hyperion/external_interaction/callbacks/log_uid_tag_callback.py +1 -1
  68. mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +1 -1
  69. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +7 -4
  70. mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +5 -3
  71. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +28 -20
  72. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +5 -4
  73. mx_bluesky/hyperion/external_interaction/config_server.py +11 -28
  74. mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +1 -1
  75. mx_bluesky/hyperion/external_interaction/ispyb/ispyb_store.py +1 -1
  76. mx_bluesky/hyperion/external_interaction/nexus/nexus_utils.py +2 -2
  77. mx_bluesky/hyperion/external_interaction/nexus/write_nexus.py +1 -1
  78. mx_bluesky/hyperion/log.py +0 -84
  79. mx_bluesky/hyperion/parameters/components.py +4 -251
  80. mx_bluesky/hyperion/parameters/constants.py +22 -119
  81. mx_bluesky/hyperion/parameters/gridscan.py +35 -74
  82. mx_bluesky/hyperion/parameters/load_centre_collect.py +16 -11
  83. mx_bluesky/hyperion/parameters/rotation.py +23 -10
  84. mx_bluesky/hyperion/utils/utils.py +17 -0
  85. mx_bluesky/hyperion/utils/validation.py +5 -6
  86. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/METADATA +36 -33
  87. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/RECORD +91 -81
  88. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/WHEEL +1 -1
  89. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +0 -161
  90. mx_bluesky/example.py +0 -19
  91. mx_bluesky/hyperion/parameters/robot_load.py +0 -16
  92. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/LICENSE +0 -0
  93. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/entry_points.txt +0 -0
  94. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/top_level.txt +0 -0
@@ -1,25 +1,36 @@
1
- import dataclasses
1
+ from collections.abc import Sequence
2
2
 
3
- from blueapi.core import BlueskyContext
3
+ import pydantic
4
+ from blueapi.core import BlueskyContext, MsgGenerator
5
+ from bluesky.preprocessors import subs_wrapper
4
6
  from dodal.devices.oav.oav_parameters import OAVParameters
7
+ from dodal.devices.smargon import Smargon
5
8
 
9
+ import mx_bluesky.hyperion.experiment_plans.common.xrc_result as flyscan_result
10
+ from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
11
+ XRayCentreEventHandler,
12
+ )
6
13
  from mx_bluesky.hyperion.experiment_plans.robot_load_then_centre_plan import (
7
14
  RobotLoadThenCentreComposite,
8
- robot_load_then_centre,
15
+ robot_load_then_xray_centre,
9
16
  )
10
17
  from mx_bluesky.hyperion.experiment_plans.rotation_scan_plan import (
18
+ MultiRotationScan,
11
19
  RotationScanComposite,
12
20
  multi_rotation_scan,
13
21
  )
22
+ from mx_bluesky.hyperion.log import LOGGER
14
23
  from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect
15
24
  from mx_bluesky.hyperion.utils.context import device_composite_from_context
16
25
 
17
26
 
18
- @dataclasses.dataclass
27
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
19
28
  class LoadCentreCollectComposite(RobotLoadThenCentreComposite, RotationScanComposite):
20
29
  """Composite that provides access to the required devices."""
21
30
 
22
- pass
31
+ @property
32
+ def sample_motors(self) -> Smargon:
33
+ return self.smargon
23
34
 
24
35
 
25
36
  def create_devices(context: BlueskyContext) -> LoadCentreCollectComposite:
@@ -27,11 +38,11 @@ def create_devices(context: BlueskyContext) -> LoadCentreCollectComposite:
27
38
  return device_composite_from_context(context, LoadCentreCollectComposite)
28
39
 
29
40
 
30
- def load_centre_collect_full_plan(
41
+ def load_centre_collect_full(
31
42
  composite: LoadCentreCollectComposite,
32
- params: LoadCentreCollect,
43
+ parameters: LoadCentreCollect,
33
44
  oav_params: OAVParameters | None = None,
34
- ):
45
+ ) -> MsgGenerator:
35
46
  """Attempt a complete data collection experiment, consisting of the following:
36
47
  * Load the sample if necessary
37
48
  * Move to the specified goniometer start angles
@@ -41,6 +52,37 @@ def load_centre_collect_full_plan(
41
52
  """
42
53
  if not oav_params:
43
54
  oav_params = OAVParameters(context="xrayCentring")
44
- yield from robot_load_then_centre(composite, params.robot_load_then_centre)
45
55
 
46
- yield from multi_rotation_scan(composite, params.multi_rotation_scan, oav_params)
56
+ flyscan_event_handler = XRayCentreEventHandler()
57
+ yield from subs_wrapper(
58
+ robot_load_then_xray_centre(composite, parameters.robot_load_then_centre),
59
+ flyscan_event_handler,
60
+ )
61
+
62
+ assert (
63
+ flyscan_event_handler.xray_centre_results
64
+ ), "Flyscan result event not received or no crystal found and exception not raised"
65
+
66
+ selection_func = flyscan_result.resolve_selection_fn(parameters.selection_params)
67
+ hits: Sequence[flyscan_result.XRayCentreResult] = selection_func(
68
+ flyscan_event_handler.xray_centre_results
69
+ )
70
+ LOGGER.info(
71
+ f"Selected hits {hits} using {selection_func}, args={parameters.selection_params}"
72
+ )
73
+
74
+ multi_rotation = parameters.multi_rotation_scan
75
+ rotation_template = multi_rotation.rotation_scans.copy()
76
+
77
+ multi_rotation.rotation_scans.clear()
78
+
79
+ for hit in hits:
80
+ for rot in rotation_template:
81
+ combination = rot.model_copy()
82
+ combination.x_start_um, combination.y_start_um, combination.z_start_um = (
83
+ axis * 1000 for axis in hit.centre_of_mass_mm
84
+ )
85
+ multi_rotation.rotation_scans.append(combination)
86
+ multi_rotation = MultiRotationScan.model_validate(multi_rotation)
87
+
88
+ yield from multi_rotation_scan(composite, multi_rotation, oav_params)
@@ -1,11 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
- import dataclasses
4
3
  import math
5
4
  from typing import TYPE_CHECKING
6
5
 
7
6
  import bluesky.plan_stubs as bps
8
7
  import numpy as np
8
+ import pydantic
9
9
  from blueapi.core import BlueskyContext
10
10
  from dodal.devices.backlight import Backlight
11
11
  from dodal.devices.oav.oav_detector import OAV
@@ -26,7 +26,7 @@ if TYPE_CHECKING:
26
26
  from dodal.devices.oav.oav_parameters import OAVParameters
27
27
 
28
28
 
29
- @dataclasses.dataclass
29
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
30
30
  class OavGridDetectionComposite:
31
31
  """All devices which are directly or indirectly required by this plan"""
32
32
 
@@ -63,7 +63,7 @@ def grid_detection_plan(
63
63
  snapshot_template: str,
64
64
  snapshot_dir: str,
65
65
  grid_width_microns: float,
66
- box_size_um: float = 20,
66
+ box_size_um: float,
67
67
  ):
68
68
  """
69
69
  Creates the parameters for two grids that are 90 degrees from each other and
@@ -74,7 +74,7 @@ def grid_detection_plan(
74
74
  parameters (OAVParameters): Object containing parameters for setting up the OAV
75
75
  snapshot_template (str): A template for the name of the snapshots, expected to be filled in with an angle
76
76
  snapshot_dir (str): The location to save snapshots
77
- grid_width_microns (int): The width of the grid to scan in microns
77
+ grid_width_microns (float): The width of the grid to scan in microns
78
78
  box_size_um (float): The size of each box of the grid in microns
79
79
  """
80
80
  oav: OAV = composite.oav
@@ -90,16 +90,17 @@ def grid_detection_plan(
90
90
 
91
91
  LOGGER.info("OAV Centring: Camera set up")
92
92
 
93
- assert isinstance(oav.parameters.micronsPerXPixel, float)
94
- box_size_x_pixels = box_size_um / oav.parameters.micronsPerXPixel
95
- assert isinstance(oav.parameters.micronsPerYPixel, float)
96
- box_size_y_pixels = box_size_um / oav.parameters.micronsPerYPixel
93
+ microns_per_pixel_x = yield from bps.rd(oav.microns_per_pixel_x)
94
+ microns_per_pixel_y = yield from bps.rd(oav.microns_per_pixel_y)
97
95
 
98
- grid_width_pixels = int(grid_width_microns / oav.parameters.micronsPerXPixel)
96
+ box_size_x_pixels = box_size_um / microns_per_pixel_x
97
+ box_size_y_pixels = box_size_um / microns_per_pixel_y
98
+
99
+ grid_width_pixels = int(grid_width_microns / microns_per_pixel_x)
99
100
 
100
101
  # The FGS uses -90 so we need to match it
101
102
  for angle in [0, -90]:
102
- yield from bps.mv(smargon.omega, angle)
103
+ yield from bps.mv(smargon.omega, angle) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
103
104
  # need to wait for the OAV image to update
104
105
  # See #673 for improvements
105
106
  yield from bps.sleep(CONST.HARDWARE.OAV_REFRESH_DELAY)
@@ -115,7 +116,7 @@ def grid_detection_plan(
115
116
  (yield from bps.rd(pin_tip_detection.triggered_bottom_edge))
116
117
  )
117
118
 
118
- full_image_height_px = yield from bps.rd(oav.cam.array_size.array_size_y)
119
+ full_image_height_px = yield from bps.rd(oav.cam.array_size_y)
119
120
 
120
121
  # only use the area from the start of the pin onwards
121
122
  top_edge = top_edge[tip_x_px : tip_x_px + grid_width_pixels]
@@ -151,20 +152,20 @@ def grid_detection_plan(
151
152
 
152
153
  upper_left = (tip_x_px, min_y)
153
154
 
154
- yield from bps.abs_set(oav.grid_snapshot.top_left_x, upper_left[0])
155
- yield from bps.abs_set(oav.grid_snapshot.top_left_y, upper_left[1])
156
- yield from bps.abs_set(oav.grid_snapshot.box_width, box_size_x_pixels)
157
- yield from bps.abs_set(oav.grid_snapshot.num_boxes_x, x_steps)
158
- yield from bps.abs_set(oav.grid_snapshot.num_boxes_y, y_steps)
155
+ yield from bps.abs_set(oav.grid_snapshot.top_left_x, upper_left[0]) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
156
+ yield from bps.abs_set(oav.grid_snapshot.top_left_y, upper_left[1]) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
157
+ yield from bps.abs_set(oav.grid_snapshot.box_width, box_size_x_pixels) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
158
+ yield from bps.abs_set(oav.grid_snapshot.num_boxes_x, x_steps) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
159
+ yield from bps.abs_set(oav.grid_snapshot.num_boxes_y, y_steps) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
159
160
 
160
161
  snapshot_filename = snapshot_template.format(angle=abs(angle))
161
162
 
162
- yield from bps.abs_set(oav.grid_snapshot.filename, snapshot_filename)
163
- yield from bps.abs_set(oav.grid_snapshot.directory, snapshot_dir)
164
- yield from bps.trigger(oav.grid_snapshot, wait=True)
163
+ yield from bps.abs_set(oav.grid_snapshot.filename, snapshot_filename) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
164
+ yield from bps.abs_set(oav.grid_snapshot.directory, snapshot_dir) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
165
+ yield from bps.trigger(oav.grid_snapshot, wait=True) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
165
166
  yield from bps.create(CONST.DESCRIPTORS.OAV_GRID_SNAPSHOT_TRIGGERED)
166
167
 
167
- yield from bps.read(oav.grid_snapshot)
168
+ yield from bps.read(oav) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
168
169
  yield from bps.read(smargon)
169
170
  yield from bps.save()
170
171
 
@@ -1,16 +1,16 @@
1
1
  from datetime import datetime
2
2
  from typing import Protocol
3
3
 
4
- from blueapi.core import MsgGenerator
5
4
  from bluesky import plan_stubs as bps
6
- from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureValue
5
+ from bluesky.utils import MsgGenerator
6
+ from dodal.devices.aperturescatterguard import ApertureScatterguard
7
7
  from dodal.devices.backlight import Backlight, BacklightPosition
8
8
  from dodal.devices.oav.oav_detector import OAV
9
9
  from dodal.devices.oav.oav_parameters import OAVParameters
10
10
  from dodal.devices.smargon import Smargon
11
11
 
12
+ from mx_bluesky.common.parameters.components import WithSnapshot
12
13
  from mx_bluesky.hyperion.device_setup_plans.setup_oav import setup_general_oav_params
13
- from mx_bluesky.hyperion.parameters.components import WithSnapshot
14
14
  from mx_bluesky.hyperion.parameters.constants import CONST, DocDescriptorNames
15
15
 
16
16
  OAV_SNAPSHOT_SETUP_SHOT = "oav_snapshot_setup_shot"
@@ -33,22 +33,16 @@ def setup_beamline_for_OAV(
33
33
  max_vel = yield from bps.rd(smargon.omega.max_velocity)
34
34
  yield from bps.abs_set(smargon.omega.velocity, max_vel, group=group)
35
35
  yield from bps.abs_set(backlight, BacklightPosition.IN, group=group)
36
- yield from bps.abs_set(
37
- aperture_scatterguard,
38
- ApertureValue.ROBOT_LOAD,
39
- group=group,
40
- )
36
+ yield from bps.trigger(aperture_scatterguard.move_out, group=group)
41
37
 
42
38
 
43
39
  def oav_snapshot_plan(
44
40
  composite: OavSnapshotComposite,
45
41
  parameters: WithSnapshot,
46
42
  oav_parameters: OAVParameters,
47
- wait: bool = True,
48
43
  ) -> MsgGenerator:
49
44
  if not parameters.take_snapshots:
50
45
  return
51
- yield from bps.wait(group=CONST.WAIT.READY_FOR_OAV)
52
46
  yield from _setup_oav(composite, parameters, oav_parameters)
53
47
  for omega in parameters.snapshot_omegas_deg or []:
54
48
  yield from _take_oav_snapshot(composite, omega)
@@ -61,7 +55,8 @@ def _setup_oav(
61
55
  ):
62
56
  yield from setup_general_oav_params(composite.oav, oav_parameters)
63
57
  yield from bps.abs_set(
64
- composite.oav.snapshot.directory, str(parameters.snapshot_directory)
58
+ composite.oav.snapshot.directory, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
59
+ str(parameters.snapshot_directory),
65
60
  )
66
61
 
67
62
 
@@ -72,10 +67,12 @@ def _take_oav_snapshot(composite: OavSnapshotComposite, omega: float):
72
67
  time_now = datetime.now()
73
68
  filename = f"{time_now.strftime('%H%M%S')}_oav_snapshot_{omega:.0f}"
74
69
  yield from bps.abs_set(
75
- composite.oav.snapshot.filename, filename, group=OAV_SNAPSHOT_SETUP_SHOT
70
+ composite.oav.snapshot.filename, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
71
+ filename,
72
+ group=OAV_SNAPSHOT_SETUP_SHOT,
76
73
  )
77
74
  yield from bps.wait(group=OAV_SNAPSHOT_SETUP_SHOT)
78
- yield from bps.trigger(composite.oav.snapshot, wait=True)
75
+ yield from bps.trigger(composite.oav.snapshot, wait=True) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
79
76
  yield from bps.create(DocDescriptorNames.OAV_ROTATION_SNAPSHOT_TRIGGERED)
80
- yield from bps.read(composite.oav.snapshot)
77
+ yield from bps.read(composite.oav.snapshot) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
81
78
  yield from bps.save()
@@ -1,9 +1,9 @@
1
- import dataclasses
2
1
  from enum import Enum
3
2
 
4
3
  import bluesky.plan_stubs as bps
5
4
  import bluesky.preprocessors as bpp
6
5
  import numpy as np
6
+ import pydantic
7
7
  from blueapi.core import BlueskyContext
8
8
  from dodal.devices.attenuator import Attenuator
9
9
  from dodal.devices.xspress3.xspress3 import Xspress3
@@ -22,7 +22,7 @@ class Direction(Enum):
22
22
  NEGATIVE = "negative"
23
23
 
24
24
 
25
- @dataclasses.dataclass
25
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
26
26
  class OptimizeAttenuationComposite:
27
27
  """All devices which are directly or indirectly required by this plan"""
28
28
 
@@ -2,14 +2,27 @@ from __future__ import annotations
2
2
 
3
3
  import json
4
4
 
5
- from blueapi.core import BlueskyContext, MsgGenerator
5
+ import bluesky.preprocessors as bpp
6
+ from blueapi.core import BlueskyContext
7
+ from bluesky.utils import MsgGenerator
6
8
  from dodal.devices.eiger import EigerDetector
7
- from dodal.devices.oav.oav_parameters import OAV_CONFIG_JSON, OAVParameters
9
+ from dodal.devices.oav.oav_parameters import OAVParameters
8
10
 
11
+ from mx_bluesky.common.parameters.constants import OavConstants
12
+ from mx_bluesky.common.parameters.gridscan import (
13
+ GridScanWithEdgeDetect,
14
+ PinTipCentreThenXrayCentre,
15
+ )
9
16
  from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import move_phi_chi_omega
10
17
  from mx_bluesky.hyperion.device_setup_plans.utils import (
11
18
  start_preparing_data_collection_then_do_plan,
12
19
  )
20
+ from mx_bluesky.hyperion.experiment_plans.change_aperture_then_move_plan import (
21
+ change_aperture_then_move_to_xtal,
22
+ )
23
+ from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
24
+ XRayCentreEventHandler,
25
+ )
13
26
  from mx_bluesky.hyperion.experiment_plans.grid_detect_then_xray_centre_plan import (
14
27
  GridDetectThenXRayCentreComposite,
15
28
  detect_grid_and_do_gridscan,
@@ -26,10 +39,6 @@ from mx_bluesky.hyperion.external_interaction.callbacks.xray_centre.ispyb_callba
26
39
  )
27
40
  from mx_bluesky.hyperion.log import LOGGER
28
41
  from mx_bluesky.hyperion.parameters.constants import CONST
29
- from mx_bluesky.hyperion.parameters.gridscan import (
30
- GridScanWithEdgeDetect,
31
- PinTipCentreThenXrayCentre,
32
- )
33
42
  from mx_bluesky.hyperion.utils.context import device_composite_from_context
34
43
 
35
44
 
@@ -52,14 +61,12 @@ def create_parameters_for_grid_detection(
52
61
  return grid_detect_and_xray_centre
53
62
 
54
63
 
55
- def pin_centre_then_xray_centre_plan(
64
+ def pin_centre_then_flyscan_plan(
56
65
  composite: GridDetectThenXRayCentreComposite,
57
66
  parameters: PinTipCentreThenXrayCentre,
58
- oav_config_file: str = OAV_CONFIG_JSON,
67
+ oav_config_file: str = OavConstants.OAV_CONFIG_JSON,
59
68
  ):
60
- """Plan that perfoms a pin tip centre followed by an xray centre to completely
61
- centre the sample"""
62
- oav_config_file = parameters.oav_centring_file
69
+ """Plan that performs a pin tip centre followed by a flyscan to determine the centres of interest"""
63
70
 
64
71
  pin_tip_centring_composite = PinTipCentringComposite(
65
72
  oav=composite.oav,
@@ -68,7 +75,7 @@ def pin_centre_then_xray_centre_plan(
68
75
  pin_tip_detection=composite.pin_tip_detection,
69
76
  )
70
77
 
71
- def _pin_centre_then_xray_centre_plan():
78
+ def _pin_centre_then_flyscan_plan():
72
79
  yield from setup_beamline_for_OAV(
73
80
  composite.smargon, composite.backlight, composite.aperture_scatterguard
74
81
  )
@@ -96,24 +103,36 @@ def pin_centre_then_xray_centre_plan(
96
103
  oav_params,
97
104
  )
98
105
 
99
- yield from ispyb_activation_wrapper(_pin_centre_then_xray_centre_plan(), parameters)
106
+ yield from ispyb_activation_wrapper(_pin_centre_then_flyscan_plan(), parameters)
100
107
 
101
108
 
102
109
  def pin_tip_centre_then_xray_centre(
103
110
  composite: GridDetectThenXRayCentreComposite,
104
111
  parameters: PinTipCentreThenXrayCentre,
105
- oav_config_file: str = OAV_CONFIG_JSON,
112
+ oav_config_file: str = OavConstants.OAV_CONFIG_JSON,
106
113
  ) -> MsgGenerator:
107
114
  """Starts preparing for collection then performs the pin tip centre and xray centre"""
108
-
109
115
  eiger: EigerDetector = composite.eiger
110
116
 
111
117
  eiger.set_detector_parameters(parameters.detector_params)
112
118
 
113
- return start_preparing_data_collection_then_do_plan(
114
- eiger,
115
- composite.detector_motion,
116
- parameters.detector_params.detector_distance,
117
- pin_centre_then_xray_centre_plan(composite, parameters, oav_config_file),
118
- group=CONST.WAIT.GRID_READY_FOR_DC,
119
+ flyscan_event_handler = XRayCentreEventHandler()
120
+
121
+ @bpp.subs_decorator(flyscan_event_handler)
122
+ def pin_centre_flyscan_then_fetch_results() -> MsgGenerator:
123
+ yield from start_preparing_data_collection_then_do_plan(
124
+ eiger,
125
+ composite.detector_motion,
126
+ parameters.detector_params.detector_distance,
127
+ pin_centre_then_flyscan_plan(composite, parameters, oav_config_file),
128
+ group=CONST.WAIT.GRID_READY_FOR_DC,
129
+ )
130
+
131
+ yield from pin_centre_flyscan_then_fetch_results()
132
+ flyscan_results = flyscan_event_handler.xray_centre_results
133
+ assert (
134
+ flyscan_results
135
+ ), "Flyscan result event not received or no crystal found and exception not raised"
136
+ yield from change_aperture_then_move_to_xtal(
137
+ flyscan_results[0], composite.smargon, composite.aperture_scatterguard
119
138
  )
@@ -1,13 +1,13 @@
1
- import dataclasses
2
1
  from collections.abc import Generator
3
2
 
4
3
  import bluesky.plan_stubs as bps
4
+ import pydantic
5
5
  from blueapi.core import BlueskyContext
6
6
  from bluesky.utils import Msg
7
7
  from dodal.devices.backlight import Backlight
8
8
  from dodal.devices.oav.oav_detector import OAV
9
9
  from dodal.devices.oav.oav_parameters import OAV_CONFIG_JSON, OAVParameters
10
- from dodal.devices.oav.pin_image_recognition import PinTipDetection
10
+ from dodal.devices.oav.pin_image_recognition import PinTipDetection, Tip
11
11
  from dodal.devices.oav.utils import (
12
12
  Pixel,
13
13
  get_move_required_so_that_beam_is_at_pixel,
@@ -27,7 +27,7 @@ from mx_bluesky.hyperion.utils.context import device_composite_from_context
27
27
  DEFAULT_STEP_SIZE = 0.5
28
28
 
29
29
 
30
- @dataclasses.dataclass
30
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
31
31
  class PinTipCentringComposite:
32
32
  """All devices which are directly or indirectly required by this plan"""
33
33
 
@@ -43,11 +43,11 @@ def create_devices(context: BlueskyContext) -> PinTipCentringComposite:
43
43
 
44
44
  def trigger_and_return_pin_tip(
45
45
  pin_tip: PinTipDetection,
46
- ) -> Generator[Msg, None, Pixel]:
46
+ ) -> Generator[Msg, None, Tip]:
47
47
  yield from bps.trigger(pin_tip, wait=True)
48
48
  tip_x_y_px = yield from bps.rd(pin_tip.triggered_tip)
49
49
  LOGGER.info(f"Pin tip found at {tip_x_y_px}")
50
- return tip_x_y_px # type: ignore
50
+ return tip_x_y_px
51
51
 
52
52
 
53
53
  def move_pin_into_view(
@@ -74,16 +74,16 @@ def move_pin_into_view(
74
74
  Tuple[int, int]: The location of the pin tip in pixels
75
75
  """
76
76
 
77
- def pin_tip_valid(pin_x: float):
78
- return pin_x != 0 and pin_x != pin_tip_device.INVALID_POSITION[0]
77
+ def pin_tip_valid(pin_xy: Tip):
78
+ return not all(pin_xy == pin_tip_device.INVALID_POSITION) and pin_xy[0] != 0
79
79
 
80
80
  for _ in range(max_steps):
81
- tip_x_px, tip_y_px = yield from trigger_and_return_pin_tip(pin_tip_device)
81
+ tip_xy_px = yield from trigger_and_return_pin_tip(pin_tip_device)
82
82
 
83
- if pin_tip_valid(tip_x_px):
84
- return (tip_x_px, tip_y_px)
83
+ if pin_tip_valid(tip_xy_px):
84
+ return (int(tip_xy_px[0]), int(tip_xy_px[1]))
85
85
 
86
- if tip_x_px == 0:
86
+ if tip_xy_px[0] == 0:
87
87
  # Pin is off in the -ve direction
88
88
  step_size_mm = -step_size_mm
89
89
 
@@ -97,19 +97,19 @@ def move_pin_into_view(
97
97
  f"Pin tip is off screen, and moving {step_size_mm} mm would cross limits, "
98
98
  f"moving to {move_within_limits} instead"
99
99
  )
100
- yield from bps.mv(smargon.x, move_within_limits)
100
+ yield from bps.mv(smargon.x, move_within_limits) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
101
101
 
102
102
  # Some time for the view to settle after the move
103
103
  yield from bps.sleep(CONST.HARDWARE.OAV_REFRESH_DELAY)
104
104
 
105
- tip_x_px, tip_y_px = yield from trigger_and_return_pin_tip(pin_tip_device)
105
+ tip_xy_px = yield from trigger_and_return_pin_tip(pin_tip_device)
106
106
 
107
- if not pin_tip_valid(tip_x_px):
107
+ if not pin_tip_valid(tip_xy_px):
108
108
  raise WarningException(
109
109
  "Pin tip centring failed - pin too long/short/bent and out of range"
110
110
  )
111
111
  else:
112
- return (tip_x_px, tip_y_px)
112
+ return (int(tip_xy_px[0]), int(tip_xy_px[1]))
113
113
 
114
114
 
115
115
  def pin_tip_centre_plan(
@@ -132,13 +132,13 @@ def pin_tip_centre_plan(
132
132
  pin_tip_setup = composite.pin_tip_detection
133
133
  pin_tip_detect = composite.pin_tip_detection
134
134
 
135
- assert oav.parameters.micronsPerXPixel is not None
136
- tip_offset_px = int(tip_offset_microns / oav.parameters.micronsPerXPixel)
135
+ microns_per_pixel_x = yield from bps.rd(oav.microns_per_pixel_x)
136
+ tip_offset_px = int(tip_offset_microns / microns_per_pixel_x)
137
137
 
138
138
  def offset_and_move(tip: Pixel):
139
139
  pixel_to_move_to = (tip[0] + tip_offset_px, tip[1])
140
140
  position_mm = yield from get_move_required_so_that_beam_is_at_pixel(
141
- smargon, pixel_to_move_to, oav.parameters
141
+ smargon, pixel_to_move_to, oav
142
142
  )
143
143
  LOGGER.info(f"Tip centring moving to : {position_mm}")
144
144
  yield from move_smargon_warn_on_out_of_range(smargon, position_mm)
@@ -154,7 +154,7 @@ def pin_tip_centre_plan(
154
154
  tip = yield from move_pin_into_view(pin_tip_detect, smargon)
155
155
  yield from offset_and_move(tip)
156
156
 
157
- yield from bps.mvr(smargon.omega, 90)
157
+ yield from bps.mvr(smargon.omega, 90) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
158
158
 
159
159
  # need to wait for the OAV image to update
160
160
  # See #673 for improvements
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import dataclasses
4
3
  from collections.abc import Generator
5
4
  from datetime import datetime
6
5
  from pathlib import Path
@@ -8,12 +7,13 @@ from typing import cast
8
7
 
9
8
  import bluesky.plan_stubs as bps
10
9
  import bluesky.preprocessors as bpp
10
+ import pydantic
11
11
  from blueapi.core import BlueskyContext
12
12
  from bluesky.utils import Msg
13
- from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureValue
13
+ from dodal.devices.aperturescatterguard import ApertureScatterguard
14
14
  from dodal.devices.attenuator import Attenuator
15
15
  from dodal.devices.dcm import DCM
16
- from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, VFMMirrorVoltages
16
+ from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, MirrorVoltages
17
17
  from dodal.devices.motors import XYZPositioner
18
18
  from dodal.devices.oav.oav_detector import OAV
19
19
  from dodal.devices.robot import BartRobot, SampleLocation
@@ -22,22 +22,22 @@ from dodal.devices.thawer import Thawer
22
22
  from dodal.devices.undulator_dcm import UndulatorDCM
23
23
  from dodal.devices.webcam import Webcam
24
24
  from dodal.devices.xbpm_feedback import XBPMFeedback
25
- from dodal.plans.motor_util_plans import MoveTooLarge, home_and_reset_wrapper
25
+ from dodal.plan_stubs.motor_utils import MoveTooLarge, home_and_reset_wrapper
26
26
 
27
+ from mx_bluesky.common.parameters.robot_load import RobotLoadAndEnergyChange
27
28
  from mx_bluesky.hyperion.experiment_plans.set_energy_plan import (
28
29
  SetEnergyComposite,
29
30
  set_energy_plan,
30
31
  )
31
32
  from mx_bluesky.hyperion.log import LOGGER
32
33
  from mx_bluesky.hyperion.parameters.constants import CONST
33
- from mx_bluesky.hyperion.parameters.robot_load import RobotLoadAndEnergyChange
34
34
 
35
35
 
36
- @dataclasses.dataclass
36
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
37
37
  class RobotLoadAndEnergyChangeComposite:
38
38
  # SetEnergyComposite fields
39
39
  vfm: FocusingMirrorWithStripes
40
- vfm_mirror_voltages: VFMMirrorVoltages
40
+ mirror_voltages: MirrorVoltages
41
41
  dcm: DCM
42
42
  undulator_dcm: UndulatorDCM
43
43
  xbpm_feedback: XBPMFeedback
@@ -94,21 +94,19 @@ def take_robot_snapshots(oav: OAV, webcam: Webcam, directory: Path):
94
94
  def prepare_for_robot_load(
95
95
  aperture_scatterguard: ApertureScatterguard, smargon: Smargon
96
96
  ):
97
- yield from bps.abs_set(
98
- aperture_scatterguard,
99
- ApertureValue.ROBOT_LOAD,
100
- group="prepare_robot_load",
101
- )
97
+ yield from bps.trigger(aperture_scatterguard.move_out, group="prepare_robot_load")
102
98
 
103
- yield from bps.mv(smargon.stub_offsets, StubPosition.RESET_TO_ROBOT_LOAD)
99
+ yield from bps.mv(smargon.stub_offsets, StubPosition.RESET_TO_ROBOT_LOAD) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
104
100
 
105
101
  # fmt: off
106
- yield from bps.mv(smargon.x, 0,
107
- smargon.y, 0,
108
- smargon.z, 0,
109
- smargon.omega, 0,
110
- smargon.chi, 0,
111
- smargon.phi, 0)
102
+ yield from bps.mv(
103
+ smargon.x, 0, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
104
+ smargon.y, 0, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
105
+ smargon.z, 0, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
106
+ smargon.omega, 0, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
107
+ smargon.chi, 0, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
108
+ smargon.phi, 0 # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
109
+ )
112
110
  # fmt: on
113
111
 
114
112
  yield from bps.wait("prepare_robot_load")
@@ -135,7 +133,9 @@ def do_robot_load(
135
133
  yield from bps.wait("robot_load")
136
134
 
137
135
  yield from bps.abs_set(
138
- composite.thawer.thaw_for_time_s, thawing_time, group="thawing_finished"
136
+ composite.thawer.thaw_for_time_s, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
137
+ thawing_time,
138
+ group="thawing_finished",
139
139
  )
140
140
  yield from wait_for_smargon_not_disabled(composite.smargon)
141
141
 
@@ -195,7 +195,7 @@ def robot_load_and_snapshots(
195
195
 
196
196
  yield from bps.create(name=CONST.DESCRIPTORS.ROBOT_LOAD)
197
197
  yield from bps.read(composite.robot.barcode)
198
- yield from bps.read(composite.oav.snapshot)
198
+ yield from bps.read(composite.oav.snapshot) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
199
199
  yield from bps.read(composite.webcam)
200
200
  yield from bps.save()
201
201