kicad-sch-api 0.3.4__py3-none-any.whl → 0.4.0__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.
Potentially problematic release.
This version of kicad-sch-api might be problematic. Click here for more details.
- kicad_sch_api/collections/__init__.py +21 -0
- kicad_sch_api/collections/base.py +294 -0
- kicad_sch_api/collections/components.py +434 -0
- kicad_sch_api/collections/junctions.py +366 -0
- kicad_sch_api/collections/labels.py +404 -0
- kicad_sch_api/collections/wires.py +406 -0
- kicad_sch_api/core/components.py +5 -0
- kicad_sch_api/core/formatter.py +3 -1
- kicad_sch_api/core/labels.py +348 -0
- kicad_sch_api/core/managers/__init__.py +26 -0
- kicad_sch_api/core/managers/file_io.py +243 -0
- kicad_sch_api/core/managers/format_sync.py +501 -0
- kicad_sch_api/core/managers/graphics.py +579 -0
- kicad_sch_api/core/managers/metadata.py +268 -0
- kicad_sch_api/core/managers/sheet.py +454 -0
- kicad_sch_api/core/managers/text_elements.py +536 -0
- kicad_sch_api/core/managers/validation.py +474 -0
- kicad_sch_api/core/managers/wire.py +346 -0
- kicad_sch_api/core/nets.py +310 -0
- kicad_sch_api/core/no_connects.py +276 -0
- kicad_sch_api/core/parser.py +75 -41
- kicad_sch_api/core/schematic.py +904 -1074
- kicad_sch_api/core/texts.py +343 -0
- kicad_sch_api/core/types.py +13 -4
- kicad_sch_api/geometry/font_metrics.py +3 -1
- kicad_sch_api/geometry/symbol_bbox.py +56 -43
- 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/label_parser.py +254 -0
- kicad_sch_api/parsers/registry.py +155 -0
- kicad_sch_api/parsers/symbol_parser.py +222 -0
- kicad_sch_api/parsers/wire_parser.py +99 -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-0.3.4.dist-info → kicad_sch_api-0.4.0.dist-info}/METADATA +1 -1
- kicad_sch_api-0.4.0.dist-info/RECORD +67 -0
- kicad_sch_api-0.3.4.dist-info/RECORD +0 -34
- {kicad_sch_api-0.3.4.dist-info → kicad_sch_api-0.4.0.dist-info}/WHEEL +0 -0
- {kicad_sch_api-0.3.4.dist-info → kicad_sch_api-0.4.0.dist-info}/entry_points.txt +0 -0
- {kicad_sch_api-0.3.4.dist-info → kicad_sch_api-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {kicad_sch_api-0.3.4.dist-info → kicad_sch_api-0.4.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Validation Manager for KiCAD schematic integrity checking.
|
|
3
|
+
|
|
4
|
+
Provides comprehensive validation for schematic integrity, design rules,
|
|
5
|
+
connectivity analysis, and format compliance while collecting and reporting
|
|
6
|
+
validation issues systematically.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Any, Dict, List, Optional, Set, Tuple
|
|
11
|
+
|
|
12
|
+
from ...utils.validation import ValidationError, ValidationIssue
|
|
13
|
+
from ..types import Point
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ValidationManager:
|
|
19
|
+
"""
|
|
20
|
+
Comprehensive validation manager for schematic integrity.
|
|
21
|
+
|
|
22
|
+
Responsible for:
|
|
23
|
+
- Schematic-wide integrity checks
|
|
24
|
+
- Component reference validation
|
|
25
|
+
- Connectivity analysis
|
|
26
|
+
- Design rule checking
|
|
27
|
+
- Format compliance validation
|
|
28
|
+
- Validation issue collection and reporting
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self, schematic_data: Dict[str, Any], component_collection=None, wire_collection=None
|
|
33
|
+
):
|
|
34
|
+
"""
|
|
35
|
+
Initialize ValidationManager.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
schematic_data: Reference to schematic data
|
|
39
|
+
component_collection: Component collection for validation
|
|
40
|
+
wire_collection: Wire collection for connectivity analysis
|
|
41
|
+
"""
|
|
42
|
+
self._data = schematic_data
|
|
43
|
+
self._components = component_collection
|
|
44
|
+
self._wires = wire_collection
|
|
45
|
+
self._validation_rules = self._initialize_validation_rules()
|
|
46
|
+
|
|
47
|
+
def validate_schematic(self) -> List[ValidationIssue]:
|
|
48
|
+
"""
|
|
49
|
+
Perform comprehensive schematic validation.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
List of all validation issues found
|
|
53
|
+
"""
|
|
54
|
+
issues = []
|
|
55
|
+
|
|
56
|
+
# Run all validation rules
|
|
57
|
+
for rule_name, rule_func in self._validation_rules.items():
|
|
58
|
+
try:
|
|
59
|
+
rule_issues = rule_func()
|
|
60
|
+
issues.extend(rule_issues)
|
|
61
|
+
logger.debug(f"Validation rule '{rule_name}' found {len(rule_issues)} issues")
|
|
62
|
+
except Exception as e:
|
|
63
|
+
issues.append(
|
|
64
|
+
ValidationIssue(
|
|
65
|
+
category="validation_system",
|
|
66
|
+
message=f"Validation rule '{rule_name}' failed: {e}",
|
|
67
|
+
level="error",
|
|
68
|
+
context={"rule": rule_name, "error": str(e)},
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
logger.error(f"Validation rule '{rule_name}' failed: {e}")
|
|
72
|
+
|
|
73
|
+
logger.info(f"Schematic validation completed with {len(issues)} issues")
|
|
74
|
+
return issues
|
|
75
|
+
|
|
76
|
+
def validate_component_references(self) -> List[ValidationIssue]:
|
|
77
|
+
"""
|
|
78
|
+
Validate component references for duplicates and format.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
List of reference validation issues
|
|
82
|
+
"""
|
|
83
|
+
issues = []
|
|
84
|
+
|
|
85
|
+
if not self._components:
|
|
86
|
+
return issues
|
|
87
|
+
|
|
88
|
+
references = []
|
|
89
|
+
reference_positions = {}
|
|
90
|
+
|
|
91
|
+
# Collect all component references
|
|
92
|
+
for component in self._components:
|
|
93
|
+
ref = component.reference
|
|
94
|
+
references.append(ref)
|
|
95
|
+
reference_positions[ref] = component.position
|
|
96
|
+
|
|
97
|
+
# Check for duplicate references
|
|
98
|
+
duplicates = set([ref for ref in references if references.count(ref) > 1])
|
|
99
|
+
for duplicate_ref in duplicates:
|
|
100
|
+
issues.append(
|
|
101
|
+
ValidationIssue(
|
|
102
|
+
category="component_references",
|
|
103
|
+
message=f"Duplicate component reference: {duplicate_ref}",
|
|
104
|
+
level="error",
|
|
105
|
+
context={"reference": duplicate_ref},
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Check reference format
|
|
110
|
+
for ref in set(references):
|
|
111
|
+
if not self._validate_reference_format(ref):
|
|
112
|
+
issues.append(
|
|
113
|
+
ValidationIssue(
|
|
114
|
+
category="component_references",
|
|
115
|
+
message=f"Invalid reference format: {ref}",
|
|
116
|
+
level="warning",
|
|
117
|
+
context={"reference": ref},
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
return issues
|
|
122
|
+
|
|
123
|
+
def validate_connectivity(self) -> List[ValidationIssue]:
|
|
124
|
+
"""
|
|
125
|
+
Validate electrical connectivity and nets.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
List of connectivity validation issues
|
|
129
|
+
"""
|
|
130
|
+
issues = []
|
|
131
|
+
|
|
132
|
+
if not self._wires or not self._components:
|
|
133
|
+
return issues
|
|
134
|
+
|
|
135
|
+
# Check for unconnected pins
|
|
136
|
+
unconnected_pins = self._find_unconnected_pins()
|
|
137
|
+
for component_ref, pin_number in unconnected_pins:
|
|
138
|
+
issues.append(
|
|
139
|
+
ValidationIssue(
|
|
140
|
+
category="connectivity",
|
|
141
|
+
message=f"Unconnected pin: {component_ref}.{pin_number}",
|
|
142
|
+
level="warning",
|
|
143
|
+
context={"component": component_ref, "pin": pin_number},
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Check for floating wires
|
|
148
|
+
floating_wires = self._find_floating_wires()
|
|
149
|
+
for wire_uuid in floating_wires:
|
|
150
|
+
issues.append(
|
|
151
|
+
ValidationIssue(
|
|
152
|
+
category="connectivity",
|
|
153
|
+
message=f"Floating wire (not connected to components): {wire_uuid}",
|
|
154
|
+
level="warning",
|
|
155
|
+
context={"wire": wire_uuid},
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Check for short circuits
|
|
160
|
+
short_circuits = self._find_potential_short_circuits()
|
|
161
|
+
for circuit_info in short_circuits:
|
|
162
|
+
issues.append(
|
|
163
|
+
ValidationIssue(
|
|
164
|
+
category="connectivity",
|
|
165
|
+
message=f"Potential short circuit: {circuit_info['description']}",
|
|
166
|
+
level="error",
|
|
167
|
+
context=circuit_info,
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
return issues
|
|
172
|
+
|
|
173
|
+
def validate_positioning(self) -> List[ValidationIssue]:
|
|
174
|
+
"""
|
|
175
|
+
Validate component and element positioning.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
List of positioning validation issues
|
|
179
|
+
"""
|
|
180
|
+
issues = []
|
|
181
|
+
|
|
182
|
+
# Check for overlapping components
|
|
183
|
+
if self._components:
|
|
184
|
+
overlapping_components = self._find_overlapping_components()
|
|
185
|
+
for comp1_ref, comp2_ref, distance in overlapping_components:
|
|
186
|
+
issues.append(
|
|
187
|
+
ValidationIssue(
|
|
188
|
+
category="positioning",
|
|
189
|
+
message=f"Components too close: {comp1_ref} and {comp2_ref} (distance: {distance:.2f})",
|
|
190
|
+
level="warning",
|
|
191
|
+
context={
|
|
192
|
+
"component1": comp1_ref,
|
|
193
|
+
"component2": comp2_ref,
|
|
194
|
+
"distance": distance,
|
|
195
|
+
},
|
|
196
|
+
)
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# Check for components outside typical bounds
|
|
200
|
+
if self._components:
|
|
201
|
+
out_of_bounds = self._find_components_out_of_bounds()
|
|
202
|
+
for component_ref, position in out_of_bounds:
|
|
203
|
+
issues.append(
|
|
204
|
+
ValidationIssue(
|
|
205
|
+
category="positioning",
|
|
206
|
+
message=f"Component outside typical bounds: {component_ref} at {position}",
|
|
207
|
+
level="info",
|
|
208
|
+
context={"component": component_ref, "position": (position.x, position.y)},
|
|
209
|
+
)
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
return issues
|
|
213
|
+
|
|
214
|
+
def validate_design_rules(self) -> List[ValidationIssue]:
|
|
215
|
+
"""
|
|
216
|
+
Validate against design rules and best practices.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
List of design rule validation issues
|
|
220
|
+
"""
|
|
221
|
+
issues = []
|
|
222
|
+
|
|
223
|
+
# Check minimum wire spacing
|
|
224
|
+
wire_spacing_issues = self._check_wire_spacing()
|
|
225
|
+
issues.extend(wire_spacing_issues)
|
|
226
|
+
|
|
227
|
+
# Check power and ground connections
|
|
228
|
+
power_issues = self._check_power_connections()
|
|
229
|
+
issues.extend(power_issues)
|
|
230
|
+
|
|
231
|
+
# Check for missing bypass capacitors (simplified check)
|
|
232
|
+
bypass_issues = self._check_bypass_capacitors()
|
|
233
|
+
issues.extend(bypass_issues)
|
|
234
|
+
|
|
235
|
+
return issues
|
|
236
|
+
|
|
237
|
+
def validate_metadata(self) -> List[ValidationIssue]:
|
|
238
|
+
"""
|
|
239
|
+
Validate schematic metadata and structure.
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
List of metadata validation issues
|
|
243
|
+
"""
|
|
244
|
+
issues = []
|
|
245
|
+
|
|
246
|
+
# Check required metadata fields
|
|
247
|
+
if not self._data.get("version"):
|
|
248
|
+
issues.append(
|
|
249
|
+
ValidationIssue(
|
|
250
|
+
category="metadata",
|
|
251
|
+
message="Missing KiCAD version information",
|
|
252
|
+
level="warning",
|
|
253
|
+
context={},
|
|
254
|
+
)
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
if not self._data.get("generator"):
|
|
258
|
+
issues.append(
|
|
259
|
+
ValidationIssue(
|
|
260
|
+
category="metadata",
|
|
261
|
+
message="Missing generator information",
|
|
262
|
+
level="info",
|
|
263
|
+
context={},
|
|
264
|
+
)
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
# Check title block
|
|
268
|
+
title_block = self._data.get("title_block", {})
|
|
269
|
+
if not title_block.get("title"):
|
|
270
|
+
issues.append(
|
|
271
|
+
ValidationIssue(
|
|
272
|
+
category="metadata", message="Missing schematic title", level="info", context={}
|
|
273
|
+
)
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
# Check paper size
|
|
277
|
+
paper = self._data.get("paper")
|
|
278
|
+
valid_papers = ["A4", "A3", "A2", "A1", "A0", "Letter", "Legal", "Tabloid"]
|
|
279
|
+
if paper and paper not in valid_papers:
|
|
280
|
+
issues.append(
|
|
281
|
+
ValidationIssue(
|
|
282
|
+
category="metadata",
|
|
283
|
+
message=f"Non-standard paper size: {paper}",
|
|
284
|
+
level="info",
|
|
285
|
+
context={"paper": paper},
|
|
286
|
+
)
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
return issues
|
|
290
|
+
|
|
291
|
+
def get_validation_summary(self, issues: List[ValidationIssue]) -> Dict[str, Any]:
|
|
292
|
+
"""
|
|
293
|
+
Generate validation summary statistics.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
issues: List of validation issues
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
Summary dictionary with counts and severity
|
|
300
|
+
"""
|
|
301
|
+
summary = {
|
|
302
|
+
"total_issues": len(issues),
|
|
303
|
+
"error_count": len([i for i in issues if i.level == "error"]),
|
|
304
|
+
"warning_count": len([i for i in issues if i.level == "warning"]),
|
|
305
|
+
"info_count": len([i for i in issues if i.level == "info"]),
|
|
306
|
+
"categories": {},
|
|
307
|
+
"severity": "info",
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
# Count by category
|
|
311
|
+
for issue in issues:
|
|
312
|
+
category = issue.category
|
|
313
|
+
if category not in summary["categories"]:
|
|
314
|
+
summary["categories"][category] = 0
|
|
315
|
+
summary["categories"][category] += 1
|
|
316
|
+
|
|
317
|
+
# Determine overall severity
|
|
318
|
+
if summary["error_count"] > 0:
|
|
319
|
+
summary["severity"] = "error"
|
|
320
|
+
elif summary["warning_count"] > 0:
|
|
321
|
+
summary["severity"] = "warning"
|
|
322
|
+
|
|
323
|
+
return summary
|
|
324
|
+
|
|
325
|
+
def _initialize_validation_rules(self) -> Dict[str, callable]:
|
|
326
|
+
"""Initialize all validation rules."""
|
|
327
|
+
return {
|
|
328
|
+
"component_references": self.validate_component_references,
|
|
329
|
+
"connectivity": self.validate_connectivity,
|
|
330
|
+
"positioning": self.validate_positioning,
|
|
331
|
+
"design_rules": self.validate_design_rules,
|
|
332
|
+
"metadata": self.validate_metadata,
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
def _validate_reference_format(self, reference: str) -> bool:
|
|
336
|
+
"""Validate component reference format."""
|
|
337
|
+
if not reference:
|
|
338
|
+
return False
|
|
339
|
+
|
|
340
|
+
# Must start with letter(s), followed by numbers
|
|
341
|
+
if not reference[0].isalpha():
|
|
342
|
+
return False
|
|
343
|
+
|
|
344
|
+
# Find where numbers start
|
|
345
|
+
alpha_end = 0
|
|
346
|
+
for i, char in enumerate(reference):
|
|
347
|
+
if char.isdigit():
|
|
348
|
+
alpha_end = i
|
|
349
|
+
break
|
|
350
|
+
else:
|
|
351
|
+
return False # No numbers found
|
|
352
|
+
|
|
353
|
+
# Check alpha part
|
|
354
|
+
alpha_part = reference[:alpha_end]
|
|
355
|
+
if not alpha_part.isalpha():
|
|
356
|
+
return False
|
|
357
|
+
|
|
358
|
+
# Check numeric part
|
|
359
|
+
numeric_part = reference[alpha_end:]
|
|
360
|
+
if not numeric_part.isdigit():
|
|
361
|
+
return False
|
|
362
|
+
|
|
363
|
+
return True
|
|
364
|
+
|
|
365
|
+
def _find_unconnected_pins(self) -> List[Tuple[str, str]]:
|
|
366
|
+
"""Find component pins that are not connected to any wires."""
|
|
367
|
+
unconnected = []
|
|
368
|
+
|
|
369
|
+
if not self._components or not self._wires:
|
|
370
|
+
return unconnected
|
|
371
|
+
|
|
372
|
+
# Get all wire endpoints
|
|
373
|
+
wire_points = set()
|
|
374
|
+
for wire in self._wires:
|
|
375
|
+
wire_points.add((wire.start.x, wire.start.y))
|
|
376
|
+
wire_points.add((wire.end.x, wire.end.y))
|
|
377
|
+
|
|
378
|
+
# Check each component pin
|
|
379
|
+
for component in self._components:
|
|
380
|
+
# This would need actual pin position calculation
|
|
381
|
+
# Simplified for now - would use component's pin positions
|
|
382
|
+
pass
|
|
383
|
+
|
|
384
|
+
return unconnected
|
|
385
|
+
|
|
386
|
+
def _find_floating_wires(self) -> List[str]:
|
|
387
|
+
"""Find wires that don't connect to any components."""
|
|
388
|
+
floating = []
|
|
389
|
+
|
|
390
|
+
if not self._wires or not self._components:
|
|
391
|
+
return floating
|
|
392
|
+
|
|
393
|
+
# This would need actual connectivity analysis
|
|
394
|
+
# Simplified implementation
|
|
395
|
+
return floating
|
|
396
|
+
|
|
397
|
+
def _find_potential_short_circuits(self) -> List[Dict[str, Any]]:
|
|
398
|
+
"""Find potential short circuits in the design."""
|
|
399
|
+
short_circuits = []
|
|
400
|
+
|
|
401
|
+
# This would need sophisticated electrical analysis
|
|
402
|
+
# Simplified implementation
|
|
403
|
+
return short_circuits
|
|
404
|
+
|
|
405
|
+
def _find_overlapping_components(self) -> List[Tuple[str, str, float]]:
|
|
406
|
+
"""Find components that are positioned too close together."""
|
|
407
|
+
overlapping = []
|
|
408
|
+
|
|
409
|
+
if not self._components:
|
|
410
|
+
return overlapping
|
|
411
|
+
|
|
412
|
+
components = list(self._components)
|
|
413
|
+
min_distance = 10.0 # Minimum distance threshold
|
|
414
|
+
|
|
415
|
+
for i, comp1 in enumerate(components):
|
|
416
|
+
for comp2 in components[i + 1 :]:
|
|
417
|
+
distance = comp1.position.distance_to(comp2.position)
|
|
418
|
+
if distance < min_distance:
|
|
419
|
+
overlapping.append((comp1.reference, comp2.reference, distance))
|
|
420
|
+
|
|
421
|
+
return overlapping
|
|
422
|
+
|
|
423
|
+
def _find_components_out_of_bounds(self) -> List[Tuple[str, Point]]:
|
|
424
|
+
"""Find components positioned outside typical schematic bounds."""
|
|
425
|
+
out_of_bounds = []
|
|
426
|
+
|
|
427
|
+
if not self._components:
|
|
428
|
+
return out_of_bounds
|
|
429
|
+
|
|
430
|
+
# Define typical bounds (these could be configurable)
|
|
431
|
+
min_x, min_y = 0, 0
|
|
432
|
+
max_x, max_y = 1000, 1000 # Adjust based on paper size
|
|
433
|
+
|
|
434
|
+
for component in self._components:
|
|
435
|
+
pos = component.position
|
|
436
|
+
if pos.x < min_x or pos.x > max_x or pos.y < min_y or pos.y > max_y:
|
|
437
|
+
out_of_bounds.append((component.reference, pos))
|
|
438
|
+
|
|
439
|
+
return out_of_bounds
|
|
440
|
+
|
|
441
|
+
def _check_wire_spacing(self) -> List[ValidationIssue]:
|
|
442
|
+
"""Check minimum wire spacing requirements."""
|
|
443
|
+
issues = []
|
|
444
|
+
|
|
445
|
+
if not self._wires:
|
|
446
|
+
return issues
|
|
447
|
+
|
|
448
|
+
# This would check wire-to-wire spacing
|
|
449
|
+
# Simplified implementation
|
|
450
|
+
return issues
|
|
451
|
+
|
|
452
|
+
def _check_power_connections(self) -> List[ValidationIssue]:
|
|
453
|
+
"""Check power and ground connection integrity."""
|
|
454
|
+
issues = []
|
|
455
|
+
|
|
456
|
+
if not self._components:
|
|
457
|
+
return issues
|
|
458
|
+
|
|
459
|
+
# Look for power components without proper connections
|
|
460
|
+
# This would need symbol analysis to identify power pins
|
|
461
|
+
# Simplified implementation
|
|
462
|
+
return issues
|
|
463
|
+
|
|
464
|
+
def _check_bypass_capacitors(self) -> List[ValidationIssue]:
|
|
465
|
+
"""Check for missing bypass capacitors near ICs."""
|
|
466
|
+
issues = []
|
|
467
|
+
|
|
468
|
+
if not self._components:
|
|
469
|
+
return issues
|
|
470
|
+
|
|
471
|
+
# Find ICs and check for nearby capacitors
|
|
472
|
+
# This would need symbol analysis and proximity checking
|
|
473
|
+
# Simplified implementation
|
|
474
|
+
return issues
|