Add MODE=pcb layout exam job with AI and deterministic checks.
Parallel pcb_status pipeline: parse board, classify domains/groups, inventory traces, PE-LAY/PLC/SI/DRT checks, per-IC datasheet review. Findings merge into the report UI. No auto-place or pcbnew write-back.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user