mx-bluesky 1.2.0__py3-none-any.whl → 1.4.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. mx_bluesky/__init__.py +8 -3
  2. mx_bluesky/__main__.py +12 -7
  3. mx_bluesky/_version.py +2 -2
  4. mx_bluesky/beamlines/i04/callbacks/murko_callback.py +14 -4
  5. mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +178 -0
  6. mx_bluesky/beamlines/i04/thawing_plan.py +49 -11
  7. mx_bluesky/beamlines/i24/serial/__init__.py +3 -0
  8. mx_bluesky/beamlines/i24/serial/dcid.py +143 -171
  9. mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +1 -1
  10. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +121 -110
  11. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +3 -6
  12. mx_bluesky/beamlines/i24/serial/fixed_target/ft_utils.py +0 -1
  13. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +164 -169
  14. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +149 -225
  15. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +7 -216
  16. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +18 -17
  17. mx_bluesky/beamlines/i24/serial/log.py +58 -49
  18. mx_bluesky/beamlines/i24/serial/parameters/__init__.py +4 -0
  19. mx_bluesky/beamlines/i24/serial/parameters/constants.py +6 -1
  20. mx_bluesky/beamlines/i24/serial/parameters/experiment_parameters.py +42 -15
  21. mx_bluesky/beamlines/i24/serial/parameters/fixed_target/cs/cs_maker.json +3 -3
  22. mx_bluesky/beamlines/i24/serial/run_extruder.sh +30 -5
  23. mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +30 -5
  24. mx_bluesky/beamlines/i24/serial/run_serial.py +24 -8
  25. mx_bluesky/beamlines/i24/serial/setup_beamline/ca.py +0 -2
  26. mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +2 -0
  27. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +104 -82
  28. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +9 -20
  29. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +26 -28
  30. mx_bluesky/beamlines/i24/serial/write_nexus.py +74 -72
  31. mx_bluesky/common/__init__.py +0 -0
  32. mx_bluesky/common/device_setup_plans/read_hardware_for_setup.py +14 -0
  33. mx_bluesky/common/external_interaction/config_server.py +46 -0
  34. mx_bluesky/common/parameters/components.py +258 -0
  35. mx_bluesky/common/parameters/constants.py +143 -0
  36. mx_bluesky/common/parameters/gridscan.py +94 -0
  37. mx_bluesky/common/parameters/robot_load.py +16 -0
  38. mx_bluesky/common/plans/__init__.py +1 -0
  39. mx_bluesky/common/plans/do_fgs.py +121 -0
  40. mx_bluesky/common/utils/log.py +118 -0
  41. mx_bluesky/{hyperion → common/utils}/tracing.py +2 -2
  42. mx_bluesky/hyperion/__main__.py +13 -10
  43. mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +47 -52
  44. mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +6 -12
  45. mx_bluesky/hyperion/device_setup_plans/setup_oav.py +6 -12
  46. mx_bluesky/hyperion/device_setup_plans/setup_panda.py +5 -6
  47. mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +49 -18
  48. mx_bluesky/hyperion/device_setup_plans/smargon.py +9 -9
  49. mx_bluesky/hyperion/device_setup_plans/utils.py +2 -2
  50. mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +4 -4
  51. mx_bluesky/hyperion/exceptions.py +13 -1
  52. mx_bluesky/hyperion/experiment_plans/__init__.py +4 -0
  53. mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +83 -0
  54. mx_bluesky/hyperion/experiment_plans/common/xrc_result.py +47 -0
  55. mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -9
  56. mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +147 -169
  57. mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +48 -22
  58. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +75 -9
  59. mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +21 -20
  60. mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +9 -6
  61. mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +2 -2
  62. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +40 -21
  63. mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +22 -22
  64. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +43 -39
  65. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +69 -18
  66. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +17 -7
  67. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +13 -13
  68. mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +0 -4
  69. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +5 -2
  70. mx_bluesky/hyperion/external_interaction/callbacks/common/abstract_event.py +66 -0
  71. mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +5 -0
  72. mx_bluesky/hyperion/external_interaction/callbacks/common/ispyb_mapping.py +1 -1
  73. mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +30 -25
  74. mx_bluesky/hyperion/external_interaction/callbacks/ispyb_callback_base.py +29 -12
  75. mx_bluesky/hyperion/external_interaction/callbacks/log_uid_tag_callback.py +1 -1
  76. mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +19 -11
  77. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +7 -4
  78. mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +5 -3
  79. mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/__init__.py +0 -0
  80. mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/sample_handling_callback.py +84 -0
  81. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +38 -27
  82. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +5 -4
  83. mx_bluesky/hyperion/external_interaction/config_server.py +11 -28
  84. mx_bluesky/hyperion/external_interaction/exceptions.py +0 -9
  85. mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +65 -15
  86. mx_bluesky/hyperion/external_interaction/ispyb/ispyb_store.py +1 -1
  87. mx_bluesky/hyperion/external_interaction/nexus/nexus_utils.py +2 -2
  88. mx_bluesky/hyperion/external_interaction/nexus/write_nexus.py +1 -1
  89. mx_bluesky/hyperion/log.py +0 -84
  90. mx_bluesky/hyperion/parameters/components.py +4 -251
  91. mx_bluesky/hyperion/parameters/constants.py +22 -119
  92. mx_bluesky/hyperion/parameters/gridscan.py +35 -74
  93. mx_bluesky/hyperion/parameters/load_centre_collect.py +16 -11
  94. mx_bluesky/hyperion/parameters/rotation.py +23 -10
  95. mx_bluesky/hyperion/utils/utils.py +17 -0
  96. mx_bluesky/hyperion/utils/validation.py +5 -6
  97. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/METADATA +36 -33
  98. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/RECORD +102 -89
  99. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/WHEEL +1 -1
  100. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +0 -161
  101. mx_bluesky/example.py +0 -19
  102. mx_bluesky/hyperion/parameters/robot_load.py +0 -16
  103. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/LICENSE +0 -0
  104. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/entry_points.txt +0 -0
  105. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/top_level.txt +0 -0
