bec-ipython-client 3.64.1__py3-none-any.whl → 3.86.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.

Potentially problematic release.


This version of bec-ipython-client might be problematic. Click here for more details.

.gitignore CHANGED
@@ -9,6 +9,9 @@
9
9
  **/*.egg*
10
10
  **/*.env
11
11
 
12
+ # bec_widgets saved profiles
13
+ widgets_settings/profiles/*
14
+
12
15
  # recovery_config files
13
16
  recovery_config_*
14
17
 
PKG-INFO CHANGED
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_ipython_client
3
- Version: 3.64.1
3
+ Version: 3.86.1
4
4
  Summary: BEC IPython client
5
5
  Project-URL: Bug Tracker, https://github.com/bec-project/bec/issues
6
6
  Project-URL: Homepage, https://github.com/bec-project/bec
7
7
  Classifier: Development Status :: 3 - Alpha
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Topic :: Scientific/Engineering
10
- Requires-Python: >=3.10
10
+ Requires-Python: >=3.11
11
11
  Requires-Dist: bec-lib~=3.0
12
12
  Requires-Dist: ipython~=8.22
13
13
  Requires-Dist: numpy<3.0,>=1.24
@@ -14,11 +14,16 @@ class LiveUpdatesDeviceProgress(LiveUpdatesTable):
14
14
 
15
15
  REPORT_TYPE = "device_progress"
16
16
 
17
- def _run_update(self, device_names: str):
17
+ def core(self):
18
+ """core function to run the live updates for the table"""
19
+ self._wait_for_report_instructions()
20
+ self._run_update(self.report_instruction[self.REPORT_TYPE])
21
+
22
+ def _run_update(self, device_names: list[str]):
18
23
  """Run the update loop for the progress bar.
19
24
 
20
25
  Args:
21
- device_names (str): The name of the device to monitor.
26
+ device_names (list[str]): The name of the device to monitor.
22
27
  """
23
28
  with ScanProgressBar(
24
29
  scan_number=self.scan_item.scan_number, clear_on_exit=False
@@ -29,7 +34,7 @@ class LiveUpdatesDeviceProgress(LiveUpdatesTable):
29
34
  self._print_client_msgs_asap()
30
35
  self._print_client_msgs_all()
31
36
 
32
- def _update_progressbar(self, progressbar: ScanProgressBar, device_names: str) -> bool:
37
+ def _update_progressbar(self, progressbar: ScanProgressBar, device_names: list[str]) -> bool:
33
38
  """Update the progressbar based on the device status message
34
39
 
35
40
  Args:
@@ -41,17 +46,17 @@ class LiveUpdatesDeviceProgress(LiveUpdatesTable):
41
46
  self.check_alarms()
42
47
  status = self.bec.connector.get(MessageEndpoints.device_progress(device_names[0]))
43
48
  if not status:
44
- logger.debug("waiting for new data point")
49
+ logger.trace("waiting for new data point")
45
50
  time.sleep(0.1)
46
51
  return False
47
52
  if status.metadata.get("scan_id") != self.scan_item.scan_id:
48
- logger.debug("waiting for new data point")
53
+ logger.trace("waiting for new data point")
49
54
  time.sleep(0.1)
50
55
  return False
51
56
 
52
57
  point_id = status.content.get("value")
53
58
  if point_id is None:
54
- logger.debug("waiting for new data point")
59
+ logger.trace("waiting for new data point")
55
60
  time.sleep(0.1)
56
61
  return False
57
62
 
@@ -31,7 +31,7 @@ class IPythonLiveUpdates:
31
31
  self._interrupted_request = None
32
32
  self._active_callback = None
33
33
  self._processed_instructions = 0
34
- self._active_request = None
34
+ self._active_request: messages.ScanQueueMessage | None = None
35
35
  self._user_callback = None
36
36
  self._request_block_index = collections.defaultdict(lambda: 0)
37
37
  self._request_block_id = None
@@ -47,9 +47,11 @@ class IPythonLiveUpdates:
47
47
  Args:
48
48
  report_instructions (list): The list of report instructions.
49
49
  """
50
- scan_type = self._active_request.content["scan_type"]
50
+ if not self._active_request:
51
+ return
52
+ scan_type = self._active_request.scan_type
51
53
  if scan_type in ["open_scan_def", "close_scan_def"]:
52
- self._process_instruction({"scan_progress": 0})
54
+ self._process_instruction({"scan_progress": {"points": 0, "show_table": True}})
53
55
  return
54
56
  if scan_type == "close_scan_group":
55
57
  return
@@ -74,6 +76,9 @@ class IPythonLiveUpdates:
74
76
  scan_report_type = list(instr.keys())[0]
75
77
  scan_def_id = self.client.scans._scan_def_id
76
78
  interactive_scan = self.client.scans._interactive_scan
79
+ if self._active_request is None:
80
+ # Already checked in caller method. It is just for type checking purposes.
81
+ return
77
82
  if scan_def_id is None or interactive_scan:
78
83
  if scan_report_type == "readback":
79
84
  LiveUpdatesReadbackProgressbar(
@@ -126,17 +131,22 @@ class IPythonLiveUpdates:
126
131
  )
127
132
  self._active_callback.run()
128
133
 
129
- def _available_req_blocks(self, queue: QueueItem, request: messages.ScanQueueMessage):
134
+ def _available_req_blocks(
135
+ self, queue: QueueItem, request: messages.ScanQueueMessage
136
+ ) -> list[messages.RequestBlock]:
130
137
  """Get the available request blocks.
131
138
 
132
139
  Args:
133
140
  queue (QueueItem): The queue item.
134
141
  request (messages.ScanQueueMessage): The request message.
142
+
143
+ Returns:
144
+ list[messages.RequestBlock]: The list of available request blocks.
135
145
  """
136
146
  available_blocks = [
137
147
  req_block
138
148
  for req_block in queue.request_blocks
139
- if req_block["RID"] == request.metadata["RID"]
149
+ if req_block.RID == request.metadata["RID"]
140
150
  ]
141
151
  return available_blocks
142
152
 
@@ -151,11 +161,14 @@ class IPythonLiveUpdates:
151
161
  scan_request = ScanRequestMixin(self.client, request.metadata["RID"])
152
162
  scan_request.wait()
153
163
 
164
+ # After .wait, we can be sure that the queue item is available, so we can
165
+ assert scan_request.scan_queue_request is not None
166
+
154
167
  # get the corresponding queue item
155
- while not scan_request.request_storage.storage[-1].queue:
168
+ while not scan_request.scan_queue_request.queue:
156
169
  time.sleep(0.01)
157
170
 
158
- self._current_queue = queue = scan_request.request_storage.storage[-1].queue
171
+ self._current_queue = queue = scan_request.scan_queue_request.queue
159
172
  self._request_block_id = req_id = self._active_request.metadata.get("RID")
160
173
 
161
174
  while queue.status not in ["COMPLETED", "ABORTED", "HALTED"]:
@@ -164,7 +177,7 @@ class IPythonLiveUpdates:
164
177
 
165
178
  available_blocks = self._available_req_blocks(queue, request)
166
179
  req_block = available_blocks[self._request_block_index[req_id]]
167
- report_instructions = req_block.get("report_instructions", [])
180
+ report_instructions = req_block.report_instructions or []
168
181
  self._process_report_instructions(report_instructions)
169
182
 
170
183
  self._reset()
@@ -188,12 +201,19 @@ class IPythonLiveUpdates:
188
201
  self.client.queue.request_scan_halt()
189
202
 
190
203
  def _element_in_queue(self) -> bool:
191
- queue = self.client.queue.queue_storage.current_scan_queue.get("primary", {}).get(
192
- "info", []
193
- )
194
- if not queue:
204
+ if self.client.queue is None:
205
+ return False
206
+ if (csq := self.client.queue.queue_storage.current_scan_queue) is None:
195
207
  return False
196
- return self._current_queue.queue_id in queue[0].get("queue_id")
208
+ scan_queue_status = csq.get("primary")
209
+ if scan_queue_status is None:
210
+ return False
211
+ queue_info = scan_queue_status.info
212
+ if not queue_info:
213
+ return False
214
+ if self._current_queue is None:
215
+ return False
216
+ return self._current_queue.queue_id == queue_info[0].queue_id
197
217
 
198
218
  def _process_queue(
199
219
  self, queue: QueueItem, request: messages.ScanQueueMessage, req_id: str
@@ -213,9 +233,11 @@ class IPythonLiveUpdates:
213
233
  if not queue.request_blocks or not queue.status or queue.queue_position is None:
214
234
  return False
215
235
  if queue.status == "PENDING" and queue.queue_position > 0:
216
- status = self.client.queue.queue_storage.current_scan_queue.get("primary", {}).get(
217
- "status"
218
- )
236
+ primary_queue = self.client.queue.queue_storage.current_scan_queue.get("primary")
237
+
238
+ if primary_queue is None:
239
+ return False
240
+ status = primary_queue.status
219
241
  print(
220
242
  "Scan is enqueued and is waiting for execution. Current position in queue:"
221
243
  f" {queue.queue_position + 1}. Queue status: {status}.",
@@ -223,14 +245,16 @@ class IPythonLiveUpdates:
223
245
  flush=True,
224
246
  )
225
247
  available_blocks = self._available_req_blocks(queue, request)
248
+ if not available_blocks:
249
+ return False
226
250
  req_block = available_blocks[self._request_block_index[req_id]]
227
- if req_block["content"]["scan_type"] in [
251
+ if req_block.msg.scan_type in [
228
252
  "open_scan_def",
229
253
  "mv",
230
254
  ]: # TODO: make this more general for all scan types that don't have report instructions
231
255
  return True
232
256
 
233
- report_instructions = req_block["report_instructions"]
257
+ report_instructions = req_block.report_instructions or []
234
258
  if not report_instructions:
235
259
  return False
236
260
  self._process_report_instructions(report_instructions)
@@ -258,7 +282,11 @@ class IPythonLiveUpdates:
258
282
  self._current_queue = None
259
283
  self._user_callback = None
260
284
  self._processed_instructions = 0
261
- scan_closed = forced or (self._active_request.content["scan_type"] == "close_scan_def")
285
+ scan_closed = (
286
+ forced
287
+ or self._active_request is None
288
+ or (self._active_request.scan_type == "close_scan_def")
289
+ )
262
290
  self._active_request = None
263
291
 
264
292
  if self.client.scans._scan_def_id and not scan_closed:
@@ -56,7 +56,6 @@ class LiveUpdatesTable(LiveUpdatesBase):
56
56
  super().__init__(
57
57
  bec, report_instruction=report_instruction, request=request, callbacks=callbacks
58
58
  )
59
- self.scan_queue_request = None
60
59
  self.scan_item = None
61
60
  self.dev_values = None
62
61
  self.point_data = None
@@ -73,12 +72,14 @@ class LiveUpdatesTable(LiveUpdatesBase):
73
72
  def wait_for_scan_to_start(self):
74
73
  """wait until the scan starts"""
75
74
  while True:
75
+ if not self.scan_item or not self.scan_item.queue:
76
+ raise RuntimeError("No scan item or scan queue available.")
76
77
  queue_pos = self.scan_item.queue.queue_position
77
78
  self.check_alarms()
78
79
  if self.scan_item.status == "closed":
79
80
  break
80
81
  if queue_pos is None:
81
- logger.debug(f"Could not find queue entry for scan_id {self.scan_item.scan_id}")
82
+ logger.trace(f"Could not find queue entry for scan_id {self.scan_item.scan_id}")
82
83
  continue
83
84
  if queue_pos == 0:
84
85
  break
@@ -160,7 +161,20 @@ class LiveUpdatesTable(LiveUpdatesBase):
160
161
  return header
161
162
 
162
163
  def update_scan_item(self, timeout: float = 15):
163
- """get the current scan item"""
164
+ """
165
+ Get the current scan item and update self.scan_item
166
+
167
+ Args:
168
+ timeout (float): timeout in seconds
169
+
170
+ Raises:
171
+ RuntimeError: if no scan queue request is available
172
+ TimeoutError: if no scan item is found before reaching the timeout
173
+
174
+ """
175
+ if not self.scan_queue_request:
176
+ raise RuntimeError("No scan queue request available.")
177
+
164
178
  start = time.time()
165
179
  while self.scan_queue_request.scan is None:
166
180
  self.check_alarms()
@@ -171,25 +185,38 @@ class LiveUpdatesTable(LiveUpdatesBase):
171
185
 
172
186
  def core(self):
173
187
  """core function to run the live updates for the table"""
188
+ self._wait_for_report_instructions()
189
+ show_table = self.report_instruction[self.REPORT_TYPE].get("show_table", True)
190
+ self._print_table_data = show_table
191
+ self._run_update(self.report_instruction[self.REPORT_TYPE]["points"])
192
+
193
+ def _wait_for_report_instructions(self):
194
+ """wait until the report instructions are available"""
195
+ if not self.scan_queue_request or not self.scan_item or not self.scan_item.queue:
196
+ logger.warning(
197
+ f"Cannot wait for report instructions. scan_queue_request: {self.scan_queue_request}, scan_item: {self.scan_item}, scan_item.queue: {getattr(self.scan_item, 'queue', None)}"
198
+ )
199
+ return
174
200
  req_ID = self.scan_queue_request.requestID
175
201
  while True:
176
202
  request_block = [
177
- req for req in self.scan_item.queue.request_blocks if req["RID"] == req_ID
203
+ req for req in self.scan_item.queue.request_blocks if req.RID == req_ID
178
204
  ][0]
179
- if not request_block["is_scan"]:
205
+ if not request_block.is_scan:
180
206
  break
181
- if request_block["report_instructions"]:
207
+ if request_block.report_instructions:
182
208
  break
183
209
  self.check_alarms()
184
210
 
185
- self._run_update(self.report_instruction[self.REPORT_TYPE])
186
-
187
211
  def _run_update(self, target_num_points: int):
188
212
  """run the update loop with the progress bar
189
213
 
190
214
  Args:
191
215
  target_num_points (int): number of points to be collected
192
216
  """
217
+ if not self.scan_item:
218
+ logger.warning("No scan item available for live updates.")
219
+ return
193
220
  with ScanProgressBar(
194
221
  scan_number=self.scan_item.scan_number, clear_on_exit=self._print_table_data
195
222
  ) as progressbar:
@@ -211,7 +238,7 @@ class LiveUpdatesTable(LiveUpdatesBase):
211
238
  self.bec.callbacks.poll()
212
239
  self.scan_item.poll_callbacks()
213
240
  else:
214
- logger.debug("waiting for new data point")
241
+ logger.trace("waiting for new data point")
215
242
  time.sleep(0.1)
216
243
 
217
244
  if not self.scan_item.num_points:
@@ -1,31 +1,110 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import threading
3
4
  from collections.abc import Callable
4
- from typing import TYPE_CHECKING
5
+ from typing import TYPE_CHECKING, cast
5
6
 
6
7
  import numpy as np
7
8
 
8
9
  from bec_ipython_client.progressbar import DeviceProgressBar
10
+ from bec_lib import messages
9
11
  from bec_lib.endpoints import MessageEndpoints
12
+ from bec_lib.redis_connector import MessageObject
10
13
 
11
14
  from .utils import LiveUpdatesBase, check_alarms
12
15
 
13
16
  if TYPE_CHECKING:
14
- from bec_lib import messages
15
17
  from bec_lib.client import BECClient
16
18
  from bec_lib.devicemanager import DeviceManagerBase
17
19
 
18
20
 
19
- class ReadbackDataMixin:
20
- def __init__(self, device_manager: DeviceManagerBase, devices: list) -> None:
21
- """Mixin to get the current device values and request-done messages.
21
+ class ReadbackDataHandler:
22
+ """Helper class to get the current device values and request-done messages."""
23
+
24
+ def __init__(
25
+ self, device_manager: DeviceManagerBase, devices: list[str], request_id: str
26
+ ) -> None:
27
+ """Helper class to get the current device values and request-done messages.
22
28
 
23
29
  Args:
24
30
  device_manager (DeviceManagerBase): device manager
25
31
  devices (list): list of devices to monitor
32
+ request_id (str): request ID
26
33
  """
27
34
  self.device_manager = device_manager
28
35
  self.devices = devices
36
+ self.connector = device_manager.connector
37
+ self.request_id = request_id
38
+ self._devices_received = {dev: False for dev in devices}
39
+ self.data: dict[str, messages.DeviceMessage] = {}
40
+ self._devices_done_state: dict[str, tuple[bool, bool]] = {
41
+ dev: (False, False) for dev in devices
42
+ }
43
+ self.requests_done = threading.Event()
44
+ self._register_callbacks()
45
+
46
+ def _register_callbacks(self):
47
+ """register callbacks for device readback messages."""
48
+ for dev in self.devices:
49
+ self.connector.register(
50
+ MessageEndpoints.device_readback(dev), cb=self.on_readback, parent=self, device=dev
51
+ )
52
+ self.connector.register(
53
+ MessageEndpoints.device_req_status(self.request_id),
54
+ cb=self.on_req_status,
55
+ from_start=True,
56
+ parent=self,
57
+ )
58
+
59
+ def _unregister_callbacks(self):
60
+ """unregister callbacks for device readback messages."""
61
+ for dev in self.devices:
62
+ self.connector.unregister(MessageEndpoints.device_readback(dev), cb=self.on_readback)
63
+ self.connector.unregister(
64
+ MessageEndpoints.device_req_status(self.request_id), cb=self.on_req_status
65
+ )
66
+
67
+ @staticmethod
68
+ def on_req_status(
69
+ msg_obj: dict[str, messages.DeviceReqStatusMessage], parent: ReadbackDataHandler
70
+ ):
71
+ """Callback for device request status messages to track which devices are done.
72
+
73
+ Args:
74
+ msg_obj (dict[str, messages.DeviceReqStatusMessage]): message object or device request status message
75
+ parent (ReadbackDataHandler): parent instance
76
+ """
77
+ # pylint: disable=protected-access
78
+ msg = msg_obj["data"]
79
+ if msg.request_id != parent.request_id:
80
+ return
81
+ device = msg.device
82
+ parent._devices_done_state[device] = (True, msg.success)
83
+
84
+ if (
85
+ all(done for done, _ in parent._devices_done_state.values())
86
+ and not parent.requests_done.is_set()
87
+ ):
88
+ parent._on_request_done()
89
+
90
+ @staticmethod
91
+ def on_readback(msg_obj: MessageObject, parent: ReadbackDataHandler, device: str):
92
+ """Callback for updating device readback data.
93
+
94
+ Args:
95
+ msg_obj (MessageObject): message object
96
+ parent (ReadbackDataHandler): parent instance
97
+ device (str): device name
98
+ """
99
+ # pylint: disable=protected-access
100
+ msg: messages.DeviceMessage = cast(messages.DeviceMessage, msg_obj.value)
101
+ parent._devices_received[device] = True
102
+ parent.data[device] = msg
103
+
104
+ def _on_request_done(self):
105
+ """Callback for when all requests are done."""
106
+ self.requests_done.set()
107
+ self._unregister_callbacks()
29
108
 
30
109
  def get_device_values(self) -> list:
31
110
  """get the current device values
@@ -35,57 +114,55 @@ class ReadbackDataMixin:
35
114
  """
36
115
  values = []
37
116
  for dev in self.devices:
38
- val = self.device_manager.devices[dev].read(cached=True)
39
- if not val:
40
- values.append(np.nan)
41
- continue
117
+ val = self.data.get(dev)
118
+ if val is None:
119
+ signal_data = self.device_manager.devices[dev].read(cached=True)
120
+ else:
121
+ signal_data = val.signals
42
122
  # pylint: disable=protected-access
43
123
  hints = self.device_manager.devices[dev]._hints
44
124
  # if we have hints, use them to get the value, otherwise just use the first value
45
125
  if hints:
46
- values.append(val.get(hints[0]).get("value"))
126
+ values.append(signal_data.get(hints[0]).get("value"))
47
127
  else:
48
- values.append(val.get(list(val.keys())[0]).get("value"))
128
+ values.append(signal_data.get(list(signal_data.keys())[0]).get("value"))
49
129
  return values
50
130
 
51
- def get_request_done_msgs(self):
52
- """get all request-done messages"""
53
- pipe = self.device_manager.connector.pipeline()
54
- for dev in self.devices:
55
- self.device_manager.connector.get(MessageEndpoints.device_req_status(dev), pipe)
56
- return self.device_manager.connector.execute_pipeline(pipe)
131
+ def done(self) -> bool:
132
+ """check if all devices are done
57
133
 
58
- def wait_for_RID(self, request: messages.ScanQueueMessage) -> None:
59
- """wait for the readback's metadata to match the request ID
134
+ Returns:
135
+ bool: True if all devices are done, False otherwise
136
+ """
137
+ return self.requests_done.is_set()
60
138
 
61
- Args:
62
- request (messages.ScanQueueMessage): request message
139
+ def device_states(self) -> dict[str, tuple[bool, bool]]:
63
140
  """
64
- while True:
65
- msgs = [
66
- self.device_manager.connector.get(MessageEndpoints.device_readback(dev))
67
- for dev in self.devices
68
- ]
69
- if all(msg.metadata.get("RID") == request.metadata["RID"] for msg in msgs if msg):
70
- break
71
- check_alarms(self.device_manager.parent)
141
+ Return the current device done states.
142
+
143
+ Returns:
144
+ dict: dictionary with device names as keys and tuples of (done, success) as values
145
+ """
146
+ return self._devices_done_state
72
147
 
73
148
 
74
149
  class LiveUpdatesReadbackProgressbar(LiveUpdatesBase):
75
150
  """Live feedback on motor movements using a progressbar.
76
151
 
77
152
  Args:
78
- dm (DeviceManagerBase): device_manager
79
- request (ScanQueueMessage): request that should be monitored
153
+ bec (BECClient): BECClient instance
154
+ report_instruction (list, optional): report instruction for the scan. Defaults to None.
155
+ request (messages.ScanQueueMessage, optional): scan queue request message. Defaults to None.
156
+ callbacks (list[Callable], optional): list of callbacks to register. Defaults to None.
80
157
 
81
158
  """
82
159
 
83
160
  def __init__(
84
161
  self,
85
162
  bec: BECClient,
86
- report_instruction: list = None,
87
- request: messages.ScanQueueMessage = None,
88
- callbacks: list[Callable] = None,
163
+ report_instruction: list | None = None,
164
+ request: messages.ScanQueueMessage | None = None,
165
+ callbacks: list[Callable] | None = None,
89
166
  ) -> None:
90
167
  super().__init__(
91
168
  bec, report_instruction=report_instruction, request=request, callbacks=callbacks
@@ -97,18 +174,20 @@ class LiveUpdatesReadbackProgressbar(LiveUpdatesBase):
97
174
 
98
175
  def core(self):
99
176
  """core function to monitor the device values and update the progressbar accordingly."""
100
- data_source = ReadbackDataMixin(self.bec.device_manager, self.devices)
177
+ request_id = self.request.metadata["RID"]
178
+ if self.report_instruction:
179
+ self.devices = self.report_instruction["readback"]["devices"]
180
+ request_id = self.report_instruction["readback"]["RID"]
181
+ data_source = ReadbackDataHandler(self.bec.device_manager, self.devices, request_id)
101
182
  start_values = data_source.get_device_values()
102
183
  self.wait_for_request_acceptance()
103
- data_source.wait_for_RID(self.request)
184
+
104
185
  if self.report_instruction:
105
- self.devices = self.report_instruction["readback"]["devices"]
106
186
  target_values = self.report_instruction["readback"]["end"]
107
187
 
108
188
  start_instr = self.report_instruction["readback"].get("start")
109
189
  if start_instr:
110
190
  start_values = self.report_instruction["readback"]["start"]
111
- data_source = ReadbackDataMixin(self.bec.device_manager, self.devices)
112
191
  else:
113
192
  target_values = [
114
193
  x for xs in self.request.content["parameter"]["args"].values() for x in xs
@@ -119,33 +198,16 @@ class LiveUpdatesReadbackProgressbar(LiveUpdatesBase):
119
198
  with DeviceProgressBar(
120
199
  self.devices, start_values=start_values, target_values=target_values
121
200
  ) as progress:
122
- req_done = False
123
- while not progress.finished or not req_done:
201
+
202
+ while not progress.finished or not data_source.done():
124
203
  check_alarms(self.bec)
125
204
 
126
205
  values = data_source.get_device_values()
127
206
  progress.update(values=values)
128
207
  self._print_client_msgs_asap()
129
208
 
130
- msgs = data_source.get_request_done_msgs()
131
- request_ids = [
132
- msg.metadata["RID"] if (msg and msg.metadata.get("RID")) else None
133
- for msg in msgs
134
- ]
135
-
136
- if self.report_instruction:
137
- compare_rids = set([self.report_instruction["readback"]["RID"]])
138
- else:
139
- compare_rids = set([self.request.metadata["RID"]])
140
- if set(request_ids) != set(compare_rids):
141
- progress.sleep()
142
- continue
143
-
144
- req_done = True
145
- for dev, msg in zip(self.devices, msgs):
146
- if not msg:
147
- continue
148
- if msg.content.get("success", False):
209
+ for dev, (done, success) in data_source.device_states().items():
210
+ if done and success:
149
211
  progress.set_finished(dev)
150
212
  # pylint: disable=protected-access
151
213
  progress._progress.refresh()
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import abc
4
- import threading
5
4
  import time
6
5
  import traceback
7
6
  from collections.abc import Callable
@@ -21,23 +20,6 @@ class ScanRequestError(Exception):
21
20
  """Error raised when a scan request is rejected"""
22
21
 
23
22
 
24
- def set_event_delayed(event: threading.Event, delay: int) -> None:
25
- """Set event with a delay
26
-
27
- Args:
28
- event (threading.Event): event that should be set
29
- delay (int): delay time in seconds
30
-
31
- """
32
-
33
- def call_set():
34
- time.sleep(delay)
35
- event.set()
36
-
37
- thread = threading.Thread(target=call_set, daemon=True)
38
- thread.start()
39
-
40
-
41
23
  def check_alarms(bec):
42
24
  """check for alarms and raise them if needed"""
43
25
  bec.alarm_handler.raise_alarms()
@@ -62,7 +44,7 @@ class LiveUpdatesBase(abc.ABC):
62
44
  self.bec = bec
63
45
  self.request = request
64
46
  self.RID = request.metadata["RID"]
65
- self.scan_queue_request = None
47
+ self.scan_queue_request: RequestItem | None = None
66
48
  self.report_instruction = report_instruction
67
49
  if callbacks is None:
68
50
  self.callbacks = []
@@ -138,22 +120,22 @@ class ScanRequestMixin:
138
120
  Returns:
139
121
  RequestItem: scan queue request
140
122
  """
141
- logger.debug("Waiting for request ID")
123
+ logger.trace("Waiting for request ID")
142
124
  start = time.time()
143
125
  while self.request_storage.find_request_by_ID(self.RID) is None:
144
126
  time.sleep(0.1)
145
127
  check_alarms(self.bec)
146
- logger.debug(f"Waiting for request ID finished after {time.time()-start} s.")
128
+ logger.trace(f"Waiting for request ID finished after {time.time()-start} s.")
147
129
  return self.request_storage.find_request_by_ID(self.RID)
148
130
 
149
131
  def _wait_for_scan_request_decision(self):
150
132
  """wait for a scan queuest decision"""
151
- logger.debug("Waiting for decision")
133
+ logger.trace("Waiting for decision")
152
134
  start = time.time()
153
135
  while self.scan_queue_request.decision_pending:
154
136
  time.sleep(0.1)
155
137
  check_alarms(self.bec)
156
- logger.debug(f"Waiting for decision finished after {time.time()-start} s.")
138
+ logger.trace(f"Waiting for decision finished after {time.time()-start} s.")
157
139
 
158
140
  def wait(self):
159
141
  """wait for the request acceptance"""