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,364 @@
|
|
|
1
|
+
|
|
2
|
+
from pymodaq_utils.enums import StrEnum
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
from threading import Event
|
|
6
|
+
from typing import cast, Optional, Union, List, Sequence, Type
|
|
7
|
+
|
|
8
|
+
from pyleco.core import COORDINATOR_PORT
|
|
9
|
+
from pyleco.json_utils.errors import JSONRPCError, RECEIVER_UNKNOWN, NODE_UNKNOWN
|
|
10
|
+
from pyleco.utils.listener import Listener, PipeHandler
|
|
11
|
+
from qtpy.QtCore import QObject, Signal # type: ignore
|
|
12
|
+
|
|
13
|
+
from pymodaq_data.data import DataWithAxes
|
|
14
|
+
from pymodaq_utils.serialize.factory import SerializableFactory, SerializableBase
|
|
15
|
+
from pymodaq_utils.utils import ThreadCommand
|
|
16
|
+
from pymodaq_gui.parameter import ioxml
|
|
17
|
+
from pymodaq_gui.parameter.utils import ParameterWithPath
|
|
18
|
+
|
|
19
|
+
from pymodaq.utils.leco.utils import binary_serialization_to_kwargs
|
|
20
|
+
from pymodaq.utils.leco.rpc_method_definitions import (
|
|
21
|
+
GenericMethods,
|
|
22
|
+
MoveMethods,
|
|
23
|
+
ViewerMethods,
|
|
24
|
+
GenericDirectorMethods,
|
|
25
|
+
MoveDirectorMethods,
|
|
26
|
+
ViewerDirectorMethods,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Commands for ThreadCommand
|
|
31
|
+
class LECOClientCommands(StrEnum):
|
|
32
|
+
LECO_CONNECTED = "leco_connected"
|
|
33
|
+
LECO_DISCONNECTED = "leco_disconnected"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class LECOCommands(StrEnum):
|
|
37
|
+
CONNECT = "ini_connection"
|
|
38
|
+
QUIT = "quit"
|
|
39
|
+
GET_SETTINGS = 'get_settings'
|
|
40
|
+
SET_DIRECTOR_SETTINGS = 'set_director_settings'
|
|
41
|
+
SET_INFO = 'set_info'
|
|
42
|
+
SEND_INFO = 'send_info'
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class LECOMoveCommands(StrEnum):
|
|
46
|
+
POSITION = 'position_is'
|
|
47
|
+
MOVE_DONE = 'move_done'
|
|
48
|
+
UNITS_CHANGED = 'units_changed'
|
|
49
|
+
STOP = 'stop_motion'
|
|
50
|
+
MOVE_ABS = 'move_abs'
|
|
51
|
+
MOVE_REL = 'move_rel'
|
|
52
|
+
MOVE_HOME = 'move_home'
|
|
53
|
+
GET_ACTUATOR_VALUE = 'get_actuator_value'
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class LECOViewerCommands(StrEnum):
|
|
57
|
+
DATA_READY = 'data_ready'
|
|
58
|
+
GRAB = 'grab'
|
|
59
|
+
SNAP = 'snap'
|
|
60
|
+
STOP = 'stop_grab'
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class ListenerSignals(QObject):
|
|
64
|
+
cmd_signal = Signal(ThreadCommand)
|
|
65
|
+
"""
|
|
66
|
+
Possible messages sendable via `cmd_signal`
|
|
67
|
+
For all modules: Info, Infos, Info_xml, set_info
|
|
68
|
+
|
|
69
|
+
For a detector: Send Data 0D, Send Data 1D, Send Data 2D
|
|
70
|
+
|
|
71
|
+
For an actuator: move_abs, move_home, move_rel, check_position, stop_motion
|
|
72
|
+
"""
|
|
73
|
+
# message = Signal(Message)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class PymodaqPipeHandler(PipeHandler):
|
|
77
|
+
|
|
78
|
+
def __init__(self, name: str, signals: ListenerSignals, **kwargs) -> None:
|
|
79
|
+
super().__init__(name, **kwargs)
|
|
80
|
+
self.signals = signals
|
|
81
|
+
self.register_data_types_for_deserialization()
|
|
82
|
+
|
|
83
|
+
def register_data_types_for_deserialization(
|
|
84
|
+
self, types: Optional[Sequence[type[SerializableBase]]] = None
|
|
85
|
+
) -> None:
|
|
86
|
+
"""Register different data types for deserialization in subclasses."""
|
|
87
|
+
if types is None:
|
|
88
|
+
return
|
|
89
|
+
for cls in types:
|
|
90
|
+
SerializableFactory().register_from_type(
|
|
91
|
+
cls, cls.serialize, cls.deserialize
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
class ActorHandler(PymodaqPipeHandler):
|
|
95
|
+
def register_data_types_for_deserialization(
|
|
96
|
+
self, types: Optional[Sequence[type[SerializableBase]]] = None
|
|
97
|
+
) -> None:
|
|
98
|
+
all_types: Sequence[type[SerializableBase]] = [DataWithAxes]
|
|
99
|
+
if types:
|
|
100
|
+
all_types.extend(types) # type: ignore
|
|
101
|
+
super().register_data_types_for_deserialization(all_types)
|
|
102
|
+
|
|
103
|
+
def register_rpc_methods(self) -> None:
|
|
104
|
+
super().register_rpc_methods()
|
|
105
|
+
self.register_binary_rpc_method(
|
|
106
|
+
self.set_info, name=GenericMethods.SET_INFO, accept_binary_input=True
|
|
107
|
+
)
|
|
108
|
+
self.register_rpc_method(self.send_data_grab, name=ViewerMethods.GRAB)
|
|
109
|
+
self.register_rpc_method(self.send_data_snap, name=ViewerMethods.SNAP)
|
|
110
|
+
self.register_binary_rpc_method(
|
|
111
|
+
self.move_abs, accept_binary_input=True, name=MoveMethods.MOVE_ABS
|
|
112
|
+
)
|
|
113
|
+
self.register_binary_rpc_method(
|
|
114
|
+
self.move_rel, accept_binary_input=True, name=MoveMethods.MOVE_REL
|
|
115
|
+
)
|
|
116
|
+
self.register_rpc_method(self.move_home, name=MoveMethods.MOVE_HOME)
|
|
117
|
+
self.register_rpc_method(self.get_actuator_value, name=MoveMethods.GET_ACTUATOR_VALUE)
|
|
118
|
+
self.register_rpc_method(self.stop_motion, name=MoveMethods.STOP_MOTION)
|
|
119
|
+
self.register_rpc_method(self.stop_grab, name=ViewerMethods.STOP)
|
|
120
|
+
self.register_rpc_method(self.get_settings, name=GenericMethods.GET_SETTINGS)
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def extract_pymodaq_object(
|
|
124
|
+
value: Optional[Union[float, str]], additional_payload: Optional[List[bytes]]
|
|
125
|
+
):
|
|
126
|
+
if value is None and additional_payload:
|
|
127
|
+
return cast(
|
|
128
|
+
DataWithAxes, SerializableFactory().get_apply_deserializer(additional_payload[0])
|
|
129
|
+
)
|
|
130
|
+
else:
|
|
131
|
+
return value
|
|
132
|
+
|
|
133
|
+
# generic commands
|
|
134
|
+
def set_info(self,
|
|
135
|
+
parameter: Optional[Union[float, str]],
|
|
136
|
+
additional_payload: Optional[List[bytes]] = None,
|
|
137
|
+
) -> None:
|
|
138
|
+
assert additional_payload
|
|
139
|
+
param = cast(
|
|
140
|
+
ParameterWithPath, SerializableFactory().get_apply_deserializer(additional_payload[0])
|
|
141
|
+
)
|
|
142
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOCommands.SET_INFO, attribute=param))
|
|
143
|
+
|
|
144
|
+
def get_settings(self):
|
|
145
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOCommands.GET_SETTINGS))
|
|
146
|
+
|
|
147
|
+
# detector commands
|
|
148
|
+
def send_data_grab(self,) -> None:
|
|
149
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOViewerCommands.GRAB))
|
|
150
|
+
|
|
151
|
+
# detector commands
|
|
152
|
+
def send_data_snap(self,) -> None:
|
|
153
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOViewerCommands.SNAP))
|
|
154
|
+
|
|
155
|
+
# actuator commands
|
|
156
|
+
def move_abs(
|
|
157
|
+
self,
|
|
158
|
+
position: Optional[Union[float, str]],
|
|
159
|
+
additional_payload: Optional[List[bytes]] = None,
|
|
160
|
+
) -> None:
|
|
161
|
+
"""Move to an absolute position.
|
|
162
|
+
|
|
163
|
+
:param position: Deprecated, should be None and content transferred binary.
|
|
164
|
+
:param additional_payload: binary frames containing the position as PyMoDAQ `DataActuator`.
|
|
165
|
+
"""
|
|
166
|
+
pos = self.extract_pymodaq_object(position, additional_payload)
|
|
167
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOMoveCommands.MOVE_ABS, pos))
|
|
168
|
+
|
|
169
|
+
def move_rel(
|
|
170
|
+
self,
|
|
171
|
+
position: Optional[Union[float, str]],
|
|
172
|
+
additional_payload: Optional[List[bytes]] = None,
|
|
173
|
+
) -> None:
|
|
174
|
+
"""Move by a relative position.
|
|
175
|
+
|
|
176
|
+
:param position: Deprecated, should be None and content transferred binary.
|
|
177
|
+
:param additional_payload: binary frames containing the position as PyMoDAQ `DataActuator`.
|
|
178
|
+
"""
|
|
179
|
+
pos = self.extract_pymodaq_object(position, additional_payload)
|
|
180
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOMoveCommands.MOVE_REL, pos))
|
|
181
|
+
|
|
182
|
+
def move_home(self) -> None:
|
|
183
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOMoveCommands.MOVE_HOME))
|
|
184
|
+
|
|
185
|
+
def get_actuator_value(self) -> None:
|
|
186
|
+
"""Request that the actuator value is sent later on."""
|
|
187
|
+
# according to DAQ_Move, this supersedes "check_position"
|
|
188
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOMoveCommands.GET_ACTUATOR_VALUE))
|
|
189
|
+
|
|
190
|
+
def stop_motion(self,) -> None:
|
|
191
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOMoveCommands.STOP))
|
|
192
|
+
|
|
193
|
+
def stop_grab(self,) -> None:
|
|
194
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOViewerCommands.STOP))
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# to be able to separate them later on
|
|
198
|
+
MoveActorHandler = ActorHandler
|
|
199
|
+
ViewerActorHandler = ActorHandler
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class PymodaqListener(Listener):
|
|
203
|
+
"""A Listener prepared for PyMoDAQ.
|
|
204
|
+
|
|
205
|
+
:param name: Name of this module.
|
|
206
|
+
:param host: Host name of the communication server.
|
|
207
|
+
:param port: Port number of the communication server.
|
|
208
|
+
"""
|
|
209
|
+
remote_names: set[str]
|
|
210
|
+
|
|
211
|
+
local_methods = ["pong", "set_log_level"]
|
|
212
|
+
|
|
213
|
+
def __init__(self,
|
|
214
|
+
name: str,
|
|
215
|
+
handler_class: Type[PymodaqPipeHandler] = PymodaqPipeHandler,
|
|
216
|
+
host: str = "localhost",
|
|
217
|
+
port: int = COORDINATOR_PORT,
|
|
218
|
+
logger: Optional[logging.Logger] = None,
|
|
219
|
+
timeout: float = 1,
|
|
220
|
+
**kwargs) -> None:
|
|
221
|
+
super().__init__(name, host, port, logger=logger, timeout=timeout,
|
|
222
|
+
**kwargs)
|
|
223
|
+
self.remote_names = set()
|
|
224
|
+
self.signals = ListenerSignals()
|
|
225
|
+
# self.signals.message.connect(self.handle_message)
|
|
226
|
+
self.cmd_signal = self.signals.cmd_signal
|
|
227
|
+
self._handler_class = handler_class
|
|
228
|
+
|
|
229
|
+
def _listen(self, name: str, stop_event: Event, coordinator_host: str, coordinator_port: int,
|
|
230
|
+
data_host: str, data_port: int) -> None:
|
|
231
|
+
self.message_handler = self._handler_class(name,
|
|
232
|
+
host=coordinator_host, port=coordinator_port,
|
|
233
|
+
data_host=data_host, data_port=data_port,
|
|
234
|
+
signals=self.signals,
|
|
235
|
+
)
|
|
236
|
+
self.message_handler.register_on_name_change_method(self.indicate_sign_in_out)
|
|
237
|
+
self.message_handler.listen(stop_event=stop_event)
|
|
238
|
+
|
|
239
|
+
def stop_listen(self) -> None:
|
|
240
|
+
super().stop_listen()
|
|
241
|
+
try:
|
|
242
|
+
del self.communicator
|
|
243
|
+
except AttributeError:
|
|
244
|
+
pass
|
|
245
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOClientCommands.LECO_DISCONNECTED))
|
|
246
|
+
|
|
247
|
+
def indicate_sign_in_out(self, full_name: str):
|
|
248
|
+
if "." in full_name:
|
|
249
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOClientCommands.LECO_CONNECTED))
|
|
250
|
+
else:
|
|
251
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOClientCommands.LECO_DISCONNECTED))
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class ActorListener(PymodaqListener):
|
|
255
|
+
"""Listener for modules being an Actor (being remote controlled)."""
|
|
256
|
+
|
|
257
|
+
def __init__(self,
|
|
258
|
+
name: str,
|
|
259
|
+
handler_class: Type[ActorHandler] = ActorHandler,
|
|
260
|
+
host: str = "localhost",
|
|
261
|
+
port: int = COORDINATOR_PORT,
|
|
262
|
+
logger: Optional[logging.Logger] = None,
|
|
263
|
+
timeout: float = 1,
|
|
264
|
+
**kwargs) -> None:
|
|
265
|
+
super().__init__(name, handler_class=handler_class, host=host, port=port,
|
|
266
|
+
logger=logger, timeout=timeout,
|
|
267
|
+
**kwargs)
|
|
268
|
+
|
|
269
|
+
def start_listen(self) -> None:
|
|
270
|
+
super().start_listen()
|
|
271
|
+
self.message_handler.register_rpc_method(
|
|
272
|
+
self.set_remote_name, name=GenericMethods.SET_REMOTE_NAME
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
def set_remote_name(self, name: str) -> None:
|
|
276
|
+
"""Define what the name of the remote for answers is."""
|
|
277
|
+
self.remote_names.add(name)
|
|
278
|
+
|
|
279
|
+
def remove_remote_name(self, name: str) -> None:
|
|
280
|
+
self.remote_names.discard(name)
|
|
281
|
+
|
|
282
|
+
def queue_command(self, command: ThreadCommand) -> None:
|
|
283
|
+
"""Queue a command to send it via LECO to the server."""
|
|
284
|
+
|
|
285
|
+
# generic commands
|
|
286
|
+
if command.command == LECOCommands.CONNECT:
|
|
287
|
+
try:
|
|
288
|
+
if self.thread.is_alive():
|
|
289
|
+
return # already started
|
|
290
|
+
except AttributeError:
|
|
291
|
+
pass # start later on, as there is no thread.
|
|
292
|
+
self.start_listen()
|
|
293
|
+
|
|
294
|
+
elif command.command == LECOCommands.QUIT:
|
|
295
|
+
try:
|
|
296
|
+
self.stop_listen()
|
|
297
|
+
except Exception:
|
|
298
|
+
pass
|
|
299
|
+
finally:
|
|
300
|
+
self.cmd_signal.emit(ThreadCommand('disconnected'))
|
|
301
|
+
|
|
302
|
+
elif command.attribute is not None:
|
|
303
|
+
# here are all methods requiring an attribute in the ThreadCommand
|
|
304
|
+
|
|
305
|
+
if command.command == LECOViewerCommands.DATA_READY:
|
|
306
|
+
value = command.attribute
|
|
307
|
+
self.send_rpc_message_to_remote(
|
|
308
|
+
method=ViewerDirectorMethods.SET_DATA,
|
|
309
|
+
**binary_serialization_to_kwargs(value),
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
elif command.command == LECOCommands.SEND_INFO:
|
|
313
|
+
self.send_rpc_message_to_remote(
|
|
314
|
+
method=GenericDirectorMethods.SET_DIRECTOR_INFO,
|
|
315
|
+
**binary_serialization_to_kwargs(command.attribute, data_key='parameter'))
|
|
316
|
+
|
|
317
|
+
elif command.command == LECOMoveCommands.POSITION:
|
|
318
|
+
value = command.attribute
|
|
319
|
+
if isinstance(value, (list, tuple)):
|
|
320
|
+
value = value[0] # for backward compatibility with attributes list
|
|
321
|
+
self.send_rpc_message_to_remote(
|
|
322
|
+
method=MoveDirectorMethods.SEND_POSITION,
|
|
323
|
+
**binary_serialization_to_kwargs(pymodaq_object=value, data_key="position"),
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
elif command.command == LECOMoveCommands.MOVE_DONE:
|
|
327
|
+
value = command.attribute
|
|
328
|
+
if isinstance(value, (list, tuple)):
|
|
329
|
+
value = value[0] # for backward compatibility with attributes list
|
|
330
|
+
self.send_rpc_message_to_remote(
|
|
331
|
+
method=MoveDirectorMethods.SET_MOVE_DONE,
|
|
332
|
+
**binary_serialization_to_kwargs(value, data_key="position"),
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
elif command.command == LECOMoveCommands.UNITS_CHANGED:
|
|
336
|
+
units: str = command.attribute
|
|
337
|
+
self.send_rpc_message_to_remote(
|
|
338
|
+
method=MoveDirectorMethods.SET_UNITS,
|
|
339
|
+
units=units.encode(),
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
elif command.command == LECOCommands.SET_DIRECTOR_SETTINGS:
|
|
343
|
+
self.send_rpc_message_to_remote(
|
|
344
|
+
method=GenericDirectorMethods.SET_DIRECTOR_SETTINGS,
|
|
345
|
+
settings=command.attribute.decode(),
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
else:
|
|
349
|
+
raise IOError("Unknown TCP client command")
|
|
350
|
+
|
|
351
|
+
def send_rpc_message_to_remote(self, method: str, **kwargs) -> None:
|
|
352
|
+
if not self.remote_names:
|
|
353
|
+
return
|
|
354
|
+
for name in list(self.remote_names):
|
|
355
|
+
try:
|
|
356
|
+
self.communicator.ask_rpc(receiver=name, method=method, **kwargs)
|
|
357
|
+
except JSONRPCError as exc:
|
|
358
|
+
if exc.rpc_error.code in (RECEIVER_UNKNOWN.code, NODE_UNKNOWN.code):
|
|
359
|
+
self.remove_remote_name(name)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
# to be able to separate them later on
|
|
363
|
+
MoveActorListener = ActorListener
|
|
364
|
+
ViewerActorListener = ActorListener
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Names of methods used between remotely controlled modules and
|
|
3
|
+
remote controlling director modules.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pymodaq_utils.enums import StrEnum
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Methods for all PyMoDAQ modules
|
|
10
|
+
class GenericMethods(StrEnum):
|
|
11
|
+
SET_INFO = "set_info"
|
|
12
|
+
GET_SETTINGS = "get_settings"
|
|
13
|
+
SET_REMOTE_NAME = "set_remote_name"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MoveMethods(StrEnum):
|
|
17
|
+
MOVE_ABS = "move_abs"
|
|
18
|
+
MOVE_REL = "move_rel"
|
|
19
|
+
MOVE_HOME = "move_home"
|
|
20
|
+
STOP_MOTION = "stop_motion"
|
|
21
|
+
GET_ACTUATOR_VALUE = "get_actuator_value"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ViewerMethods(StrEnum):
|
|
25
|
+
GRAB = "send_data_grab"
|
|
26
|
+
SNAP = "send_data_snap"
|
|
27
|
+
STOP = "stop_grab"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Director module methods
|
|
31
|
+
class GenericDirectorMethods(StrEnum):
|
|
32
|
+
SET_DIRECTOR_SETTINGS = "set_director_settings"
|
|
33
|
+
SET_DIRECTOR_INFO = "set_director_info"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class MoveDirectorMethods(StrEnum):
|
|
37
|
+
SET_UNITS = "set_units"
|
|
38
|
+
SEND_POSITION = "send_position"
|
|
39
|
+
SET_MOVE_DONE = "set_move_done"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ViewerDirectorMethods(StrEnum):
|
|
43
|
+
SET_DATA = "set_data"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
from typing import Any, Optional, Union, get_args, TypeVar
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from pymodaq.utils import data
|
|
8
|
+
from pymodaq_utils.serialize.factory import SerializableFactory
|
|
9
|
+
|
|
10
|
+
from pymodaq_utils.logger import set_logger
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
logger = set_logger('leco_utils')
|
|
14
|
+
JSON_TYPES = Union[str, int, float, list]
|
|
15
|
+
|
|
16
|
+
ser_factory = SerializableFactory()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## this form below is to be compatible with python <= 3.10
|
|
20
|
+
## for py>= 3.11 this could be written SERIALIZABLE = Union[ser_factory.get_serializables()]
|
|
21
|
+
SERIALIZABLE = Union[ser_factory.get_serializables()[0]]
|
|
22
|
+
for klass in ser_factory.get_serializables()[1:]:
|
|
23
|
+
SERIALIZABLE = Union[SERIALIZABLE, klass]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def binary_serialization(
|
|
27
|
+
pymodaq_object: Union[SERIALIZABLE, Any],
|
|
28
|
+
) -> tuple[Optional[Any], Optional[list[bytes]]]:
|
|
29
|
+
"""Serialize (binary) a pymodaq object, if it is not JSON compatible.
|
|
30
|
+
|
|
31
|
+
If an object is JSON serializable, we can send it as a value to the JSON
|
|
32
|
+
encoder and do not need a binary value.
|
|
33
|
+
Otherwise, the JSON value is None and the serialized object is returned.
|
|
34
|
+
|
|
35
|
+
:param pymodaq_object: the object which shall be sent via LECO protocol.
|
|
36
|
+
:return: tuple of the JSON value and a list of additional payload frames.
|
|
37
|
+
"""
|
|
38
|
+
if isinstance(pymodaq_object, get_args(JSON_TYPES)):
|
|
39
|
+
return pymodaq_object, None
|
|
40
|
+
return None, [SerializableFactory().get_apply_serializer(pymodaq_object)]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def binary_serialization_to_kwargs(
|
|
45
|
+
pymodaq_object: Union[SERIALIZABLE, Any], data_key: str = "data"
|
|
46
|
+
) -> dict[str, Any]:
|
|
47
|
+
"""Create a dictionary of data parameters and of additional payload to send.
|
|
48
|
+
|
|
49
|
+
This method prepares arguments for PyLECO's :meth:`Communicator.ask_rpc` method.
|
|
50
|
+
In order to send a binary value as an argument `data_key` for a method, the
|
|
51
|
+
argument has to be None, which is JSON describable.
|
|
52
|
+
The binary data has to be sent via the `additional_payload` parameter.
|
|
53
|
+
If the data can be sent as JSON, it is sent directly (`data_key`) and no
|
|
54
|
+
additional frames are sent.
|
|
55
|
+
"""
|
|
56
|
+
d, b = binary_serialization(pymodaq_object=pymodaq_object)
|
|
57
|
+
return {data_key: d, "additional_payload": b}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def run_coordinator():
|
|
61
|
+
command = [sys.executable, '-m', 'pyleco.coordinators.coordinator']
|
|
62
|
+
subprocess.Popen(command)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def start_coordinator():
|
|
66
|
+
from pyleco.directors.director import Director
|
|
67
|
+
try:
|
|
68
|
+
with Director(actor="COORDINATOR") as director:
|
|
69
|
+
if director.communicator.namespace is None:
|
|
70
|
+
run_coordinator()
|
|
71
|
+
else:
|
|
72
|
+
logger.info('Coordinator already running')
|
|
73
|
+
except ConnectionRefusedError:
|
|
74
|
+
run_coordinator()
|
pymodaq/utils/logger.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
from pymodaq_utils.logger import set_logger, get_module_name, get_set_config_dir, get_base_logger
|
|
2
|
+
|
|
3
|
+
from pymodaq_utils.warnings import deprecation_msg
|
|
4
|
+
|
|
5
|
+
deprecation_msg('Importing logger stuff from pymodaq is deprecated in pymodaq>5.0.0,'
|
|
6
|
+
'please use the pymodaq_utils package')
|
|
File without changes
|