moons-motor 0.0.1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
moons_motor/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
from moons_motor import *
|
@@ -0,0 +1,649 @@
|
|
1
|
+
import serial
|
2
|
+
from serial.tools import list_ports
|
3
|
+
import re
|
4
|
+
import time
|
5
|
+
import threading
|
6
|
+
import socketio
|
7
|
+
from rich import print
|
8
|
+
from rich.console import Console
|
9
|
+
from rich.panel import Panel
|
10
|
+
import queue
|
11
|
+
|
12
|
+
|
13
|
+
class moons_stepper_status:
|
14
|
+
def __init__(self, address=""):
|
15
|
+
|
16
|
+
# info
|
17
|
+
self.address = ""
|
18
|
+
self.position = 0 # IP
|
19
|
+
self.temperature = 0 # IT
|
20
|
+
self.sensor_status = 0 # IS
|
21
|
+
self.voltage = 0 # IU
|
22
|
+
self.acceleration = 0 # AC
|
23
|
+
self.deceleration = 0 # DE
|
24
|
+
self.velocity = 0 # VE
|
25
|
+
self.distance = 0 # DI
|
26
|
+
self.jog_speed = 0 # JS
|
27
|
+
|
28
|
+
# status
|
29
|
+
self.status_string = ""
|
30
|
+
self.alarm = False
|
31
|
+
self.disabled = False
|
32
|
+
self.drive_fault = False
|
33
|
+
self.moving = False
|
34
|
+
self.homing = False
|
35
|
+
self.jogging = False
|
36
|
+
self.motion_in_progess = False
|
37
|
+
self.ready = False
|
38
|
+
self.stoping = False
|
39
|
+
self.waiting = False
|
40
|
+
|
41
|
+
def update_info(self, info: dict) -> bool:
|
42
|
+
if info is None or len(info) < 1:
|
43
|
+
print("Update failed: input is None")
|
44
|
+
return False
|
45
|
+
self.position = info["pos"]
|
46
|
+
self.temperature = info["temp"]
|
47
|
+
self.sensor_status = info["sensor"]
|
48
|
+
self.voltage = info["vol"]
|
49
|
+
self.acceleration = info["accel"]
|
50
|
+
self.deceleration = info["decel"]
|
51
|
+
self.velocity = info["vel"]
|
52
|
+
self.distance = info["dis"]
|
53
|
+
self.jog_speed = info["jogsp"]
|
54
|
+
return True
|
55
|
+
|
56
|
+
def update_status(self, status_string) -> bool:
|
57
|
+
if status_string == None and status_string == "":
|
58
|
+
print("Update failed: input is empty or None")
|
59
|
+
return False
|
60
|
+
self.status_string = status_string
|
61
|
+
self.alarm = "A" in status_string
|
62
|
+
self.disabled = "D" in status_string
|
63
|
+
self.drive_fault = "E" in status_string
|
64
|
+
self.moving = "F" in status_string
|
65
|
+
self.homing = "H" in status_string
|
66
|
+
self.jogging = "J" in status_string
|
67
|
+
self.motion_in_progess = "M" in status_string
|
68
|
+
self.ready = "R" in status_string
|
69
|
+
self.stoping = "S" in status_string
|
70
|
+
self.waiting = "T" in status_string
|
71
|
+
return True
|
72
|
+
|
73
|
+
def get_info(self) -> str:
|
74
|
+
return f"""
|
75
|
+
Position: {self.position}
|
76
|
+
Temperature: {self.temperature}
|
77
|
+
Sensor Status: {self.sensor_status}
|
78
|
+
Voltage: {self.voltage}
|
79
|
+
Acceleration: {self.acceleration}
|
80
|
+
Deceleration: {self.deceleration}
|
81
|
+
Velocity: {self.velocity}
|
82
|
+
Distance: {self.distance}
|
83
|
+
Jog Speed: {self.jog_speed}"""
|
84
|
+
|
85
|
+
def get_status(self) -> str:
|
86
|
+
return f"""
|
87
|
+
Alarm: {self.alarm}
|
88
|
+
Disabled: {self.disabled}
|
89
|
+
Drive Fault: {self.drive_fault}
|
90
|
+
Moving: {self.moving}
|
91
|
+
Homing: {self.homing}
|
92
|
+
Jogging: {self.jogging}
|
93
|
+
Motion in Progress: {self.motion_in_progess}
|
94
|
+
Ready: {self.ready}
|
95
|
+
Stoping: {self.stoping}
|
96
|
+
Waiting: {self.waiting}"""
|
97
|
+
|
98
|
+
|
99
|
+
class moons_stepper:
|
100
|
+
motorAdress = [
|
101
|
+
"0",
|
102
|
+
"1",
|
103
|
+
"2",
|
104
|
+
"3",
|
105
|
+
"4",
|
106
|
+
"5",
|
107
|
+
"6",
|
108
|
+
"7",
|
109
|
+
"8",
|
110
|
+
"9",
|
111
|
+
"!",
|
112
|
+
'"',
|
113
|
+
"#",
|
114
|
+
"$",
|
115
|
+
"%",
|
116
|
+
"&",
|
117
|
+
"'",
|
118
|
+
"(",
|
119
|
+
")",
|
120
|
+
"*",
|
121
|
+
"+",
|
122
|
+
",",
|
123
|
+
"-",
|
124
|
+
".",
|
125
|
+
"/",
|
126
|
+
":",
|
127
|
+
";",
|
128
|
+
"<",
|
129
|
+
"=",
|
130
|
+
">",
|
131
|
+
"?",
|
132
|
+
"@",
|
133
|
+
]
|
134
|
+
|
135
|
+
def __init__(
|
136
|
+
self,
|
137
|
+
model,
|
138
|
+
VID,
|
139
|
+
PID,
|
140
|
+
SERIAL_NUM,
|
141
|
+
only_simlate=False,
|
142
|
+
universe=0,
|
143
|
+
simulate_ip="localhost",
|
144
|
+
simulate_port=3001,
|
145
|
+
):
|
146
|
+
self.universe = universe
|
147
|
+
self.model = model
|
148
|
+
self.only_simulate = only_simlate
|
149
|
+
self.device = ""
|
150
|
+
self.VID = VID
|
151
|
+
self.PID = PID
|
152
|
+
self.SERIAL_NUM = SERIAL_NUM
|
153
|
+
self.ser = None
|
154
|
+
self.listeningBuffer = ""
|
155
|
+
self.listeningBufferPre = ""
|
156
|
+
self.transmitDelay = 0.010
|
157
|
+
self.lock = False
|
158
|
+
self.Opened = False
|
159
|
+
self.new_data_event = threading.Event()
|
160
|
+
self.new_value_event = threading.Event()
|
161
|
+
self.on_send_event = threading.Event()
|
162
|
+
self.recvQueue = queue.Queue()
|
163
|
+
self.sendQueue = queue.Queue()
|
164
|
+
self.command_cache = queue.Queue()
|
165
|
+
self.usedSendQueue = queue.Queue()
|
166
|
+
self.simulator = moons_stepper_simulate(
|
167
|
+
self,
|
168
|
+
universe=universe,
|
169
|
+
server_address=f"http://{simulate_ip}:{simulate_port}",
|
170
|
+
)
|
171
|
+
|
172
|
+
self.console = Console()
|
173
|
+
|
174
|
+
self.is_log_message = True
|
175
|
+
|
176
|
+
self.microstep = {
|
177
|
+
0: 200,
|
178
|
+
1: 400,
|
179
|
+
3: 2000,
|
180
|
+
4: 5000,
|
181
|
+
5: 10000,
|
182
|
+
6: 12800,
|
183
|
+
7: 18000,
|
184
|
+
8: 20000,
|
185
|
+
9: 21600,
|
186
|
+
10: 25000,
|
187
|
+
11: 25400,
|
188
|
+
12: 25600,
|
189
|
+
13: 36000,
|
190
|
+
14: 50000,
|
191
|
+
15: 50800,
|
192
|
+
}
|
193
|
+
self.simulator.connect()
|
194
|
+
|
195
|
+
# region connection & main functions
|
196
|
+
@staticmethod
|
197
|
+
def list_all_ports():
|
198
|
+
# print("░░░░░░░░░░░░░░░░░░░ All COMPorts ░░░░░░░░░░░░░░░░░░░░░\n")
|
199
|
+
ports = list(list_ports.comports())
|
200
|
+
simple_ports = []
|
201
|
+
port_info = ""
|
202
|
+
for p in ports:
|
203
|
+
port_info += f"■ {p.device} {p.description} [blue]{p.usb_info()}[/blue]"
|
204
|
+
if p != ports[-1]:
|
205
|
+
port_info += "\n"
|
206
|
+
simple_ports.append(p.description)
|
207
|
+
# print("\n░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░")
|
208
|
+
print(Panel(port_info, title="All COMPorts"))
|
209
|
+
return simple_ports
|
210
|
+
|
211
|
+
def connect(self, COM=None, baudrate=9600, callback=None):
|
212
|
+
if self.only_simulate:
|
213
|
+
self.Opened = True
|
214
|
+
self.device = f"Simulate-{self.universe}"
|
215
|
+
print(f"{self.device} connected")
|
216
|
+
if callback:
|
217
|
+
callback(self.device, self.Opened)
|
218
|
+
return
|
219
|
+
|
220
|
+
def attempt_connect(COM, baudrate):
|
221
|
+
try:
|
222
|
+
self.ser = serial.Serial(COM, baudrate)
|
223
|
+
if self.ser is None:
|
224
|
+
print("> Device not found")
|
225
|
+
self.Opened = False
|
226
|
+
if self.ser.is_open:
|
227
|
+
print(f"Device: {self.device} | COM: {COM} connected")
|
228
|
+
self.Opened = True
|
229
|
+
except:
|
230
|
+
print("> Device error")
|
231
|
+
self.Opened = False
|
232
|
+
|
233
|
+
if COM is not None and not self.only_simulate:
|
234
|
+
attempt_connect(COM, baudrate)
|
235
|
+
if callback:
|
236
|
+
callback(self.device, self.Opened)
|
237
|
+
return
|
238
|
+
ports = list(list_ports.comports())
|
239
|
+
for p in ports:
|
240
|
+
m = re.match(
|
241
|
+
r"USB\s*VID:PID=(\w+):(\w+)\s*SER=([A-Za-z0-9]+)", p.usb_info()
|
242
|
+
)
|
243
|
+
if (
|
244
|
+
m
|
245
|
+
and m.group(1) == self.VID
|
246
|
+
and m.group(2) == self.PID
|
247
|
+
and m.group(3) == self.SERIAL_NUM
|
248
|
+
):
|
249
|
+
print(
|
250
|
+
f"Device: {p.description} | VID: {m.group(1)} | PID: {m.group(2)} | SER: {m.group(3)} connected"
|
251
|
+
)
|
252
|
+
|
253
|
+
self.device = p.description
|
254
|
+
|
255
|
+
attempt_connect(p.device, baudrate)
|
256
|
+
if callback:
|
257
|
+
callback(self.device, self.Opened)
|
258
|
+
break
|
259
|
+
# try:
|
260
|
+
# self.ser = serial.Serial(p.device, baudrate)
|
261
|
+
# except Exception as e:
|
262
|
+
# print(f"Permission Error: {e}")
|
263
|
+
# self.Opened = False
|
264
|
+
# return
|
265
|
+
|
266
|
+
# self.listeningThread = threading.Thread(target=self.listening)
|
267
|
+
# self.sendingThread = threading.Thread(target=self.sending)
|
268
|
+
# self.listeningThread.daemon = True
|
269
|
+
# self.sendingThread.daemon = True
|
270
|
+
# self.listeningThread.start()
|
271
|
+
# self.sendingThread.start()
|
272
|
+
|
273
|
+
break
|
274
|
+
|
275
|
+
if self.only_simulate:
|
276
|
+
self.device = "Simulate"
|
277
|
+
self.Opened = True
|
278
|
+
if not self.Opened:
|
279
|
+
print("> Device not found")
|
280
|
+
if callback:
|
281
|
+
callback(self.device, self.Opened)
|
282
|
+
# self.sendingThread = threading.Thread(target=self.sending)
|
283
|
+
# self.sendingThread.daemon = True
|
284
|
+
# self.sendingThread.start()
|
285
|
+
# self.device = "Target device"
|
286
|
+
# if self.ser is None and not self.only_simlate:
|
287
|
+
# print("> Device not found")
|
288
|
+
# self.Opened = False
|
289
|
+
|
290
|
+
def disconnect(self):
|
291
|
+
if self.only_simulate:
|
292
|
+
self.listen = False
|
293
|
+
self.is_sending = False
|
294
|
+
self.Opened = False
|
295
|
+
print(f"Simulate-{self.universe} disconnected")
|
296
|
+
return
|
297
|
+
if self.ser is not None and self.ser.is_open:
|
298
|
+
self.listen = False
|
299
|
+
self.is_sending = False
|
300
|
+
self.Opened = False
|
301
|
+
self.ser.flush()
|
302
|
+
self.ser.close()
|
303
|
+
# if self.device is not None:
|
304
|
+
# print("{} Disconnected".format(self.device))
|
305
|
+
# else:
|
306
|
+
print(f"{self.device} Disconnected")
|
307
|
+
|
308
|
+
def send(self, command, eol=b"\r"):
|
309
|
+
if (self.ser != None and self.ser.is_open) or self.only_simulate:
|
310
|
+
# self.sendQueue.put(command + "\r")
|
311
|
+
# self.temp_cmd = self.sendQueue.get()
|
312
|
+
self.temp_cmd = command + "\r"
|
313
|
+
|
314
|
+
if "~" in self.temp_cmd:
|
315
|
+
# remove ~ in self.temp_cmd
|
316
|
+
self.temp_cmd = self.temp_cmd[1:]
|
317
|
+
else:
|
318
|
+
self.usedSendQueue.put(self.temp_cmd)
|
319
|
+
# self.command_cache.put(self.temp_cmd)
|
320
|
+
if self.ser is not None or not self.only_simulate:
|
321
|
+
self.ser.write(self.temp_cmd.encode("ascii"))
|
322
|
+
if self.is_log_message:
|
323
|
+
print(
|
324
|
+
f"[bold green]Send to {self.device}:[/bold green] {self.temp_cmd}"
|
325
|
+
)
|
326
|
+
self.simulator.emit("motor", f"{self.universe}-{self.temp_cmd}")
|
327
|
+
else:
|
328
|
+
print(f"{self.device} is not open")
|
329
|
+
|
330
|
+
def sending(self):
|
331
|
+
self.is_sending = True
|
332
|
+
print("Sending thread started")
|
333
|
+
# start_time = time.time()
|
334
|
+
try:
|
335
|
+
while self.is_sending:
|
336
|
+
# start_time = time.time()
|
337
|
+
if self.sendQueue.empty():
|
338
|
+
continue
|
339
|
+
# self.temp_cmd = ""
|
340
|
+
self.temp_cmd = self.sendQueue.get_nowait()
|
341
|
+
|
342
|
+
if "~" in self.temp_cmd:
|
343
|
+
# remove ~ in self.temp_cmd
|
344
|
+
self.temp_cmd = self.temp_cmd[1:]
|
345
|
+
else:
|
346
|
+
self.usedSendQueue.put(self.temp_cmd)
|
347
|
+
# self.command_cache.put(self.temp_cmd)
|
348
|
+
|
349
|
+
if self.ser is not None and self.ser.is_open is not self.only_simulate:
|
350
|
+
self.ser.write(self.temp_cmd.encode("ascii"))
|
351
|
+
# print(
|
352
|
+
# f"[bold green]Send to {self.device}:[/bold green] {self.temp_cmd}"
|
353
|
+
# )
|
354
|
+
# self.simulate.emit("motor", f"{self.universe}-{self.temp_cmd}")
|
355
|
+
# print(f"[bold yellow]Cache:[/bold yellow]{self.temp_cmd}")
|
356
|
+
|
357
|
+
self.sendQueue.task_done()
|
358
|
+
|
359
|
+
time.sleep(0.05)
|
360
|
+
|
361
|
+
except Exception as e:
|
362
|
+
print("Error in sending thread:")
|
363
|
+
print(e)
|
364
|
+
self.is_sending = False
|
365
|
+
|
366
|
+
def listening(self):
|
367
|
+
self.listen = True
|
368
|
+
self.listeningBuffer = ""
|
369
|
+
print("Listening thread started")
|
370
|
+
# start_time = time.time()
|
371
|
+
try:
|
372
|
+
while True:
|
373
|
+
# start_time = time.time()
|
374
|
+
if not self.listen:
|
375
|
+
break
|
376
|
+
if self.only_simulate:
|
377
|
+
continue
|
378
|
+
if self.ser is not None and self.ser.is_open:
|
379
|
+
if self.ser.in_waiting > 0:
|
380
|
+
self.listeningBuffer += self.ser.read(1).decode(
|
381
|
+
"utf-8", "replace"
|
382
|
+
)
|
383
|
+
if self.listeningBuffer.endswith("\r"):
|
384
|
+
self.recvQueue.put(self.listeningBuffer)
|
385
|
+
self.listeningBuffer = ""
|
386
|
+
else:
|
387
|
+
print(f"{self.device} is not open")
|
388
|
+
if not self.recvQueue.empty():
|
389
|
+
self.temp_recv = self.recvQueue.get()
|
390
|
+
self.temp_used_cmd = self.usedSendQueue.get()
|
391
|
+
print(
|
392
|
+
f"[bold green]Recv:[/bold green] {self.temp_used_cmd}->{self.temp_recv}"
|
393
|
+
)
|
394
|
+
self.recvQueue.task_done()
|
395
|
+
self.usedSendQueue.task_done()
|
396
|
+
time.sleep(0.05)
|
397
|
+
# print(f"Time: {time.time()-start_time}")
|
398
|
+
except Exception as e:
|
399
|
+
print("Error in listening thread:")
|
400
|
+
print(e)
|
401
|
+
self.listen = False
|
402
|
+
print("Listening thread stopped")
|
403
|
+
|
404
|
+
# endregion
|
405
|
+
|
406
|
+
# region motor motion functions
|
407
|
+
def enable(self, motor_address="", enable=True):
|
408
|
+
cmd = "ME" if enable else "MD"
|
409
|
+
self.send(self.addressed_cmd(motor_address, cmd))
|
410
|
+
|
411
|
+
def move_absolute(self, motor_address="", position=0, speed=0.15):
|
412
|
+
# if speed > 0:
|
413
|
+
# self.set_velocity(motor_address, speed)
|
414
|
+
self.send(self.addressed_cmd(motor_address, f"VE{speed}"))
|
415
|
+
self.send(self.addressed_cmd(motor_address, f"FP{position}"))
|
416
|
+
|
417
|
+
def move_fixed_distance(self, motor_address="", distance=100, speed=0.15):
|
418
|
+
# if speed > 0:
|
419
|
+
# self.set_velocity(motor_address, speed)
|
420
|
+
self.send(self.addressed_cmd(motor_address, "VE{}".format(speed)))
|
421
|
+
self.send(self.addressed_cmd(motor_address, "FL{}".format(int(distance))))
|
422
|
+
|
423
|
+
def start_jog(self, motor_address="", speed=0.15, direction="CW"):
|
424
|
+
# if direction == "CW":
|
425
|
+
# self.send(self.addressed_cmd(motor_address, "DI1"))
|
426
|
+
# if direction == "CCW":
|
427
|
+
# self.send(self.addressed_cmd(motor_address, "DI-1"))
|
428
|
+
self.send(self.addressed_cmd(motor_address, "JS{}".format(speed)))
|
429
|
+
self.send(self.addressed_cmd(motor_address, "CJ"))
|
430
|
+
|
431
|
+
def change_jog_speed(self, motor_address="", speed=0.15):
|
432
|
+
self.send(self.addressed_cmd(motor_address, "CS{}".format(speed)))
|
433
|
+
|
434
|
+
def stop_jog(self, motor_address=""):
|
435
|
+
self.send(self.addressed_cmd(motor_address, "SJ"))
|
436
|
+
|
437
|
+
def stop(self, motor_address=""):
|
438
|
+
self.send(self.addressed_cmd(motor_address, "ST"))
|
439
|
+
|
440
|
+
def stop_with_deceleration(self, motor_address=""):
|
441
|
+
self.send(self.addressed_cmd(motor_address, "STD"))
|
442
|
+
|
443
|
+
def stop_and_kill(self, motor_address="", with_deceleration=True):
|
444
|
+
if with_deceleration:
|
445
|
+
self.send(self.addressed_cmd(motor_address, "SKD"))
|
446
|
+
else:
|
447
|
+
self.send(self.addressed_cmd(motor_address, "SK"))
|
448
|
+
|
449
|
+
def setup_motor(self, motor_address="", kill=False):
|
450
|
+
if kill:
|
451
|
+
self.stop_and_kill(motor_address)
|
452
|
+
# time.sleep(1)
|
453
|
+
self.set_transmit_delay(motor_address, 25)
|
454
|
+
self.set_return_format_dexcimal(motor_address)
|
455
|
+
# self.motor_wait(motor_address, 0.1)
|
456
|
+
# status = self.get_status(motor_address)
|
457
|
+
|
458
|
+
def calibrate(self, motor_address="", speed=0.3, onStart=None, onComplete=None):
|
459
|
+
self.send(self.addressed_cmd(motor_address, "VE{}".format(speed)))
|
460
|
+
# self.send(self.addressed_cmd(motor_address, "WT0.2"))
|
461
|
+
self.send(self.addressed_cmd(motor_address, "DI10"))
|
462
|
+
# time.sleep(self.transmitDelay)
|
463
|
+
# self.send(self.addressed_cmd(motor_address, "FS3F"))
|
464
|
+
self.send(self.addressed_cmd(motor_address, "SH3F"))
|
465
|
+
self.send(self.addressed_cmd(motor_address, "EP0"))
|
466
|
+
self.send(self.addressed_cmd(motor_address, "SP0"))
|
467
|
+
|
468
|
+
# speed slow= 0.25, medium=1, fast=5
|
469
|
+
def set_transmit_delay(self, motor_address="", delay=15):
|
470
|
+
self.send(self.addressed_cmd(motor_address, "TD{}".format(delay)))
|
471
|
+
|
472
|
+
# endregion
|
473
|
+
# region motor status functions
|
474
|
+
def get_position(self, motor_address):
|
475
|
+
self.send(self.addressed_cmd(motor_address, "IP"))
|
476
|
+
self.new_value_event.wait(timeout=0.5)
|
477
|
+
return self.get_value()
|
478
|
+
|
479
|
+
def get_temperature(self, motor_address):
|
480
|
+
self.send(self.addressed_cmd(motor_address, "IT"))
|
481
|
+
self.new_value_event.wait(timeout=0.5)
|
482
|
+
return int(self.get_value()) / 10
|
483
|
+
|
484
|
+
def get_sensor_status(self, motor_address):
|
485
|
+
self.send(self.addressed_cmd(motor_address, "IS"))
|
486
|
+
self.new_value_event.wait(timeout=0.5)
|
487
|
+
return self.get_value()
|
488
|
+
|
489
|
+
def get_votalge(self, motor_address):
|
490
|
+
self.send(self.addressed_cmd(motor_address, "IU"))
|
491
|
+
self.new_value_event.wait(timeout=0.5)
|
492
|
+
return self.get_value()
|
493
|
+
|
494
|
+
def get_acceleration(self, motor_address):
|
495
|
+
self.send(self.addressed_cmd(motor_address, "AC"))
|
496
|
+
self.new_value_event.wait(timeout=0.5)
|
497
|
+
return self.get_value()
|
498
|
+
|
499
|
+
def get_deceleration(self, motor_address):
|
500
|
+
self.send(self.addressed_cmd(motor_address, "DE"))
|
501
|
+
self.new_value_event.wait(timeout=0.5)
|
502
|
+
return self.get_value()
|
503
|
+
|
504
|
+
def get_velocity(self, motor_address):
|
505
|
+
self.send(self.addressed_cmd(motor_address, "VE"))
|
506
|
+
self.new_value_event.wait(timeout=0.5)
|
507
|
+
return self.get_value()
|
508
|
+
|
509
|
+
def get_distance(self, motor_address):
|
510
|
+
self.send(self.addressed_cmd(motor_address, "DI"))
|
511
|
+
self.new_value_event.wait(timeout=0.5)
|
512
|
+
return self.get_value()
|
513
|
+
|
514
|
+
def get_jog_speed(self, motor_address):
|
515
|
+
self.send(self.addressed_cmd(motor_address, "JS"))
|
516
|
+
self.new_value_event.wait(timeout=0.5)
|
517
|
+
return self.get_value()
|
518
|
+
|
519
|
+
def get_info(self, motor_address):
|
520
|
+
self.set_return_format_dexcimal(motor_address)
|
521
|
+
self.motor_wait(motor_address, 0.1)
|
522
|
+
info = {
|
523
|
+
"pos": str(self.get_position(motor_address)),
|
524
|
+
"temp": str(self.get_temperature(motor_address)),
|
525
|
+
"sensor": str(self.get_sensor_status(motor_address)),
|
526
|
+
"vol": str(self.get_votalge(motor_address)),
|
527
|
+
"accel": str(self.get_acceleration(motor_address)),
|
528
|
+
"decel": str(self.get_deceleration(motor_address)),
|
529
|
+
"vel": str(self.get_velocity(motor_address)),
|
530
|
+
"dis": str(self.get_distance(motor_address)),
|
531
|
+
"jogsp": str(self.get_jog_speed(motor_address)),
|
532
|
+
}
|
533
|
+
return info
|
534
|
+
|
535
|
+
def get_status(self, motor_address) -> str:
|
536
|
+
self.set_return_format_dexcimal(motor_address)
|
537
|
+
self.send(self.addressed_cmd(motor_address, "RS"))
|
538
|
+
self.new_value_event.wait(timeout=0.5)
|
539
|
+
return str(self.get_value())
|
540
|
+
|
541
|
+
def set_return_format_dexcimal(self, motor_address):
|
542
|
+
self.send(self.addressed_cmd(motor_address, "IFD"))
|
543
|
+
|
544
|
+
def set_return_format_hexdecimal(self, motor_address):
|
545
|
+
self.send(self.addressed_cmd(motor_address, "IFH"))
|
546
|
+
|
547
|
+
# endregion
|
548
|
+
|
549
|
+
# region utility functions
|
550
|
+
def motor_wait(self, motor_address, wait_time):
|
551
|
+
self.send(self.addressed_cmd(motor_address, "WT{}".format(wait_time)))
|
552
|
+
|
553
|
+
def addressed_cmd(self, motor_address, command):
|
554
|
+
if motor_address == "":
|
555
|
+
return f"~{command}"
|
556
|
+
return f"{motor_address}{command}"
|
557
|
+
|
558
|
+
def get_value(self):
|
559
|
+
print("Waiting for value")
|
560
|
+
self.new_data_event.wait(timeout=0.5)
|
561
|
+
print("Recv:" + self.listeningBufferPre)
|
562
|
+
self.new_data_event.clear()
|
563
|
+
if "%" in self.listeningBufferPre:
|
564
|
+
return "success_ack"
|
565
|
+
if "?" in self.listeningBufferPre:
|
566
|
+
return "fail_ack"
|
567
|
+
if "*" in self.listeningBufferPre:
|
568
|
+
return "buffered_ack"
|
569
|
+
self.new_value_event.set()
|
570
|
+
pattern = r"=(\w+(?:\.\w+)?|\d+(?:\.\d+)?)"
|
571
|
+
result = re.search(pattern, self.listeningBufferPre)
|
572
|
+
self.listeningBufferPre = ""
|
573
|
+
self.new_value_event.clear()
|
574
|
+
if result:
|
575
|
+
return result.group(1)
|
576
|
+
else:
|
577
|
+
return "No_value_found"
|
578
|
+
|
579
|
+
|
580
|
+
class moons_stepper_simulate:
|
581
|
+
def __init__(
|
582
|
+
self,
|
583
|
+
moons_motor: moons_stepper,
|
584
|
+
universe: int = 0,
|
585
|
+
server_address: str = "http://localhost:3001",
|
586
|
+
):
|
587
|
+
self.server_address = server_address
|
588
|
+
self.universe = universe
|
589
|
+
self.moons_motor = moons_motor
|
590
|
+
self.io = socketio.SimpleClient()
|
591
|
+
self.connected = False
|
592
|
+
self.is_log_message = True
|
593
|
+
|
594
|
+
def connect(self):
|
595
|
+
try:
|
596
|
+
self.is_log_message = False
|
597
|
+
self.io.connect(self.server_address)
|
598
|
+
self.connected = True
|
599
|
+
print(f"Socket connected to {self.server_address}[{self.io.sid}]")
|
600
|
+
# self.rederict_thread = threading.Thread(
|
601
|
+
# target=self.rederict_job,
|
602
|
+
# )
|
603
|
+
# self.rederict_thread.daemon = True
|
604
|
+
# self.rederict_thread.start()
|
605
|
+
except Exception as e:
|
606
|
+
print(f"Socket connection error: {e}")
|
607
|
+
self.connected = False
|
608
|
+
|
609
|
+
def rederict_job(self):
|
610
|
+
if not self.connected:
|
611
|
+
print("Socket not connected")
|
612
|
+
return
|
613
|
+
if self.moons_motor is None:
|
614
|
+
print("Motor is None")
|
615
|
+
return
|
616
|
+
while True:
|
617
|
+
# self.moons_motor.on_send_event.wait(timeout=0.5)
|
618
|
+
if self.moons_motor.command_cache.empty():
|
619
|
+
continue
|
620
|
+
cmd = self.moons_motor.command_cache.get_nowait()
|
621
|
+
# self.moons_motor.command_cache.task_done()
|
622
|
+
|
623
|
+
self.emit("motor", f"{self.universe}-{cmd}")
|
624
|
+
# self.moons_motor.command_cache = ""
|
625
|
+
# self.moons_motor.on_send_event.clear()
|
626
|
+
|
627
|
+
if not self.connected:
|
628
|
+
break
|
629
|
+
time.sleep(0.05)
|
630
|
+
|
631
|
+
def disconnect(self):
|
632
|
+
self.io.disconnect()
|
633
|
+
|
634
|
+
def emit(self, eventName: str, data):
|
635
|
+
if not self.connected:
|
636
|
+
print("Socket not connected")
|
637
|
+
return
|
638
|
+
self.io.emit(eventName, data)
|
639
|
+
if self.is_log_message:
|
640
|
+
print("[bold blue]Send to socket:[/bold blue] {}\n".format(data))
|
641
|
+
|
642
|
+
|
643
|
+
# endregion
|
644
|
+
|
645
|
+
# SERIAL => 上次已知父系(尾巴+A) 或是事件分頁
|
646
|
+
# reg USB\s*VID:PID=(\w+):(\w+)\s*SER=([A-Za-z0-9]+)
|
647
|
+
|
648
|
+
# serial_num 裝置例項路徑
|
649
|
+
# TD(Tramsmit Delay) = 15
|
@@ -0,0 +1,30 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: moons_motor
|
3
|
+
Version: 0.0.1
|
4
|
+
Summary: This is a python library for controlling the Moons' motor through the serial port.
|
5
|
+
Author: miroc99
|
6
|
+
Author-email: miroc <mike8503111@gmail.com>
|
7
|
+
Project-URL: Repository, https://github.com/miroc99/moons_motor.git
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
9
|
+
Classifier: Intended Audience :: Developers
|
10
|
+
Classifier: Topic :: Terminals :: Serial
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
13
|
+
Requires-Python: >=3.12
|
14
|
+
Description-Content-Type: text/markdown
|
15
|
+
Requires-Dist: pyserial
|
16
|
+
Requires-Dist: rich
|
17
|
+
Requires-Dist: python-socketio
|
18
|
+
|
19
|
+
# Moons Motor
|
20
|
+
|
21
|
+
This is a python library for control moons motor through serial port.
|
22
|
+
|
23
|
+
## Compatibility
|
24
|
+
Now only support Windows.
|
25
|
+
|
26
|
+
## Installing
|
27
|
+
Install through `pip`
|
28
|
+
```bash
|
29
|
+
python -m pip install moons_motor
|
30
|
+
```
|
@@ -0,0 +1,6 @@
|
|
1
|
+
moons_motor/__init__.py,sha256=qOpsRwizV-DpKSvNzyvj8ju3cs6vwgIICur1Oe6sxOA,27
|
2
|
+
moons_motor/moons_motor.py,sha256=NbS8_jUq8BWmuvbShkRfb6740vP1706m0H_3oxzIAp4,23238
|
3
|
+
moons_motor-0.0.1.dist-info/METADATA,sha256=9GmzXWkniEwod1fED20mXxRPP4DKcasJ-P6mA6th_uo,888
|
4
|
+
moons_motor-0.0.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
5
|
+
moons_motor-0.0.1.dist-info/top_level.txt,sha256=0dE-CR5_NYBw34jHIDGQNWpMllzO6mtUIuKyRv_rJLg,12
|
6
|
+
moons_motor-0.0.1.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
moons_motor
|