moons-motor 0.0.10__py3-none-any.whl → 0.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
moons_motor/motor.py CHANGED
@@ -8,12 +8,51 @@ from rich.panel import Panel
8
8
  import queue
9
9
  from moons_motor.subject import Subject
10
10
  import time
11
+ import threading
12
+
13
+ from dataclasses import dataclass
11
14
 
12
15
 
13
16
  class StepperModules:
14
17
  STM17S_3RN = "STM17S-3RN"
15
18
 
16
19
 
20
+ @dataclass(frozen=True)
21
+ class StepperCommand:
22
+ JOG: str = "CJ" # Start jogging
23
+ JOG_SPEED: str = "JS" # Jogging speed (Need to set before start jogging)
24
+ CHANGE_JOG_SPEED: str = "CS" # Change jogging speed while jogging
25
+ STOP_JOG: str = "SJ" # Stop jogging with deceleration
26
+ STOP: str = "ST" # Stop immediately (No deceleration)
27
+ STOP_DECEL: str = "STD" # Stop with deceleration
28
+ STOP_KILL: str = (
29
+ "SK" # Stop with deceleration(Control by AM) and kill all unexecuted commands
30
+ )
31
+ STOP_KILL_DECEL: str = (
32
+ "SKD" # Stop and kill all unexecuted commands with deceleration(Control by DE)
33
+ )
34
+ ENABLE: str = "ME" # Enable motor
35
+ DISABLE: str = "MD" # Disable motor
36
+ MOVE_ABSOLUTE: str = "FP" # Move to absolute position
37
+ MOVE_FIXED_DISTANCE: str = "FL" # Move to fixed distance
38
+ POSITION: str = "IP" # Motor absolute position(Calculated trajectory position)
39
+ TEMPERATURE: str = "IT" # Motor temperature
40
+ VOLTAGE: str = "IU" # Motor voltage
41
+
42
+ ENCODER_POSITION: str = "EP" # Encoder position
43
+ SET_POSITION: str = "SP" # Set encoder position
44
+
45
+ HOME: str = "SH" # Home position
46
+ VELOCITY: str = "VE" # Set velocity
47
+
48
+ ALARM_RESET: str = "AR" # Reset alarm
49
+
50
+ SET_RETURN_FORMAT_DECIMAL: str = "IFD" # Set return format to decimal
51
+ SET_RETURN_FORMAT_HEXADECIMAL: str = "IFH" # Set return format to hexadecimal
52
+
53
+ SET_TRANSMIT_DELAY: str = "TD" # Set transmit delay
54
+
55
+
17
56
  class MoonsStepper(Subject):
18
57
  motorAdress = [
19
58
  "0",
@@ -61,25 +100,19 @@ class MoonsStepper(Subject):
61
100
  ):
62
101
  super().__init__()
63
102
  self.universe = universe
64
- self.model = model
103
+ self.model = model # Motor model
65
104
  self.only_simulate = only_simlate
66
- self.device = ""
105
+ self.device = "" # COM port description
67
106
  self.VID = VID
68
107
  self.PID = PID
69
- self.SERIAL_NUM = SERIAL_NUM
108
+ self.SERIAL_NUM = SERIAL_NUM # ID for determent the deivice had same VID and PID, can be config using chips manufacturer tool
70
109
  self.ser = None
71
- self.listeningBuffer = ""
72
- self.listeningBufferPre = ""
73
- self.transmitDelay = 0.010
74
- self.lock = False
75
110
  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
111
  self.recvQueue = queue.Queue()
80
112
  self.sendQueue = queue.Queue()
81
- self.command_cache = queue.Queue()
82
- self.usedSendQueue = queue.Queue()
113
+ self.pending_callbacks = {}
114
+ self.update_thread = None
115
+ self.is_updating = False
83
116
 
84
117
  self.console = Console()
85
118
 
@@ -117,7 +150,29 @@ class MoonsStepper(Subject):
117
150
  print(Panel(port_info, title="All COMPorts"))
