mx-bluesky 1.2.0__py3-none-any.whl → 1.4.1__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 (105) 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/redis_to_murko_forwarder.py +178 -0
  6. mx_bluesky/beamlines/i04/thawing_plan.py +49 -11
  7. mx_bluesky/beamlines/i24/serial/__init__.py +3 -0
  8. mx_bluesky/beamlines/i24/serial/dcid.py +143 -171
  9. mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +1 -1
  10. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +121 -110
  11. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +3 -6
  12. mx_bluesky/beamlines/i24/serial/fixed_target/ft_utils.py +0 -1
  13. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +164 -169
  14. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +149 -225
  15. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +7 -216
  16. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +18 -17
  17. mx_bluesky/beamlines/i24/serial/log.py +58 -49
  18. mx_bluesky/beamlines/i24/serial/parameters/__init__.py +4 -0
  19. mx_bluesky/beamlines/i24/serial/parameters/constants.py +6 -1
  20. mx_bluesky/beamlines/i24/serial/parameters/experiment_parameters.py +42 -15
  21. mx_bluesky/beamlines/i24/serial/parameters/fixed_target/cs/cs_maker.json +3 -3
  22. mx_bluesky/beamlines/i24/serial/run_extruder.sh +30 -5
  23. mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +30 -5
  24. mx_bluesky/beamlines/i24/serial/run_serial.py +24 -8
  25. mx_bluesky/beamlines/i24/serial/setup_beamline/ca.py +0 -2
  26. mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +2 -0
  27. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +104 -82
  28. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +9 -20
  29. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +26 -28
  30. mx_bluesky/beamlines/i24/serial/write_nexus.py +74 -72
  31. mx_bluesky/common/__init__.py +0 -0
  32. mx_bluesky/common/device_setup_plans/read_hardware_for_setup.py +14 -0
  33. mx_bluesky/common/external_interaction/config_server.py +46 -0
  34. mx_bluesky/common/parameters/components.py +258 -0
  35. mx_bluesky/common/parameters/constants.py +143 -0
  36. mx_bluesky/common/parameters/gridscan.py +94 -0
  37. mx_bluesky/common/parameters/robot_load.py +16 -0
  38. mx_bluesky/common/plans/__init__.py +1 -0
  39. mx_bluesky/common/plans/do_fgs.py +121 -0
  40. mx_bluesky/common/utils/log.py +118 -0
  41. mx_bluesky/{hyperion → common/utils}/tracing.py +2 -2
  42. mx_bluesky/hyperion/__main__.py +13 -10
  43. mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +47 -52
  44. mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +6 -12
  45. mx_bluesky/hyperion/device_setup_plans/setup_oav.py +6 -12
  46. mx_bluesky/hyperion/device_setup_plans/setup_panda.py +5 -6
  47. mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +49 -18
  48. mx_bluesky/hyperion/device_setup_plans/smargon.py +9 -9
  49. mx_bluesky/hyperion/device_setup_plans/utils.py +2 -2
  50. mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +4 -4
  51. mx_bluesky/hyperion/exceptions.py +13 -1
  52. mx_bluesky/hyperion/experiment_plans/__init__.py +4 -0
  53. mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +83 -0
  54. mx_bluesky/hyperion/experiment_plans/common/xrc_result.py +47 -0
  55. mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -9
  56. mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +147 -169
  57. mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +48 -22
  58. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +75 -9
  59. mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +21 -20
  60. mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +9 -6
  61. mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +2 -2
  62. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +40 -21
  63. mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +22 -22
  64. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +43 -39
  65. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +69 -18
  66. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +17 -7
  67. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +13 -13
  68. mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +0 -4
  69. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +5 -2
  70. mx_bluesky/hyperion/external_interaction/callbacks/common/abstract_event.py +66 -0
  71. mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +5 -0
  72. mx_bluesky/hyperion/external_interaction/callbacks/common/ispyb_mapping.py +1 -1
  73. mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +30 -25
  74. mx_bluesky/hyperion/external_interaction/callbacks/ispyb_callback_base.py +29 -12
  75. mx_bluesky/hyperion/external_interaction/callbacks/log_uid_tag_callback.py +1 -1
  76. mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +19 -11
  77. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +7 -4
  78. mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +5 -3
  79. mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/__init__.py +0 -0
  80. mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/sample_handling_callback.py +84 -0
  81. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +38 -27
  82. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +5 -4
  83. mx_bluesky/hyperion/external_interaction/config_server.py +11 -28
  84. mx_bluesky/hyperion/external_interaction/exceptions.py +0 -9
  85. mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +65 -15
  86. mx_bluesky/hyperion/external_interaction/ispyb/ispyb_store.py +1 -1
  87. mx_bluesky/hyperion/external_interaction/nexus/nexus_utils.py +2 -2
  88. mx_bluesky/hyperion/external_interaction/nexus/write_nexus.py +1 -1
  89. mx_bluesky/hyperion/log.py +0 -84
  90. mx_bluesky/hyperion/parameters/components.py +4 -251
  91. mx_bluesky/hyperion/parameters/constants.py +22 -119
  92. mx_bluesky/hyperion/parameters/gridscan.py +35 -74
  93. mx_bluesky/hyperion/parameters/load_centre_collect.py +16 -11
  94. mx_bluesky/hyperion/parameters/rotation.py +23 -10
  95. mx_bluesky/hyperion/utils/utils.py +17 -0
  96. mx_bluesky/hyperion/utils/validation.py +5 -6
  97. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/METADATA +36 -33
  98. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/RECORD +102 -89
  99. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/WHEEL +1 -1
  100. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +0 -161
  101. mx_bluesky/example.py +0 -19
  102. mx_bluesky/hyperion/parameters/robot_load.py +0 -16
  103. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/LICENSE +0 -0
  104. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/entry_points.txt +0 -0
  105. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/top_level.txt +0 -0
