mx-bluesky 1.4.1a0__py3-none-any.whl → 1.4.3__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/_version.py +2 -2
  2. mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +178 -0
  3. mx_bluesky/beamlines/i24/serial/__init__.py +0 -6
  4. mx_bluesky/beamlines/i24/serial/dcid.py +125 -151
  5. mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +1 -1
  6. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +88 -43
  7. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +1 -1
  8. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/MappingLite-oxford_py3v1.edl +2 -46
  9. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +85 -122
  10. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +58 -66
  11. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +1 -19
  12. mx_bluesky/beamlines/i24/serial/parameters/__init__.py +11 -2
  13. mx_bluesky/beamlines/i24/serial/parameters/constants.py +16 -2
  14. mx_bluesky/beamlines/i24/serial/parameters/experiment_parameters.py +94 -19
  15. mx_bluesky/beamlines/i24/serial/parameters/utils.py +19 -0
  16. mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +2 -0
  17. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +61 -8
  18. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +81 -40
  19. mx_bluesky/beamlines/i24/serial/write_nexus.py +66 -67
  20. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/aperture_change_callback.py +1 -1
  21. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/grid_detection_callback.py +19 -1
  22. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/ispyb_callback_base.py +40 -34
  23. mx_bluesky/{hyperion → common}/external_interaction/callbacks/common/ispyb_mapping.py +4 -4
  24. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/logging_callback.py +1 -1
  25. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/zocalo_callback.py +14 -9
  26. mx_bluesky/{hyperion → common}/external_interaction/callbacks/xray_centre/ispyb_callback.py +46 -38
  27. mx_bluesky/{hyperion → common}/external_interaction/callbacks/xray_centre/ispyb_mapping.py +2 -2
  28. mx_bluesky/{hyperion → common}/external_interaction/callbacks/xray_centre/nexus_callback.py +20 -15
  29. mx_bluesky/common/external_interaction/config_server.py +11 -0
  30. mx_bluesky/common/external_interaction/ispyb/__init__.py +0 -0
  31. mx_bluesky/{hyperion → common}/external_interaction/ispyb/data_model.py +2 -0
  32. mx_bluesky/{hyperion → common}/external_interaction/ispyb/exp_eye_store.py +67 -17
  33. mx_bluesky/{hyperion → common}/external_interaction/ispyb/ispyb_store.py +20 -18
  34. mx_bluesky/{hyperion → common}/external_interaction/ispyb/ispyb_utils.py +2 -2
  35. mx_bluesky/common/external_interaction/nexus/__init__.py +0 -0
  36. mx_bluesky/{hyperion → common}/external_interaction/nexus/nexus_utils.py +21 -6
  37. mx_bluesky/{hyperion → common}/external_interaction/nexus/write_nexus.py +5 -5
  38. mx_bluesky/common/external_interaction/test_config_server.py +38 -0
  39. mx_bluesky/common/parameters/components.py +10 -8
  40. mx_bluesky/common/parameters/constants.py +6 -0
  41. mx_bluesky/common/parameters/gridscan.py +102 -53
  42. mx_bluesky/common/plans/do_fgs.py +4 -4
  43. mx_bluesky/{hyperion → common/utils}/exceptions.py +27 -1
  44. mx_bluesky/common/utils/log.py +17 -7
  45. mx_bluesky/hyperion/__main__.py +15 -14
  46. mx_bluesky/hyperion/device_setup_plans/check_beamstop.py +27 -0
  47. mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +34 -37
  48. mx_bluesky/hyperion/device_setup_plans/manipulate_sample.py +7 -7
  49. mx_bluesky/hyperion/device_setup_plans/position_detector.py +1 -1
  50. mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +3 -3
  51. mx_bluesky/hyperion/device_setup_plans/setup_panda.py +21 -4
  52. mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +62 -36
  53. mx_bluesky/hyperion/device_setup_plans/smargon.py +3 -3
  54. mx_bluesky/hyperion/device_setup_plans/utils.py +4 -0
  55. mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +8 -8
  56. mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +28 -17
  57. mx_bluesky/hyperion/experiment_plans/common/xrc_result.py +10 -1
  58. mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -9
  59. mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +54 -58
  60. mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +22 -31
  61. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +57 -40
  62. mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +3 -3
  63. mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +8 -2
  64. mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +6 -14
  65. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +12 -11
  66. mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +4 -4
  67. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +39 -30
  68. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +36 -18
  69. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +33 -21
  70. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +10 -9
  71. mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +0 -4
  72. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +31 -20
  73. mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +46 -30
  74. mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +39 -24
  75. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +25 -24
  76. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +1 -1
  77. mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +13 -9
  78. mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/__init__.py +0 -0
  79. mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/sample_handling_callback.py +50 -0
  80. mx_bluesky/hyperion/external_interaction/config_server.py +15 -1
  81. mx_bluesky/hyperion/parameters/components.py +3 -2
  82. mx_bluesky/hyperion/parameters/constants.py +1 -0
  83. mx_bluesky/hyperion/parameters/gridscan.py +56 -89
  84. mx_bluesky/hyperion/parameters/load_centre_collect.py +51 -6
  85. mx_bluesky/hyperion/parameters/robot_load.py +40 -0
  86. mx_bluesky/hyperion/parameters/rotation.py +28 -3
  87. mx_bluesky/hyperion/utils/context.py +1 -1
  88. mx_bluesky/hyperion/utils/validation.py +5 -3
  89. {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/METADATA +6 -6
  90. mx_bluesky-1.4.3.dist-info/RECORD +155 -0
  91. {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/WHEEL +1 -1
  92. mx_bluesky/common/parameters/robot_load.py +0 -16
  93. mx_bluesky/hyperion/external_interaction/exceptions.py +0 -13
  94. mx_bluesky/hyperion/log.py +0 -15
  95. mx_bluesky-1.4.1a0.dist-info/RECORD +0 -150
  96. /mx_bluesky/{hyperion/external_interaction/callbacks/xray_centre → common/external_interaction}/__init__.py +0 -0
  97. /mx_bluesky/{hyperion/external_interaction/ispyb → common/external_interaction/callbacks/common}/__init__.py +0 -0
  98. /mx_bluesky/{hyperion → common}/external_interaction/callbacks/common/abstract_event.py +0 -0
  99. /mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/log_uid_tag_callback.py +0 -0
  100. /mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/plan_reactive_callback.py +0 -0
  101. /mx_bluesky/{hyperion/external_interaction/nexus → common/external_interaction/callbacks/xray_centre}/__init__.py +0 -0
  102. /mx_bluesky/{hyperion → common}/utils/utils.py +0 -0
  103. {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/LICENSE +0 -0
  104. {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/entry_points.txt +0 -0
  105. {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/top_level.txt +0 -0
@@ -5,11 +5,11 @@ import bluesky.preprocessors as bpp
5
5
  import numpy as np
6
6
  import pydantic
7
7
  from blueapi.core import BlueskyContext
8
- from dodal.devices.attenuator import Attenuator
8
+ from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
9
9
  from dodal.devices.xspress3.xspress3 import Xspress3
10
- from dodal.devices.zebra_controlled_shutter import ZebraShutter, ZebraShutterState
10
+ from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter, ZebraShutterState
11
11
 
12
- from mx_bluesky.hyperion.log import LOGGER
12
+ from mx_bluesky.common.utils.log import LOGGER
13
13
  from mx_bluesky.hyperion.utils.context import device_composite_from_context
14
14
 
15
15
 
@@ -26,7 +26,7 @@ class Direction(Enum):
26
26
  class OptimizeAttenuationComposite:
27
27
  """All devices which are directly or indirectly required by this plan"""
28
28
 
29
- attenuator: Attenuator
29
+ attenuator: BinaryFilterAttenuator
30
30
  sample_shutter: ZebraShutter
31
31
  xspress3mini: Xspress3
32
32
 
@@ -197,11 +197,7 @@ def deadtime_optimisation(
197
197
  cycles to complete.
198
198
 
199
199
  Args:
200
- attenuator: (Attenuator) Ophyd device
201
-
202
- xspress3mini: (Xspress3Mini) Ophyd device
203
-
204
- sample_shutter: (ZebraShutter) Ophyd_async device for the fast shutter
200
+ composite: (OptimizeAttenuationComposite) Devices required to optimise attenuation
205
201
 
206
202
  transmission: (float)
207
203
  The initial transmission value to use for the optimising
@@ -302,11 +298,7 @@ def total_counts_optimisation(
302
298
  defined by the lower and upper limit. To protect the sample, the transmission has a maximum value of 10%.
303
299
 
304
300
  Args:
305
- attenuator: (Attenuator) Ophyd device
306
-
307
- xspress3mini: (Xspress3Mini) Ophyd device
308
-
309
- sample_shutter: (ZebraShutter) Ophyd_async device for the fast shutter
301
+ composite: (OptimizeAttenuationComposite) Devices required to optimise attenuation
310
302
 
311
303
  transmission: (float)
312
304
  The initial transmission value to use for the optimising
@@ -8,11 +8,11 @@ from bluesky.utils import MsgGenerator
8
8
  from dodal.devices.eiger import EigerDetector
9
9
  from dodal.devices.oav.oav_parameters import OAVParameters
10
10
 
11
- from mx_bluesky.common.parameters.constants import OavConstants
12
- from mx_bluesky.common.parameters.gridscan import (
13
- GridScanWithEdgeDetect,
14
- PinTipCentreThenXrayCentre,
11
+ from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback import (
12
+ ispyb_activation_wrapper,
15
13
  )
14
+ from mx_bluesky.common.parameters.constants import OavConstants
15
+ from mx_bluesky.common.utils.log import LOGGER
16
16
  from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import move_phi_chi_omega
17
17
  from mx_bluesky.hyperion.device_setup_plans.utils import (
18
18
  start_preparing_data_collection_then_do_plan,
@@ -34,11 +34,11 @@ from mx_bluesky.hyperion.experiment_plans.pin_tip_centring_plan import (
34
34
  PinTipCentringComposite,
35
35
  pin_tip_centre_plan,
36
36
  )
37
- from mx_bluesky.hyperion.external_interaction.callbacks.xray_centre.ispyb_callback import (
38
- ispyb_activation_wrapper,
39
- )
40
- from mx_bluesky.hyperion.log import LOGGER
41
37
  from mx_bluesky.hyperion.parameters.constants import CONST
38
+ from mx_bluesky.hyperion.parameters.gridscan import (
39
+ GridScanWithEdgeDetect,
40
+ PinTipCentreThenXrayCentre,
41
+ )
42
42
  from mx_bluesky.hyperion.utils.context import device_composite_from_context
43
43
 
44
44
 
@@ -121,6 +121,7 @@ def pin_tip_centre_then_xray_centre(
121
121
  @bpp.subs_decorator(flyscan_event_handler)
122
122
  def pin_centre_flyscan_then_fetch_results() -> MsgGenerator:
123
123
  yield from start_preparing_data_collection_then_do_plan(
124
+ composite.beamstop,
124
125
  eiger,
125
126
  composite.detector_motion,
126
127
  parameters.detector_params.detector_distance,
@@ -130,9 +131,9 @@ def pin_tip_centre_then_xray_centre(
130
131
 
131
132
  yield from pin_centre_flyscan_then_fetch_results()
132
133
  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"
134
+ assert flyscan_results, (
135
+ "Flyscan result event not received or no crystal found and exception not raised"
136
+ )
136
137
  yield from change_aperture_then_move_to_xtal(
137
138
  flyscan_results[0], composite.smargon, composite.aperture_scatterguard
138
139
  )
@@ -15,12 +15,12 @@ from dodal.devices.oav.utils import (
15
15
  )
16
16
  from dodal.devices.smargon import Smargon
17
17
 
18
+ from mx_bluesky.common.utils.exceptions import SampleException
19
+ from mx_bluesky.common.utils.log import LOGGER
18
20
  from mx_bluesky.hyperion.device_setup_plans.setup_oav import pre_centring_setup_oav
19
21
  from mx_bluesky.hyperion.device_setup_plans.smargon import (
20
22
  move_smargon_warn_on_out_of_range,
21
23
  )
22
- from mx_bluesky.hyperion.exceptions import WarningException
23
- from mx_bluesky.hyperion.log import LOGGER
24
24
  from mx_bluesky.hyperion.parameters.constants import CONST
25
25
  from mx_bluesky.hyperion.utils.context import device_composite_from_context
26
26
 
@@ -68,7 +68,7 @@ def move_pin_into_view(
68
68
  max_steps (int, optional): The number of steps to search with. Defaults to 2.
69
69
 
70
70
  Raises:
71
- WarningException: Error if the pin tip is never found
71
+ SampleException: Error if the pin tip is never found
72
72
 
73
73
  Returns:
74
74
  Tuple[int, int]: The location of the pin tip in pixels
@@ -105,7 +105,7 @@ def move_pin_into_view(
105
105
  tip_xy_px = yield from trigger_and_return_pin_tip(pin_tip_device)
106
106
 
107
107
  if not pin_tip_valid(tip_xy_px):
108
- raise WarningException(
108
+ raise SampleException(
109
109
  "Pin tip centring failed - pin too long/short/bent and out of range"
110
110
  )
111
111
  else:
@@ -10,8 +10,8 @@ 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
14
- from dodal.devices.attenuator import Attenuator
13
+ from dodal.devices.aperturescatterguard import ApertureScatterguard, ApertureValue
14
+ from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
15
15
  from dodal.devices.dcm import DCM
16
16
  from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, MirrorVoltages
17
17
  from dodal.devices.motors import XYZPositioner
@@ -24,13 +24,13 @@ from dodal.devices.webcam import Webcam
24
24
  from dodal.devices.xbpm_feedback import XBPMFeedback
25
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
+ from mx_bluesky.common.utils.log import LOGGER
28
28
  from mx_bluesky.hyperion.experiment_plans.set_energy_plan import (
29
29
  SetEnergyComposite,
30
30
  set_energy_plan,
31
31
  )
32
- from mx_bluesky.hyperion.log import LOGGER
33
32
  from mx_bluesky.hyperion.parameters.constants import CONST
33
+ from mx_bluesky.hyperion.parameters.robot_load import RobotLoadAndEnergyChange
34
34
 
35
35
 
36
36
  @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
@@ -41,7 +41,7 @@ class RobotLoadAndEnergyChangeComposite:
41
41
  dcm: DCM
42
42
  undulator_dcm: UndulatorDCM
43
43
  xbpm_feedback: XBPMFeedback
44
- attenuator: Attenuator
44
+ attenuator: BinaryFilterAttenuator
45
45
 
46
46
  # RobotLoad fields
47
47
  robot: BartRobot
@@ -94,7 +94,11 @@ 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.trigger(aperture_scatterguard.move_out, group="prepare_robot_load")
97
+ yield from bps.abs_set(
98
+ aperture_scatterguard,
99
+ ApertureValue.ROBOT_LOAD,
100
+ group="prepare_robot_load",
101
+ )
98
102
 
99
103
  yield from bps.mv(smargon.stub_offsets, StubPosition.RESET_TO_ROBOT_LOAD) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
100
104
 
@@ -118,17 +122,18 @@ def do_robot_load(
118
122
  demand_energy_ev: float | None,
119
123
  thawing_time: float,
120
124
  ):
125
+ error_code = yield from bps.rd(composite.robot.error_code)
126
+ # Reset robot if light curtains were tripped
127
+ if error_code == 40:
128
+ yield from bps.trigger(composite.robot.reset, wait=True)
129
+
121
130
  yield from bps.abs_set(
122
131
  composite.robot,
123
132
  sample_location,
124
133
  group="robot_load",
125
134
  )
126
135
 
127
- if demand_energy_ev:
128
- yield from set_energy_plan(
129
- demand_energy_ev / 1000,
130
- cast(SetEnergyComposite, composite),
131
- )
136
+ yield from set_energy_plan(demand_energy_ev, cast(SetEnergyComposite, composite))
132
137
 
133
138
  yield from bps.wait("robot_load")
134
139
 
@@ -214,24 +219,28 @@ def robot_load_and_change_energy_plan(
214
219
  yield from prepare_for_robot_load(
215
220
  composite.aperture_scatterguard, composite.smargon
216
221
  )
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,
222
+
223
+ yield from bpp.set_run_key_wrapper(
224
+ bpp.run_wrapper(
225
+ robot_load_and_snapshots(
226
+ composite,
227
+ sample_location,
228
+ params.snapshot_directory,
229
+ params.thawing_time,
230
+ params.demand_energy_ev,
231
+ ),
232
+ md={
233
+ "subplan_name": CONST.PLAN.ROBOT_LOAD,
234
+ "metadata": {
235
+ "visit": params.visit,
236
+ "sample_id": params.sample_id,
237
+ "sample_puck": sample_location.puck,
238
+ "sample_pin": sample_location.pin,
239
+ },
240
+ "activate_callbacks": [
241
+ "RobotLoadISPyBCallback",
242
+ ],
232
243
  },
233
- "activate_callbacks": [
234
- "RobotLoadISPyBCallback",
235
- ],
236
- },
244
+ ),
245
+ CONST.PLAN.ROBOT_LOAD_AND_SNAPSHOTS,
237
246
  )
@@ -1,13 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from math import isclose
3
4
  from typing import cast
4
5
 
5
6
  import bluesky.preprocessors as bpp
6
7
  import pydantic
7
8
  from blueapi.core import BlueskyContext
9
+ from bluesky import plan_stubs as bps
8
10
  from bluesky.utils import MsgGenerator
9
11
  from dodal.devices.aperturescatterguard import ApertureScatterguard
10
- from dodal.devices.attenuator import Attenuator
12
+ from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
11
13
  from dodal.devices.backlight import Backlight
12
14
  from dodal.devices.dcm import DCM
13
15
  from dodal.devices.detector.detector_motion import DetectorMotion
@@ -15,6 +17,7 @@ from dodal.devices.eiger import EigerDetector
15
17
  from dodal.devices.fast_grid_scan import PandAFastGridScan, ZebraFastGridScan
16
18
  from dodal.devices.flux import Flux
17
19
  from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, MirrorVoltages
20
+ from dodal.devices.i03.beamstop import Beamstop
18
21
  from dodal.devices.motors import XYZPositioner
19
22
  from dodal.devices.oav.oav_detector import OAV
20
23
  from dodal.devices.oav.pin_image_recognition import PinTipDetection
@@ -27,14 +30,13 @@ from dodal.devices.undulator import Undulator
27
30
  from dodal.devices.undulator_dcm import UndulatorDCM
28
31
  from dodal.devices.webcam import Webcam
29
32
  from dodal.devices.xbpm_feedback import XBPMFeedback
30
- from dodal.devices.zebra import Zebra
31
- from dodal.devices.zebra_controlled_shutter import ZebraShutter
33
+ from dodal.devices.zebra.zebra import Zebra
34
+ from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
32
35
  from dodal.devices.zocalo import ZocaloResults
33
36
  from dodal.log import LOGGER
34
37
  from ophyd_async.fastcs.panda import HDFPanda
35
38
 
36
39
  from mx_bluesky.common.parameters.constants import OavConstants
37
- from mx_bluesky.common.parameters.gridscan import RobotLoadThenCentre
38
40
  from mx_bluesky.hyperion.device_setup_plans.utils import (
39
41
  fill_in_energy_if_not_supplied,
40
42
  start_preparing_data_collection_then_do_plan,
@@ -56,14 +58,19 @@ from mx_bluesky.hyperion.experiment_plans.robot_load_and_change_energy import (
56
58
  pin_already_loaded,
57
59
  robot_load_and_change_energy_plan,
58
60
  )
61
+ from mx_bluesky.hyperion.experiment_plans.set_energy_plan import (
62
+ SetEnergyComposite,
63
+ set_energy_plan,
64
+ )
59
65
  from mx_bluesky.hyperion.parameters.constants import CONST
66
+ from mx_bluesky.hyperion.parameters.robot_load import RobotLoadThenCentre
60
67
 
61
68
 
62
69
  @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
63
70
  class RobotLoadThenCentreComposite:
64
71
  # common fields
65
72
  xbpm_feedback: XBPMFeedback
66
- attenuator: Attenuator
73
+ attenuator: BinaryFilterAttenuator
67
74
 
68
75
  # GridDetectThenXRayCentreComposite fields
69
76
  aperture_scatterguard: ApertureScatterguard
@@ -95,10 +102,7 @@ class RobotLoadThenCentreComposite:
95
102
  robot: BartRobot
96
103
  webcam: Webcam
97
104
  lower_gonio: XYZPositioner
98
-
99
- @property
100
- def sample_motors(self):
101
- return self.smargon
105
+ beamstop: Beamstop
102
106
 
103
107
 
104
108
  def create_devices(context: BlueskyContext) -> RobotLoadThenCentreComposite:
@@ -114,7 +118,7 @@ def _flyscan_plan_from_robot_load_params(
114
118
  ):
115
119
  yield from pin_centre_then_flyscan_plan(
116
120
  cast(GridDetectThenXRayCentreComposite, composite),
117
- params.pin_centre_then_xray_centre_params(),
121
+ params.pin_centre_then_xray_centre_params,
118
122
  )
119
123
 
120
124
 
@@ -125,7 +129,7 @@ def _robot_load_then_flyscan_plan(
125
129
  ):
126
130
  yield from robot_load_and_change_energy_plan(
127
131
  cast(RobotLoadAndEnergyChangeComposite, composite),
128
- params.robot_load_params(),
132
+ params.robot_load_params,
129
133
  )
130
134
 
131
135
  yield from _flyscan_plan_from_robot_load_params(composite, params, oav_config_file)
@@ -170,20 +174,33 @@ def robot_load_then_xray_centre(
170
174
  yield from pin_already_loaded(composite.robot, sample_location)
171
175
  )
172
176
 
173
- doing_chi_change = parameters.chi_start_deg is not None
177
+ current_chi = yield from bps.rd(composite.smargon.chi)
178
+ LOGGER.info(f"Read back current smargon chi of {current_chi} degrees.")
179
+ doing_chi_change = parameters.chi_start_deg is not None and not isclose(
180
+ current_chi, parameters.chi_start_deg, abs_tol=0.001
181
+ )
174
182
 
175
183
  if doing_sample_load:
184
+ LOGGER.info("Pin not loaded, loading and centring")
176
185
  plan = _robot_load_then_flyscan_plan(
177
186
  composite,
178
187
  parameters,
179
188
  )
180
- LOGGER.info("Pin not loaded, loading and centring")
181
- elif doing_chi_change:
182
- plan = _flyscan_plan_from_robot_load_params(composite, parameters)
183
- LOGGER.info("Pin already loaded but chi changed so centring")
184
189
  else:
185
- LOGGER.info("Pin already loaded and chi not changed so doing nothing")
186
- return
190
+ # Robot load normally sets the energy so we should do this explicitly if no load is
191
+ # being done
192
+ demand_energy_ev = parameters.demand_energy_ev
193
+ LOGGER.info(f"Setting the energy to {demand_energy_ev}eV")
194
+ yield from set_energy_plan(
195
+ demand_energy_ev, cast(SetEnergyComposite, composite)
196
+ )
197
+
198
+ if doing_chi_change:
199
+ plan = _flyscan_plan_from_robot_load_params(composite, parameters)
200
+ LOGGER.info("Pin already loaded but chi changed so centring")
201
+ else:
202
+ LOGGER.info("Pin already loaded and chi not changed so doing nothing")
203
+ return
187
204
 
188
205
  detector_params = yield from fill_in_energy_if_not_supplied(
189
206
  composite.dcm, parameters.detector_params
@@ -192,6 +209,7 @@ def robot_load_then_xray_centre(
192
209
  eiger.set_detector_parameters(detector_params)
193
210
 
194
211
  yield from start_preparing_data_collection_then_do_plan(
212
+ composite.beamstop,
195
213
  eiger,
196
214
  composite.detector_motion,
197
215
  parameters.detector_distance_mm,
@@ -8,12 +8,13 @@ import pydantic
8
8
  from blueapi.core import BlueskyContext
9
9
  from bluesky.utils import MsgGenerator
10
10
  from dodal.devices.aperturescatterguard import ApertureScatterguard
11
- from dodal.devices.attenuator import Attenuator
11
+ from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
12
12
  from dodal.devices.backlight import Backlight
13
13
  from dodal.devices.dcm import DCM
14
14
  from dodal.devices.detector.detector_motion import DetectorMotion
15
15
  from dodal.devices.eiger import EigerDetector
16
16
  from dodal.devices.flux import Flux
17
+ from dodal.devices.i03.beamstop import Beamstop
17
18
  from dodal.devices.oav.oav_detector import OAV
18
19
  from dodal.devices.oav.oav_parameters import OAVParameters
19
20
  from dodal.devices.robot import BartRobot
@@ -22,13 +23,14 @@ from dodal.devices.smargon import Smargon
22
23
  from dodal.devices.synchrotron import Synchrotron
23
24
  from dodal.devices.undulator import Undulator
24
25
  from dodal.devices.xbpm_feedback import XBPMFeedback
25
- from dodal.devices.zebra import RotationDirection, Zebra
26
- from dodal.devices.zebra_controlled_shutter import ZebraShutter
26
+ from dodal.devices.zebra.zebra import RotationDirection, Zebra
27
+ from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
27
28
  from dodal.plan_stubs.check_topup import check_topup_and_wait_if_necessary
28
29
 
29
30
  from mx_bluesky.common.device_setup_plans.read_hardware_for_setup import (
30
31
  read_hardware_for_zocalo,
31
32
  )
33
+ from mx_bluesky.common.utils.log import LOGGER
32
34
  from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import (
33
35
  cleanup_sample_environment,
34
36
  move_phi_chi_omega,
@@ -55,7 +57,6 @@ from mx_bluesky.hyperion.experiment_plans.oav_snapshot_plan import (
55
57
  oav_snapshot_plan,
56
58
  setup_beamline_for_OAV,
57
59
  )
58
- from mx_bluesky.hyperion.log import LOGGER
59
60
  from mx_bluesky.hyperion.parameters.constants import CONST
60
61
  from mx_bluesky.hyperion.parameters.rotation import (
61
62
  MultiRotationScan,
@@ -69,8 +70,9 @@ class RotationScanComposite(OavSnapshotComposite):
69
70
  """All devices which are directly or indirectly required by this plan"""
70
71
 
71
72
  aperture_scatterguard: ApertureScatterguard
72
- attenuator: Attenuator
73
+ attenuator: BinaryFilterAttenuator
73
74
  backlight: Backlight
75
+ beamstop: Beamstop
74
76
  dcm: DCM
75
77
  detector_motion: DetectorMotion
76
78
  eiger: EigerDetector
@@ -126,13 +128,26 @@ def calculate_motion_profile(
126
128
  See https://github.com/DiamondLightSource/hyperion/wiki/rotation-scan-geometry
127
129
  for a simple pictorial explanation."""
128
130
 
129
- direction = params.rotation_direction.multiplier
131
+ assert params.rotation_increment_deg > 0
132
+
133
+ direction = params.rotation_direction
134
+ start_scan_deg = params.omega_start_deg
135
+
136
+ if params.features.omega_flip:
137
+ # If omega_flip is True then the motor omega axis is inverted with respect to the
138
+ # hyperion coordinate system.
139
+ start_scan_deg = -start_scan_deg
140
+ direction = (
141
+ direction.POSITIVE
142
+ if direction == direction.NEGATIVE
143
+ else direction.NEGATIVE
144
+ )
145
+
130
146
  num_images = params.num_images
131
147
  shutter_time_s = params.shutter_opening_time_s
132
148
  image_width_deg = params.rotation_increment_deg
133
149
  exposure_time_s = params.exposure_time_s
134
150
  motor_time_to_speed_s *= ACCELERATION_MARGIN
135
- start_scan_deg = params.omega_start_deg
136
151
 
137
152
  LOGGER.info("Calculating rotation scan motion profile:")
138
153
  LOGGER.info(
@@ -153,9 +168,9 @@ def calculate_motion_profile(
153
168
  f"{acceleration_offset_deg=} = {motor_time_to_speed_s=} * {speed_for_rotation_deg_s=}"
154
169
  )
155
170
 
156
- start_motion_deg = start_scan_deg - (acceleration_offset_deg * direction)
171
+ start_motion_deg = start_scan_deg - (acceleration_offset_deg * direction.multiplier)
157
172
  LOGGER.info(
158
- f"{start_motion_deg=} = {start_scan_deg=} - ({acceleration_offset_deg=} * {direction=})"
173
+ f"{start_motion_deg=} = {start_scan_deg=} - ({acceleration_offset_deg=} * {direction.multiplier=})"
159
174
  )
160
175
 
161
176
  shutter_opening_deg = speed_for_rotation_deg_s * shutter_time_s
@@ -173,7 +188,7 @@ def calculate_motion_profile(
173
188
 
174
189
  distance_to_move_deg = (
175
190
  scan_width_deg + shutter_opening_deg + acceleration_offset_deg * 2
176
- ) * direction
191
+ ) * direction.multiplier
177
192
  LOGGER.info(
178
193
  f"{distance_to_move_deg=} = ({scan_width_deg=} + {shutter_opening_deg=} + {acceleration_offset_deg=} * 2) * {direction=})"
179
194
  )
@@ -183,7 +198,7 @@ def calculate_motion_profile(
183
198
  start_motion_deg=start_motion_deg,
184
199
  scan_width_deg=scan_width_deg,
185
200
  shutter_time_s=shutter_time_s,
186
- direction=params.rotation_direction,
201
+ direction=direction,
187
202
  speed_for_rotation_deg_s=speed_for_rotation_deg_s,
188
203
  acceleration_offset_deg=acceleration_offset_deg,
189
204
  shutter_opening_deg=shutter_opening_deg,
@@ -331,13 +346,6 @@ def _move_and_rotation(
331
346
  yield from setup_beamline_for_OAV(
332
347
  composite.smargon, composite.backlight, composite.aperture_scatterguard
333
348
  )
334
- yield from bps.wait(group=CONST.WAIT.READY_FOR_OAV)
335
- if params.selected_aperture:
336
- yield from bps.abs_set(
337
- composite.aperture_scatterguard.aperture_outside_beam,
338
- params.selected_aperture,
339
- group=CONST.WAIT.ROTATION_READY_FOR_DC,
340
- )
341
349
  yield from oav_snapshot_plan(composite, params, oav_params)
342
350
  yield from rotation_scan_plan(
343
351
  composite,
@@ -351,6 +359,8 @@ def rotation_scan(
351
359
  parameters: RotationScan,
352
360
  oav_params: OAVParameters | None = None,
353
361
  ) -> MsgGenerator:
362
+ parameters.features.update_self_from_server()
363
+
354
364
  if not oav_params:
355
365
  oav_params = OAVParameters(context="xrayCentring")
356
366
 
@@ -360,7 +370,7 @@ def rotation_scan(
360
370
  "subplan_name": CONST.PLAN.ROTATION_OUTER,
361
371
  CONST.TRIGGER.ZOCALO: CONST.PLAN.ROTATION_MAIN,
362
372
  "zocalo_environment": CONST.ZOCALO_ENV,
363
- "hyperion_parameters": parameters.model_dump_json(),
373
+ "mx_bluesky_parameters": parameters.model_dump_json(),
364
374
  "activate_callbacks": [
365
375
  "RotationISPyBCallback",
366
376
  "RotationNexusFileCallback",
@@ -384,6 +394,7 @@ def rotation_scan(
384
394
 
385
395
  LOGGER.info("setting up and staging eiger...")
386
396
  yield from start_preparing_data_collection_then_do_plan(
397
+ composite.beamstop,
387
398
  eiger,
388
399
  composite.detector_motion,
389
400
  params.detector_distance_mm,
@@ -400,6 +411,7 @@ def multi_rotation_scan(
400
411
  parameters: MultiRotationScan,
401
412
  oav_params: OAVParameters | None = None,
402
413
  ) -> MsgGenerator:
414
+ parameters.features.update_self_from_server()
403
415
  if not oav_params:
404
416
  oav_params = OAVParameters(context="xrayCentring")
405
417
  eiger: EigerDetector = composite.eiger
@@ -432,8 +444,7 @@ def multi_rotation_scan(
432
444
  md={
433
445
  "subplan_name": CONST.PLAN.ROTATION_OUTER,
434
446
  CONST.TRIGGER.ZOCALO: CONST.PLAN.ROTATION_MAIN,
435
- "zocalo_environment": CONST.ZOCALO_ENV,
436
- "hyperion_parameters": single_scan.model_dump_json(),
447
+ "mx_bluesky_parameters": single_scan.model_dump_json(),
437
448
  }
438
449
  )
439
450
  def rotation_scan_core(
@@ -445,6 +456,7 @@ def multi_rotation_scan(
445
456
 
446
457
  LOGGER.info("setting up and staging eiger...")
447
458
  yield from start_preparing_data_collection_then_do_plan(
459
+ composite.beamstop,
448
460
  eiger,
449
461
  composite.detector_motion,
450
462
  parameters.detector_distance_mm,
@@ -7,7 +7,7 @@
7
7
 
8
8
  import pydantic
9
9
  from bluesky import plan_stubs as bps
10
- from dodal.devices.attenuator import Attenuator
10
+ from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
11
11
  from dodal.devices.dcm import DCM
12
12
  from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, MirrorVoltages
13
13
  from dodal.devices.undulator_dcm import UndulatorDCM
@@ -30,7 +30,7 @@ class SetEnergyComposite:
30
30
  dcm: DCM
31
31
  undulator_dcm: UndulatorDCM
32
32
  xbpm_feedback: XBPMFeedback
33
- attenuator: Attenuator
33
+ attenuator: BinaryFilterAttenuator
34
34
 
35
35
 
36
36
  def _set_energy_plan(
@@ -48,12 +48,13 @@ def _set_energy_plan(
48
48
 
49
49
 
50
50
  def set_energy_plan(
51
- energy_kev,
51
+ energy_ev: float | None,
52
52
  composite: SetEnergyComposite,
53
53
  ):
54
- yield from transmission_and_xbpm_feedback_for_collection_wrapper(
55
- _set_energy_plan(energy_kev, composite),
56
- composite.xbpm_feedback,
57
- composite.attenuator,
58
- DESIRED_TRANSMISSION_FRACTION,
59
- )
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"]