Say which connections were not routed

freeroute exited 0 and said nothing when it dropped connections, which
cost real diagnosis time: on rc4 two simple two-pin nets came back with no
copper and no explanation. The grid now records each connection it could
not route, the exact check adds each it rejects, and the CLI lists them on
stderr (the SES is still written; the exit code stays 0, as the JAR's).

A connection whose ends both sit in one zone of its net is listed apart,
as left to the zone fill: freeroute does not route through a zone it
cannot trust to be solid, but KiCad's fill often joins those pads anyway.
This commit is contained in:
2026-09-27 12:34:20 -06:00
parent c49c79604d
commit cdb3c7b568
6 changed files with 95 additions and 5 deletions
+13 -1
View File
@@ -159,7 +159,11 @@ def route_board_exact(
# Verify connection by connection, so one conflicting segment of a
# 25-pin supply net costs that one connection, not the whole net.
for net_no, wires, vias in sorted(result.connections, key=lambda c: c[0]):
rejected: list[tuple[int, str, str]] = []
ends_list = result.connection_ends or [("?", "?", "")] * len(result.connections)
for (net_no, wires, vias), ends in sorted(
zip(result.connections, ends_list, strict=False), key=lambda c: c[0][0]
):
boxes = _copper_boxes(wires, vias, half_width, via_layers, via_half)
wire_count = len(_copper_boxes(wires, [], half_width, via_layers, via_half))
if any(
@@ -169,6 +173,7 @@ def route_board_exact(
for i, (box, box_layers) in enumerate(boxes)
for layer in box_layers
) or (outline is not None and not all(outline.box_clear(b, edge) for b, _ in boxes)):
rejected.append((net_no, *ends))
continue # exact-clearance conflict -> drop (may be recovered by shove)
acc = accepted.get(net_no)
if acc is None:
@@ -180,6 +185,7 @@ def route_board_exact(
tree.insert(TreeShape(oid, net_no, box_layers, box, routed=True))
acc.owners.append(oid)
accepted_before_recovery = set(accepted)
# a net is dropped if it has a real ratsnest (>= 2 pins on a signal layer)
# but was not accepted — whether the grid failed to route it or its exact
# copper conflicted.
@@ -214,6 +220,12 @@ def route_board_exact(
)
clean = RouteResult(half_width)
# what is still missing: what the grid could not route, plus what the
# exact check rejected, less any net shove or the diagonal pass recovered
clean.failed = [
*result.failed,
*(r for r in rejected if not (r[0] in accepted and r[0] not in accepted_before_recovery)),
]
for net_no, acc in accepted.items():
clean.wires[net_no] = acc.wires
clean.vias[net_no] = acc.vias