lockwire 0.2.0__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.
Files changed (52) hide show
  1. lockwire/__init__.py +9 -0
  2. lockwire/__main__.py +9 -0
  3. lockwire/advice.py +119 -0
  4. lockwire/api.py +1216 -0
  5. lockwire/api_payload.py +985 -0
  6. lockwire/bench.py +182 -0
  7. lockwire/blame.py +100 -0
  8. lockwire/catalog.py +100 -0
  9. lockwire/catalog_seed.yaml +545 -0
  10. lockwire/checks.py +3483 -0
  11. lockwire/cli.py +1799 -0
  12. lockwire/cli_line.py +73 -0
  13. lockwire/constraints.py +313 -0
  14. lockwire/copy.py +302 -0
  15. lockwire/describe.py +682 -0
  16. lockwire/edit.py +2098 -0
  17. lockwire/emit_ts.py +150 -0
  18. lockwire/export_mhs.py +115 -0
  19. lockwire/factdiff.py +254 -0
  20. lockwire/forces.py +334 -0
  21. lockwire/gitref.py +188 -0
  22. lockwire/hw.py +388 -0
  23. lockwire/mcp_server.py +276 -0
  24. lockwire/mechanism.py +379 -0
  25. lockwire/mesh_dae.py +555 -0
  26. lockwire/mesh_simple.py +198 -0
  27. lockwire/page.py +1485 -0
  28. lockwire/plan.py +849 -0
  29. lockwire/plan_payload.py +151 -0
  30. lockwire/plugins.py +70 -0
  31. lockwire/report.py +848 -0
  32. lockwire/report_html.py +707 -0
  33. lockwire/report_json.py +168 -0
  34. lockwire/review_draft.py +315 -0
  35. lockwire/scan.py +732 -0
  36. lockwire/scene.py +535 -0
  37. lockwire/scene_import.py +309 -0
  38. lockwire/scene_urdf.py +750 -0
  39. lockwire/scene_view.py +376 -0
  40. lockwire/schema.py +3616 -0
  41. lockwire/sdf.py +240 -0
  42. lockwire/serve.py +1256 -0
  43. lockwire/start_operation.py +92 -0
  44. lockwire/statics.py +77 -0
  45. lockwire/units.py +115 -0
  46. lockwire/urdf.py +348 -0
  47. lockwire/vcs.py +402 -0
  48. lockwire/whatif.py +161 -0
  49. lockwire-0.2.0.dist-info/METADATA +192 -0
  50. lockwire-0.2.0.dist-info/RECORD +52 -0
  51. lockwire-0.2.0.dist-info/WHEEL +4 -0
  52. lockwire-0.2.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,192 @@
