Treat KiCad /net vs schematic net as one hierarchy finding.

Pad-net compare strips leading slash and nested sheet path so /ESP32_EN
matches ESP32_EN. If every mismatch is only that prefix, emit PE-LAY-003
once instead of per-pad reconnect errors.
This commit is contained in:
2026-09-20 08:10:34 +02:00
parent 89a484830e
commit f3f96ea4d1
4 changed files with 208 additions and 26 deletions
+86
View File
@@ -62,6 +62,92 @@ def test_pad_net_mismatch_is_pe_lay_001():
assert findings[0].rule_id == "PE-LAY-001"
assert findings[0].status == "ERROR"
assert findings[0].recommendation
assert "Reconnect pad" in findings[0].recommendation
def test_kicad_hierarchy_slash_is_one_sync_finding_not_per_pad():
"""PCB /X vs schematic X (Emmaforo table) — one PE-LAY-003, pads treated as same net."""
from backend.periscopex.pcb_net_match import kicad_nets_match
pairs = [
("/ESP32_EN", "ESP32_EN"),
("/ESP_32_BOOT", "ESP_32_BOOT"),
("/REGN", "REGN"),
("/LED_SDATA", "LED_SDATA"),
("/power/REGN", "REGN"),
]
for pcb, sch in pairs:
assert kicad_nets_match(pcb, sch), (pcb, sch)
assert not kicad_nets_match("/sheet1/CLK", "/sheet2/CLK")
assert not kicad_nets_match("/ESP32_EN", "ESP32_BOOT")
pins = {
"1": "ESP32_EN",
"2": "ESP_32_BOOT",
"3": "REGN",
"4": "LED_SDATA",
}
graph = DesignGraph(
components={
"U1": Component(
reference="U1", value="", footprint="",
component_type=ComponentType.IC, mpn="ESP",
pins=pins,
),
},
nets={n: Net(name=n, net_type=NetType.SIGNAL, pins=[]) for n in pins.values()},
)
layout = LayoutGraph(
footprints={
"U1": LayoutFootprint(
reference="U1", x=0, y=0, layer="F.Cu",
pads=[
LayoutPad(number="1", x=0, y=0, net="/ESP32_EN"),
LayoutPad(number="2", x=1, y=0, net="/ESP_32_BOOT"),
LayoutPad(number="3", x=2, y=0, net="/REGN"),
LayoutPad(number="4", x=3, y=0, net="/LED_SDATA"),
],
),
},
)
findings = check_pcb_net_match(graph, layout)
assert [f.rule_id for f in findings] == ["PE-LAY-003"]
assert findings[0].status == "INFO"
assert "individual pads" in findings[0].recommendation.lower() or "Do not retouch" in findings[0].recommendation
assert "Won't fix" not in findings[0].recommendation
assert "Reconnect pad" not in findings[0].recommendation
def test_hierarchy_plus_real_mismatch_keeps_only_real_pe_lay_001():
graph = DesignGraph(
components={
"U1": Component(
reference="U1", value="", footprint="",
component_type=ComponentType.IC, mpn="X",
pins={"1": "ESP32_EN", "2": "GND"},
),
},
nets={
"ESP32_EN": Net(name="ESP32_EN", net_type=NetType.SIGNAL, pins=[]),
"GND": Net(name="GND", net_type=NetType.GROUND, pins=[]),
"+3V3": Net(name="+3V3", net_type=NetType.POWER, pins=[]),
},
)
layout = LayoutGraph(
footprints={
"U1": LayoutFootprint(
reference="U1", x=0, y=0, layer="F.Cu",
pads=[
LayoutPad(number="1", x=0, y=0, net="/ESP32_EN"),
LayoutPad(number="2", x=1, y=0, net="+3V3"),
],
),
},
)
findings = check_pcb_net_match(graph, layout)
assert [f.rule_id for f in findings] == ["PE-LAY-001"]
assert findings[0].pins == ["2"]
assert "+3V3" in findings[0].finding
def test_missing_footprint_is_pe_lay_002():