R2:底板 / 刀片布局布线脚本、快照、截图与规划文档更新(含外壳、打印件、布线归档)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
zhou yang
2026-10-04 19:24:45 +08:00
共同撰写人 Claude Opus 5.5
父节点 cd01640c40
当前提交 cf7cc9cf90
共修改 759 个文件,包含 76962 行新增和 5299 行删除
+89
查看文件
@@ -0,0 +1,89 @@
#!/usr/bin/env python3
"""解析 EasyEDA 导出的 PADS 网表 -> 规范化的对账基准。
这是转 KiCad 全程的**唯一真值**:生成的 .kicad_sch 必须能被 kicad-cli 编译出
完全一致的 (元件集合, 网络->引脚集合)。网络名本身允许改写(EasyEDA 的匿名网
$1N236 和 KiCad 的自动命名不会一样),所以比对时按「引脚集合」而不是网络名配对。
用法:
python k_netlist.py # 打印两块板的统计
python k_netlist.py --board Board1 --dump base.json
"""
import argparse, json, os, re, sys
ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "eda_src")
def parse_pads(path):
"""-> {'parts': {ref: footprint}, 'nets': {net: [ref.pin, ...]}}"""
txt = open(path, encoding="utf-8").read()
parts, nets = {}, {}
section, cur = None, None
for raw in txt.splitlines():
line = raw.rstrip()
if not line.strip():
continue
m = re.match(r"^\*([A-Z]+)\*(.*)$", line)
if m:
tag, rest = m.group(1), m.group(2).strip()
if tag == "SIGNAL":
section, cur = "SIGNAL", rest
nets[cur] = []
elif tag in ("PART", "NET"):
section, cur = tag, None
else: # *REMARK* 等一律跳过
section, cur = None, None
continue
if section == "PART":
f = line.split()
if len(f) >= 2:
parts[f[0]] = f[1]
elif f:
parts[f[0]] = ""
elif section == "SIGNAL" and cur is not None:
nets[cur].extend(line.split())
return {"parts": parts, "nets": nets}
def stats(nl, label):
parts, nets = nl["parts"], nl["nets"]
pins = [p for v in nets.values() for p in v]
anon = [n for n in nets if n.startswith("$")]
# 每个网络的引脚数分布,单引脚网络是可疑项
single = [n for n, v in nets.items() if len(v) < 2]
# 出现在网表里但不在 PART 表里的位号
refs = {p.rsplit(".", 1)[0] for p in pins}
missing = sorted(refs - set(parts))
print(f"[{label}]")
print(f" 元件 {len(parts):>4d} 网络 {len(nets):>4d}(匿名 {len(anon)}) 引脚连接 {len(pins):>5d}")
print(f" 单引脚网络 {len(single)} {single[:6]}{' …' if len(single) > 6 else ''}")
if missing:
print(f" ⚠ 网表引用了 PART 表里没有的位号: {missing[:10]}")
dup = [p for p in set(pins) if pins.count(p) > 1] if len(pins) < 20000 else []
if dup:
print(f" ⚠ 同一引脚出现在多个网络(短路): {dup[:10]}")
return {"parts": len(parts), "nets": len(nets), "pins": len(pins)}
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--board")
ap.add_argument("--dump")
a = ap.parse_args()
boards = [a.board] if a.board else ["Board1", "Board2"]
out = {}
for b in boards:
p = os.path.join(ROOT, b, "netlist.asc")
if not os.path.exists(p):
print(f"!! 缺 {p}"); sys.exit(1)
nl = parse_pads(p)
stats(nl, b)
out[b] = nl
if a.dump:
json.dump(out if not a.board else out[a.board],
open(a.dump, "w", encoding="utf-8"), ensure_ascii=False, indent=1)
print(f"\n-> {a.dump}")
if __name__ == "__main__":
main()