R2:底板 / 刀片布局布线脚本、快照、截图与规划文档更新(含外壳、打印件、布线归档)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -0,0 +1,118 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
u"""补线结果的本机校验:按真实几何自算净距,**下发前必跑**。
|
||||
|
||||
/c/Python314/python .claude/r2/fan_check.py # 读 _base_fan_repair.json + 板上铜快照
|
||||
/c/Python314/python .claude/r2/fan_check.py --push # 过了就下发(导线 + 过孔)
|
||||
|
||||
判三样(`router_check.py` 只判前两样,第三样是这块板特有的):
|
||||
|
||||
1. 新导线 ↔ 板上异网铜(L1/L4 的线、过孔、焊盘):净距 ≥ `MIN_CLR`
|
||||
2. 新过孔 ↔ 板上异网铜 / 异网孔(孔到孔按钻孔间距 `DRILL_CLR`)
|
||||
3. ⛔ **新过孔 ↔ 内层(L15/L16)上的线**:过孔是通孔、穿过内层,而布线器只认 L1/L4,
|
||||
这一层关系它**根本不查**。底板的内层名义上是整层地、不布线,但板上实测有 125 段
|
||||
用户手布的内层线(HUB_RST / +3V3_HUB / IO0 / INT# …)⇒ 不查就是往人家线上扎孔。
|
||||
"""
|
||||
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)
|
||||
import fp_edge # noqa: E402
|
||||
|
||||
MIL = fp_edge.MIL
|
||||
MIN_CLR = 0.15 # 规则底线(rules_base.MIN_CLEARANCE)
|
||||
DRILL_CLR = 0.25 # 钻孔到钻孔
|
||||
VIA_D, VIA_HOLE = 0.6, 0.3
|
||||
REPAIR = os.path.join(HERE, '_base_fan_repair.json')
|
||||
INPUT = os.path.join(HERE, '_base_fan_input.json')
|
||||
INNER = os.environ.get('DCDC_INNER_JSON') or os.path.join(HERE, '_base_fan_inner.json')
|
||||
PADS = os.path.join(HERE, '_base_pads.json')
|
||||
|
||||
|
||||
def seg_d(px, py, ax, ay, bx, by):
|
||||
dx, dy = bx - ax, by - ay
|
||||
L2 = dx * dx + dy * dy
|
||||
t = 0.0 if L2 < 1e-12 else max(0.0, min(1.0, ((px - ax) * dx + (py - ay) * dy) / L2))
|
||||
return math.hypot(px - (ax + t * dx), py - (ay + t * dy))
|
||||
|
||||
|
||||
def ss_d(a, b):
|
||||
return min(seg_d(a[0], a[1], *b), seg_d(a[2], a[3], *b),
|
||||
seg_d(b[0], b[1], *a), seg_d(b[2], b[3], *a))
|
||||
|
||||
|
||||
def main():
|
||||
rep = json.load(io.open(REPAIR, encoding='utf-8'))
|
||||
cu = json.load(io.open(INPUT, encoding='utf-8'))
|
||||
pads = json.load(io.open(PADS, encoding='utf-8'))
|
||||
inner = json.load(io.open(INNER, encoding='utf-8')) if os.path.exists(INNER) else []
|
||||
new_s, new_v = rep['segs'], rep['vias']
|
||||
print(u'补线结果:%d 段 / %d 孔;未完成 %d 条;未连通 %d 条'
|
||||
% (len(new_s), len(new_v), len(rep.get('logs') or []), len(rep.get('unconnected') or [])))
|
||||
for m in (rep.get('logs') or []):
|
||||
print(u' [!] ' + m)
|
||||
|
||||
bad = []
|
||||
for s in new_s:
|
||||
lay, x1, y1, x2, y2, w, net = s
|
||||
for t in cu['segs']:
|
||||
if t[6] == net or t[0] != lay:
|
||||
continue
|
||||
d = ss_d((x1, y1, x2, y2), (t[1], t[2], t[3], t[4])) - w / 2 - t[5] / 2
|
||||
if d < MIN_CLR:
|
||||
bad.append(u'新线 %s@L%s 距 %s 的线 %.3f' % (net, lay, t[6] or '?', d))
|
||||
for v in cu['vias']:
|
||||
if v[2] == net:
|
||||
continue
|
||||
d = seg_d(v[0], v[1], x1, y1, x2, y2) - w / 2 - VIA_D / 2
|
||||
if d < MIN_CLR:
|
||||
bad.append(u'新线 %s 距 %s 的过孔 %.3f' % (net, v[2] or '?', d))
|
||||
for p in pads:
|
||||
if p[7] == net or p[6] not in (lay, 12):
|
||||
continue
|
||||
d = seg_d(p[2], p[3], x1, y1, x2, y2) - w / 2 - max(p[4], p[5]) / 2
|
||||
if d < MIN_CLR:
|
||||
bad.append(u'新线 %s 距焊盘 %s.%s(%s) %.3f' % (net, p[0], p[1], p[7] or '-', d))
|
||||
|
||||
for v in new_v:
|
||||
vx, vy, net = v[0], v[1], v[2]
|
||||
for t in cu['segs']:
|
||||
if t[6] == net:
|
||||
continue
|
||||
d = seg_d(vx, vy, t[1], t[2], t[3], t[4]) - VIA_D / 2 - t[5] / 2
|
||||
if d < MIN_CLR:
|
||||
bad.append(u'新孔 %s 距 %s@L%s 的线 %.3f' % (net, t[6] or '?', t[0], d))
|
||||
for t in inner: # ⛔ 布线器不查的那一项
|
||||
if t[6] == net:
|
||||
continue
|
||||
d = seg_d(vx, vy, t[1], t[2], t[3], t[4]) - VIA_D / 2 - t[5] / 2
|
||||
if d < MIN_CLR:
|
||||
bad.append(u'新孔 %s 扎在**内层** L%s 的 %s 线上(净距 %.3f)' % (net, t[0], t[6] or '?', d))
|
||||
for o in cu['vias']:
|
||||
if o[2] == net:
|
||||
continue
|
||||
d = math.hypot(vx - o[0], vy - o[1]) - VIA_HOLE
|
||||
if d < DRILL_CLR:
|
||||
bad.append(u'新孔 %s 距 %s 的孔(钻孔间距)%.3f' % (net, o[2] or '?', d))
|
||||
|
||||
if bad:
|
||||
print(u'[X] 净距不过 %d 条:' % len(bad))
|
||||
for t in bad[:25]:
|
||||
print(u' ' + t)
|
||||
sys.exit(1)
|
||||
print(u'[OK] 新铜与板上现有铜逐项净距达标(线 ≥ %.2f / 钻孔 ≥ %.2f / 内层线已查)' % (MIN_CLR, DRILL_CLR))
|
||||
|
||||
if '--push' in sys.argv:
|
||||
import router as RT
|
||||
import route_base as RB # noqa: F401 顶层把 router 配成底板
|
||||
RT.push([tuple(s) for s in new_s], [tuple(v) for v in new_v])
|
||||
print(u'已下发 %d 段 / %d 孔' % (len(new_s), len(new_v)))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
在新工单中引用
屏蔽一个用户