ewoksdata 1.3.0__tar.gz → 2.0.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-1.3.0/src/ewoksdata.egg-info → ewoksdata-2.0.0}/PKG-INFO +2 -1
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/pyproject.toml +18 -4
- ewoksdata-2.0.0/src/ewoksdata/data/bliss.py +286 -0
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/data/blissdata/__init__.py +7 -1
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/data/blissdata/blissdatav0.py +21 -7
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/data/blissdata/blissdatav1.py +28 -15
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/data/blissdata/blissdatav2.py +136 -43
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/data/hdf5/config.py +5 -3
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/data/hdf5/dataset_writer.py +3 -2
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/data/hdf5/types.py +3 -1
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/data/nexus.py +6 -1
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/data/url.py +3 -1
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/data/utils.py +1 -0
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/tests/data/bliss_scans.py +4 -3
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/tests/test_data_bliss.py +9 -2
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/tests/test_dataset_writer.py +1 -2
- {ewoksdata-1.3.0 → ewoksdata-2.0.0/src/ewoksdata.egg-info}/PKG-INFO +2 -1
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata.egg-info/requires.txt +1 -0
- ewoksdata-1.3.0/src/ewoksdata/data/bliss.py +0 -148
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/LICENSE.md +0 -0
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/README.md +0 -0
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/setup.cfg +0 -0
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/__init__.py +0 -0
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/data/__init__.py +0 -0
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/data/contextiterator.py +1 -1
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/data/hdf5/__init__.py +0 -0
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/tests/__init__.py +0 -0
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/tests/conftest.py +0 -0
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/tests/data/__init__.py +0 -0
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/tests/test_contextiterator.py +0 -0
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/tests/test_data_hdf5.py +0 -0
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata/tests/test_data_nexus.py +1 -1
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata.egg-info/SOURCES.txt +0 -0
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata.egg-info/dependency_links.txt +0 -0
- {ewoksdata-1.3.0 → ewoksdata-2.0.0}/src/ewoksdata.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ewoksdata
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: Ewoks tasks for data access
|
|
5
5
|
Author-email: ESRF <dau-pydev@esrf.fr>
|
|
6
6
|
License: # MIT License
|
|
@@ -53,6 +53,7 @@ Provides-Extra: dev
|
|
|
53
53
|
Requires-Dist: ewoksdata[test]; extra == "dev"
|
|
54
54
|
Requires-Dist: black>=25; extra == "dev"
|
|
55
55
|
Requires-Dist: flake8>=4; extra == "dev"
|
|
56
|
+
Requires-Dist: isort; extra == "dev"
|
|
56
57
|
Provides-Extra: doc
|
|
57
58
|
Requires-Dist: ewoksdata[test]; extra == "doc"
|
|
58
59
|
Requires-Dist: sphinx>=4.5; extra == "doc"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ewoksdata"
|
|
7
|
-
version = "
|
|
7
|
+
version = "2.0.0"
|
|
8
8
|
authors = [{ name = "ESRF", email = "dau-pydev@esrf.fr" }]
|
|
9
9
|
description = "Ewoks tasks for data access"
|
|
10
10
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
@@ -35,9 +35,19 @@ Issues = "https://gitlab.esrf.fr/workflow/ewoksapps/ewoksdata/issues"
|
|
|
35
35
|
Changelog = "https://gitlab.esrf.fr/workflow/ewoksapps/ewoksdata/-/blob/main/CHANGELOG.md"
|
|
36
36
|
|
|
37
37
|
[project.optional-dependencies]
|
|
38
|
-
online = [
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
online = [
|
|
39
|
+
"blissdata[tango]"
|
|
40
|
+
]
|
|
41
|
+
test = [
|
|
42
|
+
"ewoksdata[online]",
|
|
43
|
+
"pytest >=7",
|
|
44
|
+
]
|
|
45
|
+
dev = [
|
|
46
|
+
"ewoksdata[test]",
|
|
47
|
+
"black >=25",
|
|
48
|
+
"flake8 >=4",
|
|
49
|
+
"isort",
|
|
50
|
+
]
|
|
41
51
|
doc = [
|
|
42
52
|
"ewoksdata[test]",
|
|
43
53
|
"sphinx >=4.5",
|
|
@@ -53,3 +63,7 @@ where = ["src"]
|
|
|
53
63
|
|
|
54
64
|
[tool.coverage.run]
|
|
55
65
|
omit = ['*/tests/*']
|
|
66
|
+
|
|
67
|
+
[tool.isort]
|
|
68
|
+
profile = "black"
|
|
69
|
+
force_single_line = "True"
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"""Utilities for reading Bliss scan data from HDF5/Nexus files
|
|
2
|
+
or from memory (Redis or device servers).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import sys
|
|
7
|
+
from numbers import Integral
|
|
8
|
+
from numbers import Number
|
|
9
|
+
from typing import Any
|
|
10
|
+
from typing import Dict
|
|
11
|
+
from typing import Iterator
|
|
12
|
+
from typing import List
|
|
13
|
+
from typing import Optional
|
|
14
|
+
from typing import Sequence
|
|
15
|
+
from typing import Tuple
|
|
16
|
+
from typing import Union
|
|
17
|
+
|
|
18
|
+
import h5py
|
|
19
|
+
import hdf5plugin # noqa: F401 - needed for transparent compression handling
|
|
20
|
+
import numpy
|
|
21
|
+
from numpy.typing import ArrayLike
|
|
22
|
+
from silx.io import h5py_utils
|
|
23
|
+
from silx.io.url import DataUrl
|
|
24
|
+
from silx.io.utils import get_data as silx_get_data
|
|
25
|
+
from silx.utils import retry as retrymod
|
|
26
|
+
|
|
27
|
+
from . import hdf5
|
|
28
|
+
from . import nexus
|
|
29
|
+
from . import url
|
|
30
|
+
from .blissdata import BUFFER_IN_MEMORY_DEFAULT
|
|
31
|
+
from .blissdata import dynamic_hdf5
|
|
32
|
+
from .blissdata import iter_bliss_scan_data_from_memory
|
|
33
|
+
from .blissdata import iter_bliss_scan_data_from_memory_slice # noqa: F401
|
|
34
|
+
from .blissdata import last_lima_image # noqa: F401
|
|
35
|
+
from .contextiterator import contextiterator
|
|
36
|
+
|
|
37
|
+
logger = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_data(
|
|
41
|
+
data: Union[str, DataUrl, ArrayLike, Number],
|
|
42
|
+
**hdf5_options: Any,
|
|
43
|
+
) -> Union[numpy.ndarray, Number, Sequence]:
|
|
44
|
+
"""
|
|
45
|
+
Return a NumPy array or a scalar from *data*.
|
|
46
|
+
|
|
47
|
+
:param data: Either a data-URL, an array-like object or a scalar number.
|
|
48
|
+
:param **hdf5_options: Additional options forwarded to :class:`silx.io.h5py_utils.File`.
|
|
49
|
+
:return: The requested data.
|
|
50
|
+
"""
|
|
51
|
+
if isinstance(data, (str, DataUrl)):
|
|
52
|
+
data_url = url.as_dataurl(data)
|
|
53
|
+
filename, h5path, idx = url.h5dataset_url_parse(data_url)
|
|
54
|
+
|
|
55
|
+
if h5py.is_hdf5(filename):
|
|
56
|
+
return _get_hdf5_data(filename, h5path, idx=idx, **hdf5_options)
|
|
57
|
+
|
|
58
|
+
if not data_url.scheme():
|
|
59
|
+
for scheme in ("fabio", "silx"):
|
|
60
|
+
if sys.platform == "win32":
|
|
61
|
+
new_data_url = url.as_dataurl(f"{scheme}:///{data}")
|
|
62
|
+
else:
|
|
63
|
+
new_data_url = url.as_dataurl(f"{scheme}://{data}")
|
|
64
|
+
if new_data_url.is_valid():
|
|
65
|
+
data_url = new_data_url
|
|
66
|
+
break
|
|
67
|
+
|
|
68
|
+
return silx_get_data(data_url)
|
|
69
|
+
|
|
70
|
+
if isinstance(data, (Sequence, Number, numpy.ndarray)):
|
|
71
|
+
return data
|
|
72
|
+
|
|
73
|
+
raise TypeError(f"Unsupported data type: {type(data)!r}")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_image(
|
|
77
|
+
data: Union[str, DataUrl, ArrayLike, Number], **hdf5_options: Any
|
|
78
|
+
) -> numpy.ndarray:
|
|
79
|
+
"""
|
|
80
|
+
Return a NumPy array or a scalar from *data*.
|
|
81
|
+
|
|
82
|
+
:param data: Either a data-URL, an array-like object or a scalar number.
|
|
83
|
+
:param retry_timeout: Timeout for the retry mechanism.
|
|
84
|
+
:param retry_period: Interval between retries.
|
|
85
|
+
:param **hdf5_options: Additional options forwarded to :class:`silx.io.h5py_utils.File`.
|
|
86
|
+
:return: A 2-D image.
|
|
87
|
+
"""
|
|
88
|
+
data = get_data(data, **hdf5_options)
|
|
89
|
+
return numpy.atleast_2d(numpy.squeeze(data))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@h5py_utils.retry()
|
|
93
|
+
def _get_hdf5_data(
|
|
94
|
+
filename: str,
|
|
95
|
+
h5path: str,
|
|
96
|
+
idx: Optional[Tuple[int, ...]] = None,
|
|
97
|
+
**hdf5_options: Any,
|
|
98
|
+
) -> numpy.ndarray:
|
|
99
|
+
"""
|
|
100
|
+
Read a slice of a dataset inside an HDF5 / Nexus file.
|
|
101
|
+
|
|
102
|
+
:param filename: Path to the HDF5 / Nexus file.
|
|
103
|
+
:param h5path: Internal path to the dataset (e.g. ``/entry/data``).
|
|
104
|
+
:param idx: Optional index or slice tuple used to read a subset of the dataset.
|
|
105
|
+
:param **hdf5_options: Additional options forwarded to :class:`silx.io.h5py_utils.File`.
|
|
106
|
+
:return: The requested slice.
|
|
107
|
+
"""
|
|
108
|
+
with hdf5.h5context(filename, h5path, **hdf5_options) as dset:
|
|
109
|
+
if _is_bliss_file(dset):
|
|
110
|
+
# ``end_time`` is added by the Bliss writer when the scan is
|
|
111
|
+
# finished. If it is missing we wait for the file to be
|
|
112
|
+
# completed.
|
|
113
|
+
if "end_time" not in nexus.get_nxentry(dset):
|
|
114
|
+
raise retrymod.RetryError("Bliss file not yet complete")
|
|
115
|
+
if idx is None:
|
|
116
|
+
idx = tuple()
|
|
117
|
+
return dset[idx]
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@contextiterator
|
|
121
|
+
def iter_bliss_scan_data_from_hdf5(
|
|
122
|
+
filename: str,
|
|
123
|
+
scan_nr: Integral,
|
|
124
|
+
subscan: Optional[Integral] = None,
|
|
125
|
+
lima_names: Optional[List[str]] = None,
|
|
126
|
+
counter_names: Optional[List[str]] = None,
|
|
127
|
+
retry_timeout: Optional[float] = None,
|
|
128
|
+
retry_period: Optional[float] = None,
|
|
129
|
+
**hdf5_options: Any,
|
|
130
|
+
) -> Iterator[Dict[str, Any]]:
|
|
131
|
+
"""
|
|
132
|
+
Iterate over the data of a single Bliss scan stored in an HDF5 file.
|
|
133
|
+
|
|
134
|
+
:param filename: Bliss dataset filename (HDF5 / Nexus).
|
|
135
|
+
:param scan_nr: Scan number inside the file.
|
|
136
|
+
:param lima_names: List of LIMA detector names.
|
|
137
|
+
:param counter_names: List of non-LIMA counter names.
|
|
138
|
+
:param subscan: Sub-scan number (e.g. ``10.2`` -> ``scan_nr=10`` and ``subscan=2``).
|
|
139
|
+
:param retry_timeout: Timeout for the retry mechanism.
|
|
140
|
+
:param retry_period: Interval between retries.
|
|
141
|
+
:param **hdf5_options: Additional options forwarded to :class:`blissdata.h5api.dynamic_hdf5.File`.
|
|
142
|
+
:yields: Mapping ``detector_name -> value`` for the each scan point.
|
|
143
|
+
"""
|
|
144
|
+
if not subscan:
|
|
145
|
+
subscan = 1
|
|
146
|
+
lima_names = lima_names or []
|
|
147
|
+
counter_names = counter_names or []
|
|
148
|
+
|
|
149
|
+
with dynamic_hdf5.File(
|
|
150
|
+
filename,
|
|
151
|
+
lima_names=lima_names,
|
|
152
|
+
retry_timeout=retry_timeout,
|
|
153
|
+
retry_period=retry_period,
|
|
154
|
+
**hdf5_options,
|
|
155
|
+
) as root:
|
|
156
|
+
scan = root[f"{scan_nr}.{subscan}"]
|
|
157
|
+
# assert _is_bliss_file(scan), "Not a Bliss dataset file"
|
|
158
|
+
measurement = scan["measurement"]
|
|
159
|
+
instrument = scan["instrument"]
|
|
160
|
+
|
|
161
|
+
datasets: Dict[str, Any] = {name: measurement[name] for name in counter_names}
|
|
162
|
+
for name in lima_names:
|
|
163
|
+
datasets[name] = instrument[f"{name}/data"]
|
|
164
|
+
|
|
165
|
+
names = list(datasets.keys())
|
|
166
|
+
for values in zip(*datasets.values()):
|
|
167
|
+
yield dict(zip(names, values))
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@contextiterator
|
|
171
|
+
def iter_bliss_data(
|
|
172
|
+
filename: str,
|
|
173
|
+
scan_nr: Integral,
|
|
174
|
+
subscan: Optional[Integral] = None,
|
|
175
|
+
scan_key: Optional[str] = None,
|
|
176
|
+
lima_names: Optional[List[str]] = None,
|
|
177
|
+
counter_names: Optional[List[str]] = None,
|
|
178
|
+
start_index: Optional[int] = None,
|
|
179
|
+
retry_timeout: Optional[float] = None,
|
|
180
|
+
retry_period: Optional[float] = None,
|
|
181
|
+
buffer_in_memory: bool = BUFFER_IN_MEMORY_DEFAULT,
|
|
182
|
+
**options: Any,
|
|
183
|
+
) -> Iterator[Tuple[int, Dict[str, Any]]]:
|
|
184
|
+
"""
|
|
185
|
+
Iterate over a Bliss scan and also return the scan-point index.
|
|
186
|
+
|
|
187
|
+
:param filename: Bliss dataset filename.
|
|
188
|
+
:param scan_nr: Scan number inside the dataset.
|
|
189
|
+
:param subscan: Sub-scan number (e.g. ``10.2`` -> ``scan_nr=10`` and ``subscan=2``).
|
|
190
|
+
:param scan_key: Identifier of a scan for blissdata. Iterate scan data from blissdata-cache.
|
|
191
|
+
:param lima_names: List of LIMA detector names.
|
|
192
|
+
:param counter_names: List of non-LIMA counter names.
|
|
193
|
+
:param start_index: First point to yield (default ``0`` -> all points).
|
|
194
|
+
:param retry_timeout: Timeout for the retry mechanism.
|
|
195
|
+
:param retry_period: Interval between retries.
|
|
196
|
+
:param buffer_in_memory: Blissdata buffering in memory. Only applies when `scan_key` is provided.
|
|
197
|
+
Use only when you have enough memory or the data processing can keep up with the scan.
|
|
198
|
+
:param **hdf5_options: Additional options forwarded to :class:`blissdata.h5api.dynamic_hdf5.File`.
|
|
199
|
+
:yields: ``(point_index, data_dict)`` where ``point_index`` is the zero-based
|
|
200
|
+
scan-point number and ``data_dict`` maps detector names to values.
|
|
201
|
+
"""
|
|
202
|
+
start_index = 0 if start_index is None else start_index
|
|
203
|
+
|
|
204
|
+
for idx, data in enumerate(
|
|
205
|
+
iter_bliss_scan_data(
|
|
206
|
+
filename,
|
|
207
|
+
scan_nr,
|
|
208
|
+
subscan=subscan,
|
|
209
|
+
scan_key=scan_key,
|
|
210
|
+
lima_names=lima_names,
|
|
211
|
+
counter_names=counter_names,
|
|
212
|
+
retry_timeout=retry_timeout,
|
|
213
|
+
retry_period=retry_period,
|
|
214
|
+
buffer_in_memory=buffer_in_memory,
|
|
215
|
+
**options,
|
|
216
|
+
)
|
|
217
|
+
):
|
|
218
|
+
if idx >= start_index:
|
|
219
|
+
yield idx, data
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
@contextiterator
|
|
223
|
+
def iter_bliss_scan_data(
|
|
224
|
+
filename: str,
|
|
225
|
+
scan_nr: Integral,
|
|
226
|
+
subscan: Optional[Integral] = None,
|
|
227
|
+
scan_key: Optional[str] = None,
|
|
228
|
+
lima_names: Optional[List[str]] = None,
|
|
229
|
+
counter_names: Optional[List[str]] = None,
|
|
230
|
+
retry_timeout: Optional[float] = None,
|
|
231
|
+
retry_period: Optional[float] = None,
|
|
232
|
+
buffer_in_memory: bool = BUFFER_IN_MEMORY_DEFAULT,
|
|
233
|
+
**hdf5_options: Any,
|
|
234
|
+
) -> Iterator[Dict[str, Any]]:
|
|
235
|
+
"""
|
|
236
|
+
Yield the data of a Bliss scan either from an HDF5 file or from memory (Redis or device servers).
|
|
237
|
+
|
|
238
|
+
:param filename: Path to the HDF5 / Nexus file (ignored when *scan_key* is supplied).
|
|
239
|
+
:param scan_nr: Scan number inside the file.
|
|
240
|
+
:param subscan: Sub-scan number (e.g. ``10.2`` -> ``scan_nr=10`` and ``subscan=2``).
|
|
241
|
+
:param scan_key: Identifier of a scan for blissdata. Iterate scan data from blissdata-cache.
|
|
242
|
+
:param lima_names: List of LIMA detector names.
|
|
243
|
+
:param counter_names: List of non-LIMA counter names.
|
|
244
|
+
:param retry_timeout: Timeout for the retry mechanism.
|
|
245
|
+
:param retry_period: Interval between retries.
|
|
246
|
+
:param buffer_in_memory: Blissdata buffering in memory. Only applies when `scan_key` is provided.
|
|
247
|
+
Use only when you have enough memory or the data processing can keep up with the scan.
|
|
248
|
+
:param **hdf5_options: Additional options forwarded to :class:`blissdata.h5api.dynamic_hdf5.File`.
|
|
249
|
+
:yields: Mapping ``detector_name -> value`` for each scan point.
|
|
250
|
+
"""
|
|
251
|
+
if scan_key:
|
|
252
|
+
yield from iter_bliss_scan_data_from_memory(
|
|
253
|
+
scan_key,
|
|
254
|
+
lima_names=lima_names,
|
|
255
|
+
counter_names=counter_names,
|
|
256
|
+
retry_timeout=retry_timeout,
|
|
257
|
+
retry_period=retry_period,
|
|
258
|
+
buffer_in_memory=buffer_in_memory,
|
|
259
|
+
)
|
|
260
|
+
else:
|
|
261
|
+
yield from iter_bliss_scan_data_from_hdf5(
|
|
262
|
+
filename,
|
|
263
|
+
scan_nr,
|
|
264
|
+
lima_names=lima_names,
|
|
265
|
+
counter_names=counter_names,
|
|
266
|
+
subscan=subscan,
|
|
267
|
+
retry_timeout=retry_timeout,
|
|
268
|
+
retry_period=retry_period,
|
|
269
|
+
**hdf5_options,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _is_bliss_file(h5item: Union[h5py.Dataset, h5py.Group]) -> bool:
|
|
274
|
+
"""
|
|
275
|
+
Return ``True`` if *h5item* belongs to a Bliss-generated file.
|
|
276
|
+
|
|
277
|
+
:param h5item: HDF5 dataset or group.
|
|
278
|
+
:return: ``True`` when the file attributes indicate a Bliss creator/publisher.
|
|
279
|
+
"""
|
|
280
|
+
attrs = h5item.file.attrs
|
|
281
|
+
creator = attrs.get("creator", "").lower()
|
|
282
|
+
publisher = attrs.get("publisher", "").lower()
|
|
283
|
+
return creator in _BLISS_PUBLISHERS or publisher in _BLISS_PUBLISHERS
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
_BLISS_PUBLISHERS = ("bliss", "blissdata")
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
from packaging.version import Version
|
|
2
1
|
from importlib.metadata import version
|
|
3
2
|
|
|
3
|
+
from packaging.version import Version
|
|
4
|
+
|
|
4
5
|
|
|
5
6
|
def _not_supported_by_blissdata_version(*_, **kw):
|
|
6
7
|
raise RuntimeError(f"Not supported by blissdata {_BLISSDATA_VERSION}")
|
|
@@ -14,12 +15,17 @@ if _BLISSDATA_VERSION >= Version("2.0.0rc1"):
|
|
|
14
15
|
from .blissdatav2 import iter_bliss_scan_data_from_memory # noqa F401
|
|
15
16
|
from .blissdatav2 import iter_bliss_scan_data_from_memory_slice # noqa F401
|
|
16
17
|
from .blissdatav2 import last_lima_image # noqa F401
|
|
18
|
+
|
|
19
|
+
BUFFER_IN_MEMORY_DEFAULT = True
|
|
17
20
|
elif _BLISSDATA_VERSION >= Version("1"):
|
|
18
21
|
from .blissdatav1 import iter_bliss_scan_data_from_memory # noqa F401
|
|
19
22
|
from .blissdatav1 import iter_bliss_scan_data_from_memory_slice # noqa F401
|
|
20
23
|
from .blissdatav1 import last_lima_image # noqa F401
|
|
24
|
+
|
|
25
|
+
BUFFER_IN_MEMORY_DEFAULT = True
|
|
21
26
|
else:
|
|
22
27
|
from .blissdatav0 import iter_bliss_scan_data_from_memory # noqa F401
|
|
23
28
|
from .blissdatav0 import last_lima_image # noqa F401
|
|
24
29
|
|
|
25
30
|
iter_bliss_scan_data_from_memory_slice = _not_supported_by_blissdata_version
|
|
31
|
+
BUFFER_IN_MEMORY_DEFAULT = False
|
|
@@ -1,28 +1,42 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from numbers import Number
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Any
|
|
4
|
+
from typing import Dict
|
|
5
|
+
from typing import Generator
|
|
6
|
+
from typing import List
|
|
7
|
+
from typing import Optional
|
|
4
8
|
|
|
9
|
+
from blissdata.data.events.lima import ImageNotSaved
|
|
10
|
+
from blissdata.data.node import get_node
|
|
5
11
|
from numpy.typing import ArrayLike
|
|
6
12
|
from silx.utils import retry as retrymod
|
|
7
|
-
from blissdata.data.node import get_node
|
|
8
|
-
from blissdata.data.events.lima import ImageNotSaved
|
|
9
|
-
|
|
10
13
|
|
|
11
14
|
logger = logging.getLogger(__name__)
|
|
12
15
|
|
|
13
16
|
|
|
14
17
|
def iter_bliss_scan_data_from_memory(
|
|
15
18
|
db_name: str,
|
|
16
|
-
lima_names: List[str],
|
|
17
|
-
counter_names: List[str],
|
|
19
|
+
lima_names: Optional[List[str]] = None,
|
|
20
|
+
counter_names: Optional[List[str]] = None,
|
|
18
21
|
retry_timeout: Optional[Number] = None,
|
|
19
22
|
retry_period: Optional[Number] = None,
|
|
20
|
-
|
|
23
|
+
buffer_in_memory: bool = True,
|
|
24
|
+
) -> Generator[Dict[str, Any], None, None]:
|
|
25
|
+
if not buffer_in_memory:
|
|
26
|
+
raise NotImplementedError("Requires blissdata>=2.0.0rc1")
|
|
27
|
+
if lima_names is None:
|
|
28
|
+
lima_names = list()
|
|
29
|
+
if counter_names is None:
|
|
30
|
+
counter_names = list()
|
|
31
|
+
|
|
21
32
|
scan_node = _get_node(
|
|
22
33
|
db_name, "scan", retry_timeout=retry_timeout, retry_period=retry_period
|
|
23
34
|
)
|
|
24
35
|
indices = {name: 0 for name in lima_names + counter_names}
|
|
25
36
|
buffers = {name: list() for name in lima_names + counter_names}
|
|
37
|
+
if not buffers:
|
|
38
|
+
return
|
|
39
|
+
|
|
26
40
|
lima_acq_nb = dict()
|
|
27
41
|
for event_type, node, event_data in scan_node.walk_events():
|
|
28
42
|
if node.type == "lima":
|
|
@@ -1,24 +1,27 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import sys
|
|
2
3
|
import time
|
|
3
|
-
import logging
|
|
4
|
-
from numbers import Number
|
|
5
4
|
from collections import Counter
|
|
6
|
-
from
|
|
5
|
+
from numbers import Number
|
|
6
|
+
from typing import Any
|
|
7
|
+
from typing import Dict
|
|
8
|
+
from typing import Generator
|
|
9
|
+
from typing import List
|
|
10
|
+
from typing import Optional
|
|
11
|
+
from typing import Tuple
|
|
7
12
|
|
|
8
13
|
import numpy
|
|
9
|
-
from numpy.typing import ArrayLike
|
|
10
14
|
from blissdata.beacon.data import BeaconData
|
|
11
|
-
from blissdata.
|
|
15
|
+
from blissdata.lima.client import lima_client_factory
|
|
16
|
+
from blissdata.redis_engine.exceptions import EndOfStream
|
|
17
|
+
from blissdata.redis_engine.exceptions import IndexNoMoreThereError
|
|
18
|
+
from blissdata.redis_engine.exceptions import IndexNotYetThereError
|
|
19
|
+
from blissdata.redis_engine.exceptions import IndexWontBeThereError
|
|
12
20
|
from blissdata.redis_engine.scan import Scan
|
|
13
21
|
from blissdata.redis_engine.scan import ScanState
|
|
22
|
+
from blissdata.redis_engine.store import DataStore
|
|
14
23
|
from blissdata.redis_engine.stream import StreamingClient
|
|
15
|
-
from
|
|
16
|
-
EndOfStream,
|
|
17
|
-
IndexNoMoreThereError,
|
|
18
|
-
IndexNotYetThereError,
|
|
19
|
-
IndexWontBeThereError,
|
|
20
|
-
)
|
|
21
|
-
from blissdata.lima.client import lima_client_factory
|
|
24
|
+
from numpy.typing import ArrayLike
|
|
22
25
|
|
|
23
26
|
try:
|
|
24
27
|
from blissdata.stream import LimaStream
|
|
@@ -38,14 +41,24 @@ def _get_data_store() -> None:
|
|
|
38
41
|
|
|
39
42
|
def iter_bliss_scan_data_from_memory(
|
|
40
43
|
scan_key: str,
|
|
41
|
-
lima_names: List[str],
|
|
42
|
-
counter_names: List[str],
|
|
44
|
+
lima_names: Optional[List[str]] = None,
|
|
45
|
+
counter_names: Optional[List[str]] = None,
|
|
43
46
|
retry_timeout: Optional[Number] = None,
|
|
44
47
|
retry_period: Optional[Number] = None,
|
|
45
|
-
|
|
48
|
+
buffer_in_memory: bool = True,
|
|
49
|
+
) -> Generator[Dict[str, Any], None, None]:
|
|
50
|
+
if not buffer_in_memory:
|
|
51
|
+
raise NotImplementedError("Requires blissdata>=2.0.0rc1")
|
|
52
|
+
if lima_names is None:
|
|
53
|
+
lima_names = list()
|
|
54
|
+
if counter_names is None:
|
|
55
|
+
counter_names = list()
|
|
56
|
+
|
|
46
57
|
data_store = _get_data_store()
|
|
47
58
|
scan = data_store.load_scan(scan_key, scan_cls=Scan)
|
|
48
59
|
buffers = {name: list() for name in lima_names + counter_names}
|
|
60
|
+
if not buffers:
|
|
61
|
+
return
|
|
49
62
|
|
|
50
63
|
while scan.state < ScanState.PREPARED:
|
|
51
64
|
scan.update()
|
|
@@ -1,23 +1,36 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import sys
|
|
2
3
|
import time
|
|
3
|
-
import logging
|
|
4
4
|
from collections import Counter
|
|
5
|
-
from
|
|
5
|
+
from collections.abc import Iterator
|
|
6
|
+
from importlib.metadata import version
|
|
7
|
+
from typing import Any
|
|
8
|
+
from typing import Dict
|
|
9
|
+
from typing import Generator
|
|
10
|
+
from typing import List
|
|
11
|
+
from typing import Optional
|
|
12
|
+
from typing import Tuple
|
|
6
13
|
|
|
7
14
|
import numpy
|
|
8
|
-
from numpy.typing import ArrayLike
|
|
9
|
-
|
|
10
15
|
from blissdata.beacon.data import BeaconData
|
|
11
|
-
from blissdata.redis_engine.store import DataStore
|
|
12
16
|
from blissdata.redis_engine.scan import ScanState
|
|
13
|
-
from blissdata.redis_engine.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
)
|
|
19
|
-
from blissdata.
|
|
20
|
-
|
|
17
|
+
from blissdata.redis_engine.store import DataStore
|
|
18
|
+
from numpy.typing import ArrayLike
|
|
19
|
+
from packaging.version import Version
|
|
20
|
+
|
|
21
|
+
_BLISSDATA_VERSION = Version(version("blissdata"))
|
|
22
|
+
if _BLISSDATA_VERSION >= Version("2.3.0"):
|
|
23
|
+
from blissdata.exceptions import EndOfStream
|
|
24
|
+
from blissdata.exceptions import IndexNoMoreThereError
|
|
25
|
+
from blissdata.exceptions import IndexNotYetThereError
|
|
26
|
+
from blissdata.exceptions import IndexWontBeThereError
|
|
27
|
+
from blissdata.streams import CursorGroup
|
|
28
|
+
else:
|
|
29
|
+
from blissdata.redis_engine.exceptions import EndOfStream
|
|
30
|
+
from blissdata.redis_engine.exceptions import IndexNoMoreThereError
|
|
31
|
+
from blissdata.redis_engine.exceptions import IndexNotYetThereError
|
|
32
|
+
from blissdata.redis_engine.exceptions import IndexWontBeThereError
|
|
33
|
+
from blissdata.streams.base import CursorGroup
|
|
21
34
|
|
|
22
35
|
logger = logging.getLogger(__name__)
|
|
23
36
|
|
|
@@ -31,51 +44,131 @@ def _get_data_store() -> None:
|
|
|
31
44
|
|
|
32
45
|
def iter_bliss_scan_data_from_memory(
|
|
33
46
|
scan_key: str,
|
|
34
|
-
lima_names: List[str],
|
|
35
|
-
counter_names: List[str],
|
|
47
|
+
lima_names: Optional[List[str]] = None,
|
|
48
|
+
counter_names: Optional[List[str]] = None,
|
|
36
49
|
retry_timeout: Optional[float] = None,
|
|
37
50
|
retry_period: Optional[float] = None,
|
|
38
|
-
|
|
51
|
+
buffer_in_memory: bool = False,
|
|
52
|
+
) -> Generator[Dict[str, Any], None, None]:
|
|
53
|
+
if lima_names is None:
|
|
54
|
+
lima_names = list()
|
|
55
|
+
if counter_names is None:
|
|
56
|
+
counter_names = list()
|
|
57
|
+
|
|
39
58
|
streams = _get_streams(scan_key, lima_names, counter_names)
|
|
40
59
|
if not streams:
|
|
41
60
|
return
|
|
42
61
|
|
|
43
62
|
cursor = CursorGroup(list(streams.values()))
|
|
44
63
|
cursor_timeout = retry_period or 0
|
|
45
|
-
|
|
46
|
-
|
|
64
|
+
ctr_names = lima_names + counter_names
|
|
65
|
+
data_buffers = {ctr_name: _DataBuffer() for ctr_name in ctr_names}
|
|
66
|
+
stream_to_ctr_names = {
|
|
67
|
+
stream.name: ctr_name for ctr_name, stream in streams.items()
|
|
68
|
+
}
|
|
47
69
|
|
|
48
70
|
while True:
|
|
49
71
|
try:
|
|
50
72
|
views = cursor.read(timeout=cursor_timeout)
|
|
51
73
|
except EndOfStream:
|
|
74
|
+
# No more data - stop the generator
|
|
75
|
+
progress = [
|
|
76
|
+
f"{ctr_name}: {data_buffer.progress}"
|
|
77
|
+
for ctr_name, data_buffer in data_buffers.items()
|
|
78
|
+
]
|
|
79
|
+
logger.info("Data progress on exit:\n %s", "\n".join(progress))
|
|
80
|
+
for ctr_name, data_buffer in data_buffers.items():
|
|
81
|
+
if data_buffer.length_left:
|
|
82
|
+
logger.warning(
|
|
83
|
+
"Data points for counter %r: %s",
|
|
84
|
+
ctr_name,
|
|
85
|
+
data_buffer.progress,
|
|
86
|
+
)
|
|
52
87
|
break
|
|
53
88
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
89
|
+
for stream, view in views.items():
|
|
90
|
+
ctr_name = stream_to_ctr_names[stream.name]
|
|
91
|
+
data_buffers[ctr_name].append_view(view)
|
|
92
|
+
|
|
93
|
+
if buffer_in_memory:
|
|
94
|
+
for data_buffer in data_buffers.values():
|
|
95
|
+
data_buffer.buffer_in_memory()
|
|
96
|
+
|
|
97
|
+
while True:
|
|
98
|
+
try:
|
|
99
|
+
ctr_values = [
|
|
100
|
+
next(data_buffer) for data_buffer in data_buffers.values()
|
|
101
|
+
]
|
|
102
|
+
except StopIteration:
|
|
103
|
+
# One of the buffers ran out of data
|
|
104
|
+
break
|
|
105
|
+
|
|
106
|
+
yield dict(zip(ctr_names, ctr_values))
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class _DataBuffer(Iterator):
|
|
110
|
+
"""Data iterator with view and data buffering for a single counter."""
|
|
111
|
+
|
|
112
|
+
def __init__(self):
|
|
113
|
+
self._views = []
|
|
114
|
+
self._view_lengths = []
|
|
115
|
+
self._first_view_index = 0
|
|
116
|
+
self._buffer = []
|
|
117
|
+
self._total_length = 0
|
|
118
|
+
self._total_yielded = 0
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def length_left(self) -> int:
|
|
122
|
+
return self._total_length - self._total_yielded
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def progress(self) -> str:
|
|
126
|
+
return f"{self._total_yielded}/{self._total_length}"
|
|
127
|
+
|
|
128
|
+
def buffer_in_memory(self) -> None:
|
|
129
|
+
"""Buffer and clear all view objects."""
|
|
130
|
+
if not self._views:
|
|
131
|
+
return
|
|
132
|
+
starts = [0] * len(self._views)
|
|
133
|
+
starts[0] = self._first_view_index
|
|
134
|
+
for view, start in zip(self._views, starts):
|
|
135
|
+
self._buffer.extend(view.get_data(start=start, stop=None))
|
|
136
|
+
self._views.clear()
|
|
137
|
+
self._view_lengths.clear()
|
|
138
|
+
self._first_view_index = 0
|
|
139
|
+
|
|
140
|
+
def append_view(self, view) -> None:
|
|
141
|
+
"""Append view object when not empty."""
|
|
142
|
+
view_length = len(view)
|
|
143
|
+
if view_length:
|
|
144
|
+
self._views.append(view)
|
|
145
|
+
self._view_lengths.append(view_length)
|
|
146
|
+
self._total_length += view_length
|
|
147
|
+
|
|
148
|
+
def __iter__(self) -> "_DataBuffer":
|
|
149
|
+
return self
|
|
150
|
+
|
|
151
|
+
def __next__(self):
|
|
152
|
+
"""Return next scan data point for this counter."""
|
|
153
|
+
if self._buffer:
|
|
154
|
+
return self._buffer.pop(0)
|
|
155
|
+
|
|
156
|
+
if len(self._views) == 0:
|
|
157
|
+
raise StopIteration
|
|
158
|
+
|
|
159
|
+
view = self._views[0]
|
|
160
|
+
start = self._first_view_index
|
|
161
|
+
|
|
162
|
+
self._total_yielded += 1
|
|
163
|
+
self._first_view_index += 1
|
|
164
|
+
|
|
165
|
+
if self._first_view_index == self._view_lengths[0]:
|
|
166
|
+
self._views.pop(0)
|
|
167
|
+
self._view_lengths.pop(0)
|
|
168
|
+
self._first_view_index = 0
|
|
169
|
+
|
|
170
|
+
data = view.get_data(start=start, stop=start + 1)
|
|
171
|
+
return data[0]
|
|
79
172
|
|
|
80
173
|
|
|
81
174
|
def last_lima_image(scan_key: str, lima_name: str) -> ArrayLike:
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""HDF5 configuration of optimal data storage (IO speed, compression, ...)"""
|
|
2
2
|
|
|
3
|
-
from typing import Optional
|
|
4
|
-
from numbers import Integral
|
|
5
3
|
from collections.abc import Mapping
|
|
4
|
+
from numbers import Integral
|
|
5
|
+
from typing import Optional
|
|
6
6
|
|
|
7
7
|
import numpy
|
|
8
8
|
from numpy.typing import DTypeLike
|
|
@@ -12,7 +12,9 @@ try:
|
|
|
12
12
|
except ImportError:
|
|
13
13
|
hdf5plugin = None
|
|
14
14
|
|
|
15
|
-
from .types import ShapeType
|
|
15
|
+
from .types import ShapeType
|
|
16
|
+
from .types import VarH5pyShapeType
|
|
17
|
+
from .types import VarShapeType
|
|
16
18
|
|
|
17
19
|
DEFAULT_CHUNK_NBYTES = 1 << 20
|
|
18
20
|
DEFAULT_COMPRESSION_LIMIT_NBYTES = 1 << 20
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import time
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import List
|
|
3
|
+
from typing import Optional
|
|
3
4
|
|
|
4
5
|
import h5py
|
|
5
6
|
import numpy
|
|
6
7
|
from numpy.typing import ArrayLike
|
|
7
8
|
|
|
8
|
-
from .types import StrictPositiveIntegral
|
|
9
9
|
from .config import guess_dataset_config
|
|
10
|
+
from .types import StrictPositiveIntegral
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class _DatasetWriterBase:
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from numbers import Integral
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import NewType
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from typing import Tuple
|
|
3
5
|
|
|
4
6
|
PositiveIntegral = NewType("PositiveIntegral", Integral) # >= 0
|
|
5
7
|
StrictPositiveIntegral = NewType("StrictPositiveIntegral", Integral) # > 0
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from contextlib import contextmanager
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Iterator
|
|
4
|
+
from typing import Optional
|
|
5
|
+
from typing import Sequence
|
|
6
|
+
from typing import Tuple
|
|
7
|
+
from typing import Union
|
|
4
8
|
|
|
5
9
|
import h5py
|
|
6
10
|
from silx.io import h5py_utils
|
|
11
|
+
|
|
7
12
|
from .url import DataUrl
|
|
8
13
|
from .url import as_dataurl
|
|
9
14
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from datetime import datetime
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Optional
|
|
5
|
-
import
|
|
6
|
-
import h5py
|
|
4
|
+
from typing import Optional
|
|
5
|
+
from typing import Tuple
|
|
7
6
|
|
|
7
|
+
import h5py
|
|
8
|
+
import numpy
|
|
8
9
|
|
|
9
10
|
__all__ = ["save_bliss_scan"]
|
|
10
11
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import numpy
|
|
2
1
|
import h5py
|
|
3
|
-
|
|
2
|
+
import numpy
|
|
4
3
|
import pytest
|
|
4
|
+
from fabio.edfimage import edfimage
|
|
5
|
+
|
|
5
6
|
from ..data import bliss
|
|
6
7
|
|
|
7
8
|
|
|
@@ -19,11 +20,17 @@ def test_get_data_hdf5(tmpdir):
|
|
|
19
20
|
img1 = numpy.random.uniform(((10, 12)))
|
|
20
21
|
with h5py.File(filename, "w") as f:
|
|
21
22
|
f["img"] = img1
|
|
23
|
+
|
|
22
24
|
img2 = bliss.get_data(f"{filename}::/img")
|
|
23
25
|
numpy.testing.assert_array_equal(img1, img2)
|
|
26
|
+
|
|
24
27
|
img3 = bliss.get_data(f"{filename}?path=/img")
|
|
25
28
|
numpy.testing.assert_array_equal(img1, img3)
|
|
26
29
|
|
|
30
|
+
filename_non_existing = str(tmpdir / "data_non_existing.h5")
|
|
31
|
+
with pytest.raises(IOError):
|
|
32
|
+
_ = bliss.get_data(f"{filename_non_existing}?path=/img")
|
|
33
|
+
|
|
27
34
|
|
|
28
35
|
@pytest.mark.parametrize("lima_names", [(), ("p3",), ("p3", "p4")])
|
|
29
36
|
@pytest.mark.parametrize("counter_names", [(), ("diode1",), ("diode1", "diode2")])
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ewoksdata
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: Ewoks tasks for data access
|
|
5
5
|
Author-email: ESRF <dau-pydev@esrf.fr>
|
|
6
6
|
License: # MIT License
|
|
@@ -53,6 +53,7 @@ Provides-Extra: dev
|
|
|
53
53
|
Requires-Dist: ewoksdata[test]; extra == "dev"
|
|
54
54
|
Requires-Dist: black>=25; extra == "dev"
|
|
55
55
|
Requires-Dist: flake8>=4; extra == "dev"
|
|
56
|
+
Requires-Dist: isort; extra == "dev"
|
|
56
57
|
Provides-Extra: doc
|
|
57
58
|
Requires-Dist: ewoksdata[test]; extra == "doc"
|
|
58
59
|
Requires-Dist: sphinx>=4.5; extra == "doc"
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
import logging
|
|
3
|
-
from numbers import Integral, Number
|
|
4
|
-
from typing import Iterator, List, Optional, Tuple, Sequence, Union
|
|
5
|
-
|
|
6
|
-
import numpy
|
|
7
|
-
import h5py
|
|
8
|
-
import hdf5plugin # noqa F401
|
|
9
|
-
from numpy.typing import ArrayLike
|
|
10
|
-
from silx.io import h5py_utils
|
|
11
|
-
from silx.utils import retry as retrymod
|
|
12
|
-
from silx.io.utils import get_data as silx_get_data
|
|
13
|
-
|
|
14
|
-
from .blissdata import iter_bliss_scan_data_from_memory # noqa F401
|
|
15
|
-
from .blissdata import iter_bliss_scan_data_from_memory_slice # noqa F401
|
|
16
|
-
from .blissdata import last_lima_image # noqa F401
|
|
17
|
-
from .blissdata import dynamic_hdf5
|
|
18
|
-
|
|
19
|
-
from .contextiterator import contextiterator
|
|
20
|
-
from . import hdf5
|
|
21
|
-
from . import nexus
|
|
22
|
-
from . import url
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
logger = logging.getLogger(__name__)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def get_data(
|
|
29
|
-
data: Union[str, ArrayLike, Number], **options
|
|
30
|
-
) -> Union[numpy.ndarray, Number]:
|
|
31
|
-
if isinstance(data, str):
|
|
32
|
-
data_url = url.as_dataurl(data)
|
|
33
|
-
filename, h5path, idx = url.h5dataset_url_parse(data_url)
|
|
34
|
-
if filename.endswith(".h5") or filename.endswith(".nx"):
|
|
35
|
-
return _get_hdf5_data(filename, h5path, idx=idx, **options)
|
|
36
|
-
if not data_url.scheme():
|
|
37
|
-
if sys.platform == "win32":
|
|
38
|
-
data_url = f"fabio:///{data}"
|
|
39
|
-
else:
|
|
40
|
-
data_url = f"fabio://{data}"
|
|
41
|
-
return silx_get_data(data_url)
|
|
42
|
-
elif isinstance(data, (Sequence, Number, numpy.ndarray)):
|
|
43
|
-
return data
|
|
44
|
-
else:
|
|
45
|
-
raise TypeError(type(data))
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def get_image(*args, **kwargs) -> numpy.ndarray:
|
|
49
|
-
data = get_data(*args, **kwargs)
|
|
50
|
-
return numpy.atleast_2d(numpy.squeeze(data))
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
@h5py_utils.retry()
|
|
54
|
-
def _get_hdf5_data(filename: str, h5path: str, idx=None, **options) -> numpy.ndarray:
|
|
55
|
-
with hdf5.h5context(filename, h5path, **options) as dset:
|
|
56
|
-
if _is_bliss_file(dset):
|
|
57
|
-
if "end_time" not in nexus.get_nxentry(dset):
|
|
58
|
-
raise retrymod.RetryError
|
|
59
|
-
if idx is None:
|
|
60
|
-
idx = tuple()
|
|
61
|
-
return dset[idx]
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
@contextiterator
|
|
65
|
-
def iter_bliss_scan_data(
|
|
66
|
-
filename: str,
|
|
67
|
-
scan_nr: Integral,
|
|
68
|
-
lima_names: Optional[List[str]] = None,
|
|
69
|
-
counter_names: Optional[List[str]] = None,
|
|
70
|
-
subscan: Optional[Integral] = None,
|
|
71
|
-
**options,
|
|
72
|
-
) -> Iterator[dict]:
|
|
73
|
-
"""Iterate over the data from one Bliss scan. The counters are assumed to have
|
|
74
|
-
many data values as scan points.
|
|
75
|
-
|
|
76
|
-
:param str filename: the Bliss dataset filename
|
|
77
|
-
:param Integral filename: the scan number in the dataset
|
|
78
|
-
:param list lima_names: names of lima detectors
|
|
79
|
-
:param list counter_names: names of non-lima detectors (you need to provide at least one)
|
|
80
|
-
:param Integral subscan: subscan number (for example "10.2" has `scan_nr=10` and `subscan=2`)
|
|
81
|
-
:param Number retry_timeout: timeout when it cannot access the data for `retry_timeout` seconds
|
|
82
|
-
:param Number retry_period: interval in seconds between data access retries
|
|
83
|
-
:yields dict: data
|
|
84
|
-
"""
|
|
85
|
-
if not subscan:
|
|
86
|
-
subscan = 1
|
|
87
|
-
if counter_names is None:
|
|
88
|
-
counter_names = list()
|
|
89
|
-
with dynamic_hdf5.File(filename, lima_names=lima_names, **options) as root:
|
|
90
|
-
scan = root[f"{scan_nr}.{subscan}"]
|
|
91
|
-
# assert _is_bliss_file(scan), "Not a Bliss dataset file"
|
|
92
|
-
measurement = scan["measurement"]
|
|
93
|
-
instrument = scan["instrument"]
|
|
94
|
-
datasets = {name: measurement[name] for name in counter_names}
|
|
95
|
-
for name in lima_names:
|
|
96
|
-
datasets[name] = instrument[f"{name}/data"]
|
|
97
|
-
names = list(datasets.keys())
|
|
98
|
-
for values in zip(*datasets.values()):
|
|
99
|
-
yield dict(zip(names, values))
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
@contextiterator
|
|
103
|
-
def iter_bliss_data(
|
|
104
|
-
filename: str,
|
|
105
|
-
scan_nr: Integral,
|
|
106
|
-
lima_names: List[str],
|
|
107
|
-
counter_names: List[str],
|
|
108
|
-
subscan: Optional[Integral] = None,
|
|
109
|
-
start_index: Optional[Integral] = None,
|
|
110
|
-
**options,
|
|
111
|
-
) -> Iterator[Tuple[int, dict]]:
|
|
112
|
-
"""Iterate over the data from one Bliss scan. The counters are assumed to have
|
|
113
|
-
many data values as scan points.
|
|
114
|
-
|
|
115
|
-
:param str filename: the Bliss dataset filename
|
|
116
|
-
:param Integral filename: the scan number in the dataset
|
|
117
|
-
:param list lima_names: names of lima detectors
|
|
118
|
-
:param list counter_names: names of non-lima detectors (you need to provide at least one)
|
|
119
|
-
:param Integral subscan: subscan number (for example "10.2" has `scan_nr=10` and `subscan=2`)
|
|
120
|
-
:param Number retry_timeout: timeout when it cannot access the data for `retry_timeout` seconds
|
|
121
|
-
:param Number retry_period: interval in seconds between data access retries
|
|
122
|
-
:param Integral start_index: start iterating from this scan point index
|
|
123
|
-
:yields tuple: scan index, data
|
|
124
|
-
"""
|
|
125
|
-
if start_index is None:
|
|
126
|
-
start_index = 0
|
|
127
|
-
for index, data in enumerate(
|
|
128
|
-
iter_bliss_scan_data(
|
|
129
|
-
filename,
|
|
130
|
-
scan_nr,
|
|
131
|
-
lima_names=lima_names,
|
|
132
|
-
counter_names=counter_names,
|
|
133
|
-
subscan=subscan,
|
|
134
|
-
**options,
|
|
135
|
-
)
|
|
136
|
-
):
|
|
137
|
-
if index >= start_index:
|
|
138
|
-
yield index, data
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def _is_bliss_file(h5item: Union[h5py.Dataset, h5py.Group]) -> bool:
|
|
142
|
-
return (
|
|
143
|
-
h5item.file.attrs.get("creator", "").lower() in _BLISS_PUBLISHERS
|
|
144
|
-
or h5item.file.attrs.get("publisher", "").lower() in _BLISS_PUBLISHERS
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
_BLISS_PUBLISHERS = ("bliss", "blissdata")
|
|
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
|