原理图/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
120 行
5.7 KiB
Python
120 行
5.7 KiB
Python
# -*- 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())
|