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.
Files changed (154) hide show
  1. pymodaq/__init__.py +98 -0
  2. pymodaq/control_modules/__init__.py +1 -0
  3. pymodaq/control_modules/daq_move.py +1238 -0
  4. pymodaq/control_modules/daq_move_ui/__init__.py +0 -0
  5. pymodaq/control_modules/daq_move_ui/factory.py +48 -0
  6. pymodaq/control_modules/daq_move_ui/ui_base.py +359 -0
  7. pymodaq/control_modules/daq_move_ui/uis/__init__.py +0 -0
  8. pymodaq/control_modules/daq_move_ui/uis/binary.py +139 -0
  9. pymodaq/control_modules/daq_move_ui/uis/original.py +120 -0
  10. pymodaq/control_modules/daq_move_ui/uis/relative.py +124 -0
  11. pymodaq/control_modules/daq_move_ui/uis/simple.py +126 -0
  12. pymodaq/control_modules/daq_viewer.py +1517 -0
  13. pymodaq/control_modules/daq_viewer_ui.py +407 -0
  14. pymodaq/control_modules/mocks.py +57 -0
  15. pymodaq/control_modules/move_utility_classes.py +1141 -0
  16. pymodaq/control_modules/thread_commands.py +137 -0
  17. pymodaq/control_modules/ui_utils.py +72 -0
  18. pymodaq/control_modules/utils.py +591 -0
  19. pymodaq/control_modules/viewer_utility_classes.py +670 -0
  20. pymodaq/daq_utils/__init__.py +0 -0
  21. pymodaq/daq_utils/daq_utils.py +6 -0
  22. pymodaq/dashboard.py +2396 -0
  23. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.aliases +3 -0
  24. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvlps +3 -0
  25. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvproj +32 -0
  26. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.vi +0 -0
  27. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_1Dgaussian.vi +0 -0
  28. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_2Dgaussian.vi +0 -0
  29. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_cmd.vi +0 -0
  30. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_float.vi +0 -0
  31. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_int.vi +0 -0
  32. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_data.vi +0 -0
  33. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_int.vi +0 -0
  34. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_scalar.vi +0 -0
  35. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_string.vi +0 -0
  36. pymodaq/examples/Labview_TCP_Client/client_state.ctl +0 -0
  37. pymodaq/examples/Labview_TCP_Client/cmd_types.ctl +0 -0
  38. pymodaq/examples/__init__.py +0 -0
  39. pymodaq/examples/function_plotter.py +160 -0
  40. pymodaq/examples/nonlinearscanner.py +126 -0
  41. pymodaq/examples/qt_less_standalone_module.py +165 -0
  42. pymodaq/examples/tcp_client.py +97 -0
  43. pymodaq/extensions/__init__.py +25 -0
  44. pymodaq/extensions/adaptive/__init__.py +2 -0
  45. pymodaq/extensions/adaptive/adaptive_optimization.py +179 -0
  46. pymodaq/extensions/adaptive/loss_function/_1d_loss_functions.py +73 -0
  47. pymodaq/extensions/adaptive/loss_function/_2d_loss_functions.py +73 -0
  48. pymodaq/extensions/adaptive/loss_function/__init__.py +3 -0
  49. pymodaq/extensions/adaptive/loss_function/loss_factory.py +110 -0
  50. pymodaq/extensions/adaptive/utils.py +123 -0
  51. pymodaq/extensions/bayesian/__init__.py +2 -0
  52. pymodaq/extensions/bayesian/acquisition/__init__.py +2 -0
  53. pymodaq/extensions/bayesian/acquisition/acquisition_function_factory.py +80 -0
  54. pymodaq/extensions/bayesian/acquisition/base_acquisition_function.py +105 -0
  55. pymodaq/extensions/bayesian/bayesian_optimization.py +143 -0
  56. pymodaq/extensions/bayesian/utils.py +180 -0
  57. pymodaq/extensions/console.py +73 -0
  58. pymodaq/extensions/daq_logger/__init__.py +1 -0
  59. pymodaq/extensions/daq_logger/abstract.py +52 -0
  60. pymodaq/extensions/daq_logger/daq_logger.py +519 -0
  61. pymodaq/extensions/daq_logger/db/__init__.py +0 -0
  62. pymodaq/extensions/daq_logger/db/db_logger.py +300 -0
  63. pymodaq/extensions/daq_logger/db/db_logger_models.py +100 -0
  64. pymodaq/extensions/daq_logger/h5logging.py +84 -0
  65. pymodaq/extensions/daq_scan.py +1218 -0
  66. pymodaq/extensions/daq_scan_ui.py +241 -0
  67. pymodaq/extensions/data_mixer/__init__.py +0 -0
  68. pymodaq/extensions/data_mixer/daq_0Dviewer_DataMixer.py +97 -0
  69. pymodaq/extensions/data_mixer/data_mixer.py +262 -0
  70. pymodaq/extensions/data_mixer/model.py +108 -0
  71. pymodaq/extensions/data_mixer/models/__init__.py +0 -0
  72. pymodaq/extensions/data_mixer/models/equation_model.py +91 -0
  73. pymodaq/extensions/data_mixer/models/gaussian_fit_model.py +65 -0
  74. pymodaq/extensions/data_mixer/parser.py +53 -0
  75. pymodaq/extensions/data_mixer/utils.py +23 -0
  76. pymodaq/extensions/h5browser.py +9 -0
  77. pymodaq/extensions/optimizers_base/__init__.py +0 -0
  78. pymodaq/extensions/optimizers_base/optimizer.py +1016 -0
  79. pymodaq/extensions/optimizers_base/thread_commands.py +22 -0
  80. pymodaq/extensions/optimizers_base/utils.py +427 -0
  81. pymodaq/extensions/pid/__init__.py +16 -0
  82. pymodaq/extensions/pid/actuator_controller.py +14 -0
  83. pymodaq/extensions/pid/daq_move_PID.py +154 -0
  84. pymodaq/extensions/pid/pid_controller.py +1016 -0
  85. pymodaq/extensions/pid/utils.py +189 -0
  86. pymodaq/extensions/utils.py +111 -0
  87. pymodaq/icon.ico +0 -0
  88. pymodaq/post_treatment/__init__.py +6 -0
  89. pymodaq/post_treatment/load_and_plot.py +352 -0
  90. pymodaq/resources/__init__.py +0 -0
  91. pymodaq/resources/config_template.toml +57 -0
  92. pymodaq/resources/preset_default.xml +1 -0
  93. pymodaq/resources/setup_plugin.py +73 -0
  94. pymodaq/splash.png +0 -0
  95. pymodaq/utils/__init__.py +0 -0
  96. pymodaq/utils/array_manipulation.py +6 -0
  97. pymodaq/utils/calibration_camera.py +180 -0
  98. pymodaq/utils/chrono_timer.py +203 -0
  99. pymodaq/utils/config.py +53 -0
  100. pymodaq/utils/conftests.py +5 -0
  101. pymodaq/utils/daq_utils.py +158 -0
  102. pymodaq/utils/data.py +128 -0
  103. pymodaq/utils/enums.py +6 -0
  104. pymodaq/utils/exceptions.py +38 -0
  105. pymodaq/utils/gui_utils/__init__.py +10 -0
  106. pymodaq/utils/gui_utils/loader_utils.py +75 -0
  107. pymodaq/utils/gui_utils/utils.py +18 -0
  108. pymodaq/utils/gui_utils/widgets/lcd.py +8 -0
  109. pymodaq/utils/h5modules/__init__.py +2 -0
  110. pymodaq/utils/h5modules/module_saving.py +526 -0
  111. pymodaq/utils/leco/__init__.py +25 -0
  112. pymodaq/utils/leco/daq_move_LECODirector.py +217 -0
  113. pymodaq/utils/leco/daq_xDviewer_LECODirector.py +163 -0
  114. pymodaq/utils/leco/director_utils.py +74 -0
  115. pymodaq/utils/leco/leco_director.py +166 -0
  116. pymodaq/utils/leco/pymodaq_listener.py +364 -0
  117. pymodaq/utils/leco/rpc_method_definitions.py +43 -0
  118. pymodaq/utils/leco/utils.py +74 -0
  119. pymodaq/utils/logger.py +6 -0
  120. pymodaq/utils/managers/__init__.py +0 -0
  121. pymodaq/utils/managers/batchscan_manager.py +346 -0
  122. pymodaq/utils/managers/modules_manager.py +589 -0
  123. pymodaq/utils/managers/overshoot_manager.py +242 -0
  124. pymodaq/utils/managers/preset_manager.py +229 -0
  125. pymodaq/utils/managers/preset_manager_utils.py +262 -0
  126. pymodaq/utils/managers/remote_manager.py +484 -0
  127. pymodaq/utils/math_utils.py +6 -0
  128. pymodaq/utils/messenger.py +6 -0
  129. pymodaq/utils/parameter/__init__.py +10 -0
  130. pymodaq/utils/parameter/utils.py +6 -0
  131. pymodaq/utils/scanner/__init__.py +5 -0
  132. pymodaq/utils/scanner/scan_config.py +16 -0
  133. pymodaq/utils/scanner/scan_factory.py +259 -0
  134. pymodaq/utils/scanner/scan_selector.py +477 -0
  135. pymodaq/utils/scanner/scanner.py +324 -0
  136. pymodaq/utils/scanner/scanners/_1d_scanners.py +174 -0
  137. pymodaq/utils/scanner/scanners/_2d_scanners.py +299 -0
  138. pymodaq/utils/scanner/scanners/__init__.py +1 -0
  139. pymodaq/utils/scanner/scanners/sequential.py +224 -0
  140. pymodaq/utils/scanner/scanners/tabular.py +319 -0
  141. pymodaq/utils/scanner/utils.py +110 -0
  142. pymodaq/utils/svg/__init__.py +6 -0
  143. pymodaq/utils/svg/svg_renderer.py +20 -0
  144. pymodaq/utils/svg/svg_view.py +35 -0
  145. pymodaq/utils/svg/svg_viewer2D.py +50 -0
  146. pymodaq/utils/tcp_ip/__init__.py +6 -0
  147. pymodaq/utils/tcp_ip/mysocket.py +12 -0
  148. pymodaq/utils/tcp_ip/serializer.py +13 -0
  149. pymodaq/utils/tcp_ip/tcp_server_client.py +772 -0
  150. pymodaq-5.1.6.dist-info/METADATA +238 -0
  151. pymodaq-5.1.6.dist-info/RECORD +154 -0
  152. pymodaq-5.1.6.dist-info/WHEEL +4 -0
  153. pymodaq-5.1.6.dist-info/entry_points.txt +7 -0
  154. pymodaq-5.1.6.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,772 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Fri Aug 30 12:21:56 2019
