pymodaq 5.0.18__py3-none-any.whl → 5.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pymodaq might be problematic. Click here for more details.
- pymodaq/__init__.py +23 -11
- pymodaq/control_modules/__init__.py +1 -0
- pymodaq/control_modules/daq_move.py +451 -246
- 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.py → daq_move_ui/ui_base.py} +168 -210
- 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 +113 -101
- pymodaq/control_modules/daq_viewer_ui.py +41 -31
- pymodaq/control_modules/mocks.py +2 -2
- pymodaq/control_modules/move_utility_classes.py +113 -41
- pymodaq/control_modules/thread_commands.py +137 -0
- pymodaq/control_modules/ui_utils.py +72 -0
- pymodaq/control_modules/utils.py +107 -63
- pymodaq/control_modules/viewer_utility_classes.py +13 -17
- pymodaq/dashboard.py +1294 -625
- pymodaq/examples/qt_less_standalone_module.py +48 -11
- pymodaq/extensions/__init__.py +8 -3
- 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 +1 -1
- 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 +71 -297
- pymodaq/extensions/daq_logger/daq_logger.py +7 -12
- pymodaq/extensions/daq_logger/h5logging.py +1 -1
- pymodaq/extensions/daq_scan.py +30 -55
- 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 +3 -34
- 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/actuator_controller.py +3 -2
- pymodaq/extensions/pid/daq_move_PID.py +107 -30
- pymodaq/extensions/pid/pid_controller.py +613 -287
- pymodaq/extensions/pid/utils.py +8 -5
- pymodaq/extensions/utils.py +17 -2
- pymodaq/resources/config_template.toml +57 -0
- pymodaq/resources/preset_default.xml +1 -1
- pymodaq/utils/config.py +10 -4
- pymodaq/utils/daq_utils.py +14 -0
- pymodaq/utils/data.py +1 -0
- pymodaq/utils/gui_utils/loader_utils.py +25 -15
- pymodaq/utils/h5modules/module_saving.py +134 -22
- pymodaq/utils/leco/daq_move_LECODirector.py +123 -84
- pymodaq/utils/leco/daq_xDviewer_LECODirector.py +84 -97
- pymodaq/utils/leco/director_utils.py +32 -16
- pymodaq/utils/leco/leco_director.py +104 -27
- pymodaq/utils/leco/pymodaq_listener.py +186 -97
- pymodaq/utils/leco/rpc_method_definitions.py +43 -0
- pymodaq/utils/leco/utils.py +25 -25
- pymodaq/utils/managers/batchscan_manager.py +12 -11
- pymodaq/utils/managers/modules_manager.py +74 -33
- pymodaq/utils/managers/overshoot_manager.py +11 -10
- pymodaq/utils/managers/preset_manager.py +100 -64
- pymodaq/utils/managers/preset_manager_utils.py +163 -107
- pymodaq/utils/managers/remote_manager.py +21 -16
- pymodaq/utils/scanner/scan_factory.py +12 -3
- pymodaq/utils/scanner/scan_selector.py +1 -3
- pymodaq/utils/scanner/scanner.py +35 -6
- pymodaq/utils/scanner/scanners/_1d_scanners.py +15 -46
- pymodaq/utils/scanner/scanners/_2d_scanners.py +21 -68
- pymodaq/utils/scanner/scanners/sequential.py +50 -31
- pymodaq/utils/scanner/scanners/tabular.py +45 -28
- {pymodaq-5.0.18.dist-info → pymodaq-5.1.0.dist-info}/METADATA +7 -6
- pymodaq-5.1.0.dist-info/RECORD +154 -0
- {pymodaq-5.0.18.dist-info → pymodaq-5.1.0.dist-info}/entry_points.txt +0 -2
- pymodaq/extensions/bayesian/bayesian_optimisation.py +0 -690
- pymodaq/utils/leco/desktop.ini +0 -2
- pymodaq-5.0.18.dist-info/RECORD +0 -121
- {pymodaq-5.0.18.dist-info → pymodaq-5.1.0.dist-info}/WHEEL +0 -0
- {pymodaq-5.0.18.dist-info → pymodaq-5.1.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,19 +3,31 @@ from pymodaq_utils.enums import StrEnum
|
|
|
3
3
|
|
|
4
4
|
import logging
|
|
5
5
|
from threading import Event
|
|
6
|
-
from typing import Optional, Union, List, Type
|
|
6
|
+
from typing import cast, Optional, Union, List, Sequence, Type
|
|
7
7
|
|
|
8
8
|
from pyleco.core import COORDINATOR_PORT
|
|
9
|
+
from pyleco.json_utils.errors import JSONRPCError, RECEIVER_UNKNOWN, NODE_UNKNOWN
|
|
9
10
|
from pyleco.utils.listener import Listener, PipeHandler
|
|
10
11
|
from qtpy.QtCore import QObject, Signal # type: ignore
|
|
11
12
|
|
|
13
|
+
from pymodaq_data.data import DataWithAxes
|
|
14
|
+
from pymodaq_utils.serialize.factory import SerializableFactory, SerializableBase
|
|
12
15
|
from pymodaq_utils.utils import ThreadCommand
|
|
13
16
|
from pymodaq_gui.parameter import ioxml
|
|
14
|
-
from
|
|
15
|
-
|
|
17
|
+
from pymodaq_gui.parameter.utils import ParameterWithPath
|
|
18
|
+
|
|
16
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
|
+
)
|
|
17
28
|
|
|
18
29
|
|
|
30
|
+
# Commands for ThreadCommand
|
|
19
31
|
class LECOClientCommands(StrEnum):
|
|
20
32
|
LECO_CONNECTED = "leco_connected"
|
|
21
33
|
LECO_DISCONNECTED = "leco_disconnected"
|
|
@@ -24,15 +36,28 @@ class LECOClientCommands(StrEnum):
|
|
|
24
36
|
class LECOCommands(StrEnum):
|
|
25
37
|
CONNECT = "ini_connection"
|
|
26
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'
|
|
27
43
|
|
|
28
44
|
|
|
29
45
|
class LECOMoveCommands(StrEnum):
|
|
30
46
|
POSITION = 'position_is'
|
|
31
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'
|
|
32
54
|
|
|
33
55
|
|
|
34
56
|
class LECOViewerCommands(StrEnum):
|
|
35
57
|
DATA_READY = 'data_ready'
|
|
58
|
+
GRAB = 'grab'
|
|
59
|
+
SNAP = 'snap'
|
|
60
|
+
STOP = 'stop_grab'
|
|
36
61
|
|
|
37
62
|
|
|
38
63
|
class ListenerSignals(QObject):
|
|
@@ -53,54 +78,120 @@ class PymodaqPipeHandler(PipeHandler):
|
|
|
53
78
|
def __init__(self, name: str, signals: ListenerSignals, **kwargs) -> None:
|
|
54
79
|
super().__init__(name, **kwargs)
|
|
55
80
|
self.signals = signals
|
|
56
|
-
|
|
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
|
+
)
|
|
57
93
|
|
|
58
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)
|
|
59
102
|
|
|
60
103
|
def register_rpc_methods(self) -> None:
|
|
61
104
|
super().register_rpc_methods()
|
|
62
|
-
self.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
self.register_rpc_method(self.
|
|
66
|
-
self.register_rpc_method(self.
|
|
67
|
-
self.
|
|
68
|
-
|
|
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)
|
|
69
121
|
|
|
70
122
|
@staticmethod
|
|
71
|
-
def
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
|
75
132
|
|
|
76
133
|
# generic commands
|
|
77
|
-
def set_info(self,
|
|
78
|
-
|
|
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))
|
|
79
146
|
|
|
80
147
|
# detector commands
|
|
81
|
-
def
|
|
82
|
-
self.signals.cmd_signal.emit(ThreadCommand(
|
|
148
|
+
def send_data_grab(self,) -> None:
|
|
149
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOViewerCommands.GRAB))
|
|
83
150
|
|
|
84
|
-
#
|
|
85
|
-
def
|
|
86
|
-
|
|
87
|
-
self.signals.cmd_signal.emit(ThreadCommand("move_abs", attribute=[pos]))
|
|
151
|
+
# detector commands
|
|
152
|
+
def send_data_snap(self,) -> None:
|
|
153
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOViewerCommands.SNAP))
|
|
88
154
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
self
|
|
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))
|
|
92
181
|
|
|
93
182
|
def move_home(self) -> None:
|
|
94
|
-
self.signals.cmd_signal.emit(ThreadCommand(
|
|
183
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOMoveCommands.MOVE_HOME))
|
|
95
184
|
|
|
96
185
|
def get_actuator_value(self) -> None:
|
|
97
186
|
"""Request that the actuator value is sent later on."""
|
|
98
187
|
# according to DAQ_Move, this supersedes "check_position"
|
|
99
|
-
self.signals.cmd_signal.emit(ThreadCommand(
|
|
188
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOMoveCommands.GET_ACTUATOR_VALUE))
|
|
100
189
|
|
|
101
190
|
def stop_motion(self,) -> None:
|
|
102
|
-
|
|
103
|
-
|
|
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))
|
|
104
195
|
|
|
105
196
|
|
|
106
197
|
# to be able to separate them later on
|
|
@@ -115,7 +206,7 @@ class PymodaqListener(Listener):
|
|
|
115
206
|
:param host: Host name of the communication server.
|
|
116
207
|
:param port: Port number of the communication server.
|
|
117
208
|
"""
|
|
118
|
-
|
|
209
|
+
remote_names: set[str]
|
|
119
210
|
|
|
120
211
|
local_methods = ["pong", "set_log_level"]
|
|
121
212
|
|
|
@@ -129,6 +220,7 @@ class PymodaqListener(Listener):
|
|
|
129
220
|
**kwargs) -> None:
|
|
130
221
|
super().__init__(name, host, port, logger=logger, timeout=timeout,
|
|
131
222
|
**kwargs)
|
|
223
|
+
self.remote_names = set()
|
|
132
224
|
self.signals = ListenerSignals()
|
|
133
225
|
# self.signals.message.connect(self.handle_message)
|
|
134
226
|
self.cmd_signal = self.signals.cmd_signal
|
|
@@ -176,13 +268,17 @@ class ActorListener(PymodaqListener):
|
|
|
176
268
|
|
|
177
269
|
def start_listen(self) -> None:
|
|
178
270
|
super().start_listen()
|
|
179
|
-
self.message_handler.register_rpc_method(
|
|
271
|
+
self.message_handler.register_rpc_method(
|
|
272
|
+
self.set_remote_name, name=GenericMethods.SET_REMOTE_NAME
|
|
273
|
+
)
|
|
180
274
|
|
|
181
275
|
def set_remote_name(self, name: str) -> None:
|
|
182
276
|
"""Define what the name of the remote for answers is."""
|
|
183
|
-
self.
|
|
277
|
+
self.remote_names.add(name)
|
|
278
|
+
|
|
279
|
+
def remove_remote_name(self, name: str) -> None:
|
|
280
|
+
self.remote_names.discard(name)
|
|
184
281
|
|
|
185
|
-
# @Slot(ThreadCommand)
|
|
186
282
|
def queue_command(self, command: ThreadCommand) -> None:
|
|
187
283
|
"""Queue a command to send it via LECO to the server."""
|
|
188
284
|
|
|
@@ -203,71 +299,64 @@ class ActorListener(PymodaqListener):
|
|
|
203
299
|
finally:
|
|
204
300
|
self.cmd_signal.emit(ThreadCommand('disconnected'))
|
|
205
301
|
|
|
206
|
-
elif command.
|
|
207
|
-
#
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
)
|
|
252
|
-
elif isinstance(value, dict):
|
|
253
|
-
self.communicator.ask_rpc(receiver=self.remote_name, method="set_x_axis", **value)
|
|
254
|
-
else:
|
|
255
|
-
raise ValueError("Nothing to send!")
|
|
256
|
-
|
|
257
|
-
elif command.command == 'y_axis':
|
|
258
|
-
value = command.attribute[0] # type: ignore
|
|
259
|
-
if isinstance(value, SERIALIZABLE):
|
|
260
|
-
self.communicator.ask_rpc(receiver=self.remote_name,
|
|
261
|
-
method="set_y_axis",
|
|
262
|
-
**binary_serialization_to_kwargs(value),
|
|
263
|
-
)
|
|
264
|
-
elif isinstance(value, dict):
|
|
265
|
-
self.communicator.ask_rpc(receiver=self.remote_name, method="set_y_axis", **value)
|
|
266
|
-
else:
|
|
267
|
-
raise ValueError("Nothing to send!")
|
|
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
|
+
)
|
|
268
347
|
|
|
269
348
|
else:
|
|
270
|
-
raise IOError(
|
|
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)
|
|
271
360
|
|
|
272
361
|
|
|
273
362
|
# to be able to separate them later on
|
|
@@ -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"
|
pymodaq/utils/leco/utils.py
CHANGED
|
@@ -3,27 +3,17 @@ import subprocess
|
|
|
3
3
|
import sys
|
|
4
4
|
from typing import Any, Optional, Union, get_args, TypeVar
|
|
5
5
|
|
|
6
|
+
|
|
6
7
|
from pymodaq.utils import data
|
|
7
|
-
|
|
8
|
-
from pymodaq_utils.serialize.serializer_legacy import Serializer, DeSerializer, SerializableFactory
|
|
8
|
+
from pymodaq_utils.serialize.factory import SerializableFactory
|
|
9
9
|
|
|
10
|
-
# type: ignore # noqa
|
|
11
10
|
from pymodaq_utils.logger import set_logger
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
logger = set_logger('leco_utils')
|
|
15
|
-
|
|
16
|
-
JSON_TYPES = Union[str, int, float]
|
|
17
|
-
|
|
14
|
+
JSON_TYPES = Union[str, int, float, list]
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
def serialize_object(pymodaq_object: Union[SERIALIZABLE, Any]) -> Union[str, Any]:
|
|
21
|
-
"""Serialize a pymodaq object, if it is not JSON compatible."""
|
|
22
|
-
if isinstance(pymodaq_object, get_args(JSON_TYPES)):
|
|
23
|
-
return pymodaq_object
|
|
24
|
-
else:
|
|
25
|
-
return Serializer(pymodaq_object).to_b64_string() # will raise a proper error if the object
|
|
26
|
-
#is not serializable
|
|
16
|
+
ser_factory = SerializableFactory()
|
|
27
17
|
|
|
28
18
|
|
|
29
19
|
## this form below is to be compatible with python <= 3.10
|
|
@@ -36,22 +26,33 @@ for klass in ser_factory.get_serializables()[1:]:
|
|
|
36
26
|
def binary_serialization(
|
|
37
27
|
pymodaq_object: Union[SERIALIZABLE, Any],
|
|
38
28
|
) -> tuple[Optional[Any], Optional[list[bytes]]]:
|
|
39
|
-
"""Serialize (binary) a pymodaq object, if it is not JSON compatible.
|
|
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
|
+
"""
|
|
40
38
|
if isinstance(pymodaq_object, get_args(JSON_TYPES)):
|
|
41
39
|
return pymodaq_object, None
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
else:
|
|
45
|
-
raise ValueError(
|
|
46
|
-
f"{pymodaq_object} of type '{type(pymodaq_object).__name__}' is neither "
|
|
47
|
-
"JSON serializable, nor via PyMoDAQ."
|
|
48
|
-
)
|
|
40
|
+
return None, [SerializableFactory().get_apply_serializer(pymodaq_object)]
|
|
41
|
+
|
|
49
42
|
|
|
50
43
|
|
|
51
44
|
def binary_serialization_to_kwargs(
|
|
52
45
|
pymodaq_object: Union[SERIALIZABLE, Any], data_key: str = "data"
|
|
53
46
|
) -> dict[str, Any]:
|
|
54
|
-
"""Create a dictionary of data parameters and of additional payload to send.
|
|
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
|
+
"""
|
|
55
56
|
d, b = binary_serialization(pymodaq_object=pymodaq_object)
|
|
56
57
|
return {data_key: d, "additional_payload": b}
|
|
57
58
|
|
|
@@ -69,6 +70,5 @@ def start_coordinator():
|
|
|
69
70
|
run_coordinator()
|
|
70
71
|
else:
|
|
71
72
|
logger.info('Coordinator already running')
|
|
72
|
-
except ConnectionRefusedError
|
|
73
|
+
except ConnectionRefusedError:
|
|
73
74
|
run_coordinator()
|
|
74
|
-
|
|
@@ -4,6 +4,7 @@ import sys
|
|
|
4
4
|
from typing import List
|
|
5
5
|
|
|
6
6
|
from qtpy import QtWidgets, QtCore
|
|
7
|
+
from qtpy.QtWidgets import QMessageBox, QDialog, QDialogButtonBox
|
|
7
8
|
|
|
8
9
|
from pymodaq_utils.logger import set_logger, get_module_name
|
|
9
10
|
from pymodaq_utils import config as config_mod
|
|
@@ -59,13 +60,13 @@ class BatchManager(ParameterManager):
|
|
|
59
60
|
self.batch_path = path
|
|
60
61
|
|
|
61
62
|
if msgbox:
|
|
62
|
-
msgBox =
|
|
63
|
+
msgBox = QMessageBox()
|
|
63
64
|
msgBox.setText("Scan Batch Manager?")
|
|
64
65
|
msgBox.setInformativeText("What do you want to do?")
|
|
65
|
-
cancel_button = msgBox.addButton(
|
|
66
|
-
new_button = msgBox.addButton("New",
|
|
67
|
-
modify_button = msgBox.addButton('Modify',
|
|
68
|
-
msgBox.setDefaultButton(
|
|
66
|
+
cancel_button = msgBox.addButton(QMessageBox.StandardButton.Cancel)
|
|
67
|
+
new_button = msgBox.addButton("New", QMessageBox.ButtonRole.AcceptRole)
|
|
68
|
+
modify_button = msgBox.addButton('Modify', QMessageBox.ButtonRole.AcceptRole)
|
|
69
|
+
msgBox.setDefaultButton(QMessageBox.StandardButton.Cancel)
|
|
69
70
|
ret = msgBox.exec()
|
|
70
71
|
|
|
71
72
|
if msgBox.clickedButton() == new_button:
|
|
@@ -154,16 +155,16 @@ class BatchManager(ParameterManager):
|
|
|
154
155
|
def show_tree(self):
|
|
155
156
|
|
|
156
157
|
|
|
157
|
-
dialog =
|
|
158
|
+
dialog = QDialog()
|
|
158
159
|
dialog.setLayout(QtWidgets.QVBoxLayout())
|
|
159
160
|
|
|
160
161
|
widget_all_settings = QtWidgets.QWidget()
|
|
161
162
|
|
|
162
163
|
dialog.layout().addWidget(widget_all_settings)
|
|
163
|
-
buttonBox =
|
|
164
|
-
buttonBox.addButton(
|
|
164
|
+
buttonBox = QDialogButtonBox(parent=dialog)
|
|
165
|
+
buttonBox.addButton("Save", QDialogButtonBox.ButtonRole.AcceptRole)
|
|
165
166
|
buttonBox.accepted.connect(dialog.accept)
|
|
166
|
-
buttonBox.addButton(
|
|
167
|
+
buttonBox.addButton("Cancel", QDialogButtonBox.ButtonRole.RejectRole)
|
|
167
168
|
buttonBox.rejected.connect(dialog.reject)
|
|
168
169
|
|
|
169
170
|
dialog.layout().addWidget(buttonBox)
|
|
@@ -186,11 +187,11 @@ class BatchManager(ParameterManager):
|
|
|
186
187
|
|
|
187
188
|
res = dialog.exec()
|
|
188
189
|
|
|
189
|
-
if res ==
|
|
190
|
+
if res == QDialog.DialogCode.Accepted:
|
|
190
191
|
ioxml.parameter_to_xml_file(
|
|
191
192
|
self.settings, self.batch_path.joinpath(self.settings.child('filename').value()))
|
|
192
193
|
|
|
193
|
-
return res ==
|
|
194
|
+
return res == QDialog.DialogCode.Accepted
|
|
194
195
|
|
|
195
196
|
def set_scanner_settings(self, settings_tree: QtWidgets.QWidget):
|
|
196
197
|
while True:
|