app
SOLIDWORKS Bridge
Public Made by Adomby adom
Launch, model and quit SOLIDWORKS from any Adom container. Nothing is ever lost.
main
Drew Owens
0.3.3 source + crate build: BOM exported from the SOLIDWORKS assembly, build log §1–11, release script
477e64f
1mo ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
#!/usr/bin/env python3
"""swx-batch.py <calls.json> [totalTimeout] [perCallTimeout] - run MANY bridge verbs in ONE round trip.
calls.json is a JSON array of {"verb":..., "args": {...}, "label": ...}. The array is pushed to
the desktop with ab's write_file (an EncodedCommand big enough to inline it blows the Windows
command-line length limit), then one small PowerShell loop POSTs each call to the dev bridge on
127.0.0.1:24695 and returns one JSON array of results.
Why this exists: every adom-bridge shell_execute round trip costs ~10-20s, and discovering a
late-bound COM signature takes dozens of probes.
"""
import base64, json, os, subprocess, sys
TARGET = ["--target", "adom_drew2", "--ai-thread", "SolidWorks Bridge"]
REQ = r"C:\Users\Public\swx_calls.json"
def ab(verb, args, timeout=300):
"""One ab call. NEVER raises.
The `try` used to wrap only `json.loads`, so a `subprocess.TimeoutExpired`
propagated out of here, through `cleanup()`, and past the invariant gate and
every RESTORE — on exactly the runs (a wedged SOLIDWORKS, a dropped relay)
where putting the machine back matters most.
"""
try:
r = subprocess.run(["adom-bridge"] + TARGET + [verb, json.dumps(args)],
capture_output=True, text=True, timeout=timeout)
except subprocess.TimeoutExpired:
return {"success": False, "error": "adom-bridge timed out after %ss" % timeout}
except BaseException as e: # a missing CLI, a killed process, anything
return {"success": False, "error": "%s: %s" % (type(e).__name__, e)}
try:
return json.loads(r.stdout)
except Exception:
return {"success": False, "raw": r.stdout[-2000:], "stderr": r.stderr[-500:]}
def run(calls, total=300, per=90, quiet=False, stop_on_failure=False):
"""Run `calls` in ONE round trip.
`stop_on_failure` makes the desktop-side loop ABORT at the first call whose
response is not `success:true`. That is not a convenience: a batch is a
single trip, so without it a failed setup step (say, `swx_new_document`
refusing) is followed by every later step in the list landing on whatever
document happens to be active — which, in a batch aimed at a scratch part,
is the user's. The remaining calls come back labelled `skipped`."""
w = ab("write_file", {"path": REQ, "content": json.dumps(calls),
"reason": "Staging a batch of SOLIDWORKS bridge probe calls so they can be run in one round trip"})
if not w.get("success", w.get("ok")):
# NEVER exit from in here. live-smoke.py's cleanup and its
# "documents unchanged" invariant run in a `finally`, and a SystemExit
# raised from the batch runner would sail straight through them — the
# invariant would never execute, on exactly the runs where it matters
# most. Return None; the caller decides.
print("write_file FAILED:", json.dumps(w)[:800])
return None
ps = (
"$calls = Get-Content -Raw '" + REQ + "' | ConvertFrom-Json\n"
"$out = @()\n"
"$stop = $" + ("true" if stop_on_failure else "false") + "\n"
"$halted = $false\n"
"foreach ($c in $calls) {\n"
" if ($halted) { $out += @{ label = $c.label; verb = $c.verb; raw = '{\"success\":false,\"skipped\":true,\"error\":\"an earlier call in this batch failed\"}' }; continue }\n"
" $body = @{ command = $c.verb; args = $c.args } | ConvertTo-Json -Depth 12 -Compress\n"
" $p = \"$env:TEMP\\swx_req.json\"\n"
" [IO.File]::WriteAllText($p, $body)\n"
" $r = & curl.exe -s --max-time " + str(per) + " -H 'Content-Type: application/json' --data-binary \"@$p\" http://127.0.0.1:24695/command 2>&1 | Out-String\n"
" $r = $r.Trim()\n"
" $out += @{ label = $c.label; verb = $c.verb; raw = $r }\n"
" if ($stop) {\n"
" $okFlag = $false\n"
" try { $okFlag = (ConvertFrom-Json $r).success -eq $true } catch { $okFlag = $false }\n"
" if (-not $okFlag) { $halted = $true }\n"
" }\n"
"}\n"
"$out | ConvertTo-Json -Depth 6 -Compress\n"
)
enc = base64.b64encode(ps.encode("utf-16le")).decode()
d = ab("shell_execute", {"command": "powershell -NoProfile -EncodedCommand " + enc,
"timeoutSeconds": total,
"reason": "Running a batch of SOLIDWORKS bridge verbs on your PC to validate them against the running SOLIDWORKS"},
timeout=total + 60)
out = (d.get("stdout") or "").strip()
try:
rows = json.loads(out)
except Exception:
print("RAW:", out[:3000], "| stderr:", (d.get("stderr") or "")[:600], "| status:", d.get("status"))
return None
if isinstance(rows, dict):
rows = [rows]
parsed = []
for row in rows:
raw = row.get("raw") or ""
try:
v = json.loads(raw)
except Exception:
v = {"success": False, "_raw": raw[:1200]}
parsed.append({"label": row.get("label") or row.get("verb"), "verb": row.get("verb"), "result": v})
if not quiet:
print("=" * 8, parsed[-1]["label"])
print(json.dumps(v, indent=1)[:2600])
return parsed
if __name__ == "__main__":
calls = json.load(open(sys.argv[1]))
total = int(sys.argv[2]) if len(sys.argv) > 2 else 300
per = int(sys.argv[3]) if len(sys.argv) > 3 else 90
run(calls, total, per)