@@ -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
13
  from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureValue
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
@@ -100,15 +100,17 @@ def prepare_for_robot_load(
100
100
  group="prepare_robot_load",
101
101
  )
102
102
 
103
- yield from bps.mv(smargon.stub_offsets, StubPosition.RESET_TO_ROBOT_LOAD)
103
+ yield from bps.mv(smargon.stub_offsets, StubPosition.RESET_TO_ROBOT_LOAD) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
104
104
 
105
105
  # 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)
106
+ yield from bps.mv(
107
+ smargon.x, 0, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
108
+ smargon.y, 0, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
109
+ smargon.z, 0, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
110
+ smargon.omega, 0, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
111
+ smargon.chi, 0, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
112
+ smargon.phi, 0 # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
113
+ )
112
114
  # fmt: on
113
115
 
114
116
  yield from bps.wait("prepare_robot_load")
@@ -126,16 +128,14 @@ def do_robot_load(
126
128
  group="robot_load",
127
129
  )
128
130
 
129
- if demand_energy_ev:
130
- yield from set_energy_plan(
131
- demand_energy_ev / 1000,
132
- cast(SetEnergyComposite, composite),
133
- )
131
+ yield from set_energy_plan(demand_energy_ev, cast(SetEnergyComposite, composite))
134
132
 
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
 
