feat: add route_pad_to_pad tool
Convenience wrapper around route_trace that eliminates the need for separate get_pad_position calls before routing. - Accepts fromRef/fromPad/toRef/toPad instead of raw coordinates - Automatically looks up pad positions from board footprints - Auto-detects net from pad assignment (overridable via net param) - Returns fromPad/toPad position info in response - Saves ~2 tool calls (64+ calls for a full TMC2209 board) vs 3-step flow Registered in: routing.py, kicad_interface.py (dispatch), routing.ts (MCP)
This commit is contained in:
@@ -71,6 +71,103 @@ class RoutingCommands:
|
|||||||
"errorDetails": str(e),
|
"errorDetails": str(e),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def route_pad_to_pad(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""Route a trace directly from one component pad to another.
|
||||||
|
|
||||||
|
Looks up pad positions automatically, then creates a trace.
|
||||||
|
Convenience wrapper around route_trace that eliminates the need
|
||||||
|
for separate get_pad_position calls.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if not self.board:
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": "No board is loaded",
|
||||||
|
"errorDetails": "Load or create a board first",
|
||||||
|
}
|
||||||
|
|
||||||
|
from_ref = params.get("fromRef")
|
||||||
|
from_pad = str(params.get("fromPad", ""))
|
||||||
|
to_ref = params.get("toRef")
|
||||||
|
to_pad = str(params.get("toPad", ""))
|
||||||
|
layer = params.get("layer", "F.Cu")
|
||||||
|
width = params.get("width")
|
||||||
|
net = params.get("net") # optional override
|
||||||
|
|
||||||
|
if not from_ref or not from_pad or not to_ref or not to_pad:
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": "Missing parameters",
|
||||||
|
"errorDetails": "fromRef, fromPad, toRef, toPad are all required",
|
||||||
|
}
|
||||||
|
|
||||||
|
scale = 1000000 # nm to mm
|
||||||
|
|
||||||
|
# Find pads
|
||||||
|
footprints = {fp.GetReference(): fp for fp in self.board.GetFootprints()}
|
||||||
|
|
||||||
|
for ref in [from_ref, to_ref]:
|
||||||
|
if ref not in footprints:
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": f"Component not found: {ref}",
|
||||||
|
"errorDetails": f"'{ref}' does not exist on the board",
|
||||||
|
}
|
||||||
|
|
||||||
|
def find_pad(ref: str, pad_num: str):
|
||||||
|
fp = footprints[ref]
|
||||||
|
for pad in fp.Pads():
|
||||||
|
if pad.GetNumber() == pad_num:
|
||||||
|
return pad
|
||||||
|
return None
|
||||||
|
|
||||||
|
start_pad = find_pad(from_ref, from_pad)
|
||||||
|
end_pad = find_pad(to_ref, to_pad)
|
||||||
|
|
||||||
|
if not start_pad:
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": f"Pad not found: {from_ref} pad {from_pad}",
|
||||||
|
"errorDetails": f"Check pad number for {from_ref}",
|
||||||
|
}
|
||||||
|
if not end_pad:
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": f"Pad not found: {to_ref} pad {to_pad}",
|
||||||
|
"errorDetails": f"Check pad number for {to_ref}",
|
||||||
|
}
|
||||||
|
|
||||||
|
start_pos = start_pad.GetPosition()
|
||||||
|
end_pos = end_pad.GetPosition()
|
||||||
|
|
||||||
|
# Use net from start pad if not overridden
|
||||||
|
if not net:
|
||||||
|
net = start_pad.GetNetname() or end_pad.GetNetname() or ""
|
||||||
|
|
||||||
|
# Delegate to route_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,
|
||||||
|
"width": width,
|
||||||
|
"net": net,
|
||||||
|
})
|
||||||
|
|
||||||
|
if result.get("success"):
|
||||||
|
result["message"] = f"Routed {from_ref}.{from_pad} → {to_ref}.{to_pad} (net: {net or 'none'})"
|
||||||
|
result["fromPad"] = {"ref": from_ref, "pad": from_pad, "x": start_pos.x / scale, "y": start_pos.y / scale}
|
||||||
|
result["toPad"] = {"ref": to_ref, "pad": to_pad, "x": end_pos.x / scale, "y": end_pos.y / scale}
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in route_pad_to_pad: {str(e)}")
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": "Failed to route pad to pad",
|
||||||
|
"errorDetails": str(e),
|
||||||
|
}
|
||||||
|
|
||||||
def route_trace(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
def route_trace(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Route a trace between two points or pads"""
|
"""Route a trace between two points or pads"""
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -308,6 +308,7 @@ class KiCADInterface:
|
|||||||
"add_text": self.board_commands.add_text,
|
"add_text": self.board_commands.add_text,
|
||||||
"add_board_text": self.board_commands.add_text, # Alias for TypeScript tool
|
"add_board_text": self.board_commands.add_text, # Alias for TypeScript tool
|
||||||
# Component commands
|
# Component commands
|
||||||
|
"route_pad_to_pad": self.routing_commands.route_pad_to_pad,
|
||||||
"place_component": self._handle_place_component,
|
"place_component": self._handle_place_component,
|
||||||
"move_component": self.component_commands.move_component,
|
"move_component": self.component_commands.move_component,
|
||||||
"rotate_component": self.component_commands.rotate_component,
|
"rotate_component": self.component_commands.rotate_component,
|
||||||
|
|||||||
@@ -322,6 +322,27 @@ export function registerRoutingTools(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Route pad to pad tool
|
||||||
|
server.tool(
|
||||||
|
"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.",
|
||||||
|
{
|
||||||
|
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)"),
|
||||||
|
toRef: z.string().describe("Reference of the target component (e.g. 'U1')"),
|
||||||
|
toPad: z.union([z.string(), z.number()]).describe("Pad number on the target component (e.g. '15' or 15)"),
|
||||||
|
layer: z.string().optional().describe("PCB layer (default: F.Cu)"),
|
||||||
|
width: z.number().optional().describe("Trace width in mm (default: board default)"),
|
||||||
|
net: z.string().optional().describe("Net name override (default: auto-detected from pad)"),
|
||||||
|
},
|
||||||
|
async (args: any) => {
|
||||||
|
const result = await callKicadScript("route_pad_to_pad", args);
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Copy routing pattern tool
|
// Copy routing pattern tool
|
||||||
server.tool(
|
server.tool(
|
||||||
"copy_routing_pattern",
|
"copy_routing_pattern",
|
||||||
|
|||||||
Reference in New Issue
Block a user