lima2.client 2.0.2__tar.gz → 2.1.1__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-2.0.2/src/lima2.client.egg-info → lima2_client-2.1.1}/PKG-INFO +2 -1
- {lima2_client-2.0.2 → lima2_client-2.1.1}/setup.cfg +2 -1
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/client.py +79 -16
- lima2_client-2.1.1/src/lima2/client/convert.py +36 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/detector.py +31 -47
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/devencoded/dense_frame.py +4 -8
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/devencoded/sparse_frame.py +1 -1
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/pipeline.py +123 -31
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/pipelines/legacy.py +9 -7
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/pipelines/smx.py +17 -14
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/pipelines/xpcs.py +13 -11
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/shell.py +4 -2
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/state_machine.py +62 -8
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/topology.py +3 -2
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/utils.py +30 -0
- lima2_client-2.1.1/src/lima2/master_file/generate.py +249 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/server/receiver.py +4 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1/src/lima2.client.egg-info}/PKG-INFO +2 -1
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2.client.egg-info/SOURCES.txt +4 -3
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2.client.egg-info/requires.txt +1 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/tests/test_client.py +118 -0
- lima2_client-2.1.1/tests/test_convert.py +39 -0
- lima2_client-2.1.1/tests/test_dense_frame.py +155 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/tests/test_detector.py +1 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/tests/test_pipeline.py +187 -5
- {lima2_client-2.0.2 → lima2_client-2.1.1}/tests/test_progress_counter.py +5 -0
- lima2_client-2.1.1/tests/test_sparse_frame.py +109 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/tests/test_state_machine.py +19 -3
- {lima2_client-2.0.2 → lima2_client-2.1.1}/tests/test_timer.py +2 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/tests/test_topology.py +5 -0
- lima2_client-2.1.1/tests/test_utils.py +62 -0
- lima2_client-2.0.2/src/lima2/client/channel.py +0 -30
- lima2_client-2.0.2/src/lima2/client/convert.py +0 -46
- lima2_client-2.0.2/src/lima2/client/devencoded/sparse_frame_with_background.py +0 -120
- lima2_client-2.0.2/tests/test_import.py +0 -10
- lima2_client-2.0.2/tests/test_utils.py +0 -30
- {lima2_client-2.0.2 → lima2_client-2.1.1}/LICENSE +0 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/README.md +0 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/pyproject.toml +0 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/__init__.py +0 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/devencoded/__init__.py +0 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/devencoded/exception.py +0 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/devencoded/structured_array.py +0 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/pipelines/__init__.py +0 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/progress_counter.py +0 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/client/py.typed +0 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/server/__init__.py +0 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/server/control.py +0 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2/server/timer.py +0 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2.client.egg-info/dependency_links.txt +0 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2.client.egg-info/entry_points.txt +0 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/src/lima2.client.egg-info/top_level.txt +0 -0
- {lima2_client-2.0.2 → lima2_client-2.1.1}/tests/test_pipelines.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: lima2.client
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: Python client library for Lima2 detector
|
|
5
5
|
Home-page: https://gitlab.esrf.fr/limagroup/lima2-client
|
|
6
6
|
Author: BCU (ESRF)
|
|
@@ -19,6 +19,7 @@ Requires-Dist: beartype
|
|
|
19
19
|
Requires-Dist: numpy
|
|
20
20
|
Requires-Dist: pyyaml
|
|
21
21
|
Requires-Dist: typing-extensions
|
|
22
|
+
Requires-Dist: h5py
|
|
22
23
|
Provides-Extra: shell
|
|
23
24
|
Requires-Dist: ipython; extra == "shell"
|
|
24
25
|
|
|
@@ -22,6 +22,7 @@ install_requires =
|
|
|
22
22
|
numpy
|
|
23
23
|
pyyaml
|
|
24
24
|
typing-extensions
|
|
25
|
+
h5py
|
|
25
26
|
|
|
26
27
|
[options.entry_points]
|
|
27
28
|
console_scripts =
|
|
@@ -40,7 +41,7 @@ per-file-ignores =
|
|
|
40
41
|
required_plugins =
|
|
41
42
|
pytest-cov>=6.0.0
|
|
42
43
|
pytest-forked>=1.6.0
|
|
43
|
-
addopts = -v
|
|
44
|
+
addopts = -v
|
|
44
45
|
pythonpath = src
|
|
45
46
|
|
|
46
47
|
[coverage:run]
|
|
@@ -18,7 +18,10 @@ individual receivers, for instance if one of them is stuck on a frame while the
|
|
|
18
18
|
processing data.
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
+
import logging
|
|
21
22
|
import os
|
|
23
|
+
import traceback
|
|
24
|
+
from concurrent import futures
|
|
22
25
|
from uuid import UUID
|
|
23
26
|
|
|
24
27
|
import tango as tg
|
|
@@ -33,6 +36,8 @@ from lima2.client.progress_counter import ProgressCounter, SingleCounter
|
|
|
33
36
|
from lima2.client.topology import TopologyKind
|
|
34
37
|
from lima2.client.utils import docstring_from
|
|
35
38
|
|
|
39
|
+
logger = logging.getLogger(__name__)
|
|
40
|
+
|
|
36
41
|
|
|
37
42
|
@beartype
|
|
38
43
|
class Client:
|
|
@@ -67,12 +72,10 @@ class Client:
|
|
|
67
72
|
f"No matching topology type for {repr(topology_name)}"
|
|
68
73
|
) from e
|
|
69
74
|
|
|
70
|
-
self.recv_devs = rcv_devs
|
|
71
|
-
|
|
72
75
|
# Cache of processing pipelines by uuid (see method `pipeline`)
|
|
73
76
|
self.__pipelines: dict[UUID, Pipeline] = {}
|
|
74
77
|
|
|
75
|
-
self.
|
|
78
|
+
self._tango_db = tg.Database()
|
|
76
79
|
|
|
77
80
|
@classmethod
|
|
78
81
|
def from_yaml(
|
|
@@ -91,6 +94,16 @@ class Client:
|
|
|
91
94
|
|
|
92
95
|
return Client(ctl_dev=ctl_dev, rcv_devs=rcv_devs)
|
|
93
96
|
|
|
97
|
+
@property
|
|
98
|
+
def control(self) -> tg.DeviceProxy:
|
|
99
|
+
"""Lima2 control tango device."""
|
|
100
|
+
return self.detector.ctrl
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def receivers(self) -> list[tg.DeviceProxy]:
|
|
104
|
+
"""Lima2 receiver tango devices."""
|
|
105
|
+
return self.detector.recvs
|
|
106
|
+
|
|
94
107
|
@property
|
|
95
108
|
def detector(self) -> Detector:
|
|
96
109
|
return self._detector
|
|
@@ -124,6 +137,11 @@ class Client:
|
|
|
124
137
|
def params_default(self) -> dict[str, Any]:
|
|
125
138
|
return self.detector.params_default
|
|
126
139
|
|
|
140
|
+
@property
|
|
141
|
+
@docstring_from(Detector.params_schema)
|
|
142
|
+
def params_schema(self) -> dict[str, Any]:
|
|
143
|
+
return self.detector.params_schema
|
|
144
|
+
|
|
127
145
|
####################################################################################
|
|
128
146
|
# Control
|
|
129
147
|
####################################################################################
|
|
@@ -138,7 +156,7 @@ class Client:
|
|
|
138
156
|
ctl_params=ctrl_params,
|
|
139
157
|
acq_params=acq_params,
|
|
140
158
|
proc_params=proc_params,
|
|
141
|
-
num_receivers=len(self.
|
|
159
|
+
num_receivers=len(self.receivers),
|
|
142
160
|
)
|
|
143
161
|
|
|
144
162
|
self.detector.prepare_acq(uuid, ctl, acq, proc)
|
|
@@ -170,7 +188,7 @@ class Client:
|
|
|
170
188
|
SingleCounter(
|
|
171
189
|
name="nb_frames_acquired",
|
|
172
190
|
value=self.detector.nb_frames_acquired,
|
|
173
|
-
source=self.
|
|
191
|
+
source=self.control.name(),
|
|
174
192
|
)
|
|
175
193
|
)
|
|
176
194
|
|
|
@@ -184,7 +202,7 @@ class Client:
|
|
|
184
202
|
value=recv.nb_frames_xferred,
|
|
185
203
|
source=recv.name(),
|
|
186
204
|
)
|
|
187
|
-
for recv in self.
|
|
205
|
+
for recv in self.receivers
|
|
188
206
|
]
|
|
189
207
|
)
|
|
190
208
|
|
|
@@ -195,7 +213,7 @@ class Client:
|
|
|
195
213
|
@property
|
|
196
214
|
def pipelines(self) -> list[UUID]:
|
|
197
215
|
"""Get list of available pipelines UUID"""
|
|
198
|
-
uuids = [recv.pipelines for recv in self.
|
|
216
|
+
uuids = [recv.pipelines for recv in self.receivers]
|
|
199
217
|
if all(uuid is None for uuid in uuids):
|
|
200
218
|
return []
|
|
201
219
|
return [UUID(u) for u in {y for x in uuids for y in x}]
|
|
@@ -209,12 +227,13 @@ class Client:
|
|
|
209
227
|
return self.__pipelines[uuid]
|
|
210
228
|
|
|
211
229
|
# uuid isn't in the local cache of pipelines: get it from Tango
|
|
212
|
-
instances = self.
|
|
230
|
+
instances = self._tango_db.get_device_exported(f"*/limaprocessing/{uuid}*")
|
|
213
231
|
if not instances:
|
|
214
232
|
raise ValueError(f"Pipeline not found in tango database: {uuid=}")
|
|
215
233
|
|
|
216
234
|
class_names = [
|
|
217
|
-
self.
|
|
235
|
+
self._tango_db.get_device_info(instance).class_name
|
|
236
|
+
for instance in instances
|
|
218
237
|
]
|
|
219
238
|
|
|
220
239
|
# Select the pipeline according to the Tango class (Homogeneous processing for now)
|
|
@@ -237,7 +256,7 @@ class Client:
|
|
|
237
256
|
Raises if no pipeline has been created, or if receivers disagree on the
|
|
238
257
|
current pipeline's uuid.
|
|
239
258
|
"""
|
|
240
|
-
uuids: list[str] = [recv.current_pipeline for recv in self.
|
|
259
|
+
uuids: list[str] = [recv.current_pipeline for recv in self.receivers]
|
|
241
260
|
|
|
242
261
|
if all([uuid == "" for uuid in uuids]):
|
|
243
262
|
raise ValueError("No pipeline present in the tango database")
|
|
@@ -250,10 +269,54 @@ class Client:
|
|
|
250
269
|
return self.pipeline(uuids[0])
|
|
251
270
|
|
|
252
271
|
def erase_pipeline(self, uuid: UUID) -> None:
|
|
253
|
-
"""Erase a pipeline instance
|
|
254
|
-
|
|
255
|
-
|
|
272
|
+
"""Erase a pipeline instance.
|
|
273
|
+
|
|
274
|
+
Raises if the pipeline is absent from a receiver.
|
|
275
|
+
"""
|
|
276
|
+
for recv in self.receivers:
|
|
256
277
|
recv.erasePipeline(str(uuid))
|
|
278
|
+
self.__pipelines.pop(uuid, None)
|
|
279
|
+
|
|
280
|
+
def clear_previous_pipelines(self) -> None:
|
|
281
|
+
"""Erase all pipelines except the current one."""
|
|
282
|
+
try:
|
|
283
|
+
current = self.current_pipeline.uuid
|
|
284
|
+
except ValueError:
|
|
285
|
+
current = None
|
|
286
|
+
|
|
287
|
+
def clear(receiver: tg.DeviceProxy):
|
|
288
|
+
"""Clear previous pipelines from a single receiver device."""
|
|
289
|
+
if receiver.pipelines is None:
|
|
290
|
+
# No pipelines
|
|
291
|
+
return []
|
|
292
|
+
|
|
293
|
+
cleared = []
|
|
294
|
+
for uuid in receiver.pipelines:
|
|
295
|
+
if uuid != str(current):
|
|
296
|
+
logger.debug(
|
|
297
|
+
f"Erasing pipeline {uuid} from recv {receiver.dev_name()}"
|
|
298
|
+
)
|
|
299
|
+
receiver.erasePipeline(uuid)
|
|
300
|
+
cleared.append(uuid)
|
|
301
|
+
return cleared
|
|
302
|
+
|
|
303
|
+
cleared: set[str] = set()
|
|
304
|
+
with futures.ThreadPoolExecutor(max_workers=len(self.receivers)) as executor:
|
|
305
|
+
fs = [executor.submit(clear, receiver) for receiver in self.receivers]
|
|
306
|
+
for receiver, future in zip(self.receivers, fs):
|
|
307
|
+
try:
|
|
308
|
+
cleared = cleared.union(future.result())
|
|
309
|
+
except Exception:
|
|
310
|
+
logger.warning(
|
|
311
|
+
f"Exception while clearing pipelines from {receiver.dev_name()}: "
|
|
312
|
+
f"{traceback.format_exc()}"
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
logger.debug(f"Cleared pipelines {[uuid for uuid in set(cleared)]}")
|
|
316
|
+
|
|
317
|
+
# Reset local cache
|
|
318
|
+
if current is not None:
|
|
319
|
+
self.__pipelines = {current: self.__pipelines[current]}
|
|
257
320
|
|
|
258
321
|
####################################################################################
|
|
259
322
|
# Utility
|
|
@@ -264,10 +327,10 @@ class Client:
|
|
|
264
327
|
[
|
|
265
328
|
"Lima2 client",
|
|
266
329
|
f"State: {self.detector.state}",
|
|
267
|
-
"
|
|
268
|
-
f" {self.
|
|
330
|
+
"Control:",
|
|
331
|
+
f" {self.control}",
|
|
269
332
|
f"Receivers ({self.topology_kind}):",
|
|
270
|
-
*[f" - {recv}" for recv in self.
|
|
333
|
+
*[f" - {recv}" for recv in self.receivers],
|
|
271
334
|
"Progress counters:",
|
|
272
335
|
f" - {self.nb_frames_acquired}",
|
|
273
336
|
f" - {self.nb_frames_xferred}",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# This file is part of the Lima2 project
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2020-2024 Beamline Control Unit, ESRF
|
|
6
|
+
# Distributed under the MIT license. See LICENSE for more info.
|
|
7
|
+
|
|
8
|
+
"""Convertion routines.
|
|
9
|
+
|
|
10
|
+
Frame info is serialized as {'dimensions': {'x': 2068, 'y': 512}, 'nb_channels': 2, 'pixel_type': 'gray16'}
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
# convert from pixel enum type to numpy type
|
|
16
|
+
pixel_type_to_np_dtype = {
|
|
17
|
+
"gray8s": np.int8,
|
|
18
|
+
"gray8": np.uint8,
|
|
19
|
+
"gray16s": np.int16,
|
|
20
|
+
"gray16": np.uint16,
|
|
21
|
+
"gray32s": np.int32,
|
|
22
|
+
"gray32": np.uint32,
|
|
23
|
+
"gray32f": np.float32,
|
|
24
|
+
"gray64f": np.float64,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def frame_info_to_shape_dtype(frame_info):
|
|
29
|
+
return dict(
|
|
30
|
+
shape=(
|
|
31
|
+
frame_info["nb_channels"],
|
|
32
|
+
frame_info["dimensions"]["y"],
|
|
33
|
+
frame_info["dimensions"]["x"],
|
|
34
|
+
),
|
|
35
|
+
dtype=pixel_type_to_np_dtype[frame_info["pixel_type"]],
|
|
36
|
+
)
|
|
@@ -24,13 +24,15 @@ import json
|
|
|
24
24
|
import logging
|
|
25
25
|
import uuid
|
|
26
26
|
|
|
27
|
-
import jsonschema
|
|
28
27
|
import tango
|
|
29
28
|
from beartype import beartype
|
|
30
29
|
from jsonschema_default import create_from
|
|
30
|
+
from typing_extensions import Optional
|
|
31
31
|
|
|
32
|
+
from lima2.client import pipelines
|
|
32
33
|
from lima2.client import state_machine as fsm
|
|
33
34
|
from lima2.client.state_machine import State
|
|
35
|
+
from lima2.client.utils import validate
|
|
34
36
|
|
|
35
37
|
# Create a logger
|
|
36
38
|
_logger = logging.getLogger(__name__)
|
|
@@ -45,7 +47,7 @@ class CommError(RuntimeError):
|
|
|
45
47
|
class Detector:
|
|
46
48
|
"""The main Detector interface."""
|
|
47
49
|
|
|
48
|
-
DEFAULT_TANGO_TIMEOUT =
|
|
50
|
+
DEFAULT_TANGO_TIMEOUT = 5
|
|
49
51
|
|
|
50
52
|
def __init__(
|
|
51
53
|
self,
|
|
@@ -72,12 +74,7 @@ class Detector:
|
|
|
72
74
|
self.__ctrl = ctrl_dev
|
|
73
75
|
self.__recvs = list(recv_devs)
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
self.ping()
|
|
77
|
-
except tango.DevFailed as e:
|
|
78
|
-
raise CommError(
|
|
79
|
-
"Failed to ping Tango servers (are the devices running?)"
|
|
80
|
-
) from e
|
|
77
|
+
self.ping()
|
|
81
78
|
|
|
82
79
|
rank_order = [d.recv_rank for d in recv_devs]
|
|
83
80
|
if not all([r == i for i, r in enumerate(rank_order)]):
|
|
@@ -85,7 +82,7 @@ class Detector:
|
|
|
85
82
|
f"Receiver MPI rank order does not match client order: {rank_order}"
|
|
86
83
|
)
|
|
87
84
|
|
|
88
|
-
self.__params_schema = None
|
|
85
|
+
self.__params_schema: Optional[dict] = None
|
|
89
86
|
self.__fsm = fsm.StateMachine(self.__ctrl, self.__recvs)
|
|
90
87
|
self.__try_sync_hard()
|
|
91
88
|
|
|
@@ -100,8 +97,6 @@ class Detector:
|
|
|
100
97
|
d.set_green_mode(tango.GreenMode.Gevent)
|
|
101
98
|
d.set_timeout_millis(timeout * 1000)
|
|
102
99
|
|
|
103
|
-
self.__try_params_schema()
|
|
104
|
-
|
|
105
100
|
@property
|
|
106
101
|
def det_info(self) -> dict:
|
|
107
102
|
"""
|
|
@@ -166,26 +161,19 @@ class Detector:
|
|
|
166
161
|
"""
|
|
167
162
|
return json.loads(self.__ctrl.det_capabilities) # type: ignore
|
|
168
163
|
|
|
169
|
-
def __try_params_schema(self):
|
|
170
|
-
"""
|
|
171
|
-
Try to init the parameters from a lazy constructed device Proxy
|
|
172
|
-
(to be used when the DS is not running when Detector is constructed)
|
|
173
|
-
"""
|
|
174
|
-
try:
|
|
175
|
-
if not self.__params_schema:
|
|
176
|
-
self.__params_schema = self.__get_params_schema()
|
|
177
|
-
return True
|
|
178
|
-
except (tango.DevFailed, RuntimeError) as e:
|
|
179
|
-
print(f"Failed to get params schema: {e}")
|
|
180
|
-
return False
|
|
181
|
-
|
|
182
164
|
def __get_params_schema(self) -> dict:
|
|
183
165
|
"""
|
|
184
166
|
Get the parameters's schema for each devices from the Tango class attribute
|
|
167
|
+
|
|
168
|
+
Raises tango.DevFailed if we cannot connect to database at TANGO_HOST.
|
|
169
|
+
|
|
170
|
+
If the lima2 server hasn't been started yet, returns a dict of the form:
|
|
171
|
+
{
|
|
172
|
+
d.dev_name(): {"init_params": None, "acq_params": None}
|
|
173
|
+
for d in self.devs
|
|
174
|
+
}
|
|
185
175
|
"""
|
|
186
176
|
tango_db = tango.Database()
|
|
187
|
-
if tango_db is None:
|
|
188
|
-
return {}
|
|
189
177
|
|
|
190
178
|
res = {}
|
|
191
179
|
for d in self.devs:
|
|
@@ -207,7 +195,7 @@ class Detector:
|
|
|
207
195
|
return res
|
|
208
196
|
|
|
209
197
|
@property
|
|
210
|
-
def params_schema(self):
|
|
198
|
+
def params_schema(self) -> dict:
|
|
211
199
|
"""Returns JSON schemas of init, acquisition and processing parameters.
|
|
212
200
|
|
|
213
201
|
Example:
|
|
@@ -235,10 +223,9 @@ class Detector:
|
|
|
235
223
|
Group:
|
|
236
224
|
Parameters
|
|
237
225
|
"""
|
|
238
|
-
if self.__params_schema:
|
|
239
|
-
|
|
226
|
+
if self.__params_schema is None:
|
|
227
|
+
self.__params_schema = self.__get_params_schema()
|
|
240
228
|
|
|
241
|
-
self.__try_params_schema()
|
|
242
229
|
return self.__params_schema
|
|
243
230
|
|
|
244
231
|
@property
|
|
@@ -281,13 +268,16 @@ class Detector:
|
|
|
281
268
|
Ping all the devices of the system.
|
|
282
269
|
|
|
283
270
|
Raises:
|
|
284
|
-
|
|
271
|
+
CommError if the connection failed.
|
|
285
272
|
|
|
286
273
|
Group:
|
|
287
274
|
Status
|
|
288
275
|
"""
|
|
289
276
|
for d in self.devs:
|
|
290
|
-
|
|
277
|
+
try:
|
|
278
|
+
d.ping()
|
|
279
|
+
except tango.DevFailed as e:
|
|
280
|
+
raise CommError(f"Failed to ping device {d.dev_name()}") from e
|
|
291
281
|
|
|
292
282
|
@property
|
|
293
283
|
def devs(self) -> list[tango.DeviceProxy]:
|
|
@@ -347,18 +337,11 @@ class Detector:
|
|
|
347
337
|
Group:
|
|
348
338
|
State
|
|
349
339
|
"""
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
map = {
|
|
356
|
-
ServerState.idle: State.IDLE,
|
|
357
|
-
ServerState.prepared: State.PREPARED,
|
|
358
|
-
ServerState.running: State.RUNNING,
|
|
359
|
-
ServerState.stopped: State.IDLE,
|
|
360
|
-
}
|
|
361
|
-
self.__fsm.state = map[server_state]
|
|
340
|
+
try:
|
|
341
|
+
self.__fsm.sync()
|
|
342
|
+
except Exception as e:
|
|
343
|
+
_logger.warning(f"Unable to sync with server: {e}")
|
|
344
|
+
|
|
362
345
|
return self.__fsm.state
|
|
363
346
|
|
|
364
347
|
@property
|
|
@@ -421,18 +404,19 @@ class Detector:
|
|
|
421
404
|
f"prepare_acq({uuid}, {ctrl_params}, {acq_params}, {proc_params})"
|
|
422
405
|
)
|
|
423
406
|
|
|
424
|
-
|
|
407
|
+
validate(
|
|
425
408
|
instance=ctrl_params,
|
|
426
409
|
schema=self.params_schema[self.ctrl.name()]["acq_params"],
|
|
427
410
|
)
|
|
428
411
|
|
|
429
412
|
for i, recv in enumerate(self.__recvs):
|
|
430
|
-
|
|
413
|
+
validate(
|
|
431
414
|
instance=acq_params[i],
|
|
432
415
|
schema=self.params_schema[recv.name()]["acq_params"],
|
|
433
416
|
)
|
|
434
417
|
|
|
435
|
-
|
|
418
|
+
pipeline_class = pipelines.get_class(proc_params[i]["class_name"])
|
|
419
|
+
validate(instance=proc_params[i], schema=pipeline_class.params_schema)
|
|
436
420
|
|
|
437
421
|
# Update parameters on the device servers
|
|
438
422
|
# TODO: Rename acq_params to ctrl_params
|
|
@@ -10,11 +10,13 @@
|
|
|
10
10
|
|
|
11
11
|
from dataclasses import dataclass
|
|
12
12
|
import struct
|
|
13
|
-
import numpy
|
|
14
13
|
import typing
|
|
15
14
|
from typing_extensions import Union
|
|
16
15
|
import enum
|
|
17
16
|
|
|
17
|
+
import numpy
|
|
18
|
+
from typing_extensions import Union
|
|
19
|
+
|
|
18
20
|
from .exception import DevEncodedFormatNotSupported
|
|
19
21
|
|
|
20
22
|
DATA_HEADER_FORMAT = "<IHHIIHHHHHHHHIIIIIIQ"
|
|
@@ -61,15 +63,9 @@ class Frame:
|
|
|
61
63
|
data: numpy.ndarray
|
|
62
64
|
"""Data of the frame"""
|
|
63
65
|
|
|
64
|
-
idx:
|
|
66
|
+
idx: int
|
|
65
67
|
"""Index of the frame where 0 is the first frame"""
|
|
66
68
|
|
|
67
|
-
def __bool__(self):
|
|
68
|
-
return not self.isEmpty()
|
|
69
|
-
|
|
70
|
-
def isEmpty(self):
|
|
71
|
-
return self.data is None
|
|
72
|
-
|
|
73
69
|
|
|
74
70
|
def decode(raw_data: EncodedFrame) -> Frame:
|
|
75
71
|
"""Decode data provided by Lima device image attribute
|