#!/usr/bin/env python3 # -*- coding: utf-8 -*- """生成 docs/images/物理分区布局.svg —— GD5F2GQ5UE 全片分区布局图(纯标准库)。 由 gen_frame_svg.py 复制而来(build() 与数据解耦,通用):协议帧图按"字节"计量 (字段宽 ∝ 字节数),本图按"MB"计量(分区宽 ∝ MB 数),其余约定一致—— 分段矩形 + 偏移标尺 + 逐段标注,连续布局无假箭头。 用法(仓库根目录,任意 python3): python tools/arch_diagram/gen_partition_svg.py 输出: docs/images/物理分区布局.svg 改分区时:编辑本文件顶部的 FIELDS / TITLE / SUB / FOOT 后重跑。 """ import html import os ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) OUT = os.path.join(ROOT, "docs", "images", "物理分区布局.svg") # ----------------------------- 画布与尺寸参数 ----------------------------- PAD = 40 # 左右留白 PX_PER_MB = 4 # 每 MB 像素宽(分区宽 ∝ MB 数) BAR_Y = 116 # 分区盒顶 y(标题 / 副标题 / 偏移标尺在其上方) BAR_H = 74 # 分区盒高 NAME_DY = 30 # 分区名在盒内的 y 偏移 SIZE_DY = 54 # 尺寸标注在盒内的 y 偏移 RULER_DY = 20 # 偏移标尺线在盒顶上方 TITLE_FONT = 20 SUB_FONT = 13 NAME_FONT = 15 SIZE_FONT = 12 RULER_FONT = 12 FOOT_FONT = 12 FONT = "Microsoft YaHei, Segoe UI, PingFang SC, sans-serif" # ----------------------------- 分区数据(改这里) ----------------------------- # name=分区名 mb=MB 数(None=变长段,按 DATA_UNITS 画) label=尺寸标注 color/bg=配色 DATA_UNITS = 5 # 变长段的"代表宽度"(按 N MB 画,仅示意) FIELDS = [ dict(name="ftl_fatfs", mb=256, label="256 MB · Block 0~2047", color="#2563eb", bg="#eff6ff"), ] TITLE = "GD5F2GQ5UE 物理分区布局(256MB)" SUB = "单分区:全片划给 FTL(无静态分区)· 块 = 128KB(64 页 × 2KB)· 共 2048 块" FOOT = ("ftl_fatfs:dhara FTL + FatFS 占用全部 2048 块(偏移 0 ~ 256MB)· " "详见 docs/STM32F4-Base存储架构说明.md §1") # ----------------------------- 工具函数 ----------------------------- def esc(s): return html.escape(s, quote=True) def field_w(f): return (f["mb"] if f["mb"] is not None else DATA_UNITS) * PX_PER_MB def offset_labels(fields): """按分区 MB 数累计边界偏移;偏移标注统一带 MB 单位。""" labs, off = ["0"], 0 for f in fields: off += f["mb"] if f["mb"] is not None else DATA_UNITS labs.append("%d MB" % off) return labs # ----------------------------- 生成 ----------------------------- def build(): out = [] add = out.append frame_w = sum(field_w(f) for f in FIELDS) W = frame_w + 2 * PAD # 预计算各分区 x / 宽 segs, x = [], PAD for f in FIELDS: w = field_w(f) segs.append([f, x, w]) x += w labels = offset_labels(FIELDS) total_h = BAR_Y + BAR_H + 56 add('' % (W, total_h)) # 标题 + 副标题 add('%s' % (PAD, TITLE_FONT, esc(TITLE))) add('%s' % (PAD, SUB_FONT, esc(SUB))) # 偏移标尺(每个分区边界一个刻度 + 偏移标注) ruler_y = BAR_Y - RULER_DY add('' % (PAD, ruler_y, PAD + frame_w, ruler_y)) bounds = [PAD] + [s[1] + s[2] for s in segs] for i, bx in enumerate(bounds): add('' % (bx, ruler_y - 4, bx, ruler_y + 4)) lab = labels[i] if i < len(labels) else "?" add('%s' % (bx, ruler_y - 8, RULER_FONT, esc(lab))) # 分区盒(名在上、尺寸在下) for (f, bx, bw) in segs: add('' % (bx, BAR_Y, bw, BAR_H, f["bg"], f["color"])) cx = bx + bw / 2.0 add('%s' % (cx, BAR_Y + NAME_DY, NAME_FONT, esc(f["name"]))) add('%s' % (cx, BAR_Y + SIZE_DY, SIZE_FONT, f["color"], esc(f["label"]))) # 脚注 add('%s' % (PAD, total_h - 16, FOOT_FONT, esc(FOOT))) svg = ('\n%s\n\n' % (W, total_h, W, total_h, FONT, "\n".join(out))) return svg, W, total_h def main(): svg, W, h = build() with open(OUT, "w", encoding="utf-8", newline="\n") as f: f.write(svg) import xml.etree.ElementTree as ET ET.fromstring(svg) print("OK %d bytes, canvas %dx%d -> %s" % (len(svg.encode("utf-8")), W, h, OUT)) if __name__ == "__main__": main()