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,1238 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Created the 29/07/2022
|
|
4
|
+
|
|
5
|
+
@author: Sebastien Weber
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import numbers
|
|
11
|
+
from importlib import import_module
|
|
12
|
+
from numbers import Number
|
|
13
|
+
|
|
14
|
+
import sys
|
|
15
|
+
from typing import List, Union, Optional, Dict, TypeVar, TYPE_CHECKING
|
|
16
|
+
import numpy as np
|
|
17
|
+
|
|
18
|
+
from qtpy.QtCore import QObject, Signal, QThread, Slot, Qt, QTimer
|
|
19
|
+
from qtpy import QtWidgets
|
|
20
|
+
|
|
21
|
+
from easydict import EasyDict as edict
|
|
22
|
+
|
|
23
|
+
from pymodaq_utils.logger import set_logger, get_module_name
|
|
24
|
+
from pymodaq_utils.utils import find_keys_from_val
|
|
25
|
+
from pymodaq_utils import utils
|
|
26
|
+
from pymodaq.utils.gui_utils import get_splash_sc
|
|
27
|
+
from pymodaq_utils import config as config_mod
|
|
28
|
+
from pymodaq.utils.exceptions import ActuatorError
|
|
29
|
+
from pymodaq_utils.warnings import deprecation_msg
|
|
30
|
+
from pymodaq.utils.data import DataToExport, DataActuator
|
|
31
|
+
from pymodaq_data.h5modules.backends import Node
|
|
32
|
+
|
|
33
|
+
from pymodaq_gui.h5modules.saving import H5Saver
|
|
34
|
+
from pymodaq_gui.parameter import ioxml, Parameter
|
|
35
|
+
from pymodaq_gui.parameter import utils as putils
|
|
36
|
+
from pymodaq_gui.utils.utils import mkQApp
|
|
37
|
+
|
|
38
|
+
from pymodaq.utils.h5modules import module_saving
|
|
39
|
+
from pymodaq.control_modules.utils import ParameterControlModule
|
|
40
|
+
from pymodaq.control_modules.thread_commands import (
|
|
41
|
+
ThreadStatus,
|
|
42
|
+
ThreadStatusMove,
|
|
43
|
+
ControlToHardwareMove,
|
|
44
|
+
UiToMainMove,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
from pymodaq.control_modules.move_utility_classes import (
|
|
49
|
+
ThreadCommand,
|
|
50
|
+
MoveCommand,
|
|
51
|
+
DAQ_Move_base,
|
|
52
|
+
DataActuatorType,
|
|
53
|
+
check_units,
|
|
54
|
+
DataUnitError,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
from pymodaq.control_modules.move_utility_classes import params as daq_move_params
|
|
59
|
+
from pymodaq.utils.leco.pymodaq_listener import (
|
|
60
|
+
MoveActorListener,
|
|
61
|
+
LECOMoveCommands,
|
|
62
|
+
LECOCommands,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
from pymodaq.utils.daq_utils import get_plugins
|
|
66
|
+
from pymodaq import Q_, Unit
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
from pymodaq.control_modules.daq_move_ui.factory import ActuatorUIFactory
|
|
70
|
+
from pymodaq.utils.config import Config as ControlModulesConfig
|
|
71
|
+
|
|
72
|
+
if TYPE_CHECKING:
|
|
73
|
+
from pymodaq.control_modules.daq_move_ui.ui_base import DAQ_Move_UI_Base
|
|
74
|
+
|
|
75
|
+
local_path = config_mod.get_set_local_dir()
|
|
76
|
+
sys.path.append(str(local_path))
|
|
77
|
+
logger = set_logger(get_module_name(__file__))
|
|
78
|
+
|
|
79
|
+
config_utils = config_mod.Config()
|
|
80
|
+
config = ControlModulesConfig()
|
|
81
|
+
|
|
82
|
+
HardwareController = TypeVar("HardwareController")
|
|
83
|
+
|
|
84
|
+
DAQ_Move_Actuators = get_plugins("daq_move")
|
|
85
|
+
ACTUATOR_TYPES = [mov["name"] for mov in DAQ_Move_Actuators]
|
|
86
|
+
if len(ACTUATOR_TYPES) == 0:
|
|
87
|
+
raise ActuatorError("No installed Actuator")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
STATUS_WAIT_TIME = 1000
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class DAQ_Move(ParameterControlModule):
|
|
94
|
+
"""Main PyMoDAQ class to drive actuators
|
|
95
|
+
|
|
96
|
+
Qt object and generic UI to drive actuators.
|
|
97
|
+
|
|
98
|
+
Attributes
|
|
99
|
+
----------
|
|
100
|
+
init_signal: Signal[bool]
|
|
101
|
+
This signal is emitted when the chosen actuator is correctly initialized
|
|
102
|
+
move_done_signal: Signal[str, DataActuator]
|
|
103
|
+
This signal is emitted when the chosen actuator finished its action. It gives the actuator's name and current
|
|
104
|
+
value
|
|
105
|
+
bounds_signal: Signal[bool]
|
|
106
|
+
This signal is emitted when the actuator reached defined limited boundaries.
|
|
107
|
+
|
|
108
|
+
See Also
|
|
109
|
+
--------
|
|
110
|
+
:class:`ControlModule`, :class:`ParameterManager`
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
settings_name = "daq_move_settings"
|
|
114
|
+
|
|
115
|
+
move_done_signal = Signal(DataActuator)
|
|
116
|
+
current_value_signal = Signal(DataActuator)
|
|
117
|
+
bounds_signal = Signal(bool)
|
|
118
|
+
|
|
119
|
+
params = daq_move_params + [
|
|
120
|
+
{'title': 'Saver Settings:', 'name': 'saver_settings', 'type': 'group',
|
|
121
|
+
'visible': False, 'children': H5Saver.params}]
|
|
122
|
+
|
|
123
|
+
listener_class = MoveActorListener
|
|
124
|
+
ui: Optional[DAQ_Move_UI_Base]
|
|
125
|
+
|
|
126
|
+
def __init__(
|
|
127
|
+
self, parent=None, title="DAQ Move", ui_identifier: Optional[str] = None, **kwargs
|
|
128
|
+
) -> None:
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
Parameters
|
|
132
|
+
----------
|
|
133
|
+
parent: QWidget or None
|
|
134
|
+
parent: QWidget or None
|
|
135
|
+
if it is a valid QWidget, it will hold the user interface to drive it
|
|
136
|
+
title: str
|
|
137
|
+
The unique (should be unique) string identifier for the underlying actuator
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
self.logger = set_logger(f"{logger.name}.{title}")
|
|
141
|
+
self.logger.info(f"Initializing DAQ_Move: {title}")
|
|
142
|
+
|
|
143
|
+
super().__init__(action_list=("save", "update"), **kwargs)
|
|
144
|
+
|
|
145
|
+
if not (
|
|
146
|
+
ui_identifier is not None and ui_identifier in ActuatorUIFactory.keys()
|
|
147
|
+
):
|
|
148
|
+
ui_identifier = config("actuator", "ui")
|
|
149
|
+
self.settings.child("main_settings", "ui_type").setValue(ui_identifier)
|
|
150
|
+
self.settings.child("main_settings", "ui_type").setOpts(readonly=True)
|
|
151
|
+
|
|
152
|
+
DAQ_Move_UI = ActuatorUIFactory.get(ui_identifier)
|
|
153
|
+
|
|
154
|
+
self.parent = parent
|
|
155
|
+
if parent is not None:
|
|
156
|
+
self.ui = DAQ_Move_UI(parent, title)
|
|
157
|
+
else:
|
|
158
|
+
self.ui = None
|
|
159
|
+
|
|
160
|
+
if self.ui is not None:
|
|
161
|
+
self.ui.actuators = ACTUATOR_TYPES
|
|
162
|
+
self.ui.set_settings_tree(self.settings_tree)
|
|
163
|
+
self.ui.command_sig.connect(self.process_ui_cmds)
|
|
164
|
+
|
|
165
|
+
self.splash_sc = get_splash_sc()
|
|
166
|
+
self._title = title
|
|
167
|
+
if len(ACTUATOR_TYPES) > 0: # will be 0 if no valid plugins are installed
|
|
168
|
+
self.actuator = kwargs.get("actuator", ACTUATOR_TYPES[0])
|
|
169
|
+
|
|
170
|
+
self._module_and_data_saver: module_saving.ActuatorTimeSaver = None
|
|
171
|
+
for hidden_param in ('custom_name',
|
|
172
|
+
'current_scan_name',
|
|
173
|
+
'current_scan_path',
|
|
174
|
+
'current_h5_file',
|
|
175
|
+
'new_file',
|
|
176
|
+
'base_name'):
|
|
177
|
+
self.settings.child('saver_settings', hidden_param).setOpts(visible=False)
|
|
178
|
+
|
|
179
|
+
self._move_done_bool = True
|
|
180
|
+
|
|
181
|
+
self._current_value = DataActuator(title, units=self.units)
|
|
182
|
+
self._target_value = DataActuator(title, units=self.units)
|
|
183
|
+
self._relative_value = DataActuator(title, units=self.units)
|
|
184
|
+
|
|
185
|
+
self._refresh_timer = QTimer()
|
|
186
|
+
self._refresh_timer.timeout.connect(self.get_actuator_value)
|
|
187
|
+
|
|
188
|
+
def process_ui_cmds(self, cmd: utils.ThreadCommand):
|
|
189
|
+
"""Process commands sent by actions done in the ui
|
|
190
|
+
|
|
191
|
+
Parameters
|
|
192
|
+
----------
|
|
193
|
+
cmd: ThreadCommand
|
|
194
|
+
Possible values are :
|
|
195
|
+
* init
|
|
196
|
+
* quit
|
|
197
|
+
* get_value
|
|
198
|
+
* loop_get_value
|
|
199
|
+
* find_home
|
|
200
|
+
* stop
|
|
201
|
+
* move_abs
|
|
202
|
+
* move_rel
|
|
203
|
+
* show_log
|
|
204
|
+
* actuator_changed
|
|
205
|
+
* rel_value
|
|
206
|
+
* show_config
|
|
207
|
+
"""
|
|
208
|
+
if cmd.command == UiToMainMove.INIT:
|
|
209
|
+
self.init_hardware(cmd.attribute[0])
|
|
210
|
+
elif cmd.command == UiToMainMove.QUIT:
|
|
211
|
+
self.quit_fun()
|
|
212
|
+
elif cmd.command == UiToMainMove.GET_VALUE:
|
|
213
|
+
self.get_actuator_value()
|
|
214
|
+
elif cmd.command == UiToMainMove.LOOP_GET_VALUE:
|
|
215
|
+
self.get_continuous_actuator_value(cmd.attribute)
|
|
216
|
+
elif cmd.command == UiToMainMove.FIND_HOME:
|
|
217
|
+
self.move_home()
|
|
218
|
+
elif cmd.command == UiToMainMove.STOP:
|
|
219
|
+
self.stop_motion()
|
|
220
|
+
elif cmd.command == UiToMainMove.MOVE_ABS:
|
|
221
|
+
data_act: DataActuator = cmd.attribute
|
|
222
|
+
if (
|
|
223
|
+
not Unit(data_act.units).is_compatible_with(self.units)
|
|
224
|
+
and data_act.units != ""
|
|
225
|
+
):
|
|
226
|
+
data_act.force_units(self.units)
|
|
227
|
+
self.move_abs(data_act)
|
|
228
|
+
elif cmd.command == UiToMainMove.MOVE_REL:
|
|
229
|
+
data_act: DataActuator = cmd.attribute
|
|
230
|
+
if (
|
|
231
|
+
not Unit(data_act.units).is_compatible_with(self.units)
|
|
232
|
+
and data_act.units != ""
|
|
233
|
+
):
|
|
234
|
+
data_act.force_units(self.units)
|
|
235
|
+
self.move_rel(data_act)
|
|
236
|
+
elif cmd.command == UiToMainMove.SHOW_LOG:
|
|
237
|
+
self.show_log()
|
|
238
|
+
elif cmd.command == UiToMainMove.SHOW_CONFIG:
|
|
239
|
+
self.config = self.show_config(self.config)
|
|
240
|
+
self.ui.config = self.config
|
|
241
|
+
elif cmd.command == UiToMainMove.ACTUATOR_CHANGED:
|
|
242
|
+
self.actuator = cmd.attribute
|
|
243
|
+
elif cmd.command == UiToMainMove.REL_VALUE:
|
|
244
|
+
self._relative_value = cmd.attribute
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
def master(self) -> bool:
|
|
248
|
+
"""Get/Set programmatically the Master/Slave status of an actuator"""
|
|
249
|
+
if self.initialized_state:
|
|
250
|
+
return (
|
|
251
|
+
self.settings["move_settings", "multiaxes", "multi_status"] == "Master"
|
|
252
|
+
)
|
|
253
|
+
else:
|
|
254
|
+
return True
|
|
255
|
+
|
|
256
|
+
@master.setter
|
|
257
|
+
def master(self, is_master: bool):
|
|
258
|
+
if self.initialized_state:
|
|
259
|
+
self.settings.child("move_settings", "multiaxes", "multi_status").setValue(
|
|
260
|
+
"Master" if is_master else "Slave"
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
def append_data(
|
|
264
|
+
self, dte: Optional[DataToExport] = None, where: Union[Node, str, None] = None
|
|
265
|
+
):
|
|
266
|
+
"""Appends current DataToExport to an ActuatorEnlargeableSaver
|
|
267
|
+
|
|
268
|
+
Parameters
|
|
269
|
+
----------
|
|
270
|
+
dte: DataToExport, optional
|
|
271
|
+
where: Node or str
|
|
272
|
+
See Also
|
|
273
|
+
--------
|
|
274
|
+
ActuatorEnlargeableSaver
|
|
275
|
+
"""
|
|
276
|
+
if dte is None:
|
|
277
|
+
dte = DataToExport(name=self.title, data=[self._current_value])
|
|
278
|
+
self._add_data_to_saver(dte, where=where)
|
|
279
|
+
self.settings.child('saver_settings', 'N_saved').setValue(self.settings['saver_settings', 'N_saved'] + 1)
|
|
280
|
+
|
|
281
|
+
def _add_data_to_saver(self, data: DataToExport, where=None, **kwargs):
|
|
282
|
+
"""Adds DataToExport data to the current node using the declared module_and_data_saver
|
|
283
|
+
|
|
284
|
+
Filters the data to be saved by DataSource as specified in the current H5Saver (see self.module_and_data_saver)
|
|
285
|
+
|
|
286
|
+
Parameters
|
|
287
|
+
----------
|
|
288
|
+
data: DataToExport
|
|
289
|
+
The data to be saved
|
|
290
|
+
kwargs: dict
|
|
291
|
+
Other named parameters to be passed as is to the module_and_data_saver
|
|
292
|
+
|
|
293
|
+
See Also
|
|
294
|
+
--------
|
|
295
|
+
DetectorSaver, DetectorEnlargeableSaver, DetectorExtendedSaver
|
|
296
|
+
|
|
297
|
+
"""
|
|
298
|
+
# todo: test this for logging
|
|
299
|
+
|
|
300
|
+
node = self.module_and_data_saver.get_set_node(where)
|
|
301
|
+
self.module_and_data_saver.add_data(node, data, **kwargs)
|
|
302
|
+
|
|
303
|
+
def stop_motion(self):
|
|
304
|
+
"""Stop any motion"""
|
|
305
|
+
try:
|
|
306
|
+
self.command_hardware.emit(ThreadCommand(ControlToHardwareMove.STOP_MOTION))
|
|
307
|
+
except Exception as e:
|
|
308
|
+
self.logger.exception(str(e))
|
|
309
|
+
|
|
310
|
+
def move(self, move_command: MoveCommand):
|
|
311
|
+
"""Generic method to trigger the correct action on the actuator
|
|
312
|
+
|
|
313
|
+
Parameters
|
|
314
|
+
----------
|
|
315
|
+
move_command: MoveCommand
|
|
316
|
+
MoveCommand with move_type attribute either:
|
|
317
|
+
* 'abs': performs an absolute action
|
|
318
|
+
* 'rel': performs a relative action
|
|
319
|
+
* 'home': find the actuator's home
|
|
320
|
+
|
|
321
|
+
See Also
|
|
322
|
+
--------
|
|
323
|
+
:meth:`move_abs`, :meth:`move_rel`, :meth:`move_home`, :class:`..utility_classes.MoveCommand`
|
|
324
|
+
|
|
325
|
+
"""
|
|
326
|
+
if move_command.move_type == "abs":
|
|
327
|
+
self.move_abs(move_command.value)
|
|
328
|
+
elif move_command.move_type == "rel":
|
|
329
|
+
self.move_rel(move_command.value)
|
|
330
|
+
elif move_command.move_type == "home":
|
|
331
|
+
self.move_home(move_command.value)
|
|
332
|
+
|
|
333
|
+
def move_abs(self, value: Union[DataActuator, numbers.Number], send_to_tcpip=False):
|
|
334
|
+
"""Move the connected hardware to the absolute value
|
|
335
|
+
|
|
336
|
+
Returns nothing but the move_done_signal will be send once the action is done
|
|
337
|
+
|
|
338
|
+
Parameters
|
|
339
|
+
----------
|
|
340
|
+
value: ndarray
|
|
341
|
+
The value the actuator should reach
|
|
342
|
+
send_to_tcpip: bool
|
|
343
|
+
if True, this position is send through the TCP/IP communication canal
|
|
344
|
+
"""
|
|
345
|
+
try:
|
|
346
|
+
if isinstance(value, Number):
|
|
347
|
+
value = DataActuator(
|
|
348
|
+
self.title, data=[np.array([value])], units=self.units
|
|
349
|
+
)
|
|
350
|
+
self._send_to_tcpip = send_to_tcpip
|
|
351
|
+
if value != self._current_value:
|
|
352
|
+
if self.ui is not None:
|
|
353
|
+
self.ui.move_done = False
|
|
354
|
+
self._move_done_bool = False
|
|
355
|
+
self._target_value = value
|
|
356
|
+
self.update_status("Moving")
|
|
357
|
+
self.command_hardware.emit(
|
|
358
|
+
ThreadCommand(ControlToHardwareMove.RESET_STOP_MOTION)
|
|
359
|
+
)
|
|
360
|
+
self.command_hardware.emit(
|
|
361
|
+
ThreadCommand(ControlToHardwareMove.MOVE_ABS, attribute=[value])
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
except Exception as e:
|
|
365
|
+
self.logger.exception(str(e))
|
|
366
|
+
|
|
367
|
+
def move_home(self, send_to_tcpip=False):
|
|
368
|
+
"""Move the connected actuator to its home value (if any)
|
|
369
|
+
|
|
370
|
+
Parameters
|
|
371
|
+
----------
|
|
372
|
+
send_to_tcpip: bool
|
|
373
|
+
if True, this position is send through the TCP/IP communication canal
|
|
374
|
+
"""
|
|
375
|
+
self._send_to_tcpip = send_to_tcpip
|
|
376
|
+
try:
|
|
377
|
+
if self.ui is not None:
|
|
378
|
+
self.ui.move_done = False
|
|
379
|
+
self._move_done_bool = False
|
|
380
|
+
self.update_status("Moving")
|
|
381
|
+
self.command_hardware.emit(
|
|
382
|
+
ThreadCommand(ControlToHardwareMove.RESET_STOP_MOTION)
|
|
383
|
+
)
|
|
384
|
+
self.command_hardware.emit(ThreadCommand(ControlToHardwareMove.MOVE_HOME))
|
|
385
|
+
|
|
386
|
+
except Exception as e:
|
|
387
|
+
self.logger.exception(str(e))
|
|
388
|
+
|
|
389
|
+
def move_rel(
|
|
390
|
+
self, rel_value: Union[DataActuator, numbers.Number], send_to_tcpip=False
|
|
391
|
+
):
|
|
392
|
+
"""Move the connected hardware to the relative value
|
|
393
|
+
|
|
394
|
+
Returns nothing but the move_done_signal will be send once the action is done
|
|
395
|
+
|
|
396
|
+
Parameters
|
|
397
|
+
----------
|
|
398
|
+
value: float
|
|
399
|
+
The relative value the actuator should reach
|
|
400
|
+
send_to_tcpip: bool
|
|
401
|
+
if True, this position is send through the TCP/IP communication canal
|
|
402
|
+
"""
|
|
403
|
+
|
|
404
|
+
try:
|
|
405
|
+
if isinstance(rel_value, Number):
|
|
406
|
+
rel_value = DataActuator(
|
|
407
|
+
self.title, data=[np.array([rel_value])], units=self.units
|
|
408
|
+
)
|
|
409
|
+
self._send_to_tcpip = send_to_tcpip
|
|
410
|
+
if self.ui is not None:
|
|
411
|
+
self.ui.move_done = False
|
|
412
|
+
self._move_done_bool = False
|
|
413
|
+
self._target_value = self._current_value + rel_value
|
|
414
|
+
self.update_status("Moving")
|
|
415
|
+
self.command_hardware.emit(
|
|
416
|
+
ThreadCommand(ControlToHardwareMove.RESET_STOP_MOTION)
|
|
417
|
+
)
|
|
418
|
+
self.command_hardware.emit(
|
|
419
|
+
ThreadCommand(ControlToHardwareMove.MOVE_REL, attribute=[rel_value])
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
except Exception as e:
|
|
423
|
+
self.logger.exception(str(e))
|
|
424
|
+
|
|
425
|
+
def move_rel_p(self):
|
|
426
|
+
self.move_rel(self._relative_value)
|
|
427
|
+
|
|
428
|
+
def move_rel_m(self):
|
|
429
|
+
self.move_rel(-self._relative_value)
|
|
430
|
+
|
|
431
|
+
def quit_fun(self):
|
|
432
|
+
"""Programmatic quitting of the current instance of DAQ_Move
|
|
433
|
+
|
|
434
|
+
Des-init the actuator then close the UI parent widget
|
|
435
|
+
"""
|
|
436
|
+
# insert anything that needs to be closed before leaving
|
|
437
|
+
|
|
438
|
+
if self._initialized_state:
|
|
439
|
+
self.init_hardware(False)
|
|
440
|
+
self.quit_signal.emit()
|
|
441
|
+
if self.ui is not None:
|
|
442
|
+
self.ui.close()
|
|
443
|
+
# self.parent.close()
|
|
444
|
+
|
|
445
|
+
def init_hardware(self, do_init=True):
|
|
446
|
+
"""Init or desinit the selected instrument plugin class"""
|
|
447
|
+
if not do_init:
|
|
448
|
+
try:
|
|
449
|
+
self.command_hardware.emit(ThreadCommand(ControlToHardwareMove.CLOSE))
|
|
450
|
+
if self.ui is not None:
|
|
451
|
+
self.ui.actuator_init = False
|
|
452
|
+
except Exception as e:
|
|
453
|
+
self.logger.exception(str(e))
|
|
454
|
+
else:
|
|
455
|
+
try:
|
|
456
|
+
hardware = DAQ_Move_Hardware(
|
|
457
|
+
self._actuator_type, self._current_value, self._title
|
|
458
|
+
)
|
|
459
|
+
self._hardware_thread = QThread()
|
|
460
|
+
hardware.moveToThread(self._hardware_thread)
|
|
461
|
+
|
|
462
|
+
self.command_hardware[ThreadCommand].connect(hardware.queue_command)
|
|
463
|
+
hardware.status_sig[ThreadCommand].connect(self.thread_status)
|
|
464
|
+
self._update_settings_signal[edict].connect(hardware.update_settings)
|
|
465
|
+
|
|
466
|
+
self._hardware_thread.hardware = hardware
|
|
467
|
+
self._hardware_thread.start()
|
|
468
|
+
self.command_hardware.emit(
|
|
469
|
+
ThreadCommand(
|
|
470
|
+
ControlToHardwareMove.INI_STAGE,
|
|
471
|
+
attribute=[
|
|
472
|
+
self.settings.child("move_settings").saveState(),
|
|
473
|
+
self.controller,
|
|
474
|
+
],
|
|
475
|
+
)
|
|
476
|
+
)
|
|
477
|
+
except Exception as e:
|
|
478
|
+
self.logger.exception(str(e))
|
|
479
|
+
|
|
480
|
+
@property
|
|
481
|
+
def initialized_state(self):
|
|
482
|
+
"""bool: status of the actuator's initialization (init or not)"""
|
|
483
|
+
return self._initialized_state
|
|
484
|
+
|
|
485
|
+
@property
|
|
486
|
+
def move_done_bool(self):
|
|
487
|
+
"""bool: status of the actuator's status (done or not)"""
|
|
488
|
+
return self._move_done_bool
|
|
489
|
+
|
|
490
|
+
def value_changed(self, param: Parameter):
|
|
491
|
+
"""Apply changes of value in the settings"""
|
|
492
|
+
super().value_changed(param=param)
|
|
493
|
+
path = self.settings.childPath(param)
|
|
494
|
+
|
|
495
|
+
if param.name() == "refresh_timeout":
|
|
496
|
+
self._refresh_timer.setInterval(param.value())
|
|
497
|
+
|
|
498
|
+
elif param.name() == 'continuous_saving_opt':
|
|
499
|
+
self.settings.child('saver_settings').setOpts(visible=param.value())
|
|
500
|
+
|
|
501
|
+
elif param.name() in putils.iter_children(self.settings.child('saver_settings'), []):
|
|
502
|
+
if param.name() == 'do_save':
|
|
503
|
+
self.setup_continuous_saving(param.value())
|
|
504
|
+
self.h5saver.settings.child(*path[1:]).setValue(param.value())
|
|
505
|
+
|
|
506
|
+
self._update_settings(param=param)
|
|
507
|
+
|
|
508
|
+
def setup_continuous_saving(self, init: bool = True):
|
|
509
|
+
"""Configure the objects dealing with the continuous saving mode"""
|
|
510
|
+
if init:
|
|
511
|
+
self.module_and_data_saver = module_saving.ActuatorTimeSaver(self)
|
|
512
|
+
self.module_and_data_saver.h5saver = self.h5saver
|
|
513
|
+
self.h5saver.settings.child('do_save').sigValueChanged.connect(self._init_continuous_save)
|
|
514
|
+
else:
|
|
515
|
+
self.h5saver.close_file()
|
|
516
|
+
|
|
517
|
+
def _init_continuous_save(self):
|
|
518
|
+
""" Initialize the continuous saving H5Saver object
|
|
519
|
+
|
|
520
|
+
Update the module_and_data_saver attribute as :class:`DetectorTimeSaver` object
|
|
521
|
+
"""
|
|
522
|
+
if self.settings.child('saver_settings', 'do_save').value():
|
|
523
|
+
|
|
524
|
+
self.settings.child('saver_settings', 'base_name').setValue('Data')
|
|
525
|
+
self.settings.child('saver_settings', 'N_saved').show()
|
|
526
|
+
self.settings.child('saver_settings', 'N_saved').setValue(0)
|
|
527
|
+
self.h5saver.init_file(update_h5=True)
|
|
528
|
+
else:
|
|
529
|
+
self.settings.child('saver_settings', 'N_saved').hide()
|
|
530
|
+
|
|
531
|
+
def param_deleted(self, param):
|
|
532
|
+
"""Apply deletion of settings"""
|
|
533
|
+
if param.name() not in putils.iter_children(
|
|
534
|
+
self.settings.child("main_settings"), []
|
|
535
|
+
):
|
|
536
|
+
self._update_settings_signal.emit(
|
|
537
|
+
edict(path=["move_settings"], param=param, change="parent")
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
def child_added(self, param, data):
|
|
541
|
+
"""Apply addition of settings"""
|
|
542
|
+
path = self.settings.childPath(param)
|
|
543
|
+
if "main_settings" not in path:
|
|
544
|
+
self._update_settings_signal.emit(
|
|
545
|
+
edict(path=path, param=data[0].saveState(), change="childAdded")
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
def raise_timeout(self):
|
|
549
|
+
"""Update status with "Timeout occurred" statement and change the timeout flag."""
|
|
550
|
+
self.update_status("Timeout occurred")
|
|
551
|
+
self.wait_position_flag = False
|
|
552
|
+
|
|
553
|
+
@Slot(ThreadCommand)
|
|
554
|
+
def thread_status(
|
|
555
|
+
self, status: ThreadCommand
|
|
556
|
+
): # general function to get datas/infos from all threads back to the main
|
|
557
|
+
"""Get back info (using the ThreadCommand object) from the hardware
|
|
558
|
+
|
|
559
|
+
And re-emit this ThreadCommand using the custom_sig signal if it should be used in a higher level module
|
|
560
|
+
|
|
561
|
+
Commands valid for all control modules are defined in the parent class, here are described only the specific
|
|
562
|
+
ones
|
|
563
|
+
|
|
564
|
+
Parameters
|
|
565
|
+
----------
|
|
566
|
+
status: ThreadCommand
|
|
567
|
+
Possible values are:
|
|
568
|
+
|
|
569
|
+
* **ini_stage**: obtains info from the initialization
|
|
570
|
+
* **get_actuator_value**: update the UI current value
|
|
571
|
+
* **move_done**: update the UI current value and emits the move_done signal
|
|
572
|
+
* **outofbounds**: emits the bounds_signal signal with a True argument
|
|
573
|
+
* **set_allowed_values**: used to change the behaviour of the spinbox controlling absolute values (see
|
|
574
|
+
:meth:`daq_move_ui.set_abs_spinbox_properties`
|
|
575
|
+
* stop: stop the motion
|
|
576
|
+
"""
|
|
577
|
+
|
|
578
|
+
super().thread_status(status, "move")
|
|
579
|
+
|
|
580
|
+
if status.command == ThreadStatusMove.INI_STAGE:
|
|
581
|
+
self.update_status(
|
|
582
|
+
f"Stage initialized: {status.attribute['initialized']} "
|
|
583
|
+
f"info: {status.attribute['info']}"
|
|
584
|
+
)
|
|
585
|
+
if status.attribute["initialized"]:
|
|
586
|
+
self.controller = status.attribute["controller"]
|
|
587
|
+
if self.ui is not None:
|
|
588
|
+
self.ui.actuator_init = True
|
|
589
|
+
self._initialized_state = True
|
|
590
|
+
else:
|
|
591
|
+
self._initialized_state = False
|
|
592
|
+
if self._initialized_state:
|
|
593
|
+
self.get_actuator_value()
|
|
594
|
+
self.init_signal.emit(self._initialized_state)
|
|
595
|
+
|
|
596
|
+
elif (
|
|
597
|
+
status.command == ThreadStatusMove.GET_ACTUATOR_VALUE
|
|
598
|
+
or status.command == "check_position"
|
|
599
|
+
):
|
|
600
|
+
data_act = self._check_data_type(status.attribute)
|
|
601
|
+
if self.ui is not None:
|
|
602
|
+
self.ui.display_value(data_act)
|
|
603
|
+
if self.ui.has_action("show_graph") and self.ui.is_action_checked(
|
|
604
|
+
"show_graph"
|
|
605
|
+
):
|
|
606
|
+
self.ui.show_data(DataToExport(name=self.title, data=[data_act]))
|
|
607
|
+
|
|
608
|
+
self._current_value = data_act
|
|
609
|
+
if self.settings['saver_settings', 'do_save']:
|
|
610
|
+
self.append_data()
|
|
611
|
+
|
|
612
|
+
self.current_value_signal.emit(self._current_value)
|
|
613
|
+
if (
|
|
614
|
+
self.settings["main_settings", "tcpip", "tcp_connected"]
|
|
615
|
+
and self._send_to_tcpip
|
|
616
|
+
):
|
|
617
|
+
self._command_tcpip.emit(ThreadCommand("position_is", data_act))
|
|
618
|
+
if (
|
|
619
|
+
self.settings["main_settings", "leco", "leco_connected"]
|
|
620
|
+
and self._send_to_tcpip
|
|
621
|
+
):
|
|
622
|
+
self._command_tcpip.emit(
|
|
623
|
+
ThreadCommand(LECOMoveCommands.POSITION, data_act)
|
|
624
|
+
)
|
|
625
|
+
|
|
626
|
+
elif status.command == ThreadStatusMove.MOVE_DONE:
|
|
627
|
+
data_act = self._check_data_type(status.attribute)
|
|
628
|
+
if self.ui is not None:
|
|
629
|
+
self.ui.display_value(data_act)
|
|
630
|
+
self.ui.move_done = True
|
|
631
|
+
self._current_value = data_act
|
|
632
|
+
self._move_done_bool = True
|
|
633
|
+
self.move_done_signal.emit(data_act)
|
|
634
|
+
if (
|
|
635
|
+
self.settings.child("main_settings", "tcpip", "tcp_connected").value()
|
|
636
|
+
and self._send_to_tcpip
|
|
637
|
+
):
|
|
638
|
+
self._command_tcpip.emit(ThreadCommand("move_done", data_act))
|
|
639
|
+
if (
|
|
640
|
+
self.settings.child("main_settings", "leco", "leco_connected").value()
|
|
641
|
+
and self._send_to_tcpip
|
|
642
|
+
):
|
|
643
|
+
self._command_tcpip.emit(
|
|
644
|
+
ThreadCommand(LECOMoveCommands.MOVE_DONE, data_act)
|
|
645
|
+
)
|
|
646
|
+
|
|
647
|
+
elif status.command == ThreadStatusMove.OUT_OF_BOUNDS:
|
|
648
|
+
logger.warning(f"The Actuator {self.title} has reached its defined bounds")
|
|
649
|
+
self.bounds_signal.emit(True)
|
|
650
|
+
|
|
651
|
+
elif status.command == ThreadStatusMove.SET_ALLOWED_VALUES:
|
|
652
|
+
if self.ui is not None:
|
|
653
|
+
self.ui.set_abs_spinbox_properties(**status.attribute)
|
|
654
|
+
|
|
655
|
+
elif status.command == ThreadStatusMove.STOP:
|
|
656
|
+
self.stop_motion()
|
|
657
|
+
|
|
658
|
+
elif status.command == ThreadStatusMove.UNITS:
|
|
659
|
+
self.units = status.attribute
|
|
660
|
+
|
|
661
|
+
def _check_data_type(
|
|
662
|
+
self, data_act: Union[list, np.ndarray, Number, DataActuator]
|
|
663
|
+
) -> DataActuator:
|
|
664
|
+
"""Make sure the data is a DataActuator
|
|
665
|
+
|
|
666
|
+
Mostly to make sure DAQ_Move is backcompatible with old style plugins
|
|
667
|
+
"""
|
|
668
|
+
if isinstance(data_act, list): # backcompatibility
|
|
669
|
+
if isinstance(data_act[0], Number):
|
|
670
|
+
data_act = DataActuator(
|
|
671
|
+
data=[np.atleast_1d(val) for val in data_act], units=self.units
|
|
672
|
+
)
|
|
673
|
+
elif isinstance(data_act[0], np.ndarray):
|
|
674
|
+
data_act = DataActuator(data=data_act, units=self.units)
|
|
675
|
+
elif isinstance(data_act[0], DataActuator):
|
|
676
|
+
data_act = data_act[0]
|
|
677
|
+
else:
|
|
678
|
+
raise TypeError("Unknown data type")
|
|
679
|
+
elif isinstance(data_act, np.ndarray): # backcompatibility
|
|
680
|
+
data_act = DataActuator(data=[data_act], units=self.units)
|
|
681
|
+
data_act.name = (
|
|
682
|
+
self.title
|
|
683
|
+
) # for the DataActuator name to be the title of the DAQ_Move
|
|
684
|
+
if (
|
|
685
|
+
not Unit(self.units).is_compatible_with(Unit(data_act.units))
|
|
686
|
+
and data_act.units == ""
|
|
687
|
+
): # this happens if the units have not been specified in
|
|
688
|
+
# the plugin
|
|
689
|
+
data_act.force_units(self.units)
|
|
690
|
+
return data_act
|
|
691
|
+
|
|
692
|
+
def get_actuator_value(self):
|
|
693
|
+
"""Get the current actuator value via the "get_actuator_value" command send to the hardware
|
|
694
|
+
|
|
695
|
+
Returns nothing but the `move_done_signal` will be send once the action is done
|
|
696
|
+
"""
|
|
697
|
+
try:
|
|
698
|
+
self.command_hardware.emit(
|
|
699
|
+
ThreadCommand(ControlToHardwareMove.GET_ACTUATOR_VALUE)
|
|
700
|
+
)
|
|
701
|
+
|
|
702
|
+
except Exception as e:
|
|
703
|
+
self.logger.exception(str(e))
|
|
704
|
+
|
|
705
|
+
def grab(self):
|
|
706
|
+
if self.ui is not None:
|
|
707
|
+
self.manage_ui_actions("refresh_value", "setChecked", False)
|
|
708
|
+
self.get_continuous_actuator_value(False)
|
|
709
|
+
|
|
710
|
+
def stop_grab(self):
|
|
711
|
+
"""Stop value polling. Mandatory
|
|
712
|
+
|
|
713
|
+
First uncheck the ui action if ui is not None, then stop the polling
|
|
714
|
+
"""
|
|
715
|
+
if self.ui is not None:
|
|
716
|
+
self.manage_ui_actions("refresh_value", "setChecked", False)
|
|
717
|
+
self.get_continuous_actuator_value(False)
|
|
718
|
+
|
|
719
|
+
def get_continuous_actuator_value(self, get_value=True):
|
|
720
|
+
"""Start the continuous getting of the actuator's value
|
|
721
|
+
|
|
722
|
+
Parameters
|
|
723
|
+
----------
|
|
724
|
+
get_value: bool
|
|
725
|
+
if True start the timer to periodically fetch the actuator's value, else stop it
|
|
726
|
+
|
|
727
|
+
Notes
|
|
728
|
+
-----
|
|
729
|
+
The current timer period is set by the refresh value *'refresh_timeout'* in the actuator main settings.
|
|
730
|
+
"""
|
|
731
|
+
if get_value:
|
|
732
|
+
self._refresh_timer.setInterval(
|
|
733
|
+
self.settings["main_settings", "refresh_timeout"]
|
|
734
|
+
)
|
|
735
|
+
self._refresh_timer.start()
|
|
736
|
+
else:
|
|
737
|
+
self._refresh_timer.stop()
|
|
738
|
+
|
|
739
|
+
@property
|
|
740
|
+
def actuator(self):
|
|
741
|
+
"""str: the selected actuator's type
|
|
742
|
+
|
|
743
|
+
Returns
|
|
744
|
+
-------
|
|
745
|
+
|
|
746
|
+
"""
|
|
747
|
+
return self._actuator_type
|
|
748
|
+
|
|
749
|
+
@actuator.setter
|
|
750
|
+
def actuator(self, act_type):
|
|
751
|
+
if act_type in ACTUATOR_TYPES:
|
|
752
|
+
self._actuator_type = act_type
|
|
753
|
+
self.update_plugin_config()
|
|
754
|
+
if self.ui is not None:
|
|
755
|
+
self.ui.actuator = act_type
|
|
756
|
+
self.update_settings()
|
|
757
|
+
else:
|
|
758
|
+
raise ActuatorError(
|
|
759
|
+
f"{act_type} is an invalid actuator, should be within {ACTUATOR_TYPES}"
|
|
760
|
+
)
|
|
761
|
+
|
|
762
|
+
@property
|
|
763
|
+
def actuators(self) -> List[str]:
|
|
764
|
+
"""Get the list of possible actuators"""
|
|
765
|
+
return ACTUATOR_TYPES
|
|
766
|
+
|
|
767
|
+
def update_plugin_config(self):
|
|
768
|
+
parent_module = utils.find_dict_in_list_from_key_val(
|
|
769
|
+
DAQ_Move_Actuators, "name", self.actuator
|
|
770
|
+
)
|
|
771
|
+
mod = import_module(parent_module["module"].__package__.split(".")[0])
|
|
772
|
+
if hasattr(mod, "config"):
|
|
773
|
+
self.plugin_config = mod.config
|
|
774
|
+
|
|
775
|
+
@property
|
|
776
|
+
def units(self):
|
|
777
|
+
"""Get/Set the units for the controller"""
|
|
778
|
+
return self.settings["move_settings", "units"]
|
|
779
|
+
|
|
780
|
+
@units.setter
|
|
781
|
+
def units(self, unit: str):
|
|
782
|
+
self.settings.child("move_settings", "units").setValue(unit)
|
|
783
|
+
if self.ui is not None and config("actuator", "display_units"):
|
|
784
|
+
unit = self.get_unit_to_display(unit)
|
|
785
|
+
self.ui.set_unit_as_suffix(unit)
|
|
786
|
+
self.ui.set_unit_prefix(
|
|
787
|
+
config("actuator", "siprefix")
|
|
788
|
+
and (unit != "" or config("actuator", "siprefix_even_without_units"))
|
|
789
|
+
)
|
|
790
|
+
|
|
791
|
+
@property
|
|
792
|
+
def axis_names(self) -> Union[List, Dict]:
|
|
793
|
+
""" Get the names of all possible axis"""
|
|
794
|
+
return self.settings.child('move_settings', 'multiaxes', 'axis').opts['limits']
|
|
795
|
+
|
|
796
|
+
@property
|
|
797
|
+
def axis_name(self) -> str:
|
|
798
|
+
""" Get/Set the current axis"""
|
|
799
|
+
limits = self.settings.child('move_settings', 'multiaxes', 'axis').opts['limits']
|
|
800
|
+
if isinstance(limits, list):
|
|
801
|
+
return self.settings['move_settings', 'multiaxes', 'axis']
|
|
802
|
+
elif isinstance(limits, dict):
|
|
803
|
+
return find_keys_from_val(limits,
|
|
804
|
+
val=self.settings['move_settings', 'multiaxes', 'axis'])[0]
|
|
805
|
+
|
|
806
|
+
@axis_name.setter
|
|
807
|
+
def axis_name(self, name: str):
|
|
808
|
+
""" Get/Set the current axis"""
|
|
809
|
+
limits = self.settings.child('move_settings', 'multiaxes', 'axis').opts['limits']
|
|
810
|
+
if name in limits:
|
|
811
|
+
if isinstance(limits, list):
|
|
812
|
+
self.settings.child('move_settings', 'multiaxes', 'axis').setValue(name)
|
|
813
|
+
elif isinstance(limits, dict):
|
|
814
|
+
self.settings.child('move_settings', 'multiaxes', 'axis').setValue(limits[name])
|
|
815
|
+
|
|
816
|
+
@property
|
|
817
|
+
def axis_names(self) -> Union[List, Dict]:
|
|
818
|
+
""" Get the names of all possible axis"""
|
|
819
|
+
return self.settings.child('move_settings', 'multiaxes', 'axis').opts['limits']
|
|
820
|
+
|
|
821
|
+
@property
|
|
822
|
+
def axis_name(self) -> str:
|
|
823
|
+
""" Get/Set the current axis"""
|
|
824
|
+
limits = self.settings.child('move_settings', 'multiaxes', 'axis').opts['limits']
|
|
825
|
+
if isinstance(limits, list):
|
|
826
|
+
return self.settings['move_settings', 'multiaxes', 'axis']
|
|
827
|
+
elif isinstance(limits, dict):
|
|
828
|
+
return find_keys_from_val(limits,
|
|
829
|
+
val=self.settings['move_settings', 'multiaxes', 'axis'])[0]
|
|
830
|
+
|
|
831
|
+
@axis_name.setter
|
|
832
|
+
def axis_name(self, name: str):
|
|
833
|
+
""" Get/Set the current axis"""
|
|
834
|
+
limits = self.settings.child('move_settings', 'multiaxes', 'axis').opts['limits']
|
|
835
|
+
if name in limits:
|
|
836
|
+
if isinstance(limits, list):
|
|
837
|
+
self.settings.child('move_settings', 'multiaxes', 'axis').setValue(name)
|
|
838
|
+
elif isinstance(limits, dict):
|
|
839
|
+
self.settings.child('move_settings', 'multiaxes', 'axis').setValue(limits[name])
|
|
840
|
+
|
|
841
|
+
@staticmethod
|
|
842
|
+
def get_unit_to_display(unit: str) -> str:
|
|
843
|
+
"""Get the unit to be displayed in the UI
|
|
844
|
+
|
|
845
|
+
If the controller units are in mm the displayed unit will be m
|
|
846
|
+
because m is the base unit, then the user could ask for mm, km, µm...
|
|
847
|
+
only issue is when the usual displayed unit is not the base one, then add cases below
|
|
848
|
+
|
|
849
|
+
Parameters
|
|
850
|
+
----------
|
|
851
|
+
unit: str
|
|
852
|
+
|
|
853
|
+
Returns
|
|
854
|
+
-------
|
|
855
|
+
str: the unit to be displayed on the ui
|
|
856
|
+
"""
|
|
857
|
+
if ("°" in unit or "degree" in unit) and not "°C" in unit:
|
|
858
|
+
# special cas as pint base unit for angles are radians
|
|
859
|
+
return "°"
|
|
860
|
+
elif "°C" in unit:
|
|
861
|
+
return "°C"
|
|
862
|
+
else:
|
|
863
|
+
for key in config("actuator", "allowed_units"):
|
|
864
|
+
if key in unit:
|
|
865
|
+
return config("actuator", "allowed_units", key)
|
|
866
|
+
return str(Q_(1, unit).to_base_units().units)
|
|
867
|
+
|
|
868
|
+
def update_settings(self):
|
|
869
|
+
self.settings.child("main_settings", "move_type").setValue(self._actuator_type)
|
|
870
|
+
self.settings.child("main_settings", "module_name").setValue(self._title)
|
|
871
|
+
try:
|
|
872
|
+
for child in self.settings.child("move_settings").children():
|
|
873
|
+
child.remove()
|
|
874
|
+
parent_module = utils.find_dict_in_list_from_key_val(
|
|
875
|
+
DAQ_Move_Actuators, "name", self._actuator_type
|
|
876
|
+
)
|
|
877
|
+
class_ = getattr(
|
|
878
|
+
getattr(parent_module["module"], "daq_move_" + self._actuator_type),
|
|
879
|
+
"DAQ_Move_" + self._actuator_type,
|
|
880
|
+
)
|
|
881
|
+
params = getattr(class_, "params")
|
|
882
|
+
move_params = Parameter.create(
|
|
883
|
+
name="move_settings", type="group", children=params
|
|
884
|
+
)
|
|
885
|
+
|
|
886
|
+
self.settings.child("move_settings").addChildren(move_params.children())
|
|
887
|
+
|
|
888
|
+
except Exception as e:
|
|
889
|
+
self.logger.exception(str(e))
|
|
890
|
+
|
|
891
|
+
def connect_tcp_ip(self):
|
|
892
|
+
super().connect_tcp_ip(
|
|
893
|
+
params_state=self.settings.child("move_settings"), client_type="ACTUATOR"
|
|
894
|
+
)
|
|
895
|
+
|
|
896
|
+
def connect_leco(self, connect: bool) -> None:
|
|
897
|
+
super().connect_leco(connect)
|
|
898
|
+
|
|
899
|
+
@Slot(ThreadCommand)
|
|
900
|
+
def process_tcpip_cmds(self, status: ThreadCommand) -> None:
|
|
901
|
+
if super().process_tcpip_cmds(status=status) is None:
|
|
902
|
+
return
|
|
903
|
+
if LECOMoveCommands.MOVE_ABS == status.command:
|
|
904
|
+
self.move_abs(status.attribute, send_to_tcpip=True)
|
|
905
|
+
|
|
906
|
+
elif LECOMoveCommands.MOVE_REL == status.command:
|
|
907
|
+
self.move_rel(status.attribute, send_to_tcpip=True)
|
|
908
|
+
|
|
909
|
+
elif LECOMoveCommands.MOVE_HOME == status.command:
|
|
910
|
+
self.move_home(send_to_tcpip=True)
|
|
911
|
+
|
|
912
|
+
elif "check_position" in status.command:
|
|
913
|
+
deprecation_msg(
|
|
914
|
+
"check_position is deprecated, you should use get_actuator_value"
|
|
915
|
+
)
|
|
916
|
+
self._send_to_tcpip = True
|
|
917
|
+
self.get_actuator_value()
|
|
918
|
+
|
|
919
|
+
elif LECOMoveCommands.GET_ACTUATOR_VALUE in status.command:
|
|
920
|
+
self._send_to_tcpip = True
|
|
921
|
+
self.get_actuator_value()
|
|
922
|
+
|
|
923
|
+
elif status.command == LECOMoveCommands.STOP:
|
|
924
|
+
self.stop_motion()
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
class DAQ_Move_Hardware(QObject):
|
|
928
|
+
"""
|
|
929
|
+
================== ========================
|
|
930
|
+
**Attributes** **Type**
|
|
931
|
+
*status_sig* instance of Signal
|
|
932
|
+
*hardware* ???
|
|
933
|
+
*actuator_type* string
|
|
934
|
+
*current_position* float
|
|
935
|
+
*target_value* float
|
|
936
|
+
*hardware_adress* string
|
|
937
|
+
*axis_address* string
|
|
938
|
+
*motion_stoped* boolean
|
|
939
|
+
================== ========================
|
|
940
|
+
"""
|
|
941
|
+
|
|
942
|
+
status_sig = Signal(ThreadCommand)
|
|
943
|
+
|
|
944
|
+
def __init__(self, actuator_type, position: DataActuator, title="actuator"):
|
|
945
|
+
super().__init__()
|
|
946
|
+
self.logger = set_logger(f"{logger.name}.{title}.actuator")
|
|
947
|
+
self._title = title
|
|
948
|
+
self.hardware: Optional[DAQ_Move_base] = None
|
|
949
|
+
self.actuator_type = actuator_type
|
|
950
|
+
self.hardware_adress = None
|
|
951
|
+
self.axis_address = None
|
|
952
|
+
self.motion_stoped = False
|
|
953
|
+
|
|
954
|
+
@property
|
|
955
|
+
def title(self):
|
|
956
|
+
return self._title
|
|
957
|
+
|
|
958
|
+
def close(self):
|
|
959
|
+
"""
|
|
960
|
+
Uninitialize the stage closing the hardware.
|
|
961
|
+
|
|
962
|
+
"""
|
|
963
|
+
if self.hardware is not None and self.hardware.controller is not None:
|
|
964
|
+
self.hardware.close()
|
|
965
|
+
|
|
966
|
+
return "Stage uninitialized"
|
|
967
|
+
|
|
968
|
+
def get_actuator_value(self):
|
|
969
|
+
"""Get the current position checking the hardware value."""
|
|
970
|
+
if self.hardware is not None:
|
|
971
|
+
pos = self.hardware.get_actuator_value()
|
|
972
|
+
if self.hardware.data_actuator_type == DataActuatorType.float:
|
|
973
|
+
pos = DataActuator(self._title, data=pos, units=self.hardware.axis_unit)
|
|
974
|
+
return pos
|
|
975
|
+
|
|
976
|
+
def check_position(self):
|
|
977
|
+
"""Get the current position checking the hardware position (deprecated)"""
|
|
978
|
+
deprecation_msg("check_position is deprecated, use get_actuator_value")
|
|
979
|
+
pos = self.hardware.get_actuator_value()
|
|
980
|
+
return pos
|
|
981
|
+
|
|
982
|
+
def ini_stage(self, params_state=None, controller: Optional[HardwareController] = None) -> edict:
|
|
983
|
+
"""
|
|
984
|
+
Init a stage updating the hardware and sending an hardware move_done signal.
|
|
985
|
+
|
|
986
|
+
=============== =================================== ==========================================================================================================================
|
|
987
|
+
**Parameters** **Type** **Description**
|
|
988
|
+
|
|
989
|
+
*params_state* ordered dictionary list The parameter state of the hardware class composed by a list representing the tree to keep a temporary save of the tree
|
|
990
|
+
|
|
991
|
+
*controller* one or many instance of DAQ_Move The controller id of the hardware
|
|
992
|
+
|
|
993
|
+
*stage* instance of DAQ_Move Defining axes and motors
|
|
994
|
+
=============== =================================== ==========================================================================================================================
|
|
995
|
+
|
|
996
|
+
See Also
|
|
997
|
+
--------
|
|
998
|
+
DAQ_utils.ThreadCommand, DAQ_Move
|
|
999
|
+
"""
|
|
1000
|
+
|
|
1001
|
+
status = edict(initialized=False, info="")
|
|
1002
|
+
try:
|
|
1003
|
+
parent_module = utils.find_dict_in_list_from_key_val(
|
|
1004
|
+
DAQ_Move_Actuators, "name", self.actuator_type
|
|
1005
|
+
)
|
|
1006
|
+
class_ = getattr(
|
|
1007
|
+
getattr(parent_module["module"], "daq_move_" + self.actuator_type),
|
|
1008
|
+
"DAQ_Move_" + self.actuator_type,
|
|
1009
|
+
)
|
|
1010
|
+
self.hardware = class_(self, params_state)
|
|
1011
|
+
assert self.hardware is not None
|
|
1012
|
+
try:
|
|
1013
|
+
infos = self.hardware.ini_stage(
|
|
1014
|
+
controller
|
|
1015
|
+
) # return edict(info="", controller=, stage=)
|
|
1016
|
+
except Exception as e:
|
|
1017
|
+
logger.exception("Hardware couldn't be initialized", exc_info=e)
|
|
1018
|
+
infos = str(e), False
|
|
1019
|
+
|
|
1020
|
+
if isinstance(infos, edict): # following old plugin templating
|
|
1021
|
+
status.update(infos)
|
|
1022
|
+
deprecation_msg(
|
|
1023
|
+
"Returns from init_stage should now be a string and a boolean,"
|
|
1024
|
+
" see pymodaq_plugins_template",
|
|
1025
|
+
stacklevel=3,
|
|
1026
|
+
)
|
|
1027
|
+
else:
|
|
1028
|
+
status.info = infos[0]
|
|
1029
|
+
status.initialized = infos[1]
|
|
1030
|
+
status.controller = self.hardware.controller
|
|
1031
|
+
self.hardware.move_done_signal.connect(self.move_done)
|
|
1032
|
+
if status.initialized:
|
|
1033
|
+
self.status_sig.emit(
|
|
1034
|
+
ThreadCommand(
|
|
1035
|
+
ThreadStatusMove.GET_ACTUATOR_VALUE, self.get_actuator_value()
|
|
1036
|
+
)
|
|
1037
|
+
)
|
|
1038
|
+
|
|
1039
|
+
return status
|
|
1040
|
+
except Exception as e:
|
|
1041
|
+
self.logger.exception(str(e))
|
|
1042
|
+
return status
|
|
1043
|
+
|
|
1044
|
+
def move_abs(self, position: DataActuator, polling: bool = True) -> None:
|
|
1045
|
+
"""
|
|
1046
|
+
|
|
1047
|
+
"""
|
|
1048
|
+
assert self.hardware is not None
|
|
1049
|
+
position = check_units(position, self.hardware.axis_unit)
|
|
1050
|
+
self.hardware.move_is_done = False
|
|
1051
|
+
self.hardware.ispolling = polling
|
|
1052
|
+
if self.hardware.data_actuator_type == self.hardware.data_actuator_type.float:
|
|
1053
|
+
self.hardware.move_abs(
|
|
1054
|
+
position.units_as(self.hardware.axis_unit).value()
|
|
1055
|
+
) # convert to plugin controller current axis units
|
|
1056
|
+
else:
|
|
1057
|
+
position.units = (
|
|
1058
|
+
self.hardware.axis_unit
|
|
1059
|
+
) # convert to plugin controller current axis units
|
|
1060
|
+
self.hardware.move_abs(position)
|
|
1061
|
+
self.hardware.poll_moving()
|
|
1062
|
+
|
|
1063
|
+
def move_rel(self, rel_position: DataActuator, polling: bool = True) -> None:
|
|
1064
|
+
"""
|
|
1065
|
+
|
|
1066
|
+
"""
|
|
1067
|
+
assert self.hardware is not None
|
|
1068
|
+
rel_position = check_units(rel_position, self.hardware.axis_unit)
|
|
1069
|
+
self.hardware.move_is_done = False
|
|
1070
|
+
self.hardware.ispolling = polling
|
|
1071
|
+
|
|
1072
|
+
if self.hardware.data_actuator_type.name == 'float':
|
|
1073
|
+
self.hardware.move_rel(rel_position.units_as(self.hardware.axis_unit).value())
|
|
1074
|
+
else:
|
|
1075
|
+
rel_position.units = (
|
|
1076
|
+
self.hardware.axis_unit
|
|
1077
|
+
) # convert to plugin current axis units
|
|
1078
|
+
self.hardware.move_rel(rel_position)
|
|
1079
|
+
|
|
1080
|
+
self.hardware.poll_moving()
|
|
1081
|
+
|
|
1082
|
+
@Slot(float)
|
|
1083
|
+
def Move_Stoped(self, pos):
|
|
1084
|
+
"""
|
|
1085
|
+
Send a "move_done" Thread Command with the given position as an attribute.
|
|
1086
|
+
|
|
1087
|
+
See Also
|
|
1088
|
+
--------
|
|
1089
|
+
DAQ_utils.ThreadCommand
|
|
1090
|
+
"""
|
|
1091
|
+
self.status_sig.emit(ThreadCommand(ThreadStatusMove.MOVE_DONE, pos))
|
|
1092
|
+
|
|
1093
|
+
def move_home(self):
|
|
1094
|
+
"""
|
|
1095
|
+
Make the hardware move to the init position.
|
|
1096
|
+
|
|
1097
|
+
"""
|
|
1098
|
+
assert self.hardware is not None
|
|
1099
|
+
self.hardware.move_is_done = False
|
|
1100
|
+
self.hardware.move_home()
|
|
1101
|
+
|
|
1102
|
+
@Slot(DataActuator)
|
|
1103
|
+
def move_done(self, pos: DataActuator):
|
|
1104
|
+
"""Send the move_done signal back to the main class"""
|
|
1105
|
+
self._current_value = pos
|
|
1106
|
+
self.status_sig.emit(
|
|
1107
|
+
ThreadCommand(command=ThreadStatusMove.MOVE_DONE, attribute=pos)
|
|
1108
|
+
)
|
|
1109
|
+
|
|
1110
|
+
@Slot(ThreadCommand)
|
|
1111
|
+
def queue_command(self, command: ThreadCommand):
|
|
1112
|
+
"""Interpret command send by DAQ_Move class
|
|
1113
|
+
* **ini_stage** command, init a stage from command attribute.
|
|
1114
|
+
* **close** command, unitinalise the stage closing hardware and emitting the corresponding status signal
|
|
1115
|
+
* **move_abs** command, call the move_Abs method with position from command attribute
|
|
1116
|
+
* **move_rel** command, call the move_Rel method with the relative position from the command attribute.
|
|
1117
|
+
* **move_home** command, call the move_home method
|
|
1118
|
+
* **get_actuator_value** command, get the current position from the check_position method
|
|
1119
|
+
* **Stop_motion** command, stop any motion via the stop_Motion method
|
|
1120
|
+
* **reset_stop_motion** command, set the motion_stopped attribute to false
|
|
1121
|
+
|
|
1122
|
+
Parameters
|
|
1123
|
+
----------
|
|
1124
|
+
command: ThreadCommand
|
|
1125
|
+
Possible commands are:
|
|
1126
|
+
* **ini_stage** command, init a stage from command attribute.
|
|
1127
|
+
* **close** command, unitinalise the stage closing hardware and emitting the corresponding status signal
|
|
1128
|
+
* **move_abs** command, call the move_abs method with position from command attribute
|
|
1129
|
+
* **move_rel** command, call the move_rel method with the relative position from the command attribute.
|
|
1130
|
+
* **move_home** command, call the move_home method
|
|
1131
|
+
* **get_actuator_value** command, get the current position from the check_position method
|
|
1132
|
+
* **stop_motion** command, stop any motion via the stop_Motion method
|
|
1133
|
+
* **reset_stop_motion** command, set the motion_stopped attribute to false
|
|
1134
|
+
"""
|
|
1135
|
+
try:
|
|
1136
|
+
logger.debug(f"Threadcommand {command.command} sent to {self.title}")
|
|
1137
|
+
if command.command == ControlToHardwareMove.INI_STAGE:
|
|
1138
|
+
status: edict = self.ini_stage(*command.attribute)
|
|
1139
|
+
self.status_sig.emit(
|
|
1140
|
+
ThreadCommand(command=ThreadStatusMove.INI_STAGE, attribute=status)
|
|
1141
|
+
)
|
|
1142
|
+
|
|
1143
|
+
elif command.command == ControlToHardwareMove.CLOSE:
|
|
1144
|
+
status = self.close()
|
|
1145
|
+
self.status_sig.emit(
|
|
1146
|
+
ThreadCommand(command=ThreadStatus.CLOSE, attribute=[status])
|
|
1147
|
+
)
|
|
1148
|
+
|
|
1149
|
+
elif command.command == ControlToHardwareMove.MOVE_ABS:
|
|
1150
|
+
self.move_abs(*command.attribute)
|
|
1151
|
+
|
|
1152
|
+
elif command.command == ControlToHardwareMove.MOVE_REL:
|
|
1153
|
+
self.move_rel(*command.attribute)
|
|
1154
|
+
|
|
1155
|
+
elif command.command == ControlToHardwareMove.MOVE_HOME:
|
|
1156
|
+
self.move_home()
|
|
1157
|
+
|
|
1158
|
+
elif command.command == ControlToHardwareMove.GET_ACTUATOR_VALUE:
|
|
1159
|
+
pos = self.get_actuator_value()
|
|
1160
|
+
self.status_sig.emit(
|
|
1161
|
+
ThreadCommand(ThreadStatusMove.GET_ACTUATOR_VALUE, pos)
|
|
1162
|
+
)
|
|
1163
|
+
|
|
1164
|
+
elif command.command == ControlToHardwareMove.STOP_MOTION:
|
|
1165
|
+
self.stop_motion()
|
|
1166
|
+
|
|
1167
|
+
elif command.command == ControlToHardwareMove.RESET_STOP_MOTION:
|
|
1168
|
+
self.motion_stoped = False
|
|
1169
|
+
|
|
1170
|
+
else: # custom commands for particular plugins (see spectrometer module 'get_spectro_wl' for instance)
|
|
1171
|
+
if hasattr(self.hardware, command.command):
|
|
1172
|
+
cmd = getattr(self.hardware, command.command)
|
|
1173
|
+
if isinstance(command.attribute, list):
|
|
1174
|
+
cmd(*command.attribute)
|
|
1175
|
+
elif isinstance(command.attribute, dict):
|
|
1176
|
+
cmd(**command.attribute)
|
|
1177
|
+
except Exception as e:
|
|
1178
|
+
self.logger.exception(str(e))
|
|
1179
|
+
|
|
1180
|
+
def stop_motion(self):
|
|
1181
|
+
"""
|
|
1182
|
+
stop hardware motion with motion_stopped attribute updtaed to True and a status signal sended with an "update_status" Thread Command
|
|
1183
|
+
|
|
1184
|
+
See Also
|
|
1185
|
+
--------
|
|
1186
|
+
DAQ_utils.ThreadCommand, stop_motion
|
|
1187
|
+
"""
|
|
1188
|
+
self.status_sig.emit(
|
|
1189
|
+
ThreadCommand(command="Update_Status", attribute=["Motion stoping", "log"])
|
|
1190
|
+
)
|
|
1191
|
+
self.motion_stoped = True
|
|
1192
|
+
assert self.hardware is not None
|
|
1193
|
+
if self.hardware is not None and self.hardware.controller is not None:
|
|
1194
|
+
self.hardware.stop_motion()
|
|
1195
|
+
self.hardware.poll_timer.stop()
|
|
1196
|
+
|
|
1197
|
+
@Slot(edict)
|
|
1198
|
+
def update_settings(self, settings_parameter_dict):
|
|
1199
|
+
"""
|
|
1200
|
+
Update settings of hardware with dictionary parameters in case of "Move_Settings" path, else update attribute with dictionnary parameters.
|
|
1201
|
+
|
|
1202
|
+
========================= =========== ======================================================
|
|
1203
|
+
**Parameters** **Type** **Description**
|
|
1204
|
+
|
|
1205
|
+
*settings_parameter_dict* dictionary Dictionary containing the path and linked parameter
|
|
1206
|
+
========================= =========== ======================================================
|
|
1207
|
+
|
|
1208
|
+
See Also
|
|
1209
|
+
--------
|
|
1210
|
+
update_settings
|
|
1211
|
+
"""
|
|
1212
|
+
# settings_parameter_dict = edict(path=path,param=param)
|
|
1213
|
+
path = settings_parameter_dict["path"]
|
|
1214
|
+
param = settings_parameter_dict["param"]
|
|
1215
|
+
if path[0] == "main_settings":
|
|
1216
|
+
if hasattr(self, path[-1]):
|
|
1217
|
+
setattr(self, path[-1], param.value())
|
|
1218
|
+
|
|
1219
|
+
elif path[0] == "move_settings":
|
|
1220
|
+
if self.hardware is not None:
|
|
1221
|
+
self.hardware.update_settings(settings_parameter_dict)
|
|
1222
|
+
|
|
1223
|
+
|
|
1224
|
+
def main(init_qt=True):
|
|
1225
|
+
if init_qt: # used for the test suite
|
|
1226
|
+
app = mkQApp("PyMoDAQ Move")
|
|
1227
|
+
|
|
1228
|
+
widget = QtWidgets.QWidget()
|
|
1229
|
+
prog = DAQ_Move(widget, title="test")
|
|
1230
|
+
widget.show()
|
|
1231
|
+
|
|
1232
|
+
if init_qt:
|
|
1233
|
+
sys.exit(app.exec_())
|
|
1234
|
+
return prog, widget
|
|
1235
|
+
|
|
1236
|
+
|
|
1237
|
+
if __name__ == "__main__":
|
|
1238
|
+
main()
|