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,252 @@
|
|
|
1
|
+
"""
|
|
2
|
+
No-connect element management for KiCAD schematics.
|
|
3
|
+
|
|
4
|
+
This module provides collection classes for managing no-connect elements,
|
|
5
|
+
featuring fast lookup, bulk operations, and validation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import uuid
|
|
10
|
+
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Union
|
|
11
|
+
|
|
12
|
+
from ..utils.validation import SchematicValidator, ValidationError, ValidationIssue
|
|
13
|
+
from .collections import BaseCollection
|
|
14
|
+
from .types import NoConnect, Point
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class NoConnectElement:
|
|
20
|
+
"""
|
|
21
|
+
Enhanced wrapper for schematic no-connect elements with modern API.
|
|
22
|
+
|
|
23
|
+
Provides intuitive access to no-connect properties and operations
|
|
24
|
+
while maintaining exact format preservation.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, no_connect_data: NoConnect, parent_collection: "NoConnectCollection"):
|
|
28
|
+
"""
|
|
29
|
+
Initialize no-connect element wrapper.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
no_connect_data: Underlying no-connect data
|
|
33
|
+
parent_collection: Parent collection for updates
|
|
34
|
+
"""
|
|
35
|
+
self._data = no_connect_data
|
|
36
|
+
self._collection = parent_collection
|
|
37
|
+
self._validator = SchematicValidator()
|
|
38
|
+
|
|
39
|
+
# Core properties with validation
|
|
40
|
+
@property
|
|
41
|
+
def uuid(self) -> str:
|
|
42
|
+
"""No-connect element UUID."""
|
|
43
|
+
return self._data.uuid
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def position(self) -> Point:
|
|
47
|
+
"""No-connect position."""
|
|
48
|
+
return self._data.position
|
|
49
|
+
|
|
50
|
+
@position.setter
|
|
51
|
+
def position(self, value: Union[Point, Tuple[float, float]]):
|
|
52
|
+
"""Set no-connect position."""
|
|
53
|
+
if isinstance(value, tuple):
|
|
54
|
+
value = Point(value[0], value[1])
|
|
55
|
+
elif not isinstance(value, Point):
|
|
56
|
+
raise ValidationError(f"Position must be Point or tuple, got {type(value)}")
|
|
57
|
+
self._data.position = value
|
|
58
|
+
self._collection._mark_modified()
|
|
59
|
+
|
|
60
|
+
def validate(self) -> List[ValidationIssue]:
|
|
61
|
+
"""Validate this no-connect element."""
|
|
62
|
+
return self._validator.validate_no_connect(self._data.__dict__)
|
|
63
|
+
|
|
64
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
65
|
+
"""Convert no-connect element to dictionary representation."""
|
|
66
|
+
return {
|
|
67
|
+
"uuid": self.uuid,
|
|
68
|
+
"position": {"x": self.position.x, "y": self.position.y},
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
def __str__(self) -> str:
|
|
72
|
+
"""String representation."""
|
|
73
|
+
return f"<NoConnect @ {self.position}>"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class NoConnectCollection(BaseCollection[NoConnectElement]):
|
|
77
|
+
"""
|
|
78
|
+
Collection class for efficient no-connect element management.
|
|
79
|
+
|
|
80
|
+
Inherits from BaseCollection for standard operations and adds no-connect-specific
|
|
81
|
+
functionality including position-based indexing.
|
|
82
|
+
|
|
83
|
+
Provides fast lookup, filtering, and bulk operations for schematic no-connect elements.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def __init__(self, no_connects: List[NoConnect] = None):
|
|
87
|
+
"""
|
|
88
|
+
Initialize no-connect collection.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
no_connects: Initial list of no-connect data
|
|
92
|
+
"""
|
|
93
|
+
# Initialize base collection
|
|
94
|
+
super().__init__([], collection_name="no_connects")
|
|
95
|
+
|
|
96
|
+
# Additional no-connect-specific index
|
|
97
|
+
self._position_index: Dict[Tuple[float, float], List[NoConnectElement]] = {}
|
|
98
|
+
|
|
99
|
+
# Add initial no-connects
|
|
100
|
+
if no_connects:
|
|
101
|
+
for no_connect_data in no_connects:
|
|
102
|
+
self._add_to_indexes(NoConnectElement(no_connect_data, self))
|
|
103
|
+
|
|
104
|
+
def add(
|
|
105
|
+
self,
|
|
106
|
+
position: Union[Point, Tuple[float, float]],
|
|
107
|
+
no_connect_uuid: Optional[str] = None,
|
|
108
|
+
) -> NoConnectElement:
|
|
109
|
+
"""
|
|
110
|
+
Add a new no-connect element to the schematic.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
position: No-connect position
|
|
114
|
+
no_connect_uuid: Specific UUID for no-connect (auto-generated if None)
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Newly created NoConnectElement
|
|
118
|
+
|
|
119
|
+
Raises:
|
|
120
|
+
ValidationError: If no-connect data is invalid
|
|
121
|
+
"""
|
|
122
|
+
# Validate inputs
|
|
123
|
+
if isinstance(position, tuple):
|
|
124
|
+
position = Point(position[0], position[1])
|
|
125
|
+
elif not isinstance(position, Point):
|
|
126
|
+
raise ValidationError(f"Position must be Point or tuple, got {type(position)}")
|
|
127
|
+
|
|
128
|
+
# Generate UUID if not provided
|
|
129
|
+
if not no_connect_uuid:
|
|
130
|
+
no_connect_uuid = str(uuid.uuid4())
|
|
131
|
+
|
|
132
|
+
# Check for duplicate UUID
|
|
133
|
+
if no_connect_uuid in self._uuid_index:
|
|
134
|
+
raise ValidationError(f"NoConnect UUID {no_connect_uuid} already exists")
|
|
135
|
+
|
|
136
|
+
# Create no-connect data
|
|
137
|
+
no_connect_data = NoConnect(
|
|
138
|
+
uuid=no_connect_uuid,
|
|
139
|
+
position=position,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Create wrapper and add to collection
|
|
143
|
+
no_connect_element = NoConnectElement(no_connect_data, self)
|
|
144
|
+
self._add_to_indexes(no_connect_element)
|
|
145
|
+
self._mark_modified()
|
|
146
|
+
|
|
147
|
+
logger.debug(f"Added no-connect: {no_connect_element}")
|
|
148
|
+
return no_connect_element
|
|
149
|
+
|
|
150
|
+
# get() method inherited from BaseCollection
|
|
151
|
+
|
|
152
|
+
def remove(self, no_connect_uuid: str) -> bool:
|
|
153
|
+
"""
|
|
154
|
+
Remove no-connect by UUID.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
no_connect_uuid: UUID of no-connect to remove
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
True if no-connect was removed, False if not found
|
|
161
|
+
"""
|
|
162
|
+
no_connect_element = self.get(no_connect_uuid)
|
|
163
|
+
if not no_connect_element:
|
|
164
|
+
return False
|
|
165
|
+
|
|
166
|
+
# Remove from position index
|
|
167
|
+
pos_key = (no_connect_element.position.x, no_connect_element.position.y)
|
|
168
|
+
if pos_key in self._position_index:
|
|
169
|
+
self._position_index[pos_key].remove(no_connect_element)
|
|
170
|
+
if not self._position_index[pos_key]:
|
|
171
|
+
del self._position_index[pos_key]
|
|
172
|
+
|
|
173
|
+
# Remove using base class method
|
|
174
|
+
super().remove(no_connect_uuid)
|
|
175
|
+
|
|
176
|
+
logger.debug(f"Removed no-connect: {no_connect_element}")
|
|
177
|
+
return True
|
|
178
|
+
|
|
179
|
+
def find_at_position(
|
|
180
|
+
self, position: Union[Point, Tuple[float, float]], tolerance: float = 0.1
|
|
181
|
+
) -> List[NoConnectElement]:
|
|
182
|
+
"""
|
|
183
|
+
Find no-connects at or near a position.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
position: Position to search around
|
|
187
|
+
tolerance: Search tolerance in mm
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
List of matching no-connect elements
|
|
191
|
+
"""
|
|
192
|
+
if isinstance(position, tuple):
|
|
193
|
+
position = Point(position[0], position[1])
|
|
194
|
+
|
|
195
|
+
matches = []
|
|
196
|
+
for no_connect_element in self._items:
|
|
197
|
+
distance = no_connect_element.position.distance_to(position)
|
|
198
|
+
if distance <= tolerance:
|
|
199
|
+
matches.append(no_connect_element)
|
|
200
|
+
return matches
|
|
201
|
+
|
|
202
|
+
def filter(self, predicate: Callable[[NoConnectElement], bool]) -> List[NoConnectElement]:
|
|
203
|
+
"""
|
|
204
|
+
Filter no-connects by predicate function (delegates to base class find).
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
predicate: Function that returns True for no-connects to include
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
List of no-connects matching predicate
|
|
211
|
+
"""
|
|
212
|
+
return self.find(predicate)
|
|
213
|
+
|
|
214
|
+
def bulk_update(self, criteria: Callable[[NoConnectElement], bool], updates: Dict[str, Any]):
|
|
215
|
+
"""
|
|
216
|
+
Update multiple no-connects matching criteria.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
criteria: Function to select no-connects to update
|
|
220
|
+
updates: Dictionary of property updates
|
|
221
|
+
"""
|
|
222
|
+
updated_count = 0
|
|
223
|
+
for no_connect_element in self._items:
|
|
224
|
+
if criteria(no_connect_element):
|
|
225
|
+
for prop, value in updates.items():
|
|
226
|
+
if hasattr(no_connect_element, prop):
|
|
227
|
+
setattr(no_connect_element, prop, value)
|
|
228
|
+
updated_count += 1
|
|
229
|
+
|
|
230
|
+
if updated_count > 0:
|
|
231
|
+
self._mark_modified()
|
|
232
|
+
logger.debug(f"Bulk updated {updated_count} no-connect properties")
|
|
233
|
+
|
|
234
|
+
def clear(self):
|
|
235
|
+
"""Remove all no-connects from collection."""
|
|
236
|
+
self._position_index.clear()
|
|
237
|
+
super().clear()
|
|
238
|
+
|
|
239
|
+
def _add_to_indexes(self, no_connect_element: NoConnectElement):
|
|
240
|
+
"""Add no-connect to internal indexes (base + position index)."""
|
|
241
|
+
self._add_item(no_connect_element)
|
|
242
|
+
|
|
243
|
+
# Add to position index
|
|
244
|
+
pos_key = (no_connect_element.position.x, no_connect_element.position.y)
|
|
245
|
+
if pos_key not in self._position_index:
|
|
246
|
+
self._position_index[pos_key] = []
|
|
247
|
+
self._position_index[pos_key].append(no_connect_element)
|
|
248
|
+
|
|
249
|
+
# Collection interface methods - __len__, __iter__, __getitem__ inherited from BaseCollection
|
|
250
|
+
def __bool__(self) -> bool:
|
|
251
|
+
"""Return True if collection has no-connects."""
|
|
252
|
+
return len(self._items) > 0
|