bec-ipython-client 3.151.2__tar.gz → 3.152.0__tar.gz
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.
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/PKG-INFO +1 -1
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/callbacks/ipython_live_updates.py +39 -34
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/callbacks/live_table.py +6 -8
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/callbacks/move_device.py +41 -1
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/callbacks/utils.py +63 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/pyproject.toml +1 -1
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/client_tests/test_ipython_live_updates.py +169 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/client_tests/test_live_table.py +33 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/client_tests/test_move_callback.py +128 -1
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/end-2-end/test_scans_e2e.py +41 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/.gitignore +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/__init__.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/beamline_mixin.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/bec_magics.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/bec_startup.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/callbacks/__init__.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/callbacks/device_progress.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/high_level_interfaces/__init__.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/high_level_interfaces/bec_hli.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/high_level_interfaces/spec_hli.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/main.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/plugins/SLS/__init__.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/plugins/SLS/sls_info.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/plugins/XTreme/__init__.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/plugins/XTreme/x-treme.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/plugins/__init__.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/plugins/flomni/flomni_config.yaml +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/prettytable.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/progressbar.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/signals.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/demo.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/client_tests/conftest.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/client_tests/test_beamline_mixins.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/client_tests/test_bec_client.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/client_tests/test_device_progress.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/client_tests/test_pretty_table.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/client_tests/test_signals.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/conftest.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/end-2-end/_ensure_requirements_container.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/end-2-end/test_actors_e2e.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/end-2-end/test_procedures_e2e.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/end-2-end/test_scans_lib_e2e.py +0 -0
- {bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/end-2-end/test_scans_v4_lib_e2e.py +0 -0
|
@@ -16,7 +16,13 @@ from bec_lib.request_context import ActiveRequestContext, active_request_context
|
|
|
16
16
|
|
|
17
17
|
from .live_table import LiveUpdatesTable
|
|
18
18
|
from .move_device import LiveUpdatesReadbackProgressbar
|
|
19
|
-
from .utils import
|
|
19
|
+
from .utils import (
|
|
20
|
+
ScanRequestMixin,
|
|
21
|
+
ScanState,
|
|
22
|
+
check_alarms,
|
|
23
|
+
evaluate_scan_state,
|
|
24
|
+
pending_queue_message,
|
|
25
|
+
)
|
|
20
26
|
|
|
21
27
|
if TYPE_CHECKING:
|
|
22
28
|
from bec_lib import messages
|
|
@@ -308,7 +314,7 @@ class IPythonLiveUpdates:
|
|
|
308
314
|
if queue.queue_position is None:
|
|
309
315
|
return False
|
|
310
316
|
|
|
311
|
-
if queue
|
|
317
|
+
if self._is_pending_queue_state(queue):
|
|
312
318
|
self._process_pending_queue_element(queue)
|
|
313
319
|
return False
|
|
314
320
|
|
|
@@ -342,6 +348,28 @@ class IPythonLiveUpdates:
|
|
|
342
348
|
|
|
343
349
|
return False
|
|
344
350
|
|
|
351
|
+
def _is_pending_queue_state(self, queue: QueueItem) -> bool:
|
|
352
|
+
"""
|
|
353
|
+
Check if the queue is in a pending state, which can be due to either the queue
|
|
354
|
+
being locked or other scans being ahead in the queue.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
queue(QueueItem): The queue item to check.
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
bool: True if the queue is in a pending state, False otherwise.
|
|
361
|
+
"""
|
|
362
|
+
if queue.status == "PENDING":
|
|
363
|
+
return True
|
|
364
|
+
|
|
365
|
+
active_request_block = queue.active_request_block
|
|
366
|
+
if active_request_block is not None and getattr(
|
|
367
|
+
active_request_block, "pending_device_locks", None
|
|
368
|
+
):
|
|
369
|
+
return True
|
|
370
|
+
|
|
371
|
+
return False
|
|
372
|
+
|
|
345
373
|
def _process_pending_queue_element(self, queue: QueueItem) -> None:
|
|
346
374
|
"""
|
|
347
375
|
Process a pending queue element.
|
|
@@ -353,45 +381,22 @@ class IPythonLiveUpdates:
|
|
|
353
381
|
|
|
354
382
|
if self.client.queue is None or self.client.queue.queue_storage.current_scan_queue is None:
|
|
355
383
|
return
|
|
356
|
-
target_queue = self.client.queue.get_default_scan_queue()
|
|
357
|
-
target_queue_status = self.client.queue.queue_storage.current_scan_queue.get(target_queue)
|
|
358
|
-
if target_queue_status is None:
|
|
359
|
-
return
|
|
360
384
|
|
|
361
385
|
queue_position = queue.queue_position
|
|
362
386
|
if queue_position is None:
|
|
363
387
|
return
|
|
364
388
|
|
|
365
|
-
|
|
366
|
-
if
|
|
367
|
-
lock_info = [
|
|
368
|
-
f"{lock.identifier}: {lock.reason}\n" for lock in target_queue_status.locks
|
|
369
|
-
]
|
|
370
|
-
message = (
|
|
371
|
-
f"Scan is waiting for the lock to be released. Active locks: \n{''.join(lock_info)}"
|
|
372
|
-
)
|
|
373
|
-
if self._status_live is None:
|
|
374
|
-
self._status_live = Live(
|
|
375
|
-
Panel(message, title="[yellow]Scan Status[/yellow]"), refresh_per_second=4
|
|
376
|
-
)
|
|
377
|
-
self._status_live.start()
|
|
378
|
-
else:
|
|
379
|
-
self._status_live.update(Panel(message, title="[yellow]Scan Status[/yellow]"))
|
|
389
|
+
message = pending_queue_message(bec=self.client, queue=queue, queue_position=queue_position)
|
|
390
|
+
if message is None:
|
|
380
391
|
return
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
f"Scan is enqueued and is waiting for execution. Current position in queue {target_queue}:"
|
|
385
|
-
f" {queue_position + 1}. Queue status: {status}."
|
|
392
|
+
if self._status_live is None:
|
|
393
|
+
self._status_live = Live(
|
|
394
|
+
Panel(message, title="[yellow]Scan Status[/yellow]"), refresh_per_second=4
|
|
386
395
|
)
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
self._status_live.start()
|
|
392
|
-
else:
|
|
393
|
-
self._status_live.update(Panel(message, title="[yellow]Scan Status[/yellow]"))
|
|
394
|
-
return
|
|
396
|
+
self._status_live.start()
|
|
397
|
+
else:
|
|
398
|
+
self._status_live.update(Panel(message, title="[yellow]Scan Status[/yellow]"))
|
|
399
|
+
return
|
|
395
400
|
|
|
396
401
|
def _reset(self, forced=False):
|
|
397
402
|
"""Reset the active request and callback.
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/callbacks/live_table.py
RENAMED
|
@@ -11,7 +11,7 @@ from bec_ipython_client.progressbar import ScanProgressBar
|
|
|
11
11
|
from bec_lib.bec_errors import ScanInterruption, ScanRestart
|
|
12
12
|
from bec_lib.logger import bec_logger
|
|
13
13
|
|
|
14
|
-
from .utils import LiveUpdatesBase, check_alarms
|
|
14
|
+
from .utils import LiveUpdatesBase, check_alarms, pending_queue_message
|
|
15
15
|
|
|
16
16
|
if TYPE_CHECKING:
|
|
17
17
|
from bec_lib import messages
|
|
@@ -75,20 +75,18 @@ class LiveUpdatesTable(LiveUpdatesBase):
|
|
|
75
75
|
while True:
|
|
76
76
|
if not self.scan_item or not self.scan_item.queue:
|
|
77
77
|
raise RuntimeError("No scan item or scan queue available.")
|
|
78
|
-
|
|
78
|
+
queue = self.scan_item.queue
|
|
79
|
+
queue_pos = queue.queue_position
|
|
79
80
|
self.check_alarms()
|
|
80
81
|
if self.scan_item.status == "closed":
|
|
81
82
|
break
|
|
82
83
|
if queue_pos is None:
|
|
83
84
|
logger.trace(f"Could not find queue entry for scan_id {self.scan_item.scan_id}")
|
|
84
85
|
continue
|
|
85
|
-
|
|
86
|
+
message = pending_queue_message(bec=self.bec, queue=queue, queue_position=queue_pos)
|
|
87
|
+
if message is None:
|
|
86
88
|
break
|
|
87
|
-
print(
|
|
88
|
-
f"Scan is enqueued and is waiting for execution. Current position in queue: {queue_pos + 1}.",
|
|
89
|
-
end="\r",
|
|
90
|
-
flush=True,
|
|
91
|
-
)
|
|
89
|
+
print(message, end="\r", flush=True)
|
|
92
90
|
time.sleep(0.1)
|
|
93
91
|
while not self.scan_item.scan_number:
|
|
94
92
|
time.sleep(0.05)
|
|
@@ -12,7 +12,13 @@ from bec_lib import messages
|
|
|
12
12
|
from bec_lib.endpoints import MessageEndpoints
|
|
13
13
|
from bec_lib.redis_connector import MessageObject
|
|
14
14
|
|
|
15
|
-
from .utils import
|
|
15
|
+
from .utils import (
|
|
16
|
+
LiveUpdatesBase,
|
|
17
|
+
ScanState,
|
|
18
|
+
check_alarms,
|
|
19
|
+
evaluate_scan_state,
|
|
20
|
+
pending_queue_message,
|
|
21
|
+
)
|
|
16
22
|
|
|
17
23
|
if TYPE_CHECKING:
|
|
18
24
|
from bec_lib.client import BECClient
|
|
@@ -183,6 +189,7 @@ class LiveUpdatesReadbackProgressbar(LiveUpdatesBase):
|
|
|
183
189
|
data_source = ReadbackDataHandler(self.bec.device_manager, self.devices, request_id)
|
|
184
190
|
start_values = data_source.get_device_values()
|
|
185
191
|
self.wait_for_request_acceptance()
|
|
192
|
+
self._wait_for_move_to_start(data_source)
|
|
186
193
|
|
|
187
194
|
if self.report_instruction:
|
|
188
195
|
target_values = self.report_instruction["readback"]["end"]
|
|
@@ -231,3 +238,36 @@ class LiveUpdatesReadbackProgressbar(LiveUpdatesBase):
|
|
|
231
238
|
if self.scan_queue_request is None or self.scan_queue_request.queue is None:
|
|
232
239
|
return ScanState.CONTINUE
|
|
233
240
|
return evaluate_scan_state(queue=self.scan_queue_request.queue)
|
|
241
|
+
|
|
242
|
+
def _wait_for_move_to_start(self, data_source: ReadbackDataHandler) -> None:
|
|
243
|
+
"""Wait until the move starts, mirroring the queue wait behavior of live table updates."""
|
|
244
|
+
if self.scan_queue_request is None or self.scan_queue_request.queue is None:
|
|
245
|
+
return
|
|
246
|
+
|
|
247
|
+
while True:
|
|
248
|
+
if self.scan_queue_request is None or self.scan_queue_request.queue is None:
|
|
249
|
+
return
|
|
250
|
+
|
|
251
|
+
queue = self.scan_queue_request.queue
|
|
252
|
+
check_alarms(self.bec)
|
|
253
|
+
scan_state = self._check_scan_state()
|
|
254
|
+
if scan_state == ScanState.WAIT:
|
|
255
|
+
time.sleep(0.05)
|
|
256
|
+
continue
|
|
257
|
+
|
|
258
|
+
queue_position = getattr(queue, "queue_position", None)
|
|
259
|
+
|
|
260
|
+
if queue_position is None:
|
|
261
|
+
if data_source.done():
|
|
262
|
+
return
|
|
263
|
+
time.sleep(0.05)
|
|
264
|
+
continue
|
|
265
|
+
|
|
266
|
+
message = pending_queue_message(
|
|
267
|
+
bec=self.bec, queue=queue, queue_position=queue_position
|
|
268
|
+
)
|
|
269
|
+
if message is None:
|
|
270
|
+
break
|
|
271
|
+
|
|
272
|
+
print(message, end="\r", flush=True)
|
|
273
|
+
time.sleep(0.1)
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/callbacks/utils.py
RENAMED
|
@@ -81,6 +81,69 @@ def evaluate_scan_state(
|
|
|
81
81
|
return ScanState.CONTINUE
|
|
82
82
|
|
|
83
83
|
|
|
84
|
+
def pending_queue_message(
|
|
85
|
+
*, bec: BECClient, queue: QueueItem, queue_position: int | None = None
|
|
86
|
+
) -> str | None:
|
|
87
|
+
"""Return a user-facing message for queue states that are still waiting to start."""
|
|
88
|
+
if queue_position is None:
|
|
89
|
+
queue_position = getattr(queue, "queue_position", None)
|
|
90
|
+
if not isinstance(queue_position, int):
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
target_queue = None
|
|
94
|
+
target_queue_status = None
|
|
95
|
+
if bec.queue is not None:
|
|
96
|
+
current_scan_queue = getattr(bec.queue.queue_storage, "current_scan_queue", None)
|
|
97
|
+
if current_scan_queue is not None:
|
|
98
|
+
target_queue = bec.queue.get_default_scan_queue()
|
|
99
|
+
target_queue_status = current_scan_queue.get(target_queue)
|
|
100
|
+
|
|
101
|
+
request_blocks = getattr(queue, "request_blocks", None) or []
|
|
102
|
+
has_scan_request = any(
|
|
103
|
+
getattr(request_block, "is_scan", False) for request_block in request_blocks
|
|
104
|
+
)
|
|
105
|
+
if (
|
|
106
|
+
target_queue_status is not None
|
|
107
|
+
and getattr(target_queue_status, "status", None) == "LOCKED"
|
|
108
|
+
and has_scan_request
|
|
109
|
+
):
|
|
110
|
+
lock_info = [
|
|
111
|
+
f"{lock.identifier}: {lock.reason}\n"
|
|
112
|
+
for lock in getattr(target_queue_status, "locks", None) or []
|
|
113
|
+
]
|
|
114
|
+
return f"Scan is waiting for the lock to be released. Active locks: \n{''.join(lock_info)}"
|
|
115
|
+
|
|
116
|
+
owned_device_locks: list[str] = []
|
|
117
|
+
pending_device_locks: list[str] = []
|
|
118
|
+
get_device_lock_state = getattr(queue, "get_device_lock_state", None)
|
|
119
|
+
if callable(get_device_lock_state):
|
|
120
|
+
try:
|
|
121
|
+
owned_device_locks, pending_device_locks = get_device_lock_state()
|
|
122
|
+
except Exception:
|
|
123
|
+
owned_device_locks, pending_device_locks = [], []
|
|
124
|
+
if pending_device_locks:
|
|
125
|
+
pending_locks = ", ".join(pending_device_locks)
|
|
126
|
+
message = f"Scan is waiting for device locks to be released. Pending device locks: {pending_locks}."
|
|
127
|
+
if owned_device_locks:
|
|
128
|
+
owned_locks = ", ".join(owned_device_locks)
|
|
129
|
+
message += f" Currently owned device locks: {owned_locks}."
|
|
130
|
+
return message
|
|
131
|
+
|
|
132
|
+
if getattr(queue, "status", None) == "PENDING" and queue_position > 0:
|
|
133
|
+
if target_queue is not None:
|
|
134
|
+
status = getattr(target_queue_status, "status", getattr(queue, "status", None))
|
|
135
|
+
return (
|
|
136
|
+
"Scan is enqueued and is waiting for execution. "
|
|
137
|
+
f"Current position in queue {target_queue}: {queue_position + 1}. Queue status: {status}."
|
|
138
|
+
)
|
|
139
|
+
return (
|
|
140
|
+
"Scan is enqueued and is waiting for execution. "
|
|
141
|
+
f"Current position in queue: {queue_position + 1}."
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
|
|
84
147
|
def _restart_signal(
|
|
85
148
|
*, scan_item: ScanItem | None = None, queue: QueueItem | None = None
|
|
86
149
|
) -> messages.ScanQueueMessage | None:
|
|
@@ -147,6 +147,41 @@ def test_live_updates_process_queue_running(ipython_live_updates_with_mocked_liv
|
|
|
147
147
|
assert res is True
|
|
148
148
|
|
|
149
149
|
|
|
150
|
+
@pytest.mark.timeout(20)
|
|
151
|
+
def test_live_updates_process_queue_waiting_for_device_locks(
|
|
152
|
+
ipython_live_updates_with_mocked_live, queue_elements
|
|
153
|
+
):
|
|
154
|
+
live_updates, _ = ipython_live_updates_with_mocked_live
|
|
155
|
+
client = live_updates.client
|
|
156
|
+
queue, request_block, request_msg = queue_elements
|
|
157
|
+
|
|
158
|
+
request_block.pending_device_locks = ["samx"]
|
|
159
|
+
queue = QueueItem(
|
|
160
|
+
scan_manager=client.queue,
|
|
161
|
+
queue_id="queue_id",
|
|
162
|
+
request_blocks=[request_block],
|
|
163
|
+
status="RUNNING",
|
|
164
|
+
active_request_block=request_block,
|
|
165
|
+
scan_id=["scan_id"],
|
|
166
|
+
)
|
|
167
|
+
client.queue.queue_storage.current_scan_queue = {
|
|
168
|
+
"primary": messages.ScanQueueStatus(info=[], status="RUNNING")
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
with (
|
|
172
|
+
mock.patch.object(queue, "_update_with_buffer"),
|
|
173
|
+
mock.patch(
|
|
174
|
+
"bec_lib.queue_items.QueueItem.queue_position", new_callable=mock.PropertyMock
|
|
175
|
+
) as queue_pos,
|
|
176
|
+
mock.patch.object(live_updates, "_process_pending_queue_element") as process_pending,
|
|
177
|
+
):
|
|
178
|
+
queue_pos.return_value = 0
|
|
179
|
+
|
|
180
|
+
assert live_updates._process_queue(queue, request_msg, "req_id") is False
|
|
181
|
+
|
|
182
|
+
process_pending.assert_called_once_with(queue)
|
|
183
|
+
|
|
184
|
+
|
|
150
185
|
def test_live_updates_process_queue_cancelled_pending_request_raises_interruption(bec_client_mock):
|
|
151
186
|
client = bec_client_mock
|
|
152
187
|
live_updates = IPythonLiveUpdates(client)
|
|
@@ -1016,3 +1051,137 @@ def test_process_pending_queue_element_locked_then_position(
|
|
|
1016
1051
|
assert "user123" in panel_renderable
|
|
1017
1052
|
# Should not show position info
|
|
1018
1053
|
assert "position in queue" not in panel_renderable
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
@pytest.mark.timeout(20)
|
|
1057
|
+
def test_process_pending_queue_element_waiting_for_device_locks(
|
|
1058
|
+
ipython_live_updates_with_mocked_live, queue_elements
|
|
1059
|
+
):
|
|
1060
|
+
live_updates, mock_live = ipython_live_updates_with_mocked_live
|
|
1061
|
+
client = live_updates.client
|
|
1062
|
+
queue, request_block, _ = queue_elements
|
|
1063
|
+
|
|
1064
|
+
request_block.pending_device_locks = ["bpm4i", "samz"]
|
|
1065
|
+
request_block.owned_device_locks = ["samx"]
|
|
1066
|
+
queue.active_request_block = request_block
|
|
1067
|
+
|
|
1068
|
+
running_scan_queue_status = messages.ScanQueueStatus(info=[], status="RUNNING")
|
|
1069
|
+
client.queue.queue_storage.current_scan_queue = {"primary": running_scan_queue_status}
|
|
1070
|
+
|
|
1071
|
+
with mock.patch(
|
|
1072
|
+
"bec_lib.queue_items.QueueItem.queue_position", new_callable=mock.PropertyMock
|
|
1073
|
+
) as queue_pos:
|
|
1074
|
+
queue_pos.return_value = 0
|
|
1075
|
+
|
|
1076
|
+
live_updates._process_pending_queue_element(queue)
|
|
1077
|
+
|
|
1078
|
+
mock_live.assert_called_once()
|
|
1079
|
+
call_args = mock_live.call_args[0][0]
|
|
1080
|
+
panel_renderable = call_args.renderable
|
|
1081
|
+
assert "waiting for device locks" in panel_renderable
|
|
1082
|
+
assert "bpm4i, samz" in panel_renderable
|
|
1083
|
+
assert "Currently owned device locks: samx" in panel_renderable
|
|
1084
|
+
|
|
1085
|
+
|
|
1086
|
+
@pytest.mark.timeout(20)
|
|
1087
|
+
def test_process_pending_queue_element_waiting_for_device_locks_without_scan_id(
|
|
1088
|
+
ipython_live_updates_with_mocked_live, queue_elements
|
|
1089
|
+
):
|
|
1090
|
+
live_updates, mock_live = ipython_live_updates_with_mocked_live
|
|
1091
|
+
client = live_updates.client
|
|
1092
|
+
queue, request_block, _ = queue_elements
|
|
1093
|
+
|
|
1094
|
+
request_block.pending_device_locks = ["bpm4i", "samz"]
|
|
1095
|
+
request_block.owned_device_locks = ["samx"]
|
|
1096
|
+
queue.active_request_block = request_block
|
|
1097
|
+
queue.scan_ids = [None]
|
|
1098
|
+
|
|
1099
|
+
running_scan_queue_status = messages.ScanQueueStatus(info=[], status="RUNNING")
|
|
1100
|
+
client.queue.queue_storage.current_scan_queue = {"primary": running_scan_queue_status}
|
|
1101
|
+
|
|
1102
|
+
with mock.patch(
|
|
1103
|
+
"bec_lib.queue_items.QueueItem.queue_position", new_callable=mock.PropertyMock
|
|
1104
|
+
) as queue_pos:
|
|
1105
|
+
queue_pos.return_value = 0
|
|
1106
|
+
|
|
1107
|
+
live_updates._process_pending_queue_element(queue)
|
|
1108
|
+
|
|
1109
|
+
mock_live.assert_called_once()
|
|
1110
|
+
call_args = mock_live.call_args[0][0]
|
|
1111
|
+
panel_renderable = call_args.renderable
|
|
1112
|
+
assert "waiting for device locks" in panel_renderable
|
|
1113
|
+
assert "bpm4i, samz" in panel_renderable
|
|
1114
|
+
assert "Currently owned device locks: samx" in panel_renderable
|
|
1115
|
+
|
|
1116
|
+
|
|
1117
|
+
@pytest.mark.timeout(20)
|
|
1118
|
+
def test_process_pending_queue_element_waiting_for_device_locks_beats_queue_position(
|
|
1119
|
+
ipython_live_updates_with_mocked_live, queue_elements
|
|
1120
|
+
):
|
|
1121
|
+
live_updates, mock_live = ipython_live_updates_with_mocked_live
|
|
1122
|
+
client = live_updates.client
|
|
1123
|
+
queue, request_block, _ = queue_elements
|
|
1124
|
+
|
|
1125
|
+
request_block.pending_device_locks = ["bpm4i"]
|
|
1126
|
+
queue.active_request_block = request_block
|
|
1127
|
+
|
|
1128
|
+
running_scan_queue_status = messages.ScanQueueStatus(info=[], status="RUNNING")
|
|
1129
|
+
client.queue.queue_storage.current_scan_queue = {"primary": running_scan_queue_status}
|
|
1130
|
+
|
|
1131
|
+
with mock.patch(
|
|
1132
|
+
"bec_lib.queue_items.QueueItem.queue_position", new_callable=mock.PropertyMock
|
|
1133
|
+
) as queue_pos:
|
|
1134
|
+
queue_pos.return_value = 2
|
|
1135
|
+
|
|
1136
|
+
live_updates._process_pending_queue_element(queue)
|
|
1137
|
+
|
|
1138
|
+
call_args = mock_live.call_args[0][0]
|
|
1139
|
+
panel_renderable = call_args.renderable
|
|
1140
|
+
assert "waiting for device locks" in panel_renderable
|
|
1141
|
+
assert "position in queue" not in panel_renderable
|
|
1142
|
+
|
|
1143
|
+
|
|
1144
|
+
@pytest.mark.timeout(20)
|
|
1145
|
+
def test_process_pending_queue_element_waiting_for_device_locks_on_secondary_queue(
|
|
1146
|
+
ipython_live_updates_with_mocked_live, queue_elements
|
|
1147
|
+
):
|
|
1148
|
+
live_updates, mock_live = ipython_live_updates_with_mocked_live
|
|
1149
|
+
client = live_updates.client
|
|
1150
|
+
queue, request_block, _ = queue_elements
|
|
1151
|
+
|
|
1152
|
+
queue.queue_id = "secondary_queue_id"
|
|
1153
|
+
request_block.pending_device_locks = ["bpm4i"]
|
|
1154
|
+
request_block.owned_device_locks = ["samx"]
|
|
1155
|
+
queue.active_request_block = request_block
|
|
1156
|
+
|
|
1157
|
+
client.queue.queue_storage.current_scan_queue = {
|
|
1158
|
+
"primary": messages.ScanQueueStatus(info=[], status="RUNNING"),
|
|
1159
|
+
"secondary": messages.ScanQueueStatus(
|
|
1160
|
+
info=[
|
|
1161
|
+
messages.QueueInfoEntry(
|
|
1162
|
+
queue_id="secondary_queue_id",
|
|
1163
|
+
scan_id=["scan_id"],
|
|
1164
|
+
is_scan=[True],
|
|
1165
|
+
request_blocks=[request_block],
|
|
1166
|
+
scan_number=[1],
|
|
1167
|
+
status="PENDING",
|
|
1168
|
+
active_request_block=request_block,
|
|
1169
|
+
)
|
|
1170
|
+
],
|
|
1171
|
+
status="RUNNING",
|
|
1172
|
+
),
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
with mock.patch(
|
|
1176
|
+
"bec_lib.queue_items.QueueItem.queue_position", new_callable=mock.PropertyMock
|
|
1177
|
+
) as queue_pos:
|
|
1178
|
+
queue_pos.return_value = 0
|
|
1179
|
+
|
|
1180
|
+
live_updates._process_pending_queue_element(queue)
|
|
1181
|
+
|
|
1182
|
+
mock_live.assert_called_once()
|
|
1183
|
+
call_args = mock_live.call_args[0][0]
|
|
1184
|
+
panel_renderable = call_args.renderable
|
|
1185
|
+
assert "waiting for device locks" in panel_renderable
|
|
1186
|
+
assert "bpm4i" in panel_renderable
|
|
1187
|
+
assert "Currently owned device locks: samx" in panel_renderable
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/client_tests/test_live_table.py
RENAMED
|
@@ -130,6 +130,39 @@ class TestLiveTable:
|
|
|
130
130
|
with mock.patch.object(client.queue.queue_storage, "find_queue_item_by_requestID"):
|
|
131
131
|
live_update.wait_for_request_acceptance()
|
|
132
132
|
|
|
133
|
+
def test_wait_for_scan_to_start_prints_pending_queue_message(self, client_with_grid_scan):
|
|
134
|
+
client, request_msg = client_with_grid_scan
|
|
135
|
+
live_update = LiveUpdatesTable(
|
|
136
|
+
client, {"scan_progress": {"points": 10, "show_table": True}}, request_msg
|
|
137
|
+
)
|
|
138
|
+
client.queue.queue_storage.current_scan_queue = None
|
|
139
|
+
|
|
140
|
+
class QueueStub:
|
|
141
|
+
def __init__(self):
|
|
142
|
+
self.status = "PENDING"
|
|
143
|
+
self.request_blocks = []
|
|
144
|
+
self._positions = iter([2, 0])
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def queue_position(self):
|
|
148
|
+
return next(self._positions)
|
|
149
|
+
|
|
150
|
+
def get_device_lock_state(self):
|
|
151
|
+
return ([], [])
|
|
152
|
+
|
|
153
|
+
live_update.scan_item = mock.MagicMock(
|
|
154
|
+
queue=QueueStub(), status="open", scan_id="scan_id", scan_number=1
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
with mock.patch("bec_ipython_client.callbacks.live_table.print") as mock_print:
|
|
158
|
+
live_update.wait_for_scan_to_start()
|
|
159
|
+
|
|
160
|
+
mock_print.assert_called_once_with(
|
|
161
|
+
"Scan is enqueued and is waiting for execution. Current position in queue: 3.",
|
|
162
|
+
end="\r",
|
|
163
|
+
flush=True,
|
|
164
|
+
)
|
|
165
|
+
|
|
133
166
|
@pytest.mark.timeout(20)
|
|
134
167
|
def test_run_update(self, bec_client_mock, scan_item):
|
|
135
168
|
request_msg = messages.ScanQueueMessage(
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/client_tests/test_move_callback.py
RENAMED
|
@@ -8,7 +8,7 @@ from bec_ipython_client.callbacks.move_device import (
|
|
|
8
8
|
LiveUpdatesReadbackProgressbar,
|
|
9
9
|
ReadbackDataHandler,
|
|
10
10
|
)
|
|
11
|
-
from bec_ipython_client.callbacks.utils import ScanState
|
|
11
|
+
from bec_ipython_client.callbacks.utils import ScanState, pending_queue_message
|
|
12
12
|
from bec_lib import messages
|
|
13
13
|
from bec_lib.bec_errors import ScanInterruption, ScanRestart
|
|
14
14
|
from bec_lib.endpoints import MessageEndpoints
|
|
@@ -218,6 +218,133 @@ def test_move_callback_check_scan_state_restart_without_restart_message_is_nonbl
|
|
|
218
218
|
assert live_update._check_scan_state() == ScanState.WAIT
|
|
219
219
|
|
|
220
220
|
|
|
221
|
+
def test_move_callback_pending_queue_message_for_device_locks(bec_client_mock):
|
|
222
|
+
bec_client_mock.queue.queue_storage.current_scan_queue = None
|
|
223
|
+
request_block = mock.MagicMock(is_scan=True)
|
|
224
|
+
queue = mock.MagicMock(status="RUNNING", request_blocks=[request_block])
|
|
225
|
+
queue.get_device_lock_state.return_value = (["samz"], ["samx", "bpm4i"])
|
|
226
|
+
|
|
227
|
+
message = pending_queue_message(bec=bec_client_mock, queue=queue, queue_position=0)
|
|
228
|
+
|
|
229
|
+
assert (
|
|
230
|
+
message
|
|
231
|
+
== "Scan is waiting for device locks to be released. Pending device locks: samx, bpm4i. "
|
|
232
|
+
"Currently owned device locks: samz."
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def test_move_callback_pending_queue_message_for_queue_position(bec_client_mock):
|
|
237
|
+
bec_client_mock.queue.queue_storage.current_scan_queue = None
|
|
238
|
+
queue = mock.MagicMock(status="PENDING", request_blocks=[])
|
|
239
|
+
queue.get_device_lock_state.return_value = ([], [])
|
|
240
|
+
|
|
241
|
+
message = pending_queue_message(bec=bec_client_mock, queue=queue, queue_position=2)
|
|
242
|
+
|
|
243
|
+
assert message == "Scan is enqueued and is waiting for execution. Current position in queue: 3."
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def test_move_callback_wait_for_move_to_start_prints_pending_message(bec_client_mock):
|
|
247
|
+
request = messages.ScanQueueMessage(
|
|
248
|
+
scan_type="umv",
|
|
249
|
+
parameter={"args": {"samx": [10]}, "kwargs": {"relative": True}},
|
|
250
|
+
metadata={"RID": "something"},
|
|
251
|
+
)
|
|
252
|
+
live_update = LiveUpdatesReadbackProgressbar(bec=bec_client_mock, request=request)
|
|
253
|
+
bec_client_mock.queue.queue_storage.current_scan_queue = None
|
|
254
|
+
|
|
255
|
+
class QueueStub:
|
|
256
|
+
def __init__(self):
|
|
257
|
+
self.status = "PENDING"
|
|
258
|
+
self.request_blocks = []
|
|
259
|
+
self._positions = iter([2, 0])
|
|
260
|
+
|
|
261
|
+
@property
|
|
262
|
+
def queue_position(self):
|
|
263
|
+
return next(self._positions)
|
|
264
|
+
|
|
265
|
+
def get_device_lock_state(self):
|
|
266
|
+
return ([], [])
|
|
267
|
+
|
|
268
|
+
queue = QueueStub()
|
|
269
|
+
live_update.scan_queue_request = mock.MagicMock(queue=queue)
|
|
270
|
+
data_source = mock.MagicMock()
|
|
271
|
+
data_source.done.return_value = False
|
|
272
|
+
|
|
273
|
+
with (
|
|
274
|
+
mock.patch("bec_ipython_client.callbacks.move_device.print") as mock_print,
|
|
275
|
+
mock.patch("bec_ipython_client.callbacks.move_device.time.sleep") as sleep,
|
|
276
|
+
mock.patch.object(live_update, "_check_scan_state", return_value=ScanState.CONTINUE),
|
|
277
|
+
):
|
|
278
|
+
live_update._wait_for_move_to_start(data_source)
|
|
279
|
+
|
|
280
|
+
mock_print.assert_called_once_with(
|
|
281
|
+
"Scan is enqueued and is waiting for execution. Current position in queue: 3.",
|
|
282
|
+
end="\r",
|
|
283
|
+
flush=True,
|
|
284
|
+
)
|
|
285
|
+
sleep.assert_called()
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def test_move_callback_wait_for_move_to_start_returns_when_queue_entry_is_gone(bec_client_mock):
|
|
289
|
+
request = messages.ScanQueueMessage(
|
|
290
|
+
scan_type="umv",
|
|
291
|
+
parameter={"args": {"samx": [10]}, "kwargs": {"relative": True}},
|
|
292
|
+
metadata={"RID": "something"},
|
|
293
|
+
)
|
|
294
|
+
live_update = LiveUpdatesReadbackProgressbar(bec=bec_client_mock, request=request)
|
|
295
|
+
queue = mock.MagicMock()
|
|
296
|
+
queue.queue_position = None
|
|
297
|
+
live_update.scan_queue_request = mock.MagicMock(queue=queue)
|
|
298
|
+
data_source = mock.MagicMock()
|
|
299
|
+
data_source.done.return_value = True
|
|
300
|
+
|
|
301
|
+
with (
|
|
302
|
+
mock.patch("bec_ipython_client.callbacks.move_device.print") as mock_print,
|
|
303
|
+
mock.patch("bec_ipython_client.callbacks.move_device.time.sleep") as sleep,
|
|
304
|
+
mock.patch.object(live_update, "_check_scan_state", return_value=ScanState.CONTINUE),
|
|
305
|
+
):
|
|
306
|
+
live_update._wait_for_move_to_start(data_source)
|
|
307
|
+
|
|
308
|
+
mock_print.assert_not_called()
|
|
309
|
+
sleep.assert_not_called()
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def test_move_callback_wait_for_move_to_start_keeps_waiting_when_not_yet_registered(
|
|
313
|
+
bec_client_mock,
|
|
314
|
+
):
|
|
315
|
+
request = messages.ScanQueueMessage(
|
|
316
|
+
scan_type="umv",
|
|
317
|
+
parameter={"args": {"samx": [10]}, "kwargs": {"relative": True}},
|
|
318
|
+
metadata={"RID": "something"},
|
|
319
|
+
)
|
|
320
|
+
live_update = LiveUpdatesReadbackProgressbar(bec=bec_client_mock, request=request)
|
|
321
|
+
|
|
322
|
+
class QueueStub:
|
|
323
|
+
def __init__(self):
|
|
324
|
+
self._positions = iter([None, None, 0])
|
|
325
|
+
|
|
326
|
+
@property
|
|
327
|
+
def queue_position(self):
|
|
328
|
+
return next(self._positions)
|
|
329
|
+
|
|
330
|
+
live_update.scan_queue_request = mock.MagicMock(queue=QueueStub())
|
|
331
|
+
data_source = mock.MagicMock()
|
|
332
|
+
data_source.done.return_value = False
|
|
333
|
+
|
|
334
|
+
with (
|
|
335
|
+
mock.patch("bec_ipython_client.callbacks.move_device.print") as mock_print,
|
|
336
|
+
mock.patch("bec_ipython_client.callbacks.move_device.time.sleep") as sleep,
|
|
337
|
+
mock.patch.object(live_update, "_check_scan_state", return_value=ScanState.CONTINUE),
|
|
338
|
+
mock.patch(
|
|
339
|
+
"bec_ipython_client.callbacks.move_device.pending_queue_message", return_value=None
|
|
340
|
+
),
|
|
341
|
+
):
|
|
342
|
+
live_update._wait_for_move_to_start(data_source)
|
|
343
|
+
|
|
344
|
+
mock_print.assert_not_called()
|
|
345
|
+
assert sleep.call_count == 2
|
|
346
|
+
|
|
347
|
+
|
|
221
348
|
def test_move_callback_run_waits_for_restart_message_before_exiting(bec_client_mock):
|
|
222
349
|
client = bec_client_mock
|
|
223
350
|
request = messages.ScanQueueMessage(
|
|
@@ -260,6 +260,47 @@ def test_scan_abort(bec_ipython_client_fixture: BECIPythonClient):
|
|
|
260
260
|
assert scan_number_end == scan_number_start + 2
|
|
261
261
|
|
|
262
262
|
|
|
263
|
+
@pytest.mark.timeout(100)
|
|
264
|
+
def test_umv_ctrl_c_stops_motion(bec_ipython_client_fixture: BECIPythonClient):
|
|
265
|
+
def send_abort_after_delay():
|
|
266
|
+
time.sleep(1)
|
|
267
|
+
_thread.interrupt_main()
|
|
268
|
+
|
|
269
|
+
bec = bec_ipython_client_fixture
|
|
270
|
+
bec.metadata.update({"unit_test": "test_umv_ctrl_c_stops_motion"})
|
|
271
|
+
scans = bec.scans
|
|
272
|
+
dev = bec.device_manager.devices
|
|
273
|
+
original_velocity = dev.samx.velocity.get()
|
|
274
|
+
aborted_scan = False
|
|
275
|
+
|
|
276
|
+
try:
|
|
277
|
+
dev.samx.velocity.set(100).wait()
|
|
278
|
+
scans.umv(dev.samx, 0, relative=False)
|
|
279
|
+
dev.samx.velocity.set(1).wait()
|
|
280
|
+
|
|
281
|
+
threading.Thread(target=send_abort_after_delay, daemon=True).start()
|
|
282
|
+
try:
|
|
283
|
+
scans.umv(dev.samx, 40, relative=False)
|
|
284
|
+
except ScanInterruption:
|
|
285
|
+
aborted_scan = True
|
|
286
|
+
|
|
287
|
+
assert aborted_scan is True
|
|
288
|
+
|
|
289
|
+
timeout = time.time() + 10
|
|
290
|
+
while dev.samx.motor_is_moving.get() and time.time() < timeout:
|
|
291
|
+
time.sleep(0.1)
|
|
292
|
+
|
|
293
|
+
assert not dev.samx.motor_is_moving.get()
|
|
294
|
+
assert dev.samx.readback.get() < 40
|
|
295
|
+
finally:
|
|
296
|
+
if dev.samx.motor_is_moving.get():
|
|
297
|
+
dev.samx.stop()
|
|
298
|
+
timeout = time.time() + 10
|
|
299
|
+
while dev.samx.motor_is_moving.get() and time.time() < timeout:
|
|
300
|
+
time.sleep(0.1)
|
|
301
|
+
dev.samx.velocity.set(original_velocity).wait()
|
|
302
|
+
|
|
303
|
+
|
|
263
304
|
@pytest.mark.timeout(100)
|
|
264
305
|
def test_limit_error(bec_ipython_client_fixture):
|
|
265
306
|
bec = bec_ipython_client_fixture
|
|
File without changes
|
|
File without changes
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/beamline_mixin.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/callbacks/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/plugins/SLS/__init__.py
RENAMED
|
File without changes
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/plugins/SLS/sls_info.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/bec_ipython_client/plugins/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/client_tests/test_beamline_mixins.py
RENAMED
|
File without changes
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/client_tests/test_bec_client.py
RENAMED
|
File without changes
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/client_tests/test_device_progress.py
RENAMED
|
File without changes
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/client_tests/test_pretty_table.py
RENAMED
|
File without changes
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/client_tests/test_signals.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/end-2-end/test_actors_e2e.py
RENAMED
|
File without changes
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/end-2-end/test_procedures_e2e.py
RENAMED
|
File without changes
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/end-2-end/test_scans_lib_e2e.py
RENAMED
|
File without changes
|
{bec_ipython_client-3.151.2 → bec_ipython_client-3.152.0}/tests/end-2-end/test_scans_v4_lib_e2e.py
RENAMED
|
File without changes
|