#!/usr/bin/env python3 """把整个 EasyEDA 工程 dump 成转 KiCad 的输入:工程树 + 每页原理图源码 + 每块板的网表。 要点: - 工程树按**实时** getCurrentProjectInfo() 取,不用 eda-parts.json(那份只有 Board1 的 11 页)。 - 网表是**每块板**一份,且取的是「当前打开文档所属的那块板」,所以每块板都要先 openDocument 它自己的一页再导。 - 页面源码 dump 前必须确认切页真的生效(skill §9l:桥接打的是当前文档)。 输出到 .claude/eda_src/: _tree.json 工程树(板 -> 原理图 -> 页 / PCB) /.esrc 每页源码(EasyEDA 行式 DSL) /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()