moons-motor 0.0.1__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.
@@ -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
+ )