moons-motor 0.0.1__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,12 @@
1
+ # Moons Motor
2
+
3
+ This is a python library for control moons motor through serial port.
4
+
5
+ ## Compatibility
6
+ Now only support Windows.
7
+
8
+ ## Installing
9
+ Install through `pip`
10
+ ```bash
11
+ python -m pip install moons_motor
12
+ ```
@@ -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,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.py
4
+ moons_motor/__init__.py
5
+ moons_motor/moons_motor.py
6
+ moons_motor.egg-info/PKG-INFO
7
+ moons_motor.egg-info/SOURCES.txt
8
+ moons_motor.egg-info/dependency_links.txt
9
+ moons_motor.egg-info/requires.txt
10
+ moons_motor.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+ pyserial
2
+ rich
3
+ python-socketio
@@ -0,0 +1 @@
1
+ moons_motor
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "moons_motor"
7
+ version = "0.0.1"
8
+ authors = [{ name = "miroc", email = "mike8503111@gmail.com" }]
9
+ description = "This is a python library for controlling the Moons' motor through the serial port."
10
+ readme = "README.md"
11
+ requires-python = ">=3.12"
12
+ dependencies = ["pyserial", "rich", "python-socketio"]
13
+ classifiers = [
14
+ # How mature is this project? Common values are
15
+ # 3 - Alpha
16
+ # 4 - Beta
17
+ # 5 - Production/Stable
18
+ "Development Status :: 3 - Alpha",
19
+
20
+ # Indicate who your project is intended for
21
+ "Intended Audience :: Developers",
22
+ "Topic :: Terminals :: Serial",
23
+
24
+ # Pick your license as you wish (see also "license" above)
25
+ "License :: OSI Approved :: MIT License",
26
+
27
+ # Specify the Python versions you support here.
28
+ "Programming Language :: Python :: 3.12",
29
+ ]
30
+ [project.urls]
31
+ Repository = "https://github.com/miroc99/moons_motor.git"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,10 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="moons_motor",
5
+ version="0.0.1",
6
+ description="A simple motor control library for the Moons stepper motor driver",
7
+ author="miroc99",
8
+ packages=find_packages(),
9
+ install_requires=["pyserial", "rich", "python-socketio"],
10
+ )