You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

11 KiB

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_uuid2D 设备实例唯一标识
  • terminal_uuid2D 端子实例唯一标识
  • instance_id3D 设备实例唯一标识,由 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 结构

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 结构

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_keyconnection_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. 第一版交换目录建议

建议在项目目录下固定一套交换目录:

<ProjectRoot>/.qet_freecad/
    2d_to_3d.json
    3d_to_2d.json
    scene.FCStd
    logs/

含义:

  • 2d_to_3d.jsonQET 导出给 FreeCAD 的输入
  • 3d_to_2d.jsonFreeCAD 回写给 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 + 打开 FreeCADFreeCAD 保存时回写 JSON”的文件交换闭环不做强耦合实时同步。