"""Pad net on the PCB vs pin net on the schematic graph. Silent when either side has no name. Power-flag footprints (#PWR) skipped. """ from __future__ import annotations from backend.periscopex.models import DesignGraph, Finding, LayoutGraph def _ensure_recommendation(f: Finding) -> Finding: if not (f.recommendation or "").strip(): f.recommendation = ( "Align the PCB pad net with the schematic pin, then re-run PCB review." ) return f def check_pcb_net_match( graph: DesignGraph, layout: LayoutGraph | None, ) -> list[Finding]: if layout is None or not layout.footprints: return [] findings: list[Finding] = [] sch_refs = { ref for ref in graph.components if not ref.startswith("#") } pcb_refs = { ref for ref in layout.footprints if not ref.startswith("#") } for ref in sorted(sch_refs - pcb_refs): comp = graph.components[ref] findings.append(_ensure_recommendation(Finding( designator=ref, mpn=comp.mpn or "", aspect="pcb_match", finding=f"{ref} is in the schematic but has no footprint on the PCB.", why="Layout review cannot check a part that is not placed.", status="WARNING", recommendation=f"Place {ref} on the board or remove it from the schematic/BOM.", source="pcb_net_match", rule_id="PE-LAY-002", pins=[], ))) for ref, fp in sorted(layout.footprints.items()): if ref.startswith("#"): continue comp = graph.components.get(ref) if comp is None: continue for pad in fp.pads: if not pad.net or not pad.number: continue sch_net = graph.pin_net(ref, pad.number) if not sch_net: continue if sch_net == pad.net: continue findings.append(_ensure_recommendation(Finding( designator=ref, mpn=comp.mpn or "", aspect="pcb_match", finding=( f"{ref}.{pad.number} PCB net '{pad.net}' does not match " f"schematic net '{sch_net}'." ), why=( "Datasheet and SI checks follow schematic net names. " "A pad on the wrong net is a layout error, not a BOM typo." ), status="ERROR", recommendation=( f"Reconnect pad {ref}.{pad.number} to '{sch_net}' " f"(or fix the schematic if the PCB is authoritative)." ), source="pcb_net_match", rule_id="PE-LAY-001", net=pad.net, pins=[pad.number], ))) return findings