# 2D / 3D 协同数据库设计(第一版最小集) 本文档只保留当前阶段真正必需的字段。 目标不是一次把未来所有能力都放进表里,而是先支持下面这条最小闭环: 1. 2D 里有设备实例 2. 2D 里有端子实例 3. FreeCAD 里创建对应的 3D 设备实例 4. FreeCAD 里创建对应的 3D 端子对象 5. 2D 与 3D 可以靠稳定主键互相找到对方 当前设计严格遵守两条原则: - 电气语义以 2D 为准 - 空间位姿以 3D 为准 另外再补一条当前版本明确采用的约定: - 3D 设备资源不在绑定表中重复保存,统一通过 `element_uuid -> device_id -> parts_3d` 回查 因此,第一版不重复存这些信息: - `display_tag` - `terminal_key` - `connection_point_key` - `host_binding_mode` - `host_object_id` - `host_object_type` - `extra_json` - `scene_diagram_uuid` - `source_diagram_uuid` 原因很简单: - 这些字段当前要么能通过 2D 主键回查 - 要么属于未来扩展 - 要么当前语义还不够稳定 ## 1. 统一主键约定 第一版只认下面这几个核心标识: - `project_uuid` - `element_uuid` - `terminal_uuid` - `instance_id` 含义: - `project_uuid`:项目唯一标识 - `element_uuid`:2D 设备实例唯一标识 - `terminal_uuid`:2D 端子实例唯一标识 - `instance_id`:3D 设备实例唯一标识,由 FreeCAD 生成 ## 2. 需要保留的最小表 第一版只建议保留 2 张表: 1. `project_2d3d_symbol_binding` 2. `project_2d3d_terminal_binding` --- ## 3. 设备绑定表 表名: `project_2d3d_symbol_binding` 作用: > 记录“一个 2D 设备实例,对应哪个 3D 设备实例,以及它使用哪个 3D 资源” 但“它使用哪个 3D 资源”这一点,第一版不在本表中直接存储,而是通过 2D 侧已有关系回查: `element_uuid -> device_id -> parts_3d` ### 3.1 保留字段 | 字段名 | 中文 | 责任方 | 说明 | | --- | --- | --- | --- | | `project_uuid` | 项目UUID | 2D | 项目范围主键 | | `element_uuid` | 设备实例UUID | 2D | 2D 设备实例唯一标识 | | `instance_id` | 3D实例ID | 3D | FreeCAD 生成的 3D 设备实例唯一标识 | ### 3.2 推荐约束 - 主唯一键:`(project_uuid, element_uuid)` - 3D 实例唯一键:`(project_uuid, instance_id)` ### 3.3 推荐 SQLite 结构 ```sql CREATE TABLE IF NOT EXISTS project_2d3d_symbol_binding ( project_uuid TEXT NOT NULL, element_uuid TEXT NOT NULL, instance_id TEXT NOT NULL, PRIMARY KEY (project_uuid, element_uuid) ); CREATE UNIQUE INDEX IF NOT EXISTS idx_symbol_binding_instance ON project_2d3d_symbol_binding(project_uuid, instance_id); ``` ### 3.4 为什么只留这 3 个字段 因为第一版要解决的问题只有: - 2D 设备是谁:`element_uuid` - 3D 设备是谁:`instance_id` - 属于哪个项目:`project_uuid` 而 3D 模型资源本身可以通过: - `element_uuid` - 回查到对应设备实例 - 再回查 `device_id` - 最后拿到 `parts_3d` 所以第一版不重复存 `asset_uri`。 --- ## 4. 端子绑定表 表名: `project_2d3d_terminal_binding` 作用: > 记录“一个 2D 端子实例,对应哪个 3D 设备实例上的端子对象” ### 4.1 保留字段 | 字段名 | 中文 | 责任方 | 说明 | | --- | --- | --- | --- | | `project_uuid` | 项目UUID | 2D | 项目范围主键 | | `terminal_uuid` | 端子UUID | 2D | 2D 端子实例唯一标识 | | `instance_id` | 3D实例ID | 3D | 该端子属于哪个 3D 设备实例 | ### 4.2 推荐约束 - 主唯一键:`(project_uuid, terminal_uuid)` - 查询索引:`(project_uuid, instance_id)` ### 4.3 推荐 SQLite 结构 ```sql CREATE TABLE IF NOT EXISTS project_2d3d_terminal_binding ( project_uuid TEXT NOT NULL, terminal_uuid TEXT NOT NULL, instance_id TEXT NOT NULL, PRIMARY KEY (project_uuid, terminal_uuid) ); CREATE INDEX IF NOT EXISTS idx_terminal_binding_instance ON project_2d3d_terminal_binding(project_uuid, instance_id); ``` ### 4.4 为什么不保留 `terminal_key` 和 `connection_point_key` 当前第一版设计里: - 3D 端子本身就是 2D 端子的空间映射 - 所有端子语义都可以通过 `terminal_uuid` 从 2D 回查 所以目前不需要额外再保存: - `terminal_key` - `symbol_terminal` - `connection_point_key` - `wire_label` - `net_id` 这些信息如果后续 3D 侧需要显示或校验,可以通过 `terminal_uuid` 回查 2D。 --- ## 5. 现阶段明确删掉的字段 下面这些字段不是说永远没用,而是 **第一版先删掉,不进入最小表结构**。 ### 5.1 从设备绑定表中删掉 - `source_diagram_uuid` - `scene_diagram_uuid` - `device_id` - `display_tag` - `asset_uri` - `host_binding_mode` - `host_object_id` - `host_object_type` - `extra_json` ### 5.2 从端子绑定表中删掉 - `element_uuid` - `binding_key` - `symbol_terminal` - `terminal_key` - `connection_point_key` - `wire_label` - `net_id` - `conductor_uuid` - `extra_json` ### 5.3 从 3D 实例表中删掉 - `project_3d_scene_instance` 整张表 - 所有位姿字段:`tx / ty / tz / rx / ry / rz` - 所有缩放字段:`sx / sy / sz` - 所有 3D 场景附加字段:`diagram_uuid / asset_id / extra_json` --- ## 6. 第一版数据流 ### 6.1 2D -> 3D 2D 提供: - `project_uuid` - `element_uuid` - `terminal_uuid` 3D 在导入和实例化时生成: - `instance_id` 然后写入: - `project_2d3d_symbol_binding` - `project_2d3d_terminal_binding` ### 6.2 3D -> 2D 3D 第一版只回写: - `instance_id` 当前不回写位姿。 原因是: - 3D 场景真相源已经切换为 FreeCAD - 2D 不再承担 3D 场景保存职责 - 避免 FreeCAD 文档和数据库各存一份位姿造成冲突 --- ## 7. 这版设计的核心思想 第一版故意把设计收得很紧,只保留: - 一个 2D 设备实例如何找到 3D 设备实例 - 一个 2D 端子实例如何找到 3D 端子对象 也就是说,当前数据库设计只服务于: > “2D 语义 + 3D 空间映射”这个最小闭环 而且这里的“空间映射”只表示: - 2D 对象知道自己对应哪个 3D 实例 不表示数据库要保存完整 3D 位姿。 --- ## 8. 后续扩展时再加的字段 只有当下面这些需求真的出现时,再补字段: - 多 3D 场景:补 `scene_diagram_uuid` - 设备挂载规则:补 `host_object_id / host_object_type` - 3D 资源缓存:补 `asset_uri` - 多连接点/复杂端子:补 `connection_point_key` - 3D 端子独立语义缓存:补 `terminal_key / symbol_terminal` - 导线级 3D 校验:补 `wire_label / net_id / conductor_uuid` - 2D 侧需要读取 3D 位姿:再恢复 3D 场景实例表或等价位姿存储 - 临时扩展:最后才考虑 `extra_json` --- ## 9. FreeCAD 与数据库的职责边界 继续收缩后的职责建议如下: ### 9.1 数据库负责 - 2D 设备实例和 3D 设备实例的绑定 - 2D 端子实例和 3D 端子对象的绑定 ### 9.2 FreeCAD 负责 - 3D 设备实例实际创建 - 设备位姿 - 导轨/安装板/柜体装配关系 - 端子在空间中的实际几何位置 - 3D 接线几何 也就是说,第一版开始就建议明确: > 3D 位姿和装配关系只保存在 FreeCAD 文档里,不在数据库中重复保存。 --- ## 10. 当前推荐结论 第一版数据库设计建议就收成这三张表: 1. `project_2d3d_symbol_binding` 2. `project_2d3d_terminal_binding` 并且只保留最小核心字段: - 设备绑定只保留:`project_uuid / element_uuid / instance_id` - 端子绑定只保留:`project_uuid / terminal_uuid / instance_id` 一句话总结: > 第一版先让 2D 能找到 3D;至于 3D 怎么摆、摆在哪、怎么装配,全部交给 FreeCAD 自己管理。 --- ## 11. 第一版交换形式 第一版 2D / 3D 协同,交换形式先统一为: - **JSON 文件交换** 当前不建议第一版直接做: - 实时数据库双向同步 - 进程间 RPC - HTTP API - FreeCAD 保存后直接写回 QET 运行库 原因: - JSON 最容易调试 - 字段改动时最容易观察 - 不会把 QET 和 FreeCAD 的运行时强耦合在一起 - 更适合当前仍在收缩字段和表结构的阶段 --- ## 12. 第一版工具入口 QET 侧建议保留并改造一个工具项: - `3D视图` 这个工具项的职责不再是走 QET 旧的 3D 模块逻辑,而是: 1. 从 QET 当前项目和当前 2D 图纸中整理 2D -> 3D 最小绑定数据 2. 导出 `2d_to_3d.json` 3. 启动 FreeCAD,或打开已有的 FreeCAD 工程文件 也就是说: > 第一版 `3D视图` 本质上是“导出 2D 快照并切换到 FreeCAD”的入口。 --- ## 13. 第一版交换目录建议 建议在项目目录下固定一套交换目录: ```text /.qet_freecad/ 2d_to_3d.json 3d_to_2d.json scene.FCStd logs/ ``` 含义: - `2d_to_3d.json`:QET 导出给 FreeCAD 的输入 - `3d_to_2d.json`:FreeCAD 回写给 QET 的输出 - `scene.FCStd`:该项目对应的 FreeCAD 3D 工程 - `logs/`:调试与排障日志 --- ## 14. 第一版时机约定 ### 14.1 QET -> FreeCAD 触发时机: - 用户点击 QET 的 `3D视图` 动作: 1. 更新并确认当前 2D 设备实例 / 端子实例绑定 2. 生成 `2d_to_3d.json` 3. 打开 FreeCAD 4. 打开或创建 `scene.FCStd` ### 14.2 FreeCAD -> QET 第一版不建议: - FreeCAD 保存时直接写数据库 第一版建议: - FreeCAD 保存时输出 `3d_to_2d.json` 然后由 QET 在后续时机主动读取: - 手动刷新 - 再次点击 `3D视图` - 或后续增加单独的“从 3D 刷新”命令 这样做的目的,是先把同步时机收成“文件交换”,避免两边运行时直接打架。 --- ## 15. 第一版最小交换内容 ### 15.1 `2d_to_3d.json` 第一版只要求包含最小绑定信息: - 设备绑定: - `project_uuid` - `element_uuid` - `instance_id` - 端子绑定: - `project_uuid` - `terminal_uuid` - `instance_id` 说明: - `instance_id` 在第一版中由 FreeCAD 侧生成更合理 - 如果首次进入 3D 时尚未生成 `instance_id`,可以先导出为空,再由 FreeCAD 创建后回写 ### 15.2 `3d_to_2d.json` 第一版只建议回写: - `project_uuid` - `element_uuid` - `instance_id` - `terminal_uuid` 当前不要求回写: - 3D 位姿 - 装配结构 - 导线几何路径 这些仍以 FreeCAD 文档为准。 --- ## 16. 第一版推荐交互流程 建议按下面这条闭环实现: 1. 用户在 QET 中点击 `3D视图` 2. QET 生成 `2d_to_3d.json` 3. QET 打开 FreeCAD,并打开 `scene.FCStd` 4. FreeCAD 读取 `2d_to_3d.json` 5. FreeCAD 创建或更新: - 3D 设备实例 - 3D 端子对象 6. 用户在 FreeCAD 中完成装配、摆放、接线 7. 用户保存 FreeCAD 工程 8. FreeCAD 生成 `3d_to_2d.json` 9. QET 在后续时机读取 `3d_to_2d.json` 一句话总结: > 第一版先做“QET 导出 JSON + 打开 FreeCAD;FreeCAD 保存时回写 JSON”的文件交换闭环,不做强耦合实时同步。