89 行
4.3 KiB
Python
89 行
4.3 KiB
Python
# -*- coding: utf-8 -*-
|
||
u"""D16:把一个网里孤立的小块接到本网**已有的铜**上(不必落在某只焊盘上)—— 服务器跑。
|
||
|
||
DCDC_ROUTE_SERVER=1 /root/pypy/bin/pypy3 .claude/r2/d16_tap.py NET [--extra 结果文件 ...] [--w 0.2]
|
||
|
||
route_nets 每块只挑一只焊盘当代表,大块挑中的那只脚出不来就整网失败(2026-09-12 SDA:U13.4 出不来)。
|
||
这里小块照旧用焊盘;大块改用本网已有线段上每 0.5 mm 取的点当虚拟焊盘,按离小块由近到远逐个试,接上即停。
|
||
--extra:本机还没下发的结果(如刚布好的 SCL)一并当障碍。写 _d16_tap.out.json。
|
||
"""
|
||
import io
|
||
import json
|
||
import math
|
||
import os
|
||
import sys
|
||
|
||
if os.environ.get('DCDC_ROUTE_SERVER') != '1':
|
||
sys.exit(u'!! 布线运算只在服务器上跑(DCDC_ROUTE_SERVER=1),见 CLAUDE.md')
|
||
if '--grid' not in sys.argv:
|
||
sys.argv += ['--grid', '0.125']
|
||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||
sys.path.insert(0, os.path.dirname(HERE))
|
||
sys.path.insert(0, HERE)
|
||
import route_nets as N # noqa: E402
|
||
|
||
R = N.R
|
||
TRIES = 30
|
||
|
||
|
||
def main():
|
||
skip = {k + 1 for k, a in enumerate(sys.argv) if a in ('--extra', '--w', '--grid')}
|
||
net = [a for k, a in enumerate(sys.argv) if k >= 1 and k not in skip and not a.startswith('--')][0]
|
||
w = float(sys.argv[sys.argv.index('--w') + 1]) if '--w' in sys.argv else 0.2
|
||
b = json.load(io.open(os.path.join(HERE, '_board_for_nets.json'), encoding='utf-8'))
|
||
segs = [tuple(s[:7]) for s in b['segs']]
|
||
vias = [tuple(v[:3]) for v in b['vias']]
|
||
for k, a in enumerate(sys.argv):
|
||
if a == '--extra':
|
||
e = json.load(io.open(os.path.join(HERE, sys.argv[k + 1]), encoding='utf-8'))
|
||
segs += [tuple(s[:7]) for s in e['segs']]
|
||
vias += [tuple(v[:3]) for v in e['vias']]
|
||
pads = [p for p in R.load_pads() if not str(p[1]).endswith(R.TH_SUFFIX)]
|
||
groups = sorted(N.components(net, pads, segs, vias), key=len)
|
||
print(u'%s:%d 块,小块 %s' % (net, len(groups), ['%s.%s' % (q[0], q[1]) for q in groups[0]]))
|
||
if len(groups) < 2:
|
||
return
|
||
sp = groups[0][0]
|
||
cand = []
|
||
for s in segs:
|
||
if s[6] != net or s[0] not in (1, 2):
|
||
continue
|
||
n = max(1, int(math.hypot(s[3] - s[1], s[4] - s[2]) / 0.5))
|
||
for k in range(n + 1):
|
||
x, y = s[1] + (s[3] - s[1]) * k / n, s[2] + (s[4] - s[2]) * k / n
|
||
cand.append((math.hypot(x - sp[2], y - sp[3]), s[0], round(x, 3), round(y, 3), s[5]))
|
||
# 本网的过孔也当落点(两个外层各一块 0.6 焊盘):大块有的地方只靠内层线连着(EasyEDA 自动布线留在 L2/L3 的 SDA),
|
||
# 外层上一根本网的线都没有,只有孔 —— 2026-09-12 SDA 第一版只取外层线段上的点,候选全在 45 mm 外、全不通
|
||
for v in vias:
|
||
if v[2] == net:
|
||
for l in (1, 2):
|
||
cand.append((math.hypot(v[0] - sp[2], v[1] - sp[3]), l, round(v[0], 3), round(v[1], 3), 0.6))
|
||
cand.sort()
|
||
picked = []
|
||
for c in cand:
|
||
if all(math.hypot(c[2] - p[2], c[3] - p[3]) > 1.0 or c[1] != p[1] for p in picked):
|
||
picked.append(c)
|
||
if len(picked) >= TRIES:
|
||
break
|
||
out = {'net': net, 'segs': [], 'vias': [], 'tries': [], 'logs': []}
|
||
for d, l, x, y, ww in picked:
|
||
vp = ['#tap', '1', x, y, max(ww, 0.2), max(ww, 0.2), l, net, 'RECT']
|
||
N.INNER[:] = [s for s in segs if s[0] in (15, 16)]
|
||
obst = [s for s in segs if s[0] in (1, 2)]
|
||
lg = []
|
||
s2, v2 = R.route_one(net, [sp, vp], w, pads + [vp], obst, vias, [], lg)
|
||
after = N.components(net, pads, segs + [tuple(q) for q in s2], vias + [tuple(q) for q in v2])
|
||
ok = len(after) < len(groups)
|
||
out['tries'].append([round(d, 2), l, x, y, ok, len(s2), len(v2)])
|
||
print(u' 试 L%s (%.2f,%.2f) 距 %.1f:%s' % (l, x, y, d, u'接上(%d 段 / %d 孔)' % (len(s2), len(v2)) if ok else u'不通'))
|
||
if ok:
|
||
out['segs'] = [list(q) for q in s2]
|
||
out['vias'] = [list(q) for q in v2]
|
||
out['logs'] = lg
|
||
break
|
||
io.open(os.path.join(HERE, '_d16_tap.out.json'), 'w', encoding='utf-8').write(json.dumps(out, ensure_ascii=False))
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|