motor-drive-layer 0.5.10__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.
Files changed (45) hide show
  1. motor_drive_layer-0.5.10/LICENSE +21 -0
  2. motor_drive_layer-0.5.10/PKG-INFO +90 -0
  3. motor_drive_layer-0.5.10/README.md +65 -0
  4. motor_drive_layer-0.5.10/pyproject.toml +50 -0
  5. motor_drive_layer-0.5.10/setup.cfg +4 -0
  6. motor_drive_layer-0.5.10/setup.py +141 -0
  7. motor_drive_layer-0.5.10/src/motor_drive_layer/__init__.py +109 -0
  8. motor_drive_layer-0.5.10/src/motor_drive_layer/__init__.pyi +48 -0
  9. motor_drive_layer-0.5.10/src/motor_drive_layer/__main__.py +5 -0
  10. motor_drive_layer-0.5.10/src/motor_drive_layer/_version.py +1 -0
  11. motor_drive_layer-0.5.10/src/motor_drive_layer/abi.py +354 -0
  12. motor_drive_layer-0.5.10/src/motor_drive_layer/abi.pyi +21 -0
  13. motor_drive_layer-0.5.10/src/motor_drive_layer/cli/__init__.py +3 -0
  14. motor_drive_layer-0.5.10/src/motor_drive_layer/cli/__main__.py +5 -0
  15. motor_drive_layer-0.5.10/src/motor_drive_layer/cli/main.py +400 -0
  16. motor_drive_layer-0.5.10/src/motor_drive_layer/cli/platform_hints.py +179 -0
  17. motor_drive_layer-0.5.10/src/motor_drive_layer/core.py +659 -0
  18. motor_drive_layer-0.5.10/src/motor_drive_layer/core.pyi +131 -0
  19. motor_drive_layer-0.5.10/src/motor_drive_layer/damiao_registers.py +89 -0
  20. motor_drive_layer-0.5.10/src/motor_drive_layer/damiao_registers.pyi +39 -0
  21. motor_drive_layer-0.5.10/src/motor_drive_layer/dm_device_runtime.py +267 -0
  22. motor_drive_layer-0.5.10/src/motor_drive_layer/dm_device_runtime.pyi +9 -0
  23. motor_drive_layer-0.5.10/src/motor_drive_layer/errors.py +10 -0
  24. motor_drive_layer-0.5.10/src/motor_drive_layer/errors.pyi +3 -0
  25. motor_drive_layer-0.5.10/src/motor_drive_layer/models.py +76 -0
  26. motor_drive_layer-0.5.10/src/motor_drive_layer/models.pyi +99 -0
  27. motor_drive_layer-0.5.10/src/motor_drive_layer/py.typed +1 -0
  28. motor_drive_layer-0.5.10/src/motor_drive_layer/stress.py +244 -0
  29. motor_drive_layer-0.5.10/src/motor_drive_layer/stress.pyi +22 -0
  30. motor_drive_layer-0.5.10/src/motor_drive_layer.egg-info/PKG-INFO +90 -0
  31. motor_drive_layer-0.5.10/src/motor_drive_layer.egg-info/SOURCES.txt +43 -0
  32. motor_drive_layer-0.5.10/src/motor_drive_layer.egg-info/dependency_links.txt +1 -0
  33. motor_drive_layer-0.5.10/src/motor_drive_layer.egg-info/entry_points.txt +5 -0
  34. motor_drive_layer-0.5.10/src/motor_drive_layer.egg-info/requires.txt +3 -0
  35. motor_drive_layer-0.5.10/src/motor_drive_layer.egg-info/top_level.txt +1 -0
  36. motor_drive_layer-0.5.10/tests/test_abi_paths.py +11 -0
  37. motor_drive_layer-0.5.10/tests/test_api_surface.py +34 -0
  38. motor_drive_layer-0.5.10/tests/test_controller_group_api.py +186 -0
  39. motor_drive_layer-0.5.10/tests/test_dm_device_api.py +101 -0
  40. motor_drive_layer-0.5.10/tests/test_feedback_stats_api.py +214 -0
  41. motor_drive_layer-0.5.10/tests/test_import.py +8 -0
  42. motor_drive_layer-0.5.10/tests/test_platform_hints.py +26 -0
  43. motor_drive_layer-0.5.10/tests/test_register_metadata.py +14 -0
  44. motor_drive_layer-0.5.10/tests/test_stress_tool.py +112 -0
  45. motor_drive_layer-0.5.10/tests/test_typing_surface.py +35 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Motor-Drive-Layer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.4
