pymodaq 5.0.17__py3-none-any.whl → 5.1.0__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.

Files changed (92) hide show
  1. pymodaq/__init__.py +23 -11
  2. pymodaq/control_modules/__init__.py +1 -0
  3. pymodaq/control_modules/daq_move.py +458 -246
  4. pymodaq/control_modules/daq_move_ui/__init__.py +0 -0
  5. pymodaq/control_modules/daq_move_ui/factory.py +48 -0
  6. pymodaq/control_modules/{daq_move_ui.py → daq_move_ui/ui_base.py} +168 -210
  7. pymodaq/control_modules/daq_move_ui/uis/__init__.py +0 -0
  8. pymodaq/control_modules/daq_move_ui/uis/binary.py +139 -0
  9. pymodaq/control_modules/daq_move_ui/uis/original.py +120 -0
  10. pymodaq/control_modules/daq_move_ui/uis/relative.py +124 -0
  11. pymodaq/control_modules/daq_move_ui/uis/simple.py +126 -0
  12. pymodaq/control_modules/daq_viewer.py +113 -101
  13. pymodaq/control_modules/daq_viewer_ui.py +41 -31
  14. pymodaq/control_modules/mocks.py +2 -2
  15. pymodaq/control_modules/move_utility_classes.py +113 -41
  16. pymodaq/control_modules/thread_commands.py +137 -0
  17. pymodaq/control_modules/ui_utils.py +72 -0
  18. pymodaq/control_modules/utils.py +107 -63
  19. pymodaq/control_modules/viewer_utility_classes.py +13 -17
  20. pymodaq/dashboard.py +1294 -625
  21. pymodaq/examples/qt_less_standalone_module.py +48 -11
  22. pymodaq/extensions/__init__.py +8 -3
  23. pymodaq/extensions/adaptive/__init__.py +2 -0
  24. pymodaq/extensions/adaptive/adaptive_optimization.py +179 -0
  25. pymodaq/extensions/adaptive/loss_function/_1d_loss_functions.py +73 -0
  26. pymodaq/extensions/adaptive/loss_function/_2d_loss_functions.py +73 -0
  27. pymodaq/extensions/adaptive/loss_function/__init__.py +3 -0
  28. pymodaq/extensions/adaptive/loss_function/loss_factory.py +110 -0
  29. pymodaq/extensions/adaptive/utils.py +123 -0
  30. pymodaq/extensions/bayesian/__init__.py +1 -1
  31. pymodaq/extensions/bayesian/acquisition/__init__.py +2 -0
  32. pymodaq/extensions/bayesian/acquisition/acquisition_function_factory.py +80 -0
  33. pymodaq/extensions/bayesian/acquisition/base_acquisition_function.py +105 -0
  34. pymodaq/extensions/bayesian/bayesian_optimization.py +143 -0
  35. pymodaq/extensions/bayesian/utils.py +71 -297
  36. pymodaq/extensions/daq_logger/daq_logger.py +7 -12
  37. pymodaq/extensions/daq_logger/h5logging.py +1 -1
  38. pymodaq/extensions/daq_scan.py +30 -55
  39. pymodaq/extensions/data_mixer/__init__.py +0 -0
  40. pymodaq/extensions/data_mixer/daq_0Dviewer_DataMixer.py +97 -0
  41. pymodaq/extensions/data_mixer/data_mixer.py +262 -0
  42. pymodaq/extensions/data_mixer/model.py +108 -0
  43. pymodaq/extensions/data_mixer/models/__init__.py +0 -0
  44. pymodaq/extensions/data_mixer/models/equation_model.py +91 -0
  45. pymodaq/extensions/data_mixer/models/gaussian_fit_model.py +65 -0
  46. pymodaq/extensions/data_mixer/parser.py +53 -0
  47. pymodaq/extensions/data_mixer/utils.py +23 -0
  48. pymodaq/extensions/h5browser.py +3 -34
  49. pymodaq/extensions/optimizers_base/__init__.py +0 -0
  50. pymodaq/extensions/optimizers_base/optimizer.py +1016 -0
  51. pymodaq/extensions/optimizers_base/thread_commands.py +22 -0
  52. pymodaq/extensions/optimizers_base/utils.py +427 -0
  53. pymodaq/extensions/pid/actuator_controller.py +3 -2
  54. pymodaq/extensions/pid/daq_move_PID.py +107 -30
  55. pymodaq/extensions/pid/pid_controller.py +613 -287
  56. pymodaq/extensions/pid/utils.py +8 -5
  57. pymodaq/extensions/utils.py +17 -2
  58. pymodaq/resources/config_template.toml +57 -0
  59. pymodaq/resources/preset_default.xml +1 -1
  60. pymodaq/utils/config.py +13 -4
  61. pymodaq/utils/daq_utils.py +14 -0
  62. pymodaq/utils/data.py +1 -0
  63. pymodaq/utils/gui_utils/loader_utils.py +25 -15
  64. pymodaq/utils/h5modules/module_saving.py +134 -22
  65. pymodaq/utils/leco/daq_move_LECODirector.py +123 -84
  66. pymodaq/utils/leco/daq_xDviewer_LECODirector.py +84 -97
  67. pymodaq/utils/leco/director_utils.py +32 -16
  68. pymodaq/utils/leco/leco_director.py +104 -27
  69. pymodaq/utils/leco/pymodaq_listener.py +186 -97
  70. pymodaq/utils/leco/rpc_method_definitions.py +43 -0
  71. pymodaq/utils/leco/utils.py +25 -25
  72. pymodaq/utils/managers/batchscan_manager.py +12 -11
  73. pymodaq/utils/managers/modules_manager.py +74 -33
  74. pymodaq/utils/managers/overshoot_manager.py +11 -10
  75. pymodaq/utils/managers/preset_manager.py +100 -64
  76. pymodaq/utils/managers/preset_manager_utils.py +163 -107
  77. pymodaq/utils/managers/remote_manager.py +21 -16
  78. pymodaq/utils/scanner/scan_factory.py +18 -4
  79. pymodaq/utils/scanner/scan_selector.py +1 -3
  80. pymodaq/utils/scanner/scanner.py +35 -6
  81. pymodaq/utils/scanner/scanners/_1d_scanners.py +15 -46
  82. pymodaq/utils/scanner/scanners/_2d_scanners.py +21 -68
  83. pymodaq/utils/scanner/scanners/sequential.py +50 -31
  84. pymodaq/utils/scanner/scanners/tabular.py +45 -28
  85. {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/METADATA +7 -6
  86. pymodaq-5.1.0.dist-info/RECORD +154 -0
  87. {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/entry_points.txt +0 -2
  88. pymodaq/extensions/bayesian/bayesian_optimisation.py +0 -685
  89. pymodaq/utils/leco/desktop.ini +0 -2
  90. pymodaq-5.0.17.dist-info/RECORD +0 -121
  91. {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/WHEEL +0 -0
  92. {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -6,19 +6,21 @@ Created the 23/11/2022
6
6
  """
7
7
  from __future__ import annotations
8
8
 
9
- from typing import Union, List, Dict, Tuple, TYPE_CHECKING
9
+ from typing import Union, List, Dict, Tuple, TYPE_CHECKING, Iterable
10
10
  import xml.etree.ElementTree as ET
11
11
 
12
12
 
13
13
  import numpy as np
14
14
  from pymodaq_utils.logger import set_logger, get_module_name
15
+ from pymodaq_utils.enums import BaseEnum
15
16
  from pymodaq_utils.abstract import ABCMeta, abstract_attribute, abstractmethod
16
17
  from pymodaq_utils.utils import capitalize
17
18
  from pymodaq_data.data import Axis, DataDim, DataWithAxes, DataToExport, DataDistribution
18
19
  from pymodaq_data.h5modules.saving import H5SaverLowLevel
19
20
  from pymodaq_data.h5modules.backends import GROUP, CARRAY, Node, GroupType
20
- from pymodaq_data.h5modules.data_saving import (DataToExportSaver, AxisSaverLoader,
21
- DataToExportTimedSaver, DataToExportExtendedSaver)
21
+ from pymodaq_data.h5modules.data_saving import (
22
+ DataToExportSaver, AxisSaverLoader, DataToExportEnlargeableSaver,
23
+ DataToExportTimedSaver, DataToExportExtendedSaver)
22
24
  from pymodaq_gui.parameter import ioxml
23
25
 
24
26
  if TYPE_CHECKING:
@@ -31,9 +33,17 @@ if TYPE_CHECKING:
31
33
  logger = set_logger(get_module_name(__file__))
32
34
 
33
35
 
36
+ class GroupModuleType(BaseEnum):
37
+ DETECTOR = 0
38
+ ACTUATOR = 1
39
+ SCAN = 2
40
+ DATALOGGER = 3
41
+ OPTIMIZER = 4
42
+
43
+
34
44
  class ModuleSaver(metaclass=ABCMeta):
35
45
  """Abstract base class to save info and data from main modules (DAQScan, DAQViewer, DAQMove, ...)"""
36
- group_type: GroupType = abstract_attribute()
46
+ group_type: GroupModuleType = abstract_attribute()
37
47
  _module = abstract_attribute()
38
48
  _h5saver: H5SaverLowLevel = abstract_attribute()
39
49
  _module_group: GROUP = abstract_attribute()
@@ -119,11 +129,11 @@ class ModuleSaver(metaclass=ABCMeta):
119
129
 
120
130
  def get_last_node_index(self, where: Union[Node, str] = None):
121
131
  node = self.get_last_node(where)
122
- return int(node.name.split(capitalize(self.group_type.name))[1])
132
+ return int(node.name.split(capitalize(self.group_type.name.lower()))[1])
123
133
 
124
134
  def get_next_node_name(self, where: Union[Node, str] = None):
125
135
  index = self.get_last_node_index(where)
126
- return f'{capitalize(self.group_type.name)}{index+1:03d}'
136
+ return f'{capitalize(self.group_type.name.lower())}{index+1:03d}'
127
137
 
128
138
 
129
139
  class DetectorSaver(ModuleSaver):
@@ -133,7 +143,7 @@ class DetectorSaver(ModuleSaver):
133
143
  ----------
134
144
  module
135
145
  """
136
- group_type = GroupType['detector']
146
+ group_type = GroupModuleType.DETECTOR
137
147
 
138
148
  def __init__(self, module: DAQ_Viewer):
139
149
  self._datatoexport_saver: DataToExportSaver = None
@@ -176,8 +186,8 @@ class DetectorSaver(ModuleSaver):
176
186
  return self._h5saver.add_det_group(where, title=self._module.title, settings_as_xml=ET.tostring(settings_xml),
177
187
  metadata=metadata)
178
188
 
179
- def add_data(self, where: Union[Node, str], data: DataToExport):
180
- self._datatoexport_saver.add_data(where, data)
189
+ def add_data(self, where: Union[Node, str], data: DataToExport, **kwargs):
190
+ self._datatoexport_saver.add_data(where, data, **kwargs)
181
191
 
182
192
  def add_bkg(self, where: Union[Node, str], data_bkg: DataToExport):
183
193
  """ Adds a DataToExport as a background node in the h5file
@@ -213,14 +223,14 @@ class DetectorSaver(ModuleSaver):
213
223
  self.logger.exception(str(e))
214
224
 
215
225
 
216
- class DetectorEnlargeableSaver(DetectorSaver):
226
+ class DetectorTimeSaver(DetectorSaver):
217
227
  """Implementation of the ModuleSaver class dedicated to DAQ_Viewer modules in order to save enlargeable data
218
228
 
219
229
  Parameters
220
230
  ----------
221
231
  module
222
232
  """
223
- group_type = GroupType['detector']
233
+ group_type = GroupModuleType.DETECTOR
224
234
 
225
235
  def __init__(self, module: DAQ_Viewer):
226
236
  super().__init__(module)
@@ -230,6 +240,28 @@ class DetectorEnlargeableSaver(DetectorSaver):
230
240
  self._datatoexport_saver = DataToExportTimedSaver(self.h5saver)
231
241
 
232
242
 
243
+ class DetectorEnlargeableSaver(DetectorSaver):
244
+ """Implementation of the ModuleSaver class dedicated to DAQ_Viewer modules in order to save enlargeable data
245
+
246
+ Parameters
247
+ ----------
248
+ module
249
+ """
250
+ group_type = GroupModuleType.DETECTOR
251
+
252
+ def __init__(self, module: DAQ_Viewer,
253
+ enl_axis_names: Iterable[str] = None,
254
+ enl_axis_units: Iterable[str] = None,):
255
+ super().__init__(module)
256
+ self.enl_axis_names = enl_axis_names
257
+ self.enl_axis_units = enl_axis_units
258
+ self._datatoexport_saver: DataToExportEnlargeableSaver = None
259
+
260
+ def update_after_h5changed(self):
261
+ self._datatoexport_saver = DataToExportEnlargeableSaver(
262
+ self.h5saver, self.enl_axis_names, self.enl_axis_units)
263
+
264
+
233
265
  class DetectorExtendedSaver(DetectorSaver):
234
266
  """Implementation of the ModuleSaver class dedicated to DAQ_Viewer modules in order to save enlargeable data
235
267
 
@@ -237,7 +269,7 @@ class DetectorExtendedSaver(DetectorSaver):
237
269
  ----------
238
270
  module
239
271
  """
240
- group_type = GroupType['detector']
272
+ group_type = GroupModuleType.DETECTOR
241
273
 
242
274
  def __init__(self, module: DAQ_Viewer, extended_shape: Tuple[int]):
243
275
  super().__init__(module)
@@ -263,18 +295,20 @@ class ActuatorSaver(ModuleSaver):
263
295
  h5saver
264
296
  module
265
297
  """
266
- group_type = GroupType['actuator']
298
+ group_type = GroupModuleType.ACTUATOR
267
299
 
268
300
  def __init__(self, module: DAQ_Move):
269
- self._datatoexport_saver: DataToExportTimedSaver = None
301
+ self._datatoexport_saver: DataToExportSaver = None
270
302
  self._module_group: GROUP = None
271
303
  self._module: DAQ_Move = module
272
304
  self._h5saver = None
273
305
 
274
306
  def update_after_h5changed(self, ):
275
- self._datatoexport_saver = DataToExportTimedSaver(self.h5saver)
307
+ self._datatoexport_saver = DataToExportSaver(self.h5saver)
276
308
 
277
- def _add_module(self, where: Union[Node, str] = None, metadata={}):
309
+ def _add_module(self, where: Union[Node, str] = None, metadata=None):
310
+ if metadata is None:
311
+ metadata={}
278
312
  if where is None:
279
313
  where = self._h5saver.raw_group
280
314
 
@@ -288,6 +322,46 @@ class ActuatorSaver(ModuleSaver):
288
322
  self._datatoexport_saver.add_data(where, data)
289
323
 
290
324
 
325
+ class ActuatorTimeSaver(ActuatorSaver):
326
+ def __init__(self, module: DAQ_Move):
327
+ super().__init__(module)
328
+ self._datatoexport_saver: DataToExportTimedSaver = None
329
+
330
+ def update_after_h5changed(self, ):
331
+ self._datatoexport_saver = DataToExportTimedSaver(self.h5saver)
332
+
333
+ def add_data(self, where: Union[Node, str], data: DataToExport):
334
+ self._datatoexport_saver.add_data(where, data)
335
+
336
+
337
+ class ActuatorEnlargeableSaver(ActuatorTimeSaver):
338
+ """Implementation of the ModuleSaver class dedicated to DAQ_Move modules
339
+
340
+ Parameters
341
+ ----------
342
+ h5saver
343
+ module
344
+ """
345
+
346
+ def __init__(self, module: DAQ_Move,
347
+ enl_axis_names: Iterable[str] = None,
348
+ enl_axis_units: Iterable[str] = None,):
349
+ super().__init__(module)
350
+ self.enl_axis_names = enl_axis_names
351
+ self.enl_axis_units = enl_axis_units
352
+ self._datatoexport_saver: DataToExportEnlargeableSaver = None
353
+
354
+ def update_after_h5changed(self, ):
355
+ self._datatoexport_saver = DataToExportEnlargeableSaver(
356
+ self.h5saver, self.enl_axis_names, self.enl_axis_units)
357
+
358
+
359
+ def add_data(self, where: Union[Node, str], data: DataToExport,
360
+ axis_values: List[Union[float, np.ndarray]] = None,
361
+ **kwargs):
362
+ self._datatoexport_saver.add_data(where, data, axis_values, **kwargs)
363
+
364
+
291
365
  class ScanSaver(ModuleSaver):
292
366
  """Implementation of the ModuleSaver class dedicated to DAQScan module
293
367
 
@@ -296,7 +370,7 @@ class ScanSaver(ModuleSaver):
296
370
  h5saver
297
371
  module
298
372
  """
299
- group_type = GroupType['scan']
373
+ group_type = GroupModuleType.SCAN
300
374
 
301
375
  def __init__(self, module):
302
376
  self._module_group: GROUP = None
@@ -341,7 +415,7 @@ class ScanSaver(ModuleSaver):
341
415
  module.module_and_data_saver.get_set_node(self._module_group)
342
416
  return self._module_group
343
417
 
344
- def _add_module(self, where: Union[Node, str] = None, metadata={}) -> Node:
418
+ def _add_module(self, where: Union[Node, str] = None, metadata=None) -> Node:
345
419
  """
346
420
 
347
421
  Parameters
@@ -354,6 +428,8 @@ class ScanSaver(ModuleSaver):
354
428
  -------
355
429
 
356
430
  """
431
+ if metadata is None:
432
+ metadata = {}
357
433
  if where is None:
358
434
  where = self._h5saver.raw_group
359
435
 
@@ -363,16 +439,17 @@ class ScanSaver(ModuleSaver):
363
439
  saver_xml = ET.SubElement(settings_xml, 'H5Saver', type='group')
364
440
  saver_xml.append(ioxml.walk_parameters_to_xml(param=self._h5saver.settings))
365
441
 
366
- return self._h5saver.add_scan_group(where, title=self._module.title,
442
+ return self._h5saver.add_generic_group(where, title=self._module.title,
367
443
  settings_as_xml=ET.tostring(settings_xml),
368
- metadata=metadata)
444
+ metadata=metadata,
445
+ group_type=self.group_type.name)
369
446
 
370
447
  def add_nav_axes(self, axes: List[Axis]):
371
448
  for detector in self._module.modules_manager.detectors:
372
449
  detector.module_and_data_saver.add_nav_axes(self._module_group, axes)
373
450
 
374
451
  def add_data(self, dte: DataToExport = None, indexes: Tuple[int] = None,
375
- distribution=DataDistribution['uniform']):
452
+ distribution=DataDistribution['uniform'], **kwargs):
376
453
  for detector in self._module.modules_manager.detectors:
377
454
  try:
378
455
  detector.insert_data(indexes, where=self._module_group, distribution=distribution)
@@ -390,7 +467,7 @@ class LoggerSaver(ScanSaver):
390
467
  h5saver
391
468
  module
392
469
  """
393
- group_type = GroupType['data_logger']
470
+ group_type = GroupModuleType.DATALOGGER
394
471
 
395
472
  def add_data(self, dte: DataToExport):
396
473
  """Add data to it's corresponding control module
@@ -407,3 +484,38 @@ class LoggerSaver(ScanSaver):
407
484
  return
408
485
 
409
486
  control_module.append_data(dte=dte, where=self._module_group)
487
+
488
+
489
+ class OptimizerSaver(ScanSaver):
490
+ """Implementation of the ModuleSaver class dedicated to Optimizer based modules
491
+
492
+ Parameters
493
+ ----------
494
+ h5saver
495
+ module
496
+ """
497
+ group_type = GroupModuleType.OPTIMIZER
498
+
499
+ def __init__(self, module,
500
+ enl_axis_names: Iterable[str] = None,
501
+ enl_axis_units: Iterable[str] = None,):
502
+ super().__init__(module)
503
+ self.enl_axis_names = enl_axis_names
504
+ self.enl_axis_units = enl_axis_units
505
+
506
+ def update_after_h5changed(self, ):
507
+ for module in self._module.modules_manager.detectors:
508
+ module.module_and_data_saver = DetectorEnlargeableSaver(
509
+ module, self.enl_axis_names, self.enl_axis_units)
510
+ module.module_and_data_saver.h5saver = self.h5saver
511
+
512
+
513
+ def add_data(self, *args, axis_values: List[Union[float, np.ndarray]] = None,
514
+ **kwargs):
515
+ for module in self._module.modules_manager.detectors:
516
+ try:
517
+ module.append_data(where=self._module_group,
518
+ axis_values=axis_values,
519
+ **kwargs)
520
+ except Exception as e:
521
+ logger.exception(f'Cannot append data: {str(e)}')
@@ -7,17 +7,27 @@ running: `python -m pyleco.coordinators.coordinator`
7
7
 
8
8
  """
9
9
 
10
- from typing import Union
10
+ import numpy as np
11
+
12
+ from typing import Optional, Union
11
13
 
12
14
  from pymodaq.control_modules.move_utility_classes import (DAQ_Move_base, comon_parameters_fun, main,
13
15
  DataActuatorType, DataActuator)
16
+ from pymodaq.control_modules.thread_commands import ThreadStatus, ThreadStatusMove
14
17
 
15
18
  from pymodaq_utils.utils import ThreadCommand
19
+ from pymodaq_utils.utils import find_dict_in_list_from_key_val
20
+ from pymodaq_utils.serialize.factory import SerializableFactory
16
21
  from pymodaq_gui.parameter import Parameter
17
22
 
18
- from pymodaq.utils.leco.leco_director import LECODirector, leco_parameters
23
+ from pymodaq.utils.leco.leco_director import (LECODirector, leco_parameters, DirectorCommands,
24
+ DirectorReceivedCommands)
19
25
  from pymodaq.utils.leco.director_utils import ActuatorDirector
20
- from pymodaq_utils.serialize.serializer_legacy import DeSerializer
26
+
27
+ from pymodaq_utils.logger import set_logger, get_module_name
28
+
29
+ logger = set_logger(get_module_name(__file__))
30
+
21
31
 
22
32
  class DAQ_Move_LECODirector(LECODirector, DAQ_Move_base):
23
33
  """A control module, which in the dashboard, allows to control a remote Move module.
@@ -35,38 +45,44 @@ class DAQ_Move_LECODirector(LECODirector, DAQ_Move_base):
35
45
  utility_classes.DAQ_TCP_server
36
46
  """
37
47
  settings: Parameter
38
- controller: ActuatorDirector
48
+ controller: Optional[ActuatorDirector]
49
+ _axis_names = ['']
50
+ _controller_units = ['']
51
+ _epsilon = 1
39
52
 
40
53
  params_client = [] # parameters of a client grabber
41
- data_actuator_type = DataActuatorType['float'] # DataActuatorType['DataActuator']
42
-
43
- message_list = LECODirector.message_list + ["move_abs", 'move_home', 'move_rel',
44
- 'get_actuator_value', 'stop_motion', 'position_is',
45
- 'move_done']
46
- socket_types = ["ACTUATOR"]
47
- params = comon_parameters_fun() + leco_parameters
48
-
49
- def __init__(self, parent=None, params_state=None, **kwargs) -> None:
50
- super().__init__(parent=parent,
51
- params_state=params_state, **kwargs)
54
+ data_actuator_type = DataActuatorType.DataActuator
55
+ params = comon_parameters_fun(axis_names=_axis_names, epsilon=_epsilon) + leco_parameters
56
+
57
+ for param_name in ('multiaxes', 'units', 'epsilon', 'bounds', 'scaling'):
58
+ param_dict = find_dict_in_list_from_key_val(params, 'name', param_name)
59
+ if param_dict is not None:
60
+ param_dict['visible'] = False
61
+
62
+ def __init__(
63
+ self, parent=None, params_state=None, host: Optional[str] = None, port: Optional[int] = None, **kwargs
64
+ ) -> None:
65
+ DAQ_Move_base.__init__(self, parent=parent, params_state=params_state)
66
+ if host is not None:
67
+ self.settings["host"] = host
68
+ if port is not None:
69
+ self.settings["port"] = port
70
+ LECODirector.__init__(self, host=self.settings["host"], port=self.settings["port"])
52
71
  self.register_rpc_methods((
53
- self.set_info,
72
+ self.set_units, # to set units accordingly to the one of the actor
73
+ ))
74
+
75
+ self.register_binary_rpc_methods((
76
+ self.send_position, # to display the actor position
77
+ self.set_move_done, # to set the move as done
54
78
  ))
55
- for method in (
56
- self.set_position,
57
- self.set_move_done,
58
- self.set_x_axis,
59
- self.set_y_axis,
60
- ):
61
- self.listener.register_binary_rpc_method(method, accept_binary_input=True)
62
-
63
- # copied, I think it is good:
64
- self.settings.child('bounds').hide()
65
- self.settings.child('scaling').hide()
66
- self.settings.child('epsilon').setValue(1)
67
-
68
- def commit_settings(self, param) -> None:
69
- self.commit_leco_settings(param=param)
79
+ self.start_timer()
80
+ # To distinguish how to encode positions, it needs to now if it deals
81
+ # with a json-accepting or a binary-accepting actuator
82
+ # It is set to False by default. It then use the first received message
83
+ # from the actuator that should contain its position to decide if it
84
+ # need to switch to json.
85
+ self.json = False
70
86
 
71
87
  def ini_stage(self, controller=None):
72
88
  """Actuator communication initialization
@@ -83,96 +99,119 @@ class DAQ_Move_LECODirector(LECODirector, DAQ_Move_base):
83
99
  initialized: bool
84
100
  False if initialization failed otherwise True
85
101
  """
86
- actor_name = self.settings.child("actor_name").value()
87
- self.controller = self.ini_stage_init( # type: ignore
88
- old_controller=controller,
89
- new_controller=ActuatorDirector(actor=actor_name, communicator=self.communicator),
90
- )
91
- try:
92
- self.controller.set_remote_name(self.communicator.full_name) # type: ignore
93
- except TimeoutError:
94
- print("Timeout setting remote name.") # TODO change to real logging
95
- # self.settings.child('infos').addChildren(self.params_client)
96
-
97
- self.settings.child('units').hide()
98
- self.settings.child('epsilon').hide()
99
-
100
- self.status.info = "LECODirector"
101
- self.status.controller = self.controller
102
- self.status.initialized = True
103
- return self.status
102
+ actor_name = self.settings["actor_name"]
103
+
104
+ if self.is_master:
105
+ self.controller = ActuatorDirector(actor=actor_name, communicator=self.communicator)
106
+ try:
107
+ self.controller.set_remote_name(self.communicator.full_name)
108
+ except TimeoutError:
109
+ logger.warning("Timeout setting remote name.")
110
+ else:
111
+ self.controller = controller
112
+
113
+ self.json = False
114
+ # send a command to the Actor whose name is actor_name to send its settings
115
+ self.controller.get_settings()
116
+
117
+ info = f"LECODirector: {self._title} is initialized"
118
+ initialized = True
119
+ return info, initialized
104
120
 
105
121
  def move_abs(self, position: DataActuator) -> None:
106
122
  position = self.check_bound(position)
107
123
  position = self.set_position_with_scaling(position)
108
-
109
- self.controller.move_abs(position=position)
110
-
111
124
  self.target_value = position
125
+ if self.json:
126
+ # if it's 0D, just send the position as a scalar
127
+ if hasattr(self, 'shape') and self.shape == ():
128
+ position = position.value(self.axis_unit)
129
+ else:
130
+ # Until the GUI allows for it (next line), we send the single value repeated
131
+ # position = [data.m_as(self.axis_unit) for data in position.quantities]
132
+ position = np.full(self.shape, position.value(self.axis_unit)).tolist()
133
+ self.controller.move_abs(position=position)
112
134
 
113
135
  def move_rel(self, position: DataActuator) -> None:
114
136
  position = self.check_bound(self.current_value + position) - self.current_value # type: ignore # noqa
115
137
  self.target_value = position + self.current_value
116
138
 
117
139
  position = self.set_position_relative_with_scaling(position)
140
+ if self.json:
141
+ # if it's 0D, just send the position as a scalar
142
+ if hasattr(self, 'ndim') and self.shape == ():
143
+ position = position.value(self.axis_unit)
144
+ else:
145
+ # Until the GUI allows for it (next line), we send the single value repeated
146
+ #position = [data.m_as(self.axis_unit) for data in position.quantities]
147
+ position = np.full(self.shape, position.value(self.axis_unit)).tolist()
148
+
118
149
  self.controller.move_rel(position=position)
119
150
 
120
151
  def move_home(self):
121
152
  self.controller.move_home()
122
153
 
123
154
  def get_actuator_value(self) -> DataActuator:
124
- """
125
- Get the current hardware position with scaling conversion given by
126
- `get_position_with_scaling`.
127
-
128
- See Also
129
- --------
130
- daq_move_base.get_position_with_scaling, daq_utils.ThreadCommand
131
- """
132
- self.controller.set_remote_name(self.communicator.full_name) # to ensure communication
155
+ """ Get the current hardware value """
133
156
  self.controller.get_actuator_value()
134
157
  return self._current_value
135
158
 
136
159
  def stop_motion(self) -> None:
137
160
  """
138
- See Also
139
- --------
140
- daq_move_base.move_done
141
161
  """
142
162
  self.controller.stop_motion()
143
163
 
144
164
  # Methods accessible via remote calls
145
165
  def _set_position_value(
146
- self, position: Union[str, float, None], additional_payload=None
166
+ self, data: Union[dict, list, str, float, None], additional_payload=None
147
167
  ) -> DataActuator:
148
- if position:
149
- if isinstance(position, str):
150
- deserializer = DeSerializer.from_b64_string(position)
151
- pos = deserializer.dwa_deserialization()
152
- else:
153
- pos = DataActuator(data=position)
154
- elif additional_payload is not None:
155
- pos = DeSerializer(additional_payload[0]).dwa_deserialization()
168
+
169
+ # This is the first received message, if position is set then
170
+ # it's included in the json payload and the director should
171
+ # usejson
172
+
173
+
174
+ if data is not None:
175
+ position = data.get('position', [])
176
+
177
+ self.shape = np.array(position).shape
178
+ position = [np.atleast_1d(position)]
179
+
180
+ pos = DataActuator(data=position)
181
+ self.json = True
182
+ elif additional_payload:
183
+ pos = SerializableFactory().get_apply_deserializer(additional_payload[0])
156
184
  else:
157
185
  raise ValueError("No position given")
158
186
  pos = self.get_position_with_scaling(pos) # type: ignore
159
187
  self._current_value = pos
160
188
  return pos
161
189
 
162
- def set_position(self, position: Union[str, float, None], additional_payload=None) -> None:
163
- pos = self._set_position_value(position=position, additional_payload=additional_payload)
164
- self.emit_status(ThreadCommand('get_actuator_value', [pos]))
190
+ def send_position(self, data: Union[dict, list, str, float, None], additional_payload=None) -> None:
191
+ pos = self._set_position_value(data=data, additional_payload=additional_payload)
192
+ self.emit_status(ThreadCommand(ThreadStatusMove.GET_ACTUATOR_VALUE, pos))
193
+
194
+ def set_move_done(self, data: Union[dict, list, str, float, None], additional_payload=None) -> None:
195
+ pos = self._set_position_value(data=data, additional_payload=additional_payload)
196
+ self.emit_status(ThreadCommand(ThreadStatusMove.MOVE_DONE, pos))
197
+
198
+ def set_units(self, units: str, additional_payload=None) -> None:
199
+ if units not in self.axis_units:
200
+ self.axis_units.append(units)
201
+ self.axis_unit = units
165
202
 
166
- def set_move_done(self, position: Union[str, float, None], additional_payload=None) -> None:
167
- pos = self._set_position_value(position=position, additional_payload=additional_payload)
168
- self.emit_status(ThreadCommand('move_done', [pos]))
203
+ def set_director_settings(self, settings: bytes):
204
+ """ Get the content of the actor settings to pe populated in this plugin
205
+ 'settings_client' parameter
169
206
 
170
- def set_x_axis(self, data, label: str = "", units: str = "") -> None:
171
- raise NotImplementedError("where is it handled?")
207
+ Then set the plugin units from this information"""
208
+ super().set_director_settings(settings)
209
+ self.axis_unit = self.settings['settings_client', 'units']
172
210
 
173
- def set_y_axis(self, data, label: str = "", units: str = "") -> None:
174
- raise NotImplementedError("where is it handled?")
211
+ def close(self) -> None:
212
+ """ Clear the content of the settings_clients setting"""
213
+ super().close()
175
214
 
176
215
 
177
216
  if __name__ == '__main__':
178
- main(__file__)
217
+ main(__file__, init=False)