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
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import numbers
|
|
2
|
+
|
|
1
3
|
from time import perf_counter
|
|
2
4
|
from typing import Union, List, Dict, TYPE_CHECKING, Optional
|
|
3
5
|
from numbers import Number
|
|
6
|
+
from collections.abc import Iterable
|
|
4
7
|
|
|
5
8
|
from easydict import EasyDict as edict
|
|
6
9
|
import numpy as np
|
|
@@ -16,11 +19,20 @@ from pymodaq_utils.logger import set_logger, get_module_name
|
|
|
16
19
|
import pymodaq_gui.parameter.utils as putils
|
|
17
20
|
from pymodaq_gui.parameter import Parameter
|
|
18
21
|
from pymodaq_gui.parameter import ioxml
|
|
22
|
+
from pymodaq_gui.utils.utils import mkQApp
|
|
19
23
|
|
|
20
24
|
from pymodaq.utils.tcp_ip.tcp_server_client import TCPServer, tcp_parameters
|
|
25
|
+
|
|
26
|
+
from pymodaq_data.data import DataUnitError, Q_
|
|
27
|
+
|
|
28
|
+
from pymodaq.utils.messenger import deprecation_msg
|
|
21
29
|
from pymodaq.utils.data import DataActuator
|
|
22
|
-
from
|
|
23
|
-
|
|
30
|
+
from pymodaq_utils.enums import BaseEnum, enum_checker
|
|
31
|
+
|
|
32
|
+
from pymodaq_utils.serialize.mysocket import Socket
|
|
33
|
+
from pymodaq_utils.serialize.serializer_legacy import DeSerializer, Serializer
|
|
34
|
+
from pymodaq import Unit
|
|
35
|
+
from pint.errors import OffsetUnitCalculusError
|
|
24
36
|
|
|
25
37
|
if TYPE_CHECKING:
|
|
26
38
|
from pymodaq.control_modules.daq_move import DAQ_Move_Hardware
|
|
@@ -29,13 +41,45 @@ logger = set_logger(get_module_name(__file__))
|
|
|
29
41
|
config = configmod.Config()
|
|
30
42
|
|
|
31
43
|
|
|
44
|
+
def check_units(dwa: DataActuator, units: str):
|
|
45
|
+
""" Check if dwa units is compatible with the units argument
|
|
46
|
+
|
|
47
|
+
If it is incompatible and has dimensionless units, brute force change the dwa units to units,
|
|
48
|
+
otherwise raise a DataUnitError
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
dwa: DataActuator
|
|
53
|
+
units: str
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
DataActuator
|
|
58
|
+
"""
|
|
59
|
+
if Unit(dwa.units).is_compatible_with(units):
|
|
60
|
+
return dwa
|
|
61
|
+
elif Unit(dwa.units).dimensionless: # dimensionless
|
|
62
|
+
dwa.force_units(units)
|
|
63
|
+
return dwa
|
|
64
|
+
else:
|
|
65
|
+
raise DataUnitError(f'Units incompatibility between {dwa} and "{units}" units')
|
|
66
|
+
|
|
67
|
+
|
|
32
68
|
class DataActuatorType(BaseEnum):
|
|
33
69
|
"""Enum for new or old style holding the value of the actuator"""
|
|
34
70
|
float = 0
|
|
35
71
|
DataActuator = 1
|
|
36
72
|
|
|
37
73
|
|
|
38
|
-
def comon_parameters(epsilon=config('actuator', 'epsilon_default')
|
|
74
|
+
def comon_parameters(epsilon=config('actuator', 'epsilon_default'),
|
|
75
|
+
epsilons=None):
|
|
76
|
+
if epsilons is not None:
|
|
77
|
+
epsilon = epsilons
|
|
78
|
+
if isinstance(epsilon, list):
|
|
79
|
+
epsilon = epsilon[0]
|
|
80
|
+
elif isinstance(epsilon, dict):
|
|
81
|
+
epsilon = epsilon[list[epsilon.keys()][0]]
|
|
82
|
+
|
|
39
83
|
return [{'title': 'Units:', 'name': 'units', 'type': 'str', 'value': '', 'readonly': True},
|
|
40
84
|
{'title': 'Epsilon:', 'name': 'epsilon', 'type': 'float',
|
|
41
85
|
'value': epsilon,
|
|
@@ -78,28 +122,52 @@ class MoveCommand:
|
|
|
78
122
|
self.value = value
|
|
79
123
|
|
|
80
124
|
|
|
81
|
-
def comon_parameters_fun(is_multiaxes=False, axes_names=
|
|
125
|
+
def comon_parameters_fun(is_multiaxes=False, axes_names=None,
|
|
126
|
+
axis_names: Union[List, Dict] = [],
|
|
127
|
+
master=True,
|
|
128
|
+
epsilon: float = config('actuator', 'epsilon_default')):
|
|
129
|
+
|
|
82
130
|
"""Function returning the common and mandatory parameters that should be on the actuator plugin level
|
|
83
131
|
|
|
84
132
|
Parameters
|
|
85
133
|
----------
|
|
86
134
|
is_multiaxes: bool
|
|
87
135
|
If True, display the particular settings to define which axis the controller is driving
|
|
88
|
-
|
|
136
|
+
axes_names: deprecated, use axis_names
|
|
137
|
+
axis_names: list of str or dictionnary of string as key and integer as value
|
|
89
138
|
The string identifier of every axis the controller can drive
|
|
90
139
|
master: bool
|
|
91
140
|
If True consider this plugin has to init the controller, otherwise use an already initialized instance
|
|
141
|
+
epsilon: float
|
|
142
|
+
deprecated (< 5.0.0) no more used here
|
|
143
|
+
|
|
92
144
|
"""
|
|
93
|
-
if
|
|
145
|
+
if axes_names is not None and len(axis_names) == 0:
|
|
146
|
+
if len(axes_names) == 0:
|
|
147
|
+
axes_names = ['']
|
|
94
148
|
axis_names = axes_names
|
|
95
149
|
|
|
150
|
+
is_multiaxes = len(axis_names) > 1 or is_multiaxes
|
|
151
|
+
if isinstance(axis_names, list):
|
|
152
|
+
if len(axis_names) > 0:
|
|
153
|
+
axis_name = axis_names[0]
|
|
154
|
+
else:
|
|
155
|
+
axis_names = ['']
|
|
156
|
+
axis_name = ''
|
|
157
|
+
elif isinstance(axis_names, dict):
|
|
158
|
+
axis_name = axis_names[list(axis_names.keys())[0]]
|
|
159
|
+
else:
|
|
160
|
+
raise ValueError('axis_names should be either a list of string or a dict with strings '
|
|
161
|
+
'as keys')
|
|
96
162
|
params = [
|
|
97
|
-
{'title': 'MultiAxes:', 'name': 'multiaxes', 'type': 'group',
|
|
98
|
-
|
|
99
|
-
|
|
163
|
+
{'title': 'MultiAxes:', 'name': 'multiaxes', 'type': 'group',
|
|
164
|
+
'visible': True, 'children': [
|
|
165
|
+
{'title': 'Controller ID:', 'name': 'controller_ID', 'type': 'int', 'value': 0,
|
|
166
|
+
'default': 0},
|
|
100
167
|
{'title': 'Status:', 'name': 'multi_status', 'type': 'list',
|
|
101
168
|
'value': 'Master' if master else 'Slave', 'limits': ['Master', 'Slave']},
|
|
102
|
-
{'title': 'Axis:', 'name': 'axis', 'type': 'list', 'limits': axis_names
|
|
169
|
+
{'title': 'Axis:', 'name': 'axis', 'type': 'list', 'limits': axis_names,
|
|
170
|
+
'value': axis_name},
|
|
103
171
|
]},
|
|
104
172
|
] + comon_parameters(epsilon)
|
|
105
173
|
return params
|
|
@@ -110,7 +178,7 @@ params = [
|
|
|
110
178
|
{'title': 'Actuator type:', 'name': 'move_type', 'type': 'str', 'value': '', 'readonly': True},
|
|
111
179
|
{'title': 'Actuator name:', 'name': 'module_name', 'type': 'str', 'value': '', 'readonly': True},
|
|
112
180
|
{'title': 'Plugin Config:', 'name': 'plugin_config', 'type': 'bool_push', 'label': 'Show Config', },
|
|
113
|
-
|
|
181
|
+
|
|
114
182
|
{'title': 'Refresh value (ms):', 'name': 'refresh_timeout', 'type': 'int',
|
|
115
183
|
'value': config('actuator', 'refresh_timeout_ms')},
|
|
116
184
|
{'title': 'TCP/IP options:', 'name': 'tcpip', 'type': 'group', 'visible': True, 'expanded': False,
|
|
@@ -147,13 +215,13 @@ def main(plugin_file, init=True, title='test'):
|
|
|
147
215
|
from qtpy import QtWidgets
|
|
148
216
|
from pymodaq.control_modules.daq_move import DAQ_Move
|
|
149
217
|
from pathlib import Path
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
218
|
+
|
|
219
|
+
act = Path(plugin_file).stem.split('daq_move_')[1]
|
|
220
|
+
|
|
221
|
+
app = mkQApp("PyMoDAQ Viewer")
|
|
154
222
|
|
|
155
223
|
widget = QtWidgets.QWidget()
|
|
156
|
-
prog = DAQ_Move(widget, title=title,)
|
|
224
|
+
prog = DAQ_Move(widget, title=title, actuator=act)
|
|
157
225
|
widget.show()
|
|
158
226
|
prog.actuator = Path(plugin_file).stem[9:]
|
|
159
227
|
if init:
|
|
@@ -199,11 +267,17 @@ class DAQ_Move_base(QObject):
|
|
|
199
267
|
|
|
200
268
|
move_done_signal = Signal(DataActuator)
|
|
201
269
|
is_multiaxes = False
|
|
202
|
-
stage_names = []
|
|
270
|
+
stage_names = [] # deprecated
|
|
271
|
+
|
|
272
|
+
_axis_names: Union[list, Dict[str, int]] = None
|
|
273
|
+
_controller_units: Union[str, List[str], Dict[str, int]] = ''
|
|
274
|
+
_epsilons: Union[float, List[float], Dict[str, float]] = None
|
|
275
|
+
_epsilon = 1.0 # deprecated
|
|
276
|
+
|
|
277
|
+
|
|
203
278
|
params = []
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
data_actuator_type = DataActuatorType['float']
|
|
279
|
+
|
|
280
|
+
data_actuator_type = DataActuatorType.float
|
|
207
281
|
data_shape = (1, ) # expected shape of the underlying actuator's value (in general a float so shape = (1, ))
|
|
208
282
|
|
|
209
283
|
def __init__(self, parent: Optional['DAQ_Move_Hardware'] = None,
|
|
@@ -212,6 +286,7 @@ class DAQ_Move_base(QObject):
|
|
|
212
286
|
self.move_is_done = False
|
|
213
287
|
self.parent = parent
|
|
214
288
|
self.stage = None
|
|
289
|
+
self.controller = None
|
|
215
290
|
self.status = edict(info="", controller=None, stage=None, initialized=False)
|
|
216
291
|
|
|
217
292
|
self._ispolling = True
|
|
@@ -225,17 +300,25 @@ class DAQ_Move_base(QObject):
|
|
|
225
300
|
self.settings.restoreState(params_state.saveState())
|
|
226
301
|
|
|
227
302
|
self.settings.sigTreeStateChanged.connect(self.send_param_status)
|
|
228
|
-
|
|
229
|
-
# 'axis').sigLimitsChanged.connect(lambda param,
|
|
230
|
-
# limits: self.send_param_status(
|
|
231
|
-
# param, [(param, 'limits', None)]))
|
|
303
|
+
|
|
232
304
|
if parent is not None:
|
|
233
305
|
self._title = parent.title
|
|
234
306
|
else:
|
|
235
307
|
self._title = "myactuator"
|
|
236
|
-
|
|
237
|
-
self.
|
|
238
|
-
self.
|
|
308
|
+
|
|
309
|
+
self._axis_units: Union[Dict[str, str], List[str]] = None
|
|
310
|
+
self.axis_units = self._controller_units
|
|
311
|
+
if self._epsilons is None:
|
|
312
|
+
self._epsilons = self._epsilon
|
|
313
|
+
self.epsilons = self._epsilons
|
|
314
|
+
self.axis_name = self.axis_name # to trigger some actions on units and epsilons
|
|
315
|
+
|
|
316
|
+
self._current_value = DataActuator(self._title,
|
|
317
|
+
data=[np.zeros(self.data_shape, dtype=float)],
|
|
318
|
+
units=self.axis_unit)
|
|
319
|
+
self._target_value = DataActuator(self._title,
|
|
320
|
+
data=[np.zeros(self.data_shape, dtype=float)],
|
|
321
|
+
units=self.axis_unit)
|
|
239
322
|
|
|
240
323
|
self.poll_timer = QTimer()
|
|
241
324
|
self.poll_timer.setInterval(config('actuator', 'polling_interval_ms'))
|
|
@@ -245,7 +328,108 @@ class DAQ_Move_base(QObject):
|
|
|
245
328
|
self.ini_attributes()
|
|
246
329
|
|
|
247
330
|
@property
|
|
248
|
-
def
|
|
331
|
+
def axis_unit(self) -> str:
|
|
332
|
+
""" Get/set the unit of the currently chosen axis
|
|
333
|
+
|
|
334
|
+
Will update the printed controller unit in the UI
|
|
335
|
+
|
|
336
|
+
New in 4.4.0
|
|
337
|
+
"""
|
|
338
|
+
return self.axis_units[self.axis_index_key]
|
|
339
|
+
|
|
340
|
+
@axis_unit.setter
|
|
341
|
+
def axis_unit(self, unit: str):
|
|
342
|
+
self.axis_units[self.axis_index_key] = unit
|
|
343
|
+
self.settings.child('units').setValue(unit)
|
|
344
|
+
self.emit_status(ThreadCommand('units', unit))
|
|
345
|
+
|
|
346
|
+
@property
|
|
347
|
+
def axis_units(self) -> Union[List[str], Dict[str, str]]:
|
|
348
|
+
""" Get/Set the units for each axis of the controller
|
|
349
|
+
|
|
350
|
+
New in 4.4.0
|
|
351
|
+
"""
|
|
352
|
+
return self._axis_units
|
|
353
|
+
|
|
354
|
+
@axis_units.setter
|
|
355
|
+
def axis_units(self, units: Union[str, List[str], Dict[str, str]]):
|
|
356
|
+
if isinstance(units, str):
|
|
357
|
+
if isinstance(self.axis_names, list):
|
|
358
|
+
units_tmp = [units for _ in range(len(self.axis_names))]
|
|
359
|
+
else:
|
|
360
|
+
units_tmp = {}
|
|
361
|
+
for key in self.axis_names:
|
|
362
|
+
units_tmp[key] = units
|
|
363
|
+
else:
|
|
364
|
+
if not isinstance(units, type(self.axis_names)):
|
|
365
|
+
raise TypeError('units should be defined just like axis_names: a str, list of string or'
|
|
366
|
+
'dict of string')
|
|
367
|
+
if len(units) != len(self.axis_names):
|
|
368
|
+
raise ValueError('Units should be defined either as a single str or a list/dict with'
|
|
369
|
+
'a str defined for each axis')
|
|
370
|
+
units_tmp = units
|
|
371
|
+
self._axis_units = units_tmp
|
|
372
|
+
|
|
373
|
+
@property
|
|
374
|
+
def epsilon(self) -> float:
|
|
375
|
+
""" Get/Set the epsilon of the currently chosen axis
|
|
376
|
+
|
|
377
|
+
New in 4.4.0
|
|
378
|
+
"""
|
|
379
|
+
return self.epsilons[self.axis_index_key]
|
|
380
|
+
|
|
381
|
+
@epsilon.setter
|
|
382
|
+
def epsilon(self, eps: float):
|
|
383
|
+
self.epsilons[self.axis_index_key] = eps
|
|
384
|
+
|
|
385
|
+
@property
|
|
386
|
+
def epsilons(self) -> Union[List[float], Dict[str, float]]:
|
|
387
|
+
""" Get/Set the epsilon for each axis of the controller
|
|
388
|
+
|
|
389
|
+
New in 4.4.0
|
|
390
|
+
"""
|
|
391
|
+
return self._epsilons
|
|
392
|
+
|
|
393
|
+
@epsilons.setter
|
|
394
|
+
def epsilons(self, epsilons: Union[float, List[float], Dict[str, float]]):
|
|
395
|
+
if isinstance(epsilons, numbers.Number):
|
|
396
|
+
if isinstance(self.axis_names, list):
|
|
397
|
+
epsilons_tmp = [epsilons for _ in range(len(self.axis_names))]
|
|
398
|
+
else:
|
|
399
|
+
epsilons_tmp = {}
|
|
400
|
+
for key in self.axis_names:
|
|
401
|
+
epsilons_tmp[key] = epsilons
|
|
402
|
+
else:
|
|
403
|
+
if not isinstance(epsilons, type(self.axis_names)):
|
|
404
|
+
raise TypeError('units should be defined just like axis_names: a float, list of '
|
|
405
|
+
'float or dict of float')
|
|
406
|
+
if len(epsilons) != len(self.axis_names):
|
|
407
|
+
raise ValueError('epsilons should be defined either as a single float or a '
|
|
408
|
+
'list/dict with'
|
|
409
|
+
'a float defined for each axis')
|
|
410
|
+
epsilons_tmp = epsilons
|
|
411
|
+
self._epsilons = epsilons_tmp
|
|
412
|
+
|
|
413
|
+
@property
|
|
414
|
+
def controller_units(self):
|
|
415
|
+
""" Get/Set the units of the currently chosen axis of the controller
|
|
416
|
+
|
|
417
|
+
Deprecated with pymodaq >= 4.4.0
|
|
418
|
+
|
|
419
|
+
The property controller_units is deprecated please use the axis_unit property
|
|
420
|
+
"""
|
|
421
|
+
deprecation_msg('The property controller_units is deprecated please use the'
|
|
422
|
+
'axis_unit property.')
|
|
423
|
+
return self.axis_unit
|
|
424
|
+
|
|
425
|
+
@controller_units.setter
|
|
426
|
+
def controller_units(self, units: str = ''):
|
|
427
|
+
deprecation_msg('The property controller_units is deprecated please use the'
|
|
428
|
+
'axis_unit property.')
|
|
429
|
+
self._axis_units[self.axis_index_key] = units
|
|
430
|
+
|
|
431
|
+
@property
|
|
432
|
+
def axis_name(self) -> Union[str]:
|
|
249
433
|
"""Get/Set the current axis using its string identifier"""
|
|
250
434
|
limits = self.settings.child('multiaxes', 'axis').opts['limits']
|
|
251
435
|
if isinstance(limits, list):
|
|
@@ -262,6 +446,10 @@ class DAQ_Move_base(QObject):
|
|
|
262
446
|
elif isinstance(limits, dict):
|
|
263
447
|
self.settings.child('multiaxes', 'axis').setValue(limits[name])
|
|
264
448
|
QtWidgets.QApplication.processEvents()
|
|
449
|
+
self.axis_unit = self.axis_unit
|
|
450
|
+
self.settings.child('epsilon').setValue(self.epsilon)
|
|
451
|
+
if self.controller is not None:
|
|
452
|
+
self._current_value = self.get_actuator_value()
|
|
265
453
|
|
|
266
454
|
@property
|
|
267
455
|
def axis_names(self) -> Union[List, Dict]:
|
|
@@ -279,15 +467,35 @@ class DAQ_Move_base(QObject):
|
|
|
279
467
|
QtWidgets.QApplication.processEvents()
|
|
280
468
|
|
|
281
469
|
@property
|
|
282
|
-
def axis_value(self) ->
|
|
283
|
-
"""Get the current value selected from the current axis
|
|
284
|
-
|
|
470
|
+
def axis_value(self) -> int:
|
|
471
|
+
"""Get the current value selected from the current axis
|
|
472
|
+
|
|
473
|
+
In case axis_names is a list, return the element of the list: self.axis_name
|
|
474
|
+
In case axis_names is a dict, return the value of the dict self.axis_names[self.axis_name]
|
|
475
|
+
"""
|
|
476
|
+
if isinstance(self.axis_names, list):
|
|
477
|
+
return self.axis_name
|
|
478
|
+
else:
|
|
479
|
+
return self.axis_names[self.axis_name]
|
|
480
|
+
|
|
481
|
+
@property
|
|
482
|
+
def axis_index_key(self) -> Union[int, str]:
|
|
483
|
+
""" Get the current index or key correspondingto the current axis
|
|
484
|
+
|
|
485
|
+
In case axis_names is a list, return the index wihtin the list
|
|
486
|
+
In case axis_names is a dict, return the key of the dict self.axis_name
|
|
487
|
+
|
|
488
|
+
"""
|
|
489
|
+
if isinstance(self.axis_names, list):
|
|
490
|
+
return self.axis_names.index(self.axis_name)
|
|
491
|
+
else:
|
|
492
|
+
return self.axis_name
|
|
285
493
|
|
|
286
494
|
def ini_attributes(self):
|
|
287
495
|
""" To be subclassed, in order to init specific attributes needed by the real implementation"""
|
|
288
496
|
self.controller = None
|
|
289
497
|
|
|
290
|
-
def ini_stage_init(self, old_controller=None, new_controller=None):
|
|
498
|
+
def ini_stage_init(self, old_controller=None, new_controller=None, slave_controller=None):
|
|
291
499
|
"""Manage the Master/Slave controller issue
|
|
292
500
|
|
|
293
501
|
First initialize the status dictionary
|
|
@@ -305,10 +513,14 @@ class DAQ_Move_base(QObject):
|
|
|
305
513
|
The particular object that allow the communication with the hardware, in general a python wrapper around the
|
|
306
514
|
hardware library. In case of Master it is the new instance of your plugin controller
|
|
307
515
|
"""
|
|
516
|
+
if old_controller is None and slave_controller is not None:
|
|
517
|
+
old_controller = slave_controller
|
|
518
|
+
|
|
308
519
|
self.status.update(edict(info="", controller=None, initialized=False))
|
|
309
|
-
if
|
|
520
|
+
if not self.is_master:
|
|
310
521
|
if old_controller is None:
|
|
311
|
-
raise Exception('no controller has been defined externally while this axe
|
|
522
|
+
raise Exception('no controller has been defined externally while this axe '
|
|
523
|
+
'is a slave one')
|
|
312
524
|
else:
|
|
313
525
|
controller = old_controller
|
|
314
526
|
else: # Master stage
|
|
@@ -318,30 +530,40 @@ class DAQ_Move_base(QObject):
|
|
|
318
530
|
|
|
319
531
|
@property
|
|
320
532
|
def current_value(self):
|
|
321
|
-
if self.data_actuator_type
|
|
533
|
+
if self.data_actuator_type == self.data_actuator_type.float:
|
|
322
534
|
return self._current_value.value()
|
|
323
535
|
else:
|
|
324
536
|
return self._current_value
|
|
325
537
|
|
|
326
538
|
@current_value.setter
|
|
327
|
-
def current_value(self, value: Union[float, DataActuator]):
|
|
328
|
-
if
|
|
329
|
-
self._current_value = DataActuator(self._title, data=value
|
|
539
|
+
def current_value(self, value: Union[float, np.ndarray, DataActuator]):
|
|
540
|
+
if isinstance(value, numbers.Number) or isinstance(value, np.ndarray):
|
|
541
|
+
self._current_value = DataActuator(self._title, data=value,
|
|
542
|
+
units=self.axis_unit)
|
|
330
543
|
else:
|
|
544
|
+
if (not Unit(self.axis_unit).is_compatible_with(
|
|
545
|
+
Unit(value.units)) and
|
|
546
|
+
value.units == ''):
|
|
547
|
+
value.force_units(self.axis_unit)
|
|
331
548
|
self._current_value = value
|
|
332
549
|
|
|
333
550
|
@property
|
|
334
551
|
def target_value(self):
|
|
335
|
-
if self.data_actuator_type.name ==
|
|
552
|
+
if self.data_actuator_type.name == self.data_actuator_type.float:
|
|
336
553
|
return self._target_value.value()
|
|
337
554
|
else:
|
|
338
555
|
return self._target_value
|
|
339
556
|
|
|
340
557
|
@target_value.setter
|
|
341
|
-
def target_value(self, value: Union[
|
|
342
|
-
if
|
|
343
|
-
self._target_value = DataActuator(self._title, data=value
|
|
558
|
+
def target_value(self, value: Union[numbers.Number, DataActuator]):
|
|
559
|
+
if isinstance(value, numbers.Number):
|
|
560
|
+
self._target_value = DataActuator(self._title, data=value,
|
|
561
|
+
units=self.axis_unit)
|
|
344
562
|
else:
|
|
563
|
+
if (not Unit(self.axis_unit).is_compatible_with(
|
|
564
|
+
Unit(value.units)) and
|
|
565
|
+
value.units == ''):
|
|
566
|
+
value.force_units(self.axis_unit)
|
|
345
567
|
self._target_value = value
|
|
346
568
|
|
|
347
569
|
@property
|
|
@@ -370,20 +592,6 @@ class DAQ_Move_base(QObject):
|
|
|
370
592
|
"""
|
|
371
593
|
return self.settings['multiaxes', 'multi_status'] == 'Master'
|
|
372
594
|
|
|
373
|
-
@property
|
|
374
|
-
def controller_units(self):
|
|
375
|
-
""" Get/Set the units of this plugin"""
|
|
376
|
-
return self._controller_units
|
|
377
|
-
|
|
378
|
-
@controller_units.setter
|
|
379
|
-
def controller_units(self, units: str = ''):
|
|
380
|
-
self._controller_units = units
|
|
381
|
-
try:
|
|
382
|
-
self.settings.child('units').setValue(units)
|
|
383
|
-
self.emit_status(ThreadCommand('units', units))
|
|
384
|
-
except Exception:
|
|
385
|
-
pass
|
|
386
|
-
|
|
387
595
|
@property
|
|
388
596
|
def ispolling(self):
|
|
389
597
|
""" Get/Set the polling status"""
|
|
@@ -398,13 +606,11 @@ class DAQ_Move_base(QObject):
|
|
|
398
606
|
|
|
399
607
|
Return the new position eventually coerced within the bounds
|
|
400
608
|
"""
|
|
401
|
-
if self.settings
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
self.
|
|
405
|
-
|
|
406
|
-
position = DataActuator(self._title, data=self.settings.child('bounds', 'min_bound').value())
|
|
407
|
-
self.emit_status(ThreadCommand('outofbounds', []))
|
|
609
|
+
if self.settings['bounds', 'is_bounds']:
|
|
610
|
+
for data_array in position:
|
|
611
|
+
data_array[data_array > self.settings['bounds', 'max_bound']] = self.settings['bounds', 'max_bound']
|
|
612
|
+
data_array[data_array < self.settings['bounds', 'min_bound']] = self.settings['bounds', 'min_bound']
|
|
613
|
+
self.emit_status(ThreadCommand('outofbounds', []))
|
|
408
614
|
return position
|
|
409
615
|
|
|
410
616
|
def get_actuator_value(self):
|
|
@@ -414,6 +620,10 @@ class DAQ_Move_base(QObject):
|
|
|
414
620
|
else:
|
|
415
621
|
raise NotImplementedError
|
|
416
622
|
|
|
623
|
+
|
|
624
|
+
def close(self):
|
|
625
|
+
raise NotImplementedError
|
|
626
|
+
|
|
417
627
|
def move_abs(self, value: Union[float, DataActuator]):
|
|
418
628
|
if hasattr(self, 'move_Abs'):
|
|
419
629
|
deprecation_msg('move_Abs method in plugins is deprecated, use move_abs', 3)
|
|
@@ -470,12 +680,14 @@ class DAQ_Move_base(QObject):
|
|
|
470
680
|
"""
|
|
471
681
|
if position is None:
|
|
472
682
|
if self.data_actuator_type.name == 'float':
|
|
473
|
-
position = DataActuator(self._title, data=self.get_actuator_value()
|
|
683
|
+
position = DataActuator(self._title, data=self.get_actuator_value(),
|
|
684
|
+
units=self.axis_unit)
|
|
474
685
|
else:
|
|
475
686
|
position = self.get_actuator_value()
|
|
476
687
|
if position.name != self._title: # make sure the emitted DataActuator has the name of the real implementation
|
|
477
688
|
#of the plugin
|
|
478
|
-
position = DataActuator(self._title, data=position.value()
|
|
689
|
+
position = DataActuator(self._title, data=position.value(),
|
|
690
|
+
units=self.axis_unit)
|
|
479
691
|
self.move_done_signal.emit(position)
|
|
480
692
|
self.move_is_done = True
|
|
481
693
|
|
|
@@ -491,31 +703,74 @@ class DAQ_Move_base(QObject):
|
|
|
491
703
|
if self.ispolling:
|
|
492
704
|
self.poll_timer.start()
|
|
493
705
|
else:
|
|
494
|
-
if self.data_actuator_type
|
|
495
|
-
self._current_value = DataActuator(data=self.get_actuator_value()
|
|
706
|
+
if self.data_actuator_type == DataActuatorType.float:
|
|
707
|
+
self._current_value = DataActuator(data=self.get_actuator_value(),
|
|
708
|
+
units=self.axis_unit)
|
|
496
709
|
else:
|
|
497
710
|
self._current_value = self.get_actuator_value()
|
|
711
|
+
if (not Unit(self.axis_unit).is_compatible_with(
|
|
712
|
+
Unit(self._current_value.units)) and
|
|
713
|
+
self._current_value.units == ''):
|
|
714
|
+
# this happens if the units have not been specified in
|
|
715
|
+
# the plugin
|
|
716
|
+
self._current_value.force_units(self.axis_unit)
|
|
717
|
+
|
|
498
718
|
logger.debug(f'Current position: {self._current_value}')
|
|
499
719
|
self.move_done(self._current_value)
|
|
500
720
|
|
|
501
|
-
def
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
721
|
+
def _condition_to_reach_target(self) -> bool:
|
|
722
|
+
""" Implement the condition for exiting the polling mechanism and specifying that the
|
|
723
|
+
target value has been reached
|
|
724
|
+
|
|
725
|
+
Returns
|
|
726
|
+
-------
|
|
727
|
+
bool: if True, PyMoDAQ considers the target value has been reached at epsilon
|
|
728
|
+
|
|
729
|
+
See Also
|
|
730
|
+
--------
|
|
731
|
+
user_condition_to_reach_target
|
|
732
|
+
"""
|
|
733
|
+
try:
|
|
734
|
+
epsilon_calculated = (
|
|
735
|
+
self._current_value - self._target_value).abs().value(self.axis_unit)
|
|
736
|
+
except DataUnitError as e:
|
|
737
|
+
epsilon_calculated = abs(self._current_value.value() - self._target_value.value())
|
|
738
|
+
logger.warning(f'Unit issue when calculating epsilon, units are not compatible between'
|
|
739
|
+
f'target and current values')
|
|
740
|
+
except OffsetUnitCalculusError as e:
|
|
741
|
+
if '°C' in self.axis_unit or 'celcius' in self.axis_unit.lower():
|
|
742
|
+
epsilon_calculated = (
|
|
743
|
+
self._current_value.to_base_units() -
|
|
744
|
+
self._target_value.to_base_units()).abs().value()
|
|
745
|
+
else:
|
|
746
|
+
raise e
|
|
506
747
|
|
|
507
|
-
|
|
748
|
+
return (epsilon_calculated < self.epsilon) and self.user_condition_to_reach_target()
|
|
749
|
+
|
|
750
|
+
def user_condition_to_reach_target(self) -> bool:
|
|
751
|
+
""" Implement a user defined condition for exiting the polling mechanism and specifying
|
|
752
|
+
that the target value has been reached (on top of the existing epsilon mechanism)
|
|
753
|
+
|
|
754
|
+
Should be reimplemented in plugins to implement other conditions
|
|
755
|
+
|
|
756
|
+
Returns
|
|
757
|
+
-------
|
|
758
|
+
bool: if True, PyMoDAQ considers the target value has been reached
|
|
759
|
+
"""
|
|
760
|
+
return True
|
|
761
|
+
|
|
762
|
+
def check_target_reached(self):
|
|
763
|
+
logger.debug(f"epsilon value is {self.epsilon}")
|
|
508
764
|
logger.debug(f"current_value value is {self._current_value}")
|
|
509
765
|
logger.debug(f"target_value value is {self._target_value}")
|
|
510
766
|
|
|
511
|
-
if not
|
|
767
|
+
if not self._condition_to_reach_target():
|
|
512
768
|
|
|
513
769
|
logger.debug(f'Check move_is_done: {self.move_is_done}')
|
|
514
770
|
if self.move_is_done:
|
|
515
771
|
self.emit_status(ThreadCommand('Move has been stopped', ))
|
|
516
772
|
logger.info('Move has been stopped')
|
|
517
773
|
self.current_value = self.get_actuator_value()
|
|
518
|
-
|
|
519
774
|
self.emit_value(self._current_value)
|
|
520
775
|
logger.debug(f'Current value: {self._current_value}')
|
|
521
776
|
|
|
@@ -525,6 +780,7 @@ class DAQ_Move_base(QObject):
|
|
|
525
780
|
logger.info('Timeout activated')
|
|
526
781
|
else:
|
|
527
782
|
self.poll_timer.stop()
|
|
783
|
+
self.current_value = self.get_actuator_value()
|
|
528
784
|
logger.debug(f'Current value: {self._current_value}')
|
|
529
785
|
self.move_done(self._current_value)
|
|
530
786
|
|
|
@@ -605,6 +861,11 @@ class DAQ_Move_base(QObject):
|
|
|
605
861
|
self.commit_common_settings(param)
|
|
606
862
|
self.commit_settings(param)
|
|
607
863
|
|
|
864
|
+
if param.name() == 'axis':
|
|
865
|
+
self.axis_name = param.value()
|
|
866
|
+
elif param.name() == 'epsilon':
|
|
867
|
+
self.epsilon = param.value()
|
|
868
|
+
|
|
608
869
|
|
|
609
870
|
class DAQ_Move_TCP_server(DAQ_Move_base, TCPServer):
|
|
610
871
|
"""
|
|
@@ -622,12 +883,12 @@ class DAQ_Move_TCP_server(DAQ_Move_base, TCPServer):
|
|
|
622
883
|
"""
|
|
623
884
|
params_client = [] # parameters of a client grabber
|
|
624
885
|
command_server = Signal(list)
|
|
625
|
-
data_actuator_type = DataActuatorType
|
|
886
|
+
data_actuator_type = DataActuatorType.DataActuator
|
|
626
887
|
|
|
627
888
|
message_list = ["Quit", "Status", "Done", "Server Closed", "Info", "Infos", "Info_xml", "move_abs",
|
|
628
889
|
'move_home', 'move_rel', 'get_actuator_value', 'stop_motion', 'position_is', 'move_done']
|
|
629
890
|
socket_types = ["ACTUATOR"]
|
|
630
|
-
params =
|
|
891
|
+
params = comon_parameters_fun() + tcp_parameters
|
|
631
892
|
|
|
632
893
|
def __init__(self, parent=None, params_state=None):
|
|
633
894
|
"""
|
|
@@ -654,13 +915,13 @@ class DAQ_Move_TCP_server(DAQ_Move_base, TCPServer):
|
|
|
654
915
|
|
|
655
916
|
pos = self.get_position_with_scaling(pos)
|
|
656
917
|
self._current_value = pos
|
|
657
|
-
self.emit_status(ThreadCommand('get_actuator_value',
|
|
918
|
+
self.emit_status(ThreadCommand('get_actuator_value', pos))
|
|
658
919
|
|
|
659
920
|
elif command == 'move_done':
|
|
660
921
|
pos = DeSerializer(sock).dwa_deserialization()
|
|
661
922
|
pos = self.get_position_with_scaling(pos)
|
|
662
923
|
self._current_value = pos
|
|
663
|
-
self.emit_status(ThreadCommand('move_done',
|
|
924
|
+
self.emit_status(ThreadCommand('move_done', pos))
|
|
664
925
|
else:
|
|
665
926
|
self.send_command(sock, command)
|
|
666
927
|
|
|
@@ -699,6 +960,12 @@ class DAQ_Move_TCP_server(DAQ_Move_base, TCPServer):
|
|
|
699
960
|
initialized = True
|
|
700
961
|
return info, initialized
|
|
701
962
|
|
|
963
|
+
def read_infos(self, sock: Socket = None, infos=''):
|
|
964
|
+
"""Reimplemented to get the units"""
|
|
965
|
+
super().read_infos(sock, infos)
|
|
966
|
+
|
|
967
|
+
self.axis_unit = self.settings['settings_client', 'units']
|
|
968
|
+
|
|
702
969
|
def close(self):
|
|
703
970
|
"""
|
|
704
971
|
Should be used to uninitialize hardware.
|