文件

131 行
6.1 KiB
Python

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# -*- coding: utf-8 -*-
u"""重载 EasyEDA 客户端页面(底板版,照 `.claude/pcb2_reload.py`,skill §9k / §9ae)。
/c/Python314/python .claude/r2/reload_base.py
为什么要重载:API 下发两千段导线之后 `getCalculatingRatlineStatus()` 一直是 `active`,
铺铜「重建所有」永远填 0 块(§9ae-4);`stopCalculatingRatline()` 解不开,只能重载。
DRC 的连通性索引也要重载之后才认 API 新建的铜(§9aa)。
顺序:关对话框 → 数图元(基线)→ 删铺铜边框(重载反正会丢)→ save → reload → 等 windowId 变掉 →
**再数一遍,必须与基线逐项相等**(比 pcb2 版的「不为零」更严)。不过关就停手,后面任何会 save() 的脚本都不许跑。
重载之后接 `pour_base.py --push`(重建铜面 + 重建所有)。
"""
import json
import os
import sys
import time
import urllib.request
sys.stdout.reconfigure(encoding='utf-8')
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.dirname(HERE))
sys.path.insert(0, HERE)
from eda import run, port # noqa: E402
# `--blade` 对刀片做同一件事(2026-09-11:刀片 90×68 下发之后也要重载再 DRC / 铺铜,skill §9aa)
if '--blade' in sys.argv:
import pcb_blade as B # noqa: E402
else:
import pcb_base as B # noqa: E402
OPEN = ("await eda.dmt_EditorControl.openDocument('%s');"
"await new Promise(r=>setTimeout(r,%d));")
COUNT_JS = u"""
const n = async k => ((await eda[k].getAll()) || []).length;
return {doc: (await eda.dmt_Pcb.getCurrentPcbInfo() || {}).name,
comps: await n('pcb_PrimitiveComponent'), lines: await n('pcb_PrimitiveLine'),
vias: await n('pcb_PrimitiveVia'), pads: await n('pcb_PrimitivePad')};
"""
RELOAD_JS = u"""
const sleep = ms => new Promise(r => setTimeout(r, ms));
let n = 0;
for (const o of await eda.pcb_PrimitivePour.getAll()) { await eda.pcb_PrimitivePour.delete(o.primitiveId); n++; }
await eda.pcb_Document.save();
await sleep(1500);
window.onbeforeunload = null;
setTimeout(() => location.reload(true), 300);
return {deletedPours: n};
"""
CANCEL_JS = u"""
const sleep = ms => new Promise(r => setTimeout(r, ms));
const nat = b => { const o={bubbles:true,cancelable:true,view:window,button:0};
for (const t of ['pointerdown','mousedown','pointerup','mouseup','click'])
b.dispatchEvent(new (t.startsWith('pointer')?PointerEvent:MouseEvent)(t,o)); };
let k = 0;
for (const b of document.querySelectorAll('button'))
if ((b.innerText||'').trim()==='取消' && b.offsetParent!==null) { nat(b); k++; await sleep(800); }
return {cancelled: k};
"""
def wid():
try:
with urllib.request.urlopen('http://127.0.0.1:%d/health' % port(), timeout=3) as r:
return json.loads(r.read()).get('activeWindowId')
except Exception:
return None
def count(wait_ms):
r = run(OPEN % (B.PCB, wait_ms) + COUNT_JS, timeout=180)
if not r.get('success'):
return None
return r['result']
KEYS = ('comps', 'lines', 'vias', 'pads')
def main():
u"""关对话框 → 数基线 → 删铺铜边框 → save → reload → 再数一遍,必须与基线逐项相等。
⚠ 2026-09-11 以前这段是**模块顶层代码**:别的脚本 `import reload_base` 想借 `CANCEL_JS` 关个对话框,
结果整套重载直接跑了(删了 119 块铺铜边框、存盘、重载)。现在 import 只拿常量 / 函数。"""
run(CANCEL_JS, timeout=60)
base = count(1500)
if not base or not all(base.get(k) for k in KEYS):
sys.exit(u'!! 重载前数图元就不对,不重载: %s' % base)
print(u'重载前:%s 元件 %d / 导线 %d / 过孔 %d / 焊盘 %d'
% (base['doc'], base['comps'], base['lines'], base['vias'], base['pads']))
w0 = wid()
r = run(OPEN % (B.PCB, 1500) + RELOAD_JS, timeout=120)
if not r.get('success'):
sys.exit(u'!! 删铜面 / 存盘失败,没有重载: %s' % json.dumps(r, ensure_ascii=False)[:400])
print(u'重载前 windowId %s,删铺铜边框 %s' % (w0, r['result'].get('deletedPours')))
for i in range(36):
time.sleep(5)
w = wid()
if w and w != w0:
print(u'已重载,新 windowId %s(%d s)' % (w, (i + 1) * 5))
time.sleep(10)
now = count(9000)
if not now:
sys.exit(u'!! 重载后数图元失败 —— **不要再跑任何会 save() 的脚本**(§9ae)')
print(u'重载后:%s 元件 %d / 导线 %d / 过孔 %d / 焊盘 %d'
% (now['doc'], now['comps'], now['lines'], now['vias'], now['pads']))
# 元件 / 过孔 / 焊盘必须逐项相等;导线允许**少**不超过 1%(2026-09-10 实测 2222 → 2220:
# 客户端载入时把重合段并掉,`board_check` 37 个网逐条不变)。失同步的样子是整层读回 0,照样拦得住。
diff = [k for k in KEYS if k != 'lines' and now.get(k) != base.get(k)]
nl, bl = now.get('lines') or 0, base['lines']
if not (bl * 0.99 <= nl <= bl):
diff.append('lines')
elif nl != bl:
print(u' (导线少了 %d 段,≤1%%,按重合段被合并处理)' % (bl - nl))
if diff:
sys.exit(u'!! 重载后与基线不符(%s)—— 桥接与文档失同步(§9ae)。\n'
u' **立刻停手,不要 save(),不要跑 pour_base.py** —— 那一步会把错的状态写回去。\n'
u' 先 API Gateway → 重新连接,不行就重启客户端,再数一遍。' % ', '.join(diff))
rat = run(OPEN % (B.PCB, 500) + u"return await eda.pcb_Document.getCalculatingRatlineStatus();",
timeout=60).get('result')
print(u'[OK] 图元与基线逐项相等;飞线计算状态 %s' % rat)
sys.exit(0)
sys.exit(u'!! 180 s 内 windowId 没变,重载没成功(可能被「未保存改动」挡掉,§9k)')
if __name__ == '__main__':
main()