bec-ipython-client 3.35.6__py3-none-any.whl → 3.84.0__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 +3 -0
- PKG-INFO +4 -4
- bec_ipython_client/bec_startup.py +5 -2
- bec_ipython_client/callbacks/device_progress.py +11 -6
- bec_ipython_client/callbacks/ipython_live_updates.py +8 -3
- bec_ipython_client/callbacks/live_table.py +58 -30
- bec_ipython_client/callbacks/move_device.py +121 -59
- bec_ipython_client/callbacks/utils.py +4 -4
- bec_ipython_client/main.py +83 -8
- {bec_ipython_client-3.35.6.dist-info → bec_ipython_client-3.84.0.dist-info}/METADATA +4 -4
- {bec_ipython_client-3.35.6.dist-info → bec_ipython_client-3.84.0.dist-info}/RECORD +23 -20
- {bec_ipython_client-3.35.6.dist-info → bec_ipython_client-3.84.0.dist-info}/WHEEL +1 -1
- demo.py +2 -1
- pyproject.toml +4 -4
- tests/client_tests/conftest.py +19 -0
- tests/client_tests/test_bec_client.py +36 -1
- tests/client_tests/test_live_table.py +369 -225
- tests/client_tests/test_move_callback.py +112 -70
- tests/end-2-end/_ensure_requirements_container.py +21 -0
- tests/end-2-end/test_procedures_e2e.py +134 -0
- tests/end-2-end/test_scans_e2e.py +4 -4
- tests/end-2-end/test_scans_lib_e2e.py +36 -32
- {bec_ipython_client-3.35.6.dist-info → bec_ipython_client-3.84.0.dist-info}/entry_points.txt +0 -0
.gitignore
CHANGED
PKG-INFO
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bec_ipython_client
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.84.0
|
|
4
4
|
Summary: BEC IPython client
|
|
5
|
-
Project-URL: Bug Tracker, https://
|
|
6
|
-
Project-URL: Homepage, https://
|
|
5
|
+
Project-URL: Bug Tracker, https://github.com/bec-project/bec/issues
|
|
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
|
+
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
|
|
@@ -32,9 +32,12 @@ except (BECAuthenticationError, KeyboardInterrupt) as exc:
|
|
|
32
32
|
except Exception:
|
|
33
33
|
sys.excepthook(*sys.exc_info())
|
|
34
34
|
else:
|
|
35
|
-
if bec.started and
|
|
35
|
+
if bec.started and BECGuiClient is not None:
|
|
36
36
|
gui = bec.gui = BECGuiClient()
|
|
37
|
-
|
|
37
|
+
if _main_dict["args"].gui_id:
|
|
38
|
+
gui.connect_to_gui_server(_main_dict["args"].gui_id)
|
|
39
|
+
if not _main_dict["args"].nogui:
|
|
40
|
+
gui.show()
|
|
38
41
|
|
|
39
42
|
_available_plugins = plugin_helper.get_ipython_client_startup_plugins(state="post")
|
|
40
43
|
if _available_plugins:
|
|
@@ -14,11 +14,16 @@ class LiveUpdatesDeviceProgress(LiveUpdatesTable):
|
|
|
14
14
|
|
|
15
15
|
REPORT_TYPE = "device_progress"
|
|
16
16
|
|
|
17
|
-
def
|
|
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.
|
|
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.
|
|
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.
|
|
59
|
+
logger.trace("waiting for new data point")
|
|
55
60
|
time.sleep(0.1)
|
|
56
61
|
return False
|
|
57
62
|
|
|
@@ -49,7 +49,7 @@ class IPythonLiveUpdates:
|
|
|
49
49
|
"""
|
|
50
50
|
scan_type = self._active_request.content["scan_type"]
|
|
51
51
|
if scan_type in ["open_scan_def", "close_scan_def"]:
|
|
52
|
-
self._process_instruction({"scan_progress": 0})
|
|
52
|
+
self._process_instruction({"scan_progress": {"points": 0, "show_table": True}})
|
|
53
53
|
return
|
|
54
54
|
if scan_type == "close_scan_group":
|
|
55
55
|
return
|
|
@@ -151,11 +151,14 @@ class IPythonLiveUpdates:
|
|
|
151
151
|
scan_request = ScanRequestMixin(self.client, request.metadata["RID"])
|
|
152
152
|
scan_request.wait()
|
|
153
153
|
|
|
154
|
+
# After .wait, we can be sure that the queue item is available, so we can
|
|
155
|
+
assert scan_request.scan_queue_request is not None
|
|
156
|
+
|
|
154
157
|
# get the corresponding queue item
|
|
155
|
-
while not scan_request.
|
|
158
|
+
while not scan_request.scan_queue_request.queue:
|
|
156
159
|
time.sleep(0.01)
|
|
157
160
|
|
|
158
|
-
self._current_queue = queue = scan_request.
|
|
161
|
+
self._current_queue = queue = scan_request.scan_queue_request.queue
|
|
159
162
|
self._request_block_id = req_id = self._active_request.metadata.get("RID")
|
|
160
163
|
|
|
161
164
|
while queue.status not in ["COMPLETED", "ABORTED", "HALTED"]:
|
|
@@ -223,6 +226,8 @@ class IPythonLiveUpdates:
|
|
|
223
226
|
flush=True,
|
|
224
227
|
)
|
|
225
228
|
available_blocks = self._available_req_blocks(queue, request)
|
|
229
|
+
if not available_blocks:
|
|
230
|
+
return False
|
|
226
231
|
req_block = available_blocks[self._request_block_index[req_id]]
|
|
227
232
|
if req_block["content"]["scan_type"] in [
|
|
228
233
|
"open_scan_def",
|
|
@@ -2,7 +2,9 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import time
|
|
4
4
|
from collections.abc import Callable
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
5
|
+
from typing import TYPE_CHECKING, Any, SupportsFloat
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
6
8
|
|
|
7
9
|
from bec_ipython_client.prettytable import PrettyTable
|
|
8
10
|
from bec_ipython_client.progressbar import ScanProgressBar
|
|
@@ -66,6 +68,7 @@ class LiveUpdatesTable(LiveUpdatesBase):
|
|
|
66
68
|
if print_table_data is not None
|
|
67
69
|
else self.REPORT_TYPE == "scan_progress"
|
|
68
70
|
)
|
|
71
|
+
self._devices_with_bad_precision = set()
|
|
69
72
|
|
|
70
73
|
def wait_for_scan_to_start(self):
|
|
71
74
|
"""wait until the scan starts"""
|
|
@@ -75,7 +78,7 @@ class LiveUpdatesTable(LiveUpdatesBase):
|
|
|
75
78
|
if self.scan_item.status == "closed":
|
|
76
79
|
break
|
|
77
80
|
if queue_pos is None:
|
|
78
|
-
logger.
|
|
81
|
+
logger.trace(f"Could not find queue entry for scan_id {self.scan_item.scan_id}")
|
|
79
82
|
continue
|
|
80
83
|
if queue_pos == 0:
|
|
81
84
|
break
|
|
@@ -168,6 +171,13 @@ class LiveUpdatesTable(LiveUpdatesBase):
|
|
|
168
171
|
|
|
169
172
|
def core(self):
|
|
170
173
|
"""core function to run the live updates for the table"""
|
|
174
|
+
self._wait_for_report_instructions()
|
|
175
|
+
show_table = self.report_instruction[self.REPORT_TYPE].get("show_table", True)
|
|
176
|
+
self._print_table_data = show_table
|
|
177
|
+
self._run_update(self.report_instruction[self.REPORT_TYPE]["points"])
|
|
178
|
+
|
|
179
|
+
def _wait_for_report_instructions(self):
|
|
180
|
+
"""wait until the report instructions are available"""
|
|
171
181
|
req_ID = self.scan_queue_request.requestID
|
|
172
182
|
while True:
|
|
173
183
|
request_block = [
|
|
@@ -179,8 +189,6 @@ class LiveUpdatesTable(LiveUpdatesBase):
|
|
|
179
189
|
break
|
|
180
190
|
self.check_alarms()
|
|
181
191
|
|
|
182
|
-
self._run_update(self.report_instruction[self.REPORT_TYPE])
|
|
183
|
-
|
|
184
192
|
def _run_update(self, target_num_points: int):
|
|
185
193
|
"""run the update loop with the progress bar
|
|
186
194
|
|
|
@@ -208,7 +216,7 @@ class LiveUpdatesTable(LiveUpdatesBase):
|
|
|
208
216
|
self.bec.callbacks.poll()
|
|
209
217
|
self.scan_item.poll_callbacks()
|
|
210
218
|
else:
|
|
211
|
-
logger.
|
|
219
|
+
logger.trace("waiting for new data point")
|
|
212
220
|
time.sleep(0.1)
|
|
213
221
|
|
|
214
222
|
if not self.scan_item.num_points:
|
|
@@ -219,6 +227,24 @@ class LiveUpdatesTable(LiveUpdatesBase):
|
|
|
219
227
|
if self.point_id > self.scan_item.num_points:
|
|
220
228
|
raise RuntimeError("Received more points than expected.")
|
|
221
229
|
|
|
230
|
+
if len(self.scan_item.live_data) == 0 and self.scan_item.status == "closed":
|
|
231
|
+
msg = self.scan_item.status_message
|
|
232
|
+
if not msg:
|
|
233
|
+
continue
|
|
234
|
+
if msg.readout_priority.get("monitored", []):
|
|
235
|
+
continue
|
|
236
|
+
|
|
237
|
+
logger.warning(
|
|
238
|
+
f"\n Scan {self.scan_item.scan_number} finished. No monitored devices enabled, please check your config."
|
|
239
|
+
)
|
|
240
|
+
break
|
|
241
|
+
|
|
242
|
+
def _warn_bad_precisions(self):
|
|
243
|
+
if self._devices_with_bad_precision != set():
|
|
244
|
+
for dev, prec in self._devices_with_bad_precision:
|
|
245
|
+
logger.warning(f"Device {dev} reported malformed precision of {prec}!")
|
|
246
|
+
self._devices_with_bad_precision = set()
|
|
247
|
+
|
|
222
248
|
@property
|
|
223
249
|
def _print_table_data(self) -> bool:
|
|
224
250
|
"""Checks if the table should be printed or not.
|
|
@@ -261,40 +287,41 @@ class LiveUpdatesTable(LiveUpdatesBase):
|
|
|
261
287
|
|
|
262
288
|
if self.point_id % 100 == 0:
|
|
263
289
|
print(self.table.get_header_lines())
|
|
264
|
-
|
|
290
|
+
|
|
291
|
+
signals_precisions = []
|
|
265
292
|
for dev in self.devices:
|
|
266
293
|
if dev in self.bec.device_manager.devices:
|
|
267
294
|
obj = self.bec.device_manager.devices[dev]
|
|
268
295
|
for hint in obj._hints:
|
|
269
296
|
signal = self.point_data.content["data"].get(obj.root.name, {}).get(hint)
|
|
270
|
-
if signal is None
|
|
271
|
-
|
|
297
|
+
if signal is None:
|
|
298
|
+
signals_precisions.append((None, None))
|
|
272
299
|
else:
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
if isinstance(value, (int, float)):
|
|
279
|
-
print_value = f"{value:.{precision}f}"
|
|
280
|
-
else:
|
|
281
|
-
print_value = str(value)
|
|
282
|
-
self.dev_values[ind] = print_value
|
|
283
|
-
ind += 1
|
|
300
|
+
prec = getattr(obj, "precision", 2)
|
|
301
|
+
if not isinstance(prec, int):
|
|
302
|
+
self._devices_with_bad_precision.add((dev, prec))
|
|
303
|
+
prec = 2
|
|
304
|
+
signals_precisions.append((signal, prec))
|
|
284
305
|
else:
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
else:
|
|
291
|
-
print_value = str(value)
|
|
292
|
-
else:
|
|
293
|
-
print_value = "N/A"
|
|
294
|
-
self.dev_values[ind] = print_value
|
|
295
|
-
ind += 1
|
|
306
|
+
signals_precisions.append((self.point_data.content["data"].get(dev, {}), 2))
|
|
307
|
+
|
|
308
|
+
for i, (signal, precision) in enumerate(signals_precisions):
|
|
309
|
+
self.dev_values[i] = self._format_value(signal, precision)
|
|
310
|
+
|
|
296
311
|
print(self.table.get_row(str(self.point_id), *self.dev_values))
|
|
297
312
|
|
|
313
|
+
def _format_value(self, signal: dict | None, precision: int = 2):
|
|
314
|
+
if signal is None:
|
|
315
|
+
return "N/A"
|
|
316
|
+
val = signal.get("value")
|
|
317
|
+
if isinstance(val, SupportsFloat) and not isinstance(val, np.ndarray):
|
|
318
|
+
if precision < 0:
|
|
319
|
+
# This is to cover the special case when EPICS returns a negative precision.
|
|
320
|
+
# More info: https://epics.anl.gov/tech-talk/2004/msg00434.php
|
|
321
|
+
return f"{float(val):.{-precision}g}"
|
|
322
|
+
return f"{float(val):.{precision}f}"
|
|
323
|
+
return str(val)
|
|
324
|
+
|
|
298
325
|
def close_table(self):
|
|
299
326
|
"""close the table and print the footer"""
|
|
300
327
|
if not self.table:
|
|
@@ -305,6 +332,7 @@ class LiveUpdatesTable(LiveUpdatesBase):
|
|
|
305
332
|
f"Scan {self.scan_item.scan_number} finished. Scan ID {self.scan_item.scan_id}. Elapsed time: {elapsed_time:.2f} s"
|
|
306
333
|
)
|
|
307
334
|
)
|
|
335
|
+
self._warn_bad_precisions()
|
|
308
336
|
|
|
309
337
|
def process_request(self):
|
|
310
338
|
"""process the request and start the core loop for live updates"""
|
|
@@ -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
|
|
20
|
-
|
|
21
|
-
|
|
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.
|
|
39
|
-
if
|
|
40
|
-
|
|
41
|
-
|
|
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(
|
|
126
|
+
values.append(signal_data.get(hints[0]).get("value"))
|
|
47
127
|
else:
|
|
48
|
-
values.append(
|
|
128
|
+
values.append(signal_data.get(list(signal_data.keys())[0]).get("value"))
|
|
49
129
|
return values
|
|
50
130
|
|
|
51
|
-
def
|
|
52
|
-
"""
|
|
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
|
-
|
|
59
|
-
|
|
134
|
+
Returns:
|
|
135
|
+
bool: True if all devices are done, False otherwise
|
|
136
|
+
"""
|
|
137
|
+
return self.requests_done.is_set()
|
|
60
138
|
|
|
61
|
-
|
|
62
|
-
request (messages.ScanQueueMessage): request message
|
|
139
|
+
def device_states(self) -> dict[str, tuple[bool, bool]]:
|
|
63
140
|
"""
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
79
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
123
|
-
while not progress.finished or not
|
|
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
|
-
|
|
131
|
-
|
|
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()
|
|
@@ -138,22 +138,22 @@ class ScanRequestMixin:
|
|
|
138
138
|
Returns:
|
|
139
139
|
RequestItem: scan queue request
|
|
140
140
|
"""
|
|
141
|
-
logger.
|
|
141
|
+
logger.trace("Waiting for request ID")
|
|
142
142
|
start = time.time()
|
|
143
143
|
while self.request_storage.find_request_by_ID(self.RID) is None:
|
|
144
144
|
time.sleep(0.1)
|
|
145
145
|
check_alarms(self.bec)
|
|
146
|
-
logger.
|
|
146
|
+
logger.trace(f"Waiting for request ID finished after {time.time()-start} s.")
|
|
147
147
|
return self.request_storage.find_request_by_ID(self.RID)
|
|
148
148
|
|
|
149
149
|
def _wait_for_scan_request_decision(self):
|
|
150
150
|
"""wait for a scan queuest decision"""
|
|
151
|
-
logger.
|
|
151
|
+
logger.trace("Waiting for decision")
|
|
152
152
|
start = time.time()
|
|
153
153
|
while self.scan_queue_request.decision_pending:
|
|
154
154
|
time.sleep(0.1)
|
|
155
155
|
check_alarms(self.bec)
|
|
156
|
-
logger.
|
|
156
|
+
logger.trace(f"Waiting for decision finished after {time.time()-start} s.")
|
|
157
157
|
|
|
158
158
|
def wait(self):
|
|
159
159
|
"""wait for the request acceptance"""
|