moons-motor 0.0.11__tar.gz → 0.1.3__tar.gz

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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: moons_motor
3
- Version: 0.0.11
3
+ Version: 0.1.3
4
4
  Summary: This is a python library for controlling the Moons' motor through the serial port.
5
5
  Author-email: miroc <mike8503111@gmail.com>
6
6
  Project-URL: Repository, https://github.com/miroc99/moons_motor.git
@@ -16,6 +16,7 @@ Requires-Dist: pyserial
16
16
  Requires-Dist: rich
17
17
  Requires-Dist: python-socketio
18
18
  Requires-Dist: requests
19
+ Dynamic: license-file
19
20
 
20
21
  # Moons Motor
21
22
 
@@ -0,0 +1,398 @@
1
+ import serial
2
+ import serial.rs485
3
+ from serial.tools import list_ports
4
+ import re
5
+ import threading
6
+ from rich import print
7
+ from rich.console import Console
8
+ from rich.panel import Panel
9
+ import queue
10
+ from moons_motor.subject import Subject
11
+ import time
12
+ import threading
13
+
14
+ from dataclasses import dataclass
15
+
16
+
17
+ class StepperModules:
18
+ STM17S_3RN = "STM17S-3RN"
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class StepperCommand:
23
+ JOG: str = "CJ" # Start jogging
24
+ JOG_SPEED: str = "JS" # Jogging speed (Need to set before start jogging)
25
+ CHANGE_JOG_SPEED: str = "CS" # Change jogging speed while jogging
26
+ STOP_JOG: str = "SJ" # Stop jogging with deceleration
27
+ STOP: str = "ST" # Stop immediately (No deceleration)
28
+ STOP_DECEL: str = "STD" # Stop with deceleration
29
+ STOP_KILL: str = (
30
+ "SK" # Stop with deceleration(Control by AM) and kill all unexecuted commands
31
+ )
32
+ STOP_KILL_DECEL: str = (
33
+ "SKD" # Stop and kill all unexecuted commands with deceleration(Control by DE)
34
+ )
35
+ ENABLE: str = "ME" # Enable motor
36
+ DISABLE: str = "MD" # Disable motor
37
+ MOVE_ABSOLUTE: str = "FP" # Move to absolute position
38
+ MOVE_FIXED_DISTANCE: str = "FL" # Move to fixed distance
39
+ POSITION: str = "IP" # Motor absolute position(Calculated trajectory position)
40
+ TEMPERATURE: str = "IT" # Motor temperature
41
+ VOLTAGE: str = "IU" # Motor voltage
42
+
43
+ ENCODER_POSITION: str = "EP" # Encoder position
44
+ SET_POSITION: str = "SP" # Set encoder position
45
+
46
+ HOME: str = "SH" # Home position
47
+ VELOCITY: str = "VE" # Set velocity
48
+
49
+ ALARM_RESET: str = "AR" # Reset alarm
50
+
51
+ SET_RETURN_FORMAT_DECIMAL: str = "IFD" # Set return format to decimal
52
+ SET_RETURN_FORMAT_HEXADECIMAL: str = "IFH" # Set return format to hexadecimal
53
+
54
+ SET_TRANSMIT_DELAY: str = "TD" # Set transmit delay
55
+
56
+
57
+ class MoonsStepper(Subject):
58
+ motorAdress = [
59
+ "0",
60
+ "1",
61
+ "2",
62
+ "3",
63
+ "4",
64
+ "5",
65
+ "6",
66
+ "7",
67
+ "8",
68
+ "9",
69
+ "!",
70
+ '"',
71
+ "#",
72
+ "$",
73
+ "%",
74
+ "&",
75
+ "'",
76
+ "(",
77
+ ")",
78
+ "*",
79
+ "+",
80
+ ",",
81
+ "-",
82
+ ".",
83
+ "/",
84
+ ":",
85
+ ";",
86
+ "<",
87
+ "=",
88
+ ">",
89
+ "?",
90
+ "@",
91
+ ]
92
+
93
+ def __init__(
94
+ self,
95
+ model: StepperModules,
96
+ VID,
97
+ PID,
98
+ SERIAL_NUM,
99
+ only_simlate=False,
100
+ universe=0,
101
+ ):
102
+ super().__init__()
103
+ self.universe = universe
104
+ self.model = model # Motor model
105
+ self.only_simulate = only_simlate
106
+ self.device = "" # COM port description
107
+ self.VID = VID
108
+ self.PID = PID
109
+ self.SERIAL_NUM = SERIAL_NUM # ID for determent the deivice had same VID and PID, can be config using chips manufacturer tool
110
+ self.ser = None
111
+ self.Opened = False
112
+ self.recvQueue = queue.Queue()
113
+ self.sendQueue = queue.Queue()
114
+ self.pending_callbacks = queue.Queue()
115
+ self.update_thread = None
116
+ self.is_updating = False
117
+ self.readBuffer = ""
118
+
119
+ self.console = Console()
120
+
121
+ self.is_log_message = True
122
+
123
+ self.microstep = {
124
+ 0: 200,
125
+ 1: 400,
126
+ 3: 2000,
127
+ 4: 5000,
128
+ 5: 10000,
129
+ 6: 12800,
130
+ 7: 18000,
131
+ 8: 20000,
132
+ 9: 21600,
133
+ 10: 25000,
134
+ 11: 25400,
135
+ 12: 25600,
136
+ 13: 36000,
137
+ 14: 50000,
138
+ 15: 50800,
139
+ }
140
+
141
+ # region connection & main functions
142
+ @staticmethod
143
+ def list_all_ports():
144
+ ports = list(list_ports.comports())
145
+ simple_ports = []
146
+ port_info = ""
147
+ for p in ports:
148
+ port_info += f"■ {p.device} {p.description} [blue]{p.usb_info()}[/blue]"
149
+ if p != ports[-1]:
150
+ port_info += "\n"
151
+ simple_ports.append(p.description)
152
+ print(Panel(port_info, title="All COMPorts"))
153
+ return simple_ports
154
+
155
+ @staticmethod
156
+ def process_response(response):
157
+ equal_sign_index = response.index("=")
158
+ address = response[0]
159
+ command = response[1:equal_sign_index]
160
+ value = response[equal_sign_index + 1 :]
161
+
162
+ if command == "IT" or command == "IU":
163
+ # Handle temperature response
164
+ value = int(value) / 10.0
165
+ return {
166
+ "address": address,
167
+ "command": command,
168
+ "value": value,
169
+ }
170
+
171
+ def __start_update_thread(self):
172
+ self.update_thread = threading.Thread(target=self.update, daemon=True)
173
+ self.is_updating = True
174
+ self.update_thread.start()
175
+
176
+ def connect(self, COM=None, baudrate=9600, callback=None):
177
+ # Simulate mode
178
+ if self.only_simulate:
179
+ self.Opened = True
180
+ self.device = f"Simulate-{self.universe}"
181
+ print(f"{self.device} connected")
182
+ if callback:
183
+ callback(self.device, self.Opened)
184
+ return
185
+
186
+ def attempt_connect(COM, baudrate):
187
+ try:
188
+ # self.ser = serial.Serial(
189
+ # port=COM,
190
+ # baudrate=baudrate,
191
+ # bytesize=serial.EIGHTBITS,
192
+ # parity=serial.PARITY_NONE,
193
+ # stopbits=serial.STOPBITS_ONE,
194
+ # timeout=0.5,
195
+ # )
196
+ self.ser = serial.rs485.RS485(port=COM, baudrate=baudrate)
197
+ self.ser.rs485_mode = serial.rs485.RS485Settings(
198
+ rts_level_for_tx=True,
199
+ rts_level_for_rx=False,
200
+ loopback=False,
201
+ delay_before_tx=0.02,
202
+ delay_before_rx=0.02,
203
+ )
204
+ if self.ser is None:
205
+ self.Opened = False
206
+ if self.ser.is_open:
207
+ self.Opened = True
208
+ print(f"[bold green]Device connected[/bold green]: {self.device}")
209
+
210
+ except Exception as e:
211
+ print(f"[bold red]Device error:[/bold red] {e} ")
212
+ self.Opened = False
213
+
214
+ ports = list(list_ports.comports())
215
+ if COM is not None and not self.only_simulate:
216
+ attempt_connect(COM, baudrate)
217
+ if callback:
218
+ callback(self.device, self.Opened)
219
+ else:
220
+ for p in ports:
221
+ m = re.match(
222
+ r"USB\s*VID:PID=(\w+):(\w+)\s*SER=([A-Za-z0-9]*)", p.usb_info()
223
+ )
224
+ print(p.usb_info())
225
+ if (
226
+ m
227
+ and m.group(1) == self.VID
228
+ and m.group(2) == self.PID
229
+ # and m.group(3) == self.SERIAL_NUM
230
+ ):
231
+ if m.group(3) == self.SERIAL_NUM or self.SERIAL_NUM == "":
232
+ print(
233
+ f"[bold yellow]Device founded:[/bold yellow] {p.description} | VID: {m.group(1)} | PID: {m.group(2)} | SER: {m.group(3)}"
234
+ )
235
+
236
+ self.device = p.description
237
+
238
+ attempt_connect(p.device, baudrate)
239
+
240
+ break
241
+
242
+ if self.only_simulate:
243
+ self.device = "Simulate"
244
+ self.Opened = True
245
+ time.sleep(0.5)
246
+ self.__start_update_thread()
247
+ if callback:
248
+ callback(self.device, self.Opened)
249
+
250
+ if not self.Opened:
251
+ print(f"[bold red]Device not found[/bold red]")
252
+ if callback:
253
+ callback(self.device, self.Opened)
254
+
255
+ def disconnect(self):
256
+ self.send_command(command=StepperCommand.STOP_KILL)
257
+ time.sleep(0.5)
258
+ self.sendQueue.queue.clear()
259
+ self.recvQueue.queue.clear()
260
+ self.is_updating = False
261
+ self.update_thread = None
262
+ if self.only_simulate:
263
+ self.listen = False
264
+ self.is_sending = False
265
+ self.Opened = False
266
+ print(f"Simulate-{self.universe} disconnected")
267
+ return
268
+ if self.ser is not None and self.ser.is_open:
269
+ self.listen = False
270
+ self.is_sending = False
271
+ self.Opened = False
272
+ self.ser.flush()
273
+ self.ser.close()
274
+ print(f"[bold red]Device disconnected[/bold red]: {self.device}")
275
+
276
+ def send(self, command, eol=b"\r"):
277
+ if (self.ser != None and self.ser.is_open) or self.only_simulate:
278
+ self.temp_cmd = command + "\r"
279
+
280
+ if self.ser is not None or not self.only_simulate:
281
+ self.ser.write(self.temp_cmd.encode("ascii"))
282
+ if self.is_log_message:
283
+ print(
284
+ f"[bold green]Send to {self.device}:[/bold green] {self.temp_cmd}"
285
+ )
286
+ super().notify_observers(f"{self.universe}-{self.temp_cmd}")
287
+ else:
288
+ print(f"Target device is not opened. Command: {command}")
289
+
290
+ def send_command(self, address="", command="", value=None):
291
+ if command == "":
292
+ print("Command can't be empty")
293
+ return
294
+ if value is not None:
295
+ command = self.addressed_cmd(address, command + str(value))
296
+ else:
297
+ command = self.addressed_cmd(address, command)
298
+
299
+ self.sendQueue.put_nowait(command)
300
+
301
+ def update(self):
302
+
303
+ while self.is_updating:
304
+ if self.ser is not None:
305
+ if self.ser.in_waiting > 0:
306
+ response = self.ser.read(self.ser.in_waiting)
307
+ response = response.decode("ascii", errors="ignore").strip()
308
+ response = response.split("\r")
309
+
310
+ for r in response:
311
+ if r != "":
312
+ self.readBuffer += r
313
+ self.handle_recv(r)
314
+
315
+ if self.sendQueue.empty() != True:
316
+ # time.sleep(
317
+ # 0.02
318
+ # ) # Time for RS485 converter to switch between Transmit and Receive mode
319
+ while not self.sendQueue.empty():
320
+ # time.sleep(
321
+ # 0.05
322
+ # ) # Time for RS485 converter to switch between Transmit and Receive mode
323
+ cmd = self.sendQueue.get_nowait()
324
+ self.send(cmd)
325
+ self.sendQueue.task_done()
326
+
327
+ def handle_recv(self, response):
328
+ if "*" in response:
329
+ print(f"[bold green](o)buffered_ack[/bold green]")
330
+ elif "%" in response:
331
+ print(f"[bold green](v)success_ack[/bold green]")
332
+ elif "?" in response:
333
+ print(f"[bold red](x)fail_ack[/bold red]")
334
+ else:
335
+ print(f"[bold blue]Received from {self.device}: [/bold blue]", response)
336
+ self.recvQueue.put_nowait(response)
337
+
338
+ if "=" in response:
339
+ callback = self.pending_callbacks.get_nowait()
340
+ if callback:
341
+ callback(response)
342
+ # for command, callback in list(self.pending_callbacks.items()):
343
+ # if command in response:
344
+ # if callback:
345
+ # callback(response)
346
+ # del self.pending_callbacks[command]
347
+ # break
348
+
349
+ # endregion
350
+
351
+ # region motor motion functions
352
+
353
+ # def setup_motor(self, motor_address="", kill=False):
354
+ # if kill:
355
+ # self.stop_and_kill(motor_address)
356
+ # self.set_transmit_delay(motor_address, 25)
357
+ # self.set_return_format_dexcimal(motor_address)
358
+
359
+ def home(self, motor_address="", speed=0.3, onComplete=None):
360
+ self.send_command(
361
+ address=motor_address, command=StepperCommand.VELOCITY, value=speed
362
+ )
363
+ self.send_command(
364
+ address=motor_address, command=StepperCommand.HOME, value="3F"
365
+ )
366
+ self.send_command(
367
+ address=motor_address, command=StepperCommand.ENCODER_POSITION
368
+ )
369
+ self.send_command(
370
+ address=motor_address, command=StepperCommand.SET_POSITION, value=0
371
+ )
372
+ if onComplete:
373
+ self.get_status(
374
+ motor_address, StepperCommand.SET_POSITION, callback=onComplete
375
+ )
376
+
377
+ # endregion
378
+ def get_status(self, motor_address, command: StepperCommand, callback=None):
379
+ command = self.addressed_cmd(motor_address, command)
380
+ if callback:
381
+ self.pending_callbacks.put_nowait(callback)
382
+ self.sendQueue.put_nowait(command)
383
+
384
+ # endregion
385
+
386
+ # region utility functions
387
+
388
+ def addressed_cmd(self, motor_address, command):
389
+ return f"{motor_address}{command}"
390
+
391
+
392
+ # endregion
393
+
394
+ # SERIAL => 上次已知父系(尾巴+A) 或是事件分頁
395
+ # reg USB\s*VID:PID=(\w+):(\w+)\s*SER=([A-Za-z0-9]+)
396
+
397
+ # serial_num 裝置例項路徑
398
+ # TD(Tramsmit Delay) = 15
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: moons_motor
3
- Version: 0.0.11
3
+ Version: 0.1.3
4
4
  Summary: This is a python library for controlling the Moons' motor through the serial port.
