mx-bluesky 1.5.0__py3-none-any.whl → 1.5.2__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 (56) hide show
  1. mx_bluesky/_version.py +2 -2
  2. mx_bluesky/beamlines/i04/__init__.py +4 -1
  3. mx_bluesky/beamlines/i04/callbacks/murko_callback.py +56 -1
  4. mx_bluesky/beamlines/i04/experiment_plans/__init__.py +0 -0
  5. mx_bluesky/beamlines/i04/experiment_plans/i04_grid_detect_then_xray_centre_plan.py +259 -0
  6. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +8 -8
  7. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +8 -6
  8. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +4 -4
  9. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +2 -2
  10. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +5 -5
  11. mx_bluesky/beamlines/i24/serial/web_gui_plans/general_plans.py +3 -3
  12. mx_bluesky/common/device_setup_plans/robot_load_unload.py +123 -0
  13. mx_bluesky/common/experiment_plans/change_aperture_then_move_plan.py +5 -1
  14. mx_bluesky/common/experiment_plans/common_flyscan_xray_centre_plan.py +27 -3
  15. mx_bluesky/common/experiment_plans/common_grid_detect_then_xray_centre_plan.py +1 -0
  16. mx_bluesky/common/experiment_plans/inner_plans/do_fgs.py +3 -1
  17. mx_bluesky/common/external_interaction/ispyb/exp_eye_store.py +26 -24
  18. mx_bluesky/common/external_interaction/ispyb/ispyb_store.py +11 -7
  19. mx_bluesky/common/external_interaction/nexus/write_nexus.py +2 -2
  20. mx_bluesky/common/parameters/__init__.py +0 -0
  21. mx_bluesky/common/parameters/components.py +7 -2
  22. mx_bluesky/common/parameters/constants.py +5 -3
  23. mx_bluesky/common/parameters/device_composites.py +1 -1
  24. mx_bluesky/common/parameters/gridscan.py +1 -0
  25. mx_bluesky/common/xrc_result.py +25 -2
  26. mx_bluesky/hyperion/__main__.py +1 -1
  27. mx_bluesky/hyperion/baton_handler.py +36 -4
  28. mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +4 -93
  29. mx_bluesky/hyperion/experiment_plans/hyperion_flyscan_xray_centre_plan.py +19 -31
  30. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +26 -8
  31. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +21 -75
  32. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +2 -2
  33. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +14 -9
  34. mx_bluesky/hyperion/external_interaction/agamemnon.py +4 -4
  35. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +1 -1
  36. mx_bluesky/hyperion/external_interaction/callbacks/{robot_load → robot_actions}/ispyb_callback.py +28 -19
  37. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +1 -1
  38. mx_bluesky/hyperion/external_interaction/callbacks/snapshot_callback.py +3 -0
  39. mx_bluesky/hyperion/external_interaction/config_server.py +0 -11
  40. mx_bluesky/hyperion/parameters/constants.py +2 -7
  41. mx_bluesky/hyperion/parameters/gridscan.py +2 -6
  42. mx_bluesky/hyperion/parameters/load_centre_collect.py +15 -0
  43. mx_bluesky/hyperion/parameters/rotation.py +7 -3
  44. mx_bluesky/hyperion/utils/context.py +19 -5
  45. mx_bluesky/phase1_zebra/__init__.py +1 -0
  46. mx_bluesky/phase1_zebra/device_setup_plans/__init__.py +0 -0
  47. mx_bluesky/phase1_zebra/device_setup_plans/setup_zebra.py +112 -0
  48. {mx_bluesky-1.5.0.dist-info → mx_bluesky-1.5.2.dist-info}/METADATA +5 -4
  49. {mx_bluesky-1.5.0.dist-info → mx_bluesky-1.5.2.dist-info}/RECORD +55 -49
  50. mx_bluesky/hyperion/utils/validation.py +0 -196
  51. /mx_bluesky/common/experiment_plans/{read_hardware.py → inner_plans/read_hardware.py} +0 -0
  52. /mx_bluesky/common/experiment_plans/{write_sample_status.py → inner_plans/write_sample_status.py} +0 -0
  53. {mx_bluesky-1.5.0.dist-info → mx_bluesky-1.5.2.dist-info}/WHEEL +0 -0
  54. {mx_bluesky-1.5.0.dist-info → mx_bluesky-1.5.2.dist-info}/entry_points.txt +0 -0
  55. {mx_bluesky-1.5.0.dist-info → mx_bluesky-1.5.2.dist-info}/licenses/LICENSE +0 -0
  56. {mx_bluesky-1.5.0.dist-info → mx_bluesky-1.5.2.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,4 @@
1
1
  import bluesky.plan_stubs as bps
2
- from bluesky.utils import MsgGenerator
3
2
  from dodal.devices.zebra.zebra import (
4
3
  ArmDemand,
5
4
  EncEnum,
@@ -12,9 +11,11 @@ from dodal.devices.zebra.zebra_controlled_shutter import (
12
11
  ZebraShutterControl,
13
12
  )
14
13
 
14
+ from mx_bluesky.common.parameters.constants import ZEBRA_STATUS_TIMEOUT
15
15
  from mx_bluesky.common.utils.log import LOGGER
16
-
17
- ZEBRA_STATUS_TIMEOUT = 30
16
+ from mx_bluesky.phase1_zebra.device_setup_plans.setup_zebra import (
17
+ configure_zebra_and_shutter_for_auto_shutter,
18
+ )
18
19
 
19
20
 
20
21
  def arm_zebra(zebra: Zebra):
@@ -35,47 +36,6 @@ def tidy_up_zebra_after_rotation_scan(
35
36
  yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
36
37
 
37
38
 
38
- def set_shutter_auto_input(zebra: Zebra, input: int, group="set_shutter_trigger"):
39
- """Set the signal that controls the shutter. We use the second input to the
40
- Zebra's AND2 gate for this input. ZebraShutter control mode must be in auto for this input to take control
41
-
42
- For more details see the ZebraShutter device."""
43
- auto_gate = zebra.mapping.AND_GATE_FOR_AUTO_SHUTTER
44
- auto_shutter_control = zebra.logic_gates.and_gates[auto_gate]
45
- yield from bps.abs_set(auto_shutter_control.sources[2], input, group)
46
-
47
-
48
- def configure_zebra_and_shutter_for_auto_shutter(
49
- zebra: Zebra, zebra_shutter: ZebraShutter, input: int, group="use_automatic_shutter"
50
- ):
51
- """Set the shutter to auto mode, and configure the zebra to trigger the shutter on
52
- an input source. For the input, use one of the source constants in zebra.py
53
-
54
- When the shutter is in auto/manual, logic in EPICS sets the Zebra's
55
- SOFT_IN1 to low/high respectively. The Zebra's AND2 gate should be used to control the shutter while in auto mode.
56
- To do this, we need (AND2 = SOFT_IN1 AND input), where input is the zebra signal we want to control the shutter when in auto mode.
57
- """
58
- # See https://github.com/DiamondLightSource/dodal/issues/813 for better typing here.
59
-
60
- # Set shutter to auto mode
61
- yield from bps.abs_set(
62
- zebra_shutter.control_mode, ZebraShutterControl.AUTO, group=group
63
- )
64
-
65
- auto_gate = zebra.mapping.AND_GATE_FOR_AUTO_SHUTTER
66
-
67
- # Set first input of AND2 gate to SOFT_IN1, which is high when shutter is in auto mode
68
- # Note the Zebra should ALWAYS be setup this way. See https://github.com/DiamondLightSource/mx-bluesky/issues/551
69
- yield from bps.abs_set(
70
- zebra.logic_gates.and_gates[auto_gate].sources[1],
71
- zebra.mapping.sources.SOFT_IN1,
72
- group=group,
73
- )
74
-
75
- # Set the second input of AND2 gate to the requested zebra input source
76
- yield from set_shutter_auto_input(zebra, input, group=group)
77
-
78
-
79
39
  def setup_zebra_for_rotation(
80
40
  zebra: Zebra,
81
41
  zebra_shutter: ZebraShutter,
@@ -155,55 +115,6 @@ def setup_zebra_for_rotation(
155
115
  yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
156
116
 
157
117
 
158
- def setup_zebra_for_gridscan(
159
- zebra: Zebra,
160
- zebra_shutter: ZebraShutter,
161
- group="setup_zebra_for_gridscan",
162
- wait=True,
163
- ):
164
- # Set shutter to automatic and to trigger via motion controller GPIO signal (IN4_TTL)
165
- yield from configure_zebra_and_shutter_for_auto_shutter(
166
- zebra, zebra_shutter, zebra.mapping.sources.IN4_TTL, group=group
167
- )
168
-
169
- yield from bps.abs_set(
170
- zebra.output.out_pvs[zebra.mapping.outputs.TTL_DETECTOR],
171
- zebra.mapping.sources.IN3_TTL,
172
- group=group,
173
- )
174
- yield from bps.abs_set(
175
- zebra.output.out_pvs[zebra.mapping.outputs.TTL_XSPRESS3],
176
- zebra.mapping.sources.DISCONNECT,
177
- group=group,
178
- )
179
- yield from bps.abs_set(
180
- zebra.output.pulse_1.input, zebra.mapping.sources.DISCONNECT, group=group
181
- )
182
-
183
- if wait:
184
- yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
185
-
186
-
187
- def tidy_up_zebra_after_gridscan(
188
- zebra: Zebra,
189
- zebra_shutter: ZebraShutter,
190
- group="tidy_up_zebra_after_gridscan",
191
- wait=True,
192
- ) -> MsgGenerator:
193
- yield from bps.abs_set(
194
- zebra.output.out_pvs[zebra.mapping.outputs.TTL_DETECTOR],
195
- zebra.mapping.sources.PC_PULSE,
196
- group=group,
197
- )
198
- yield from bps.abs_set(
199
- zebra_shutter.control_mode, ZebraShutterControl.MANUAL, group=group
200
- )
201
- yield from set_shutter_auto_input(zebra, zebra.mapping.sources.PC_GATE, group=group)
202
-
203
- if wait:
204
- yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)
205
-
206
-
207
118
  def setup_zebra_for_panda_flyscan(
208
119
  zebra: Zebra,
209
120
  zebra_shutter: ZebraShutter,
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Callable
3
4
  from functools import partial
4
5
  from pathlib import Path
5
6
 
@@ -19,14 +20,16 @@ from mx_bluesky.hyperion.device_setup_plans.setup_panda import (
19
20
  setup_panda_for_flyscan,
20
21
  )
21
22
  from mx_bluesky.hyperion.device_setup_plans.setup_zebra import (
22
- setup_zebra_for_gridscan,
23
23
  setup_zebra_for_panda_flyscan,
24
- tidy_up_zebra_after_gridscan,
25
24
  )
26
25
  from mx_bluesky.hyperion.parameters.device_composites import (
27
26
  HyperionFlyScanXRayCentreComposite,
28
27
  )
29
28
  from mx_bluesky.hyperion.parameters.gridscan import HyperionSpecifiedThreeDGridScan
29
+ from mx_bluesky.phase1_zebra.device_setup_plans.setup_zebra import (
30
+ setup_zebra_for_gridscan,
31
+ tidy_up_zebra_after_gridscan,
32
+ )
30
33
 
31
34
 
32
35
  class SmargonSpeedException(Exception):
@@ -59,9 +62,11 @@ def construct_hyperion_specific_features(
59
62
  xrc_composite.eiger.bit_depth,
60
63
  ]
61
64
 
65
+ setup_trigger_plan: Callable[..., MsgGenerator]
66
+
62
67
  if xrc_parameters.features.use_panda_for_gridscan:
63
68
  setup_trigger_plan = _panda_triggering_setup
64
- tidy_plan = _panda_tidy
69
+ tidy_plan = partial(_panda_tidy, xrc_composite)
65
70
  set_flyscan_params_plan = partial(
66
71
  set_fast_grid_scan_params,
67
72
  xrc_composite.panda_fast_grid_scan,
@@ -70,8 +75,14 @@ def construct_hyperion_specific_features(
70
75
  fgs_motors = xrc_composite.panda_fast_grid_scan
71
76
 
72
77
  else:
73
- setup_trigger_plan = _zebra_triggering_setup
74
- tidy_plan = partial(_generic_tidy, group="flyscan_zebra_tidy", wait=True)
78
+ setup_trigger_plan = setup_zebra_for_gridscan
79
+ tidy_plan = partial(
80
+ tidy_up_zebra_after_gridscan,
81
+ xrc_composite.zebra,
82
+ xrc_composite.sample_shutter,
83
+ group="flyscan_zebra_tidy",
84
+ wait=True,
85
+ )
75
86
  set_flyscan_params_plan = partial(
76
87
  set_fast_grid_scan_params,
77
88
  xrc_composite.zebra_fast_grid_scan,
@@ -89,40 +100,17 @@ def construct_hyperion_specific_features(
89
100
  )
90
101
 
91
102
 
92
- def _generic_tidy(
93
- xrc_composite: HyperionFlyScanXRayCentreComposite, group, wait=True
94
- ) -> MsgGenerator:
95
- LOGGER.info("Tidying up Zebra")
96
- yield from tidy_up_zebra_after_gridscan(
97
- xrc_composite.zebra, xrc_composite.sample_shutter, group=group, wait=wait
98
- )
99
- LOGGER.info("Tidying up Zocalo")
100
- # make sure we don't consume any other results
101
- yield from bps.unstage(xrc_composite.zocalo, group=group, wait=wait)
102
-
103
- # Turn off dev/shm streaming to avoid filling disk, see https://github.com/DiamondLightSource/hyperion/issues/1395
104
- LOGGER.info("Turning off Eiger dev/shm streaming")
105
- yield from bps.abs_set(xrc_composite.eiger.odin.fan.dev_shm_enable, 0) # type: ignore # Fix types in ophyd-async (https://github.com/DiamondLightSource/mx-bluesky/issues/855)
106
-
107
-
108
103
  def _panda_tidy(xrc_composite: HyperionFlyScanXRayCentreComposite):
109
104
  group = "panda_flyscan_tidy"
110
105
  LOGGER.info("Disabling panda blocks")
111
106
  yield from disarm_panda_for_gridscan(xrc_composite.panda, group)
112
- yield from _generic_tidy(xrc_composite, group, False)
107
+ yield from tidy_up_zebra_after_gridscan(
108
+ xrc_composite.zebra, xrc_composite.sample_shutter, group=group, wait=False
109
+ )
113
110
  yield from bps.wait(group, timeout=10)
114
111
  yield from bps.unstage(xrc_composite.panda)
115
112
 
116
113
 
117
- def _zebra_triggering_setup(
118
- xrc_composite: HyperionFlyScanXRayCentreComposite,
119
- parameters: HyperionSpecifiedThreeDGridScan,
120
- ) -> MsgGenerator:
121
- yield from setup_zebra_for_gridscan(
122
- xrc_composite.zebra, xrc_composite.sample_shutter, wait=True
123
- )
124
-
125
-
126
114
  def _panda_triggering_setup(
127
115
  xrc_composite: HyperionFlyScanXRayCentreComposite,
128
116
  parameters: HyperionSpecifiedThreeDGridScan,
@@ -76,17 +76,29 @@ def load_centre_collect_full(
76
76
  flyscan_event_handler,
77
77
  )
78
78
 
79
- locations_to_collect_um: list[np.ndarray] = []
79
+ locations_to_collect_um: list[np.ndarray]
80
+ samples_to_collect: list[int]
80
81
 
81
82
  if flyscan_event_handler.xray_centre_results:
82
83
  selection_func = flyscan_result.resolve_selection_fn(
83
84
  parameters.selection_params
84
85
  )
85
86
  hits = selection_func(flyscan_event_handler.xray_centre_results)
86
- locations_to_collect_um = [hit.centre_of_mass_mm * 1000 for hit in hits]
87
+ hits_to_collect = []
88
+ for hit in hits:
89
+ if hit.sample_id is None:
90
+ LOGGER.warning(
91
+ f"Diffracting centre {hit} not collected because no sample id was assigned."
92
+ )
93
+ else:
94
+ hits_to_collect.append(hit)
87
95
 
96
+ locations_to_collect_um = [
97
+ hit.centre_of_mass_mm * 1000 for hit in hits_to_collect
98
+ ]
99
+ samples_to_collect = [hit.sample_id for hit in hits_to_collect]
88
100
  LOGGER.info(
89
- f"Selected hits {hits} using {selection_func}, args={parameters.selection_params}"
101
+ f"Selected hits {hits_to_collect} using {selection_func}, args={parameters.selection_params}"
90
102
  )
91
103
  else:
92
104
  # If the xray centring hasn't found a result but has not thrown an error it
@@ -98,6 +110,7 @@ def load_centre_collect_full(
98
110
  locations_to_collect_um = [
99
111
  np.array([initial_x_mm, initial_y_mm, initial_z_mm]) * 1000
100
112
  ]
113
+ samples_to_collect = [parameters.sample_id]
101
114
 
102
115
  multi_rotation = parameters.multi_rotation_scan
103
116
  rotation_template = multi_rotation.rotation_scans.copy()
@@ -108,9 +121,11 @@ def load_centre_collect_full(
108
121
 
109
122
  generator = rotation_scan_generator(is_alternating)
110
123
  next(generator)
111
- for location in locations_to_collect_um:
124
+ for location, sample_id in zip(
125
+ locations_to_collect_um, samples_to_collect, strict=True
126
+ ):
112
127
  for rot in rotation_template:
113
- combination = generator.send((rot, location))
128
+ combination = generator.send((rot, location, sample_id))
114
129
  multi_rotation.rotation_scans.append(combination)
115
130
  multi_rotation = RotationScan.model_validate(multi_rotation)
116
131
 
@@ -125,8 +140,10 @@ def load_centre_collect_full(
125
140
 
126
141
  def rotation_scan_generator(
127
142
  is_alternating: bool,
128
- ) -> Generator[RotationScanPerSweep, tuple[RotationScanPerSweep, np.ndarray], None]:
129
- scan_template, location = yield # type: ignore
143
+ ) -> Generator[
144
+ RotationScanPerSweep, tuple[RotationScanPerSweep, np.ndarray, int], None
145
+ ]:
146
+ scan_template, location, sample_id = yield # type: ignore
130
147
  next_rotation_direction = scan_template.rotation_direction
131
148
  while True:
132
149
  scan = scan_template.model_copy()
@@ -135,6 +152,7 @@ def rotation_scan_generator(
135
152
  scan.y_start_um,
136
153
  scan.z_start_um,
137
154
  ) = location
155
+ scan.sample_id = sample_id
138
156
  if is_alternating:
139
157
  if next_rotation_direction != scan.rotation_direction:
140
158
  # If originally specified direction of the current scan is different
@@ -146,4 +164,4 @@ def rotation_scan_generator(
146
164
  scan.rotation_direction = next_rotation_direction
147
165
  next_rotation_direction = next_rotation_direction.opposite
148
166
 
149
- scan_template, location = yield scan
167
+ scan_template, location, sample_id = yield scan
@@ -10,22 +10,25 @@ import bluesky.preprocessors as bpp
10
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.attenuator import BinaryFilterAttenuator
15
15
  from dodal.devices.backlight import Backlight, BacklightPosition
16
16
  from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, MirrorVoltages
17
17
  from dodal.devices.i03.dcm import DCM
18
18
  from dodal.devices.i03.undulator_dcm import UndulatorDCM
19
- from dodal.devices.motors import XYZPositioner
19
+ from dodal.devices.motors import XYZStage
20
20
  from dodal.devices.oav.oav_detector import OAV
21
21
  from dodal.devices.robot import BartRobot, SampleLocation
22
- from dodal.devices.smargon import CombinedMove, Smargon, StubPosition
22
+ from dodal.devices.smargon import Smargon
23
23
  from dodal.devices.thawer import Thawer
24
24
  from dodal.devices.webcam import Webcam
25
25
  from dodal.devices.xbpm_feedback import XBPMFeedback
26
- from dodal.plan_stubs.motor_utils import MoveTooLarge, home_and_reset_wrapper
27
26
 
28
- from mx_bluesky.common.utils.log import LOGGER
27
+ from mx_bluesky.common.device_setup_plans.robot_load_unload import (
28
+ do_plan_while_lower_gonio_at_home,
29
+ prepare_for_robot_load,
30
+ wait_for_smargon_not_disabled,
31
+ )
29
32
  from mx_bluesky.hyperion.experiment_plans.set_energy_plan import (
30
33
  SetEnergyComposite,
31
34
  set_energy_plan,
@@ -47,7 +50,7 @@ class RobotLoadAndEnergyChangeComposite:
47
50
  # RobotLoad fields
48
51
  robot: BartRobot
49
52
  webcam: Webcam
50
- lower_gonio: XYZPositioner
53
+ lower_gonio: XYZStage
51
54
  thawer: Thawer
52
55
  oav: OAV
53
56
  smargon: Smargon
@@ -61,25 +64,6 @@ def create_devices(context: BlueskyContext) -> RobotLoadAndEnergyChangeComposite
61
64
  return device_composite_from_context(context, RobotLoadAndEnergyChangeComposite)
62
65
 
63
66
 
64
- def wait_for_smargon_not_disabled(smargon: Smargon, timeout=60):
65
- """Waits for the smargon disabled flag to go low. The robot hardware is responsible
66
- for setting this to low when it is safe to move. It does this through a physical
67
- connection between the robot and the smargon.
68
- """
69
- LOGGER.info("Waiting for smargon enabled")
70
- SLEEP_PER_CHECK = 0.1
71
- times_to_check = int(timeout / SLEEP_PER_CHECK)
72
- for _ in range(times_to_check):
73
- smargon_disabled = yield from bps.rd(smargon.disabled)
74
- if not smargon_disabled:
75
- LOGGER.info("Smargon now enabled")
76
- return
77
- yield from bps.sleep(SLEEP_PER_CHECK)
78
- raise TimeoutError(
79
- "Timed out waiting for smargon to become enabled after robot load"
80
- )
81
-
82
-
83
67
  def take_robot_snapshots(oav: OAV, webcam: Webcam, directory: Path):
84
68
  time_now = datetime.now()
85
69
  snapshot_format = f"{time_now.strftime('%H%M%S')}_{{device}}_after_load"
@@ -93,28 +77,15 @@ def take_robot_snapshots(oav: OAV, webcam: Webcam, directory: Path):
93
77
  yield from bps.wait("snapshots")
94
78
 
95
79
 
96
- def prepare_for_robot_load(
97
- aperture_scatterguard: ApertureScatterguard, smargon: Smargon
98
- ):
99
- yield from bps.abs_set(
100
- aperture_scatterguard.selected_aperture,
101
- ApertureValue.OUT_OF_BEAM,
102
- group="prepare_robot_load",
103
- )
104
-
105
- yield from bps.mv(smargon.stub_offsets, StubPosition.RESET_TO_ROBOT_LOAD)
106
-
107
- yield from bps.mv(smargon, CombinedMove(x=0, y=0, z=0, chi=0, phi=0, omega=0))
108
-
109
- yield from bps.wait("prepare_robot_load")
110
-
111
-
112
80
  def do_robot_load(
113
81
  composite: RobotLoadAndEnergyChangeComposite,
114
82
  sample_location: SampleLocation,
83
+ sample_id: int,
115
84
  demand_energy_ev: float | None,
116
85
  thawing_time: float,
117
86
  ):
87
+ yield from bps.abs_set(composite.robot.next_sample_id, sample_id, wait=True)
88
+
118
89
  yield from bps.abs_set(
119
90
  composite.robot,
120
91
  sample_location,
@@ -133,16 +104,6 @@ def do_robot_load(
133
104
  yield from wait_for_smargon_not_disabled(composite.smargon)
134
105
 
135
106
 
136
- def raise_exception_if_moved_out_of_cryojet(exception):
137
- yield from bps.null()
138
- if isinstance(exception, MoveTooLarge):
139
- raise Exception(
140
- f"Moving {exception.axis} back to {exception.position} after \
141
- robot load would move it out of the cryojet. The max safe \
142
- distance is {exception.maximum_move}"
143
- )
144
-
145
-
146
107
  def pin_already_loaded(
147
108
  robot: BartRobot, sample_location: SampleLocation
148
109
  ) -> Generator[Msg, None, bool]:
@@ -158,6 +119,7 @@ def robot_load_and_snapshots(
158
119
  composite: RobotLoadAndEnergyChangeComposite,
159
120
  location: SampleLocation,
160
121
  snapshot_directory: Path,
122
+ sample_id: int,
161
123
  thawing_time: float,
162
124
  demand_energy_ev: float | None,
163
125
  ):
@@ -166,37 +128,25 @@ def robot_load_and_snapshots(
166
128
  robot_load_plan = do_robot_load(
167
129
  composite,
168
130
  location,
131
+ sample_id,
169
132
  demand_energy_ev,
170
133
  thawing_time,
171
134
  )
172
135
 
173
- # The lower gonio must be in the correct position for the robot load and we
174
- # want to put it back afterwards. Note we don't wait the robot is interlocked
175
- # to the lower gonio and the move is quicker than the robot takes to get to the
176
- # load position.
177
- yield from bpp.contingency_wrapper(
178
- home_and_reset_wrapper(
179
- robot_load_plan,
180
- composite.lower_gonio,
181
- BartRobot.LOAD_TOLERANCE_MM,
182
- CONST.HARDWARE.CRYOJET_MARGIN_MM,
183
- "lower_gonio",
184
- wait_for_all=False,
185
- ),
186
- except_plan=raise_exception_if_moved_out_of_cryojet,
136
+ gonio_finished = yield from do_plan_while_lower_gonio_at_home(
137
+ robot_load_plan, composite.lower_gonio
187
138
  )
188
-
189
139
  yield from bps.wait(group="snapshot")
190
140
 
191
141
  yield from take_robot_snapshots(composite.oav, composite.webcam, snapshot_directory)
192
142
 
193
- yield from bps.create(name=CONST.DESCRIPTORS.ROBOT_LOAD)
194
- yield from bps.read(composite.robot.barcode)
143
+ yield from bps.create(name=CONST.DESCRIPTORS.ROBOT_UPDATE)
144
+ yield from bps.read(composite.robot)
195
145
  yield from bps.read(composite.oav.snapshot)
196
146
  yield from bps.read(composite.webcam)
197
147
  yield from bps.save()
198
148
 
199
- yield from bps.wait("reset-lower_gonio")
149
+ yield from bps.wait(gonio_finished)
200
150
 
201
151
 
202
152
  def robot_load_and_change_energy_plan(
@@ -218,17 +168,13 @@ def robot_load_and_change_energy_plan(
218
168
  composite,
219
169
  sample_location,
220
170
  params.snapshot_directory,
171
+ params.sample_id,
221
172
  params.thawing_time,
222
173
  params.demand_energy_ev,
223
174
  ),
224
175
  md={
225
176
  "subplan_name": CONST.PLAN.ROBOT_LOAD,
226
- "metadata": {
227
- "visit": params.visit,
228
- "sample_id": params.sample_id,
229
- "sample_puck": sample_location.puck,
230
- "sample_pin": sample_location.pin,
231
- },
177
+ "metadata": {"visit": params.visit, "sample_id": params.sample_id},
232
178
  "activate_callbacks": [
233
179
  "RobotLoadISPyBCallback",
234
180
  ],
@@ -18,7 +18,7 @@ from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, MirrorVolta
18
18
  from dodal.devices.i03 import Beamstop
19
19
  from dodal.devices.i03.dcm import DCM
20
20
  from dodal.devices.i03.undulator_dcm import UndulatorDCM
21
- from dodal.devices.motors import XYZPositioner
21
+ from dodal.devices.motors import XYZStage
22
22
  from dodal.devices.oav.oav_detector import OAV
23
23
  from dodal.devices.oav.pin_image_recognition import PinTipDetection
24
24
  from dodal.devices.robot import BartRobot, SampleLocation
@@ -96,7 +96,7 @@ class RobotLoadThenCentreComposite:
96
96
  # RobotLoad fields
97
97
  robot: BartRobot
98
98
  webcam: Webcam
99
- lower_gonio: XYZPositioner
99
+ lower_gonio: XYZStage
100
100
  beamstop: Beamstop
101
101
 
102
102
 
@@ -37,16 +37,16 @@ from mx_bluesky.common.device_setup_plans.manipulate_sample import (
37
37
  from mx_bluesky.common.device_setup_plans.utils import (
38
38
  start_preparing_data_collection_then_do_plan,
39
39
  )
40
+ from mx_bluesky.common.experiment_plans.inner_plans.read_hardware import (
41
+ read_hardware_for_zocalo,
42
+ standard_read_hardware_during_collection,
43
+ standard_read_hardware_pre_collection,
44
+ )
40
45
  from mx_bluesky.common.experiment_plans.oav_snapshot_plan import (
41
46
  OavSnapshotComposite,
42
47
  oav_snapshot_plan,
43
48
  setup_beamline_for_OAV,
44
49
  )
45
- from mx_bluesky.common.experiment_plans.read_hardware import (
46
- read_hardware_for_zocalo,
47
- standard_read_hardware_during_collection,
48
- standard_read_hardware_pre_collection,
49
- )
50
50
  from mx_bluesky.common.parameters.components import WithSnapshot
51
51
  from mx_bluesky.common.preprocessors.preprocessors import (
52
52
  transmission_and_xbpm_feedback_for_collection_decorator,
@@ -342,10 +342,15 @@ def _move_and_rotation(
342
342
 
343
343
  if params.take_snapshots:
344
344
  yield from bps.wait(CONST.WAIT.MOVE_GONIO_TO_START)
345
- yield from setup_beamline_for_OAV(
346
- composite.smargon, composite.backlight, composite.aperture_scatterguard
347
- )
348
- yield from bps.wait(group=CONST.WAIT.READY_FOR_OAV)
345
+
346
+ if not params.use_grid_snapshots:
347
+ yield from setup_beamline_for_OAV(
348
+ composite.smargon,
349
+ composite.backlight,
350
+ composite.aperture_scatterguard,
351
+ wait=True,
352
+ )
353
+
349
354
  if params.selected_aperture:
350
355
  yield from bps.prepare(
351
356
  composite.aperture_scatterguard,
@@ -148,6 +148,7 @@ def populate_parameters_from_agamemnon(agamemnon_params) -> Sequence[LoadCentreC
148
148
  "name": "TopNByMaxCount",
149
149
  "n": pin_type.expected_number_of_crystals,
150
150
  },
151
+ "features": {"use_gpu_results": True},
151
152
  "robot_load_then_centre": {
152
153
  "storage_directory": str(visit_directory) + "/xraycentring",
153
154
  "file_name": file_name,
@@ -156,7 +157,6 @@ def populate_parameters_from_agamemnon(agamemnon_params) -> Sequence[LoadCentreC
156
157
  "omega_start_deg": 0.0,
157
158
  "chi_start_deg": collection["chi"],
158
159
  "transmission_frac": 1.0,
159
- "features": {"use_gpu_results": True},
160
160
  **with_energy_params,
161
161
  },
162
162
  "multi_rotation_scan": {
@@ -228,10 +228,10 @@ def update_params_from_agamemnon(parameters: T) -> T:
228
228
  parameters.robot_load_then_centre.grid_width_um = pin_type.full_width
229
229
  parameters.select_centres.n = pin_type.expected_number_of_crystals
230
230
  if pin_type != SinglePin():
231
- # Snapshots between each collection take a lot of time.
232
- # Before we do https://github.com/DiamondLightSource/mx-bluesky/issues/226
233
- # this will give no snapshots but that's preferable
231
+ # Rotation snapshots will be generated from the gridscan snapshots,
232
+ # no need to specify snapshot omega.
234
233
  parameters.multi_rotation_scan.snapshot_omegas_deg = []
234
+ parameters.multi_rotation_scan.use_grid_snapshots = True
235
235
  except (ValueError, ValidationError) as e:
236
236
  LOGGER.warning(f"Failed to update parameters: {e}")
237
237
  except Exception as e:
@@ -29,7 +29,7 @@ from mx_bluesky.common.utils.log import (
29
29
  _get_logging_dirs,
30
30
  tag_filter,
31
31
  )
32
- from mx_bluesky.hyperion.external_interaction.callbacks.robot_load.ispyb_callback import (
32
+ from mx_bluesky.hyperion.external_interaction.callbacks.robot_actions.ispyb_callback import (
33
33
  RobotLoadISPyBCallback,
34
34
  )
35
35
  from mx_bluesky.hyperion.external_interaction.callbacks.rotation.ispyb_callback import (