Integrate Measured ESP32 PCB Geometry

- Add measured F.Fab bounds and USB mouth markers
- Add guarded preservation and regression validation
- Refresh review exports and remove draft presentation labels
This commit is contained in:
2026-09-21 22:54:37 +02:00
parent ab6a8010ba
commit 901afbe754
25 changed files with 20490 additions and 850 deletions
+91
View File
@@ -0,0 +1,91 @@
#!/usr/bin/env python3
"""Synchronize measured U1 F.Fab graphics only; never replace the footprint."""
from pathlib import Path
import uuid
import pcbnew as p
ROOT = Path(__file__).resolve().parent
PCB = ROOT / "serial-carrier.kicad_pcb"
NS = uuid.UUID("36693251-e1f8-44e9-a8a9-e201bd658ceb")
def library_graphics():
fp = p.FootprintLoad(str(ROOT / "Carrier.pretty"), "HW678_2x22_Provisional")
if fp is None:
raise RuntimeError("Cannot load measured HW678 footprint")
return fp
def signature(g, origin):
def point(v):
return v.x-origin.x, v.y-origin.y
if isinstance(g, p.PCB_SHAPE):
return ("shape", g.GetShape(), point(g.GetStart()), point(g.GetEnd()), g.GetWidth())
return ("text", g.GetText(), point(g.GetPosition()),
(g.GetTextSize().x, g.GetTextSize().y), g.GetTextThickness())
def measured_items(fp):
# Generic library captions would overlap existing board labels.
return sorted((g for g in fp.GraphicalItems() if g.GetLayer() == p.F_Fab
and not (isinstance(g, p.PCB_TEXT) and g.GetText() in (
"PROVISIONAL CONNECTOR FIT", "ANTENNA UP / USB DOWN"))),
key=lambda g: signature(g, fp.GetPosition()))
def verify(board):
u1 = next(f for f in board.GetFootprints() if f.GetReference() == "U1")
assert u1.GetOrientationDegrees() == 0 and u1.GetLayer() == p.F_Cu
assert len([g for g in u1.GraphicalItems() if g.GetLayer() == p.F_Fab]) == 6
library = library_graphics()
expected = [signature(g, library.GetPosition()) for g in measured_items(library)]
actual = [signature(g, u1.GetPosition()) for g in measured_items(u1)]
assert actual == expected, "Embedded U1 F.Fab differs from measured library; run update_u1_graphics.py"
def main():
lock = ROOT / "~serial-carrier.kicad_pcb.lck"
if lock.exists():
raise SystemExit("Close PCB Editor before updating U1 graphics")
before = PCB.read_bytes()
project = ROOT / "serial-carrier.kicad_pro"
project_before = project.read_bytes()
board = p.LoadBoard(str(PCB))
u1 = next(f for f in board.GetFootprints() if f.GetReference() == "U1")
assert u1.GetOrientationDegrees() == 0 and u1.GetLayer() == p.F_Cu
library = library_graphics()
sources = measured_items(library)
assert len(sources) == 6, "Review changed measured library before integration"
owned = {str(uuid.uuid5(NS, str(i))) for i in range(len(sources))}
existing = list(u1.GraphicalItems())
assert all(g.m_Uuid.AsString() in owned for g in existing if g.GetLayer() == p.F_Fab), "Unowned U1 fabrication graphics; review manually"
if [signature(g, u1.GetPosition()) for g in measured_items(u1)] == [
signature(g, library.GetPosition()) for g in sources]:
verify(board)
print("Embedded measured U1 graphics already current; no board write")
return
for g in existing:
if g.m_Uuid.AsString() in owned:
u1.Remove(g)
for i, source in enumerate(sources):
graphic = source.Duplicate()
graphic.SetParent(u1)
graphic.SetUuid(p.KIID(str(uuid.uuid5(NS, str(i)))))
graphic.Move(u1.GetPosition() - library.GetPosition())
u1.Add(graphic)
verify(board)
temp = ROOT / "validation/pcb-u1-tmp.kicad_pcb"
try:
assert p.SaveBoard(str(temp), board, True)
verify(p.LoadBoard(str(temp)))
if lock.exists() or PCB.read_bytes() != before:
raise RuntimeError("PCB changed or reopened; refusing to overwrite")
assert project.read_bytes() == project_before, "Project settings changed"
temp.replace(PCB)
finally:
temp.unlink(missing_ok=True)
print("Embedded measured U1 graphics; existing footprint fields, pads and routing retained")
if __name__ == "__main__":
main()