1
+ Metadata-Version: 2.4
2
+ Name: lockwire
3
+ Version: 0.2.0
4
+ Summary: The physical-assumption contract for robots: physics lockfile + preflight gate
5
+ Keywords: robotics,urdf,ros,preflight,lockfile,ci,hardware,drone
6
+ Author: Shivansh
7
+ License-Expression: Apache-2.0
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: License :: OSI Approved :: Apache Software License
10
+ Classifier: Intended Audience :: Manufacturing
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Topic :: Scientific/Engineering
15
+ Classifier: Topic :: Software Development :: Quality Assurance
16
+ Classifier: Topic :: Software Development :: Testing
17
+ Requires-Dist: numpy>=2.5.1
18
+ Requires-Dist: pydantic>=2.13.4
19
+ Requires-Dist: rich>=15.0.0
20
+ Requires-Dist: ruamel-yaml>=0.19.1
21
+ Requires-Dist: typer>=0.26.8
22
+ Requires-Dist: mcp>=2.1.1 ; extra == 'mcp'
23
+ Requires-Dist: fastapi>=0.141.1 ; extra == 'serve'
24
+ Requires-Dist: uvicorn>=0.52.4 ; extra == 'serve'
25
+ Requires-Python: >=3.12
26
+ Project-URL: Homepage, https://github.com/Shivansh2703/lockwire
27
+ Project-URL: Repository, https://github.com/Shivansh2703/lockwire
28
+ Project-URL: Issues, https://github.com/Shivansh2703/lockwire/issues
29
+ Provides-Extra: mcp
30
+ Provides-Extra: serve
31
+ Description-Content-Type: text/markdown
32
+
33
+ # lockwire
34
+
35
+ **Find out your robot won't work before you buy the parts.**
36
+
37
+ Every robot is a stack of numbers from a dozen datasheets: what each part weighs, what each power rail can supply, how much the bus can carry, how much torque each joint gets. Lockwire keeps those numbers in one file and tells you the moment they stop adding up. Use it while you're still planning a build, then keep it in CI so a part swap can't quietly break it later.
38
+
39
+ Prefer clicking to typing? The free desktop app does the same visually: Open, Design, Declare, Plan, Check.
40
+
41
+ ## Plan a robot from scratch
42
+
43
+ Start from a URDF (one line is enough), add the parts you're thinking of buying, and check:
44
+
45
+ ```bash
46
+ pip install "git+https://github.com/Shivansh2703/lockwire@v0.2.0" # not on PyPI
47
+ echo '<robot name="rover"><link name="base_link"/></robot>' > robot.urdf
48
+ lockwire start robot.urdf
49
+ lockwire add rail lockwire.yaml --id rail.5v --voltage "5 V" --capacity "15 W" --source "UBEC datasheet: 5 V 3 A" --witness Sam
50
+ lockwire add device lockwire.yaml --id dev.pi5 --family fam.pi --model mod.pi5 --rail rail.5v --draw "12 W" --mass "46 g" --mount-frame base_link --owner you --source "Raspberry Pi 5 product brief" --witness Sam
51
+ lockwire add device lockwire.yaml --id dev.servo --family fam.servo --model mod.mg996r --rail rail.5v --draw "6 W" --mass "55 g" --mount-frame base_link --owner you --source "MG996R datasheet" --witness Sam
52
+ lockwire add budget lockwire.yaml --id bud.5v --kind rail_power --rail rail.5v --limit "15 W" --owner you --source "UBEC datasheet" --witness Sam
53
+ lockwire check lockwire.yaml
54
+ ```
55
+
56
+ `--witness` is the person who read the number off the datasheet; use a real name, not "you".
57
+
58
+ ```console
59
+ VIOLATION budgets/bud.5v — observed 18.00 W, expected <= 15.00 W (Δ +3.00 W (+20%))
60
+ dependent: dev.pi5, dev.servo
61
+ owner: you
62
+ source: UBEC datasheet: 5 V 3 A
63
+ action: shed load on rail 'rail.5v' or raise its capacity with provenance
64
+ warning model/links/base_link/mass — observed zero or missing mass, expected a positive mass with units
65
+ …
66
+ scored 2: 0 add up, 1 do not · 0 not scored
67
+
68
+ NO-GO — 1 violation(s), 1 warning(s)
69
+ ```
70
+
71
+ The check exits 1. A Pi 5 and one servo on a common 3 A regulator: caught before you ordered either. Swap in a 5 A regulator and it passes.
72
+
73
+ ## A real-world example: the X500 drone
74
+
75
+ ```bash
76
+ git clone https://github.com/Shivansh2703/lockwire && cd lockwire # the X500 example lives in the repo, not the wheel
77
+ lockwire check tests/fixtures/x500/contract.yaml
78
+ ```
79
+
80
+ Output (trimmed):
81
+
82
+ ```console
83
+ $ lockwire check tests/fixtures/x500/contract.yaml
84
+ VIOLATION budgets/bud.power.pm02-5v — observed 35.20 W (draw mode: nominal), expected <= 15.60 W (Δ +19.60 W (+126%))
85
+ dependent: dev.camera.oak-d-lite, dev.companion.jetson-orin-nano, dev.radio.rfd900x
86
+ owner: power-integration
87
+ action: shed load on rail 'rail.pm02-5v' or raise its capacity with provenance
88
+ VIOLATION budgets/bud.power.pm02-5v.7w-mode — observed 17.20 W (draw mode: 7W), expected <= 15.60 W (Δ +1.60 W (+10%))
89
+ … (same rail, Jetson in its 7 W mode: the radio's transmit peak still tips it over)
90
+ not scored devices/dev.camera.oak-d-lite/physical/mass — observed mass not declared, expected a positive mass with units
91
+ … (more not-scored lines: things the lockfile does not declare yet, listed, never hidden)
92
+ scored 10: 8 add up, 2 do not · 9 not scored
93
+
94
+ NO-GO — 2 violation(s), 0 warning(s)
95
+ $ echo $?
96
+ 1
97
+ ```
98
+
99
+ ## What just happened
100
+
101
+ A Holybro PM02 power module publishes a regulated 5 V rail at 3 A: 15.6 W. An NVIDIA Jetson
102
+ Orin Nano publishes a 25 W power mode at 5 V: 5 A. Put a camera on the same rail and the
103
+ published parts **cannot power the published companion computer**. This is a custom
104
+ integration of published parts, and the two vendors' own numbers still do not add up.
105
+
106
+ Nobody printed "this build is broken". Nobody printed "5 A" either: that is 25 W / 5 V, a
107
+ quotient of figures published on different pages. The records disagree with each other, and
108
+ you find out before you apply power. Give the Jetson its own regulator
109
+ (`tests/fixtures/x500/contract_corrected.yaml`) and the same check passes.
110
+
111
+ ## Your own robot
112
+
113
+ `lockwire start` reads links, joints, masses and the structure hash from your URDF or SDF. A fresh
114
+ lockfile knows the frame and nothing else; fill in devices, rails and buses as you go, and
115
+ `check` lists what it still could not check.
116
+
117
+ A lockfile whose `schema_version` this lockwire does not recognise is refused before any check runs.
118
+ `check` prints `REFUSED — contract declares schema_version '0.99'; this lockwire supports 0.2, 0.3, 0.4` and exits 2.
119
+ A lockfile at an older supported version is accepted as-is, with no automatic upgrade.
120
+
121
+ Re-running `lockwire start --force` on the same URDF is destructive:
122
+ re-declare every fact in the lockfile by hand, because no command carries those facts forward.
123
+ Any field you hand-edited (including `robot.name`) is silently overwritten back to the URDF-derived value.
124
+
125
+ ## Exit codes
126
+
127
+ | command | 0 | 1 | 2 | 3 |
128
+ |---|---|---|---|---|
129
+ | `check` | `GO`, `GO with notes` | `NO-GO` | a contract that could not be found, read, or parsed | `NOT SCORED` (nothing compared) |
130
+ | `start` | the contract was written (or an existing one kept); no page is written | the URDF/SDF could not be parsed, or the contract could not be read or written | — | — |
131
+
132
+ `NOT SCORED` (exit 3) means no comparison had both operands: it is not a pass and not a failure.
133
+ The `scored N: …` line above the verdict is where a script should look.
134
+
135
+ ## In CI
136
+
137
+ ```yaml
138
+ - uses: actions/checkout@v4
139
+ - uses: Shivansh2703/lockwire@v0 # composite action: installs lockwire, runs the check, uploads an HTML report
140
+ with:
141
+ contract: lockwire.yaml
142
+ ```
143
+
144
+ Inputs: `contract` (default `lockwire.yaml`), `version` (default latest), `python-version` (default 3.12).
145
+ Or just `pip install "git+https://github.com/Shivansh2703/lockwire@v0.2.0" && lockwire check lockwire.yaml`.
146
+
147
+ ## Pre-commit
148
+
149
+ ```yaml
150
+ repos:
151
+ - repo: https://github.com/Shivansh2703/lockwire
152
+ rev: v0.2.0
153
+ hooks:
154
+ - id: lockwire-check
155
+ ```
156
+
157
+ Runs `lockwire check` (on `lockwire.yaml` in the repo root) whenever a lockfile changes.
158
+
159
+ ## What it checks
160
+
161
+ Each check compares **two separately declared figures**, each with its own provenance line.
162
+ It shows who said what; it cannot verify that a datasheet is true.
163
+
164
+ | check | source A | source B |
165
+ |---|---|---|
166
+ | rail power budget | device draw (datasheet) | rail capacity (power module datasheet) |
167
+ | rail tree rollup | child rail draw / efficiency | parent rail capacity |
168
+ | torque budget | joint effort limit (URDF) | actuator continuous torque x gear ratio |
169
+ | bus bandwidth | message rate x payload | bus bitrate x utilisation ceiling |
170
+ | mass budget | per-device mass | declared total |
171
+ | endurance | pack energy | summed draw |
172
+ | voltage compatibility | rail voltage | device input voltage range |
173
+ | reference integrity | every `bus_ref`, `mount_frame`, dependency edge | the thing it points at |
174
+
175
+ No physics simulation, no machine learning, no network. The answer is the same twice.
176
+
177
+ ## Free and Pro
178
+
179
+ - **Free forever (this repo):** the lockfile, all deterministic checks including
180
+ power/thermal/bus/link/torque budgets, edit / describe / plan / what-if, the MCP server, the
181
+ HTML report, and the desktop planning app (Open, Design, Declare, Plan, Check).
182
+ - **Lockwire Pro** adds the powered-on preflight gate (Test, Run), recording analysis
183
+ (rosbag2 / ULog), and health.
184
+
185
+ ## Development
186
+
187
+ See [CONTRIBUTING.md](CONTRIBUTING.md). Python 3.12 or newer; runtime dependencies are
188
+ pydantic, typer, rich, ruamel.yaml and numpy.
189
+
190
+ ## Licence
191
+
192
+ Apache-2.0. See [LICENSE](LICENSE).
@@ -0,0 +1,52 @@
1
+ lockwire/__init__.py,sha256=923RzgFwDTghnqH4Xo1b5E4A-uqDG6usq6BQcvzUA3U,186
2
+ lockwire/__main__.py,sha256=khG27tWkjV4K71hlzemHYH36eONhNNwuyiGbUf_aQQQ,249
3
+ lockwire/advice.py,sha256=YZyD7y62R9890QyNe_R-Q6nZ7sn5djrsgSxOIvqfnxY,4598
4
+ lockwire/api.py,sha256=vgLGlY6fHfLWOw6oZC-v67W2IacN0uUyfU6dQuOOhq8,45212
5
+ lockwire/api_payload.py,sha256=0scGG7bysYVWDfSKuhcBepEH0eU_xeDQM3UvNCOPJpg,38994
6
+ lockwire/bench.py,sha256=Tc08EwUMYP7hzf7F7Pg7Ff0Uyico8woW1Rh1CE6cFm8,7435
7
+ lockwire/blame.py,sha256=ULccKlplC2aimCqRQS55eGD_oGGgEBA8cq_uxl5xiIg,3769
8
+ lockwire/catalog.py,sha256=_eKyt_Z8hSd2-Cc-4vHmdCqVWvfr9BtVFNiKY7oUpkM,4360
9
+ lockwire/catalog_seed.yaml,sha256=dKkODE7Z2qxH_4qFTXvsg6C2jBxvZrCbrSqHWAjfbdw,18762
10
+ lockwire/checks.py,sha256=wC2ya8Tv3GH9qBShIh_S4NmYe5DFZwqrC2S7xp0rZqk,153709
11
+ lockwire/cli.py,sha256=AC_WPv4vKG2yDN-wbaP9l-VzQJNuPR6ZraX-0z7cCbk,72569
12
+ lockwire/cli_line.py,sha256=VDdHRtDRCULDx4nw59P_NJo7eQ2gp7Ld11BiBxSM7XA,2930
13
+ lockwire/constraints.py,sha256=G_Ey4IJ-sXcU7rR0wLZCzmMLfcLesXo68l3r3jC4TB4,11324
14
+ lockwire/copy.py,sha256=2y3CUp-5-GauSm5oZtLtKmCXOHicyefUWcGcJupCb74,11766
15
+ lockwire/describe.py,sha256=re18Op5-mMr2sxfB7FXJmoLoxSn6sv7Nz8BZv73VqHE,28367
16
+ lockwire/edit.py,sha256=9zjiUWBo0gBhTOD6KbX_1FGZSxU_KHhuCil0ynEZ4fw,83858
17
+ lockwire/emit_ts.py,sha256=tS3160N5xDLvNFET1aL039Cc3pVql-Qw3QaJCyOakmQ,6328
18
+ lockwire/export_mhs.py,sha256=CDYzwbjJuBa1JJpXmN_x8ejl-fZq3z9tHbVmG0UzPlk,4977
19
+ lockwire/factdiff.py,sha256=eC0nx-kDwB8aaPZXX95c75co3_-eYDBdOsKIHXtpRHk,9219
20
+ lockwire/forces.py,sha256=8MeCEQ-GVHGBRGRxHp6bhiR1SQS56_a67KhTofSLT8U,13445
21
+ lockwire/gitref.py,sha256=dvdAUtNtBrj8DUdbJAMmlhuY3QgcekcM-IfA-ZJedmk,7267
22
+ lockwire/hw.py,sha256=jmVx-sPe2Hu8015ryYGr4qPLQbAuTFZvf03MHNrkrCU,12837
23
+ lockwire/mcp_server.py,sha256=3t4Lj6TvgeG2HbzsRuok8GJWfaxfE5piuQb_dif5vPk,12402
24
+ lockwire/mechanism.py,sha256=wdcslubZNp5SShC2rlrlLNpFelFBx-AV_cDUcLZ1wdU,13889
25
+ lockwire/mesh_dae.py,sha256=tqc7_6Awzp_9MzQ6YZdTp90VC-sws1LWt2f1z5pI6Fc,18469
26
+ lockwire/mesh_simple.py,sha256=6MZn3cBkrT5Ls9NFpwcDLCGgQ9r3I45J_96w73g5DQw,8564
27
+ lockwire/page.py,sha256=lu2o4fNwPJ5I6KLlBHSuNHJ_R4gJANP1JGYpIx2vKx8,61323
28
+ lockwire/plan.py,sha256=MScC9S_ic31z4xxUwu8y7e8-z9PEmNjQ1VuBotmllFs,36994
29
+ lockwire/plan_payload.py,sha256=nI3FK-92VghuGnumLlcRLI0VpS9s-IlZUab5dANNT5c,5315
30
+ lockwire/plugins.py,sha256=vat7ADaNtDVnP6OnoRj1kcHp0z_nInj8zT7oGv2L3z0,2617
31
+ lockwire/report.py,sha256=XqaRtXjPZT63cPuWTqVbxNevQ3U6H_CNU04iLB8IhJE,36169
32
+ lockwire/report_html.py,sha256=GlY8l1NC5ZCSkpzFkQJ2by-60RR7BUYbaU4T3CR4Ahw,42843
33
+ lockwire/report_json.py,sha256=eDC0-bp_vxvPuJZebQDkqllJ08FJCDD6FN9qKYqJqsI,7597
34
+ lockwire/review_draft.py,sha256=dDNpWcR5mQbswMNiQWziATr7wSQnFZRfskVWUoPtJsw,11723
35
+ lockwire/scan.py,sha256=rdbCb7xelU66StgLkRp2JCMcZZOiF1KEoLqJTNB7L1o,27616
36
+ lockwire/scene.py,sha256=Q9ueUFlQ6ZqOPd_cn0Xyej9HmQYyAEJ1nlRZgVyMixE,20367
37
+ lockwire/scene_import.py,sha256=IVXr5JUdtEi-HN5I_vXYADzHi_vh9b3STADZZIfBfyI,11265
38
+ lockwire/scene_urdf.py,sha256=LhMmSyw-SLg3suS2HC0HiP0xg0xAI_pRPSvNxOtB3B4,27895
39
+ lockwire/scene_view.py,sha256=EqV1k9wrJOKi-bPzWsiY4AgYrxQ4wePQVHoFep0xiJI,16371
40
+ lockwire/schema.py,sha256=aISUFDkpqrS47Jsr5nKUn4uCW0M1ewN76iDQTCapFkM,145719
41
+ lockwire/sdf.py,sha256=WFCQ0-cAnt6z_XDGsJDVh0hwmTSH6EH4YYeO8GZB6dk,9084
42
+ lockwire/serve.py,sha256=iawjk87waucIw46r8BtdVNJyS8lpdH4frj31yeYxOu8,44236
43
+ lockwire/start_operation.py,sha256=CcwdcWvZsb6AwI_71-oDZlme0hPxvZwsvrEGYCPCVnY,4330
44
+ lockwire/statics.py,sha256=N19s1EOo8bzbNQkfeQZq53tKf4DYwHHg6SQ-6IicXJk,3548
45
+ lockwire/units.py,sha256=bf4G5bttvlLA5D6CTbs7SckoPtbtopmDK-b59Ka6VhU,3679
46
+ lockwire/urdf.py,sha256=L2s1JxVs3RkTVdcaQUafjg0PeDEc2X8BZ2NW_ha6oD0,14001
47
+ lockwire/vcs.py,sha256=2T9wLSxc6ipXVT6bijiLWc5WM9AayY5wh9tfm1hx7cg,17261
48
+ lockwire/whatif.py,sha256=_xTP2hgOCy8el5xfARmLtaeBojCRcx-od7RpXyo0be0,5292
49
+ lockwire-0.2.0.dist-info/WHEEL,sha256=uOqnPWqgFlbov4NeTCercq7cBQ2UN7xh5fiW55lOnAg,81
50
+ lockwire-0.2.0.dist-info/entry_points.txt,sha256=F6VC3aX5vhvT0O0ANWE6g2zfjOlYZpsAx1LYt1xIf1o,44
51
+ lockwire-0.2.0.dist-info/METADATA,sha256=YNFxWeyREHhY_QGOcKsbbVoyuv-Fny-UMqlMv2sjaSw,9007
52
+ lockwire-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.11.26
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ lockwire = lockwire:main
3
+