R2:底板 / 刀片布局布线脚本、快照、截图与规划文档更新(含外壳、打印件、布线归档)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env python3
|
||||
"""把整个 EasyEDA 工程 dump 成转 KiCad 的输入:工程树 + 每页原理图源码 + 每块板的网表。
|
||||
|
||||
要点:
|
||||
- 工程树按**实时** getCurrentProjectInfo() 取,不用 eda-parts.json(那份只有 Board1 的 11 页)。
|
||||
- 网表是**每块板**一份,且取的是「当前打开文档所属的那块板」,所以每块板都要先 openDocument
|
||||
它自己的一页再导。
|
||||
- 页面源码 dump 前必须确认切页真的生效(skill §9l:桥接打的是当前文档)。
|
||||
|
||||
输出到 .claude/eda_src/:
|
||||
_tree.json 工程树(板 -> 原理图 -> 页 / PCB)
|
||||
<board>/<page>.esrc 每页源码(EasyEDA 行式 DSL)
|
||||
<board>/netlist.json|.net|.asc 该板网表(EasyEDA JSON / Protel2 / PADS)
|
||||
"""
|
||||
import json, os, sys
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
import eda
|
||||
|
||||
ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||
OUT = os.path.join(ROOT, "eda_src")
|
||||
|
||||
TREE_JS = '''
|
||||
const info = await eda.dmt_Project.getCurrentProjectInfo();
|
||||
const boards = [];
|
||||
for (const b of (info.data || [])) {
|
||||
if (b.itemType !== 'Board') continue;
|
||||
const sch = b.schematic || {};
|
||||
boards.push({
|
||||
board: b.name,
|
||||
schematic: sch.name || null,
|
||||
schematicUuid: sch.uuid || null,
|
||||
pages: (sch.page || []).map(p => ({ name: p.name, uuid: p.uuid })),
|
||||
pcb: b.pcb ? { name: b.pcb.name, uuid: b.pcb.uuid } : null,
|
||||
});
|
||||
}
|
||||
return { project: { uuid: info.uuid, name: info.name, friendly: info.friendlyName }, boards };
|
||||
'''
|
||||
|
||||
PAGE_JS = '''
|
||||
await eda.dmt_EditorControl.openDocument("%s");
|
||||
await new Promise(r=>setTimeout(r,1500));
|
||||
const info = await eda.sch_Document?.getCurrentSchematicPageInfo?.();
|
||||
return { uuid: info?.uuid ?? null, src: await eda.sys_FileManager.getDocumentSource() };
|
||||
'''
|
||||
|
||||
NET_JS = '''
|
||||
await eda.dmt_EditorControl.openDocument("%s");
|
||||
await new Promise(r=>setTimeout(r,1500));
|
||||
const out = {};
|
||||
for (const t of ["EasyEDA","Protel2","PADS"]) {
|
||||
const f = await eda.sch_ManufactureData.getNetlistFile("nl", t);
|
||||
out[t] = f ? await f.text() : null;
|
||||
}
|
||||
return out;
|
||||
'''
|
||||
EXT = {"EasyEDA": "json", "Protel2": "net", "PADS": "asc"}
|
||||
|
||||
|
||||
def call(js, what, timeout=300):
|
||||
r = eda.run(js, timeout=timeout)
|
||||
if not r.get("success"):
|
||||
sys.exit(f"!! {what} 失败: " + json.dumps(r, ensure_ascii=False)[:400])
|
||||
return r["result"]
|
||||
|
||||
|
||||
def main():
|
||||
os.makedirs(OUT, exist_ok=True)
|
||||
tree = call(TREE_JS, "取工程树")
|
||||
json.dump(tree, open(os.path.join(OUT, "_tree.json"), "w", encoding="utf-8"),
|
||||
ensure_ascii=False, indent=1)
|
||||
print(f"工程 {tree['project']['friendly']} {len(tree['boards'])} 块板\n")
|
||||
|
||||
total = 0
|
||||
for b in tree["boards"]:
|
||||
d = os.path.join(OUT, b["board"])
|
||||
os.makedirs(d, exist_ok=True)
|
||||
print(f"[{b['board']}] {b['schematic']} {len(b['pages'])} 页 -> {b['pcb']['name'] if b['pcb'] else '无 PCB'}")
|
||||
for p in b["pages"]:
|
||||
res = call(PAGE_JS % p["uuid"], f"{b['board']}/{p['name']} 源码")
|
||||
if res.get("uuid") and res["uuid"] != p["uuid"]:
|
||||
sys.exit(f"!! {p['name']} 切页没生效: 期望 {p['uuid']} 实得 {res['uuid']}")
|
||||
src = res["src"]
|
||||
open(os.path.join(d, p["name"] + ".esrc"), "w", encoding="utf-8", newline="").write(src)
|
||||
total += len(src)
|
||||
print(f" {p['name']:12s} {len(src):>8,d} B")
|
||||
# 网表:用本板任意一页把上下文切过去
|
||||
nl = call(NET_JS % b["pages"][0]["uuid"], f"{b['board']} 网表")
|
||||
for t, txt in nl.items():
|
||||
if txt is None:
|
||||
print(f" !! 网表 {t} 返回 null"); continue
|
||||
open(os.path.join(d, "netlist." + EXT[t]), "w", encoding="utf-8", newline="").write(txt)
|
||||
print(f" netlist.{EXT[t]:4s} {len(txt):>8,d} B")
|
||||
print()
|
||||
print(f"合计源码 {total:,d} B -> {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
在新工单中引用
屏蔽一个用户