🎉 Major v2.0 rebuild kickoff - Week 1 accomplished! ## Highlights ### Cross-Platform Support 🌍 - ✅ Linux primary platform (Ubuntu/Debian tested) - ✅ Windows fully supported - ✅ macOS experimental support - ✅ Platform-agnostic path handling (XDG spec) - ✅ Auto-detection of KiCAD installation ### Infrastructure 🏗️ - ✅ GitHub Actions CI/CD pipeline - ✅ Pytest framework with 20+ tests - ✅ Pre-commit hooks (Black, MyPy, ESLint) - ✅ Automated Linux installation script - ✅ Enhanced npm scripts ### IPC API Migration Prep 🚀 - ✅ Comprehensive migration plan (30 pages) - ✅ Backend abstraction layer (800+ lines) - ✅ Factory pattern with auto-detection - ✅ SWIG backward compatibility wrapper - ✅ IPC backend skeleton ready ### Documentation 📚 - ✅ Updated README (Linux installation) - ✅ CONTRIBUTING.md guide - ✅ Linux compatibility audit - ✅ IPC API migration plan - ✅ Session summaries - ✅ Platform-specific config templates ## Files Changed - 27 files created - ~3,000 lines of code/docs - 8 comprehensive documentation pages - 20+ unit tests - 5 abstraction layer modules ## Next Steps - Week 2: IPC API migration (project.py → component.py → routing.py) - Migrate from deprecated SWIG to official IPC API - JLCPCB/Digikey integration prep 🤖 Generated with Claude Code https://claude.com/claude-code Co-Authored-By: Claude <noreply@anthropic.com>
240 lines
9.9 KiB
Python
240 lines
9.9 KiB
Python
"""
|
|
Design rules command implementations for KiCAD interface
|
|
"""
|
|
|
|
import os
|
|
import pcbnew
|
|
import logging
|
|
from typing import Dict, Any, Optional, List, Tuple
|
|
|
|
logger = logging.getLogger('kicad_interface')
|
|
|
|
class DesignRuleCommands:
|
|
"""Handles design rule checking and configuration"""
|
|
|
|
def __init__(self, board: Optional[pcbnew.BOARD] = None):
|
|
"""Initialize with optional board instance"""
|
|
self.board = board
|
|
|
|
def set_design_rules(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""Set design rules for the PCB"""
|
|
try:
|
|
if not self.board:
|
|
return {
|
|
"success": False,
|
|
"message": "No board is loaded",
|
|
"errorDetails": "Load or create a board first"
|
|
}
|
|
|
|
design_settings = self.board.GetDesignSettings()
|
|
|
|
# Convert mm to nanometers for KiCAD internal units
|
|
scale = 1000000 # mm to nm
|
|
|
|
# Set clearance
|
|
if "clearance" in params:
|
|
design_settings.SetMinClearance(int(params["clearance"] * scale))
|
|
|
|
# Set track width
|
|
if "trackWidth" in params:
|
|
design_settings.SetCurrentTrackWidth(int(params["trackWidth"] * scale))
|
|
|
|
# Set via settings
|
|
if "viaDiameter" in params:
|
|
design_settings.SetCurrentViaSize(int(params["viaDiameter"] * scale))
|
|
if "viaDrill" in params:
|
|
design_settings.SetCurrentViaDrill(int(params["viaDrill"] * scale))
|
|
|
|
# Set micro via settings
|
|
if "microViaDiameter" in params:
|
|
design_settings.SetCurrentMicroViaSize(int(params["microViaDiameter"] * scale))
|
|
if "microViaDrill" in params:
|
|
design_settings.SetCurrentMicroViaDrill(int(params["microViaDrill"] * scale))
|
|
|
|
# Set minimum values
|
|
if "minTrackWidth" in params:
|
|
design_settings.m_TrackMinWidth = int(params["minTrackWidth"] * scale)
|
|
if "minViaDiameter" in params:
|
|
design_settings.m_ViasMinSize = int(params["minViaDiameter"] * scale)
|
|
if "minViaDrill" in params:
|
|
design_settings.m_ViasMinDrill = int(params["minViaDrill"] * scale)
|
|
if "minMicroViaDiameter" in params:
|
|
design_settings.m_MicroViasMinSize = int(params["minMicroViaDiameter"] * scale)
|
|
if "minMicroViaDrill" in params:
|
|
design_settings.m_MicroViasMinDrill = int(params["minMicroViaDrill"] * scale)
|
|
|
|
# Set hole diameter
|
|
if "minHoleDiameter" in params:
|
|
design_settings.m_MinHoleDiameter = int(params["minHoleDiameter"] * scale)
|
|
|
|
# Set courtyard settings
|
|
if "requireCourtyard" in params:
|
|
design_settings.m_RequireCourtyards = params["requireCourtyard"]
|
|
if "courtyardClearance" in params:
|
|
design_settings.m_CourtyardMinClearance = int(params["courtyardClearance"] * scale)
|
|
|
|
return {
|
|
"success": True,
|
|
"message": "Updated design rules",
|
|
"rules": {
|
|
"clearance": design_settings.GetMinClearance() / scale,
|
|
"trackWidth": design_settings.GetCurrentTrackWidth() / scale,
|
|
"viaDiameter": design_settings.GetCurrentViaSize() / scale,
|
|
"viaDrill": design_settings.GetCurrentViaDrill() / scale,
|
|
"microViaDiameter": design_settings.GetCurrentMicroViaSize() / scale,
|
|
"microViaDrill": design_settings.GetCurrentMicroViaDrill() / scale,
|
|
"minTrackWidth": design_settings.m_TrackMinWidth / scale,
|
|
"minViaDiameter": design_settings.m_ViasMinSize / scale,
|
|
"minViaDrill": design_settings.m_ViasMinDrill / scale,
|
|
"minMicroViaDiameter": design_settings.m_MicroViasMinSize / scale,
|
|
"minMicroViaDrill": design_settings.m_MicroViasMinDrill / scale,
|
|
"minHoleDiameter": design_settings.m_MinHoleDiameter / scale,
|
|
"requireCourtyard": design_settings.m_RequireCourtyards,
|
|
"courtyardClearance": design_settings.m_CourtyardMinClearance / scale
|
|
}
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error setting design rules: {str(e)}")
|
|
return {
|
|
"success": False,
|
|
"message": "Failed to set design rules",
|
|
"errorDetails": str(e)
|
|
}
|
|
|
|
def get_design_rules(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""Get current design rules"""
|
|
try:
|
|
if not self.board:
|
|
return {
|
|
"success": False,
|
|
"message": "No board is loaded",
|
|
"errorDetails": "Load or create a board first"
|
|
}
|
|
|
|
design_settings = self.board.GetDesignSettings()
|
|
scale = 1000000 # nm to mm
|
|
|
|
return {
|
|
"success": True,
|
|
"rules": {
|
|
"clearance": design_settings.GetMinClearance() / scale,
|
|
"trackWidth": design_settings.GetCurrentTrackWidth() / scale,
|
|
"viaDiameter": design_settings.GetCurrentViaSize() / scale,
|
|
"viaDrill": design_settings.GetCurrentViaDrill() / scale,
|
|
"microViaDiameter": design_settings.GetCurrentMicroViaSize() / scale,
|
|
"microViaDrill": design_settings.GetCurrentMicroViaDrill() / scale,
|
|
"minTrackWidth": design_settings.m_TrackMinWidth / scale,
|
|
"minViaDiameter": design_settings.m_ViasMinSize / scale,
|
|
"minViaDrill": design_settings.m_ViasMinDrill / scale,
|
|
"minMicroViaDiameter": design_settings.m_MicroViasMinSize / scale,
|
|
"minMicroViaDrill": design_settings.m_MicroViasMinDrill / scale,
|
|
"minHoleDiameter": design_settings.m_MinHoleDiameter / scale,
|
|
"requireCourtyard": design_settings.m_RequireCourtyards,
|
|
"courtyardClearance": design_settings.m_CourtyardMinClearance / scale
|
|
}
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error getting design rules: {str(e)}")
|
|
return {
|
|
"success": False,
|
|
"message": "Failed to get design rules",
|
|
"errorDetails": str(e)
|
|
}
|
|
|
|
def run_drc(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""Run Design Rule Check"""
|
|
try:
|
|
if not self.board:
|
|
return {
|
|
"success": False,
|
|
"message": "No board is loaded",
|
|
"errorDetails": "Load or create a board first"
|
|
}
|
|
|
|
report_path = params.get("reportPath")
|
|
|
|
# Create DRC runner
|
|
drc = pcbnew.DRC(self.board)
|
|
|
|
# Run DRC
|
|
drc.Run()
|
|
|
|
# Get violations
|
|
violations = []
|
|
for marker in drc.GetMarkers():
|
|
violations.append({
|
|
"type": marker.GetErrorCode(),
|
|
"severity": "error",
|
|
"message": marker.GetDescription(),
|
|
"location": {
|
|
"x": marker.GetPos().x / 1000000,
|
|
"y": marker.GetPos().y / 1000000,
|
|
"unit": "mm"
|
|
}
|
|
})
|
|
|
|
# Save report if path provided
|
|
if report_path:
|
|
report_path = os.path.abspath(os.path.expanduser(report_path))
|
|
drc.WriteReport(report_path)
|
|
|
|
return {
|
|
"success": True,
|
|
"message": f"Found {len(violations)} DRC violations",
|
|
"violations": violations,
|
|
"reportPath": report_path if report_path else None
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error running DRC: {str(e)}")
|
|
return {
|
|
"success": False,
|
|
"message": "Failed to run DRC",
|
|
"errorDetails": str(e)
|
|
}
|
|
|
|
def get_drc_violations(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""Get list of DRC violations"""
|
|
try:
|
|
if not self.board:
|
|
return {
|
|
"success": False,
|
|
"message": "No board is loaded",
|
|
"errorDetails": "Load or create a board first"
|
|
}
|
|
|
|
severity = params.get("severity", "all")
|
|
|
|
# Get DRC markers
|
|
violations = []
|
|
for marker in self.board.GetDRCMarkers():
|
|
violation = {
|
|
"type": marker.GetErrorCode(),
|
|
"severity": "error", # KiCAD DRC markers are always errors
|
|
"message": marker.GetDescription(),
|
|
"location": {
|
|
"x": marker.GetPos().x / 1000000,
|
|
"y": marker.GetPos().y / 1000000,
|
|
"unit": "mm"
|
|
}
|
|
}
|
|
|
|
# Filter by severity if specified
|
|
if severity == "all" or severity == violation["severity"]:
|
|
violations.append(violation)
|
|
|
|
return {
|
|
"success": True,
|
|
"violations": violations
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error getting DRC violations: {str(e)}")
|
|
return {
|
|
"success": False,
|
|
"message": "Failed to get DRC violations",
|
|
"errorDetails": str(e)
|
|
}
|