Files
ykcheungab b37c861254 文档:刷新架构报告的指标与结论(app_window 拆分已落地)
- What: `docs/architecture/studio-architecture.md` 的数字与结论按当前源码重扫后刷新:
  - 分层图/纯文本图:`app_window` 3044 → **2309 行 / fan-out 37**,并新增一层
    "②b Studio 控制器"(session_controller 437 / replay_controller 641 / view_layout 398 /
    display_prefs 227);领域服务与共享层的行数一并更新(replay 291、calibration_service 194、
    coordinator 619、camera 238、view 757、asset_source 256、gl_backend 870;
    shared scene3d 4565 / acquisition 2627 / record 3008 / force 3057 / 基础设施 1507)。
  - 边界规则:模块数 89(studio 32 + shared 57)→ **94(studio 37 + shared 57)**,环 0 个不变。
  - 指标表:新增 `replay_controller`(641) 与 `session_controller`(437) 两行,并写明
    "app_window 的 fan-out 仍是最高(37),因为它本来就是装配器——但规则已经不在它里面"。
  - 生产面/验证面:studio 生产 14 655 行、shared 14 764、测试 15 639(shared 7226/334 项 +
    studio 8413/310 项)、工具 4046(ui_check 3401);验证面 19.7k > 生产面 14.7k。
  - 评估第 1 条与"一句话结论"改写为:上帝对象已按职责拆开,并**诚实记账**留下的东西——
    约 40 个过渡转发(给 tests/`ui_check` 的桥,属过渡层)、仍留在窗口的预览与 2D 渲染
    (它们同时属于面板与相机),以及 fan-out 从 36 升到 37 的原因。
  - 建议表第 1 条标记为已完成(写清五个去处与新增 41 项控制器单测),新增第 1b 条
    "下次改动顺手删掉对应转发",并补第 7 条"真机与 ARM 验收"(本机无设备/板卡,是当前最大空白)。
- What: `studio-architecture.svg` / `.png` 由 `.repro/make_studio_svg.py` 重新生成:
  窗口框改为"2309 行 · 158 方法 · fan-out 37 · 装配 + 信号转发",右侧四个控制器小框,
  面板/服务/共享层/验证面的行数与测试项数同步;底部结论改为"已按职责拆开 + 剩下的胶水"。
  顺带修好 `svg_to_png.py` 里写死的 1560×1180(现在读 SVG 自己的尺寸),否则海报加高后会被裁掉。
- Why: 这份报告是"判断组织是否合理"的依据,数字或结论一旦过期就会误导决策;
  拆分改变最大的一处正是它第 1 条风险与第 1 条建议,必须同步。
- Verification: 图表脚本重跑无异常(SVG well-formed,PNG 1560×1210 已目视检查);
  同批源码的验证为 pytest 644 passed / 0 failed / 15 skipped(offscreen)、
  tools/ui_check.py 587 项 0 失败、.repro/locale_check.py 0 缺失。
- Not in this change: 不改代码;不改 `.repro` 下的扫描脚本口径(仍以扫描输出为准)。
2026-09-15 10:02:37 +08:00

