pymodaq 5.1.6__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.
Files changed (154) hide show
  1. pymodaq/__init__.py +98 -0
  2. pymodaq/control_modules/__init__.py +1 -0
  3. pymodaq/control_modules/daq_move.py +1238 -0
  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/ui_base.py +359 -0
  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 +1517 -0
  13. pymodaq/control_modules/daq_viewer_ui.py +407 -0
  14. pymodaq/control_modules/mocks.py +57 -0
  15. pymodaq/control_modules/move_utility_classes.py +1141 -0
  16. pymodaq/control_modules/thread_commands.py +137 -0
  17. pymodaq/control_modules/ui_utils.py +72 -0
  18. pymodaq/control_modules/utils.py +591 -0
  19. pymodaq/control_modules/viewer_utility_classes.py +670 -0
  20. pymodaq/daq_utils/__init__.py +0 -0
  21. pymodaq/daq_utils/daq_utils.py +6 -0
  22. pymodaq/dashboard.py +2396 -0
  23. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.aliases +3 -0
  24. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvlps +3 -0
  25. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvproj +32 -0
  26. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.vi +0 -0
  27. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_1Dgaussian.vi +0 -0
  28. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_2Dgaussian.vi +0 -0
  29. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_cmd.vi +0 -0
  30. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_float.vi +0 -0
  31. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_int.vi +0 -0
  32. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_data.vi +0 -0
  33. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_int.vi +0 -0
  34. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_scalar.vi +0 -0
  35. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_string.vi +0 -0
  36. pymodaq/examples/Labview_TCP_Client/client_state.ctl +0 -0
  37. pymodaq/examples/Labview_TCP_Client/cmd_types.ctl +0 -0
  38. pymodaq/examples/__init__.py +0 -0
  39. pymodaq/examples/function_plotter.py +160 -0
  40. pymodaq/examples/nonlinearscanner.py +126 -0
  41. pymodaq/examples/qt_less_standalone_module.py +165 -0
  42. pymodaq/examples/tcp_client.py +97 -0
  43. pymodaq/extensions/__init__.py +25 -0
  44. pymodaq/extensions/adaptive/__init__.py +2 -0
  45. pymodaq/extensions/adaptive/adaptive_optimization.py +179 -0
  46. pymodaq/extensions/adaptive/loss_function/_1d_loss_functions.py +73 -0
  47. pymodaq/extensions/adaptive/loss_function/_2d_loss_functions.py +73 -0
  48. pymodaq/extensions/adaptive/loss_function/__init__.py +3 -0
  49. pymodaq/extensions/adaptive/loss_function/loss_factory.py +110 -0
  50. pymodaq/extensions/adaptive/utils.py +123 -0
  51. pymodaq/extensions/bayesian/__init__.py +2 -0
  52. pymodaq/extensions/bayesian/acquisition/__init__.py +2 -0
  53. pymodaq/extensions/bayesian/acquisition/acquisition_function_factory.py +80 -0
  54. pymodaq/extensions/bayesian/acquisition/base_acquisition_function.py +105 -0
  55. pymodaq/extensions/bayesian/bayesian_optimization.py +143 -0
  56. pymodaq/extensions/bayesian/utils.py +180 -0
  57. pymodaq/extensions/console.py +73 -0
  58. pymodaq/extensions/daq_logger/__init__.py +1 -0
  59. pymodaq/extensions/daq_logger/abstract.py +52 -0
  60. pymodaq/extensions/daq_logger/daq_logger.py +519 -0
  61. pymodaq/extensions/daq_logger/db/__init__.py +0 -0
  62. pymodaq/extensions/daq_logger/db/db_logger.py +300 -0
  63. pymodaq/extensions/daq_logger/db/db_logger_models.py +100 -0
  64. pymodaq/extensions/daq_logger/h5logging.py +84 -0
  65. pymodaq/extensions/daq_scan.py +1218 -0
  66. pymodaq/extensions/daq_scan_ui.py +241 -0
  67. pymodaq/extensions/data_mixer/__init__.py +0 -0
  68. pymodaq/extensions/data_mixer/daq_0Dviewer_DataMixer.py +97 -0
  69. pymodaq/extensions/data_mixer/data_mixer.py +262 -0
  70. pymodaq/extensions/data_mixer/model.py +108 -0
  71. pymodaq/extensions/data_mixer/models/__init__.py +0 -0
  72. pymodaq/extensions/data_mixer/models/equation_model.py +91 -0
  73. pymodaq/extensions/data_mixer/models/gaussian_fit_model.py +65 -0
  74. pymodaq/extensions/data_mixer/parser.py +53 -0
  75. pymodaq/extensions/data_mixer/utils.py +23 -0
  76. pymodaq/extensions/h5browser.py +9 -0
  77. pymodaq/extensions/optimizers_base/__init__.py +0 -0
  78. pymodaq/extensions/optimizers_base/optimizer.py +1016 -0
  79. pymodaq/extensions/optimizers_base/thread_commands.py +22 -0
  80. pymodaq/extensions/optimizers_base/utils.py +427 -0
  81. pymodaq/extensions/pid/__init__.py +16 -0
  82. pymodaq/extensions/pid/actuator_controller.py +14 -0
  83. pymodaq/extensions/pid/daq_move_PID.py +154 -0
  84. pymodaq/extensions/pid/pid_controller.py +1016 -0
  85. pymodaq/extensions/pid/utils.py +189 -0
  86. pymodaq/extensions/utils.py +111 -0
  87. pymodaq/icon.ico +0 -0
  88. pymodaq/post_treatment/__init__.py +6 -0
  89. pymodaq/post_treatment/load_and_plot.py +352 -0
  90. pymodaq/resources/__init__.py +0 -0
  91. pymodaq/resources/config_template.toml +57 -0
  92. pymodaq/resources/preset_default.xml +1 -0
  93. pymodaq/resources/setup_plugin.py +73 -0
  94. pymodaq/splash.png +0 -0
  95. pymodaq/utils/__init__.py +0 -0
  96. pymodaq/utils/array_manipulation.py +6 -0
  97. pymodaq/utils/calibration_camera.py +180 -0
  98. pymodaq/utils/chrono_timer.py +203 -0
  99. pymodaq/utils/config.py +53 -0
  100. pymodaq/utils/conftests.py +5 -0
  101. pymodaq/utils/daq_utils.py +158 -0
  102. pymodaq/utils/data.py +128 -0
  103. pymodaq/utils/enums.py +6 -0
  104. pymodaq/utils/exceptions.py +38 -0
  105. pymodaq/utils/gui_utils/__init__.py +10 -0
  106. pymodaq/utils/gui_utils/loader_utils.py +75 -0
  107. pymodaq/utils/gui_utils/utils.py +18 -0
  108. pymodaq/utils/gui_utils/widgets/lcd.py +8 -0
  109. pymodaq/utils/h5modules/__init__.py +2 -0
  110. pymodaq/utils/h5modules/module_saving.py +526 -0
  111. pymodaq/utils/leco/__init__.py +25 -0
  112. pymodaq/utils/leco/daq_move_LECODirector.py +217 -0
  113. pymodaq/utils/leco/daq_xDviewer_LECODirector.py +163 -0
  114. pymodaq/utils/leco/director_utils.py +74 -0
  115. pymodaq/utils/leco/leco_director.py +166 -0
  116. pymodaq/utils/leco/pymodaq_listener.py +364 -0
  117. pymodaq/utils/leco/rpc_method_definitions.py +43 -0
  118. pymodaq/utils/leco/utils.py +74 -0
  119. pymodaq/utils/logger.py +6 -0
  120. pymodaq/utils/managers/__init__.py +0 -0
  121. pymodaq/utils/managers/batchscan_manager.py +346 -0
  122. pymodaq/utils/managers/modules_manager.py +589 -0
  123. pymodaq/utils/managers/overshoot_manager.py +242 -0
  124. pymodaq/utils/managers/preset_manager.py +229 -0
  125. pymodaq/utils/managers/preset_manager_utils.py +262 -0
  126. pymodaq/utils/managers/remote_manager.py +484 -0
  127. pymodaq/utils/math_utils.py +6 -0
  128. pymodaq/utils/messenger.py +6 -0
  129. pymodaq/utils/parameter/__init__.py +10 -0
  130. pymodaq/utils/parameter/utils.py +6 -0
  131. pymodaq/utils/scanner/__init__.py +5 -0
  132. pymodaq/utils/scanner/scan_config.py +16 -0
  133. pymodaq/utils/scanner/scan_factory.py +259 -0
  134. pymodaq/utils/scanner/scan_selector.py +477 -0
  135. pymodaq/utils/scanner/scanner.py +324 -0
  136. pymodaq/utils/scanner/scanners/_1d_scanners.py +174 -0
  137. pymodaq/utils/scanner/scanners/_2d_scanners.py +299 -0
  138. pymodaq/utils/scanner/scanners/__init__.py +1 -0
  139. pymodaq/utils/scanner/scanners/sequential.py +224 -0
  140. pymodaq/utils/scanner/scanners/tabular.py +319 -0
  141. pymodaq/utils/scanner/utils.py +110 -0
  142. pymodaq/utils/svg/__init__.py +6 -0
  143. pymodaq/utils/svg/svg_renderer.py +20 -0
  144. pymodaq/utils/svg/svg_view.py +35 -0
  145. pymodaq/utils/svg/svg_viewer2D.py +50 -0
  146. pymodaq/utils/tcp_ip/__init__.py +6 -0
  147. pymodaq/utils/tcp_ip/mysocket.py +12 -0
  148. pymodaq/utils/tcp_ip/serializer.py +13 -0
  149. pymodaq/utils/tcp_ip/tcp_server_client.py +772 -0
  150. pymodaq-5.1.6.dist-info/METADATA +238 -0
  151. pymodaq-5.1.6.dist-info/RECORD +154 -0
  152. pymodaq-5.1.6.dist-info/WHEEL +4 -0
  153. pymodaq-5.1.6.dist-info/entry_points.txt +7 -0
  154. pymodaq-5.1.6.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,217 @@
