# -*- coding: utf-8 -*- u"""FAN-R2 落到下板 PCB1:**只动风扇那一块**的 24 个元件,别的 277 个和已有铜一律不碰。 /c/Python314/python .claude/pcb1_fan.py # 干跑 /c/Python314/python .claude/pcb1_fan.py --push # 摆件 + 拆废铜 + 存盘 为什么不直接 `pcb1_place.py --push`:那会重摆全部 301 个元件, 把已经布好的 3116 段铜全部作废。整板重布是 PROGRESS 里单列的下一步,不该由这一轮顺手引爆。 拆铜的判据只有一条:**这个网络在板上已经一个焊盘都没有了**(旧风扇拓扑留下的孤儿), 外加 FAN_PWM —— 它的两个焊盘都还在,但 R733 从 (98,80.5) 挪到了 (108,74),旧铜接不上了。 """ import io import json import os import sys HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) from eda import run import pcb1_place as PL PCB = '7e9415a16a909c51' MIL = 39.3701 FAN = ['Q6', 'Q7', 'Q8', 'Q9', 'Q10', 'DZ3', 'D2', 'C738', 'R733', 'R734', 'R746', 'R747', 'R748', 'R749', 'R750', 'R751', 'R752', 'R753', 'R754', 'R755', 'R756', 'R757', 'J26', 'J27'] # 死网络(板上已无焊盘)+ FAN_PWM(焊盘在、但 R733 挪了位,旧铜作废) RIP = ['$17N148', '$17N151', '$17N154', 'FAN_TACH', 'FAN_PWM'] def call(js, timeout=300): r = run(js, timeout=timeout) if not r.get('success'): raise RuntimeError(json.dumps(r, ensure_ascii=False)[:900]) return r['result'] def main(): push = '--push' in sys.argv plan = [[d, round(PL.POS[d][0] * MIL, 2), round(PL.POS[d][1] * MIL, 2), PL.POS[d][2]] for d in FAN] print(u'风扇块 %d 个元件;拆废铜网络 %s' % (len(plan), ', '.join(RIP))) for d, x, y, r in plan: print(u' %-5s (%8.2f, %8.2f) rot %d' % (d, x, y, r)) if not push: print(u'加 --push 执行') return call(""" await eda.dmt_EditorControl.openDocument('%s'); await new Promise(r=>setTimeout(r,2000)); return 1;""" % PCB) n = call(""" const P = %s; const map = {}; for (const c of await eda.pcb_PrimitiveComponent.getAll()) map[c.getState_Designator()] = c; let n = 0; for (const [d, x, y, rot] of P) { const c = map[d]; if (!c) throw new Error('板上没有 ' + d); await eda.pcb_PrimitiveComponent.modify(c.getState_PrimitiveId(), {layer: 1}); await eda.pcb_PrimitiveComponent.modify(c.getState_PrimitiveId(), {x, y, rotation: rot}); n++; } return n; """ % json.dumps(plan)) print(u'已摆 %d 个元件' % n) # 拆铜:循环删到剩 0(skill §9x:一次 getAll 逐条 delete 删不干净) rip = call(""" const NETS = %s; let dl = 0, dv = 0; for (let pass = 0; pass < 4; pass++) { let left = 0; for (const l of await eda.pcb_PrimitiveLine.getAll()) if (NETS.includes(l.getState_Net())) { await eda.pcb_PrimitiveLine.delete(l.getState_PrimitiveId()); dl++; } for (const v of await eda.pcb_PrimitiveVia.getAll()) if (NETS.includes(v.getState_Net())) { await eda.pcb_PrimitiveVia.delete(v.getState_PrimitiveId()); dv++; } for (const l of await eda.pcb_PrimitiveLine.getAll()) if (NETS.includes(l.getState_Net())) left++; for (const v of await eda.pcb_PrimitiveVia.getAll()) if (NETS.includes(v.getState_Net())) left++; if (!left) return {dl, dv, left: 0, pass}; } let left = 0; for (const l of await eda.pcb_PrimitiveLine.getAll()) if (NETS.includes(l.getState_Net())) left++; for (const v of await eda.pcb_PrimitiveVia.getAll()) if (NETS.includes(v.getState_Net())) left++; return {dl, dv, left}; """ % json.dumps(RIP)) print(u'拆铜:导线 %d 段 / 过孔 %d 个,剩余 %d' % (rip['dl'], rip['dv'], rip['left'])) if rip['left']: raise SystemExit(u'废铜没删干净') got = call(""" const P = %s; const map = {}; for (const c of await eda.pcb_PrimitiveComponent.getAll()) map[c.getState_Designator()] = c; const out = []; for (const [d, x, y, rot] of P) { const c = map[d]; out.push([d, Math.round(c.getState_X()*100)/100, Math.round(c.getState_Y()*100)/100, c.getState_Rotation(), c.getState_Layer()]); } await eda.pcb_Document.save(); return out; """ % json.dumps(plan)) bad = [] for (d, x, y, r), (gd, gx, gy, gr, gl) in zip(plan, got): if abs(gx - x) > 0.02 or abs(gy - y) > 0.02 or gr != r or gl != 1: bad.append(u'%s 期望 (%.2f,%.2f) rot %d L1,实测 (%.2f,%.2f) rot %s L%s' % (d, x, y, r, gx, gy, gr, gl)) if bad: raise SystemExit(u'回读对不上:\n ' + u'\n '.join(bad)) print(u'回读 %d 个元件,位置/角度/层逐个一致;已存盘' % len(got)) main()