- Add pinned STEP-derived DE-9 mechanical measurements and provenance - Refresh placement, routing, silkscreen, and nominal CAD envelopes - Document provisional dimensions, assumptions, and validation status
94 lines
3.7 KiB
Python
94 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Add nominal STEP-derived metal bounding projections on Dwgs.User only.
|
|
|
|
Not silkscreen, a courtyard, or a case cutout. See db9-mechanical-notes.md for
|
|
source attribution, selected face subsets and the user-confirmed male/female
|
|
external-geometry equivalence. No module height or case-wall thickness assumed.
|
|
"""
|
|
import json
|
|
from pathlib import Path
|
|
import uuid
|
|
import pcbnew as p
|
|
|
|
ROOT=Path(__file__).resolve().parent
|
|
PCB=ROOT/"serial-carrier.kicad_pcb"
|
|
NS=uuid.UUID("ba7274a8-c35d-46bc-908d-cd86708fc6c4")
|
|
|
|
|
|
def main():
|
|
lock=ROOT/"~serial-carrier.kicad_pcb.lck"
|
|
if lock.exists():
|
|
raise SystemExit("Close PCB Editor before refreshing the CAD envelopes")
|
|
before=PCB.read_bytes()
|
|
project=ROOT/"serial-carrier.kicad_pro"
|
|
project_before=project.read_bytes()
|
|
board=p.LoadBoard(str(PCB))
|
|
u2=next(f for f in board.GetFootprints() if f.GetReference()=="U2")
|
|
assert u2.GetOrientationDegrees()==0, "Review transform for rotated module"
|
|
x0,y0=p.ToMM(u2.GetPosition().x),p.ToMM(u2.GetPosition().y)
|
|
data=json.loads((ROOT/"reference/db9-cad/measurements.json").read_text())
|
|
owned={str(uuid.uuid5(NS,str(i))) for i in range(64)}
|
|
for drawing in list(board.GetDrawings()):
|
|
if drawing.m_Uuid.AsString() in owned:
|
|
board.Delete(drawing)
|
|
count=0
|
|
|
|
def point(x,y):
|
|
# STEP/Eagle axes +Y toward connector; KiCad local Y is downward.
|
|
return p.VECTOR2I(p.FromMM(x0+x-1.905),p.FromMM(y0+2.667-y))
|
|
|
|
def add(item):
|
|
nonlocal count
|
|
assert count<64
|
|
item.SetLayer(p.Dwgs_User)
|
|
item.SetUuid(p.KIID(str(uuid.uuid5(NS,str(count)))))
|
|
board.Add(item)
|
|
count+=1
|
|
|
|
for feature in ("front_flange_exposed_faces", "shell_outer_including_root_and_lip",
|
|
"left_front_hex_post", "right_front_hex_post"):
|
|
xmin,ymin,_,xmax,ymax,_=data[feature]["bounds"]
|
|
rect=p.PCB_SHAPE(board)
|
|
rect.SetShape(p.SHAPE_T_RECT)
|
|
rect.SetStart(point(xmin,ymax)); rect.SetEnd(point(xmax,ymin))
|
|
rect.SetWidth(p.FromMM(0.1))
|
|
add(rect)
|
|
for axis in data["front_lock_axes"]:
|
|
x=axis["location"][0]
|
|
# These mark front-facing Y axes, not holes through the carrier PCB.
|
|
line=p.PCB_SHAPE(board)
|
|
line.SetShape(p.SHAPE_T_SEGMENT)
|
|
line.SetStart(point(x,26.907)); line.SetEnd(point(x,32.707))
|
|
line.SetWidth(p.FromMM(0.08))
|
|
add(line)
|
|
label=p.PCB_TEXT(board)
|
|
label.SetText("CAD METAL ENVELOPES - NOT CASE CUTOUT")
|
|
label.SetPosition(p.VECTOR2I(p.FromMM(x0+13.97),p.FromMM(y0-17.67)))
|
|
label.SetTextSize(p.VECTOR2I(p.FromMM(0.65),p.FromMM(0.65)))
|
|
label.SetTextThickness(p.FromMM(0.1))
|
|
add(label)
|
|
for drawing in board.GetDrawings():
|
|
if not isinstance(drawing,p.PCB_TEXT):
|
|
continue
|
|
value=drawing.GetText()
|
|
if value.startswith("DE-9 OUT /"):
|
|
drawing.SetPosition(p.VECTOR2I(p.FromMM(x0+13.97),p.FromMM(y0-20.67)))
|
|
elif value=="82 x 80 mm / 2 copper layers":
|
|
drawing.SetPosition(p.VECTOR2I(p.FromMM(141),p.FromMM(91)))
|
|
elif value.startswith("Nominal RS-232 metal verified;"):
|
|
drawing.SetPosition(p.VECTOR2I(p.FromMM(141),p.FromMM(93.5)))
|
|
temp=ROOT/"serial-carrier-envelope-tmp.kicad_pcb"
|
|
try:
|
|
assert p.SaveBoard(str(temp),board,True)
|
|
if lock.exists() or PCB.read_bytes()!=before:
|
|
raise RuntimeError("PCB changed or reopened; refusing overwrite")
|
|
assert project.read_bytes()==project_before
|
|
temp.replace(PCB)
|
|
finally:
|
|
temp.unlink(missing_ok=True)
|
|
print(f"Added {count} nominal CAD projection graphics on Dwgs.User; no copper/project changes")
|
|
|
|
|
|
if __name__=="__main__":
|
|
main()
|