119 行
5.0 KiB
Python
119 行
5.0 KiB
Python
# -*- 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()
|