R2:底板 / 刀片布局布线脚本、快照、截图与规划文档更新(含外壳、打印件、布线归档)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
zhou yang
2026-10-04 19:24:45 +08:00
共同撰写人 Claude Opus 5.5
父节点 cd01640c40
当前提交 cf7cc9cf90
共修改 759 个文件,包含 76962 行新增和 5299 行删除
+112
查看文件
@@ -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()