4
+
5
+ @author: Weber
6
+ """
7
+ from collections import OrderedDict
8
+ import select
9
+ from typing import List
10
+ import socket
11
+ from threading import Timer
12
+
13
+ import numpy as np
14
+ from qtpy.QtCore import QObject, Signal, Slot, QThread
15
+ from qtpy import QtWidgets
16
+
17
+
18
+ from pymodaq_utils.utils import getLineInfo, ThreadCommand
19
+ from pymodaq_utils import math_utils as mutils
20
+ from pymodaq_utils.config import Config
21
+ from pymodaq_gui.parameter import utils as putils
22
+ from pymodaq_gui.parameter import ioxml
23
+ from pymodaq_gui.parameter import Parameter
24
+ from pymodaq_data.data import DataToExport
25
+
26
+ from pymodaq.utils.data import DataFromPlugins, DataActuator
27
+ from pymodaq_utils.serialize.mysocket import Socket
28
+ from pymodaq_utils.serialize.serializer_legacy import Serializer, DeSerializer
29
+ from pymodaq_gui.managers.parameter_manager import ParameterManager
30
+
31
+ config = Config()
32
+
33
+ tcp_parameters = [
34
+ {'title': 'Port:', 'name': 'port_id', 'type': 'int', 'value': config('network', 'tcp-server', 'port'), },
35
+ {'title': 'IP:', 'name': 'socket_ip', 'type': 'str', 'value': config('network', 'tcp-server', 'ip'), },
36
+ {'title': 'Settings PyMoDAQ Client:', 'name': 'settings_client', 'type': 'group', 'children': []},
37
+ {'title': 'Infos Client:', 'name': 'infos', 'type': 'group', 'children': []},
38
+ {'title': 'Connected clients:', 'name': 'conn_clients', 'type': 'table',
39
+ 'value': dict(), 'header': ['Type', 'adress']}, ]
40
+
41
+
42
+ class TCPClientTemplate:
43
+ def __init__(self, ipaddress="192.168.1.62", port=6341, client_type=""):
44
+ """Create a socket client
45
+
46
+ Parameters
47
+ ----------
48
+ ipaddress: (str) the IP address of the server
49
+ port: (int) the port where to communicate with the server
50
+ client_type: (str) should be one of the accepted client_type by the TCPServer instance (within pymodaq it is
51
+ either 'GRABBER' or 'ACTUATOR'
52
+ """
53
+ #super().__init__()
54
+
55
+ self.ipaddress = ipaddress
56
+ self.port = port
57
+ self._socket: Socket = None
58
+ self._deserializer: DeSerializer = None
59
+ self.connected = False
60
+ self.client_type = client_type
61
+ self.timer = Timer(0.1, self.poll_connection)
62
+
63
+ @property
64
+ def socket(self) -> Socket:
65
+ return self._socket
66
+
67
+ @socket.setter
68
+ def socket(self, sock: Socket):
69
+ self._socket = sock
70
+ self._deserializer = DeSerializer(sock)
71
+
72
+ def close(self):
73
+ if self.socket is not None:
74
+ self.socket.close()
75
+
76
+ def _connect_socket(self):
77
+ # create an INET, STREAMing socket
78
+ self.socket = Socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM))
79
+ # now connect to the web server on port 80 - the normal http port
80
+ self.socket.connect((self.ipaddress, self.port))
81
+
82
+ def init_connection(self, extra_commands=[]):
83
+ """init the socket connection then call the post_init method where to place custom
84
+ initialization"""
85
+ try:
86
+ self._connect_socket()
87
+ self.post_init(extra_commands)
88
+ self.connected = True
89
+
90
+ self.poll_connection()
91
+ #self.timer.start()
92
+
93
+ except ConnectionRefusedError as e:
94
+ self.not_connected(e)
95
+ self.connected = False
96
+
97
+ def poll_connection(self):
98
+ while True:
99
+ try:
100
+ ready_to_read, ready_to_write, in_error = \
101
+ select.select([self.socket.socket], [self.socket.socket], [self.socket.socket], 0)
102
+
103
+ if len(ready_to_read) != 0:
104
+ self.ready_to_read()
105
+
106
+ if len(in_error) != 0:
107
+ self.ready_with_error()
108
+
109
+ if len(ready_to_write) != 0:
110
+ self.ready_to_write()
111
+
112
+ QtWidgets.QApplication.processEvents()
113
+
114
+ except Exception as e:
115
+ self.process_error_in_polling(e)
116
+ break
117
+
118
+ def not_connected(self, e: ConnectionRefusedError):
119
+ raise NotImplementedError
120
+
121
+ def ready_to_read(self):
122
+ """Do stuff (like read data) when messages arrive through the socket"""
123
+ raise NotImplementedError
124
+
125
+ def ready_to_write(self):
126
+ """Send stuff into the socket"""
127
+ raise NotImplementedError
128
+
129
+ def ready_with_error(self):
130
+ """Error in the socket communication"""
131
+ raise NotImplementedError
132
+
133
+ def process_error_in_polling(self, e: Exception):
134
+ raise NotImplementedError
135
+
136
+ def post_init(self, extra_commands=[]):
137
+ """To implement in a real object implementation"""
138
+ raise NotImplementedError
139
+
140
+
141
+ class TCPClient(TCPClientTemplate, QObject):
142
+ """
143
+ PyQt5 object initializing a TCP socket client. Can be used by any module but is a builtin functionality of all
144
+ actuators and detectors of PyMoDAQ
145
+
146
+ The module should init TCPClient, move it in a thread and communicate with it using a custom signal connected to
147
+ TCPClient.queue_command slot. The module should also connect TCPClient.cmd_signal to one of its methods inorder to
148
+ get info/data back from the client
149
+
150
+ The client itself communicate with a TCP server, it is best to use a server object subclassing the TCPServer
151
+ class defined within this python module
152
+
153
+ Parameters
154
+ ----------
155
+ params_state: (dict) state of the Parameter settings of the module instantiating this client and wishing to
156
+ export its settings to the server. Obtained from param.saveState() where param is an
157
+ instance of Parameter object, see pyqtgraph.parametertree::Parameter
158
+
159
+ """
160
+ cmd_signal = Signal(ThreadCommand) # signal to connect with a module slot in order to start communication back
161
+ params = []
162
+
163
+ def __init__(self, ipaddress="192.168.1.62", port=6341, params_state=None,
164
+ client_type="GRABBER"):
165
+ """Create a socket client particularly fit to be used with PyMoDAQ's TCPServer
166
+
167
+ Parameters
168
+ ----------
169
+ ipaddress: (str) the IP address of the server
170
+ port: (int) the port where to communicate with the server
171
+ params_state: (dict) state of the Parameter settings of the module instantiating this client and wishing to
172
+ export its settings to the server. Obtained from param.saveState() where param is an
173
+ instance of Parameter object, see pyqtgraph.parametertree::Parameter
174
+ client_type: (str) should be one of the accepted client_type by the TCPServer instance (within pymodaq it is
175
+ either 'GRABBER' or 'ACTUATOR'
176
+ """
177
+ QObject.__init__(self)
178
+ TCPClientTemplate.__init__(self, ipaddress, port, client_type)
179
+
180
+ self.settings = Parameter.create(name='Settings', type='group', children=self.params)
181
+ if params_state is not None:
182
+ if isinstance(params_state, dict):
183
+ self.settings.restoreState(params_state)
184
+ elif isinstance(params_state, Parameter):
185
+ self.settings.restoreState(params_state.saveState())
186
+
187
+ def send_data(self, data: DataToExport):
188
+ # first send 'Done' and then send the length of the list
189
+ if not isinstance(data, DataToExport):
190
+ raise TypeError(f'should send a DataToExport object')
191
+ if self.socket is not None:
192
+ self.socket.check_sended_with_serializer('Done')
193
+ self.socket.check_sended_with_serializer(data)
194
+
195
+ def send_infos_xml(self, infos: str):
196
+ if self.socket is not None:
197
+ self.socket.check_sended_with_serializer('Infos')
198
+ self.socket.check_sended_with_serializer(infos)
199
+
200
+ def send_info_string(self, info_to_display, value_as_string):
201
+ if self.socket is not None:
202
+ self.socket.check_sended_with_serializer('Info')
203
+ self.socket.check_sended_with_serializer(info_to_display) # the actual info to display as a string
204
+ if not isinstance(value_as_string, str):
205
+ value_as_string = str(value_as_string)
206
+ self.socket.check_sended_with_serializer(value_as_string)
207
+
208
+ @Slot(ThreadCommand)
209
+ def queue_command(self, command=ThreadCommand):
210
+ """
211
+ when this TCPClient object is within a thread, the corresponding module communicate with it with signal and slots
212
+ from module to client: module_signal to queue_command slot
213
+ from client to module: self.cmd_signal to a module slot
214
+ """
215
+ if command.command == "ini_connection":
216
+ status = self.init_connection()
217
+
218
+ elif command.command == "quit":
219
+ try:
220
+ self.socket.close()
221
+ except Exception as e:
222
+ pass
223
+ finally:
224
+ self.cmd_signal.emit(ThreadCommand('disconnected'))
225
+
226
+ elif command.command == 'update_connection':
227
+ self.ipaddress = command.attribute['ipaddress']
228
+ self.port = command.attribute['port']
229
+
230
+ elif command.command == 'data_ready':
231
+ self.data_ready(command.attribute)
232
+
233
+ elif command.command == 'send_info':
234
+ if self.socket is not None:
235
+ path = command.attribute['path']
236
+ param = command.attribute['param']
237
+
238
+ self.socket.check_sended_with_serializer('Info_xml')
239
+ self.socket.check_sended_with_serializer(path)
240
+
241
+ # send value
242
+ data = ioxml.parameter_to_xml_string(param)
243
+ self.socket.check_sended_with_serializer(data)
244
+
245
+ elif command.command == 'position_is':
246
+ if self.socket is not None:
247
+ self.socket.check_sended_with_serializer('position_is')
248
+ self.socket.check_sended_with_serializer(command.attribute)
249
+
250
+ elif command.command == 'move_done':
251
+ if self.socket is not None:
252
+ self.socket.check_sended_with_serializer('move_done')
253
+ self.socket.check_sended_with_serializer(command.attribute)
254
+
255
+ elif command.command == 'x_axis':
256
+ raise DeprecationWarning('Getting axis though TCPIP is deprecated use the data objects directly')
257
+
258
+ elif command.command == 'y_axis':
259
+ raise DeprecationWarning('Getting axis though TCPIP is deprecated use the data objects directly')
260
+ else:
261
+ raise IOError('Unknown TCP client command')
262
+
263
+ def not_connected(self, e):
264
+ self.connected = False
265
+ self.cmd_signal.emit(ThreadCommand('disconnected'))
266
+ self.cmd_signal.emit(ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log']))
267
+
268
+ def ready_to_read(self):
269
+ message = self._deserializer.string_deserialization()
270
+ self.get_data(message)
271
+
272
+ def ready_to_write(self):
273
+ pass
274
+
275
+ def ready_with_error(self):
276
+ self.connected = False
277
+ self.cmd_signal.emit(ThreadCommand('disconnected'))
278
+
279
+ def process_error_in_polling(self, e: Exception):
280
+ try:
281
+ self.cmd_signal.emit(ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log']))
282
+ self.socket.check_sended_with_serializer('Quit')
283
+ self.socket.close()
284
+ except Exception: # pragma: no cover
285
+ pass
286
+
287
+ def post_init(self, extra_commands=[]):
288
+
289
+ self.cmd_signal.emit(ThreadCommand('connected'))
290
+ self.socket.check_sended_with_serializer(self.client_type)
291
+
292
+ self.send_infos_xml(ioxml.parameter_to_xml_string(self.settings))
293
+ for command in extra_commands:
294
+ if isinstance(command, ThreadCommand):
295
+ self.cmd_signal.emit(command)
296
+
297
+ def get_data(self, message: str):
298
+ """
299
+
300
+ Parameters
301
+ ----------
302
+ message
303
+
304
+ Returns
305
+ -------
306
+
307
+ """
308
+ if self.socket is not None:
309
+ messg = ThreadCommand(message)
310
+
311
+ if message == 'set_info':
312
+ path = self._deserializer.list_deserialization()
313
+ param_xml = self._deserializer.string_deserialization()
314
+ messg.attribute = [path, param_xml]
315
+
316
+ elif message == 'move_abs' or message == 'move_rel':
317
+ position = self._deserializer.dwa_deserialization()
318
+ messg.attribute = [position]
319
+
320
+ self.cmd_signal.emit(messg)
321
+
322
+ def data_ready(self, data: DataToExport):
323
+ self.send_data(data)
324
+
325
+
326
+ class TCPServer(QObject):
327
+ """
328
+ Abstract class to be used as inherited by DAQ_Viewer_TCP or DAQ_Move_TCP
329
+ """
330
+
331
+ def __init__(self, client_type='GRABBER'):
332
+ QObject.__init__(self)
333
+ self.serversocket: Socket = None
334
+ self.connected_clients = []
335
+ self.listening = True
336
+ self.processing = False
337
+ self.client_type = client_type
338
+
339
+ def close_server(self):
340
+ """
341
+ close the current opened server.
342
+ Update the settings tree consequently.
343
+
344
+ See Also
345
+ --------
346
+ set_connected_clients_table, daq_utils.ThreadCommand
347
+ """
348
+ server_socket = self.find_socket_within_connected_clients('server')
349
+ self.remove_client(server_socket)
350
+
351
+ def init_server(self):
352
+ self.emit_status(ThreadCommand("Update_Status", [
353
+ "Started new server for {:s}:{:d}".format(self.settings['socket_ip'],
354
+ self.settings['port_id']), 'log']))
355
+ serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
356
+ serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
357
+ self.serversocket = Socket(serversocket)
358
+ # bind the socket to a public host, and a well-known port
359
+ try:
360
+ self.serversocket.bind(
361
+ (self.settings['socket_ip'], self.settings['port_id']))
362
+ # self.serversocket.bind((socket.gethostname(), self.settings.child(('port_id')).value()))
363
+ except socket.error as msg:
364
+ self.emit_status(ThreadCommand("Update_Status",
365
+ ['Bind failed. Error Code : ' + str(msg.errno) + ' Message ' + msg.strerror,
366
+ 'log']))
367
+ raise ConnectionError('Bind failed. Error Code : ' + str(msg.errno) + ' Message ' + msg.strerror)
368
+
369
+ self.serversocket.listen(1)
370
+ self.connected_clients.append(dict(socket=self.serversocket, type='server'))
371
+ self.settings.child('conn_clients').setValue(self.set_connected_clients_table())
372
+
373
+ self.timer = self.startTimer(100) # Timer event fired every 100ms
374
+ # self.listen_client()
375
+
376
+ def timerEvent(self, event):
377
+ """
378
+ Called by set timers.
379
+ If the process is free, start the listen_client function.
380
+
381
+ =============== ==================== ==============================================
382
+ **Parameters** **Type** **Description**
383
+
384
+ *event* QTimerEvent object Containing id from timer issuing this event
385
+ =============== ==================== ==============================================
386
+
387
+ See Also
388
+ --------
389
+ listen_client
390
+ """
391
+ if not self.processing:
392
+ self.listen_client()
393
+
394
+ def find_socket_within_connected_clients(self, client_type) -> Socket:
395
+ """
396
+ Find a socket from a connected client with socket type corresponding.
397
+
398
+ =============== =========== ================================
399
+ **Parameters** **Type** **Description**
400
+ *client_type* string The corresponding client type
401
+ =============== =========== ================================
402
+
403
+ Returns
404
+ -------
405
+ dictionnary
406
+ the socket dictionnary
407
+ """
408
+ res = None
409
+ for socket_dict in self.connected_clients:
410
+ if socket_dict['type'] == client_type:
411
+ res = socket_dict['socket']
412
+ return res
413
+
414
+ def find_socket_type_within_connected_clients(self, sock):
415
+ """
416
+ Find a socket type from a connected client with socket content corresponding.
417
+
418
+ =============== =========== ===================================
419
+ **Parameters** **Type** **Description**
420
+ *sock* ??? The socket content corresponding.
421
+ =============== =========== ===================================
422
+
423
+ Returns
424
+ -------
425
+ dictionnary
426
+ the socket dictionnary
427
+ """
428
+ res = None
429
+ for socket_dict in self.connected_clients:
430
+ if socket_dict['socket'] == sock:
431
+ res = socket_dict['type']
432
+ return res
433
+
434
+ def set_connected_clients_table(self):
435
+ """
436
+
437
+ """
438
+ con_clients = OrderedDict()
439
+ for socket_dict in self.connected_clients:
440
+ try:
441
+ address = str(socket_dict['socket'].getsockname())
442
+ except Exception:
443
+ address = "unconnected invalid socket"
444
+ con_clients[socket_dict['type']] = address
445
+ return con_clients
446
+
447
+ @Slot(list)
448
+ def print_status(self, status):
449
+ """
450
+ Print the given status.
451
+
452
+ =============== ============= ================================================
453
+ **Parameters** **Type** **Description**
454
+ *status* string list a string list representing the status socket
455
+ =============== ============= ================================================
456
+ """
457
+ print(status)
458
+
459
+ def remove_client(self, sock):
460
+ sock_type = self.find_socket_type_within_connected_clients(sock)
461
+ if sock_type is not None:
462
+ self.connected_clients.remove(dict(socket=sock, type=sock_type))
463
+ self.settings.child('conn_clients').setValue(self.set_connected_clients_table())
464
+ try:
465
+ sock.close()
466
+ except Exception:
467
+ pass
468
+ self.emit_status(ThreadCommand("Update_Status", ['Client ' + sock_type + ' disconnected', 'log']))
469
+
470
+ def select(self, rlist, wlist=[], xlist=[], timeout=0):
471
+ """
472
+ Implements the select method, https://docs.python.org/3/library/select.html
473
+ Parameters
474
+ ----------
475
+ rlist: (list) wait until ready for reading
476
+ wlist: (list) wait until ready for writing
477
+ xlist: (list) wait for an “exceptional condition”
478
+ timeout: (float) optional timeout argument specifies a time-out as a floating point number in seconds.
479
+ When the timeout argument is omitted the function blocks until at least one file descriptor is ready.
480
+ A time-out value of zero specifies a poll and never blocks.
481
+
482
+ Returns
483
+ -------
484
+ list: readable sockets
485
+ list: writable sockets
486
+ list: sockets with error pending
487
+ """
488
+
489
+ read_sockets, write_sockets, error_sockets = select.select([sock.socket for sock in rlist],
490
+ [sock.socket for sock in wlist],
491
+ [sock.socket for sock in xlist],
492
+ timeout)
493
+
494
+ return ([Socket(sock) for sock in read_sockets], [Socket(sock) for sock in write_sockets],
495
+ [Socket(sock) for sock in error_sockets])
496
+
497
+ def listen_client(self):
498
+ """
499
+ Server function.
500
+ Used to connect or listen incoming message from a client.
501
+ """
502
+ try:
503
+ self.processing = True
504
+ # QtWidgets.QApplication.processEvents() #to let external commands in
505
+ read_sockets, write_sockets, error_sockets = self.select(
506
+ [client['socket'] for client in self.connected_clients], [],
507
+ [client['socket'] for client in self.connected_clients],
508
+ 0)
509
+ for sock in error_sockets:
510
+ self.remove_client(sock)
511
+
512
+ for sock in read_sockets:
513
+ QThread.msleep(100)
514
+ if sock == self.serversocket: # New connection
515
+ # means a new socket (client) try to reach the server
516
+ (client_socket, address) = self.serversocket.accept()
517
+ DAQ_type = DeSerializer(client_socket).string_deserialization()
518
+ if DAQ_type not in self.socket_types:
519
+ self.emit_status(ThreadCommand("Update_Status", [DAQ_type + ' is not a valid type', 'log']))
520
+ client_socket.close()
521
+ break
522
+
523
+ self.connected_clients.append(dict(socket=client_socket, type=DAQ_type))
524
+ self.settings.child('conn_clients').setValue(self.set_connected_clients_table())
525
+ self.emit_status(ThreadCommand("Update_Status",
526
+ [DAQ_type + ' connected with ' + address[0] + ':' + str(address[1]),
527
+ 'log']))
528
+ QtWidgets.QApplication.processEvents()
529
+
530
+ else: # Some incoming message from a client
531
+ # Data received from client, process it
532
+ try:
533
+ message = DeSerializer(sock).string_deserialization()
534
+ if message in ['Done', 'Info', 'Infos', 'Info_xml', 'position_is', 'move_done']:
535
+ self.process_cmds(message, command_sock=None)
536
+ elif message == 'Quit':
537
+ raise Exception("socket disconnect by user")
538
+ else:
539
+ self.process_cmds(message, command_sock=sock)
540
+
541
+ # client disconnected, so remove from socket list
542
+ except Exception as e:
543
+ self.remove_client(sock)
544
+
545
+ self.processing = False
546
+
547
+ except Exception as e:
548
+ self.emit_status(ThreadCommand("Update_Status", [str(e), 'log']))
549
+
550
+ def send_command(self, sock: Socket, command="move_at"):
551
+ """
552
+ Send one of the message contained in self.message_list toward a socket with identity socket_type.
553
+ First send the length of the command with 4bytes.
554
+
555
+ =============== =========== ==========================
556
+ **Parameters** **Type** **Description**
557
+ *sock* ??? The current socket
558
+ *command* string The command as a string
559
+ =============== =========== ==========================
560
+
561
+ See Also
562
+ --------
563
+ utility_classes.DAQ_Viewer_base.emit_status, daq_utils.ThreadCommand, message_to_bytes
564
+ """
565
+ if command not in self.message_list:
566
+ self.emit_status(
567
+ ThreadCommand("Update_Status", [f'Command: {command} is not in the specified list: {self.message_list}',
568
+ 'log']))
569
+ return
570
+
571
+ if sock is not None:
572
+ sock.check_sended_with_serializer(command)
573
+
574
+ def emit_status(self, status):
575
+ print(status)
576
+
577
+ def read_data(self, sock):
578
+ pass
579
+
580
+ def send_data(self, sock, data):
581
+ pass
582
+
583
+ def command_done(self, command_sock):
584
+ pass
585
+
586
+ def command_to_from_client(self, command):
587
+ pass
588
+
589
+ def process_cmds(self, command, command_sock=None):
590
+ """
591
+ Process the given command.
592
+ """
593
+ if command not in self.message_list:
594
+ self.emit_status(
595
+ ThreadCommand("Update_Status", [f'Command: {command} is not in the specified list: {self.message_list}',
596
+ 'log']))
597
+ return
598
+
599
+ if command == 'Done': # means the given socket finished grabbing data and is ready to send them
600
+ self.command_done(command_sock)
601
+
602
+ elif command == "Infos":
603
+ """replace entirely the client settings information on the server widget
604
+ should be done as the init of the client module"""
605
+ try:
606
+ sock = self.find_socket_within_connected_clients(self.client_type)
607
+ if sock is not None: # if client self.client_type is connected then send it the command
608
+ self.read_infos(sock)
609
+
610
+ except Exception as e:
611
+ self.emit_status(ThreadCommand("Update_Status", [str(e), 'log']))
612
+
613
+ elif command == 'Info_xml':
614
+ """update the state of one of the client settings on the server widget"""
615
+ sock = self.find_socket_within_connected_clients(self.client_type)
616
+ if sock is not None:
617
+ self.read_info_xml(sock)
618
+
619
+ elif command == "Info":
620
+ # add a custom info (as a string value) in the server widget settings. To be used if the client is not a
621
+ # PyMoDAQ's module
622
+
623
+ try:
624
+ sock = self.find_socket_within_connected_clients(self.client_type)
625
+ if sock is not None: # if client self.client_type is connected then send it the command
626
+ self.read_info(sock)
627
+ except Exception as e:
628
+ self.emit_status(ThreadCommand("Update_Status", [str(e), 'log']))
629
+
630
+ else:
631
+ self.command_to_from_client(command)
632
+
633
+ def read_infos(self, sock: Socket = None, infos=''):
634
+ if sock is not None:
635
+ infos = DeSerializer(sock).string_deserialization()
636
+ params = ioxml.XML_string_to_parameter(infos)
637
+
638
+ param_state = {'title': 'Infos Client:', 'name': 'settings_client', 'type': 'group', 'children': params}
639
+ self.settings.child('settings_client').restoreState(param_state)
640
+
641
+ def read_info_xml(self, sock: Socket, path=['settings', 'apathinsettings'], param_xml=''):
642
+ if sock is not None:
643
+ deser = DeSerializer(sock)
644
+ path = deser.list_deserialization()
645
+ param_xml = deser.string_deserialization()
646
+ try:
647
+ param_dict = ioxml.XML_string_to_parameter(param_xml)[0]
648
+ except Exception as e:
649
+ raise Exception(f'Invalid xml structure for TCP server settings: {str(e)}')
650
+ try:
651
+ param_here = self.settings.child('settings_client', *path[1:])
652
+ except Exception as e:
653
+ raise Exception(f'Invalid path for TCP server settings: {str(e)}')
654
+ param_here.restoreState(param_dict)
655
+
656
+ def read_info(self, sock: Socket=None, test_info='an_info', test_value=''):
657
+ """
658
+ if the client is not from PyMoDAQ it can use this method to display some info into the server widget
659
+ """
660
+ # #first get the info type
661
+ if sock is None:
662
+ info = test_info
663
+ data = test_value
664
+ else:
665
+ deser = DeSerializer(sock)
666
+ info = deser.string_deserialization()
667
+ data = deser.string_deserialization()
668
+
669
+ if info not in putils.iter_children(self.settings.child('infos'), []):
670
+ self.settings.child('infos').addChild({'name': info, 'type': 'str', 'value': data})
671
+ pass
672
+ else:
673
+ self.settings.child('infos', info).setValue(data)
674
+
675
+
676
+ class MockServer(TCPServer):
677
+ params = []
678
+
679
+ def __init__(self, client_type='GRABBER'):
680
+ super().__init__(client_type)
681
+
682
+ self.settings = Parameter.create(name='settings', type='group', children=tcp_parameters)
683
+
684
+
685
+ class MockDataGrabber:
686
+
687
+ def __init__(self, grabber_dim='2D'):
688
+ super().__init__()
689
+ self.Nx = 256
690
+ self.Ny = 128
691
+ self.x_axis = np.linspace(0, self.Nx-1, self.Nx)
692
+ self.y_axis = np.linspace(0, self.Ny-1, self.Ny)
693
+ self.grabber_dim = grabber_dim
694
+
695
+ def grab(self) -> List[DataFromPlugins]:
696
+ if self.grabber_dim == '0D':
697
+ return [DataFromPlugins(data=[np.array([np.random.rand()])])]
698
+
699
+ elif self.grabber_dim == '1D':
700
+ return [DataFromPlugins(data=[mutils.gauss1D(self.x_axis, 128, 25) +
701
+ np.random.rand(self.Nx)])]
702
+
703
+ elif self.grabber_dim == '2D':
704
+ return [DataFromPlugins(data=[mutils.gauss2D(self.x_axis, 128, 65,
705
+ self.y_axis, 60, 10) +
706
+ np.random.rand(self.Ny, self.Nx)])]
707
+
708
+
709
+ class Grabber(QObject):
710
+ command_tcpip = Signal(ThreadCommand)
711
+
712
+ def __init__(self, grab_method=None):
713
+ super().__init__()
714
+ self.send_to_tcpip = False
715
+ self.grab_method = grab_method
716
+
717
+ def connect_tcp_ip(self, ip='localhost', port=6341):
718
+ self.tcpclient_thread = QThread()
719
+ tcpclient = TCPClient(ip, port=port)
720
+
721
+ tcpclient.moveToThread(self.tcpclient_thread)
722
+ self.tcpclient_thread.tcpclient = tcpclient
723
+ tcpclient.cmd_signal.connect(self.process_tcpip_cmds)
724
+
725
+ self.command_tcpip[ThreadCommand].connect(tcpclient.queue_command)
726
+
727
+ self.tcpclient_thread.start()
728
+ #tcpclient.init_connection(extra_commands=[ThreadCommand('get_axis', )])
729
+ tcpclient.init_connection()
730
+ self.send_to_tcpip = True
731
+
732
+ def snapshot(self, info='', send_to_tcpip=True):
733
+ self.grab_data()
734
+
735
+ def grab_data(self):
736
+ """
737
+ Do a grab session using 2 profile :
738
+ * if grab pb checked do a continous save and send an "update_channels" thread command and a "grab" too.
739
+ * if not send a "stop_grab" thread command with settings "main settings-naverage" node value as an attribute.
740
+
741
+ See Also
742
+ --------
743
+ daq_utils.ThreadCommand, set_enabled_Ini_buttons
744
+ """
745
+ data = self.grab_method()
746
+ self.command_tcpip.emit(ThreadCommand('data_ready', data))
747
+
748
+ @Slot(ThreadCommand)
749
+ def process_tcpip_cmds(self, status):
750
+ if 'Send Data' in status.command:
751
+ self.snapshot('', send_to_tcpip=True)
752
+
753
+ elif status.command == 'connected':
754
+ #self.settings.child('main_settings', 'tcpip', 'tcp_connected').setValue(True)
755
+ pass
756
+
757
+ elif status.command == 'disconnected':
758
+ #self.settings.child('main_settings', 'tcpip', 'tcp_connected').setValue(False)
759
+ pass
760
+
761
+ elif status.command == 'Update_Status':
762
+ print(status)
763
+
764
+
765
+ if __name__ == '__main__': # pragma: no cover
766
+ import sys
767
+ app = QtWidgets.QApplication(sys.argv)
768
+ mockdata_grabber = MockDataGrabber('2D')
769
+ grabber = Grabber(mockdata_grabber.grab)
770
+ grabber.connect_tcp_ip()
771
+
772
+ sys.exit(app.exec_())