2
+ Name: motor-drive-layer
3
+ Version: 0.5.10
4
+ Summary: Python SDK for the Motor-Drive-Layer C++ ABI
5
+ Author: Motor-Drive-Layer contributors
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/TheYoungW/Motor-Drive-Layer
8
+ Project-URL: Repository, https://github.com/TheYoungW/Motor-Drive-Layer
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Operating System :: MacOS
17
+ Classifier: Operating System :: Microsoft :: Windows
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Provides-Extra: test
23
+ Requires-Dist: pytest>=7; extra == "test"
24
+ Dynamic: license-file
25
+
26
+ # motor-drive-layer
27
+
28
+ Python bindings for the Motor-Drive-Layer native C++ Damiao motor driver.
29
+
30
+ The package loads the bundled C ABI library through Python `ctypes` and exposes
31
+ SocketCAN, SocketCAN-FD, Damiao serial bridge, and optional DM_Device transports.
32
+ Published wheels cover Linux x86_64/ARM64, macOS Intel/Apple Silicon, and Windows x64. The serial
33
+ transport is cross-platform; SocketCAN transports remain Linux-only.
34
+
35
+ DM-USB2FDCAN Dual works with its original `dual_app` firmware through
36
+ `Controller.from_dm_device(device="usb2canfd-dual", channel=0, bitrate=1_000_000,
37
+ data_bitrate=5_000_000)`. CH0 and CH1 are independently selectable, old positional arguments remain
38
+ compatible, and the loader probes both vendor v1.0 and v1.1 ABIs. Install the separate vendor
39
+ runtime with `motor-drive-layer-install-dm-device --download` or set `MOTOR_DM_DEVICE_LIB`.
40
+ On final Controller close, v1.1 fully tears down its device session. The Linux v1.0 runtime instead
41
+ closes the selected channels and motor-layer resources but retains its legacy context/device until
42
+ process exit, allowing a later `Controller.from_dm_device(...)` call to reconnect reliably in the
43
+ same Python process. A process-scope cleanup closes and destroys those retained vendor objects at
44
+ normal process exit.
45
+
46
+ Use `Motor.request_fresh_state(timeout_ms=50)` when the caller must wait for a newly requested
47
+ feedback frame. For multiple motors, `Controller.request_feedback_all(timeout_ms=50)` sends every
48
+ request with the configured TX pacing and waits against one shared deadline; a timeout reports the
49
+ missing motor IDs. The lower-level `request_feedback()`, `get_state()`, and
50
+ `poll_feedback_once()` methods remain non-blocking asynchronous/cache operations.
51
+
52
+ For synchronized multi-channel control, `ControllerGroup([ch0, ch1])` creates one persistent
53
+ native worker per Controller. `send_pos_vel([...])` and `send_mit([...])` dispatch typed
54
+ `PosVelCommand` or `MitCommand` values across their owning controllers, overlap independent TX
55
+ pacing waits, and return after every controller finishes. Errors retain the controller/channel,
56
+ motor ID, and native reason.
57
+ For fixed high-rate layouts, `prepare_pos_vel(motors)` and `prepare_mit(motors)` reuse one validated
58
+ ctypes command array across calls. `Controller.transport_capabilities()` reports the active
59
+ transport's CAN-FD, channel, parallel-send, reconnect, session-reuse, and timestamp capabilities.
60
+ `Controller.transport_health()` reports live connection state, TX/RX counters, errors, and last
61
+ activity ages without adding robot-product policy to the transport layer.
62
+
63
+ The `motor-drive-layer-stress` command provides a feedback-only DM_Device load/reconnect test and
64
+ reports latency plus Linux file-descriptor/thread counts. It never enables or commands motors.
65
+
66
+ The wheel includes `py.typed` and `.pyi` declarations for editor completion and static type
67
+ checking. The main public APIs are:
68
+
69
+ - `Controller(...)`, `from_socketcanfd(...)`, `from_dm_serial(...)`, and `from_dm_device(...)`.
70
+ - `Controller.add_damiao_motor(...)`, `enable_all()`, `disable_all()`,
71
+ `request_feedback_all()`, `set_tx_gap_us()`, `transport_capabilities()`, `shutdown()`, and
72
+ `close_bus()`.
73
+ - `ControllerGroup(...)`, `send_pos_vel(...)`, and `send_mit(...)` for persistent native
74
+ multi-controller dispatch, plus reusable prepared batches for fixed layouts.
75
+ - `Motor.enable()`, `disable()`, `ensure_mode()`, all four control-mode send methods,
76
+ fresh/cached feedback methods, typed register access, parameter aliases, and
77
+ `store_parameters()`.
78
+ - `MitCommand`, `PosVelCommand`, `MotorState`, `FeedbackStats`, `Mode`, register
79
+ metadata/constants, and SDK exception classes.
80
+
81
+ `Motor` is a logical child of its creating `Controller`. After the parent closes, motor operations
82
+ raise `CallError`; `motor.close()` can still release the handle. Both classes support context
83
+ managers. Leaving a Motor context only frees its handle, while leaving a Controller context attempts
84
+ to disable all motors before closing the bus.
85
+
86
+ See the [project README](https://github.com/TheYoungW/Motor-Drive-Layer)
87
+ for build instructions, architecture, configuration, safety guidance, and examples.
88
+
89
+ This package is distributed under the MIT License. The optional DM_Device vendor
90
+ runtime is distributed separately and may have its own license terms.
@@ -0,0 +1,65 @@
1
+ # motor-drive-layer
2
+
3
+ Python bindings for the Motor-Drive-Layer native C++ Damiao motor driver.
4
+
5
+ The package loads the bundled C ABI library through Python `ctypes` and exposes
6
+ SocketCAN, SocketCAN-FD, Damiao serial bridge, and optional DM_Device transports.
7
+ Published wheels cover Linux x86_64/ARM64, macOS Intel/Apple Silicon, and Windows x64. The serial
8
+ transport is cross-platform; SocketCAN transports remain Linux-only.
9
+
10
+ DM-USB2FDCAN Dual works with its original `dual_app` firmware through
11
+ `Controller.from_dm_device(device="usb2canfd-dual", channel=0, bitrate=1_000_000,
12
+ data_bitrate=5_000_000)`. CH0 and CH1 are independently selectable, old positional arguments remain
13
+ compatible, and the loader probes both vendor v1.0 and v1.1 ABIs. Install the separate vendor
14
+ runtime with `motor-drive-layer-install-dm-device --download` or set `MOTOR_DM_DEVICE_LIB`.
15
+ On final Controller close, v1.1 fully tears down its device session. The Linux v1.0 runtime instead
16
+ closes the selected channels and motor-layer resources but retains its legacy context/device until
17
+ process exit, allowing a later `Controller.from_dm_device(...)` call to reconnect reliably in the
18
+ same Python process. A process-scope cleanup closes and destroys those retained vendor objects at
19
+ normal process exit.
20
+
21
+ Use `Motor.request_fresh_state(timeout_ms=50)` when the caller must wait for a newly requested
22
+ feedback frame. For multiple motors, `Controller.request_feedback_all(timeout_ms=50)` sends every
23
+ request with the configured TX pacing and waits against one shared deadline; a timeout reports the
24
+ missing motor IDs. The lower-level `request_feedback()`, `get_state()`, and
25
+ `poll_feedback_once()` methods remain non-blocking asynchronous/cache operations.
26
+
27
+ For synchronized multi-channel control, `ControllerGroup([ch0, ch1])` creates one persistent
28
+ native worker per Controller. `send_pos_vel([...])` and `send_mit([...])` dispatch typed
29
+ `PosVelCommand` or `MitCommand` values across their owning controllers, overlap independent TX
30
+ pacing waits, and return after every controller finishes. Errors retain the controller/channel,
31
+ motor ID, and native reason.
32
+ For fixed high-rate layouts, `prepare_pos_vel(motors)` and `prepare_mit(motors)` reuse one validated
33
+ ctypes command array across calls. `Controller.transport_capabilities()` reports the active
34
+ transport's CAN-FD, channel, parallel-send, reconnect, session-reuse, and timestamp capabilities.
35
+ `Controller.transport_health()` reports live connection state, TX/RX counters, errors, and last
36
+ activity ages without adding robot-product policy to the transport layer.
37
+
38
+ The `motor-drive-layer-stress` command provides a feedback-only DM_Device load/reconnect test and
39
+ reports latency plus Linux file-descriptor/thread counts. It never enables or commands motors.
40
+
41
+ The wheel includes `py.typed` and `.pyi` declarations for editor completion and static type
42
+ checking. The main public APIs are:
43
+
44
+ - `Controller(...)`, `from_socketcanfd(...)`, `from_dm_serial(...)`, and `from_dm_device(...)`.
45
+ - `Controller.add_damiao_motor(...)`, `enable_all()`, `disable_all()`,
46
+ `request_feedback_all()`, `set_tx_gap_us()`, `transport_capabilities()`, `shutdown()`, and
47
+ `close_bus()`.
48
+ - `ControllerGroup(...)`, `send_pos_vel(...)`, and `send_mit(...)` for persistent native
49
+ multi-controller dispatch, plus reusable prepared batches for fixed layouts.
50
+ - `Motor.enable()`, `disable()`, `ensure_mode()`, all four control-mode send methods,
51
+ fresh/cached feedback methods, typed register access, parameter aliases, and
52
+ `store_parameters()`.
53
+ - `MitCommand`, `PosVelCommand`, `MotorState`, `FeedbackStats`, `Mode`, register
54
+ metadata/constants, and SDK exception classes.
55
+
56
+ `Motor` is a logical child of its creating `Controller`. After the parent closes, motor operations
57
+ raise `CallError`; `motor.close()` can still release the handle. Both classes support context
58
+ managers. Leaving a Motor context only frees its handle, while leaving a Controller context attempts
59
+ to disable all motors before closing the bus.
60
+
61
+ See the [project README](https://github.com/TheYoungW/Motor-Drive-Layer)
62
+ for build instructions, architecture, configuration, safety guidance, and examples.
63
+
64
+ This package is distributed under the MIT License. The optional DM_Device vendor
65
+ runtime is distributed separately and may have its own license terms.
@@ -0,0 +1,50 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0.3", "wheel>=0.43"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "motor-drive-layer"
7
+ dynamic = ["version"]
8
+ description = "Python SDK for the Motor-Drive-Layer C++ ABI"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Motor-Drive-Layer contributors" }]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3 :: Only",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Programming Language :: Python :: 3.14",
22
+ "Operating System :: MacOS",
23
+ "Operating System :: Microsoft :: Windows",
24
+ "Operating System :: POSIX :: Linux",
25
+ ]
26
+
27
+ [tool.setuptools.dynamic]
28
+ version = { attr = "motor_drive_layer._version.VERSION" }
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/TheYoungW/Motor-Drive-Layer"
32
+ Repository = "https://github.com/TheYoungW/Motor-Drive-Layer"
33
+
34
+ [project.scripts]
35
+ motor-drive-layer = "motor_drive_layer.cli:main"
36
+ motor-drive-layer-cli = "motor_drive_layer.cli:main"
37
+ motor-drive-layer-install-dm-device = "motor_drive_layer.dm_device_runtime:main"
38
+ motor-drive-layer-stress = "motor_drive_layer.stress:main"
39
+
40
+ [project.optional-dependencies]
41
+ test = ["pytest>=7"]
42
+
43
+ [tool.setuptools]
44
+ package-dir = {"" = "src"}
45
+
46
+ [tool.setuptools.packages.find]
47
+ where = ["src"]
48
+
49
+ [tool.setuptools.package-data]
50
+ motor_drive_layer = ["lib/*", "lib/dm_device/*", "*.pyi", "py.typed"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,141 @@
1
+ import os
2
+ import platform
3
+ import shutil
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ from setuptools import Distribution, setup
8
+ from setuptools.command.bdist_wheel import bdist_wheel as _bdist_wheel
9
+ from setuptools.command.build_py import build_py as _build_py
10
+
11
+
12
+ def _platform_lib_name() -> str:
13
+ if sys.platform.startswith("win"):
14
+ return "motor_abi.dll"
15
+ if sys.platform == "darwin":
16
+ return "libmotor_abi.dylib"
17
+ return "libmotor_abi.so"
18
+
19
+
20
+ def _dm_device_platform_relpath() -> Path | None:
21
+ machine = platform.machine().lower()
22
+ if sys.platform.startswith("linux"):
23
+ if machine in {"x86_64", "amd64"}:
24
+ return Path("linux/x86_64/libdm_device.so")
25
+ if machine in {"aarch64", "arm64"}:
26
+ return Path("linux/arm64/libdm_device.so")
27
+ if sys.platform == "darwin":
28
+ if machine in {"arm64", "aarch64"}:
29
+ return Path("macos/arm64/libdm_device.dylib")
30
+ if machine in {"x86_64", "amd64"}:
31
+ return Path("macos/x86_64/libdm_device.dylib")
32
+ if sys.platform.startswith("win") and machine in {"x86_64", "amd64"}:
33
+ return Path("windows/msvc/dm_device.dll")
34
+ return None
35
+
36
+
37
+ def _candidate_dm_device_paths() -> list[Path]:
38
+ here = Path(__file__).resolve()
39
+ repo_root = here.parents[2]
40
+ rel = _dm_device_platform_relpath()
41
+ candidates: list[Path] = []
42
+
43
+ env = os.getenv("MOTOR_DM_DEVICE_LIB")
44
+ if env:
45
+ candidates.append(Path(env).expanduser())
46
+
47
+ if rel is None:
48
+ return candidates
49
+
50
+ candidates.append(repo_root / "third_party" / "dm_device" / "v1.1.0" / rel)
51
+ candidates.append(repo_root / "dm-device-sdk" / "C&C++" / "lib" / "v1.1.0" / rel)
52
+ candidates.append(repo_root.parent / "dm-device-sdk" / "C&C++" / "lib" / "v1.1.0" / rel)
53
+ if sys.platform.startswith("linux"):
54
+ legacy_rel = Path("linux/libdm_device.so") if platform.machine().lower() in {"x86_64", "amd64"} else Path("aarch64/libdm_device.so")
55
+ candidates.append(repo_root / "third_party" / "dm_device" / "v1.0.0" / legacy_rel)
56
+ candidates.append(repo_root / "dm-device-sdk" / "C&C++" / "lib" / "v1.0.0" / legacy_rel)
57
+ candidates.append(repo_root.parent / "dm-device-sdk" / "C&C++" / "lib" / "v1.0.0" / legacy_rel)
58
+ candidates.append(here.parent / "src" / "motor_drive_layer" / "lib" / "dm_device" / rel.name)
59
+ return candidates
60
+
61
+
62
+ def _bundle_dm_device_runtime() -> bool:
63
+ raw = os.getenv("MOTOR_DM_DEVICE_BUNDLE", "0").strip().lower()
64
+ return raw not in {"0", "false", "off", "no"}
65
+
66
+
67
+ def _find_dm_device_path() -> Path | None:
68
+ if not _bundle_dm_device_runtime():
69
+ return None
70
+ for path in _candidate_dm_device_paths():
71
+ if path.exists():
72
+ return path
73
+ return None
74
+
75
+
76
+ def _candidate_abi_paths() -> list[Path]:
77
+ here = Path(__file__).resolve()
78
+ repo_root = here.parents[2]
79
+ lib_name = _platform_lib_name()
80
+ candidates: list[Path] = []
81
+
82
+ env = os.getenv("MOTOR_DRIVE_LAYER_LIB")
83
+ if env:
84
+ candidates.append(Path(env).expanduser())
85
+
86
+ candidates.append(repo_root / "cpp_damiao" / "build" / lib_name)
87
+ candidates.append(repo_root / "cpp_damiao" / "build" / "Release" / lib_name)
88
+ candidates.append(here.parent / "src" / "motor_drive_layer" / "lib" / lib_name)
89
+ return candidates
90
+
91
+
92
+ def _resolve_abi_path() -> Path:
93
+ for p in _candidate_abi_paths():
94
+ if p.exists():
95
+ return p
96
+ tried = "\n".join(f"- {p}" for p in _candidate_abi_paths())
97
+ raise RuntimeError(
98
+ "Cannot locate motor_abi shared library for wheel build.\n"
99
+ f"Tried:\n{tried}\n"
100
+ "Build ABI first (`cmake -S cpp_damiao -B cpp_damiao/build && cmake --build cpp_damiao/build`) "
101
+ "or set MOTOR_DRIVE_LAYER_LIB."
102
+ )
103
+
104
+
105
+ class BuildPyWithAbi(_build_py):
106
+ def run(self):
107
+ super().run()
108
+ abi_src = _resolve_abi_path()
109
+ dst_dir = Path(self.build_lib) / "motor_drive_layer" / "lib"
110
+ if dst_dir.exists():
111
+ shutil.rmtree(dst_dir)
112
+ dst_dir.mkdir(parents=True, exist_ok=True)
113
+ shutil.copy2(abi_src, dst_dir / abi_src.name)
114
+
115
+ dm_src = _find_dm_device_path()
116
+ if dm_src is not None:
117
+ dm_dst_dir = dst_dir / "dm_device"
118
+ dm_dst_dir.mkdir(parents=True, exist_ok=True)
119
+ shutil.copy2(dm_src, dm_dst_dir / dm_src.name)
120
+
121
+ class BdistWheelWithAbi(_bdist_wheel):
122
+ def finalize_options(self):
123
+ super().finalize_options()
124
+ self.root_is_pure = False
125
+
126
+ def get_tag(self):
127
+ _, _, platform_tag = super().get_tag()
128
+ return "py3", "none", platform_tag
129
+
130
+
131
+ class BinaryDistribution(Distribution):
132
+ """Install the ctypes shared library into platlib, not purelib."""
133
+
134
+ def has_ext_modules(self) -> bool:
135
+ return True
136
+
137
+
138
+ setup(
139
+ cmdclass={"build_py": BuildPyWithAbi, "bdist_wheel": BdistWheelWithAbi},
140
+ distclass=BinaryDistribution,
141
+ )
@@ -0,0 +1,109 @@
1
+ from importlib import import_module
2
+
3
+ from .abi import abi_capabilities, abi_version
4
+ from .core import (
5
+ Controller,
6
+ ControllerGroup,
7
+ Motor,
8
+ PreparedMitBatch,
9
+ PreparedPosVelBatch,
10
+ )
11
+ from .dm_device_runtime import ensure_dm_device_runtime
12
+ from .errors import AbiLoadError, CallError, MotorBridgeError
13
+ from .models import (
14
+ FeedbackStats,
15
+ MitCommand,
16
+ Mode,
17
+ MotorState,
18
+ PosVelCommand,
19
+ TransportCapabilities,
20
+ TransportHealth,
21
+ )
22
+ from ._version import VERSION
23
+
24
+
25
+ _REGISTER_EXPORTS = {
26
+ "RegisterSpec",
27
+ "DAMIAO_RW_REGISTERS",
28
+ "DAMIAO_HIGH_IMPACT_RIDS",
29
+ "DAMIAO_PROTECTION_RIDS",
30
+ "get_damiao_register_spec",
31
+ "RID_CTRL_MODE",
32
+ "RID_MST_ID",
33
+ "RID_ESC_ID",
34
+ "RID_TIMEOUT",
35
+ "RID_PMAX",
36
+ "RID_VMAX",
37
+ "RID_TMAX",
38
+ "RID_KP_ASR",
39
+ "RID_KI_ASR",
40
+ "RID_KP_APR",
41
+ "RID_KI_APR",
42
+ "MODE_MIT",
43
+ "MODE_POS_VEL",
44
+ "MODE_VEL",
45
+ "MODE_FORCE_POS",
46
+ }
47
+
48
+
49
+ def __getattr__(name: str):
50
+ if name in _REGISTER_EXPORTS:
51
+ module = import_module(".damiao_registers", __name__)
52
+ value = getattr(module, name)
53
+ globals()[name] = value
54
+ return value
55
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
56
+
57
+
58
+ def __dir__() -> list[str]:
59
+ return sorted(set(globals()) | _REGISTER_EXPORTS)
60
+
61
+
62
+ def get_version() -> str:
63
+ return VERSION
64
+
65
+
66
+ __version__ = get_version()
67
+
68
+ __all__ = [
69
+ "__version__",
70
+ "get_version",
71
+ "abi_version",
72
+ "abi_capabilities",
73
+ "ensure_dm_device_runtime",
74
+ "Controller",
75
+ "ControllerGroup",
76
+ "PreparedMitBatch",
77
+ "PreparedPosVelBatch",
78
+ "Motor",
79
+ "Mode",
80
+ "MotorState",
81
+ "FeedbackStats",
82
+ "MitCommand",
83
+ "PosVelCommand",
84
+ "TransportCapabilities",
85
+ "TransportHealth",
86
+ "RegisterSpec",
87
+ "DAMIAO_RW_REGISTERS",
88
+ "DAMIAO_HIGH_IMPACT_RIDS",
89
+ "DAMIAO_PROTECTION_RIDS",
90
+ "get_damiao_register_spec",
91
+ "RID_CTRL_MODE",
92
+ "RID_MST_ID",
93
+ "RID_ESC_ID",
94
+ "RID_TIMEOUT",
95
+ "RID_PMAX",
96
+ "RID_VMAX",
97
+ "RID_TMAX",
98
+ "RID_KP_ASR",
99
+ "RID_KI_ASR",
100
+ "RID_KP_APR",
101
+ "RID_KI_APR",
102
+ "MODE_MIT",
103
+ "MODE_POS_VEL",
104
+ "MODE_VEL",
105
+ "MODE_FORCE_POS",
106
+ "MotorBridgeError",
107
+ "AbiLoadError",
108
+ "CallError",
109
+ ]
@@ -0,0 +1,48 @@
1
+ from .abi import abi_capabilities as abi_capabilities, abi_version as abi_version
2
+ from .core import (
3
+ Controller as Controller,
4
+ ControllerGroup as ControllerGroup,
5
+ Motor as Motor,
6
+ PreparedMitBatch as PreparedMitBatch,
7
+ PreparedPosVelBatch as PreparedPosVelBatch,
8
+ )
9
+ from .damiao_registers import (
10
+ DAMIAO_HIGH_IMPACT_RIDS as DAMIAO_HIGH_IMPACT_RIDS,
11
+ DAMIAO_PROTECTION_RIDS as DAMIAO_PROTECTION_RIDS,
12
+ DAMIAO_RW_REGISTERS as DAMIAO_RW_REGISTERS,
13
+ MODE_FORCE_POS as MODE_FORCE_POS,
14
+ MODE_MIT as MODE_MIT,
15
+ MODE_POS_VEL as MODE_POS_VEL,
16
+ MODE_VEL as MODE_VEL,
17
+ RID_CTRL_MODE as RID_CTRL_MODE,
18
+ RID_ESC_ID as RID_ESC_ID,
19
+ RID_KI_APR as RID_KI_APR,
20
+ RID_KI_ASR as RID_KI_ASR,
21
+ RID_KP_APR as RID_KP_APR,
22
+ RID_KP_ASR as RID_KP_ASR,
23
+ RID_MST_ID as RID_MST_ID,
24
+ RID_PMAX as RID_PMAX,
25
+ RID_TMAX as RID_TMAX,
26
+ RID_TIMEOUT as RID_TIMEOUT,
27
+ RID_VMAX as RID_VMAX,
28
+ RegisterSpec as RegisterSpec,
29
+ get_damiao_register_spec as get_damiao_register_spec,
30
+ )
31
+ from .dm_device_runtime import ensure_dm_device_runtime as ensure_dm_device_runtime
32
+ from .errors import (
33
+ AbiLoadError as AbiLoadError,
34
+ CallError as CallError,
35
+ MotorBridgeError as MotorBridgeError,
36
+ )
37
+ from .models import (
38
+ FeedbackStats as FeedbackStats,
39
+ MitCommand as MitCommand,
40
+ Mode as Mode,
41
+ MotorState as MotorState,
42
+ PosVelCommand as PosVelCommand,
43
+ TransportCapabilities as TransportCapabilities,
44
+ TransportHealth as TransportHealth,
45
+ )
46
+
47
+ __version__: str
48
+ def get_version() -> str: ...
@@ -0,0 +1,5 @@
1
+ from .cli import main
2
+
3
+
4
+ if __name__ == "__main__":
5
+ main()
@@ -0,0 +1 @@
1
+ VERSION = "0.5.10"