Copies the board (stripping its tracks unless --keep), exports the DSN through mckicad, routes, applies the session and runs DRC with zones refilled, counting hole problems whatever their severity. FREEROUTE and MCK choose the builds under test.
68 lines
3.0 KiB
Bash
Executable File
68 lines
3.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Route one KiCad board with freeroute and judge the result with KiCad's DRC.
|
|
#
|
|
# bench/drc_check.sh [--keep] <board.kicad_pcb> <out-dir> [freeroute 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.
|
|
#
|
|
# FREEROUTE (default: this checkout's .venv/bin/freeroute) 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)"
|
|
FREEROUTE="${FREEROUTE:-$here/.venv/bin/freeroute}"
|
|
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)
|
|
"$FREEROUTE" "$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
|