R2:底板 / 刀片布局布线脚本、快照、截图与规划文档更新(含外壳、打印件、布线归档)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
+418
@@ -0,0 +1,418 @@
|
||||
#!/usr/bin/env python3
|
||||
"""从 EasyEDA 几何数据生成 KiCad 原理图工程(每块板一个工程,层级页)。
|
||||
|
||||
坐标变换(三条实测依据,见 easyeda-sch-geometry skill):
|
||||
· EasyEDA 原理图 Y 轴**向上**,KiCad .kicad_sch 页面 Y 轴**向下** -> 放置时翻 Y;
|
||||
而 .kicad_sym 和 EasyEDA 符号同为 Y 向上 -> 符号内部图形不翻。
|
||||
两者合起来使得「按同一坐标摆符号 + 画同样导线」得到同样的连接性。
|
||||
· 旋转是 Y-up 下的**逆时针**,KiCad 角度 = EasyEDA 角度(503/508 个元件实测)。
|
||||
· 镜像是在旋转**之后**翻 X,等价于 KiCad 的 (mirror y) + 角度取 (360-θ)。
|
||||
|
||||
元素映射:
|
||||
part -> (symbol) lib_id "PhoneRail:<符号名>"
|
||||
netflag -> (symbol) 电源符号 引脚在原点,rot 1:1
|
||||
netport -> (global_label) KiCad 靠同名全局标签跨页连接,正是 netport 的语义
|
||||
wire -> (wire) + 必要的 (junction)
|
||||
Text -> (text)
|
||||
"""
|
||||
import json, os, re, sys, uuid, collections
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
import k_labels
|
||||
|
||||
ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||
SRC = os.path.join(ROOT, "eda_src")
|
||||
OUT = os.path.join(os.path.dirname(ROOT), "kicad")
|
||||
LIB = "PhoneRail"
|
||||
MM = 0.254
|
||||
FONT_K = 0.15875 # EasyEDA 字号 8 -> KiCad 默认 1.27 mm
|
||||
MARGIN = 12.7
|
||||
NS = uuid.UUID("6f1d5a0e-9c3b-4a71-8e2d-phonerail00".replace("phonerail00", "0f1e2d3c4b5a"))
|
||||
|
||||
PAPERS = [("A4", 297, 210), ("A3", 420, 297), ("A2", 594, 420), ("A1", 841, 594), ("A0", 1189, 841)]
|
||||
PROJ = {"Board1": "PhoneRail-Board1", "Board2": "PhoneRail-Board2"}
|
||||
# netport 的 dir + rotation -> 标签朝向(skill §8b 实测:IN 和 BI/OUT 相反)
|
||||
ADD_NC = "--nc" in sys.argv
|
||||
|
||||
PORT_SHAPE = {"Netport-BI": "bidirectional", "Netport-OUT": "output", "Netport-IN": "input"}
|
||||
|
||||
|
||||
def U(*parts):
|
||||
"""确定性 UUID:重跑生成器不会打乱 KiCad 的实例路径"""
|
||||
return str(uuid.uuid5(NS, "|".join(str(p) for p in parts)))
|
||||
|
||||
|
||||
def q(s):
|
||||
return '"' + str(s if s is not None else "").replace("\\", "\\\\").replace('"', '\\"') + '"'
|
||||
|
||||
|
||||
def f(v):
|
||||
x = round(float(v), 4)
|
||||
return (f"{x:.4f}".rstrip("0").rstrip(".")) or "0"
|
||||
|
||||
|
||||
# ---------- 从生成好的符号库里取出各符号定义,内嵌进每页的 lib_symbols ----------
|
||||
def load_lib_symbols():
|
||||
p = os.path.join(OUT, "lib", LIB + ".kicad_sym")
|
||||
s = open(p, encoding="utf-8").read()
|
||||
out = {}
|
||||
for m in re.finditer(r'^\t\(symbol "([^"]+)"', s, re.M):
|
||||
name = m.group(1)
|
||||
i = m.start(); d = 0
|
||||
for j in range(i, len(s)):
|
||||
if s[j] == "(": d += 1
|
||||
elif s[j] == ")":
|
||||
d -= 1
|
||||
if d == 0: break
|
||||
out[name] = s[i:j + 1]
|
||||
return out
|
||||
|
||||
|
||||
def embed(block, name):
|
||||
"""把库里的 (symbol "NAME" ...) 改成页面 lib_symbols 里的 (symbol "LIB:NAME" ...) 并多缩进一层"""
|
||||
# 只有**最外层**符号名带库前缀;内层 NAME_0_1 / NAME_1_1 必须保持原样,
|
||||
# 加了前缀 KiCad 会直接「加载原理图失败」(2026-09-07 实测)
|
||||
block = block.replace(f'(symbol "{name}"', f'(symbol "{LIB}:{name}"', 1)
|
||||
return "\n".join("\t" + ln for ln in block.splitlines())
|
||||
|
||||
|
||||
# ---------- 页面数据 ----------
|
||||
def page_data(board, page):
|
||||
return json.load(open(os.path.join(SRC, board, page + ".geom.json"), encoding="utf-8"))
|
||||
|
||||
|
||||
def netlist_props(board):
|
||||
"""位号 -> 解析后的属性(Name/MPN/厂商/LCSC/Datasheet)。用编译后的网表,公式已求值。"""
|
||||
d = json.load(open(os.path.join(SRC, board, "netlist.json"), encoding="utf-8"))
|
||||
out = {}
|
||||
for _, c in d["components"].items():
|
||||
p = c.get("props", {})
|
||||
if p.get("Designator"):
|
||||
out[p["Designator"]] = p
|
||||
return out
|
||||
|
||||
|
||||
def segments(line):
|
||||
return [((line[i], line[i + 1]), (line[i + 2], line[i + 3]))
|
||||
for i in range(0, len(line) - 3, 4)
|
||||
if (line[i], line[i + 1]) != (line[i + 2], line[i + 3])]
|
||||
|
||||
|
||||
def transform_for(g):
|
||||
xs, ys = [], []
|
||||
for c in (g["prims"]["Component"] or []):
|
||||
if c["ComponentType"] == "sheet":
|
||||
continue # 图框不参与,KiCad 用自己的图框
|
||||
xs.append(c["X"]); ys.append(c["Y"])
|
||||
for p in (c.get("__pins") or []):
|
||||
xs.append(p["X"]); ys.append(p["Y"])
|
||||
for w in (g["prims"]["Wire"] or []):
|
||||
for a, b in segments(w.get("Line") or []):
|
||||
xs += [a[0], b[0]]; ys += [a[1], b[1]]
|
||||
for t in (g["prims"]["Text"] or []):
|
||||
xs.append(t["X"]); ys.append(t["Y"])
|
||||
if not xs:
|
||||
xs, ys = [0], [0]
|
||||
x0 = (min(xs) // 5) * 5
|
||||
y1 = -((-max(ys)) // 5) * 5 # 向上取到 5 的倍数,保持 1.27 mm 栅格
|
||||
w = (max(xs) - x0) * MM + 2 * MARGIN
|
||||
h = (y1 - min(ys)) * MM + 2 * MARGIN
|
||||
paper = next((p for p in PAPERS if w <= p[1] and h <= p[2]), PAPERS[-1])
|
||||
return (x0, y1), paper[0], (w, h)
|
||||
|
||||
|
||||
def mk(off):
|
||||
x0, y1 = off
|
||||
return (lambda x: round((x - x0) * MM + MARGIN, 4),
|
||||
lambda y: round((y1 - y) * MM + MARGIN, 4))
|
||||
|
||||
|
||||
# ---------- 各元素的 S-expression ----------
|
||||
def sx_wire(a, b, uid):
|
||||
return (f'\t(wire (pts (xy {f(a[0])} {f(a[1])}) (xy {f(b[0])} {f(b[1])}))\n'
|
||||
f'\t\t(stroke (width 0) (type default))\n\t\t(uuid {q(uid)})\n\t)')
|
||||
|
||||
|
||||
def sx_junction(p, uid):
|
||||
return (f'\t(junction (at {f(p[0])} {f(p[1])}) (diameter 0) (color 0 0 0 0)\n'
|
||||
f'\t\t(uuid {q(uid)})\n\t)')
|
||||
|
||||
|
||||
def sx_text(t, tx, ty, uid):
|
||||
size = max(round(float(t.get("FontSize") or 8) * FONT_K, 3), 0.5)
|
||||
return (f'\t(text {q(t["Content"])}\n'
|
||||
f'\t\t(at {f(tx(t["X"]))} {f(ty(t["Y"]))} {int(t.get("Rotation") or 0)})\n'
|
||||
f'\t\t(effects (font (size {f(size)} {f(size)})) (justify left bottom))\n'
|
||||
f'\t\t(uuid {q(uid)})\n\t)')
|
||||
|
||||
|
||||
def sx_glabel(c, tx, ty, uid):
|
||||
shape = PORT_SHAPE.get((c.get("Component") or {}).get("name"), "bidirectional")
|
||||
rot = int(c.get("Rotation") or 0) % 360
|
||||
# IN 的符号体在引脚背面(skill §8b),映射到 KiCad 就是角度反过来
|
||||
ang = (rot + 180) % 360 if shape == "input" else rot
|
||||
just = "right" if ang == 180 else "left"
|
||||
return (f'\t(global_label {q(c.get("Net"))}\n'
|
||||
f'\t\t(shape {shape})\n'
|
||||
f'\t\t(at {f(tx(c["X"]))} {f(ty(c["Y"]))} {ang})\n'
|
||||
f'\t\t(fields_autoplaced yes)\n'
|
||||
f'\t\t(effects (font (size 1.27 1.27)) (justify {just}))\n'
|
||||
f'\t\t(uuid {q(uid)})\n\t)')
|
||||
|
||||
|
||||
def sx_symbol(lib_id, x, y, rot, mirror, uid, props, path, ref, unit=1):
|
||||
# KiCad 的字段渲染角 = 存储角 + 元件旋转(对照官方 demo:rot=270 的电容 Reference
|
||||
# 存的就是 90,净得 0 -> 水平;rot=180 的存 0)。存 0 会让 90/270 的标签竖排互压,
|
||||
# 而 180 上补 180 会让文字镜像 —— 所以只对 90/270 补角。
|
||||
fang = (360 - rot) % 360 if rot in (90, 270) else 0
|
||||
L = [f'\t(symbol',
|
||||
f'\t\t(lib_id {q(lib_id)})',
|
||||
f'\t\t(at {f(x)} {f(y)} {rot})']
|
||||
if mirror:
|
||||
L.append('\t\t(mirror y)')
|
||||
L += [f'\t\t(unit {unit})', '\t\t(exclude_from_sim no)', '\t\t(in_bom yes)',
|
||||
'\t\t(on_board yes)', '\t\t(dnp no)', f'\t\t(uuid {q(uid)})']
|
||||
for k, v, hide, pos in props:
|
||||
h = " (hide yes)" if hide else ""
|
||||
# pos 是数字 = 相对 y 偏移;是二元组 = 绝对坐标。用 EasyEDA 里标签的真实位置,
|
||||
# 否则密集区的位号会叠成一团(P1B-5V 的 C731/C733 第一版就是这样)。
|
||||
ax, ay = (x, y + pos) if not isinstance(pos, tuple) else pos
|
||||
# EasyEDA 的标签锚点是左下,KiCad 默认居中;不对齐的话位号会压在符号体上
|
||||
just = ' (justify left bottom)' if isinstance(pos, tuple) else ''
|
||||
L.append(f'\t\t(property {q(k)} {q(v)}\n'
|
||||
f'\t\t\t(at {f(ax)} {f(ay)} {fang})\n'
|
||||
f'\t\t\t(effects (font (size 1.27 1.27)){h}{just})\n\t\t)')
|
||||
L += ['\t\t(instances',
|
||||
f'\t\t\t(project {q(path[0])}',
|
||||
f'\t\t\t\t(path {q(path[1])}',
|
||||
f'\t\t\t\t\t(reference {q(ref)})',
|
||||
f'\t\t\t\t\t(unit {unit})',
|
||||
'\t\t\t\t)', '\t\t\t)', '\t\t)', '\t)']
|
||||
return "\n".join(L)
|
||||
|
||||
|
||||
def junctions(segs):
|
||||
"""KiCad 里导线端点落在另一条导线**中间**时必须有节点才导通;三线共点也补上。"""
|
||||
cnt = collections.Counter()
|
||||
for a, b in segs:
|
||||
cnt[a] += 1; cnt[b] += 1
|
||||
js = {p for p, n in cnt.items() if n >= 3}
|
||||
pts = set(cnt)
|
||||
for a, b in segs:
|
||||
ax, ay = a; bx, by = b
|
||||
for p in pts:
|
||||
if p == a or p == b:
|
||||
continue
|
||||
px, py = p
|
||||
if abs(ax - bx) < 1e-6: # 竖直
|
||||
if abs(px - ax) < 1e-6 and min(ay, by) < py < max(ay, by):
|
||||
js.add(p)
|
||||
elif abs(ay - by) < 1e-6: # 水平
|
||||
if abs(py - ay) < 1e-6 and min(ax, bx) < px < max(ax, bx):
|
||||
js.add(p)
|
||||
else:
|
||||
t = (px - ax) / (bx - ax)
|
||||
if 0 < t < 1 and abs(ay + t * (by - ay) - py) < 1e-6:
|
||||
js.add(p)
|
||||
return sorted(js)
|
||||
|
||||
|
||||
# ---------- 生成一页 ----------
|
||||
def gen_page(board, page, g, symnames, powers, mirrors, libsyms, proj, rootuid, sheetuid, props, pwr):
|
||||
off, paper, _ = transform_for(g)
|
||||
tx, ty = mk(off)
|
||||
labels = k_labels.page_labels(board, page)
|
||||
used, body = set(), []
|
||||
|
||||
segs = []
|
||||
for w in (g["prims"]["Wire"] or []):
|
||||
for a, b in segments(w.get("Line") or []):
|
||||
segs.append(((tx(a[0]), ty(a[1])), (tx(b[0]), ty(b[1]))))
|
||||
for i, (a, b) in enumerate(segs):
|
||||
body.append(sx_wire(a, b, U(board, page, "w", i)))
|
||||
for i, p in enumerate(junctions(segs)):
|
||||
body.append(sx_junction(p, U(board, page, "j", i)))
|
||||
|
||||
for c in (g["prims"]["Component"] or []):
|
||||
t = c["ComponentType"]
|
||||
pid = c["PrimitiveId"]
|
||||
if t == "sheet":
|
||||
continue
|
||||
if t == "netport":
|
||||
body.append(sx_glabel(c, tx, ty, U(board, page, "gl", pid)))
|
||||
continue
|
||||
rot = int(c.get("Rotation") or 0) % 360
|
||||
mirror = bool(c.get("Mirror"))
|
||||
if mirror:
|
||||
# EasyEDA 的镜像 = 先按 θ 旋转、再翻 X。由 flipX∘R(θ) = R(-θ)∘flipX,
|
||||
# 等价于「用镜像过的符号 + 转 (360-θ)」。不用 KiCad 的 (mirror y),
|
||||
# 那条路 2026-09-07 实测会把 U24~U28 的引脚摆到错误位置。
|
||||
rot = (360 - rot) % 360
|
||||
x, y = tx(c["X"]), ty(c["Y"])
|
||||
if t == "netflag":
|
||||
name = powers.get(c.get("Net"))
|
||||
if not name:
|
||||
continue
|
||||
used.add(name)
|
||||
# KiCad 的电源符号也必须有唯一位号,否则整个工程算「未批注」,
|
||||
# 网表导出会直接被拒(2026-09-07 实测:报「原理图存在批注错误」且 0 元件)
|
||||
pwr[0] += 1
|
||||
ref = f"#PWR{pwr[0]:04d}"
|
||||
body.append(sx_symbol(f"{LIB}:{name}", x, y, rot, mirror,
|
||||
U(board, page, "pf", pid),
|
||||
[("Reference", ref, True, 0),
|
||||
("Value", c.get("Net"), False, -3.81 if rot == 0 else 3.81)],
|
||||
(proj, f"/{rootuid}/{sheetuid}"), ref))
|
||||
continue
|
||||
# 元件
|
||||
suuid = (c.get("Symbol") or {}).get("uuid")
|
||||
name = (mirrors.get(suuid) if mirror else None) or symnames.get(suuid)
|
||||
if mirror and not mirrors.get(suuid):
|
||||
raise SystemExit(f"!! {board}/{page} {c.get('Designator')} 是镜像件但库里没有镜像变体")
|
||||
if not name:
|
||||
raise SystemExit(f"!! {board}/{page} {c.get('Designator')} 找不到符号 {c.get('Symbol')}")
|
||||
used.add(name)
|
||||
ref = c.get("Designator") or "?"
|
||||
pr = props.get(ref, {})
|
||||
val = pr.get("Name") or c.get("ManufacturerId") or name
|
||||
fp = (c.get("Footprint") or {}).get("name") or ""
|
||||
lab = labels.get(pid, {})
|
||||
dpos = lab.get("Designator")
|
||||
npos = lab.get("Name")
|
||||
rpos = (tx(dpos[0]), ty(dpos[1])) if dpos else -5.08
|
||||
vpos = (tx(npos[0]), ty(npos[1])) if npos else 5.08
|
||||
plist = [("Reference", ref, False, rpos),
|
||||
("Value", val, not (npos[2] if npos else True), vpos),
|
||||
("Footprint", f"{LIB}:{fp}" if fp else "", True, 0),
|
||||
("Datasheet", pr.get("Datasheet", ""), True, 0),
|
||||
("Description", pr.get("Description", ""), True, 0)]
|
||||
for k, src in (("Manufacturer", "Manufacturer"), ("MPN", "Manufacturer Part"),
|
||||
("LCSC", "Supplier Part")):
|
||||
if pr.get(src):
|
||||
plist.append((k, pr[src], True, 0))
|
||||
body.append(sx_symbol(f"{LIB}:{name}", x, y, rot, False,
|
||||
U(board, page, "sym", pid), plist,
|
||||
(proj, f"/{rootuid}/{sheetuid}"), ref))
|
||||
|
||||
if ADD_NC:
|
||||
# 未连接引脚打 KiCad 的 no_connect 标记。**默认关闭**:EasyEDA 侧这 236 个引脚
|
||||
# 的 NoConnected 全是 false,设计者从未声明「故意不接」,替他断言是不对的。
|
||||
# 想让 ERC 干净时用 --nc 打开。
|
||||
occ = collections.Counter()
|
||||
at = {}
|
||||
for a, b2 in segs:
|
||||
occ[a] += 1; occ[b2] += 1
|
||||
for c in (g["prims"]["Component"] or []):
|
||||
if c["ComponentType"] == "sheet":
|
||||
continue
|
||||
for qq in (c.get("__pins") or []):
|
||||
k = (tx(qq["X"]), ty(qq["Y"]))
|
||||
occ[k] += 1
|
||||
at.setdefault(k, 0)
|
||||
at[k] += 1
|
||||
for k, nplaced in at.items():
|
||||
if occ[k] == 1 and nplaced == 1:
|
||||
body.append(f' (no_connect (at {f(k[0])} {f(k[1])}) (uuid {q(U(board, page, "nc", k))}))')
|
||||
|
||||
for t in (g["prims"]["Text"] or []):
|
||||
body.append(sx_text(t, tx, ty, U(board, page, "t", t["PrimitiveId"])))
|
||||
|
||||
head = ["(kicad_sch", "\t(version 20250610)", f"\t(generator {q('phonerail-eda2kicad')})",
|
||||
'\t(generator_version "10.0")', f'\t(uuid {q(U(board, page, "doc"))})',
|
||||
f"\t(paper {q(paper)})",
|
||||
"\t(title_block", f"\t\t(title {q(page)})", f'\t\t(comment 1 {q("由 EasyEDA 工程自动转换")})', "\t)",
|
||||
"\t(lib_symbols"]
|
||||
for nm in sorted(used):
|
||||
head.append(embed(libsyms[nm], nm))
|
||||
head.append("\t)")
|
||||
return "\n".join(head + body + ["\t(embedded_fonts no)", ")"]) + "\n", paper
|
||||
|
||||
|
||||
# ---------- 根图纸与工程文件 ----------
|
||||
def gen_root(board, proj, pages, rootuid, sheetuids):
|
||||
body = []
|
||||
for i, p in enumerate(pages):
|
||||
col, row = i % 3, i // 3
|
||||
x, y = 25.4 + col * 88.9, 25.4 + row * 38.1
|
||||
su = sheetuids[p]
|
||||
body.append("\n".join([
|
||||
"\t(sheet",
|
||||
f"\t\t(at {f(x)} {f(y)})", "\t\t(size 76.2 25.4)",
|
||||
"\t\t(exclude_from_sim no)", "\t\t(in_bom yes)", "\t\t(on_board yes)", "\t\t(dnp no)",
|
||||
"\t\t(stroke (width 0.1524) (type solid))", "\t\t(fill (color 0 0 0 0.0000))",
|
||||
f"\t\t(uuid {q(su)})",
|
||||
f'\t\t(property "Sheetname" {q(p)}\n\t\t\t(at {f(x)} {f(y - 0.7)} 0)\n'
|
||||
f'\t\t\t(effects (font (size 1.27 1.27)) (justify left bottom))\n\t\t)',
|
||||
f'\t\t(property "Sheetfile" {q(p + ".kicad_sch")}\n\t\t\t(at {f(x)} {f(y + 26.1)} 0)\n'
|
||||
f'\t\t\t(effects (font (size 1 1)) (justify left top))\n\t\t)',
|
||||
"\t\t(instances",
|
||||
f"\t\t\t(project {q(proj)}",
|
||||
f'\t\t\t\t(path {q("/" + rootuid)}',
|
||||
f'\t\t\t\t\t(page {q(str(i + 2))})',
|
||||
"\t\t\t\t)", "\t\t\t)", "\t\t)", "\t)"]))
|
||||
head = ["(kicad_sch", "\t(version 20250610)", f"\t(generator {q('phonerail-eda2kicad')})",
|
||||
'\t(generator_version "10.0")', f"\t(uuid {q(rootuid)})", '\t(paper "A4")',
|
||||
"\t(title_block", f"\t\t(title {q(proj)})",
|
||||
f'\t\t(comment 1 {q("由 EasyEDA 工程「五路手机直供电源板」自动转换")})', "\t)",
|
||||
"\t(lib_symbols", "\t)"]
|
||||
tail = ["\t(sheet_instances", '\t\t(path "/" (page "1"))', "\t)",
|
||||
"\t(embedded_fonts no)", ")"]
|
||||
return "\n".join(head + body + tail) + "\n"
|
||||
|
||||
|
||||
KICAD_PRO = {
|
||||
"board": {"design_settings": {}, "layer_presets": [], "viewports": []},
|
||||
"boards": [],
|
||||
"libraries": {"pinned_footprint_libs": [], "pinned_symbol_libs": []},
|
||||
"meta": {"filename": "", "version": 3},
|
||||
"net_settings": {"classes": [{"name": "Default", "clearance": 0.2,
|
||||
"track_width": 0.25, "via_diameter": 0.6,
|
||||
"via_drill": 0.3, "wire_width": 6, "bus_width": 12}]},
|
||||
"pcbnew": {"page_layout_descr_file": ""},
|
||||
"schematic": {"legacy_lib_dir": "", "legacy_lib_list": []},
|
||||
"sheets": [], "text_variables": {},
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
tree = json.load(open(os.path.join(SRC, "_tree.json"), encoding="utf-8"))
|
||||
names = json.load(open(os.path.join(SRC, "_kicad_symnames.json"), encoding="utf-8"))
|
||||
libsyms = load_lib_symbols()
|
||||
for b in tree["boards"]:
|
||||
board = b["board"]; proj = PROJ[board]
|
||||
d = os.path.join(OUT, proj)
|
||||
os.makedirs(d, exist_ok=True)
|
||||
props = netlist_props(board)
|
||||
pages = [p["name"] for p in b["pages"]]
|
||||
pwr = [0]
|
||||
rootuid = U(board, "root")
|
||||
sheetuids = {p: U(board, p, "sheet") for p in pages}
|
||||
print(f"[{board}] -> {proj} {len(pages)} 页")
|
||||
for p in pages:
|
||||
g = page_data(board, p)
|
||||
txt, paper = gen_page(board, p, g, names["symbols"], names["powers"],
|
||||
names.get("mirrors", {}), libsyms, proj, rootuid,
|
||||
sheetuids[p], props, pwr)
|
||||
fp = os.path.join(d, p + ".kicad_sch")
|
||||
open(fp, "w", encoding="utf-8", newline="\n").write(txt)
|
||||
print(f" {p:12s} {paper} {os.path.getsize(fp):>8,d} B")
|
||||
open(os.path.join(d, proj + ".kicad_sch"), "w", encoding="utf-8", newline="\n") \
|
||||
.write(gen_root(board, proj, pages, rootuid, sheetuids))
|
||||
pro = dict(KICAD_PRO)
|
||||
pro["meta"] = {"filename": proj + ".kicad_pro", "version": 3}
|
||||
pro["sheets"] = [[rootuid, "Root"]] + [[sheetuids[p], p] for p in pages]
|
||||
json.dump(pro, open(os.path.join(d, proj + ".kicad_pro"), "w", encoding="utf-8"),
|
||||
ensure_ascii=False, indent=2)
|
||||
open(os.path.join(d, "sym-lib-table"), "w", encoding="utf-8", newline="\n").write(
|
||||
"(sym_lib_table\n\t(version 7)\n"
|
||||
f'\t(lib (name {q(LIB)})(type "KiCad")(uri "${{KIPRJMOD}}/../lib/{LIB}.kicad_sym")'
|
||||
'(options "")(descr "PhoneRail 由 EasyEDA 转换"))\n)\n')
|
||||
open(os.path.join(d, "fp-lib-table"), "w", encoding="utf-8", newline="\n").write(
|
||||
"(fp_lib_table\n\t(version 7)\n"
|
||||
f'\t(lib (name {q(LIB)})(type "KiCad")(uri "${{KIPRJMOD}}/../lib/{LIB}.pretty")'
|
||||
'(options "")(descr "PhoneRail 由 EasyEDA 转换"))\n)\n')
|
||||
print(" 根图纸 + .kicad_pro + sym-lib-table + fp-lib-table 已写")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
在新工单中引用
屏蔽一个用户