文件
phonerail/.claude/pcb1_wipe.py
T
zhou yang和Claude Opus 5 2bfe308b30 初始导入:PhoneRail 五路手机直供电源板
原理图/PCB 已在 EasyEDA 里做到 12 页跨页 0 漏接,固件刚完成骨架包 A1。
这次是把已有的一切一次性纳入版本控制,没有改动任何设计内容。

纳入:
- 设计与需求文档、PROGRESS.md、小米8 参考原理图 PDF 与追脚脚本
- .claude/ 下的 EasyEDA 桥接工具链与几何/网表缓存(eda-parts.json、
  pcb-parts.json、pcb-pads.json、p2-ch1.json 等,是重画各页的唯一事实来源)
- 117 张验证截图(本项目所有方向问题都是靠人眼看截图发现的,属于证据不是垃圾)
- firmware/ 的 A1 骨架与 site/ 文档站源码

.gitignore 排除:构建产物(firmware/build、host/build、sdkconfig)、
site/node_modules、__pycache__、桥接日志 _bridge.log、脚本自标的 *.stale。

.gitattributes 默认 `* -text`:现有 .py/.json 大多是 CRLF,原样存,
不让 git 重写行尾。唯一例外是 *.sh 强制 LF —— 带 \r 的 shell 脚本 bash 跑不了。

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QYuExfgyYa6DzRRcUso8M8
2026-09-06 18:18:14 +08:00

120 行
5.7 KiB
Python
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# -*- 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())