lima2.client 2.1.1__tar.gz → 2.1.3__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.1.1/src/lima2.client.egg-info → lima2_client-2.1.3}/PKG-INFO +1 -1
- lima2_client-2.1.3/src/lima2/client/devencoded/smx_sparse_frame.py +128 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/pipeline.py +8 -1
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/pipelines/smx.py +11 -1
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/state_machine.py +21 -5
- {lima2_client-2.1.1 → lima2_client-2.1.3/src/lima2.client.egg-info}/PKG-INFO +1 -1
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2.client.egg-info/SOURCES.txt +1 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/LICENSE +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/README.md +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/pyproject.toml +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/setup.cfg +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/__init__.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/client.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/convert.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/detector.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/devencoded/__init__.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/devencoded/dense_frame.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/devencoded/exception.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/devencoded/sparse_frame.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/devencoded/structured_array.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/pipelines/__init__.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/pipelines/legacy.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/pipelines/xpcs.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/progress_counter.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/py.typed +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/shell.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/topology.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/client/utils.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/master_file/generate.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/server/__init__.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/server/control.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/server/receiver.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2/server/timer.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2.client.egg-info/dependency_links.txt +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2.client.egg-info/entry_points.txt +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2.client.egg-info/requires.txt +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/src/lima2.client.egg-info/top_level.txt +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/tests/test_client.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/tests/test_convert.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/tests/test_dense_frame.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/tests/test_detector.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/tests/test_pipeline.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/tests/test_pipelines.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/tests/test_progress_counter.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/tests/test_sparse_frame.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/tests/test_state_machine.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/tests/test_timer.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/tests/test_topology.py +0 -0
- {lima2_client-2.1.1 → lima2_client-2.1.3}/tests/test_utils.py +0 -0
|
@@ -0,0 +1,128 @@
|
|
|
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
|
+
"""Utility functions to decode Lima data from device server.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import struct
|
|
12
|
+
import numpy as np
|
|
13
|
+
import typing
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
|
|
16
|
+
from .exception import DevEncodedFormatNotSupported
|
|
17
|
+
|
|
18
|
+
DATA_HEADER_FORMAT = "<HHIII"
|
|
19
|
+
DATA_HEADER_SIZE = struct.calcsize(DATA_HEADER_FORMAT)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class SmxSparseFrame:
|
|
24
|
+
"""
|
|
25
|
+
Provide sparse data frame from Lima including few metadata
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
index: np.ndarray
|
|
29
|
+
intensity: np.ndarray
|
|
30
|
+
background_avg: np.ndarray
|
|
31
|
+
background_std: np.ndarray
|
|
32
|
+
|
|
33
|
+
idx: typing.Optional[int]
|
|
34
|
+
"""Index of the frame where 0 is the first frame"""
|
|
35
|
+
|
|
36
|
+
shape: tuple[int, ...]
|
|
37
|
+
"""Shape of the frame"""
|
|
38
|
+
|
|
39
|
+
def __bool__(self):
|
|
40
|
+
return not self.isEmpty()
|
|
41
|
+
|
|
42
|
+
def isEmpty(self):
|
|
43
|
+
return self.index is None
|
|
44
|
+
|
|
45
|
+
def densify(self, mask, radius, dummy, normalization=None):
|
|
46
|
+
"""Generate a dense image of its sparse representation with background
|
|
47
|
+
|
|
48
|
+
:param mask: 2D array with NaNs for mask and pixel radius for the valid pixels
|
|
49
|
+
:param radius: 1D array with the radial distance
|
|
50
|
+
:param dummy: numerical value for masked-out pixels in dense image
|
|
51
|
+
:param normalization: flat*solidangle*polarization*... array
|
|
52
|
+
:return dense array
|
|
53
|
+
"""
|
|
54
|
+
dense = np.interp(mask, radius, self.background_avg)
|
|
55
|
+
if self.background_std is not None:
|
|
56
|
+
std = np.interp(mask, radius, self.background_std)
|
|
57
|
+
np.maximum(0.0, np.random.normal(dense, std), out=dense)
|
|
58
|
+
if normalization is not None:
|
|
59
|
+
dense *= normalization
|
|
60
|
+
|
|
61
|
+
flat = dense.ravel()
|
|
62
|
+
flat[self.index] = self.intensity
|
|
63
|
+
dtype = self.intensity.dtype
|
|
64
|
+
if np.issubdtype(dtype, np.integer):
|
|
65
|
+
dense = np.round(dense)
|
|
66
|
+
dense = np.ascontiguousarray(dense, dtype=dtype)
|
|
67
|
+
dense[np.logical_not(np.isfinite(mask))] = dummy
|
|
68
|
+
return dense
|
|
69
|
+
|
|
70
|
+
def densify_peaks(self):
|
|
71
|
+
"""Generate a dense image of its sparse representation with peaks only
|
|
72
|
+
|
|
73
|
+
:param mask: 2D array with NaNs for mask and pixel radius for the valid pixels
|
|
74
|
+
:return dense array
|
|
75
|
+
"""
|
|
76
|
+
dense = np.zeros(self.shape, self.intensity.dtype)
|
|
77
|
+
flat = dense.ravel()
|
|
78
|
+
flat[self.index] = self.intensity
|
|
79
|
+
return dense
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def data(self):
|
|
83
|
+
return self.densify_peaks()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def decode_sparse_frame(raw_data: bytes) -> SmxSparseFrame:
|
|
87
|
+
"""Decode data provided by Lima2
|
|
88
|
+
|
|
89
|
+
Argument:
|
|
90
|
+
raw_data: DevEncoded data
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
A SmxSparseFrame.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
if isinstance(raw_data, tuple):
|
|
97
|
+
# Support the direct output from proxy.readImage
|
|
98
|
+
if raw_data[0] != "SMX_SPARSE_FRAME":
|
|
99
|
+
raise DevEncodedFormatNotSupported(
|
|
100
|
+
"Data type SMX_SPARSE_FRAME expected (found %s)." % raw_data[0]
|
|
101
|
+
)
|
|
102
|
+
raw_data = raw_data[1]
|
|
103
|
+
|
|
104
|
+
(
|
|
105
|
+
width,
|
|
106
|
+
height,
|
|
107
|
+
nb_bins,
|
|
108
|
+
nb_peaks,
|
|
109
|
+
frame_idx,
|
|
110
|
+
) = struct.unpack_from(DATA_HEADER_FORMAT, raw_data)
|
|
111
|
+
|
|
112
|
+
offset = DATA_HEADER_SIZE
|
|
113
|
+
index = np.frombuffer(raw_data, count=nb_peaks, offset=offset, dtype=np.int32)
|
|
114
|
+
offset += index.nbytes
|
|
115
|
+
intensity = np.frombuffer(raw_data, count=nb_peaks, offset=offset, dtype=np.float32)
|
|
116
|
+
offset += intensity.nbytes
|
|
117
|
+
background_avg = np.frombuffer(
|
|
118
|
+
raw_data, count=nb_bins, offset=offset, dtype=np.float32
|
|
119
|
+
)
|
|
120
|
+
offset += background_avg.nbytes
|
|
121
|
+
background_std = np.frombuffer(
|
|
122
|
+
raw_data, count=nb_bins, offset=offset, dtype=np.float32
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
shape = (1, height, width)
|
|
126
|
+
return SmxSparseFrame(
|
|
127
|
+
index, intensity, background_avg, background_std, frame_idx, shape
|
|
128
|
+
)
|
|
@@ -28,7 +28,12 @@ from typing_extensions import Optional
|
|
|
28
28
|
|
|
29
29
|
from lima2.client import progress_counter, utils
|
|
30
30
|
from lima2.client.convert import frame_info_to_shape_dtype
|
|
31
|
-
from lima2.client.devencoded import
|
|
31
|
+
from lima2.client.devencoded import (
|
|
32
|
+
dense_frame,
|
|
33
|
+
sparse_frame,
|
|
34
|
+
smx_sparse_frame,
|
|
35
|
+
structured_array,
|
|
36
|
+
)
|
|
32
37
|
from lima2.client.progress_counter import ProgressCounter, SingleCounter
|
|
33
38
|
from lima2.client.topology import (
|
|
34
39
|
RoundRobinTopology,
|
|
@@ -45,11 +50,13 @@ logger = logging.getLogger(__name__)
|
|
|
45
50
|
class FrameType(Enum):
|
|
46
51
|
DENSE = auto()
|
|
47
52
|
SPARSE = auto()
|
|
53
|
+
SMX_SPARSE = auto()
|
|
48
54
|
|
|
49
55
|
|
|
50
56
|
decoder_by_type: dict[FrameType, Callable] = {
|
|
51
57
|
FrameType.DENSE: dense_frame.decode,
|
|
52
58
|
FrameType.SPARSE: sparse_frame.decode,
|
|
59
|
+
FrameType.SMX_SPARSE: smx_sparse_frame.decode_sparse_frame,
|
|
53
60
|
}
|
|
54
61
|
"""Mapping from frame type to associated decode function"""
|
|
55
62
|
|
|
@@ -36,7 +36,7 @@ class Smx(Pipeline):
|
|
|
36
36
|
),
|
|
37
37
|
"sparse_frame": FrameSource(
|
|
38
38
|
getter_name="getSparseFrame",
|
|
39
|
-
frame_type=FrameType.
|
|
39
|
+
frame_type=FrameType.SMX_SPARSE,
|
|
40
40
|
saving_channel="saving_sparse",
|
|
41
41
|
),
|
|
42
42
|
"acc_corrected": FrameSource(
|
|
@@ -121,3 +121,13 @@ class Smx(Pipeline):
|
|
|
121
121
|
logger.debug(f"Returning peak counters for frames: {data['frame_idx']}")
|
|
122
122
|
|
|
123
123
|
return (data["frame_idx"], data["nb_peaks"])
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def channels(self):
|
|
127
|
+
"""Return the channel descriptions"""
|
|
128
|
+
return {
|
|
129
|
+
"frame": self.input_frame_info[0],
|
|
130
|
+
"sparse_frame": self.processed_frame_info[0],
|
|
131
|
+
"acc_corrected": self.processed_frame_info[0],
|
|
132
|
+
"acc_peaks": self.processed_frame_info[0],
|
|
133
|
+
}
|
|
@@ -83,7 +83,10 @@ class StateMachine(fsm.StateMachine):
|
|
|
83
83
|
State.FAULT,
|
|
84
84
|
State.UNKNOWN,
|
|
85
85
|
]:
|
|
86
|
-
|
|
86
|
+
_logger.error(
|
|
87
|
+
f"Device {evt.device.dev_name()} in FAULT state. Stopping all devices."
|
|
88
|
+
)
|
|
89
|
+
gevent.spawn(self.abort).join()
|
|
87
90
|
|
|
88
91
|
if self.state == State.DISCONNECTED:
|
|
89
92
|
_logger.info(
|
|
@@ -123,6 +126,8 @@ class StateMachine(fsm.StateMachine):
|
|
|
123
126
|
# Tolerate a mix of prepared and idle devices. In this case,
|
|
124
127
|
# the global state shall be IDLE (forcing user to prepare again).
|
|
125
128
|
self.state = State.IDLE
|
|
129
|
+
elif any(s == ServerState.fault for s in states):
|
|
130
|
+
self.state = State.FAULT
|
|
126
131
|
elif not all(s == ctl_state for s in states):
|
|
127
132
|
# Otherwise, if states are divergent, sync cannot be done
|
|
128
133
|
self._state = State.UNKNOWN
|
|
@@ -153,7 +158,7 @@ class StateMachine(fsm.StateMachine):
|
|
|
153
158
|
self.on_state_change(value)
|
|
154
159
|
|
|
155
160
|
@transition(
|
|
156
|
-
source=[State.IDLE, State.PREPARED
|
|
161
|
+
source=[State.IDLE, State.PREPARED],
|
|
157
162
|
target=State.PREPARING,
|
|
158
163
|
on_error=State.IDLE,
|
|
159
164
|
)
|
|
@@ -240,8 +245,18 @@ class StateMachine(fsm.StateMachine):
|
|
|
240
245
|
],
|
|
241
246
|
target=State.FAULT,
|
|
242
247
|
)
|
|
243
|
-
def
|
|
244
|
-
|
|
248
|
+
def abort(self):
|
|
249
|
+
"""Called when a device enters FAULT state."""
|
|
250
|
+
self.ctrl.Stop()
|
|
251
|
+
|
|
252
|
+
# Stop acquisition on receivers that are running
|
|
253
|
+
greenlets = [
|
|
254
|
+
gevent.spawn(recv.Stop)
|
|
255
|
+
for recv in self.recvs
|
|
256
|
+
if recv.acq_state.name == "running"
|
|
257
|
+
]
|
|
258
|
+
gevent.joinall(greenlets, raise_error=False)
|
|
259
|
+
|
|
245
260
|
self.ctrl.Close()
|
|
246
261
|
|
|
247
262
|
def __subscribe_events(self):
|
|
@@ -271,7 +286,8 @@ class StateMachine(fsm.StateMachine):
|
|
|
271
286
|
r.unsubscribe_event(e)
|
|
272
287
|
|
|
273
288
|
_logger.debug(f"on_end_acq while {self.state}")
|
|
274
|
-
|
|
289
|
+
if self.state == State.RUNNING:
|
|
290
|
+
gevent.spawn(self.close)
|
|
275
291
|
|
|
276
292
|
nb_frames_xferreds = []
|
|
277
293
|
|
|
@@ -22,6 +22,7 @@ src/lima2/client/utils.py
|
|
|
22
22
|
src/lima2/client/devencoded/__init__.py
|
|
23
23
|
src/lima2/client/devencoded/dense_frame.py
|
|
24
24
|
src/lima2/client/devencoded/exception.py
|
|
25
|
+
src/lima2/client/devencoded/smx_sparse_frame.py
|
|
25
26
|
src/lima2/client/devencoded/sparse_frame.py
|
|
26
27
|
src/lima2/client/devencoded/structured_array.py
|
|
27
28
|
src/lima2/client/pipelines/__init__.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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|