fix: route_pad_to_pad auto-inserts via when pads are on different copper layers

- Detects F.Cu<->B.Cu layer mismatch in route_pad_to_pad
- Splits route into: trace on start layer + via at midpoint + trace on end layer
- route_trace description warns: use route_pad_to_pad for cross-layer routing
- route_pad_to_pad description highlights automatic via insertion
This commit is contained in:
Tom
2026-03-07 15:46:54 +01:00
parent 9d344c95a5
commit 3e329cbf6b
2 changed files with 54 additions and 30 deletions

View File

@@ -144,40 +144,64 @@ class RoutingCommands:
if not net: if not net:
net = start_pad.GetNetname() or end_pad.GetNetname() or "" net = start_pad.GetNetname() or end_pad.GetNetname() or ""
# Delegate to route_trace # Detect if pads are on different copper layers → need via
result = self.route_trace( start_layer = start_pad.GetLayerName()
{ end_layer = end_pad.GetLayerName()
"start": { copper_layers = {"F.Cu", "B.Cu"}
"x": start_pos.x / scale, needs_via = (
"y": start_pos.y / scale, start_layer in copper_layers
"unit": "mm", and end_layer in copper_layers
}, and start_layer != end_layer
"end": {
"x": end_pos.x / scale,
"y": end_pos.y / scale,
"unit": "mm",
},
"layer": layer,
"width": width,
"net": net,
}
) )
if needs_via:
# Place via at midpoint between the two pads
via_x = (start_pos.x + end_pos.x) / 2 / scale
via_y = (start_pos.y + end_pos.y) / 2 / scale
# Trace on start layer: start_pad → via
r1 = self.route_trace({
"start": {"x": start_pos.x / scale, "y": start_pos.y / scale, "unit": "mm"},
"end": {"x": via_x, "y": via_y, "unit": "mm"},
"layer": start_layer, "width": width, "net": net,
})
# Via connecting both layers
self.add_via({
"position": {"x": via_x, "y": via_y, "unit": "mm"},
"net": net,
"from_layer": start_layer,
"to_layer": end_layer,
})
# Trace on end layer: via → end_pad
r2 = self.route_trace({
"start": {"x": via_x, "y": via_y, "unit": "mm"},
"end": {"x": end_pos.x / scale, "y": end_pos.y / scale, "unit": "mm"},
"layer": end_layer, "width": width, "net": net,
})
success = r1.get("success") and r2.get("success")
result = {
"success": success,
"message": f"Routed {from_ref}.{from_pad} → via → {to_ref}.{to_pad} (net: {net}, via at {via_x:.2f},{via_y:.2f})",
"via_added": True,
"via_position": {"x": via_x, "y": via_y},
}
else:
# Same layer — direct trace
result = self.route_trace({
"start": {"x": start_pos.x / scale, "y": start_pos.y / scale, "unit": "mm"},
"end": {"x": end_pos.x / scale, "y": end_pos.y / scale, "unit": "mm"},
"layer": layer if layer else start_layer,
"width": width, "net": net,
})
if result.get("success"): if result.get("success"):
result["message"] = (
f"Routed {from_ref}.{from_pad}{to_ref}.{to_pad} (net: {net or 'none'})"
)
result["fromPad"] = { result["fromPad"] = {
"ref": from_ref, "ref": from_ref, "pad": from_pad,
"pad": from_pad, "x": start_pos.x / scale, "y": start_pos.y / scale,
"x": start_pos.x / scale,
"y": start_pos.y / scale,
} }
result["toPad"] = { result["toPad"] = {
"ref": to_ref, "ref": to_ref, "pad": to_pad,
"pad": to_pad, "x": end_pos.x / scale, "y": end_pos.y / scale,
"x": end_pos.x / scale,
"y": end_pos.y / scale,
} }
return result return result

View File

@@ -33,7 +33,7 @@ export function registerRoutingTools(
// Route trace tool // Route trace tool
server.tool( server.tool(
"route_trace", "route_trace",
"Route a trace between two points", "Route a trace segment between two XY points on a fixed layer. WARNING: Does NOT handle layer changes — if start and end are on different copper layers, use route_pad_to_pad instead, which automatically inserts a via.",
{ {
start: z start: z
.object({ .object({
@@ -331,7 +331,7 @@ export function registerRoutingTools(
// Route pad to pad tool // Route pad to pad tool
server.tool( server.tool(
"route_pad_to_pad", "route_pad_to_pad",
"Route a trace directly from one component pad to another without needing separate get_pad_position calls. Automatically looks up pad coordinates and uses the pad's net. Saves token usage compared to the 3-step get_pad_position + get_pad_position + route_trace sequence.", "PREFERRED tool for pad-to-pad routing. Looks up pad positions automatically, detects the net from the pad, and — critically — if the two pads are on different copper layers (e.g. J1 on F.Cu and J2 on B.Cu) automatically inserts a via at the midpoint so the connection is complete. Always use this instead of route_trace when routing between named component pads.",
{ {
fromRef: z.string().describe("Reference of the source component (e.g. 'U2')"), fromRef: z.string().describe("Reference of the source component (e.g. 'U2')"),
fromPad: z.union([z.string(), z.number()]).describe("Pad number on the source component (e.g. '6' or 6)"), fromPad: z.union([z.string(), z.number()]).describe("Pad number on the source component (e.g. '6' or 6)"),