文件
phonerail/.claude/pcb2_strap_gnd.py

131 行
5.6 KiB
Python

# -*- coding: utf-8 -*-
u"""R900~R909 的地端扇出:每颗一条短线 + 一个过孔落到 Inner2 地平面。
/c/Python314/python .claude/pcb2_strap_gnd.py # 只规划
/c/Python314/python .claude/pcb2_strap_gnd.py --push # 下发 + 存盘
为什么不跑 `pcb2_fanout.py`:那个是**整板**扇出,文件头写明「布局一动就要
`--clean` → 重新 dump → `--push`」三步一起 —— 会把板上 415 个扇出孔全拆了重打,
而现在只是多了十颗 0402。照 `pcb2_keypads.py --gnd` 的先例做局部扇出(那次是 5 个 GND 盘),
净距判据直接复用它的 `_clear` / `_seg_clear`,和整板扇出是同一套。
⚠️ 元件在**底层**,所以短线画在 layer 2(keypads 那次的盘在顶层,画的是 layer 1)。
"""
import json
import math
import os
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
from eda import run
import pcb2_route_dp as D
import pcb2_keypads as K
PCB = '942ed1560d81c1e7'
MIL = 39.3700787401575
LAY = 2 # 底层
W_STUB = K.W_STUB
VIA_D, VIA_H = K.VIA_D, K.VIA_H
REFS = ['R900', 'R901', 'R902', 'R903', 'R904', 'R905', 'R906', 'R907', 'R908', 'R909']
def call(js, timeout=300):
r = run("await eda.dmt_EditorControl.openDocument('%s');"
"await new Promise(r=>setTimeout(r,2000));" % PCB + js, timeout=timeout)
if not r.get('success'):
sys.exit(u'桥接失败: ' + json.dumps(r, ensure_ascii=False)[:600])
return r['result']
def gnd_pads():
u"""读回这十颗电阻的 GND 焊盘中心(mm)。"""
return call("""
const name = {};
for (const c of await eda.pcb_PrimitiveComponent.getAll()) name[c.getState_PrimitiveId()] = c.getState_Designator();
const ids = Object.keys(name).sort((a,b)=>b.length-a.length);
const M = %f, out = {};
for (const p of await eda.pcb_PrimitivePad.getAll()) {
const pid = p.primitiveId || '';
let hit = null;
for (const id of ids) if (pid.length > id.length && pid.indexOf(id) === 0) { hit = name[id]; break; }
if (!hit || !/^R9[0-9][0-9]$/.test(hit)) continue;
if (p.getState_Net() !== 'GND') continue;
out[hit] = [Math.round(p.getState_X()/M*1000)/1000, Math.round(p.getState_Y()/M*1000)/1000];
}
return out;""" % MIL)
def plan(pads, vias, lines, gp):
out, bad = {}, []
for ref in REFS:
x, y = gp[ref]
# ⚠️ 第一版只试 5 个方向 x 5 个距离,四颗找不到位置。载板这一片太密,
# 候选集必须放开:每 15 度一个方向、0.9~3.5 mm 每 0.1 一档,由近到远。
cands = []
for dist in [d / 10.0 for d in range(9, 36)]:
for a in range(0, 360, 15):
th = math.radians(a)
cands.append((round(x + math.cos(th) * dist, 3), round(y + math.sin(th) * dist, 3)))
chosen, why = None, []
placed = [v['via'] for v in out.values()]
for vx, vy in cands:
# 本轮新打的过孔之间也要保钻孔间距(skill §9v)
if any(math.hypot(vx - ax, vy - ay) < 0.33 + VIA_H for ax, ay in placed):
continue
ok, msg = K._clear(vx, vy, VIA_D / 2, 12, pads, vias, lines, ref)
if not ok:
why.append(u'(%.2f,%.2f) 孔撞 %s' % (vx, vy, msg)); continue
ok, msg = K._seg_clear((x, y), (vx, vy), W_STUB, pads, vias, lines, ref)
if not ok:
why.append(u'(%.2f,%.2f) 线撞 %s' % (vx, vy, msg)); continue
chosen = (vx, vy)
break
if chosen is None:
bad.append((ref, why[:6]))
continue
out[ref] = {'pad': (x, y), 'via': chosen}
print(u' %-5s 盘 (%7.3f,%6.3f) -> 过孔 (%7.3f,%6.3f) 距 %.2f mm'
% (ref, x, y, chosen[0], chosen[1], math.hypot(chosen[0] - x, chosen[1] - y)))
# 新打的过孔彼此也要判钻孔间距(skill §9v 补的那一条)
ks = list(out)
for i in range(len(ks)):
for j in range(i + 1, len(ks)):
a, b = out[ks[i]]['via'], out[ks[j]]['via']
if math.hypot(a[0] - b[0], a[1] - b[1]) < 0.33 + VIA_H:
bad.append((ks[i] + '/' + ks[j], [u'两个新过孔钻孔间距不足']))
if bad:
for ref, why in bad:
print(u'!! %s 找不到扇出位置:' % ref)
for w in why:
print(u' ' + w)
sys.exit(1)
return out
def main():
D.dump()
pads, vias, lines = D.geom()
gp = gnd_pads()
missing = [r for r in REFS if r not in gp]
if missing:
sys.exit(u'板上找不到这些 GND 焊盘: %s' % missing)
p = plan(pads, vias, lines, gp)
print(u'十条地端扇出全部找到位置(净距判据同整板扇出)')
if '--push' not in sys.argv:
print(u'加 --push 下发')
return
segs = [['GND', LAY, v['pad'][0], v['pad'][1], v['via'][0], v['via'][1], W_STUB] for v in p.values()]
vs = [['GND', v['via'][0], v['via'][1]] for v in p.values()]
r = call(u"""
const M = %f, SEGS = %s, VIAS = %s;
let nl = 0, nv = 0;
for (const [net, lay, x0, y0, x1, y1, w] of SEGS) { await eda.pcb_PrimitiveLine.create(net, lay, x0*M, y0*M, x1*M, y1*M, w*M); nl++; }
for (const [net, x, y] of VIAS) { await eda.pcb_PrimitiveVia.create(net, x*M, y*M, %f*M, %f*M); nv++; }
await eda.pcb_Document.save();
return {nl, nv};""" % (MIL, json.dumps(segs), json.dumps(vs), VIA_H, VIA_D))
print(u'已下发:%d 条短线 / %d 个过孔' % (r['nl'], r['nv']))
main()