style: apply black formatting to changed files
This commit is contained in:
@@ -8,7 +8,8 @@ import logging
|
||||
from typing import Dict, Any, Optional, List, Tuple
|
||||
import base64
|
||||
|
||||
logger = logging.getLogger('kicad_interface')
|
||||
logger = logging.getLogger("kicad_interface")
|
||||
|
||||
|
||||
class ExportCommands:
|
||||
"""Handles export-related KiCAD operations"""
|
||||
@@ -24,7 +25,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "No board is loaded",
|
||||
"errorDetails": "Load or create a board first"
|
||||
"errorDetails": "Load or create a board first",
|
||||
}
|
||||
|
||||
output_dir = params.get("outputDir")
|
||||
@@ -38,7 +39,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Missing output directory",
|
||||
"errorDetails": "outputDir parameter is required"
|
||||
"errorDetails": "outputDir parameter is required",
|
||||
}
|
||||
|
||||
# Create output directory if it doesn't exist
|
||||
@@ -47,7 +48,7 @@ class ExportCommands:
|
||||
|
||||
# Create plot controller
|
||||
plotter = pcbnew.PLOT_CONTROLLER(self.board)
|
||||
|
||||
|
||||
# Set up plot options
|
||||
plot_opts = plotter.GetPlotOptions()
|
||||
plot_opts.SetOutputDirectory(output_dir)
|
||||
@@ -84,28 +85,40 @@ class ExportCommands:
|
||||
|
||||
if kicad_cli and board_file and os.path.exists(board_file):
|
||||
import subprocess
|
||||
|
||||
# Generate drill files using kicad-cli
|
||||
cmd = [
|
||||
kicad_cli,
|
||||
'pcb', 'export', 'drill',
|
||||
'--output', output_dir,
|
||||
'--format', 'excellon',
|
||||
'--drill-origin', 'absolute',
|
||||
'--excellon-separate-th', # Separate plated/non-plated
|
||||
board_file
|
||||
"pcb",
|
||||
"export",
|
||||
"drill",
|
||||
"--output",
|
||||
output_dir,
|
||||
"--format",
|
||||
"excellon",
|
||||
"--drill-origin",
|
||||
"absolute",
|
||||
"--excellon-separate-th", # Separate plated/non-plated
|
||||
board_file,
|
||||
]
|
||||
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
|
||||
result = subprocess.run(
|
||||
cmd, capture_output=True, text=True, timeout=60
|
||||
)
|
||||
if result.returncode == 0:
|
||||
# Get list of generated drill files
|
||||
for file in os.listdir(output_dir):
|
||||
if file.endswith((".drl", ".cnc")):
|
||||
drill_files.append(file)
|
||||
else:
|
||||
logger.warning(f"Drill file generation failed: {result.stderr}")
|
||||
logger.warning(
|
||||
f"Drill file generation failed: {result.stderr}"
|
||||
)
|
||||
except Exception as drill_error:
|
||||
logger.warning(f"Could not generate drill files: {str(drill_error)}")
|
||||
logger.warning(
|
||||
f"Could not generate drill files: {str(drill_error)}"
|
||||
)
|
||||
else:
|
||||
logger.warning("kicad-cli not available for drill file generation")
|
||||
|
||||
@@ -115,9 +128,9 @@ class ExportCommands:
|
||||
"files": {
|
||||
"gerber": plotted_layers,
|
||||
"drill": drill_files,
|
||||
"map": ["job.gbrjob"] if generate_map_file else []
|
||||
"map": ["job.gbrjob"] if generate_map_file else [],
|
||||
},
|
||||
"outputDir": output_dir
|
||||
"outputDir": output_dir,
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
@@ -125,7 +138,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Failed to export Gerber files",
|
||||
"errorDetails": str(e)
|
||||
"errorDetails": str(e),
|
||||
}
|
||||
|
||||
def export_pdf(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||
@@ -135,7 +148,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "No board is loaded",
|
||||
"errorDetails": "Load or create a board first"
|
||||
"errorDetails": "Load or create a board first",
|
||||
}
|
||||
|
||||
output_path = params.get("outputPath")
|
||||
@@ -148,7 +161,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Missing output path",
|
||||
"errorDetails": "outputPath parameter is required"
|
||||
"errorDetails": "outputPath parameter is required",
|
||||
}
|
||||
|
||||
# Create output directory if it doesn't exist
|
||||
@@ -180,13 +193,15 @@ class ExportCommands:
|
||||
plot_opts.SetAutoScale(True)
|
||||
# Note: KiCAD 9.0 doesn't support explicit page size selection
|
||||
# for formats other than A4. The PDF will auto-scale to fit.
|
||||
logger.warning(f"Page size '{page_size}' requested, but KiCAD 9.0 only supports A4 explicitly. Using auto-scale instead.")
|
||||
logger.warning(
|
||||
f"Page size '{page_size}' requested, but KiCAD 9.0 only supports A4 explicitly. Using auto-scale instead."
|
||||
)
|
||||
|
||||
# Open plot for writing
|
||||
# Note: For PDF, all layers are combined into a single file
|
||||
# KiCAD prepends the board filename to the plot file name
|
||||
base_name = os.path.basename(output_path).replace('.pdf', '')
|
||||
plotter.OpenPlotfile(base_name, pcbnew.PLOT_FORMAT_PDF, '')
|
||||
base_name = os.path.basename(output_path).replace(".pdf", "")
|
||||
plotter.OpenPlotfile(base_name, pcbnew.PLOT_FORMAT_PDF, "")
|
||||
|
||||
# Plot specified layers or all enabled layers
|
||||
plotted_layers = []
|
||||
@@ -212,7 +227,9 @@ class ExportCommands:
|
||||
# Get the actual output filename that was created
|
||||
board_name = os.path.splitext(os.path.basename(self.board.GetFileName()))[0]
|
||||
actual_filename = f"{board_name}-{base_name}.pdf"
|
||||
actual_output_path = os.path.join(os.path.dirname(output_path), actual_filename)
|
||||
actual_output_path = os.path.join(
|
||||
os.path.dirname(output_path), actual_filename
|
||||
)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
@@ -221,8 +238,8 @@ class ExportCommands:
|
||||
"path": actual_output_path,
|
||||
"requestedPath": output_path,
|
||||
"layers": plotted_layers,
|
||||
"pageSize": page_size if page_size == "A4" else "auto-scaled"
|
||||
}
|
||||
"pageSize": page_size if page_size == "A4" else "auto-scaled",
|
||||
},
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
@@ -230,7 +247,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Failed to export PDF file",
|
||||
"errorDetails": str(e)
|
||||
"errorDetails": str(e),
|
||||
}
|
||||
|
||||
def export_svg(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||
@@ -240,7 +257,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "No board is loaded",
|
||||
"errorDetails": "Load or create a board first"
|
||||
"errorDetails": "Load or create a board first",
|
||||
}
|
||||
|
||||
output_path = params.get("outputPath")
|
||||
@@ -252,7 +269,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Missing output path",
|
||||
"errorDetails": "outputPath parameter is required"
|
||||
"errorDetails": "outputPath parameter is required",
|
||||
}
|
||||
|
||||
# Create output directory if it doesn't exist
|
||||
@@ -261,7 +278,7 @@ class ExportCommands:
|
||||
|
||||
# Create plot controller
|
||||
plotter = pcbnew.PLOT_CONTROLLER(self.board)
|
||||
|
||||
|
||||
# Set up plot options
|
||||
plot_opts = plotter.GetPlotOptions()
|
||||
plot_opts.SetOutputDirectory(os.path.dirname(output_path))
|
||||
@@ -290,10 +307,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": True,
|
||||
"message": "Exported SVG file",
|
||||
"file": {
|
||||
"path": output_path,
|
||||
"layers": plotted_layers
|
||||
}
|
||||
"file": {"path": output_path, "layers": plotted_layers},
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
@@ -301,7 +315,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Failed to export SVG file",
|
||||
"errorDetails": str(e)
|
||||
"errorDetails": str(e),
|
||||
}
|
||||
|
||||
def export_3d(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||
@@ -315,7 +329,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "No board is loaded",
|
||||
"errorDetails": "Load or create a board first"
|
||||
"errorDetails": "Load or create a board first",
|
||||
}
|
||||
|
||||
output_path = params.get("outputPath")
|
||||
@@ -329,7 +343,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Missing output path",
|
||||
"errorDetails": "outputPath parameter is required"
|
||||
"errorDetails": "outputPath parameter is required",
|
||||
}
|
||||
|
||||
# Get board file path
|
||||
@@ -338,7 +352,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Board file not found",
|
||||
"errorDetails": "Board must be saved before exporting 3D models"
|
||||
"errorDetails": "Board must be saved before exporting 3D models",
|
||||
}
|
||||
|
||||
# Create output directory if it doesn't exist
|
||||
@@ -351,7 +365,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "kicad-cli not found",
|
||||
"errorDetails": "KiCAD CLI tool not found. Install KiCAD 8.0+ or set PATH."
|
||||
"errorDetails": "KiCAD CLI tool not found. Install KiCAD 8.0+ or set PATH.",
|
||||
}
|
||||
|
||||
# Build command based on format
|
||||
@@ -360,30 +374,39 @@ class ExportCommands:
|
||||
if format_upper == "STEP":
|
||||
cmd = [
|
||||
kicad_cli,
|
||||
'pcb', 'export', 'step',
|
||||
'--output', output_path,
|
||||
'--force' # Overwrite existing file
|
||||
"pcb",
|
||||
"export",
|
||||
"step",
|
||||
"--output",
|
||||
output_path,
|
||||
"--force", # Overwrite existing file
|
||||
]
|
||||
|
||||
# Add options based on parameters
|
||||
if not include_components:
|
||||
cmd.append('--no-components')
|
||||
cmd.append("--no-components")
|
||||
if include_copper:
|
||||
cmd.extend(['--include-tracks', '--include-pads', '--include-zones'])
|
||||
cmd.extend(
|
||||
["--include-tracks", "--include-pads", "--include-zones"]
|
||||
)
|
||||
if include_silkscreen:
|
||||
cmd.append('--include-silkscreen')
|
||||
cmd.append("--include-silkscreen")
|
||||
if include_solder_mask:
|
||||
cmd.append('--include-soldermask')
|
||||
cmd.append("--include-soldermask")
|
||||
|
||||
cmd.append(board_file)
|
||||
|
||||
elif format_upper == "VRML":
|
||||
cmd = [
|
||||
kicad_cli,
|
||||
'pcb', 'export', 'vrml',
|
||||
'--output', output_path,
|
||||
'--units', 'mm', # Use mm for consistency
|
||||
'--force'
|
||||
"pcb",
|
||||
"export",
|
||||
"vrml",
|
||||
"--output",
|
||||
output_path,
|
||||
"--units",
|
||||
"mm", # Use mm for consistency
|
||||
"--force",
|
||||
]
|
||||
|
||||
if not include_components:
|
||||
@@ -397,7 +420,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Unsupported format",
|
||||
"errorDetails": f"Format {format} is not supported. Use 'STEP' or 'VRML'."
|
||||
"errorDetails": f"Format {format} is not supported. Use 'STEP' or 'VRML'.",
|
||||
}
|
||||
|
||||
# Execute kicad-cli command
|
||||
@@ -407,7 +430,7 @@ class ExportCommands:
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=300 # 5 minute timeout for 3D export
|
||||
timeout=300, # 5 minute timeout for 3D export
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
@@ -415,16 +438,13 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "3D export command failed",
|
||||
"errorDetails": result.stderr
|
||||
"errorDetails": result.stderr,
|
||||
}
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Exported {format_upper} file",
|
||||
"file": {
|
||||
"path": output_path,
|
||||
"format": format_upper
|
||||
}
|
||||
"file": {"path": output_path, "format": format_upper},
|
||||
}
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
@@ -432,14 +452,14 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "3D export timed out",
|
||||
"errorDetails": "Export took longer than 5 minutes"
|
||||
"errorDetails": "Export took longer than 5 minutes",
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Error exporting 3D model: {str(e)}")
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Failed to export 3D model",
|
||||
"errorDetails": str(e)
|
||||
"errorDetails": str(e),
|
||||
}
|
||||
|
||||
def export_bom(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||
@@ -449,7 +469,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "No board is loaded",
|
||||
"errorDetails": "Load or create a board first"
|
||||
"errorDetails": "Load or create a board first",
|
||||
}
|
||||
|
||||
output_path = params.get("outputPath")
|
||||
@@ -461,7 +481,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Missing output path",
|
||||
"errorDetails": "outputPath parameter is required"
|
||||
"errorDetails": "outputPath parameter is required",
|
||||
}
|
||||
|
||||
# Create output directory if it doesn't exist
|
||||
@@ -475,7 +495,7 @@ class ExportCommands:
|
||||
"reference": module.GetReference(),
|
||||
"value": module.GetValue(),
|
||||
"footprint": module.GetFPID().GetUniStringLibId(),
|
||||
"layer": self.board.GetLayerName(module.GetLayer())
|
||||
"layer": self.board.GetLayerName(module.GetLayer()),
|
||||
}
|
||||
|
||||
# Add requested attributes
|
||||
@@ -495,7 +515,7 @@ class ExportCommands:
|
||||
"value": comp["value"],
|
||||
"footprint": comp["footprint"],
|
||||
"quantity": 1,
|
||||
"references": [comp["reference"]]
|
||||
"references": [comp["reference"]],
|
||||
}
|
||||
else:
|
||||
grouped[key]["quantity"] += 1
|
||||
@@ -515,7 +535,7 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Unsupported format",
|
||||
"errorDetails": f"Format {format} is not supported"
|
||||
"errorDetails": f"Format {format} is not supported",
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -524,8 +544,8 @@ class ExportCommands:
|
||||
"file": {
|
||||
"path": output_path,
|
||||
"format": format,
|
||||
"componentCount": len(components)
|
||||
}
|
||||
"componentCount": len(components),
|
||||
},
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
@@ -533,13 +553,14 @@ class ExportCommands:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Failed to export BOM",
|
||||
"errorDetails": str(e)
|
||||
"errorDetails": str(e),
|
||||
}
|
||||
|
||||
def _export_bom_csv(self, path: str, components: List[Dict[str, Any]]) -> None:
|
||||
"""Export BOM to CSV format"""
|
||||
import csv
|
||||
with open(path, 'w', newline='') as f:
|
||||
|
||||
with open(path, "w", newline="") as f:
|
||||
writer = csv.DictWriter(f, fieldnames=components[0].keys())
|
||||
writer.writeheader()
|
||||
writer.writerows(components)
|
||||
@@ -547,6 +568,7 @@ class ExportCommands:
|
||||
def _export_bom_xml(self, path: str, components: List[Dict[str, Any]]) -> None:
|
||||
"""Export BOM to XML format"""
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
root = ET.Element("bom")
|
||||
for comp in components:
|
||||
comp_elem = ET.SubElement(root, "component")
|
||||
@@ -554,7 +576,7 @@ class ExportCommands:
|
||||
elem = ET.SubElement(comp_elem, key)
|
||||
elem.text = str(value)
|
||||
tree = ET.ElementTree(root)
|
||||
tree.write(path, encoding='utf-8', xml_declaration=True)
|
||||
tree.write(path, encoding="utf-8", xml_declaration=True)
|
||||
|
||||
def _export_bom_html(self, path: str, components: List[Dict[str, Any]]) -> None:
|
||||
"""Export BOM to HTML format"""
|
||||
@@ -571,13 +593,14 @@ class ExportCommands:
|
||||
html.append(f"<td>{value}</td>")
|
||||
html.append("</tr>")
|
||||
html.append("</table></body></html>")
|
||||
with open(path, 'w') as f:
|
||||
with open(path, "w") as f:
|
||||
f.write("\n".join(html))
|
||||
|
||||
def _export_bom_json(self, path: str, components: List[Dict[str, Any]]) -> None:
|
||||
"""Export BOM to JSON format"""
|
||||
import json
|
||||
with open(path, 'w') as f:
|
||||
|
||||
with open(path, "w") as f:
|
||||
json.dump({"components": components}, f, indent=2)
|
||||
|
||||
def _find_kicad_cli(self) -> Optional[str]:
|
||||
|
||||
Reference in New Issue
Block a user