Files
rpmroute/bench/drc_check.sh
T
rsp2k a6aa1991a1 Rename to rpmroute
The router began as a port of FreeRouting and has grown its own engine
(negotiated congestion, tree nets, exact checks, axis escapes), and
'freeroute' was one letter from the project it no longer follows. The
package, module and command are now rpmroute; the freeroute command stays
as an alias, and the RPMROUTE / RPMROUTE_ORACLE_JAVA variables fall back
to the old FREEROUTE names. Version 2026.10.07. Routing is unchanged: a
PCBench board routes to a byte-identical session either way.
2026-10-07 14:10:36 -06:00

68 lines
3.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# Route one KiCad board with rpmroute and judge the result with KiCad's DRC.
#
# bench/drc_check.sh [--keep] <board.kicad_pcb> <out-dir> [rpmroute args...]
#
# Copies the board (and its .kicad_pro) into <out-dir>, strips its tracks and
# vias unless --keep is given (then they reach the router as fixed wiring),
# exports the DSN through mckicad, routes, applies the session, and runs DRC
# with zones refilled at every severity. Prints one line: time, copper added,
# shorts, opens, hole problems (whatever severity the board gives them) and
# the other errors. The source board is never touched.
#
# RPMROUTE (or the older FREEROUTE; default: this checkout's .venv/bin/rpmroute) and MCK (default
# ~/claude/mckicad) pick the builds to judge.
set -euo pipefail
keep=0
if [[ "${1:-}" == "--keep" ]]; then keep=1; shift; fi
src="$1"; out="$2"; shift 2
here="$(cd "$(dirname "$0")/.." && pwd)"
RPMROUTE="${RPMROUTE:-${FREEROUTE:-$here/.venv/bin/rpmroute}}"
MCK="${MCK:-$HOME/claude/mckicad}"
rm -rf "$out"; mkdir -p "$out"
cp "${src%.kicad_pcb}.kicad_pro" "$out/board.kicad_pro" 2>/dev/null || true
if [[ $keep == 1 ]]; then
cp "$src" "$out/board.kicad_pcb"
else
(cd "$MCK" && uv run --no-sync python - "$src" "$out/board.kicad_pcb" <<'EOF'
import sys
from mckicad.utils.sexp_tree import parse
text = open(sys.argv[1]).read()
spans = sorted((c.span for c in parse(text).children if c.tag in ("segment", "arc", "via")), reverse=True)
for a, b in spans:
text = text[:a] + text[b:]
open(sys.argv[2], "w").write(text)
EOF
)
fi
(cd "$MCK" && uv run --no-sync python -c "
from mckicad.utils.freerouting import FreeRoutingEngine
assert FreeRoutingEngine().export_dsn_from_kicad('$out/board.kicad_pcb', '$out/board.dsn', {})") \
>/dev/null 2>&1 || { echo "$out: DSN export failed"; exit 1; }
start=$(date +%s.%N)
"$RPMROUTE" "$out/board.dsn" -do "$out/board.ses" "$@" >"$out/route.log" 2>&1 || true
secs=$(python3 -c "print(round($(date +%s.%N)-$start,1))")
[[ -s "$out/board.ses" ]] || { echo "$out: no session ($(tail -1 "$out/route.log"))"; exit 1; }
(cd "$MCK" && uv run --no-sync python -c "
from mckicad.utils.ses_apply import apply_ses_to_board as a
r = a('$out/board.kicad_pcb', '$out/board.ses', '$out/board.kicad_pcb')
print(r['segments_added'], r['vias_added'])" 2>/dev/null) >"$out/apply.txt"
kicad-cli pcb drc --refill-zones --format json --severity-all \
-o "$out/drc.json" "$out/board.kicad_pcb" >/dev/null 2>&1 || true
python3 - "$out" "$secs" <<'EOF'
import collections, json, sys
out, secs = sys.argv[1:]
seg, via = open(f"{out}/apply.txt").read().split()
d = json.load(open(f"{out}/drc.json"))
errors = collections.Counter(v["type"] for v in d["violations"] if v.get("severity") == "error")
other = collections.Counter(v["type"] for v in d["violations"] if v.get("severity") != "error")
holes = {k: errors.pop(k, 0) + other.get(k, 0) for k in ("holes_co_located", "hole_to_hole")}
print(f"{out}: {secs}s {seg} seg {via} via shorts {errors.pop('shorting_items', 0)} "
f"opens {len(d.get('unconnected_items', []))} holes {holes} errors {dict(errors)}")
EOF