ewoksdata 0.5.0__tar.gz → 0.6.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.
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/PKG-INFO +4 -1
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/setup.cfg +3 -0
- ewoksdata-0.6.0/src/ewoksdata/__init__.py +1 -0
- ewoksdata-0.6.0/src/ewoksdata/data/blissdata/__init__.py +30 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/data/blissdata/blissdatav0.py +2 -8
- ewoksdata-0.6.0/src/ewoksdata/data/blissdata/blissdatav1.py +267 -0
- ewoksdata-0.6.0/src/ewoksdata/data/blissdata/blissdatav2.py +234 -0
- ewoksdata-0.6.0/src/ewoksdata/data/contextiterator.py +43 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/data/hdf5/dataset_writer.py +22 -14
- ewoksdata-0.6.0/src/ewoksdata/tests/test_contextiterator.py +61 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata.egg-info/PKG-INFO +4 -1
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata.egg-info/SOURCES.txt +2 -2
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata.egg-info/requires.txt +3 -0
- ewoksdata-0.5.0/src/ewoksdata/__init__.py +0 -1
- ewoksdata-0.5.0/src/ewoksdata/data/blissdata/__init__.py +0 -36
- ewoksdata-0.5.0/src/ewoksdata/data/blissdata/blissdatav1.py +0 -91
- ewoksdata-0.5.0/src/ewoksdata/data/blissdata/blissdatavid31.py +0 -105
- ewoksdata-0.5.0/src/ewoksdata/data/blissdata/exceptions.py +0 -4
- ewoksdata-0.5.0/src/ewoksdata/data/contextiterator.py +0 -35
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/LICENSE.md +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/README.md +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/pyproject.toml +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/setup.py +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/data/__init__.py +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/data/bliss.py +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/data/hdf5/__init__.py +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/data/hdf5/config.py +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/data/hdf5/types.py +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/data/nexus.py +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/data/url.py +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/data/utils.py +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/tests/__init__.py +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/tests/conftest.py +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/tests/data/__init__.py +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/tests/data/bliss_scans.py +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/tests/test_data_bliss.py +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/tests/test_data_hdf5.py +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/tests/test_data_nexus.py +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata/tests/test_dataset_writer.py +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata.egg-info/dependency_links.txt +0 -0
- {ewoksdata-0.5.0 → ewoksdata-0.6.0}/src/ewoksdata.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ewoksdata
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Ewoks tasks for data access
|
|
5
5
|
Home-page: https://gitlab.esrf.fr/workflow/ewoksapps/ewoksdata/
|
|
6
6
|
Author: ESRF
|
|
@@ -18,9 +18,12 @@ License-File: LICENSE.md
|
|
|
18
18
|
Requires-Dist: h5py>=2.8
|
|
19
19
|
Requires-Dist: silx>=1.1.0
|
|
20
20
|
Requires-Dist: hdf5plugin
|
|
21
|
+
Requires-Dist: blissdata>=0.3.4
|
|
21
22
|
Requires-Dist: blissdata<1.0.1; python_version < "3.8"
|
|
22
23
|
Requires-Dist: blissdata; python_version >= "3.8"
|
|
23
24
|
Requires-Dist: redis
|
|
25
|
+
Requires-Dist: packaging
|
|
26
|
+
Requires-Dist: importlib_metadata; python_version < "3.8"
|
|
24
27
|
Provides-Extra: online
|
|
25
28
|
Requires-Dist: blissdata[tango]; extra == "online"
|
|
26
29
|
Provides-Extra: test
|
|
@@ -26,9 +26,12 @@ install_requires =
|
|
|
26
26
|
h5py >=2.8
|
|
27
27
|
silx >=1.1.0
|
|
28
28
|
hdf5plugin
|
|
29
|
+
blissdata >=0.3.4
|
|
29
30
|
blissdata <1.0.1; python_version < "3.8"
|
|
30
31
|
blissdata; python_version >= "3.8"
|
|
31
32
|
redis # blissdata<1
|
|
33
|
+
packaging
|
|
34
|
+
importlib_metadata; python_version < "3.8"
|
|
32
35
|
|
|
33
36
|
[options.packages.find]
|
|
34
37
|
where = src
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.6.0"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
if sys.version_info >= (3, 8):
|
|
4
|
+
from importlib.metadata import version
|
|
5
|
+
else:
|
|
6
|
+
from importlib_metadata import version
|
|
7
|
+
from packaging.specifiers import SpecifierSet
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _not_supported_by_blissdata_version(*_, **kw):
|
|
11
|
+
raise RuntimeError(f"Not supported by blissdata {_BLISSDATA_VERSION}")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
from blissdata.h5api import dynamic_hdf5 # noqa F401
|
|
15
|
+
|
|
16
|
+
_BLISSDATA_VERSION = version("blissdata")
|
|
17
|
+
|
|
18
|
+
if _BLISSDATA_VERSION in SpecifierSet("<1", prereleases=True):
|
|
19
|
+
from .blissdatav0 import iter_bliss_scan_data_from_memory # noqa F401
|
|
20
|
+
from .blissdatav0 import last_lima_image # noqa F401
|
|
21
|
+
|
|
22
|
+
iter_bliss_scan_data_from_memory_slice = _not_supported_by_blissdata_version
|
|
23
|
+
elif _BLISSDATA_VERSION in SpecifierSet("<2", prereleases=True):
|
|
24
|
+
from .blissdatav1 import iter_bliss_scan_data_from_memory # noqa F401
|
|
25
|
+
from .blissdatav1 import iter_bliss_scan_data_from_memory_slice # noqa F401
|
|
26
|
+
from .blissdatav1 import last_lima_image # noqa F401
|
|
27
|
+
else:
|
|
28
|
+
from .blissdatav2 import iter_bliss_scan_data_from_memory # noqa F401
|
|
29
|
+
from .blissdatav2 import iter_bliss_scan_data_from_memory_slice # noqa F401
|
|
30
|
+
from .blissdatav2 import last_lima_image # noqa F401
|
|
@@ -4,14 +4,8 @@ from typing import List, Optional
|
|
|
4
4
|
|
|
5
5
|
from numpy.typing import ArrayLike
|
|
6
6
|
from silx.utils import retry as retrymod
|
|
7
|
-
|
|
8
|
-
from .
|
|
9
|
-
|
|
10
|
-
try:
|
|
11
|
-
from blissdata.data.node import get_node
|
|
12
|
-
from blissdata.data.events.lima import ImageNotSaved
|
|
13
|
-
except ImportError as e:
|
|
14
|
-
raise VersionError(str(e)) from e
|
|
7
|
+
from blissdata.data.node import get_node
|
|
8
|
+
from blissdata.data.events.lima import ImageNotSaved
|
|
15
9
|
|
|
16
10
|
|
|
17
11
|
logger = logging.getLogger(__name__)
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import time
|
|
3
|
+
import logging
|
|
4
|
+
from numbers import Number
|
|
5
|
+
from collections import Counter
|
|
6
|
+
from typing import List, Optional, Tuple
|
|
7
|
+
|
|
8
|
+
import numpy
|
|
9
|
+
from numpy.typing import ArrayLike
|
|
10
|
+
from blissdata.beacon.data import BeaconData
|
|
11
|
+
from blissdata.redis_engine.store import DataStore
|
|
12
|
+
from blissdata.redis_engine.scan import Scan
|
|
13
|
+
from blissdata.redis_engine.scan import ScanState
|
|
14
|
+
from blissdata.redis_engine.stream import StreamingClient
|
|
15
|
+
from blissdata.redis_engine.exceptions import (
|
|
16
|
+
EndOfStream,
|
|
17
|
+
IndexNoMoreThereError,
|
|
18
|
+
IndexNotYetThereError,
|
|
19
|
+
IndexWontBeThereError,
|
|
20
|
+
)
|
|
21
|
+
from blissdata.lima.client import lima_client_factory
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
from blissdata.stream import LimaStream
|
|
25
|
+
except ImportError:
|
|
26
|
+
from blissdata.streams.lima_stream import LimaStream
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
INFINITY = sys.maxsize
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _get_data_store() -> None:
|
|
35
|
+
redis_url = BeaconData().get_redis_data_db()
|
|
36
|
+
return DataStore(redis_url)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def iter_bliss_scan_data_from_memory(
|
|
40
|
+
scan_key: str,
|
|
41
|
+
lima_names: List[str],
|
|
42
|
+
counter_names: List[str],
|
|
43
|
+
retry_timeout: Optional[Number] = None,
|
|
44
|
+
retry_period: Optional[Number] = None,
|
|
45
|
+
):
|
|
46
|
+
data_store = _get_data_store()
|
|
47
|
+
scan = data_store.load_scan(scan_key, scan_cls=Scan)
|
|
48
|
+
buffers = {name: list() for name in lima_names + counter_names}
|
|
49
|
+
|
|
50
|
+
while scan.state < ScanState.PREPARED:
|
|
51
|
+
scan.update()
|
|
52
|
+
|
|
53
|
+
lima_streams = dict()
|
|
54
|
+
lima_clients = dict()
|
|
55
|
+
counter_streams = dict()
|
|
56
|
+
for name, stream in scan.streams.items():
|
|
57
|
+
if stream.encoding["type"] == "json" and "lima" in stream.info["format"]:
|
|
58
|
+
if name.split(":")[-2] in lima_names:
|
|
59
|
+
lima_streams[name] = stream
|
|
60
|
+
lima_clients[name] = lima_client_factory(data_store, stream.info)
|
|
61
|
+
elif name.split(":")[-1] in counter_names:
|
|
62
|
+
counter_streams[name] = stream
|
|
63
|
+
|
|
64
|
+
client = StreamingClient({**lima_streams, **counter_streams})
|
|
65
|
+
client_timeout = retry_period or 0
|
|
66
|
+
lima_buffer_count = Counter()
|
|
67
|
+
|
|
68
|
+
while True:
|
|
69
|
+
try:
|
|
70
|
+
output = client.read(timeout=client_timeout)
|
|
71
|
+
except EndOfStream:
|
|
72
|
+
break
|
|
73
|
+
for stream, (_, payload) in output.items():
|
|
74
|
+
name_parts = stream.name.split(":")
|
|
75
|
+
if stream.name in lima_streams:
|
|
76
|
+
# payload is a sequence of JSON statuses
|
|
77
|
+
ctr_name = name_parts[-2]
|
|
78
|
+
last_status = payload[-1]
|
|
79
|
+
lima_client = lima_clients[stream.name]
|
|
80
|
+
lima_client.update(**last_status)
|
|
81
|
+
n_already_read = lima_buffer_count[ctr_name]
|
|
82
|
+
try:
|
|
83
|
+
data = lima_client[n_already_read:]
|
|
84
|
+
except IndexNoMoreThereError:
|
|
85
|
+
continue
|
|
86
|
+
buffers[ctr_name].extend(data)
|
|
87
|
+
lima_buffer_count[ctr_name] += len(data)
|
|
88
|
+
else:
|
|
89
|
+
# payload is a sequence of data points (0D, 1D, 2D)
|
|
90
|
+
ctr_name = name_parts[-1]
|
|
91
|
+
buffers[ctr_name].extend(payload)
|
|
92
|
+
|
|
93
|
+
nyield = min(len(v) for v in buffers.values())
|
|
94
|
+
if nyield:
|
|
95
|
+
for i in range(nyield):
|
|
96
|
+
yield {name: values[i] for name, values in buffers.items()}
|
|
97
|
+
buffers = {name: values[nyield:] for name, values in buffers.items()}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def last_lima_image(channel_info: dict) -> ArrayLike:
|
|
101
|
+
"""Get last lima image from memory"""
|
|
102
|
+
data_store = _get_data_store()
|
|
103
|
+
lima_client = lima_client_factory(data_store, channel_info)
|
|
104
|
+
return lima_client.get_last_live_image().array
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _get_streams(
|
|
108
|
+
scan_key: str,
|
|
109
|
+
lima_names: List[str],
|
|
110
|
+
counter_names: List[str],
|
|
111
|
+
):
|
|
112
|
+
data_store = _get_data_store()
|
|
113
|
+
scan = data_store.load_scan(scan_key, scan_cls=Scan)
|
|
114
|
+
|
|
115
|
+
while scan.state < ScanState.PREPARED:
|
|
116
|
+
scan.update()
|
|
117
|
+
|
|
118
|
+
lima_streams = dict()
|
|
119
|
+
counter_streams = dict()
|
|
120
|
+
|
|
121
|
+
for name, stream in scan.streams.items():
|
|
122
|
+
if stream.encoding["type"] == "json" and "lima" in stream.info["format"]:
|
|
123
|
+
if name.split(":")[-2] in lima_names:
|
|
124
|
+
lima_streams[name.split(":")[-2]] = LimaStream(stream)
|
|
125
|
+
|
|
126
|
+
elif name.split(":")[-1] in counter_names:
|
|
127
|
+
counter_streams[name.split(":")[-1]] = stream
|
|
128
|
+
|
|
129
|
+
return lima_streams, counter_streams
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def iter_bliss_scan_data_from_memory_slice(
|
|
133
|
+
scan_key: str,
|
|
134
|
+
lima_names: List[str],
|
|
135
|
+
counter_names: List[str],
|
|
136
|
+
slice_range: Optional[Tuple[int, int]] = None,
|
|
137
|
+
retry_timeout: Optional[Number] = None,
|
|
138
|
+
retry_period: Optional[Number] = None,
|
|
139
|
+
yield_timeout: Optional[Number] = None,
|
|
140
|
+
max_slicing_size: Optional[Number] = None,
|
|
141
|
+
verbose: Optional[bool] = False,
|
|
142
|
+
):
|
|
143
|
+
"""Iterates over the data from a Bliss scan, slicing the streams associated to a lima detector or a counter between specific indexes of the scan (optional)
|
|
144
|
+
|
|
145
|
+
:param str scan_key: key of the Bliss scan (e.g. "esrf:scan:XXXX")
|
|
146
|
+
:param list lima_names: names of lima detectors
|
|
147
|
+
:param list counter_names: names of non-lima detectors (you need to provide at least one)
|
|
148
|
+
:param tuple slice_range: two elements which define the limits of the iteration along the scan. If None, it iterates along the whole scan
|
|
149
|
+
:param Number retry_timeout: timeout when it cannot access the data for `retry_timeout` seconds
|
|
150
|
+
:param Number retry_period: interval in seconds between data access retries
|
|
151
|
+
:param Number yield_timeout: timeout to stop slicing the stream and yield the buffered data
|
|
152
|
+
:param Number max_slicing_size: maximum size of frames to be sliced out of the stream in one single iteration. If None, it will slice all the available data in the stream
|
|
153
|
+
:yields dict: data
|
|
154
|
+
"""
|
|
155
|
+
lima_streams, counter_streams = _get_streams(scan_key, lima_names, counter_names)
|
|
156
|
+
|
|
157
|
+
all_streams = {**lima_streams, **counter_streams}
|
|
158
|
+
if not all_streams:
|
|
159
|
+
logger.warning("There is no stream to slice")
|
|
160
|
+
return
|
|
161
|
+
|
|
162
|
+
if slice_range is None:
|
|
163
|
+
slice_range = (0, INFINITY)
|
|
164
|
+
|
|
165
|
+
if retry_period is None:
|
|
166
|
+
retry_period = 1
|
|
167
|
+
|
|
168
|
+
if yield_timeout is None:
|
|
169
|
+
yield_timeout = 0.01
|
|
170
|
+
|
|
171
|
+
buffers_count = Counter({counter: slice_range[0] for counter in all_streams.keys()})
|
|
172
|
+
|
|
173
|
+
# Read and yield continuously
|
|
174
|
+
stream_on = True
|
|
175
|
+
|
|
176
|
+
incoming_buffers = {stream_name: [] for stream_name in all_streams.keys()}
|
|
177
|
+
non_yielded_buffers = {stream_name: [] for stream_name in all_streams.keys()}
|
|
178
|
+
|
|
179
|
+
restart_buffer = time.perf_counter()
|
|
180
|
+
while stream_on:
|
|
181
|
+
|
|
182
|
+
# While loop will stop unless one single stream is successfully sliced
|
|
183
|
+
stream_on = False
|
|
184
|
+
|
|
185
|
+
for stream_name, stream in all_streams.items():
|
|
186
|
+
try:
|
|
187
|
+
# Stop condition for limited slices
|
|
188
|
+
if (
|
|
189
|
+
slice_range[1] is not INFINITY
|
|
190
|
+
and buffers_count[stream_name] >= slice_range[1]
|
|
191
|
+
):
|
|
192
|
+
continue
|
|
193
|
+
|
|
194
|
+
# Test first index, (slicing between limits do not fall into Error)
|
|
195
|
+
_ = stream[buffers_count[stream_name]]
|
|
196
|
+
if max_slicing_size is None:
|
|
197
|
+
stream_data = stream[buffers_count[stream_name] : slice_range[1]]
|
|
198
|
+
else:
|
|
199
|
+
stream_data = stream[
|
|
200
|
+
buffers_count[stream_name] : min(
|
|
201
|
+
slice_range[1],
|
|
202
|
+
buffers_count[stream_name] + max_slicing_size,
|
|
203
|
+
)
|
|
204
|
+
]
|
|
205
|
+
incoming_buffers[stream_name] = stream_data
|
|
206
|
+
buffers_count[stream_name] += len(stream_data)
|
|
207
|
+
stream_on = True
|
|
208
|
+
|
|
209
|
+
except IndexNotYetThereError:
|
|
210
|
+
stream_on = True
|
|
211
|
+
except IndexWontBeThereError:
|
|
212
|
+
pass
|
|
213
|
+
except IndexNoMoreThereError:
|
|
214
|
+
pass
|
|
215
|
+
except EndOfStream:
|
|
216
|
+
pass
|
|
217
|
+
except RuntimeError:
|
|
218
|
+
pass
|
|
219
|
+
|
|
220
|
+
for stream_name in incoming_buffers.keys():
|
|
221
|
+
if len(incoming_buffers[stream_name]) > 0:
|
|
222
|
+
if len(non_yielded_buffers[stream_name]) == 0:
|
|
223
|
+
non_yielded_buffers[stream_name] = numpy.array(
|
|
224
|
+
incoming_buffers[stream_name]
|
|
225
|
+
)
|
|
226
|
+
else:
|
|
227
|
+
non_yielded_buffers[stream_name] = numpy.concatenate(
|
|
228
|
+
(
|
|
229
|
+
non_yielded_buffers[stream_name],
|
|
230
|
+
incoming_buffers[stream_name],
|
|
231
|
+
)
|
|
232
|
+
)
|
|
233
|
+
incoming_buffers[stream_name] = []
|
|
234
|
+
|
|
235
|
+
if not stream_on or ((time.perf_counter() - restart_buffer) > yield_timeout):
|
|
236
|
+
|
|
237
|
+
frames_to_yield = min(
|
|
238
|
+
[len(value) for value in non_yielded_buffers.values()]
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
if frames_to_yield > 0:
|
|
242
|
+
if verbose:
|
|
243
|
+
for stream_name, stream_buffer in non_yielded_buffers.items():
|
|
244
|
+
print(
|
|
245
|
+
f"After slicing the stream: {stream_name} buffer contains {len(stream_buffer)} items"
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
# Yield point by point
|
|
249
|
+
for index in range(frames_to_yield):
|
|
250
|
+
yield {
|
|
251
|
+
stream_name: stream_buffer[index]
|
|
252
|
+
for stream_name, stream_buffer in non_yielded_buffers.items()
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
# Save the non-yielded points for the next iteration
|
|
256
|
+
for stream_name in non_yielded_buffers.keys():
|
|
257
|
+
non_yielded_buffers[stream_name] = non_yielded_buffers[stream_name][
|
|
258
|
+
frames_to_yield:
|
|
259
|
+
]
|
|
260
|
+
|
|
261
|
+
if verbose:
|
|
262
|
+
for stream_name, stream_buffer in non_yielded_buffers.items():
|
|
263
|
+
print(
|
|
264
|
+
f"After yielding: {stream_name} buffer contains {len(stream_buffer)} non-yielded items"
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
restart_buffer = time.perf_counter()
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import time
|
|
3
|
+
import logging
|
|
4
|
+
from collections import Counter
|
|
5
|
+
from typing import List, Optional, Tuple
|
|
6
|
+
|
|
7
|
+
import numpy
|
|
8
|
+
from numpy.typing import ArrayLike
|
|
9
|
+
|
|
10
|
+
from blissdata.beacon.data import BeaconData
|
|
11
|
+
from blissdata.redis_engine.store import DataStore
|
|
12
|
+
from blissdata.redis_engine.scan import ScanState
|
|
13
|
+
from blissdata.redis_engine.exceptions import (
|
|
14
|
+
EndOfStream,
|
|
15
|
+
IndexNoMoreThereError,
|
|
16
|
+
IndexNotYetThereError,
|
|
17
|
+
IndexWontBeThereError,
|
|
18
|
+
)
|
|
19
|
+
from blissdata.streams.base import CursorGroup
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
INFINITY = sys.maxsize
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _get_data_store() -> None:
|
|
28
|
+
redis_url = BeaconData().get_redis_data_db()
|
|
29
|
+
return DataStore(redis_url)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def iter_bliss_scan_data_from_memory(
|
|
33
|
+
scan_key: str,
|
|
34
|
+
lima_names: List[str],
|
|
35
|
+
counter_names: List[str],
|
|
36
|
+
retry_timeout: Optional[float] = None,
|
|
37
|
+
retry_period: Optional[float] = None,
|
|
38
|
+
):
|
|
39
|
+
streams = _get_streams(scan_key, lima_names, counter_names)
|
|
40
|
+
if not streams:
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
cursor = CursorGroup(list(streams.values()))
|
|
44
|
+
cursor_timeout = retry_period or 0
|
|
45
|
+
buffers = {ctr_name: list() for ctr_name in lima_names + counter_names}
|
|
46
|
+
ctr_names = {stream.name: ctr_name for ctr_name, stream in streams.items()}
|
|
47
|
+
|
|
48
|
+
while True:
|
|
49
|
+
try:
|
|
50
|
+
views = cursor.read(timeout=cursor_timeout)
|
|
51
|
+
except EndOfStream:
|
|
52
|
+
break
|
|
53
|
+
|
|
54
|
+
for stream, view in views.items():
|
|
55
|
+
ctr_name = ctr_names[stream.name]
|
|
56
|
+
buffers[ctr_name].extend(view.get_data())
|
|
57
|
+
|
|
58
|
+
nyield = min(len(v) for v in buffers.values())
|
|
59
|
+
if nyield:
|
|
60
|
+
for i in range(nyield):
|
|
61
|
+
yield {name: values[i] for name, values in buffers.items()}
|
|
62
|
+
buffers = {name: values[nyield:] for name, values in buffers.items()}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def last_lima_image(scan_key: str, lima_name: str) -> ArrayLike:
|
|
66
|
+
"""Get last lima image from memory"""
|
|
67
|
+
streams = _get_streams(scan_key, [lima_name], [])
|
|
68
|
+
stream = list(streams.values())[0]
|
|
69
|
+
return stream.get_last_live_image().array
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _get_streams(
|
|
73
|
+
scan_key: str,
|
|
74
|
+
lima_names: List[str],
|
|
75
|
+
counter_names: List[str],
|
|
76
|
+
):
|
|
77
|
+
data_store = _get_data_store()
|
|
78
|
+
scan = data_store.load_scan(scan_key)
|
|
79
|
+
|
|
80
|
+
while scan.state < ScanState.PREPARED:
|
|
81
|
+
scan.update()
|
|
82
|
+
|
|
83
|
+
streams = dict()
|
|
84
|
+
|
|
85
|
+
for name, stream in scan.streams.items():
|
|
86
|
+
if (
|
|
87
|
+
stream.event_stream.encoding["type"] == "json"
|
|
88
|
+
and "lima" in stream.info["format"]
|
|
89
|
+
):
|
|
90
|
+
if name.split(":")[-2] in lima_names:
|
|
91
|
+
streams[name.split(":")[-2]] = stream
|
|
92
|
+
|
|
93
|
+
elif name.split(":")[-1] in counter_names:
|
|
94
|
+
streams[name.split(":")[-1]] = stream
|
|
95
|
+
|
|
96
|
+
nnames = len(lima_names) + len(counter_names)
|
|
97
|
+
nstreams = len(streams)
|
|
98
|
+
if nnames != nstreams:
|
|
99
|
+
logger.warning("asked for %d names but got %s streams", nnames, nstreams)
|
|
100
|
+
return dict()
|
|
101
|
+
|
|
102
|
+
return streams
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def iter_bliss_scan_data_from_memory_slice(
|
|
106
|
+
scan_key: str,
|
|
107
|
+
lima_names: List[str],
|
|
108
|
+
counter_names: List[str],
|
|
109
|
+
slice_range: Optional[Tuple[int, int]] = None,
|
|
110
|
+
retry_timeout: Optional[float] = None,
|
|
111
|
+
retry_period: Optional[float] = None,
|
|
112
|
+
yield_timeout: Optional[float] = None,
|
|
113
|
+
max_slicing_size: Optional[float] = None,
|
|
114
|
+
verbose: Optional[bool] = False,
|
|
115
|
+
):
|
|
116
|
+
"""Iterates over the data from a Bliss scan, slicing the streams associated to a lima detector or a counter between specific indexes of the scan (optional)
|
|
117
|
+
|
|
118
|
+
:param str scan_key: key of the Bliss scan (e.g. "esrf:scan:XXXX")
|
|
119
|
+
:param list lima_names: names of lima detectors
|
|
120
|
+
:param list counter_names: names of non-lima detectors (you need to provide at least one)
|
|
121
|
+
:param tuple slice_range: two elements which define the limits of the iteration along the scan. If None, it iterates along the whole scan
|
|
122
|
+
:param float retry_timeout: timeout when it cannot access the data for `retry_timeout` seconds
|
|
123
|
+
:param float retry_period: interval in seconds between data access retries
|
|
124
|
+
:param float yield_timeout: timeout to stop slicing the stream and yield the buffered data
|
|
125
|
+
:param float max_slicing_size: maximum size of frames to be sliced out of the stream in one single iteration. If None, it will slice all the available data in the stream
|
|
126
|
+
:yields dict: data
|
|
127
|
+
"""
|
|
128
|
+
streams = _get_streams(scan_key, lima_names, counter_names)
|
|
129
|
+
if not streams:
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
if slice_range is None:
|
|
133
|
+
slice_range = (0, INFINITY)
|
|
134
|
+
|
|
135
|
+
if yield_timeout is None:
|
|
136
|
+
yield_timeout = 0.01
|
|
137
|
+
|
|
138
|
+
buffers_count = Counter({counter: slice_range[0] for counter in streams.keys()})
|
|
139
|
+
|
|
140
|
+
# Read and yield continuously
|
|
141
|
+
stream_on = True
|
|
142
|
+
|
|
143
|
+
incoming_buffers = {stream_name: [] for stream_name in streams.keys()}
|
|
144
|
+
non_yielded_buffers = {stream_name: [] for stream_name in streams.keys()}
|
|
145
|
+
|
|
146
|
+
restart_buffer = time.perf_counter()
|
|
147
|
+
while stream_on:
|
|
148
|
+
|
|
149
|
+
# While loop will stop unless one single stream is successfully sliced
|
|
150
|
+
stream_on = False
|
|
151
|
+
|
|
152
|
+
for stream_name, stream in streams.items():
|
|
153
|
+
try:
|
|
154
|
+
# Stop condition for limited slices
|
|
155
|
+
if (
|
|
156
|
+
slice_range[1] is not INFINITY
|
|
157
|
+
and buffers_count[stream_name] >= slice_range[1]
|
|
158
|
+
):
|
|
159
|
+
continue
|
|
160
|
+
|
|
161
|
+
# Test first index, (slicing between limits do not fall into Error)
|
|
162
|
+
_ = stream[buffers_count[stream_name]]
|
|
163
|
+
if max_slicing_size is None:
|
|
164
|
+
stream_data = stream[buffers_count[stream_name] : slice_range[1]]
|
|
165
|
+
else:
|
|
166
|
+
stream_data = stream[
|
|
167
|
+
buffers_count[stream_name] : min(
|
|
168
|
+
slice_range[1],
|
|
169
|
+
buffers_count[stream_name] + max_slicing_size,
|
|
170
|
+
)
|
|
171
|
+
]
|
|
172
|
+
incoming_buffers[stream_name] = stream_data
|
|
173
|
+
buffers_count[stream_name] += len(stream_data)
|
|
174
|
+
stream_on = True
|
|
175
|
+
|
|
176
|
+
except IndexNotYetThereError:
|
|
177
|
+
stream_on = True
|
|
178
|
+
except IndexWontBeThereError:
|
|
179
|
+
pass
|
|
180
|
+
except IndexNoMoreThereError:
|
|
181
|
+
pass
|
|
182
|
+
except EndOfStream:
|
|
183
|
+
pass
|
|
184
|
+
except RuntimeError:
|
|
185
|
+
pass
|
|
186
|
+
|
|
187
|
+
for stream_name in incoming_buffers.keys():
|
|
188
|
+
if len(incoming_buffers[stream_name]) > 0:
|
|
189
|
+
if len(non_yielded_buffers[stream_name]) == 0:
|
|
190
|
+
non_yielded_buffers[stream_name] = numpy.array(
|
|
191
|
+
incoming_buffers[stream_name]
|
|
192
|
+
)
|
|
193
|
+
else:
|
|
194
|
+
non_yielded_buffers[stream_name] = numpy.concatenate(
|
|
195
|
+
(
|
|
196
|
+
non_yielded_buffers[stream_name],
|
|
197
|
+
incoming_buffers[stream_name],
|
|
198
|
+
)
|
|
199
|
+
)
|
|
200
|
+
incoming_buffers[stream_name] = []
|
|
201
|
+
|
|
202
|
+
if not stream_on or ((time.perf_counter() - restart_buffer) > yield_timeout):
|
|
203
|
+
|
|
204
|
+
frames_to_yield = min(
|
|
205
|
+
[len(value) for value in non_yielded_buffers.values()]
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
if frames_to_yield > 0:
|
|
209
|
+
if verbose:
|
|
210
|
+
for stream_name, stream_buffer in non_yielded_buffers.items():
|
|
211
|
+
print(
|
|
212
|
+
f"After slicing the stream: {stream_name} buffer contains {len(stream_buffer)} items"
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# Yield point by point
|
|
216
|
+
for index in range(frames_to_yield):
|
|
217
|
+
yield {
|
|
218
|
+
stream_name: stream_buffer[index]
|
|
219
|
+
for stream_name, stream_buffer in non_yielded_buffers.items()
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
# Save the non-yielded points for the next iteration
|
|
223
|
+
for stream_name in non_yielded_buffers.keys():
|
|
224
|
+
non_yielded_buffers[stream_name] = non_yielded_buffers[stream_name][
|
|
225
|
+
frames_to_yield:
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
if verbose:
|
|
229
|
+
for stream_name, stream_buffer in non_yielded_buffers.items():
|
|
230
|
+
print(
|
|
231
|
+
f"After yielding: {stream_name} buffer contains {len(stream_buffer)} non-yielded items"
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
restart_buffer = time.perf_counter()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
from typing import Iterator
|
|
3
|
+
from collections.abc import Generator
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ContextIterator(Generator):
|
|
7
|
+
"""Can wrap an iterator or generator to ensure cleanup when used as a context manager."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, it: Iterator):
|
|
10
|
+
self._it = it
|
|
11
|
+
|
|
12
|
+
def __iter__(self):
|
|
13
|
+
return self
|
|
14
|
+
|
|
15
|
+
def __next__(self):
|
|
16
|
+
return next(self._it)
|
|
17
|
+
|
|
18
|
+
def __enter__(self):
|
|
19
|
+
return self
|
|
20
|
+
|
|
21
|
+
def __exit__(self, *_):
|
|
22
|
+
if hasattr(self._it, "close"):
|
|
23
|
+
return self._it.close()
|
|
24
|
+
|
|
25
|
+
def send(self, value):
|
|
26
|
+
if hasattr(self._it, "send"):
|
|
27
|
+
return self._it.send(value)
|
|
28
|
+
|
|
29
|
+
def throw(self, *args):
|
|
30
|
+
if hasattr(self._it, "throw"):
|
|
31
|
+
return self._it.throw(*args)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def contextiterator(iterator):
|
|
35
|
+
"""Decorator that allows to use an iterator or generator as a context manager.
|
|
36
|
+
This ensures that the iterator is closed when exiting the context manager.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
@functools.wraps(iterator)
|
|
40
|
+
def wrapper(*args, **kw):
|
|
41
|
+
return ContextIterator(iterator(*args, **kw))
|
|
42
|
+
|
|
43
|
+
return wrapper
|
|
@@ -57,17 +57,21 @@ class _DatasetWriterBase:
|
|
|
57
57
|
class DatasetWriter(_DatasetWriterBase):
|
|
58
58
|
"""Append arrays of the same shape to a new HDF5 dataset in a sequential manner.
|
|
59
59
|
|
|
60
|
+
Instead of creating a dataset with the :code:`h5py` API
|
|
61
|
+
|
|
60
62
|
.. code-block::python
|
|
61
63
|
|
|
62
|
-
|
|
63
|
-
writer.add_point([1,2,3])
|
|
64
|
-
writer.add_points([[4,5,6], [7,8,9]])
|
|
64
|
+
h5group["mydataset"] = [[1,2,3], [4,5,6], [7,8,9]]
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
it can be done like this
|
|
67
67
|
|
|
68
68
|
.. code-block::python
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
with DatasetWriter(h5group, "mydataset") as writer:
|
|
71
|
+
writer.add_point([1,2,3])
|
|
72
|
+
writer.add_points([[4,5,6], [7,8,9]])
|
|
73
|
+
|
|
74
|
+
Chunk size determination, chunk-aligned writing, compression and flushing is handled.
|
|
71
75
|
"""
|
|
72
76
|
|
|
73
77
|
def __init__(
|
|
@@ -168,21 +172,25 @@ class StackDatasetWriter(_DatasetWriterBase):
|
|
|
168
172
|
in a sequential manner per item. So each item of the HDF5 dataset is a
|
|
169
173
|
stack to which we can append data in a sequential manner.
|
|
170
174
|
|
|
175
|
+
Instead of creating a dataset with the :code:`h5py` API
|
|
176
|
+
|
|
171
177
|
.. code-block::python
|
|
172
178
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
writer.add_points([[4,5,6], [7,8,9]], 0)
|
|
177
|
-
writer.add_points([[13,14,15], [16,17,18]], 1)
|
|
179
|
+
stack0 = [[1,2,3], [4,5,6], [7,8,9]]
|
|
180
|
+
stack1 = [[10,11,12], [13,14,15], [16,17,18]]
|
|
181
|
+
h5group["mydataset"] = [stack0, stack1]
|
|
178
182
|
|
|
179
|
-
|
|
183
|
+
it can be done like this
|
|
180
184
|
|
|
181
185
|
.. code-block::python
|
|
182
186
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
187
|
+
with StackDatasetWriter(h5group, "mydataset") as writer:
|
|
188
|
+
writer.add_point([1,2,3], 0)
|
|
189
|
+
writer.add_point([10,11,12], 1)
|
|
190
|
+
writer.add_points([[13,14,15], [16,17,18]], 1)
|
|
191
|
+
writer.add_points([[4,5,6], [7,8,9]], 0)
|
|
192
|
+
|
|
193
|
+
Chunk size determination, chunk-aligned writing, compression and flushing is handled.
|
|
186
194
|
"""
|
|
187
195
|
|
|
188
196
|
def __init__(
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from ..data import contextiterator
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_context_iterator():
|
|
5
|
+
|
|
6
|
+
exited = False
|
|
7
|
+
|
|
8
|
+
@contextiterator.contextiterator
|
|
9
|
+
def iterator():
|
|
10
|
+
nonlocal exited
|
|
11
|
+
exited = False
|
|
12
|
+
try:
|
|
13
|
+
yield from [1, 2, 3]
|
|
14
|
+
finally:
|
|
15
|
+
exited = True
|
|
16
|
+
|
|
17
|
+
# Use like a normal iterator
|
|
18
|
+
it = iterator()
|
|
19
|
+
assert next(it) == 1
|
|
20
|
+
assert not exited
|
|
21
|
+
|
|
22
|
+
# Use like a context iterator
|
|
23
|
+
with iterator() as it:
|
|
24
|
+
assert next(it) == 1
|
|
25
|
+
assert not exited
|
|
26
|
+
assert exited
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_context_generator():
|
|
30
|
+
|
|
31
|
+
@contextiterator.contextiterator
|
|
32
|
+
def generator():
|
|
33
|
+
nonlocal exited
|
|
34
|
+
exited = False
|
|
35
|
+
try:
|
|
36
|
+
while True:
|
|
37
|
+
x = yield
|
|
38
|
+
yield x * 2
|
|
39
|
+
finally:
|
|
40
|
+
exited = True
|
|
41
|
+
|
|
42
|
+
# Use like a normal generator
|
|
43
|
+
exited = False
|
|
44
|
+
gen = generator()
|
|
45
|
+
assert next(gen) is None # run up to first yield
|
|
46
|
+
assert not exited
|
|
47
|
+
assert gen.send(10) == 20
|
|
48
|
+
assert next(gen) is None
|
|
49
|
+
assert gen.send(15) == 30
|
|
50
|
+
assert not exited
|
|
51
|
+
|
|
52
|
+
# Use like a context iterator
|
|
53
|
+
exited = False
|
|
54
|
+
with generator() as gen:
|
|
55
|
+
assert next(gen) is None
|
|
56
|
+
assert not exited
|
|
57
|
+
assert gen.send(10) == 20
|
|
58
|
+
assert next(gen) is None
|
|
59
|
+
assert gen.send(15) == 30
|
|
60
|
+
assert not exited
|
|
61
|
+
assert exited
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ewoksdata
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Ewoks tasks for data access
|
|
5
5
|
Home-page: https://gitlab.esrf.fr/workflow/ewoksapps/ewoksdata/
|
|
6
6
|
Author: ESRF
|
|
@@ -18,9 +18,12 @@ License-File: LICENSE.md
|
|
|
18
18
|
Requires-Dist: h5py>=2.8
|
|
19
19
|
Requires-Dist: silx>=1.1.0
|
|
20
20
|
Requires-Dist: hdf5plugin
|
|
21
|
+
Requires-Dist: blissdata>=0.3.4
|
|
21
22
|
Requires-Dist: blissdata<1.0.1; python_version < "3.8"
|
|
22
23
|
Requires-Dist: blissdata; python_version >= "3.8"
|
|
23
24
|
Requires-Dist: redis
|
|
25
|
+
Requires-Dist: packaging
|
|
26
|
+
Requires-Dist: importlib_metadata; python_version < "3.8"
|
|
24
27
|
Provides-Extra: online
|
|
25
28
|
Requires-Dist: blissdata[tango]; extra == "online"
|
|
26
29
|
Provides-Extra: test
|
|
@@ -18,14 +18,14 @@ src/ewoksdata/data/utils.py
|
|
|
18
18
|
src/ewoksdata/data/blissdata/__init__.py
|
|
19
19
|
src/ewoksdata/data/blissdata/blissdatav0.py
|
|
20
20
|
src/ewoksdata/data/blissdata/blissdatav1.py
|
|
21
|
-
src/ewoksdata/data/blissdata/
|
|
22
|
-
src/ewoksdata/data/blissdata/exceptions.py
|
|
21
|
+
src/ewoksdata/data/blissdata/blissdatav2.py
|
|
23
22
|
src/ewoksdata/data/hdf5/__init__.py
|
|
24
23
|
src/ewoksdata/data/hdf5/config.py
|
|
25
24
|
src/ewoksdata/data/hdf5/dataset_writer.py
|
|
26
25
|
src/ewoksdata/data/hdf5/types.py
|
|
27
26
|
src/ewoksdata/tests/__init__.py
|
|
28
27
|
src/ewoksdata/tests/conftest.py
|
|
28
|
+
src/ewoksdata/tests/test_contextiterator.py
|
|
29
29
|
src/ewoksdata/tests/test_data_bliss.py
|
|
30
30
|
src/ewoksdata/tests/test_data_hdf5.py
|
|
31
31
|
src/ewoksdata/tests/test_data_nexus.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.5.0"
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
from .exceptions import VersionError
|
|
2
|
-
|
|
3
|
-
try:
|
|
4
|
-
# blissdata >=1
|
|
5
|
-
from .blissdatav1 import iter_bliss_scan_data_from_memory
|
|
6
|
-
from .blissdatav1 import last_lima_image
|
|
7
|
-
from blissdata.h5api import dynamic_hdf5
|
|
8
|
-
except VersionError as exc:
|
|
9
|
-
try:
|
|
10
|
-
# blissdata >0.3.3,<1 (unreleased, branch id31_2.0)
|
|
11
|
-
from .blissdatavid31 import iter_bliss_scan_data_from_memory
|
|
12
|
-
from .blissdatavid31 import last_lima_image
|
|
13
|
-
from blissdata.h5api import dynamic_hdf5
|
|
14
|
-
except VersionError:
|
|
15
|
-
try:
|
|
16
|
-
# blissdata <=0.3.3
|
|
17
|
-
from .blissdatav0 import iter_bliss_scan_data_from_memory
|
|
18
|
-
from .blissdatav0 import last_lima_image
|
|
19
|
-
from blissdata.h5api import dynamic_hdf5
|
|
20
|
-
except VersionError: # noqa F841
|
|
21
|
-
_EXC = exc
|
|
22
|
-
|
|
23
|
-
def _reraise(*_, **kw):
|
|
24
|
-
raise _EXC
|
|
25
|
-
|
|
26
|
-
class BlissDataVersionNotSupported:
|
|
27
|
-
def __getattr__(self, *_):
|
|
28
|
-
return _reraise
|
|
29
|
-
|
|
30
|
-
dynamic_hdf5 = BlissDataVersionNotSupported()
|
|
31
|
-
|
|
32
|
-
def iter_bliss_scan_data_from_memory(*_, **kw):
|
|
33
|
-
raise _EXC
|
|
34
|
-
|
|
35
|
-
def last_lima_image(*_, **kw):
|
|
36
|
-
raise _EXC
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
from numbers import Number
|
|
2
|
-
from collections import Counter
|
|
3
|
-
from typing import List, Optional
|
|
4
|
-
|
|
5
|
-
from numpy.typing import ArrayLike
|
|
6
|
-
|
|
7
|
-
from .exceptions import VersionError
|
|
8
|
-
|
|
9
|
-
try:
|
|
10
|
-
from blissdata.beacon.data import BeaconData
|
|
11
|
-
from blissdata.redis_engine.store import DataStore
|
|
12
|
-
from blissdata.redis_engine.scan import Scan
|
|
13
|
-
from blissdata.redis_engine.scan import ScanState
|
|
14
|
-
from blissdata.redis_engine.stream import StreamingClient
|
|
15
|
-
from blissdata.redis_engine.exceptions import EndOfStream
|
|
16
|
-
from blissdata.redis_engine.exceptions import IndexNoMoreThereError
|
|
17
|
-
from blissdata.lima.client import lima_client_factory
|
|
18
|
-
except ImportError as e:
|
|
19
|
-
raise VersionError(str(e)) from e
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def iter_bliss_scan_data_from_memory(
|
|
23
|
-
db_name: str,
|
|
24
|
-
lima_names: List[str],
|
|
25
|
-
counter_names: List[str],
|
|
26
|
-
retry_timeout: Optional[Number] = None,
|
|
27
|
-
retry_period: Optional[Number] = None,
|
|
28
|
-
):
|
|
29
|
-
data_store = _get_data_store()
|
|
30
|
-
scan = data_store.load_scan(db_name, scan_cls=Scan)
|
|
31
|
-
buffers = {name: list() for name in lima_names + counter_names}
|
|
32
|
-
|
|
33
|
-
while scan.state < ScanState.PREPARED:
|
|
34
|
-
scan.update()
|
|
35
|
-
|
|
36
|
-
lima_streams = dict()
|
|
37
|
-
lima_clients = dict()
|
|
38
|
-
counter_streams = dict()
|
|
39
|
-
for name, stream in scan.streams.items():
|
|
40
|
-
if stream.encoding["type"] == "json" and "lima" in stream.info["format"]:
|
|
41
|
-
if name.split(":")[-2] in lima_names:
|
|
42
|
-
lima_streams[name] = stream
|
|
43
|
-
lima_clients[name] = lima_client_factory(data_store, stream.info)
|
|
44
|
-
elif name.split(":")[-1] in counter_names:
|
|
45
|
-
counter_streams[name] = stream
|
|
46
|
-
|
|
47
|
-
client = StreamingClient({**lima_streams, **counter_streams})
|
|
48
|
-
lima_buffer_count = Counter()
|
|
49
|
-
|
|
50
|
-
while True:
|
|
51
|
-
try:
|
|
52
|
-
output = client.read()
|
|
53
|
-
except EndOfStream:
|
|
54
|
-
break
|
|
55
|
-
for stream, (_, payload) in output.items():
|
|
56
|
-
name_parts = stream.name.split(":")
|
|
57
|
-
if stream.name in lima_streams:
|
|
58
|
-
# payload is a sequence of JSON statuses
|
|
59
|
-
ctr_name = name_parts[-2]
|
|
60
|
-
last_status = payload[-1]
|
|
61
|
-
lima_client = lima_clients[stream.name]
|
|
62
|
-
lima_client.update(**last_status)
|
|
63
|
-
n_already_read = lima_buffer_count[ctr_name]
|
|
64
|
-
try:
|
|
65
|
-
data = lima_client[n_already_read:]
|
|
66
|
-
except IndexNoMoreThereError:
|
|
67
|
-
continue
|
|
68
|
-
buffers[ctr_name].extend(data)
|
|
69
|
-
lima_buffer_count[ctr_name] += len(data)
|
|
70
|
-
else:
|
|
71
|
-
# payload is a sequence of data points (0D, 1D, 2D)
|
|
72
|
-
ctr_name = name_parts[-1]
|
|
73
|
-
buffers[ctr_name].extend(payload)
|
|
74
|
-
|
|
75
|
-
nyield = min(len(v) for v in buffers.values())
|
|
76
|
-
if nyield:
|
|
77
|
-
for i in range(nyield):
|
|
78
|
-
yield {name: values[i] for name, values in buffers.items()}
|
|
79
|
-
buffers = {name: values[nyield:] for name, values in buffers.items()}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def last_lima_image(channel_info: dict) -> ArrayLike:
|
|
83
|
-
"""Get last lima image from memory"""
|
|
84
|
-
data_store = _get_data_store()
|
|
85
|
-
lima_client = lima_client_factory(data_store, channel_info)
|
|
86
|
-
return lima_client.get_last_live_image().array
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def _get_data_store() -> None:
|
|
90
|
-
redis_url = BeaconData().get_redis_data_db()
|
|
91
|
-
return DataStore(redis_url)
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
from numbers import Number
|
|
2
|
-
from collections import Counter
|
|
3
|
-
from typing import List, Optional
|
|
4
|
-
|
|
5
|
-
from numpy.typing import ArrayLike
|
|
6
|
-
|
|
7
|
-
from .exceptions import VersionError
|
|
8
|
-
|
|
9
|
-
try:
|
|
10
|
-
from blissdata.redis_engine.scan import Scan
|
|
11
|
-
from blissdata.redis_engine.stream import StreamingClient
|
|
12
|
-
from blissdata.redis_engine.models import ScanState
|
|
13
|
-
from blissdata.redis_engine.exceptions import EndOfStream
|
|
14
|
-
from blissdata.lima.client import lima_client_factory
|
|
15
|
-
from blissdata.beacon.data import BeaconData
|
|
16
|
-
from blissdata import redis_engine
|
|
17
|
-
except ImportError as e:
|
|
18
|
-
raise VersionError(str(e)) from e
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def iter_bliss_scan_data_from_memory(
|
|
22
|
-
db_name: str,
|
|
23
|
-
lima_names: List[str],
|
|
24
|
-
counter_names: List[str],
|
|
25
|
-
retry_timeout: Optional[Number] = None,
|
|
26
|
-
retry_period: Optional[Number] = None,
|
|
27
|
-
):
|
|
28
|
-
_ensure_redis()
|
|
29
|
-
scan = Scan.load(db_name)
|
|
30
|
-
buffers = {name: list() for name in lima_names + counter_names}
|
|
31
|
-
|
|
32
|
-
while scan.state < ScanState.PREPARED:
|
|
33
|
-
scan.update()
|
|
34
|
-
if scan.state is ScanState.ABORTED:
|
|
35
|
-
return
|
|
36
|
-
|
|
37
|
-
lima_streams = dict()
|
|
38
|
-
lima_clients = dict()
|
|
39
|
-
lima_buffer_count = dict()
|
|
40
|
-
counter_streams = dict()
|
|
41
|
-
for name, stream in scan.streams.items():
|
|
42
|
-
if stream.encoding["type"] == "json" and "lima" in stream.info["format"]:
|
|
43
|
-
if name.split(":")[-2] in lima_names:
|
|
44
|
-
lima_streams[name] = stream
|
|
45
|
-
lima_clients[name] = lima_client_factory(stream.info)
|
|
46
|
-
lima_buffer_count[name] = 0
|
|
47
|
-
elif name.split(":")[-1] in counter_names:
|
|
48
|
-
counter_streams[name] = stream
|
|
49
|
-
|
|
50
|
-
client = StreamingClient({**lima_streams, **counter_streams})
|
|
51
|
-
lima_buffer_count = Counter()
|
|
52
|
-
|
|
53
|
-
while True:
|
|
54
|
-
try:
|
|
55
|
-
output = client.read()
|
|
56
|
-
except EndOfStream:
|
|
57
|
-
break
|
|
58
|
-
for stream, (_, payload) in output.items():
|
|
59
|
-
name_parts = stream.name.split(":")
|
|
60
|
-
if stream.name in lima_streams:
|
|
61
|
-
# payload is a sequence of JSON statuses
|
|
62
|
-
ctr_name = name_parts[-2]
|
|
63
|
-
lima_client = lima_clients[stream.name]
|
|
64
|
-
lima_client.update(**payload[-1])
|
|
65
|
-
n_already_read = lima_buffer_count[ctr_name]
|
|
66
|
-
data = lima_client[n_already_read:]
|
|
67
|
-
buffers[ctr_name].extend(data)
|
|
68
|
-
lima_buffer_count[ctr_name] += len(data)
|
|
69
|
-
else:
|
|
70
|
-
# payload is a sequence of data points (0D, 1D, 2D)
|
|
71
|
-
ctr_name = name_parts[-1]
|
|
72
|
-
buffers[ctr_name].extend(payload)
|
|
73
|
-
|
|
74
|
-
nyield = min(len(v) for v in buffers.values())
|
|
75
|
-
if nyield:
|
|
76
|
-
for i in range(nyield):
|
|
77
|
-
yield {name: values[i] for name, values in buffers.items()}
|
|
78
|
-
buffers = {name: values[nyield:] for name, values in buffers.items()}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def last_lima_image(channel_info: dict) -> ArrayLike:
|
|
82
|
-
"""Get last lima image from memory"""
|
|
83
|
-
_ensure_redis()
|
|
84
|
-
lima_client = lima_client_factory(channel_info)
|
|
85
|
-
return lima_client.get_last_live_image().array
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def _ensure_redis() -> None:
|
|
89
|
-
url = BeaconData().get_redis_data_db()
|
|
90
|
-
current_url = _current_redis_url()
|
|
91
|
-
if current_url:
|
|
92
|
-
if url == current_url:
|
|
93
|
-
return
|
|
94
|
-
raise RuntimeError("The Redis URL has changed. Restart the Nexus writer.")
|
|
95
|
-
redis_engine.set_redis_url(url)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def _current_redis_url() -> Optional[str]:
|
|
99
|
-
if redis_engine._redis is None:
|
|
100
|
-
return
|
|
101
|
-
kwargs = redis_engine._redis.connection_pool.connection_kwargs
|
|
102
|
-
if "path" in kwargs:
|
|
103
|
-
return f"unix://{kwargs['path']}"
|
|
104
|
-
else:
|
|
105
|
-
return f"redis://{kwargs['host']}:{kwargs['port']}"
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
from collections.abc import Generator
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class ContextIterator(Generator):
|
|
5
|
-
def __init__(self, it):
|
|
6
|
-
self._it = it
|
|
7
|
-
|
|
8
|
-
def __iter__(self):
|
|
9
|
-
return self
|
|
10
|
-
|
|
11
|
-
def __next__(self):
|
|
12
|
-
return next(self._it)
|
|
13
|
-
|
|
14
|
-
def __enter__(self):
|
|
15
|
-
return self
|
|
16
|
-
|
|
17
|
-
def __exit__(self, *_):
|
|
18
|
-
self._it.close()
|
|
19
|
-
|
|
20
|
-
def send(self):
|
|
21
|
-
return next(self._it)
|
|
22
|
-
|
|
23
|
-
def throw(self):
|
|
24
|
-
raise StopIteration
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def contextiterator(iterator):
|
|
28
|
-
"""Decorator that allows to use an iterator as a context manager.
|
|
29
|
-
This ensures that the iterator is closed when exiting the context manager.
|
|
30
|
-
"""
|
|
31
|
-
|
|
32
|
-
def wrapper(*args, **kw):
|
|
33
|
-
return ContextIterator(iterator(*args, **kw))
|
|
34
|
-
|
|
35
|
-
return wrapper
|
|
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
|