Files
Commander1024 c257158ada Record Accepted PCB Placement And Fit
Align component groups, repair affected routing, and add preservation
audits.
Assign the confirmed U2 footprint and update pull-ups to 2 kΩ across
design files.
Document user acceptance, switch to an A3 worksheet, and refresh PCB
exports.
2026-09-22 10:28:08 +02:00

76 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""Guarded accepted-fit label cleanup and A3 worksheet; never move board items."""
import copy
from pathlib import Path
import tempfile
import pcbnew as p
ROOT = Path(__file__).resolve().parent
PCB = ROOT / "serial-carrier.kicad_pcb"
COMMENT = "USB / antenna geometry pending; verify module pinout / assembly"
USB = "USB ACCESS - VERIFY"
OLD_CAPTION = "Nominal RS-232 metal verified; ESP32 USB / antenna / assembly clearance pending"
CAPTION = "Nominal RS-232 metal verified; RF qualification not established"
def expected_tree(tree):
result = copy.deepcopy(tree)
for item in list(result):
if not isinstance(item, list):
continue
if item[0] == "paper":
assert item in (["paper", '"A4"'], ["paper", '"A3"'])
item[1] = '"A3"'
elif item[0] == "title_block":
for field in list(item[1:]):
if field == ["comment", "1", '"' + COMMENT + '"']:
item.remove(field)
elif item[0] == "gr_text":
if item[1] == '"' + USB + '"':
assert ['layer', '"Dwgs.User"'] in item
result.remove(item)
elif item[1] == '"' + OLD_CAPTION + '"':
assert ['layer', '"Dwgs.User"'] in item
item[1] = '"' + CAPTION + '"'
return result
def main():
from validate_u1_update import parse, canonical
lock = ROOT / "~serial-carrier.kicad_pcb.lck"
if lock.exists():
raise RuntimeError("Close PCB Editor before changing presentation")
original = PCB.read_bytes()
protected = {path: path.read_bytes() for path in ROOT.glob("serial-carrier.kicad_pr*")}
expected = expected_tree(parse(PCB))
with tempfile.TemporaryDirectory(prefix="carrier-presentation-", dir=ROOT) as temporary:
candidate = Path(temporary) / PCB.name
# PAGE_INFO is opaque in this pcbnew binding. Load the exact paper-only
# substitution, then use the native saver for all label changes.
text = original.decode()
assert text.count('(paper "A4")') + text.count('(paper "A3")') == 1
candidate.write_text(text.replace('(paper "A4")', '(paper "A3")'))
board = p.LoadBoard(str(candidate))
title = board.GetTitleBlock()
assert title.GetComment(0) in (COMMENT, "")
title.SetComment(0, "")
for drawing in list(board.GetDrawings()):
if isinstance(drawing, p.PCB_TEXT):
if drawing.GetText() == USB:
assert drawing.GetLayer() == p.Dwgs_User
board.Remove(drawing)
elif drawing.GetText() == OLD_CAPTION:
drawing.SetText(CAPTION)
assert p.SaveBoard(str(candidate), board, True)
assert canonical(parse(candidate)) == canonical(expected), "Unexpected physical/content change"
assert canonical(expected_tree(parse(candidate))) == canonical(expected), "Not idempotent"
if lock.exists() or PCB.read_bytes() != original:
raise RuntimeError("PCB changed or reopened; refusing overwrite")
assert all(path.read_bytes() == data for path, data in protected.items()), "Settings changed"
candidate.replace(PCB)
print("A3 worksheet / accepted-fit labels updated; exact preservation and idempotence PASS")
if __name__ == "__main__":
main()