Merge remote-tracking branch 'origin/dev' into dev

dev
Zhaowenlong 4 weeks ago
commit affafbf5f6

1
.gitignore vendored

@ -51,6 +51,7 @@ install_manifest.txt
/bulid/
/build-*/
/cmake-build*/
/run/
/src/Tools/offlinedoc/localwiki/
/src/Tools/offlinedoc/*.txt
/conda/environment.yml

@ -110,7 +110,6 @@
},
"cabinet": {},
"devices": [],
"terminals": [],
"device_models": [],
"wires": []
}
@ -123,8 +122,7 @@
- `generated_at`:导出时间
- `source`:导出来源信息
- `cabinet`:当前图纸属性中绑定的机柜信息
- `devices`:设备实例绑定
- `terminals`:端子实例绑定
- `devices`:设备实例绑定,以及每个设备名下的接线处
- `device_models`:设备 3D 模型解析结果
- `wires`:导线起点/终点与标注快照
@ -193,7 +191,8 @@
{
"element_uuid": "string",
"instance_id": "string",
"display_tag": "string"
"display_tag": "string",
"terminals": []
}
```
@ -204,20 +203,22 @@
| `element_uuid` | 2D设备实例UUID | 是 | QET 图纸中的设备实例主键 |
| `instance_id` | 3D实例ID | 是 | FreeCAD 侧设备实例主键 |
| `display_tag` | 2D设备实例标注 | 否 | JSON 显示辅助字段,优先使用 2D 中设备标注作为 FreeCAD 树标签;为空时再退回 `instance_id` / `element_uuid` |
| `terminals` | 设备接线处列表 | 否 | 当前设备名下的 2D 接线处集合FreeCAD 会先按设备再导入接线处 |
### 6.4 说明
- 如果第一次进入 3D 时还没有 `instance_id`,允许先导出空字符串或缺省值
- FreeCAD 创建 3D 实例后,再在回写阶段补齐
- `display_tag` 不进入第一版数据库最小字段集,它只存在于交换 JSON 中,用来让 3D 树视图与 2D 标注更容易对上
- `terminals` 是设备级子结构,不再单独放在顶层
---
## 7. `terminals` 结构
## 7. `devices[].terminals` 结构
### 7.1 作用
`terminals` 负责表达:
`devices[].terminals` 负责表达:
> 一个 2D 端子实例,属于哪个 3D 设备实例。
@ -227,7 +228,8 @@
{
"terminal_uuid": "string",
"instance_id": "string",
"element_uuid": "string"
"element_uuid": "string",
"terminal_display": "string"
}
```
@ -238,6 +240,7 @@
| `terminal_uuid` | 2D端子UUID | 是 | QET 端子实例主键 |
| `instance_id` | 3D实例ID | 是 | 该端子所属的 3D 设备实例 |
| `element_uuid` | 2D设备实例UUID | 否 | JSON 导入辅助字段,帮助 FreeCAD 在首次没有 `instance_id` 时仍能知道端子属于哪个设备 |
| `terminal_display` | 接线处标注 | 否 | 2D 端子在图纸上的显示标注,供 FreeCAD 端子对象显示和槽位匹配使用 |
### 7.4 为什么这里允许带 `element_uuid`

@ -337,43 +337,55 @@ def _normalize_devices(payload):
index
)
)
device_terminals = item.get("terminals", [])
if device_terminals is None:
device_terminals = []
if not isinstance(device_terminals, list):
raise ExchangeValidationError(
"Field 'terminals' in device entry #{0} must be a list.".format(index)
)
normalized_terminals = []
for terminal_index, terminal_item in enumerate(device_terminals):
terminal_entry_label = "device entry #{0} terminal entry #{1}".format(
index, terminal_index
)
if not isinstance(terminal_item, dict):
raise ExchangeValidationError(
"{0} must be an object.".format(terminal_entry_label.capitalize())
)
terminal_uuid = _require_string(terminal_item, "terminal_uuid")
terminal_element_uuid = _optional_string(
terminal_item, "element_uuid", terminal_entry_label
) or element_uuid
normalized_terminals.append(
{
"terminal_uuid": terminal_uuid,
"instance_id": _normalize_instance_id(terminal_item)
or _normalize_instance_id(item),
"element_uuid": terminal_element_uuid,
"terminal_display": _optional_string(
terminal_item, "terminal_display", terminal_entry_label
),
}
)
normalized.append(
{
"element_uuid": element_uuid,
"instance_id": _normalize_instance_id(item),
"display_tag": display_tag.strip() if isinstance(display_tag, str) else "",
"terminals": normalized_terminals,
}
)
return normalized
def _normalize_terminals(payload):
terminals = payload.get("terminals", [])
if not isinstance(terminals, list):
raise ExchangeValidationError("Field 'terminals' must be a list.")
def _normalize_terminals(devices):
normalized = []
for index, item in enumerate(terminals):
if not isinstance(item, dict):
raise ExchangeValidationError(
"Terminal entry #{0} must be an object.".format(index)
)
terminal_uuid = _require_string(item, "terminal_uuid")
element_uuid = item.get("element_uuid", "")
if element_uuid and not isinstance(element_uuid, str):
raise ExchangeValidationError(
"Field 'element_uuid' in terminal entry #{0} must be a string.".format(
index
)
)
normalized.append(
{
"terminal_uuid": terminal_uuid,
"instance_id": _normalize_instance_id(item),
"element_uuid": element_uuid.strip() if isinstance(element_uuid, str) else "",
"terminal_display": _optional_string(item, "terminal_display", "terminal entry #{0}".format(index)),
}
)
for device in devices:
for terminal in device.get("terminals", []) or []:
normalized.append(dict(terminal))
return normalized
@ -562,14 +574,16 @@ def load_exchange_payload(json_path):
if not isinstance(schema_version, str) or not schema_version.strip():
raise ExchangeValidationError("Field 'schema_version' must be a string.")
normalized_devices = _normalize_devices(payload)
normalized = {
"schema_version": schema_version.strip(),
"project_uuid": project_uuid,
"generated_at": payload.get("generated_at", ""),
"source": payload.get("source", {}),
"cabinet": _normalize_cabinet(payload),
"devices": _normalize_devices(payload),
"terminals": _normalize_terminals(payload),
"devices": normalized_devices,
"terminals": _normalize_terminals(normalized_devices),
"device_models": _normalize_device_models(payload),
"wires": _normalize_wires(payload),
}

Loading…
Cancel
Save