mx_bluesky/__init__.py CHANGED
@@ -1,6 +1,11 @@
1
- from importlib.metadata import version
1
+ """Top level API.
2
2
 
3
- __version__ = version("mx_bluesky")
4
- del version
3
+ .. data:: __version__
4
+ :type: str
5
+
6
+ Version number as calculated by https://github.com/pypa/setuptools_scm
7
+ """
8
+
9
+ from ._version import __version__
5
10
 
6
11
  __all__ = ["__version__"]
mx_bluesky/__main__.py CHANGED
@@ -1,19 +1,24 @@
1
- from argparse import ArgumentParser
1
+ """Interface for ``python -m mx_bluesky``."""
2
2
 
3
- from mx_bluesky.example import run_plan
3
+ from argparse import ArgumentParser
4
+ from collections.abc import Sequence
4
5
 
5
6
  from . import __version__
6
7
 
7
8
  __all__ = ["main"]
8
9
 
9
10
 
10
- def main(args=None):
11
+ def main(args: Sequence[str] | None = None) -> None:
12
+ """Argument parser for the CLI."""
11
13
  parser = ArgumentParser()
12
- parser.add_argument("-v", "--version", action="version", version=__version__)
13
- args = parser.parse_args(args)
14
- run_plan()
14
+ parser.add_argument(
15
+ "-v",
16
+ "--version",
17
+ action="version",
18
+ version=__version__,
19
+ )
20
+ parser.parse_args(args)
15
21
 
16
22
 
17
- # test with: python -m mx_bluesky
18
23
  if __name__ == "__main__":
19
24
  main()
mx_bluesky/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.2.0'
16
- __version_tuple__ = version_tuple = (1, 2, 0)
15
+ __version__ = version = '1.4.1'
16
+ __version_tuple__ = version_tuple = (1, 4, 1)
@@ -1,13 +1,16 @@
1
1
  import copy
2
2
  import json
3
+ from datetime import timedelta
3
4
 
4
5
  from bluesky.callbacks import CallbackBase
5
6
  from dodal.log import LOGGER
6
- from event_model.documents import Event, RunStart
7
+ from event_model.documents import Event, RunStart, RunStop
7
8
  from redis import StrictRedis
