Replace findings list with a tree; filter ESD/PI false positives.

PE-ESD-001 only on J* connector–IC nets (skip NC, unconnected, VSYS/GND/3V3).
PE-PI-001 treats any capacitor on the rail, including KiCad slash prefixes, as
decoupling. Sort findings ERROR then WARNING then INFO, then RULE/RISK/REVIEW/INFO.
Report sidebar is a collapsed expand-on-click tree instead of an all-open list.
GET /report and complete_findings fail-soft and sort the same way.
This commit is contained in:
2026-09-20 10:03:50 +02:00
parent 342792df2d
commit bee6369c1d
13 changed files with 628 additions and 137 deletions
+12 -4
View File
@@ -14,6 +14,10 @@ from backend.periscopex.models import (
LayoutGraph,
)
from backend.periscopex.passive_rail_check import _is_ic_supply_pin
from backend.periscopex.pcb_net_match import (
normalize_kicad_hierarchy_net,
refs_on_matched_net,
)
from backend.periscopex.validate import _match_constraints
_LOAD_KEYS = ("i_load_a", "i_load", "iout", "i_out")
@@ -78,15 +82,18 @@ def check_power_integrity(
cons = _match_constraints(comp.mpn or comp.value, cmap)
i_load = _i_load(comp)
for pin_num, net in sorted(comp.pins.items(), key=lambda x: str(x[0])):
if not net or net in seen:
if not net:
continue
net_key = normalize_kicad_hierarchy_net(net)
if net_key in seen:
continue
if not _is_ic_supply_pin(graph, cons, pin_num, net):
continue
seen.add(net)
seen.add(net_key)
locals_: list[tuple[str, float]] = []
bulks: list[tuple[str, float]] = []
unvalued = 0
for cref in graph.components_on_net(net):
for cref in refs_on_matched_net(graph, net):
ccomp = graph.components.get(cref)
if not ccomp:
continue
@@ -99,7 +106,8 @@ def check_power_integrity(
bulks.append((cref, farads))
else:
locals_.append((cref, farads))
if not locals_ and unvalued == 0:
# Any capacitor on the rail (local, bulk, or unvalued) is decoupling.
if not locals_ and not bulks and unvalued == 0:
rec = f"Add a local decoupling capacitor on {net} at {ref}."
facts = f"{ref} supply {net}: 0 local caps (<1 µF); bulk={len(bulks)}."
if i_load is not None: