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:
@@ -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()
|
||||
Reference in New Issue
Block a user