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:
@@ -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
|
||||||
|
|||||||
@@ -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)"),
|
||||||
|
|||||||
Reference in New Issue
Block a user