113 行
5.7 KiB
Python
113 行
5.7 KiB
Python
# -*- coding: utf-8 -*-
|
||
u"""**底板**的定点补线器:在板上现有铜之上只布指定的几个网,别的一根线都不动。
|
||
|
||
# ⛔ A* 只在 10.5.5.13 上跑(CLAUDE.md)。入口:
|
||
bash .claude/r2/remote_repair.sh FAN_12V_SW FAN_IN_TACH_RAW FAN_OUT_TACH_RAW FAN_PWM_L
|
||
# 服务器上实际执行的是:
|
||
DCDC_ROUTE_SERVER=1 pypy3 .claude/r2/repair_base.py <网名...>
|
||
|
||
`repair_route.py` 是**刀片**那块板的同名工具(硬编码 `_blade_*`),底板用不了:
|
||
板级常量、障碍(12V 大铜 / 天线净空 / NPTH / 通孔焊盘副本)全在 `route_base` 里。
|
||
这里的做法是**直接 import route_base** —— 它模块顶层就把 `router` 配成底板了
|
||
(`RT.B / PCB / PADS / CLR / WIDTH / build_grid / pen_pads` …),
|
||
而它的 `__main__` 有保护、顶层没有任何动板调用,import 安全。
|
||
|
||
## 现有铜从哪来
|
||
|
||
`_base_fan_input.json`(本机 `remote_repair.sh` 抓板生成,只含 L1/L4 的铜):
|
||
|
||
{"segs": [[layer, x1, y1, x2, y2, w, net], ...], # 板上现有导线
|
||
"vias": [[x, y, net], ...], # 板上现有过孔
|
||
"board_vias": [[x, y, net, d], ...]} # 同上,另作固定障碍
|
||
|
||
⛔ **不读 `_base_routes.json`**。那是布线器自己的缓存,和板上现状能差出几十段
|
||
(用户在客户端手工改过线、内层还手布了 126 段)。补线必须以**板上**为准。
|
||
|
||
## 内层(L15 / L16)的线不在这里
|
||
|
||
`router` 只布 L1/L4,内层是地平面。但板上内层有用户手布的线,**新打的过孔会穿过它们**,
|
||
而这一层关系 router 不查 ⇒ 跑完必须在本机用 `remote_repair.sh` 的回读一步核
|
||
「新孔 vs 内层线」的净距(脚本里已经做了)。
|
||
"""
|
||
import io
|
||
import json
|
||
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'⛔ 补线(A*)只在 10.5.5.13 上跑(CLAUDE.md):bash .claude/r2/remote_repair.sh <网名...>')
|
||
|
||
import router as RT # noqa: E402
|
||
import route_base as RB # noqa: E402
|
||
|
||
if os.environ.get('FAN_W'):
|
||
_w = float(os.environ['FAN_W'])
|
||
for _n in ('FAN_12V_SW', 'FAN_IN_TACH_RAW', 'FAN_OUT_TACH_RAW', 'FAN_PWM_L'):
|
||
RT.WIDTH[_n] = _w
|
||
_orig_rule_min = RT.rule_min
|
||
RT.rule_min = lambda net, auto: (_w if net.startswith('FAN_') else _orig_rule_min(net, auto))
|
||
print(u'线宽覆盖:FAN_* = %.2f mm' % _w)
|
||
|
||
# ⚠ 自动网名里的 `$` 写成 `@` 传进来(同 `repair_route.py`):网名经 ssh 命令串传到远端,
|
||
# 远端 shell 会把 `$6N45` 当变量展开成空串 —— 不转义的话补的是一个叫「」的网,还不报错。
|
||
NETS = [a.replace('@', '$') for a in sys.argv[1:] if not a.startswith('--')] or \
|
||
['FAN_12V_SW', 'FAN_IN_TACH_RAW', 'FAN_OUT_TACH_RAW', 'FAN_PWM_L']
|
||
# `FAN_W=0.15`:把这几个网的线宽压到规则下限再布。0402 的两只脚净距只有 0.30,
|
||
# 默认宽度下 A* 在 0.25 网格上找不到出口格点 —— 报「没布通」,看着像没地方走,其实是出不来。
|
||
# 测速 / PWM 是小信号,0.15 足够;`FAN_12V_SW` 载的是一只风扇的电流(≈0.2 A),也够。
|
||
_FW = os.environ.get('FAN_W')
|
||
if _FW:
|
||
for _n in NETS:
|
||
RT_W = float(_FW)
|
||
globals().setdefault('_W_OVERRIDE', {})[_n] = RT_W
|
||
SRC = os.path.join(HERE, '_base_fan_input.json')
|
||
OUT = os.path.join(HERE, '_base_fan_repair.json')
|
||
|
||
cp = json.load(io.open(SRC, encoding='utf-8'))
|
||
segs = [tuple(s) for s in cp['segs']]
|
||
vias = [tuple(v) for v in cp['vias']]
|
||
board_vias = [tuple(v) for v in cp['board_vias']]
|
||
pads = RB.load_pads()
|
||
|
||
# 参与布线的焊盘:去掉通孔盘的 L1 障碍副本(`route_base.TH_SUFFIX`)和平面网
|
||
bypad = {}
|
||
for p in pads:
|
||
if not p[7] or p[7] not in NETS or str(p[1]).endswith(RB.TH_SUFFIX):
|
||
continue
|
||
bypad.setdefault(p[7], []).append(p)
|
||
missing = [n for n in NETS if n not in bypad]
|
||
if missing:
|
||
sys.exit(u'⛔ 这些网在 _base_pads.json 里一只脚都没有(焊盘缓存过期?): %s' % ' '.join(missing))
|
||
for n in NETS:
|
||
print(u' %-18s %d 只脚: %s' % (n, len(bypad[n]),
|
||
' '.join('%s.%s' % (q[0], q[1]) for q in bypad[n])))
|
||
|
||
print(u'现有铜 %d 段 / %d 孔(固定障碍孔 %d);要补 %s' % (len(segs), len(vias), len(board_vias), ' '.join(NETS)))
|
||
n0, v0 = len(segs), len(vias)
|
||
RT.AUTO_CACHE.clear()
|
||
logs = RT.route_groups(bypad, pads, segs, vias, board_vias, {},
|
||
priority=tuple(NETS), only=set(NETS))
|
||
# `--repair`:还有没布通的就拆线重布。⛔ 只拆**本次这几个网自己**刚布的线
|
||
# (`fixed_s/fixed_v` 把板上原有的铜全部钉死)—— 板上别的铜是用户手工调过的,一根都不许动。
|
||
if logs and '--repair' in sys.argv:
|
||
print(u' 还剩 %d 条没布通,进入拆线重布(只拆本次新布的)' % len(logs))
|
||
segs, vias, logs = RT.repair(bypad, pads, segs, vias, logs, board_vias, {}, n0, v0)
|
||
new_s, new_v = [list(s) for s in segs[n0:]], [list(v) for v in vias[v0:]]
|
||
print(u'新增 %d 段 / %d 孔' % (len(new_s), len(new_v)))
|
||
for m in logs:
|
||
print(u' [!] ' + m)
|
||
|
||
chk = [q for q in RB.check_pads(pads) if q[7] in set(NETS)]
|
||
left = RT.check_connected(chk, segs, vias)
|
||
print(u'连通性(布线器口径):%s' % (u'这几个网全通' if not left else left))
|
||
|
||
json.dump({'nets': NETS, 'segs': new_s, 'vias': new_v,
|
||
'logs': list(logs), 'unconnected': list(left)},
|
||
io.open(OUT, 'w', encoding='utf-8'), ensure_ascii=False)
|
||
print(u'已写 %s' % os.path.basename(OUT))
|