Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
69aa8bed8This file is pinned to the selected release. Download the original to open it in your design tool.
"""
Project-related command implementations for KiCAD interface
"""
import logging
import os
import shutil
from typing import Any, Dict, Optional
import pcbnew # type: ignore
logger = logging.getLogger("kicad_interface")
class ProjectCommands:
"""Handles project-related KiCAD operations"""
def __init__(self, board: Optional[pcbnew.BOARD] = None):
"""Initialize with optional board instance"""
self.board = board
def create_project(self, params: Dict[str, Any]) -> Dict[str, Any]:
"""Create a new KiCAD project"""
try:
# Accept both 'name' (from MCP tool) and 'projectName' (legacy)
project_name = params.get("name") or params.get("projectName", "New_Project")
path = params.get("path", os.getcwd())
template = params.get("template")
# Generate the full project path
project_path = os.path.join(path, project_name)
if not project_path.endswith(".kicad_pro"):
project_path += ".kicad_pro"
# Create project directory if it doesn't exist
os.makedirs(os.path.dirname(project_path), exist_ok=True)
# Create a new board
board = pcbnew.BOARD()
# Set project properties
board.GetTitleBlock().SetTitle(project_name)
# Set current date with proper parameter
from datetime import datetime
current_date = datetime.now().strftime("%Y-%m-%d")
board.GetTitleBlock().SetDate(current_date)
# If template is specified, try to load it
if template:
template_path = os.path.expanduser(template)
if os.path.exists(template_path):
template_board = pcbnew.LoadBoard(template_path)
# Copy settings from template
board.SetDesignSettings(template_board.GetDesignSettings())
board.SetLayerStack(template_board.GetLayerStack())
# Save the board
board_path = project_path.replace(".kicad_pro", ".kicad_pcb")
board.SetFileName(board_path)
pcbnew.SaveBoard(board_path, board)
# Create schematic from template (use expanded template with symbol definitions)
schematic_path = project_path.replace(".kicad_pro", ".kicad_sch")
template_sch_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"..",
"templates",
"template_with_symbols_expanded.kicad_sch",
)
if os.path.exists(template_sch_path):
# Copy template schematic
shutil.copy(template_sch_path, schematic_path)
# Regenerate UUID to ensure uniqueness for each created project
import re
import uuid as uuid_module
with open(schematic_path, "r", encoding="utf-8") as f:
content = f.read()
new_uuid = str(uuid_module.uuid4())
content = re.sub(
r"\(uuid [0-9a-fA-F-]+\)",
f"(uuid {new_uuid})",
content,
count=1, # Only replace first (schematic) UUID
)
with open(schematic_path, "w", encoding="utf-8", newline="\n") as f:
f.write(content)
logger.info(f"Created schematic from template: {schematic_path}")
else:
# Fallback: create minimal schematic
logger.warning(
f"Template not found at {template_sch_path}, creating minimal schematic"
)
import uuid as uuid_module
schematic_uuid = str(uuid_module.uuid4())
with open(schematic_path, "w", encoding="utf-8", newline="\n") as f:
f.write('(kicad_sch (version 20250114) (generator "KiCAD-MCP-Server")\n\n')
f.write(f" (uuid {schematic_uuid})\n\n")
f.write(' (paper "A4")\n\n')
f.write(" (lib_symbols\n )\n\n")
f.write(' (sheet_instances\n (path "/" (page "1"))\n )\n')
f.write(")\n")
# Create project file with schematic reference
with open(project_path, "w") as f:
f.write("{\n")
f.write(' "board": {\n')
f.write(f' "filename": "{os.path.basename(board_path)}"\n')
f.write(" },\n")
f.write(' "sheets": [\n')
f.write(f' ["root", "{os.path.basename(schematic_path)}"]\n')
f.write(" ]\n")
f.write("}\n")
self.board = board
return {
"success": True,
"message": f"Created project: {project_name}",
"project": {
"name": project_name,
"path": project_path,
"boardPath": board_path,
"schematicPath": schematic_path,
},
}
except Exception as e:
logger.error(f"Error creating project: {str(e)}")
return {
"success": False,
"message": "Failed to create project",
"errorDetails": str(e),
}
def open_project(self, params: Dict[str, Any]) -> Dict[str, Any]:
"""Open an existing KiCAD project"""
try:
filename = params.get("filename")
if not filename:
return {
"success": False,
"message": "No filename provided",
"errorDetails": "The filename parameter is required",
}
# Expand user path and make absolute
filename = os.path.abspath(os.path.expanduser(filename))
# If it's a project file, get the board file
if filename.endswith(".kicad_pro"):
board_path = filename.replace(".kicad_pro", ".kicad_pcb")
else:
board_path = filename
# Load the board
board = pcbnew.LoadBoard(board_path)
self.board = board
return {
"success": True,
"message": f"Opened project: {os.path.basename(board_path)}",
"project": {
"name": os.path.splitext(os.path.basename(board_path))[0],
"path": filename,
"boardPath": board_path,
},
}
except Exception as e:
logger.error(f"Error opening project: {str(e)}")
return {
"success": False,
"message": "Failed to open project",
"errorDetails": str(e),
}
def save_project(self, params: Dict[str, Any]) -> Dict[str, Any]:
"""Save the current KiCAD project"""
try:
if not self.board:
return {
"success": False,
"message": "No board is loaded",
"errorDetails": "Load or create a board first",
}
filename = params.get("filename")
if filename:
# Save to new location
filename = os.path.abspath(os.path.expanduser(filename))
self.board.SetFileName(filename)
# Save the board
pcbnew.SaveBoard(self.board.GetFileName(), self.board)
return {
"success": True,
"message": f"Saved project to: {self.board.GetFileName()}",
"project": {
"name": os.path.splitext(os.path.basename(self.board.GetFileName()))[0],
"path": self.board.GetFileName(),
},
}
except Exception as e:
logger.error(f"Error saving project: {str(e)}")
return {
"success": False,
"message": "Failed to save project",
"errorDetails": str(e),
}
def get_project_info(self, params: Dict[str, Any]) -> Dict[str, Any]:
"""Get information about the current project"""
try:
if not self.board:
return {
"success": False,
"message": "No board is loaded",
"errorDetails": "Load or create a board first",
}
title_block = self.board.GetTitleBlock()
filename = self.board.GetFileName()
return {
"success": True,
"project": {
"name": os.path.splitext(os.path.basename(filename))[0],
"path": filename,
"title": title_block.GetTitle(),
"date": title_block.GetDate(),
"revision": title_block.GetRevision(),
"company": title_block.GetCompany(),
"comment1": title_block.GetComment(0),
"comment2": title_block.GetComment(1),
"comment3": title_block.GetComment(2),
"comment4": title_block.GetComment(3),
},
}
except Exception as e:
logger.error(f"Error getting project info: {str(e)}")
return {
"success": False,
"message": "Failed to get project information",
"errorDetails": str(e),
}