English | 简体中文
本文记录嘉立创 EDA 专业版在 API 自动化场景下的一批行为特征,这些内容官方参考文档里没有。 建议动手之前先扫一遍标题。
先约定几个词,后面反复出现:
- 桥(Bridge):官方
easyeda-api-skill里那个 Node 服务,把 HTTP 请求转成 WebSocket 发给嘉立创客户端执行。 - 作业(job):一次通过桥发过去执行的 JS 代码块。一次 HTTP 请求 = 一个作业。
- 图元(primitive):板上的线、过孔、焊盘、铺铜这些可编程对象,每个有一个
primitiveId。 - 假成功:API 返回成功、当场读回也是新值,但重启软件后发现根本没落盘。这个词在下面出现很多次。
嘉立创的 API 有一类行为极其危险:改属性的调用返回成功,当场读回也是新值,重启软件后却是旧值。 也就是说这个改动只进了内存视图,没进文档。
已经确认属于这一类的:
| 调用 | 表现 | 正确做法 |
|---|---|---|
pcb_PrimitiveVia.modify(id, {diameter, holeDiameter}) |
改孔径/盘径返回成功、当场读回是新值,重启后还原 | 删除重建:delete([id]) 再 create(net, x, y, 孔径, 盘径) |
pcb_PrimitivePour.modify(id, {lineWidth}) |
改铺铜轮廓宽,返回体显示新值,读回不落地;单字段回写、整对象回写都无效 | 目前只能在软件界面里手工改 |
pcb_Drc.overwriteCurrentRuleConfiguration(整个包装对象) |
静默失败,不报错也不生效 | 只传 cfg.config 这个子对象,并且检查返回值是不是 true |
由此得到本文档的第一原则:
"当场读回是新值"不算验证。改了关键属性,必须重启嘉立创(带工程路径参数启动)之后再读一遍,才算数。
注意这条对人和对自动化脚本同样成立:脚本拿到的"读回是新值"也不构成落盘证据。
这是整条链路里最容易让人以为"API 坏了"的一段。
1. 器件必须带 addIntoPcb=true,否则同步过去是 0 个元件。
sch_PrimitiveComponent.create 的完整签名是
create(component, x, y, subPartName, rotation, mirror, addIntoBom, addIntoPcb),
最后两个参数漏传,器件就只存在于原理图、进不了 PCB。
已经画错的板可以补救:逐器件 sch_PrimitiveComponent.modify(id, {addIntoPcb: true, addIntoBom: true}),然后 sch_Document.save()。
2. pcb_Document.importChanges(schUuid) 会弹一个确认对话框,按钮名叫「应用修改」,不叫「确定」。
API 返回 true 只表示"我把对话框叫出来了",元件还没落板。不点这个按钮,什么都不会发生。
这个按钮在同一个框里还有个邻居叫「取消」,别点错。
3. 那个对话框的渲染可能要好几分钟。
不是几秒,是分钟级。所以任何 15 秒 / 40 秒的看门狗都会误判成"对话框没出现",
进而得出"API 无效"的错误结论。
正确做法是持续轮询扫描按钮,等待上限给到 300 秒。sync_pcb_via_importchanges.py 默认就是这个值。
4. 连续重复触发会把上一个对话框冲掉。 调试时手快连点几次,看起来像"这次不弹框了",其实是自己把框冲没了。慢一点,一次一个。
5. 触发之后不要再 openDocument + activateDocument。
元件正在往板上放的过程中重新激活文档,会把放置打断,元件停在中途。
要看进度就用裸的 pcb_PrimitiveComponent.getAll() 数个数,别碰文档焦点。
6. 点按钮用 Windows UIAutomation,中文参数不要跨进程传。
Python 用 subprocess 往 PowerShell 传中文参数会乱码。
click_eda_confirm.ps1 的做法是在脚本内部用 Unicode 码点拼出「应用修改」四个字
([char]0x5E94, [char]0x7528, [char]0x4FEE, [char]0x6539),彻底绕开所有编码层。
不知道按钮叫什么名字时,先跑 list_eda_buttons.ps1 把当前所有按钮名打出来看。
1. DRC 不检查连通性。这是实证结论:故意把一条网断开,DRC 照样报 0 违例。
嘉立创的 DRC 只管间距、孔径这类几何/工艺规则。
所以"DRC 全绿"绝不等于"板子连对了"。连通必须独立校验——自己建端点图去算,或者用 netcmp_live.py 逐脚比对。
2. 连通引擎只认精确共享端点。 线身交叠不算连接,T 形穿越不算连接,端点"几乎重合"也不算连接。 必须是两段线的端点坐标完全相等。 推论:写坐标时不能四舍五入到两位小数——0.005 的误差就是断路,而且 DRC 不会告诉你。
3. 共线同网的相邻段会在创建时被自动合并。 你想在一条干线中间留个接头,于是把它拆成两段来画——软件会把它们粘回一条长线, 接头就落到了线身上(见第 2 条:线身不算连接)。 破解办法是走"之字形":让接头处的过孔在干线中心线上下各偏 4 mil 交替, 相邻段斜率不同就不会被合并,而每个接头都恰好是某一段的端点。 经 API 布长干线时,这是目前已知稳定可用的做法。
1. 读到的 primitiveId 出了当前作业就可能失效。
如果你在作业 A 里读出一批 id,在作业 B 里拿这些 id 去 modify,
作业 B 开头的 openDocument 会让这批 id 全部作废。
症状是抛 t.isAsync is not a function,或者批量操作跑到一半突然死掉——
死掉那一刻之前的改动已经落板了,于是你得到一个被撕成两半的网络。
铁律:读 id 和用 id 必须在同一个作业里完成。
2. 单个作业有 30 秒左右的执行上限。 所以批量操作要自己分批,经验值:属性 modify 每批 40 个左右,几何 modify 每批 150 个左右。
3. 传 JS 一律用文件,不要用命令行字符串。
eda_bridge.py run job.js 而不是 eda_bridge.py exec "..."。
PowerShell 会把引号和 Ω 之类的字符改得面目全非。
4. 桥服务会死,但不用重启嘉立创。
npm run server 重新拉起后,嘉立创侧的扩展大约 20 秒会自动重连。
5. 桥必须监听双栈 ::。
嘉立创把 localhost 解析成 IPv6 的 ::1,只监听 IPv4 会连不上。
1. DRC 有"幻影违例"。
有些违例项在 closeDocument 再重新打开之后就消失了,它们从一开始就不存在。
在你决定"这块板设计失败要重做"之前,先关文档再开,复核一遍。
幻影违例足以把一块没问题的板误判成需要推倒重来。
2. 改过几何之后,DRC 会读陈旧缓存。 尤其是用 API 挪过孔之后。要拿真值,必须重启嘉立创(用带工程路径参数的方式启动), 不是关文档重开,是整个软件重启。
3. 间距规则按铜厚分成不同的表。 板子层数变化时可能切换到另一张生效表,校准规则时每张表都要写。
4. 规则表的单位按板而异。
同一个工程里,新建的板 Safe Spacing 条目单位可能是 mm,另一块老板可能是 mil。
写规则之前必须先读 entry.unit 做换算,别假定。
5. 铺铜灌注体(Copper Region / Filled)不要当成违例目标去修。 它是铺铜的产物,重灌一次就消失了。把它列进"待修图元"名单,会导致挪线挪孔来回震荡。
6. 重灌铺铜没有可靠的 API,只能发按键。
repour_safe.py 的做法是:先经桥把目标板激活,再用 Win32 校验嘉立创确实是前台窗口,
确认了才发 Shift+B。前台校验失败就直接中止——绝不能把按键发进别人的窗口。
另外,被抢了前台导致中止的那一轮是没灌成的,必须重试。
没灌成的铺铜会凭空产生大量 "Copper Region" 违例,重灌一次即全部消失——
所以拿到异常暴涨的违例数时,先确认铺铜灌成了没有。
违例数很大的时候,按这个顺序打,性价比最高:
- 先重灌铺铜再看数——很多违例是铺铜没灌导致的假象。
neck_analyze.py出台账——按网络、按网络对统计,先看清楚是哪几条网在闹。gap_nudge.py微挪——不改拓扑只改几何,最安全。原则是"不恶化":挪之前查背后有没有空间,没有就分摊或跳过。width_cut.py降宽——如果那些粗线本来就是画板时留的余量(载流早就够了), 把违例涉及的粗线统一降到目标线宽。前提是先确认那部分宽度确实是余量而非载流需要。neck_sink.py下沉内层——走廊实在塞满了,把干线的拥堵段挪到内层去走,表层腾出净空。这一步会动拓扑,做之前先备份。fix_sink.py收拾落点——下沉时打的过孔可能落在离板框太近或离别的孔太近的地方,这个脚本负责重定位。
然后是一条经验判断,不是技术条款:脚本在窄缝区(过孔堵车、密集束)的边际收益是负的, 继续加轮次往往不如直接交互式处理。可行的分工是——脚本跑批量重建与校验,人收口难处理的区域。
还有一条判断纪律,值得单独说: 判定"这块板要重做"之前,必须先做三件事:逐路电流核算、去掉幻影违例后复核、 把问题分清是"电气问题 / 制造性问题 / 规则问题"三类中的哪一类。 制造性问题(比如线宽低于厂家工艺下限、孔径太小)的解法是逐点微调,不是推倒重来。
pcb_PrimitivePolyline.create(net, layer, polygon, lineWidth, primitiveLock)—— net 在第一位(画板框传空字符串"")。polygon必须用工厂函数造:eda.pcb_MathPolygon.createPolygon(["R", x, y, w, h, 0, 0]), 直接传数组或裸对象都会报"参数不正确"。 矩形的 y 语义是顶边,竖直方向覆盖[y-h, y]。pcb_PrimitiveVia.delete只接受数组:delete([id]);而pcb_PrimitiveLine.delete接受字符串。pcb_PrimitiveVia.create(net, x, y, 孔径, 盘径)—— 参数顺序容易记反。- 枚举值不要猜。文档里写枚举的地方就用枚举成员,比如层参数写
EPCB_LayerId.TOP而不是写1。 说明一句:本仓库几个几何脚本(neck_sink.py、fix_sink.py)在批量拼 JS 时传的是裸层号(顶层 1、内层 15/16), 那是为了把大批操作压进一个作业里少写字符,不是推荐写法。你新写代码请用枚举。 - 有些枚举(例如单位枚举)在桥的 eval 上下文里取不到,这时改传字符串
"mm"/"mil"是有效的。
1. 建新板用 eda.dmt_Board.createBoard(),不传参。
它会建一块完整的板(自带原理图 + 图页 + PCB),返回板名如 "Board2"。
注意这个方法在 dmt_Board 命名空间下,不在 dmt_Schematic 下。
流程是:
createBoard() → dmt_Board.getBoardInfo(boardName) 取 schematic.uuid 和 schematic.page[0].uuid
→ dmt_EditorControl.openDocument(pageUuid) 拿 tabId → activateDocument(tabId)。
必须激活之后那个图页才是"活动图页",此时 modifySchematicName(schUuid, 名字) 才会生效。
2. 不要用 createSchematic()。
不传参会建出一张游离的原理图,之后 getAllSchematicPagesInfo() / getSchematicInfo() 会开始报
reading 'Symbol'——整个工程的原理图查询被污染了。
传一个名字进去也不对,它会把这个名字当板名用,结果 undefined。
3. deleteBoard 只删容器,不级联删原理图。
要彻底删干净得再调 deleteSchematic(schUuid)。
4. 导入前先数一遍,别假定新板是空的。
新建的板里有一个默认占位器件,getAll().length 是 1 不是 0。
5. 同一张原理图的不同图页,网表是全局共享的(同名网会合并)。 所以想要网表互相独立的板,必须是独立的 board,不能只是不同图页。
- Python 打印中文/
✓✗会崩。 控制台是 GBK,遇到编不了的字符抛UnicodeEncodeError,脚本直接死。 所有脚本开头都要sys.stdout.reconfigure(encoding="utf-8", errors="replace")。 这个坑的危险之处在于伪装性:操作本身成功了,脚本却在打印结果那一行崩掉, 看起来和"操作失败"完全一样,很容易把排查引向错误方向。 - 读 PowerShell 的输出要显式指定编码。
subprocess.run(..., text=True)在中文 Windows 上默认按 GBK 解, 遇到「应用修改」里的非 GBK 字节就崩。必须encoding="utf-8", errors="replace"。 - 嘉立创导出的所谓 "csv" 其实是 UTF-16LE + TAB 分隔。
按 utf-8 + 逗号去解会得到一堆垃圾。而且文件内容过桥时会被 stdout 的编码层折腾,
所以要在 JS 侧转 base64 再传回来(见
export_mfg.py)。
- 网表重建扩展**只认
props["Supplier Part"](立创 C 料号)**去查库。device_name、封装名都不参与匹配。没有料号就是导入失败,报 "no component placed"。 - 料号错了没法事后补救:
modify改不了器件的库关联(属性表里根本没有 component/symbol/footprint 这些字段)。 只能改 JSON 重新导入。软件界面上的"器件标准化"也救不了——它只会把器件标准化成那个错料号对应的标准件。 - 料号会下架。库里查不到(
getByLcscIds和搜索都空)时,按 MPN 去找现行料号替换。 - 网表重建导入之后,器件的 SupplierId 可能被设成 MPN 而不是 C 料号,会触发 DRC 的"供应商不符"。
用
fix_supplier_ids.py批量刷回来。 - 器件标准化没有程序化 API,只有界面面板。DRC 也不能逐条取,只能取汇总。
- 想核物料的权威信息,用
lib_Device.getByLcscIds(["C..."]): 返回的device.otherProperty里有 Supplier Part / Manufacturer / Manufacturer Part,顶层有 manufacturer / supplier。 这比你自己维护的 BOM 可信。
这些不是嘉立创的行为特征,而是驱动它的脚本本身容易出问题的地方。
- 不可逆动作不交给守护进程。 脚本可以观察、可以打标记,删除/覆盖/发按键这类动作要么人来点,要么多重独立确认。
- 空集合不能恒真。 "所有目标都完成了"这个判断,在目标集合为空时必须判成"异常"而不是"完成"。
- 改完必须自己看一眼。
render_at.py就是干这个的——截一张图,用眼睛确认,别只信 API 返回值。 - 动拓扑之前先备份。
neck_sink.py每动一个网络之前先把该网络的原始几何存成 JSON,失败可以整网恢复。 - 每一步都留可回归的校验。 逐脚网表比对(
netcmp_live.py/compare_netlist.py) 应该在每一轮几何操作之后都跑一遍——这是判断"改动有没有把网表改坏"的唯一可靠手段。