feat: add run_erc tool for schematic electrical rules checking
This commit is contained in:
@@ -378,6 +378,7 @@ class KiCADInterface:
|
||||
"connect_passthrough": self._handle_connect_passthrough,
|
||||
"get_schematic_pin_locations": self._handle_get_schematic_pin_locations,
|
||||
"get_net_connections": self._handle_get_net_connections,
|
||||
"run_erc": self._handle_run_erc,
|
||||
"generate_netlist": self._handle_generate_netlist,
|
||||
"list_schematic_libraries": self._handle_list_schematic_libraries,
|
||||
"export_schematic_pdf": self._handle_export_schematic_pdf,
|
||||
@@ -1367,6 +1368,88 @@ class KiCADInterface:
|
||||
logger.error(f"Error getting net connections: {str(e)}")
|
||||
return {"success": False, "message": str(e)}
|
||||
|
||||
def _handle_run_erc(self, params):
|
||||
"""Run Electrical Rules Check on a schematic via kicad-cli"""
|
||||
logger.info("Running ERC on schematic")
|
||||
import subprocess
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
try:
|
||||
schematic_path = params.get("schematicPath")
|
||||
if not schematic_path or not os.path.exists(schematic_path):
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Schematic file not found",
|
||||
"errorDetails": f"Path does not exist: {schematic_path}",
|
||||
}
|
||||
|
||||
kicad_cli = self.design_rule_commands._find_kicad_cli()
|
||||
if not kicad_cli:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "kicad-cli not found",
|
||||
"errorDetails": "Install KiCAD 8.0+ or add kicad-cli to PATH.",
|
||||
}
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as tmp:
|
||||
json_output = tmp.name
|
||||
|
||||
try:
|
||||
cmd = [kicad_cli, "sch", "erc", "--format", "json", "--output", json_output, schematic_path]
|
||||
logger.info(f"Running ERC command: {' '.join(cmd)}")
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
|
||||
|
||||
if result.returncode != 0:
|
||||
logger.error(f"ERC command failed: {result.stderr}")
|
||||
return {
|
||||
"success": False,
|
||||
"message": "ERC command failed",
|
||||
"errorDetails": result.stderr,
|
||||
}
|
||||
|
||||
with open(json_output, "r", encoding="utf-8") as f:
|
||||
erc_data = json.load(f)
|
||||
|
||||
violations = []
|
||||
severity_counts = {"error": 0, "warning": 0, "info": 0}
|
||||
|
||||
for v in erc_data.get("violations", []):
|
||||
vseverity = v.get("severity", "error")
|
||||
items = v.get("items", [])
|
||||
loc = {}
|
||||
if items and "pos" in items[0]:
|
||||
loc = {"x": items[0]["pos"].get("x", 0), "y": items[0]["pos"].get("y", 0)}
|
||||
violations.append({
|
||||
"type": v.get("type", "unknown"),
|
||||
"severity": vseverity,
|
||||
"message": v.get("description", ""),
|
||||
"location": loc,
|
||||
})
|
||||
if vseverity in severity_counts:
|
||||
severity_counts[vseverity] += 1
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"ERC complete: {len(violations)} violation(s)",
|
||||
"summary": {
|
||||
"total": len(violations),
|
||||
"by_severity": severity_counts,
|
||||
},
|
||||
"violations": violations,
|
||||
}
|
||||
|
||||
finally:
|
||||
if os.path.exists(json_output):
|
||||
os.unlink(json_output)
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
return {"success": False, "message": "ERC timed out after 120 seconds"}
|
||||
except Exception as e:
|
||||
logger.error(f"Error running ERC: {str(e)}")
|
||||
return {"success": False, "message": str(e)}
|
||||
|
||||
def _handle_generate_netlist(self, params):
|
||||
"""Generate netlist from schematic"""
|
||||
logger.info("Generating netlist from schematic")
|
||||
|
||||
@@ -1513,6 +1513,21 @@ SCHEMATIC_TOOLS = [
|
||||
"required": ["schematicPath", "sourceRef", "targetRef"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "run_erc",
|
||||
"title": "Run Electrical Rules Check (ERC)",
|
||||
"description": "Runs the KiCAD Electrical Rules Check (ERC) on a schematic via kicad-cli and returns all violations with type, severity, and location. Use this to verify the schematic is electrically correct before generating a netlist or exporting.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"schematicPath": {
|
||||
"type": "string",
|
||||
"description": "Path to the .kicad_sch schematic file"
|
||||
}
|
||||
},
|
||||
"required": ["schematicPath"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "generate_netlist",,
|
||||
"title": "Generate Netlist",
|
||||
|
||||
@@ -445,6 +445,41 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp
|
||||
},
|
||||
);
|
||||
|
||||
// Run Electrical Rules Check (ERC)
|
||||
server.tool(
|
||||
"run_erc",
|
||||
"Runs the KiCAD Electrical Rules Check (ERC) on a schematic and returns all violations. Use after wiring to verify the schematic before generating a netlist.",
|
||||
{
|
||||
schematicPath: z.string().describe("Path to the .kicad_sch schematic file"),
|
||||
},
|
||||
async (args: { schematicPath: string }) => {
|
||||
const result = await callKicadScript("run_erc", args);
|
||||
if (result.success) {
|
||||
const violations: any[] = result.violations || [];
|
||||
const lines: string[] = [`ERC result: ${violations.length} violation(s)`];
|
||||
if (result.summary?.by_severity) {
|
||||
const s = result.summary.by_severity;
|
||||
lines.push(` Errors: ${s.error ?? 0} Warnings: ${s.warning ?? 0} Info: ${s.info ?? 0}`);
|
||||
}
|
||||
if (violations.length > 0) {
|
||||
lines.push("");
|
||||
violations.slice(0, 30).forEach((v: any, i: number) => {
|
||||
const loc = v.location && (v.location.x !== undefined) ? ` @ (${v.location.x}, ${v.location.y})` : "";
|
||||
lines.push(`${i + 1}. [${v.severity}] ${v.message}${loc}`);
|
||||
});
|
||||
if (violations.length > 30) {
|
||||
lines.push(`... and ${violations.length - 30} more`);
|
||||
}
|
||||
}
|
||||
return { content: [{ type: "text", text: lines.join("\n") }] };
|
||||
} else {
|
||||
return {
|
||||
content: [{ type: "text", text: `ERC failed: ${result.message || "Unknown error"}${result.errorDetails ? "\n" + result.errorDetails : ""}` }],
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Generate netlist
|
||||
server.tool(
|
||||
"generate_netlist",
|
||||
|
||||
Reference in New Issue
Block a user