lima2.client 3.0a1__tar.gz → 3.1a0__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.
- {lima2_client-3.0a1/src/lima2_client.egg-info → lima2_client-3.1a0}/PKG-INFO +1 -1
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/client.py +41 -1
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/pipeline.py +16 -16
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/topology.py +11 -6
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/master_file/generate.py +24 -59
- lima2_client-3.1a0/src/lima2/master_file/py.typed +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0/src/lima2_client.egg-info}/PKG-INFO +1 -1
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2_client.egg-info/SOURCES.txt +1 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/LICENSE +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/README.md +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/pyproject.toml +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/setup.cfg +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/finite_state_machine/__init__.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/finite_state_machine/draw_state_diagram.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/finite_state_machine/examples/__init__.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/finite_state_machine/examples/async_github_pull_request.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/finite_state_machine/examples/async_turnstile.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/finite_state_machine/examples/boolean_field.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/finite_state_machine/examples/github_pull_request.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/finite_state_machine/examples/turnstile.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/finite_state_machine/exceptions.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/finite_state_machine/state_machine.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/__init__.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/convert.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/detector.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/devencoded/__init__.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/devencoded/dense_frame.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/devencoded/exception.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/devencoded/smx_sparse_frame.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/devencoded/sparse_frame.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/devencoded/structured_array.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/pipelines/__init__.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/pipelines/failing.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/pipelines/legacy.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/pipelines/smx.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/pipelines/xpcs.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/progress_counter.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/py.typed +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/shell.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/state_machine.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/client/utils.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/server/__init__.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/server/control.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/server/receiver.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2/server/timer.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2_client.egg-info/dependency_links.txt +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2_client.egg-info/entry_points.txt +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2_client.egg-info/requires.txt +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1a0}/src/lima2_client.egg-info/top_level.txt +0 -0
|
@@ -34,6 +34,8 @@ from lima2.client.progress_counter import ProgressCounter, SingleCounter
|
|
|
34
34
|
from lima2.client.topology import TopologyKind
|
|
35
35
|
from lima2.client.utils import docstring_from
|
|
36
36
|
|
|
37
|
+
from lima2.master_file import generate as mfg
|
|
38
|
+
|
|
37
39
|
logger = logging.getLogger(__name__)
|
|
38
40
|
|
|
39
41
|
|
|
@@ -77,6 +79,16 @@ class Client:
|
|
|
77
79
|
|
|
78
80
|
self._tango_db = tg.Database()
|
|
79
81
|
|
|
82
|
+
# Show master file generation info messages
|
|
83
|
+
mfg.logger.setLevel(logging.INFO)
|
|
84
|
+
|
|
85
|
+
self.master_file_tasks: dict[UUID, dict[str, mfg.MasterFileTask]] = {}
|
|
86
|
+
"""Master file generation tasks for each pipeline UUID.
|
|
87
|
+
|
|
88
|
+
Each entry corresponds to a pipeline, and holds a dict structured like
|
|
89
|
+
{saving channel name: list of tasks}.
|
|
90
|
+
"""
|
|
91
|
+
|
|
80
92
|
@classmethod
|
|
81
93
|
def from_yaml(
|
|
82
94
|
cls,
|
|
@@ -147,7 +159,11 @@ class Client:
|
|
|
147
159
|
####################################################################################
|
|
148
160
|
|
|
149
161
|
def prepare_acq(
|
|
150
|
-
self,
|
|
162
|
+
self,
|
|
163
|
+
uuid: UUID,
|
|
164
|
+
ctrl_params: dict,
|
|
165
|
+
acq_params: dict,
|
|
166
|
+
proc_params: dict,
|
|
151
167
|
) -> None:
|
|
152
168
|
"""Prepare acquisition with a given uuid and set of params."""
|
|
153
169
|
|
|
@@ -175,16 +191,40 @@ class Client:
|
|
|
175
191
|
|
|
176
192
|
self.current_pipeline.register_on_error(on_error)
|
|
177
193
|
|
|
194
|
+
# Remove master file tasks of previous acquisitions if done or not started
|
|
195
|
+
for prev_uuid in list(self.master_file_tasks.keys()):
|
|
196
|
+
tasks = self.master_file_tasks[prev_uuid].values()
|
|
197
|
+
if all([not task.started() or task.ready() for task in tasks]):
|
|
198
|
+
logger.debug(
|
|
199
|
+
"All master file tasks done (or never started) for "
|
|
200
|
+
f"{prev_uuid} -> cleaning up"
|
|
201
|
+
)
|
|
202
|
+
self.master_file_tasks.pop(prev_uuid)
|
|
203
|
+
else:
|
|
204
|
+
logger.debug(f"Not all master file tasks done for {prev_uuid}...")
|
|
205
|
+
|
|
206
|
+
self.master_file_tasks[uuid] = mfg.enable_generation(
|
|
207
|
+
pipeline=self.current_pipeline,
|
|
208
|
+
num_frames=ctrl_params["acq"]["nb_frames"],
|
|
209
|
+
ctrl_params=ctl,
|
|
210
|
+
recv_params=acq[0],
|
|
211
|
+
proc_params=proc[0],
|
|
212
|
+
)
|
|
213
|
+
|
|
178
214
|
@docstring_from(Detector.start_acq)
|
|
179
215
|
def start_acq(self) -> None:
|
|
180
216
|
pipeline = self.current_pipeline
|
|
181
217
|
pipeline.register_on_finished(pipeline.unregister_events)
|
|
218
|
+
|
|
182
219
|
try:
|
|
183
220
|
self.detector.start_acq()
|
|
184
221
|
except Exception:
|
|
185
222
|
pipeline.unregister_events()
|
|
186
223
|
raise
|
|
187
224
|
|
|
225
|
+
for task in self.master_file_tasks[self.current_pipeline.uuid].values():
|
|
226
|
+
task.start()
|
|
227
|
+
|
|
188
228
|
@docstring_from(Detector.trigger)
|
|
189
229
|
def trigger(self) -> None:
|
|
190
230
|
self.detector.trigger()
|
|
@@ -379,13 +379,13 @@ class Pipeline:
|
|
|
379
379
|
|
|
380
380
|
# Pop stats for new frames from each receiver and concatenate, mixing in cached data.
|
|
381
381
|
# Data comes from server as 1D array with size (num_frames * num_rois)
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
]
|
|
388
|
-
|
|
382
|
+
def pop_data(dev) -> np.ndarray:
|
|
383
|
+
return structured_array.decode(dev.popRoiStatistics(), dtype)
|
|
384
|
+
|
|
385
|
+
num_rcvs = len(self._devs)
|
|
386
|
+
with ThreadPoolExecutor(max_workers=num_rcvs) as pool:
|
|
387
|
+
flat_cache = [cache.flatten()]
|
|
388
|
+
new_data = np.concatenate(flat_cache + list(pool.map(pop_data, self._devs)))
|
|
389
389
|
|
|
390
390
|
# Guess number of frames from the data
|
|
391
391
|
num_frames = np.unique(new_data["frame_idx"]).size
|
|
@@ -473,13 +473,13 @@ class Pipeline:
|
|
|
473
473
|
|
|
474
474
|
# Pop profiles for new frames from each receiver and concatenate, mixing in cached data.
|
|
475
475
|
# Data comes from server as 1D array with size (num_frames * sum(roi_lengths))
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
]
|
|
482
|
-
|
|
476
|
+
def pop_data(dev) -> np.ndarray:
|
|
477
|
+
return structured_array.decode(dev.popRoiProfiles(), dtype)
|
|
478
|
+
|
|
479
|
+
num_rcvs = len(self._devs)
|
|
480
|
+
with ThreadPoolExecutor(max_workers=num_rcvs) as pool:
|
|
481
|
+
flat_cache = [cache.flatten()]
|
|
482
|
+
new_data = np.concatenate(flat_cache + list(pool.map(pop_data, self._devs)))
|
|
483
483
|
|
|
484
484
|
# Reshape into (num_frames, sum(roi_lengths))
|
|
485
485
|
new_data = new_data.reshape((-1, sum(roi_lengths)))
|
|
@@ -654,9 +654,9 @@ class Pipeline:
|
|
|
654
654
|
"no associated label"
|
|
655
655
|
)
|
|
656
656
|
|
|
657
|
-
def last_frames(rcv_idx):
|
|
657
|
+
def last_frames(rcv_idx: int) -> int:
|
|
658
658
|
last_frames_str = self._devs[rcv_idx].last_frames
|
|
659
|
-
return json.loads(last_frames_str)[f"{source.label}_idx"]
|
|
659
|
+
return int(json.loads(last_frames_str)[f"{source.label}_idx"])
|
|
660
660
|
|
|
661
661
|
num_rcvs = len(self._devs)
|
|
662
662
|
with ThreadPoolExecutor(max_workers=num_rcvs) as pool:
|
|
@@ -9,6 +9,7 @@ import copy
|
|
|
9
9
|
from abc import ABC, abstractmethod
|
|
10
10
|
from collections.abc import Callable
|
|
11
11
|
from enum import Enum
|
|
12
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
12
13
|
|
|
13
14
|
import tango as tg
|
|
14
15
|
from beartype import beartype
|
|
@@ -107,18 +108,22 @@ class UnevenTopology(Topology):
|
|
|
107
108
|
If the frame is found on multiple devices (e.g. frame_idx=-1),
|
|
108
109
|
return the one found last (not necessarily the most recent).
|
|
109
110
|
"""
|
|
110
|
-
frame: EncodedFrame | None = None
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
def get_frame(rcv_idx) -> EncodedFrame | None:
|
|
113
113
|
try:
|
|
114
|
-
frame = getters[
|
|
114
|
+
frame: EncodedFrame = getters[rcv_idx](frame_idx)
|
|
115
|
+
return frame
|
|
115
116
|
except tg.DevFailed:
|
|
116
|
-
|
|
117
|
+
return None
|
|
117
118
|
|
|
118
|
-
|
|
119
|
+
num_rcvs = self.num_receivers
|
|
120
|
+
with ThreadPoolExecutor(max_workers=num_rcvs) as pool:
|
|
121
|
+
frames = [f for f in pool.map(get_frame, range(num_rcvs)) if f is not None]
|
|
122
|
+
|
|
123
|
+
if len(frames) == 0:
|
|
119
124
|
raise IndexError(f"Frame {frame_idx} not available from any receiver")
|
|
120
125
|
|
|
121
|
-
return
|
|
126
|
+
return frames[0]
|
|
122
127
|
|
|
123
128
|
|
|
124
129
|
def distribute_acq(
|
|
@@ -13,7 +13,6 @@ processing pipelines finish.
|
|
|
13
13
|
import json
|
|
14
14
|
import logging
|
|
15
15
|
import os
|
|
16
|
-
import sys
|
|
17
16
|
import time
|
|
18
17
|
import traceback
|
|
19
18
|
from copy import deepcopy
|
|
@@ -25,6 +24,7 @@ import h5py
|
|
|
25
24
|
import numpy as np
|
|
26
25
|
|
|
27
26
|
import gevent
|
|
27
|
+
import gevent.event
|
|
28
28
|
|
|
29
29
|
from lima2.client.pipeline import FrameType, Pipeline
|
|
30
30
|
|
|
@@ -39,13 +39,19 @@ class LUTSource(Enum):
|
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
class MasterFileTask:
|
|
42
|
-
def __init__(self, task, end_event):
|
|
42
|
+
def __init__(self, task: gevent.Greenlet, end_event):
|
|
43
43
|
self.task = task
|
|
44
44
|
self.end_event = end_event
|
|
45
45
|
|
|
46
46
|
def ready(self):
|
|
47
47
|
return self.task.ready()
|
|
48
48
|
|
|
49
|
+
def start(self):
|
|
50
|
+
self.task.start()
|
|
51
|
+
|
|
52
|
+
def started(self) -> bool:
|
|
53
|
+
return self.task.started
|
|
54
|
+
|
|
49
55
|
def wait(self, timeout):
|
|
50
56
|
finished = gevent.wait([self.task], timeout=timeout)
|
|
51
57
|
return len(finished) > 0
|
|
@@ -60,45 +66,6 @@ class MasterFileTask:
|
|
|
60
66
|
self.task.join()
|
|
61
67
|
|
|
62
68
|
|
|
63
|
-
active_tasks = {}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def check_generation(
|
|
67
|
-
ctrl_params: dict, recv_params: dict, proc_params: dict
|
|
68
|
-
) -> list[dict]:
|
|
69
|
-
"""Check the parameters to be sent to prepare to ensure master file info"""
|
|
70
|
-
|
|
71
|
-
# group all active saving subsystems
|
|
72
|
-
active_saving_params: list[dict] = []
|
|
73
|
-
|
|
74
|
-
# first check receiver raw saving or SDK saving
|
|
75
|
-
for params in [recv_params, ctrl_params]:
|
|
76
|
-
if "saving" in params and params["saving"]["enabled"]:
|
|
77
|
-
active_saving_params.append(params["saving"])
|
|
78
|
-
break
|
|
79
|
-
|
|
80
|
-
# then check the saving streams of the current pipeline
|
|
81
|
-
for source, params in proc_params.items():
|
|
82
|
-
# Skip other subsystems
|
|
83
|
-
if not source.startswith("saving"):
|
|
84
|
-
continue
|
|
85
|
-
# Skip source if channel is disabled
|
|
86
|
-
if params["enabled"]:
|
|
87
|
-
active_saving_params.append(params)
|
|
88
|
-
|
|
89
|
-
# include detector frame_idx dataset in HDF5 files
|
|
90
|
-
for saving_params in active_saving_params:
|
|
91
|
-
if "include_frame_idx" in saving_params:
|
|
92
|
-
saving_params["include_frame_idx"] = True
|
|
93
|
-
|
|
94
|
-
# allow retreiving recv-det-frame-idx mapping
|
|
95
|
-
if "frame_idx_enabled" in proc_params:
|
|
96
|
-
logger.debug("Enabling frame_idx generation")
|
|
97
|
-
proc_params["frame_idx_enabled"] = True
|
|
98
|
-
|
|
99
|
-
return ctrl_params, recv_params, proc_params
|
|
100
|
-
|
|
101
|
-
|
|
102
69
|
def enable_generation(
|
|
103
70
|
pipeline: Pipeline,
|
|
104
71
|
num_frames: int,
|
|
@@ -107,7 +74,7 @@ def enable_generation(
|
|
|
107
74
|
proc_params: dict,
|
|
108
75
|
rr_offset: int = 0,
|
|
109
76
|
lut_source: LUTSource = LUTSource.AUTO,
|
|
110
|
-
) ->
|
|
77
|
+
) -> dict[str, MasterFileTask]:
|
|
111
78
|
"""Enable the generation of a master file for a given pipeline.
|
|
112
79
|
|
|
113
80
|
Must be called between Client.prepare_acq() and Client.start_acq(), so that the
|
|
@@ -244,31 +211,29 @@ def enable_generation(
|
|
|
244
211
|
logger.warning(
|
|
245
212
|
"Master file generation not enabled: no dense frames saving channel enabled."
|
|
246
213
|
)
|
|
247
|
-
return
|
|
214
|
+
return {}
|
|
248
215
|
|
|
249
216
|
def master_file_task(src, params) -> None:
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
logger.debug(f"On finished {src} master file generation")
|
|
255
|
-
except Exception as e:
|
|
256
|
-
logger.error("Error generating %s master file: %s", src, e)
|
|
257
|
-
traceback.print_exception(*sys.exc_info())
|
|
217
|
+
p = MasterFileGeneration.Params(**params)
|
|
218
|
+
gen = MasterFileGeneration(p)
|
|
219
|
+
gen.run()
|
|
220
|
+
logger.debug(f"On finished {src} master file generation")
|
|
258
221
|
|
|
259
|
-
tasks = {}
|
|
222
|
+
tasks: dict[str, MasterFileTask] = {}
|
|
260
223
|
|
|
261
224
|
for src, params in generate_params.items():
|
|
262
|
-
task = gevent.
|
|
225
|
+
task = gevent.Greenlet(master_file_task, src, params)
|
|
263
226
|
tasks[src] = MasterFileTask(task, end_event)
|
|
264
227
|
|
|
265
|
-
def
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
228
|
+
def log_error(greenlet: gevent.Greenlet) -> None:
|
|
229
|
+
msg = (
|
|
230
|
+
f"Exception in master file task {str(uuid)} (channel '{src}'):\n"
|
|
231
|
+
+ "".join(traceback.format_exception(greenlet.exception))
|
|
232
|
+
)
|
|
233
|
+
logger.error(msg)
|
|
234
|
+
print(msg)
|
|
270
235
|
|
|
271
|
-
|
|
236
|
+
task.link_exception(callback=log_error)
|
|
272
237
|
|
|
273
238
|
return tasks
|
|
274
239
|
|
|
File without changes
|
|
@@ -34,6 +34,7 @@ src/lima2/client/pipelines/legacy.py
|
|
|
34
34
|
src/lima2/client/pipelines/smx.py
|
|
35
35
|
src/lima2/client/pipelines/xpcs.py
|
|
36
36
|
src/lima2/master_file/generate.py
|
|
37
|
+
src/lima2/master_file/py.typed
|
|
37
38
|
src/lima2/server/__init__.py
|
|
38
39
|
src/lima2/server/control.py
|
|
39
40
|
src/lima2/server/receiver.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lima2_client-3.0a1 → lima2_client-3.1a0}/src/finite_state_machine/examples/async_turnstile.py
RENAMED
|
File without changes
|
{lima2_client-3.0a1 → lima2_client-3.1a0}/src/finite_state_machine/examples/boolean_field.py
RENAMED
|
File without changes
|
{lima2_client-3.0a1 → lima2_client-3.1a0}/src/finite_state_machine/examples/github_pull_request.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|