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,1141 @@
|
|
|
1
|
+
import numbers
|
|
2
|
+
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
from time import perf_counter
|
|
5
|
+
from typing import Union, List, Dict, TYPE_CHECKING, Optional, TypeVar
|
|
6
|
+
from numbers import Number
|
|
7
|
+
from collections.abc import Iterable
|
|
8
|
+
|
|
9
|
+
from easydict import EasyDict as edict
|
|
10
|
+
import numpy as np
|
|
11
|
+
from qtpy import QtWidgets
|
|
12
|
+
from qtpy.QtCore import QObject, Slot, Signal, QTimer
|
|
13
|
+
|
|
14
|
+
from pymodaq_utils.utils import ThreadCommand, find_keys_from_val
|
|
15
|
+
from pymodaq_utils import config as configmod
|
|
16
|
+
from pymodaq_utils.warnings import deprecation_msg
|
|
17
|
+
from pymodaq_utils.enums import BaseEnum, enum_checker
|
|
18
|
+
from pymodaq_utils.logger import set_logger, get_module_name
|
|
19
|
+
|
|
20
|
+
import pymodaq_gui.parameter.utils as putils
|
|
21
|
+
from pymodaq_gui.parameter import Parameter
|
|
22
|
+
from pymodaq_gui.parameter import ioxml
|
|
23
|
+
from pymodaq_gui.utils.utils import mkQApp
|
|
24
|
+
|
|
25
|
+
from pymodaq.utils.tcp_ip.tcp_server_client import TCPServer, tcp_parameters
|
|
26
|
+
|
|
27
|
+
from pymodaq_data.data import DataUnitError, Q_
|
|
28
|
+
|
|
29
|
+
from pymodaq.utils.messenger import deprecation_msg
|
|
30
|
+
from pymodaq.utils.data import DataActuator
|
|
31
|
+
from pymodaq_utils.enums import BaseEnum, enum_checker
|
|
32
|
+
|
|
33
|
+
from pymodaq_utils.serialize.mysocket import Socket
|
|
34
|
+
from pymodaq_utils.serialize.serializer_legacy import DeSerializer, Serializer
|
|
35
|
+
from pymodaq import Unit
|
|
36
|
+
from pint.errors import OffsetUnitCalculusError
|
|
37
|
+
|
|
38
|
+
from pymodaq.control_modules.thread_commands import ThreadStatus, ThreadStatusMove
|
|
39
|
+
from pymodaq.utils.config import Config as ControlModulesConfig
|
|
40
|
+
from pymodaq.control_modules.daq_move_ui.factory import ActuatorUIFactory
|
|
41
|
+
|
|
42
|
+
if TYPE_CHECKING:
|
|
43
|
+
from pymodaq.control_modules.daq_move import DAQ_Move_Hardware
|
|
44
|
+
|
|
45
|
+
logger = set_logger(get_module_name(__file__))
|
|
46
|
+
|
|
47
|
+
config_utils = configmod.Config()
|
|
48
|
+
config = ControlModulesConfig()
|
|
49
|
+
|
|
50
|
+
HardwareController = TypeVar("HardwareController")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def check_units(dwa: DataActuator, units: str):
|
|
54
|
+
""" Check if dwa units is compatible with the units argument
|
|
55
|
+
|
|
56
|
+
If it is incompatible and has dimensionless units, brute force change the dwa units to units,
|
|
57
|
+
otherwise raise a DataUnitError
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
dwa: DataActuator
|
|
62
|
+
units: str
|
|
63
|
+
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
DataActuator
|
|
67
|
+
"""
|
|
68
|
+
if Unit(dwa.units).is_compatible_with(units):
|
|
69
|
+
return dwa
|
|
70
|
+
elif Unit(dwa.units).dimensionless: # dimensionless
|
|
71
|
+
dwa.force_units(units)
|
|
72
|
+
return dwa
|
|
73
|
+
else:
|
|
74
|
+
raise DataUnitError(f'Units incompatibility between {dwa} and "{units}" units')
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class DataActuatorType(BaseEnum):
|
|
78
|
+
"""Enum for new or old style holding the value of the actuator"""
|
|
79
|
+
float = 0
|
|
80
|
+
DataActuator = 1
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def comon_parameters(epsilon=config('actuator', 'epsilon_default'),
|
|
84
|
+
epsilons=None):
|
|
85
|
+
if epsilons is not None:
|
|
86
|
+
epsilon = epsilons
|
|
87
|
+
if isinstance(epsilon, list):
|
|
88
|
+
epsilon = epsilon[0]
|
|
89
|
+
elif isinstance(epsilon, dict):
|
|
90
|
+
epsilon = epsilon[list(epsilon.keys())[0]]
|
|
91
|
+
|
|
92
|
+
return [{'title': 'Units:', 'name': 'units', 'type': 'str', 'value': '', 'readonly': True},
|
|
93
|
+
{'title': 'Epsilon:', 'name': 'epsilon', 'type': 'float',
|
|
94
|
+
'value': epsilon,
|
|
95
|
+
'tip': 'Differential Value at which the controller considers it reached the target position'},
|
|
96
|
+
{'title': 'Timeout (s):', 'name': 'timeout', 'type': 'int',
|
|
97
|
+
'value': config('actuator', 'polling_timeout_s')},
|
|
98
|
+
{'title': 'Bounds:', 'name': 'bounds', 'type': 'group', 'children': [
|
|
99
|
+
{'title': 'Set Bounds:', 'name': 'is_bounds', 'type': 'bool', 'value': False},
|
|
100
|
+
{'title': 'Min:', 'name': 'min_bound', 'type': 'float', 'value': 0, 'default': 0},
|
|
101
|
+
{'title': 'Max:', 'name': 'max_bound', 'type': 'float', 'value': 1, 'default': 1}, ]},
|
|
102
|
+
{'title': 'Scaling:', 'name': 'scaling', 'type': 'group', 'children': [
|
|
103
|
+
{'title': 'Use scaling:', 'name': 'use_scaling', 'type': 'bool', 'value': False,
|
|
104
|
+
'default': False},
|
|
105
|
+
{'title': 'Scaling factor:', 'name': 'scaling', 'type': 'float', 'value': 1., 'default': 1.},
|
|
106
|
+
{'title': 'Offset factor:', 'name': 'offset', 'type': 'float', 'value': 0., 'default': 0.}]}]
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
MOVE_COMMANDS = ['abs', 'rel', 'home']
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class MoveCommand:
|
|
113
|
+
"""Utility class to contain a given move type and value
|
|
114
|
+
|
|
115
|
+
Attributes
|
|
116
|
+
----------
|
|
117
|
+
move_type: str
|
|
118
|
+
either:
|
|
119
|
+
|
|
120
|
+
* 'abs': performs an absolute action
|
|
121
|
+
* 'rel': performs a relative action
|
|
122
|
+
* 'home': find the actuator's home
|
|
123
|
+
value: float
|
|
124
|
+
the value the move should reach
|
|
125
|
+
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def __init__(self, move_type, value=0):
|
|
129
|
+
if move_type not in MOVE_COMMANDS:
|
|
130
|
+
raise ValueError(f'The allowed move types fro an actuator are {MOVE_COMMANDS}')
|
|
131
|
+
self.move_type = move_type
|
|
132
|
+
self.value = value
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def comon_parameters_fun(is_multiaxes=False, axes_names=None,
|
|
136
|
+
axis_names: Union[List, Dict] = [],
|
|
137
|
+
master=True,
|
|
138
|
+
epsilon: float = config('actuator', 'epsilon_default')):
|
|
139
|
+
"""Function returning the common and mandatory parameters that should be on the actuator plugin level
|
|
140
|
+
|
|
141
|
+
Parameters
|
|
142
|
+
----------
|
|
143
|
+
is_multiaxes: bool
|
|
144
|
+
If True, display the particular settings to define which axis the controller is driving
|
|
145
|
+
axes_names: deprecated, use axis_names
|
|
146
|
+
axis_names: list of str or dictionnary of string as key and integer as value
|
|
147
|
+
The string identifier of every axis the controller can drive
|
|
148
|
+
master: bool
|
|
149
|
+
If True consider this plugin has to init the controller, otherwise use an already initialized instance
|
|
150
|
+
epsilon: float
|
|
151
|
+
deprecated (< 5.0.0) no more used here
|
|
152
|
+
|
|
153
|
+
"""
|
|
154
|
+
if axes_names is not None and len(axis_names) == 0:
|
|
155
|
+
if len(axes_names) == 0:
|
|
156
|
+
axes_names = ['']
|
|
157
|
+
axis_names = axes_names
|
|
158
|
+
|
|
159
|
+
is_multiaxes = len(axis_names) > 1 or is_multiaxes
|
|
160
|
+
if isinstance(axis_names, list):
|
|
161
|
+
if len(axis_names) > 0:
|
|
162
|
+
axis_name = axis_names[0]
|
|
163
|
+
else:
|
|
164
|
+
axis_names = ['']
|
|
165
|
+
axis_name = ''
|
|
166
|
+
elif isinstance(axis_names, dict):
|
|
167
|
+
axis_name = axis_names[list(axis_names.keys())[0]]
|
|
168
|
+
else:
|
|
169
|
+
raise ValueError('axis_names should be either a list of string or a dict with strings '
|
|
170
|
+
'as keys')
|
|
171
|
+
params = [
|
|
172
|
+
{'title': 'MultiAxes:', 'name': 'multiaxes', 'type': 'group',
|
|
173
|
+
'visible': True, 'children': [
|
|
174
|
+
{'title': 'Controller ID:', 'name': 'controller_ID', 'type': 'int', 'value': 0,
|
|
175
|
+
'default': 0},
|
|
176
|
+
{'title': 'Status:', 'name': 'multi_status', 'type': 'list',
|
|
177
|
+
'value': 'Master' if master else 'Slave', 'limits': ['Master', 'Slave']},
|
|
178
|
+
{'title': 'Axis:', 'name': 'axis', 'type': 'list', 'limits': axis_names.copy(),
|
|
179
|
+
'value': axis_name},
|
|
180
|
+
]},
|
|
181
|
+
] + comon_parameters(epsilon)
|
|
182
|
+
return params
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
params = [
|
|
186
|
+
{'title': 'Main Settings:', 'name': 'main_settings', 'type': 'group', 'children': [
|
|
187
|
+
{'title': 'Actuator type:', 'name': 'move_type', 'type': 'str', 'value': '', 'readonly': True},
|
|
188
|
+
{'title': 'Actuator name:', 'name': 'module_name', 'type': 'str', 'value': '', 'readonly': True},
|
|
189
|
+
{'title': 'UI type:', 'name': 'ui_type', 'type': 'list',
|
|
190
|
+
'value': config('actuator', 'ui') if config('actuator', 'ui') in ActuatorUIFactory.keys() else
|
|
191
|
+
ActuatorUIFactory.keys()[0],
|
|
192
|
+
'limits': ActuatorUIFactory.keys()},
|
|
193
|
+
{'title': 'Plugin Config:', 'name': 'plugin_config', 'type': 'bool_push', 'label': 'Show Config', },
|
|
194
|
+
|
|
195
|
+
{'title': 'Refresh value (ms):', 'name': 'refresh_timeout', 'type': 'int',
|
|
196
|
+
'value': config('actuator', 'refresh_timeout_ms')},
|
|
197
|
+
{'title': 'Continuous saving:', 'name': 'continuous_saving_opt', 'type': 'bool', 'default': False,
|
|
198
|
+
'value': False},
|
|
199
|
+
{'title': 'TCP/IP options:', 'name': 'tcpip', 'type': 'group', 'visible': True, 'expanded': False,
|
|
200
|
+
'children': [
|
|
201
|
+
{'title': 'Connect to server:', 'name': 'connect_server', 'type': 'bool_push', 'label': 'Connect',
|
|
202
|
+
'value': False},
|
|
203
|
+
{'title': 'Connected?:', 'name': 'tcp_connected', 'type': 'led', 'value': False},
|
|
204
|
+
{'title': 'IP address:', 'name': 'ip_address', 'type': 'str',
|
|
205
|
+
'value': config_utils('network', 'tcp-server', 'ip')},
|
|
206
|
+
{'title': 'Port:', 'name': 'port', 'type': 'int', 'value': config_utils('network', 'tcp-server', 'port')},
|
|
207
|
+
]},
|
|
208
|
+
{'title': 'LECO options:', 'name': 'leco', 'type': 'group', 'visible': True, 'expanded': False,
|
|
209
|
+
'children': [
|
|
210
|
+
{'title': 'Connect:', 'name': 'connect_leco_server', 'type': 'bool_push', 'label': 'Connect',
|
|
211
|
+
'value': False},
|
|
212
|
+
{'title': 'Connected?:', 'name': 'leco_connected', 'type': 'led', 'value': False},
|
|
213
|
+
{'title': 'Name', 'name': 'leco_name', 'type': 'str', 'value': "", 'default': ""},
|
|
214
|
+
{'title': 'Host:', 'name': 'host', 'type': 'str', 'value': config_utils('network', "leco-server", "host"),
|
|
215
|
+
"default": "localhost"},
|
|
216
|
+
{'title': 'Port:', 'name': 'port', 'type': 'int', 'value': config_utils('network', 'leco-server', 'port')},
|
|
217
|
+
]},
|
|
218
|
+
]},
|
|
219
|
+
{'title': 'Actuator Settings:', 'name': 'move_settings', 'type': 'group'}
|
|
220
|
+
]
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def main(plugin_file, init=True, title='test'):
|
|
224
|
+
"""
|
|
225
|
+
this method start a DAQ_Move object with this defined plugin as actuator
|
|
226
|
+
Returns
|
|
227
|
+
-------
|
|
228
|
+
|
|
229
|
+
"""
|
|
230
|
+
import sys
|
|
231
|
+
from qtpy import QtWidgets
|
|
232
|
+
from pymodaq.control_modules.daq_move import DAQ_Move
|
|
233
|
+
from pathlib import Path
|
|
234
|
+
|
|
235
|
+
act = Path(plugin_file).stem.split('daq_move_')[1]
|
|
236
|
+
|
|
237
|
+
app = mkQApp("PyMoDAQ Viewer")
|
|
238
|
+
|
|
239
|
+
widget = QtWidgets.QWidget()
|
|
240
|
+
prog = DAQ_Move(widget, title=title, actuator=act)
|
|
241
|
+
widget.show()
|
|
242
|
+
prog.actuator = Path(plugin_file).stem[9:]
|
|
243
|
+
if init:
|
|
244
|
+
prog.init_hardware_ui()
|
|
245
|
+
|
|
246
|
+
sys.exit(app.exec_())
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class DAQ_Move_base(QObject):
|
|
250
|
+
""" The base class to be inherited by all actuator modules
|
|
251
|
+
|
|
252
|
+
This base class implements all necessary parameters and methods for the plugin to communicate with its parent (the
|
|
253
|
+
DAQ_Move module)
|
|
254
|
+
|
|
255
|
+
Parameters
|
|
256
|
+
----------
|
|
257
|
+
parent : DAQ_Move_Hardware
|
|
258
|
+
params_state : Parameter
|
|
259
|
+
pyqtgraph Parameter instance from which the module will get the initial settings (as defined in the preset)
|
|
260
|
+
Attributes
|
|
261
|
+
----------
|
|
262
|
+
move_done_signal: Signal
|
|
263
|
+
signal represented by a float. Is emitted each time the hardware reached the target position within the epsilon
|
|
264
|
+
precision (see comon_parameters variable)
|
|
265
|
+
controller: object
|
|
266
|
+
the object representing the hardware in the plugin. Used to access hardware functionality
|
|
267
|
+
settings: Parameter
|
|
268
|
+
instance representing the hardware settings defined from the params attribute. Modifications on the GUI settings
|
|
269
|
+
will be transferred to this attribute. It stores at all times the current state of the hardware/plugin settings
|
|
270
|
+
params: List of dict used to create a Parameter object.
|
|
271
|
+
Its definition on the class level enable the automatic update of the GUI settings when changing plugins
|
|
272
|
+
(even in managers mode creation). To be populated on the plugin level as the base class does't represents a
|
|
273
|
+
real hardware
|
|
274
|
+
is_multiaxes: bool
|
|
275
|
+
class level attribute. Defines if the plugin controller controls multiple axes. If True, one has to define
|
|
276
|
+
a Master instance of this plugin and slave instances of this plugin (all sharing the same controller_ID
|
|
277
|
+
parameter)
|
|
278
|
+
current_value: DataActuator
|
|
279
|
+
stores the current position after each call to the get_actuator_value in the plugin
|
|
280
|
+
target_value: DataActuator
|
|
281
|
+
stores the target position the controller should reach within epsilon
|
|
282
|
+
"""
|
|
283
|
+
|
|
284
|
+
move_done_signal = Signal(DataActuator)
|
|
285
|
+
is_multiaxes = False
|
|
286
|
+
stage_names = [] # deprecated
|
|
287
|
+
|
|
288
|
+
_axis_names: Union[list, Dict[str, int]] = None
|
|
289
|
+
_controller_units: Union[str, List[str], Dict[str, int]] = ''
|
|
290
|
+
_epsilons: Union[float, List[float], Dict[str, float]] = None
|
|
291
|
+
_epsilon = 1.0 # deprecated
|
|
292
|
+
|
|
293
|
+
params = []
|
|
294
|
+
|
|
295
|
+
data_actuator_type = DataActuatorType.float
|
|
296
|
+
data_shape = (1,) # expected shape of the underlying actuator's value (in general a float so shape = (1, ))
|
|
297
|
+
|
|
298
|
+
def __init__(self, parent: Optional['DAQ_Move_Hardware'] = None,
|
|
299
|
+
params_state: Optional[dict] = None,
|
|
300
|
+
**kwargs):
|
|
301
|
+
QObject.__init__(self) # to make sure this is the parent class
|
|
302
|
+
self.move_is_done = False
|
|
303
|
+
self.parent = parent
|
|
304
|
+
self.stage = None
|
|
305
|
+
self.controller = None
|
|
306
|
+
self.status = edict(info="", controller=None, stage=None, initialized=False)
|
|
307
|
+
|
|
308
|
+
self._ispolling = True
|
|
309
|
+
self.parent_parameters_path = [] # this is to be added in the send_param_status to take into account when the
|
|
310
|
+
# current class instance parameter list is a child of some other class
|
|
311
|
+
self.settings = Parameter.create(name='Settings', type='group', children=self.params)
|
|
312
|
+
if params_state is not None:
|
|
313
|
+
if isinstance(params_state, dict):
|
|
314
|
+
self.settings.restoreState(params_state)
|
|
315
|
+
elif isinstance(params_state, Parameter):
|
|
316
|
+
self.settings.restoreState(params_state.saveState())
|
|
317
|
+
|
|
318
|
+
self.settings.sigTreeStateChanged.connect(self.send_param_status)
|
|
319
|
+
|
|
320
|
+
if parent is not None:
|
|
321
|
+
self._title = parent.title
|
|
322
|
+
else:
|
|
323
|
+
self._title = "myactuator"
|
|
324
|
+
|
|
325
|
+
self._axis_units: Union[Dict[str, str], List[str]] = None
|
|
326
|
+
if isinstance(self._controller_units, str):
|
|
327
|
+
self.axis_units = self._controller_units
|
|
328
|
+
else:
|
|
329
|
+
self.axis_units = self._controller_units.copy()
|
|
330
|
+
if self._epsilons is None:
|
|
331
|
+
self._epsilons = self._epsilon
|
|
332
|
+
self.epsilons = self._epsilons
|
|
333
|
+
self.axis_name = self.axis_name # to trigger some actions on units and epsilons
|
|
334
|
+
|
|
335
|
+
self._current_value = DataActuator(self._title,
|
|
336
|
+
data=[np.zeros(self.data_shape, dtype=float)],
|
|
337
|
+
units=self.axis_unit)
|
|
338
|
+
self._target_value = DataActuator(self._title,
|
|
339
|
+
data=[np.zeros(self.data_shape, dtype=float)],
|
|
340
|
+
units=self.axis_unit)
|
|
341
|
+
|
|
342
|
+
self.poll_timer = QTimer()
|
|
343
|
+
self.poll_timer.setInterval(config('actuator', 'polling_interval_ms'))
|
|
344
|
+
self._poll_timeout = config('actuator', 'polling_timeout_s')
|
|
345
|
+
self.poll_timer.timeout.connect(self.check_target_reached)
|
|
346
|
+
|
|
347
|
+
self.ini_attributes()
|
|
348
|
+
|
|
349
|
+
@property
|
|
350
|
+
def axis_unit(self) -> str:
|
|
351
|
+
""" Get/set the unit of the currently chosen axis
|
|
352
|
+
|
|
353
|
+
Will update the printed controller unit in the UI
|
|
354
|
+
|
|
355
|
+
New in 4.4.0
|
|
356
|
+
"""
|
|
357
|
+
return self.axis_units[self.axis_index_key]
|
|
358
|
+
|
|
359
|
+
@axis_unit.setter
|
|
360
|
+
def axis_unit(self, unit: str):
|
|
361
|
+
self.axis_units[self.axis_index_key] = unit
|
|
362
|
+
self.settings.child('units').setValue(unit)
|
|
363
|
+
self.emit_status(ThreadCommand(ThreadStatusMove.UNITS, unit))
|
|
364
|
+
|
|
365
|
+
@property
|
|
366
|
+
def axis_units(self) -> Union[List[str], Dict[str, str]]:
|
|
367
|
+
""" Get/Set the units for each axis of the controller
|
|
368
|
+
|
|
369
|
+
New in 4.4.0
|
|
370
|
+
"""
|
|
371
|
+
return self._axis_units
|
|
372
|
+
|
|
373
|
+
@axis_units.setter
|
|
374
|
+
def axis_units(self, units: Union[str, List[str], Dict[str, str]]):
|
|
375
|
+
if isinstance(units, str):
|
|
376
|
+
if isinstance(self.axis_names, list):
|
|
377
|
+
units_tmp = [units for _ in range(len(self.axis_names))]
|
|
378
|
+
else:
|
|
379
|
+
units_tmp = {}
|
|
380
|
+
for key in self.axis_names:
|
|
381
|
+
units_tmp[key] = units
|
|
382
|
+
else:
|
|
383
|
+
if not isinstance(units, type(self.axis_names)):
|
|
384
|
+
raise TypeError('units should be defined just like axis_names: a str, list of string or'
|
|
385
|
+
'dict of string')
|
|
386
|
+
if len(units) != len(self.axis_names):
|
|
387
|
+
raise ValueError('Units should be defined either as a single str or a list/dict with'
|
|
388
|
+
'a str defined for each axis')
|
|
389
|
+
units_tmp = units
|
|
390
|
+
self._axis_units = units_tmp
|
|
391
|
+
|
|
392
|
+
@property
|
|
393
|
+
def epsilon(self) -> float:
|
|
394
|
+
""" Get/Set the epsilon of the currently chosen axis
|
|
395
|
+
|
|
396
|
+
New in 4.4.0
|
|
397
|
+
"""
|
|
398
|
+
return self.epsilons[self.axis_index_key]
|
|
399
|
+
|
|
400
|
+
@epsilon.setter
|
|
401
|
+
def epsilon(self, eps: float):
|
|
402
|
+
self.epsilons[self.axis_index_key] = eps
|
|
403
|
+
|
|
404
|
+
@property
|
|
405
|
+
def epsilons(self) -> Union[List[float], Dict[str, float]]:
|
|
406
|
+
""" Get/Set the epsilon for each axis of the controller
|
|
407
|
+
|
|
408
|
+
New in 4.4.0
|
|
409
|
+
"""
|
|
410
|
+
return self._epsilons
|
|
411
|
+
|
|
412
|
+
@epsilons.setter
|
|
413
|
+
def epsilons(self, epsilons: Union[float, List[float], Dict[str, float]]):
|
|
414
|
+
if isinstance(epsilons, numbers.Number):
|
|
415
|
+
if isinstance(self.axis_names, list):
|
|
416
|
+
epsilons_tmp = [epsilons for _ in range(len(self.axis_names))]
|
|
417
|
+
else:
|
|
418
|
+
epsilons_tmp = {}
|
|
419
|
+
for key in self.axis_names:
|
|
420
|
+
epsilons_tmp[key] = epsilons
|
|
421
|
+
else:
|
|
422
|
+
if not isinstance(epsilons, type(self.axis_names)):
|
|
423
|
+
raise TypeError('units should be defined just like axis_names: a float, list of '
|
|
424
|
+
'float or dict of float')
|
|
425
|
+
if len(epsilons) != len(self.axis_names):
|
|
426
|
+
raise ValueError('epsilons should be defined either as a single float or a '
|
|
427
|
+
'list/dict with'
|
|
428
|
+
'a float defined for each axis')
|
|
429
|
+
epsilons_tmp = epsilons
|
|
430
|
+
self._epsilons = epsilons_tmp
|
|
431
|
+
|
|
432
|
+
@property
|
|
433
|
+
def controller_units(self):
|
|
434
|
+
""" Get/Set the units of the currently chosen axis of the controller
|
|
435
|
+
|
|
436
|
+
Deprecated with pymodaq >= 4.4.0
|
|
437
|
+
|
|
438
|
+
The property controller_units is deprecated please use the axis_unit property
|
|
439
|
+
"""
|
|
440
|
+
deprecation_msg('The property controller_units is deprecated please use the'
|
|
441
|
+
'axis_unit property.')
|
|
442
|
+
return self.axis_unit
|
|
443
|
+
|
|
444
|
+
@controller_units.setter
|
|
445
|
+
def controller_units(self, units: str = ''):
|
|
446
|
+
deprecation_msg('The property controller_units is deprecated please use the'
|
|
447
|
+
'axis_unit property.')
|
|
448
|
+
self._axis_units[self.axis_index_key] = units
|
|
449
|
+
|
|
450
|
+
@property
|
|
451
|
+
def axis_name(self) -> Union[str]:
|
|
452
|
+
"""Get/Set the current axis using its string identifier"""
|
|
453
|
+
limits = self.settings.child('multiaxes', 'axis').opts['limits']
|
|
454
|
+
if isinstance(limits, list):
|
|
455
|
+
return self.settings['multiaxes', 'axis']
|
|
456
|
+
elif isinstance(limits, dict):
|
|
457
|
+
return find_keys_from_val(limits, val=self.settings['multiaxes', 'axis'])[0]
|
|
458
|
+
else:
|
|
459
|
+
return ''
|
|
460
|
+
|
|
461
|
+
@axis_name.setter
|
|
462
|
+
def axis_name(self, name: str):
|
|
463
|
+
limits = self.settings.child('multiaxes', 'axis').opts['limits']
|
|
464
|
+
if name in limits:
|
|
465
|
+
if isinstance(limits, list):
|
|
466
|
+
self.settings.child('multiaxes', 'axis').setValue(name)
|
|
467
|
+
elif isinstance(limits, dict):
|
|
468
|
+
self.settings.child('multiaxes', 'axis').setValue(limits[name])
|
|
469
|
+
QtWidgets.QApplication.processEvents()
|
|
470
|
+
self.axis_unit = self.axis_unit
|
|
471
|
+
self.settings.child('epsilon').setValue(self.epsilon)
|
|
472
|
+
if self.controller is not None:
|
|
473
|
+
self._current_value = self.get_actuator_value()
|
|
474
|
+
|
|
475
|
+
@property
|
|
476
|
+
def axis_names(self) -> Union[List, Dict]:
|
|
477
|
+
""" Get/Set the names of all axes controlled by this instrument plugin
|
|
478
|
+
|
|
479
|
+
Returns
|
|
480
|
+
-------
|
|
481
|
+
List of string or dictionary mapping names to integers
|
|
482
|
+
"""
|
|
483
|
+
return self.settings.child('multiaxes', 'axis').opts['limits']
|
|
484
|
+
|
|
485
|
+
@axis_names.setter
|
|
486
|
+
def axis_names(self, names: Union[List, Dict]):
|
|
487
|
+
self.settings.child('multiaxes', 'axis').setLimits(names)
|
|
488
|
+
QtWidgets.QApplication.processEvents()
|
|
489
|
+
|
|
490
|
+
@property
|
|
491
|
+
def axis_value(self) -> int:
|
|
492
|
+
"""Get the current value selected from the current axis
|
|
493
|
+
|
|
494
|
+
In case axis_names is a list, return the element of the list: self.axis_name
|
|
495
|
+
In case axis_names is a dict, return the value of the dict self.axis_names[self.axis_name]
|
|
496
|
+
"""
|
|
497
|
+
if isinstance(self.axis_names, list):
|
|
498
|
+
return self.axis_name
|
|
499
|
+
else:
|
|
500
|
+
return self.axis_names[self.axis_name]
|
|
501
|
+
|
|
502
|
+
@property
|
|
503
|
+
def axis_index_key(self) -> Union[int, str]:
|
|
504
|
+
""" Get the current index or key correspondingto the current axis
|
|
505
|
+
|
|
506
|
+
In case axis_names is a list, return the index wihtin the list
|
|
507
|
+
In case axis_names is a dict, return the key of the dict self.axis_name
|
|
508
|
+
|
|
509
|
+
"""
|
|
510
|
+
if isinstance(self.axis_names, list):
|
|
511
|
+
return self.axis_names.index(self.axis_name)
|
|
512
|
+
else:
|
|
513
|
+
return self.axis_name
|
|
514
|
+
|
|
515
|
+
def ini_attributes(self):
|
|
516
|
+
""" To be subclassed, in order to init specific attributes needed by the real implementation"""
|
|
517
|
+
self.controller = None
|
|
518
|
+
|
|
519
|
+
def ini_stage_init(
|
|
520
|
+
self,
|
|
521
|
+
old_controller: Optional[HardwareController] = None,
|
|
522
|
+
new_controller: Optional[HardwareController] = None,
|
|
523
|
+
slave_controller: Optional[HardwareController] = None,
|
|
524
|
+
) -> Optional[HardwareController]:
|
|
525
|
+
"""Manage the Master/Slave controller issue
|
|
526
|
+
|
|
527
|
+
First initialize the status dictionary
|
|
528
|
+
Then check whether this stage is controlled by a multiaxe controller (to be defined for each plugin)
|
|
529
|
+
if it is a multiaxes controller then:
|
|
530
|
+
* if it is Master: init the controller here
|
|
531
|
+
* if it is Slave: use an already initialized controller (defined in the preset of the dashboard)
|
|
532
|
+
|
|
533
|
+
Parameters
|
|
534
|
+
----------
|
|
535
|
+
old_controller: object
|
|
536
|
+
The particular object that allow the communication with the hardware, in general a python wrapper around the
|
|
537
|
+
hardware library. In case of Slave this one comes from a previously initialized plugin
|
|
538
|
+
new_controller: object
|
|
539
|
+
The particular object that allow the communication with the hardware, in general a python wrapper around the
|
|
540
|
+
hardware library. In case of Master it is the new instance of your plugin controller
|
|
541
|
+
"""
|
|
542
|
+
if old_controller is None and slave_controller is not None:
|
|
543
|
+
old_controller = slave_controller
|
|
544
|
+
|
|
545
|
+
self.status.update(edict(info="", controller=None, initialized=False))
|
|
546
|
+
if not self.is_master:
|
|
547
|
+
if old_controller is None:
|
|
548
|
+
raise Exception('no controller has been defined externally while this axe '
|
|
549
|
+
'is a slave one')
|
|
550
|
+
else:
|
|
551
|
+
controller = old_controller
|
|
552
|
+
else: # Master stage
|
|
553
|
+
controller = new_controller
|
|
554
|
+
self.controller = controller
|
|
555
|
+
return controller
|
|
556
|
+
|
|
557
|
+
@property
|
|
558
|
+
def current_value(self):
|
|
559
|
+
if self.data_actuator_type == self.data_actuator_type.float:
|
|
560
|
+
return self._current_value.value()
|
|
561
|
+
else:
|
|
562
|
+
return self._current_value
|
|
563
|
+
|
|
564
|
+
@current_value.setter
|
|
565
|
+
def current_value(self, value: Union[float, np.ndarray, DataActuator]):
|
|
566
|
+
if isinstance(value, numbers.Number) or isinstance(value, np.ndarray):
|
|
567
|
+
self._current_value = DataActuator(self._title, data=value,
|
|
568
|
+
units=self.axis_unit)
|
|
569
|
+
else:
|
|
570
|
+
if (not Unit(self.axis_unit).is_compatible_with(
|
|
571
|
+
Unit(value.units)) and
|
|
572
|
+
value.units == ''):
|
|
573
|
+
value.force_units(self.axis_unit)
|
|
574
|
+
self._current_value = value
|
|
575
|
+
|
|
576
|
+
@property
|
|
577
|
+
def target_value(self):
|
|
578
|
+
if self.data_actuator_type.name == self.data_actuator_type.float:
|
|
579
|
+
return self._target_value.value()
|
|
580
|
+
else:
|
|
581
|
+
return self._target_value
|
|
582
|
+
|
|
583
|
+
@target_value.setter
|
|
584
|
+
def target_value(self, value: Union[numbers.Number, DataActuator]):
|
|
585
|
+
if isinstance(value, numbers.Number):
|
|
586
|
+
self._target_value = DataActuator(self._title, data=value,
|
|
587
|
+
units=self.axis_unit)
|
|
588
|
+
else:
|
|
589
|
+
if (not Unit(self.axis_unit).is_compatible_with(
|
|
590
|
+
Unit(value.units)) and
|
|
591
|
+
value.units == ''):
|
|
592
|
+
value.force_units(self.axis_unit)
|
|
593
|
+
self._target_value = value
|
|
594
|
+
|
|
595
|
+
@property
|
|
596
|
+
def current_position(self):
|
|
597
|
+
deprecation_msg('current_position attribute should not be used, use current_value')
|
|
598
|
+
return self.current_value
|
|
599
|
+
|
|
600
|
+
@current_position.setter
|
|
601
|
+
def current_position(self, value):
|
|
602
|
+
self.current_value = value
|
|
603
|
+
|
|
604
|
+
@property
|
|
605
|
+
def target_position(self):
|
|
606
|
+
deprecation_msg('target_position attribute should not be used, use target_value')
|
|
607
|
+
return self.target_value
|
|
608
|
+
|
|
609
|
+
@target_position.setter
|
|
610
|
+
def target_position(self, value):
|
|
611
|
+
self.target_value = value
|
|
612
|
+
|
|
613
|
+
@property
|
|
614
|
+
def is_master(self) -> bool:
|
|
615
|
+
""" Get the controller master/slave status
|
|
616
|
+
|
|
617
|
+
new in version 4.3.0
|
|
618
|
+
"""
|
|
619
|
+
return self.settings['multiaxes', 'multi_status'] == 'Master'
|
|
620
|
+
|
|
621
|
+
@property
|
|
622
|
+
def ispolling(self):
|
|
623
|
+
""" Get/Set the polling status"""
|
|
624
|
+
return self._ispolling
|
|
625
|
+
|
|
626
|
+
@ispolling.setter
|
|
627
|
+
def ispolling(self, polling=True):
|
|
628
|
+
self._ispolling = polling
|
|
629
|
+
|
|
630
|
+
def check_bound(self, position: DataActuator) -> DataActuator:
|
|
631
|
+
""" Check if the current position is within the software bounds
|
|
632
|
+
|
|
633
|
+
Return the new position eventually coerced within the bounds
|
|
634
|
+
"""
|
|
635
|
+
if self.settings['bounds', 'is_bounds']:
|
|
636
|
+
if self.data_actuator_type == DataActuatorType.DataActuator:
|
|
637
|
+
for data_array in position:
|
|
638
|
+
if np.any(data_array > self.settings['bounds', 'max_bound']) or \
|
|
639
|
+
np.any(data_array < self.settings['bounds', 'min_bound']):
|
|
640
|
+
self.emit_status(ThreadCommand('outofbounds'))
|
|
641
|
+
data_array[data_array > self.settings['bounds', 'max_bound']] = self.settings['bounds', 'max_bound']
|
|
642
|
+
data_array[data_array < self.settings['bounds', 'min_bound']] = self.settings['bounds', 'min_bound']
|
|
643
|
+
|
|
644
|
+
else:
|
|
645
|
+
if position > self.settings['bounds', 'max_bound']:
|
|
646
|
+
self.emit_status(ThreadCommand('outofbounds'))
|
|
647
|
+
position = self.settings['bounds', 'max_bound']
|
|
648
|
+
elif position < self.settings['bounds', 'min_bound']:
|
|
649
|
+
self.emit_status(ThreadCommand('outofbounds'))
|
|
650
|
+
position = self.settings['bounds', 'min_bound']
|
|
651
|
+
return position
|
|
652
|
+
|
|
653
|
+
@abstractmethod
|
|
654
|
+
def get_actuator_value(self):
|
|
655
|
+
if hasattr(self, 'check_position'):
|
|
656
|
+
deprecation_msg('check_position method in plugins is deprecated, use get_actuator_value',3)
|
|
657
|
+
return self.check_position() # type: ignore
|
|
658
|
+
else:
|
|
659
|
+
raise NotImplementedError
|
|
660
|
+
|
|
661
|
+
@abstractmethod
|
|
662
|
+
def close(self) -> None:
|
|
663
|
+
raise NotImplementedError
|
|
664
|
+
|
|
665
|
+
def move_abs(self, value: Union[float, DataActuator]):
|
|
666
|
+
if hasattr(self, 'move_Abs'):
|
|
667
|
+
deprecation_msg('move_Abs method in plugins is deprecated, use move_abs', 3)
|
|
668
|
+
self.move_Abs(value) # type: ignore
|
|
669
|
+
else:
|
|
670
|
+
raise NotImplementedError
|
|
671
|
+
|
|
672
|
+
def move_rel(self, value: Union[float, DataActuator]):
|
|
673
|
+
if hasattr(self, 'move_Rel'):
|
|
674
|
+
deprecation_msg('move_Rel method in plugins is deprecated, use move_rel', 3)
|
|
675
|
+
self.move_Rel(value) # type: ignore
|
|
676
|
+
else:
|
|
677
|
+
raise NotImplementedError
|
|
678
|
+
|
|
679
|
+
def move_home(self, value: Union[float, DataActuator]):
|
|
680
|
+
if hasattr(self, 'move_Home'):
|
|
681
|
+
deprecation_msg('move_Home method in plugins is deprecated, use move_home', 3)
|
|
682
|
+
self.move_Home() # type: ignore
|
|
683
|
+
else:
|
|
684
|
+
raise NotImplementedError
|
|
685
|
+
|
|
686
|
+
def emit_status(self, status: ThreadCommand):
|
|
687
|
+
""" Emit the status_sig signal with the given status ThreadCommand back to the main GUI.
|
|
688
|
+
"""
|
|
689
|
+
if self.parent is not None:
|
|
690
|
+
self.parent.status_sig.emit(status)
|
|
691
|
+
QtWidgets.QApplication.processEvents()
|
|
692
|
+
else:
|
|
693
|
+
print(status)
|
|
694
|
+
|
|
695
|
+
def emit_value(self, pos: DataActuator):
|
|
696
|
+
"""Convenience method to emit the current actuator value back to the UI"""
|
|
697
|
+
|
|
698
|
+
self.emit_status(ThreadCommand(ThreadStatusMove.GET_ACTUATOR_VALUE, pos))
|
|
699
|
+
|
|
700
|
+
def commit_settings(self, param: Parameter):
|
|
701
|
+
"""
|
|
702
|
+
to subclass to transfer parameters to hardware
|
|
703
|
+
"""
|
|
704
|
+
|
|
705
|
+
def commit_common_settings(self, param):
|
|
706
|
+
pass
|
|
707
|
+
|
|
708
|
+
def move_done(self, position: Optional[
|
|
709
|
+
DataActuator] = None): # the position argument is just there to match some signature of child classes
|
|
710
|
+
"""
|
|
711
|
+
| Emit a move done signal transmitting the float position to hardware.
|
|
712
|
+
| The position argument is just there to match some signature of child classes.
|
|
713
|
+
|
|
714
|
+
=============== ========== =============================================================================
|
|
715
|
+
**Arguments** **Type** **Description**
|
|
716
|
+
*position* float The position argument is just there to match some signature of child classes
|
|
717
|
+
=============== ========== =============================================================================
|
|
718
|
+
|
|
719
|
+
"""
|
|
720
|
+
if position is None:
|
|
721
|
+
if self.data_actuator_type.name == 'float':
|
|
722
|
+
position = DataActuator(self._title, data=self.get_actuator_value(),
|
|
723
|
+
units=self.axis_unit)
|
|
724
|
+
else:
|
|
725
|
+
position = self.get_actuator_value()
|
|
726
|
+
if position.name != self._title: # make sure the emitted DataActuator has the name of the real implementation
|
|
727
|
+
#of the plugin
|
|
728
|
+
position = DataActuator(self._title, data=position.value(self.axis_unit),
|
|
729
|
+
units=self.axis_unit)
|
|
730
|
+
self.move_done_signal.emit(position)
|
|
731
|
+
self.move_is_done = True
|
|
732
|
+
|
|
733
|
+
def poll_moving(self):
|
|
734
|
+
""" Poll the current moving. In case of timeout emit the raise timeout Thread command.
|
|
735
|
+
|
|
736
|
+
See Also
|
|
737
|
+
--------
|
|
738
|
+
DAQ_utils.ThreadCommand, move_done
|
|
739
|
+
"""
|
|
740
|
+
if not ('TCPServer' in self.__class__.__name__ or
|
|
741
|
+
'LECODirector' in self.__class__.__name__):
|
|
742
|
+
self.start_time = perf_counter()
|
|
743
|
+
if self.ispolling:
|
|
744
|
+
self.poll_timer.start()
|
|
745
|
+
else:
|
|
746
|
+
if self.data_actuator_type == DataActuatorType.float:
|
|
747
|
+
self._current_value = DataActuator(data=self.get_actuator_value(),
|
|
748
|
+
units=self.axis_unit)
|
|
749
|
+
else:
|
|
750
|
+
self._current_value = self.get_actuator_value()
|
|
751
|
+
if (not Unit(self.axis_unit).is_compatible_with(
|
|
752
|
+
Unit(self._current_value.units)) and
|
|
753
|
+
self._current_value.units == ''):
|
|
754
|
+
# this happens if the units have not been specified in
|
|
755
|
+
# the plugin
|
|
756
|
+
self._current_value.force_units(self.axis_unit)
|
|
757
|
+
|
|
758
|
+
logger.debug(f'Current position: {self._current_value}')
|
|
759
|
+
self.move_done(self._current_value)
|
|
760
|
+
|
|
761
|
+
def _condition_to_reach_target(self, check_absolute_difference=True,) -> bool:
|
|
762
|
+
"""Implement the condition for exiting the polling mechanism and specifying that the
|
|
763
|
+
target value has been reached
|
|
764
|
+
|
|
765
|
+
Returns
|
|
766
|
+
-------
|
|
767
|
+
bool: if True, PyMoDAQ considers the target value has been reached
|
|
768
|
+
See Also
|
|
769
|
+
--------
|
|
770
|
+
absolute_difference_condition_to_reach_target
|
|
771
|
+
user_condition_to_reach_target
|
|
772
|
+
"""
|
|
773
|
+
cond = True
|
|
774
|
+
if check_absolute_difference:
|
|
775
|
+
cond = self.absolute_difference_condition_to_reach_target()
|
|
776
|
+
return cond and self.user_condition_to_reach_target()
|
|
777
|
+
|
|
778
|
+
def absolute_difference_condition_to_reach_target(self) -> bool:
|
|
779
|
+
""" Implement the condition for exiting the polling mechanism and specifying that the
|
|
780
|
+
target value has been reached
|
|
781
|
+
|
|
782
|
+
Returns
|
|
783
|
+
-------
|
|
784
|
+
bool: if True, PyMoDAQ considers the target value has been reached at epsilon
|
|
785
|
+
|
|
786
|
+
See Also
|
|
787
|
+
--------
|
|
788
|
+
user_condition_to_reach_target
|
|
789
|
+
"""
|
|
790
|
+
try:
|
|
791
|
+
epsilon_calculated = (
|
|
792
|
+
self._current_value - self._target_value).abs().value(self.axis_unit)
|
|
793
|
+
except DataUnitError as e:
|
|
794
|
+
epsilon_calculated = abs(self._current_value.value() - self._target_value.value())
|
|
795
|
+
logger.warning(f'Unit issue when calculating epsilon, units are not compatible between'
|
|
796
|
+
f'target and current values')
|
|
797
|
+
except OffsetUnitCalculusError as e:
|
|
798
|
+
if '°C' in self.axis_unit or 'celcius' in self.axis_unit.lower():
|
|
799
|
+
epsilon_calculated = (
|
|
800
|
+
self._current_value.to_base_units() -
|
|
801
|
+
self._target_value.to_base_units()).abs().value()
|
|
802
|
+
else:
|
|
803
|
+
raise e
|
|
804
|
+
|
|
805
|
+
return (epsilon_calculated < self.epsilon)
|
|
806
|
+
|
|
807
|
+
def user_condition_to_reach_target(self) -> bool:
|
|
808
|
+
""" Implement a user defined condition for exiting the polling mechanism and specifying
|
|
809
|
+
that the target value has been reached (on top of the existing epsilon mechanism)
|
|
810
|
+
|
|
811
|
+
Should be reimplemented in plugins to implement other conditions
|
|
812
|
+
|
|
813
|
+
Returns
|
|
814
|
+
-------
|
|
815
|
+
bool: if True, PyMoDAQ considers the target value has been reached
|
|
816
|
+
"""
|
|
817
|
+
return True
|
|
818
|
+
|
|
819
|
+
def check_target_reached(self):
|
|
820
|
+
logger.debug(f"epsilon value is {self.epsilon}")
|
|
821
|
+
logger.debug(f"current_value value is {self._current_value}")
|
|
822
|
+
logger.debug(f"target_value value is {self._target_value}")
|
|
823
|
+
|
|
824
|
+
if not self._condition_to_reach_target():
|
|
825
|
+
|
|
826
|
+
logger.debug(f'Check move_is_done: {self.move_is_done}')
|
|
827
|
+
if self.move_is_done:
|
|
828
|
+
self.emit_status(ThreadCommand(ThreadStatus.UPDATE_STATUS, 'Move has been stopped', ))
|
|
829
|
+
logger.info('Move has been stopped')
|
|
830
|
+
self.current_value = self.get_actuator_value()
|
|
831
|
+
self.emit_value(self._current_value)
|
|
832
|
+
logger.debug(f'Current value: {self._current_value}')
|
|
833
|
+
|
|
834
|
+
if perf_counter() - self.start_time >= self.settings['timeout']:
|
|
835
|
+
self.poll_timer.stop()
|
|
836
|
+
self.emit_status(ThreadCommand(ThreadStatus.RAISE_TIMEOUT, ))
|
|
837
|
+
logger.info('Timeout activated')
|
|
838
|
+
else:
|
|
839
|
+
self.poll_timer.stop()
|
|
840
|
+
self.current_value = self.get_actuator_value()
|
|
841
|
+
logger.debug(f'Current value: {self._current_value}')
|
|
842
|
+
self.move_done(self._current_value)
|
|
843
|
+
|
|
844
|
+
def send_param_status(self, param, changes):
|
|
845
|
+
""" Send changes value updates to the gui to update consequently the User Interface
|
|
846
|
+
|
|
847
|
+
The message passing is made via the ThreadCommand "update_settings".
|
|
848
|
+
"""
|
|
849
|
+
|
|
850
|
+
for param, change, data in changes:
|
|
851
|
+
path = self.settings.childPath(param)
|
|
852
|
+
if change == 'childAdded':
|
|
853
|
+
self.emit_status(ThreadCommand(ThreadStatus.UPDATE_SETTINGS,
|
|
854
|
+
[self.parent_parameters_path + path, [data[0].saveState(), data[1]],
|
|
855
|
+
change])) # send parameters values/limits back to the GUI. Send kind of a copy back the GUI otherwise the child reference will be the same in both th eUI and the plugin so one of them will be removed
|
|
856
|
+
elif change == 'value' or change == 'limits' or change == 'options':
|
|
857
|
+
self.emit_status(ThreadCommand(ThreadStatus.UPDATE_SETTINGS,
|
|
858
|
+
[self.parent_parameters_path + path, data,
|
|
859
|
+
change])) # send parameters values/limits back to the GUI
|
|
860
|
+
elif change == 'parent':
|
|
861
|
+
pass
|
|
862
|
+
elif change == 'limits':
|
|
863
|
+
self.emit_status(ThreadCommand(ThreadStatus.UPDATE_SETTINGS,
|
|
864
|
+
[self.parent_parameters_path + path, data,
|
|
865
|
+
change]))
|
|
866
|
+
|
|
867
|
+
def get_position_with_scaling(self, pos: DataActuator) -> DataActuator:
|
|
868
|
+
""" Get the current position from the hardware with scaling conversion.
|
|
869
|
+
"""
|
|
870
|
+
if self.settings['scaling', 'use_scaling']:
|
|
871
|
+
if self.data_actuator_type == DataActuatorType.float:
|
|
872
|
+
pos = (pos - self.settings['scaling', 'offset']) * self.settings['scaling', 'scaling']
|
|
873
|
+
else:
|
|
874
|
+
pos = (pos - Q_(self.settings['scaling', 'offset'],
|
|
875
|
+
self.axis_unit)) * self.settings['scaling', 'scaling']
|
|
876
|
+
return pos
|
|
877
|
+
|
|
878
|
+
def set_position_with_scaling(self, pos: DataActuator) -> DataActuator:
|
|
879
|
+
""" Set the current position from the parameter and hardware with scaling conversion.
|
|
880
|
+
"""
|
|
881
|
+
if self.settings['scaling', 'use_scaling']:
|
|
882
|
+
if self.data_actuator_type == DataActuatorType.DataActuator:
|
|
883
|
+
pos = pos / self.settings['scaling', 'scaling'] + Q_(self.settings['scaling', 'offset'], self.axis_unit)
|
|
884
|
+
else:
|
|
885
|
+
pos = pos / self.settings['scaling', 'scaling'] + self.settings['scaling', 'offset']
|
|
886
|
+
return pos
|
|
887
|
+
|
|
888
|
+
def set_position_relative_with_scaling(self, pos: DataActuator) -> DataActuator:
|
|
889
|
+
""" Set the scaled positions in case of relative moves
|
|
890
|
+
"""
|
|
891
|
+
if self.settings['scaling', 'use_scaling']:
|
|
892
|
+
pos = pos / self.settings['scaling', 'scaling']
|
|
893
|
+
return pos
|
|
894
|
+
|
|
895
|
+
@Slot(edict)
|
|
896
|
+
def update_settings(self, settings_parameter_dict): # settings_parameter_dict=edict(path=path,param=param)
|
|
897
|
+
""" Receive the settings_parameter signal from the param_tree_changed method and make hardware updates of
|
|
898
|
+
modified values.
|
|
899
|
+
"""
|
|
900
|
+
path = settings_parameter_dict['path']
|
|
901
|
+
param = settings_parameter_dict['param']
|
|
902
|
+
change = settings_parameter_dict['change']
|
|
903
|
+
apply_settings = True
|
|
904
|
+
try:
|
|
905
|
+
self.settings.sigTreeStateChanged.disconnect(self.send_param_status)
|
|
906
|
+
except Exception:
|
|
907
|
+
pass
|
|
908
|
+
if change == 'value':
|
|
909
|
+
self.settings.child(*path[1:]).setValue(param.value()) # blocks signal back to main UI
|
|
910
|
+
elif change == 'childAdded':
|
|
911
|
+
try:
|
|
912
|
+
child = Parameter.create(name='tmp')
|
|
913
|
+
child.restoreState(param)
|
|
914
|
+
param = child
|
|
915
|
+
self.settings.child(*path[1:]).addChild(child) # blocks signal back to main UI
|
|
916
|
+
except ValueError:
|
|
917
|
+
apply_settings = False
|
|
918
|
+
elif change == 'parent':
|
|
919
|
+
try:
|
|
920
|
+
children = putils.get_param_from_name(self.settings, param.name())
|
|
921
|
+
|
|
922
|
+
if children is not None:
|
|
923
|
+
path = putils.get_param_path(children)
|
|
924
|
+
self.settings.child(*path[1:-1]).removeChild(children)
|
|
925
|
+
except IndexError:
|
|
926
|
+
logger.debug(f'Could not remove children from {param.name()}')
|
|
927
|
+
self.settings.sigTreeStateChanged.connect(self.send_param_status)
|
|
928
|
+
if apply_settings:
|
|
929
|
+
self.commit_common_settings(param)
|
|
930
|
+
self.commit_settings(param)
|
|
931
|
+
|
|
932
|
+
if param.name() == 'axis':
|
|
933
|
+
self.axis_name = param.value()
|
|
934
|
+
elif param.name() == 'epsilon':
|
|
935
|
+
self.epsilon = param.value()
|
|
936
|
+
|
|
937
|
+
# abstract methods to be overwritten by the concrete implementations
|
|
938
|
+
@abstractmethod
|
|
939
|
+
def ini_stage(self, controller: Optional[HardwareController] = None) -> tuple[str, bool]:
|
|
940
|
+
"""Actuator communication initialization
|
|
941
|
+
|
|
942
|
+
Parameters
|
|
943
|
+
----------
|
|
944
|
+
controller: (object)
|
|
945
|
+
custom object of a PyMoDAQ plugin (Slave case). None if only one actuator by controller (Master case)
|
|
946
|
+
|
|
947
|
+
Returns
|
|
948
|
+
-------
|
|
949
|
+
info: str
|
|
950
|
+
initialized: bool
|
|
951
|
+
False if initialization failed otherwise True
|
|
952
|
+
"""
|
|
953
|
+
pass
|
|
954
|
+
|
|
955
|
+
@abstractmethod
|
|
956
|
+
def stop_motion(self, value: DataActuator) -> None:
|
|
957
|
+
"""Stop the actuator and emit move_done signal."""
|
|
958
|
+
pass
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
class DAQ_Move_TCP_server(DAQ_Move_base, TCPServer):
|
|
962
|
+
"""
|
|
963
|
+
================= ==============================
|
|
964
|
+
**Attributes** **Type**
|
|
965
|
+
*command_server* instance of Signal
|
|
966
|
+
*x_axis* 1D numpy array
|
|
967
|
+
*y_axis* 1D numpy array
|
|
968
|
+
*data* double precision float array
|
|
969
|
+
================= ==============================
|
|
970
|
+
|
|
971
|
+
See Also
|
|
972
|
+
--------
|
|
973
|
+
utility_classes.DAQ_TCP_server
|
|
974
|
+
"""
|
|
975
|
+
params_client = [] # parameters of a client grabber
|
|
976
|
+
command_server = Signal(list)
|
|
977
|
+
data_actuator_type = DataActuatorType.DataActuator
|
|
978
|
+
|
|
979
|
+
message_list = ["Quit", "Status", "Done", "Server Closed", "Info", "Infos", "Info_xml", "move_abs",
|
|
980
|
+
'move_home', 'move_rel', 'get_actuator_value', 'stop_motion', 'position_is', 'move_done']
|
|
981
|
+
socket_types = ["ACTUATOR"]
|
|
982
|
+
params = comon_parameters_fun() + tcp_parameters
|
|
983
|
+
|
|
984
|
+
def __init__(self, parent=None, params_state=None):
|
|
985
|
+
"""
|
|
986
|
+
|
|
987
|
+
Parameters
|
|
988
|
+
----------
|
|
989
|
+
parent
|
|
990
|
+
params_state
|
|
991
|
+
"""
|
|
992
|
+
self.client_type = "ACTUATOR"
|
|
993
|
+
DAQ_Move_base.__init__(self, parent, params_state) # initialize base class with commom attribute and methods
|
|
994
|
+
self.settings.child('bounds').hide()
|
|
995
|
+
self.settings.child('scaling').hide()
|
|
996
|
+
self.settings.child('epsilon').setValue(1)
|
|
997
|
+
|
|
998
|
+
TCPServer.__init__(self, self.client_type)
|
|
999
|
+
|
|
1000
|
+
def command_to_from_client(self, command):
|
|
1001
|
+
sock: Socket = self.find_socket_within_connected_clients(self.client_type)
|
|
1002
|
+
if sock is not None: # if client 'ACTUATOR' is connected then send it the command
|
|
1003
|
+
|
|
1004
|
+
if command == 'position_is':
|
|
1005
|
+
pos = DeSerializer(sock).dwa_deserialization()
|
|
1006
|
+
|
|
1007
|
+
pos = self.get_position_with_scaling(pos)
|
|
1008
|
+
self._current_value = pos
|
|
1009
|
+
self.emit_status(ThreadCommand(ThreadStatusMove.GET_ACTUATOR_VALUE, pos))
|
|
1010
|
+
|
|
1011
|
+
elif command == 'move_done':
|
|
1012
|
+
pos = DeSerializer(sock).dwa_deserialization()
|
|
1013
|
+
pos = self.get_position_with_scaling(pos)
|
|
1014
|
+
self._current_value = pos
|
|
1015
|
+
self.emit_status(ThreadCommand(ThreadStatusMove.MOVE_DONE, pos))
|
|
1016
|
+
else:
|
|
1017
|
+
self.send_command(sock, command)
|
|
1018
|
+
|
|
1019
|
+
def commit_settings(self, param):
|
|
1020
|
+
|
|
1021
|
+
if param.name() in putils.iter_children(self.settings.child('settings_client'), []):
|
|
1022
|
+
actuator_socket: Socket = \
|
|
1023
|
+
[client['socket'] for client in self.connected_clients if client['type'] == 'ACTUATOR'][0]
|
|
1024
|
+
actuator_socket.check_sended_with_serializer('set_info')
|
|
1025
|
+
path = putils.get_param_path(param)[2:]
|
|
1026
|
+
# get the path of this param as a list starting at parent 'infos'
|
|
1027
|
+
|
|
1028
|
+
actuator_socket.check_sended_with_serializer(path)
|
|
1029
|
+
|
|
1030
|
+
# send value
|
|
1031
|
+
data = ioxml.parameter_to_xml_string(param)
|
|
1032
|
+
actuator_socket.check_sended_with_serializer(data)
|
|
1033
|
+
|
|
1034
|
+
def ini_stage(self, controller=None):
|
|
1035
|
+
"""
|
|
1036
|
+
| Initialisation procedure of the detector updating the status dictionary.
|
|
1037
|
+
|
|
|
1038
|
+
| Init axes from image , here returns only None values (to tricky to di it with the server and not really necessary for images anyway)
|
|
1039
|
+
|
|
1040
|
+
See Also
|
|
1041
|
+
--------
|
|
1042
|
+
utility_classes.DAQ_TCP_server.init_server, get_xaxis, get_yaxis
|
|
1043
|
+
"""
|
|
1044
|
+
self.settings.child('infos').addChildren(self.params_client)
|
|
1045
|
+
|
|
1046
|
+
self.init_server()
|
|
1047
|
+
self.controller = self.serversocket
|
|
1048
|
+
self.settings.child('units').hide()
|
|
1049
|
+
self.settings.child('epsilon').hide()
|
|
1050
|
+
|
|
1051
|
+
info = 'TCP Server actuator'
|
|
1052
|
+
initialized = True
|
|
1053
|
+
return info, initialized
|
|
1054
|
+
|
|
1055
|
+
def read_infos(self, sock: Socket = None, infos=''):
|
|
1056
|
+
"""Reimplemented to get the units"""
|
|
1057
|
+
super().read_infos(sock, infos)
|
|
1058
|
+
|
|
1059
|
+
self.axis_unit = self.settings['settings_client', 'units']
|
|
1060
|
+
|
|
1061
|
+
def close(self):
|
|
1062
|
+
"""
|
|
1063
|
+
Should be used to uninitialize hardware.
|
|
1064
|
+
|
|
1065
|
+
See Also
|
|
1066
|
+
--------
|
|
1067
|
+
utility_classes.DAQ_TCP_server.close_server
|
|
1068
|
+
"""
|
|
1069
|
+
self.listening = False
|
|
1070
|
+
self.close_server()
|
|
1071
|
+
|
|
1072
|
+
def move_abs(self, position: DataActuator):
|
|
1073
|
+
"""
|
|
1074
|
+
|
|
1075
|
+
"""
|
|
1076
|
+
position = self.check_bound(position)
|
|
1077
|
+
self.target_value = position
|
|
1078
|
+
|
|
1079
|
+
position = self.set_position_with_scaling(position)
|
|
1080
|
+
|
|
1081
|
+
sock = self.find_socket_within_connected_clients(self.client_type)
|
|
1082
|
+
if sock is not None: # if client self.client_type is connected then send it the command
|
|
1083
|
+
sock.check_sended_with_serializer('move_abs')
|
|
1084
|
+
sock.check_sended_with_serializer(position)
|
|
1085
|
+
|
|
1086
|
+
def move_rel(self, position: DataActuator):
|
|
1087
|
+
position = self.check_bound(self.current_value + position) - self.current_value
|
|
1088
|
+
self.target_value = position + self.current_value
|
|
1089
|
+
|
|
1090
|
+
position = self.set_position_relative_with_scaling(position)
|
|
1091
|
+
sock = self.find_socket_within_connected_clients(self.client_type)
|
|
1092
|
+
if sock is not None: # if client self.client_type is connected then send it the command
|
|
1093
|
+
sock.check_sended_with_serializer('move_rel')
|
|
1094
|
+
sock.check_sended_with_serializer(position)
|
|
1095
|
+
|
|
1096
|
+
def move_home(self):
|
|
1097
|
+
"""
|
|
1098
|
+
Make the absolute move to original position (0).
|
|
1099
|
+
|
|
1100
|
+
See Also
|
|
1101
|
+
--------
|
|
1102
|
+
move_Abs
|
|
1103
|
+
"""
|
|
1104
|
+
sock = self.find_socket_within_connected_clients(self.client_type)
|
|
1105
|
+
if sock is not None: # if client self.client_type is connected then send it the command
|
|
1106
|
+
sock.check_sended_with_serializer('move_home')
|
|
1107
|
+
|
|
1108
|
+
def get_actuator_value(self):
|
|
1109
|
+
"""
|
|
1110
|
+
Get the current hardware position with scaling conversion given by get_position_with_scaling.
|
|
1111
|
+
|
|
1112
|
+
See Also
|
|
1113
|
+
--------
|
|
1114
|
+
daq_move_base.get_position_with_scaling, daq_utils.ThreadCommand
|
|
1115
|
+
"""
|
|
1116
|
+
sock = self.find_socket_within_connected_clients(self.client_type)
|
|
1117
|
+
if sock is not None: # if client self.client_type is connected then send it the command
|
|
1118
|
+
self.send_command(sock, 'get_actuator_value')
|
|
1119
|
+
|
|
1120
|
+
return self._current_value
|
|
1121
|
+
|
|
1122
|
+
def stop_motion(self):
|
|
1123
|
+
"""
|
|
1124
|
+
See Also
|
|
1125
|
+
--------
|
|
1126
|
+
daq_move_base.move_done
|
|
1127
|
+
"""
|
|
1128
|
+
sock = self.find_socket_within_connected_clients(self.client_type)
|
|
1129
|
+
if sock is not None: # if client self.client_type is connected then send it the command
|
|
1130
|
+
self.send_command(sock, 'stop_motion')
|
|
1131
|
+
|
|
1132
|
+
def stop(self):
|
|
1133
|
+
"""
|
|
1134
|
+
not implemented.
|
|
1135
|
+
"""
|
|
1136
|
+
pass
|
|
1137
|
+
return ""
|
|
1138
|
+
|
|
1139
|
+
|
|
1140
|
+
if __name__ == '__main__':
|
|
1141
|
+
test = DAQ_Move_base()
|