Gate PCB SI not_reviewed to board ICs, not the shared library.
Emmaforo listed 20 stale 1.5–1.7 MPNs (LAN8720A, W25Q, …) as not reviewed while the four BOM ICs were already extracted and reviewed.
This commit is contained in:
@@ -23,7 +23,9 @@ from backend.periscopex.finding_engine import apply_decisions
|
|||||||
from backend.periscopex.functional_groups import build_placement_plan
|
from backend.periscopex.functional_groups import build_placement_plan
|
||||||
from backend.periscopex.graph import build_graph
|
from backend.periscopex.graph import build_graph
|
||||||
from backend.periscopex.layout_rules import needs_layout_rules_refresh
|
from backend.periscopex.layout_rules import needs_layout_rules_refresh
|
||||||
from backend.periscopex.models import ComponentConstraints, DesignGraph, LayoutGraph, ValidationReport
|
from backend.periscopex.models import (
|
||||||
|
ComponentConstraints, ComponentType, DesignGraph, LayoutGraph, ValidationReport,
|
||||||
|
)
|
||||||
from backend.periscopex.pcb_checks import assign_pcb_finding_ids, run_pcb_checks
|
from backend.periscopex.pcb_checks import assign_pcb_finding_ids, run_pcb_checks
|
||||||
from backend.periscopex.pcb_inventory import build_pcb_inventory
|
from backend.periscopex.pcb_inventory import build_pcb_inventory
|
||||||
from backend.services import projects as proj_svc
|
from backend.services import projects as proj_svc
|
||||||
@@ -104,6 +106,50 @@ def _load_constraints_map(
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def si_extract_needed_skips(
|
||||||
|
graph: DesignGraph,
|
||||||
|
cmap: dict,
|
||||||
|
scan_ver: str,
|
||||||
|
) -> list[dict]:
|
||||||
|
"""Stale SI extracts for *board* ICs only — not the shared library dump.
|
||||||
|
|
||||||
|
``cmap`` includes every ``library/extracted`` JSON. Warning those MPNs
|
||||||
|
as ``not_reviewed`` on an unrelated project (Emmaforo vs LAN8720A) is
|
||||||
|
a false banner.
|
||||||
|
"""
|
||||||
|
board_mpns: set[str] = set()
|
||||||
|
refs_by_mpn: dict[str, list[str]] = {}
|
||||||
|
for ref, comp in graph.components.items():
|
||||||
|
if comp.component_type != ComponentType.IC:
|
||||||
|
continue
|
||||||
|
mpn = (comp.mpn or comp.value or "").strip()
|
||||||
|
if not mpn:
|
||||||
|
continue
|
||||||
|
board_mpns.add(mpn)
|
||||||
|
refs_by_mpn.setdefault(mpn, []).append(ref)
|
||||||
|
out: list[dict] = []
|
||||||
|
for mpn, cons in sorted(cmap.items()):
|
||||||
|
if mpn not in board_mpns:
|
||||||
|
continue
|
||||||
|
payload = {
|
||||||
|
"model_version": cons.model_version,
|
||||||
|
"layout_rules": cons.layout_rules,
|
||||||
|
}
|
||||||
|
if not needs_layout_rules_refresh(payload, min_scan_version=scan_ver):
|
||||||
|
continue
|
||||||
|
refs = ",".join(sorted(refs_by_mpn.get(mpn, []))) or mpn
|
||||||
|
out.append({
|
||||||
|
"designator": refs,
|
||||||
|
"reason": (
|
||||||
|
f"SI layout_rules empty at model_version="
|
||||||
|
f"{cons.model_version}; re-run schematic review "
|
||||||
|
f"to re-extract pintable {scan_ver} "
|
||||||
|
"(PCB does not re-read the PDF)."
|
||||||
|
),
|
||||||
|
})
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
def _publish(project_id: str, event: str, data: dict) -> None:
|
def _publish(project_id: str, event: str, data: dict) -> None:
|
||||||
broker.publish(project_id, event, data)
|
broker.publish(project_id, event, data)
|
||||||
|
|
||||||
@@ -222,26 +268,8 @@ async def run_pcb_pipeline(
|
|||||||
|
|
||||||
_step(project_id, "checks", "running")
|
_step(project_id, "checks", "running")
|
||||||
findings = run_pcb_checks(graph, cmap, layout, plan, zrep)
|
findings = run_pcb_checks(graph, cmap, layout, plan, zrep)
|
||||||
si_skip: list[dict] = []
|
|
||||||
scan_ver = app_settings.get_default_model_version()
|
scan_ver = app_settings.get_default_model_version()
|
||||||
for mpn, cons in sorted(cmap.items()):
|
si_skip = si_extract_needed_skips(graph, cmap, scan_ver)
|
||||||
payload = {
|
|
||||||
"model_version": cons.model_version,
|
|
||||||
"layout_rules": cons.layout_rules,
|
|
||||||
}
|
|
||||||
if needs_layout_rules_refresh(
|
|
||||||
payload, min_scan_version=scan_ver,
|
|
||||||
):
|
|
||||||
si_skip.append({
|
|
||||||
"designator": mpn,
|
|
||||||
"reason": (
|
|
||||||
f"SI layout_rules empty at model_version="
|
|
||||||
f"{cons.model_version}; re-run schematic review "
|
|
||||||
f"to re-extract pintable {scan_ver} "
|
|
||||||
"(PCB does not re-read the PDF)."
|
|
||||||
),
|
|
||||||
})
|
|
||||||
if si_skip:
|
|
||||||
sipath = ws.local_path("si_extract_needed.json")
|
sipath = ws.local_path("si_extract_needed.json")
|
||||||
sipath.write_text(json.dumps(si_skip, indent=2) + "\n")
|
sipath.write_text(json.dumps(si_skip, indent=2) + "\n")
|
||||||
ws._upload_file("si_extract_needed.json")
|
ws._upload_file("si_extract_needed.json")
|
||||||
|
|||||||
@@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
What's new in Periscope.
|
What's new in Periscope.
|
||||||
|
|
||||||
|
## 2.37.1 — 2026-09-20 — PCB not_reviewed is board ICs, not the shared library
|
||||||
|
|
||||||
|
Emmaforo showed 20 “components not reviewed” that were other projects’ 1.5–1.7 extracts (LAN8720A, W25Q, …). BOM ICs were already 1.13.0 and *were* reviewed.
|
||||||
|
|
||||||
|
- [Fixed] `si_extract_needed_skips` only flags ICs on this graph. Shared `library/extracted` cache is not `not_reviewed`.
|
||||||
|
|
||||||
## 2.37.0 — 2026-09-20 — SI bus gate (EN RC ≠ USB series R)
|
## 2.37.0 — 2026-09-20 — SI bus gate (EN RC ≠ USB series R)
|
||||||
|
|
||||||
Datasheet SI facts apply only to the bus in the quote. ESP32 EN RC 10 kΩ / 1 µF is not a USB D+/D− series resistor. USB without a library Z number stays measurement-only (88 Ω), not a 90 Ω FAIL. Coverage: PHY+RJ45 MDI, MAC–PHY RGMII/SGMII, DDR3, USB-C SuperSpeed.
|
Datasheet SI facts apply only to the bus in the quote. ESP32 EN RC 10 kΩ / 1 µF is not a USB D+/D− series resistor. USB without a library Z number stays measurement-only (88 Ω), not a 90 Ω FAIL. Coverage: PHY+RJ45 MDI, MAC–PHY RGMII/SGMII, DDR3, USB-C SuperSpeed.
|
||||||
|
|||||||
@@ -80,6 +80,52 @@ def test_si_refresh_when_old_extract_has_only_decoupling():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_si_extract_needed_skips_board_ics_only_not_shared_library():
|
||||||
|
from backend.services.pcb_pipeline import si_extract_needed_skips
|
||||||
|
|
||||||
|
graph = DesignGraph(
|
||||||
|
components={
|
||||||
|
"U5": Component(
|
||||||
|
reference="U5", value="ESP32-C6-WROOM-1", footprint="",
|
||||||
|
component_type=ComponentType.IC, mpn="ESP32-C6-WROOM-1",
|
||||||
|
pins={"1": "EN"},
|
||||||
|
),
|
||||||
|
"J1": Component(
|
||||||
|
reference="J1", value="USB-C", footprint="",
|
||||||
|
component_type=ComponentType.CONNECTOR, mpn="TYPE-C-31-M-12",
|
||||||
|
pins={},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
nets={},
|
||||||
|
)
|
||||||
|
fresh = ComponentConstraints(
|
||||||
|
mpn="ESP32-C6-WROOM-1",
|
||||||
|
model_version="1.13.0",
|
||||||
|
pintable=[Pin(number="1", name="EN")],
|
||||||
|
absolute_maximum_ratings=[],
|
||||||
|
rules=[],
|
||||||
|
layout_rules=[{"kind": "decoupling_proximity", "pin": "EN"}],
|
||||||
|
)
|
||||||
|
stale_lib = ComponentConstraints(
|
||||||
|
mpn="LAN8720A",
|
||||||
|
model_version="1.5.0",
|
||||||
|
pintable=[Pin(number="1", name="TXP")],
|
||||||
|
absolute_maximum_ratings=[],
|
||||||
|
rules=[],
|
||||||
|
layout_rules=[],
|
||||||
|
)
|
||||||
|
stale_board = fresh.model_copy(update={"model_version": "1.5.0", "layout_rules": []})
|
||||||
|
assert si_extract_needed_skips(
|
||||||
|
graph, {"ESP32-C6-WROOM-1": fresh, "LAN8720A": stale_lib}, "1.13.0",
|
||||||
|
) == []
|
||||||
|
skips = si_extract_needed_skips(
|
||||||
|
graph, {"ESP32-C6-WROOM-1": stale_board, "LAN8720A": stale_lib}, "1.13.0",
|
||||||
|
)
|
||||||
|
assert len(skips) == 1
|
||||||
|
assert skips[0]["designator"] == "U5"
|
||||||
|
assert "LAN8720A" not in skips[0]["designator"]
|
||||||
|
|
||||||
|
|
||||||
def test_package_family_mismatch_is_pe_bom_010():
|
def test_package_family_mismatch_is_pe_bom_010():
|
||||||
from backend.periscopex.bom_pcb_check import check_bom_pcb_datasheet
|
from backend.periscopex.bom_pcb_check import check_bom_pcb_datasheet
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user