microeye 2.3.2__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 (190) hide show
  1. microEye/__init__.py +47 -0
  2. microEye/_version.py +2 -0
  3. microEye/analysis/__init__.py +1 -0
  4. microEye/analysis/checklist_dialog.py +143 -0
  5. microEye/analysis/cmosMaps.py +228 -0
  6. microEye/analysis/filters/__init__.py +9 -0
  7. microEye/analysis/filters/base.py +21 -0
  8. microEye/analysis/filters/spatial.py +338 -0
  9. microEye/analysis/filters/temporal.py +76 -0
  10. microEye/analysis/fitting/__init__.py +0 -0
  11. microEye/analysis/fitting/fit.py +680 -0
  12. microEye/analysis/fitting/nena.py +375 -0
  13. microEye/analysis/fitting/phasor_fit.py +90 -0
  14. microEye/analysis/fitting/processing.py +317 -0
  15. microEye/analysis/fitting/psf/__init__.py +6 -0
  16. microEye/analysis/fitting/psf/extract.py +1129 -0
  17. microEye/analysis/fitting/psf/rubost_mean.py +150 -0
  18. microEye/analysis/fitting/psf/spline.py +167 -0
  19. microEye/analysis/fitting/psf/stats/__init__.py +12 -0
  20. microEye/analysis/fitting/psf/stats/core.py +295 -0
  21. microEye/analysis/fitting/psf/stats/curve_fit.py +708 -0
  22. microEye/analysis/fitting/psf/stats/io.py +104 -0
  23. microEye/analysis/fitting/psf/stats/slope_fit.py +171 -0
  24. microEye/analysis/fitting/psf/temp.py +147 -0
  25. microEye/analysis/fitting/psf/test.py +47 -0
  26. microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUfunctions.py +657 -0
  27. microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUmleFit_LM.py +1336 -0
  28. microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUsplineLib.py +270 -0
  29. microEye/analysis/fitting/pyfit3Dcspline/CPU/__init__.py +1 -0
  30. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUfunctions.py +609 -0
  31. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_EMCCD.py +1396 -0
  32. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_sCMOS.py +1426 -0
  33. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUsplineLib.py +231 -0
  34. microEye/analysis/fitting/pyfit3Dcspline/GPU/__init__.py +2 -0
  35. microEye/analysis/fitting/pyfit3Dcspline/__init__.py +10 -0
  36. microEye/analysis/fitting/pyfit3Dcspline/constants.py +27 -0
  37. microEye/analysis/fitting/pyfit3Dcspline/mainfunctions.py +903 -0
  38. microEye/analysis/fitting/results.py +917 -0
  39. microEye/analysis/fitting/results_stats.py +251 -0
  40. microEye/analysis/fitting/tardis.py +209 -0
  41. microEye/analysis/multi_viewer.py +568 -0
  42. microEye/analysis/processing/__init__.py +1 -0
  43. microEye/analysis/processing/frc.py +120 -0
  44. microEye/analysis/rendering/__init__.py +3 -0
  45. microEye/analysis/rendering/base.py +452 -0
  46. microEye/analysis/rendering/cloud.py +215 -0
  47. microEye/analysis/rendering/core.py +47 -0
  48. microEye/analysis/rendering/volumetric.py +198 -0
  49. microEye/analysis/tools/__init__.py +0 -0
  50. microEye/analysis/tools/kymograms.py +1079 -0
  51. microEye/analysis/tools/roi_selectors.py +556 -0
  52. microEye/analysis/utils/__init__.py +3 -0
  53. microEye/analysis/utils/coordinates.py +18 -0
  54. microEye/analysis/utils/images.py +68 -0
  55. microEye/analysis/utils/windows.py +19 -0
  56. microEye/analysis/viewer/__init__.py +3 -0
  57. microEye/analysis/viewer/image_options_widget.py +615 -0
  58. microEye/analysis/viewer/images.py +1225 -0
  59. microEye/analysis/viewer/layers_widget.py +415 -0
  60. microEye/analysis/viewer/localizations.py +1304 -0
  61. microEye/analysis/viewer/psf.py +1003 -0
  62. microEye/analysis/viewer/volume.py +456 -0
  63. microEye/hardware/__init__.py +3 -0
  64. microEye/hardware/cams/__init__.py +29 -0
  65. microEye/hardware/cams/camera_calibration.py +99 -0
  66. microEye/hardware/cams/camera_list.py +528 -0
  67. microEye/hardware/cams/camera_options.py +694 -0
  68. microEye/hardware/cams/camera_panel.py +941 -0
  69. microEye/hardware/cams/dummy/__init__.py +1 -0
  70. microEye/hardware/cams/dummy/dummy_panel.py +759 -0
  71. microEye/hardware/cams/jobs.py +497 -0
  72. microEye/hardware/cams/line_profiler.py +99 -0
  73. microEye/hardware/cams/linescan/IR_Cam.py +474 -0
  74. microEye/hardware/cams/linescan/__init__.py +1 -0
  75. microEye/hardware/cams/micam.py +524 -0
  76. microEye/hardware/cams/pco/__init__.py +58 -0
  77. microEye/hardware/cams/pco/enums.py +382 -0
  78. microEye/hardware/cams/pco/pco_cam.py +761 -0
  79. microEye/hardware/cams/pco/pco_panel.py +477 -0
  80. microEye/hardware/cams/shortcuts.py +312 -0
  81. microEye/hardware/cams/thorlabs/__init__.py +1 -0
  82. microEye/hardware/cams/thorlabs/thorlabs.py +1508 -0
  83. microEye/hardware/cams/thorlabs/thorlabs_panel.py +850 -0
  84. microEye/hardware/cams/ueye/__init__.py +1 -0
  85. microEye/hardware/cams/ueye/ueye_camera.py +1023 -0
  86. microEye/hardware/cams/ueye/ueye_panel.py +861 -0
  87. microEye/hardware/cams/vimba/__init__.py +1 -0
  88. microEye/hardware/cams/vimba/vimba_cam.py +1000 -0
  89. microEye/hardware/cams/vimba/vimba_panel.py +813 -0
  90. microEye/hardware/device.py +60 -0
  91. microEye/hardware/lasers/__init__.py +13 -0
  92. microEye/hardware/lasers/io_matchbox.py +791 -0
  93. microEye/hardware/lasers/io_params.py +85 -0
  94. microEye/hardware/lasers/io_single_laser.py +742 -0
  95. microEye/hardware/lasers/laser_relay.py +594 -0
  96. microEye/hardware/mieye/__init__.py +1 -0
  97. microEye/hardware/mieye/acquisition_manager.py +467 -0
  98. microEye/hardware/mieye/devices_manager.py +533 -0
  99. microEye/hardware/mieye/miEye.py +659 -0
  100. microEye/hardware/misc/__init__.py +0 -0
  101. microEye/hardware/misc/acquisition_view.py +71 -0
  102. microEye/hardware/misc/reglo.py +761 -0
  103. microEye/hardware/misc/temp.py +188 -0
  104. microEye/hardware/port_config.py +59 -0
  105. microEye/hardware/protocols/__init__.py +2 -0
  106. microEye/hardware/protocols/actions.py +402 -0
  107. microEye/hardware/protocols/actions_items.py +703 -0
  108. microEye/hardware/protocols/designer.py +244 -0
  109. microEye/hardware/protocols/scene_manager.py +191 -0
  110. microEye/hardware/protocols/serialization.py +97 -0
  111. microEye/hardware/pycromanager/__init__.py +16 -0
  112. microEye/hardware/pycromanager/core.py +1433 -0
  113. microEye/hardware/pycromanager/devices.py +461 -0
  114. microEye/hardware/pycromanager/enums.py +107 -0
  115. microEye/hardware/pycromanager/headless.py +153 -0
  116. microEye/hardware/pycromanager/utils.py +34 -0
  117. microEye/hardware/pycromanager/widgets/__init__.py +5 -0
  118. microEye/hardware/pycromanager/widgets/bridges.py +407 -0
  119. microEye/hardware/pycromanager/widgets/headless_manager.py +258 -0
  120. microEye/hardware/pycromanager/widgets/headless_options.py +224 -0
  121. microEye/hardware/pycromanager/widgets/pycro_panel.py +455 -0
  122. microEye/hardware/stages/__init__.py +18 -0
  123. microEye/hardware/stages/elliptec/__init__.py +5 -0
  124. microEye/hardware/stages/elliptec/baseDevice.py +314 -0
  125. microEye/hardware/stages/elliptec/device.py +384 -0
  126. microEye/hardware/stages/elliptec/deviceID.py +212 -0
  127. microEye/hardware/stages/elliptec/devicePort.py +379 -0
  128. microEye/hardware/stages/elliptec/deviceStatus.py +65 -0
  129. microEye/hardware/stages/elliptec/devicesView.py +706 -0
  130. microEye/hardware/stages/elliptec/ellDevices.py +134 -0
  131. microEye/hardware/stages/elliptec/messageUpdater.py +34 -0
  132. microEye/hardware/stages/elliptec/motorInfo.py +153 -0
  133. microEye/hardware/stages/elliptec/stage.py +62 -0
  134. microEye/hardware/stages/elliptec/test.py +139 -0
  135. microEye/hardware/stages/kinesis/__init__.py +1 -0
  136. microEye/hardware/stages/kinesis/kdc101/__init__.py +1 -0
  137. microEye/hardware/stages/kinesis/kdc101/enums.py +1002 -0
  138. microEye/hardware/stages/kinesis/kdc101/factory.py +171 -0
  139. microEye/hardware/stages/kinesis/kdc101/kdc101.py +718 -0
  140. microEye/hardware/stages/kinesis/kinesis.py +776 -0
  141. microEye/hardware/stages/piezo_concept.py +607 -0
  142. microEye/hardware/stages/stabilizer.py +785 -0
  143. microEye/hardware/stages/stage.py +89 -0
  144. microEye/hardware/widgets/__init__.py +10 -0
  145. microEye/hardware/widgets/controller.py +246 -0
  146. microEye/hardware/widgets/devices.py +133 -0
  147. microEye/hardware/widgets/focusWidget.py +264 -0
  148. microEye/hardware/widgets/qlist_slider.py +113 -0
  149. microEye/hardware/widgets/scan_acquisition.py +424 -0
  150. microEye/icons/1024.png +0 -0
  151. microEye/icons/128.png +0 -0
  152. microEye/icons/16.png +0 -0
  153. microEye/icons/24.png +0 -0
  154. microEye/icons/256.png +0 -0
  155. microEye/icons/32.png +0 -0
  156. microEye/icons/48.png +0 -0
  157. microEye/icons/512.png +0 -0
  158. microEye/icons/64.png +0 -0
  159. microEye/icons/__init__.py +0 -0
  160. microEye/icons/close.svg +88 -0
  161. microEye/icons/mieye.png +0 -0
  162. microEye/icons/min.svg +83 -0
  163. microEye/icons/viewer.png +0 -0
  164. microEye/launcher.py +42 -0
  165. microEye/qt.py +181 -0
  166. microEye/utils/__init__.py +2 -0
  167. microEye/utils/enum_encoder.py +10 -0
  168. microEye/utils/expandable_groupbox.py +93 -0
  169. microEye/utils/gui_helper.py +457 -0
  170. microEye/utils/hid/__init__.py +8 -0
  171. microEye/utils/hid/controller.py +153 -0
  172. microEye/utils/hid/device.py +63 -0
  173. microEye/utils/hid/enums.py +140 -0
  174. microEye/utils/hid/utils.py +58 -0
  175. microEye/utils/labelled_slider.py +134 -0
  176. microEye/utils/metadata.py +599 -0
  177. microEye/utils/metadata_tree.py +718 -0
  178. microEye/utils/micro_launcher.py +245 -0
  179. microEye/utils/parameter_tree.py +325 -0
  180. microEye/utils/pyscripting.py +444 -0
  181. microEye/utils/retry_exec.py +35 -0
  182. microEye/utils/start_gui.py +112 -0
  183. microEye/utils/thread_worker.py +115 -0
  184. microEye/utils/uImage.py +1512 -0
  185. microeye-2.3.2.dist-info/METADATA +401 -0
  186. microeye-2.3.2.dist-info/RECORD +190 -0
  187. microeye-2.3.2.dist-info/WHEEL +5 -0
  188. microeye-2.3.2.dist-info/entry_points.txt +2 -0
  189. microeye-2.3.2.dist-info/licenses/LICENSE +674 -0
  190. microeye-2.3.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,718 @@
