Ship remaining PCB plan slices: BOM chain, SPOF, EMI FACT, gated Tj.

Package/pinout/ratings mismatches, SPOF as REVIEW, EMI only with a
datasheet quote, and Tj from copper+vias+θJA when every parameter exists.
Re-extract SI layout_rules from 1.12.0; ImpedenceFinder license stays UNKNOWN.
This commit is contained in:
2026-09-20 12:29:27 +02:00
parent 65a91419a2
commit 1b3529501f
20 changed files with 1337 additions and 30 deletions
+28 -2
View File
@@ -12,6 +12,7 @@ import logging
from datetime import datetime, timezone
from pathlib import Path
from backend.config import settings as app_settings
from backend.periscopex.cad_bridge import (
annotate_findings_cad,
build_cad_bridge,
@@ -21,6 +22,7 @@ from backend.periscopex.cad_bridge import (
from backend.periscopex.finding_engine import apply_decisions
from backend.periscopex.functional_groups import build_placement_plan
from backend.periscopex.graph import build_graph
from backend.periscopex.layout_rules import needs_layout_rules_refresh
from backend.periscopex.models import ComponentConstraints, DesignGraph, LayoutGraph, ValidationReport
from backend.periscopex.pcb_checks import assign_pcb_finding_ids, run_pcb_checks
from backend.periscopex.pcb_inventory import build_pcb_inventory
@@ -220,9 +222,33 @@ async def run_pcb_pipeline(
_step(project_id, "checks", "running")
findings = run_pcb_checks(graph, cmap, layout, plan, zrep)
si_skip: list[dict] = []
scan_ver = app_settings.get_default_model_version()
for mpn, cons in sorted(cmap.items()):
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.write_text(json.dumps(si_skip, indent=2) + "\n")
ws._upload_file("si_extract_needed.json")
_step(
project_id, "checks", "complete",
f"{len(findings)} deterministic findings",
f"{len(findings)} deterministic findings"
+ (f"; {len(si_skip)} SI re-extract" if si_skip else ""),
)
if _cancelled(storage, user_id, project_id):
@@ -283,7 +309,7 @@ async def run_pcb_pipeline(
findings=findings,
summary=summary,
coverage=coverage,
not_reviewed=skipped,
not_reviewed=skipped + si_skip,
)
report_path = ws.local_path("pcb_report.json")
report_path.write_text(report.model_dump_json(indent=2) + "\n")