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,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility functions for parsing S-expression data.
|
|
3
|
+
|
|
4
|
+
This module contains helper functions used by various parsers
|
|
5
|
+
to handle common parsing patterns safely and consistently.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import sexpdata
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def parse_bool_property(value: Any, default: bool = True) -> bool:
|
|
17
|
+
"""
|
|
18
|
+
Parse a boolean property from S-expression data.
|
|
19
|
+
|
|
20
|
+
Handles both sexpdata.Symbol and string types, converting yes/no to bool.
|
|
21
|
+
This is the canonical way to parse boolean properties from KiCad files.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
value: Value from S-expression (Symbol, str, bool, or None)
|
|
25
|
+
default: Default value if parsing fails or value is None
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
bool: Parsed boolean value
|
|
29
|
+
|
|
30
|
+
Examples:
|
|
31
|
+
>>> parse_bool_property(sexpdata.Symbol('yes'))
|
|
32
|
+
True
|
|
33
|
+
>>> parse_bool_property('no')
|
|
34
|
+
False
|
|
35
|
+
>>> parse_bool_property(None, default=False)
|
|
36
|
+
False
|
|
37
|
+
>>> parse_bool_property('YES') # Case insensitive
|
|
38
|
+
True
|
|
39
|
+
|
|
40
|
+
Note:
|
|
41
|
+
This function was added to fix a critical bug where Symbol('yes') == 'yes'
|
|
42
|
+
returned False, causing properties like in_bom and on_board to be parsed
|
|
43
|
+
incorrectly.
|
|
44
|
+
"""
|
|
45
|
+
# If value is None, use default
|
|
46
|
+
if value is None:
|
|
47
|
+
return default
|
|
48
|
+
|
|
49
|
+
# Convert Symbol to string
|
|
50
|
+
if isinstance(value, sexpdata.Symbol):
|
|
51
|
+
value = str(value)
|
|
52
|
+
|
|
53
|
+
# Handle string values (case-insensitive)
|
|
54
|
+
if isinstance(value, str):
|
|
55
|
+
return value.lower() == "yes"
|
|
56
|
+
|
|
57
|
+
# Handle boolean values directly
|
|
58
|
+
if isinstance(value, bool):
|
|
59
|
+
return value
|
|
60
|
+
|
|
61
|
+
# Unexpected type - use default
|
|
62
|
+
logger.warning(f"Unexpected type for boolean property: {type(value)}, using default={default}")
|
|
63
|
+
return default
|
kicad_sch_api/core/pin_utils.py
CHANGED
|
@@ -66,14 +66,14 @@ def get_component_pin_position(component: SchematicSymbol, pin_number: str) -> O
|
|
|
66
66
|
|
|
67
67
|
# Look for pin in symbol definition
|
|
68
68
|
pins_found = []
|
|
69
|
-
for pin_def in symbol_def.
|
|
70
|
-
pins_found.append(pin_def.
|
|
71
|
-
if pin_def.
|
|
69
|
+
for pin_def in symbol_def.pins:
|
|
70
|
+
pins_found.append(pin_def.number)
|
|
71
|
+
if pin_def.number == pin_number:
|
|
72
72
|
logger.info(f" Found pin {pin_number} in symbol definition")
|
|
73
73
|
|
|
74
74
|
# Get pin position from definition
|
|
75
|
-
pin_x = pin_def.
|
|
76
|
-
pin_y = pin_def.
|
|
75
|
+
pin_x = pin_def.position.x
|
|
76
|
+
pin_y = pin_def.position.y
|
|
77
77
|
logger.info(f" Symbol pin position: ({pin_x}, {pin_y})")
|
|
78
78
|
|
|
79
79
|
# Apply component transformations
|
|
@@ -96,6 +96,100 @@ def get_component_pin_position(component: SchematicSymbol, pin_number: str) -> O
|
|
|
96
96
|
return None
|
|
97
97
|
|
|
98
98
|
|
|
99
|
+
def get_component_pin_info(
|
|
100
|
+
component: SchematicSymbol, pin_number: str
|
|
101
|
+
) -> Optional[Tuple[Point, float]]:
|
|
102
|
+
"""
|
|
103
|
+
Get the absolute position and rotation of a component pin.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
component: Component containing the pin
|
|
107
|
+
pin_number: Pin number to find
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Tuple of (absolute_position, absolute_rotation_degrees), or None if not found
|
|
111
|
+
"""
|
|
112
|
+
logger.info(f"Getting pin info for {component.reference} pin {pin_number}")
|
|
113
|
+
logger.info(f" Component position: ({component.position.x}, {component.position.y})")
|
|
114
|
+
component_rotation = getattr(component, "rotation", 0)
|
|
115
|
+
logger.info(f" Component rotation: {component_rotation}°")
|
|
116
|
+
logger.info(f" Component mirror: {getattr(component, 'mirror', None)}")
|
|
117
|
+
|
|
118
|
+
# First check if pin is already in component data
|
|
119
|
+
for pin in component.pins:
|
|
120
|
+
if pin.number == pin_number:
|
|
121
|
+
logger.info(f" Found pin {pin_number} in component data")
|
|
122
|
+
logger.info(f" Pin relative position: ({pin.position.x}, {pin.position.y})")
|
|
123
|
+
logger.info(f" Pin rotation: {pin.rotation}°")
|
|
124
|
+
|
|
125
|
+
# Apply component transformations to position
|
|
126
|
+
absolute_pos = apply_transformation(
|
|
127
|
+
(pin.position.x, pin.position.y),
|
|
128
|
+
component.position,
|
|
129
|
+
component_rotation,
|
|
130
|
+
getattr(component, "mirror", None),
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Calculate absolute rotation (pin rotation + component rotation)
|
|
134
|
+
absolute_rotation = (pin.rotation + component_rotation) % 360
|
|
135
|
+
|
|
136
|
+
result_pos = Point(absolute_pos[0], absolute_pos[1])
|
|
137
|
+
logger.info(
|
|
138
|
+
f" Final absolute position: ({result_pos.x}, {result_pos.y}), rotation: {absolute_rotation}°"
|
|
139
|
+
)
|
|
140
|
+
return (result_pos, absolute_rotation)
|
|
141
|
+
|
|
142
|
+
# If not in component data, try to get from symbol library
|
|
143
|
+
logger.info(f" Pin {pin_number} not in component data, checking symbol library")
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
symbol_cache = get_symbol_cache()
|
|
147
|
+
symbol_def = symbol_cache.get_symbol(component.lib_id)
|
|
148
|
+
|
|
149
|
+
if not symbol_def:
|
|
150
|
+
logger.warning(f" Symbol definition not found for {component.lib_id}")
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
logger.info(f" Found symbol definition for {component.lib_id}")
|
|
154
|
+
|
|
155
|
+
# Look for pin in symbol definition
|
|
156
|
+
pins_found = []
|
|
157
|
+
for pin_def in symbol_def.pins:
|
|
158
|
+
pins_found.append(pin_def.number)
|
|
159
|
+
if pin_def.number == pin_number:
|
|
160
|
+
logger.info(f" Found pin {pin_number} in symbol definition")
|
|
161
|
+
|
|
162
|
+
# Get pin position and rotation from definition
|
|
163
|
+
pin_x = pin_def.position.x
|
|
164
|
+
pin_y = pin_def.position.y
|
|
165
|
+
pin_rotation = pin_def.rotation
|
|
166
|
+
logger.info(f" Symbol pin position: ({pin_x}, {pin_y}), rotation: {pin_rotation}°")
|
|
167
|
+
|
|
168
|
+
# Apply component transformations to position
|
|
169
|
+
absolute_pos = apply_transformation(
|
|
170
|
+
(pin_x, pin_y),
|
|
171
|
+
component.position,
|
|
172
|
+
component_rotation,
|
|
173
|
+
getattr(component, "mirror", None),
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# Calculate absolute rotation
|
|
177
|
+
absolute_rotation = (pin_rotation + component_rotation) % 360
|
|
178
|
+
|
|
179
|
+
result_pos = Point(absolute_pos[0], absolute_pos[1])
|
|
180
|
+
logger.info(
|
|
181
|
+
f" Final absolute position: ({result_pos.x}, {result_pos.y}), rotation: {absolute_rotation}°"
|
|
182
|
+
)
|
|
183
|
+
return (result_pos, absolute_rotation)
|
|
184
|
+
|
|
185
|
+
logger.warning(f" Pin {pin_number} not found in symbol. Available pins: {pins_found}")
|
|
186
|
+
|
|
187
|
+
except Exception as e:
|
|
188
|
+
logger.error(f" Error accessing symbol cache: {e}")
|
|
189
|
+
|
|
190
|
+
return None
|
|
191
|
+
|
|
192
|
+
|
|
99
193
|
def list_component_pins(component: SchematicSymbol) -> List[Tuple[str, Point]]:
|
|
100
194
|
"""
|
|
101
195
|
List all pins for a component with their absolute positions.
|
|
@@ -127,10 +221,10 @@ def list_component_pins(component: SchematicSymbol) -> List[Tuple[str, Point]]:
|
|
|
127
221
|
symbol_def = symbol_cache.get_symbol(component.lib_id)
|
|
128
222
|
|
|
129
223
|
if symbol_def:
|
|
130
|
-
for pin_def in symbol_def.
|
|
131
|
-
pin_number = pin_def.
|
|
132
|
-
pin_x = pin_def.
|
|
133
|
-
pin_y = pin_def.
|
|
224
|
+
for pin_def in symbol_def.pins:
|
|
225
|
+
pin_number = pin_def.number
|
|
226
|
+
pin_x = pin_def.position.x
|
|
227
|
+
pin_y = pin_def.position.y
|
|
134
228
|
|
|
135
229
|
absolute_pos = apply_transformation(
|
|
136
230
|
(pin_x, pin_y),
|