1
+ from collections import deque
2
+ from dataclasses import dataclass, field
3
+
4
+ from microEye.hardware.stages.kinesis.kdc101.factory import *
5
+ from microEye.qt import QtCore, QtSerialPort, Signal
6
+ from microEye.utils.thread_worker import *
7
+
8
+
9
+ @dataclass
10
+ class KDC101State:
11
+ position: float = 0.0
12
+ velocity: float = 0.0
13
+ backlash: float = 0.0
14
+ current: float = 0.0
15
+ encoder_count: int = 0
16
+ info_serial_number: int = 0
17
+ info_model: str = ''
18
+ info_type: int = 0
19
+ info_minor_revision_number: int = 0
20
+ info_interim_revision_number: int = 0
21
+ info_major_revision_number: int = 0
22
+ info_hw_version: int = 0
23
+ info_mod_state: int = 0
24
+ info_nchs: int = 0
25
+ status_code: int = 0
26
+ velocity_min_vel: float = 0.0
27
+ velocity_accn: float = 0.0
28
+ velocity_max_vel: float = 0.0
29
+ jog_mode: int = 0
30
+ jog_step: float = 0.0
31
+ jog_min_vel: float = 0.0
32
+ jog_accn: float = 0.0
33
+ jog_max_vel: float = 0.0
34
+ jog_stop_mode: int = 0
35
+ home_direction: int = 0
36
+ home_limit_switch: bool = False
37
+ home_velocity: float = 0.0
38
+ home_zero_offset: float = 0.0
39
+ limit_switch_cw_hard_limit: int = 0
40
+ limit_switch_ccw_hard_limit: int = 0
41
+ limit_switch_cw_soft_limit: float = 0.0
42
+ limit_switch_ccw_soft_limit: float = 0.0
43
+ limit_switch_soft_limit_mode: int = 0
44
+ response_msg_id: int = 0
45
+ response_code: int = 0
46
+ response_notes: str = ''
47
+
48
+
49
+ class KDC101Controller(QtCore.QObject):
50
+ '''Class for controlling Thorlab Z825B 25mm actuator by a KDC101'''
51
+
52
+ SOURCE = 0x01
53
+ DESTINATION = 0x50
54
+ ENCODER_COUNT = 34555
55
+ SOFTLIMIT_FACTOR = 134218
56
+
57
+ dataReceived = Signal(KDC101_RESPONSES, dict)
58
+ '''Signal emitted when data is received from the device'''
59
+ onWrite = Signal(bytearray)
60
+ '''Signal emitted when data is written to the device'''
61
+ stateChanged = Signal(str, object)
62
+ '''Signal for state changes'''
63
+
64
+ def __init__(self, **kwargs) -> None:
65
+ super().__init__()
66
+
67
+ self.__serial = QtSerialPort.QSerialPort(None, readyRead=self.rx_slot)
68
+ self.__serial.setBaudRate(115200)
69
+ self.__serial.setDataBits(QtSerialPort.QSerialPort.DataBits.Data8)
70
+ self.__serial.setParity(QtSerialPort.QSerialPort.Parity.NoParity)
71
+ self.__serial.setFlowControl(
72
+ QtSerialPort.QSerialPort.FlowControl.HardwareControl
73
+ )
74
+ self.__serial.setPortName(kwargs.get('portName', 'COM1'))
75
+
76
+ self.__buffer = []
77
+ self.__responses = deque(maxlen=256)
78
+
79
+ self.__channel = kwargs.get('channel', Channels.NA)
80
+
81
+ self.onWrite.connect(lambda data: self.__serial.write(data))
82
+
83
+ self.__state = KDC101State()
84
+
85
+ @property
86
+ def state(self) -> KDC101State:
87
+ return self.__state
88
+
89
+ @property
90
+ def position(self) -> float:
91
+ return self.__state.position
92
+
93
+ def _update_state(self, attr: str, value: object):
94
+ setattr(self.__state, attr, value)
95
+ self.stateChanged.emit(attr, value)
96
+
97
+ def _handle_state_update(self, response_enum: KDC101_RESPONSES, parsed_data: dict):
98
+ if response_enum in [
99
+ KDC101_RESPONSES.MGMSG_MOT_MOVE_COMPLETED,
100
+ KDC101_RESPONSES.MGMSG_MOT_MOVE_STOPPED,
101
+ KDC101_RESPONSES.MGMSG_MOT_GET_DCSTATUSUPDATE,
102
+ ]:
103
+ self._update_state('position', parsed_data['position'] / self.ENCODER_COUNT)
104
+ self._update_state('velocity', parsed_data['velocity'] / self.ENCODER_COUNT)
105
+ self._update_state(
106
+ 'current',
107
+ parsed_data['motor current'] / 1000,
108
+ )
109
+ self._update_state(
110
+ 'status_code',
111
+ parsed_data['status bits'],
112
+ )
113
+ elif response_enum == KDC101_RESPONSES.MGMSG_MOT_GET_VELPARAMS:
114
+ self._update_state(
115
+ 'velocity_min_vel', parsed_data['min velocity'] / self.ENCODER_COUNT
116
+ )
117
+ self._update_state(
118
+ 'velocity_accn', parsed_data['acceleration'] / self.ENCODER_COUNT
119
+ )
120
+ self._update_state(
121
+ 'velocity_max_vel', parsed_data['max velocity'] / self.ENCODER_COUNT
122
+ )
123
+ elif response_enum == KDC101_RESPONSES.MGMSG_MOT_GET_JOGPARAMS:
124
+ self._update_state('jog_mode', JogMode(parsed_data['jog mode']))
125
+ self._update_state(
126
+ 'jog_step', parsed_data['jog step size'] / self.ENCODER_COUNT
127
+ )
128
+ self._update_state(
129
+ 'jog_min_vel', parsed_data['jog min velocity'] / self.ENCODER_COUNT
130
+ )
131
+ self._update_state(
132
+ 'jog_accn', parsed_data['jog acceleration'] / self.ENCODER_COUNT
133
+ )
134
+ self._update_state(
135
+ 'jog_max_vel', parsed_data['jog max velocity'] / self.ENCODER_COUNT
136
+ )
137
+ self._update_state('jog_stop_mode', StopMode(parsed_data['jog stop mode']))
138
+ elif response_enum == KDC101_RESPONSES.MGMSG_MOT_GET_HOMEPARAMS:
139
+ self._update_state('home_direction', parsed_data['direction'])
140
+ self._update_state('home_limit_switch', parsed_data['limit switch'])
141
+ self._update_state(
142
+ 'home_velocity', parsed_data['velocity'] / self.ENCODER_COUNT
143
+ )
144
+ self._update_state(
145
+ 'home_zero_offset', parsed_data['zero offset'] / self.ENCODER_COUNT
146
+ )
147
+ elif response_enum == KDC101_RESPONSES.MGMSG_MOT_GET_LIMSWITCHPARAMS:
148
+ self._update_state(
149
+ 'limit_switch_cw_hard_limit',
150
+ LimitSwitchOperation(parsed_data['cw hard limit']),
151
+ )
152
+ self._update_state(
153
+ 'limit_switch_ccw_hard_limit',
154
+ LimitSwitchOperation(parsed_data['ccw hard limit']),
155
+ )
156
+ self._update_state(
157
+ 'limit_switch_cw_soft_limit',
158
+ parsed_data['cw soft limit'] / self.SOFTLIMIT_FACTOR,
159
+ )
160
+ self._update_state(
161
+ 'limit_switch_ccw_soft_limit',
162
+ parsed_data['ccw soft limit'] / self.SOFTLIMIT_FACTOR,
163
+ )
164
+ self._update_state(
165
+ 'limit_switch_soft_limit_mode',
166
+ LimitSwitchMode(parsed_data['soft limit mode']),
167
+ )
168
+ elif response_enum == KDC101_RESPONSES.MGMSG_MOT_GET_ENCCOUNTER:
169
+ self._update_state('encoder_count', parsed_data['encoder count'])
170
+ elif response_enum == KDC101_RESPONSES.MGMSG_MOT_GET_STATUSBITS:
171
+ self._update_state('status_code', parsed_data['status bits'])
172
+ elif response_enum == KDC101_RESPONSES.MGMSG_HW_GET_INFO:
173
+ self._update_state('info_serial_number', parsed_data['serial number'])
174
+ self._update_state('info_model', parsed_data['model'])
175
+ self._update_state('info_type', parsed_data['type'])
176
+ self._update_state(
177
+ 'info_minor_revision_number', parsed_data['minor revision number']
178
+ )
179
+ self._update_state(
180
+ 'info_interim_revision_number', parsed_data['interim revision number']
181
+ )
182
+ self._update_state(
183
+ 'info_major_revision_number', parsed_data['major revision number']
184
+ )
185
+ self._update_state('info_hw_version', parsed_data['hw version'])
186
+ self._update_state('info_mod_state', parsed_data['mod state'])
187
+ self._update_state('info_nchs', parsed_data['nchs'])
188
+ elif response_enum == KDC101_RESPONSES.MGMSG_MOT_GET_GENMOVEPARAMS:
189
+ self._update_state('backlash', parsed_data['distance'] / self.ENCODER_COUNT)
190
+ elif response_enum == KDC101_RESPONSES.MGMSG_MOT_GET_POSCOUNTER:
191
+ self._update_state('position', parsed_data['position'] / self.ENCODER_COUNT)
192
+ elif response_enum == KDC101_RESPONSES.MGMSG_HW_RESPONSE:
193
+ self._update_state('response_code', parsed_data['code'])
194
+ elif response_enum == KDC101_RESPONSES.MGMSG_HW_RICHRESPONSE:
195
+ self._update_state('response_msg_id', parsed_data['msg_id'])
196
+ self._update_state('response_code', parsed_data['code'])
197
+ self._update_state('response_notes', parsed_data['notes'])
198
+ elif response_enum == KDC101_RESPONSES.MGMSG_MOT_MOVE_HOMED:
199
+ self._update_state('position', 0)
200
+
201
+ @property
202
+ def source(self):
203
+ return self.SOURCE
204
+
205
+ @property
206
+ def destination(self):
207
+ return self.DESTINATION
208
+
209
+ @property
210
+ def channel(self):
211
+ return self.__channel
212
+
213
+ def open(self, portName: str = None):
214
+ '''
215
+ Open the serial port.
216
+ '''
217
+ if portName:
218
+ self.__serial.setPortName(portName)
219
+
220
+ if not self.isOpen():
221
+ self.__serial.open(QtCore.QIODevice.OpenModeFlag.ReadWrite)
222
+
223
+ self.resume_end_of_move_msgs()
224
+
225
+ self.get_position()
226
+
227
+ return self.isOpen()
228
+
229
+ def close(self):
230
+ '''
231
+ Close the serial port.
232
+ '''
233
+ if self.isOpen():
234
+ self.__serial.close()
235
+
236
+ def isOpen(self):
237
+ '''
238
+ Check if the serial port is open.
239
+
240
+ Returns
241
+ -------
242
+ bool
243
+ True if the serial port is open, False otherwise.
244
+ '''
245
+ return self.__serial.isOpen()
246
+
247
+ def portName(self):
248
+ '''
249
+ Get the name of the serial port.
250
+
251
+ Returns
252
+ -------
253
+ str
254
+ The name of the serial port.
255
+ '''
256
+ return self.__serial.portName()
257
+
258
+ def baudRate(self):
259
+ '''
260
+ Get the baud rate of the serial port.
261
+
262
+ Returns
263
+ -------
264
+ int
265
+ The baud rate of the serial port.
266
+ '''
267
+ return self.__serial.baudRate()
268
+
269
+ def setBaudRate(self, baudRate: int):
270
+ '''
271
+ Set the baud rate of the serial port.
272
+
273
+ Parameters
274
+ ----------
275
+ baudRate : int
276
+ The new baud rate.
277
+ '''
278
+ if not self.isOpen():
279
+ self.__serial.setBaudRate(baudRate)
280
+
281
+ def setPortName(self, name: str):
282
+ '''
283
+ Set the name of the serial port.
284
+
285
+ Parameters
286
+ ----------
287
+ name : str
288
+ The new port name.
289
+ '''
290
+ if not self.isOpen():
291
+ self.__serial.setPortName(name)
292
+
293
+ def write(self, cmd: KDC101Command) -> Optional[tuple[KDC101_RESPONSES, dict]]:
294
+ self.onWrite.emit(bytearray(cmd.data))
295
+
296
+ response = KDC101_CMDS.get(cmd.command_id).response
297
+ if response:
298
+ return self.wait_for_response(response)
299
+
300
+ def identify(self):
301
+ if self.isOpen():
302
+ _IDENTIFY = KDC101_Factory.create_command(
303
+ KDC101_CMDS.MGMSG_MOD_IDENTIFY,
304
+ channel=self.__channel,
305
+ destination=self.destination,
306
+ source=self.source,
307
+ )
308
+ return self.write(_IDENTIFY)
309
+
310
+ def home(self):
311
+ if self.isOpen():
312
+ _HOME = KDC101_Factory.create_command(
313
+ KDC101_CMDS.MGMSG_MOT_MOVE_HOME,
314
+ channel=self.__channel,
315
+ destination=self.destination,
316
+ source=self.source,
317
+ )
318
+ return self.write(_HOME)
319
+
320
+ def jog_fw(self):
321
+ if self.isOpen():
322
+ _JOG_FW = KDC101_Factory.create_command(
323
+ KDC101_CMDS.MGMSG_MOT_MOVE_JOG,
324
+ channel=self.__channel,
325
+ param=JogDirection.FORWARD,
326
+ destination=self.destination,
327
+ source=self.source,
328
+ )
329
+ return self.write(_JOG_FW)
330
+
331
+ def jog_bw(self):
332
+ if self.isOpen():
333
+ _JOG_BW = KDC101_Factory.create_command(
334
+ KDC101_CMDS.MGMSG_MOT_MOVE_JOG,
335
+ channel=self.__channel,
336
+ param=JogDirection.REVERSE,
337
+ destination=self.destination,
338
+ source=self.source,
339
+ )
340
+ return self.write(_JOG_BW)
341
+
342
+ def to_encoder(self, value: float):
343
+ return int(self.ENCODER_COUNT * value)
344
+
345
+ def move_absolute(self, position=0.1):
346
+ if self.isOpen():
347
+ _position = self.to_encoder(position)
348
+ _ABSOLUTE = KDC101_Factory.create_command(
349
+ KDC101_CMDS.MGMSG_MOT_MOVE_ABSOLUTE,
350
+ channel=self.__channel,
351
+ destination=self.destination,
352
+ source=self.source,
353
+ position=_position,
354
+ )
355
+ return self.write(_ABSOLUTE)
356
+
357
+ def move_relative(self, distance=0.1):
358
+ if self.isOpen():
359
+ _distance = self.to_encoder(distance)
360
+ _RELATIVE = KDC101_Factory.create_command(
361
+ KDC101_CMDS.MGMSG_MOT_MOVE_RELATIVE,
362
+ channel=self.__channel,
363
+ destination=self.destination,
364
+ source=self.source,
365
+ distance=_distance,
366
+ )
367
+ return self.write(_RELATIVE)
368
+
369
+ def move_stop(self, mode=StopMode.IMMEDIATE):
370
+ if self.isOpen():
371
+ _STOP = KDC101_Factory.create_command(
372
+ KDC101_CMDS.MGMSG_MOT_MOVE_STOP,
373
+ channel=self.__channel,
374
+ param=mode,
375
+ destination=self.destination,
376
+ source=self.source,
377
+ )
378
+ return self.write(_STOP)
379
+
380
+ def start_updates(self):
381
+ if self.isOpen():
382
+ _START_UPDATES = KDC101_Factory.create_command(
383
+ KDC101_CMDS.MGMSG_HW_START_UPDATEMSGS,
384
+ channel=self.__channel,
385
+ destination=self.destination,
386
+ source=self.source,
387
+ )
388
+ return self.write(_START_UPDATES)
389
+
390
+ def stop_updates(self):
391
+ if self.isOpen():
392
+ _STOP_UPDATES = KDC101_Factory.create_command(
393
+ KDC101_CMDS.MGMSG_HW_STOP_UPDATEMSGS,
394
+ channel=self.__channel,
395
+ destination=self.destination,
396
+ source=self.source,
397
+ )
398
+ return self.write(_STOP_UPDATES)
399
+
400
+ def resume_end_of_move_msgs(self):
401
+ if self.isOpen():
402
+ _RESUME_EOM = KDC101_Factory.create_command(
403
+ KDC101_CMDS.MGMSG_MOT_RESUME_ENDOFMOVEMSGS,
404
+ channel=self.__channel,
405
+ destination=self.destination,
406
+ source=self.source,
407
+ )
408
+ return self.write(_RESUME_EOM)
409
+
410
+ def suspend_end_of_move_msgs(self):
411
+ if self.isOpen():
412
+ _STOP_EOM = KDC101_Factory.create_command(
413
+ KDC101_CMDS.MGMSG_MOT_SUSPEND_ENDOFMOVEMSGS,
414
+ channel=self.__channel,
415
+ destination=self.destination,
416
+ source=self.source,
417
+ )
418
+ return self.write(_STOP_EOM)
419
+
420
+ def get_info(self):
421
+ if self.isOpen():
422
+ _REQ_INFO = KDC101_Factory.create_command(
423
+ KDC101_CMDS.MGMSG_HW_REQ_INFO,
424
+ channel=self.__channel,
425
+ destination=self.destination,
426
+ source=self.source,
427
+ )
428
+ response = self.write(_REQ_INFO)
429
+ if response:
430
+ _, parsed_data = response
431
+ return parsed_data
432
+
433
+ def get_position(self):
434
+ if self.isOpen():
435
+ _REQ_POS = KDC101_Factory.create_command(
436
+ KDC101_CMDS.MGMSG_MOT_REQ_POSCOUNTER,
437
+ channel=self.__channel,
438
+ destination=self.destination,
439
+ source=self.source,
440
+ )
441
+ response = self.write(_REQ_POS)
442
+ if response:
443
+ _, parsed_data = response
444
+ return parsed_data['position'] / self.ENCODER_COUNT
445
+ return None
446
+
447
+ def get_encoder_count(self):
448
+ if self.isOpen():
449
+ _REQ_ENC = KDC101_Factory.create_command(
450
+ KDC101_CMDS.MGMSG_MOT_REQ_ENCCOUNTER,
451
+ channel=self.__channel,
452
+ destination=self.destination,
453
+ source=self.source,
454
+ )
455
+ response = self.write(_REQ_ENC)
456
+ if response:
457
+ _, parsed_data = response
458
+ return parsed_data['encoder count']
459
+ return None
460
+
461
+ def get_status(self):
462
+ if self.isOpen():
463
+ _REQ_STATUS = KDC101_Factory.create_command(
464
+ KDC101_CMDS.MGMSG_MOT_REQ_STATUSBITS,
465
+ channel=self.__channel,
466
+ destination=self.destination,
467
+ source=self.source,
468
+ )
469
+ response = self.write(_REQ_STATUS)
470
+ if response:
471
+ _, parsed_data = response
472
+ return KDC101_Factory.interpret_status_bits(parsed_data['status_bits'])
473
+ return None
474
+
475
+ def get_status_update(self):
476
+ if self.isOpen():
477
+ _REQ_STATUS = KDC101_Factory.create_command(
478
+ KDC101_CMDS.MGMSG_MOT_REQ_DCSTATUSUPDATE,
479
+ channel=self.__channel,
480
+ destination=self.destination,
481
+ source=self.source,
482
+ )
483
+ return self.write(_REQ_STATUS)
484
+
485
+ def get_velocity_params(self):
486
+ if self.isOpen():
487
+ _REQ_VEL = KDC101_Factory.create_command(
488
+ KDC101_CMDS.MGMSG_MOT_REQ_VELPARAMS,
489
+ channel=self.__channel,
490
+ destination=self.destination,
491
+ source=self.source,
492
+ )
493
+ return self.write(_REQ_VEL)
494
+
495
+ def get_jog_params(self):
496
+ if self.isOpen():
497
+ _REQ_JOG = KDC101_Factory.create_command(
498
+ KDC101_CMDS.MGMSG_MOT_REQ_JOGPARAMS,
499
+ channel=self.__channel,
500
+ destination=self.destination,
501
+ source=self.source,
502
+ )
503
+ return self.write(_REQ_JOG)
504
+
505
+ def set_velocity_params(self, min_vel=0.0, accn=0.0, max_vel=0.0):
506
+ if self.isOpen():
507
+ return
508
+ _min_vel = self.to_encoder(min_vel)
509
+ _accn = self.to_encoder(accn)
510
+ _max_vel = self.to_encoder(max_vel)
511
+ _SET_VEL = KDC101_Factory.create_command(
512
+ KDC101_CMDS.MGMSG_MOT_SET_VELPARAMS,
513
+ channel=self.__channel,
514
+ destination=self.destination,
515
+ source=self.source,
516
+ min_velocity=_min_vel,
517
+ acceleration=_accn,
518
+ max_velocity=_max_vel,
519
+ )
520
+ return self.write(_SET_VEL)
521
+
522
+ def set_jog_params(
523
+ self,
524
+ mode=JogMode.SINGLE_STEP,
525
+ step=0.1,
526
+ min_vel=0.0,
527
+ accn=0.0,
528
+ max_vel=1.0,
529
+ stop_mode=StopMode.PROFILED,
530
+ ):
531
+ if self.isOpen():
532
+ return
533
+ _mode = mode.value
534
+ _step = self.to_encoder(step)
535
+ _min_vel = self.to_encoder(min_vel)
536
+ _accn = self.to_encoder(accn)
537
+ _max_vel = self.to_encoder(max_vel)
538
+ _stop_mode = stop_mode.value
539
+ _SET_JOG = KDC101_Factory.create_command(
540
+ KDC101_CMDS.MGMSG_MOT_SET_JOGPARAMS,
541
+ channel=self.__channel,
542
+ destination=self.destination,
543
+ source=self.source,
544
+ jog_mode=_mode,
545
+ jog_step_size=_step,
546
+ jog_min_velocity=_min_vel,
547
+ jog_acceleration=_accn,
548
+ jog_max_velocity=_max_vel,
549
+ jog_stop_mode=_stop_mode,
550
+ )
551
+ return self.write(_SET_JOG)
552
+
553
+ def get_backlash(self):
554
+ if self.isOpen():
555
+ _REQ_BACKLASH = KDC101_Factory.create_command(
556
+ KDC101_CMDS.MGMSG_MOT_REQ_GENMOVEPARAMS,
557
+ channel=self.__channel,
558
+ destination=self.destination,
559
+ source=self.source,
560
+ )
561
+ return self.write(_REQ_BACKLASH)
562
+
563
+ def set_backlash(self, backlash=0.0):
564
+ if self.isOpen():
565
+ _backlash = self.to_encoder(backlash)
566
+ _SET_BACKLASH = KDC101_Factory.create_command(
567
+ KDC101_CMDS.MGMSG_MOT_SET_GENMOVEPARAMS,
568
+ channel=self.__channel,
569
+ destination=self.destination,
570
+ source=self.source,
571
+ distance=_backlash,
572
+ )
573
+ return self.write(_SET_BACKLASH)
574
+
575
+ def get_limit_switch_params(self):
576
+ if self.isOpen():
577
+ _REQ_LIMIT = KDC101_Factory.create_command(
578
+ KDC101_CMDS.MGMSG_MOT_REQ_LIMSWITCHPARAMS,
579
+ channel=self.__channel,
580
+ destination=self.destination,
581
+ source=self.source,
582
+ )
583
+ return self.write(_REQ_LIMIT)
584
+
585
+ def set_limit_switch_params(
586
+ self,
587
+ cw_hard_limit: LimitSwitchOperation,
588
+ ccw_hard_limit: LimitSwitchOperation,
589
+ cw_soft_limit: float,
590
+ ccw_soft_limit: float,
591
+ soft_limit_mode: LimitSwitchMode,
592
+ ):
593
+ if self.isOpen():
594
+ return
595
+ cw_hard_limit = cw_hard_limit.value
596
+ ccw_hard_limit = ccw_hard_limit.value
597
+ cw_soft_limit *= self.SOFTLIMIT_FACTOR
598
+ ccw_soft_limit *= self.SOFTLIMIT_FACTOR
599
+ soft_limit_mode = soft_limit_mode.value
600
+ _SET_LIMIT = KDC101_Factory.create_command(
601
+ KDC101_CMDS.MGMSG_MOT_SET_LIMSWITCHPARAMS,
602
+ channel=self.__channel,
603
+ destination=self.destination,
604
+ source=self.source,
605
+ cw_hard_limit=cw_hard_limit,
606
+ ccw_hard_limit=ccw_hard_limit,
607
+ cw_soft_limit=cw_soft_limit,
608
+ ccw_soft_limit=ccw_soft_limit,
609
+ soft_limit_mode=soft_limit_mode,
610
+ )
611
+ return self.write(_SET_LIMIT)
612
+
613
+ def get_home_params(self):
614
+ if self.isOpen():
615
+ _REQ_HOME = KDC101_Factory.create_command(
616
+ KDC101_CMDS.MGMSG_MOT_REQ_HOMEPARAMS,
617
+ channel=self.__channel,
618
+ destination=self.destination,
619
+ source=self.source,
620
+ )
621
+ return self.write(_REQ_HOME)
622
+
623
+ def set_home_params(
624
+ self,
625
+ direction: JogDirection,
626
+ limit_switch: bool,
627
+ velocity: float,
628
+ zero_offset: float,
629
+ ):
630
+ if self.isOpen():
631
+ return
632
+ direction = direction.value
633
+ limit_switch = 0x01 if limit_switch else 0x02
634
+ velocity = self.to_encoder(velocity)
635
+ zero_offset = self.to_encoder(zero_offset)
636
+ _SET_HOME = KDC101_Factory.create_command(
637
+ KDC101_CMDS.MGMSG_MOT_SET_HOMEPARAMS,
638
+ channel=self.__channel,
639
+ destination=self.destination,
640
+ source=self.source,
641
+ direction=direction,
642
+ lim_switch=limit_switch,
643
+ velocity=velocity,
644
+ zero_offset=zero_offset,
645
+ )
646
+ return self.write(_SET_HOME)
647
+
648
+ def rx_slot(self):
649
+ '''
650
+ Handle incoming data from the serial port.
651
+ '''
652
+ try:
653
+ data = self.__serial.readAll().data()
654
+ self.__buffer.extend(data)
655
+
656
+ while len(self.__buffer) >= 6:
657
+ msg_id = struct.unpack('<H', bytes(self.__buffer[:2]))[0]
658
+
659
+ msg_size = KDC101_Factory.get_response_size(msg_id)
660
+
661
+ if len(self.__buffer) < msg_size:
662
+ break # Wait for more data
663
+
664
+ message = bytes(self.__buffer[:msg_size])
665
+ self.__buffer = self.__buffer[msg_size:]
666
+
667
+ try:
668
+ response_enum, parsed_data = KDC101_Factory.interpret_response(
669
+ message
670
+ )
671
+ print(response_enum, parsed_data)
672
+ self.__responses.append((response_enum, parsed_data))
673
+ self._handle_state_update(response_enum, parsed_data)
674
+ self.dataReceived.emit(response_enum, parsed_data)
675
+ except ValueError as e:
676
+ print(f'Error interpreting response: {e}')
677
+
678
+ except Exception as e:
679
+ self.__serial.errorOccurred.emit(str(e))
680
+
681
+ def clearResponses(self):
682
+ '''
683
+ Clear the response buffer.
684
+ '''
685
+ self.__responses.clear()
686
+ self.__buffer = []
687
+
688
+ def wait_for_response(
689
+ self, expected_response: KDC101_RESPONSES, timeout: int = 60000
690
+ ) -> tuple[KDC101_RESPONSES, dict]:
691
+ '''
692
+ This method blocks until the expected response is received or
693
+ the specified timeout is reached.
694
+
695
+ Parameters
696
+ ----------
697
+ expected_response : KDC101_RESPONSES
698
+ The expected response type.
699
+ timeout : int, optional
700
+ The timeout in milliseconds (default is 60000).
701
+ '''
702
+ start_time = QtCore.QDateTime.currentMSecsSinceEpoch()
703
+
704
+ while QtCore.QDateTime.currentMSecsSinceEpoch() - start_time < timeout:
705
+ if self.__responses:
706
+ response_enum, parsed_data = self.__responses.popleft()
707
+ if response_enum == expected_response:
708
+ return response_enum, parsed_data
709
+
710
+ if not self.isOpen():
711
+ print('Serial port closed while waiting for response.')
712
+ return None, None
713
+
714
+ QtCore.QCoreApplication.processEvents()
715
+ QtCore.QThread.msleep(10)
716
+
717
+ print(f'Timeout waiting for response: {expected_response}')
718
+ return None, None