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.
- pymodaq/__init__.py +98 -0
- pymodaq/control_modules/__init__.py +1 -0
- pymodaq/control_modules/daq_move.py +1238 -0
- pymodaq/control_modules/daq_move_ui/__init__.py +0 -0
- pymodaq/control_modules/daq_move_ui/factory.py +48 -0
- pymodaq/control_modules/daq_move_ui/ui_base.py +359 -0
- pymodaq/control_modules/daq_move_ui/uis/__init__.py +0 -0
- pymodaq/control_modules/daq_move_ui/uis/binary.py +139 -0
- pymodaq/control_modules/daq_move_ui/uis/original.py +120 -0
- pymodaq/control_modules/daq_move_ui/uis/relative.py +124 -0
- pymodaq/control_modules/daq_move_ui/uis/simple.py +126 -0
- pymodaq/control_modules/daq_viewer.py +1517 -0
- pymodaq/control_modules/daq_viewer_ui.py +407 -0
- pymodaq/control_modules/mocks.py +57 -0
- pymodaq/control_modules/move_utility_classes.py +1141 -0
- pymodaq/control_modules/thread_commands.py +137 -0
- pymodaq/control_modules/ui_utils.py +72 -0
- pymodaq/control_modules/utils.py +591 -0
- pymodaq/control_modules/viewer_utility_classes.py +670 -0
- pymodaq/daq_utils/__init__.py +0 -0
- pymodaq/daq_utils/daq_utils.py +6 -0
- pymodaq/dashboard.py +2396 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.aliases +3 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvlps +3 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvproj +32 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_1Dgaussian.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_2Dgaussian.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_cmd.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_float.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_int.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_data.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_int.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_scalar.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_string.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/client_state.ctl +0 -0
- pymodaq/examples/Labview_TCP_Client/cmd_types.ctl +0 -0
- pymodaq/examples/__init__.py +0 -0
- pymodaq/examples/function_plotter.py +160 -0
- pymodaq/examples/nonlinearscanner.py +126 -0
- pymodaq/examples/qt_less_standalone_module.py +165 -0
- pymodaq/examples/tcp_client.py +97 -0
- pymodaq/extensions/__init__.py +25 -0
- pymodaq/extensions/adaptive/__init__.py +2 -0
- pymodaq/extensions/adaptive/adaptive_optimization.py +179 -0
- pymodaq/extensions/adaptive/loss_function/_1d_loss_functions.py +73 -0
- pymodaq/extensions/adaptive/loss_function/_2d_loss_functions.py +73 -0
- pymodaq/extensions/adaptive/loss_function/__init__.py +3 -0
- pymodaq/extensions/adaptive/loss_function/loss_factory.py +110 -0
- pymodaq/extensions/adaptive/utils.py +123 -0
- pymodaq/extensions/bayesian/__init__.py +2 -0
- pymodaq/extensions/bayesian/acquisition/__init__.py +2 -0
- pymodaq/extensions/bayesian/acquisition/acquisition_function_factory.py +80 -0
- pymodaq/extensions/bayesian/acquisition/base_acquisition_function.py +105 -0
- pymodaq/extensions/bayesian/bayesian_optimization.py +143 -0
- pymodaq/extensions/bayesian/utils.py +180 -0
- pymodaq/extensions/console.py +73 -0
- pymodaq/extensions/daq_logger/__init__.py +1 -0
- pymodaq/extensions/daq_logger/abstract.py +52 -0
- pymodaq/extensions/daq_logger/daq_logger.py +519 -0
- pymodaq/extensions/daq_logger/db/__init__.py +0 -0
- pymodaq/extensions/daq_logger/db/db_logger.py +300 -0
- pymodaq/extensions/daq_logger/db/db_logger_models.py +100 -0
- pymodaq/extensions/daq_logger/h5logging.py +84 -0
- pymodaq/extensions/daq_scan.py +1218 -0
- pymodaq/extensions/daq_scan_ui.py +241 -0
- pymodaq/extensions/data_mixer/__init__.py +0 -0
- pymodaq/extensions/data_mixer/daq_0Dviewer_DataMixer.py +97 -0
- pymodaq/extensions/data_mixer/data_mixer.py +262 -0
- pymodaq/extensions/data_mixer/model.py +108 -0
- pymodaq/extensions/data_mixer/models/__init__.py +0 -0
- pymodaq/extensions/data_mixer/models/equation_model.py +91 -0
- pymodaq/extensions/data_mixer/models/gaussian_fit_model.py +65 -0
- pymodaq/extensions/data_mixer/parser.py +53 -0
- pymodaq/extensions/data_mixer/utils.py +23 -0
- pymodaq/extensions/h5browser.py +9 -0
- pymodaq/extensions/optimizers_base/__init__.py +0 -0
- pymodaq/extensions/optimizers_base/optimizer.py +1016 -0
- pymodaq/extensions/optimizers_base/thread_commands.py +22 -0
- pymodaq/extensions/optimizers_base/utils.py +427 -0
- pymodaq/extensions/pid/__init__.py +16 -0
- pymodaq/extensions/pid/actuator_controller.py +14 -0
- pymodaq/extensions/pid/daq_move_PID.py +154 -0
- pymodaq/extensions/pid/pid_controller.py +1016 -0
- pymodaq/extensions/pid/utils.py +189 -0
- pymodaq/extensions/utils.py +111 -0
- pymodaq/icon.ico +0 -0
- pymodaq/post_treatment/__init__.py +6 -0
- pymodaq/post_treatment/load_and_plot.py +352 -0
- pymodaq/resources/__init__.py +0 -0
- pymodaq/resources/config_template.toml +57 -0
- pymodaq/resources/preset_default.xml +1 -0
- pymodaq/resources/setup_plugin.py +73 -0
- pymodaq/splash.png +0 -0
- pymodaq/utils/__init__.py +0 -0
- pymodaq/utils/array_manipulation.py +6 -0
- pymodaq/utils/calibration_camera.py +180 -0
- pymodaq/utils/chrono_timer.py +203 -0
- pymodaq/utils/config.py +53 -0
- pymodaq/utils/conftests.py +5 -0
- pymodaq/utils/daq_utils.py +158 -0
- pymodaq/utils/data.py +128 -0
- pymodaq/utils/enums.py +6 -0
- pymodaq/utils/exceptions.py +38 -0
- pymodaq/utils/gui_utils/__init__.py +10 -0
- pymodaq/utils/gui_utils/loader_utils.py +75 -0
- pymodaq/utils/gui_utils/utils.py +18 -0
- pymodaq/utils/gui_utils/widgets/lcd.py +8 -0
- pymodaq/utils/h5modules/__init__.py +2 -0
- pymodaq/utils/h5modules/module_saving.py +526 -0
- pymodaq/utils/leco/__init__.py +25 -0
- pymodaq/utils/leco/daq_move_LECODirector.py +217 -0
- pymodaq/utils/leco/daq_xDviewer_LECODirector.py +163 -0
- pymodaq/utils/leco/director_utils.py +74 -0
- pymodaq/utils/leco/leco_director.py +166 -0
- pymodaq/utils/leco/pymodaq_listener.py +364 -0
- pymodaq/utils/leco/rpc_method_definitions.py +43 -0
- pymodaq/utils/leco/utils.py +74 -0
- pymodaq/utils/logger.py +6 -0
- pymodaq/utils/managers/__init__.py +0 -0
- pymodaq/utils/managers/batchscan_manager.py +346 -0
- pymodaq/utils/managers/modules_manager.py +589 -0
- pymodaq/utils/managers/overshoot_manager.py +242 -0
- pymodaq/utils/managers/preset_manager.py +229 -0
- pymodaq/utils/managers/preset_manager_utils.py +262 -0
- pymodaq/utils/managers/remote_manager.py +484 -0
- pymodaq/utils/math_utils.py +6 -0
- pymodaq/utils/messenger.py +6 -0
- pymodaq/utils/parameter/__init__.py +10 -0
- pymodaq/utils/parameter/utils.py +6 -0
- pymodaq/utils/scanner/__init__.py +5 -0
- pymodaq/utils/scanner/scan_config.py +16 -0
- pymodaq/utils/scanner/scan_factory.py +259 -0
- pymodaq/utils/scanner/scan_selector.py +477 -0
- pymodaq/utils/scanner/scanner.py +324 -0
- pymodaq/utils/scanner/scanners/_1d_scanners.py +174 -0
- pymodaq/utils/scanner/scanners/_2d_scanners.py +299 -0
- pymodaq/utils/scanner/scanners/__init__.py +1 -0
- pymodaq/utils/scanner/scanners/sequential.py +224 -0
- pymodaq/utils/scanner/scanners/tabular.py +319 -0
- pymodaq/utils/scanner/utils.py +110 -0
- pymodaq/utils/svg/__init__.py +6 -0
- pymodaq/utils/svg/svg_renderer.py +20 -0
- pymodaq/utils/svg/svg_view.py +35 -0
- pymodaq/utils/svg/svg_viewer2D.py +50 -0
- pymodaq/utils/tcp_ip/__init__.py +6 -0
- pymodaq/utils/tcp_ip/mysocket.py +12 -0
- pymodaq/utils/tcp_ip/serializer.py +13 -0
- pymodaq/utils/tcp_ip/tcp_server_client.py +772 -0
- pymodaq-5.1.6.dist-info/METADATA +238 -0
- pymodaq-5.1.6.dist-info/RECORD +154 -0
- pymodaq-5.1.6.dist-info/WHEEL +4 -0
- pymodaq-5.1.6.dist-info/entry_points.txt +7 -0
- 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)
|