pymodaq 5.0.0__py3-none-any.whl → 5.0.2__py3-none-any.whl
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.
Potentially problematic release.
This version of pymodaq might be problematic. Click here for more details.
- pymodaq/__init__.py +55 -89
- pymodaq/control_modules/daq_move.py +129 -55
- pymodaq/control_modules/daq_move_ui.py +42 -11
- pymodaq/control_modules/daq_viewer.py +32 -13
- pymodaq/control_modules/move_utility_classes.py +346 -79
- pymodaq/control_modules/utils.py +26 -9
- pymodaq/control_modules/viewer_utility_classes.py +51 -14
- pymodaq/daq_utils/daq_utils.py +6 -0
- pymodaq/dashboard.py +532 -263
- pymodaq/examples/qt_less_standalone_module.py +128 -0
- pymodaq/extensions/bayesian/bayesian_optimisation.py +30 -21
- pymodaq/extensions/bayesian/utils.py +6 -3
- pymodaq/extensions/daq_logger/__init__.py +1 -0
- pymodaq/extensions/daq_logger/daq_logger.py +4 -5
- pymodaq/extensions/daq_scan.py +28 -8
- pymodaq/extensions/daq_scan_ui.py +7 -9
- pymodaq/extensions/pid/__init__.py +0 -1
- pymodaq/extensions/pid/actuator_controller.py +13 -0
- pymodaq/extensions/pid/daq_move_PID.py +25 -46
- pymodaq/extensions/pid/pid_controller.py +49 -41
- pymodaq/extensions/pid/utils.py +7 -31
- pymodaq/extensions/utils.py +41 -7
- pymodaq/post_treatment/load_and_plot.py +43 -10
- pymodaq/resources/setup_plugin.py +1 -0
- pymodaq/updater.py +107 -0
- pymodaq/utils/chrono_timer.py +6 -7
- pymodaq/utils/daq_utils.py +6 -3
- pymodaq/utils/data.py +21 -17
- pymodaq/utils/enums.py +6 -0
- pymodaq/utils/gui_utils/loader_utils.py +29 -2
- pymodaq/utils/gui_utils/utils.py +9 -12
- pymodaq/utils/gui_utils/widgets/lcd.py +8 -0
- pymodaq/utils/h5modules/module_saving.py +5 -2
- pymodaq/utils/leco/daq_move_LECODirector.py +22 -16
- pymodaq/utils/leco/daq_xDviewer_LECODirector.py +15 -9
- pymodaq/utils/leco/leco_director.py +4 -3
- pymodaq/utils/leco/pymodaq_listener.py +9 -13
- pymodaq/utils/leco/utils.py +40 -7
- pymodaq/utils/managers/modules_manager.py +22 -12
- pymodaq/utils/managers/overshoot_manager.py +45 -1
- pymodaq/utils/managers/preset_manager.py +22 -46
- pymodaq/utils/managers/preset_manager_utils.py +17 -13
- pymodaq/utils/managers/remote_manager.py +1 -1
- pymodaq/utils/messenger.py +6 -0
- pymodaq/utils/parameter/__init__.py +5 -1
- pymodaq/utils/tcp_ip/mysocket.py +4 -110
- pymodaq/utils/tcp_ip/serializer.py +4 -769
- pymodaq/utils/tcp_ip/tcp_server_client.py +5 -5
- pymodaq-5.0.2.dist-info/METADATA +242 -0
- {pymodaq-5.0.0.dist-info → pymodaq-5.0.2.dist-info}/RECORD +54 -55
- {pymodaq-5.0.0.dist-info → pymodaq-5.0.2.dist-info}/WHEEL +1 -1
- {pymodaq-5.0.0.dist-info → pymodaq-5.0.2.dist-info}/entry_points.txt +1 -0
- pymodaq/examples/custom_app.py +0 -255
- pymodaq/examples/custom_viewer.py +0 -112
- pymodaq/examples/parameter_ex.py +0 -158
- pymodaq/examples/preset_MockCamera.xml +0 -1
- pymodaq/post_treatment/daq_measurement/daq_measurement_GUI.py +0 -142
- pymodaq/post_treatment/daq_measurement/daq_measurement_GUI.ui +0 -232
- pymodaq/post_treatment/daq_measurement/daq_measurement_main.py +0 -391
- pymodaq/post_treatment/daq_measurement/process_from_QtDesigner_DAQ_Measurement_GUI.bat +0 -2
- pymodaq-5.0.0.dist-info/METADATA +0 -166
- /pymodaq/{post_treatment/daq_measurement → daq_utils}/__init__.py +0 -0
- {pymodaq-5.0.0.dist-info → pymodaq-5.0.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -4,775 +4,10 @@ Created the 20/10/2023
|
|
|
4
4
|
|
|
5
5
|
@author: Sebastien Weber
|
|
6
6
|
"""
|
|
7
|
-
from base64 import b64encode, b64decode
|
|
8
|
-
import numbers
|
|
9
|
-
from typing import Tuple, List, Union, TYPE_CHECKING, Iterable
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
from pymodaq_data import data as data_mod
|
|
13
|
-
from pymodaq_data.data import DataWithAxes, DataToExport, Axis, DwaType
|
|
8
|
+
from pymodaq_utils.utils import deprecation_msg
|
|
14
9
|
|
|
15
|
-
from
|
|
10
|
+
from pymodaq_utils.serialize.serializer_legacy import Serializer, DeSerializer, SocketString, Socket
|
|
16
11
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if TYPE_CHECKING:
|
|
20
|
-
from pymodaq.utils.tcp_ip.mysocket import Socket
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
SERIALIZABLE = Union[
|
|
24
|
-
int,
|
|
25
|
-
str,
|
|
26
|
-
numbers.Number,
|
|
27
|
-
list,
|
|
28
|
-
np.ndarray,
|
|
29
|
-
Axis,
|
|
30
|
-
DataWithAxes,
|
|
31
|
-
DataToExport,
|
|
32
|
-
]
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class SocketString:
|
|
36
|
-
"""Mimic the Socket object but actually using a bytes string not a socket connection
|
|
37
|
-
|
|
38
|
-
Implements a minimal interface of two methods
|
|
39
|
-
|
|
40
|
-
Parameters
|
|
41
|
-
----------
|
|
42
|
-
bytes_string: bytes
|
|
43
|
-
|
|
44
|
-
See Also
|
|
45
|
-
--------
|
|
46
|
-
:class:`~pymodaq.utils.tcp_ip.mysocket.Socket`
|
|
47
|
-
"""
|
|
48
|
-
def __init__(self, bytes_string: bytes):
|
|
49
|
-
self._bytes_string = bytes_string
|
|
50
|
-
|
|
51
|
-
def check_received_length(self, length: int) -> bytes:
|
|
52
|
-
"""
|
|
53
|
-
Make sure all bytes (length) that should be received are received through the socket.
|
|
54
|
-
|
|
55
|
-
Here just read the content of the underlying bytes string
|
|
56
|
-
|
|
57
|
-
Parameters
|
|
58
|
-
----------
|
|
59
|
-
length: int
|
|
60
|
-
The number of bytes to be read from the socket
|
|
61
|
-
|
|
62
|
-
Returns
|
|
63
|
-
-------
|
|
64
|
-
bytes
|
|
65
|
-
"""
|
|
66
|
-
data = self._bytes_string[0:length]
|
|
67
|
-
self._bytes_string = self._bytes_string[length:]
|
|
68
|
-
return data
|
|
69
|
-
|
|
70
|
-
def get_first_nbytes(self, length: int) -> bytes:
|
|
71
|
-
""" Read the first N bytes from the socket
|
|
72
|
-
|
|
73
|
-
Parameters
|
|
74
|
-
----------
|
|
75
|
-
length: int
|
|
76
|
-
The number of bytes to be read from the socket
|
|
77
|
-
|
|
78
|
-
Returns
|
|
79
|
-
-------
|
|
80
|
-
bytes
|
|
81
|
-
the read bytes string
|
|
82
|
-
"""
|
|
83
|
-
return self.check_received_length(length)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
class Serializer:
|
|
87
|
-
"""Used to Serialize to bytes python objects, numpy arrays and PyMoDAQ DataWithAxes and
|
|
88
|
-
DataToExport objects"""
|
|
89
|
-
|
|
90
|
-
def __init__(self, obj: SERIALIZABLE = None):
|
|
91
|
-
self._bytes_string = b''
|
|
92
|
-
self._obj = obj
|
|
93
|
-
|
|
94
|
-
def to_bytes(self):
|
|
95
|
-
""" Generic method to obtain the bytes string from various objects
|
|
96
|
-
|
|
97
|
-
Compatible objects are:
|
|
98
|
-
|
|
99
|
-
* :class:`bytes`
|
|
100
|
-
* :class:`numbers.Number`
|
|
101
|
-
* :class:`str`
|
|
102
|
-
* :class:`numpy.ndarray`
|
|
103
|
-
* :class:`~pymodaq.utils.data.Axis`
|
|
104
|
-
* :class:`~pymodaq.utils.data.DataWithAxes` and sub-flavours
|
|
105
|
-
* :class:`~pymodaq.utils.data.DataToExport`
|
|
106
|
-
* :class:`list` of any objects above
|
|
107
|
-
|
|
108
|
-
"""
|
|
109
|
-
if isinstance(self._obj, bytes):
|
|
110
|
-
return self.bytes_serialization(self._obj)
|
|
111
|
-
elif isinstance(self._obj, numbers.Number):
|
|
112
|
-
return self.scalar_serialization(self._obj)
|
|
113
|
-
elif isinstance(self._obj, str):
|
|
114
|
-
return self.string_serialization(self._obj)
|
|
115
|
-
elif isinstance(self._obj, np.ndarray):
|
|
116
|
-
return self.ndarray_serialization(self._obj)
|
|
117
|
-
elif isinstance(self._obj, Axis):
|
|
118
|
-
return self.axis_serialization(self._obj)
|
|
119
|
-
elif self._obj.__class__.__name__ in DwaType.names():
|
|
120
|
-
return self.dwa_serialization(self._obj)
|
|
121
|
-
elif isinstance(self._obj, DataToExport):
|
|
122
|
-
return self.dte_serialization(self._obj)
|
|
123
|
-
elif isinstance(self._obj, list):
|
|
124
|
-
return self.list_serialization(self._obj)
|
|
125
|
-
elif isinstance(self._obj, bool):
|
|
126
|
-
return self.scalar_serialization(int(self._obj))
|
|
127
|
-
raise ValueError
|
|
128
|
-
|
|
129
|
-
def to_b64_string(self) -> str:
|
|
130
|
-
b = self.to_bytes()
|
|
131
|
-
return b64encode(b).decode()
|
|
132
|
-
|
|
133
|
-
@staticmethod
|
|
134
|
-
def int_to_bytes(an_integer: int) -> bytes:
|
|
135
|
-
"""Convert an unsigned integer into a byte array of length 4 in big endian
|
|
136
|
-
|
|
137
|
-
Parameters
|
|
138
|
-
----------
|
|
139
|
-
an_integer: int
|
|
140
|
-
|
|
141
|
-
Returns
|
|
142
|
-
-------
|
|
143
|
-
bytearray
|
|
144
|
-
"""
|
|
145
|
-
if not isinstance(an_integer, int):
|
|
146
|
-
raise TypeError(f'{an_integer} should be an integer, not a {type(an_integer)}')
|
|
147
|
-
elif an_integer < 0:
|
|
148
|
-
raise ValueError('Can only serialize unsigned integer using this method')
|
|
149
|
-
return an_integer.to_bytes(4, 'big')
|
|
150
|
-
|
|
151
|
-
@staticmethod
|
|
152
|
-
def str_to_bytes(message: str) -> bytes:
|
|
153
|
-
if not isinstance(message, str):
|
|
154
|
-
raise TypeError('Can only serialize str object using this method')
|
|
155
|
-
return message.encode()
|
|
156
|
-
|
|
157
|
-
@classmethod
|
|
158
|
-
def str_len_to_bytes(cls, message: Union[str, bytes]) -> (bytes, bytes):
|
|
159
|
-
""" Convert a string and its length to two bytes
|
|
160
|
-
Parameters
|
|
161
|
-
----------
|
|
162
|
-
message: str
|
|
163
|
-
the message to convert
|
|
164
|
-
|
|
165
|
-
Returns
|
|
166
|
-
-------
|
|
167
|
-
bytes: message converted as a byte array
|
|
168
|
-
bytes: length of the message byte array, itself as a byte array of length 4
|
|
169
|
-
"""
|
|
170
|
-
|
|
171
|
-
if not isinstance(message, str) and not isinstance(message, bytes):
|
|
172
|
-
message = str(message)
|
|
173
|
-
if not isinstance(message, bytes):
|
|
174
|
-
message = cls.str_to_bytes(message)
|
|
175
|
-
return message, cls.int_to_bytes(len(message))
|
|
176
|
-
|
|
177
|
-
def _int_serialization(self, int_obj: int) -> bytes:
|
|
178
|
-
"""serialize an unsigned integer used for getting the length of messages internaly, for outside integer
|
|
179
|
-
serialization or deserialization use scalar_serialization"""
|
|
180
|
-
int_bytes = self.int_to_bytes(int_obj)
|
|
181
|
-
bytes_string = int_bytes
|
|
182
|
-
self._bytes_string += bytes_string
|
|
183
|
-
return bytes_string
|
|
184
|
-
|
|
185
|
-
def bytes_serialization(self, bytes_string_in: bytes) -> bytes:
|
|
186
|
-
bytes_string = b''
|
|
187
|
-
bytes_string += self.int_to_bytes(len(bytes_string_in))
|
|
188
|
-
bytes_string += bytes_string_in
|
|
189
|
-
return bytes_string
|
|
190
|
-
|
|
191
|
-
def string_serialization(self, string: str) -> bytes:
|
|
192
|
-
""" Convert a string into a bytes message together with the info to convert it back
|
|
193
|
-
|
|
194
|
-
Parameters
|
|
195
|
-
----------
|
|
196
|
-
string: str
|
|
197
|
-
|
|
198
|
-
Returns
|
|
199
|
-
-------
|
|
200
|
-
bytes: the total bytes message to serialize the string
|
|
201
|
-
"""
|
|
202
|
-
bytes_string = b''
|
|
203
|
-
cmd_bytes, cmd_length_bytes = self.str_len_to_bytes(string)
|
|
204
|
-
bytes_string += cmd_length_bytes
|
|
205
|
-
bytes_string += cmd_bytes
|
|
206
|
-
self._bytes_string += bytes_string
|
|
207
|
-
return bytes_string
|
|
208
|
-
|
|
209
|
-
def scalar_serialization(self, scalar: numbers.Number) -> bytes:
|
|
210
|
-
""" Convert a scalar into a bytes message together with the info to convert it back
|
|
211
|
-
|
|
212
|
-
Parameters
|
|
213
|
-
----------
|
|
214
|
-
scalar: str
|
|
215
|
-
|
|
216
|
-
Returns
|
|
217
|
-
-------
|
|
218
|
-
bytes: the total bytes message to serialize the scalar
|
|
219
|
-
"""
|
|
220
|
-
if not isinstance(scalar, numbers.Number):
|
|
221
|
-
raise TypeError(f'{scalar} should be an integer or a float, not a {type(scalar)}')
|
|
222
|
-
scalar_array = np.array([scalar])
|
|
223
|
-
data_type = scalar_array.dtype.descr[0][1]
|
|
224
|
-
data_bytes = scalar_array.tobytes()
|
|
225
|
-
|
|
226
|
-
bytes_string = b''
|
|
227
|
-
bytes_string += self.string_serialization(data_type)
|
|
228
|
-
bytes_string += self._int_serialization(len(data_bytes))
|
|
229
|
-
bytes_string += data_bytes
|
|
230
|
-
self._bytes_string += bytes_string
|
|
231
|
-
return bytes_string
|
|
232
|
-
|
|
233
|
-
def ndarray_serialization(self, array: np.ndarray) -> bytes:
|
|
234
|
-
""" Convert a ndarray into a bytes message together with the info to convert it back
|
|
235
|
-
|
|
236
|
-
Parameters
|
|
237
|
-
----------
|
|
238
|
-
array: np.ndarray
|
|
239
|
-
|
|
240
|
-
Returns
|
|
241
|
-
-------
|
|
242
|
-
bytes: the total bytes message to serialize the scalar
|
|
243
|
-
|
|
244
|
-
Notes
|
|
245
|
-
-----
|
|
246
|
-
|
|
247
|
-
The bytes sequence is constructed as:
|
|
248
|
-
|
|
249
|
-
* get data type as a string
|
|
250
|
-
* reshape array as 1D array and get the array dimensionality (len of array's shape)
|
|
251
|
-
* convert Data array as bytes
|
|
252
|
-
* serialize data type
|
|
253
|
-
* serialize data length
|
|
254
|
-
* serialize data shape length
|
|
255
|
-
* serialize all values of the shape as integers converted to bytes
|
|
256
|
-
* serialize array as bytes
|
|
257
|
-
"""
|
|
258
|
-
if not isinstance(array, np.ndarray):
|
|
259
|
-
raise TypeError(f'{array} should be an numpy array, not a {type(array)}')
|
|
260
|
-
array_type = array.dtype.descr[0][1]
|
|
261
|
-
array_shape = array.shape
|
|
262
|
-
|
|
263
|
-
array = array.reshape(array.size)
|
|
264
|
-
array_bytes = array.tobytes()
|
|
265
|
-
bytes_string = b''
|
|
266
|
-
bytes_string += self.string_serialization(array_type)
|
|
267
|
-
bytes_string += self._int_serialization(len(array_bytes))
|
|
268
|
-
bytes_string += self._int_serialization(len(array_shape))
|
|
269
|
-
for shape_elt in array_shape:
|
|
270
|
-
bytes_string += self._int_serialization(shape_elt)
|
|
271
|
-
bytes_string += array_bytes
|
|
272
|
-
self._bytes_string += bytes_string
|
|
273
|
-
return bytes_string
|
|
274
|
-
|
|
275
|
-
def object_type_serialization(self, obj: Union[Axis, DataToExport, DataWithAxes]) -> bytes:
|
|
276
|
-
""" Convert an object type into a bytes message as a string together with the info to
|
|
277
|
-
convert it back
|
|
278
|
-
|
|
279
|
-
Applies to Data object from the pymodaq.utils.data module
|
|
280
|
-
"""
|
|
281
|
-
return self.string_serialization(obj.__class__.__name__)
|
|
282
|
-
|
|
283
|
-
def axis_serialization(self, axis: Axis) -> bytes:
|
|
284
|
-
""" Convert an Axis object into a bytes message together with the info to convert it back
|
|
285
|
-
|
|
286
|
-
Parameters
|
|
287
|
-
----------
|
|
288
|
-
axis: Axis
|
|
289
|
-
|
|
290
|
-
Returns
|
|
291
|
-
-------
|
|
292
|
-
bytes: the total bytes message to serialize the Axis
|
|
293
|
-
|
|
294
|
-
Notes
|
|
295
|
-
-----
|
|
296
|
-
|
|
297
|
-
The bytes sequence is constructed as:
|
|
298
|
-
|
|
299
|
-
* serialize the type: 'Axis'
|
|
300
|
-
* serialize the axis label
|
|
301
|
-
* serialize the axis units
|
|
302
|
-
* serialize the axis array
|
|
303
|
-
* serialize the axis
|
|
304
|
-
* serialize the axis spread_order
|
|
305
|
-
"""
|
|
306
|
-
if not isinstance(axis, Axis):
|
|
307
|
-
raise TypeError(f'{axis} should be a list, not a {type(axis)}')
|
|
308
|
-
|
|
309
|
-
bytes_string = b''
|
|
310
|
-
bytes_string += self.object_type_serialization(axis)
|
|
311
|
-
bytes_string += self.string_serialization(axis.label)
|
|
312
|
-
bytes_string += self.string_serialization(axis.units)
|
|
313
|
-
bytes_string += self.ndarray_serialization(axis.get_data())
|
|
314
|
-
bytes_string += self.scalar_serialization(axis.index)
|
|
315
|
-
bytes_string += self.scalar_serialization(axis.spread_order)
|
|
316
|
-
self._bytes_string += bytes_string
|
|
317
|
-
return bytes_string
|
|
318
|
-
|
|
319
|
-
def list_serialization(self, list_object: List) -> bytes:
|
|
320
|
-
""" Convert a list of objects into a bytes message together with the info to convert it back
|
|
321
|
-
|
|
322
|
-
Parameters
|
|
323
|
-
----------
|
|
324
|
-
list_object: list
|
|
325
|
-
the list could contains either scalars, strings or ndarrays or Axis objects or DataWithAxis objects
|
|
326
|
-
module
|
|
327
|
-
|
|
328
|
-
Returns
|
|
329
|
-
-------
|
|
330
|
-
bytes: the total bytes message to serialize the list of objects
|
|
331
|
-
|
|
332
|
-
Notes
|
|
333
|
-
-----
|
|
334
|
-
|
|
335
|
-
The bytes sequence is constructed as:
|
|
336
|
-
* the length of the list
|
|
337
|
-
|
|
338
|
-
Then for each object:
|
|
339
|
-
|
|
340
|
-
* get data type as a string
|
|
341
|
-
* use the serialization method adapted to each object in the list
|
|
342
|
-
"""
|
|
343
|
-
if not isinstance(list_object, list):
|
|
344
|
-
raise TypeError(f'{list_object} should be a list, not a {type(list_object)}')
|
|
345
|
-
|
|
346
|
-
bytes_string = b''
|
|
347
|
-
|
|
348
|
-
bytes_string += self._int_serialization(len(list_object))
|
|
349
|
-
for obj in list_object:
|
|
350
|
-
bytes_string += self.type_and_object_serialization(obj)
|
|
351
|
-
self._bytes_string += bytes_string
|
|
352
|
-
return bytes_string
|
|
353
|
-
|
|
354
|
-
def type_and_object_serialization(self, obj):
|
|
355
|
-
bytes_string = b''
|
|
356
|
-
if isinstance(obj, DataWithAxes):
|
|
357
|
-
bytes_string += self.string_serialization('dwa')
|
|
358
|
-
bytes_string += self.dwa_serialization(obj)
|
|
359
|
-
|
|
360
|
-
elif isinstance(obj, Axis):
|
|
361
|
-
bytes_string += self.string_serialization('axis')
|
|
362
|
-
bytes_string += self.axis_serialization(obj)
|
|
363
|
-
|
|
364
|
-
elif isinstance(obj, np.ndarray):
|
|
365
|
-
bytes_string += self.string_serialization('array')
|
|
366
|
-
bytes_string += self.ndarray_serialization(obj)
|
|
367
|
-
|
|
368
|
-
elif isinstance(obj, bytes):
|
|
369
|
-
bytes_string += self.string_serialization('bytes')
|
|
370
|
-
bytes_string += self.bytes_serialization(obj)
|
|
371
|
-
|
|
372
|
-
elif isinstance(obj, str):
|
|
373
|
-
bytes_string += self.string_serialization('string')
|
|
374
|
-
bytes_string += self.string_serialization(obj)
|
|
375
|
-
|
|
376
|
-
elif isinstance(obj, numbers.Number):
|
|
377
|
-
bytes_string += self.string_serialization('scalar')
|
|
378
|
-
bytes_string += self.scalar_serialization(obj)
|
|
379
|
-
|
|
380
|
-
elif isinstance(obj, bool):
|
|
381
|
-
bytes_string += self.string_serialization('bool')
|
|
382
|
-
bytes_string += self.scalar_serialization(int(obj))
|
|
383
|
-
|
|
384
|
-
elif isinstance(obj, list):
|
|
385
|
-
bytes_string += self.string_serialization('list')
|
|
386
|
-
bytes_string += self.list_serialization(obj)
|
|
387
|
-
|
|
388
|
-
elif isinstance(obj, putils.ParameterWithPath):
|
|
389
|
-
path = obj.path
|
|
390
|
-
param_as_xml = ioxml.parameter_to_xml_string(obj.parameter)
|
|
391
|
-
bytes_string += self.string_serialization('parameter')
|
|
392
|
-
bytes_string += self.list_serialization(path)
|
|
393
|
-
bytes_string += self.string_serialization(param_as_xml)
|
|
394
|
-
|
|
395
|
-
elif isinstance(obj, DataToExport):
|
|
396
|
-
bytes_string += self.string_serialization('dte')
|
|
397
|
-
bytes_string += self.dte_serialization(obj)
|
|
398
|
-
|
|
399
|
-
else:
|
|
400
|
-
raise TypeError(
|
|
401
|
-
f'the element {obj} type cannot be serialized into bytes, only numpy arrays'
|
|
402
|
-
f', strings, or scalars (int or float)')
|
|
403
|
-
|
|
404
|
-
return bytes_string
|
|
405
|
-
|
|
406
|
-
def dwa_serialization(self, dwa: DataWithAxes) -> bytes:
|
|
407
|
-
""" Convert a DataWithAxes into a bytes string
|
|
408
|
-
|
|
409
|
-
Parameters
|
|
410
|
-
----------
|
|
411
|
-
dwa: DataWithAxes
|
|
412
|
-
|
|
413
|
-
Returns
|
|
414
|
-
-------
|
|
415
|
-
bytes: the total bytes message to serialize the DataWithAxes
|
|
416
|
-
|
|
417
|
-
Notes
|
|
418
|
-
-----
|
|
419
|
-
The bytes sequence is constructed as:
|
|
420
|
-
|
|
421
|
-
* serialize the string type: 'DataWithAxes'
|
|
422
|
-
* serialize the timestamp: float
|
|
423
|
-
* serialize the name
|
|
424
|
-
* serialize the source enum as a string
|
|
425
|
-
* serialize the dim enum as a string
|
|
426
|
-
* serialize the distribution enum as a string
|
|
427
|
-
* serialize the list of numpy arrays
|
|
428
|
-
* serialize the list of labels
|
|
429
|
-
* serialize the origin
|
|
430
|
-
* serialize the nav_index tuple as a list of int
|
|
431
|
-
* serialize the list of axis
|
|
432
|
-
* serialize the errors attributes (None or list(np.ndarray))
|
|
433
|
-
* serialize the list of names of extra attributes
|
|
434
|
-
* serialize the extra attributes
|
|
435
|
-
"""
|
|
436
|
-
if not isinstance(dwa, DataWithAxes):
|
|
437
|
-
raise TypeError(f'{dwa} should be a DataWithAxes, not a {type(dwa)}')
|
|
438
|
-
|
|
439
|
-
bytes_string = b''
|
|
440
|
-
bytes_string += self.object_type_serialization(dwa)
|
|
441
|
-
bytes_string += self.scalar_serialization(dwa.timestamp)
|
|
442
|
-
bytes_string += self.string_serialization(dwa.name)
|
|
443
|
-
bytes_string += self.string_serialization(dwa.source.name)
|
|
444
|
-
bytes_string += self.string_serialization(dwa.dim.name)
|
|
445
|
-
bytes_string += self.string_serialization(dwa.distribution.name)
|
|
446
|
-
bytes_string += self.list_serialization(dwa.data)
|
|
447
|
-
bytes_string += self.list_serialization(dwa.labels)
|
|
448
|
-
bytes_string += self.string_serialization(dwa.origin)
|
|
449
|
-
bytes_string += self.list_serialization(list(dwa.nav_indexes))
|
|
450
|
-
bytes_string += self.list_serialization(dwa.axes)
|
|
451
|
-
if dwa.errors is None:
|
|
452
|
-
errors = [] # have to use this extra attribute as if I force dwa.errors = [], it will be
|
|
453
|
-
#internally modified as None again
|
|
454
|
-
else:
|
|
455
|
-
errors = dwa.errors
|
|
456
|
-
bytes_string += self.list_serialization(errors)
|
|
457
|
-
bytes_string += self.list_serialization(dwa.extra_attributes)
|
|
458
|
-
for attribute in dwa.extra_attributes:
|
|
459
|
-
bytes_string += self.type_and_object_serialization(getattr(dwa, attribute))
|
|
460
|
-
self._bytes_string += bytes_string
|
|
461
|
-
return bytes_string
|
|
462
|
-
|
|
463
|
-
def dte_serialization(self, dte: DataToExport) -> bytes:
|
|
464
|
-
""" Convert a DataToExport into a bytes string
|
|
465
|
-
|
|
466
|
-
Parameters
|
|
467
|
-
----------
|
|
468
|
-
dte: DataToExport
|
|
469
|
-
|
|
470
|
-
Returns
|
|
471
|
-
-------
|
|
472
|
-
bytes: the total bytes message to serialize the DataToExport
|
|
473
|
-
|
|
474
|
-
Notes
|
|
475
|
-
-----
|
|
476
|
-
The bytes sequence is constructed as:
|
|
477
|
-
|
|
478
|
-
* serialize the string type: 'DataToExport'
|
|
479
|
-
* serialize the timestamp: float
|
|
480
|
-
* serialize the name
|
|
481
|
-
* serialize the list of DataWithAxes
|
|
482
|
-
"""
|
|
483
|
-
if not isinstance(dte, DataToExport):
|
|
484
|
-
raise TypeError(f'{dte} should be a DataToExport, not a {type(dte)}')
|
|
485
|
-
|
|
486
|
-
bytes_string = b''
|
|
487
|
-
bytes_string += self.object_type_serialization(dte)
|
|
488
|
-
bytes_string += self.scalar_serialization(dte.timestamp)
|
|
489
|
-
bytes_string += self.string_serialization(dte.name)
|
|
490
|
-
bytes_string += self.list_serialization(dte.data)
|
|
491
|
-
self._bytes_string += bytes_string
|
|
492
|
-
return bytes_string
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
class DeSerializer:
|
|
496
|
-
"""Used to DeSerialize bytes to python objects, numpy arrays and PyMoDAQ Axis, DataWithAxes and DataToExport
|
|
497
|
-
objects
|
|
498
|
-
|
|
499
|
-
Parameters
|
|
500
|
-
----------
|
|
501
|
-
bytes_string: bytes or Socket
|
|
502
|
-
the bytes string to deserialize into an object: int, float, string, arrays, list, Axis, DataWithAxes...
|
|
503
|
-
Could also be a Socket object reading bytes from the network having a `get_first_nbytes` method
|
|
504
|
-
|
|
505
|
-
See Also
|
|
506
|
-
--------
|
|
507
|
-
:py:class:`~pymodaq.utils.tcp_ip.serializer.SocketString`
|
|
508
|
-
:py:class:`~pymodaq.utils.tcp_ip.mysocket.Socket`
|
|
509
|
-
"""
|
|
510
|
-
|
|
511
|
-
def __init__(self, bytes_string: Union[bytes, 'Socket'] = None):
|
|
512
|
-
if isinstance(bytes_string, bytes):
|
|
513
|
-
bytes_string = SocketString(bytes_string)
|
|
514
|
-
self._bytes_string = bytes_string
|
|
515
|
-
|
|
516
|
-
@classmethod
|
|
517
|
-
def from_b64_string(cls, b64_string: Union[bytes, str]) -> "DeSerializer":
|
|
518
|
-
return cls(b64decode(b64_string))
|
|
519
|
-
|
|
520
|
-
@staticmethod
|
|
521
|
-
def bytes_to_string(message: bytes) -> str:
|
|
522
|
-
return message.decode()
|
|
523
|
-
|
|
524
|
-
@staticmethod
|
|
525
|
-
def bytes_to_int(bytes_string: bytes) -> int:
|
|
526
|
-
"""Convert a bytes of length 4 into an integer"""
|
|
527
|
-
if not isinstance(bytes_string, bytes):
|
|
528
|
-
raise TypeError(f'{bytes_string} should be an bytes string, not a {type(bytes_string)}')
|
|
529
|
-
assert len(bytes_string) == 4
|
|
530
|
-
return int.from_bytes(bytes_string, 'big')
|
|
531
|
-
|
|
532
|
-
@staticmethod
|
|
533
|
-
def bytes_to_scalar(data: bytes, dtype: np.dtype) -> numbers.Number:
|
|
534
|
-
"""Convert bytes to a scalar given a certain numpy dtype
|
|
535
|
-
|
|
536
|
-
Parameters
|
|
537
|
-
----------
|
|
538
|
-
data: bytes
|
|
539
|
-
dtype:np.dtype
|
|
540
|
-
|
|
541
|
-
Returns
|
|
542
|
-
-------
|
|
543
|
-
numbers.Number
|
|
544
|
-
"""
|
|
545
|
-
return np.frombuffer(data, dtype=dtype)[0]
|
|
546
|
-
|
|
547
|
-
@staticmethod
|
|
548
|
-
def bytes_to_nd_array(data: bytes, dtype: np.dtype, shape: Tuple[int]) -> np.ndarray:
|
|
549
|
-
"""Convert bytes to a ndarray given a certain numpy dtype and shape
|
|
550
|
-
|
|
551
|
-
Parameters
|
|
552
|
-
----------
|
|
553
|
-
data: bytes
|
|
554
|
-
dtype: np.dtype
|
|
555
|
-
shape: tuple of int
|
|
556
|
-
|
|
557
|
-
Returns
|
|
558
|
-
-------
|
|
559
|
-
np.ndarray
|
|
560
|
-
"""
|
|
561
|
-
array = np.frombuffer(data, dtype=dtype)
|
|
562
|
-
array = array.reshape(tuple(shape))
|
|
563
|
-
array = np.atleast_1d(array) # remove singleton dimensions but keeping ndarrays
|
|
564
|
-
return array
|
|
565
|
-
|
|
566
|
-
def _int_deserialization(self) -> int:
|
|
567
|
-
"""Convert the fourth first bytes into an unsigned integer to be used internally. For integer serialization
|
|
568
|
-
use scal_serialization"""
|
|
569
|
-
int_obj = self.bytes_to_int(self._bytes_string.get_first_nbytes(4))
|
|
570
|
-
return int_obj
|
|
571
|
-
|
|
572
|
-
def bytes_deserialization(self) -> bytes:
|
|
573
|
-
bstring_len = self._int_deserialization()
|
|
574
|
-
bstr = self._bytes_string.get_first_nbytes(bstring_len)
|
|
575
|
-
return bstr
|
|
576
|
-
|
|
577
|
-
def string_deserialization(self) -> str:
|
|
578
|
-
"""Convert bytes into a str object
|
|
579
|
-
|
|
580
|
-
Convert first the fourth first bytes into an int encoding the length of the string to decode
|
|
581
|
-
|
|
582
|
-
Returns
|
|
583
|
-
-------
|
|
584
|
-
str: the decoded string
|
|
585
|
-
"""
|
|
586
|
-
string_len = self._int_deserialization()
|
|
587
|
-
str_obj = self._bytes_string.get_first_nbytes(string_len).decode()
|
|
588
|
-
return str_obj
|
|
589
|
-
|
|
590
|
-
def scalar_deserialization(self) -> numbers.Number:
|
|
591
|
-
"""Convert bytes into a numbers.Number object
|
|
592
|
-
|
|
593
|
-
Get first the data type from a string deserialization, then the data length and finally convert this
|
|
594
|
-
length of bytes into a number (float, int)
|
|
595
|
-
|
|
596
|
-
Returns
|
|
597
|
-
-------
|
|
598
|
-
numbers.Number: the decoded number
|
|
599
|
-
"""
|
|
600
|
-
data_type = self.string_deserialization()
|
|
601
|
-
data_len = self._int_deserialization()
|
|
602
|
-
number = np.frombuffer(self._bytes_string.get_first_nbytes(data_len), dtype=data_type)[0]
|
|
603
|
-
if 'f' in data_type:
|
|
604
|
-
number = float(number) # because one get numpy float type
|
|
605
|
-
elif 'i' in data_type:
|
|
606
|
-
number = int(number) # because one get numpy int type
|
|
607
|
-
return number
|
|
608
|
-
|
|
609
|
-
def boolean_deserialization(self) -> bool:
|
|
610
|
-
"""Convert bytes into a boolean object
|
|
611
|
-
|
|
612
|
-
Get first the data type from a string deserialization, then the data length and finally
|
|
613
|
-
convert this length of bytes into a number (float, int) and cast it to a bool
|
|
614
|
-
|
|
615
|
-
Returns
|
|
616
|
-
-------
|
|
617
|
-
bool: the decoded boolean
|
|
618
|
-
"""
|
|
619
|
-
number = self.scalar_deserialization()
|
|
620
|
-
return bool(number)
|
|
621
|
-
|
|
622
|
-
def ndarray_deserialization(self) -> np.ndarray:
|
|
623
|
-
"""Convert bytes into a numpy ndarray object
|
|
624
|
-
|
|
625
|
-
Convert the first bytes into a ndarray reading first information about the array's data
|
|
626
|
-
|
|
627
|
-
Returns
|
|
628
|
-
-------
|
|
629
|
-
ndarray: the decoded numpy array
|
|
630
|
-
"""
|
|
631
|
-
ndarray_type = self.string_deserialization()
|
|
632
|
-
ndarray_len = self._int_deserialization()
|
|
633
|
-
shape_len = self._int_deserialization()
|
|
634
|
-
shape = []
|
|
635
|
-
for ind in range(shape_len):
|
|
636
|
-
shape_elt = self._int_deserialization()
|
|
637
|
-
shape.append(shape_elt)
|
|
638
|
-
|
|
639
|
-
ndarray = np.frombuffer(self._bytes_string.get_first_nbytes(ndarray_len), dtype=ndarray_type)
|
|
640
|
-
ndarray = ndarray.reshape(tuple(shape))
|
|
641
|
-
ndarray = np.atleast_1d(ndarray) # remove singleton dimensions
|
|
642
|
-
return ndarray
|
|
643
|
-
|
|
644
|
-
def type_and_object_deserialization(self):
|
|
645
|
-
""" Deserialize specific objects from their binary representation (inverse of `Serializer.type_and_object_serialization`).
|
|
646
|
-
|
|
647
|
-
See Also
|
|
648
|
-
--------
|
|
649
|
-
Serializer.dwa_serialization, Serializer.dte_serialization
|
|
650
|
-
|
|
651
|
-
"""
|
|
652
|
-
obj_type = self.string_deserialization()
|
|
653
|
-
elt = None
|
|
654
|
-
if obj_type == 'scalar':
|
|
655
|
-
elt = self.scalar_deserialization()
|
|
656
|
-
elif obj_type == 'string':
|
|
657
|
-
elt = self.string_deserialization()
|
|
658
|
-
elif obj_type == 'bytes':
|
|
659
|
-
elt = self.bytes_deserialization()
|
|
660
|
-
elif obj_type == 'array':
|
|
661
|
-
elt = self.ndarray_deserialization()
|
|
662
|
-
elif obj_type == 'dwa':
|
|
663
|
-
elt = self.dwa_deserialization()
|
|
664
|
-
elif obj_type == 'dte':
|
|
665
|
-
elt = self.dte_deserialization()
|
|
666
|
-
elif obj_type == 'axis':
|
|
667
|
-
elt = self.axis_deserialization()
|
|
668
|
-
elif obj_type == 'bool':
|
|
669
|
-
elt = self.boolean_deserialization()
|
|
670
|
-
elif obj_type == 'list':
|
|
671
|
-
elt = self.list_deserialization()
|
|
672
|
-
elif obj_type == 'parameter':
|
|
673
|
-
elt = self.parameter_deserialization()
|
|
674
|
-
else:
|
|
675
|
-
print(f'invalid object type {obj_type}')
|
|
676
|
-
return elt
|
|
677
|
-
|
|
678
|
-
def list_deserialization(self) -> list:
|
|
679
|
-
"""Convert bytes into a list of homogeneous objects
|
|
680
|
-
|
|
681
|
-
Convert the first bytes into a list reading first information about the list elt types, length ...
|
|
682
|
-
|
|
683
|
-
Returns
|
|
684
|
-
-------
|
|
685
|
-
list: the decoded list
|
|
686
|
-
"""
|
|
687
|
-
list_obj = []
|
|
688
|
-
list_len = self._int_deserialization()
|
|
689
|
-
|
|
690
|
-
for ind in range(list_len):
|
|
691
|
-
list_obj.append(self.type_and_object_deserialization())
|
|
692
|
-
return list_obj
|
|
693
|
-
|
|
694
|
-
def parameter_deserialization(self) -> putils.ParameterWithPath:
|
|
695
|
-
path = self.list_deserialization()
|
|
696
|
-
param_as_xml = self.string_deserialization()
|
|
697
|
-
param_dict = ioxml.XML_string_to_parameter(param_as_xml)
|
|
698
|
-
param_obj = Parameter(**param_dict[0])
|
|
699
|
-
return putils.ParameterWithPath(param_obj, path)
|
|
700
|
-
|
|
701
|
-
def axis_deserialization(self) -> Axis:
|
|
702
|
-
"""Convert bytes into an Axis object
|
|
703
|
-
|
|
704
|
-
Convert the first bytes into an Axis reading first information about the Axis
|
|
705
|
-
|
|
706
|
-
Returns
|
|
707
|
-
-------
|
|
708
|
-
Axis: the decoded Axis
|
|
709
|
-
"""
|
|
710
|
-
|
|
711
|
-
class_name = self.string_deserialization()
|
|
712
|
-
if class_name != Axis.__name__:
|
|
713
|
-
raise TypeError(f'Attempting to deserialize an Axis but got the bytes for a {class_name}')
|
|
714
|
-
axis_label = self.string_deserialization()
|
|
715
|
-
axis_units = self.string_deserialization()
|
|
716
|
-
axis_array = self.ndarray_deserialization()
|
|
717
|
-
axis_index = self.scalar_deserialization()
|
|
718
|
-
axis_spread_order = self.scalar_deserialization()
|
|
719
|
-
|
|
720
|
-
axis = Axis(axis_label, axis_units, data=axis_array, index=axis_index,
|
|
721
|
-
spread_order=axis_spread_order)
|
|
722
|
-
return axis
|
|
723
|
-
|
|
724
|
-
def dwa_deserialization(self) -> DataWithAxes:
|
|
725
|
-
"""Convert bytes into a DataWithAxes object
|
|
726
|
-
|
|
727
|
-
Convert the first bytes into a DataWithAxes reading first information about the underlying data
|
|
728
|
-
|
|
729
|
-
Returns
|
|
730
|
-
-------
|
|
731
|
-
DataWithAxes: the decoded DataWithAxes
|
|
732
|
-
"""
|
|
733
|
-
class_name = self.string_deserialization()
|
|
734
|
-
if class_name not in DwaType.names():
|
|
735
|
-
raise TypeError(f'Attempting to deserialize a DataWithAxes flavor but got the bytes for a {class_name}')
|
|
736
|
-
timestamp = self.scalar_deserialization()
|
|
737
|
-
try:
|
|
738
|
-
dwa_class = getattr(data_mod, class_name)
|
|
739
|
-
except AttributeError: # in case it is a particular data object defined only in pymodaq
|
|
740
|
-
dwa_class = getattr(data_mod_pymodaq, class_name)
|
|
741
|
-
dwa = dwa_class(self.string_deserialization(),
|
|
742
|
-
source=self.string_deserialization(),
|
|
743
|
-
dim=self.string_deserialization(),
|
|
744
|
-
distribution=self.string_deserialization(),
|
|
745
|
-
data=self.list_deserialization(),
|
|
746
|
-
labels=self.list_deserialization(),
|
|
747
|
-
origin=self.string_deserialization(),
|
|
748
|
-
nav_indexes=tuple(self.list_deserialization()),
|
|
749
|
-
axes=self.list_deserialization(),
|
|
750
|
-
)
|
|
751
|
-
errors = self.list_deserialization()
|
|
752
|
-
if len(errors) != 0:
|
|
753
|
-
dwa.errors = errors
|
|
754
|
-
dwa.extra_attributes = self.list_deserialization()
|
|
755
|
-
for attribute in dwa.extra_attributes:
|
|
756
|
-
setattr(dwa, attribute, self.type_and_object_deserialization())
|
|
757
|
-
|
|
758
|
-
dwa.timestamp = timestamp
|
|
759
|
-
return dwa
|
|
760
|
-
|
|
761
|
-
def dte_deserialization(self) -> DataToExport:
|
|
762
|
-
"""Convert bytes into a DataToExport object
|
|
763
|
-
|
|
764
|
-
Convert the first bytes into a DataToExport reading first information about the underlying data
|
|
765
|
-
|
|
766
|
-
Returns
|
|
767
|
-
-------
|
|
768
|
-
DataToExport: the decoded DataToExport
|
|
769
|
-
"""
|
|
770
|
-
class_name = self.string_deserialization()
|
|
771
|
-
if class_name != DataToExport.__name__:
|
|
772
|
-
raise TypeError(f'Attempting to deserialize a DataToExport but got the bytes for a {class_name}')
|
|
773
|
-
timestamp = self.scalar_deserialization()
|
|
774
|
-
dte = DataToExport(self.string_deserialization(),
|
|
775
|
-
data=self.list_deserialization(),
|
|
776
|
-
)
|
|
777
|
-
dte.timestamp = timestamp
|
|
778
|
-
return dte
|
|
12
|
+
deprecation_msg('Importing Serializer, DeSerializer from pymodaq is deprecated in PyMoDAQ >= 5,'
|
|
13
|
+
'import them from pymodaq_data.serialize.serializer')
|