118
151
  return simple_ports
119
152
 
153
+ @staticmethod
154
+ def process_response(response):
155
+ equal_sign_index = response.index("=")
156
+ address = response[0]
157
+ command = response[1:equal_sign_index]
158
+ value = response[equal_sign_index + 1 :]
159
+
160
+ if command == "IT" or command == "IU":
161
+ # Handle temperature response
162
+ value = int(value) / 10.0
163
+ return {
164
+ "address": address,
165
+ "command": command,
166
+ "value": value,
167
+ }
168
+
169
+ def __start_update_thread(self):
170
+ self.update_thread = threading.Thread(target=self.update, daemon=True)
171
+ self.is_updating = True
172
+ self.update_thread.start()
173
+
120
174
  def connect(self, COM=None, baudrate=9600, callback=None):
175
+ # Simulate mode
121
176
  if self.only_simulate:
122
177
  self.Opened = True
123
178
  self.device = f"Simulate-{self.universe}"
@@ -128,57 +183,63 @@ class MoonsStepper(Subject):
128
183
 
129
184
  def attempt_connect(COM, baudrate):
130
185
  try:
131
- self.ser = serial.Serial(COM, baudrate)
186
+ self.ser = serial.Serial(port=COM, baudrate=baudrate)
132
187
  if self.ser is None:
133
- # print("> Device not found")
134
188
  self.Opened = False
135
189
  if self.ser.is_open:
136
- # print(f"Device: {self.device} | COM: {COM} connected")
137
190
  self.Opened = True
138
- except:
139
- print("> Device error")
191
+ print(f"[bold green]Device connected[/bold green]: {self.device}")
192
+
193
+ except Exception as e:
194
+ print(f"[bold red]Device error:[/bold red] {e} ")
140
195
  self.Opened = False
141
196
 
197
+ ports = list(list_ports.comports())
142
198
  if COM is not None and not self.only_simulate:
143
199
  attempt_connect(COM, baudrate)
144
200
  if callback:
145
201
  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)
202
+ else:
203
+ for p in ports:
204
+ m = re.match(
205
+ r"USB\s*VID:PID=(\w+):(\w+)\s*SER=([A-Za-z0-9]*)", p.usb_info()
206
+ )
207
+ print(p.usb_info())
208
+ if (
209
+ m
210
+ and m.group(1) == self.VID
211
+ and m.group(2) == self.PID
212
+ # and m.group(3) == self.SERIAL_NUM
213
+ ):
214
+ if m.group(3) == self.SERIAL_NUM or self.SERIAL_NUM == "":
215
+ print(
216
+ f"[bold yellow]Device founded:[/bold yellow] {p.description} | VID: {m.group(1)} | PID: {m.group(2)} | SER: {m.group(3)}"
217
+ )
218
+
219
+ self.device = p.description
220
+
221
+ attempt_connect(p.device, baudrate)
222
+
170
223
  break
171
- break
172
224
 
173
- if self.only_simulate:
174
- self.device = "Simulate"
175
- self.Opened = True
225
+ if self.only_simulate:
226
+ self.device = "Simulate"
227
+ self.Opened = True
228
+ time.sleep(0.5)
229
+ self.__start_update_thread()
230
+ if callback:
231
+ callback(self.device, self.Opened)
232
+
176
233
  if not self.Opened:
177
- print("> Device not found")
234
+ print(f"[bold red]Device not found[/bold red]")
178
235
  if callback:
179
236
  callback(self.device, self.Opened)
180
237
 
181
238
  def disconnect(self):
239
+ self.sendQueue.queue.clear()
240
+ self.recvQueue.queue.clear()
241
+ self.is_updating = False
242
+ self.update_thread = None
182
243
  if self.only_simulate:
183
244
  self.listen = False
184
245
  self.is_sending = False
@@ -191,17 +252,12 @@ class MoonsStepper(Subject):
191
252
  self.Opened = False
192
253
  self.ser.flush()
