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.
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user