mx-bluesky 1.5.11__py3-none-any.whl → 1.5.12__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 (85) hide show
  1. mx_bluesky/_version.py +2 -2
  2. mx_bluesky/beamlines/aithre_lasershaping/experiment_plans/__init__.py +0 -0
  3. mx_bluesky/beamlines/aithre_lasershaping/experiment_plans/robot_load_plan.py +198 -0
  4. mx_bluesky/beamlines/aithre_lasershaping/parameters/__init__.py +0 -0
  5. mx_bluesky/beamlines/aithre_lasershaping/parameters/constants.py +17 -0
  6. mx_bluesky/beamlines/aithre_lasershaping/parameters/robot_load_parameters.py +13 -0
  7. mx_bluesky/beamlines/aithre_lasershaping/pin_tip_centring.py +31 -0
  8. mx_bluesky/beamlines/aithre_lasershaping/robot_load.py +80 -0
  9. mx_bluesky/beamlines/i04/__init__.py +6 -2
  10. mx_bluesky/beamlines/i04/callbacks/murko_callback.py +27 -12
  11. mx_bluesky/beamlines/i04/experiment_plans/i04_grid_detect_then_xray_centre_plan.py +87 -13
  12. mx_bluesky/beamlines/i04/external_interaction/__init__.py +0 -0
  13. mx_bluesky/beamlines/i04/external_interaction/config_server.py +15 -0
  14. mx_bluesky/beamlines/i04/oav_centering_plans/__init__.py +0 -0
  15. mx_bluesky/beamlines/i04/oav_centering_plans/oav_imaging.py +115 -0
  16. mx_bluesky/beamlines/i04/parameters/__init__.py +0 -0
  17. mx_bluesky/beamlines/i04/parameters/constants.py +21 -0
  18. mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +24 -1
  19. mx_bluesky/beamlines/i04/thawing_plan.py +147 -152
  20. mx_bluesky/beamlines/i24/serial/dcid.py +4 -5
  21. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_extruder_collect_py3v2.py +5 -2
  22. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/CustomChip_py3v1.edl +11 -11
  23. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DetStage.edl +3 -3
  24. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +142 -142
  25. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/MappingLite-oxford_py3v1.edl +135 -135
  26. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/PMAC_Command.edl +8 -8
  27. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +13 -13
  28. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_chip_collect_py3v1.py +7 -4
  29. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_chip_manager_py3v1.py +35 -32
  30. mx_bluesky/beamlines/i24/serial/parameters/utils.py +5 -5
  31. mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +113 -306
  32. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +8 -2
  33. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +1 -1
  34. mx_bluesky/beamlines/i24/serial/web_gui_plans/general_plans.py +1 -1
  35. mx_bluesky/beamlines/i24/serial/web_gui_plans/oav_plans.py +64 -0
  36. mx_bluesky/{hyperion/device_setup_plans/smargon.py → common/device_setup_plans/gonio.py} +9 -6
  37. mx_bluesky/common/device_setup_plans/manipulate_sample.py +8 -1
  38. mx_bluesky/common/device_setup_plans/robot_load_unload.py +1 -1
  39. mx_bluesky/common/device_setup_plans/setup_oav.py +8 -0
  40. mx_bluesky/common/device_setup_plans/setup_zebra_and_shutter.py +0 -5
  41. mx_bluesky/common/device_setup_plans/xbpm_feedback.py +8 -1
  42. mx_bluesky/common/experiment_plans/beamstop_check.py +229 -0
  43. mx_bluesky/common/experiment_plans/common_flyscan_xray_centre_plan.py +2 -0
  44. mx_bluesky/common/experiment_plans/inner_plans/read_hardware.py +5 -2
  45. mx_bluesky/common/experiment_plans/oav_snapshot_plan.py +0 -1
  46. mx_bluesky/{hyperion → common}/experiment_plans/pin_tip_centring_plan.py +20 -21
  47. mx_bluesky/common/external_interaction/callbacks/common/grid_detection_callback.py +5 -0
  48. mx_bluesky/common/external_interaction/callbacks/common/ispyb_callback_base.py +10 -12
  49. mx_bluesky/common/external_interaction/callbacks/common/ispyb_mapping.py +3 -5
  50. mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py +5 -5
  51. mx_bluesky/common/external_interaction/config_server.py +2 -2
  52. mx_bluesky/common/external_interaction/ispyb/data_model.py +11 -4
  53. mx_bluesky/common/external_interaction/ispyb/exp_eye_store.py +159 -2
  54. mx_bluesky/common/external_interaction/ispyb/ispyb_store.py +76 -166
  55. mx_bluesky/common/external_interaction/ispyb/ispyb_utils.py +0 -14
  56. mx_bluesky/common/parameters/components.py +1 -0
  57. mx_bluesky/common/parameters/constants.py +3 -2
  58. mx_bluesky/common/parameters/device_composites.py +4 -2
  59. mx_bluesky/common/utils/exceptions.py +15 -0
  60. mx_bluesky/common/utils/log.py +9 -0
  61. mx_bluesky/common/utils/utils.py +48 -0
  62. mx_bluesky/hyperion/__main__.py +3 -13
  63. mx_bluesky/hyperion/baton_handler.py +23 -6
  64. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +5 -6
  65. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +3 -10
  66. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +4 -2
  67. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +8 -2
  68. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +2 -2
  69. mx_bluesky/hyperion/experiment_plans/udc_default_state.py +160 -0
  70. mx_bluesky/hyperion/external_interaction/agamemnon.py +1 -1
  71. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +2 -2
  72. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +1 -0
  73. mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +1 -4
  74. mx_bluesky/hyperion/external_interaction/config_server.py +5 -5
  75. mx_bluesky/hyperion/parameters/constants.py +10 -3
  76. mx_bluesky/hyperion/parameters/device_composites.py +2 -2
  77. mx_bluesky/hyperion/parameters/robot_load.py +1 -9
  78. {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.12.dist-info}/METADATA +6 -5
  79. {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.12.dist-info}/RECORD +83 -69
  80. mx_bluesky/common/experiment_plans/inner_plans/udc_default_state.py +0 -86
  81. mx_bluesky/common/external_interaction/callbacks/common/logging_callback.py +0 -29
  82. {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.12.dist-info}/WHEEL +0 -0
  83. {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.12.dist-info}/entry_points.txt +0 -0
  84. {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.12.dist-info}/licenses/LICENSE +0 -0
  85. {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.12.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,8 @@ from math import asin
4
4
  from scanspec.core import AxesPoints, Axis
5
5
  from scipy.constants import physical_constants
6
6
 
7
+ from mx_bluesky.common.utils.log import LOGGER
8
+
7
9
  hc_in_ev_and_angstrom: float = (
8
10
  physical_constants["speed of light in vacuum"][0]
9
11
  * physical_constants["Planck constant in eV/Hz"][0]
@@ -40,3 +42,49 @@ def energy_to_bragg_angle(energy_kev: float, d_a: float) -> float:
40
42
  wavelength_a = convert_ev_to_angstrom(energy_kev * 1000)
41
43
  d = d_a
42
44
  return asin(wavelength_a / (2 * d)) * 180 / math.pi
45
+
46
+
47
+ def fix_transmission_and_exposure_time_for_current_wavelength(
48
+ current_wavelength_a: float,
49
+ assumed_wavelength_a_from_settings: float,
50
+ requested_trans_frac: float = 100,
51
+ requested_exposure_time_s: float = 0.004,
52
+ ) -> tuple[float, float]:
53
+ """
54
+ Calculates an exposure time and transmission fraction for XRC which will provide a good signal
55
+ on the detector by using a known good wavelength, comparing it to the beamlines current wavelength,
56
+ then scaling accordingly.
57
+
58
+ Args:
59
+ current_wavelength_a: Current energy of the beamline in angstroms.
60
+ assumed_wavelength_a_from_settings: The known "good" wavelength. This should be read from
61
+ 'gda.px.expttable.default.wavelength' in GDA's domain.properties, via the config server.
62
+ requested_trans_frac: Requested transmission fraction to use.
63
+ requested_exposure_time_s: Requested exposure time to use.
64
+
65
+ Returns:
66
+ The scaled transmission fraction and exposure time respectively, in a tuple.
67
+ """
68
+
69
+ wavelength_scale = (assumed_wavelength_a_from_settings / current_wavelength_a) ** 2
70
+
71
+ # Transmission frac needed to get ideal signal
72
+ ideal_trans_frac = requested_trans_frac * wavelength_scale
73
+ if ideal_trans_frac <= 1:
74
+ new_trans_frac = ideal_trans_frac
75
+ new_exposure_time_s = requested_exposure_time_s
76
+ else:
77
+ # If the scaling would result in transmission fraction > 1,
78
+ # cap it to 1, find remaining scaling needed, and apply it
79
+ # to exposure time instead.
80
+ new_trans_frac = 1
81
+ scaling_applied_to_trans = new_trans_frac / requested_trans_frac
82
+ remaining_scaling_needed = wavelength_scale / scaling_applied_to_trans
83
+ new_exposure_time_s = requested_exposure_time_s * remaining_scaling_needed
84
+
85
+ LOGGER.info(
86
+ f"Fixing transmission fraction to {new_trans_frac} and exposure time to {new_exposure_time_s}s"
87
+ )
88
+
89
+ # Exposure time in FGS IOC is in ms, and must be an integer, so round it here
90
+ return new_trans_frac, round(new_exposure_time_s, 3)
@@ -45,21 +45,13 @@ from mx_bluesky.hyperion.runner import (
45
45
  from mx_bluesky.hyperion.utils.context import setup_context
46
46
 
47
47
 
48
- def compose_start_args(context: BlueskyContext, plan_name: str, action: Actions):
48
+ def compose_start_args(context: BlueskyContext, plan_name: str):
49
49
  experiment_registry_entry = PLAN_REGISTRY.get(plan_name)
50
50
  if experiment_registry_entry is None:
51
51
  raise PlanNotFoundError(f"Experiment plan '{plan_name}' not found in registry.")
52
52
 
53
53
  experiment_internal_param_type = experiment_registry_entry.get("param_type")
54
54
  plan = context.plan_functions.get(plan_name)
55
- if experiment_internal_param_type is None:
56
- raise PlanNotFoundError(
57
- f"Corresponding internal param type for '{plan_name}' not found in registry."
58
- )
59
- if plan is None:
60
- raise PlanNotFoundError(
61
- f"Experiment plan '{plan_name}' not found in context. Context has {context.plan_functions.keys()}"
62
- )
63
55
  try:
64
56
  parameters = experiment_internal_param_type(**json.loads(request.data))
65
57
  parameters = update_params_from_agamemnon(parameters)
@@ -80,13 +72,11 @@ class RunExperiment(Resource):
80
72
  self.runner = runner
81
73
  self.context = context
82
74
 
83
- def put(self, plan_name: str, action: Actions):
75
+ def put(self, plan_name: str, action: str):
84
76
  status_and_message = StatusAndMessage(Status.FAILED, f"{action} not understood")
85
77
  if action == Actions.START.value:
86
78
  try:
87
- plan, params, plan_name = compose_start_args(
88
- self.context, plan_name, action
89
- )
79
+ plan, params, plan_name = compose_start_args(self.context, plan_name)
90
80
  status_and_message = self.runner.start(plan, params, plan_name)
91
81
  except Exception as e:
92
82
  status_and_message = make_error_status_and_message(e)
@@ -15,10 +15,6 @@ from dodal.devices.robot import BartRobot
15
15
  from dodal.devices.smargon import Smargon
16
16
 
17
17
  from mx_bluesky.common.device_setup_plans.robot_load_unload import robot_unload
18
- from mx_bluesky.common.experiment_plans.inner_plans.udc_default_state import (
19
- UDCDefaultDevices,
20
- move_to_udc_default_state,
21
- )
22
18
  from mx_bluesky.common.external_interaction.alerting import (
23
19
  AlertService,
24
20
  get_alerting_service,
@@ -28,11 +24,16 @@ from mx_bluesky.common.utils.context import (
28
24
  device_composite_from_context,
29
25
  find_device_in_context,
30
26
  )
27
+ from mx_bluesky.common.utils.exceptions import BeamlineCheckFailureError
31
28
  from mx_bluesky.common.utils.log import LOGGER
32
29
  from mx_bluesky.hyperion.experiment_plans.load_centre_collect_full_plan import (
33
30
  create_devices,
34
31
  load_centre_collect_full,
35
32
  )
33
+ from mx_bluesky.hyperion.experiment_plans.udc_default_state import (
34
+ UDCDefaultDevices,
35
+ move_to_udc_default_state,
36
+ )
36
37
  from mx_bluesky.hyperion.external_interaction.agamemnon import (
37
38
  create_parameters_from_agamemnon,
38
39
  )
@@ -100,7 +101,11 @@ def run_udc_when_requested(context: BlueskyContext, runner: PlanRunner):
100
101
  runner: The runner
101
102
  """
102
103
  _raise_udc_start_alert(get_alerting_service())
103
- yield from _move_to_udc_default_state(context)
104
+ yield from bpp.contingency_wrapper(
105
+ _move_to_udc_default_state(context),
106
+ except_plan=trap_default_state_exception,
107
+ auto_raise=False,
108
+ )
104
109
 
105
110
  # re-fetch the baton because the device has been reinstantiated
106
111
  baton = _get_baton(context)
@@ -121,8 +126,20 @@ def run_udc_when_requested(context: BlueskyContext, runner: PlanRunner):
121
126
  yield from bps.abs_set(baton.current_user, NO_USER, wait=True)
122
127
  _raise_baton_released_alert(get_alerting_service(), previous_requested_user)
123
128
 
129
+ def trap_default_state_exception(e: Exception):
130
+ yield from bps.null()
131
+ if isinstance(e, BeamlineCheckFailureError):
132
+ LOGGER.warning("Caught default state check failure:", exc_info=e)
133
+ raise PlanError("Caught default state check failure") from e
134
+ else:
135
+ LOGGER.warning("Caught unexpected exception", exc_info=e)
136
+ raise PlanError("Unexpected exception from UDC Default State plan") from e
137
+
124
138
  def collect_then_release() -> MsgGenerator:
125
- yield from bpp.contingency_wrapper(collect(), final_plan=release_baton)
139
+ yield from bpp.contingency_wrapper(
140
+ collect(),
141
+ final_plan=release_baton,
142
+ )
126
143
 
127
144
  context.run_engine(acquire_baton())
128
145
  _initialise_udc(context, runner.is_dev_mode)
@@ -21,6 +21,10 @@ from mx_bluesky.common.experiment_plans.common_grid_detect_then_xray_centre_plan
21
21
  from mx_bluesky.common.experiment_plans.oav_snapshot_plan import (
22
22
  setup_beamline_for_oav,
23
23
  )
24
+ from mx_bluesky.common.experiment_plans.pin_tip_centring_plan import (
25
+ PinTipCentringComposite,
26
+ pin_tip_centre_plan,
27
+ )
24
28
  from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback import (
25
29
  ispyb_activation_wrapper,
26
30
  )
@@ -31,10 +35,6 @@ from mx_bluesky.common.xrc_result import XRayCentreEventHandler
31
35
  from mx_bluesky.hyperion.experiment_plans.hyperion_flyscan_xray_centre_plan import (
32
36
  construct_hyperion_specific_features,
33
37
  )
34
- from mx_bluesky.hyperion.experiment_plans.pin_tip_centring_plan import (
35
- PinTipCentringComposite,
36
- pin_tip_centre_plan,
37
- )
38
38
  from mx_bluesky.hyperion.parameters.constants import CONST
39
39
  from mx_bluesky.hyperion.parameters.device_composites import (
40
40
  HyperionGridDetectThenXRayCentreComposite,
@@ -78,8 +78,7 @@ def pin_centre_then_flyscan_plan(
78
78
 
79
79
  pin_tip_centring_composite = PinTipCentringComposite(
80
80
  oav=composite.oav,
81
- smargon=composite.smargon,
82
- backlight=composite.backlight,
81
+ gonio=composite.smargon,
83
82
  pin_tip_detection=composite.pin_tip_detection,
84
83
  )
85
84
 
@@ -20,7 +20,7 @@ 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
22
  from dodal.devices.smargon import Smargon
23
- from dodal.devices.thawer import Thawer
23
+ from dodal.devices.thawer import OnOff, Thawer
24
24
  from dodal.devices.webcam import Webcam
25
25
  from dodal.devices.xbpm_feedback import XBPMFeedback
26
26
 
@@ -82,7 +82,6 @@ def do_robot_load(
82
82
  sample_location: SampleLocation,
83
83
  sample_id: int,
84
84
  demand_energy_ev: float | None,
85
- thawing_time: float,
86
85
  ):
87
86
  yield from bps.abs_set(composite.robot.next_sample_id, sample_id, wait=True)
88
87
 
@@ -96,13 +95,10 @@ def do_robot_load(
96
95
 
97
96
  yield from bps.wait("robot_load")
98
97
 
99
- yield from bps.abs_set(
100
- composite.thawer.thaw_for_time_s,
101
- thawing_time,
102
- group="thawing_finished",
103
- )
104
98
  yield from wait_for_smargon_not_disabled(composite.smargon)
105
99
 
100
+ yield from bps.mv(composite.thawer, OnOff.ON)
101
+
106
102
 
107
103
  def pin_already_loaded(
108
104
  robot: BartRobot, sample_location: SampleLocation
@@ -120,7 +116,6 @@ def robot_load_and_snapshots(
120
116
  location: SampleLocation,
121
117
  snapshot_directory: Path,
122
118
  sample_id: int,
123
- thawing_time: float,
124
119
  demand_energy_ev: float | None,
125
120
  ):
126
121
  yield from bps.abs_set(composite.backlight, InOut.IN, group="snapshot")
@@ -134,7 +129,6 @@ def robot_load_and_snapshots(
134
129
  location,
135
130
  sample_id,
136
131
  demand_energy_ev,
137
- thawing_time,
138
132
  )
139
133
 
140
134
  gonio_finished = yield from do_plan_while_lower_gonio_at_home(
@@ -173,7 +167,6 @@ def robot_load_and_change_energy_plan(
173
167
  sample_location,
174
168
  params.snapshot_directory,
175
169
  params.sample_id,
176
- params.thawing_time,
177
170
  params.demand_energy_ev,
178
171
  ),
179
172
  md={
@@ -10,6 +10,7 @@ from bluesky.utils import MsgGenerator
10
10
  from dodal.devices.aperturescatterguard import ApertureScatterguard
11
11
  from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
12
12
  from dodal.devices.backlight import Backlight
13
+ from dodal.devices.beamsize.beamsize import BeamsizeBase
13
14
  from dodal.devices.detector.detector_motion import DetectorMotion
14
15
  from dodal.devices.eiger import EigerDetector
15
16
  from dodal.devices.fast_grid_scan import PandAFastGridScan, ZebraFastGridScanThreeD
@@ -26,7 +27,7 @@ from dodal.devices.s4_slit_gaps import S4SlitGaps
26
27
  from dodal.devices.smargon import Smargon
27
28
  from dodal.devices.synchrotron import Synchrotron
28
29
  from dodal.devices.thawer import Thawer
29
- from dodal.devices.undulator import Undulator
30
+ from dodal.devices.undulator import UndulatorInKeV
30
31
  from dodal.devices.webcam import Webcam
31
32
  from dodal.devices.xbpm_feedback import XBPMFeedback
32
33
  from dodal.devices.zebra.zebra import Zebra
@@ -70,6 +71,7 @@ class RobotLoadThenCentreComposite:
70
71
  # HyperionGridDetectThenXRayCentreComposite fields
71
72
  aperture_scatterguard: ApertureScatterguard
72
73
  backlight: Backlight
74
+ beamsize: BeamsizeBase
73
75
  detector_motion: DetectorMotion
74
76
  eiger: EigerDetector
75
77
  zebra_fast_grid_scan: ZebraFastGridScanThreeD
@@ -79,7 +81,7 @@ class RobotLoadThenCentreComposite:
79
81
  smargon: Smargon
80
82
  synchrotron: Synchrotron
81
83
  s4_slit_gaps: S4SlitGaps
82
- undulator: Undulator
84
+ undulator: UndulatorInKeV
83
85
  zebra: Zebra
84
86
  zocalo: ZocaloResults
85
87
  panda: HDFPanda
@@ -10,6 +10,7 @@ from bluesky.utils import MsgGenerator
10
10
  from dodal.devices.aperturescatterguard import ApertureScatterguard
11
11
  from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
12
12
  from dodal.devices.backlight import Backlight
13
+ from dodal.devices.beamsize.beamsize import BeamsizeBase
13
14
  from dodal.devices.detector.detector_motion import DetectorMotion
14
15
  from dodal.devices.eiger import EigerDetector
15
16
  from dodal.devices.flux import Flux
@@ -21,7 +22,8 @@ from dodal.devices.robot import BartRobot
21
22
  from dodal.devices.s4_slit_gaps import S4SlitGaps
22
23
  from dodal.devices.smargon import CombinedMove, Smargon
23
24
  from dodal.devices.synchrotron import Synchrotron
24
- from dodal.devices.undulator import Undulator
25
+ from dodal.devices.thawer import Thawer
26
+ from dodal.devices.undulator import UndulatorInKeV
25
27
  from dodal.devices.xbpm_feedback import XBPMFeedback
26
28
  from dodal.devices.zebra.zebra import RotationDirection, Zebra
27
29
  from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
@@ -74,6 +76,7 @@ class RotationScanComposite(OavSnapshotComposite):
74
76
  aperture_scatterguard: ApertureScatterguard
75
77
  attenuator: BinaryFilterAttenuator
76
78
  backlight: Backlight
79
+ beamsize: BeamsizeBase
77
80
  beamstop: Beamstop
78
81
  dcm: DCM
79
82
  detector_motion: DetectorMotion
@@ -81,13 +84,14 @@ class RotationScanComposite(OavSnapshotComposite):
81
84
  flux: Flux
82
85
  robot: BartRobot
83
86
  smargon: Smargon
84
- undulator: Undulator
87
+ undulator: UndulatorInKeV
85
88
  synchrotron: Synchrotron
86
89
  s4_slit_gaps: S4SlitGaps
87
90
  sample_shutter: ZebraShutter
88
91
  zebra: Zebra
89
92
  oav: OAV
90
93
  xbpm_feedback: XBPMFeedback
94
+ thawer: Thawer
91
95
 
92
96
 
93
97
  def create_devices(context: BlueskyContext) -> RotationScanComposite:
@@ -258,6 +262,7 @@ def rotation_scan_plan(
258
262
  composite.aperture_scatterguard,
259
263
  params.selected_aperture,
260
264
  composite.backlight,
265
+ composite.thawer,
261
266
  group=CONST.WAIT.ROTATION_READY_FOR_DC,
262
267
  )
263
268
 
@@ -301,6 +306,7 @@ def rotation_scan_plan(
301
306
  composite.flux,
302
307
  composite.dcm,
303
308
  composite.eiger,
309
+ composite.beamsize,
304
310
  )
305
311
 
306
312
  yield from _rotation_scan_plan(motion_values, composite)
@@ -12,7 +12,7 @@ from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
12
12
  from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, MirrorVoltages
13
13
  from dodal.devices.i03.dcm import DCM
14
14
  from dodal.devices.i03.undulator_dcm import UndulatorDCM
15
- from dodal.devices.undulator import Undulator
15
+ from dodal.devices.undulator import UndulatorInKeV
16
16
  from dodal.devices.xbpm_feedback import XBPMFeedback
17
17
 
18
18
  from mx_bluesky.common.parameters.constants import PlanNameConstants
@@ -39,7 +39,7 @@ class SetEnergyComposite:
39
39
  # Remove composite after https://github.com/DiamondLightSource/dodal/issues/1092
40
40
  @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
41
41
  class XBPMWrapperComposite:
42
- undulator: Undulator
42
+ undulator: UndulatorInKeV
43
43
  xbpm_feedback: XBPMFeedback
44
44
  attenuator: BinaryFilterAttenuator
45
45
  dcm: DCM
@@ -0,0 +1,160 @@
1
+ import bluesky.plan_stubs as bps
2
+ import pydantic
3
+ from bluesky.utils import MsgGenerator
4
+ from dodal.common.beamlines.beamline_parameters import (
5
+ get_beamline_parameters,
6
+ )
7
+ from dodal.devices.aperturescatterguard import ApertureValue
8
+ from dodal.devices.collimation_table import CollimationTable
9
+ from dodal.devices.cryostream import CryoStream, CryoStreamGantry, CryoStreamSelection
10
+ from dodal.devices.cryostream import InOut as CryoInOut
11
+ from dodal.devices.fluorescence_detector_motion import FluorescenceDetector
12
+ from dodal.devices.fluorescence_detector_motion import InOut as FlouInOut
13
+ from dodal.devices.hutch_shutter import HutchShutter, ShutterDemand
14
+ from dodal.devices.mx_phase1.beamstop import BeamstopPositions
15
+ from dodal.devices.robot import BartRobot, PinMounted
16
+ from dodal.devices.scintillator import InOut as ScinInOut
17
+ from dodal.devices.scintillator import Scintillator
18
+ from dodal.devices.smargon import Smargon
19
+ from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutterState
20
+
21
+ from mx_bluesky.common.experiment_plans.beamstop_check import (
22
+ BeamstopCheckDevices,
23
+ move_beamstop_in_and_verify_using_diode,
24
+ )
25
+ from mx_bluesky.common.utils.exceptions import BeamlineCheckFailureError
26
+ from mx_bluesky.common.utils.log import LOGGER
27
+ from mx_bluesky.hyperion.external_interaction.config_server import (
28
+ get_hyperion_config_client,
29
+ )
30
+ from mx_bluesky.hyperion.parameters.constants import HyperionFeatureSettings
31
+
32
+ _GROUP_PRE_BEAMSTOP_CHECK = "pre_beamstop_check"
33
+ _GROUP_POST_BEAMSTOP_CHECK = "post_beamstop_check"
34
+
35
+
36
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
37
+ class UDCDefaultDevices(BeamstopCheckDevices):
38
+ collimation_table: CollimationTable
39
+ cryostream: CryoStream
40
+ cryostream_gantry: CryoStreamGantry
41
+ fluorescence_det_motion: FluorescenceDetector
42
+ hutch_shutter: HutchShutter
43
+ robot: BartRobot
44
+ scintillator: Scintillator
45
+ smargon: Smargon
46
+
47
+
48
+ class UnexpectedSampleError(BeamlineCheckFailureError): ...
49
+
50
+
51
+ class CryoStreamError(BeamlineCheckFailureError): ...
52
+
53
+
54
+ def move_to_udc_default_state(devices: UDCDefaultDevices):
55
+ """Moves beamline to known positions prior to UDC start"""
56
+ yield from _verify_correct_cryostream_selected(devices.cryostream_gantry)
57
+
58
+ cryostream_temp = yield from bps.rd(devices.cryostream.temperature_k)
59
+ cryostream_pressure = yield from bps.rd(devices.cryostream.back_pressure_bar)
60
+ if cryostream_temp > devices.cryostream.MAX_TEMP_K:
61
+ raise CryoStreamError("Cryostream temperature is too high, not starting UDC")
62
+ if cryostream_pressure > devices.cryostream.MAX_PRESSURE_BAR:
63
+ raise CryoStreamError("Cryostream back pressure is too high, not starting UDC")
64
+
65
+ yield from _verify_no_sample_present(devices.robot)
66
+
67
+ # Close fast shutter before opening hutch shutter
68
+ yield from bps.abs_set(devices.sample_shutter, ZebraShutterState.CLOSE, wait=True)
69
+
70
+ commissioning_mode_enabled = yield from bps.rd(devices.baton.commissioning)
71
+
72
+ if commissioning_mode_enabled:
73
+ LOGGER.warning("Not opening hutch shutter - commissioning mode is enabled.")
74
+ else:
75
+ yield from bps.abs_set(
76
+ devices.hutch_shutter, ShutterDemand.OPEN, group=_GROUP_PRE_BEAMSTOP_CHECK
77
+ )
78
+
79
+ yield from bps.abs_set(devices.scintillator.selected_pos, ScinInOut.OUT, wait=True)
80
+
81
+ yield from bps.abs_set(
82
+ devices.fluorescence_det_motion.pos,
83
+ FlouInOut.OUT,
84
+ group=_GROUP_PRE_BEAMSTOP_CHECK,
85
+ )
86
+
87
+ yield from bps.abs_set(
88
+ devices.collimation_table.inboard_y,
89
+ 0,
90
+ group=_GROUP_PRE_BEAMSTOP_CHECK,
91
+ )
92
+ yield from bps.abs_set(
93
+ devices.collimation_table.outboard_y, 0, group=_GROUP_PRE_BEAMSTOP_CHECK
94
+ )
95
+ yield from bps.abs_set(
96
+ devices.collimation_table.upstream_y, 0, group=_GROUP_PRE_BEAMSTOP_CHECK
97
+ )
98
+ yield from bps.abs_set(
99
+ devices.collimation_table.upstream_x, 0, group=_GROUP_PRE_BEAMSTOP_CHECK
100
+ )
101
+ yield from bps.abs_set(
102
+ devices.collimation_table.downstream_x, 0, group=_GROUP_PRE_BEAMSTOP_CHECK
103
+ )
104
+
105
+ # Wait for all of the above to complete
106
+ yield from bps.wait(group=_GROUP_PRE_BEAMSTOP_CHECK, timeout=0.1)
107
+
108
+ feature_flags: HyperionFeatureSettings = (
109
+ get_hyperion_config_client().get_feature_flags()
110
+ )
111
+ if feature_flags.BEAMSTOP_DIODE_CHECK:
112
+ beamline_parameters = get_beamline_parameters()
113
+ config_client = get_hyperion_config_client()
114
+ features_settings: HyperionFeatureSettings = config_client.get_feature_flags()
115
+ detector_min_z = features_settings.DETECTOR_DISTANCE_LIMIT_MIN_MM
116
+ detector_max_z = features_settings.DETECTOR_DISTANCE_LIMIT_MAX_MM
117
+ yield from move_beamstop_in_and_verify_using_diode(
118
+ devices, beamline_parameters, detector_min_z, detector_max_z
119
+ )
120
+ else:
121
+ yield from bps.abs_set(
122
+ devices.beamstop.selected_pos, BeamstopPositions.DATA_COLLECTION, wait=True
123
+ )
124
+
125
+ yield from bps.abs_set(
126
+ devices.aperture_scatterguard.selected_aperture,
127
+ ApertureValue.SMALL,
128
+ group=_GROUP_POST_BEAMSTOP_CHECK,
129
+ )
130
+
131
+ yield from bps.abs_set(
132
+ devices.cryostream.course, CryoInOut.IN, group=_GROUP_POST_BEAMSTOP_CHECK
133
+ )
134
+ yield from bps.abs_set(
135
+ devices.cryostream.fine, CryoInOut.IN, group=_GROUP_POST_BEAMSTOP_CHECK
136
+ )
137
+
138
+ yield from bps.wait(_GROUP_POST_BEAMSTOP_CHECK)
139
+
140
+
141
+ def _verify_correct_cryostream_selected(
142
+ cryostream_gantry: CryoStreamGantry,
143
+ ) -> MsgGenerator:
144
+ cryostream_selection = yield from bps.rd(cryostream_gantry.cryostream_selector)
145
+ cryostream_selected = yield from bps.rd(cryostream_gantry.cryostream_selected)
146
+ if cryostream_selection != CryoStreamSelection.CRYOJET or cryostream_selected != 1:
147
+ raise CryoStreamError(
148
+ f"Cryostream is not selected for use, control PV selection = {cryostream_selection}, "
149
+ f"current status {cryostream_selected}"
150
+ )
151
+
152
+
153
+ def _verify_no_sample_present(robot: BartRobot):
154
+ pin_mounted = yield from bps.rd(robot.gonio_pin_sensor)
155
+
156
+ if pin_mounted != PinMounted.NO_PIN_MOUNTED:
157
+ # Cannot unload this sample because we do not know the correct visit for it
158
+ raise UnexpectedSampleError(
159
+ "An unexpected sample was found, please unload the sample manually."
160
+ )
@@ -220,7 +220,7 @@ def _get_withvisit_parameters_from_agamemnon(parameters: dict) -> tuple:
220
220
  def _get_withenergy_parameters_from_agamemnon(parameters: dict) -> dict[str, Any]:
221
221
  try:
222
222
  first_collection: dict = parameters["collection"][0]
223
- wavelength = first_collection.get("wavelength")
223
+ wavelength: float | None = first_collection.get("wavelength")
224
224
  assert isinstance(wavelength, float)
225
225
  demand_energy_ev = convert_angstrom_to_ev(wavelength)
226
226
  return {"demand_energy_ev": demand_energy_ev}
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Callable, Sequence
4
- from typing import TYPE_CHECKING, Any, cast
4
+ from typing import TYPE_CHECKING, Any
5
5
 
6
6
  from dodal.devices.zocalo import ZocaloStartInfo
7
7
 
@@ -92,7 +92,7 @@ class RotationISPyBCallback(BaseISPyBCallback):
92
92
  ISPYB_ZOCALO_CALLBACK_LOGGER.info("Beginning ispyb deposition")
93
93
  data_collection_group_info = populate_data_collection_group(self.params)
94
94
  data_collection_info = populate_data_collection_info_for_rotation(
95
- cast(SingleRotationScan, self.params)
95
+ self.params
96
96
  )
97
97
  data_collection_info = populate_remaining_data_collection_info(
98
98
  self.params.comment,
@@ -6,6 +6,7 @@ from mx_bluesky.hyperion.parameters.rotation import SingleRotationScan
6
6
 
7
7
  def populate_data_collection_info_for_rotation(params: SingleRotationScan):
8
8
  info = DataCollectionInfo(
9
+ chi_start=params.chi_start_deg,
9
10
  omega_start=params.omega_start_deg,
10
11
  data_collection_number=params.detector_params.run_number, # type:ignore # the validator always makes this int
11
12
  n_images=params.num_images,
@@ -2,9 +2,6 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING
4
4
 
5
- from mx_bluesky.common.external_interaction.callbacks.common.logging_callback import (
6
- format_doc_for_log,
7
- )
8
5
  from mx_bluesky.common.external_interaction.callbacks.common.plan_reactive_callback import (
9
6
  PlanReactiveCallback,
10
7
  )
@@ -14,7 +11,7 @@ from mx_bluesky.common.external_interaction.nexus.nexus_utils import (
14
11
  vds_type_based_on_bit_depth,
15
12
  )
16
13
  from mx_bluesky.common.external_interaction.nexus.write_nexus import NexusWriter
17
- from mx_bluesky.common.utils.log import NEXUS_LOGGER
14
+ from mx_bluesky.common.utils.log import NEXUS_LOGGER, format_doc_for_log
18
15
  from mx_bluesky.hyperion.parameters.constants import CONST, I03Constants
19
16
  from mx_bluesky.hyperion.parameters.rotation import SingleRotationScan
20
17
 
@@ -2,15 +2,15 @@ from functools import cache
2
2
 
3
3
  from mx_bluesky.common.external_interaction.config_server import MXConfigClient
4
4
  from mx_bluesky.hyperion.parameters.constants import (
5
- HyperionFeatureSetting,
6
- HyperionFeatureSettingSources,
5
+ HyperionFeatureSettings,
6
+ HyperionFeatureSettingsSources,
7
7
  )
8
8
 
9
9
 
10
10
  @cache
11
- def get_hyperion_config_client() -> MXConfigClient[HyperionFeatureSetting]:
11
+ def get_hyperion_config_client() -> MXConfigClient[HyperionFeatureSettings]:
12
12
  return MXConfigClient(
13
- feature_sources=HyperionFeatureSettingSources,
14
- feature_dc=HyperionFeatureSetting,
13
+ feature_sources=HyperionFeatureSettingsSources,
14
+ feature_dc=HyperionFeatureSettings,
15
15
  url="https://daq-config.diamond.ac.uk",
16
16
  )
@@ -8,7 +8,7 @@ from mx_bluesky.common.parameters.constants import (
8
8
  DocDescriptorNames,
9
9
  EnvironmentConstants,
10
10
  ExperimentParamConstants,
11
- FeatureSetting,
11
+ FeatureSettings,
12
12
  FeatureSettingSources,
13
13
  HardwareConstants,
14
14
  OavConstants,
@@ -32,20 +32,27 @@ class I03Constants:
32
32
 
33
33
 
34
34
  # These currently exist in GDA domain.properties
35
- class HyperionFeatureSettingSources(FeatureSettingSources):
35
+ class HyperionFeatureSettingsSources(FeatureSettingSources):
36
36
  USE_GPU_RESULTS = "gda.mx.hyperion.xrc.use_gpu_results"
37
37
  USE_PANDA_FOR_GRIDSCAN = "gda.mx.hyperion.use_panda_for_gridscans"
38
38
  SET_STUB_OFFSETS = "gda.mx.hyperion.do_stub_offsets"
39
39
  PANDA_RUNUP_DISTANCE_MM = "gda.mx.hyperion.panda_runup_distance_mm"
40
+ DETECTOR_DISTANCE_LIMIT_MAX_MM = "gda.detector.distance.limit.max"
41
+ DETECTOR_DISTANCE_LIMIT_MIN_MM = "gda.detector.distance.limit.min"
42
+ BEAMSTOP_DIODE_CHECK = "gda.mx.hyperion.enable_beamstop_diode_check"
40
43
 
41
44
 
42
45
  # Use these defaults if we can't read from the config server
43
46
  @dataclass
44
- class HyperionFeatureSetting(FeatureSetting):
47
+ class HyperionFeatureSettings(FeatureSettings):
45
48
  USE_GPU_RESULTS: bool = True
46
49
  USE_PANDA_FOR_GRIDSCAN: bool = False
47
50
  SET_STUB_OFFSETS: bool = False
48
51
  PANDA_RUNUP_DISTANCE_MM: float = 0.16
52
+ # From GDA mx-config hutch_utilities.py default values
53
+ DETECTOR_DISTANCE_LIMIT_MAX_MM: float = 700
54
+ DETECTOR_DISTANCE_LIMIT_MIN_MM: float = 250
55
+ BEAMSTOP_DIODE_CHECK: bool = False
49
56
 
50
57
 
51
58
  @dataclass(frozen=True)
@@ -16,7 +16,7 @@ from dodal.devices.flux import Flux
16
16
  from dodal.devices.robot import BartRobot
17
17
  from dodal.devices.s4_slit_gaps import S4SlitGaps
18
18
  from dodal.devices.synchrotron import Synchrotron
19
- from dodal.devices.undulator import Undulator
19
+ from dodal.devices.undulator import UndulatorInKeV
20
20
  from dodal.devices.xbpm_feedback import XBPMFeedback
21
21
  from dodal.devices.zebra.zebra import Zebra
22
22
  from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
@@ -41,7 +41,7 @@ class HyperionFlyScanXRayCentreComposite(FlyScanEssentialDevices):
41
41
  eiger: EigerDetector
42
42
  flux: Flux
43
43
  s4_slit_gaps: S4SlitGaps
44
- undulator: Undulator
44
+ undulator: UndulatorInKeV
45
45
  synchrotron: Synchrotron
46
46
  zebra: Zebra
47
47
  zocalo: ZocaloResults