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
mx_bluesky/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '1.5.11'
32
- __version_tuple__ = version_tuple = (1, 5, 11)
31
+ __version__ = version = '1.5.12'
32
+ __version_tuple__ = version_tuple = (1, 5, 12)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -0,0 +1,198 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+ from pathlib import Path
5
+
6
+ import bluesky.plan_stubs as bps
7
+ import bluesky.preprocessors as bpp
8
+ import pydantic
9
+ from dodal.devices.motors import XYZOmegaStage, XYZStage
10
+ from dodal.devices.oav.oav_detector import OAV
11
+ from dodal.devices.oav.pin_image_recognition import PinTipDetection
12
+ from dodal.devices.robot import BartRobot, SampleLocation
13
+
14
+ from mx_bluesky.beamlines.aithre_lasershaping.parameters.constants import CONST
15
+ from mx_bluesky.beamlines.aithre_lasershaping.parameters.robot_load_parameters import (
16
+ AithreRobotLoad,
17
+ )
18
+ from mx_bluesky.common.device_setup_plans.robot_load_unload import (
19
+ do_plan_while_lower_gonio_at_home,
20
+ )
21
+ from mx_bluesky.common.experiment_plans.pin_tip_centring_plan import (
22
+ PinTipCentringComposite,
23
+ pin_tip_centre_plan,
24
+ )
25
+ from mx_bluesky.common.parameters.constants import (
26
+ DocDescriptorNames,
27
+ PlanNameConstants,
28
+ )
29
+
30
+
31
+ @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
32
+ class RobotLoadComposite:
33
+ # RobotLoad fields
34
+ robot: BartRobot
35
+ lower_gonio: XYZStage
36
+ oav: OAV
37
+ gonio: XYZOmegaStage
38
+
39
+
40
+ def _move_gonio_to_home_position(
41
+ composite: RobotLoadComposite,
42
+ x_home: float = 0.0,
43
+ y_home: float = 0.0,
44
+ z_home: float = 0.0,
45
+ omega_home: float = 0.0,
46
+ group: str = "group",
47
+ ):
48
+ """
49
+ Move Gonio to home position, default is zero
50
+ """
51
+ yield from bps.abs_set(composite.gonio.omega, omega_home, group=group)
52
+ yield from bps.abs_set(composite.gonio.x, x_home, group=group)
53
+ yield from bps.abs_set(composite.gonio.y, y_home, group=group)
54
+ yield from bps.abs_set(composite.gonio.z, z_home, group=group)
55
+
56
+ yield from bps.wait(group=group)
57
+
58
+
59
+ def _take_robot_snapshots(oav: OAV, directory: Path):
60
+ time_now = datetime.now()
61
+ snapshot_format = f"{time_now.strftime('%H%M%S')}_{{device}}_after_load"
62
+ for device in [oav.snapshot]:
63
+ yield from bps.abs_set(
64
+ device.filename, snapshot_format.format(device=device.name)
65
+ )
66
+ yield from bps.abs_set(device.directory, str(directory))
67
+ # Note: should be able to use `wait=True` after https://github.com/bluesky/bluesky/issues/1795
68
+ yield from bps.trigger(device, group="snapshots")
69
+ yield from bps.wait("snapshots")
70
+
71
+
72
+ def _do_robot_load_and_centre(
73
+ composite: RobotLoadComposite,
74
+ sample_location: SampleLocation,
75
+ sample_id: int,
76
+ pin_tip_detection: PinTipDetection,
77
+ tip_offset_microns: float = 0,
78
+ oav_config_file: str = CONST.OAV_CENTRING_FILE,
79
+ ):
80
+ yield from bps.abs_set(composite.robot.next_sample_id, sample_id, wait=True)
81
+ yield from bps.abs_set(
82
+ composite.robot,
83
+ sample_location,
84
+ group="robot_load",
85
+ )
86
+
87
+ yield from _move_gonio_to_home_position(composite=composite, group="robot_load")
88
+
89
+ yield from bps.wait(group="robot_load")
90
+
91
+ pin_tip_centring_composite = PinTipCentringComposite(
92
+ composite.oav, composite.gonio, pin_tip_detection
93
+ )
94
+ yield from pin_tip_centre_plan(
95
+ pin_tip_centring_composite, tip_offset_microns, oav_config_file
96
+ )
97
+
98
+
99
+ def _robot_load_and_snapshots(
100
+ composite: RobotLoadComposite,
101
+ location: SampleLocation,
102
+ snapshot_directory: Path,
103
+ sample_id: int,
104
+ pin_tip_detection: PinTipDetection,
105
+ tip_offset_microns: float = 0,
106
+ oav_config_file: str = CONST.OAV_CENTRING_FILE,
107
+ ):
108
+ yield from bps.create(name=DocDescriptorNames.ROBOT_PRE_LOAD)
109
+ yield from bps.read(composite.robot)
110
+ yield from bps.save()
111
+
112
+ robot_load_plan = _do_robot_load_and_centre(
113
+ composite,
114
+ location,
115
+ sample_id,
116
+ pin_tip_detection,
117
+ tip_offset_microns,
118
+ oav_config_file,
119
+ )
120
+
121
+ gonio_finished = yield from do_plan_while_lower_gonio_at_home(
122
+ robot_load_plan, composite.lower_gonio
123
+ )
124
+ yield from bps.wait(group="snapshot")
125
+
126
+ yield from _take_robot_snapshots(composite.oav, snapshot_directory)
127
+
128
+ yield from bps.create(name=DocDescriptorNames.ROBOT_UPDATE)
129
+ yield from bps.read(composite.robot)
130
+ yield from bps.read(composite.oav.snapshot)
131
+ yield from bps.save()
132
+
133
+ yield from bps.wait(gonio_finished)
134
+
135
+
136
+ def robot_load_and_snapshots_plan(
137
+ composite: RobotLoadComposite,
138
+ params: AithreRobotLoad,
139
+ ptd: PinTipDetection,
140
+ tip_offset_microns: float = 0,
141
+ oav_config_file: str = CONST.OAV_CENTRING_FILE,
142
+ ):
143
+ assert params.sample_puck is not None
144
+ assert params.sample_pin is not None
145
+
146
+ sample_location = SampleLocation(params.sample_puck, params.sample_pin)
147
+
148
+ yield from _move_gonio_to_home_position(composite)
149
+
150
+ yield from bpp.set_run_key_wrapper(
151
+ bpp.run_wrapper(
152
+ _robot_load_and_snapshots(
153
+ composite,
154
+ sample_location,
155
+ params.snapshot_directory,
156
+ params.sample_id,
157
+ ptd,
158
+ tip_offset_microns,
159
+ oav_config_file,
160
+ ),
161
+ md={
162
+ "subplan_name": PlanNameConstants.ROBOT_LOAD,
163
+ "metadata": {"visit": params.visit, "sample_id": params.sample_id},
164
+ "activate_callbacks": [
165
+ "RobotLoadISPyBCallback",
166
+ ],
167
+ },
168
+ ),
169
+ PlanNameConstants.ROBOT_LOAD_AND_SNAPSHOTS,
170
+ )
171
+
172
+
173
+ def robot_unload_plan(
174
+ composite: RobotLoadComposite,
175
+ params: AithreRobotLoad,
176
+ ):
177
+ @bpp.run_decorator(
178
+ md={
179
+ "subplan_name": PlanNameConstants.ROBOT_UNLOAD,
180
+ "metadata": {"visit": params.visit, "sample_id": params.sample_id},
181
+ "activate_callbacks": [
182
+ "RobotLoadISPyBCallback",
183
+ ],
184
+ },
185
+ )
186
+ def do_robot_unload_and_send_to_ispyb():
187
+ yield from _take_robot_snapshots(composite.oav, params.snapshot_directory)
188
+ yield from bps.wait(group="snapshot")
189
+ yield from _move_gonio_to_home_position(composite)
190
+
191
+ yield from bps.abs_set(composite.robot, None, wait=True)
192
+
193
+ yield from bps.create(name=DocDescriptorNames.ROBOT_UPDATE)
194
+ yield from bps.read(composite.robot)
195
+ yield from bps.read(composite.oav.snapshot)
196
+ yield from bps.save()
197
+
198
+ yield from do_robot_unload_and_send_to_ispyb()
@@ -0,0 +1,17 @@
1
+ import os
2
+
3
+ from pydantic.dataclasses import dataclass
4
+
5
+ TEST_MODE = os.environ.get("AITHRE_TEST_MODE")
6
+
7
+
8
+ @dataclass(frozen=True)
9
+ class AithreConstants:
10
+ BEAMLINE = "aithre"
11
+ OAV_CENTRING_FILE = (
12
+ "/dls/science/groups/i23/aithre/daq_configuration/json/OAVCentring_aithre.json"
13
+ )
14
+ LOG_FILE_NAME = "aithre.log"
15
+
16
+
17
+ CONST = AithreConstants()
@@ -0,0 +1,13 @@
1
+ from mx_bluesky.common.parameters.components import (
2
+ WithSample,
3
+ WithSnapshot,
4
+ WithVisit,
5
+ )
6
+
7
+
8
+ class AithreRobotLoad(
9
+ WithSample,
10
+ WithSnapshot,
11
+ WithVisit,
12
+ ):
13
+ pass
@@ -0,0 +1,31 @@
1
+ from bluesky.utils import MsgGenerator
2
+ from dodal.common import inject
3
+ from dodal.devices.aithre_lasershaping.goniometer import Goniometer
4
+ from dodal.devices.oav.oav_detector import OAV
5
+ from dodal.devices.oav.pin_image_recognition import PinTipDetection
6
+
7
+ from mx_bluesky.beamlines.aithre_lasershaping.parameters.constants import CONST
8
+ from mx_bluesky.common.experiment_plans.pin_tip_centring_plan import (
9
+ PinTipCentringComposite,
10
+ pin_tip_centre_plan,
11
+ )
12
+
13
+
14
+ def aithre_pin_tip_centre(
15
+ oav: OAV = inject("OAV"),
16
+ gonio: Goniometer = inject("gonio"),
17
+ pin_tip_detection: PinTipDetection = inject("pin_tip_detection"),
18
+ tip_offset_microns: float = 0,
19
+ oav_config_file: str = CONST.OAV_CENTRING_FILE,
20
+ ) -> MsgGenerator:
21
+ """
22
+ A plan that use pin_tip_centre_plan from common for aithre
23
+ """
24
+
25
+ composite = PinTipCentringComposite(oav, gonio, pin_tip_detection)
26
+
27
+ yield from pin_tip_centre_plan(
28
+ composite=composite,
29
+ tip_offset_microns=tip_offset_microns,
30
+ oav_config_file=oav_config_file,
31
+ )
@@ -0,0 +1,80 @@
1
+ import datetime
2
+
3
+ from bluesky.utils import MsgGenerator
4
+ from dodal.common import inject
5
+ from dodal.devices.motors import XYZOmegaStage
6
+ from dodal.devices.oav.oav_detector import OAV
7
+ from dodal.devices.oav.pin_image_recognition import PinTipDetection
8
+ from dodal.devices.robot import BartRobot
9
+
10
+ from mx_bluesky.beamlines.aithre_lasershaping.experiment_plans.robot_load_plan import (
11
+ RobotLoadComposite,
12
+ robot_load_and_snapshots_plan,
13
+ robot_unload_plan,
14
+ )
15
+ from mx_bluesky.beamlines.aithre_lasershaping.parameters.constants import CONST
16
+ from mx_bluesky.beamlines.aithre_lasershaping.parameters.robot_load_parameters import (
17
+ AithreRobotLoad,
18
+ )
19
+
20
+
21
+ def robot_load_and_snapshot(
22
+ robot: BartRobot = inject("robot"),
23
+ gonio: XYZOmegaStage = inject("gonio"),
24
+ oav: OAV = inject("oav"),
25
+ ptd: PinTipDetection = inject("ptd"),
26
+ tip_offset_microns: float = 0,
27
+ oav_config_file: str = CONST.OAV_CENTRING_FILE,
28
+ sample_puck: int = 0,
29
+ sample_pin: int = 0,
30
+ sample_id: int = 0,
31
+ visit: str = "cm40645-5",
32
+ categories: str = "",
33
+ ) -> MsgGenerator:
34
+ """
35
+ categories: type of the sample, e.g. Germ, ProteinaseK, Screening
36
+ """
37
+ time_now = datetime.datetime.now()
38
+ year_now = str(time_now.year)
39
+ snapshot_directory = f"/dls/i23/data/{year_now}/{visit}/{categories}"
40
+ composite = RobotLoadComposite(robot, gonio, oav, gonio)
41
+ params = AithreRobotLoad(
42
+ sample_id=sample_id,
43
+ sample_puck=sample_puck,
44
+ sample_pin=sample_pin,
45
+ snapshot_directory=snapshot_directory,
46
+ visit=visit,
47
+ beamline="BL23I",
48
+ )
49
+
50
+ yield from robot_load_and_snapshots_plan(
51
+ composite, params, ptd, tip_offset_microns, oav_config_file
52
+ )
53
+
54
+
55
+ def robot_unload(
56
+ robot: BartRobot = inject("robot"),
57
+ gonio: XYZOmegaStage = inject("gonio"),
58
+ oav: OAV = inject("oav"),
59
+ sample_puck: int = 0,
60
+ sample_pin: int = 0,
61
+ sample_id: int = 0,
62
+ visit: str = "cm40645-5",
63
+ categories: str = "",
64
+ ) -> MsgGenerator:
65
+ """
66
+ categories: type of the sample, e.g. Germ, ProteinaseK, Screening
67
+ """
68
+ time_now = datetime.datetime.now()
69
+ year_now = str(time_now.year)
70
+ snapshot_directory = f"/dls/i23/data/{year_now}/{visit}/{categories}"
71
+ composite = RobotLoadComposite(robot, gonio, oav, gonio)
72
+ params = AithreRobotLoad(
73
+ sample_id=sample_id,
74
+ sample_puck=sample_puck,
75
+ sample_pin=sample_pin,
76
+ snapshot_directory=snapshot_directory,
77
+ visit=visit,
78
+ beamline="BL23I",
79
+ )
80
+ yield from robot_unload_plan(composite, params)
@@ -1,5 +1,8 @@
1
1
  from mx_bluesky.beamlines.i04.experiment_plans.i04_grid_detect_then_xray_centre_plan import (
2
- i04_grid_detect_then_xray_centre,
2
+ i04_default_grid_detect_and_xray_centre,
3
+ )
4
+ from mx_bluesky.beamlines.i04.oav_centering_plans.oav_imaging import (
5
+ take_oav_image_with_scintillator_in,
3
6
  )
