kicad-sch-api 0.3.0__py3-none-any.whl → 0.5.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 +68 -3
- 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/kicad_to_python.py +169 -0
- kicad_sch_api/cli/netlist.py +94 -0
- kicad_sch_api/cli/types.py +43 -0
- kicad_sch_api/collections/__init__.py +36 -0
- kicad_sch_api/collections/base.py +604 -0
- kicad_sch_api/collections/components.py +1623 -0
- kicad_sch_api/collections/junctions.py +206 -0
- kicad_sch_api/collections/labels.py +508 -0
- kicad_sch_api/collections/wires.py +292 -0
- kicad_sch_api/core/__init__.py +37 -2
- 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 +34 -7
- kicad_sch_api/core/components.py +213 -52
- kicad_sch_api/core/config.py +110 -15
- kicad_sch_api/core/connectivity.py +692 -0
- kicad_sch_api/core/exceptions.py +175 -0
- kicad_sch_api/core/factories/__init__.py +5 -0
- kicad_sch_api/core/factories/element_factory.py +278 -0
- kicad_sch_api/core/formatter.py +60 -9
- kicad_sch_api/core/geometry.py +94 -5
- kicad_sch_api/core/junctions.py +26 -75
- kicad_sch_api/core/labels.py +324 -0
- kicad_sch_api/core/managers/__init__.py +30 -0
- kicad_sch_api/core/managers/base.py +76 -0
- kicad_sch_api/core/managers/file_io.py +246 -0
- kicad_sch_api/core/managers/format_sync.py +502 -0
- kicad_sch_api/core/managers/graphics.py +580 -0
- kicad_sch_api/core/managers/hierarchy.py +661 -0
- kicad_sch_api/core/managers/metadata.py +271 -0
- kicad_sch_api/core/managers/sheet.py +492 -0
- kicad_sch_api/core/managers/text_elements.py +537 -0
- kicad_sch_api/core/managers/validation.py +476 -0
- kicad_sch_api/core/managers/wire.py +410 -0
- kicad_sch_api/core/nets.py +305 -0
- kicad_sch_api/core/no_connects.py +252 -0
- kicad_sch_api/core/parser.py +194 -970
- kicad_sch_api/core/parsing_utils.py +63 -0
- kicad_sch_api/core/pin_utils.py +103 -9
- kicad_sch_api/core/schematic.py +1328 -1079
- kicad_sch_api/core/texts.py +316 -0
- kicad_sch_api/core/types.py +159 -23
- kicad_sch_api/core/wires.py +27 -75
- kicad_sch_api/exporters/__init__.py +10 -0
- kicad_sch_api/exporters/python_generator.py +610 -0
- kicad_sch_api/exporters/templates/default.py.jinja2 +65 -0
- kicad_sch_api/geometry/__init__.py +38 -0
- kicad_sch_api/geometry/font_metrics.py +22 -0
- kicad_sch_api/geometry/routing.py +211 -0
- kicad_sch_api/geometry/symbol_bbox.py +608 -0
- kicad_sch_api/interfaces/__init__.py +17 -0
- kicad_sch_api/interfaces/parser.py +76 -0
- kicad_sch_api/interfaces/repository.py +70 -0
- kicad_sch_api/interfaces/resolver.py +117 -0
- kicad_sch_api/parsers/__init__.py +14 -0
- kicad_sch_api/parsers/base.py +145 -0
- 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 +216 -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 +485 -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/registry.py +155 -0
- kicad_sch_api/parsers/utils.py +80 -0
- kicad_sch_api/symbols/__init__.py +18 -0
- kicad_sch_api/symbols/cache.py +467 -0
- kicad_sch_api/symbols/resolver.py +361 -0
- kicad_sch_api/symbols/validators.py +504 -0
- kicad_sch_api/utils/logging.py +555 -0
- kicad_sch_api/utils/logging_decorators.py +587 -0
- kicad_sch_api/utils/validation.py +16 -22
- 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/wrappers/__init__.py +14 -0
- kicad_sch_api/wrappers/base.py +89 -0
- kicad_sch_api/wrappers/wire.py +198 -0
- kicad_sch_api-0.5.1.dist-info/METADATA +540 -0
- kicad_sch_api-0.5.1.dist-info/RECORD +114 -0
- kicad_sch_api-0.5.1.dist-info/entry_points.txt +4 -0
- {kicad_sch_api-0.3.0.dist-info → kicad_sch_api-0.5.1.dist-info}/top_level.txt +1 -0
- mcp_server/__init__.py +34 -0
- mcp_server/example_logging_integration.py +506 -0
- mcp_server/models.py +252 -0
- mcp_server/server.py +357 -0
- mcp_server/tools/__init__.py +32 -0
- mcp_server/tools/component_tools.py +516 -0
- mcp_server/tools/connectivity_tools.py +532 -0
- mcp_server/tools/consolidated_tools.py +1216 -0
- mcp_server/tools/pin_discovery.py +333 -0
- mcp_server/utils/__init__.py +38 -0
- mcp_server/utils/logging.py +127 -0
- mcp_server/utils.py +36 -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-0.3.0.dist-info/METADATA +0 -483
- kicad_sch_api-0.3.0.dist-info/RECORD +0 -31
- kicad_sch_api-0.3.0.dist-info/entry_points.txt +0 -2
- {kicad_sch_api-0.3.0.dist-info → kicad_sch_api-0.5.1.dist-info}/WHEEL +0 -0
- {kicad_sch_api-0.3.0.dist-info → kicad_sch_api-0.5.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Hierarchical sheet elements parser for KiCAD schematics.
|
|
3
|
+
|
|
4
|
+
Handles parsing and serialization of Hierarchical sheet elements.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
import sexpdata
|
|
11
|
+
|
|
12
|
+
from ...core.config import config
|
|
13
|
+
from ..base import BaseElementParser
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SheetParser(BaseElementParser):
|
|
19
|
+
"""Parser for Hierarchical sheet elements."""
|
|
20
|
+
|
|
21
|
+
def __init__(self):
|
|
22
|
+
"""Initialize sheet parser."""
|
|
23
|
+
super().__init__("sheet")
|
|
24
|
+
|
|
25
|
+
def _parse_sheet(self, item: List[Any]) -> Optional[Dict[str, Any]]:
|
|
26
|
+
"""Parse a hierarchical sheet."""
|
|
27
|
+
# Complex format with position, size, properties, pins, instances
|
|
28
|
+
sheet_data = {
|
|
29
|
+
"position": {"x": 0, "y": 0},
|
|
30
|
+
"size": {"width": 0, "height": 0},
|
|
31
|
+
"exclude_from_sim": False,
|
|
32
|
+
"in_bom": True,
|
|
33
|
+
"on_board": True,
|
|
34
|
+
"dnp": False,
|
|
35
|
+
"fields_autoplaced": True,
|
|
36
|
+
"stroke_width": 0.1524,
|
|
37
|
+
"stroke_type": "solid",
|
|
38
|
+
"fill_color": (0, 0, 0, 0.0),
|
|
39
|
+
"uuid": None,
|
|
40
|
+
"name": "Sheet",
|
|
41
|
+
"filename": "sheet.kicad_sch",
|
|
42
|
+
"pins": [],
|
|
43
|
+
"project_name": "",
|
|
44
|
+
"page_number": "2",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
for elem in item[1:]:
|
|
48
|
+
if not isinstance(elem, list):
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
elem_type = str(elem[0]) if isinstance(elem[0], sexpdata.Symbol) else None
|
|
52
|
+
|
|
53
|
+
if elem_type == "at":
|
|
54
|
+
if len(elem) >= 3:
|
|
55
|
+
sheet_data["position"] = {"x": float(elem[1]), "y": float(elem[2])}
|
|
56
|
+
elif elem_type == "size":
|
|
57
|
+
if len(elem) >= 3:
|
|
58
|
+
sheet_data["size"] = {"width": float(elem[1]), "height": float(elem[2])}
|
|
59
|
+
elif elem_type == "exclude_from_sim":
|
|
60
|
+
sheet_data["exclude_from_sim"] = str(elem[1]) == "yes" if len(elem) > 1 else False
|
|
61
|
+
elif elem_type == "in_bom":
|
|
62
|
+
sheet_data["in_bom"] = str(elem[1]) == "yes" if len(elem) > 1 else True
|
|
63
|
+
elif elem_type == "on_board":
|
|
64
|
+
sheet_data["on_board"] = str(elem[1]) == "yes" if len(elem) > 1 else True
|
|
65
|
+
elif elem_type == "dnp":
|
|
66
|
+
sheet_data["dnp"] = str(elem[1]) == "yes" if len(elem) > 1 else False
|
|
67
|
+
elif elem_type == "fields_autoplaced":
|
|
68
|
+
sheet_data["fields_autoplaced"] = str(elem[1]) == "yes" if len(elem) > 1 else True
|
|
69
|
+
elif elem_type == "stroke":
|
|
70
|
+
for stroke_elem in elem[1:]:
|
|
71
|
+
if isinstance(stroke_elem, list):
|
|
72
|
+
stroke_type = str(stroke_elem[0])
|
|
73
|
+
if stroke_type == "width" and len(stroke_elem) >= 2:
|
|
74
|
+
sheet_data["stroke_width"] = float(stroke_elem[1])
|
|
75
|
+
elif stroke_type == "type" and len(stroke_elem) >= 2:
|
|
76
|
+
sheet_data["stroke_type"] = str(stroke_elem[1])
|
|
77
|
+
elif elem_type == "fill":
|
|
78
|
+
for fill_elem in elem[1:]:
|
|
79
|
+
if isinstance(fill_elem, list) and str(fill_elem[0]) == "color":
|
|
80
|
+
if len(fill_elem) >= 5:
|
|
81
|
+
sheet_data["fill_color"] = (
|
|
82
|
+
int(fill_elem[1]),
|
|
83
|
+
int(fill_elem[2]),
|
|
84
|
+
int(fill_elem[3]),
|
|
85
|
+
float(fill_elem[4]),
|
|
86
|
+
)
|
|
87
|
+
elif elem_type == "uuid":
|
|
88
|
+
sheet_data["uuid"] = str(elem[1]) if len(elem) > 1 else None
|
|
89
|
+
elif elem_type == "property":
|
|
90
|
+
if len(elem) >= 3:
|
|
91
|
+
prop_name = str(elem[1])
|
|
92
|
+
prop_value = str(elem[2])
|
|
93
|
+
if prop_name == "Sheetname":
|
|
94
|
+
sheet_data["name"] = prop_value
|
|
95
|
+
elif prop_name == "Sheetfile":
|
|
96
|
+
sheet_data["filename"] = prop_value
|
|
97
|
+
elif elem_type == "pin":
|
|
98
|
+
# Parse sheet pin - reuse existing _parse_sheet_pin helper
|
|
99
|
+
pin_data = self._parse_sheet_pin_for_read(elem)
|
|
100
|
+
if pin_data:
|
|
101
|
+
sheet_data["pins"].append(pin_data)
|
|
102
|
+
elif elem_type == "instances":
|
|
103
|
+
# Parse instances for project name and page number
|
|
104
|
+
for inst_elem in elem[1:]:
|
|
105
|
+
if isinstance(inst_elem, list) and str(inst_elem[0]) == "project":
|
|
106
|
+
if len(inst_elem) >= 2:
|
|
107
|
+
sheet_data["project_name"] = str(inst_elem[1])
|
|
108
|
+
for path_elem in inst_elem[2:]:
|
|
109
|
+
if isinstance(path_elem, list) and str(path_elem[0]) == "path":
|
|
110
|
+
for page_elem in path_elem[1:]:
|
|
111
|
+
if isinstance(page_elem, list) and str(page_elem[0]) == "page":
|
|
112
|
+
sheet_data["page_number"] = (
|
|
113
|
+
str(page_elem[1]) if len(page_elem) > 1 else "2"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return sheet_data
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _parse_sheet_pin_for_read(self, item: List[Any]) -> Optional[Dict[str, Any]]:
|
|
120
|
+
"""Parse a sheet pin (for reading during sheet parsing)."""
|
|
121
|
+
# Format: (pin "name" type (at x y rotation) (uuid ...) (effects ...))
|
|
122
|
+
if len(item) < 3:
|
|
123
|
+
return None
|
|
124
|
+
|
|
125
|
+
pin_data = {
|
|
126
|
+
"name": str(item[1]),
|
|
127
|
+
"pin_type": str(item[2]) if len(item) > 2 else "input",
|
|
128
|
+
"position": {"x": 0, "y": 0},
|
|
129
|
+
"rotation": 0,
|
|
130
|
+
"size": config.defaults.font_size,
|
|
131
|
+
"justify": "right",
|
|
132
|
+
"uuid": None,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
for elem in item[3:]:
|
|
136
|
+
if not isinstance(elem, list):
|
|
137
|
+
continue
|
|
138
|
+
|
|
139
|
+
elem_type = str(elem[0]) if isinstance(elem[0], sexpdata.Symbol) else None
|
|
140
|
+
|
|
141
|
+
if elem_type == "at":
|
|
142
|
+
if len(elem) >= 3:
|
|
143
|
+
pin_data["position"] = {"x": float(elem[1]), "y": float(elem[2])}
|
|
144
|
+
if len(elem) >= 4:
|
|
145
|
+
pin_data["rotation"] = float(elem[3])
|
|
146
|
+
elif elem_type == "uuid":
|
|
147
|
+
pin_data["uuid"] = str(elem[1]) if len(elem) > 1 else None
|
|
148
|
+
elif elem_type == "effects":
|
|
149
|
+
for effect_elem in elem[1:]:
|
|
150
|
+
if isinstance(effect_elem, list):
|
|
151
|
+
effect_type = str(effect_elem[0])
|
|
152
|
+
if effect_type == "font":
|
|
153
|
+
for font_elem in effect_elem[1:]:
|
|
154
|
+
if isinstance(font_elem, list) and str(font_elem[0]) == "size":
|
|
155
|
+
if len(font_elem) >= 2:
|
|
156
|
+
pin_data["size"] = float(font_elem[1])
|
|
157
|
+
elif effect_type == "justify":
|
|
158
|
+
if len(effect_elem) >= 2:
|
|
159
|
+
pin_data["justify"] = str(effect_elem[1])
|
|
160
|
+
|
|
161
|
+
return pin_data
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _parse_sheet_instances(self, item: List[Any]) -> List[Dict[str, Any]]:
|
|
165
|
+
"""Parse sheet_instances section."""
|
|
166
|
+
sheet_instances = []
|
|
167
|
+
for sheet_item in item[1:]: # Skip 'sheet_instances' header
|
|
168
|
+
if isinstance(sheet_item, list) and len(sheet_item) > 0:
|
|
169
|
+
sheet_data = {"path": "/", "page": "1"}
|
|
170
|
+
for element in sheet_item[1:]: # Skip element header
|
|
171
|
+
if isinstance(element, list) and len(element) >= 2:
|
|
172
|
+
key = (
|
|
173
|
+
str(element[0])
|
|
174
|
+
if isinstance(element[0], sexpdata.Symbol)
|
|
175
|
+
else str(element[0])
|
|
176
|
+
)
|
|
177
|
+
if key == "path":
|
|
178
|
+
sheet_data["path"] = element[1]
|
|
179
|
+
elif key == "page":
|
|
180
|
+
sheet_data["page"] = element[1]
|
|
181
|
+
sheet_instances.append(sheet_data)
|
|
182
|
+
return sheet_instances
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _sheet_to_sexp(self, sheet_data: Dict[str, Any], schematic_uuid: str) -> List[Any]:
|
|
186
|
+
"""Convert hierarchical sheet to S-expression."""
|
|
187
|
+
sexp = [sexpdata.Symbol("sheet")]
|
|
188
|
+
|
|
189
|
+
# Add position
|
|
190
|
+
pos = sheet_data["position"]
|
|
191
|
+
x, y = pos["x"], pos["y"]
|
|
192
|
+
if isinstance(x, float) and x.is_integer():
|
|
193
|
+
x = int(x)
|
|
194
|
+
if isinstance(y, float) and y.is_integer():
|
|
195
|
+
y = int(y)
|
|
196
|
+
sexp.append([sexpdata.Symbol("at"), x, y])
|
|
197
|
+
|
|
198
|
+
# Add size
|
|
199
|
+
size = sheet_data["size"]
|
|
200
|
+
w, h = size["width"], size["height"]
|
|
201
|
+
sexp.append([sexpdata.Symbol("size"), w, h])
|
|
202
|
+
|
|
203
|
+
# Add basic properties
|
|
204
|
+
sexp.append(
|
|
205
|
+
[
|
|
206
|
+
sexpdata.Symbol("exclude_from_sim"),
|
|
207
|
+
sexpdata.Symbol("yes" if sheet_data.get("exclude_from_sim", False) else "no"),
|
|
208
|
+
]
|
|
209
|
+
)
|
|
210
|
+
sexp.append(
|
|
211
|
+
[
|
|
212
|
+
sexpdata.Symbol("in_bom"),
|
|
213
|
+
sexpdata.Symbol("yes" if sheet_data.get("in_bom", True) else "no"),
|
|
214
|
+
]
|
|
215
|
+
)
|
|
216
|
+
sexp.append(
|
|
217
|
+
[
|
|
218
|
+
sexpdata.Symbol("on_board"),
|
|
219
|
+
sexpdata.Symbol("yes" if sheet_data.get("on_board", True) else "no"),
|
|
220
|
+
]
|
|
221
|
+
)
|
|
222
|
+
sexp.append(
|
|
223
|
+
[
|
|
224
|
+
sexpdata.Symbol("dnp"),
|
|
225
|
+
sexpdata.Symbol("yes" if sheet_data.get("dnp", False) else "no"),
|
|
226
|
+
]
|
|
227
|
+
)
|
|
228
|
+
sexp.append(
|
|
229
|
+
[
|
|
230
|
+
sexpdata.Symbol("fields_autoplaced"),
|
|
231
|
+
sexpdata.Symbol("yes" if sheet_data.get("fields_autoplaced", True) else "no"),
|
|
232
|
+
]
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Add stroke
|
|
236
|
+
stroke_width = sheet_data.get("stroke_width", 0.1524)
|
|
237
|
+
stroke_type = sheet_data.get("stroke_type", "solid")
|
|
238
|
+
stroke_sexp = [sexpdata.Symbol("stroke")]
|
|
239
|
+
stroke_sexp.append([sexpdata.Symbol("width"), stroke_width])
|
|
240
|
+
stroke_sexp.append([sexpdata.Symbol("type"), sexpdata.Symbol(stroke_type)])
|
|
241
|
+
sexp.append(stroke_sexp)
|
|
242
|
+
|
|
243
|
+
# Add fill
|
|
244
|
+
fill_color = sheet_data.get("fill_color", (0, 0, 0, 0.0))
|
|
245
|
+
fill_sexp = [sexpdata.Symbol("fill")]
|
|
246
|
+
fill_sexp.append(
|
|
247
|
+
[sexpdata.Symbol("color"), fill_color[0], fill_color[1], fill_color[2], fill_color[3]]
|
|
248
|
+
)
|
|
249
|
+
sexp.append(fill_sexp)
|
|
250
|
+
|
|
251
|
+
# Add UUID
|
|
252
|
+
if "uuid" in sheet_data:
|
|
253
|
+
sexp.append([sexpdata.Symbol("uuid"), sheet_data["uuid"]])
|
|
254
|
+
|
|
255
|
+
# Add sheet properties (name and filename)
|
|
256
|
+
name = sheet_data.get("name", "Sheet")
|
|
257
|
+
filename = sheet_data.get("filename", "sheet.kicad_sch")
|
|
258
|
+
|
|
259
|
+
# Sheetname property
|
|
260
|
+
from ...core.config import config
|
|
261
|
+
|
|
262
|
+
name_prop = [sexpdata.Symbol("property"), "Sheetname", name]
|
|
263
|
+
name_prop.append(
|
|
264
|
+
[sexpdata.Symbol("at"), x, round(y + config.sheet.name_offset_y, 4), 0]
|
|
265
|
+
) # Above sheet
|
|
266
|
+
name_prop.append(
|
|
267
|
+
[
|
|
268
|
+
sexpdata.Symbol("effects"),
|
|
269
|
+
[sexpdata.Symbol("font"), [sexpdata.Symbol("size"), config.defaults.font_size, config.defaults.font_size]],
|
|
270
|
+
[sexpdata.Symbol("justify"), sexpdata.Symbol("left"), sexpdata.Symbol("bottom")],
|
|
271
|
+
]
|
|
272
|
+
)
|
|
273
|
+
sexp.append(name_prop)
|
|
274
|
+
|
|
275
|
+
# Sheetfile property
|
|
276
|
+
file_prop = [sexpdata.Symbol("property"), "Sheetfile", filename]
|
|
277
|
+
file_prop.append(
|
|
278
|
+
[sexpdata.Symbol("at"), x, round(y + h + config.sheet.file_offset_y, 4), 0]
|
|
279
|
+
) # Below sheet
|
|
280
|
+
file_prop.append(
|
|
281
|
+
[
|
|
282
|
+
sexpdata.Symbol("effects"),
|
|
283
|
+
[sexpdata.Symbol("font"), [sexpdata.Symbol("size"), config.defaults.font_size, config.defaults.font_size]],
|
|
284
|
+
[sexpdata.Symbol("justify"), sexpdata.Symbol("left"), sexpdata.Symbol("top")],
|
|
285
|
+
]
|
|
286
|
+
)
|
|
287
|
+
sexp.append(file_prop)
|
|
288
|
+
|
|
289
|
+
# Add sheet pins if any
|
|
290
|
+
for pin in sheet_data.get("pins", []):
|
|
291
|
+
pin_sexp = self._sheet_pin_to_sexp(pin)
|
|
292
|
+
sexp.append(pin_sexp)
|
|
293
|
+
|
|
294
|
+
# Add instances
|
|
295
|
+
if schematic_uuid:
|
|
296
|
+
instances_sexp = [sexpdata.Symbol("instances")]
|
|
297
|
+
project_name = sheet_data.get("project_name", "")
|
|
298
|
+
page_number = sheet_data.get("page_number", "2")
|
|
299
|
+
project_sexp = [sexpdata.Symbol("project"), project_name]
|
|
300
|
+
path_sexp = [sexpdata.Symbol("path"), f"/{schematic_uuid}"]
|
|
301
|
+
path_sexp.append([sexpdata.Symbol("page"), page_number])
|
|
302
|
+
project_sexp.append(path_sexp)
|
|
303
|
+
instances_sexp.append(project_sexp)
|
|
304
|
+
sexp.append(instances_sexp)
|
|
305
|
+
|
|
306
|
+
return sexp
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def _sheet_pin_to_sexp(self, pin_data: Dict[str, Any]) -> List[Any]:
|
|
310
|
+
"""Convert sheet pin to S-expression."""
|
|
311
|
+
pin_sexp = [
|
|
312
|
+
sexpdata.Symbol("pin"),
|
|
313
|
+
pin_data["name"],
|
|
314
|
+
sexpdata.Symbol(pin_data.get("pin_type", "input")),
|
|
315
|
+
]
|
|
316
|
+
|
|
317
|
+
# Add position
|
|
318
|
+
pos = pin_data["position"]
|
|
319
|
+
x, y = pos["x"], pos["y"]
|
|
320
|
+
rotation = pin_data.get("rotation", 0)
|
|
321
|
+
pin_sexp.append([sexpdata.Symbol("at"), x, y, rotation])
|
|
322
|
+
|
|
323
|
+
# Add UUID
|
|
324
|
+
if "uuid" in pin_data:
|
|
325
|
+
pin_sexp.append([sexpdata.Symbol("uuid"), pin_data["uuid"]])
|
|
326
|
+
|
|
327
|
+
# Add effects
|
|
328
|
+
size = pin_data.get("size", config.defaults.font_size)
|
|
329
|
+
effects = [sexpdata.Symbol("effects")]
|
|
330
|
+
font = [sexpdata.Symbol("font"), [sexpdata.Symbol("size"), size, size]]
|
|
331
|
+
effects.append(font)
|
|
332
|
+
justify = pin_data.get("justify", "right")
|
|
333
|
+
effects.append([sexpdata.Symbol("justify"), sexpdata.Symbol(justify)])
|
|
334
|
+
pin_sexp.append(effects)
|
|
335
|
+
|
|
336
|
+
return pin_sexp
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def _sheet_instances_to_sexp(self, sheet_instances: List[Dict[str, Any]]) -> List[Any]:
|
|
340
|
+
"""Convert sheet_instances to S-expression."""
|
|
341
|
+
sexp = [sexpdata.Symbol("sheet_instances")]
|
|
342
|
+
for sheet in sheet_instances:
|
|
343
|
+
# Create: (path "/" (page "1"))
|
|
344
|
+
sheet_sexp = [
|
|
345
|
+
sexpdata.Symbol("path"),
|
|
346
|
+
sheet.get("path", "/"),
|
|
347
|
+
[sexpdata.Symbol("page"), str(sheet.get("page", "1"))],
|
|
348
|
+
]
|
|
349
|
+
sexp.append(sheet_sexp)
|
|
350
|
+
return sexp
|
|
351
|
+
|
|
352
|
+
|