# -*- coding: utf-8 -*- u"""服务器上一次做完「剩 3 只被围死的脚」(2026-09-11 混合布线收尾,用户选「拆邻线重排」): DCDC_ROUTE_SERVER=1 /root/pypy/bin/pypy3 .claude/r2/fix3.py [--fence-y1 35.0] 读 _board_for_nets.json(板上快照)+ _base_preroute_0911.json(UP 两端预布段 / 孔),写 _fix3.out.json 1. UP 中心线重布(up_route.route_centerline,pitch 0.52),L1 上 J37 北排上方加禁区 (48, 33.0)~(62, FENCE_Y1): UP 在这一段北移,给 J37.19 的 +5V 留出沿北排焊盘顶上走到 J37.32 的带子。障碍 = 板上除 UP 以外的全部铜。 2. 板面数据里的 UP 换成新 UP(+ 预布两端段 / 孔)。 3. 拆 U32 南 / 西南区的 strap 网(全是自动布线的短线)。 4. 按序补:+5V(J37.19)→ +3V3_HUB(C744.1 那块)→ CFGSEL0 → 拆掉的 strap 网(route_nets 的连通块 + 代表焊盘 + route_one)。 输出:新 UP、各网新增线 / 孔、被拆的网、每个网布前 / 布后的连通块数。本机拿去离线复核后再删旧加新。 """ 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_base as R # noqa: E402 import up_route as UPR # noqa: E402 import route_nets as RN # noqa: E402 RT = R.RT UP = ('UP_DP', 'UP_DM') RIP = ['$4N596', '$4N597', '$4N598', '$4N602', '$4N603', 'CFGSEL1', 'NONREM0', 'NONREM1', 'VBUS_DET'] ORDER = [('+5V', 0.3), ('+3V3_HUB', 0.2), ('CFGSEL0', 0.15)] + [(n, 0.15) for n in RIP] FENCE_Y1 = float(sys.argv[sys.argv.index('--fence-y1') + 1]) if '--fence-y1' in sys.argv else 35.0 L = lambda s: math.hypot(s[3] - s[1], s[4] - s[2]) # noqa: E731 def main(): board = json.load(io.open(os.path.join(HERE, '_board_for_nets.json'), encoding='utf-8')) pre = json.load(io.open(os.path.join(HERE, '_base_preroute_0911.json'), encoding='utf-8')) pads = R.load_pads() logs = [] seg_all = [tuple(s[:7]) for s in board['segs']] via_all = [tuple(v[:3]) for v in board['vias']] if '--no-up' in sys.argv: # UP 不动(整板障碍下中心线找不到通道,2026-09-11 两种禁区都失败):只拆 strap 网、按序补 # --rip A,B,... 只拆这些网(默认 RIP 全拆);--order A,B,... 补网顺序(默认 +3V3_HUB → CFGSEL0 → 拆掉的网) rip = sys.argv[sys.argv.index('--rip') + 1].split(',') if '--rip' in sys.argv else list(RIP) wid = dict(ORDER) if '--order' in sys.argv: order = [(n, wid.get(n, 0.15)) for n in sys.argv[sys.argv.index('--order') + 1].split(',')] else: order = [('+3V3_HUB', 0.2), ('CFGSEL0', 0.15)] + [(n, 0.15) for n in rip] segs = [s for s in seg_all if s[6] not in rip] vias = [v for v in via_all if v[2] not in rip] print(u'拆:%s;顺序:%s' % (' '.join(rip), ' '.join(n for n, _w in order))) out = {} for net, w in order: groups = RN.components(net, pads, segs, vias) if len(groups) < 2: print(u'%-9s 已连通,跳过' % net) continue lg = [] s, v = R.route_one(net, RN.reps(groups), w, pads, [x for x in segs if x[0] in (1, 2)], vias, [], lg) segs += [tuple(x) for x in s] vias += [tuple(x) for x in v] after = len(RN.components(net, pads, segs, vias)) out[net] = {'segs': [list(x) for x in s], 'vias': [list(x) for x in v], 'before': len(groups), 'after': after} print(u'%-9s %d 块 → %d 块;新增 %d 段 %.1f mm / %d 孔(线宽 %.2f)%s' % (net, len(groups), after, len(s), sum(L(x) for x in s), len(v), w, (u';' + ';'.join(lg)) if lg else '')) bad = [n for n, o in out.items() if o['after'] > 1] print(u'仍断开:%s' % (' '.join(bad) or u'无')) io.open(os.path.join(HERE, '_fix3.out.json'), 'w', encoding='utf-8').write(json.dumps( {'ok': True, 'up_new': None, 'rip': rip, 'order': [n for n, _w in order], 'nets': out, 'bad': bad}, ensure_ascii=False)) return # ── 1. UP 中心线重布(障碍 = 板上除 UP 以外的铜) obs_s = [s for s in seg_all if s[0] in (1, 2) and s[6] not in UP] obs_v = [v for v in via_all if v[2] not in UP] UPR.PITCH = 0.52 UPR.EXTRA_FENCE = [(1, (48.0, 33.0, 62.0, FENCE_Y1))] pad_pen, layer_dir = RT.PAD_PEN, RT.LAYER_DIR RT.PAD_PEN, RT.LAYER_DIR = R.DP_PAD_PEN, {} try: up_new, _ = UPR.route_centerline(pads, obs_s, obs_v, pre, logs) finally: RT.PAD_PEN, RT.LAYER_DIR = pad_pen, layer_dir if not up_new: print(u'UP 中心线重布失败:%s' % logs) io.open(os.path.join(HERE, '_fix3.out.json'), 'w', encoding='utf-8').write(json.dumps({'ok': False, 'logs': logs})) return worst, pair = UPR.check(up_new, obs_s, obs_v, pads) lens = {n: sum(L(s) for s in up_new if s[6] == n) + sum(L(s) for s in pre['segs'] if s[6] == n) for n in UP} print(u'UP 重布:DP %.1f / DM %.1f mm(差 %.2f),对异网最小 %.3f(%s),对内 %.3f;禁区 y 33.0~%.2f' % (lens['UP_DP'], lens['UP_DM'], abs(lens['UP_DP'] - lens['UP_DM']), worst[0], worst[1], pair, FENCE_Y1)) # ── 2. 板面换成新 UP;3. 拆 strap 网 up_stub_s = [tuple(s[:7]) for s in pre['segs'] if s[6] in UP] up_stub_v = [tuple(v[:3]) for v in pre['vias'] if v[2] in UP] segs = [s for s in seg_all if s[6] not in UP and s[6] not in RIP] + up_stub_s + [tuple(s) for s in up_new] vias = [v for v in via_all if v[2] not in UP and v[2] not in RIP] + up_stub_v # ── 4. 按序补 out, before = {}, {} for net, w in ORDER: groups = RN.components(net, pads, segs, vias) before[net] = len(groups) if len(groups) < 2: print(u'%-9s 已连通,跳过' % net) continue ps = RN.reps(groups) lg = [] s, v = R.route_one(net, ps, w, pads, [x for x in segs if x[0] in (1, 2)], vias, [], lg) segs += [tuple(x) for x in s] vias += [tuple(x) for x in v] after = len(RN.components(net, pads, segs, vias)) out[net] = {'segs': [list(x) for x in s], 'vias': [list(x) for x in v], 'before': len(groups), 'after': after} print(u'%-9s %d 块 → %d 块;新增 %d 段 %.1f mm / %d 孔(线宽 %.2f)%s' % (net, len(groups), after, len(s), sum(L(x) for x in s), len(v), w, (u';' + ';'.join(lg)) if lg else '')) logs += lg bad = [n for n, o in out.items() if o['after'] > 1] print(u'仍断开:%s' % (' '.join(bad) or u'无')) io.open(os.path.join(HERE, '_fix3.out.json'), 'w', encoding='utf-8').write(json.dumps( {'ok': True, 'up_new': [list(s) for s in up_new], 'rip': RIP, 'nets': out, 'before': before, 'bad': bad, 'logs': logs, 'fence_y1': FENCE_Y1, 'up_worst': worst, 'up_pair': pair}, ensure_ascii=False)) if __name__ == '__main__': main()