# -*- coding: utf-8 -*- u"""下板 PCB1:清掉 `C609` / `C612` 转 180° 之后留下的**接错脚的旧铜**。 /c/Python314/python .claude/pcb1_satflip_fix.py # 只列 /c/Python314/python .claude/pcb1_satflip_fix.py --push # 删 + 存盘 + 回查 ## 怎么来的 `pcb1_place.SAT_FLIP` 把 C609 / C612(DAC63204 的 VREF 电容)转了 180°, 好让 VREF 脚朝南正对 U6.16 / U7.16(§26.22 的 A 类唯一几何解)。 转完两个脚**南北互换**,而板上那几段铜还是按旧朝向画的,于是: | 旧铜 | 现在落在 | 后果 | |---|---|---| | GND 扇出短线(从电容南脚往南到 GND 过孔)| 南脚现在是 **`$6N532`/`$6N547`** | **GND 短到 VREF** | | `$6N532`/`$6N547` 出线(从北脚往北去 U6.16)| 北脚现在是 **GND** | **VREF 短到 GND** | ⚠️ **这两条是真短路,不是 DRC 挑剔** —— 端点直接落在对方焊盘的轮廓里 (DRC 报「距离为 0mil」)。`pcb1_place.py` 里预告过「下发时要删 2 段扇出短线」, 就是这件事。 删完这四段之后: * `$6N532` / `$6N547` 剩下的那截会变成悬空 → 跑 `pcb1_orphan_cu.py --push` 收掉; * 两颗电容的 GND 脚没有扇出孔了 → 跑 `pcb_fanout.py --net GND --only C609,C612 --push`; * VREF 那条 0.8 mm 的短连接交给 `pcb1_route_sig.py`。 """ import json import os import sys HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) from eda import run PCB = '7e9415a16a909c51' MIL = 39.3700787 # (网络, 层, x0, y0, x1, y1, 为什么删)。坐标 mm,两端顺序无关,带 0.005 容差匹配。 DEAD = [ ('GND', 1, 78.030, 40.277, 78.030, 39.608, u'C609 的旧 GND 扇出短线;北端 40.277 落在 C609.2($6N532, y 40.205~40.745) 里 = 短路'), ('$6N532', 1, 78.031, 41.318, 78.031, 41.869, u'C609 的旧 VREF 出线;南端 41.318 落在 C609.1(GND, y 41.045~41.586) 里 = 短路'), ('GND', 1, 108.021, 36.975, 108.021, 36.306, u'C612 的旧 GND 扇出短线;北端落在 C612.2($6N547) 里 = 短路'), ('$6N547', 1, 108.022, 38.016, 108.022, 38.566, u'C612 的旧 VREF 出线;南端落在 C612.1(GND) 里 = 短路'), ] 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'] JS = u""" await eda.dmt_EditorControl.openDocument('%s'); await new Promise(r => setTimeout(r, 2000)); const MIL = %f, D = %s, PUSH = %s; const TOL = 0.005; const near = (a, b) => Math.abs(a - b) < TOL; const same = (r, a, b, c, d) => (near(r[2], a) && near(r[3], b) && near(r[4], c) && near(r[5], d)) || (near(r[2], c) && near(r[3], d) && near(r[4], a) && near(r[5], b)); const found = [], ids = []; for (const l of await eda.pcb_PrimitiveLine.getAll()) { const net = l.net || '', lay = l.layer; const a = l.startX/MIL, b = l.startY/MIL, c = l.endX/MIL, d = l.endY/MIL; for (let k = 0; k < D.length; k++) { const r = D[k]; if (r[0] === net && r[1] === lay && same(r, a, b, c, d)) { found.push(k); ids.push(l.getState_PrimitiveId()); } } } let deleted = 0; if (PUSH) { for (const id of ids) { await eda.pcb_PrimitiveLine.delete(id); deleted++; } await eda.pcb_Document.save(); } return {found, deleted, nLine: (await eda.pcb_PrimitiveLine.getAll()).length}; """ def main(): push = '--push' in sys.argv rows = [[d[0], d[1], d[2], d[3], d[4], d[5]] for d in DEAD] r = call(JS % (PCB, MIL, json.dumps(rows), 'true' if push else 'false')) hit = set(r['found']) print(u'板上匹配到 %d / %d 段:' % (len(hit), len(DEAD))) for k, d in enumerate(DEAD): print(u' [%s] %-8s L%d (%7.3f,%6.3f)-(%7.3f,%6.3f)' % ('✔' if k in hit else ' ', d[0], d[1], d[2], d[3], d[4], d[5])) print(u' %s' % d[6]) if not push: print(u'\n加 --push 删除并存盘') return print(u'\n已删 %d 段,板上还剩 %d 条导线' % (r['deleted'], r['nLine'])) back = call(JS % (PCB, MIL, json.dumps(rows), 'false')) if back['found']: sys.exit(u'⛔ 回查还剩 %d 段没删干净' % len(set(back['found']))) print(u' ✔ 回查:四段全部不在板上了') print(u'\n接着跑:pcb1_orphan_cu.py --push → ' u'pcb_fanout.py --net GND --only C609,C612 --push → drc_read.py') if __name__ == '__main__': main()