odrive 0.6.9.dev0__py37-none-win_amd64.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.
Files changed (49) hide show
  1. odrive/__init__.py +142 -0
  2. odrive/api_client.py +54 -0
  3. odrive/config.py +1296 -0
  4. odrive/crypto.py +369 -0
  5. odrive/data/brakeRs.json +18 -0
  6. odrive/data/drvs.json +82 -0
  7. odrive/data/encoders.json +86 -0
  8. odrive/data/motors.json +128 -0
  9. odrive/data/odrive-micro-x1.json +48 -0
  10. odrive/data/odrive-micro-x3.json +40 -0
  11. odrive/data/odrive-micro-x4.json +40 -0
  12. odrive/data/odrive-s1-x4.json +52 -0
  13. odrive/data/odrive-v4.4.json +58 -0
  14. odrive/data/schema-brakeR.json +6 -0
  15. odrive/data/schema-drv.json +6 -0
  16. odrive/data/schema-encoders.json +6 -0
  17. odrive/data/schema-motors.json +6 -0
  18. odrive/data/schema-odrive.json +5 -0
  19. odrive/data/schema.json +244 -0
  20. odrive/database.py +184 -0
  21. odrive/dfu.py +195 -0
  22. odrive/dfuse/DfuDevice.py +297 -0
  23. odrive/dfuse/__init__.py +1 -0
  24. odrive/enums.py +415 -0
  25. odrive/firmware.py +102 -0
  26. odrive/hw_version.py +67 -0
  27. odrive/legacy.py +73 -0
  28. odrive/legacy_config.py +123 -0
  29. odrive/legacy_dfu.py +442 -0
  30. odrive/lib/libodrive-windows-x64.dll +0 -0
  31. odrive/libodrive.py +325 -0
  32. odrive/pyfibre/fibre/__init__.py +4 -0
  33. odrive/pyfibre/fibre/libfibre-windows-amd64.dll +0 -0
  34. odrive/pyfibre/fibre/libfibre.py +1078 -0
  35. odrive/pyfibre/fibre/libwinpthread-1.dll +0 -0
  36. odrive/pyfibre/fibre/shell.py +162 -0
  37. odrive/pyfibre/fibre/utils.py +133 -0
  38. odrive/release_api.py +184 -0
  39. odrive/rich_text.py +134 -0
  40. odrive/shell.py +144 -0
  41. odrive/utils.py +881 -0
  42. odrive/version.py +1 -0
  43. odrive-0.6.9.dev0.data/scripts/odrive_demo.py +50 -0
  44. odrive-0.6.9.dev0.data/scripts/odrivetool +263 -0
  45. odrive-0.6.9.dev0.data/scripts/odrivetool.bat +2 -0
  46. odrive-0.6.9.dev0.dist-info/METADATA +22 -0
  47. odrive-0.6.9.dev0.dist-info/RECORD +49 -0
  48. odrive-0.6.9.dev0.dist-info/WHEEL +5 -0
  49. odrive-0.6.9.dev0.dist-info/top_level.txt +1 -0
