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.
LightWork3D/docs/2D-3D交换协议.md

553 lines
14 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 交换协议(第一版)
本文档只描述 **QET 与 FreeCAD 之间的 JSON 交换格式**
不负责描述:
- 数据库建表
- 数据库存储约束
- 旧表兼容策略
这些内容统一放在:
- [数据库设计.md](D:\LightWork3D\docs\数据库设计.md)
---
## 1. 协议目标
第一版协议只解决下面这条最小闭环:
1. QET 能把当前项目中的设备实例和端子实例导出给 FreeCAD
2. FreeCAD 能根据导出结果创建或更新 3D 设备实例
3. FreeCAD 能根据导出结果创建或更新 3D 端子对象
4. FreeCAD 保存后,能把最小回写结果再交还给 QET
当前版本明确采用:
- **文件交换**
- **JSON 格式**
当前版本明确不采用:
- 实时 RPC
- HTTP API
- 直接双向数据库同步
---
## 2. 交换文件位置建议
建议所有交换文件都放到项目目录下:
```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 工程
---
## 3. 第一版 `2d_to_3d.json` 设计原则
### 3.1 最小主键集
第一版最小主键集只认:
- `project_uuid`
- `element_uuid`
- `terminal_uuid`
- `instance_id`
### 3.2 数据来源
`2d_to_3d.json` 不是数据库整表导出,而是:
> 从 QET 当前项目状态中整理出的、面向 FreeCAD 使用的最小交换快照。
也就是说:
- 它可以来自数据库
- 也可以来自当前内存状态
- 但最终输出是给 FreeCAD 消费的统一协议格式
### 3.3 协议可以比数据库稍微丰富
数据库设计要求尽量去冗余。
但 JSON 交换协议可以为了:
- 降低 FreeCAD 读取复杂度
- 提升可调试性
- 避免 FreeCAD 再回查 QET 内部数据库
而适当带上一些“已解析数据”。
这不等于数据库必须保留这些字段。
---
## 4. 第一版 `2d_to_3d.json` 顶层结构
推荐结构:
```json
{
"schema_version": "1.2",
"project_uuid": "string",
"generated_at": "2026-05-18T10:30:00+08:00",
"source": {
"app": "QET",
"version": "string"
},
"cabinet": {},
"devices": [],
"terminals": [],
"device_models": [],
"wires": []
}
```
说明:
- `schema_version`:协议版本,便于后续兼容升级
- `project_uuid`:项目主键
- `generated_at`:导出时间
- `source`:导出来源信息
- `cabinet`:当前图纸属性中绑定的机柜信息
- `devices`:设备实例绑定
- `terminals`:端子实例绑定
- `device_models`:设备 3D 模型解析结果
- `wires`:导线起点/终点与标注快照
---
## 5. `cabinet` 结构
### 5.1 作用
当前版本仍然以 **图纸** 作为交换单位。
但图纸属性本身已经绑定了一个机柜,因此 `2d_to_3d.json` 顶层会额外带上:
> 当前图纸绑定的机柜信息
这样 FreeCAD 在保持“按图纸导入设备/端子”的同时,也能知道:
- 这批设备属于哪个机柜
- 这个机柜当前绑定了哪个 3D 相对路径
### 5.2 推荐字段
```json
{
"location_id": 12,
"label": "C",
"name": "电容柜",
"display_text": "C - 电容柜",
"associated_fileset": "电容柜文件集",
"three_d_relative_path": "3D/Cabinets/C.FCStd",
"resolved_scene_path": "C:/Users/Admin/Documents/MingTuProject/xxx/3D/Cabinets/C.FCStd"
}
```
### 5.3 字段说明
| 字段 | 中文 | 必需 | 说明 |
| --- | --- | --- | --- |
| `location_id` | 机柜位置ID | 否 | 当前图纸属性绑定的机柜位置 ID |
| `label` | 机柜标注 | 否 | 用于 3D 侧快速识别机柜 |
| `name` | 机柜名称 | 否 | 机柜名称 |
| `display_text` | 机柜显示文本 | 否 | 等价于 QET 图纸属性界面中看到的机柜显示文本 |
| `associated_fileset` | 关联文件集 | 否 | 当前机柜位置绑定的文件集信息 |
| `three_d_relative_path` | 3D 相对路径 | 否 | 相对于工程目录保存的 3D 机柜路径 |
| `resolved_scene_path` | 已解析机柜场景路径 | 否 | QET 已解析出的本地场景文件路径,便于 FreeCAD 直接使用 |
### 5.4 说明
- `cabinet` 是一个 **图纸级上下文对象**
- 它不是设备绑定表或端子绑定表的扩张
- 它只描述“当前图纸绑定了哪个机柜,以及机柜当前对应哪个 3D 文件”
---
## 6. `devices` 结构
### 6.1 作用
`devices` 负责表达:
> 一个 2D 设备实例,对应哪个 3D 设备实例。
### 6.2 第一版字段
```json
{
"element_uuid": "string",
"instance_id": "string",
"display_tag": "string"
}
```
### 6.3 字段说明
| 字段 | 中文 | 必需 | 说明 |
| --- | --- | --- | --- |
| `element_uuid` | 2D设备实例UUID | 是 | QET 图纸中的设备实例主键 |
| `instance_id` | 3D实例ID | 是 | FreeCAD 侧设备实例主键 |
| `display_tag` | 2D设备实例标注 | 否 | JSON 显示辅助字段,优先使用 2D 中设备标注作为 FreeCAD 树标签;为空时再退回 `instance_id` / `element_uuid` |
### 6.4 说明
- 如果第一次进入 3D 时还没有 `instance_id`,允许先导出空字符串或缺省值
- FreeCAD 创建 3D 实例后,再在回写阶段补齐
- `display_tag` 不进入第一版数据库最小字段集,它只存在于交换 JSON 中,用来让 3D 树视图与 2D 标注更容易对上
---
## 7. `terminals` 结构
### 7.1 作用
`terminals` 负责表达:
> 一个 2D 端子实例,属于哪个 3D 设备实例。
### 7.2 第一版字段
```json
{
"terminal_uuid": "string",
"instance_id": "string",
"element_uuid": "string"
}
```
### 7.3 字段说明
| 字段 | 中文 | 必需 | 说明 |
| --- | --- | --- | --- |
| `terminal_uuid` | 2D端子UUID | 是 | QET 端子实例主键 |
| `instance_id` | 3D实例ID | 是 | 该端子所属的 3D 设备实例 |
| `element_uuid` | 2D设备实例UUID | 否 | JSON 导入辅助字段,帮助 FreeCAD 在首次没有 `instance_id` 时仍能知道端子属于哪个设备 |
### 7.4 为什么这里允许带 `element_uuid`
注意:
- `element_uuid` **不是**第一版端子绑定表的数据库字段扩张
- 它只是交换 JSON 中的上下文辅助字段
原因:
- 当某些设备第一次进入 3D、暂时还没有 `instance_id`
- FreeCAD 仍需要知道该端子属于哪个 2D 设备实例
所以这里允许 JSON 比数据库稍微丰富一些。
### 7.5 为什么第一版不带更多字段
第一版先不强制包含:
- `terminal_key`
- `connection_point_key`
- `symbol_terminal`
---
## 8. `wires` 结构
### 8.1 作用
`wires` 负责表达:
> 当前图纸中每一条逻辑导线,连接了哪两个端子,以及它当前的导线标注。
第一版先只解决:
- 导线是谁
- 起点端子是谁
- 终点端子是谁
- 当前导线标注是什么
不在这一版里解决:
- 3D 路径点
- 3D 线束位姿
- 导线自动布线路由结果
### 8.2 第一版字段
```json
{
"wire_id": "string",
"net_uuid": "string",
"group_uuid": "string",
"wire_mark": "string",
"wire_mark_is_manual": false,
"wire_style_id": 0,
"start_element_uuid": "string",
"start_terminal_uuid": "string",
"end_element_uuid": "string",
"end_terminal_uuid": "string",
"start_terminal_display": "string",
"end_terminal_display": "string"
}
```
### 8.3 字段说明
| 字段 | 中文 | 必需 | 说明 |
| --- | --- | --- | --- |
| `wire_id` | 导线交换ID | 是 | JSON 交换层稳定标识;按 `DirectionInfo` 生成,格式为 `direction:${net_uuid}:${index}` |
| `net_uuid` | 网络UUID | 否 | 当前逻辑导线所属网络 |
| `group_uuid` | 网络分组UUID | 否 | 当前逻辑导线所属网络分组 |
| `wire_mark` | 导线标注 | 否 | 导线当前标注;为空时导出为 `无标注导线` |
| `wire_mark_is_manual` | 导线标注是否手工 | 否 | 是否手工修改过导线标注 |
| `wire_style_id` | 导线样式ID | 否 | 取 `start_terminal` 所连接导线的样式 ID |
| `start_element_uuid` | 起点设备UUID | 是 | 起点端子所属 2D 设备实例 |
| `start_terminal_uuid` | 起点端子UUID | 是 | 起点 2D 端子实例 |
| `end_element_uuid` | 终点设备UUID | 是 | 终点端子所属 2D 设备实例 |
| `end_terminal_uuid` | 终点端子UUID | 是 | 终点 2D 端子实例 |
| `start_terminal_display` | 起点端子显示号 | 否 | 起点端子在 QET 中的显示编号 |
| `end_terminal_display` | 终点端子显示号 | 否 | 终点端子在 QET 中的显示编号 |
### 8.4 说明
- `wire_mark` 来源于当前图纸导线方向信息里的 `wireMark`
- 它是**导线标注**
- 不是设备实例标注,也不是符号设备标注
- `wire_id` 代表一条 `DirectionInfo`
- 不再混入几何 `Conductor` UUID 作为导线主标识
- `wire_style_id` 只取 `start_terminal` 所连接导线的样式
- 不按整条几何路径聚合多个样式
- `wires` 是交换 JSON 的扩展层
- 不意味着第一版数据库绑定表要新增导线绑定表
- 第一版 FreeCAD 侧即使暂时不消费 `wires`
- 也建议先由 QET 输出出来,方便后续 3D 布线与调试
---
## 9. `device_models` 结构
### 9.1 作用
`device_models` 不是绑定表本身,而是:
> QET 在导出时,顺手把设备对应 3D 模型资源解析出来,减少 FreeCAD 再回查 QET 内部数据库的复杂度。
### 9.2 第二步设备导入推荐字段
```json
{
"element_uuid": "string",
"device_id": 123,
"parts_3d": "string",
"resolved_model_path": "string"
}
```
### 8.3 字段说明
| 字段 | 中文 | 必需 | 说明 |
| --- | --- | --- | --- |
| `element_uuid` | 2D设备实例UUID | 是 | 与 `devices` 关联 |
| `device_id` | 设备类型ID | 否 | QET 设备主数据 ID |
| `parts_3d` | 3D模型资源URI | 否 | 原始资源引用,来自 `device_3d_asset.uri``device_attribute.parts_3d` |
| `resolved_model_path` | 已解析模型路径 | 是 | QET 已经解析好的本地模型文件路径FreeCAD 第二步直接用它导入 STEP / FCStd |
### 8.4 为什么 `resolved_model_path` 是第二步关键字段
第二步开始FreeCAD 不再只做 JSON 校验,而要真正导入设备模型。
如果没有 `resolved_model_path`FreeCAD 就必须自己理解和回查:
1. `element_uuid`
2. `device_id`
3. `device_3d_asset`
4. `device_attribute.parts_3d`
5. 本地路径解析规则
这会让 FreeCAD 过度耦合 QET 内部数据库结构。
所以第二步推荐由 QET 在导出时直接给出:
- `resolved_model_path`
让 FreeCAD 只负责消费结果。
### 8.5 `.FCStd` 设备资产支持
A 方案下,`.FCStd` 是正式可复用设备资产格式。QET 导出时不需要解析 `.FCStd` 内部内容,只需要把设备资产路径解析成 `resolved_model_path` 交给 FreeCAD。
示例:
```json
{
"element_uuid": "elem-1001",
"device_id": 123,
"parts_3d": "models/mccb/MCCB_1P.FCStd",
"resolved_model_path": "C:/Users/Admin/Documents/MingTuProject/models/mccb/MCCB_1P.FCStd"
}
```
FreeCAD 根据 `resolved_model_path` 的扩展名导入 `.FCStd`,并在导入后的设备组内优先扫描 `Role="Terminal"` 的 LCS 作为模板端子槽位。
如果 QET 侧已经在 `device_3d_asset.format` 中保存了资源格式,可以在后续协议中追加可选字段:
```json
{
"format": "fcstd"
}
```
第一版 FreeCAD 不应强依赖该字段,避免旧版本 JSON 缺少 `format` 时无法导入。
### 8.6 为什么这里允许同时带 `parts_3d`
注意:
- **数据库设计**里我们已经决定不在绑定表冗余保存 `asset_uri`
- 但**交换协议**里允许带 `parts_3d`
原因是:
- JSON 是导出快照
- 不是绑定数据库本身
- 这样 FreeCAD 读取时更简单
也就是说:
> 数据库去冗余
> JSON 可适度带已解析结果
---
## 8. 第一版 `2d_to_3d.json` 完整样例
```json
{
"schema_version": "1.0",
"project_uuid": "proj-001",
"generated_at": "2026-05-18T10:30:00+08:00",
"source": {
"app": "QET",
"version": "1.0"
},
"devices": [
{
"element_uuid": "elem-1001",
"instance_id": "fc-inst-0001"
}
],
"terminals": [
{
"terminal_uuid": "term-2001",
"instance_id": "fc-inst-0001",
"element_uuid": "elem-1001"
},
{
"terminal_uuid": "term-2002",
"instance_id": "fc-inst-0001",
"element_uuid": "elem-1001"
}
],
"device_models": [
{
"element_uuid": "elem-1001",
"device_id": 123,
"parts_3d": "models/mccb/model.step",
"resolved_model_path": "C:/Users/Admin/Documents/MingTuProject/models/mccb/model.step"
}
]
}
```
---
## 9. 第一版 `3d_to_2d.json` 建议
第一版回写建议同样保持最小化。
推荐结构:
```json
{
"schema_version": "1.0",
"project_uuid": "string",
"generated_at": "2026-05-18T11:00:00+08:00",
"instances": [],
"terminals": []
}
```
### 9.1 `instances`
```json
{
"element_uuid": "string",
"instance_id": "string"
}
```
### 9.2 `terminals`
```json
{
"terminal_uuid": "string",
"instance_id": "string"
}
```
### 9.3 说明
第一版不回写:
- 3D 位姿
- 装配层级
- 几何路径
- 线槽信息
这些仍以 FreeCAD 文档为准。
---
## 10. 第一版推荐交互流程
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`
---
## 11. 当前推荐结论
第一版协议建议明确分层:
- **数据库设计**:尽量去冗余
- **JSON 协议**:允许带少量已解析结果,方便 FreeCAD 使用
一句话总结:
> 第一版先把 `2d_to_3d.json` 做成“面向 FreeCAD 的最小项目快照”,而不是数据库整表镜像。