#!/usr/bin/env python3 """把每一页原理图的图元几何抓成 JSON,作为生成 .kicad_sch 的输入。 做法:对每一类 sch_Primitive* 调 getAll(),再把对象上所有 getState_* 扫一遍。 这样不依赖我对 API 的记忆,EasyEDA 加了字段也自动带上。 两个必须守住的点: - 每次都自己 openDocument 并**回读确认切页生效**(skill §9l:桥接打的是当前文档)。 - 导线 getState_Line() 返回的是**端点对**,不是折线路径;这里原样存,解析放到生成端做 (skill:当折线解析会凭空造出斜线段,进而误判连接)。 输出 .claude/eda_src//.geom.json """ 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") JS = r''' const PAGE = "%s"; await eda.dmt_EditorControl.openDocument(PAGE); await new Promise(r=>setTimeout(r,1500)); const info = await eda.sch_Document?.getCurrentSchematicPageInfo?.(); if (info && info.uuid && info.uuid !== PAGE) return { __switchFailed: info.uuid }; function getters(o){ const s=new Set(); let p=o; while(p && p!==Object.prototype){ Object.getOwnPropertyNames(p).forEach(n=>{ if(n.startsWith('getState_')) s.add(n); }); p=Object.getPrototypeOf(p); } return [...s]; } function sweep(o){ const r={}; for(const g of getters(o)){ try{ const v=o[g](); r[g.slice(9)] = (v===undefined)?null:JSON.parse(JSON.stringify(v)); } catch(e){ r[g.slice(9)] = {__err:String(e.message||e)}; } } return r; } const CLASSES = ["Component","Wire","Bus","Text","Line","Rectangle","Circle","Arc","Polygon","Attribute","Pin"]; const out = { page: PAGE, pageInfo: info?JSON.parse(JSON.stringify(info)):null, prims: {} }; for (const cls of CLASSES){ const api = eda["sch_Primitive"+cls]; if (!api || typeof api.getAll !== "function") { out.prims[cls] = null; continue; } let objs; try{ objs = await api.getAll(); }catch(e){ out.prims[cls] = {__err:String(e.message||e)}; continue; } const arr = []; for (const o of (objs||[])){ const rec = sweep(o); if (cls === "Component" && typeof o.getAllPins === "function"){ let pins=null; try{ pins = await o.getAllPins(); }catch(e){} rec.__pins = (pins||[]).map(sweep); } if (cls === "Wire" && typeof o.getState_Line === "function"){ try{ rec.__line = JSON.parse(JSON.stringify(o.getState_Line())); }catch(e){} } arr.push(rec); } out.prims[cls] = arr; } return out; ''' def main(): tree = json.load(open(os.path.join(OUT, "_tree.json"), encoding="utf-8")) only = sys.argv[1] if len(sys.argv) > 1 else None grand = 0 for b in tree["boards"]: d = os.path.join(OUT, b["board"]) for p in b["pages"]: if only and only not in (p["name"], b["board"]): continue r = eda.run(JS % p["uuid"], timeout=300) if not r.get("success"): sys.exit(f"!! {b['board']}/{p['name']} 提取失败: " + json.dumps(r, ensure_ascii=False)[:400]) res = r["result"] if res.get("__switchFailed"): sys.exit(f"!! {p['name']} 切页没生效,实得 {res['__switchFailed']}") path = os.path.join(d, p["name"] + ".geom.json") json.dump(res, open(path, "w", encoding="utf-8"), ensure_ascii=False) n = {k: (len(v) if isinstance(v, list) else v) for k, v in res["prims"].items()} sz = os.path.getsize(path); grand += sz keep = {k: v for k, v in n.items() if isinstance(v, int) and v} print(f" {b['board']}/{p['name']:12s} {sz:>8,d} B " + " ".join(f"{k}={v}" for k, v in keep.items())) print(f"\n合计 {grand:,d} B") if __name__ == "__main__": main()