文件
phonerail/.claude/k_extract.py
T

93 行
3.7 KiB
Python

#!/usr/bin/env python3
"""把每一页原理图的图元几何抓成 JSON,作为生成 .kicad_sch 的输入。
做法:对每一类 sch_Primitive* 调 getAll(),再把对象上所有 getState_* 扫一遍。
这样不依赖我对 API 的记忆,EasyEDA 加了字段也自动带上。
两个必须守住的点:
- 每次都自己 openDocument 并**回读确认切页生效**(skill §9l:桥接打的是当前文档)。
- 导线 getState_Line() 返回的是**端点对**,不是折线路径;这里原样存,解析放到生成端做
(skill:当折线解析会凭空造出斜线段,进而误判连接)。
输出 .claude/eda_src/<board>/<page>.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()