# -*- coding: utf-8 -*- u"""把下板 PCB1 上的**全部铜**清空:走线 / 过孔 / 铺铜。元件、板框、丝印一概不动。 /c/Python314/python .claude/pcb1_wipe.py # 只统计,不删 /c/Python314/python .claude/pcb1_wipe.py --push # 真删 ## 什么时候用 **摆位大改之后。** 走线和过孔是按旧摆位算的绝对坐标,元件挪走了它们**不会跟着动** —— 板上会剩下一堆连到空气里的铜,而 DRC 只会报「连接性错误」,看不出「这些铜已经作废」。 逐个跑各脚本的 `clean()` 也不保险:它们各自按坐标对账,摆位一变对账基准就没了。 ⚠️⚠️ **判据是「铜层白名单」,不是「排除板框层」。** `pcb1_pour.py` 里那句 `if (l.layer === 11) continue; // 11 = 板框` 其实是**死代码** —— 2026-09-06 实测:板上 2951 条 `pcb_PrimitiveLine` **全部在 layer 1(2298) / 2(653)**, layer 11 一条都没有,板框根本不是 Line 图元(API 的 Line/Arc/Region 里都找不到它)。 所以这里只删 `1/2/15/16` 这四个铜层的线;就算板框哪天真在别的层上,也碰不到它。 ⚠️ 删完必须按顺序重来: `pcb_fanout.py --clean → dump_pads.py → --push` → `pcb1_pour.py --push` → `pcb1_route_pwr.py --push` → `pcb1_route_sig.py --push`, 而且 `_pcb1_sig_routes.json` / `_pcb1_sig_plan*.json` 这些账本要一起作废 (不然下一轮的归属表会拿旧账本去认板上的新铜)。 """ import json import os import sys HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) from eda import run # noqa: E402 PCB = '7e9415a16a909c51' COPPER = (1, 2, 15, 16) # 只有这四层的线会被删 JS_COUNT = u""" await eda.dmt_EditorControl.openDocument('%s'); await new Promise(r => setTimeout(r, 2000)); const lines = await eda.pcb_PrimitiveLine.getAll(); const vias = await eda.pcb_PrimitiveVia.getAll(); const pours = await eda.pcb_PrimitivePour.getAll(); const CU = %s; let cu = 0, other = 0; for (const l of lines) { if (CU.includes(l.layer)) cu++; else other++; } return {line_cu: cu, line_other: other, via: vias.length, pour: pours.length, comp: (await eda.pcb_PrimitiveComponent.getAll()).length}; """ % (PCB, list(COPPER)) JS_WIPE = u""" await eda.dmt_EditorControl.openDocument('%s'); await new Promise(r => setTimeout(r, 2000)); const CU = %s; const dl = []; for (const l of await eda.pcb_PrimitiveLine.getAll()) if (CU.includes(l.layer)) dl.push(l.primitiveId); const dv = (await eda.pcb_PrimitiveVia.getAll()).map(v => v.primitiveId); const dp = []; for (const p of await eda.pcb_PrimitivePour.getAll()) dp.push(p.getState_PrimitiveId()); if (dl.length) await eda.pcb_PrimitiveLine.delete(dl); if (dv.length) await eda.pcb_PrimitiveVia.delete(dv); if (dp.length) await eda.pcb_PrimitivePour.delete(dp); await eda.pcb_Document.save(); await new Promise(r => setTimeout(r, 2500)); const lines = await eda.pcb_PrimitiveLine.getAll(); let cu = 0, other = 0; for (const l of lines) { if (CU.includes(l.layer)) cu++; else other++; } return {killed_line: dl.length, killed_via: dv.length, killed_pour: dp.length, left_cu: cu, left_other: other, left_via: (await eda.pcb_PrimitiveVia.getAll()).length, left_pour: (await eda.pcb_PrimitivePour.getAll()).length, comp: (await eda.pcb_PrimitiveComponent.getAll()).length}; """ % (PCB, list(COPPER)) # 摆位一变就作废的账本(留着的话下一轮的归属表会拿它去认板上的新铜) STALE = ['_pcb1_sig_routes.json', '_pcb1_sig_plan.json', '_pcb1_sig_plan_all.json', '_pcb1_sig_geom.json', '_pcb1_poured.json', '_pcb1_todo.json', '_fanout_plan_GND.json', '_fanout_plan_GND_full.json', '_fanout_plan_12V.json', '_fanout_plan_3V3.json'] + \ ['_fanout_plan_VO%d_P.json' % n for n in range(1, 6)] def main(): push = '--push' in sys.argv[1:] r = run(JS_COUNT, timeout=600) if not r.get('success'): sys.exit(u'读板失败: ' + json.dumps(r, ensure_ascii=False)[:400]) d = r['result'] print(u'板上:%d 元件 / %d 条铜走线(layer 1/2/15/16)/ %d 条其它层的线 / %d 过孔 / %d 铺铜' % (d['comp'], d['line_cu'], d['line_other'], d['via'], d['pour'])) if not push: print(u'(没加 --push,什么都没删)') return 0 r = run(JS_WIPE, timeout=1800) if not r.get('success'): sys.exit(u'清空失败: ' + json.dumps(r, ensure_ascii=False)[:400]) x = r['result'] print(u' 删掉 %d 走线 / %d 过孔 / %d 铺铜' % (x['killed_line'], x['killed_via'], x['killed_pour'])) print(u' 剩下:%d 元件 / %d 条铜走线 / %d 条其它层的线 / %d 过孔 / %d 铺铜' % (x['comp'], x['left_cu'], x['left_other'], x['left_via'], x['left_pour'])) if x['left_cu'] or x['left_via'] or x['left_pour']: print(u' ⚠️ 没清干净,再跑一次') if x['comp'] != d['comp']: sys.exit(u'⛔⛔ 元件数从 %d 变成 %d —— 删到不该删的了,立刻撤销(Ctrl+Z)' % (d['comp'], x['comp'])) n = 0 for f in STALE: p = os.path.join(HERE, f) if os.path.exists(p): os.rename(p, p + '.stale') n += 1 print(u' 作废 %d 个旧账本(改名成 .stale)' % n) print(u'\n接着按顺序重来:pcb_fanout(--clean → dump_pads → --push) → pcb1_pour --push' u' → pcb1_route_pwr --push → pcb1_route_sig --push') return 0 if __name__ == '__main__': sys.exit(main())