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.

482 lines
11 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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
<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视图`
动作:
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”的文件交换闭环不做强耦合实时同步。