app
KiCad - the KiCad Bridge
Public Made by Adomby adom
Reference implementation of the KiCad bridge: multi-instance Python server, forward path via kicad-cli, reverse path via in-process plugin. Most complex of the three bundled bridges.
← Commit history
0.9.341: Release-day fixes: stock library path resolution (#80), screenshot_all sizeKB (#81), readiness re-detect (#82), routing DRC completeness (PR 82), verified source pushes (#79), full verb-test coverage (#78)
1 file changed
+20−2
handlers/live_routing.py+20−2@@ -174,6 +174,18 @@ def _plan(data, args): return {"netName": netname, "segments": segments, "vias": vias, "blocks": blocks} +def _report_limits(violations, unconnected):+ # KiCad 10.0.3 DRC_ENGINE: 199 per type, extended to 499 for+ # clearance and unconnected items. At the boundary completeness is unknown.+ counts = Counter(v.get("type") for v in violations)+ capped = sorted(str(kind) for kind, count in counts.items()+ if count >= (499 if kind == "clearance" else 199))+ return {"unconnectedReportMayBeTruncated": len(unconnected) >= 499,+ "unconnectedCountIsLowerBound": len(unconnected) >= 499,+ "violationReportMayBeTruncated": bool(capped),+ "possiblyTruncatedViolationTypes": capped}++ def _drc(info, args, text): exe = info.get("kicad_cli_exe") if not exe or not Path(exe).is_file():@@ -191,8 +203,10 @@ def _drc(info, args, text): shutil.copy2(sibling, candidate.with_suffix(suffix)) copied.append(str(sibling)) report = Path(tmp) / "drc.json"+ # Copper edits invalidate cached zone fills. Refill the disposable+ # snapshot so connectivity reflects the proposed copper, not old pours. proc = subprocess.run([exe, "pcb", "drc", "--format", "json", "--severity-all",- "--all-track-errors", "--output", str(report), str(candidate)],+ "--all-track-errors", "--refill-zones", "--output", str(report), str(candidate)], capture_output=True, text=True, timeout=90) if proc.returncode or not report.is_file(): raise RoutingError("drc_failed", proc.stderr or proc.stdout or "No DRC report")@@ -204,7 +218,8 @@ def _drc(info, args, text): "unconnected": len(unconnected), "violations": violations, "unconnectedItems": unconnected, "clean": not violations and not unconnected,- "source": "live-editor-snapshot", "projectRulesCopied": copied}+ "source": "live-editor-snapshot", "projectRulesCopied": copied,+ "zonesRefilled": True, **_report_limits(violations, unconnected)} def _fingerprint(v):@@ -213,6 +228,9 @@ def _fingerprint(v): def _new_errors(before, after):+ if any(r.get("violationReportMayBeTruncated") for r in (before, after)):+ raise RoutingError("drc_incomplete", "DRC violation lists may be capped; cannot certify the error delta. No live copper was changed",+ mutated=False) existing = Counter(_fingerprint(v) for v in before["violations"] if v.get("severity") == "error") added = [] for v in after["violations"]: