# -*- coding: utf-8 -*- u"""在现有铜之上**只重布几根绕远的慢速信号**(2026-09-15,BLADE-PCB_1 新摆位)。 # 本机:先把要重布的网从缓存里拆掉(保留 ESC 逃逸孔 / 金手指引出),写成 _blade_routes.json # 服务器(搜索只在服务器跑): DCDC_ROUTE_SERVER=1 pypy3 .claude/r2/opt_blade1.py <网,网,...> [<网,网,...> ...] # 每个参数 = 一种布线顺序,结果写 _blade_routes.opt.json(合并后的完整缓存) 和 `repair_route.py` 的区别: · 起止点用 **ESC 逃逸孔**(router.ESC_VIAS)当虚拟焊盘,不从被围住的原焊盘起步(repair 在 U3/U4 上一直「没布通」); · 线宽强制 0.15(规则默认值;repair 按 rule_width 走出了 0.30,同样的走廊少一半通道)。 """ 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)) sys.path.insert(0, HERE) if not os.environ.get('DCDC_ROUTE_SERVER'): sys.exit(u'⛔ 搜索只在 10.5.5.13 上跑(CLAUDE.md)') import router as RT # noqa: E402 import pcb_rules as R # noqa: E402 W = 0.15 def main(): base = json.load(io.open(os.path.join(HERE, '_blade_routes.json'), encoding='utf-8')) pads = RT.load_pads() auto = R.resolve_auto() _fs, _fv, launch = RT.finger_escape() esc = {k: v for k, v in RT.ESC_VIAS.items()} vp = os.path.join(HERE, '_opt_virt.json') VIRT = json.load(io.open(vp, encoding='utf-8')) if os.path.exists(vp) else {} for k_, order in enumerate(sys.argv[1:]): nets = [n.replace('@', '$') for n in order.split(',') if n] segs = [tuple(s) for s in base['segs']] vias = [tuple(v) for v in base['vias']] board_vias = [(v[0], v[1], v[2], RT.VIA_D) for v in vias] # U2 的 QFN 预扇出(被拆掉了就补回来):扇出段落铜,焊盘换成扇出端点的虚拟焊盘 qs, qv, qvirt = RT.qfn_fan(pads) have = set(tuple(round(c, 3) for c in s[1:5]) for s in segs) qmap = {} for sg in qs: if sg[6] in nets and tuple(round(c, 3) for c in sg[1:5]) not in have: segs.append(tuple(sg)) for (x, y, n) in qv: if n in nets: vias.append((x, y, n)) for p, (ex, ey), lay in qvirt: qmap[(p[0], p[1])] = (ex, ey, lay) # 手算过的出口(在 BLADE-PCB_1 定稿缓存上核过净距):U2.2 扇出端点 → R15.1;R40.1 西出打孔 # 2026-09-15 晚:上拉电阻 R13/R15/R40/R41 挪到 U4 东南之后,原来的 R15.1 / R40.1 / R41.1 手算出口作废 FIX = {} fixdrop, fixvirt = set(), {} for net, (fs_, fv_, fd_, fvp) in FIX.items(): if net in nets: segs += [tuple(x) for x in fs_] vias += [tuple(x) for x in fv_] fixdrop |= set(fd_) if fvp: fixvirt[net] = fvp _es, _ev, _evirt, edrop = RT.esc_fan(pads) have = set(tuple(round(c, 3) for c in sg[1:5]) for sg in segs) for sg in _es: # ESC_PATHS 的折线(被拆掉了就补回来) if sg[6] in nets and tuple(round(c, 3) for c in sg[1:5]) not in have: segs.append(tuple(sg)) for p, (ex, ey), lay in _evirt: if (p[0], p[1]) in RT.ESC_PATHS: qmap[(p[0], p[1])] = (ex, ey, lay) dropk = set((p[0], p[1]) for p in edrop) | fixdrop bypad = {} for net in nets: ps = [] for p in pads: if p[7] != net or p[0] == 'J1': continue if (p[0], p[1]) in dropk: continue if (p[0], p[1]) in qmap: ex, ey, lay = qmap[(p[0], p[1])] ps.append([p[0], 'qfan:%s' % p[1], ex, ey, 0.3, 0.3, lay, net, 'RECT']) continue e = esc.get((p[0], p[1])) if e: lay = RT.LAY[1] if p[6] == RT.LAY[0] else RT.LAY[0] ps.append([p[0], 'qfan:%s' % p[1], e[0], e[1], 0.3, 0.3, lay, net, 'RECT']) else: ps.append(p) for (lay, x, y) in launch.get(net, []): ps.append(['J1', 'launch', x, y, 0.3, 0.3, lay, net, 'RECT']) if net in fixvirt: ps.append(fixvirt[net]) # `_opt_virt.json`(可选):{网: {"drop": [[位号, 脚], ...], "add": [[x, y, 层], ...]}} # drop = 已由手定短线接好的焊盘;add = 那些短线的端点,当虚拟焊盘 if net in VIRT: dk = set(tuple(x) for x in VIRT[net].get('drop', [])) ps = [q for q in ps if (q[0], str(q[1]).split(':')[-1]) not in dk] ps += [['VIRT', 'v%d' % n_, x, y, 0.3, 0.3, l, net, 'RECT'] for n_, (x, y, l) in enumerate(VIRT[net].get('add', []))] bypad[net] = ps RT.rule_width = lambda net, a, _n=set(nets): W if net in _n else RT.W_DEFAULT RT.rule_min = lambda net, a: W n0, v0 = len(segs), len(vias) # 逐网调用 route_groups:顺序真正按参数走(一次传全部时它自己按「出口数 / 跨度」重排,顺序参数不起作用) logs = [] for net in nets: logs += RT.route_groups({net: bypad[net]}, pads, segs, vias, board_vias, auto, priority=(net,), only={net}) board_vias = [(v[0], v[1], v[2], RT.VIA_D) for v in vias] L = {} for s in segs[n0:]: L[s[6]] = L.get(s[6], 0.0) + math.hypot(s[3] - s[1], s[4] - s[2]) print(u'[%d] %s:新增 %d 段 / %d 孔,总长 %.1f,长度 %s,未通 %s' % (k_, order, len(segs) - n0, len(vias) - v0, sum(L.values()), ' '.join('%s=%.1f' % kv for kv in sorted(L.items())), logs)) json.dump({'segs': [list(s) for s in segs], 'vias': [list(v) for v in vias], 'logs': logs}, io.open(os.path.join(HERE, '_blade_routes.opt%d.json' % k_), 'w', encoding='utf-8'), ensure_ascii=False) if __name__ == '__main__': main()