10 KiB
2D / 3D 交换协议(第一版)
本文档只描述 QET 与 FreeCAD 之间的 JSON 交换格式。
不负责描述:
- 数据库建表
- 数据库存储约束
- 旧表兼容策略
这些内容统一放在:
- 数据库设计.md
1. 协议目标
第一版协议只解决下面这条最小闭环:
- QET 能把当前项目中的设备实例和端子实例导出给 FreeCAD
- FreeCAD 能根据导出结果创建或更新 3D 设备实例
- FreeCAD 能根据导出结果创建或更新 3D 端子对象
- FreeCAD 保存后,能把最小回写结果再交还给 QET
当前版本明确采用:
- 文件交换
- JSON 格式
当前版本明确不采用:
- 实时 RPC
- HTTP API
- 直接双向数据库同步
2. 交换文件位置建议
建议所有交换文件都放到项目目录下:
<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_uuidelement_uuidterminal_uuidinstance_id
3.2 数据来源
2d_to_3d.json 不是数据库整表导出,而是:
从 QET 当前项目状态中整理出的、面向 FreeCAD 使用的最小交换快照。
也就是说:
- 它可以来自数据库
- 也可以来自当前内存状态
- 但最终输出是给 FreeCAD 消费的统一协议格式
3.3 协议可以比数据库稍微丰富
数据库设计要求尽量去冗余。
但 JSON 交换协议可以为了:
- 降低 FreeCAD 读取复杂度
- 提升可调试性
- 避免 FreeCAD 再回查 QET 内部数据库
而适当带上一些“已解析数据”。
这不等于数据库必须保留这些字段。
4. 第一版 2d_to_3d.json 顶层结构
推荐结构:
{
"schema_version": "1.1",
"project_uuid": "string",
"generated_at": "2026-05-18T10:30:00+08:00",
"source": {
"app": "QET",
"version": "string"
},
"cabinet": {},
"devices": [],
"terminals": [],
"device_models": []
}
说明:
schema_version:协议版本,便于后续兼容升级project_uuid:项目主键generated_at:导出时间source:导出来源信息cabinet:当前图纸属性中绑定的机柜信息devices:设备实例绑定terminals:端子实例绑定device_models:设备 3D 模型解析结果
5. cabinet 结构
5.1 作用
当前版本仍然以 图纸 作为交换单位。
但图纸属性本身已经绑定了一个机柜,因此 2d_to_3d.json 顶层会额外带上:
当前图纸绑定的机柜信息
这样 FreeCAD 在保持“按图纸导入设备/端子”的同时,也能知道:
- 这批设备属于哪个机柜
- 这个机柜当前绑定了哪个 3D 相对路径
5.2 推荐字段
{
"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 第一版字段
{
"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 第一版字段
{
"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_keyconnection_point_keysymbol_terminalwire_labelnet_id
原因:
- 这些字段当前都不是 FreeCAD 第一版创建端子对象的硬前提
- 端子语义可以通过
terminal_uuid回查 QET - 先把绑定关系打通,比先堆字段更重要
8. device_models 结构
8.1 作用
device_models 不是绑定表本身,而是:
QET 在导出时,顺手把设备对应 3D 模型资源解析出来,减少 FreeCAD 再回查 QET 内部数据库的复杂度。
8.2 第二步设备导入推荐字段
{
"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 就必须自己理解和回查:
element_uuiddevice_iddevice_3d_assetdevice_attribute.parts_3d- 本地路径解析规则
这会让 FreeCAD 过度耦合 QET 内部数据库结构。
所以第二步推荐由 QET 在导出时直接给出:
resolved_model_path
让 FreeCAD 只负责消费结果。
7.4 为什么这里允许同时带 parts_3d
注意:
- 数据库设计里我们已经决定不在绑定表冗余保存
asset_uri - 但交换协议里允许带
parts_3d
原因是:
- JSON 是导出快照
- 不是绑定数据库本身
- 这样 FreeCAD 读取时更简单
也就是说:
数据库去冗余
JSON 可适度带已解析结果
8. 第一版 2d_to_3d.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 建议
第一版回写建议同样保持最小化。
推荐结构:
{
"schema_version": "1.0",
"project_uuid": "string",
"generated_at": "2026-05-18T11:00:00+08:00",
"instances": [],
"terminals": []
}
9.1 instances
{
"element_uuid": "string",
"instance_id": "string"
}
9.2 terminals
{
"terminal_uuid": "string",
"instance_id": "string"
}
9.3 说明
第一版不回写:
- 3D 位姿
- 装配层级
- 几何路径
- 线槽信息
这些仍以 FreeCAD 文档为准。
10. 第一版推荐交互流程
- 用户在 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
11. 当前推荐结论
第一版协议建议明确分层:
- 数据库设计:尽量去冗余
- JSON 协议:允许带少量已解析结果,方便 FreeCAD 使用
一句话总结:
第一版先把
2d_to_3d.json做成“面向 FreeCAD 的最小项目快照”,而不是数据库整表镜像。