193
254
  self.ser.close()
194
- print(f"{self.device} Disconnected")
255
+ print(f"[bold red]Device disconnected[/bold red]: {self.device}")
195
256
 
196
257
  def send(self, command, eol=b"\r"):
197
258
  if (self.ser != None and self.ser.is_open) or self.only_simulate:
198
259
  self.temp_cmd = command + "\r"
199
260
 
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
261
  if self.ser is not None or not self.only_simulate:
206
262
  self.temp_cmd += "\r"
207
263
  self.ser.write(self.temp_cmd.encode("ascii"))
@@ -213,75 +269,56 @@ class MoonsStepper(Subject):
213
269
  else:
214
270
  print(f"Target device is not opened. Command: {command}")
215
271
 
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
272
+ def send_command(self, address="", command="", value=None):
273
+ if command == "":
274
+ print("Command can't be empty")
275
+ return
276
+ if value is not None:
277
+ command = self.addressed_cmd(address, command + str(value))
278
+ else:
279
+ command = self.addressed_cmd(address, command)
280
+
281
+ self.sendQueue.put_nowait(command)
282
+
283
+ def update(self):
284
+
285
+ while self.is_updating:
286
+ if self.ser is not None:
287
+ if self.ser.in_waiting > 0:
288
+ response = self.ser.read(self.ser.in_waiting)
289
+ response = response.decode("ascii", errors="ignore").strip()
290
+ self.handle_recv(response)
291
+ time.sleep(0.01) # Send delay
292
+ if self.sendQueue.empty() != True:
293
+ while not self.sendQueue.empty():
294
+ cmd = self.sendQueue.get_nowait()
295
+ self.send(cmd)
296
+ self.sendQueue.task_done()
297
+ time.sleep(
298
+ 0.01
299
+ ) # Time for RS485 converter to switch between Transmit and Receive mode
300
+
301
+ def handle_recv(self, response):
302
+ if "*" in response:
303
+ print("buffered_ack")
304
+ elif "%" in response:
305
+ print(f"[bold green](v)success_ack[/bold green]")
306
+ elif "?" in response:
307
+ print(f"[bold red](x)fail_ack[/bold red]")
243
308
  else:
244
- print("Device not open, read fail.")
245
- return None
309
+ print(f"[bold blue]Received from {self.device}: [/bold blue]", response)
310
+ self.recvQueue.put_nowait(response)
311
+
312
+ for command, callback in list(self.pending_callbacks.items()):
313
+ if command in response:
314
+ if callback:
315
+ callback(response)
316
+ del self.pending_callbacks[command]
317
+ break
246
318
 
247
319
  # endregion
248
320
 
249
321
  # 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
322
 
286
323
  def setup_motor(self, motor_address="", kill=False):
287
324
  if kill:
@@ -289,173 +326,38 @@ class MoonsStepper(Subject):
289
326
  self.set_transmit_delay(motor_address, 25)
290
327
  self.set_return_format_dexcimal(motor_address)
291
328
 
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
329
+ def home(self, motor_address="", speed=0.3, onComplete=None):
330
+ self.send_command(
331
+ address=motor_address, command=StepperCommand.VELOCITY, value=speed
376
332
  )
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"))
333
+ self.send_command(
334
+ address=motor_address, command=StepperCommand.HOME, value="3F"
335
+ )
336
+ self.send_command(
337
+ address=motor_address, command=StepperCommand.ENCODER_POSITION
338
+ )
339
+ self.send_command(
340
+ address=motor_address, command=StepperCommand.SET_POSITION, value=0
341
+ )
342
+ if onComplete:
343
+ self.get_status(
344
+ motor_address, StepperCommand.SET_POSITION, callback=onComplete
345
+ )
412
346
 
413
- def set_return_format_hexdecimal(self, motor_address):
414
- self.send(self.addressed_cmd(motor_address, "IFH"))
347
+ # endregion
348
+ def get_status(self, motor_address, command: StepperCommand, callback=None):
349
+ command = self.addressed_cmd(motor_address, command)
350
+ if callback:
351
+ self.pending_callbacks[command] = callback
352
+ self.sendQueue.put_nowait(command)
415
353
 
