kicad-sch-api 0.4.0__py3-none-any.whl → 0.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kicad_sch_api/__init__.py +2 -2
- kicad_sch_api/cli/__init__.py +45 -0
- kicad_sch_api/cli/base.py +302 -0
- kicad_sch_api/cli/bom.py +164 -0
- kicad_sch_api/cli/erc.py +229 -0
- kicad_sch_api/cli/export_docs.py +289 -0
- kicad_sch_api/cli/netlist.py +94 -0
- kicad_sch_api/cli/types.py +43 -0
- kicad_sch_api/core/collections/__init__.py +5 -0
- kicad_sch_api/core/collections/base.py +248 -0
- kicad_sch_api/core/component_bounds.py +5 -0
- kicad_sch_api/core/components.py +62 -45
- kicad_sch_api/core/config.py +85 -3
- kicad_sch_api/core/factories/__init__.py +5 -0
- kicad_sch_api/core/factories/element_factory.py +276 -0
- kicad_sch_api/core/junctions.py +26 -75
- kicad_sch_api/core/labels.py +28 -52
- kicad_sch_api/core/managers/file_io.py +3 -2
- kicad_sch_api/core/managers/metadata.py +6 -5
- kicad_sch_api/core/managers/validation.py +3 -2
- kicad_sch_api/core/managers/wire.py +7 -1
- kicad_sch_api/core/nets.py +38 -43
- kicad_sch_api/core/no_connects.py +29 -53
- kicad_sch_api/core/parser.py +75 -1765
- kicad_sch_api/core/schematic.py +211 -148
- kicad_sch_api/core/texts.py +28 -55
- kicad_sch_api/core/types.py +59 -18
- kicad_sch_api/core/wires.py +27 -75
- kicad_sch_api/parsers/elements/__init__.py +22 -0
- kicad_sch_api/parsers/elements/graphics_parser.py +564 -0
- kicad_sch_api/parsers/elements/label_parser.py +194 -0
- kicad_sch_api/parsers/elements/library_parser.py +165 -0
- kicad_sch_api/parsers/elements/metadata_parser.py +58 -0
- kicad_sch_api/parsers/elements/sheet_parser.py +352 -0
- kicad_sch_api/parsers/elements/symbol_parser.py +313 -0
- kicad_sch_api/parsers/elements/text_parser.py +250 -0
- kicad_sch_api/parsers/elements/wire_parser.py +242 -0
- kicad_sch_api/parsers/utils.py +80 -0
- kicad_sch_api/validation/__init__.py +25 -0
- kicad_sch_api/validation/erc.py +171 -0
- kicad_sch_api/validation/erc_models.py +203 -0
- kicad_sch_api/validation/pin_matrix.py +243 -0
- kicad_sch_api/validation/validators.py +391 -0
- {kicad_sch_api-0.4.0.dist-info → kicad_sch_api-0.4.1.dist-info}/METADATA +17 -9
- kicad_sch_api-0.4.1.dist-info/RECORD +87 -0
- kicad_sch_api/core/manhattan_routing.py +0 -430
- kicad_sch_api/core/simple_manhattan.py +0 -228
- kicad_sch_api/core/wire_routing.py +0 -380
- kicad_sch_api/parsers/label_parser.py +0 -254
- kicad_sch_api/parsers/symbol_parser.py +0 -222
- kicad_sch_api/parsers/wire_parser.py +0 -99
- kicad_sch_api-0.4.0.dist-info/RECORD +0 -67
- {kicad_sch_api-0.4.0.dist-info → kicad_sch_api-0.4.1.dist-info}/WHEEL +0 -0
- {kicad_sch_api-0.4.0.dist-info → kicad_sch_api-0.4.1.dist-info}/entry_points.txt +0 -0
- {kicad_sch_api-0.4.0.dist-info → kicad_sch_api-0.4.1.dist-info}/licenses/LICENSE +0 -0
- {kicad_sch_api-0.4.0.dist-info → kicad_sch_api-0.4.1.dist-info}/top_level.txt +0 -0
kicad_sch_api/cli/erc.py
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"""Electrical Rule Check (ERC) functionality using kicad-cli."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
from kicad_sch_api.cli.base import KiCadExecutor
|
|
9
|
+
from kicad_sch_api.cli.types import ErcFormat, ErcSeverity, Units
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class ErcViolation:
|
|
14
|
+
"""Represents a single ERC violation."""
|
|
15
|
+
severity: str
|
|
16
|
+
type: str
|
|
17
|
+
description: str
|
|
18
|
+
sheet: str
|
|
19
|
+
position: Optional[Dict[str, float]] = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class ErcReport:
|
|
24
|
+
"""ERC report with violations and summary."""
|
|
25
|
+
violations: List[ErcViolation]
|
|
26
|
+
error_count: int
|
|
27
|
+
warning_count: int
|
|
28
|
+
exclusion_count: int
|
|
29
|
+
schematic_path: Path
|
|
30
|
+
raw_output: str
|
|
31
|
+
|
|
32
|
+
def has_errors(self) -> bool:
|
|
33
|
+
"""Check if report contains any errors."""
|
|
34
|
+
return self.error_count > 0
|
|
35
|
+
|
|
36
|
+
def has_warnings(self) -> bool:
|
|
37
|
+
"""Check if report contains any warnings."""
|
|
38
|
+
return self.warning_count > 0
|
|
39
|
+
|
|
40
|
+
def has_violations(self) -> bool:
|
|
41
|
+
"""Check if report contains any violations."""
|
|
42
|
+
return len(self.violations) > 0
|
|
43
|
+
|
|
44
|
+
def get_errors(self) -> List[ErcViolation]:
|
|
45
|
+
"""Get all error-level violations."""
|
|
46
|
+
return [v for v in self.violations if v.severity == "error"]
|
|
47
|
+
|
|
48
|
+
def get_warnings(self) -> List[ErcViolation]:
|
|
49
|
+
"""Get all warning-level violations."""
|
|
50
|
+
return [v for v in self.violations if v.severity == "warning"]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def run_erc(
|
|
54
|
+
schematic_path: Path,
|
|
55
|
+
output_path: Optional[Path] = None,
|
|
56
|
+
format: ErcFormat = "json",
|
|
57
|
+
severity: ErcSeverity = "all",
|
|
58
|
+
units: Units = "mm",
|
|
59
|
+
exit_code_violations: bool = False,
|
|
60
|
+
variables: Optional[Dict[str, str]] = None,
|
|
61
|
+
executor: Optional[KiCadExecutor] = None,
|
|
62
|
+
) -> ErcReport:
|
|
63
|
+
"""
|
|
64
|
+
Run Electrical Rule Check (ERC) on schematic using kicad-cli.
|
|
65
|
+
|
|
66
|
+
Validates schematic for electrical errors like unconnected pins,
|
|
67
|
+
conflicting drivers, etc.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
schematic_path: Path to .kicad_sch file
|
|
71
|
+
output_path: Output report path (auto-generated if None)
|
|
72
|
+
format: Report format ('json' or 'report')
|
|
73
|
+
severity: Severity levels to report ('all', 'error', 'warning', 'exclusions')
|
|
74
|
+
units: Measurement units for coordinates ('mm', 'in', 'mils')
|
|
75
|
+
exit_code_violations: Return non-zero exit code if violations exist
|
|
76
|
+
variables: Project variables to override (key=value pairs)
|
|
77
|
+
executor: Custom KiCadExecutor instance (creates default if None)
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
ErcReport with violations and summary
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
RuntimeError: If kicad-cli not found or ERC fails
|
|
84
|
+
FileNotFoundError: If schematic file doesn't exist
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
>>> from pathlib import Path
|
|
88
|
+
>>> report = run_erc(Path('circuit.kicad_sch'))
|
|
89
|
+
>>> if report.has_errors():
|
|
90
|
+
... print(f"Found {report.error_count} errors:")
|
|
91
|
+
... for error in report.get_errors():
|
|
92
|
+
... print(f" - {error.description}")
|
|
93
|
+
|
|
94
|
+
>>> # Check for specific severity
|
|
95
|
+
>>> report = run_erc(
|
|
96
|
+
... Path('circuit.kicad_sch'),
|
|
97
|
+
... severity='error', # Only errors
|
|
98
|
+
... )
|
|
99
|
+
|
|
100
|
+
>>> # Generate human-readable report
|
|
101
|
+
>>> report = run_erc(
|
|
102
|
+
... Path('circuit.kicad_sch'),
|
|
103
|
+
... format='report',
|
|
104
|
+
... )
|
|
105
|
+
>>> print(report.raw_output)
|
|
106
|
+
"""
|
|
107
|
+
schematic_path = Path(schematic_path)
|
|
108
|
+
|
|
109
|
+
if not schematic_path.exists():
|
|
110
|
+
raise FileNotFoundError(f"Schematic not found: {schematic_path}")
|
|
111
|
+
|
|
112
|
+
# Auto-generate output path if not provided
|
|
113
|
+
if output_path is None:
|
|
114
|
+
ext = ".json" if format == "json" else ".rpt"
|
|
115
|
+
output_path = schematic_path.with_stem(f"{schematic_path.stem}_erc").with_suffix(ext)
|
|
116
|
+
else:
|
|
117
|
+
output_path = Path(output_path)
|
|
118
|
+
|
|
119
|
+
# Create executor if not provided
|
|
120
|
+
if executor is None:
|
|
121
|
+
executor = KiCadExecutor()
|
|
122
|
+
|
|
123
|
+
# Build command
|
|
124
|
+
args = [
|
|
125
|
+
"sch", "erc",
|
|
126
|
+
"--output", str(output_path),
|
|
127
|
+
"--format", format,
|
|
128
|
+
"--units", units,
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
# Add severity flags
|
|
132
|
+
if severity == "all":
|
|
133
|
+
args.append("--severity-all")
|
|
134
|
+
elif severity == "error":
|
|
135
|
+
args.append("--severity-error")
|
|
136
|
+
elif severity == "warning":
|
|
137
|
+
args.append("--severity-warning")
|
|
138
|
+
elif severity == "exclusions":
|
|
139
|
+
args.append("--severity-exclusions")
|
|
140
|
+
|
|
141
|
+
# Add optional parameters
|
|
142
|
+
if exit_code_violations:
|
|
143
|
+
args.append("--exit-code-violations")
|
|
144
|
+
|
|
145
|
+
if variables:
|
|
146
|
+
for key, value in variables.items():
|
|
147
|
+
args.extend(["--define-var", f"{key}={value}"])
|
|
148
|
+
|
|
149
|
+
# Add schematic path
|
|
150
|
+
args.append(str(schematic_path))
|
|
151
|
+
|
|
152
|
+
# Execute command (don't check return code if exit_code_violations is True)
|
|
153
|
+
result = executor.run(args, cwd=schematic_path.parent, check=not exit_code_violations)
|
|
154
|
+
|
|
155
|
+
# Read output file
|
|
156
|
+
output_content = output_path.read_text()
|
|
157
|
+
|
|
158
|
+
# Parse report
|
|
159
|
+
if format == "json":
|
|
160
|
+
report = _parse_json_report(output_content, schematic_path)
|
|
161
|
+
else:
|
|
162
|
+
report = _parse_text_report(output_content, schematic_path)
|
|
163
|
+
|
|
164
|
+
return report
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _parse_json_report(content: str, schematic_path: Path) -> ErcReport:
|
|
168
|
+
"""Parse JSON ERC report."""
|
|
169
|
+
try:
|
|
170
|
+
data = json.loads(content)
|
|
171
|
+
except json.JSONDecodeError:
|
|
172
|
+
# If JSON parsing fails, return empty report
|
|
173
|
+
return ErcReport(
|
|
174
|
+
violations=[],
|
|
175
|
+
error_count=0,
|
|
176
|
+
warning_count=0,
|
|
177
|
+
exclusion_count=0,
|
|
178
|
+
schematic_path=schematic_path,
|
|
179
|
+
raw_output=content,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
violations = []
|
|
183
|
+
error_count = 0
|
|
184
|
+
warning_count = 0
|
|
185
|
+
exclusion_count = 0
|
|
186
|
+
|
|
187
|
+
# Parse violations from JSON
|
|
188
|
+
for violation_data in data.get("violations", []):
|
|
189
|
+
violation = ErcViolation(
|
|
190
|
+
severity=violation_data.get("severity", "unknown"),
|
|
191
|
+
type=violation_data.get("type", "unknown"),
|
|
192
|
+
description=violation_data.get("description", ""),
|
|
193
|
+
sheet=violation_data.get("sheet", ""),
|
|
194
|
+
position=violation_data.get("position"),
|
|
195
|
+
)
|
|
196
|
+
violations.append(violation)
|
|
197
|
+
|
|
198
|
+
if violation.severity == "error":
|
|
199
|
+
error_count += 1
|
|
200
|
+
elif violation.severity == "warning":
|
|
201
|
+
warning_count += 1
|
|
202
|
+
elif violation.severity == "exclusion":
|
|
203
|
+
exclusion_count += 1
|
|
204
|
+
|
|
205
|
+
return ErcReport(
|
|
206
|
+
violations=violations,
|
|
207
|
+
error_count=error_count,
|
|
208
|
+
warning_count=warning_count,
|
|
209
|
+
exclusion_count=exclusion_count,
|
|
210
|
+
schematic_path=schematic_path,
|
|
211
|
+
raw_output=content,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _parse_text_report(content: str, schematic_path: Path) -> ErcReport:
|
|
216
|
+
"""Parse text ERC report."""
|
|
217
|
+
# For text reports, do simple counting
|
|
218
|
+
lines = content.split("\n")
|
|
219
|
+
error_count = sum(1 for line in lines if "error" in line.lower())
|
|
220
|
+
warning_count = sum(1 for line in lines if "warning" in line.lower())
|
|
221
|
+
|
|
222
|
+
return ErcReport(
|
|
223
|
+
violations=[], # Text format doesn't provide structured violations
|
|
224
|
+
error_count=error_count,
|
|
225
|
+
warning_count=warning_count,
|
|
226
|
+
exclusion_count=0,
|
|
227
|
+
schematic_path=schematic_path,
|
|
228
|
+
raw_output=content,
|
|
229
|
+
)
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""Document export functionality (PDF, SVG, DXF) using kicad-cli."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
from kicad_sch_api.cli.base import KiCadExecutor
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def export_pdf(
|
|
10
|
+
schematic_path: Path,
|
|
11
|
+
output_path: Optional[Path] = None,
|
|
12
|
+
theme: Optional[str] = None,
|
|
13
|
+
black_and_white: bool = False,
|
|
14
|
+
drawing_sheet: Optional[Path] = None,
|
|
15
|
+
exclude_drawing_sheet: bool = False,
|
|
16
|
+
default_font: str = "KiCad Font",
|
|
17
|
+
exclude_pdf_property_popups: bool = False,
|
|
18
|
+
exclude_pdf_hierarchical_links: bool = False,
|
|
19
|
+
exclude_pdf_metadata: bool = False,
|
|
20
|
+
no_background_color: bool = False,
|
|
21
|
+
pages: Optional[List[int]] = None,
|
|
22
|
+
variables: Optional[Dict[str, str]] = None,
|
|
23
|
+
executor: Optional[KiCadExecutor] = None,
|
|
24
|
+
) -> Path:
|
|
25
|
+
"""
|
|
26
|
+
Export schematic as PDF using kicad-cli.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
schematic_path: Path to .kicad_sch file
|
|
30
|
+
output_path: Output PDF path (auto-generated if None)
|
|
31
|
+
theme: Color theme to use (default: schematic settings)
|
|
32
|
+
black_and_white: Export in black and white
|
|
33
|
+
drawing_sheet: Path to custom drawing sheet
|
|
34
|
+
exclude_drawing_sheet: Don't include drawing sheet
|
|
35
|
+
default_font: Default font name (default: "KiCad Font")
|
|
36
|
+
exclude_pdf_property_popups: Don't generate property popups
|
|
37
|
+
exclude_pdf_hierarchical_links: Don't generate clickable hierarchical links
|
|
38
|
+
exclude_pdf_metadata: Don't generate PDF metadata from variables
|
|
39
|
+
no_background_color: Don't set background color
|
|
40
|
+
pages: List of page numbers to export (None = all pages)
|
|
41
|
+
variables: Project variables to override
|
|
42
|
+
executor: Custom KiCadExecutor instance
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Path to generated PDF file
|
|
46
|
+
|
|
47
|
+
Example:
|
|
48
|
+
>>> from pathlib import Path
|
|
49
|
+
>>> pdf = export_pdf(
|
|
50
|
+
... Path('circuit.kicad_sch'),
|
|
51
|
+
... theme='KiCad Classic',
|
|
52
|
+
... )
|
|
53
|
+
"""
|
|
54
|
+
schematic_path = Path(schematic_path)
|
|
55
|
+
|
|
56
|
+
if not schematic_path.exists():
|
|
57
|
+
raise FileNotFoundError(f"Schematic not found: {schematic_path}")
|
|
58
|
+
|
|
59
|
+
if output_path is None:
|
|
60
|
+
output_path = schematic_path.with_suffix(".pdf")
|
|
61
|
+
else:
|
|
62
|
+
output_path = Path(output_path)
|
|
63
|
+
|
|
64
|
+
if executor is None:
|
|
65
|
+
executor = KiCadExecutor()
|
|
66
|
+
|
|
67
|
+
# Build command
|
|
68
|
+
args = ["sch", "export", "pdf", "--output", str(output_path)]
|
|
69
|
+
|
|
70
|
+
if theme:
|
|
71
|
+
args.extend(["--theme", theme])
|
|
72
|
+
|
|
73
|
+
if black_and_white:
|
|
74
|
+
args.append("--black-and-white")
|
|
75
|
+
|
|
76
|
+
if drawing_sheet:
|
|
77
|
+
args.extend(["--drawing-sheet", str(drawing_sheet)])
|
|
78
|
+
|
|
79
|
+
if exclude_drawing_sheet:
|
|
80
|
+
args.append("--exclude-drawing-sheet")
|
|
81
|
+
|
|
82
|
+
args.extend(["--default-font", default_font])
|
|
83
|
+
|
|
84
|
+
if exclude_pdf_property_popups:
|
|
85
|
+
args.append("--exclude-pdf-property-popups")
|
|
86
|
+
|
|
87
|
+
if exclude_pdf_hierarchical_links:
|
|
88
|
+
args.append("--exclude-pdf-hierarchical-links")
|
|
89
|
+
|
|
90
|
+
if exclude_pdf_metadata:
|
|
91
|
+
args.append("--exclude-pdf-metadata")
|
|
92
|
+
|
|
93
|
+
if no_background_color:
|
|
94
|
+
args.append("--no-background-color")
|
|
95
|
+
|
|
96
|
+
if pages:
|
|
97
|
+
args.extend(["--pages", ",".join(map(str, pages))])
|
|
98
|
+
|
|
99
|
+
if variables:
|
|
100
|
+
for key, value in variables.items():
|
|
101
|
+
args.extend(["--define-var", f"{key}={value}"])
|
|
102
|
+
|
|
103
|
+
args.append(str(schematic_path))
|
|
104
|
+
|
|
105
|
+
# Execute command
|
|
106
|
+
executor.run(args, cwd=schematic_path.parent)
|
|
107
|
+
|
|
108
|
+
return output_path
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def export_svg(
|
|
112
|
+
schematic_path: Path,
|
|
113
|
+
output_dir: Optional[Path] = None,
|
|
114
|
+
theme: Optional[str] = None,
|
|
115
|
+
black_and_white: bool = False,
|
|
116
|
+
drawing_sheet: Optional[Path] = None,
|
|
117
|
+
exclude_drawing_sheet: bool = False,
|
|
118
|
+
default_font: str = "KiCad Font",
|
|
119
|
+
no_background_color: bool = False,
|
|
120
|
+
pages: Optional[List[int]] = None,
|
|
121
|
+
variables: Optional[Dict[str, str]] = None,
|
|
122
|
+
executor: Optional[KiCadExecutor] = None,
|
|
123
|
+
) -> List[Path]:
|
|
124
|
+
"""
|
|
125
|
+
Export schematic as SVG using kicad-cli.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
schematic_path: Path to .kicad_sch file
|
|
129
|
+
output_dir: Output directory (default: schematic directory)
|
|
130
|
+
theme: Color theme to use (default: schematic settings)
|
|
131
|
+
black_and_white: Export in black and white
|
|
132
|
+
drawing_sheet: Path to custom drawing sheet
|
|
133
|
+
exclude_drawing_sheet: Don't include drawing sheet
|
|
134
|
+
default_font: Default font name (default: "KiCad Font")
|
|
135
|
+
no_background_color: Don't set background color
|
|
136
|
+
pages: List of page numbers to export (None = all pages)
|
|
137
|
+
variables: Project variables to override
|
|
138
|
+
executor: Custom KiCadExecutor instance
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
List of paths to generated SVG files
|
|
142
|
+
|
|
143
|
+
Example:
|
|
144
|
+
>>> from pathlib import Path
|
|
145
|
+
>>> svgs = export_svg(
|
|
146
|
+
... Path('circuit.kicad_sch'),
|
|
147
|
+
... theme='KiCad Classic',
|
|
148
|
+
... )
|
|
149
|
+
>>> for svg in svgs:
|
|
150
|
+
... print(f"Generated: {svg}")
|
|
151
|
+
"""
|
|
152
|
+
schematic_path = Path(schematic_path)
|
|
153
|
+
|
|
154
|
+
if not schematic_path.exists():
|
|
155
|
+
raise FileNotFoundError(f"Schematic not found: {schematic_path}")
|
|
156
|
+
|
|
157
|
+
if output_dir is None:
|
|
158
|
+
output_dir = schematic_path.parent
|
|
159
|
+
else:
|
|
160
|
+
output_dir = Path(output_dir)
|
|
161
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
162
|
+
|
|
163
|
+
if executor is None:
|
|
164
|
+
executor = KiCadExecutor()
|
|
165
|
+
|
|
166
|
+
# Build command
|
|
167
|
+
args = ["sch", "export", "svg", "--output", str(output_dir)]
|
|
168
|
+
|
|
169
|
+
if theme:
|
|
170
|
+
args.extend(["--theme", theme])
|
|
171
|
+
|
|
172
|
+
if black_and_white:
|
|
173
|
+
args.append("--black-and-white")
|
|
174
|
+
|
|
175
|
+
if drawing_sheet:
|
|
176
|
+
args.extend(["--drawing-sheet", str(drawing_sheet)])
|
|
177
|
+
|
|
178
|
+
if exclude_drawing_sheet:
|
|
179
|
+
args.append("--exclude-drawing-sheet")
|
|
180
|
+
|
|
181
|
+
args.extend(["--default-font", default_font])
|
|
182
|
+
|
|
183
|
+
if no_background_color:
|
|
184
|
+
args.append("--no-background-color")
|
|
185
|
+
|
|
186
|
+
if pages:
|
|
187
|
+
args.extend(["--pages", ",".join(map(str, pages))])
|
|
188
|
+
|
|
189
|
+
if variables:
|
|
190
|
+
for key, value in variables.items():
|
|
191
|
+
args.extend(["--define-var", f"{key}={value}"])
|
|
192
|
+
|
|
193
|
+
args.append(str(schematic_path))
|
|
194
|
+
|
|
195
|
+
# Execute command
|
|
196
|
+
executor.run(args, cwd=schematic_path.parent)
|
|
197
|
+
|
|
198
|
+
# Find generated SVG files
|
|
199
|
+
svg_files = list(output_dir.glob(f"{schematic_path.stem}*.svg"))
|
|
200
|
+
|
|
201
|
+
return svg_files
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def export_dxf(
|
|
205
|
+
schematic_path: Path,
|
|
206
|
+
output_dir: Optional[Path] = None,
|
|
207
|
+
theme: Optional[str] = None,
|
|
208
|
+
black_and_white: bool = False,
|
|
209
|
+
drawing_sheet: Optional[Path] = None,
|
|
210
|
+
exclude_drawing_sheet: bool = False,
|
|
211
|
+
default_font: str = "KiCad Font",
|
|
212
|
+
no_background_color: bool = False,
|
|
213
|
+
pages: Optional[List[int]] = None,
|
|
214
|
+
variables: Optional[Dict[str, str]] = None,
|
|
215
|
+
executor: Optional[KiCadExecutor] = None,
|
|
216
|
+
) -> List[Path]:
|
|
217
|
+
"""
|
|
218
|
+
Export schematic as DXF using kicad-cli.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
schematic_path: Path to .kicad_sch file
|
|
222
|
+
output_dir: Output directory (default: schematic directory)
|
|
223
|
+
theme: Color theme to use (default: schematic settings)
|
|
224
|
+
black_and_white: Export in black and white
|
|
225
|
+
drawing_sheet: Path to custom drawing sheet
|
|
226
|
+
exclude_drawing_sheet: Don't include drawing sheet
|
|
227
|
+
default_font: Default font name (default: "KiCad Font")
|
|
228
|
+
no_background_color: Don't set background color
|
|
229
|
+
pages: List of page numbers to export (None = all pages)
|
|
230
|
+
variables: Project variables to override
|
|
231
|
+
executor: Custom KiCadExecutor instance
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
List of paths to generated DXF files
|
|
235
|
+
|
|
236
|
+
Example:
|
|
237
|
+
>>> from pathlib import Path
|
|
238
|
+
>>> dxfs = export_dxf(Path('circuit.kicad_sch'))
|
|
239
|
+
"""
|
|
240
|
+
schematic_path = Path(schematic_path)
|
|
241
|
+
|
|
242
|
+
if not schematic_path.exists():
|
|
243
|
+
raise FileNotFoundError(f"Schematic not found: {schematic_path}")
|
|
244
|
+
|
|
245
|
+
if output_dir is None:
|
|
246
|
+
output_dir = schematic_path.parent
|
|
247
|
+
else:
|
|
248
|
+
output_dir = Path(output_dir)
|
|
249
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
250
|
+
|
|
251
|
+
if executor is None:
|
|
252
|
+
executor = KiCadExecutor()
|
|
253
|
+
|
|
254
|
+
# Build command
|
|
255
|
+
args = ["sch", "export", "dxf", "--output", str(output_dir)]
|
|
256
|
+
|
|
257
|
+
if theme:
|
|
258
|
+
args.extend(["--theme", theme])
|
|
259
|
+
|
|
260
|
+
if black_and_white:
|
|
261
|
+
args.append("--black-and-white")
|
|
262
|
+
|
|
263
|
+
if drawing_sheet:
|
|
264
|
+
args.extend(["--drawing-sheet", str(drawing_sheet)])
|
|
265
|
+
|
|
266
|
+
if exclude_drawing_sheet:
|
|
267
|
+
args.append("--exclude-drawing-sheet")
|
|
268
|
+
|
|
269
|
+
args.extend(["--default-font", default_font])
|
|
270
|
+
|
|
271
|
+
if no_background_color:
|
|
272
|
+
args.append("--no-background-color")
|
|
273
|
+
|
|
274
|
+
if pages:
|
|
275
|
+
args.extend(["--pages", ",".join(map(str, pages))])
|
|
276
|
+
|
|
277
|
+
if variables:
|
|
278
|
+
for key, value in variables.items():
|
|
279
|
+
args.extend(["--define-var", f"{key}={value}"])
|
|
280
|
+
|
|
281
|
+
args.append(str(schematic_path))
|
|
282
|
+
|
|
283
|
+
# Execute command
|
|
284
|
+
executor.run(args, cwd=schematic_path.parent)
|
|
285
|
+
|
|
286
|
+
# Find generated DXF files
|
|
287
|
+
dxf_files = list(output_dir.glob(f"{schematic_path.stem}*.dxf"))
|
|
288
|
+
|
|
289
|
+
return dxf_files
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Netlist export functionality using kicad-cli."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from kicad_sch_api.cli.base import KiCadExecutor
|
|
7
|
+
from kicad_sch_api.cli.types import NetlistFormat
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def export_netlist(
|
|
11
|
+
schematic_path: Path,
|
|
12
|
+
output_path: Optional[Path] = None,
|
|
13
|
+
format: NetlistFormat = "kicadsexpr",
|
|
14
|
+
executor: Optional[KiCadExecutor] = None,
|
|
15
|
+
) -> Path:
|
|
16
|
+
"""
|
|
17
|
+
Export netlist from schematic using kicad-cli.
|
|
18
|
+
|
|
19
|
+
Supports 8 different netlist formats for PCB layout and simulation.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
schematic_path: Path to .kicad_sch file
|
|
23
|
+
output_path: Output netlist path (auto-generated if None)
|
|
24
|
+
format: Netlist format (see NetlistFormat for options)
|
|
25
|
+
executor: Custom KiCadExecutor instance (creates default if None)
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Path to generated netlist file
|
|
29
|
+
|
|
30
|
+
Raises:
|
|
31
|
+
RuntimeError: If kicad-cli not found or netlist generation fails
|
|
32
|
+
FileNotFoundError: If schematic file doesn't exist
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
>>> from pathlib import Path
|
|
36
|
+
>>> netlist = export_netlist(
|
|
37
|
+
... Path('circuit.kicad_sch'),
|
|
38
|
+
... format='spice'
|
|
39
|
+
... )
|
|
40
|
+
>>> print(f"Netlist: {netlist}")
|
|
41
|
+
|
|
42
|
+
Supported formats:
|
|
43
|
+
- kicadsexpr: KiCad S-expression netlist (default)
|
|
44
|
+
- kicadxml: KiCad XML netlist
|
|
45
|
+
- cadstar: Cadstar format
|
|
46
|
+
- orcadpcb2: OrCAD PCB2 format
|
|
47
|
+
- spice: SPICE netlist
|
|
48
|
+
- spicemodel: SPICE with models
|
|
49
|
+
- pads: PADS format
|
|
50
|
+
- allegro: Allegro format
|
|
51
|
+
"""
|
|
52
|
+
schematic_path = Path(schematic_path)
|
|
53
|
+
|
|
54
|
+
if not schematic_path.exists():
|
|
55
|
+
raise FileNotFoundError(f"Schematic not found: {schematic_path}")
|
|
56
|
+
|
|
57
|
+
# Auto-generate output path if not provided
|
|
58
|
+
if output_path is None:
|
|
59
|
+
ext = _get_extension_for_format(format)
|
|
60
|
+
output_path = schematic_path.with_suffix(ext)
|
|
61
|
+
else:
|
|
62
|
+
output_path = Path(output_path)
|
|
63
|
+
|
|
64
|
+
# Create executor if not provided
|
|
65
|
+
if executor is None:
|
|
66
|
+
executor = KiCadExecutor()
|
|
67
|
+
|
|
68
|
+
# Build command
|
|
69
|
+
args = [
|
|
70
|
+
"sch", "export", "netlist",
|
|
71
|
+
"--format", format,
|
|
72
|
+
"--output", str(output_path),
|
|
73
|
+
str(schematic_path),
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
# Execute command
|
|
77
|
+
executor.run(args, cwd=schematic_path.parent)
|
|
78
|
+
|
|
79
|
+
return output_path
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _get_extension_for_format(format: NetlistFormat) -> str:
|
|
83
|
+
"""Get file extension for netlist format."""
|
|
84
|
+
extensions = {
|
|
85
|
+
"kicadsexpr": ".net",
|
|
86
|
+
"kicadxml": ".xml",
|
|
87
|
+
"cadstar": ".frp",
|
|
88
|
+
"orcadpcb2": ".net",
|
|
89
|
+
"spice": ".cir",
|
|
90
|
+
"spicemodel": ".cir",
|
|
91
|
+
"pads": ".asc",
|
|
92
|
+
"allegro": ".alg",
|
|
93
|
+
}
|
|
94
|
+
return extensions.get(format, ".net")
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Type definitions for KiCad CLI operations."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
# Netlist export formats
|
|
6
|
+
NetlistFormat = Literal[
|
|
7
|
+
"kicadsexpr", # KiCad S-expression netlist (default)
|
|
8
|
+
"kicadxml", # KiCad XML netlist
|
|
9
|
+
"cadstar", # Cadstar format
|
|
10
|
+
"orcadpcb2", # OrCAD PCB2 format
|
|
11
|
+
"spice", # SPICE netlist
|
|
12
|
+
"spicemodel", # SPICE with models
|
|
13
|
+
"pads", # PADS format
|
|
14
|
+
"allegro", # Allegro format
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
# ERC (Electrical Rule Check) formats
|
|
18
|
+
ErcFormat = Literal[
|
|
19
|
+
"json", # JSON format (machine-readable)
|
|
20
|
+
"report", # Human-readable text report
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
# ERC severity levels
|
|
24
|
+
ErcSeverity = Literal[
|
|
25
|
+
"all", # Report all violations
|
|
26
|
+
"error", # Error level only
|
|
27
|
+
"warning", # Warning level only
|
|
28
|
+
"exclusions", # Excluded violations only
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
# Units for measurements
|
|
32
|
+
Units = Literal[
|
|
33
|
+
"mm", # Millimeters (default)
|
|
34
|
+
"in", # Inches
|
|
35
|
+
"mils", # Mils (1/1000 inch)
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
# Execution modes
|
|
39
|
+
ExecutionMode = Literal[
|
|
40
|
+
"auto", # Auto-detect (try local, fall back to Docker)
|
|
41
|
+
"local", # Force local kicad-cli
|
|
42
|
+
"docker", # Force Docker mode
|
|
43
|
+
]
|