lima2.client 3.0a1__tar.gz → 3.1.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.
- {lima2_client-3.0a1/src/lima2_client.egg-info → lima2_client-3.1.0}/PKG-INFO +2 -2
- {lima2_client-3.0a1 → lima2_client-3.1.0}/pyproject.toml +1 -1
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/client.py +67 -1
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/detector.py +16 -2
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/pipeline.py +16 -16
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/state_machine.py +3 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/topology.py +11 -6
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/master_file/generate.py +39 -27
- lima2_client-3.1.0/src/lima2/master_file/py.typed +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0/src/lima2_client.egg-info}/PKG-INFO +2 -2
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2_client.egg-info/SOURCES.txt +1 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2_client.egg-info/requires.txt +1 -1
- {lima2_client-3.0a1 → lima2_client-3.1.0}/LICENSE +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/README.md +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/setup.cfg +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/finite_state_machine/__init__.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/finite_state_machine/draw_state_diagram.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/finite_state_machine/examples/__init__.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/finite_state_machine/examples/async_github_pull_request.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/finite_state_machine/examples/async_turnstile.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/finite_state_machine/examples/boolean_field.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/finite_state_machine/examples/github_pull_request.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/finite_state_machine/examples/turnstile.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/finite_state_machine/exceptions.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/finite_state_machine/state_machine.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/__init__.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/convert.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/devencoded/__init__.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/devencoded/dense_frame.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/devencoded/exception.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/devencoded/smx_sparse_frame.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/devencoded/sparse_frame.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/devencoded/structured_array.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/pipelines/__init__.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/pipelines/failing.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/pipelines/legacy.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/pipelines/smx.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/pipelines/xpcs.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/progress_counter.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/py.typed +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/shell.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/client/utils.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/server/__init__.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/server/control.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/server/receiver.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2/server/timer.py +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2_client.egg-info/dependency_links.txt +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2_client.egg-info/entry_points.txt +0 -0
- {lima2_client-3.0a1 → lima2_client-3.1.0}/src/lima2_client.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lima2-client
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.1.0
|
|
4
4
|
Summary: Python client library for Lima2 detector
|
|
5
5
|
Author: BCU (ESRF)
|
|
6
6
|
License: Copyright (c) 2020-2024 Beamline Control Unit, ESRF
|
|
@@ -24,7 +24,7 @@ Requires-Dist: gevent
|
|
|
24
24
|
Requires-Dist: jsonschema
|
|
25
25
|
Requires-Dist: jsonschema-default
|
|
26
26
|
Requires-Dist: beartype
|
|
27
|
-
Requires-Dist: numpy
|
|
27
|
+
Requires-Dist: numpy<2.3
|
|
28
28
|
Requires-Dist: pyyaml
|
|
29
29
|
Requires-Dist: typing-extensions
|
|
30
30
|
Requires-Dist: h5py
|
|
@@ -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,32 @@ 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
|
+
|
|
92
|
+
def cleanup(self):
|
|
93
|
+
"""Unregister all state loggers and tango event handlers.
|
|
94
|
+
|
|
95
|
+
This method serves only in specific cases where event handlers must for
|
|
96
|
+
some reason be unregistered. In a normal use case, a Client is
|
|
97
|
+
instantiated once per Lima2 instance, and no cleanup is necessary.
|
|
98
|
+
|
|
99
|
+
NOTE: After this call, the Client instance should no longer be used.
|
|
100
|
+
"""
|
|
101
|
+
self._detector.unregister_events()
|
|
102
|
+
self._detector = None
|
|
103
|
+
|
|
104
|
+
for pipeline in self.__pipelines.values():
|
|
105
|
+
pipeline.unregister_events()
|
|
106
|
+
self.__pipelines.clear()
|
|
107
|
+
|
|
80
108
|
@classmethod
|
|
81
109
|
def from_yaml(
|
|
82
110
|
cls,
|
|
@@ -147,10 +175,23 @@ class Client:
|
|
|
147
175
|
####################################################################################
|
|
148
176
|
|
|
149
177
|
def prepare_acq(
|
|
150
|
-
self,
|
|
178
|
+
self,
|
|
179
|
+
uuid: UUID,
|
|
180
|
+
ctrl_params: dict,
|
|
181
|
+
acq_params: dict,
|
|
182
|
+
proc_params: dict,
|
|
151
183
|
) -> None:
|
|
152
184
|
"""Prepare acquisition with a given uuid and set of params."""
|
|
153
185
|
|
|
186
|
+
# Check parameters required for master file generation
|
|
187
|
+
checked_params = mfg.check_generation(
|
|
188
|
+
ctrl_params=ctrl_params,
|
|
189
|
+
recv_params=acq_params,
|
|
190
|
+
proc_params=proc_params,
|
|
191
|
+
topology_kind=self.topology_kind,
|
|
192
|
+
)
|
|
193
|
+
ctrl_params, acq_params, proc_params = checked_params
|
|
194
|
+
|
|
154
195
|
# Adjust params for distributed acquisition
|
|
155
196
|
ctl, acq, proc = topology.distribute_acq(
|
|
156
197
|
ctl_params=ctrl_params,
|
|
@@ -175,16 +216,41 @@ class Client:
|
|
|
175
216
|
|
|
176
217
|
self.current_pipeline.register_on_error(on_error)
|
|
177
218
|
|
|
219
|
+
# Remove master file tasks of previous acquisitions if done or not started
|
|
220
|
+
for prev_uuid in list(self.master_file_tasks.keys()):
|
|
221
|
+
tasks = self.master_file_tasks[prev_uuid].values()
|
|
222
|
+
if all([not task.started() or task.ready() for task in tasks]):
|
|
223
|
+
logger.debug(
|
|
224
|
+
"All master file tasks done (or never started) for "
|
|
225
|
+
f"{prev_uuid} -> cleaning up"
|
|
226
|
+
)
|
|
227
|
+
self.master_file_tasks.pop(prev_uuid)
|
|
228
|
+
else:
|
|
229
|
+
logger.debug(f"Not all master file tasks done for {prev_uuid}...")
|
|
230
|
+
|
|
231
|
+
self.master_file_tasks[uuid] = mfg.enable_generation(
|
|
232
|
+
pipeline=self.current_pipeline,
|
|
233
|
+
num_frames=ctrl_params["acq"]["nb_frames"],
|
|
234
|
+
ctrl_params=ctl,
|
|
235
|
+
recv_params=acq[0],
|
|
236
|
+
proc_params=proc[0],
|
|
237
|
+
topology_kind=self.topology_kind,
|
|
238
|
+
)
|
|
239
|
+
|
|
178
240
|
@docstring_from(Detector.start_acq)
|
|
179
241
|
def start_acq(self) -> None:
|
|
180
242
|
pipeline = self.current_pipeline
|
|
181
243
|
pipeline.register_on_finished(pipeline.unregister_events)
|
|
244
|
+
|
|
182
245
|
try:
|
|
183
246
|
self.detector.start_acq()
|
|
184
247
|
except Exception:
|
|
185
248
|
pipeline.unregister_events()
|
|
186
249
|
raise
|
|
187
250
|
|
|
251
|
+
for task in self.master_file_tasks[self.current_pipeline.uuid].values():
|
|
252
|
+
task.start()
|
|
253
|
+
|
|
188
254
|
@docstring_from(Detector.trigger)
|
|
189
255
|
def trigger(self) -> None:
|
|
190
256
|
self.detector.trigger()
|
|
@@ -73,6 +73,7 @@ class Detector:
|
|
|
73
73
|
|
|
74
74
|
self.__ctrl = ctrl_dev
|
|
75
75
|
self.__recvs = list(recv_devs)
|
|
76
|
+
self._state_event_ids: dict[tango.DeviceProxy, int] = {}
|
|
76
77
|
|
|
77
78
|
self.ping()
|
|
78
79
|
|
|
@@ -100,6 +101,17 @@ class Detector:
|
|
|
100
101
|
d.set_green_mode(tango.GreenMode.Gevent)
|
|
101
102
|
d.set_timeout_millis(timeout * 1000)
|
|
102
103
|
|
|
104
|
+
def unregister_events(self):
|
|
105
|
+
"""Unregister all state loggers and tango event handlers.
|
|
106
|
+
|
|
107
|
+
This method serves only in specific cases where event handlers must for
|
|
108
|
+
some reason be unregistered. In a normal use case, a Client is
|
|
109
|
+
instantiated once per Lima2 instance, and no cleanup is necessary.
|
|
110
|
+
"""
|
|
111
|
+
for device, event_id in self._state_event_ids.items():
|
|
112
|
+
device.unsubscribe_event(event_id)
|
|
113
|
+
self._state_event_ids.clear()
|
|
114
|
+
|
|
103
115
|
@contextlib.contextmanager
|
|
104
116
|
def _locked_fsm(self) -> Iterator[fsm.StateMachine]:
|
|
105
117
|
self.__fsm_lock.acquire()
|
|
@@ -633,12 +645,14 @@ class Detector:
|
|
|
633
645
|
finally:
|
|
634
646
|
_logger.info(f"Current state: {fsm.state}")
|
|
635
647
|
|
|
636
|
-
|
|
637
|
-
dev.subscribe_event(
|
|
648
|
+
self._state_event_ids = {
|
|
649
|
+
dev: dev.subscribe_event(
|
|
638
650
|
"acq_state",
|
|
639
651
|
tango.EventType.CHANGE_EVENT,
|
|
640
652
|
on_acq_state_change,
|
|
641
653
|
)
|
|
654
|
+
for dev in self.devs
|
|
655
|
+
}
|
|
642
656
|
|
|
643
657
|
def _register_on_end_acq_handlers(self):
|
|
644
658
|
"""Register an "end of acquisition" callback on each receiver.
|
|
@@ -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:
|
|
@@ -239,6 +239,9 @@ class StateMachine(fsm.StateMachine):
|
|
|
239
239
|
if logger in self.__loggers:
|
|
240
240
|
self.__loggers.remove(logger)
|
|
241
241
|
|
|
242
|
+
def clear_transition_loggers(self):
|
|
243
|
+
self.__loggers.clear()
|
|
244
|
+
|
|
242
245
|
def on_state_change(self, state):
|
|
243
246
|
_logger.debug(f"on_state_change {state}")
|
|
244
247
|
futures = []
|
|
@@ -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,8 +24,10 @@ 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
|
+
from lima2.client.topology import TopologyKind
|
|
30
31
|
|
|
31
32
|
logger = logging.getLogger(__name__)
|
|
32
33
|
|
|
@@ -39,13 +40,19 @@ class LUTSource(Enum):
|
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
class MasterFileTask:
|
|
42
|
-
def __init__(self, task, end_event):
|
|
43
|
+
def __init__(self, task: gevent.Greenlet, end_event):
|
|
43
44
|
self.task = task
|
|
44
45
|
self.end_event = end_event
|
|
45
46
|
|
|
46
47
|
def ready(self):
|
|
47
48
|
return self.task.ready()
|
|
48
49
|
|
|
50
|
+
def start(self):
|
|
51
|
+
self.task.start()
|
|
52
|
+
|
|
53
|
+
def started(self) -> bool:
|
|
54
|
+
return self.task.started
|
|
55
|
+
|
|
49
56
|
def wait(self, timeout):
|
|
50
57
|
finished = gevent.wait([self.task], timeout=timeout)
|
|
51
58
|
return len(finished) > 0
|
|
@@ -60,11 +67,11 @@ class MasterFileTask:
|
|
|
60
67
|
self.task.join()
|
|
61
68
|
|
|
62
69
|
|
|
63
|
-
active_tasks = {}
|
|
64
|
-
|
|
65
|
-
|
|
66
70
|
def check_generation(
|
|
67
|
-
ctrl_params: dict,
|
|
71
|
+
ctrl_params: dict,
|
|
72
|
+
recv_params: dict,
|
|
73
|
+
proc_params: dict,
|
|
74
|
+
topology_kind: TopologyKind,
|
|
68
75
|
) -> list[dict]:
|
|
69
76
|
"""Check the parameters to be sent to prepare to ensure master file info"""
|
|
70
77
|
|
|
@@ -86,13 +93,15 @@ def check_generation(
|
|
|
86
93
|
if params["enabled"]:
|
|
87
94
|
active_saving_params.append(params)
|
|
88
95
|
|
|
96
|
+
need_frame_lut = topology_kind == TopologyKind.UNEVEN
|
|
97
|
+
|
|
89
98
|
# include detector frame_idx dataset in HDF5 files
|
|
90
99
|
for saving_params in active_saving_params:
|
|
91
|
-
if "include_frame_idx" in saving_params:
|
|
100
|
+
if need_frame_lut and "include_frame_idx" in saving_params:
|
|
92
101
|
saving_params["include_frame_idx"] = True
|
|
93
102
|
|
|
94
103
|
# allow retreiving recv-det-frame-idx mapping
|
|
95
|
-
if "frame_idx_enabled" in proc_params:
|
|
104
|
+
if need_frame_lut and "frame_idx_enabled" in proc_params:
|
|
96
105
|
logger.debug("Enabling frame_idx generation")
|
|
97
106
|
proc_params["frame_idx_enabled"] = True
|
|
98
107
|
|
|
@@ -105,9 +114,10 @@ def enable_generation(
|
|
|
105
114
|
ctrl_params: dict,
|
|
106
115
|
recv_params: dict,
|
|
107
116
|
proc_params: dict,
|
|
117
|
+
topology_kind: TopologyKind,
|
|
108
118
|
rr_offset: int = 0,
|
|
109
119
|
lut_source: LUTSource = LUTSource.AUTO,
|
|
110
|
-
) ->
|
|
120
|
+
) -> dict[str, MasterFileTask]:
|
|
111
121
|
"""Enable the generation of a master file for a given pipeline.
|
|
112
122
|
|
|
113
123
|
Must be called between Client.prepare_acq() and Client.start_acq(), so that the
|
|
@@ -177,6 +187,8 @@ def enable_generation(
|
|
|
177
187
|
proc_params=proc_params,
|
|
178
188
|
)
|
|
179
189
|
|
|
190
|
+
need_frame_lut = topology_kind == TopologyKind.UNEVEN
|
|
191
|
+
|
|
180
192
|
for source, frame_info, saving_params, cnt_name in active_saving_params:
|
|
181
193
|
base_path = saving_params["base_path"]
|
|
182
194
|
filename_prefix = saving_params["filename_prefix"]
|
|
@@ -204,10 +216,12 @@ def enable_generation(
|
|
|
204
216
|
this_lut_source = LUTSource.ROUND_ROBIN
|
|
205
217
|
if this_lut_source is None:
|
|
206
218
|
raise ValueError(f"Cannot satisfy {lut_source=}: {file_lut=}, {recv_lut=}")
|
|
219
|
+
elif this_lut_source == LUTSource.ROUND_ROBIN and need_frame_lut:
|
|
220
|
+
raise ValueError(f"Cannot use {this_lut_source} with {topology_kind}")
|
|
207
221
|
|
|
208
222
|
logger.info(
|
|
209
223
|
f"Master file (channel={source}) will be generated "
|
|
210
|
-
f"at {master_file_path}..."
|
|
224
|
+
f"at {master_file_path} [{this_lut_source}]..."
|
|
211
225
|
)
|
|
212
226
|
|
|
213
227
|
if frame_info is not None:
|
|
@@ -244,31 +258,29 @@ def enable_generation(
|
|
|
244
258
|
logger.warning(
|
|
245
259
|
"Master file generation not enabled: no dense frames saving channel enabled."
|
|
246
260
|
)
|
|
247
|
-
return
|
|
261
|
+
return {}
|
|
248
262
|
|
|
249
263
|
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())
|
|
264
|
+
p = MasterFileGeneration.Params(**params)
|
|
265
|
+
gen = MasterFileGeneration(p)
|
|
266
|
+
gen.run()
|
|
267
|
+
logger.debug(f"On finished {src} master file generation")
|
|
258
268
|
|
|
259
|
-
tasks = {}
|
|
269
|
+
tasks: dict[str, MasterFileTask] = {}
|
|
260
270
|
|
|
261
271
|
for src, params in generate_params.items():
|
|
262
|
-
task = gevent.
|
|
272
|
+
task = gevent.Greenlet(master_file_task, src, params)
|
|
263
273
|
tasks[src] = MasterFileTask(task, end_event)
|
|
264
274
|
|
|
265
|
-
def
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
275
|
+
def log_error(greenlet: gevent.Greenlet) -> None:
|
|
276
|
+
msg = (
|
|
277
|
+
f"Exception in master file task {str(uuid)} (channel '{src}'):\n"
|
|
278
|
+
+ "".join(traceback.format_exception(greenlet.exception))
|
|
279
|
+
)
|
|
280
|
+
logger.error(msg)
|
|
281
|
+
print(msg)
|
|
270
282
|
|
|
271
|
-
|
|
283
|
+
task.link_exception(callback=log_error)
|
|
272
284
|
|
|
273
285
|
return tasks
|
|
274
286
|
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lima2-client
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.1.0
|
|
4
4
|
Summary: Python client library for Lima2 detector
|
|
5
5
|
Author: BCU (ESRF)
|
|
6
6
|
License: Copyright (c) 2020-2024 Beamline Control Unit, ESRF
|
|
@@ -24,7 +24,7 @@ Requires-Dist: gevent
|
|
|
24
24
|
Requires-Dist: jsonschema
|
|
25
25
|
Requires-Dist: jsonschema-default
|
|
26
26
|
Requires-Dist: beartype
|
|
27
|
-
Requires-Dist: numpy
|
|
27
|
+
Requires-Dist: numpy<2.3
|
|
28
28
|
Requires-Dist: pyyaml
|
|
29
29
|
Requires-Dist: typing-extensions
|
|
30
30
|
Requires-Dist: h5py
|
|
@@ -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
|
{lima2_client-3.0a1 → lima2_client-3.1.0}/src/finite_state_machine/examples/async_turnstile.py
RENAMED
|
File without changes
|
{lima2_client-3.0a1 → lima2_client-3.1.0}/src/finite_state_machine/examples/boolean_field.py
RENAMED
|
File without changes
|
{lima2_client-3.0a1 → lima2_client-3.1.0}/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
|