@@ -214,24 +214,28 @@ def robot_load_and_change_energy_plan(
214
214
  yield from prepare_for_robot_load(
215
215
  composite.aperture_scatterguard, composite.smargon
216
216
  )
217
- yield from bpp.run_wrapper(
218
- robot_load_and_snapshots(
219
- composite,
220
- sample_location,
221
- params.snapshot_directory,
222
- params.thawing_time,
223
- params.demand_energy_ev,
224
- ),
225
- md={
226
- "subplan_name": CONST.PLAN.ROBOT_LOAD,
227
- "metadata": {
228
- "visit": params.visit,
229
- "sample_id": params.sample_id,
230
- "sample_puck": sample_location.puck,
231
- "sample_pin": sample_location.pin,
217
+
218
+ yield from bpp.set_run_key_wrapper(
219
+ bpp.run_wrapper(
220
+ robot_load_and_snapshots(
221
+ composite,
222
+ sample_location,
223
+ params.snapshot_directory,
224
+ params.thawing_time,
225
+ params.demand_energy_ev,
226
+ ),
227
+ md={
228
+ "subplan_name": CONST.PLAN.ROBOT_LOAD,
229
+ "metadata": {
230
+ "visit": params.visit,
231
+ "sample_id": params.sample_id,
232
+ "sample_puck": sample_location.puck,
233
+ "sample_pin": sample_location.pin,
234
+ },
235
+ "activate_callbacks": [
236
+ "RobotLoadISPyBCallback",
237
+ ],
232
238
  },
233
- "activate_callbacks": [
234
- "RobotLoadISPyBCallback",
235
- ],
236
- },
239
+ ),
240
+ CONST.PLAN.ROBOT_LOAD_AND_SNAPSHOTS,
237
241
  )
@@ -1,9 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
- import dataclasses
4
3
  from typing import cast
5
4
 
6
- from blueapi.core import BlueskyContext, MsgGenerator
5
+ import bluesky.preprocessors as bpp
6
+ import pydantic
7
+ from blueapi.core import BlueskyContext
8
+ from bluesky.utils import MsgGenerator
7
9
  from dodal.devices.aperturescatterguard import ApertureScatterguard
8
10
  from dodal.devices.attenuator import Attenuator
9
11
  from dodal.devices.backlight import Backlight
@@ -12,7 +14,7 @@ from dodal.devices.detector.detector_motion import DetectorMotion
12
14
  from dodal.devices.eiger import EigerDetector
13
15
  from dodal.devices.fast_grid_scan import PandAFastGridScan, ZebraFastGridScan
14
16
  from dodal.devices.flux import Flux
15
- from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, VFMMirrorVoltages
17
+ from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, MirrorVoltages
16
18
  from dodal.devices.motors import XYZPositioner
17
19
  from dodal.devices.oav.oav_detector import OAV
18
20
  from dodal.devices.oav.pin_image_recognition import PinTipDetection
@@ -31,26 +33,37 @@ from dodal.devices.zocalo import ZocaloResults
31
33
  from dodal.log import LOGGER
32
34
  from ophyd_async.fastcs.panda import HDFPanda
33
35
 
36
+ from mx_bluesky.common.parameters.constants import OavConstants
37
+ from mx_bluesky.common.parameters.gridscan import RobotLoadThenCentre
34
38
  from mx_bluesky.hyperion.device_setup_plans.utils import (
35
39
  fill_in_energy_if_not_supplied,
36
40
  start_preparing_data_collection_then_do_plan,
37
41
  )
42
+ from mx_bluesky.hyperion.experiment_plans.change_aperture_then_move_plan import (
43
+ change_aperture_then_move_to_xtal,
44
+ )
45
+ from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
46
+ XRayCentreEventHandler,
47
+ )
38
48
  from mx_bluesky.hyperion.experiment_plans.grid_detect_then_xray_centre_plan import (
39
49
  GridDetectThenXRayCentreComposite,
40
50
  )
41
51
  from mx_bluesky.hyperion.experiment_plans.pin_centre_then_xray_centre_plan import (
42
- pin_centre_then_xray_centre_plan,
52
+ pin_centre_then_flyscan_plan,
43
53
  )
44
54
  from mx_bluesky.hyperion.experiment_plans.robot_load_and_change_energy import (
45
55
  RobotLoadAndEnergyChangeComposite,
46
56
  pin_already_loaded,
47
57
  robot_load_and_change_energy_plan,
48
58
  )
59
+ from mx_bluesky.hyperion.experiment_plans.set_energy_plan import (
60
+ SetEnergyComposite,
61
+ set_energy_plan,
62
+ )
49
63
  from mx_bluesky.hyperion.parameters.constants import CONST
50
- from mx_bluesky.hyperion.parameters.gridscan import RobotLoadThenCentre
51
64
 
52
65
 
53
- @dataclasses.dataclass
66
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
54
67
  class RobotLoadThenCentreComposite:
55
68
  # common fields
56
69
  xbpm_feedback: XBPMFeedback
@@ -78,7 +91,7 @@ class RobotLoadThenCentreComposite:
78
91
 
79
92
  # SetEnergyComposite fields
80
93
  vfm: FocusingMirrorWithStripes
81
- vfm_mirror_voltages: VFMMirrorVoltages
94
+ mirror_voltages: MirrorVoltages
82
95
  dcm: DCM
83
96
  undulator_dcm: UndulatorDCM
84
97
 
@@ -87,6 +100,10 @@ class RobotLoadThenCentreComposite:
87
100
  webcam: Webcam
88
101
  lower_gonio: XYZPositioner
89
102
 
103
+ @property
104
+ def sample_motors(self):
105
+ return self.smargon
106
+
90
107
 
91
108
  def create_devices(context: BlueskyContext) -> RobotLoadThenCentreComposite:
92
109
  from mx_bluesky.hyperion.utils.context import device_composite_from_context
@@ -94,32 +111,57 @@ def create_devices(context: BlueskyContext) -> RobotLoadThenCentreComposite:
94
111
  return device_composite_from_context(context, RobotLoadThenCentreComposite)
95
112
 
96
113
 
97
- def centring_plan_from_robot_load_params(
114
+ def _flyscan_plan_from_robot_load_params(
98
115
  composite: RobotLoadThenCentreComposite,
99
116
  params: RobotLoadThenCentre,
117
+ oav_config_file: str = OavConstants.OAV_CONFIG_JSON,
100
118
  ):
101
- yield from pin_centre_then_xray_centre_plan(
119
+ yield from pin_centre_then_flyscan_plan(
102
120
  cast(GridDetectThenXRayCentreComposite, composite),
103
121
  params.pin_centre_then_xray_centre_params(),
104
122
  )
105
123
 
106
124
 
107
- def robot_load_then_centre_plan(
125
+ def _robot_load_then_flyscan_plan(
108
126
  composite: RobotLoadThenCentreComposite,
109
127
  params: RobotLoadThenCentre,
128
+ oav_config_file: str = OavConstants.OAV_CONFIG_JSON,
110
129
  ):
111
130
  yield from robot_load_and_change_energy_plan(
112
131
  cast(RobotLoadAndEnergyChangeComposite, composite),
113
132
  params.robot_load_params(),
114
133
  )
115
134
 
116
- yield from centring_plan_from_robot_load_params(composite, params)
135
+ yield from _flyscan_plan_from_robot_load_params(composite, params, oav_config_file)
117
136
 
118
137
 
119
138
  def robot_load_then_centre(
120
139
  composite: RobotLoadThenCentreComposite,
121
140
  parameters: RobotLoadThenCentre,
122
141
  ) -> MsgGenerator:
142
+ """Perform pin-tip detection followed by a flyscan to determine centres of interest.
143
+ Performs a robot load if necessary. Centre on the best diffracting centre.
144
+ """
145
+
146
+ xray_centre_event_handler = XRayCentreEventHandler()
147
+
148
+ yield from bpp.subs_wrapper(
149
+ robot_load_then_xray_centre(composite, parameters), xray_centre_event_handler
150
+ )
151
+ flyscan_results = xray_centre_event_handler.xray_centre_results
152
+ if flyscan_results is not None:
153
+ yield from change_aperture_then_move_to_xtal(
154
+ flyscan_results[0], composite.smargon, composite.aperture_scatterguard
155
+ )
156
+ # else no chi change, no need to recentre.
157
+
158
+
159
+ def robot_load_then_xray_centre(
160
+ composite: RobotLoadThenCentreComposite,
161
+ parameters: RobotLoadThenCentre,
162
+ ) -> MsgGenerator:
163
+ """Perform pin-tip detection followed by a flyscan to determine centres of interest.
164
+ Performs a robot load if necessary."""
123
165
  eiger: EigerDetector = composite.eiger
124
166
 
125
167
  # TODO: get these from one source of truth #254
@@ -135,17 +177,26 @@ def robot_load_then_centre(
135
177
  doing_chi_change = parameters.chi_start_deg is not None
136
178
 
137
179
  if doing_sample_load:
138
- plan = robot_load_then_centre_plan(
180
+ LOGGER.info("Pin not loaded, loading and centring")
181
+ plan = _robot_load_then_flyscan_plan(
139
182
  composite,
140
183
  parameters,
141
184
  )
142
- LOGGER.info("Pin not loaded, loading and centring")
143
- elif doing_chi_change:
144
- plan = centring_plan_from_robot_load_params(composite, parameters)
145
- LOGGER.info("Pin already loaded but chi changed so centring")
146
185
  else:
147
- LOGGER.info("Pin already loaded and chi not changed so doing nothing")
148
- return
186
+ # Robot load normally sets the energy so we should do this explicitly if no load is
187
+ # being done
188
+ demand_energy_ev = parameters.demand_energy_ev
189
+ LOGGER.info(f"Setting the energy to {demand_energy_ev}eV")
190
+ yield from set_energy_plan(
191
+ demand_energy_ev, cast(SetEnergyComposite, composite)
192
+ )
193
+
194
+ if doing_chi_change:
195
+ plan = _flyscan_plan_from_robot_load_params(composite, parameters)
196
+ LOGGER.info("Pin already loaded but chi changed so centring")
197
+ else:
198
+ LOGGER.info("Pin already loaded and chi not changed so doing nothing")
199
+ return
149
200
 
150
201
  detector_params = yield from fill_in_energy_if_not_supplied(
151
202
  composite.dcm, parameters.detector_params
@@ -4,7 +4,9 @@ import dataclasses
4
4
 
5
5
  import bluesky.plan_stubs as bps
6
6
  import bluesky.preprocessors as bpp
7
- from blueapi.core import BlueskyContext, MsgGenerator
7
+ import pydantic
8
+ from blueapi.core import BlueskyContext
9
+ from bluesky.utils import MsgGenerator
8
10
  from dodal.devices.aperturescatterguard import ApertureScatterguard
9
11
  from dodal.devices.attenuator import Attenuator
10
12
  from dodal.devices.backlight import Backlight
@@ -22,8 +24,11 @@ from dodal.devices.undulator import Undulator
22
24
  from dodal.devices.xbpm_feedback import XBPMFeedback
23
25
  from dodal.devices.zebra import RotationDirection, Zebra
24
26
  from dodal.devices.zebra_controlled_shutter import ZebraShutter
25
- from dodal.plans.check_topup import check_topup_and_wait_if_necessary
27
+ from dodal.plan_stubs.check_topup import check_topup_and_wait_if_necessary
26
28
 
29
+ from mx_bluesky.common.device_setup_plans.read_hardware_for_setup import (
30
+ read_hardware_for_zocalo,
31
+ )
27
32
  from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import (
28
33
  cleanup_sample_environment,
29
34
  move_phi_chi_omega,
@@ -32,7 +37,6 @@ from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import (
32
37
  )
33
38
  from mx_bluesky.hyperion.device_setup_plans.read_hardware_for_setup import (
34
39
  read_hardware_during_collection,
35
- read_hardware_for_zocalo,
36
40
  read_hardware_pre_collection,
37
41
  )
38
42
  from mx_bluesky.hyperion.device_setup_plans.setup_zebra import (
@@ -60,7 +64,7 @@ from mx_bluesky.hyperion.parameters.rotation import (
60
64
  from mx_bluesky.hyperion.utils.context import device_composite_from_context
61
65
 
62
66
 
63
- @dataclasses.dataclass
67
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
64
68
  class RotationScanComposite(OavSnapshotComposite):
65
69
  """All devices which are directly or indirectly required by this plan"""
66
70
 
@@ -252,7 +256,7 @@ def rotation_scan_plan(
252
256
  composite.undulator,
253
257
  composite.synchrotron,
254
258
  composite.s4_slit_gaps,
255
- composite.robot,
259
+ composite.dcm,
256
260
  composite.smargon,
257
261
  )
258
262
 
@@ -348,7 +352,7 @@ def rotation_scan(
348
352
  md={
349
353
  "subplan_name": CONST.PLAN.ROTATION_OUTER,
350
354
  CONST.TRIGGER.ZOCALO: CONST.PLAN.ROTATION_MAIN,
351
- "zocalo_environment": parameters.zocalo_environment,
355
+ "zocalo_environment": CONST.ZOCALO_ENV,
352
356
  "hyperion_parameters": parameters.model_dump_json(),
353
357
  "activate_callbacks": [
354
358
  "RotationISPyBCallback",
@@ -379,7 +383,7 @@ def rotation_scan(
379
383
  rotation_with_cleanup_and_stage(params),
380
384
  group=CONST.WAIT.ROTATION_READY_FOR_DC,
381
385
  )
382
- yield from bps.unstage(eiger)
386
+ yield from bps.unstage(eiger) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
383
387
 
384
388
  yield from rotation_scan_plan_with_stage_and_cleanup(parameters)
385
389
 
@@ -407,6 +411,11 @@ def multi_rotation_scan(
407
411
  }
408
412
  )
409
413
  @bpp.stage_decorator([eiger])
414
+ @transmission_and_xbpm_feedback_for_collection_decorator(
415
+ composite.xbpm_feedback,
416
+ composite.attenuator,
417
+ parameters.transmission_frac,
418
+ )
410
419
  @bpp.finalize_decorator(lambda: _cleanup_plan(composite))
411
420
  def _multi_rotation_scan():
412
421
  for single_scan in parameters.single_rotation_scans:
@@ -416,6 +425,7 @@ def multi_rotation_scan(
416
425
  md={
417
426
  "subplan_name": CONST.PLAN.ROTATION_OUTER,
418
427
  CONST.TRIGGER.ZOCALO: CONST.PLAN.ROTATION_MAIN,
428
+ "zocalo_environment": CONST.ZOCALO_ENV,
419
429
  "hyperion_parameters": single_scan.model_dump_json(),
420
430
  }
421
431
  )
@@ -5,12 +5,11 @@
5
5
  * reenable feedback
6
6
  """
7
7
 
8
- import dataclasses
9
-
8
+ import pydantic
10
9
  from bluesky import plan_stubs as bps
11
10
  from dodal.devices.attenuator import Attenuator
12
11
  from dodal.devices.dcm import DCM
13
- from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, VFMMirrorVoltages
12
+ from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, MirrorVoltages
14
13
  from dodal.devices.undulator_dcm import UndulatorDCM
15
14
  from dodal.devices.xbpm_feedback import XBPMFeedback
16
15
 
@@ -24,10 +23,10 @@ DESIRED_TRANSMISSION_FRACTION = 0.1
24
23
  UNDULATOR_GROUP = "UNDULATOR_GROUP"
25
24
 
26
25
 
27
- @dataclasses.dataclass
26
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
28
27
  class SetEnergyComposite:
29
28
  vfm: FocusingMirrorWithStripes
30
- vfm_mirror_voltages: VFMMirrorVoltages
29
+ mirror_voltages: MirrorVoltages
31
30
  dcm: DCM
32
31
  undulator_dcm: UndulatorDCM
33
32
  xbpm_feedback: XBPMFeedback
@@ -42,19 +41,20 @@ def _set_energy_plan(
42
41
  yield from dcm_pitch_roll_mirror_adjuster.adjust_dcm_pitch_roll_vfm_from_lut(
43
42
  composite.undulator_dcm,
44
43
  composite.vfm,
45
- composite.vfm_mirror_voltages,
44
+ composite.mirror_voltages,
46
45
  energy_kev,
47
46
  )
48
47
  yield from bps.wait(group=UNDULATOR_GROUP)
49
48
 
50
49
 
51
50
  def set_energy_plan(
52
- energy_kev,
51
+ energy_ev: float | None,
53
52
  composite: SetEnergyComposite,
54
53
  ):
55
- yield from transmission_and_xbpm_feedback_for_collection_wrapper(
56
- _set_energy_plan(energy_kev, composite),
57
- composite.xbpm_feedback,
58
- composite.attenuator,
59
- DESIRED_TRANSMISSION_FRACTION,
60
- )
54
+ if energy_ev:
55
+ yield from transmission_and_xbpm_feedback_for_collection_wrapper(
56
+ _set_energy_plan(energy_ev / 1000, composite),
57
+ composite.xbpm_feedback,
58
+ composite.attenuator,
59
+ DESIRED_TRANSMISSION_FRACTION,
60
+ )
@@ -4,7 +4,3 @@ execution of an experimental plan.
4
4
 
5
5
  Callbacks used for the Hyperion fast grid scan are prefixed with 'FGS'.
6
6
  """
7
-
8
- from .__main__ import main
9
-
10
- __all__ = ["main"]
@@ -7,6 +7,7 @@ from bluesky.callbacks.zmq import Proxy, RemoteDispatcher
7
7
  from dodal.log import LOGGER as dodal_logger
8
8
  from dodal.log import set_up_all_logging_handlers
9
9
 
10
+ from mx_bluesky.common.utils.log import _get_logging_dir, tag_filter
10
11
  from mx_bluesky.hyperion.external_interaction.callbacks.log_uid_tag_callback import (
11
12
  LogUidTaggingCallback,
12
13
  )
@@ -19,6 +20,9 @@ from mx_bluesky.hyperion.external_interaction.callbacks.rotation.ispyb_callback
19
20
  from mx_bluesky.hyperion.external_interaction.callbacks.rotation.nexus_callback import (
20
21
  RotationNexusFileCallback,
21
22
  )
23
+ from mx_bluesky.hyperion.external_interaction.callbacks.sample_handling.sample_handling_callback import (
24
+ SampleHandlingCallback,
25
+ )
22
26
  from mx_bluesky.hyperion.external_interaction.callbacks.xray_centre.ispyb_callback import (
23
27
  GridscanISPyBCallback,
24
28
  )
@@ -31,8 +35,6 @@ from mx_bluesky.hyperion.external_interaction.callbacks.zocalo_callback import (
31
35
  from mx_bluesky.hyperion.log import (
32
36
  ISPYB_LOGGER,
33
37
  NEXUS_LOGGER,
34
- _get_logging_dir,
35
- tag_filter,
36
38
  )
37
39
  from mx_bluesky.hyperion.parameters.cli import parse_callback_dev_mode_arg
38
40
  from mx_bluesky.hyperion.parameters.constants import CONST
@@ -50,6 +52,7 @@ def setup_callbacks():
50
52
  RotationISPyBCallback(emit=zocalo),
51
53
  LogUidTaggingCallback(),
52
54
  RobotLoadISPyBCallback(),
55
+ SampleHandlingCallback(),
53
56
  ]
54
57
 
55
58
 
@@ -0,0 +1,66 @@
1
+ import builtins
2
+ import dataclasses
3
+ import time
4
+ from abc import ABC
5
+ from typing import Literal
6
+
7
+ from bluesky.protocols import Readable, Reading
8
+ from event_model import DataKey
9
+
10
+
11
+ @dataclasses.dataclass(frozen=True)
12
+ class AbstractEvent(Readable, ABC):
13
+ """An abstract superclass that can be extended to provide lightweight software events
14
+ for bluesky plans, without having to incur the overhead of creating ophyd-async devices
15
+ specifically for the purpose.
16
+
17
+ The currently supported types for field annotations in the event are ``str``, ``int``, ``float``, ``bool``
18
+
19
+ In future array types may be supported.
20
+
21
+ Examples:
22
+ Subclasses should extend this class and decorate with::
23
+
24
+ @dataclasses.dataclass(frozen=True)
25
+
26
+ To raise an event, simply construct the event and then ``read`` it as you would a device::
27
+
28
+ yield from bps.create("MY_EVENT_NAME")
29
+ my_event = MyEvent(an_int=1)
30
+ yield from bps.read(my_event)
31
+ yield from bps.save()
32
+ """
33
+
34
+ def read(self) -> dict[str, Reading]:
35
+ return {
36
+ f.name: AbstractEvent._reading_from_value(getattr(self, f.name))
37
+ for f in dataclasses.fields(self)
38
+ }
39
+
40
+ def describe(self) -> dict[str, DataKey]:
41
+ return {
42
+ f.name: DataKey(dtype=AbstractEvent._dtype_of(f.type), shape=[], source="")
43
+ for f in dataclasses.fields(self)
44
+ }
45
+
46
+ @classmethod
47
+ def _reading_from_value(cls, value):
48
+ return Reading(timestamp=time.time(), value=value)
49
+
50
+ @classmethod
51
+ def _dtype_of(cls, t) -> Literal["string", "number", "boolean", "integer"]:
52
+ match t:
53
+ case builtins.str:
54
+ return "string"
55
+ case builtins.bool:
56
+ return "boolean"
57
+ case builtins.int:
58
+ return "integer"
59
+ case builtins.float:
60
+ return "number"
61
+ # TODO array support
62
+ raise ValueError(f"Unsupported type for AbstractEvent: {t}")
63
+
64
+ @property
65
+ def name(self) -> str:
66
+ return type(self).__name__
@@ -11,6 +11,9 @@ from mx_bluesky.hyperion.external_interaction.callbacks.rotation.ispyb_callback
11
11
  from mx_bluesky.hyperion.external_interaction.callbacks.rotation.nexus_callback import (
12
12
  RotationNexusFileCallback,
13
13
  )
14
+ from mx_bluesky.hyperion.external_interaction.callbacks.sample_handling.sample_handling_callback import (
15
+ SampleHandlingCallback,
16
+ )
14
17
  from mx_bluesky.hyperion.external_interaction.callbacks.xray_centre.ispyb_callback import (
15
18
  GridscanISPyBCallback,
16
19
  )
@@ -53,6 +56,7 @@ def create_load_centre_collect_callbacks() -> (
53
56
  RobotLoadISPyBCallback,
54
57
  RotationNexusFileCallback,
55
58
  RotationISPyBCallback,
59
+ SampleHandlingCallback,
56
60
  ]
57
61
  ):
58
62
  return (
@@ -61,4 +65,5 @@ def create_load_centre_collect_callbacks() -> (
61
65
  RobotLoadISPyBCallback(),
62
66
  RotationNexusFileCallback(),
63
67
  RotationISPyBCallback(emit=ZocaloCallback()),
68
+ SampleHandlingCallback(),
64
69
  )
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from mx_bluesky.common.parameters.components import DiffractionExperimentWithSample
3
4
  from mx_bluesky.hyperion.external_interaction.ispyb.data_model import (
4
5
  DataCollectionGroupInfo,
5
6
  DataCollectionInfo,
@@ -11,7 +12,6 @@ from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_store import (
11
12
  from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_utils import (
12
13
  get_current_time_string,
13
14
  )
14
- from mx_bluesky.hyperion.parameters.components import DiffractionExperimentWithSample
15
15
 
16
16
 
17
17
  def populate_data_collection_group(params: DiffractionExperimentWithSample):