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,1016 @@
1
+ import time
2
+ from functools import partial # needed for the button to sync setpoint with currpoint
3
+ from typing import Dict, List, TYPE_CHECKING
4
+ from collections import deque
5
+ import numpy as np
6
+
7
+ from qtpy import QtGui, QtWidgets
8
+ from qtpy.QtCore import QObject, Slot, QThread, Signal
9
+
10
+ from simple_pid import PID
11
+
12
+ from pymodaq_utils.logger import set_logger, get_module_name
13
+ from pymodaq_utils.utils import ThreadCommand, find_dict_in_list_from_key_val
14
+ from pymodaq.utils.exceptions import DetectorError, ActuatorError, PIDError
15
+
16
+ from pymodaq_gui.parameter import utils as putils
17
+ from pymodaq_gui.parameter import Parameter, ParameterTree
18
+ from pymodaq_gui.plotting.data_viewers.viewer0D import Viewer0D
19
+ from pymodaq_gui.utils.widgets import QLED, LabelWithFont, SpinBox
20
+ from pymodaq_gui.utils.dock import DockArea, Dock
21
+
22
+
23
+ from pymodaq_data.data import DataToExport, DataCalculated, DataRaw
24
+ from pymodaq_utils.config import Config
25
+
26
+ from pymodaq.utils.managers.modules_manager import ModulesManager
27
+ from pymodaq.extensions.pid.utils import get_models
28
+ from pymodaq.utils.data import DataActuator, DataToActuators
29
+ from pymodaq.extensions.pid.actuator_controller import PIDController
30
+ from pymodaq.extensions.pid.utils import PIDModelGeneric
31
+
32
+ from pymodaq.extensions.utils import CustomExt
33
+
34
+ if TYPE_CHECKING:
35
+ from pymodaq.control_modules.daq_move import DAQ_Move
36
+
37
+
38
+ config = Config()
39
+ logger = set_logger(get_module_name(__file__))
40
+
41
+
42
+ class DAQ_PID(CustomExt):
43
+ """ """
44
+
45
+ command_pid = Signal(ThreadCommand)
46
+ curr_points_signal = Signal(dict)
47
+ setpoints_signal = Signal(dict)
48
+ emit_curr_points_sig = Signal()
49
+
50
+ models = get_models()
51
+
52
+ params = [
53
+ {
54
+ "title": "Models",
55
+ "name": "models",
56
+ "type": "group",
57
+ "expanded": True,
58
+ "visible": True,
59
+ "children": [
60
+ {
61
+ "title": "Models class:",
62
+ "name": "model_class",
63
+ "type": "list",
64
+ "limits": [d["name"] for d in models],
65
+ },
66
+ {
67
+ "title": "Model params:",
68
+ "name": "model_params",
69
+ "type": "group",
70
+ "children": [],
71
+ },
72
+ ],
73
+ },
74
+ {
75
+ "title": "Move settings:",
76
+ "name": "move_settings",
77
+ "expanded": True,
78
+ "type": "group",
79
+ "visible": False,
80
+ "children": [
81
+ {"title": "Units:", "name": "units", "type": "str", "value": ""}
82
+ ],
83
+ },
84
+ # here only to be compatible with DAQ_Scan, the model could update it
85
+ {
86
+ "title": "Main Settings:",
87
+ "name": "main_settings",
88
+ "expanded": True,
89
+ "type": "group",
90
+ "children": [
91
+ {
92
+ "title": "Acquisition Timeout (ms):",
93
+ "name": "timeout",
94
+ "type": "int",
95
+ "value": 10000,
96
+ },
97
+ {
98
+ "title": "epsilon",
99
+ "name": "epsilon",
100
+ "type": "float",
101
+ "value": 0.01,
102
+ "tooltip": "Precision at which move is considered as done",
103
+ },
104
+ {
105
+ "title": "Queue length",
106
+ "name": "queue_length",
107
+ "type": "int",
108
+ "value": 25,
109
+ "tooltip": "Length of queue to calculate stability",
110
+ },
111
+ {
112
+ "title": "Weighting type",
113
+ "name": "queue_weighting",
114
+ "type": "int",
115
+ "value": 0,
116
+ "tooltip": """Type of weighting when calculating the stability. The weight goes as q**N where q is the qth element in the queue and N is the wanted order.
117
+ """,
118
+ },
119
+ {
120
+ "title": "Refresh queue",
121
+ "name": "refresh_queue",
122
+ "type": "bool_push",
123
+ "value": False,
124
+ "tooltip": "Refresh queues to zero length",
125
+ },
126
+ {
127
+ "title": "Refresh plot",
128
+ "name": "refresh_plot",
129
+ "type": "bool_push",
130
+ "value": False,
131
+ "tooltip": "Refresh both plots to zero length",
132
+ },
133
+ {
134
+ "title": "PID settings:",
135
+ "name": "pid_settings",
136
+ "type": "group",
137
+ "children": [
138
+ {
139
+ "title": "Time per loop (ms):",
140
+ "name": "effective_sample_time",
141
+ "type": "int",
142
+ "value": 0,
143
+ "readonly": True,
144
+ "tooltip": "Time elapsed in the PID thread for each loop, ",
145
+ },
146
+ {
147
+ "title": "Sample time (ms):",
148
+ "name": "sample_time",
149
+ "type": "int",
150
+ "value": 10,
151
+ "tooltip": "The minimum desired time between two PID calculations, ",
152
+ },
153
+ {
154
+ "title": "Refresh plot time (ms):",
155
+ "name": "refresh_plot_time",
156
+ "type": "int",
157
+ "value": 200,
158
+ "tooltip": "Update display every x ms, ",
159
+ },
160
+ {
161
+ "title": "Output limits:",
162
+ "name": "output_limits",
163
+ "expanded": True,
164
+ "type": "group",
165
+ "tooltip": "Limits on the output of the PID controller, ",
166
+ "children": [
167
+ {
168
+ "title": "Output limit (min):",
169
+ "name": "output_limit_min_enabled",
170
+ "type": "bool",
171
+ "value": False,
172
+ },
173
+ {
174
+ "title": "Output limit (min):",
175
+ "name": "output_limit_min",
176
+ "type": "float",
177
+ "value": 0,
178
+ },
179
+ {
180
+ "title": "Output limit (max):",
181
+ "name": "output_limit_max_enabled",
182
+ "type": "bool",
183
+ "value": False,
184
+ },
185
+ {
186
+ "title": "Output limit (max:",
187
+ "name": "output_limit_max",
188
+ "type": "float",
189
+ "value": 100,
190
+ },
191
+ ],
192
+ },
193
+ {
194
+ "title": "Prop. on measurement:",
195
+ "name": "proportional_on_measurement",
196
+ "type": "bool",
197
+ "value": False,
198
+ "tooltip": "If True, the PID will stabilize the value instead of reducing the error (see pid.py).",
199
+ },
200
+ {
201
+ "title": "PID constants:",
202
+ "name": "pid_constants",
203
+ "type": "group",
204
+ "children": [
205
+ {
206
+ "title": "Kp:",
207
+ "name": "kp",
208
+ "type": "float",
209
+ "value": 0.1,
210
+ "min": 0,
211
+ },
212
+ {
213
+ "title": "Ki:",
214
+ "name": "ki",
215
+ "type": "float",
216
+ "value": 0.01,
217
+ "min": 0,
218
+ },
219
+ {
220
+ "title": "Kd:",
221
+ "name": "kd",
222
+ "type": "float",
223
+ "value": 0.001,
224
+ "min": 0,
225
+ },
226
+ ],
227
+ },
228
+ ],
229
+ },
230
+ ],
231
+ },
232
+ ]
233
+
234
+ def __init__(self, dockarea, dashboard):
235
+ super().__init__(dockarea, dashboard)
236
+
237
+ self.settings = Parameter.create(
238
+ title="PID settings",
239
+ name="pid_settings",
240
+ type="group",
241
+ children=self.params,
242
+ )
243
+ self.title = "PyMoDAQ PID"
244
+
245
+ self.initialized_state = False
246
+ self.model_class: PIDModelGeneric = None
247
+ self._curr_points = dict([])
248
+ self._setpoints = dict([])
249
+ self.queue_points = dict([])
250
+ self.build_weight_array()
251
+
252
+ self.dock_area = dockarea
253
+ self.check_moving = False
254
+ self.setup_ui()
255
+
256
+ self.enable_controls_pid(False)
257
+
258
+ self.enable_controls_pid_run(False)
259
+
260
+ self.emit_curr_points_sig.connect(self.emit_curr_points)
261
+
262
+ def ini_PID(self):
263
+ if self.is_action_checked("ini_pid"):
264
+ output_limits = self.get_output_limits()
265
+ self.update_queues(refresh=True)
266
+ self.runner_thread = QThread()
267
+ pid_runner = PIDRunner(
268
+ self.model_class,
269
+ self.modules_manager,
270
+ setpoints=self.setpoints,
271
+ params=dict(
272
+ Kp=self.settings[
273
+ "main_settings", "pid_settings", "pid_constants", "kp"
274
+ ],
275
+ Ki=self.settings[
276
+ "main_settings", "pid_settings", "pid_constants", "ki"
277
+ ],
278
+ Kd=self.settings[
279
+ "main_settings", "pid_settings", "pid_constants", "kd"
280
+ ],
281
+ sample_time=self.settings[
282
+ "main_settings", "pid_settings", "sample_time"
283
+ ]
284
+ / 1000,
285
+ output_limits=output_limits,
286
+ auto_mode=False,
287
+ ),
288
+ )
289
+
290
+ self.runner_thread.pid_runner = pid_runner
291
+ pid_runner.pid_output_signal.connect(self.process_output)
292
+ pid_runner.time_elapsed_signal.connect(self.display_time_elapsed)
293
+ pid_runner.status_sig.connect(self.thread_status)
294
+ self.command_pid.connect(pid_runner.queue_command)
295
+
296
+ pid_runner.moveToThread(self.runner_thread)
297
+
298
+ self.runner_thread.start()
299
+ self.get_action("pid_led").set_as_true()
300
+ self.enable_controls_pid_run(True)
301
+
302
+ else:
303
+ if hasattr(self, "runner_thread"):
304
+ self.exit_runner_thread()
305
+ self.get_action("pid_led").set_as_false()
306
+ self.enable_controls_pid_run(False)
307
+
308
+ self.initialized_state = True
309
+
310
+ def process_output(self, data: DataToExport):
311
+ outputs: DataRaw = data.get_data_from_name("outputs")
312
+ inputs: DataRaw = data.get_data_from_name("inputs")
313
+ self.curr_points = [float(_input[-1]) for _input in inputs.isig[-1]]
314
+ inputs_plot = DataRaw("inputs",data=[np.array([_curr_point,]) for _curr_point in self.curr_points],labels=self.model_class.setpoints_names)
315
+ for _input, _setpoint_name in zip(inputs, self.model_class.setpoints_names):
316
+ self.queue_points[_setpoint_name].extend(_input)
317
+
318
+ # Would that be useful to add too?
319
+ # self.mean_points = [
320
+ # np.mean(np.array(queue) - setpoint)
321
+ # for queue, setpoint in zip(self.queue_points.values(), self.setpoints)
322
+ # ]
323
+ # queues_length = len(list(self.queue_points.values())[0])
324
+ self.stab_points = [
325
+ np.sqrt(np.cov(_queue, aweights=self.queue_weight[:len(_queue)], ddof=0))
326
+ for _queue in self.queue_points.values()
327
+ ]
328
+ self.output_viewer.show_data(outputs)
329
+ self.input_viewer.show_data(inputs_plot)
330
+ def build_weight_array(self):
331
+ # Build a weighting array of the form np.arange(1+queue_length)**(queue_weighting)
332
+ self.queue_weight = np.arange(1+
333
+ self.settings.child("main_settings","queue_length").value()
334
+ )**self.settings.child("main_settings","queue_weighting").value()
335
+
336
+ def get_output_limits(self):
337
+ output_limits = [None, None]
338
+ output_parameters = self.settings.child("main_settings", "pid_settings", "output_limits")
339
+ if output_parameters["output_limit_min_enabled"]:
340
+ output_limits[0] = output_parameters[ "output_limit_min"]
341
+ if output_parameters["output_limit_max_enabled"]:
342
+ output_limits[1] = output_parameters[ "output_limit_max"]
343
+ return output_limits
344
+
345
+ def display_time_elapsed(self, time_elapsed: float):
346
+ """Display the time elapsed in the PID thread"""
347
+ self.settings.child("main_settings", "pid_settings", "effective_sample_time").setValue(time_elapsed*1000)
348
+
349
+ def enable_controls_pid(self, enable=False):
350
+ self.set_action_enabled("ini_pid", enable)
351
+ # self.setpoint_sb.setOpts(enabled=enable)
352
+
353
+ def enable_controls_pid_run(self, enable=False):
354
+ self.set_action_enabled("run", enable)
355
+ self.set_action_enabled("pause", enable)
356
+
357
+ def setup_menu(self, menubar: QtWidgets.QMenuBar = None):
358
+ """
359
+ to be subclassed
360
+ create menu for actions contained into the self.actions_manager, for instance:
361
+
362
+ For instance:
363
+
364
+ file_menu = self.menubar.addMenu('File')
365
+ self.actions_manager.affect_to('load', file_menu)
366
+ self.actions_manager.affect_to('save', file_menu)
367
+
368
+ file_menu.addSeparator()
369
+ self.actions_manager.affect_to('quit', file_menu)
370
+ """
371
+ pass
372
+
373
+ def value_changed(self, param):
374
+ """to be subclassed for actions to perform when one of the param's value in self.settings is changed
375
+
376
+ For instance:
377
+ if param.name() == 'do_something':
378
+ if param.value():
379
+ print('Do something')
380
+ self.settings.child('main_settings', 'something_done').setValue(False)
381
+
382
+ Parameters
383
+ ----------
384
+ param: (Parameter) the parameter whose value just changed
385
+ """
386
+ if param.name() == "model_class":
387
+ self.get_set_model_params(param.value())
388
+
389
+ elif param.name() == "refresh_plot_time" or param.name() == "timeout":
390
+ self.command_pid.emit(
391
+ ThreadCommand("update_timer", [param.name(), param.value()])
392
+ )
393
+
394
+ elif param.name() == "sample_time":
395
+ self.command_pid.emit(
396
+ ThreadCommand("update_options", dict(sample_time=param.value()))
397
+ )
398
+
399
+ elif param.name() in putils.iter_children(
400
+ self.settings.child("main_settings", "pid_settings", "output_limits"), []
401
+ ):
402
+ output_limits = self.get_output_limits()
403
+ self.command_pid.emit(
404
+ ThreadCommand("update_options", dict(output_limits=output_limits))
405
+ )
406
+
407
+ elif param.name() in putils.iter_children(
408
+ self.settings.child("main_settings", "pid_settings", "pid_constants"), []
409
+ ):
410
+ Kp = self.settings["main_settings", "pid_settings", "pid_constants", "kp"]
411
+ Ki = self.settings["main_settings", "pid_settings", "pid_constants", "ki"]
412
+ Kd = self.settings["main_settings", "pid_settings", "pid_constants", "kd"]
413
+ self.command_pid.emit(
414
+ ThreadCommand("update_options", dict(tunings=(Kp, Ki, Kd)))
415
+ )
416
+
417
+ elif param.name() in putils.iter_children(
418
+ self.settings.child("models", "model_params"), []
419
+ ):
420
+ if self.model_class is not None:
421
+ self.model_class.update_settings(param)
422
+
423
+ elif param.name() == "detector_modules":
424
+ self.model_class.update_detector_names()
425
+ elif param.name() == "queue_length":
426
+ self.update_queues()
427
+ elif param.name() == "queue_weighting":
428
+ self.build_weight_array()
429
+ elif param.name() == "refresh_queue":
430
+ if param.value():
431
+ self.update_queues(refresh=True)
432
+ self.settings.child("main_settings", param.name()).setValue(False)
433
+ elif param.name() == "refresh_plot":
434
+ if param.value():
435
+ self.input_viewer.view.data_displayer.clear_data()
436
+ self.output_viewer.view.data_displayer.clear_data()
437
+ self.settings.child('main_settings', param.name()).setValue(False)
438
+
439
+ def connect_things(self):
440
+ logger.debug("connecting actions and other")
441
+ self.connect_action(
442
+ "quit",
443
+ self.quit_fun,
444
+ )
445
+ self.connect_action("ini_model", self.ini_model)
446
+ self.connect_action("create_setp_actuators", self.create_setp_actuators)
447
+ self.connect_action("ini_pid", self.ini_PID)
448
+ self.connect_action("run", self.run_PID)
449
+ self.connect_action("pause", self.pause_PID)
450
+ logger.debug("connecting done")
451
+
452
+ def setup_actions(self):
453
+ logger.debug("setting actions")
454
+ self.add_action("quit", "Quit", "close2", "Quit program")
455
+ self.add_widget("model_label", QtWidgets.QLabel, "Init Model:")
456
+ self.add_action(
457
+ "ini_model",
458
+ "Init Model",
459
+ "ini",
460
+ tip="Initialize the selected model: algo/data conversion",
461
+ )
462
+ self.add_widget("model_led", QLED, toolbar=self.toolbar)
463
+
464
+ self.add_action(
465
+ "create_setp_actuators",
466
+ "Create SetPoint Actuators",
467
+ "Add_Step",
468
+ tip="Create a DAQ_Move Control Module for each SetPoint allowing to"
469
+ "control them from the DashBoard, therefore within other extensions",
470
+ )
471
+
472
+ self.add_widget("model_label", QtWidgets.QLabel, "Init PID Runner:")
473
+ self.add_action(
474
+ "ini_pid",
475
+ "Init the PID loop",
476
+ "ini",
477
+ tip="Init the PID thread",
478
+ checkable=True,
479
+ )
480
+ self.add_widget("pid_led", QLED, toolbar=self.toolbar)
481
+ self.add_action(
482
+ "run",
483
+ "Run The PID loop",
484
+ "run2",
485
+ tip="run or stop the pid loop",
486
+ checkable=True,
487
+ )
488
+ self.add_action(
489
+ "pause",
490
+ "Pause the PID loop",
491
+ "pause",
492
+ tip="Pause the PID loop",
493
+ checkable=True,
494
+ )
495
+ self.set_action_checked("pause", True)
496
+ self.set_action_enabled("create_setp_actuators", False)
497
+ logger.debug("actions set")
498
+
499
+ def setup_docks(self):
500
+ logger.debug("settings the extension docks")
501
+ self.dock_pid = Dock("PID controller", self.dock_area)
502
+ self.dock_area.addDock(self.dock_pid)
503
+
504
+ widget = QtWidgets.QWidget()
505
+ widget_toolbar = QtWidgets.QWidget()
506
+ verlayout = QtWidgets.QVBoxLayout()
507
+ widget.setLayout(verlayout)
508
+ self.toolbar_layout = QtWidgets.QGridLayout()
509
+ widget_toolbar.setLayout(self.toolbar_layout)
510
+
511
+ logger.debug("settings the extension docks done")
512
+
513
+ labmaj = QtWidgets.QLabel("Setpoints:")
514
+ self.toolbar_layout.addWidget(labmaj, 3, 0, 1, 2)
515
+ labmaj = QtWidgets.QLabel("Value:")
516
+ self.toolbar_layout.addWidget(labmaj, 4, 0, 1, 2)
517
+ labmaj = QtWidgets.QLabel("Stability:")
518
+ self.toolbar_layout.addWidget(labmaj, 5, 0, 1, 2)
519
+ labmaj = QtWidgets.QLabel("Sync Value:")
520
+ self.toolbar_layout.addWidget(labmaj, 6, 0, 1, 2)
521
+
522
+ verlayout.addWidget(widget_toolbar)
523
+ verlayout.addWidget(self.settings_tree)
524
+
525
+ self.dock_output = Dock("PID output")
526
+ widget_output = QtWidgets.QWidget()
527
+ self.output_viewer = Viewer0D(widget_output)
528
+ self.dock_output.addWidget(widget_output)
529
+ self.dock_area.addDock(self.dock_output, "right", self.dock_pid)
530
+
531
+ self.dock_input = Dock("PID input")
532
+ widget_input = QtWidgets.QWidget()
533
+ self.input_viewer = Viewer0D(widget_input)
534
+ self.dock_input.addWidget(widget_input)
535
+ self.dock_area.addDock(self.dock_input, "bottom", self.dock_output)
536
+
537
+ if len(self.models) != 0:
538
+ self.get_set_model_params(self.models[0]["name"])
539
+
540
+ self.dock_pid.addWidget(widget)
541
+
542
+ def create_setp_actuators(self):
543
+ # Now that we have the module manager, load PID if it is checked in managers
544
+ try:
545
+ for setp in self.model_class.setpoints_names:
546
+ self.dashboard.add_move_from_extension(setp, "PID", PIDController(self, setp))
547
+ self.set_action_enabled("create_setp_actuators", False)
548
+
549
+ except Exception as e:
550
+ raise PIDError(
551
+ "Could not load the PID extension and create setpoints actuators"
552
+ f"{str(e)}"
553
+ )
554
+
555
+ def get_set_model_params(self, model_name):
556
+ self.settings.child("models", "model_params").clearChildren()
557
+ models = get_models()
558
+ if len(models) > 0:
559
+ model_class = find_dict_in_list_from_key_val(models, "name", model_name)[
560
+ "class"
561
+ ]
562
+ params = getattr(model_class, "params")
563
+ self.settings.child("models", "model_params").addChildren(params)
564
+
565
+ def run_PID(self):
566
+ if self.is_action_checked("run"):
567
+ self.get_action("run").set_icon("stop")
568
+ self.command_pid.emit(ThreadCommand("start_PID", []))
569
+ QtWidgets.QApplication.processEvents()
570
+
571
+ QtWidgets.QApplication.processEvents()
572
+
573
+ self.command_pid.emit(
574
+ ThreadCommand("run_PID", [np.zeros_like(self.model_class.curr_output)])
575
+ )
576
+ else:
577
+ self.get_action("run").set_icon("run2")
578
+ self.command_pid.emit(ThreadCommand("stop_PID"))
579
+
580
+ QtWidgets.QApplication.processEvents()
581
+
582
+ def pause_PID(self):
583
+ for setp in self.setpoints_sb:
584
+ setp.setEnabled(not self.is_action_checked("pause"))
585
+ self.command_pid.emit(
586
+ ThreadCommand("pause_PID", [self.is_action_checked("pause")])
587
+ )
588
+
589
+ def stop_moves(self, overshoot):
590
+ """
591
+ Foreach module of the move module object list, stop motion.
592
+
593
+ See Also
594
+ --------
595
+ stop_scan, DAQ_Move_main.daq_move.stop_Motion
596
+ """
597
+ self.overshoot = overshoot
598
+ for mod in self.modules_manager.actuators:
599
+ mod.stop_Motion()
600
+
601
+ def set_model(self):
602
+ model_name = self.settings["models", "model_class"]
603
+ self.model_class: PIDModelGeneric = find_dict_in_list_from_key_val(
604
+ self.models, "name", model_name
605
+ )["class"](self)
606
+ self.set_setpoints_buttons()
607
+ self.update_queues(refresh=True)
608
+ self.model_class.ini_model()
609
+ self.settings.child("main_settings", "epsilon").setValue(
610
+ self.model_class.epsilon
611
+ )
612
+
613
+ def init_queues(self):
614
+ self.queue_points = {
615
+ setpoint_name: deque(maxlen=self.settings.child("main_settings", "queue_length").value())
616
+ for setpoint_name in self.model_class.setpoints_names
617
+ }
618
+
619
+ def update_queues(self, refresh=False):
620
+ if refresh:
621
+ self.init_queues()
622
+ else:
623
+ self.queue_points = {
624
+ setpoint_name: deque(queue, maxlen=self.settings.child("main_settings", "queue_length").value())
625
+ for queue, setpoint_name in zip(self.queue_points.values(), self.model_class.setpoints_names)
626
+ }
627
+ self.build_weight_array()
628
+
629
+ def ini_model(self):
630
+ try:
631
+ if self.model_class is None:
632
+ self.set_model()
633
+
634
+ self.modules_manager.selected_actuators_name = (
635
+ self.model_class.actuators_name
636
+ )
637
+ self.modules_manager.selected_detectors_name = (
638
+ self.model_class.detectors_name
639
+ )
640
+
641
+ self.enable_controls_pid(True)
642
+ self.get_action("model_led").set_as_true()
643
+ self.set_action_enabled("ini_model", False)
644
+ self.set_action_enabled("create_setp_actuators", True)
645
+
646
+ except Exception as e:
647
+ logger.exception(str(e))
648
+
649
+ @property
650
+ def setpoints(self):
651
+ return [sp.value() for sp in self.setpoints_sb]
652
+
653
+ @setpoints.setter
654
+ def setpoints(self, values):
655
+ for ind, sp in enumerate(self.setpoints_sb):
656
+ sp.setValue(values[ind])
657
+ self.update_queues(refresh=True) # Refresh queues when setpoints are updated
658
+
659
+ def setpoints_external(self, values_dict: Dict[str, DataActuator]):
660
+ for key in values_dict:
661
+ index = self.model_class.setpoints_names.index(key)
662
+ self.setpoints_sb[index].setValue(values_dict[key].value())
663
+ self.queue_points[key].clear() # Refresh queue when setpoint is updated
664
+
665
+ @property
666
+ def curr_points(self):
667
+ return [sp.value() for sp in self.currpoints_sb]
668
+
669
+ @curr_points.setter
670
+ def curr_points(self, values):
671
+ for ind, sp in enumerate(self.currpoints_sb):
672
+ sp.setValue(values[ind])
673
+
674
+ def emit_curr_points(self):
675
+ if self.model_class is not None:
676
+ self.curr_points_signal.emit(
677
+ dict(zip(self.model_class.setpoints_names, self.curr_points))
678
+ )
679
+
680
+ @property
681
+ def stab_points(self):
682
+ return [sp.value() for sp in self.stabpoints_sb]
683
+
684
+ @stab_points.setter
685
+ def stab_points(self, values):
686
+ for ind, sp in enumerate(self.stabpoints_sb):
687
+ sp.setValue(values[ind])
688
+
689
+ def set_setpoints_buttons(self):
690
+ self.setpoints_sb = []
691
+ self.currpoints_sb = []
692
+ self.stabpoints_sb = []
693
+ # self.meanpoints_sb = []
694
+ self.syncvalue_pb = []
695
+ for ind_set in range(self.model_class.Nsetpoints):
696
+ label = LabelWithFont(
697
+ self.model_class.setpoints_names[ind_set],
698
+ font_name="Tahoma",
699
+ font_size=14,
700
+ isbold=True,
701
+ isitalic=True,
702
+ )
703
+ col_ind = 2 + ind_set
704
+
705
+ self.toolbar_layout.addWidget(label, 2, col_ind, 1, 1)
706
+
707
+ self.setpoints_sb.append(self.make_spinbox(no_button=False))
708
+ self.toolbar_layout.addWidget(
709
+ self.setpoints_sb[-1], 3, col_ind, 1, 1
710
+ )
711
+
712
+ self.setpoints_sb[-1].valueChanged.connect(self.update_runner_setpoints)
713
+
714
+ self.currpoints_sb.append(self.make_spinbox())
715
+ self.toolbar_layout.addWidget(
716
+ self.currpoints_sb[-1], 4, col_ind, 1, 1
717
+ )
718
+
719
+ self.stabpoints_sb.append(self.make_spinbox())
720
+ self.toolbar_layout.addWidget(self.stabpoints_sb[-1], 5, col_ind, 1, 1)
721
+ self.syncvalue_pb.append(
722
+ QtWidgets.QPushButton("Synchro {}".format(ind_set))
723
+ )
724
+ self.syncvalue_pb[ind_set].clicked.connect(
725
+ partial(self.currpoint_as_setpoint, ind_set)
726
+ )
727
+ self.toolbar_layout.addWidget(self.syncvalue_pb[-1], 6, col_ind, 1, 1)
728
+ self.setpoints_signal.connect(self.setpoints_external)
729
+
730
+ def make_spinbox(self, no_button=True):
731
+ """ """
732
+ spinbox = SpinBox()
733
+ spinbox.setMinimumHeight(40)
734
+ font = spinbox.font()
735
+ font.setPointSizeF(20)
736
+ spinbox.setFont(font)
737
+ spinbox.setDecimals(6)
738
+ if no_button:
739
+ spinbox.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)
740
+ return spinbox
741
+
742
+ def currpoint_as_setpoint(self, i=0):
743
+ """
744
+ Function used by the sync buttons. The button i will attribute the value of the i-th currpoint to the i-th setpoint.
745
+ """
746
+ self.setpoints_sb[i].setValue(self.curr_points[i])
747
+ self.update_runner_setpoints()
748
+
749
+ def quit_fun(self):
750
+ """ """
751
+ try:
752
+ try:
753
+ self.exit_runner_thread()
754
+ except Exception as e:
755
+ print(e)
756
+
757
+ areas = self.dock_area.tempAreas[:]
758
+ for area in areas:
759
+ area.win.close()
760
+ QtWidgets.QApplication.processEvents()
761
+ QThread.msleep(1000)
762
+ QtWidgets.QApplication.processEvents()
763
+
764
+ self.dock_area.parent().close()
765
+ self.dashboard.remove_modules([setp for setp in self.model_class.setpoints_names])
766
+
767
+ except Exception as e:
768
+ print(e)
769
+
770
+ def update_runner_setpoints(self):
771
+ self.command_pid.emit(ThreadCommand("update_setpoints", self.setpoints))
772
+
773
+ @Slot(list)
774
+ def thread_status(
775
+ self, status
776
+ ): # general function to get datas/infos from all threads back to the main
777
+ """ """
778
+ pass
779
+
780
+
781
+ class PIDRunner(QObject):
782
+ status_sig = Signal(list)
783
+ pid_output_signal = Signal(DataToExport)
784
+ time_elapsed_signal = Signal(float)
785
+
786
+ def __init__(
787
+ self,
788
+ model_class: PIDModelGeneric,
789
+ modules_manager: ModulesManager,
790
+ setpoints=[],
791
+ params=dict([]),
792
+ ):
793
+ """
794
+ Init the PID instance with params as initial conditions
795
+
796
+ Parameters
797
+ ----------
798
+ params: (dict) Kp=1.0, Ki=0.0, Kd=0.0,setpoints=[0], sample_time=0.01, output_limits=(None, None),
799
+ auto_mode=True,
800
+ proportional_on_measurement=False)
801
+ """
802
+ super().__init__()
803
+ self.model_class = model_class
804
+ self.modules_manager = modules_manager
805
+ Nsetpoints = model_class.Nsetpoints
806
+ self.current_time = 0
807
+ self.time_elapsed = 0 # Time elapsed in the running loop
808
+ self.inputs_from_dets = DataToExport(
809
+ "inputs",
810
+ data=[
811
+ DataCalculated(
812
+ self.model_class.setpoints_names[ind],
813
+ data=[np.array([setpoints[ind]])],
814
+ )
815
+ for ind in range(Nsetpoints)
816
+ ],
817
+ )
818
+ self.outputs = [0.0 for _ in range(Nsetpoints)]
819
+ self.outputs_to_actuators = DataToActuators(
820
+ "pid",
821
+ mode="rel",
822
+ data=[
823
+ DataActuator(
824
+ self.model_class.actuators_name[ind], data=self.outputs[ind]
825
+ )
826
+ for ind in range(Nsetpoints)
827
+ ],
828
+ )
829
+
830
+ if "sample_time" in params:
831
+ self.sample_time = params["sample_time"]
832
+ else:
833
+ self.sample_time = 0.010 # in secs
834
+
835
+ self.pids = [
836
+ PID(setpoint=setpoints[0], **params) for ind in range(Nsetpoints)
837
+ ] # #PID(object):
838
+ for pid in self.pids:
839
+ pid.set_auto_mode(False)
840
+ self.refreshing_ouput_time = 200
841
+ self.running = True
842
+ self.timer = self.startTimer(self.refreshing_ouput_time)
843
+
844
+ self.paused = True
845
+
846
+ self.refresh_queues() # Initialize queues with the desired length
847
+
848
+ [queue_input.append(output) for queue_input, output in zip(self.queue_inputs, self.outputs)] # Prefill queues with initial output
849
+ self.clear_queues = True # Clear queues on first iteration
850
+
851
+ # self.timeout_timer = QtCore.QTimer()
852
+ # self.timeout_timer.setInterval(10000)
853
+ # self.timeout_scan_flag = False
854
+ # self.timeout_timer.timeout.connect(self.timeout)
855
+ #
856
+ def timerEvent(self, event):
857
+ outputs_dwa = self.outputs_to_actuators.merge_as_dwa("Data0D", name="outputs")
858
+ outputs_dwa.labels = self.modules_manager.selected_actuators_name
859
+ dte = DataToExport("toplot", data=[outputs_dwa])
860
+ inputs_dwa = DataRaw("inputs", data=[np.array(queue_input) for queue_input in self.queue_inputs], labels=self.modules_manager.selected_actuators_name)
861
+ dte.append(inputs_dwa)
862
+ self.pid_output_signal.emit(dte)
863
+ self.time_elapsed_signal.emit(self.time_elapsed)
864
+ self.clear_queues = True
865
+
866
+ @Slot(ThreadCommand)
867
+ def queue_command(self, command: ThreadCommand):
868
+ """ """
869
+ if command.command == "start_PID":
870
+ self.start_PID(*command.attribute)
871
+
872
+ elif command.command == "run_PID":
873
+ self.run_PID(*command.attribute)
874
+
875
+ elif command.command == "pause_PID":
876
+ self.pause_PID(*command.attribute)
877
+
878
+ elif command.command == "stop_PID":
879
+ self.stop_PID()
880
+
881
+ elif command.command == "update_options":
882
+ self.set_option(**command.attribute)
883
+
884
+ elif command.command == "update_setpoints":
885
+ self.update_setpoints(command.attribute)
886
+
887
+ elif command.command == "input":
888
+ self.update_input(*command.attribute)
889
+
890
+ elif command.command == "update_timer":
891
+ if command.attribute[0] == "refresh_plot_time":
892
+ self.killTimer(self.timer)
893
+ self.refreshing_ouput_time = command.attribute[1]
894
+ self.refresh_queues()
895
+ self.timer = self.startTimer(self.refreshing_ouput_time)
896
+
897
+ elif command.attribute[0] == "timeout":
898
+ self.timeout_timer.setInterval(command.attribute[1])
899
+
900
+ def update_input(self, measurements: DataToExport):
901
+ self.inputs_from_dets = self.model_class.convert_input(measurements)
902
+
903
+ def start_PID(self, sync_detectors=True, sync_acts=False):
904
+ """Start the pid controller loop
905
+
906
+ Parameters
907
+ ----------
908
+ sync_detectors: (bool) if True will make sure all selected detectors (if any) all got their data before calling
909
+ the model
910
+ sync_acts: (bool) if True will make sure all selected actuators (if any) all reached their target position
911
+ before calling the model
912
+ """
913
+ self.running = True
914
+ try:
915
+ if sync_detectors:
916
+ self.modules_manager.connect_detectors()
917
+ if sync_acts:
918
+ self.modules_manager.connect_actuators()
919
+
920
+ self.current_time = time.perf_counter()
921
+ logger.info("PID loop starting")
922
+ while self.running:
923
+ # # GRAB DATA FIRST AND WAIT ALL DETECTORS RETURNED
924
+ self.det_done_datas: DataToExport = self.modules_manager.grab_datas()
925
+
926
+ self.inputs_from_dets: DataToExport = self.model_class.convert_input(
927
+ self.det_done_datas
928
+ )
929
+ # # CHECK TIME ELAPSED FROM LAST LOOP
930
+ self.time_elapsed = (
931
+ time.perf_counter() - self.current_time
932
+ ) # Time elapsed since last loop
933
+ sleep_time = self.sample_time - self.time_elapsed
934
+ if sleep_time > 0:
935
+ QThread.msleep(int(sleep_time * 1000))
936
+ # # EXECUTE THE PID
937
+ self.outputs = []
938
+ for ind, pid in enumerate(self.pids):
939
+ self.outputs.append(pid(float(self.inputs_from_dets[ind][0][0])))
940
+
941
+ self.current_time = time.perf_counter() # Update current time
942
+
943
+ # # APPLY THE PID OUTPUT TO THE ACTUATORS
944
+ self.outputs_to_actuators: DataToActuators = (
945
+ self.model_class.convert_output(self.outputs, dt=None)
946
+ )
947
+ if self.clear_queues:
948
+ [queue_input.clear() for queue_input in self.queue_inputs]
949
+ self.clear_queues = False
950
+ for data_input, queue_input in zip(self.inputs_from_dets.data, self.queue_inputs):
951
+ queue_input.append(data_input[0][0])
952
+
953
+ if not self.paused:
954
+ self.modules_manager.move_actuators(
955
+ self.outputs_to_actuators,
956
+ self.outputs_to_actuators.mode,
957
+ polling=False,
958
+ )
959
+ QtWidgets.QApplication.processEvents()
960
+
961
+ logger.info("PID loop exiting")
962
+ self.modules_manager.connect_actuators(False)
963
+ self.modules_manager.connect_detectors(False)
964
+
965
+ except Exception as e:
966
+ logger.exception(str(e))
967
+
968
+ def update_setpoints(self, setpoints):
969
+ for ind, pid in enumerate(self.pids):
970
+ pid.setpoint = setpoints[ind]
971
+
972
+ def refresh_queues(self):
973
+ self.queue_length = 5 * int(self.refreshing_ouput_time * 1e-3 / self.sample_time) # Queue length is approximately output_time/sampling_time (*5 for safety)
974
+ self.queue_inputs = [deque(maxlen=self.queue_length) for ind in range(len(self.outputs))]
975
+
976
+ def set_option(self, **option):
977
+ for pid in self.pids:
978
+ for key in option:
979
+ if hasattr(pid, key):
980
+ if key == "sample_time":
981
+ self.sample_time = option[key] / 1000
982
+ setattr(pid, key, self.sample_time)
983
+ self.refresh_queues()
984
+ else:
985
+ setattr(pid, key, option[key])
986
+
987
+ def run_PID(self, last_values):
988
+ logger.info("Stabilization started")
989
+ for ind, pid in enumerate(self.pids):
990
+ pid.set_auto_mode(True, last_values[ind])
991
+
992
+ def pause_PID(self, pause_state):
993
+ for ind, pid in enumerate(self.pids):
994
+ if pause_state:
995
+ pid.set_auto_mode(False)
996
+ logger.info("Stabilization paused")
997
+ else:
998
+ pid.set_auto_mode(True, self.outputs[ind])
999
+ logger.info("Stabilization restarted from pause")
1000
+ self.paused = pause_state
1001
+
1002
+ def stop_PID(self):
1003
+ self.running = False
1004
+ logger.info("PID loop exiting")
1005
+
1006
+
1007
+ if __name__ == "__main__":
1008
+ from pymodaq_gui.utils.utils import mkQApp
1009
+ from pymodaq.utils.gui_utils.loader_utils import load_dashboard_with_preset
1010
+
1011
+ app = mkQApp("DAQ_PID")
1012
+ preset_file_name = config("presets", f"default_preset_for_pid")
1013
+
1014
+ dashboard, extension, win = load_dashboard_with_preset(preset_file_name, "DAQ_PID")
1015
+
1016
+ app.exec()