11 KiB
2D / 3D 协同数据库设计(第一版最小集)
本文档只保留当前阶段真正必需的字段。
目标不是一次把未来所有能力都放进表里,而是先支持下面这条最小闭环:
- 2D 里有设备实例
- 2D 里有端子实例
- FreeCAD 里创建对应的 3D 设备实例
- FreeCAD 里创建对应的 3D 端子对象
- 2D 与 3D 可以靠稳定主键互相找到对方
当前设计严格遵守两条原则:
- 电气语义以 2D 为准
- 空间位姿以 3D 为准
另外再补一条当前版本明确采用的约定:
- 3D 设备资源不在绑定表中重复保存,统一通过
element_uuid -> device_id -> parts_3d回查
因此,第一版不重复存这些信息:
display_tagterminal_keyconnection_point_keyhost_binding_modehost_object_idhost_object_typeextra_jsonscene_diagram_uuidsource_diagram_uuid
原因很简单:
- 这些字段当前要么能通过 2D 主键回查
- 要么属于未来扩展
- 要么当前语义还不够稳定
1. 统一主键约定
第一版只认下面这几个核心标识:
project_uuidelement_uuidterminal_uuidinstance_id
含义:
project_uuid:项目唯一标识element_uuid:2D 设备实例唯一标识terminal_uuid:2D 端子实例唯一标识instance_id:3D 设备实例唯一标识,由 FreeCAD 生成
2. 需要保留的最小表
第一版只建议保留 2 张表:
project_2d3d_symbol_bindingproject_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_key 和 connection_point_key
当前第一版设计里:
- 3D 端子本身就是 2D 端子的空间映射
- 所有端子语义都可以通过
terminal_uuid从 2D 回查
所以目前不需要额外再保存:
terminal_keysymbol_terminalconnection_point_keywire_labelnet_id
这些信息如果后续 3D 侧需要显示或校验,可以通过 terminal_uuid 回查 2D。
5. 现阶段明确删掉的字段
下面这些字段不是说永远没用,而是 第一版先删掉,不进入最小表结构。
5.1 从设备绑定表中删掉
source_diagram_uuidscene_diagram_uuiddevice_iddisplay_tagasset_urihost_binding_modehost_object_idhost_object_typeextra_json
5.2 从端子绑定表中删掉
element_uuidbinding_keysymbol_terminalterminal_keyconnection_point_keywire_labelnet_idconductor_uuidextra_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_uuidelement_uuidterminal_uuid
3D 在导入和实例化时生成:
instance_id
然后写入:
project_2d3d_symbol_bindingproject_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. 当前推荐结论
第一版数据库设计建议就收成这三张表:
project_2d3d_symbol_bindingproject_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 模块逻辑,而是:
- 从 QET 当前项目和当前 2D 图纸中整理 2D -> 3D 最小绑定数据
- 导出
2d_to_3d.json - 启动 FreeCAD,或打开已有的 FreeCAD 工程文件
也就是说:
第一版
3D视图本质上是“导出 2D 快照并切换到 FreeCAD”的入口。
13. 第一版交换目录建议
建议在项目目录下固定一套交换目录:
<ProjectRoot>/.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视图
动作:
- 更新并确认当前 2D 设备实例 / 端子实例绑定
- 生成
2d_to_3d.json - 打开 FreeCAD
- 打开或创建
scene.FCStd
14.2 FreeCAD -> QET
第一版不建议:
- FreeCAD 保存时直接写数据库
第一版建议:
- FreeCAD 保存时输出
3d_to_2d.json
然后由 QET 在后续时机主动读取:
- 手动刷新
- 再次点击
3D视图 - 或后续增加单独的“从 3D 刷新”命令
这样做的目的,是先把同步时机收成“文件交换”,避免两边运行时直接打架。
15. 第一版最小交换内容
15.1 2d_to_3d.json
第一版只要求包含最小绑定信息:
- 设备绑定:
project_uuidelement_uuidinstance_id
- 端子绑定:
project_uuidterminal_uuidinstance_id
说明:
instance_id在第一版中由 FreeCAD 侧生成更合理- 如果首次进入 3D 时尚未生成
instance_id,可以先导出为空,再由 FreeCAD 创建后回写
15.2 3d_to_2d.json
第一版只建议回写:
project_uuidelement_uuidinstance_idterminal_uuid
当前不要求回写:
- 3D 位姿
- 装配结构
- 导线几何路径
这些仍以 FreeCAD 文档为准。
16. 第一版推荐交互流程
建议按下面这条闭环实现:
- 用户在 QET 中点击
3D视图 - QET 生成
2d_to_3d.json - QET 打开 FreeCAD,并打开
scene.FCStd - FreeCAD 读取
2d_to_3d.json - FreeCAD 创建或更新:
- 3D 设备实例
- 3D 端子对象
- 用户在 FreeCAD 中完成装配、摆放、接线
- 用户保存 FreeCAD 工程
- FreeCAD 生成
3d_to_2d.json - QET 在后续时机读取
3d_to_2d.json
一句话总结:
第一版先做“QET 导出 JSON + 打开 FreeCAD;FreeCAD 保存时回写 JSON”的文件交换闭环,不做强耦合实时同步。