150 lines
15 KiB
XML
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<svg xmlns="http://www.w3.org/2000/svg" width="1560" height="1210" viewBox="0 0 1560 1210" font-family="Segoe UI, sans-serif">
<rect width="1560" height="1210" fill="#0f1420"/>
<text x="40" y="42" fill="#e8edf7" font-size="23" font-weight="700">Orisys Studio 架构(按源码实际 import 关系生成)</text>
<text x="40" y="66" fill="#9fb0c8" font-size="12.5">绿 = import 时不拉 Qt/OpenGL 蓝 = 需要 Qt 橙 = GL 仅运行时惰性加载 紫 = 验证面 数字为行数(含空行)</text>
<rect x="30" y="84" width="1500" height="62" rx="10" fill="#161d2c" stroke="#27324a" stroke-width="1"/>
<text x="46" y="111" fill="#7dd3fc" font-size="15.5" font-weight="600">① 入口</text>
<rect x="30" y="176" width="1500" height="116" rx="10" fill="#161d2c" stroke="#27324a" stroke-width="1"/>
<text x="46" y="203" fill="#7dd3fc" font-size="15.5" font-weight="600">② 窗口外壳</text>
<text x="46" y="223" fill="#9fb0c8" font-size="11.5">只有它能看见全部 UI</text>
<rect x="30" y="322" width="1500" height="148" rx="10" fill="#161d2c" stroke="#27324a" stroke-width="1"/>
<text x="46" y="349" fill="#7dd3fc" font-size="15.5" font-weight="600">③ 面板</text>
<text x="46" y="369" fill="#9fb0c8" font-size="11.5">各自的画布与控件</text>
<rect x="30" y="500" width="1500" height="190" rx="10" fill="#161d2c" stroke="#27324a" stroke-width="1"/>
<text x="46" y="527" fill="#7dd3fc" font-size="15.5" font-weight="600">④ Studio 领域服务</text>
<text x="46" y="547" fill="#9fb0c8" font-size="11.5">有 Qt 的与无 Qt 的分开</text>
<rect x="30" y="720" width="1500" height="210" rx="10" fill="#161d2c" stroke="#27324a" stroke-width="1"/>
<text x="46" y="747" fill="#7dd3fc" font-size="15.5" font-weight="600">⑤ 共享领域层</text>
<text x="46" y="767" fill="#9fb0c8" font-size="11.5">采集 / 记录 / 力 / 三维契约 —— import 时不拉 Qt</text>
<rect x="30" y="960" width="1500" height="150" rx="10" fill="#161d2c" stroke="#27324a" stroke-width="1"/>
<text x="46" y="987" fill="#7dd3fc" font-size="15.5" font-weight="600">⑥ 验证面</text>
<text x="46" y="1007" fill="#9fb0c8" font-size="11.5">为什么这个架构敢改</text>
<rect x="220" y="100" width="520" height="34" rx="7" fill="#1d2b45" stroke="#4a5a76" stroke-width="1.3"/>
<text x="231" y="121" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">main.py</text>
<text x="231" y="139" fill="#9fb0c8" font-size="11">110 行 · QApplication · 主题 · 首选项 · 单实例</text>
<rect x="220" y="200" width="700" height="76" rx="7" fill="#1d2b45" stroke="#7dd3fc" stroke-width="1.3"/>
<text x="231" y="221" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">app_window.py · StudioMainWindow</text>
<text x="231" y="239" fill="#9fb0c8" font-size="11">2309 行 · 158 个方法 · fan-out 37(含四个控制器)</text>
<text x="231" y="255" fill="#7dd3fc" font-size="11" font-family="Consolas, monospace">装配 + 信号转发;状态与规则在四个控制器里(右侧)</text>
<rect x="950" y="200" width="270" height="44" rx="7" fill="#1d2b45" stroke="#4a5a76" stroke-width="1.3"/>
<text x="961" y="221" fill="#e8edf7" font-size="12" font-family="Consolas, monospace">session_controller.py</text>
<text x="961" y="239" fill="#9fb0c8" font-size="11">会话 · 相位 · 437 行</text>
<text x="961" y="255" fill="#7dd3fc" font-size="11" font-family="Consolas, monospace">worker / 遥测 / 结束原因</text>
<rect x="1235" y="200" width="270" height="44" rx="7" fill="#1d2b45" stroke="#4a5a76" stroke-width="1.3"/>
<text x="1246" y="221" fill="#e8edf7" font-size="12" font-family="Consolas, monospace">replay_controller.py</text>
<text x="1246" y="239" fill="#9fb0c8" font-size="11">回放策略 · 641 行</text>
<text x="1246" y="255" fill="#7dd3fc" font-size="11" font-family="Consolas, monospace">播放 · 打开 · 重算</text>
<rect x="950" y="250" width="270" height="44" rx="7" fill="#1d2b45" stroke="#4a5a76" stroke-width="1.3"/>
<text x="961" y="271" fill="#e8edf7" font-size="12" font-family="Consolas, monospace">view_layout.py</text>
<text x="961" y="289" fill="#9fb0c8" font-size="11">面板布局 · 398 行</text>
<text x="961" y="305" fill="#7dd3fc" font-size="11" font-family="Consolas, monospace">可见性 · 预设 · 3D 开关</text>
<rect x="1235" y="250" width="270" height="44" rx="7" fill="#1d2b45" stroke="#4a5a76" stroke-width="1.3"/>
<text x="1246" y="271" fill="#e8edf7" font-size="12" font-family="Consolas, monospace">display_prefs.py</text>
<text x="1246" y="289" fill="#9fb0c8" font-size="11">显示偏好 · 227 行</text>
<text x="1246" y="305" fill="#7dd3fc" font-size="11" font-family="Consolas, monospace">单位 · 量程 · 箭头 · fps</text>
<rect x="220" y="352" width="238" height="92" rx="7" fill="#1d2b45" stroke="#4a5a76" stroke-width="1.3"/>
<text x="231" y="373" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">ui/arrow_panel</text>
<text x="231" y="391" fill="#9fb0c8" font-size="11">2D 图像 · 518 行</text>
<text x="231" y="407" fill="#7dd3fc" font-size="11" font-family="Consolas, monospace">→ image_backend(共享)</text>
<rect x="478" y="352" width="238" height="92" rx="7" fill="#1d2b45" stroke="#4a5a76" stroke-width="1.3"/>
<text x="489" y="373" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">ui/mesh3d_panel</text>
<text x="489" y="391" fill="#9fb0c8" font-size="11">3D 宿主容器 · 404 行</text>
<text x="489" y="407" fill="#7dd3fc" font-size="11" font-family="Consolas, monospace">→ scene3d/view</text>
<rect x="736" y="352" width="238" height="92" rx="7" fill="#1d2b45" stroke="#4a5a76" stroke-width="1.3"/>
<text x="747" y="373" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">ui/force_plots</text>
<text x="747" y="391" fill="#9fb0c8" font-size="11">三轴曲线 · 1242 行</text>
<text x="747" y="407" fill="#7dd3fc" font-size="11" font-family="Consolas, monospace">→ force.history</text>
<rect x="994" y="352" width="238" height="92" rx="7" fill="#1d2b45" stroke="#4a5a76" stroke-width="1.3"/>
<text x="1005" y="373" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">ui/cal_panel</text>
<text x="1005" y="391" fill="#9fb0c8" font-size="11">标定表 · 792 行</text>
<text x="1005" y="407" fill="#7dd3fc" font-size="11" font-family="Consolas, monospace">→ force 标定</text>
<rect x="1252" y="352" width="238" height="92" rx="7" fill="#1d2b45" stroke="#4a5a76" stroke-width="1.3"/>
<text x="1263" y="373" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">对话框 ×3</text>
<text x="1263" y="391" fill="#9fb0c8" font-size="11">sensor / plot / cal_point</text>
<rect x="220" y="524" width="296" height="56" rx="7" fill="#1d2b45" stroke="#4a5a76" stroke-width="1.3"/>
<text x="231" y="545" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">recording.py</text>
<text x="231" y="563" fill="#9fb0c8" font-size="11">记录协调 · 267 行</text>
<rect x="538" y="524" width="296" height="56" rx="7" fill="#1d2b45" stroke="#4a5a76" stroke-width="1.3"/>
<text x="549" y="545" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">replay.py</text>
<text x="549" y="563" fill="#9fb0c8" font-size="11">回放引擎 · 291 行(QObject)</text>
<text x="549" y="579" fill="#7dd3fc" font-size="11" font-family="Consolas, monospace">→ record 读取</text>
<rect x="856" y="524" width="296" height="56" rx="7" fill="#1d2b45" stroke="#4a5a76" stroke-width="1.3"/>
<text x="867" y="545" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">calibration_service.py</text>
<text x="867" y="563" fill="#9fb0c8" font-size="11">标定文件与归属 · 194 行</text>
<text x="867" y="579" fill="#7dd3fc" font-size="11" font-family="Consolas, monospace">无 Qt</text>
<rect x="1174" y="524" width="296" height="56" rx="7" fill="#152b25" stroke="#4a5a76" stroke-width="1.3"/>
<text x="1185" y="545" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">scene3d/coordinator.py</text>
<text x="1185" y="563" fill="#9fb0c8" font-size="11">调度 · 619 行 · 无 Qt</text>
<text x="1185" y="579" fill="#7dd3fc" font-size="11" font-family="Consolas, monospace">mailbox + epoch</text>
<rect x="220" y="594" width="296" height="56" rx="7" fill="#152b25" stroke="#4a5a76" stroke-width="1.3"/>
<text x="231" y="615" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">scene3d/camera.py</text>
<text x="231" y="633" fill="#9fb0c8" font-size="11">相机 · 238 行 · 无 Qt</text>
<rect x="538" y="594" width="296" height="56" rx="7" fill="#1d2b45" stroke="#4a5a76" stroke-width="1.3"/>
<text x="549" y="615" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">scene3d/view.py</text>
<text x="549" y="633" fill="#9fb0c8" font-size="11">3D 唯一入口 · 757 行</text>
<text x="549" y="649" fill="#7dd3fc" font-size="11" font-family="Consolas, monospace">describe() / render_totals()</text>
<rect x="856" y="594" width="296" height="56" rx="7" fill="#1d2b45" stroke="#4a5a76" stroke-width="1.3"/>
<text x="867" y="615" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">scene3d/asset_source.py</text>
<text x="867" y="633" fill="#9fb0c8" font-size="11">加载线程 · 256 行</text>
<text x="867" y="649" fill="#7dd3fc" font-size="11" font-family="Consolas, monospace">验签 + 设备几何校验</text>
<rect x="1174" y="594" width="296" height="56" rx="7" fill="#1e273a" stroke="#fbbf24" stroke-width="1.3" stroke-dasharray="4 3"/>
<text x="1185" y="615" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">scene3d/gl_backend.py</text>
<text x="1185" y="633" fill="#9fb0c8" font-size="11">GL 惰性 · 870 行</text>
<rect x="220" y="744" width="406" height="54" rx="7" fill="#152b25" stroke="#4a5a76" stroke-width="1.3"/>
<text x="231" y="765" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">orisys_app_shared.scene3d</text>
<text x="231" y="783" fill="#9fb0c8" font-size="11">契约 / 采样 / 合成 / 包 / 签名 · 4565 行</text>
<rect x="648" y="744" width="406" height="54" rx="7" fill="#152b25" stroke="#4a5a76" stroke-width="1.3"/>
<text x="659" y="765" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">orisys_app_shared.acquisition</text>
<text x="659" y="783" fill="#9fb0c8" font-size="11">worker / packet / sinks / 图像后端 · 2627 行</text>
<rect x="1076" y="744" width="406" height="54" rx="7" fill="#152b25" stroke="#4a5a76" stroke-width="1.3"/>
<text x="1087" y="765" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">orisys_app_shared.record</text>
<text x="1087" y="783" fill="#9fb0c8" font-size="11">录像读写 / CSV / session.json · 3008 行</text>
<rect x="220" y="810" width="406" height="54" rx="7" fill="#3a2a18" stroke="#4a5a76" stroke-width="1.3"/>
<text x="231" y="831" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">orisys_app_shared.force</text>
<text x="231" y="849" fill="#9fb0c8" font-size="11">力源 / 标定 / 滤波 · 3057 行</text>
<rect x="648" y="810" width="406" height="54" rx="7" fill="#152b25" stroke="#4a5a76" stroke-width="1.3"/>
<text x="659" y="831" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">camera · prefs · paths · i18n · theme · logging</text>
<text x="659" y="849" fill="#9fb0c8" font-size="11">基础设施 · 1507 行</text>
<text x="1076" y="828" fill="#fbbf24" font-size="11.5">⚠ 唯一的例外:force.calibration_io 在 import 时</text>
<text x="1076" y="846" fill="#fbbf24" font-size="11.5">  import orisys(力相关单测需要装 SDK)</text>
<rect x="220" y="984" width="616" height="44" rx="7" fill="#251d33" stroke="#7c5cbf" stroke-width="1.3"/>
<text x="231" y="1005" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">shared/tests</text>
<text x="231" y="1023" fill="#9fb0c8" font-size="11">22 文件 · 7226 行 · 334 项</text>
<rect x="860" y="984" width="616" height="44" rx="7" fill="#251d33" stroke="#7c5cbf" stroke-width="1.3"/>
<text x="871" y="1005" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">apps/studio/tests</text>
<text x="871" y="1023" fill="#9fb0c8" font-size="11">15 文件 · 8413 行 · 310 项 + 真 GL 14 项</text>
<rect x="220" y="1036" width="616" height="44" rx="7" fill="#251d33" stroke="#7c5cbf" stroke-width="1.3"/>
<text x="231" y="1057" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">tools/ui_check.py</text>
<text x="231" y="1075" fill="#9fb0c8" font-size="11">3401 行 · 587 项 UI 断言</text>
<rect x="860" y="1036" width="616" height="44" rx="7" fill="#251d33" stroke="#7c5cbf" stroke-width="1.3"/>
<text x="871" y="1057" fill="#e8edf7" font-size="13" font-family="Consolas, monospace">tools/bench_scene.py · soak_scene.py · probe_gl.py</text>
<text x="871" y="1075" fill="#9fb0c8" font-size="11">性能 / 长时 / 平台报告</text>
<line x1="300" y1="146" x2="300" y2="166" stroke="#7dd3fc" stroke-width="2.2"/>
<path d="M 293 164 L 300 176 L 307 164 Z" fill="#7dd3fc"/>
<text x="314" y="165" fill="#9fb0c8" font-size="11.5">启动</text>
<line x1="300" y1="292" x2="300" y2="312" stroke="#7dd3fc" stroke-width="2.2"/>
<path d="M 293 310 L 300 322 L 307 310 Z" fill="#7dd3fc"/>
<text x="314" y="311" fill="#9fb0c8" font-size="11.5">装配窗口 + 面板 + 信号转发</text>
<line x1="880" y1="292" x2="880" y2="312" stroke="#7dd3fc" stroke-width="2.2"/>
<path d="M 873 310 L 880 322 L 887 310 Z" fill="#7dd3fc"/>
<line x1="300" y1="470" x2="300" y2="490" stroke="#7dd3fc" stroke-width="2.2"/>
<path d="M 293 488 L 300 500 L 307 488 Z" fill="#7dd3fc"/>
<text x="314" y="489" fill="#9fb0c8" font-size="11.5">领域服务:会话 / 记录 / 回放 / 标定</text>
<line x1="880" y1="470" x2="880" y2="490" stroke="#7dd3fc" stroke-width="2.2"/>
<path d="M 873 488 L 880 500 L 887 488 Z" fill="#7dd3fc"/>
<text x="894" y="489" fill="#9fb0c8" font-size="11.5">三维:view → coordinator / GL / 加载线程</text>
<line x1="300" y1="690" x2="300" y2="710" stroke="#7dd3fc" stroke-width="2.2"/>
<path d="M 293 708 L 300 720 L 307 708 Z" fill="#7dd3fc"/>
<text x="314" y="709" fill="#9fb0c8" font-size="11.5">契约 · packet · mailbox(无 Qt 的共享层)</text>
<line x1="880" y1="690" x2="880" y2="710" stroke="#7dd3fc" stroke-width="2.2"/>
<path d="M 873 708 L 880 720 L 887 708 Z" fill="#7dd3fc"/>
<line x1="300" y1="930" x2="300" y2="950" stroke="#7dd3fc" stroke-width="2.2"/>
<path d="M 293 948 L 300 960 L 307 948 Z" fill="#7dd3fc"/>
<text x="314" y="949" fill="#9fb0c8" font-size="11.5">验证:单测 · 真 GL · UI 检查 · 性能长时报告</text>
<line x1="880" y1="930" x2="880" y2="950" stroke="#7dd3fc" stroke-width="2.2"/>
<path d="M 873 948 L 880 960 L 887 948 Z" fill="#7dd3fc"/>
<text x="40" y="1128" fill="#9fb0c8" font-size="11.5">依赖方向只有向下:扫全部 94 个模块(studio 37 + shared 57),模块级环 0 个;新增模块只要不反向 import 就不会破坏分层。</text>
<text x="40" y="1146" fill="#9fb0c8" font-size="11.5">2D 与 3D 是两条独立流水线,共同上游只有同一个 SdkPacket;任一失败只降级自己(2D/记录/曲线不受影响)。</text>
<text x="40" y="1164" fill="#9fb0c8" font-size="11.5">app_window 已按职责拆开(会话 / 回放 / 布局 / 显示偏好),158 个方法里约 40 个是给测试与 ui_check 的一层转发;</text>
<text x="40" y="1182" fill="#9fb0c8" font-size="11.5">剩下的胶水是预览(_start_preview/_stop_preview)与 2D 渲染(_show_image_frame/_depth_range),它们同时属于面板与相机。</text>
</svg>