- 规则:铜层 2→4,差分 0.28/0.18,间距 0.15,过孔 0.3/0.2(rules_base) - 几何:98×98 板框 + 4×M3,槽阵居中,L1 12V 大铜 + 指头(pcb_base) - 摆位:五插槽留 L1,其余 133 件翻到 L4;背面镜像规则实测;Hub 放走廊 2、 东移 2.5 mm;扇出圈 / 小件间距 1.5(去耦例外)/ 9 A 过孔阵禁区(place_base) - 叠层按用户决定改为 L1 槽面 / L2 整层 +3V3 / L3 整层 GND / L4 器件面 - 布线:D± 七对按构造布通(USB3 掉头、USB1/2 北去掉头、USB4/5 东绕、 J23 桥),+3V3 / GND / +12V 平面网每脚一孔,0.125 网格 + PathFinder 开关(route_base、remote_route.ps1);剩 37 个网待手工补 - 铺铜:4 层重写,入口链自南向北切段 + 断面校核(pour_base); 已下发布线 / 缝合孔 / 铜面轮廓,填铜未成功(待查) - 工具:plot_base / plot_routes / shot_base,board_check 通孔两层都算 Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_0168Bu6SEx3cXXCJLkdnE6zB
105 行
4.4 KiB
Python
105 行
4.4 KiB
Python
# -*- coding: utf-8 -*-
|
||
u"""离线画底板布线缓存(L4 / L1 并排,从上往下看),失败的脚标红叉。
|
||
|
||
/c/Python314/python .claude/r2/plot_routes.py <out.png>
|
||
/c/Python314/python .claude/r2/plot_routes.py <out.png> --zoom 30 25 65 50 # 只看一块(mm)
|
||
|
||
只读 `_base_routes.json` + `_base_pads.json`,不碰 EasyEDA、不跑 A*。
|
||
"""
|
||
import io
|
||
import json
|
||
import os
|
||
import re
|
||
import sys
|
||
|
||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||
sys.path.insert(0, os.path.dirname(HERE))
|
||
sys.path.insert(0, HERE)
|
||
import matplotlib # noqa: E402
|
||
matplotlib.use('Agg')
|
||
import matplotlib.pyplot as plt # noqa: E402
|
||
from matplotlib.patches import Rectangle, Polygon, Circle # noqa: E402
|
||
import pcb_base as B # noqa: E402
|
||
|
||
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'DejaVu Sans']
|
||
plt.rcParams['axes.unicode_minus'] = False
|
||
|
||
POWER = ('VIN_RAW', 'VIN_FUSED', 'VIN_SW', '+5V', '+3V3', '+3V3_HUB', 'FAN_12V_SW')
|
||
|
||
|
||
def color(net):
|
||
if net == 'GND':
|
||
return '#2e9e44'
|
||
if net == '+12V':
|
||
return '#d62728'
|
||
if net.endswith('_DP') or net.endswith('_DM') or net in ('UP_DP', 'UP_DM'):
|
||
return '#8e44ad'
|
||
if net in POWER:
|
||
return '#ff8c00'
|
||
return '#1f5fbf'
|
||
|
||
|
||
def main():
|
||
out = sys.argv[1]
|
||
zoom = None
|
||
if '--zoom' in sys.argv:
|
||
k = sys.argv.index('--zoom')
|
||
zoom = [float(v) for v in sys.argv[k + 1:k + 5]]
|
||
c = json.load(io.open(os.path.join(HERE, '_base_routes.json'), encoding='utf-8'))
|
||
pads = json.load(io.open(os.path.join(HERE, '_base_pads.json'), encoding='utf-8'))
|
||
pads = [p for p in pads if p[4] >= 0.05 and p[5] >= 0.05]
|
||
fail = set()
|
||
for m in c['logs']:
|
||
for ref, pin in re.findall(r'([A-Z]+[0-9]+[a-z]*)\.([A-Za-z0-9#]+)', m):
|
||
fail.add(('J1' if ref == 'J1dc' else ref, pin))
|
||
fig, axes = plt.subplots(1, 2, figsize=(19, 9.8), dpi=150)
|
||
for ax, lay, title in ((axes[0], 2, u'L4 器件面'), (axes[1], 1, u'L1 插槽面')):
|
||
ax.add_patch(Polygon(B.outline_pts(), closed=True, fill=False, lw=1.0, ec='black'))
|
||
if lay == 1:
|
||
for r in B.l1_12v_rects():
|
||
ax.add_patch(Rectangle((r[0], r[1]), r[2] - r[0], r[3] - r[1],
|
||
fc='#f7c6c6', ec='none', alpha=0.6))
|
||
for p in pads:
|
||
if p[6] not in (lay, 12):
|
||
continue
|
||
ax.add_patch(Rectangle((p[2] - p[4] / 2, p[3] - p[5] / 2), p[4], p[5],
|
||
fc='#bbbbbb' if p[6] == lay else '#dddddd', ec='none'))
|
||
for s in c['segs']:
|
||
if s[0] != lay:
|
||
continue
|
||
lw = max(0.3, s[5] * (3.0 if zoom else 1.2))
|
||
ax.plot([s[1], s[3]], [s[2], s[4]], color=color(s[6]), lw=lw, solid_capstyle='round')
|
||
for v in c['vias']:
|
||
ax.add_patch(Circle((v[0], v[1]), 0.3, fc=color(v[2]), ec='black', lw=0.2))
|
||
for p in pads:
|
||
if (p[0], str(p[1])) in fail and p[6] in (lay, 12):
|
||
ax.plot(p[2], p[3], 'x', color='red', ms=6 if zoom else 3, mew=1.2)
|
||
for _n, (hx, hy) in B.MOUNT_HOLES.items():
|
||
ax.add_patch(Circle((hx, hy), B.HOLE_D / 2, fc='white', ec='black'))
|
||
if zoom:
|
||
ax.set_xlim(zoom[0], zoom[2])
|
||
ax.set_ylim(zoom[1], zoom[3])
|
||
# 放大时标出焊盘的「位号.脚」,失败的那几只一眼能认
|
||
for p in pads:
|
||
if p[6] in (lay, 12) and zoom[0] <= p[2] <= zoom[2] and zoom[1] <= p[3] <= zoom[3]:
|
||
if p[0].startswith('J3') and p[7] in ('GND', ''):
|
||
continue
|
||
ax.text(p[2], p[3], '%s.%s' % (p[0], p[1]), fontsize=3.2, ha='center',
|
||
va='center', color='red' if (p[0], str(p[1])) in fail else '#333333')
|
||
else:
|
||
ax.set_xlim(-1, B.BW + 1)
|
||
ax.set_ylim(-1, B.BH + 1)
|
||
ax.set_aspect('equal')
|
||
ax.set_title(title, fontsize=10)
|
||
ax.tick_params(labelsize=6)
|
||
ax.grid(True, lw=0.2, alpha=0.4)
|
||
fig.suptitle(u'底板布线缓存:%d 段 / %d 孔 / %d 条未完成 紫 = D± 红 = +12V 绿 = GND 橙 = 电源 蓝 = 信号 红叉 = 失败的脚'
|
||
% (len(c['segs']), len(c['vias']), len(c['logs'])), fontsize=10)
|
||
fig.tight_layout()
|
||
fig.savefig(out)
|
||
print('->', out)
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|