布线(窗口整体重布,已下发): - 拆窗口内 43 段(用户手布的那批,用户选「拆掉重来」),补 139 段 / 7 孔, GND 回流孔 10 个;fan_check 对板上真实快照逐项净距达标(含「新孔 vs 内层线」)。 - ⛔ 根因:pcb_base.PD_L1 里的 L1 走廊是按**旧摆位**开的(注释坐标全是旧的), 用户重摆后与件对不上 ⇒ 四条长途网必然布不通。重开六条走廊后未完成 4 → 2。 走廊必须盖住焊盘正上方,只盖沿途不行。断面:几何 11.80 / 铺铜后实测 9.82(要 8.0)。 - 与拆线前逐网对比:没引入任何新的连通性问题(GND 未接 58→48、+3V3 2→0、 两路 TACH 2/1→0、+12V 一字不差)。 定版:板上铜以**用户自己布好的那版**为准,不再改动。 丝印(silk_base.py,已下发并读回核对 150/150): - 压焊盘 128→4、压过孔 48→8、越框 8→3、互相重叠 34 对→0;字号不变。 - 三条坑:rotation 是度且不归一化(900=180°);别拿「本体≥5mm」当保留白名单 (会豁免掉 R9A/D3/U13 这些该重排的件);「离本体」必须设硬上限 + 降级策略。 新工具:silk_base.py(丝印重排)、l1_section.py(L1 12V 主块逐 y 断面核算)、 fan_rip.py(按窗口拆线,拆前后比对全板线/孔总数防连带删除)。 Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_0168Bu6SEx3cXXCJLkdnE6zB
98 行
4.0 KiB
Python
98 行
4.0 KiB
Python
# -*- coding: utf-8 -*-
|
||
u"""按坐标**拆掉窗口内待布网的旧铜**(下发新布线之前的第一步)。
|
||
|
||
/c/Python314/python .claude/r2/fan_rip.py # 只列清单,不动板
|
||
/c/Python314/python .claude/r2/fan_rip.py --push # 真删
|
||
|
||
⛔ 2026-09-12 实测:**删外层段会连带清掉过孔和它在内层的延续** —— 点名删 6 段,读回发现线少 12 段、
|
||
孔少 8 个,多掉的全在 L15/L16。⇒ 拆前后都要比对**全板**线 / 孔总数,不能只数自己点名的那几段。
|
||
这里每次都先读回全板、删完再读回,逐项打印差值。
|
||
|
||
判据与 `repair_window` 同口径:`_base_window.json` 的窗口 + 网名,**两个端点都在窗口内**才算窗口内的铜
|
||
(跨窗边界的段是主干接口,必须留着)。
|
||
"""
|
||
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)
|
||
from eda import run # noqa: E402
|
||
import pcb_base as B # noqa: E402
|
||
|
||
MIL = 0.0254
|
||
CFG = json.load(io.open(os.path.join(HERE, '_base_window.json'), encoding='utf-8'))
|
||
WIN = CFG['window']
|
||
NETS = set(n.replace('@', '$') for n in CFG['nets'])
|
||
OPEN = ("await eda.dmt_EditorControl.openDocument('%s');"
|
||
"await new Promise(r=>setTimeout(r,2500));" % B.PCB)
|
||
READ = OPEN + """
|
||
const L=(await eda.pcb_PrimitiveLine.getAll()||[]).filter(l=>[1,2,15,16].includes(l.layer))
|
||
.map(l=>[l.primitiveId,l.net,l.layer,l.startX,l.startY,l.endX,l.endY,l.lineWidth]);
|
||
const V=(await eda.pcb_PrimitiveVia.getAll()||[]).map(v=>[v.primitiveId,v.net,v.x,v.y]);
|
||
return {L, V};
|
||
"""
|
||
|
||
|
||
def inw(x, y):
|
||
return WIN[0] <= x <= WIN[2] and WIN[1] <= y <= WIN[3]
|
||
|
||
|
||
def read():
|
||
r = run(READ, timeout=300)
|
||
assert r.get('success'), json.dumps(r, ensure_ascii=False)[:400]
|
||
return r['result']
|
||
|
||
|
||
def main():
|
||
d = read()
|
||
L, V = d['L'], d['V']
|
||
outer = [l for l in L if l[2] in (1, 2)]
|
||
inner = [l for l in L if l[2] in (15, 16)]
|
||
print(u'板上:外层 %d 段 / 内层 %d 段 / 过孔 %d' % (len(outer), len(inner), len(V)))
|
||
kill = []
|
||
for pid, net, lay, x1, y1, x2, y2, w in outer:
|
||
if net not in NETS:
|
||
continue
|
||
if inw(x1 * MIL, y1 * MIL) and inw(x2 * MIL, y2 * MIL):
|
||
kill.append((pid, net, lay, x1 * MIL, y1 * MIL, x2 * MIL, y2 * MIL))
|
||
killv = [(v[0], v[1]) for v in V if v[1] in NETS and inw(v[2] * MIL, v[3] * MIL)]
|
||
print(u'要拆:%d 段 / %d 孔' % (len(kill), len(killv)))
|
||
by = {}
|
||
for k in kill:
|
||
by[k[1]] = by.get(k[1], 0) + 1
|
||
for n in sorted(by):
|
||
print(u' %-18s %d 段' % (n, by[n]))
|
||
io.open(os.path.join(HERE, '_base_fan_ripped.json'), 'w', encoding='utf-8').write(
|
||
json.dumps({'segs': [[k[2], k[3], k[4], k[5], k[6], 0, k[1]] for k in kill],
|
||
'vias': []}, ensure_ascii=False))
|
||
if '--push' not in sys.argv:
|
||
print(u'(只列清单;加 --push 才真删)')
|
||
return
|
||
ids = [k[0] for k in kill] + [v[0] for v in killv]
|
||
r = run(OPEN + """
|
||
const ids = %s;
|
||
let n = 0;
|
||
for (const id of ids) { try { await eda.pcb_PrimitiveLine.delete(id); n++; } catch (e) {
|
||
try { await eda.pcb_PrimitiveVia.delete(id); n++; } catch (e2) {} } }
|
||
await eda.pcb_Document.save();
|
||
return n;
|
||
""" % json.dumps(ids), timeout=600)
|
||
print(u'删除返回:%s' % json.dumps(r.get('result'), ensure_ascii=False))
|
||
d2 = read()
|
||
o2 = [l for l in d2['L'] if l[2] in (1, 2)]
|
||
i2 = [l for l in d2['L'] if l[2] in (15, 16)]
|
||
print(u'拆后:外层 %d 段(−%d,点名 %d)/ 内层 %d 段(−%d)/ 过孔 %d(−%d)'
|
||
% (len(o2), len(outer) - len(o2), len(kill), len(i2), len(inner) - len(i2),
|
||
len(d2['V']), len(V) - len(d2['V'])))
|
||
if len(outer) - len(o2) != len(kill) or len(inner) != len(i2):
|
||
print(u'⛔ 连带删除!实际掉的和点名的对不上,检查内层 / 过孔')
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|