8
9
 
9
10
 
10
11
  class MurkoCallback(CallbackBase):
12
+ DATA_EXPIRY_DAYS = 7
13
+
11
14
  def __init__(self, redis_host: str, redis_password: str, redis_db: int = 0):
12
15
  self.redis_client = StrictRedis(
13
16
  host=redis_host, password=redis_password, db=redis_db
@@ -15,15 +18,17 @@ class MurkoCallback(CallbackBase):
15
18
  self.last_uuid = None
16
19
 
17
20
  def start(self, doc: RunStart) -> RunStart | None:
21
+ self.sample_id = doc.get("sample_id")
18
22
  self.murko_metadata = {
19
23
  "zoom_percentage": doc.get("zoom_percentage"),
20
24
  "microns_per_x_pixel": doc.get("microns_per_x_pixel"),
21
25
  "microns_per_y_pixel": doc.get("microns_per_y_pixel"),
22
26
  "beam_centre_i": doc.get("beam_centre_i"),
23
27
  "beam_centre_j": doc.get("beam_centre_j"),
24
- "sample_id": doc.get("sample_id"),
28
+ "sample_id": self.sample_id,
25
29
  }
26
30
  self.last_uuid = None
31
+ LOGGER.info(f"Starting to stream metadata to murko under {self.sample_id}")
27
32
  return doc
28
33
 
29
34
  def event(self, doc: Event) -> Event:
@@ -40,6 +45,11 @@ class MurkoCallback(CallbackBase):
40
45
  metadata["uuid"] = uuid
41
46
 
42
47
  # Send metadata to REDIS and trigger murko
43
- self.redis_client.hset("test-metadata", uuid, json.dumps(metadata))
48
+ redis_key = f"murko:{metadata['sample_id']}:metadata"
49
+ self.redis_client.hset(redis_key, uuid, json.dumps(metadata))
50
+ self.redis_client.expire(redis_key, timedelta(days=self.DATA_EXPIRY_DAYS))
44
51
  self.redis_client.publish("murko", json.dumps(metadata))
45
- LOGGER.info("Metadata sent to redis")
52
+
53
+ def stop(self, doc: RunStop) -> RunStop | None:
54
+ LOGGER.info(f"Finished streaming {self.sample_id} to murko")
55
+ return doc
@@ -0,0 +1,178 @@
1
+ import io
2
+ import json
3
+ import pickle
4
+ from typing import TypedDict
5
+
6
+ import numpy as np
7
+ import zmq
8
+ from dodal.beamlines.i04 import MURKO_REDIS_DB, REDIS_HOST, REDIS_PASSWORD
9
+ from numpy.typing import NDArray
10
+ from PIL import Image
11
+ from redis import StrictRedis
12
+
13
+ from mx_bluesky.common.utils.log import LOGGER
14
+
15
+ MURKO_ADDRESS = "tcp://i04-murko-prod.diamond.ac.uk:8008"
16
+
17
+
18
+ class MurkoRequest(TypedDict):
19
+ to_predict: NDArray
20
+ model_img_size: tuple[int, int]
21
+ save: bool
22
+ min_size: int
23
+ description: list
24
+ prefix: list[str]
25
+
26
+
27
+ def get_image_size(image: NDArray) -> tuple[int, int]:
28
+ """Returns the width and height of a numpy image"""
29
+ return image.shape[1], image.shape[0]
30
+
31
+
32
+ def send_to_murko_and_get_results(request: MurkoRequest) -> dict:
33
+ LOGGER.info(f"Sending {request['prefix']} to murko")
34
+ context = zmq.Context()
35
+ socket = context.socket(zmq.REQ)
36
+ socket.connect(MURKO_ADDRESS)
37
+ socket.send(pickle.dumps(request))
38
+ raw_results = socket.recv()
39
+ results = pickle.loads(raw_results)
40
+ LOGGER.info(f"Got {len(results['descriptions'])} results")
41
+ return results
42
+
43
+
44
+ def correlate_results_to_uuids(request: MurkoRequest, murko_results: dict) -> list:
45
+ results = []
46
+ uuids = request["prefix"]
47
+
48
+ width, height = get_image_size(request["to_predict"][0])
49
+
50
+ for uuid, prediction in zip(uuids, murko_results["descriptions"], strict=False):
51
+ coords = prediction["most_likely_click"]
52
+ y_coord = coords[0] * height
53
+ x_coord = coords[1] * width
54
+ results.append(
55
+ {"uuid": uuid, "x_pixel_coord": x_coord, "y_pixel_coord": y_coord}
56
+ )
57
+ return results
58
+
59
+
60
+ class BatchMurkoForwarder:
61
+ def __init__(self, redis_client: StrictRedis, batch_size: int):
62
+ """
63
+ Holds image data streamed from redis and forwards it to murko when:
64
+ * A set number have been received
65
+ * The shape of the images changes
66
+ * When `flush` is called
67
+
68
+ Once data has been forwarded this will then wait on the results and put them
69
+ back in redis.
70
+
71
+ Args:
72
+ redis_client: The client to send murko results back to redis.
73
+ batch_size: How many results to accumulate until they are flushed to redis.
74
+ """
75
+ self.redis_client = redis_client
76
+ self.batch_size = batch_size
77
+ self._uuids_and_images: dict[str, NDArray] = {}
78
+ self._last_image_size: tuple[int, int] | None = None
79
+ self._last_sample_id = ""
80
+
81
+ def _handle_batch_of_images(self, sample_id, images, uuids):
82
+ request_arguments: MurkoRequest = {
83
+ "model_img_size": (256, 320),
84
+ "to_predict": np.array(images),
85
+ "save": False,
86
+ "min_size": 64,
87
+ "description": [
88
+ "foreground",
89
+ "crystal",
90
+ "loop_inside",
91
+ "loop",
92
+ ["crystal", "loop"],
93
+ ["crystal", "loop", "stem"],
94
+ ],
95
+ "prefix": uuids,
96
+ }
97
+ predictions = send_to_murko_and_get_results(request_arguments)
98
+ results = correlate_results_to_uuids(request_arguments, predictions)
99
+ self._send_murko_results_to_redis(sample_id, results)
100
+
101
+ def _send_murko_results_to_redis(self, sample_id: str, results: list):
102
+ for result in results:
103
+ self.redis_client.hset(
104
+ f"murko:{sample_id}:results", result["uuid"], json.dumps(result)
105
+ )
106
+ self.redis_client.publish("murko-results", json.dumps(results))
107
+
108
+ def add(self, sample_id: str, uuid: str, image: NDArray):
109
+ """Add an image to the batch to send to murko."""
110
+ image_size = get_image_size(image)
111
+ self._last_sample_id = sample_id
112
+ if self._last_image_size and self._last_image_size != image_size:
113
+ self.flush()
114
+ self._uuids_and_images[uuid] = image
115
+ self._last_image_size = image_size
116
+ if len(self._uuids_and_images.keys()) >= self.batch_size:
117
+ self.flush()
118
+
119
+ def flush(self):
120
+ """Flush the batch to murko."""
121
+ if self._uuids_and_images:
122
+ self._handle_batch_of_images(
123
+ self._last_sample_id,
124
+ list(self._uuids_and_images.values()),
125
+ list(self._uuids_and_images.keys()),
126
+ )
127
+ self._uuids_and_images = {}
128
+ self._last_image_size = None
129
+
130
+
131
+ class RedisListener:
132
+ TIMEOUT_S = 2
133
+
134
+ def __init__(
135
+ self,
136
+ redis_host=REDIS_HOST,
137
+ redis_password=REDIS_PASSWORD,
138
+ db=MURKO_REDIS_DB,
139
+ redis_channel="murko",
140
+ ):
141
+ self.redis_client = StrictRedis(
142
+ host=redis_host,
143
+ password=redis_password,
144
+ db=db,
145
+ )
146
+ self.pubsub = self.redis_client.pubsub()
147
+ self.channel = redis_channel
148
+ self.forwarder = BatchMurkoForwarder(self.redis_client, 10)
149
+
150
+ def _get_and_handle_message(self):
151
+ message = self.pubsub.get_message(timeout=self.TIMEOUT_S)
152
+ if message and message["type"] == "message":
153
+ data = json.loads(message["data"])
154
+ LOGGER.info(f"Received from redis: {data}")
155
+ uuid = data["uuid"]
156
+ sample_id = data["sample_id"]
157
+
158
+ # Images are put in redis as raw jpeg bytes, murko needs numpy arrays
159
+ raw_image = self.redis_client.hget(f"murko:{sample_id}:raw", uuid)
160
+ assert isinstance(raw_image, bytes)
161
+ image = Image.open(io.BytesIO(raw_image))
162
+ image = np.asarray(image)
163
+
164
+ self.forwarder.add(sample_id, uuid, image)
165
+
166
+ elif not message:
167
+ self.forwarder.flush()
168
+
169
+ def listen_for_image_data_forever(self):
170
+ self.pubsub.subscribe(self.channel)
171
+
172
+ while True:
173
+ self._get_and_handle_message()
174
+
175
+
176
+ if __name__ == "__main__":
177
+ client = RedisListener()
178
+ client.listen_for_image_data_forever()
@@ -1,11 +1,13 @@
1
+ from collections.abc import Callable
2
+
1
3
  import bluesky.plan_stubs as bps
2
4
  import bluesky.preprocessors as bpp
3
5
  from bluesky.preprocessors import run_decorator, subs_decorator
4
- from dls_bluesky_core.core import MsgGenerator
6
+ from bluesky.utils import MsgGenerator
5
7
  from dodal.beamlines.i04 import MURKO_REDIS_DB, REDIS_HOST, REDIS_PASSWORD
6
8
  from dodal.common import inject
7
9
  from dodal.devices.oav.oav_detector import OAV
8
- from dodal.devices.oav.oav_to_redis_forwarder import OAVToRedisForwarder
10
+ from dodal.devices.oav.oav_to_redis_forwarder import OAVToRedisForwarder, Source
9
11
  from dodal.devices.robot import BartRobot
10
12
  from dodal.devices.smargon import Smargon
11
13
  from dodal.devices.thawer import Thawer, ThawerStates
@@ -22,31 +24,62 @@ def thaw_and_stream_to_redis(
22
24
  oav: OAV = inject("oav"),
23
25
  oav_to_redis_forwarder: OAVToRedisForwarder = inject("oav_to_redis_forwarder"),
24
26
  ) -> MsgGenerator:
25
- zoom_percentage = yield from bps.rd(oav.zoom_controller.percentage)
27
+ zoom_percentage = yield from bps.rd(oav.zoom_controller.percentage) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
26
28
  sample_id = yield from bps.rd(robot.sample_id)
27
29
 
28
- yield from bps.abs_set(oav.zoom_controller.level, "1.0x", wait=True)
29
- yield from bps.abs_set(oav_to_redis_forwarder.sample_id, sample_id)
30
+ sample_id = int(sample_id)
31
+ zoom_level_before_thawing = yield from bps.rd(oav.zoom_controller.level) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
32
+
33
+ yield from bps.mv(oav.zoom_controller.level, "1.0x") # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
34
+
35
+ def switch_forwarder_to_ROI() -> MsgGenerator:
36
+ yield from bps.complete(oav_to_redis_forwarder, wait=True)
37
+ yield from bps.mv(
38
+ # See: https://github.com/bluesky/bluesky/issues/1809
39
+ oav_to_redis_forwarder.selected_source, # type: ignore
40
+ Source.ROI.value, # type: ignore
41
+ )
42
+ yield from bps.kickoff(oav_to_redis_forwarder, wait=True)
43
+
44
+ microns_per_pixel_x = yield from bps.rd(oav.microns_per_pixel_x)
45
+ microns_per_pixel_y = yield from bps.rd(oav.microns_per_pixel_y)
46
+ beam_centre_i = yield from bps.rd(oav.beam_centre_i)
47
+ beam_centre_j = yield from bps.rd(oav.beam_centre_j)
30
48
 
31
49
  @subs_decorator(MurkoCallback(REDIS_HOST, REDIS_PASSWORD, MURKO_REDIS_DB))
32
50
  @run_decorator(
33
51
  md={
34
- "microns_per_x_pixel": oav.parameters.micronsPerXPixel,
35
- "microns_per_y_pixel": oav.parameters.micronsPerYPixel,
36
- "beam_centre_i": oav.parameters.beam_centre_i,
37
- "beam_centre_j": oav.parameters.beam_centre_j,
52
+ "microns_per_x_pixel": microns_per_pixel_x,
53
+ "microns_per_y_pixel": microns_per_pixel_y,
54
+ "beam_centre_i": beam_centre_i,
55
+ "beam_centre_j": beam_centre_j,
38
56
  "zoom_percentage": zoom_percentage,
39
57
  "sample_id": sample_id,
40
58
  }
41
59
  )
42
60
  def _thaw_and_stream_to_redis():
61
+ yield from bps.mv(
62
+ oav_to_redis_forwarder.sample_id, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
63
+ sample_id, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
64
+ oav_to_redis_forwarder.selected_source, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
65
+ Source.FULL_SCREEN.value, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
66
+ )
67
+
43
68
  yield from bps.kickoff(oav_to_redis_forwarder, wait=True)
44
69
  yield from bps.monitor(smargon.omega.user_readback, name="smargon")
45
70
  yield from bps.monitor(oav_to_redis_forwarder.uuid, name="oav")
46
- yield from thaw(time_to_thaw, rotation, thawer, smargon)
71
+ yield from thaw(
72
+ time_to_thaw, rotation, thawer, smargon, switch_forwarder_to_ROI
73
+ )
47
74
  yield from bps.complete(oav_to_redis_forwarder)
48
75
 
49
- yield from _thaw_and_stream_to_redis()
76
+ def cleanup():
77
+ yield from bps.mv(oav.zoom_controller.level, zoom_level_before_thawing) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
78
+
79
+ yield from bpp.contingency_wrapper(
80
+ _thaw_and_stream_to_redis(),
81
+ final_plan=cleanup,
82
+ )
50
83
 
51
84
 
52
85
  def thaw(
@@ -54,6 +87,7 @@ def thaw(
54
87
  rotation: float = 360,
55
88
  thawer: Thawer = inject("thawer"),
56
89
  smargon: Smargon = inject("smargon"),
90
+ plan_between_rotations: Callable[[], MsgGenerator] | None = None,
57
91
  ) -> MsgGenerator:
58
92
  """Rotates the sample and thaws it at the same time.
59
93
 
@@ -64,6 +98,8 @@ def thaw(
64
98
  thawer (Thawer, optional): The thawing device. Defaults to inject("thawer").
65
99
  smargon (Smargon, optional): The smargon used to rotate.
66
100
  Defaults to inject("smargon")
101
+ plan_between_rotations (MsgGenerator, optional): A plan to run between rotations
102
+ of the smargon. Defaults to no plan.
67
103
  """
68
104
  inital_velocity = yield from bps.rd(smargon.omega.velocity)
69
105
  new_velocity = abs(rotation / time_to_thaw) * 2.0
@@ -72,6 +108,8 @@ def thaw(
72
108
  yield from bps.abs_set(smargon.omega.velocity, new_velocity, wait=True)
73
109
  yield from bps.abs_set(thawer.control, ThawerStates.ON, wait=True)
74
110
  yield from bps.rel_set(smargon.omega, rotation, wait=True)
111
+ if plan_between_rotations:
112
+ yield from plan_between_rotations()
75
113
  yield from bps.rel_set(smargon.omega, -rotation, wait=True)
76
114
 
77
115
  def cleanup():
@@ -22,6 +22,7 @@ from .fixed_target.i24ssx_Chip_Manager_py3v1 import (
22
22
  upload_parameters,
23
23
  write_parameter_file,
24
24
  )
25
+ from .log import clean_up_log_config_at_end, setup_collection_logs
25
26
  from .setup_beamline.setup_detector import setup_detector_stage
26
27
 
27
28
  __all__ = [
@@ -46,4 +47,6 @@ __all__ = [
46
47
  "save_screen_map",
47
48
  "upload_parameters",
48
49
  "write_parameter_file",
50
+ "setup_collection_logs",
51
+ "clean_up_log_config_at_end",
49
52
  ]