416
354
  # endregion
417
355
 
418
356
  # 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
357
 
422
358
  def addressed_cmd(self, motor_address, command):
423
- if motor_address == "":
424
- return f"~{command}"
425
359
  return f"{motor_address}{command}"
426
360
 
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
361
 
460
362
  # endregion
461
363
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: moons_motor
3
- Version: 0.0.10
3
+ Version: 0.1.2
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,11 @@
1
+ moons_motor/__init__.py,sha256=qOpsRwizV-DpKSvNzyvj8ju3cs6vwgIICur1Oe6sxOA,27
2
+ moons_motor/motor.py,sha256=fiaKLb9NC3uf5rgBHBmQM2M2DzGw_wLu_eo9keTvkXY,12044
3
+ moons_motor/observer.py,sha256=PXzuPYKRb2HpjArJcD8HakYIPfFGAs1uBDIL8PSizgA,124
4
+ moons_motor/simulate.py,sha256=J0y1fZhoOim9i-BAkprxnPern1SAdkDfKPqT2MWyDwU,2561
5
+ moons_motor/status.py,sha256=jXQZFZTt9ugHktkWKLII8MpEQQaeO-UjlwTrrP4LJNE,2872
6
+ moons_motor/subject.py,sha256=L_GS6fvJTeX7X23o3T92oiZ4rtLVKA2OEd9GpHn_Dz4,445
7
+ moons_motor-0.1.2.dist-info/licenses/LICENSE,sha256=nsYjO800SjIjI85y2kVHR5mC3tca2vs4kK_BhNe89bM,1074
8
+ moons_motor-0.1.2.dist-info/METADATA,sha256=LYi8aOV7Ft14G-SIPo4IDdsclgtmKDbfQJDst7X6uh8,1304
9
+ moons_motor-0.1.2.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
10
+ moons_motor-0.1.2.dist-info/top_level.txt,sha256=0dE-CR5_NYBw34jHIDGQNWpMllzO6mtUIuKyRv_rJLg,12
11
+ moons_motor-0.1.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.2.0)
2
+ Generator: setuptools (80.7.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,11 +0,0 @@
1
- moons_motor/__init__.py,sha256=qOpsRwizV-DpKSvNzyvj8ju3cs6vwgIICur1Oe6sxOA,27
2
- moons_motor/motor.py,sha256=BA_dMHJeqWuUEknL2lMGgGXt7PH5hFsl5L7spiFnB2Q,16075
3
- moons_motor/observer.py,sha256=PXzuPYKRb2HpjArJcD8HakYIPfFGAs1uBDIL8PSizgA,124
4
- moons_motor/simulate.py,sha256=J0y1fZhoOim9i-BAkprxnPern1SAdkDfKPqT2MWyDwU,2561
5
- moons_motor/status.py,sha256=jXQZFZTt9ugHktkWKLII8MpEQQaeO-UjlwTrrP4LJNE,2872
6
- moons_motor/subject.py,sha256=L_GS6fvJTeX7X23o3T92oiZ4rtLVKA2OEd9GpHn_Dz4,445
7
- moons_motor-0.0.10.dist-info/LICENSE,sha256=nsYjO800SjIjI85y2kVHR5mC3tca2vs4kK_BhNe89bM,1074
8
- moons_motor-0.0.10.dist-info/METADATA,sha256=mnp8YTh-_GclQE8tS44AaHdgMVwGx2-W5Sz3H9vmcZo,1282
9
- moons_motor-0.0.10.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
10
- moons_motor-0.0.10.dist-info/top_level.txt,sha256=0dE-CR5_NYBw34jHIDGQNWpMllzO6mtUIuKyRv_rJLg,12
11
- moons_motor-0.0.10.dist-info/RECORD,,