R2:底板 / 刀片布局布线脚本、快照、截图与规划文档更新(含外壳、打印件、布线归档)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -0,0 +1,112 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
u"""把 _d16_route.out.json(服务器算、d16_check 复核过)下发到底板:带网名建线 / 孔 → 读回逐条核对 → 存盘。
|
||||
|
||||
/c/Python314/python .claude/r2/d16_push.py # 只打印要下发多少
|
||||
/c/Python314/python .claude/r2/d16_push.py --push # 真下发
|
||||
|
||||
读回按「同层同网同宽、两端 + 中点都落在读回线段上」认(EasyEDA 会把共线段合并 / 拆开,按 ID 对不上)。
|
||||
缺一条都不存盘,打印缺的清单。
|
||||
"""
|
||||
import io
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.stdout.reconfigure(encoding='utf-8')
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, os.path.dirname(HERE))
|
||||
from eda import run # noqa: E402
|
||||
|
||||
U = json.load(io.open(os.path.join(HERE, 'r2-uuids.json'), encoding='utf-8'))
|
||||
PCB = U['boards']['BASE']['pcb']['uuid']
|
||||
MIL = 39.3700787401575
|
||||
VIA_D, VIA_HOLE = 0.6, 0.3
|
||||
|
||||
|
||||
def call(js, timeout=300):
|
||||
r = run(js, timeout=timeout)
|
||||
if not r.get('success'):
|
||||
sys.exit(u'失败: ' + json.dumps(r, ensure_ascii=False)[:600])
|
||||
return r['result']
|
||||
|
||||
|
||||
def pd(px, py, s):
|
||||
x1, y1, x2, y2 = s
|
||||
dx, dy = x2 - x1, y2 - y1
|
||||
L = dx * dx + dy * dy
|
||||
t = 0 if L == 0 else max(0, min(1, ((px - x1) * dx + (py - y1) * dy) / L))
|
||||
return math.hypot(px - (x1 + t * dx), py - (y1 + t * dy))
|
||||
|
||||
|
||||
def main():
|
||||
arg = [a for a in sys.argv[1:] if not a.startswith('--')]
|
||||
res = json.load(io.open(os.path.join(HERE, arg[0] if arg else '_d16_route.out.json'), encoding='utf-8'))
|
||||
S = [list(s[:7]) for s in res['segs'] if math.hypot(s[3] - s[1], s[4] - s[2]) > 1e-4]
|
||||
V = [list(v[:3]) for v in res['vias']]
|
||||
RS = [list(s[:7]) for s in res.get('ripped_segs', [])]
|
||||
RV = [list(v[:3]) for v in res.get('ripped_vias', [])]
|
||||
print(u'要下发:线 %d 段 / 孔 %d 个;先拆旧铜 %d 段 / %d 孔' % (len(S), len(V), len(RS), len(RV)))
|
||||
if '--push' not in sys.argv:
|
||||
return
|
||||
if RS or RV:
|
||||
r0 = call(u"""
|
||||
await eda.dmt_EditorControl.openDocument('%s'); await new Promise(r=>setTimeout(r,2000));
|
||||
const M = 0.0254, TL = %s, TV = %s, E = 0.012;
|
||||
const near = (a, b) => Math.abs(a - b) < E;
|
||||
const L = (await eda.pcb_PrimitiveLine.getAll()) || [], V = (await eda.pcb_PrimitiveVia.getAll()) || [];
|
||||
const kl = [], kv = [], miss = [];
|
||||
for (const t of TL) {
|
||||
const hit = L.filter(l => l.layer === t[0] && (l.net || '') === t[6] && near(l.lineWidth * M, t[5]) &&
|
||||
((near(l.startX*M, t[1]) && near(l.startY*M, t[2]) && near(l.endX*M, t[3]) && near(l.endY*M, t[4])) ||
|
||||
(near(l.startX*M, t[3]) && near(l.startY*M, t[4]) && near(l.endX*M, t[1]) && near(l.endY*M, t[2]))));
|
||||
if (hit.length === 1) kl.push(hit[0].primitiveId); else miss.push(['L', t, hit.length]);
|
||||
}
|
||||
for (const t of TV) {
|
||||
const hit = V.filter(v => (v.net || '') === t[2] && near(v.x*M, t[0]) && near(v.y*M, t[1]));
|
||||
if (hit.length === 1) kv.push(hit[0].primitiveId); else miss.push(['V', t, hit.length]);
|
||||
}
|
||||
if (miss.length) return {stop: true, miss: miss.slice(0, 8)};
|
||||
for (const id of kl) await eda.pcb_PrimitiveLine.delete(id);
|
||||
for (const id of kv) await eda.pcb_PrimitiveVia.delete(id);
|
||||
await new Promise(r=>setTimeout(r,800));
|
||||
const L1 = (await eda.pcb_PrimitiveLine.getAll()) || [], V1 = (await eda.pcb_PrimitiveVia.getAll()) || [];
|
||||
return {dl: kl.length, dv: kv.length, left: L1.filter(l => kl.includes(l.primitiveId)).length + V1.filter(v => kv.includes(v.primitiveId)).length};
|
||||
""" % (PCB, json.dumps(RS), json.dumps(RV)), timeout=300)
|
||||
print(u'拆旧铜:%s' % r0)
|
||||
if r0.get('stop') or r0.get('left'):
|
||||
sys.exit(u'!! 拆除清单在板上没对上 / 没删干净,停(没存盘)')
|
||||
r = call(u"""
|
||||
await eda.dmt_EditorControl.openDocument('%s'); await new Promise(r=>setTimeout(r,2000));
|
||||
const MIL = %s, S = %s, V = %s, D = %s, H = %s;
|
||||
let ls = 0, vs = 0; const err = [];
|
||||
for (const s of S) { try { const o = await eda.pcb_PrimitiveLine.create(s[6], s[0], s[1]*MIL, s[2]*MIL, s[3]*MIL, s[4]*MIL, s[5]*MIL);
|
||||
if (o) ls++; else err.push('L ' + JSON.stringify(s)); } catch (e) { err.push('L ' + e); } }
|
||||
for (const v of V) { try { const o = await eda.pcb_PrimitiveVia.create(v[2], v[0]*MIL, v[1]*MIL, H*MIL, D*MIL);
|
||||
if (o) vs++; else err.push('V ' + JSON.stringify(v)); } catch (e) { err.push('V ' + e); } }
|
||||
return {ls, vs, err: err.slice(0, 8)};
|
||||
""" % (PCB, MIL, json.dumps(S), json.dumps(V), VIA_D, VIA_HOLE), timeout=900)
|
||||
print(u'下发:线 %d / 孔 %d;失败 %s' % (r['ls'], r['vs'], r['err']))
|
||||
back = call(u"""
|
||||
const M = 0.0254;
|
||||
return {L: (await eda.pcb_PrimitiveLine.getAll()||[]).filter(l=>[1,2,15,16].includes(l.layer))
|
||||
.map(l=>[l.layer, l.startX*M, l.startY*M, l.endX*M, l.endY*M, l.lineWidth*M, l.net||'']),
|
||||
V: (await eda.pcb_PrimitiveVia.getAll()||[]).map(v=>[v.x*M, v.y*M, v.net||''])};""")
|
||||
miss_s = []
|
||||
for s in S:
|
||||
cand = [t for t in back['L'] if t[0] == s[0] and t[6] == s[6] and abs(t[5] - s[5]) < 0.01]
|
||||
pts = ((s[1], s[2]), (s[3], s[4]), ((s[1] + s[3]) / 2, (s[2] + s[4]) / 2))
|
||||
if not all(any(pd(x, y, t[1:5]) < 0.01 for t in cand) for x, y in pts):
|
||||
miss_s.append(s)
|
||||
miss_v = [v for v in V if not any(w[2] == v[2] and math.hypot(w[0] - v[0], w[1] - v[1]) < 0.01 for w in back['V'])]
|
||||
print(u'读回:缺线 %d / 缺孔 %d' % (len(miss_s), len(miss_v)))
|
||||
for x in (miss_s + miss_v)[:10]:
|
||||
print(u' ', x)
|
||||
if miss_s or miss_v or r['err']:
|
||||
sys.exit(u'!! 读回不全,没存盘')
|
||||
call(u"return await eda.pcb_Document.save();")
|
||||
print(u'已存盘。接着:reload_base.py → pour_base.py --push → board_check.py --base → drc_read.py --base')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
在新工单中引用
屏蔽一个用户