1
+ """
2
+ LECO Director instrument plugin are to be used to communicate (and control) remotely real
3
+ instrument plugin through TCP/IP using the LECO Protocol
4
+
5
+ For this to work a coordinator must be instantiated can be done within the dashboard or directly
6
+ running: `python -m pyleco.coordinators.coordinator`
7
+
8
+ """
9
+
10
+ import numpy as np
11
+
12
+ from typing import Optional, Union
13
+
14
+ from pymodaq.control_modules.move_utility_classes import (DAQ_Move_base, comon_parameters_fun, main,
15
+ DataActuatorType, DataActuator)
16
+ from pymodaq.control_modules.thread_commands import ThreadStatus, ThreadStatusMove
17
+
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
21
+ from pymodaq_gui.parameter import Parameter
22
+
23
+ from pymodaq.utils.leco.leco_director import (LECODirector, leco_parameters, DirectorCommands,
24
+ DirectorReceivedCommands)
25
+ from pymodaq.utils.leco.director_utils import ActuatorDirector
26
+
27
+ from pymodaq_utils.logger import set_logger, get_module_name
28
+
29
+ logger = set_logger(get_module_name(__file__))
30
+
31
+
32
+ class DAQ_Move_LECODirector(LECODirector, DAQ_Move_base):
33
+ """A control module, which in the dashboard, allows to control a remote Move module.
34
+
35
+ ================= ==============================
36
+ **Attributes** **Type**
37
+ *command_server* instance of Signal
38
+ *x_axis* 1D numpy array
39
+ *y_axis* 1D numpy array
40
+ *data* double precision float array
41
+ ================= ==============================
42
+
43
+ See Also
44
+ --------
45
+ utility_classes.DAQ_TCP_server
46
+ """
47
+ settings: Parameter
48
+ controller: Optional[ActuatorDirector]
49
+ _axis_names = ['']
50
+ _controller_units = ['']
51
+ _epsilon = 1
52
+
53
+ params_client = [] # parameters of a client grabber
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"])
71
+ self.register_rpc_methods((
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
78
+ ))
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
86
+
87
+ def ini_stage(self, controller=None):
88
+ """Actuator communication initialization
89
+
90
+ Parameters
91
+ ----------
92
+ controller: (object)
93
+ custom object of a PyMoDAQ plugin (Slave case). None if only one actuator by controller
94
+ (Master case)
95
+
96
+ Returns
97
+ -------
98
+ info: str
99
+ initialized: bool
100
+ False if initialization failed otherwise True
101
+ """
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
120
+
121
+ def move_abs(self, position: DataActuator) -> None:
122
+ position = self.check_bound(position)
123
+ position = self.set_position_with_scaling(position)
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)
134
+
135
+ def move_rel(self, position: DataActuator) -> None:
136
+ position = self.check_bound(self.current_value + position) - self.current_value # type: ignore # noqa
137
+ self.target_value = position + self.current_value
138
+
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
+
149
+ self.controller.move_rel(position=position)
150
+
151
+ def move_home(self):
152
+ self.controller.move_home()
153
+
154
+ def get_actuator_value(self) -> DataActuator:
155
+ """ Get the current hardware value """
156
+ self.controller.get_actuator_value()
157
+ return self._current_value
158
+
159
+ def stop_motion(self) -> None:
160
+ """
161
+ """
162
+ self.controller.stop_motion()
163
+
164
+ # Methods accessible via remote calls
165
+ def _set_position_value(
166
+ self, data: Union[dict, list, str, float, None], additional_payload=None
167
+ ) -> DataActuator:
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])
184
+ else:
185
+ raise ValueError("No position given")
186
+ pos = self.get_position_with_scaling(pos) # type: ignore
187
+ self._current_value = pos
188
+ return pos
189
+
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
202
+
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
206
+
207
+ Then set the plugin units from this information"""
208
+ super().set_director_settings(settings)
209
+ self.axis_unit = self.settings['settings_client', 'units']
210
+
211
+ def close(self) -> None:
212
+ """ Clear the content of the settings_clients setting"""
213
+ super().close()
214
+
215
+
216
+ if __name__ == '__main__':
217
+ main(__file__, init=False)
@@ -0,0 +1,163 @@
1
+ from __future__ import annotations
2
+ from typing import Optional, Union
3
+
4
+ from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main
5
+ from pymodaq.control_modules.thread_commands import ThreadStatus, ThreadStatusViewer
6
+ from pymodaq.utils.data import DataFromPlugins, Axis
7
+ from pymodaq_data import DataToExport
8
+ from pymodaq_utils.serialize.factory import SerializableFactory
9
+ from pymodaq_utils.utils import ThreadCommand, getLineInfo
10
+
11
+ from pymodaq.utils import data # for serialization factory registration # noqa: F401
12
+ from pymodaq_gui.parameter import Parameter
13
+
14
+ from pymodaq.utils.leco.leco_director import LECODirector, leco_parameters
15
+ from pymodaq.utils.leco.director_utils import DetectorDirector
16
+ from pymodaq_utils.logger import set_logger, get_module_name
17
+
18
+ import numpy as np
19
+
20
+ logger = set_logger(get_module_name(__file__))
21
+
22
+
23
+ class DAQ_xDViewer_LECODirector(LECODirector, DAQ_Viewer_base):
24
+ """A control module, which in the dashboard, allows to control a remote Viewer module.
25
+
26
+ This is the base class for the viewer LECO director modules.
27
+ """
28
+
29
+ settings: Parameter
30
+ controller: DetectorDirector
31
+
32
+ params_GRABBER = []
33
+
34
+ socket_types = ["GRABBER"]
35
+ params = comon_parameters + leco_parameters
36
+ live_mode_available = True
37
+
38
+ def __init__(
39
+ self,
40
+ parent=None,
41
+ params_state=None,
42
+ grabber_type: str = "0D",
43
+ host: Optional[str] = None,
44
+ port: Optional[int] = None,
45
+ **kwargs,
46
+ ) -> None:
47
+ DAQ_Viewer_base.__init__(self, parent=parent, params_state=params_state)
48
+ if host is not None:
49
+ self.settings["host"] = host
50
+ if port is not None:
51
+ self.settings["port"] = port
52
+ LECODirector.__init__(self, host=self.settings["host"], port=self.settings["port"])
53
+
54
+ self.register_binary_rpc_methods((self.set_data,))
55
+
56
+ self.client_type = "GRABBER"
57
+ self.x_axis = None
58
+ self.y_axis = None
59
+ self.data = None
60
+ self.grabber_type = grabber_type
61
+ self.ind_data = 0
62
+ self.data_mock = None
63
+ self.start_timer()
64
+
65
+ def ini_detector(self, controller=None):
66
+ """
67
+ | Initialisation procedure of the detector updating the status dictionary.
68
+ |
69
+ | Init axes from image , here returns only None values (to tricky to di it with the
70
+ server and not really necessary for images anyway)
71
+
72
+ See Also
73
+ --------
74
+ utility_classes.DAQ_TCP_server.init_server, get_xaxis, get_yaxis
75
+ """
76
+
77
+ actor_name = self.settings["actor_name"]
78
+ if self.is_master:
79
+ self.controller = DetectorDirector(actor=actor_name,
80
+ communicator=self.communicator)
81
+ try:
82
+ self.controller.set_remote_name(self.communicator.full_name)
83
+ except TimeoutError:
84
+ logger.warning("Timeout setting remote name.")
85
+ else:
86
+ self.controller = controller
87
+
88
+ self.controller.get_settings()
89
+
90
+ initialized = True
91
+ info = 'Viewer Director ready'
92
+ return info, initialized
93
+
94
+ def grab_data(self, Naverage=1, **kwargs):
95
+ """
96
+ Start new acquisition.
97
+ Grabbed indice is used to keep track of the current image in the average.
98
+
99
+ ============== ========== ==============================
100
+ **Parameters** **Type** **Description**
101
+
102
+ *Naverage* int Number of images to average
103
+ ============== ========== ==============================
104
+
105
+ See Also
106
+ --------
107
+ utility_classes.DAQ_TCP_server.process_cmds
108
+ """
109
+
110
+ self.ind_grabbed = 0 # to keep track of the current image in the average
111
+ self.Naverage = Naverage
112
+ if kwargs.get('live', False):
113
+ self.controller.send_data_grab()
114
+ else:
115
+ self.controller.send_data_snap()
116
+
117
+ def stop(self):
118
+ """Stop grabbing."""
119
+ self.controller.stop_grab()
120
+
121
+ def set_data(self, data: Union[dict,list, str, float, None],
122
+ additional_payload: Optional[list[bytes]] = None) -> None:
123
+ """
124
+ Set the grabbed data signal.
125
+
126
+ corresponds to the "data_ready" signal
127
+
128
+ :param data: If None, look for the additional object
129
+ """
130
+ if additional_payload:
131
+ dte = SerializableFactory().get_apply_deserializer(additional_payload[0])
132
+ elif data is not None:
133
+ axes = []
134
+ labels = []
135
+ multichannel = False
136
+ if isinstance(data, dict):
137
+ axes = [
138
+ Axis( label=axis.get('label', ''),
139
+ units=axis.get('units', ''),
140
+ data=np.array(axis.get('data', [])),
141
+ index=ind
142
+ ) for ind, axis in enumerate(data.get('axes', []))
143
+ ]
144
+ labels = data.get('labels', [])
145
+ multichannel = data.get('multichannel', False)
146
+ data = data.get('data', [])
147
+ if multichannel:
148
+ # data[0] may fail if data is empty, but it shouldn't happen
149
+ ndim = np.array(data[0]).ndim
150
+ data = [np.atleast_1d(d) for d in data]
151
+ else:
152
+ ndim = np.array(data).ndim
153
+ data = [np.atleast_1d(data)]
154
+
155
+ dfp = DataFromPlugins(self.controller.actor, data=data, axes=axes[:ndim], labels=labels)
156
+ dte = DataToExport('Copy', data=[dfp])
157
+ else:
158
+ raise ValueError("Can't set_data when data is None")
159
+ self.dte_signal.emit(dte)
160
+
161
+
162
+ if __name__ == '__main__':
163
+ main(__file__, init=False)
@@ -0,0 +1,74 @@
1
+ """
2
+ Utils for the Director Modules
3
+
4
+ These directors correspond to the PymodaqListener
5
+ """
6
+
7
+ from typing import Optional, Union, List
8
+
9
+ from pyleco.directors.director import Director
10
+
11
+ import pymodaq_gui.parameter.utils as putils
12
+ from pymodaq_gui.parameter import Parameter, ioxml
13
+ from pymodaq.utils.data import DataActuator
14
+ from pymodaq.utils.leco.utils import binary_serialization_to_kwargs, SerializableFactory
15
+ from pymodaq.utils.leco.rpc_method_definitions import GenericMethods, MoveMethods, ViewerMethods
16
+
17
+ from pymodaq_gui.parameter.utils import ParameterWithPath
18
+
19
+
20
+
21
+ class GenericDirector(Director):
22
+ """Director helper to control some Module remotely."""
23
+
24
+ def set_remote_name(self, name: Optional[str] = None):
25
+ """Set the remote name of the Module (i.e. where it should send responses to)."""
26
+ self.ask_rpc(method=GenericMethods.SET_REMOTE_NAME, name=name or self.communicator.name)
27
+
28
+ def set_info(self, param: Parameter):
29
+ # It removes the first two parts (main_settings and detector_settings?)
30
+ pwp = ParameterWithPath(param, putils.get_param_path(param)[2:])
31
+ self.ask_rpc(method=GenericMethods.SET_INFO,
32
+ **binary_serialization_to_kwargs(pwp, data_key='parameter'))
33
+
34
+ def get_settings(self,) -> None:
35
+ self.ask_rpc(GenericMethods.GET_SETTINGS)
36
+
37
+
38
+ class DetectorDirector(GenericDirector):
39
+ def send_data_grab(self) -> None:
40
+ self.ask_rpc(ViewerMethods.GRAB)
41
+
42
+ def send_data_snap(self) -> None:
43
+ self.ask_rpc(ViewerMethods.SNAP)
44
+
45
+ def stop_grab(self) -> None:
46
+ self.ask_rpc(ViewerMethods.STOP)
47
+
48
+
49
+ class ActuatorDirector(GenericDirector):
50
+ def move_abs(self, position: Union[list, float, DataActuator]) -> None:
51
+ self.ask_rpc(
52
+ MoveMethods.MOVE_ABS, **binary_serialization_to_kwargs(position, data_key="position")
53
+ )
54
+
55
+ def move_rel(self, position: Union[list, float, DataActuator]) -> None:
56
+ self.ask_rpc(
57
+ MoveMethods.MOVE_REL, **binary_serialization_to_kwargs(position, data_key="position")
58
+ )
59
+
60
+ def move_home(self) -> None:
61
+ self.ask_rpc(MoveMethods.MOVE_HOME)
62
+
63
+ def get_actuator_value(self) -> None:
64
+ """Request that the actuator value is sent later on.
65
+
66
+ Later the `set_data` method will be called.
67
+ """
68
+ # according to DAQ_Move, this supersedes "check_position"
69
+ self.ask_rpc(MoveMethods.GET_ACTUATOR_VALUE)
70
+
71
+ def stop_motion(self,) -> None:
72
+ # not implemented in DAQ_Move!
73
+ self.ask_rpc(MoveMethods.STOP_MOTION)
74
+
@@ -0,0 +1,166 @@
1
+
2
+ import random
3
+
4
+ from pyleco.core import COORDINATOR_PORT
5
+ from pymodaq_utils.enums import StrEnum
6
+ from typing import Callable, cast, Sequence, List, Optional, Union
7
+
8
+ from pyleco.json_utils.errors import JSONRPCError, RECEIVER_UNKNOWN
9
+ import pymodaq_gui.parameter.utils as putils
10
+ from pymodaq_utils.config import Config
11
+ # object used to send info back to the main thread:
12
+ from pymodaq_utils.utils import ThreadCommand
13
+ from pymodaq_gui.parameter import Parameter
14
+ from pymodaq_gui.parameter import ioxml
15
+ from pymodaq_gui.parameter.utils import ParameterWithPath
16
+ from qtpy.QtCore import QTimer
17
+
18
+ from pymodaq.utils.leco.director_utils import GenericDirector
19
+ from pymodaq.utils.leco.pymodaq_listener import PymodaqListener
20
+ from pymodaq.utils.leco.rpc_method_definitions import GenericDirectorMethods, MoveDirectorMethods
21
+ from pymodaq_utils.serialize.factory import SerializableFactory
22
+ from pymodaq.control_modules.thread_commands import ThreadStatus, ThreadStatusMove
23
+
24
+ config = Config()
25
+
26
+ class DirectorCommands(StrEnum):
27
+ SET_SETTINGS = GenericDirectorMethods.SET_DIRECTOR_SETTINGS
28
+ SET_INFO = GenericDirectorMethods.SET_DIRECTOR_INFO
29
+
30
+ SEND_POSITION = MoveDirectorMethods.SEND_POSITION # to display the actor position
31
+ SET_MOVE_DONE = MoveDirectorMethods.SET_MOVE_DONE
32
+ SET_UNITS = MoveDirectorMethods.SET_UNITS # to set units accordingly to the one of the actor
33
+
34
+
35
+ class DirectorReceivedCommands(StrEnum):
36
+ MOVE_DONE = ThreadStatusMove.MOVE_DONE
37
+ GET_ACTUATOR_VALUE = ThreadStatusMove.GET_ACTUATOR_VALUE
38
+
39
+ config = Config()
40
+
41
+ leco_parameters = [
42
+ {'title': 'Actor name:', 'name': 'actor_name', 'type': 'str', 'value': "actor_name",
43
+ 'tip': 'Name of the actor plugin to communicate with.'},
44
+ {'title': 'Coordinator Host:', 'name': 'host', 'type': 'str', 'value': config('network', "leco-server", "host")},
45
+ {'title': 'Coordinator Port:', 'name': 'port', 'type': 'int', 'value': config('network', "leco-server", "port")},
46
+ {'title': 'Settings PyMoDAQ Client:', 'name': 'settings_client', 'type': 'group', 'children': []},
47
+ ]
48
+
49
+
50
+ class LECODirector:
51
+ """
52
+ This is a mixin for a Control module to direct another, remote module (analogous to TCP Server).
53
+
54
+
55
+ """
56
+
57
+ controller: GenericDirector
58
+ settings: Parameter
59
+ _title: str
60
+
61
+ def __init__(self, host: str = 'localhost', port : int = COORDINATOR_PORT, **kwargs) -> None:
62
+
63
+ name = f'{self._title}_{random.randrange(0, 10000)}_director'
64
+
65
+ self.listener = PymodaqListener(name=name, host=host, port=port)
66
+ self.listener.start_listen()
67
+
68
+ self.communicator = self.listener.get_communicator()
69
+
70
+ #registering rpc methods common to all Directors
71
+ self.register_rpc_methods((
72
+ self.set_director_settings,
73
+ ))
74
+ self.register_binary_rpc_methods((
75
+ self.set_director_info,
76
+ ))
77
+
78
+ def register_binary_rpc_methods(self, methods: Sequence[Callable]) -> None:
79
+ for method in methods:
80
+ self.listener.register_binary_rpc_method(method, accept_binary_input=True)
81
+
82
+ def register_rpc_methods(self, methods: Sequence[Callable]) -> None:
83
+ for method in methods:
84
+ self.communicator.register_rpc_method(method=method)
85
+
86
+ def commit_settings(self, param) -> None:
87
+ self.commit_leco_settings(param=param)
88
+
89
+ def commit_leco_settings(self, param: Parameter) -> None:
90
+ if param.name() == "actor_name":
91
+ self.controller.actor = param.value()
92
+ elif param.name() in putils.iter_children(self.settings.child('settings_client'), []):
93
+ self.controller.set_info(param=param)
94
+
95
+ def close(self) -> None:
96
+ """ Clear the content of the settings_clients setting"""
97
+ self.settings.child('settings_client').clearChildren()
98
+ self.listener.stop_listen()
99
+
100
+ def start_timer(self) -> None:
101
+ """To be called in child classes."""
102
+ self.timer = QTimer()
103
+ self.timer.timeout.connect(self.check_actor_connection)
104
+ try:
105
+ # cast is used by the type checker to infer the returned type (when many are possible)
106
+ timeout = cast(int, config("network", "leco-server", "heartbeat-timeout"))
107
+ except KeyError:
108
+ timeout = 1000
109
+ self.timer.start(timeout) # in milli seconds
110
+
111
+ def check_actor_connection(self) -> None:
112
+ try:
113
+ self.controller.ask_rpc("pong", timeout=0.1)
114
+ except JSONRPCError as exc:
115
+ if exc.rpc_error.code == RECEIVER_UNKNOWN.code:
116
+ self.emit_status(ThreadCommand(ThreadStatus.UPDATE_UI, "do_init", args=[False]))
117
+ else:
118
+ self.emit_status(
119
+ ThreadCommand(
120
+ ThreadStatus.UPDATE_STATUS,
121
+ f"Connection error to actor: {exc.rpc_error.message}",
122
+ )
123
+ )
124
+
125
+ def stop(self):
126
+ """
127
+ not implemented.
128
+ """
129
+ pass
130
+ return ""
131
+
132
+ def emit_status(self, status: ThreadCommand) -> None:
133
+ """ Emit the status_sig signal with the given status ThreadCommand back to the main GUI.
134
+ """
135
+ super().emit_status(status=status) # type: ignore
136
+
137
+ # Methods accessible via remote calls
138
+ def set_director_info(self,
139
+ parameter: Optional[Union[float, str]],
140
+ additional_payload: Optional[List[bytes]] = None,
141
+ ) -> None:
142
+ """ Write the value of a param updated from the actor to here in the
143
+ Parameter with path: ('move_settings', 'settings_client')
144
+ """
145
+ GenericDirectorMethods.SET_DIRECTOR_INFO # defined here
146
+ assert additional_payload
147
+ # cast is used by the type checker to infer the returned type (when many are possible)
148
+ param = cast(
149
+ ParameterWithPath, SerializableFactory().get_apply_deserializer(additional_payload[0])
150
+ )
151
+
152
+ try:
153
+ path = ['settings_client']
154
+ path.extend(param.path[1:])
155
+
156
+ self.settings.child(*path).setValue(param.value())
157
+ except Exception as e:
158
+ print(f'could not set the param {param} in the director:\n'
159
+ f'{str(e)}')
160
+
161
+ def set_director_settings(self, settings: bytes):
162
+ """ Get the content of the actor settings to pe populated in this plugin
163
+ 'settings_client' parameter"""
164
+ GenericDirectorMethods.SET_DIRECTOR_INFO # defined here
165
+ params = ioxml.XML_string_to_parameter(settings)
166
+ self.settings.child('settings_client').addChildren(params)