5
5
  Author-email: miroc <mike8503111@gmail.com>
6
6
  Project-URL: Repository, https://github.com/miroc99/moons_motor.git
@@ -16,6 +16,7 @@ Requires-Dist: pyserial
16
16
  Requires-Dist: rich
17
17
  Requires-Dist: python-socketio
18
18
  Requires-Dist: requests
19
+ Dynamic: license-file
19
20
 
20
21
  # Moons Motor
21
22
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "moons_motor"
7
- version = "0.0.11"
7
+ version = "0.1.3"
8
8
  authors = [{ name = "miroc", email = "mike8503111@gmail.com" }]
9
9
  description = "This is a python library for controlling the Moons' motor through the serial port."
10
10
  readme = "README.md"
@@ -1,466 +0,0 @@
1
- import serial
2
- from serial.tools import list_ports
3
- import re
4
- import threading
5
- from rich import print
6
- from rich.console import Console
7
- from rich.panel import Panel
8
- import queue
9
- from moons_motor.subject import Subject
10
- import time
11
-
12
-
13
- class StepperModules:
14
- STM17S_3RN = "STM17S-3RN"
15
-
16
-
17
- class MoonsStepper(Subject):
18
- motorAdress = [
19
- "0",
20
- "1",
21
- "2",
22
- "3",
23
- "4",
24
- "5",
25
- "6",
26
- "7",
27
- "8",
28
- "9",
29
- "!",
30
- '"',
31
- "#",
32
- "$",
33
- "%",
34
- "&",
35
- "'",
36
- "(",
37
- ")",
38
- "*",
39
- "+",
40
- ",",
41
- "-",
42
- ".",
43
- "/",
44
- ":",
45
- ";",
46
- "<",
47
- "=",
48
- ">",
49
- "?",
50
- "@",
51
- ]
52
-
53
- def __init__(
54
- self,
55
- model: StepperModules,
56
- VID,
57
- PID,
58
- SERIAL_NUM,
59
- only_simlate=False,
60
- universe=0,
61
- ):
62
- super().__init__()
63
- self.universe = universe
64
- self.model = model
65
- self.only_simulate = only_simlate
66
- self.device = ""
67
- self.VID = VID
68
- self.PID = PID
69
- self.SERIAL_NUM = SERIAL_NUM
70
- self.ser = None
71
- self.listeningBuffer = ""
72
- self.listeningBufferPre = ""
73
- self.transmitDelay = 0.010
74
- self.lock = False
75
- self.Opened = False
76
- self.new_data_event = threading.Event()
77
- self.new_value_event = threading.Event()
78
- self.on_send_event = threading.Event()
79
- self.recvQueue = queue.Queue()
80
- self.sendQueue = queue.Queue()
81
- self.command_cache = queue.Queue()
82
- self.usedSendQueue = queue.Queue()
83
-
84
- self.console = Console()
85
-
86
- self.is_log_message = True
87
-
88
- self.microstep = {
89
- 0: 200,
90
- 1: 400,
91
- 3: 2000,
92
- 4: 5000,
93
- 5: 10000,
94
- 6: 12800,
95
- 7: 18000,
96
- 8: 20000,
97
- 9: 21600,
98
- 10: 25000,
99
- 11: 25400,
100
- 12: 25600,
101
- 13: 36000,
102
- 14: 50000,
103
- 15: 50800,
104
- }
105
-
106
- # region connection & main functions
107
- @staticmethod
108
- def list_all_ports():
109
- ports = list(list_ports.comports())
110
- simple_ports = []
111
- port_info = ""
112
- for p in ports:
113
- port_info += f"■ {p.device} {p.description} [blue]{p.usb_info()}[/blue]"
114
- if p != ports[-1]:
115
- port_info += "\n"
116
- simple_ports.append(p.description)
117
- print(Panel(port_info, title="All COMPorts"))
118
- return simple_ports
119
-
120
- def connect(self, COM=None, baudrate=9600, callback=None):
121
- if self.only_simulate:
122
- self.Opened = True
123
- self.device = f"Simulate-{self.universe}"
124
- print(f"{self.device} connected")
125
- if callback:
126
- callback(self.device, self.Opened)
127
- return
128
-
129
- def attempt_connect(COM, baudrate):
130
- try:
131
- self.ser = serial.Serial(COM, baudrate)
132
- if self.ser is None:
133
- # print("> Device not found")
134
- self.Opened = False
135
- if self.ser.is_open:
136
- # print(f"Device: {self.device} | COM: {COM} connected")
137
- self.Opened = True
138
- except:
139
- print("> Device error")
140
- self.Opened = False
141
-
142
- if COM is not None and not self.only_simulate:
143
- attempt_connect(COM, baudrate)
144
- if callback:
145
- callback(self.device, self.Opened)
146
- return
147
- ports = list(list_ports.comports())
148
- for p in ports:
149
- m = re.match(
150
- r"USB\s*VID:PID=(\w+):(\w+)\s*SER=([A-Za-z0-9]*)", p.usb_info()
151
- )
152
- print(m, p.usb_info())
153
- if (
154
- m
155
- and m.group(1) == self.VID
156
- and m.group(2) == self.PID
157
- # and m.group(3) == self.SERIAL_NUM
158
- ):
159
- print("find vid pid match")
160
- if m.group(3) == self.SERIAL_NUM or self.SERIAL_NUM == "":
161
- print(
162
- f"Device: {p.description} | VID: {m.group(1)} | PID: {m.group(2)} | SER: {m.group(3)} connected"
163
- )
164
-
165
- self.device = p.description
166
-
167
- attempt_connect(p.device, baudrate)
168
- if callback:
169
- callback(self.device, self.Opened)
170
- break
171
- break
172
-
173
- if self.only_simulate:
174
- self.device = "Simulate"
175
- self.Opened = True
176
- if not self.Opened:
177
- print("> Device not found")
178
- if callback:
179
- callback(self.device, self.Opened)
180
-
181
- def disconnect(self):
182
- if self.only_simulate:
183
- self.listen = False
184
- self.is_sending = False
185
- self.Opened = False
186
- print(f"Simulate-{self.universe} disconnected")
187
- return
188
- if self.ser is not None and self.ser.is_open:
189
- self.listen = False
190
- self.is_sending = False
191
- self.Opened = False
192
- self.ser.flush()
193
- self.ser.close()
194
- print(f"{self.device} Disconnected")
195
-
196
- def send(self, command, eol=b"\r"):
197
- if (self.ser != None and self.ser.is_open) or self.only_simulate:
198
- self.temp_cmd = command + "\r"
199
-
200
- if "~" in self.temp_cmd:
201
- # remove ~ in self.temp_cmd
202
- self.temp_cmd = self.temp_cmd[1:]
203
- else:
204
- self.usedSendQueue.put(self.temp_cmd)
205
- if self.ser is not None or not self.only_simulate:
206
- self.temp_cmd += "\r"
207
- self.ser.write(self.temp_cmd.encode("ascii"))
208
- if self.is_log_message:
209
- print(
210
- f"[bold green]Send to {self.device}:[/bold green] {self.temp_cmd}"
211
- )
212
- super().notify_observers(f"{self.universe}-{self.temp_cmd}")
213
- else:
214
- print(f"Target device is not opened. Command: {command}")
215
-
216
- def read(self, timeout=1):
217
- if self.ser is not None and self.ser.is_open:
218
- print("reading...")
219
- try:
220
- start_time = time.time()
221
- while time.time() - start_time < timeout:
222
- if self.ser.in_waiting > 0:
223
- response = self.ser.read(self.ser.in_waiting)
224
- response = response.decode("utf-8").strip()
225
- if self.is_log_message:
226
- print(
227
- f"[bold blue]Recv from {self.device} :[/bold blue] {response}"
228
- )
229
- return response
230
- time.sleep(0.01)
231
- print("reading timeout")
232
- return None
233
- except Exception as e:
234
- print(f"Error when reading serial port: {str(e)}")
235
- return None
236
- elif self.only_simulate:
237
- simulated_response = "simulate response"
238
- if self.is_log_message:
239
- print(
240
- f"[bold blue]Recv from simulate device:[/bold blue] {simulated_response}"
241
- )
242
- return simulated_response
243
- else:
244
- print("Device not open, read fail.")
245
- return None
246
-
247
- # endregion
248
-
249
- # region motor motion functions
250
- def enable(self, motor_address="", enable=True):
251
- cmd = "ME" if enable else "MD"
252
- self.send(self.addressed_cmd(motor_address, cmd))
253
-
254
- def move_absolute(self, motor_address="", position=0, speed=0.15):
255
- self.send(self.addressed_cmd(motor_address, f"VE{speed}"))
256
- self.send(self.addressed_cmd(motor_address, f"FP{position}"))
257
-
258
- def move_fixed_distance(self, motor_address="", distance=100, speed=0.15):
259
- self.send(self.addressed_cmd(motor_address, "VE{}".format(speed)))
260
- self.send(self.addressed_cmd(motor_address, "FL{}".format(int(distance))))
261
-
262
- def start_jog(self, motor_address="", speed=0.15, direction="CW"):
263
- self.send(self.addressed_cmd(motor_address, "JS{}".format(speed)))
264
- time.sleep(0.01)
265
- self.send(self.addressed_cmd(motor_address, "CJ"))
266
- # self.send(self.addressed_cmd(motor_address, "CS{}".format(speed)))
267
-
268
- def change_jog_speed(self, motor_address="", speed=0.15):
269
- self.send(self.addressed_cmd(motor_address, "CS{}".format(speed)))
270
-
271
- def stop_jog(self, motor_address=""):
272
- self.send(self.addressed_cmd(motor_address, "SJ"))
273
-
274
- def stop(self, motor_address=""):
275
- self.send(self.addressed_cmd(motor_address, "ST"))
276
-
277
- def stop_with_deceleration(self, motor_address=""):
278
- self.send(self.addressed_cmd(motor_address, "STD"))
279
-
280
- def stop_and_kill(self, motor_address="", with_deceleration=True):
281
- if with_deceleration:
282
- self.send(self.addressed_cmd(motor_address, "SKD"))
283
- else:
284
- self.send(self.addressed_cmd(motor_address, "SK"))
285
-
286
- def setup_motor(self, motor_address="", kill=False):
287
- if kill:
288
- self.stop_and_kill(motor_address)
289
- self.set_transmit_delay(motor_address, 25)
290
- self.set_return_format_dexcimal(motor_address)
291
-
292
- def calibrate(self, motor_address="", speed=0.3, onStart=None, onComplete=None):
293
- self.send(self.addressed_cmd(motor_address, "VE{}".format(speed)))
294
- # time.sleep(0.01)
295
- # self.send(self.addressed_cmd(motor_address, "DI10"))
296
- # time.sleep(0.01)
297
- self.send(self.addressed_cmd(motor_address, "SH3F"))
298
- # time.sleep(0.01)
299
- self.send(self.addressed_cmd(motor_address, "EP0"))
300
- # time.sleep(0.01)
301
- self.send(self.addressed_cmd(motor_address, "SP0"))
302
-
303
- def alarm_reset(self, motor_address=""):
304
- self.send(self.addressed_cmd(motor_address, "AR"))
305
-
306
- # speed slow= 0.25, medium=1, fast=5
307
- def set_transmit_delay(self, motor_address="", delay=15):
308
- self.send(self.addressed_cmd(motor_address, "TD{}".format(delay)))
309
-
310
- # endregion
311
-
312
- # region motor status functions
313
- def get_position(self, motor_address):
314
- self.send(self.addressed_cmd(motor_address, "IP"))
315
- return self.read()
316
- # self.new_value_event.wait(timeout=0.5)
317
- # return self.get_value()
318
-
319
- def get_temperature(self, motor_address):
320
- self.send(self.addressed_cmd(motor_address, "IT"))
321
- # self.new_value_event.wait(timeout=0.5)
322
- return self.read()
323
- # return int(self.get_value()) / 10
324
-
325
- def get_sensor_status(self, motor_address):
326
- self.send(self.addressed_cmd(motor_address, "IS"))
327
- return self.read()
328
- # self.new_value_event.wait(timeout=0.5)
329
- # return self.get_value()
330
-
331
- def get_votalge(self, motor_address):
332
- self.send(self.addressed_cmd(motor_address, "IU"))
333
- return self.read()
334
- # self.new_value_event.wait(timeout=0.5)
335
- # return self.get_value()
336
-
337
- def get_acceleration(self, motor_address):
338
- self.send(self.addressed_cmd(motor_address, "AC"))
339
- return self.read()
340
- # self.new_value_event.wait(timeout=0.5)
341
- # return self.get_value()
342
-
343
- def get_deceleration(self, motor_address):
344
- self.send(self.addressed_cmd(motor_address, "DE"))
345
- return self.read()
346
- # self.new_value_event.wait(timeout=0.5)
347
- # return self.get_value()
348
-
349
- def get_velocity(self, motor_address):
350
- self.send(self.addressed_cmd(motor_address, "VE"))
351
- return self.read()
352
- # self.new_value_event.wait(timeout=0.5)
353
- # return self.get_value()
354
-
355
- def get_distance(self, motor_address):
356
- self.send(self.addressed_cmd(motor_address, "DI"))
357
- return self.read()
358
- # self.new_value_event.wait(timeout=0.5)
359
- # return self.get_value()
360
-
361
- def get_jog_speed(self, motor_address):
362
- self.send(self.addressed_cmd(motor_address, "JS"))
363
- # self.new_value_event.wait(timeout=0.5)
364
- # return self.get_value()
365
- return self.read()
366
-
367
- def get_info(self, motor_address, progress=None):
368
- self.set_return_format_dexcimal(motor_address)
369
- self.motor_wait(motor_address, 0.1)
370
- totalInfoCount = 7
371
- pos = self.extractValueFromResponse(self.get_position(motor_address))
372
- if progress:
373
- progress(round(1 / totalInfoCount, 1))
374
- temp = (
375
- int(self.extractValueFromResponse(self.get_temperature(motor_address))) / 10
376
- )
377
- if progress:
378
- progress(round(2 / totalInfoCount, 1))
379
- vol = int(self.extractValueFromResponse(self.get_votalge(motor_address))) / 10
380
- if progress:
381
- progress(round(3 / totalInfoCount, 1))
382
- accel = self.extractValueFromResponse(self.get_acceleration(motor_address))
383
- if progress:
384
- progress(round(4 / totalInfoCount, 1))
385
- decel = self.extractValueFromResponse(self.get_deceleration(motor_address))
386
- if progress:
387
- progress(round(5 / totalInfoCount, 1))
388
- jogsp = self.extractValueFromResponse(self.get_jog_speed(motor_address))
389
- if progress:
390
- progress(round(6 / totalInfoCount, 1))
391
- info = {
392
- "pos": pos,
393
- "temp": temp,
394
- "vol": vol,
395
- "accel": accel,
396
- "decel": decel,
397
- "jogsp": jogsp,
398
- }
399
- if progress:
400
- progress(round(7 / totalInfoCount))
401
-
402
- return info
403
-
404
- def get_status(self, motor_address) -> str:
405
- self.set_return_format_dexcimal(motor_address)
406
- self.send(self.addressed_cmd(motor_address, "RS"))
407
- self.new_value_event.wait(timeout=0.5)
408
- return str(self.get_value())
409
-
410
- def set_return_format_dexcimal(self, motor_address):
411
- self.send(self.addressed_cmd(motor_address, "IFD"))
412
-
413
- def set_return_format_hexdecimal(self, motor_address):
414
- self.send(self.addressed_cmd(motor_address, "IFH"))
415
-
416
- # endregion
417
-
418
- # region utility functions
419
- def motor_wait(self, motor_address, wait_time):
420
- self.send(self.addressed_cmd(motor_address, "WT{}".format(wait_time)))
421
-
422
- def addressed_cmd(self, motor_address, command):
423
- if motor_address == "":
424
- return f"~{command}"
425
- return f"{motor_address}{command}"
426
-
427
- def extractValueFromResponse(self, response):
428
- pattern = r"=(.*)"
429
- if response == None:
430
- return None
431
- result = re.search(pattern, response)
432
- if result:
433
- return result.group(1)
434
- else:
435
- return None
436
-
437
- def get_value(self):
438
- print("Waiting for value")
439
- self.new_data_event.wait(timeout=0.5)
440
- print("Recv:" + self.listeningBufferPre)
441
- self.new_data_event.clear()
442
- return self.listeningBufferPre
443
- # if "%" in self.listeningBufferPre:
444
- # return "success_ack"
445
- # if "?" in self.listeningBufferPre:
446
- # return "fail_ack"
447
- # if "*" in self.listeningBufferPre:
448
- # return "buffered_ack"
449
- # self.new_value_event.set()
450
- # pattern = r"=(\w+(?:\.\w+)?|\d+(?:\.\d+)?)"
451
- # result = re.search(pattern, self.listeningBufferPre)
452
- # self.listeningBufferPre = ""
453
- # self.new_value_event.clear()
454
- # if result:
455
- # return result.group(1)
456
- # else:
457
- # return "No_value_found"
458
-
459
-
460
- # endregion
461
-
462
- # SERIAL => 上次已知父系(尾巴+A) 或是事件分頁
463
- # reg USB\s*VID:PID=(\w+):(\w+)\s*SER=([A-Za-z0-9]+)
464
-
465
- # serial_num 裝置例項路徑
466
- # TD(Tramsmit Delay) = 15
File without changes
File without changes
File without changes
File without changes