kicad:EasyEDA→KiCad 工程(Board1/Board2)、库、脚本与评审文档(IC 引脚、风扇、热、EDL 供电、布局规则)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -0,0 +1,79 @@
|
||||
"""Rotate J1 by 180 degrees and place its mating face at the left board edge."""
|
||||
import hashlib
|
||||
import json
|
||||
import shutil
|
||||
|
||||
import pcbnew as pcb
|
||||
|
||||
import route_local_r1 as base
|
||||
from schematic_maintenance import ROOT
|
||||
|
||||
OUT = ROOT / 'out/input-connector-r1'
|
||||
|
||||
|
||||
def snapshot(board):
|
||||
return {f.GetReference(): (f.GetPosition().x, f.GetPosition().y, f.GetOrientationDegrees(),
|
||||
f.GetLayer(), [(p.GetNumber(), p.GetPosition().x, p.GetPosition().y,
|
||||
p.GetNetname()) for p in f.Pads()])
|
||||
for f in board.GetFootprints()}
|
||||
|
||||
|
||||
def copper(board):
|
||||
return {t.m_Uuid.AsString(): (t.GetStart().x, t.GetStart().y, t.GetEnd().x, t.GetEnd().y,
|
||||
t.GetLayer(), t.GetWidth(), t.GetNetname()) for t in board.GetTracks()}
|
||||
|
||||
|
||||
def main():
|
||||
assert not (OUT / 'verification.json').exists(), 'Completed checkpoint exists'
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
base.OUT = OUT
|
||||
path = base.BOARD
|
||||
start_hash = hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
backup = OUT / ('before-' + path.name)
|
||||
if backup.exists():
|
||||
assert hashlib.sha256(backup.read_bytes()).hexdigest() == start_hash, 'Source changed since failed attempt'
|
||||
else:
|
||||
shutil.copy2(path, backup)
|
||||
board = pcb.LoadBoard(str(path))
|
||||
before, tracks = snapshot(board), copper(board)
|
||||
initial = base.unconnected_count(board)
|
||||
part = next(f for f in board.GetFootprints() if f.GetReference() == 'J1')
|
||||
assert part.GetOrientationDegrees() == 90
|
||||
assert not any(t.GetNetname() in {p.GetNetname() for p in part.Pads()} for t in board.GetTracks()), 'Review existing input copper first'
|
||||
part.SetOrientationDegrees(270)
|
||||
part.SetPosition(pcb.VECTOR2I(pcb.FromMM(58.4), pcb.FromMM(67)))
|
||||
part.Reference().SetTextAngle(pcb.EDA_ANGLE(0, pcb.DEGREES_T))
|
||||
part.Reference().SetPosition(pcb.VECTOR2I(pcb.FromMM(58.4), pcb.FromMM(59)))
|
||||
candidate = path.with_name('PhoneRail-Board1-input-candidate.kicad_pcb')
|
||||
temporary = [candidate.with_suffix(s) for s in ('.kicad_pcb', '.kicad_pro', '.kicad_dru', '.kicad_prl')]
|
||||
assert all(not p.exists() for p in temporary)
|
||||
try:
|
||||
for suffix in ('.kicad_pro', '.kicad_dru'):
|
||||
shutil.copy2(path.with_suffix(suffix), candidate.with_suffix(suffix))
|
||||
pcb.SaveBoard(str(candidate), board)
|
||||
report = base.drc(candidate, 'drc')
|
||||
assert not report['violations'], report['violations']
|
||||
reloaded = pcb.LoadBoard(str(candidate))
|
||||
after = snapshot(reloaded)
|
||||
assert all(value == after[ref] for ref, value in before.items() if ref != 'J1')
|
||||
assert copper(reloaded) == tracks
|
||||
assert base.unconnected_count(reloaded) == initial
|
||||
assert sorted((p[0], p[3]) for p in before['J1'][4]) == sorted((p[0], p[3]) for p in after['J1'][4])
|
||||
assert hashlib.sha256(path.read_bytes()).hexdigest() == start_hash
|
||||
shutil.copy2(candidate, path)
|
||||
result = {'status': 'PLACEMENT_CORRECTED_NOT_RELEASED', 'reference': 'J1',
|
||||
'before_position_mm': [63, 67], 'after_position_mm': [58.4, 67],
|
||||
'before_angle_deg': 90, 'after_angle_deg': 270,
|
||||
'mating_face': 'Left/outward; nominal outline x=50.25041mm, board edge x=50mm',
|
||||
'native_drc_violations': 0, 'other_footprints_preserved': len(before)-1,
|
||||
'existing_track_segments_preserved': len(tracks), 'unconnected_items': initial,
|
||||
'board_sha256': hashlib.sha256(path.read_bytes()).hexdigest()}
|
||||
(OUT / 'verification.json').write_text(json.dumps(result, indent=2)+'\n', encoding='utf-8')
|
||||
print(json.dumps(result))
|
||||
finally:
|
||||
for p in temporary:
|
||||
p.unlink(missing_ok=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
在新工单中引用
屏蔽一个用户