4
7
  from mx_bluesky.beamlines.i04.thawing_plan import (
5
8
  thaw,
@@ -10,6 +13,7 @@ from mx_bluesky.beamlines.i04.thawing_plan import (
10
13
  __all__ = [
11
14
  "thaw",
12
15
  "thaw_and_stream_to_redis",
13
- "i04_grid_detect_then_xray_centre",
16
+ "i04_default_grid_detect_and_xray_centre",
14
17
  "thaw_and_murko_centre",
18
+ "take_oav_image_with_scintillator_in",
15
19
  ]
@@ -8,6 +8,8 @@ from dodal.log import LOGGER
8
8
  from event_model.documents import Event, RunStart, RunStop
9
9
  from redis import StrictRedis
10
10
 
11
+ FORWARDING_COMPLETE_MESSAGE = "image_forwarding_complete"
12
+
11
13
 
12
14
  class OmegaReading(TypedDict):
13
15
  value: float
@@ -56,22 +58,28 @@ class MurkoCallback(CallbackBase):
56
58
  self.previous_omegas: list[OmegaReading] = []
57
59
 
58
60
  def start(self, doc: RunStart) -> RunStart | None:
59
- self.sample_id = doc.get("sample_id")
60
- self.murko_metadata = {
61
- "zoom_percentage": doc.get("zoom_percentage"),
62
- "microns_per_x_pixel": doc.get("microns_per_x_pixel"),
63
- "microns_per_y_pixel": doc.get("microns_per_y_pixel"),
64
- "beam_centre_i": doc.get("beam_centre_i"),
65
- "beam_centre_j": doc.get("beam_centre_j"),
66
- "sample_id": self.sample_id,
67
- }
61
+ self.murko_metadata: dict = {"sample_id": doc.get("sample_id")}
68
62
  self.last_uuid = None
69
63
  self.previous_omegas = []
70
- LOGGER.info(f"Starting to stream metadata to murko under {self.sample_id}")
64
+ LOGGER.info(
65
+ f"Starting to stream metadata to murko under {self.murko_metadata['sample_id']}"
66
+ )
71
67
  return doc
72
68
 
73
69
  def event(self, doc: Event) -> Event:
74
- if latest_omega := doc["data"].get("smargon-omega"):
70
+ data = doc["data"]
71
+ for prefix in ("oav", "oav_full_screen"):
72
+ if f"{prefix}-beam_centre_j" in data:
73
+ self.murko_metadata.update(
74
+ {
75
+ "microns_per_x_pixel": data[f"{prefix}-microns_per_pixel_x"],
76
+ "microns_per_y_pixel": data[f"{prefix}-microns_per_pixel_y"],
77
+ "beam_centre_i": data[f"{prefix}-beam_centre_i"],
78
+ "beam_centre_j": data[f"{prefix}-beam_centre_j"],
79
+ }
80
+ )
81
+
82
+ if (latest_omega := data.get("smargon-omega")) is not None:
75
83
  if len(self.previous_omegas) <= 2 and self.last_uuid:
76
84
  # For the first few images there's not enough data to extrapolate so we
77
85
  # match them one to one
@@ -106,5 +114,12 @@ class MurkoCallback(CallbackBase):
106
114
  self.redis_client.publish("murko", json.dumps(metadata))
107
115
 
108
116
  def stop(self, doc: RunStop) -> RunStop | None:
109
- LOGGER.info(f"Finished streaming {self.sample_id} to murko")
117
+ LOGGER.info(f"Finished streaming {self.murko_metadata['sample_id']} to murko")
118
+ LOGGER.info(
119
+ f"Publishing forwarding complete message: {FORWARDING_COMPLETE_MESSAGE}"
120
+ )
121
+ self.redis_client.publish(
122
+ "murko",
123
+ json.dumps(FORWARDING_COMPLETE_MESSAGE),
124
+ )
110
125
  return doc
@@ -17,6 +17,7 @@ from dodal.devices.fast_grid_scan import (
17
17
  set_fast_grid_scan_params,
18
18
  )
19
19
  from dodal.devices.flux import Flux
20
+ from dodal.devices.i04.beamsize import Beamsize
20
21
  from dodal.devices.i04.transfocator import Transfocator
21
22
  from dodal.devices.mx_phase1.beamstop import Beamstop
22
23
  from dodal.devices.oav.oav_detector import OAV
@@ -25,7 +26,7 @@ from dodal.devices.robot import BartRobot
25
26
  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
- from dodal.devices.undulator import Undulator
29
+ from dodal.devices.undulator import UndulatorInKeV
29
30
  from dodal.devices.xbpm_feedback import XBPMFeedback
30
31
  from dodal.devices.zebra.zebra import Zebra
31
32
  from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
@@ -33,7 +34,11 @@ from dodal.devices.zocalo import ZocaloResults
33
34
  from dodal.plans.preprocessors.verify_undulator_gap import (
34
35
  verify_undulator_gap_before_run_decorator,
35
36
  )
37
+ from pydantic import BaseModel
36
38
 
39
+ from mx_bluesky.beamlines.i04.external_interaction.config_server import (
40
+ get_i04_config_client,
41
+ )
37
42
  from mx_bluesky.common.device_setup_plans.setup_zebra_and_shutter import (
38
43
  setup_zebra_for_gridscan,
39
44
  tidy_up_zebra_after_gridscan,
@@ -58,6 +63,7 @@ from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback
58
63
  from mx_bluesky.common.external_interaction.callbacks.xray_centre.nexus_callback import (
59
64
  GridscanNexusFileCallback,
60
65
  )
66
+ from mx_bluesky.common.parameters.components import PARAMETER_VERSION
61
67
  from mx_bluesky.common.parameters.constants import (
62
68
  EnvironmentConstants,
63
69
  OavConstants,
@@ -67,13 +73,28 @@ from mx_bluesky.common.parameters.constants import (
67
73
  from mx_bluesky.common.parameters.device_composites import (
68
74
  GridDetectThenXRayCentreComposite,
69
75
  )
70
- from mx_bluesky.common.parameters.gridscan import GridCommon, SpecifiedThreeDGridScan
76
+ from mx_bluesky.common.parameters.gridscan import (
77
+ GridCommon,
78
+ SpecifiedThreeDGridScan,
79
+ )
71
80
  from mx_bluesky.common.preprocessors.preprocessors import (
72
81
  transmission_and_xbpm_feedback_for_collection_decorator,
73
82
  )
83
+ from mx_bluesky.common.utils.exceptions import CrystalNotFoundError
74
84
  from mx_bluesky.common.utils.log import LOGGER
85
+ from mx_bluesky.common.utils.utils import (
86
+ fix_transmission_and_exposure_time_for_current_wavelength,
87
+ )
75
88
 
76
- DEFAULT_BEAMSIZE_MICRONS = 20
89
+ DEFAULT_XRC_BEAMSIZE_MICRONS = 20
90
+
91
+
92
+ class I04AutoXrcParams(BaseModel):
93
+ sample_id: int
94
+ file_name: str
95
+ visit: str
96
+ detector_distance_mm: float
97
+ storage_directory: str
77
98
 
78
99
 
79
100
  def _change_beamsize(
@@ -91,19 +112,20 @@ def _change_beamsize(
91
112
 
92
113
 
93
114
  # See https://github.com/DiamondLightSource/blueapi/issues/506 for using device composites
94
- def i04_grid_detect_then_xray_centre(
95
- parameters: GridCommon,
115
+ def i04_default_grid_detect_and_xray_centre(
116
+ parameters: I04AutoXrcParams,
96
117
  aperture_scatterguard: ApertureScatterguard = inject("aperture_scatterguard"),
97
118
  attenuator: BinaryFilterAttenuator = inject("attenuator"),
98
119
  backlight: Backlight = inject("backlight"),
99
120
  beamstop: Beamstop = inject("beamstop"),
121
+ beamsize: Beamsize = inject("beamsize"),
100
122
  dcm: DoubleCrystalMonochromator = inject("dcm"),
101
123
  zebra_fast_grid_scan: ZebraFastGridScanThreeD = inject("zebra_fast_grid_scan"),
102
124
  flux: Flux = inject("flux"),
103
125
  oav: OAV = inject("oav"),
104
126
  pin_tip_detection: PinTipDetection = inject("pin_tip_detection"),
105
127
  s4_slit_gaps: S4SlitGaps = inject("s4_slit_gaps"),
106
- undulator: Undulator = inject("undulator"),
128
+ undulator: UndulatorInKeV = inject("undulator"),
107
129
  xbpm_feedback: XBPMFeedback = inject("xbpm_feedback"),
108
130
  zebra: Zebra = inject("zebra"),
109
131
  robot: BartRobot = inject("robot"),
@@ -124,7 +146,6 @@ def i04_grid_detect_then_xray_centre(
124
146
  - Changes the aperture to match the beam size to the crystal size
125
147
  - Moves the sample to the crystal centre of mass
126
148
 
127
-
128
149
  i04's implementation of this plan is very similar to Hyperion. However, since i04
129
150
  isn't running in a continuous Bluesky UDC loop, we take additional steps in beamline
130
151
  tidy-up.
@@ -139,6 +160,7 @@ def i04_grid_detect_then_xray_centre(
139
160
  attenuator,
140
161
  backlight,
141
162
  beamstop,
163
+ beamsize,
142
164
  dcm,
143
165
  detector_motion,
144
166
  zebra_fast_grid_scan,
@@ -152,9 +174,18 @@ def i04_grid_detect_then_xray_centre(
152
174
  robot,
153
175
  sample_shutter,
154
176
  )
155
- initial_beamsize = yield from bps.rd(transfocator.beamsize_set_microns)
177
+ initial_beamsize = yield from bps.rd(transfocator.current_vertical_size_rbv)
178
+
179
+ initial_x = yield from bps.rd(smargon.x.user_readback)
180
+ initial_y = yield from bps.rd(smargon.y.user_readback)
181
+ initial_z = yield from bps.rd(smargon.z.user_readback)
182
+
183
+ _current_wavelength_a = yield from bps.rd(composite.dcm.wavelength_in_a)
184
+ grid_common_params = _get_grid_common_params(_current_wavelength_a, parameters)
156
185
 
157
186
  def tidy_beamline():
187
+ yield from bps.mv(transfocator, initial_beamsize)
188
+
158
189
  if not udc:
159
190
  yield from get_ready_for_oav_and_close_shutter(
160
191
  composite.smargon,
@@ -162,7 +193,6 @@ def i04_grid_detect_then_xray_centre(
162
193
  composite.aperture_scatterguard,
163
194
  composite.detector_motion,
164
195
  )
165
- yield from bps.mv(transfocator, initial_beamsize)
166
196
 
167
197
  @bpp.finalize_decorator(tidy_beamline)
168
198
  def _inner_grid_detect_then_xrc():
@@ -174,20 +204,30 @@ def i04_grid_detect_then_xray_centre(
174
204
  @bpp.subs_decorator(callbacks)
175
205
  @verify_undulator_gap_before_run_decorator(composite)
176
206
  @transmission_and_xbpm_feedback_for_collection_decorator(
177
- composite, parameters.transmission_frac, PlanNameConstants.GRIDSCAN_OUTER
207
+ composite,
208
+ grid_common_params.transmission_frac,
209
+ PlanNameConstants.GRIDSCAN_OUTER,
178
210
  )
179
211
  def grid_detect_then_xray_centre_with_callbacks():
180
212
  yield from grid_detect_then_xray_centre(
181
213
  composite=composite,
182
- parameters=parameters,
214
+ parameters=grid_common_params,
183
215
  xrc_params_type=SpecifiedThreeDGridScan,
184
216
  construct_beamline_specific=construct_i04_specific_features,
185
217
  oav_config=oav_config,
186
218
  )
187
219
 
188
- yield from grid_detect_then_xray_centre_with_callbacks()
220
+ try:
221
+ yield from grid_detect_then_xray_centre_with_callbacks()
222
+ except CrystalNotFoundError:
223
+ yield from bps.mv(
224
+ smargon.x, initial_x, smargon.y, initial_y, smargon.z, initial_z
225
+ )
226
+ raise
189
227
 
190
- yield from _change_beamsize(transfocator, DEFAULT_BEAMSIZE_MICRONS, parameters)
228
+ yield from _change_beamsize(
229
+ transfocator, DEFAULT_XRC_BEAMSIZE_MICRONS, grid_common_params
230
+ )
191
231
  yield from _inner_grid_detect_then_xrc()
192
232
 
193
233
 
@@ -278,3 +318,37 @@ def construct_i04_specific_features(
278
318
  signals_to_read_during_collection,
279
319
  get_xrc_results_from_zocalo=True,
280
320
  )
321
+
322
+
323
+ def _get_grid_common_params(
324
+ _current_wavelength_a: float, parameters: I04AutoXrcParams
325
+ ) -> GridCommon:
326
+ """Calculate scaled transmission and exposure by comparing current beamline energy to default energy"""
327
+ _assumed_wavelength_a = (
328
+ get_i04_config_client().get_feature_flags().ASSUMED_WAVELENGTH_IN_A
329
+ )
330
+ _unscaled_transmission = (
331
+ get_i04_config_client().get_feature_flags().XRC_UNSCALED_TRANSMISSION_FRAC
332
+ )
333
+ _unscaled_exposure_time_s = (
334
+ get_i04_config_client().get_feature_flags().XRC_UNSCALED_EXPOSURE_TIME_S
335
+ )
336
+ transmission_frac, exposure_time_s = (
337
+ fix_transmission_and_exposure_time_for_current_wavelength(
338
+ _current_wavelength_a,
339
+ _assumed_wavelength_a,
340
+ _unscaled_transmission,
341
+ _unscaled_exposure_time_s,
342
+ )
343
+ )
344
+
345
+ return GridCommon(
346
+ sample_id=parameters.sample_id,
347
+ file_name=parameters.file_name,
348
+ visit=parameters.visit,
349
+ detector_distance_mm=parameters.detector_distance_mm,
350
+ storage_directory=parameters.storage_directory,
351
+ transmission_frac=transmission_frac,
352
+ exposure_time_s=exposure_time_s,
353
+ parameter_model_version=PARAMETER_VERSION,
354
+ )
@@ -0,0 +1,15 @@
1
+ from functools import cache
2
+
3
+ from mx_bluesky.beamlines.i04.parameters.constants import (
4
+ I04FeatureSettings,
5
+ I04FeatureSettingsSources,
6
+ )
7
+ from mx_bluesky.common.external_interaction.config_server import MXConfigClient
8
+
9
+
10
+ @cache
11
+ def get_i04_config_client() -> MXConfigClient[I04FeatureSettings]:
12
+ return MXConfigClient(
13
+ feature_sources=I04FeatureSettingsSources,
14
+ feature_dc=I04FeatureSettings,
15
+ )