odrive/libodrive.py ADDED
@@ -0,0 +1,325 @@
1
+ import asyncio
2
+ import ctypes
3
+ from dataclasses import dataclass
4
+ import os
5
+ import platform
6
+ import sys
7
+ import threading
8
+ from typing import Callable, Dict, List, Optional, Tuple
9
+ from .hw_version import HwVersion
10
+
11
+ _lib_names = {
12
+ ('Linux', 'x86_64'): 'libodrive-linux-x86_64.so',
13
+ ('Linux', 'aarch64'): 'libodrive-linux-aarch64.so',
14
+ ('Windows', 'AMD64'): 'libodrive-windows-x64.dll',
15
+ ('Darwin', 'x86_64'): 'libodrive-macos-x86_64.dylib',
16
+ ('Darwin', 'arm64'): 'libodrive-macos-arm64.dylib',
17
+ }
18
+
19
+ class _HwVersion(ctypes.Structure):
20
+ _fields_ = [("product_line", ctypes.c_uint8),
21
+ ("version", ctypes.c_uint8),
22
+ ("variant", ctypes.c_uint8),
23
+ ("reserved", ctypes.c_uint8)]
24
+
25
+ class _FwManifest(ctypes.Structure):
26
+ _fields_ = [("magic_number", ctypes.c_uint32),
27
+ ("fw_version_major", ctypes.c_uint8),
28
+ ("fw_version_minor", ctypes.c_uint8),
29
+ ("fw_version_revision", ctypes.c_uint8),
30
+ ("fw_version_unreleased", ctypes.c_uint8),
31
+ ("hw_version", _HwVersion),
32
+ ("reserved", ctypes.c_uint8 * 32),
33
+ ("build", ctypes.c_uint8 * 20)]
34
+
35
+
36
+ class LibODrive():
37
+ """
38
+ This class is not thread-safe.
39
+
40
+ All public member functions should be called from the same thread as `loop`
41
+ and all callbacks are called on `loop`.
42
+
43
+ Internally, this class launches a separate I/O thread on which most of
44
+ libodrive's backend actually runs. Libodrive handles event passing between
45
+ the two threads.
46
+ """
47
+
48
+ @staticmethod
49
+ def get_default_lib_path():
50
+ _system_desc = (platform.system(), platform.machine())
51
+
52
+ if _system_desc == ('Linux', 'aarch64') and sys.maxsize <= 2**32:
53
+ _system_desc = ('Linux', 'armv7l') # Python running in 32-bit mode on 64-bit OS
54
+
55
+ if not _system_desc in _lib_names:
56
+ raise ModuleNotFoundError("libodrive is not supported on your platform ({} {}).")
57
+
58
+ _script_dir = os.path.dirname(os.path.realpath(__file__))
59
+ return os.path.join(_script_dir, "lib", _lib_names[_system_desc])
60
+
61
+ @property
62
+ def version(self):
63
+ return (
64
+ (self._version.value >> 24) & 0xff,
65
+ (self._version.value >> 16) & 0xff,
66
+ (self._version.value >> 8) & 0xff,
67
+ (self._version.value >> 0) & 0xff,
68
+ )
69
+
70
+ def __init__(self, loop: asyncio.AbstractEventLoop, lib_path: Optional[str] = None):
71
+ self._loop: asyncio.AbstractEventLoop = asyncio.get_running_loop() if loop is None else loop
72
+
73
+ if lib_path is None:
74
+ lib_path = LibODrive.get_default_lib_path()
75
+ if not os.path.isfile(lib_path):
76
+ raise ImportError(f"{lib_path} not found. Try to reinstall the Python package. If you're a developer, run tools/setup.sh first.")
77
+
78
+ if os.name == 'nt':
79
+ dll_dir = os.path.dirname(lib_path)
80
+ if sys.version_info >= (3, 8):
81
+ os.add_dll_directory(dll_dir)
82
+ else:
83
+ os.environ['PATH'] = dll_dir + os.pathsep + os.environ['PATH']
84
+ self._lib = ctypes.windll.LoadLibrary(lib_path)
85
+ else:
86
+ self._lib = ctypes.cdll.LoadLibrary(lib_path)
87
+
88
+ self._version = ctypes.c_uint32.in_dll(self._lib, 'libodrive_version')
89
+ if self._version.value & 0xffff0000 != 0x00070000:
90
+ raise ImportError(f"Incompatible libodrive version ({self._version.value:08X}). Try to reinstall the Python package. If you're a developer, run tools/setup.sh first.")
91
+
92
+ # Load functions
93
+ self._lib.libodrive_init.argtypes = []
94
+ self._lib.libodrive_init.restype = ctypes.c_void_p
95
+
96
+ self._lib.libodrive_deinit.argtypes = [ctypes.c_void_p]
97
+ self._lib.libodrive_deinit.restype = None
98
+
99
+ self._lib.libodrive_iteration.argtypes = [ctypes.c_void_p, ctypes.c_int]
100
+ self._lib.libodrive_iteration.restype = ctypes.c_int
101
+
102
+ self._lib.libodrive_interrupt_iteration.argtypes = [ctypes.c_void_p]
103
+ self._lib.libodrive_interrupt_iteration.restype = ctypes.c_int
104
+
105
+ self._lib.libodrive_handle_callbacks.argtypes = [ctypes.c_void_p]
106
+ self._lib.libodrive_handle_callbacks.restype = ctypes.c_int
107
+
108
+ self._lib.libodrive_start_usb_discovery.argtypes = [ctypes.c_void_p, _TOnFoundDevice, _TOnLostDevice, ctypes.c_void_p]
109
+ self._lib.libodrive_start_usb_discovery.restype = ctypes.c_void_p
110
+
111
+ self._lib.libodrive_stop_discovery.argtypes = [ctypes.c_void_p]
112
+ self._lib.libodrive_stop_discovery.restype = ctypes.c_int
113
+
114
+ self._lib.libodrive_usb_device_from_handle.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
115
+ self._lib.libodrive_usb_device_from_handle.restype = None
116
+
117
+ self._lib.libodrive_connect_bootloader.argtypes = [ctypes.c_void_p, _TOnConnected, _TOnConnectionFailed]
118
+ self._lib.libodrive_connect_bootloader.restype = ctypes.c_int
119
+
120
+ self._lib.libodrive_disconnect.argtypes = [ctypes.c_void_p]
121
+ self._lib.libodrive_disconnect.restype = ctypes.c_int
122
+
123
+ self._lib.libodrive_start_installation.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, _TOnInstallationProgress, _TOnInstallationDone]
124
+ self._lib.libodrive_start_installation.restype = ctypes.c_int
125
+
126
+ self._lib.libodrive_open_firmware.argtypes = [ctypes.c_char_p, ctypes.c_size_t, ctypes.POINTER(ctypes.c_void_p), ctypes.POINTER(ctypes.POINTER(_FwManifest))]
127
+ self._lib.libodrive_open_firmware.restype = ctypes.c_int
128
+
129
+ self._lib.libodrive_close_firmware.argtypes = [ctypes.c_void_p]
130
+ self._lib.libodrive_close_firmware.restype = None
131
+
132
+ # Init
133
+ self._ctx = self._lib.libodrive_init()
134
+ assert self._ctx
135
+
136
+ self._notify_handle: Optional[asyncio.Handle] = None
137
+ self._worker_thread_shutdown = False
138
+ self._worker_thread = threading.Thread(target=self._worker_thread_func)
139
+ self._notify_pending = False
140
+ #self._worker_thread.start()
141
+
142
+ def stop_thread(self):
143
+ self._worker_thread_shutdown = True
144
+ self._lib.libodrive_interrupt_iteration(self._ctx)
145
+ self._worker_thread.join()
146
+ if not self._notify_handle is None:
147
+ self._notify_handle.cancel()
148
+
149
+ def deinit(self):
150
+ self._lib.libodrive_deinit(self._ctx)
151
+
152
+ def _notify(self):
153
+ self._notify_pending = False
154
+ self._lib.libodrive_handle_callbacks(self._ctx)
155
+
156
+ def _worker_thread_func(self):
157
+ while not self._worker_thread_shutdown:
158
+ result = self._lib.libodrive_iteration(self._ctx, -1) # no timeout
159
+ assert result == 0 or result == 1
160
+ if result == 1 and not self._notify_pending:
161
+ self._notify_pending = True
162
+ self._notify_handle = self._loop.call_soon_threadsafe(self._notify)
163
+
164
+ def start_usb_discovery(self):
165
+ def _enter(intf: Interface):
166
+ handle = self._lib.libodrive_start_usb_discovery(self._ctx, _on_found_device, _on_lost_device, id(intf))
167
+ assert handle
168
+ intf._handle = handle
169
+ _intf_map[id(intf)] = intf # callbacks only called on same thread, so it's safe to assign this after starting
170
+ return intf
171
+ intf = Interface(self._lib, _enter)
172
+ return intf
173
+
174
+ def open_firmware(self, data: bytes):
175
+ handle = ctypes.c_void_p()
176
+ manifest = ctypes.POINTER(_FwManifest)()
177
+ self._lib.libodrive_open_firmware(data, len(data), ctypes.byref(handle), ctypes.byref(manifest))
178
+ assert handle
179
+ assert manifest
180
+ return Firmware(self._lib, handle, manifest.contents)
181
+
182
+ class Interface():
183
+ @staticmethod
184
+ def _on_found_device(udata: int, device: int, serial_number: bytes, product_string: bytes, fibre2_capable: bool, handle: int, msg: bytes):
185
+ intf = _intf_map[udata]
186
+ py_dev = Device(intf._lib, device, serial_number.decode(), product_string.decode(), fibre2_capable)
187
+ _dev_map[device] = py_dev
188
+ intf.devices.append(py_dev)
189
+
190
+ # signal all waiting tasks and recycle _event for next round
191
+ intf._event.set()
192
+ intf._event = asyncio.Event()
193
+
194
+ @staticmethod
195
+ def _on_lost_device(udata: int, device: int):
196
+ intf = _intf_map[udata]
197
+ py_dev = _dev_map.pop(device)
198
+ intf.devices.remove(py_dev)
199
+
200
+ def __init__(self, lib, enter_impl):
201
+ self._lib = lib
202
+ self._enter_impl = enter_impl
203
+ self._handle = 0 # set in start_usb_discovery()
204
+ self.devices: List[Device] = []
205
+ self._event = asyncio.Event()
206
+
207
+ def __enter__(self):
208
+ return self._enter_impl(self)
209
+
210
+ def __exit__(self, exc_type, exc_val, exc_tb):
211
+ _intf_map.pop(id(self))
212
+ self._lib.libodrive_stop_discovery(self._handle)
213
+ self._handle = 0
214
+
215
+ def get_device(self, serial_number: Optional[str], is_bootloader: Optional[bool]):
216
+ for dev in self.devices:
217
+ if not serial_number is None and dev.serial_number != serial_number:
218
+ continue
219
+ if not is_bootloader is None and dev.is_bootloader != is_bootloader:
220
+ continue
221
+ return dev
222
+ return None
223
+
224
+ async def wait_for(self, serial_number: Optional[str], is_bootloader: Optional[bool]):
225
+ while True:
226
+ dev = self.get_device(serial_number, is_bootloader)
227
+ if not dev is None:
228
+ return dev
229
+ await self._event.wait() # wait for another change in the set of known devices
230
+
231
+
232
+ class Device():
233
+ def __init__(self, lib, handle: int, serial_number: str, product_string: str, is_bootloader: bool):
234
+ self._lib = lib
235
+ self._handle = handle
236
+ self.serial_number = serial_number
237
+ self.product_string = product_string
238
+ self.is_bootloader = is_bootloader
239
+ self.hw_version = None
240
+ self._connection_future: asyncio.Future = asyncio.Future()
241
+ self._on_installation_progress_cb: Optional[Callable[[bool, str, int, int], None]] = None
242
+
243
+ @staticmethod
244
+ def _on_connected(device: int, hw_version, manufacturer: bytes):
245
+ _dev_map[device].hw_version = HwVersion.from_tuple((hw_version.contents.product_line, hw_version.contents.version, hw_version.contents.variant))
246
+ _dev_map[device]._connection_future.set_result(None)
247
+
248
+ @staticmethod
249
+ def _on_connection_failed(device: int, msg: bytes):
250
+ # This can be called when the connection has already be established.
251
+ if not _dev_map[device]._connection_future.done():
252
+ _dev_map[device]._connection_future.set_exception(Exception(msg.decode()))
253
+
254
+ @staticmethod
255
+ def _on_installation_progress(device: int, new_action_group: bool, action_string: bytes, action_index: int, n_actions: int):
256
+ _dev_map[device]._on_installation_progress_cb(new_action_group, action_string.decode(), action_index, n_actions)
257
+
258
+ @staticmethod
259
+ def _on_installation_done(device: int, msg: bytes):
260
+ if len(msg) == 0:
261
+ _dev_map[device]._installation_done.set_result(None)
262
+ else:
263
+ _dev_map[device]._installation_done.set_exception(Exception(msg.decode()))
264
+
265
+ async def connect_bootloader(self) -> None:
266
+ assert self._lib.libodrive_connect_bootloader(self._handle, _on_connected, _on_connection_failed) == 0
267
+ await self._connection_future
268
+
269
+ async def run_installation(self, fw: 'Firmware', erase_all: bool, on_installation_progress: Callable[[bool, str, int, int], None]) -> None:
270
+ self._installation_done: asyncio.Future[None] = asyncio.Future()
271
+ self._on_installation_progress_cb = on_installation_progress
272
+ assert self._lib.libodrive_start_installation(self._handle, fw._handle, erase_all, _on_installation_progress, _on_installation_done) == 0
273
+ await self._installation_done
274
+
275
+
276
+ class Firmware():
277
+ def __init__(self, lib, handle: ctypes.c_void_p, manifest: _FwManifest):
278
+ self._lib = lib
279
+ self._handle = handle
280
+ self._manifest = manifest
281
+
282
+ def __enter__(self):
283
+ return self
284
+
285
+ def __exit__(self, exc_type, exc_val, exc_tb):
286
+ self._lib.libodrive_close_firmware(self._handle)
287
+
288
+ @property
289
+ def fw_version(self) -> Tuple[int, int, int]:
290
+ return (
291
+ self._manifest.fw_version_major,
292
+ self._manifest.fw_version_minor,
293
+ self._manifest.fw_version_revision,
294
+ )
295
+
296
+ @property
297
+ def hw_version(self) -> HwVersion:
298
+ return HwVersion.from_tuple((
299
+ self._manifest.hw_version.product_line,
300
+ self._manifest.hw_version.version,
301
+ self._manifest.hw_version.variant,
302
+ ))
303
+
304
+ @property
305
+ def build(self) -> bytes:
306
+ return bytes(self._manifest.build)
307
+
308
+
309
+ _TOnFoundDevice = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_bool, ctypes.c_void_p, ctypes.c_char_p)
310
+ _TOnLostDevice = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p)
311
+ _TOnConnected = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(_HwVersion), ctypes.c_void_p)
312
+ _TOnConnectionFailed = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_char_p)
313
+ _TOnInstallationProgress = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_bool, ctypes.c_char_p, ctypes.c_int, ctypes.c_int)
314
+ _TOnInstallationDone = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_char_p)
315
+
316
+ # Must keep references to FFI-wrapped callbacks to keep them alive
317
+ _on_found_device = _TOnFoundDevice(Interface._on_found_device)
318
+ _on_lost_device = _TOnLostDevice(Interface._on_lost_device)
319
+ _on_connected = _TOnConnected(Device._on_connected)
320
+ _on_connection_failed = _TOnConnectionFailed(Device._on_connection_failed)
321
+ _on_installation_progress = _TOnInstallationProgress(Device._on_installation_progress)
322
+ _on_installation_done = _TOnInstallationDone(Device._on_installation_done)
323
+
324
+ _dev_map: Dict[int, Device] = {}
325
+ _intf_map: Dict[int, Interface] = {}
@@ -0,0 +1,4 @@
1
+
2
+ from .utils import Logger
3
+ from .shell import launch_shell
4
+ from .libfibre import Domain, ObjectLostError