feat(freecad): 完善自动布线路径网络与诊断

dev
Zhaowenlong 2 weeks ago
parent 9d31edff70
commit 3615f62442

@ -333,6 +333,11 @@
- 不再混入几何 `Conductor` UUID 作为导线主标识
- `wire_style_id` 只取 `start_terminal` 所连接导线的样式
- 如果 FreeCAD 需要直接渲染导线颜色/线宽,可在顶层额外提供 `wire_style_database_path`,指向包含 `wire_properties` 表的项目 SQLite 数据库FreeCAD 会按 `wires[].wire_style_id -> wire_properties.id` 查询样式。该字段是可选字段,也可以通过环境变量 `QET_WIRE_PROPERTIES_DB` 提供。
- 如果顶层没有提供数据库路径FreeCAD 导入 `2d_to_3d.json` 时会尝试扫描 JSON 同目录下的 `.sqlite / .sqlite3 / .db` 文件;只有确认其中存在 `wire_properties` 表时,才会自动使用该库作为 `wire_style_database_path`,并在 `_qet_exchange_summary.wire_style_database_path`、批量布线 report 与 compact 诊断中记录最终路径。这是 FreeCAD 侧便利推断,不是 QET 必填输出字段。
- 当前 FreeCAD 会读取 `wire_properties.line_color / line_width / diameter_mm / line_type / area_or_spec` 做第一版显示渲染;颜色支持 `#RRGGBB`、`RRGGBB`、`0xRRGGBB`、`#AARRGGBB`、`0xAARRGGBB`、十进制颜色整数、`rgb(...)`、逗号 RGB 和常见英文色名ARGB 的 alpha 暂不参与线颜色。显示线宽优先使用 `line_width`,缺失时用 `diameter_mm`,两者都缺失时会尝试从 `area_or_spec``2.5mm2 / 2.5mm^2 / 2.5mm²` 等截面积文本估算。
- 查到样式后FreeCAD 会把常用样式字段展开成 3D 导线对象属性:`QetWireStyleName`、`QetWireSpecText`、`QetWireColorText`、`QetWireLineType`、`QetWireType`、`QetWireFormat`、`QetWireDiameterMm`、`QetWireLineWidth`;这些是 3D 侧查看/调试属性,不是 QET 必填输出字段。
- FreeCAD 会在生成导线对象上写入 `QetWireStyleStatus=Resolved/Missing`,并同步写入 `QetRouteDiagnosticsJson.wire_style_status`,用于判断 `wire_style_id` 是否成功回查到 `wire_properties`;批量报告会汇总 `wire_style_status_counts`,已解析样式会进入批量 `routes[].wire_style` 和 compact `route_samples[].wire_style`compact 诊断的 `missing_wire_style_samples[]` 会列出缺失样式样例,`route_samples[]` 会保留 `wire_style_id``wire_style_status`。这是 3D 侧诊断结果,不是 QET 必填输出字段。
- 不按整条几何路径聚合多个样式
- `wires` 是交换 JSON 的扩展层

@ -126,6 +126,45 @@ terminal_uuid
导线规格、颜色、线耳等导线数据由 QET 提供FreeCAD 第一版只消费和保留,不在 3D 侧重新发明导线主数据。
#### 1.4.4 空机柜和未装配状态的处理
自动布线必须基于 FreeCAD 文档中的真实 3D 位姿。左侧树目录中存在设备、线槽或导轨,只表示对象已经导入,并不表示它们已经装配到机柜内,也不表示已经形成可走线的路径网络。
因此,下面状态不能用来判断自动布线最终效果:
1. 设备、端子排、小型断路器仍停留在导入位置,尚未摆到导轨或安装板上。
2. 线槽、导轨尚未贴到机柜背板或安装板。
3. 安装板、柜内空间或柜体没有作为柜内边界/布线区域参与识别。
4. 没有生成 `WireDuct`、`RoutingRange`、`UserPath` 或 `TerminalAccess` carrier。
当前 FreeCAD 自动布线预检会把这些前置状态显式报告出来:
```text
路径网络0 段
布线源:未识别到线槽/布线面/用户路径
柜内边界:未标记
```
点击自动布线面板中的 `检查布线准备度`FreeCAD 还会在树目录 `QETWiring_05_Diagnostics` 下写入一个 `RoutingPreflight` 诊断对象。该对象会保存 `QetProjectUuid``QetDiagnosticOk` 表示本次诊断是否通过;`QetDiagnosticIssueCodes` 直接列出问题码;`QetDiagnosticIssueLabels` 直接列出中文问题标签;`QetDiagnosticMessage` 保存中文摘要;`QetDiagnosticJson` 保存压缩后的最新预检结果,包括导线任务数量、工程端子数量、路径网络段数、布线源摘要、柜内边界摘要、导线样式库状态、问题码 `issue_codes`、缺失端点样例等。重复检查时旧的 `RoutingPreflight` 会被替换,只保留最新一次结果。
`RoutingPreflight` 还会附带 compact 路径网络诊断。若已标记 `CabinetInterior`,但主路径 carrier 或工程端子越出柜内边界,预检报告会直接追加 `route_carriers_outside_boundary``terminals_outside_boundary`,并在中文摘要中给出“越界路径”或“越界端子”样例。这样用户在生成导线前就能发现装配态问题。
预检的端点缺失示例会同时显示导线标签和端子对,例如 `导线 N4111terminal-start -> terminal-missing`。这用于第一时间判断问题来自哪条 QET 导线任务、哪个端子 UUID 没有绑定到 FreeCAD 工程端子。
看到这类提示时,应先完成最小装配闭环,再测试布线结果:
```text
安装板/背板
-> 导轨
-> 线槽或用户主路径
-> 真实 QET 设备实例
-> 工程端子
-> 布线路径网络
-> 自动布线连接
```
如果现场本来没有线槽也需要用草图、Draft 线或线段定义柜内主路径,并点击 `选中路径作为用户路径`,让它生成 `UserPath` carrier。否则算法没有可走的主网络只能报告缺少路径网络。
### 1.5 设备脚号与 3D 脚点绑定方案
设备脚号与 3D 脚点绑定分成两层:模板槽位绑定和工程端子绑定。
@ -199,6 +238,37 @@ QET 侧还需要保证导线任务中继续提供:
其中导线规格、颜色、线耳等导线主数据以后由 `wire_style_id` 或等价字段回查 QET。
当前自动布线已经支持第一版导线样式渲染:`wires[].wire_style_id` 对应 QET 项目数据库 `wire_properties.id`。FreeCAD 可通过自动布线 options 中的 `wire_style_database_path`、`2d_to_3d.json` 顶层 `wire_style_database_path`,或环境变量 `QET_WIRE_PROPERTIES_DB`,打开项目 SQLite 数据库并查询 `wire_properties`。查询时优先匹配当前 `project_uuid`,读取到的样式会保存到已生成导线对象的 `QetWireStyleJson``QetRouteDiagnosticsJson.wire_style`
如果 `2d_to_3d.json` 没有显式提供数据库路径FreeCAD 导入交换文件时会尝试扫描 JSON 同目录下的 `.sqlite / .sqlite3 / .db` 文件;只有确认数据库中存在 `wire_properties` 表时,才会自动写入 `wire_style_database_path`。导入完成后,`_qet_exchange_summary.wire_style_database_path` 会记录最终使用的路径;自动布线面板摘要、批量布线 report、compact 诊断和中文报告都会显示“导线样式库:<路径>”,便于检查 FreeCAD 是否识别到样式库。这是 FreeCAD 侧便利推断,不要求 QET 修改输出协议。
第一版使用字段如下:
```text
wire_properties.id -> wires[].wire_style_id
wire_properties.name -> 样式名称,写入诊断
wire_properties.line_color -> FreeCAD 导线显示颜色,支持 #RRGGBB / RRGGBB / 0xRRGGBB / #AARRGGBB / 0xAARRGGBB / 十进制颜色整数 / rgb(...) / 逗号 RGB / 常见英文色名ARGB 的 alpha 暂不参与线颜色
wire_properties.line_type -> FreeCAD 导线线型,支持 Solid / DashLine / DotLine / DashDotLine 等常见写法
wire_properties.line_width -> FreeCAD 视图线宽,优先使用
wire_properties.diameter_mm -> line_width 缺失时作为显示线宽回退
wire_properties.area_or_spec -> 导线规格文本,写入诊断;当 line_width 和 diameter_mm 都缺失时,支持从 2.5mm2 / 2.5mm^2 / 2.5mm² 等文本估算显示线宽
```
查到样式后FreeCAD 除了保存完整 `QetWireStyleJson`,还会把常用字段展开到导线对象属性,方便在 FreeCAD 属性面板中直接查看:
```text
QetWireStyleName -> 样式名称
QetWireSpecText -> 导线规格文本
QetWireColorText -> 原始颜色文本
QetWireLineType -> 原始线型文本
QetWireType -> 导线类型
QetWireFormat -> 导线格式
QetWireDiameterMm -> 直径
QetWireLineWidth -> 显示线宽
```
如果查不到样式,导线仍按默认蓝色显示,并在导线对象 `QetWireStyleStatus``QetRouteDiagnosticsJson.wire_style_status` 中写入 `Missing`;查到样式时写入 `Resolved`。批量布线报告会汇总 `wire_style_status_counts`,中文报告会提示“导线样式:缺失 N 条”,并带上第一条缺失样例,例如“示例导线 N2 样式 404”。已解析样式会进入批量 `routes[].wire_style` 和 compact `route_samples[].wire_style`compact 诊断也会输出 `missing_wire_style_samples[]``route_samples[]` 会保留 `wire_style_id``wire_style_status`,方便定位是哪条线缺少样式。这个状态由 FreeCAD 根据查询结果生成,不要求 QET 新增字段。如果导线存在碰撞告警,颜色仍会被红色告警覆盖,但线宽可以继续使用样式值。当前这是“视图线宽/颜色”的渲染,不是带真实半径的 3D 圆管导线。
### 1.6 区域与批量排布方案
#### 1.6.1 设备区域/柜内区域
@ -303,14 +373,14 @@ QetTerminalUuid = local:<instance_id>:<slot>
QetTerminalBindingMode = local
```
这类端子只有空间槽位,没有 2D 电气语义,不能直接用于批量导线任务。正式布线前需要执行“检查/绑定工程端子”。系统会根据导线任务中的 `start/end_terminal_uuid`、`start/end_instance_id`、`start/end_element_uuid` 和端子显示号,在对应 3D 设备下查找 local 端子或模板槽位;匹配成功后写入:
这类端子只有空间槽位,没有 2D 电气语义,不能直接用于批量导线任务。正式布线前需要执行“检查/绑定工程端子”。系统会优先根据导线任务中的 `start/end_terminal_uuid`、`start/end_element_uuid` 和端子显示号,在对应 3D 设备下查找 local 端子或模板槽位;如果任务或 `devices[]` 中已有 `instance_id`,只作为 FreeCAD 侧辅助定位信息使用,不作为第一版 QET `wires[]` 的必填字段。匹配成功后写入:
```text
QetTerminalUuid = <QET terminal_uuid>
QetTerminalBindingMode = qet
```
如果导线任务缺少实例定位信息,或 QET 端子显示号与模板槽位名称不一致,绑定会跳过并给出诊断。
如果导线任务缺少 `element_uuid`、找不到对应 3D 设备,或 QET 端子显示号与模板槽位名称不一致,绑定会跳过并给出诊断。
### 3.2 路由路径 Carrier
@ -330,9 +400,11 @@ Points = [Vector, Vector, ...]
QetRouteSourceName = <FreeCAD source object name>
QetRouteSourceLabel = <FreeCAD source object label>
QetRouteSourceKind = "WireDuct" | "RoutingRange" | "WiringCutOut" | "UserPath" | "TerminalAccess"
QetRouteSourcePathIndex = "1" | "2" | ...
```
这些属性只用于 FreeCAD 文档内部刷新和清理,不写入数据库,也不要求 QET 提供。
其中 `QetRouteSourcePathIndex` 主要用于同一个草图拆成多条 `UserPath` 时区分第几条源路径,便于诊断和路径示例回溯。只有同一源对象生成多条 `UserPath` 时才保留该序号;草图刷新后只剩单条路径时会清空旧序号。最终导线的 `QetRouteTrackJson` segment carrier payload 和公开 `carrier_payload()` 都会同步输出 `source_path_index`,便于导出诊断 JSON 后直接定位源草图路径。批量布线中文报告的“路径示例”在存在序号时会显示为 `源路径标签(路径1)`、`源路径标签(路径2)` 这类格式;普通单路径不写该字段,因此不会显示 `(路径1)`
carrier 统一放在:
@ -543,19 +615,125 @@ QetWiringCutOutBridgeExtensionMm = 20.0
生成导线的 `QetRouteTrackJson` 会记录实际经过的 carrier。carrier 如果来自线槽、过线孔、支撑面或端子接入源对象route track 中还会保留 `source_name`、`source_label`、`source_kind`,用于手动测试时追踪“这段线实际走过哪个 3D 源对象”。route track 同时记录 carrier 的 `capacity`,用于后续核对多根线共路、容量偏好和绕行行为。
生成导线对象的树目录 Label 会尽量包含导线标识、起终点端子和状态,例如 `N4111: terminal-start -> terminal-end (Routed)``N4111: terminal-start -> terminal-end (CollisionWarning)`。这里的端点优先使用 FreeCAD 端子对象 Label在 QET 导入端子中通常就是 `terminal_uuid`。这样手动测试截图时,可以直接从树目录定位是哪条 QET 导线、哪两个端子以及当前状态。
单根导线对象会写入 `QetRouteIssueCodes``QetRouteIssueLabels`,用于汇总这根线自身的问题。当前会把长接入、碰撞/安全间隙、路径兜底、容量压力、柜内越界和候选入口碰撞风险映射成与批量诊断一致的问题码,例如 `long_terminal_access`、`collision_warnings`、`route_quality_warnings`、`route_capacity_pressure`、`route_candidate_boundary_violations`。`QetRouteDiagnosticsJson.issue_codes` 和 `issue_labels` 会保存同一份数组,便于导出 JSON 后按单线筛选。
为了让手动测试不用每次展开 `QetRouteTrackJson`,单根导线对象会同步写入 `QetRouteSourceLabels``QetRouteCarrierNames`。`QetRouteSourceLabels` 优先显示源对象标签,例如线槽、黄色草图路径或用户路径标签;同一个源草图拆成多条 `UserPath` 时会显示为 `源路径标签(路径2)` 这类格式。`QetRouteCarrierNames` 保留实际经过的 carrier 对象名,便于在树目录中进一步定位。完整数组也会写入 `QetRouteDiagnosticsJson.route_source_labels``QetRouteDiagnosticsJson.route_carrier_names`
如果导线实际走过自动桥接边,`QetRouteTrackJson` 中对应段会记录 `is_bridge=true`,并汇总 `bridged_segments`。批量布线报告和诊断对象中的 route sample 会优先使用这个本路线实际桥接数量;旧诊断缺少该字段时,再回退到整张路径网络的桥接数量。自动桥接段是虚拟连通边,不代表真实线槽截面,因此不参与容量最小值计算,也不参与共路 lane 计数、路径复用惩罚、真实 carrier 类型汇总、诊断样例 carrier 列表和路径质量提示。
批量生成布线连接后,面板/控制台报告会从第一条可追踪路径中提取一条“路径示例”,显示导线经过的源对象标签,便于快速确认线路是否进入了预期线槽、过线孔和支撑面。路径示例会跳过 `is_bridge=true` 的虚拟桥接段,避免把自动补出来的连通边误显示成真实线槽或用户路径。
批量生成布线连接后,面板/控制台报告会从第一条可追踪路径中提取一条“路径示例”,显示导线经过的源对象标签,便于快速确认线路是否进入了预期线槽、过线孔和支撑面。如果某个 carrier 没有 `source_label` / `source_name`,路径示例会回退显示 carrier 自身的 `label` / `name`,避免手动创建或旧版本 carrier 完全无法定位。路径示例会跳过 `is_bridge=true` 的虚拟桥接段,避免把自动补出来的连通边误显示成真实线槽或用户路径。
批量布线报告还会汇总本批次路线中使用到的路径网络特征:如果路线依赖相邻/投影主路径自动桥接,报告会显示自动桥接段数;如果主动避障时屏蔽了穿过障碍包围盒的网络边,报告会显示避障屏蔽段数。这里采用路线中的最大值展示,避免多条导线共用同一网络时重复累加。
一键执行“生成布线连接”时,系统会在更新路径网络后附带一份 `routing_path_network_diagnostic` 摘要到批量报告中。即使用户没有单独点击路径网络检查,报告也会显示“路径网络检查提示”,把空路径网络、路径对象几何无效、仅使用布线面兜底、端子局部路径无效、端子接入过长等问题带出来。
中文报告会区分 `布线布局空间``当前路径网络`。前者表示本次操作中新生成或刷新的 carrier 数量,因此已有线槽路径复用时可能显示线槽路径 0 条;后者来自实际构建的路径网络 `route_network_carrier_kind_counts`,会显示当前参与求路的 `WireDuct / WireDuctOpenEnd / UserPath / TerminalAccess / RoutingRange` 等总数。判断是否识别到线槽或用户路径时,以 `当前路径网络``路径采用` 为准。
`RoutingConnectionBatch.QetDiagnosticJson.route_samples[]` 会保留少量导线样例。每个 route sample 除了基础导线、端子、路径来源和 network 数据外,还会同步写入 `access`、`collision_summary`、`quality`、`capacity`、`boundary` 等状态分组,字段口径与单根导线对象属性一致。这样手动测试后即使不逐根选中导线,也可以直接从诊断对象 JSON 看出样例线是否存在长接入、穿模/安全间隙、路径兜底、容量压力或柜内越界。
每个 route sample 还会保留 `wire_object_label`,其值与 FreeCAD 左侧树目录中生成导线对象的 Label 一致,例如 `N4111: terminal-start -> terminal-end (CollisionWarning)`。当用户把诊断 JSON 发回开发侧时,可以用这个字段直接对应到树目录里的导线对象,减少反复按 UUID 查找。
各类 warning sample 也会尽量保留 `wire_object_label`,包括接入距离、路径质量、候选入口碰撞风险、柜内边界、路径约束、容量压力和碰撞样例。这样不论问题来自哪一类诊断,都可以用同一个字段回到 `QETWiring_04_Routed` 下定位导线。
中文报告会区分“定位类示例”和“统计类提示”:碰撞示例、缺失端点示例优先显示 `wire_object_label`,方便在 FreeCAD 树目录中直接查找;导线样式缺失、路径示例、总量统计等仍使用短导线号,避免一行报告被完整对象 Label 拉得过长。
`route_samples[]` 不是简单截取前几条导线,而是优先保留带 `issue_codes` 的问题路线;问题数量相同或没有问题时,再按原生成顺序保留。这样当一次布线有很多正常线、少量异常线时,压缩诊断对象仍会优先给出异常样例,避免手动测试复制 JSON 后看不到真正需要处理的导线。
一键执行“生成布线连接”时,系统会在更新路径网络后附带一份 `routing_path_network_diagnostic` 摘要到批量报告中,并会按诊断建议先生成必要的 `UserPath` 桥接。脚本或调试场景直接调用 `route_eplan_connection_tasks()` 时,也会先执行同一类诊断桥接,保证任务入口和面板入口都优先尝试把孤立线槽接入端子主网络。直接从 QET payload 生成批量布线时,如果发现导线已经生成但没有使用线槽、`UserPath` 或过线孔主路径,也会自动补一次路径网络诊断,并把线槽未接入端子主网络、桥接建议等根因写回同一份批量报告。即使用户没有单独点击路径网络检查,报告也会显示“路径网络检查提示”,把空路径网络、路径对象几何无效、仅使用布线面兜底、端子局部路径无效、端子接入过长、端子越出柜内边界、路径越出柜内边界等问题带出来。如果路径源本身越出 `CabinetInterior`,批量报告会额外显示“越界路径:<路径标签> N 个越界点”,便于直接定位错误的线槽中心线或 `UserPath`。如果工程端子越出边界,批量报告会显示“越界端子:<端子对象/UUID> N 个越界点”,便于直接定位未装配到柜内的设备端子。
真实工程中路径 carrier 数量可能达到数百个,入口候选组合会直接影响批量布线耗时。第一版保留单根布线的 `network_entry_candidate_limit`,同时在批量布线中增加 `batch_network_entry_candidate_limit`,默认按更保守的候选数求路,避免 `入口候选 x 出口候选 x 导线数量` 过度放大。批量入口候选还增加了总量保护 `batch_network_entry_total_candidate_limit`,当前默认值为 6它会限制单根导线最终参与组合评分的入口/出口候选总量,避免“距离候选 + 柜内候选 + 避障候选”叠加后把一次布线放大成几十次 Dijkstra 求路。缺路径重试仍可以按 `missing_route_retry_candidate_limit` 临时放宽候选数量,但正常批量路径优先受总上限保护。批量布线还会复用本次已构建的基础路径图,避免每根导线重复构建同一套网络;碰撞障碍物也会先收集成候选缓存,再按每根导线的端点设备和端点附近规则过滤,避免重复扫描数千个模型对象。当前批量默认采用性能优先的 `batch_avoid_obstacles=false`:不额外构建障碍过滤图,但仍会在生成后做碰撞诊断并输出 `collision_warnings`;需要更激进避障时再开启批量障碍过滤。相关参数会写入 `RoutingConnectionBatch.QetDiagnosticJson.batch_network_entry_candidate_limit`、`batch_network_entry_total_candidate_limit`、`batch_avoid_obstacles` 和 `batch_obstacle_candidates`,便于手测时确认当前性能保护是否生效。
线槽接入主网络采用保守桥接策略。当前 `adjoining_duct_tolerance` 默认只允许 5mm 内的相邻端点或端点到主路径中段投影自动桥接,不会为了让线槽被使用而把远距离线槽强行接到布线面或端子接入网络。这样可以避免误把柜内无关路径连成一个错误网络。若诊断出现 `wire_ducts_without_terminal_access / 线槽未接入端子主网络`,第一版推荐用户显式添加 UserPath、线槽开口或桥接路径诊断会在 `bridge_suggestion` 中给出建议连接的两段 carrier、最近点和距离。面板已提供 `按诊断建议生成桥接`,用于先刷新诊断再按明确建议生成桥接;也提供 `选中两路径生成桥接`,用于在用户选中的两个路径 carrier 最近点之间生成一段 `UserPath`。这两个能力都属于半自动路径网络编辑,不会扫描全柜并自动连接所有远距离线槽。对于 UserPath 端点正好落在线槽中段的 0mm 接入,路径图会把被接入的线槽段在该点切开并并网,避免视觉上已经接触但路径组件仍被诊断为孤立。
孤立路径网络诊断只针对可行动的路径组件。线槽、UserPath、过线孔、辅助路径和端子接入如果分成多个组件会继续输出 `isolated_network_components`;但纯 `RoutingRange` 布线面孤岛只作为兜底网格保留在 `components` 明细中,不再单独触发“存在孤立路径网络”问题码。这样可以避免真实工程中安装板/布线面网格被误当作主路径断网问题,手测时优先处理线槽、用户路径和端子局部接入。
端子接入过长属于质量告警,不等同于路径断开。`terminal_access_max_distance` 控制是否允许生成端子接入;`terminal_access_warning_distance` 只控制超过多长时提示 `long_terminal_accesses`。当该值为 0 时继续沿用默认自动阈值;在较大机柜或端子到线槽本来就有较长局部出线的场景,可以把警告距离设置为 700mm 等工程可接受值,以减少误报,同时仍保留最大接入距离作为硬限制。
为了减少手动测试时反复展开属性查 JSON`3D 布线连接` 面板提供 `汇总布线诊断`。它读取 FreeCAD 文档中最新的 `RoutingPreflight`、`RoutingPathNetwork`、`RoutingConnectionBatch` 三类诊断对象,按诊断类型合并状态、中文消息和 `issue_codes`,输出一个总的“通过/未通过”摘要,并刷新 `RoutingDiagnosticSummary` 对象。所有这类诊断对象都会把问题码同步写到 `QetDiagnosticIssueCodes`,把中文问题标签同步写到 `QetDiagnosticIssueLabels`,方便在属性面板中直接查看;完整明细仍保留在 `QetDiagnosticJson`。汇总诊断会从诊断 payload 中提取 `runtime_version`,优先采用 `RoutingConnectionBatch` 的版本号,没有批量布线结果时再回退到预检或路径网络版本,便于确认当前工程是否加载了最新运行模块。该汇总不重新生成路径、不重新布线,也不访问 QET 或修改数据库;它只是把 FreeCAD 已保存的诊断状态集中显示并固化到诊断树中,便于把一次装配/布线测试的问题快速分成“准备不足、路径网络问题、批量布线问题”三类。
当最新 `RoutingConnectionBatch` 存在时,汇总诊断会把它视为最终诊断入口。若批量报告中已经内嵌 `routing_path_network_diagnostic`,则不再要求额外存在独立 `RoutingPathNetwork` 诊断对象;同样,也不会因为用户没有单独执行 `检查布线准备度` 而把一次完整批量布线误判为失败。这样面板既支持完整流程,也支持现场更常用的简化流程:直接 `生成布线连接` 后点击 `汇总布线诊断`
如果汇总时发现旧版诊断对象存在但 `QetDiagnosticJson` 为空,会追加 `diagnostic_json_empty / 诊断 JSON 为空`。这类对象不能证明当前布线状态有效,应重新运行对应诊断或重新生成布线连接,让新版本写入完整 `QetDiagnosticJson`
汇总诊断还会扫描 `QETWiring_04_Routed` 下的已生成导线。如果发现旧版 `RoutedConnection` 缺少单线 `QetRouteDiagnosticsJson`,会追加 `routed_wire_diagnostics_missing / 导线诊断缺失`,并给出一条导线 Label 示例。这能区分“模型里看得到线”和“这条线具备当前版本碰撞、柜内边界、路径质量等诊断数据”。
旧版批量诊断对象可能没有 `issue_codes`,但会保留 `route_status_counts`、`skipped_missing_terminal` 和 `missing_endpoint_samples`。汇总诊断会从这些旧字段反推出 `routing_errors`、`missing_terminals` 和 `missing_endpoints`,并在中文摘要里显示“结果状态:错误 N 条,缺失端子 N 条”。这样用户重新打开旧工程时,不会因为旧诊断缺少新版字段而误以为没有布线错误。
如果单线 `QetRouteDiagnosticsJson` 存在但无法解析为合法 JSON会追加 `routed_wire_diagnostics_invalid / 导线诊断 JSON 无效`。这类对象同样不能作为当前版本诊断依据,需要重新生成布线连接,让 FreeCAD 重新写入完整单线诊断。
当导线因为缺少布线路径网络被跳过时,批量报告会显示一条“缺路径网络示例”,包含导线号、起终点端子标签和已记录的失败原因。这里既包括整份文档没有有效路径段,也包括路径网络存在但该导线两端无法连通、端子接入距离阈值过小等情况。手动测试时可先按该示例定位设备两端附近是否缺线槽、`UserPath`、过线孔或布线面路径,或判断是否需要调整端子接入距离。
当最终导线虽然布通、但起点或终点到主路径入口距离超过警戒阈值时,批量报告会显示“接入距离提示”,列出触发导线数量、一条导线样例及起点/终点接入距离。这个提示不阻止生成导线,用于暴露设备附近缺少局部路径、主路径离端子过远或端子接入距离设置过大的情况。批量诊断 JSON 也会记录 `route_entry_distance_warning_count``route_entry_distance_warning_samples`,便于导出后定位全部样例。
批量 `issue_codes` 会把缺端子原因从样例提升到顶层:`missing_device_binding_metadata / 端点缺少绑定信息`、`device_not_in_3d_scene / 3D场景缺少设备`、`no_3d_terminals_for_element / 设备缺少工程端子`、`no_3d_terminals_for_instance / 实例缺少工程端子`、`terminal_uuid_not_in_element / 端子UUID不匹配`。这样真实工程中只有少量缺端子时,也可以不展开 JSON 就判断下一步是补 QET 端点元数据、补 3D 设备装配/绑定,还是核对同设备端子 UUID。
批量布线原始 report 和 compact 批量诊断都会同步写入 `missing_terminal_summary`,复用汇总诊断的缺端子分组口径。`reason_code_counts` 统计每类原因,`device_groups[]` 按缺失侧设备聚合 `element_uuid`、`instance_id`、缺失端子、端子 UUID 和相关导线,便于把“缺 3D 设备”或“端子 UUID 不匹配”的问题直接交给装配/绑定流程处理。脚本或面板二次处理时应优先读取这个结构化字段,而不是解析中文报告里的 `需补端子设备` 文本。
当用户从已打开的 FCStd 任务对象直接执行布线,而任务对象自身没有携带完整 `devices[]`FreeCAD 会尝试从当前 QET 交换上下文的 `2d_to_3d.json` 只读回补设备列表。该回补只合并 `devices[]`,不会用磁盘 JSON 覆盖当前 FreeCAD 文档中的导线任务;项目 UUID 不一致时会拒绝回补。批量 report 会写入 `context_devices_loaded`、`context_device_count` 和 `context_devices_json_path`,用于确认本次是否加载了上下文设备列表。这样真实工程中 `UD:8 / UD:10 / UD:5` 这类缺设备分组可以继续带出 `instance_id`,方便装配/绑定侧定位。
当批量布线已经生成导线,但 `route_path_usage.main_path_routes = 0``fallback_routes > 0` 时,诊断会追加 `main_path_not_used / 未使用线槽或用户主路径`。这表示导线虽然能连通,但全部依赖 `RoutingRange``AuxiliaryPath` 兜底路径,没有真正进入线槽、过线孔或用户主路径网络。手动测试看到该提示时,应优先补线槽中心路径、柜内黄色 `UserPath`、设备局部出线路径或主路径桥接,再重新生成布线连接。
批量诊断还会记录 `route_network_carrier_kind_counts``route_network_main_path_carriers`。如果 `route_network_main_path_carriers > 0` 但仍触发 `main_path_not_used`,说明 FreeCAD 已经识别到线槽/UserPath/过线孔主路径,但这些主路径没有接入端子局部网络,或距离/连通关系导致最终选路仍退回布线面。此时优先检查线槽端点、线槽到端子附近的桥接、`TerminalAccess` 最大距离、`UserPath` 是否贴近线槽中段,以及是否需要点击“按诊断建议生成桥接”。
当最终导线虽然布通、但起点或终点到主路径入口距离超过警戒阈值时,批量报告会显示“接入距离提示”,列出触发导线数量、一条导线样例、起点/终点接入距离和该样例实际经过的路径标签。这个提示不阻止生成导线,用于暴露设备附近缺少局部路径、主路径离端子过远或端子接入距离设置过大的情况。批量诊断 JSON 也会记录 `route_entry_distance_warning_count``route_entry_distance_warning_samples`,其中 warning sample 会保留 `route_source_labels`,便于导出后定位全部样例。
当单条路线使用 `RoutingRange``AuxiliaryPath` 时,批量报告会提示“路径质量提示”,说明该导线可能没有完全优先进入线槽。这个提示不阻止布线,只用于暴露“当前路径依赖布线面兜底”的情况,方便后续补线槽、补 `UserPath` 或调整设备位置。批量诊断 JSON 也会记录这类提示:`route_quality_warning_count` 表示依赖布线面/辅助路径的导线数量,`route_quality_warning_samples` 保留少量导线样例及其使用的 carrier 类型。
单根导线对象也会展开端子接入距离,避免手动测试时只能打开 JSON。`QetRouteEntryDistanceMm` / `QetRouteExitDistanceMm` 分别表示起点、终点端子出线点到主路径网络入口的距离;`QetRouteEntryPointMode` / `QetRouteExitPointMode` 表示接入点来自路径端点还是中段投影;`QetRouteEntryCandidateRank` / `QetRouteExitCandidateRank` 表示最终采用的入口候选排名;`QetRouteAccessWarningDistanceMm` 保存本次告警阈值。若 `QetRouteAccessStatus=LongAccessWarning`,说明该线虽然已经布通,但起点或终点接入主路径过长,`QetRouteAccessWarningSides` 会标出 `entry`、`exit` 或二者都触发。完整明细同步写入 `QetRouteDiagnosticsJson.access`。手动测试看到该告警时,应优先补设备局部路径、把用户路径/线槽靠近设备端子,或重新检查设备是否已经装配到正确位置。
当候选路线评分发现最终候选仍有接入段或候选折线穿过障碍包围盒时,批量报告会显示“接入避障提示”,列出触发导线数量、一条样例和该样例实际经过的路径标签。这个提示通常表示当前附近缺少可绕开的线槽、`UserPath` 或设备局部路径;它不替代最终碰撞状态,但能帮助区分“已经布通但入口路径仍不理想”的情况。批量诊断 JSON 也会记录 `route_candidate_obstacle_warning_count``route_candidate_obstacle_warning_samples`,其中 warning sample 会保留 `route_source_labels``route_samples[].network` 中仍保留样例路线自身的候选入口 rank、候选评分和候选障碍命中数。
入口候选会按“投影点 + carrier”去重避免同一个近路径因为桥接边或重复边占满候选名额。有障碍物参与候选评分时系统除保留距离排序靠前的候选外还会额外保留一批接入折线不穿过障碍包围盒的候选再统一评分。这样可以减少“近入口需要穿设备、远一点入口更干净但远入口被候选上限截掉”的穿模问题。
最终导线碰撞诊断会区分两类碰撞:`HardIntersection` 表示导线段穿过原始障碍包围盒,中文报告显示为“硬碰撞”;`ClearanceWarning` 表示导线没有穿过原始障碍但进入了按安全间隙膨胀后的包围盒中文报告显示为“安全间隙”。批量报告会增加“碰撞分类”compact 诊断 JSON 会记录 `collision_kind_counts`,用于快速判断当前问题是明显穿模还是安全距离不足。单根导线对象的 `QetRouteDiagnosticsJson.collisions[]` 和批量碰撞样例都会尽量保留该导线实际经过的路径标签,例如 `路径 主线槽A`,方便判断是线槽中心线、`UserPath` 还是兜底路径导致穿模。
批量 `issue_codes` 会在 `collision_warnings` 之外进一步追加碰撞处理分类:`structural_collision_candidates / 结构件碰撞候选` 表示存在可确认的柜体、门板、支架等结构件碰撞候选;`device_or_layout_collisions / 设备/布局碰撞` 表示存在真实设备或布局碰撞。这样汇总诊断和手动测试可以直接区分“可确认忽略的结构件”与“需要补路径或调整装配的设备碰撞”。
如果端子或设备模板明确提供了 `QetTerminalLocalRoutePointsJson` 局部出线路径,最终导线会保留这些局部路径点,并在 `QetRouteDiagnosticsJson.endpoint_access` 中写入起点/终点接入路径。碰撞诊断会把这些明确的端子局部接入段视为设备内部或设备附近的受控出线段,不把它们当成主路径穿模问题;但局部路径接入后的柜内主路径、中段线槽和 `UserPath` 仍会继续做碰撞诊断。这样可以减少设备壳体附近的误报,同时不会隐藏真正穿过柜体、设备或主路径障碍的导线。
批量 `collision_samples[]` 也会保留 `wire_object_label`,其值与树目录导线对象 Label 一致。这样当报告显示某条线有硬碰撞或安全间隙告警时,可以直接用 `wire_object_label``QETWiring_04_Routed` 下找到对应导线,再检查 `QetRouteCollisionStatus`、路径来源和碰撞包围盒。
单根导线对象会同步展开碰撞状态:`QetRouteCollisionCount` 表示总碰撞/间隙告警数量;`QetRouteHardIntersectionCount` 表示硬碰撞数量;`QetRouteClearanceWarningCount` 表示安全间隙告警数量;`QetRouteCollisionStatus` 会在 `NoCollision`、`ClearanceWarning`、`HardIntersectionWarning` 之间切换。手动测试时,如果状态是 `HardIntersectionWarning`,应优先检查导线是否穿过设备、线槽壁或柜体;如果只是 `ClearanceWarning`,通常表示路线贴近障碍,需要调大线槽/用户路径距离、调整设备位置或降低安全间隙阈值后复测。
`3D 布线连接` 面板提供“障碍安全间隙 mm”设置对应 `obstacle_clearance`。该值用于膨胀障碍包围盒:值越大,越容易把贴近设备或柜体的导线标记为 `ClearanceWarning`,也会让候选入口评分更倾向避开贴近障碍的接入折线;值为 0 时只按原始障碍包围盒判断明显穿模。
柜内区域边界可以由 FreeCAD 文档中的对象提供,给对象设置 `QetRoutingBoundaryKind = "CabinetInterior"` 即可把该对象包围盒视为柜内可布线范围。边界对象不写数据库,也不会被当作障碍物;它先参与路径图过滤,再参与最终导线候选评分。当存在 `CabinetInterior` 时,系统会优先构建“柜内路径图”,只保留完全落在柜内边界内的路径段并先在这张图上求路;只有柜内路径图不可达时,才回退到原始路径图并保留柜内越界告警。这样即使柜外路径几何距离更近、柜内路径稍远,也会优先选择仍在柜内的线槽、`UserPath` 或支撑面路径。批量诊断 JSON 的 `route_samples[].network` 会记录 `boundary_aware`、`boundary_filtered`、`boundary_filtered_segments` 和 `route_candidate_boundary_violations`,用于确认本次布线是否启用了柜内边界约束、是否先使用了柜内过滤图,以及最终候选是否仍存在越界点。
如果柜内过滤图无法连通两端系统仍会回退到原始路径图并生成综合评分最高的路径但批量报告会显示“柜内边界提示”指出有多少条导线最终路径仍越出柜内区域并给出一条导线样例、越界点数量和该样例实际经过的路径标签。compact 诊断 JSON 同步记录 `route_candidate_boundary_warning_count``route_candidate_boundary_warning_samples`,其中 warning sample 会保留 `route_source_labels`。单根导线对象也会写入 `QetRouteBoundaryAware=true`、`QetRouteBoundaryStatus=BoundaryWarning` 和 `QetRouteBoundaryViolationCount`,便于选中某条导线后直接判断它是否跑出柜内区域。手动测试看到该提示时,应优先补柜内 `UserPath`、线槽或设备局部路径;如果边界对象本身建模过窄,也可以调整 `CabinetInterior` 对象包围盒。
有柜内边界时,入口候选不会只按几何距离截断。系统会先保留距离排序靠前的候选,再额外保留一批投影点位于 `CabinetInterior` 内部的候选,最后统一按路径成本、接入距离、障碍命中和柜内越界点数评分。这样可以避免柜外近路径数量较多时,把稍远但正确的柜内 `UserPath` / 线槽在评分前就挤掉。
路径网络检查也会提前检查主路径 carrier 自身是否越出柜内边界。当文档中存在 `CabinetInterior` 时,`WireDuct`、`UserPath`、`WiringCutOut` 等非 `TerminalAccess` 路径点如果落在边界外,会记录 `route_carriers_outside_boundary / 路径越出柜内边界`,并在中文报告中给出路径标签和越界点数量。这用于在生成导线前发现“用户路径或线槽中心线本身画到柜外”的问题,避免后续所有导线都沿错误主路径求路。
路径网络检查还会检查工程端子是否落在柜内边界内。当端子原点或端子出线末端位于 `CabinetInterior` 外时,会记录 `terminals_outside_boundary / 端子越出柜内边界`,并高亮对应端子对象。这主要用于发现设备还停留在导入位置、端子 LCS 没有跟随装配实例移动、或柜内边界对象标得过窄等装配态问题。
`3D 布线连接` 面板摘要会显示当前文档内已识别的柜内边界数量,例如 `柜内边界1`。执行“选中对象作为柜内边界”后,可先看摘要确认边界对象已经生效,再生成布线路径网络和布线连接。
被标记为 `CabinetInterior` 的对象只作为边界使用,不会再被自动识别成线槽、安装面、过线孔或 `UserPath` 源对象。这样可以避免辅助边界盒、柜体内腔对象或用户误选的柜体对象在生成布线路径网络时又变成导线可走 carrier。
路径约束已支持 SW/EPLAN 风格“禁止经过”和“必须经过”的第一版能力。调用自动布线时可以通过 options 传入 `forbidden_route_carrier_names`、`forbidden_route_carrier_labels`、`forbidden_route_carrier_source_names`、`forbidden_route_carrier_source_labels` 或 `forbidden_route_carrier_kinds`Dijkstra 搜索会直接跳过匹配 carrier 的边;也可以传入 `required_route_carrier_names`、`required_route_carrier_labels`、`required_route_carrier_source_names`、`required_route_carrier_source_labels` 或 `required_route_carrier_kinds`Dijkstra 状态会记录已经经过的必经 carrier 或源对象,只有所有必经条件满足时才允许结束。批量 `wires[]` 中的单条导线任务也可以携带这些同名字段,用于表达“某一根导线必须/禁止经过某些路径”。该能力当前先作为算法层和导线任务可选字段后续可接入面板、FreeCAD carrier 属性或 QET 导线规则;如果禁止/必经规则导致两端不再连通,导线会按“缺少布线路径网络/不连通”进入失败诊断。
当必经/禁经规则导致无可用路径时,单条布线会提示“没有满足路径约束的布线路径网络”;批量布线会把它归入 `MissingRouteNetwork`,并在“缺路径网络示例”的 `error` 字段里保留路径约束原因。这样手动测试时可以区分“真的没有线槽/UserPath”和“路径存在但被规则禁止或必经规则无法满足”。
当路径约束成功生效并生成导线时,`QetRouteNetworkJson` 会保留 `route_constraints`,记录本路线使用到的 required / forbidden carrier 名称、标签、源标签或类型。批量 compact 诊断的 `route_samples[].network.route_constraints` 也会保留这些字段,方便回看某条线为什么没有走最近路径。
批量生成布线连接的中文报告会汇总路径约束使用情况:当有导线应用必经/禁经规则时,报告显示“路径约束提示”,列出触发导线数量和一条样例导线,并按“必须经过/禁止经过”展示标签、名称、源标签或类型。compact 诊断 JSON 也会记录 `route_constraint_warning_count``route_constraint_warning_samples`,便于导出后快速确认哪些导线受全局或单线规则影响。
为了便于 FreeCAD 手动测试,也可以直接在路径 carrier 对象上设置 `QetRouteConstraintMode`。值为 `Forbidden` 时,该 carrier 会被所有自动布线跳过;值为 `Required` 时,所有自动布线都必须经过该 carrier否则进入路径约束失败诊断。这个对象属性适合验证工程规则或临时屏蔽某段线槽如果未来要做到“只对某一根导线生效”仍建议通过单条 `wires[]` 的 required/forbidden 字段表达。
`3D 布线连接` 面板提供“选中路径必须经过”“选中路径禁止经过”“清除选中路径约束”和“清除全部路径约束”四个入口。用户可以选中已经生成的 route carrier或选中草图、Draft 线等源路径对象,然后点击对应按钮写入或清空 `QetRouteConstraintMode`。如果源路径对象还没有生成 carrier面板会按“源路径”计数后续生成 `UserPath` 时会继承该约束,避免用户必须严格按“先生成路径、再设置约束”的顺序操作。如果多次手动测试后不确定哪些路径仍保留 Required / Forbidden可使用“清除全部路径约束”它会同时清空当前文档内 route carrier 和源路径对象上的约束,避免重新生成路径网络后旧约束再次继承回来。该设置是 FreeCAD 文档内的全局路径规则,会影响后续所有自动布线。
手动编辑属性时,`QetRouteConstraintMode` 同时支持英文和中文别名:`Required`、`必须经过`、`必经` 都表示必经路径;`Forbidden`、`禁止经过`、`禁经`、`禁止` 都表示禁经路径。面板按钮仍写入标准英文值,便于程序稳定判断。
面板摘要会显示当前文档中 carrier 级路径约束数量,例如 `路径约束:必经 1禁经 1`。如果约束写在尚未生成 carrier 的草图、Draft 线等源路径对象上,或写在线槽、过线孔、支撑面等已标记路由源对象上,摘要会单独显示 `源路径约束:必经 1禁经 0`。前者代表已经参与当前路径网络求路的 carrier 规则,后者代表后续生成或刷新 `UserPath` / `WireDuct` / `WiringCutOut` 等 carrier 时会继承的源对象规则。标记或清除路径约束后,可先看摘要确认状态,再重新生成布线连接。
当用户选中源路径对象例如草图、Draft 线或线槽源对象)设置路径约束时,系统会同时把 `QetRouteConstraintMode` 写到源对象和它已经生成的 carrier 上。后续即使清除走线路径并重新生成 carrier新 carrier 也会继承源对象上的 Required / Forbidden 约束,避免工程规则在刷新路径网络后丢失。如果源路径对象的约束已经清空,刷新 `UserPath` 时也会同步清空旧 carrier 上残留的 Required / Forbidden避免多轮手动测试后旧规则继续影响自动布线。
同一个草图或 Draft 源对象可能包含多条不连通 `Wire`,生成时会拆成多条 `UserPath`。当约束来自源对象时,系统会按 `QetRouteSourceName` 聚合判断:`Required` 表示路线至少经过该源对象生成网络中的一条相关路径即可满足,不再强制同时经过所有生成子路径;`Forbidden` 表示该源对象生成的全部路径都不可走。这样更符合甲方“预先画黄色路径作为布线输入”的操作习惯,也避免多分支草图被误判为必经规则无法满足。
当单条路线使用 `RoutingRange``AuxiliaryPath` 时,批量报告会提示“路径质量提示”,说明该导线可能没有完全优先进入线槽。这个提示不阻止布线,只用于暴露“当前路径依赖布线面兜底”的情况,方便后续补线槽、补 `UserPath` 或调整设备位置。报告会尽量显示具体的布线面/辅助路径 carrier 标签,例如 `示例 N4111 使用布线面:安装板辅助路径`;没有具体标签时仍回退显示 carrier 类型。批量诊断 JSON 也会记录这类提示:`route_quality_warning_count` 表示依赖布线面/辅助路径的导线数量,`route_quality_warning_samples` 保留少量导线样例、使用的 carrier 类型和 `route_carrier_labels`
单根导线对象也会展开路径质量状态:`QetRouteQualityStatus=NormalPath` 表示该线没有使用布线面/辅助路径兜底;`QetRouteQualityStatus=FallbackPathWarning` 表示实际路线经过了 `RoutingRange``AuxiliaryPath`。`QetRouteFallbackCarrierKinds` 会列出兜底 carrier 类型,`QetRouteFallbackCarrierLabels` 会列出具体标签,例如安装板辅助路径。这个状态不代表布线失败,但说明第一版算法是在“能连通”的基础上用了低优先级路径;手动测试看到它时,应优先补线槽、黄色草图 `UserPath`、过线孔或设备局部路径,让导线进入更明确的工程主路径。
当并行 lane 数超过实际经过路径的最小容量时批量报告会提示“容量提示”显示最大并行线数、路径最小容量并给出一条样例导线和真实经过的路径名称。这个提示不阻止布线它用于暴露线槽外共线拥挤、线槽容量设置过小或需要增加并行路径的场景。compact 诊断 JSON 同步记录 `route_capacity_pressure_warning_count``route_capacity_pressure_warning_samples`样例包含导线、并行线数、最小容量、lane index、carrier 名称和源路径标签。若容量压力来自同一个草图拆出的多条 `UserPath`,中文报告和 compact 诊断会优先显示 `源路径标签(路径1)` 这类可回溯到黄色草图线的标签,而不是只显示自动生成的 carrier 名称。
`3D 布线连接` 面板提供“共路复用惩罚”设置,对应 `segment_reuse_penalty`。当某段路径的已用导线数超过该 carrier 的 `QetRouteCarrierCapacity`Dijkstra 会按该惩罚增加复用成本;调高后更倾向绕到备用线槽或 `UserPath`,调低后更倾向继续走几何距离更短的公共路径。该参数只影响搜索成本,不代表真实线槽填充率校核。
路径网络检查还会识别“只有 `RoutingRange`、没有 `WireDuct` / `UserPath` / `WiringCutOut` 主路径”的情况,并记录 `routing_range_only_network`。这类网络可以作为无线槽或路径不完整时的临时兜底,但不是推荐的第一版主路径形态;手动测试看到该提示时,优先补线槽、补 `UserPath` 或补过线孔路径。
@ -569,6 +747,8 @@ QetWiringCutOutBridgeExtensionMm = 20.0
当单条路线的最大并行线数超过该路线 route track 中记录的路径最小容量时,批量报告会给出容量提示。这个提示只基于 `QetRouteCarrierCapacity` 和当前 lane 情况,用于暴露“可能容量不足”的调试线索,不等同于按线径、截面积和线槽填充率计算的工程容量校核。
单根导线对象会展开共路和容量状态,便于选中导线后直接检查。`QetRouteLaneIndex` 表示该线在共享路径中的 lane 序号,`QetRouteLaneAxis` / `QetRouteLaneOffsetMm` 表示显示错位方向和偏移量,`QetRouteParallelWireCount` 表示到该 lane 为止的并行线数量,`QetRouteMinCarrierCapacity` 表示该线实际经过路径的最小 carrier 容量。若 `QetRouteCapacityStatus=CapacityWarning`,说明该线所在共享路径的并行线数已经超过当前路径容量,应补备用线槽、用户路径或调高真实 carrier 容量属性。
### 5.3 布线连接功能
已完成:
@ -586,6 +766,7 @@ QetWiringCutOutBridgeExtensionMm = 20.0
11. 自动导线可见显示并保存到 FreeCAD 文档。
12. 生成布线连接时保存 `QetRouteTrackJson`,记录实际经过的 `WireDuct` / `RoutingRange` / `TerminalAccess` / `WiringCutOut` carrier。
13. 支持检查布线路径网络,诊断孤立网络、未接入端子和疑似线槽端点断点,并写入 `QETWiring_05_Diagnostics`
14. 支持柜内边界约束:当文档中存在 `QetRoutingBoundaryKind = "CabinetInterior"` 对象时,自动布线会先在柜内过滤后的路径图上求路,再回退原始路径图;路径网络检查也会提前提示主路径 carrier 或工程端子越出柜内边界。
### 5.4 FreeCAD 面板
@ -599,6 +780,11 @@ QET模板 -> 3D布线连接
```text
准备布线布局空间
选中对象作为柜内边界
选中路径必须经过
选中路径禁止经过
清除选中路径约束
清除全部路径约束
生成布线路径网络
检查布线路径网络
生成布线连接
@ -639,7 +825,7 @@ tests/python/freecad_exchange_auto_routing_test.py
20. “生成布线连接”会先更新同一套布线路径网络,再按全部 QET 导线任务批量求路。
21. 相邻主路径端点在容差内会被网络自动连通;支路端点靠近主路径中段时也会投影桥接;端子接入会连接到最近的网络线段点,而不是只连接到已有端点。
22. 线槽端部会生成 `WireDuctOpenEnd` 横向路径,穿线孔/过线孔会生成 `WiringCutOut` carrier。
23. 导线会保存 routing track网络检查会生成 `RoutingPathNetwork` 诊断对象。
23. 导线会保存 routing track网络检查会生成 `RoutingPathNetwork` 诊断对象,并在返回结果中同步给出 `issue_codes`。诊断对象会保存 `QetProjectUuid``QetDiagnosticOk` 表示是否通过;`QetDiagnosticIssueCodes` 直接列出问题码;`QetDiagnosticIssueLabels` 直接列出中文问题标签;`QetDiagnosticMessage` 保存中文摘要,`QetDiagnosticJson` 保存路径网络诊断明细和 `issue_codes`
24. 自动生成的线槽、过线孔和支撑面 carrier 会在源对象移动、缩放、删除或失效后刷新/清理。
25. `WiringCutOut` 会在穿孔方向外扩虚拟路径,用于桥接开孔两侧附近的线槽或支撑面网络,并支持通过 `QetWiringCutOutBridgeExtensionMm` 按对象调整外扩距离。
26. `QetRouteTrackJson` 会在 carrier 有源对象元数据时保存 `source_name`、`source_label`、`source_kind`,方便核对导线实际走过的线槽、过线孔或支撑面。
@ -654,19 +840,99 @@ tests/python/freecad_exchange_auto_routing_test.py
35. `QetRouteTrackJson` 的 carrier payload 会记录 `capacity`,方便后续分析线槽容量偏好和共路绕行。
36. 批量布线报告会在最大并行线数超过路径最小容量时显示容量提示,但当前仍不做真实填充率计算。
37. `3D 布线连接` 面板提供“并行线间距 mm”、“并行线最大偏移 mm”和“并行线方向”设置用于调整多线共路时的可视 lane 偏移。
38. 最终导线选路会在多个入口候选中避开接入段穿障碍的入口,并优先选择可避障的线槽 / `UserPath` 入口。
39. 同一入口下的端子接入正交折线会尝试不同轴向顺序,优先选择不穿过障碍包围盒的折线。
38. `3D 布线连接` 面板提供“障碍安全间隙 mm”设置用于调整安全间隙告警和接入候选避障评分。
39. `3D 布线连接` 面板提供“共路复用惩罚”设置,用于调整超过 carrier 容量后的复用成本和绕行倾向。
40. 最终导线选路会在多个入口候选中避开接入段穿障碍的入口,并优先选择可避障的线槽 / `UserPath` 入口。
41. 入口候选按投影点和 carrier 去重;存在障碍时会额外保留干净接入候选,避免重复近候选挤掉稍远的避障入口。
41. 同一入口下的端子接入正交折线会尝试不同轴向顺序,优先选择不穿过障碍包围盒的折线。
40. 并行导线可视 lane 偏移默认限制在固定上限内,防止密集共路时导线被显示到柜外。
41. 完整自动布线流程会使用支路端点到主路径中段的投影桥接,避免这类支路网络被误判为孤立。
42. `QetRouteTrackJson` 会标记实际走过的自动桥接段,并记录本路线实际使用的桥接段数量。
43. 批量布线报告的路径示例会跳过虚拟桥接段,只列出真实经过的源对象标签。
44. 共路 lane 计数和路径复用惩罚会跳过虚拟桥接段,避免仅共享自动桥接边的导线被误判为真实共路。
45. `3D 布线连接` 面板提供“选中对象作为柜内边界”,用于把选中对象的包围盒标记为 `CabinetInterior`,让自动布线优先留在柜内区域。
46. 面板摘要会显示已识别的柜内边界数量,方便确认边界约束是否生效。
47. `CabinetInterior` 边界对象不会被自动识别成 `WireDuct`、`RoutingRange`、`WiringCutOut` 或 `UserPath` 路径源。
48. 柜内边界存在时,入口候选会额外保留柜内候选参与评分,并且系统会先尝试柜内过滤路径图,避免大量柜外近路径或柜外捷径挤掉稍远但正确的柜内路径。
45. 路径质量提示会按非桥接段重新判断 carrier 类型,避免把虚拟桥接到 `RoutingRange` 误报为真实使用布线面兜底。
46. 缺少布线路径网络或路径网络两端不连通时,批量布线报告会显示一条导线、端点样例和失败原因,便于直接定位需要补路径的设备区域。
47. “端子接入最大距离”同时约束自动 `TerminalAccess` 和最终导线入口候选,防止最终求路绕过面板设置生成超长接入线。
48. 批量诊断 JSON 的 route sample 会跳过虚拟桥接段统计 carrier 类型和 carrier 名称,保持与中文报告一致。
49. 最终导线路由不会把 `TerminalAccess` 当作公共 transit carrier入口候选也会优先真实主路径避免端子局部接入线被误用来桥接主路径缺口或作为其它导线的起步路径。
50. 批量布线报告会提示最终导线起点/终点接入距离过长的样例,用于排查设备附近缺局部路径或主路径离端子太远。
51. 单根导线对象会展开 `QetRouteEntryDistanceMm`、`QetRouteExitDistanceMm` 和 `QetRouteAccessStatus`,用于选中某条线后直接判断是否存在长接入。
52. 单根导线对象会展开 `QetRouteSourceLabels``QetRouteCarrierNames`,用于直接查看该线实际经过哪些线槽、黄色草图路径或用户路径。
53. 单根导线对象会展开 `QetRouteCollisionStatus`、`QetRouteHardIntersectionCount` 和 `QetRouteClearanceWarningCount`,用于直接区分穿模和安全间隙告警。
54. 单根导线对象会展开 `QetRouteQualityStatus`、`QetRouteFallbackCarrierKinds` 和 `QetRouteFallbackCarrierLabels`,用于判断该线是否依赖布线面/辅助路径兜底。
55. 批量诊断的 `route_samples[]` 会同步输出 `access`、`collision_summary`、`quality`、`capacity`、`boundary` 状态分组,便于不逐根选中导线也能复盘样例路线。
56. 生成导线对象的树目录 Label 会包含导线标识、起终点端子和状态,便于在 FreeCAD 左侧树中定位问题导线。
57. 单根导线对象会展开 `QetRouteIssueCodes``QetRouteIssueLabels`,用与批量诊断一致的问题码汇总该线自身的问题。
58. 批量诊断的 `route_samples[]` 会优先保留带问题码的路线样例,避免问题线排在后面时被 sample limit 截掉。
59. 批量诊断的 `route_samples[]` 会保留 `wire_object_label`,用于从诊断 JSON 直接定位 FreeCAD 树目录中的导线对象。
60. 批量碰撞样例 `collision_samples[]` 会保留 `wire_object_label`,便于从穿模/安全间隙告警直接定位问题导线。
61. 接入距离、路径质量、候选入口、柜内边界、路径约束和容量压力 warning samples 会保留 `wire_object_label`,统一诊断定位方式。
62. 路径网络长接入样例会保留父设备、端子全局点、接入折点、主要超长方向和各轴长度。真实工程中如果 `terminal_access_dominant_axis=z` 且竖向长度占大头,可以直接判断为端子/设备高度或局部出线路径问题,而不是主路径网络断开。
63. 碰撞样例会保留障碍对象的父装配名称和标签,方便把 `NAUOxxx` 这类导入零件追溯到前门、柜体、安装板或具体设备,再决定是标记忽略碰撞还是补柜内路径。
64. 面板提供“选择越界路径/端子”,从最新 `RoutingPathNetwork` 诊断的 `route_carriers_outside_boundary``terminals_outside_boundary` 反向选择越出柜内边界的路径 carrier 与工程端子。该功能只定位对象,不自动调整边界、不移动设备、不写数据库。
64. 批量布线报告的 `top_collision_obstacles[]` 不再只记录对象标签和次数,还会记录对象名称、父装配、硬碰撞/安全间隙分类计数;中文摘要也会显示父装配,例如 `NAUO118(CABINET ASS'Y) 18 处`,便于区分柜体/门板 AABB 误报和真实设备穿模。
65. 缺失端点样例会记录同 2D 设备、同 3D 实例下的 FreeCAD 工程端子数量,并输出原因码。真实工程里若原因是 `missing_device_binding_metadata`,说明 QET 导线任务端点缺少 `element_uuid`FreeCAD 无法判断缺失端子属于哪个 2D 设备;第一版不要求 QET 在 `wires[]` 端点提供 `start/end_instance_id`。若原因是 `device_not_in_3d_scene`,说明该 2D 设备当前没有对应 3D 设备实例,应回到设备导入、装配和 2D/3D 绑定流程排查;若原因是 `no_3d_terminals_for_element`,说明设备实例在场景中但没有生成工程端子,应回到端子生成流程排查。这些都不是路径网络问题。
66. 高发碰撞对象会输出处理建议。疑似柜体、门板、支架、盖板等结构件时,建议用户确认后通过现有“选中对象忽略碰撞”标记为 `PassThrough`;疑似设备或安装区域碰撞时,建议补主路径、局部出线路径或调整装配,而不是直接忽略。
67. 面板提供“选择高发碰撞对象”,从最新批量诊断 `top_collision_obstacles[]` 反向选择 FreeCAD 对象,方便现场确认后再手动标记忽略或调整路径。该功能只定位对象,不自动修改碰撞规则。
68. 面板提供“选择碰撞导线”,从最新批量诊断 `collision_samples[]` 和带 `collision_warnings``route_samples[]` 中反向选择 RoutedConnection 导线对象,便于和高发碰撞对象一起核对穿模位置。该功能只定位导线,不重新求路。
68. 面板提供“选择缺主路径导线”,从最新 `route_samples[]` 和导线对象自身的 `QetRouteIssueCodes` 中选择带 `main_path_detour_missing` 的 RoutedConnection 导线。该功能用于定位“选择性避障重算本可减少碰撞,但会退回到辅助路径/布线面兜底,因此被当前主路径优先策略拒绝”的导线;下一步应补 `UserPath`、桥接主路径、调整线槽入口或完善设备局部出线路径,不自动接受 fallback 结果。
68. 自动布线会对明确的 `main_path_detour_missing` 做一次收敛处理:当选择性避障已经得到碰撞更少的 fallback 折线,但该折线因包含 `RoutingRange` 被拒绝时,系统会把这条折线固化为 `MainPathDetourPath` 类型的 `UserPath`,再按 `兜底区域 -> 当前主路径` 生成 `MainPathDetourBridge`,随后只重试受影响导线。这样保持主路径优先,不直接接受宽泛布线面兜底,同时避免整批导线二次全量重跑。
69. 面板提供“选择长接入端子”,从最新批量诊断 `routing_path_network_diagnostic.long_terminal_accesses[]` 中反向选择端子对象,便于检查端子高度、设备装配和局部出线路径。该功能只定位端子,不修改端子或路径数据。
70. 面板提供“选择缺端子设备”,从最新批量诊断 `missing_endpoint_samples[]` 的缺失侧读取 `*_instance_id` / `*_element_uuid` 并反向选择 3D 设备,便于补工程端子或检查 2D/3D 绑定。若缺失设备不在当前场景中,控制器仍会返回 `missing_terminal_device_instance_ids[]`、`missing_terminal_device_element_uuids[]` 和可读标签,状态栏也会显示 instance_id便于把缺设备清单交给装配/绑定流程。该功能只定位设备,不自动创建端子、不修改 QET 数据。
71. 面板提供“选择缺端子另一端”,从缺端子样例中选择已找到的另一端工程端子,便于确认失败导线本来要连接到哪里,再对照缺失侧设备和端子脚号。该功能只定位端子,不自动补端子、不写数据库。
72. 面板提供“选择缺端子候选端子”,从 `*_instance_terminal_samples` / `*_element_terminal_samples` 中反向选择同设备或同实例已有工程端子,便于排查 `terminal_uuid_not_in_element` 这类“同设备已有端子但 UUID 不匹配”的问题。该功能只定位候选端子,不自动改绑定、不写数据库。
73. 碰撞障碍语义按 FreeCAD 父装配链递归识别。用户把柜体、门板、支架、盖板等父装配标记为 `PassThrough` 后,深层导入子零件也会被排除在导线障碍之外;碰撞样例的 `parent_refs` 也会尽量输出完整父链,避免只看到中间 `Compound/NAUO`
73. 第一版会自动过滤一类未绑定导入结构件碰撞:障碍物没有 QET `element_uuid`,并且位于 `QET Exchange Devices / QETCabinet / LinkGroup / Compound / NAUO` 等导入装配上下文,同时名称或父链命中柜体、门板、支架、盖板等结构关键词时,不再计入导线碰撞。该规则不作用于带 `element_uuid` 的 3D 设备,因此不会吞掉真实设备/端子/断路器碰撞。
74. 面板提供“选择碰撞父装配”,从最新 `top_collision_obstacles[]``parent_names / parent_labels` 反向选择父装配,适合先确认前门、柜体、支架等总成是否可穿越,再统一标记 `PassThrough`。该功能只定位父装配,不自动忽略碰撞。
75. 面板提供“确认结构件忽略碰撞”,按最新诊断只把疑似柜体、门板、支架、盖板等结构件碰撞的最近结构父装配标记为 `PassThrough`。该动作不会沿父链继续标记 `QET Exchange Devices` 这类工程根组,不会标记 `review_device_or_layout_collision`,也不写数据库;重新生成布线连接后,下级导入结构子件会因对应父装配 `PassThrough` 被排除出障碍候选,真实设备碰撞仍会继续保留诊断。
76. 面板提供“选择设备碰撞对象”,只从最新 `top_collision_obstacles[]` 中选择 `review_device_or_layout_collision` 候选。该功能用于处理结构件忽略后仍剩余的真实设备/布局碰撞,只定位对象,不自动忽略碰撞;后续应补设备局部出线路径、调整 UserPath/线槽入口或检查设备装配。
77. 面板提供“选择长接入设备”,从 `long_terminal_accesses[]``parent_device_name / parent_device_label` 反向选择设备对象,便于从设备整体高度、端子 LCS 跟随和局部出线路径三个角度排查长接入。
78. 面板提供“选择异常导线”,从最新 `route_samples[]` 中选择所有带 `issue_codes` 的 RoutedConnection 导线,并补充扫描导线对象自身的 `QetRouteIssueCodes`,避免 compact 样例数量有限时漏选问题线。该功能是长接入、越界、容量、路径质量和碰撞问题的统一入口,只定位导线,不重新求路。
79. 面板提供“选择异常导线路径”,从异常 `route_samples[]``carrier_names``route_track.segments[].carrier` 反向选择导线实际经过的路径 carrier并尽量选择 carrier 的源草图/线槽对象。该功能只定位路径,不自动改 Required/Forbidden、容量或几何。
80. 面板提供“选择选中导线路径”,从当前选中的 RoutedConnection 导线对象读取 `QetRouteTrackJson`,反向选择该线实际经过的路径 carrier 和源草图/线槽。该功能用于 compact `route_samples[]` 为空或样例不足时的单线排查,只读取 FreeCAD 文档中的导线元数据,不写数据库、不要求 QET 提供 3D 路径。
80. 面板提供“选择拒绝兜底路径”,从当前选中的 RoutedConnection 导线对象读取 `QetRouteDiagnosticsJson.selective_collision_reroute.rejected_fallback_labels`,反向选择被局部避障重算发现但因使用 `RoutingRange` / `AuxiliaryPath` 而被拒绝的路径来源。该功能只用于判断应在哪里补 `UserPath`、桥接主路径或设备局部出线路径,不会自动接受 fallback 路线,也不写数据库。
81. `汇总布线诊断` 会统计实际 RoutedConnection 导线对象上的 `QetRouteIssueCodes`,输出异常导线总数和各问题码数量。该统计来自 FreeCAD 文档中的导线对象,不依赖 compact `route_samples[]` 的样例上限。
82. `汇总布线诊断` 会统计批量缺端子数量和缺失端点原因,例如 `missing_device_binding_metadata / 导线端点缺少 2D/3D 设备绑定信息`、`device_not_in_3d_scene / 该 2D 设备未在 FreeCAD 场景中找到`、`no_3d_terminals_for_element / 该 2D 设备在 FreeCAD 中没有工程端子`,用于区分 QET 端点绑定元数据缺失、设备未导入/未绑定、端子未生成和路径网络问题。
83. `汇总布线诊断` 会根据当前问题生成手测建议动作,例如选择缺端子设备、选择异常导线、选择长接入端子/设备、选择碰撞父装配等。建议只引导 FreeCAD 面板操作,不自动修改路径或数据库。
84. 缺端子建议会按原因码分流:`missing_device_binding_metadata` 提示检查 QET 导线端点是否提供 `element_uuid``terminal_uuid`,并明确第一版不要求 `start/end_instance_id``device_not_in_3d_scene` 优先提示检查设备是否已导入、装配并完成 2D/3D 绑定;`no_3d_terminals_for_element` / `no_3d_terminals_for_instance` 才提示选择缺端子设备;`terminal_uuid_not_in_element` 提示选择缺端子候选端子核对 UUID 与脚号绑定。
85. `选择缺端子设备` 本身也会按原因码分流状态提示:如果缺失设备不在当前 FreeCAD 场景中,提示先补设备导入、装配和绑定;如果缺少 QET 端点 `element_uuid`,提示先补齐 QET 端点绑定信息,避免用户在 3D 场景中反复寻找不存在的对象。
86. `生成布线连接` 的中文报告会根据缺端子原因追加关键提示:若包含 `missing_device_binding_metadata`,直接提示 `QET 导线端点缺少 element_uuid`,并注明第一版不要求 `start/end_instance_id`;若包含 `device_not_in_3d_scene`,直接提示部分导线引用的设备未在当前 FreeCAD 场景中找到。这个提示不依赖 `routed=0`,即使多数导线已经成功、只有少量导线缺端子,也会在报告中显示。
87. 对旧版批量诊断中缺端子样例没有原因码的情况,汇总诊断会尝试用当前 FreeCAD 文档回填原因码:样例里有 `terminal_uuid` 但没有 `element_uuid` 或可回查的设备标识时回填为 `missing_device_binding_metadata`;样例里有设备标识但当前场景找不到设备时回填为 `device_not_in_3d_scene`;设备存在但无工程端子时回填为 `no_3d_terminals_for_element`。只有基础字段不足以判断时才显示 `缺端点原因未记录` 并建议重新生成布线连接。
62. 对于 `missing_route_network_samples[]`、`error_samples[]`、`missing_endpoint_samples[]` 这类失败样例,`wire_object_label` 保存的是任务侧最接近对象标题的显示名,不一定已经对应到真实 3D 导线对象。
51. 自动布线 options 支持按 carrier 名称、标签、源对象名称、源标签或类型设置禁止经过路径Dijkstra 会跳过这些路径边。
52. 自动布线 options 支持按 carrier 名称、标签、源对象名称、源标签或类型设置必须经过路径Dijkstra 状态会记录必经条件并只返回满足条件的路径。
53. 批量 `wires[]` 的单条导线任务可选携带 `required_route_carrier_*` / `forbidden_route_carrier_*` 字段,实现不同导线使用不同路径约束。
54. 成功布线的 `QetRouteNetworkJson` 和 compact route sample 会记录 `route_constraints`,用于追踪某条线实际应用的必经/禁经规则。
55. 批量布线中文报告和 compact 诊断会汇总路径约束使用情况,显示受 Required/Forbidden 规则影响的导线数量和样例。
55. 批量布线后会在返回 report 和 `QETWiring_05_Diagnostics` 下的 `RoutingConnectionBatch` 诊断对象中写入同一套 `issue_codes`;诊断对象会保存 `QetProjectUuid``QetDiagnosticOk` 表示本次批量布线是否无问题码;`QetDiagnosticIssueCodes` 直接列出问题码;`QetDiagnosticIssueLabels` 直接列出中文问题标签;`QetDiagnosticMessage` 保存中文摘要,`QetDiagnosticJson` 保存 compact 批量诊断明细,并包含当前 `runtime_version`,方便手测时确认 FreeCAD 已加载最新自动布线代码。即使当前没有导线任务,也会保留该诊断对象并提示“没有导线任务”。问题码示例包括 `no_wire_tasks`、`no_routed_connections`、`missing_terminals`、`missing_route_network`、`collision_warnings`、`route_capacity_pressure` 等,便于脚本或人工快速筛选问题。
56. 当批量布线 `total_wires > 0` 但最终 `routed = 0` 时,中文报告会明确提示“未生成有效导线:本次只有路径承载/诊断对象,未生成 RoutedConnection 导线”。这类场景常见于端子缺失、路径网络不连通、模块版本不一致或普通布线错误,不能把树目录中的 `WireDuct`、`RoutingRange`、`TerminalAccess` carrier 当成成功布线结果。
57. `检查布线准备度` 会记录 `runtime_capabilities`。如果当前 FreeCAD 会话加载的 `RoutingNetwork` 模块缺少路径约束收集函数,会写入 `runtime_route_constraint_collector_missing` 并提示同步运行目录、重启 FreeCAD避免模块版本不一致导致批量布线整批失败。
58. 外层“生成布线连接”在补齐路径网络检查、`hidden_route_carriers` 和 `routing_path_network_updated` 等最终字段后,会重写一次 `RoutingConnectionBatch` 诊断对象,保证“汇总布线诊断”读取到的是最终 report而不是内层批量求路刚结束时的半成品诊断。
59. compact 批量诊断会记录容量压力样例,便于定位哪条导线在哪些路径上触发“并行线数超过路径容量”。
60. 批量布线中文报告和 compact 诊断会按 `HardIntersection` / `ClearanceWarning` 汇总碰撞分类,便于区分穿模和安全间隙不足。
57. FreeCAD 路径 carrier 支持 `QetRouteConstraintMode = Forbidden / Required`,用于手动测试全局禁经/必经路径约束。
58. `3D 布线连接` 面板提供“选中路径必须经过”和“选中路径禁止经过”,可直接给选中 route carrier 写入 `QetRouteConstraintMode`
59. `3D 布线连接` 面板提供“清除选中路径约束”,可把选中 route carrier 的 `QetRouteConstraintMode` 清空。
60. `3D 布线连接` 面板提供“清除全部路径约束”,可一次清空当前文档中 route carrier 和源路径对象上的 Required/Forbidden 规则。
61. 面板摘要会显示 carrier 级路径约束数量,便于确认当前 Required/Forbidden 规则是否仍在生效。
62. 源路径对象上的 `QetRouteConstraintMode` 会在清除/重生成 carrier 后继承到新 carrier避免路径约束刷新后丢失。
63. 选中的草图、Draft 线和纯线状对象如果包含弧线、样条边或整条 `Wire` 拓扑,会先离散为 polyline 再生成 `UserPath`,避免曲线路径在自动布线网络中被拉直。
64. `UserPath` 从草图、Draft 线、边或 `Wire` 提取点时会按源对象的 `Placement` / `getGlobalPlacement()` 转成文档坐标,避免装配移动后的路径仍按本地坐标生成。
65. 同一个草图或 Draft 对象中存在多条不连通 `Wire` 时,会分别生成多条 `UserPath`,不会把第一条路径末端和第二条路径起点硬连成一条假路径。
66. 面板和报告中的 `user_path_carriers` 表示本次生成或刷新成功的 `UserPath` 数量;重复选择同一路径对象重生成时会刷新原 carrier并仍计入数量便于确认操作生效。
67. 同一个源草图生成多条 `UserPath` 时,对源草图设置 Required/Forbidden 路径约束会同步标记全部生成 carrier避免多路径草图只约束第一条路径。
68. 多 `Wire` 源草图刷新时,如果草图中的路径数量减少,系统会删除多余旧 `UserPath` carrier如果路径数量增加系统会新增对应 carrier避免布线网络和当前黄色路径不一致。
69. 同一个源草图生成多条 `UserPath` 时,每条 carrier 会记录 `QetRouteSourcePathIndex`,用于区分同一源对象下第几条路径,方便诊断和路径示例回溯。
70. 通用 route carrier 创建入口也使用同样的多 `Wire` 分段规则,脚本或旧入口创建 `RoutingPath` 时不会把不连通路径硬拼成一条。
71. 多 `Wire` 源草图设置 `QetRouteCarrierCapacity` / `QetWireCapacity` 时,生成的每条 `UserPath` 都会继承该容量,用于共路容量提示和复用成本。
72. 创建 `UserPath` 时如果同时选中支撑面 Face 和悬空草图/Draft 线,系统会把路径点投影到该支撑面并保留默认偏移,减少 Draft 工作平面不正确导致的悬空路径。
73. 同一个源草图生成多条 `UserPath` 时,通过面板清除选中路径约束会同时清空源草图和全部生成 carrier 的 Required/Forbidden 约束。
74. 多 `Wire` 源草图设置 Required 时,自动布线按源对象聚合判断,经过该源对象生成的任一相关 `UserPath` 即可满足必经条件;设置 Forbidden 时,该源对象生成的全部 `UserPath` 都会被跳过。
63. `QetRouteConstraintMode` 支持中文别名:`必须经过` / `必经` / `禁止经过` / `禁经` / `禁止`
已完成 FreeCAD smoke
@ -687,20 +953,22 @@ tests/manual/freecad_auto_routing_smoke.py
4. 清除走线路径
5. 点击“准备布线布局空间”
6. 按当前机柜情况调整主路径桥接容差、端子接入最大距离、端子出线长度
7. 可选:选中无法自动识别的线槽实体
8. 点击“生成布线路径网络”;如果不选择,则使用整份文档自动识别
9. 点击“生成布线连接”
7. 可选:选中柜内空间、柜体内腔或辅助实体,点击“选中对象作为柜内边界”
8. 可选:选中无法自动识别的线槽实体
9. 点击“生成布线路径网络”;如果不选择,则使用整份文档自动识别
10. 点击“生成布线连接”
```
三个按钮的职责:
```text
准备布线布局空间:识别并标记 layout space 里的线槽、支撑面、工程端子和障碍处理方式
选中对象作为柜内边界:把选中对象包围盒标记为 CabinetInterior只参与 3D 自动布线边界评分
生成布线路径网络:按 EPLAN routing path network 逻辑生成 WireDuct、UserPath、RoutingRange 和 TerminalAccess carrier
生成布线连接:先更新布线路径网络,再检查/绑定工程端子,按 QET 导线任务批量求路并生成 AutoSuggested 导线
```
如果模型名称/标签足够规范可以不手动选择直接执行三步也可以只点击“生成布线连接”系统会准备当前可识别的布线路径网络。若线槽无法自动识别则先选中线槽实体执行“生成布线路径网络”作为补充。若甲方现场没有线槽或需要绕开线槽自由定义柜内主路径可以选中草图、Draft 线、线段或纯线状对象,再执行“生成布线路径网络”,系统会生成 `UserPath`
如果模型名称/标签足够规范可以不手动选择直接执行三步也可以只点击“生成布线连接”系统会准备当前可识别的布线路径网络。若线槽无法自动识别则先选中线槽实体执行“生成布线路径网络”作为补充。若甲方现场没有线槽或需要绕开线槽自由定义柜内主路径可以选中草图、Draft 线、线段或纯线状对象,再执行“生成布线路径网络”,系统会生成 `UserPath`若手动测试发现导线容易跑到柜外,可先选中柜内空间或辅助包围盒执行“选中对象作为柜内边界”。
### 6.2 批量生成布线连接前提
@ -708,16 +976,20 @@ tests/manual/freecad_auto_routing_smoke.py
2. 每条导线包含:
```text
start_instance_id 或 start_element_uuid
start_element_uuid
start_terminal_uuid
start_terminal_display
end_instance_id 或 end_element_uuid
end_element_uuid
end_terminal_uuid
end_terminal_display
```
3. FreeCAD 文档中存在对应 `QetTerminalUuid` 的工程端子,或存在可按设备和端子显示号匹配的 `local:*` 模板端子。
4. 布线连接只按导线任务生成,不会把场景里所有端子任意两两相连。
`start_instance_id / end_instance_id` 不作为第一版 `wires[]` 的必填字段FreeCAD 会通过端点 `element_uuid`、`terminal_uuid`、`devices[]` 和当前 3D 文档中的绑定属性回查 3D 实例。
3. 可选:单条导线可以携带 `required_route_carrier_*` / `forbidden_route_carrier_*` 路径约束字段,按 carrier 名称、标签、源标签或类型控制必须/禁止经过。
4. 可选QET 启动 FreeCAD 时提供 `QET_WIRE_PROPERTIES_DB=<项目数据库路径>`,或在 `2d_to_3d.json` 顶层提供 `wire_style_database_path`,或调用自动布线时传入同名 options 字段,用于按 `wire_style_id` 查询 `wire_properties` 并渲染导线颜色/线宽。
5. FreeCAD 文档中存在对应 `QetTerminalUuid` 的工程端子,或存在可按设备和端子显示号匹配的 `local:*` 模板端子。
6. 布线连接只按导线任务生成,不会把场景里所有端子任意两两相连。
注意:批量生成布线连接的依据是导线任务,不是“所有端子自动互连”。如果文档中只有端子而没有 `wires[]``QETWiring_01_Tasks`,系统不能判断哪些端子应该连接。
@ -733,6 +1005,14 @@ end_terminal_display
6. 线槽是导线主路径。导线应优先从设备端子经 `TerminalAccess` 进入线槽,再沿 `WireDuct` 网络到达另一端。
7. 过线孔/穿线孔用于连接不同安装面、线槽或柜体开孔处的网络,应建模为 `WiringCutOut`,不是普通障碍。
从甲方提供的 KYN28-12 3D 布线教程和新增截图看,成熟软件里的流程并不是“导入设备后完全自动推导所有路径”。实际操作会先完成 3D 装配和电气设备关联再由工程人员预先绘制黄色草图路径最后自动布线沿这些路径生成导线。也就是说预置草图路径是正式布线输入不是临时调试线FreeCAD 第一版应优先保证“导入/选择已有草图路径后稳定生成 `UserPath` 并参与求路”。
视频里还显示门板、仪表、端子排和柜内主区域之间存在跨部件路径。例如门板上的局部梳状路径会接入一条跨门板到柜内的长主路径,端子排区域也会先形成局部短路径,再接入主路径。这说明自动布线需要支持“设备局部路径 -> 柜内主路径”的分层网络,而不是只按端子到最近线槽的一跳距离判断。
装配阶段和布线阶段应分开理解:端子排、仪表、按钮、导轨等对象先通过装配/配合确定最终 3D 位置自动布线只读取最终几何位姿、工程端子、草图路径和导线任务。FreeCAD 不应让自动布线反向修改装配关系,也不应把 3D 草图路径写入第一版数据库绑定表。
视频中的 SW Electrical 还要求把 3D 设备和电气树中的设备/端子进行关联。对应到本项目QET 的 `wires[]`、`terminal_uuid` 和 2D/3D 绑定仍是电气真相源FreeCAD 侧需要在自动布线前校验工程端子是否已由这些 UUID 绑定到正确的 3D 端子对象。
因此,自动布线的推荐空间语义是:
```text
@ -748,9 +1028,17 @@ end_terminal_display
1. 选中草图、Draft 线、线段或纯线状对象。
2. 点击 3D 布线连接面板中的“选中路径作为用户路径”,或直接点击“生成布线路径网络”。
3. 系统把选中路径转换为 `UserPath` carrier并参与后续自动布线最短路搜索。“选中路径作为用户路径”只创建用户路径“生成布线路径网络”会同时更新线槽、布线面、端子接入等完整网络。
4. 再次选择同一个路径对象生成网络时,系统会刷新原 carrier不会重复生成。
4. 再次选择同一个路径对象生成网络时,系统会刷新原 carrier不会重复生成;面板报告中的 `user_path_carriers` 会把刷新成功的 carrier 也计入数量。如果多 `Wire` 草图里的路径数量减少,刷新时会删除多余旧 carrier
5. 如果删除了原草图/线段源对象,再点击“选中路径作为用户路径”或重新生成网络,系统会清理对应的失效 `UserPath` carrier。
6. 如果源对象设置了 `QetRouteCarrierCapacity``QetWireCapacity`,生成/刷新出的 `UserPath` 会继承该容量,用于多根线共路和容量提示。
6. 如果源对象设置了 `QetRouteCarrierCapacity``QetWireCapacity`,生成/刷新出的 `UserPath` 会继承该容量;同一个源草图拆成多条 `UserPath` 时,每条生成路径都会继承,用于多根线共路和容量提示。
甲方视频里的黄色路径有两类:一类是柜内/门板区域的主路径或过渡路径,适合生成 `UserPath`;另一类是每个设备、端子排、按钮附近的短梳状局部出线路径,更适合沉淀为端子级 `TerminalAccess``QetTerminalLocalRoutePointsJson`。两者都可以来自草图,但语义不同:主路径可被多根线共享,局部路径只服务对应端子或设备附近接入。
SW 教程中路径可以由“创建草图”或“转换草图”得到并且路径可能包含弧线、样条或多段折线。FreeCAD 侧处理草图路径时不能只依赖直线 `Points`;当前已支持把草图边、整条 `Wire`、Draft 线和曲线按离散精度转换为稳定 polyline再按源对象的最终 `Placement` 转到文档坐标生成 route carrier并保留 `QetRouteSourceName` / `QetRouteSourceLabel`,方便路径刷新和诊断回溯。这样甲方视频里的黄色曲线路径不会被简单拉直成首尾两点,也不容易因为导入对象只暴露 `Shape.Wires` 或装配移动后仍使用本地坐标而漏建、错位用户路径。若同一个草图里有多条不连通 `Wire`,系统会分别生成多条 `UserPath`,避免在两条路径之间产生并不存在的直连段。
已有草图路径随工程导入一起存在时,手动测试推荐流程是:先打开工程并确认黄色/草图路径在树中可见,再执行“生成布线路径网络”或“选中路径作为用户路径”,最后生成布线连接。这样比在 FreeCAD 里临时画 Draft 线更接近甲方实际流程,也能减少因工作平面选错导致的悬空路径问题。
如果临时画的 Draft 线或草图线明显悬空,可同时选中安装板/柜板上的支撑面 Face 和该路径对象,再执行“选中路径作为用户路径”或“生成布线路径网络”。系统会把路径点投影到支撑面附近,而不是直接使用 Draft 当前工作平面坐标。
`UserPath` 与线槽的关系:
@ -773,6 +1061,18 @@ QetTerminalLocalRoutePointsJson
[[0, 0, 0], [10, 0, 0], [10, 30, 0]]
```
也可以写成对象包装格式,便于后续模板工具附加其它元数据;当前会识别 `points`、`route_points` 或 `local_points` 三个数组键:
```json
{
"points": [
{"x": 0, "y": 0, "z": 0},
{"x": 10, "y": 0, "z": 0},
{"x": 10, "y": 30, "z": 0}
]
}
```
自动生成 `TerminalAccess` 时,系统会先把这些局部点按端子和父设备的 `Placement` 转成全局点,再从局部路径末端连接到最近的柜内主路径、线槽、用户路径或布线面。没有该字段时,仍使用原来的端子 LCS `+Z` 方向短出线。
路径网络检查也使用同一口径:如果端子有有效局部路径,端子到主路径网络的接入距离按局部路径末端计算,而不是按默认 LCS 出线点计算。这样可以避免局部路径已经接入线槽、但诊断仍误报“端子未接入”的情况。
@ -794,7 +1094,7 @@ QetTerminalLocalRoutePointsJson
导入/更新工程端子时FreeCAD 会把 `local_route_points` 写入该端子的 `QetTerminalLocalRoutePointsJson`。后续自动生成 `TerminalAccess` 和最终导线几何时都会使用这段局部路径。
路径网络检查会校验端子局部路径元数据。`QetTerminalLocalRoutePointsJson` / `QetLocalRoutePointsJson` 必须是 JSON 数组,并且至少能解析出两个不同的有效点;如果 JSON 格式错误、不是数组或有效点不足,诊断对象会记录 `invalid_terminal_local_routes`,中文报告会提示“端子局部路径无效”。这类问题不会让 FreeCAD 依赖 QET 提供 3D 路径,只是提示模板端子或工程端子的 3D 局部出线元数据需要修正。
路径网络检查会校验端子局部路径元数据。`QetTerminalLocalRoutePointsJson` / `QetLocalRoutePointsJson` 必须是 JSON 数组,或包含 `points` / `route_points` / `local_points` 数组的 JSON 对象,并且至少能解析出两个不同的有效点;如果 JSON 格式错误、没有可识别的点数组或有效点不足,诊断对象会记录 `invalid_terminal_local_routes`,中文报告会提示“端子局部路径无效”。这类问题不会让 FreeCAD 依赖 QET 提供 3D 路径,只是提示模板端子或工程端子的 3D 局部出线元数据需要修正。
如果直接在 FCStd 模板端子 LCS 上维护,也可以给模板端子写入同名属性 `QetTerminalLocalRoutePointsJson`。当前模板作者工具提供了内部函数:
@ -813,6 +1113,18 @@ TemplateAuthoring.set_template_terminal_local_route_points(terminal, points)
4. 系统把所选路径的文档坐标转换为该端子的本地坐标,并写入 QetTerminalLocalRoutePointsJson。
```
如果已经在工程机柜里完成装配,也可以直接给工程端子补现场局部出线路径:
```text
1. 在 3D 布线连接面板中,先选中一个可布线工程端子。
2. 再选中一条表示该端子局部出线的草图、Draft 线、边或连续 Wire。
3. 点击“选中端子设置局部出线”。
4. 系统把所选路径从 FreeCAD 文档坐标转换为该端子的本地坐标,并写入工程端子的 QetTerminalLocalRoutePointsJson。
5. 重新点击“生成布线路径网络”或“生成布线连接”,新的 TerminalAccess 会优先沿这段局部路径接入柜内主路径。
```
这个工程端子现场设置入口只修改当前 FreeCAD 文档,不写数据库,也不要求 QET 输出 3D 路径。它适合手动测试中发现某个设备端子接入过长、从设备内部穿模、或默认 LCS 出线方向不符合实物时使用。若同一类设备会在多个项目复用,应优先把局部路径沉淀到 FCStd 设备模板里;若只是当前机柜现场微调,可以直接在工程端子上设置。
第一版不要求 QET 提供这个字段。它属于 FreeCAD 设备模板/工程端子的 3D 几何元数据,由 FreeCAD 模板作者维护QET 仍只提供导线任务、设备实例、端子实例和 2D/3D 绑定所需 UUID。
## 7. 当前限制
@ -840,14 +1152,14 @@ TemplateAuthoring.set_template_terminal_local_route_points(terminal, points)
2. 路径网络规模较大,但检查提示存在孤立路径网络和端子接入过长,说明部分设备局部路径没有可靠接入柜内主路径。
3. 部分导线穿过设备模型。当前碰撞检测只给出告警,不会强制阻止生成,因此在可用绕行路径不足时仍可能生成穿模导线。
4. 多根导线在公共路径上共线或高度拥挤。在线槽内共路可以接受,但在线槽外和设备端子附近需要更好的并行错位、束线显示和容量策略。
5. 部分导线跑到机柜外侧。该问题需要后续增加柜内有效区域/柜体边界诊断,但当前优先级低于提升布通率
5. 部分导线跑到机柜外侧。当前已支持把柜内空间、柜体或辅助实体标记为 `CabinetInterior` 边界,并在批量报告中提示仍越出柜内区域的导线;但该能力依赖用户先标记边界,且仍需要补充柜内 `UserPath`、线槽或局部路径来提供可选的柜内路线
6. Draft 线段可能悬空。原因通常是 Draft 当前工作平面没有锁定到安装板或线槽面;作为自由空间 `UserPath` 这是允许的,但作为贴面主路径时需要投影、吸附或明确提示。
当前开发优先级调整为:
1. 先保证更多导线能稳定布通,优先处理孤立路径网络和端子接入过长。
2. 其次降低明显穿模和线槽外共线拥挤。
3. 柜内越界诊断放到后续阶段,不阻塞当前布通率改进
3. 柜内越界诊断已进入第一版收尾能力:有边界对象时会参与候选评分,并在最终路径仍越界时输出“柜内边界提示”;后续重点是让边界更容易自动识别和让用户路径更容易贴合到柜内结构
已完成的对应改进:
@ -860,6 +1172,7 @@ TemplateAuthoring.set_template_terminal_local_route_points(terminal, points)
7. 接入候选评分会检查端子出口到路径网络入口之间的小段是否穿过障碍包围盒;当近入口接入段穿模、稍远入口可避开障碍时,会优先选择不穿模的入口。最终碰撞诊断仍保留端点附近设备外壳的宽容规则,避免把端子自身外壳误报成碰撞。
8. 同一个路径入口已经确定后,端子出口到入口、主路径出口到端子入口的正交折线会尝试不同轴向顺序;当“先走 X”会穿设备、“先走 Y/Z”可绕开时优先使用不穿模的折线顺序。
9. 并行导线 lane 偏移增加默认上限,避免大量导线共路时可视错位距离随 lane 序号无限增大把导线推到线槽或柜体外。lane 序号仍保留,用于容量提示和并行数量报告。
10. 柜内边界对象会先参与路径图过滤再参与路径候选评分。若柜内过滤图不可达且最终路径仍存在柜内越界点中文报告会显示“柜内边界提示”compact 诊断会写入 `route_candidate_boundary_warning_count``route_candidate_boundary_warning_samples`
### 8.1 近期优先级
@ -953,6 +1266,8 @@ PE 线优先路径
11. 两条相交或重叠的线槽中心路径能在交点/重叠端点处连通并自动拐弯。
12. 自动识别出的安装板/柜面能生成低优先级 `RoutingRange`,并可被布线连接使用。
13. 保存 FreeCAD 文档后,自动导线和路由网络仍保留。
14. 如果 `wires[].wire_style_id` 能在 `wire_properties` 中解析,生成导线会使用对应的显示颜色、线宽和线型;解析失败时诊断显示 `Missing`,但仍按默认蓝色样式生成导线。
15. “生成布线连接”后的 `RoutingConnectionBatch` 诊断对象保存最终 report包括 `hidden_route_carriers`、`routing_path_network_updated`、路径网络检查结果和 `no_routed_connections` 等问题码。
## 10. 开发验证命令

@ -691,7 +691,10 @@ QETExchangeDevices
3. 点击 `生成布线连接`
4. 查看状态中的 routed、collision_warnings、missing_terminals。
5. 若有 missing terminals说明某些 2D 端子没有对应工程端子。
6. 保存。
6. 在树目录 `QETWiring_05_Diagnostics` 下查看 `RoutingConnectionBatch`。该对象会保存 `QetProjectUuid``QetDiagnosticOk` 表示本次批量布线是否没有问题码;`QetDiagnosticIssueCodes` 直接列出问题码;`QetDiagnosticIssueLabels` 直接列出中文问题标签;`QetDiagnosticMessage` 是本次批量布线中文摘要;`QetDiagnosticJson` 是 compact 诊断明细,包含 `runtime_version`、`issue_codes`、缺失端点、碰撞、路径质量、容量、柜内边界和路径约束样例。重启 FreeCAD 后手测时,可以先看 `3D 布线连接` 面板状态摘要中的“版本:...”或诊断 JSON 里的 `runtime_version` 是否为当前开发版本,避免旧模块未刷新导致误判。
- 真实工程批量布线还会记录 `batch_network_entry_candidate_limit`、`batch_avoid_obstacles` 和 `batch_obstacle_candidates`。前者表示批量求路时每端最多采用多少个路径入口候选,第二个字段表示是否额外构建障碍过滤路径图,第三个字段表示本次复用的碰撞障碍物候选数量;当前默认性能优先,仍会在结果中输出碰撞诊断。如果批量按钮长时间无响应,优先把这三个字段和 `route_network_carriers / route_network_segments` 一起反馈给开发侧。
7. 如果没有导线任务,也会生成 `RoutingConnectionBatch` 诊断对象,并在 `QetDiagnosticMessage` 中提示“没有导线任务”,便于确认问题来自 QET `wires[]``QETWiring_01_Tasks`
8. 保存。
---
@ -752,6 +755,237 @@ FreeCADExchange 会生成 3D -> 2D 的回写结果。
19. 点击 `检查最近导线``检查全部导线`
20. 点击 `保存并回写`
### 14.1 自动布线前的最小可测装配
如果目标是测试 `3D布线连接` 的自动布线效果,空机柜本身还不够。至少需要先完成下面的最小装配:
1. 安装板或背板已经放入机柜,并作为设备安装基准。
2. 导轨已经贴到安装板或背板上。
3. 线槽已经放到柜内,或已经用草图/Draft 线定义用户主路径。
4. QET 导入的真实设备实例已经摆到导轨或安装板上。
5. 已点击 `生成工程端子`,工程端子能在 `QETTerminals_*` 分组中看到。
6. 如需限制导线不能跑出柜外,选择柜内空间、柜体或辅助包围盒,点击 `选中对象作为柜内边界`
完成后按下面顺序检查:
```text
3D布线连接
-> 检查布线准备度
-> 准备布线布局空间
-> 生成布线路径网络
-> 检查布线路径网络
-> 生成布线连接
```
如果 `检查布线准备度` 显示:
```text
路径网络0 段
布线源:未识别到线槽/布线面/用户路径
柜内边界:未标记
```
说明当前问题仍是装配或路径源准备不足,不应把此时的布线效果当作自动布线算法结果。
每次点击 `检查布线准备度`,树目录 `QETWiring_05_Diagnostics` 下会刷新一个 `RoutingPreflight` 诊断对象。该对象会保存 `QetProjectUuid``QetDiagnosticOk` 表示预检是否通过,`QetDiagnosticIssueCodes` 直接列出问题码,`QetDiagnosticIssueLabels` 直接列出中文问题标签,`QetDiagnosticMessage` 是中文摘要;展开属性中的 `QetDiagnosticJson`,可以查看缺失端点、路径源数量、柜内边界数量、路径网络诊断和导线样式库状态。这个对象只保存最新一次预检结果,避免多次测试后诊断对象堆积。
`检查布线准备度` 默认不再抽样求解导线可达性,避免真实机柜中大量设备、路径 carrier 和障碍对象导致预检长时间卡住。需要排查少量导线是否能连通时,再把面板里的 `可达性抽样` 数量从 `0` 调到 1、5 或更高;这个抽样只用于诊断,不影响正式点击 `生成布线连接` 时的全量布线。
预检阶段也会读取路径网络诊断摘要。如果已经标记 `CabinetInterior`,但工程端子或主路径 carrier 越出柜内边界,`检查布线准备度` 会显示“路径网络检查提示”,并带出“越界端子”或“越界路径”样例。这样可以在生成导线前发现装配位置、端子 LCS 或用户路径本身的问题。
如果通过 QET 的 `3D` 按钮启动 FreeCAD随后关闭它新建的工程再手动打开已经装配好的 `FCStd`,可以直接在装配工程里点击 `检查布线准备度`。中文报告会显示 `导线来源`:显示 `QET 会话交换数据` 表示 FreeCAD 仍在读取 QET 按钮传入的 `2d_to_3d.json / wires[]`;显示 `当前 FreeCAD 文档任务` 表示读取的是当前文档中已保存的 `QETWiring_01_Tasks`;如果导线任务为 0则需要重新从 QET 导入或确认当前会话交换数据是否还在。
同一份中文报告还会显示 `运行版本`。如果截图中没有 `运行版本``导线来源`,说明当前 FreeCAD 窗口仍在使用旧的 `AutoRouting.py`,需要完全关闭 FreeCAD 后重新从 QET 3D 按钮启动,再打开已装配工程。
如果预检发现导线端点没有匹配到 3D 工程端子,`QetDiagnosticMessage` 的“端点缺失示例”会同时显示导线标签和起终点端子,例如 `导线 N4111terminal-start -> terminal-missing`。这样可以先回到 QET 导线任务或 FreeCAD 工程端子绑定处排查,而不必只靠 terminal UUID 猜是哪根线。
端点缺失明细还会显示 `FreeCAD同设备端子=N`。如果该值为 `0`,说明这根导线端点所属的 2D 设备在当前 FreeCAD 工程里没有任何 3D 工程端子,优先检查该设备是否已装配、是否已生成端子 LCS、以及 `project_2d3d_terminal_binding` 是否包含对应 `terminal_uuid`。如果该值大于 `0` 但目标端子仍缺失,通常表示同设备已有端子,但这一个端子的 `terminal_uuid` 没有绑定或不一致。
每次点击 `检查布线路径网络`,同一目录下会刷新 `RoutingPathNetwork` 诊断对象。该对象会保存 `QetProjectUuid``QetDiagnosticOk` 表示路径网络检查是否通过,`QetDiagnosticIssueCodes` 直接列出问题码,`QetDiagnosticIssueLabels` 直接列出中文问题标签,`QetDiagnosticMessage` 会直接提示空路径网络、端子未接入、端子接入过长、端子越出柜内边界、路径对象几何无效、路径越出柜内边界、孤立路径网络等问题;`QetDiagnosticJson` 保存完整诊断明细和 `issue_codes`
面板中的 `端子接入警告距离 mm` 用于判断“端子接入过长”。设为 `0` 时按默认规则自动计算;如果当前机柜尺度较大,且 600-700mm 的端子接入属于可接受的设备局部出线,可以把该值调到 700mm 左右再检查。这个参数只影响质量告警,不会放宽 `端子接入最大距离 mm`,也不会让超过最大距离的端子强行接入。
如果有线槽但导线仍大量走布线面,优先看 `RoutingPathNetwork.QetDiagnosticIssueCodes` 是否包含 `wire_ducts_without_terminal_access / 线槽未接入端子主网络`。这个问题表示线槽已经识别成路径 carrier但它所在的路径组件没有任何 `TerminalAccess`,导线很难自然进入线槽。中文报告会尽量显示“建议桥接到哪个主网络”和最近距离;`QetDiagnosticJson.wire_ducts_without_terminal_access[].bridge_suggestion` 会保存建议连接的两段 carrier、两个最近点和距离。处理方式是在 FreeCAD 中用 UserPath、线槽开口或桥接路径把线槽组件接到端子接入所在的主网络再重新生成布线路径网络和导线。
`生成布线路径网络` 不会把 FreeCAD 的 Origin 坐标轴、已有 `QETRouteCarrier*` 或异常巨大包围盒对象当成用户路径源。真正的 `UserPath` 需要来自你选中的草图线、Draft 线、带 `Points` 的路径对象,或通过 `按诊断建议生成桥接` / `选中两路径生成桥接` 生成。如果 `生成布线连接` 后诊断显示 `路径采用:线槽/主路径 0 条,布线面/辅助路径 N 条`,说明当前导线基本都在走安装板/门板等辅助 RoutingRange优先补线槽到端子主网络的桥接路径或手动画柜内主路径后点击 `选中路径作为用户路径`
`生成布线连接` 会先按路径网络诊断里的 `wire_ducts_without_terminal_access.bridge_suggestion` 自动生成一次 `UserPath` 桥接,再重新生成路径网络并开始布线。脚本或调试场景直接调用 `route_eplan_connection_tasks()` 时,也会先执行同一类诊断桥接,避免绕过面板按钮后又退回布线面兜底。报告中如果出现 `自动诊断桥接:生成 UserPath N 条`,表示系统已经自动把孤立线槽组件接入端子主网络。这个动作只修改 FreeCAD 文档中的路径 carrier不写 QET 数据库;重复点击时已存在的同点桥接不会重复生成。
端子局部接入会优先连接 `WireDuct / UserPath / WiringCutOut` 等主路径;即使附近有更近的 `RoutingRange` 布线面,只要主路径仍在 `端子接入最大距离 mm` 内,系统会优先接主路径。`RoutingRange` 只作为没有线槽、没有用户主路径、或主路径距离超限时的兜底区域。这一规则用于贴近 SW/EPLAN 的工程习惯:设备端子先接入线槽/主路径,柜内大路径再沿主网络走。
手动桥接建议流程:
1. 在树目录或 3D 视图中找到诊断提示的线槽 carrier例如 `QETRouteCarrier`、`QETRouteCarrier_1`。
2. 优先点击 `按诊断建议生成桥接`。系统会先刷新路径网络诊断,再按 `bridge_suggestion` 自动生成对应的 `UserPath` 桥接路径;如果重复点击时网络已经接通,不会再重复生成同一条桥。
3. 如果诊断没有建议,或建议对象已经失效,再找到端子接入所在的主网络,通常是靠近设备端子、安装板或布线面的 `TerminalAccess` / `RoutingRange` carrier如果报告已经显示“建议桥接到 xxx”优先选中这个目标 carrier。
4. 同时选中线槽 carrier 和目标 carrier点击 `选中两路径生成桥接`。系统会在两条路径最近点之间生成一段 `UserPath` 桥接路径。
5. 如果选不到已生成的 carrier也可以选中能找到 live carrier 的源路径对象若仍无法生成再用草图线、Draft 线或已有线段,从线槽开口附近画到主网络附近,然后点击 `选中路径作为用户路径`
6. 桥接线段不需要变成实体线槽,但应落在合理柜内空间,避免悬空穿过设备。
7. 点击 `生成布线路径网络` 或直接重新点击 `生成布线连接`
8. 再点击 `检查布线路径网络`,确认 `wire_ducts_without_terminal_access` 消失或数量减少。
9. 重新生成导线后,选中导线查看 `QetRouteQualityStatus`;如果从 `FallbackPathWarning` 变成更少兜底,说明线槽接入已经改善。
完成 `检查布线准备度`、`检查布线路径网络` 和 `生成布线连接` 后,可以点击 `汇总布线诊断`。该按钮不会重新布线,也不会修改 QET 数据;它只读取 `QETWiring_05_Diagnostics` 下最新的 `RoutingPreflight`、`RoutingPathNetwork`、`RoutingConnectionBatch`,合并 `issue_codes` 并在面板中显示“通过/未通过、缺了哪类诊断、主要问题是什么”。同时会刷新一个 `RoutingDiagnosticSummary` 诊断对象,保存 `QetDiagnosticIssueCodes`、`QetDiagnosticIssueLabels`、`QetDiagnosticMessage` 和 `QetDiagnosticJson`,便于手动测试后在树目录中复查或复制给开发侧分析。汇总消息会显示本次诊断采用的 `runtime_version`,优先取 `RoutingConnectionBatch`,用于确认当前工程确实由最新自动布线模块生成。手动测试截图或录屏时,建议最后点一次这个按钮,方便快速判断问题来自装配准备、路径网络,还是批量布线结果。
如果只点击了 `生成布线连接`,没有单独点击 `检查布线准备度``检查布线路径网络``汇总布线诊断` 会把最新 `RoutingConnectionBatch` 当作最终诊断入口;只要批量报告内已经包含路径网络诊断摘要,就不会再因为缺少独立的 `RoutingPreflight / RoutingPathNetwork` 对象而判定失败。也就是说,真实手测可以采用“生成布线连接 -> 汇总布线诊断”的简化流程;汇总结果仍会显示端子缺失、路径网络、碰撞、柜内越界等真正问题。
批量报告里的 `布线布局空间` 表示本次点击时新生成或刷新的路径 carrier 数量;如果线槽路径已经存在,可能会显示 `线槽路径 0 条`。这不等于当前工程没有线槽路径。继续看下一行 `当前路径网络`,它表示本次真实参与求路的全部路径 carrier例如 `当前路径网络:线槽路径 4 条,线槽开口 8 条,用户路径 2 条,端子接入 132 条,布线面 391 条`。判断是否识别到线槽时,以 `当前路径网络``路径采用:线槽/主路径 N 条` 为准。
如果旧版工程中已经存在空白的诊断对象,`汇总布线诊断` 会把它标记为 `diagnostic_json_empty / 诊断 JSON 为空`。这通常表示该诊断对象不是当前版本完整生成的,应重新执行对应步骤,例如重新点击 `检查布线准备度`、`检查布线路径网络` 或 `生成布线连接`
如果工程里已有旧版 `RoutedConnection` 导线对象,但单根导线缺少 `QetRouteDiagnosticsJson`,汇总诊断会提示 `routed_wire_diagnostics_missing / 导线诊断缺失`,并给出一条导线 Label 示例。这类旧线可以显示在模型里,但无法提供碰撞、越界、接入距离等新诊断,应重新执行 `生成布线连接`
如果单根导线的 `QetRouteDiagnosticsJson` 存在但不是合法 JSON汇总诊断会提示 `routed_wire_diagnostics_invalid / 导线诊断 JSON 无效`,并给出一条导线 Label 示例。这通常表示对象来自旧版本、手工修改过属性,或导线对象保存过程中诊断字段损坏,应重新生成布线连接。
`RoutingConnectionBatch``QetDiagnosticJson.route_samples[]` 会保留少量样例导线。样例里可以直接看 `access`、`collision_summary`、`quality`、`capacity`、`boundary` 分组,分别对应长接入、碰撞/安全间隙、路径兜底、容量压力和柜内越界状态。手动测试后如果只想快速反馈问题,可以复制这个诊断对象 JSON开发侧不一定需要逐根在模型里点选导线。
样例中的 `wire_object_label` 与左侧树目录中导线对象 Label 一致。复制诊断 JSON 给开发侧时,可以同时说明这个 Label开发侧就能更快在 `QETWiring_04_Routed` 下定位对应导线。
接入距离、路径质量、候选入口碰撞风险、柜内边界、路径约束、容量压力和碰撞等 warning sample 都会尽量带 `wire_object_label`。手动反馈时优先复制这个字段,比只说“第几条线”更稳定。对于 `missing_route_network_samples[]`、`error_samples[]`、`missing_endpoint_samples[]` 这类失败样例,`wire_object_label` 是任务侧最接近对象标题的显示名,不一定已经生成出真实 3D 导线对象。
中文报告中,碰撞示例和缺失端点示例会优先显示 `wire_object_label`,便于直接在左侧树目录定位对象;导线样式、路径示例和统计类提示仍优先显示短导线号,避免报告过长。
`route_samples[]` 会优先保留有问题码的导线样例。也就是说,如果本次布线大多数线正常、少数线穿模或接入过长,压缩诊断会优先把异常线放进样例里,便于后续排查。
如果批量报告出现 `MissingTerminal / 缺失端子`,先看 `missing_endpoint_samples[]`。每个缺失侧会记录 `*_element_uuid`、`*_instance_id`、`*_terminal_display`、`*_device_in_scene`、`*_device_name`、`*_element_terminal_count`、`*_instance_terminal_count`、`*_missing_endpoint_reason_code` 和中文 `*_missing_endpoint_reason_label`。其中 `missing_device_binding_metadata / 导线端点缺少 2D 设备绑定信息` 表示导线任务端点缺少 `element_uuid`FreeCAD 无法判断缺失端子属于哪个 2D 设备;第一版不要求 QET 在 `wires[]` 端点提供 `start/end_instance_id`。`device_not_in_3d_scene / 该 2D 设备未在 FreeCAD 场景中找到` 表示这条导线引用的设备当前没有对应 3D 设备实例,应优先检查该设备是否已导入、装配并完成 2D/3D 绑定;`no_3d_terminals_for_element / 该 2D 设备在 FreeCAD 中没有工程端子` 表示设备实例在场景中,但没有生成工程端子,应重新生成端子或检查模板端子。这些都不是线槽、用户路径或 Dijkstra 路径网络问题。
`汇总布线诊断` 会把批量报告里的缺端子信息汇总成类似 `缺端子4 条(该 2D 设备未在 FreeCAD 场景中找到 4 处)``缺端子2 条(该 2D 设备在 FreeCAD 中没有工程端子 2 处)` 的文本。这个统计能帮助快速判断当前问题是不是设备未导入/未绑定、设备端子未生成,还是路径网络不连通。
当缺端点原因是 `device_not_in_3d_scene` 时,`汇总布线诊断` 的建议会优先提示“检查缺失 3D 设备是否已导入、装配并完成 2D/3D 绑定”。这种情况下场景里没有可选中的缺失设备,不能靠调整线槽或点击路径按钮解决;应先回到设备导入、装配和绑定流程。只有原因是 `no_3d_terminals_for_element``no_3d_terminals_for_instance` 时,才优先使用 `选择缺端子设备` 去定位已存在但缺工程端子的设备。
当缺端点原因是 `missing_device_binding_metadata` 时,`汇总布线诊断` 的建议会提示“检查 QET 导线端点是否提供 element_uuid 和 terminal_uuid第一版不要求 start/end_instance_id”。这表示 FreeCAD 无法判断缺失端子属于哪个 2D 设备,应由 QET 导线任务补齐端点设备标识;第一版只要求 `wires[]` 每个端点携带 `terminal_uuid``element_uuid``instance_id` 由 FreeCAD 通过 `devices[]`、端子绑定或当前 3D 文档回查。
直接点击 `生成布线连接` 时,如果本次没有生成任何有效导线且缺端子原因包含 `missing_device_binding_metadata`,面板中文报告也会直接提示 `QET 导线端点缺少 element_uuid`,并注明第一版不要求 `start/end_instance_id`。这时不要去调整线槽、柜内边界或 Dijkstra 参数,应先检查 QET 导出的 `wires[]` 端点数据是否完整。
如果本次大多数导线已经成功、只有少量导线缺端子,`生成布线连接` 的中文报告也会显示 `缺端子原因提示`。例如真实工程中 `total_wires=75, routed=71, missing_terminals=4` 时,如果缺失侧设备未在当前 FreeCAD 场景中找到,报告会直接提示先检查设备导入、装配和 2D/3D 绑定,而不是只显示一条缺失样例。报告还会按设备聚合输出类似 `需补端子设备UD:8 缺 2 处as、saUD:10 缺 1 处saUD:5 缺 1 处1` 的文本;这表示自动布线本身已经完成可布的 71 条,剩余 4 条应先补这些 3D 设备/端子场景数据,再重新生成布线连接。
批量诊断的 `issue_codes` 会把缺端子原因提升到顶层,例如 `device_not_in_3d_scene / 3D场景缺少设备`、`missing_device_binding_metadata / 端点缺少绑定信息`、`terminal_uuid_not_in_element / 端子UUID不匹配`、`no_3d_terminals_for_element / 设备缺少工程端子`。手测时如果看到 `device_not_in_3d_scene`,先补设备导入、装配和 2D/3D 绑定;如果看到 `terminal_uuid_not_in_element`,再用 `选择缺端子候选端子` 核对同设备端子 UUID 和脚号。
批量诊断还会写入 `missing_terminal_summary`。其中 `reason_code_counts` 汇总每类缺端子原因,`device_groups[]` 按缺失侧设备归并,包含 `element_uuid`、`instance_id`、缺失端子显示名、端子 UUID 和相关导线号。把该分组发给装配或绑定相关同事时,比逐条复制 `missing_endpoint_samples[]` 更容易定位需要补哪个设备。
这个 `missing_terminal_summary` 不只存在于 `RoutingConnectionBatch.QetDiagnosticJson`,也会直接存在于本次批量布线返回的原始 report 中。后续脚本、面板按钮或调试探针如果要拿缺设备清单,应优先读取 `report.missing_terminal_summary.device_groups[]`,不要解析中文 `需补端子设备` 文本。
如果布线是直接从 FCStd 里的任务对象发起而任务对象没有保存完整设备列表FreeCAD 会尝试从当前 QET 交换上下文的 `2d_to_3d.json` 回补 `devices[]`,用于补齐 `device_groups[].instance_id` 和设备标签。这个回补只读 JSON不写数据库、不覆盖当前导线任务如果项目 UUID 不一致,则不会回补。批量 report 会写入 `context_devices_loaded`、`context_device_count` 和 `context_devices_json_path`,用于判断本次是否真的加载了上下文设备列表。若缺设备分组里 `instance_id` 仍为空,优先检查当前 FreeCAD 会话是否还保留正确的交换 JSON 上下文,或环境变量 `QET_2D_TO_3D_JSON` 是否指向当前项目。
如果工程里保存的是旧版批量诊断,缺少 `*_missing_endpoint_reason_code / label``汇总布线诊断` 会尝试根据当前 FreeCAD 文档现场回填原因。只要样例里还有 `terminal_uuid`、`element_uuid` 或 `instance_id`通常可以直接回填成“QET 端点绑定信息缺失”“设备未在 3D 场景中找到”“设备存在但无工程端子”等原因。只有样例里连这些基础字段也缺失时,才会显示 `缺端点原因未记录`,此时需要重新点击 `生成布线连接` 刷新诊断。
如果要快速定位这些缺端子设备,点击 `选择缺端子设备`。系统会从最新批量布线诊断的 `missing_endpoint_samples[]` 中读取缺失侧的 `*_instance_id``*_element_uuid`,并在 FreeCAD 中选中对应 3D 设备。若缺失原因是 `device_not_in_3d_scene`,当前场景里没有可选中的 3D 对象,面板会优先显示可读设备标签,例如 `UD:8`、`UD:10`,并在状态栏补充对应 `instance_id`,而不是只显示很长的 UUID这类提示表示要先补设备导入、装配和 2D/3D 绑定。控制器返回值也会包含 `missing_terminal_device_instance_ids[]``missing_terminal_device_element_uuids[]`,便于脚本直接交给装配/绑定流程处理。该按钮只做定位,不会自动创建端子;选中后应检查该设备是否已有工程端子、端子是否来自 QET 绑定、以及设备是否装配在当前工程中。
如果点击 `选择缺端子设备` 后没有选中任何对象,并且诊断原因是 `device_not_in_3d_scene`,面板会直接提示缺失侧 2D 设备未在当前 FreeCAD 场景中找到。这表示没有可选中的设备对象,应先补设备导入、装配和 2D/3D 绑定;如果原因是 `missing_device_binding_metadata`,面板会提示 QET 导线端点缺少 `element_uuid`,应先补齐 QET 端点绑定信息。
如果缺端子样例中一端已经找到、另一端缺失,可以点击 `选择缺端子另一端`。系统会选择这些失败导线里已找到的工程端子,例如真实工程中起点缺失但终点已找到时,会选中终点端子。这个按钮用于快速确认“这根失败导线本来要连到哪里”,再回到缺失侧设备检查是否没有生成工程端子、端子脚号是否不一致或 `terminal_uuid` 是否绑定错位;它只定位端子,不会自动补端子或改 QET 数据。
如果缺端点原因是 `terminal_uuid_not_in_element / 同设备存在端子,但没有匹配该 terminal_uuid`,可以点击 `选择缺端子候选端子`。系统会从 `*_instance_terminal_samples``*_element_terminal_samples` 中选择同设备/同实例已有的工程端子,便于直接查看这些端子的 `QetTerminalUuid`、`QetTemplateSlotName`、`QetTerminalLabel` 和位置。若候选端子存在但 UUID 不一致,优先检查 QET 端子绑定或重新生成工程端子;若没有候选端子,则回到 `选择缺端子设备` 检查该设备是否真的生成了端子。
如果已经标记柜内边界,自动布线会先在柜内过滤后的路径图上求路;如果这张图不能连通两端,才回退到原始路径图。批量报告里的样例 network 会记录 `boundary_filtered``boundary_filtered_segments`,用于判断本次路线是否使用了柜内过滤图。如果某条导线仍跑出柜内区域,批量报告会提示“柜内边界提示”。选中具体导线对象时,也可以在属性里查看 `QetRouteBoundaryAware`、`QetRouteBoundaryStatus` 和 `QetRouteBoundaryViolationCount`:其中 `BoundaryWarning` 表示该导线存在柜内越界点,应优先补柜内 `UserPath`、线槽或设备局部路径。
导线样式来自 QET 导线任务中的 `wire_style_id`FreeCAD 会按项目数据库 `wire_properties` 查询颜色、线宽、线型和规格文本。手动测试时,选中生成的 `RoutedConnection` 导线,可以先看 `QetWireStyleStatus``Resolved` 表示已查到样式,`Missing` 表示样式 ID 没查到;再看 `QetWireColorText`、`QetWireLineType`、`QetWireLineWidth`、`QetWireDiameterMm` 等属性确认原始样式数据。GUI FreeCAD 中导线会按解析到的颜色、线宽和线型显示;如果用 `FreeCADCmd` 做命令行验证,因为没有 `ViewObject`,只能确认样式属性写入,不能证明界面颜色已经显示。
如果 `检查布线路径网络` 提示 `route_carriers_outside_boundary / 路径越出柜内边界`,说明某条线槽中心线、`UserPath` 或过线孔路径本身已经有点落在 `CabinetInterior` 外。此时应先调整该路径源或重新标记正确的柜内边界,再生成导线;否则后续自动布线即使能连通,也容易把线带到柜外。
如果提示 `terminals_outside_boundary / 端子越出柜内边界`,说明至少一个工程端子的原点或出线末端落在 `CabinetInterior` 外。优先检查该设备是否真的装配到机柜内、端子 LCS 是否跟随设备实例移动,以及柜内边界对象是否过窄或标错。
如果要快速定位这些对象,点击 `选择越界路径/端子`。系统会从最新 `RoutingPathNetwork` 诊断里选择越界的路径 carrier 和工程端子。选中后通常按下面顺序处理:若是 `UserPath` 或线槽越界,调整源草图/线槽或重新标记正确柜内边界;若是端子越界,检查设备是否真正装配到柜内、端子 LCS 是否跟随设备移动;修正后重新点击 `检查布线路径网络``生成布线连接`
如果直接点击 `生成布线连接(全部导线)`,批量报告也会带出同类路径网络检查提示。出现“越界路径:<路径标签> N 个越界点”时,优先在树目录中定位该路径源或其生成的 carrier修正后重新生成布线路径网络和导线。出现“越界端子<端子对象/UUID> N 个越界点”时,优先定位该端子所属设备,确认设备已经装配到柜内且端子 LCS 跟随实例移动。
如果多根导线共用同一路径,选中具体导线对象可以查看 `QetRouteLaneIndex`、`QetRouteLaneOffsetMm`、`QetRouteParallelWireCount`、`QetRouteMinCarrierCapacity` 和 `QetRouteCapacityStatus`。其中 `CapacityWarning` 表示这条线所在共享路径的并行线数已经超过当前路径容量,需要补备用路径、调整线槽容量或优化路径约束。
如果某条线已经生成但端子附近拉出很长一段斜线或折线,选中该导线对象查看 `QetRouteEntryDistanceMm`、`QetRouteExitDistanceMm`、`QetRouteAccessWarningDistanceMm` 和 `QetRouteAccessStatus`。其中 `LongAccessWarning` 表示起点或终点到主路径网络的接入距离超过当前告警阈值;`QetRouteAccessWarningSides` 会显示触发侧,`entry` 是起点侧,`exit` 是终点侧。出现该提示时,优先检查设备是否已经装配到正确位置、端子局部出线路径是否存在、用户路径或线槽是否离设备端子太远。
`检查布线路径网络` 和批量布线的 `routing_path_network_diagnostic.long_terminal_accesses[]` 会保留长接入样例。样例里包含 `parent_device_label / parent_device_name`、`terminal_origin`、`terminal_access_points`、`terminal_access_dominant_axis` 和 `terminal_access_axis_lengths_mm`。如果 `terminal_access_dominant_axis``z`,且 `z` 方向长度占大头,通常表示端子点和柜内主路径平面高度差过大;优先检查该设备装配高度、端子 LCS 方向,或为该设备补局部出线路径。
如果要快速定位这些端子,点击 `选择长接入端子`。系统会从最新批量布线诊断中的 `routing_path_network_diagnostic.long_terminal_accesses[]` 查找端子对象并选中。真实工程中类似 PEN 325-328 这类端子被选中后,可以直接检查它们是否位于异常高度、是否缺设备局部出线路径,或附近是否缺主路径入口。
如果要从设备角度排查,点击 `选择长接入设备`。系统会读取长接入样例里的 `parent_device_name / parent_device_label` 并选中对应设备。通常先用 `选择长接入端子` 看具体端子点,再用 `选择长接入设备` 检查该设备整体是否装配到正确高度、端子 LCS 是否随设备移动,以及设备附近是否需要补局部出线路径。
如果确认是某个工程端子缺少设备局部出线路径,可以直接在当前装配工程里补:
1. 选中一个可布线工程端子。
2. 再选中一条表示该端子出线方向的草图、Draft 线、边或连续 Wire。
3. 点击 `选中端子设置局部出线`
4. 系统会把所选路径写入该工程端子的 `QetTerminalLocalRoutePointsJson`,只修改当前 FreeCAD 文档,不写 QET 数据库。
5. 重新点击 `生成布线路径网络``生成布线连接`
这个动作适合处理端子附近拉出长斜线、从设备内部穿模、或默认 LCS 出线方向不符合实物的情况。若同类设备后续会反复使用,应把局部出线路径沉淀到 FCStd 设备模板里;当前装配工程里的按钮更适合现场手动测试和个别端子的快速修正。
如果要确认某根线到底走了哪条线槽或黄色草图路径,选中导线对象查看 `QetRouteSourceLabels`。该属性会优先显示源路径标签;同一个草图拆成多条路径时会显示 `源路径标签(路径2)` 这类后缀。`QetRouteCarrierNames` 则显示实际经过的 carrier 对象名,适合在左侧树目录中继续定位。
生成导线对象在左侧树目录中的 Label 会尽量显示为 `导线号: 起点端子 -> 终点端子 (状态)`,例如 `N4111: terminal-start -> terminal-end (CollisionWarning)`。如果批量报告提示某条线有问题,可以先按导线号或端子 UUID 在 `QETWiring_04_Routed` 下定位对象,再查看它的属性。
选中导线对象后,也可以先看 `QetRouteIssueCodes``QetRouteIssueLabels`。这两个属性会用与批量诊断一致的问题码汇总该线自身的问题,例如端子接入过长、碰撞告警、路径质量告警、路径容量压力或柜内越界。看到问题码后,再展开对应的详细属性。
如果要先把本次批量布线中的问题线全部找出来,点击 `选择异常导线`。系统会从最新批量诊断 `route_samples[]` 中选择所有带 `issue_codes` 的 RoutedConnection 导线,同时也会扫描已生成导线对象自身的 `QetRouteIssueCodes`,避免 compact 样例数量有限时漏掉问题线。这个按钮适合统一排查长接入、柜内越界、容量压力、路径质量或碰撞问题;不会选择没有问题码的正常导线,也不会修改路径。
如果只想定位“主路径网络不够导致无法按主路径绕开碰撞”的线,点击 `选择缺主路径导线`。系统会选择带 `main_path_detour_missing` 的 RoutedConnection 导线;这类线通常表示选择性避障重算发现了可行绕法,但该绕法会退回 `RoutingRange`、`AuxiliaryPath` 或其它兜底空间路径,所以当前版本按主路径优先策略拒绝采用。选中后应优先补黄色草图 `UserPath`、桥接线槽/主路径、调整线槽入口,或给设备端子补局部出线路径,而不是直接把 fallback 结果当作正式布线。
如果要看这些缺主路径导线当前实际走了哪条线槽、`UserPath` 或源草图,点击 `选择缺主路径线路径`。系统会先从带 `main_path_detour_missing` 的样例中选择当前路径 carrier 和源对象,并补充扫描已生成导线对象自身的 `QetRouteIssueCodes / QetRouteTrackJson`,避免 compact 样例数量有限时漏掉问题线;其它异常线的路径不会混进来。这个按钮适合与 `选择拒绝兜底路径` 对照:前者看“现在走哪里”,后者看“算法想绕到哪里但被拒绝”,两边之间通常就是需要补主路径或局部路径的位置。
如果要继续定位异常导线实际经过了哪条线槽、`UserPath` 或源草图,点击 `选择异常导线路径`。系统会从最新异常 `route_samples[]``carrier_names``route_track.segments[].carrier` 中选择路径 carrier并尽量选择其 `source_name / source_label` 对应的源对象。选中后可以检查该路径是否越界、穿模、容量不足,或直接对源路径设置 Required/Forbidden、调整容量、移动草图路径。
如果你已经在树目录或 3D 视图中选中了某一根问题导线,可以直接点击 `选择选中导线路径`。系统会读取该导线对象上的 `QetRouteTrackJson`,反向选择这根线实际经过的路径 carrier 和源草图/线槽;这个功能不依赖 compact `route_samples[]`,适合诊断样例为空、样例数量不足,或你想单独排查某一根穿模/越界/共线导线的场景。该操作只定位对象,不修改 QET 数据库、不重新生成导线。
如果这根线带 `main_path_detour_missing`,可以在选中导线后继续点击 `选择拒绝兜底路径`。系统会读取导线 `QetRouteDiagnosticsJson` 里被拒绝的 `RoutingRange` / `AuxiliaryPath` 标签,并反选对应的路径源对象;状态栏会显示 `需补路径位置`,列出前几个被拒绝的兜底路径标签。这个按钮的用途不是把兜底路线改成正式结果,而是帮助判断算法“想从哪里绕过去”:若选中的是安装板布线面或辅助路径,通常应在该区域补一条明确的黄色草图 `UserPath`、桥接到线槽/主路径,或给设备端子补局部出线路径。
`汇总布线诊断` 也会扫描已生成导线对象的 `QetRouteIssueCodes`,输出类似 `异常导线2/71 条(端子接入过长 1 条、碰撞告警 1 条)` 的统计。这个数量来自实际导线对象,不受 `route_samples[]` 样例数量限制,更适合作为手测总览。
执行 `生成布线连接(全部导线)` 后,批量报告本身也会带出 `缺主路径绕行N 条``需补路径位置`,不用必须再点一次汇总诊断才能看到剩余补路区域;后续若需要更完整的异常线统计,再点击 `汇总布线诊断`。如果系统还能读取到这些导线当前实际经过的主路径,会继续显示 `补路配对:兜底区域 -> 当前主路径`,例如 `FRONT DOOR-R_P00 -> WireDuct_Body001 4 条`。这表示应优先在该门板/布线面区域和对应主线槽或 UserPath 之间补桥接路径,而不是把门板兜底路线直接当成正式布线。
如果存在 `main_path_detour_missing``汇总布线诊断` 会额外显示 `缺主路径绕行N 条`,并把这些导线诊断中被拒绝的 `RoutingRange / AuxiliaryPath` 标签汇总成 `需补路径位置`。如果单线诊断中保存了 `QetRouteTrackJson`,汇总还会显示 `补路配对`,把被拒绝的兜底区域和当前实际主路径成对列出来。这一步适合在逐根选线前先判断问题集中在哪个柜内区域、应桥接到哪条主路径;随后再点击 `选择缺主路径导线``选择拒绝兜底路径` 做单线定位。
如果 `补路配对` 两端都能在当前 FreeCAD 文档里找到 live carrier可以点击 `按诊断建议生成桥接`。该按钮除了处理“线槽未接入端子主网络”的桥接建议,也会按 `兜底区域 -> 当前主路径` 配对生成一段 `UserPath` 桥接。生成的桥接对象会写入 `QetRouteBridgeKind=MainPathDetourBridge`、`QetRouteBridgePairLabel`、左右源对象 `Name/Label`,便于后续在属性面板里确认这条桥到底连接了哪两个区域。生成后重新点击 `生成布线连接(全部导线)`,检查 `main_path_detour_missing` 是否减少。若状态栏提示“未找到配对”,说明对应兜底区域或主路径源对象无法定位,需要先用 `选择缺主路径补路位置` 查看两端是否存在,或手动画 UserPath。
`生成布线连接(全部导线)` 默认只按当前路径网络布线,不会悄悄把诊断建议固化成新的 `UserPath`。如果现场确认某个 `main_path_detour_missing` 确实需要补主路径,可先用面板里的桥接/补路径按钮手动生成,或在脚本选项中显式开启 `auto_create_main_path_detour_bridges`。开启后,如果选择性避障已经找到一条无碰撞或碰撞更少的兜底绕行,但该绕行包含 `RoutingRange` 而被主路径优先策略拒绝,系统会先把这条已验证折线固化成 `QetRouteBridgeKind=MainPathDetourPath``UserPath`,再按 `兜底区域 -> 当前主路径` 生成 `MainPathDetourBridge`,随后只重试受影响的导线,不会整批全量重跑。批量报告中的 `auto_main_path_detour_bridges` 会记录生成数量、重试导线数和替换结果。
碰撞告警会自动过滤一类导入结构件误报:如果障碍物没有 QET `element_uuid`,并且位于 `QET Exchange Devices / QETCabinet / LinkGroup / Compound / NAUO` 这类导入装配上下文,同时名称或父装配命中柜体、门板、支架、盖板等结构关键词,系统会把它视为未绑定结构件,不再让导线变成 `CollisionWarning`。带 `element_uuid` 的真实设备、断路器、端子等仍然保留为碰撞告警,需要通过补局部路径、调整装配或设置路径约束处理。
如果只想从汇总结果直接定位这些区域,点击 `选择缺主路径补路位置`。系统会读取汇总诊断里的 `需补路径位置` 标签,并按对象 `Name / Label` 以及路径源 `QetRouteSourceName / QetRouteSourceLabel` 反选对应的 `RoutingRange / AuxiliaryPath` 来源对象;如果诊断里已经有 `补路配对`,还会同时选择当前实际主路径对应的线槽或 `UserPath` 源对象。状态栏会显示兜底区域、当前主路径和配对统计,例如 `FRONT DOOR-R_P00 -> WireDuct_Body001 4 条`。这个按钮不要求你先选中导线,适合快速查看剩余问题集中在哪个安装板、布线面或辅助路径附近,以及应桥接到哪条主路径。
当汇总诊断已经能读取到缺主路径绕行问题时,建议动作会按顺序提示:先点击 `选择缺主路径导线` 选中问题线,再点击 `选择缺主路径线路径` 对照当前实际路径;如果诊断中还有被拒绝的兜底路径标签,可以先点击 `选择缺主路径补路位置` 快速定位汇总需补区域,也可以在这些导线保持选中的情况下点击 `选择拒绝兜底路径` 查看单线需补路径位置。这个顺序适合处理真实工程中少量剩余碰撞线。
`汇总布线诊断` 还会根据当前问题给出下一步建议,例如 `点击“选择缺端子设备”定位需要补工程端子的设备`、`点击“选择异常导线”定位带问题码的导线`、`点击“选择碰撞父装配”确认结构件后再标记忽略碰撞`。手测时可以先看这一行,再决定下一步点哪个定位按钮。
如果要判断某根线是明显穿模还是只是距离太近,选中导线对象查看 `QetRouteCollisionStatus`、`QetRouteHardIntersectionCount` 和 `QetRouteClearanceWarningCount`。`HardIntersectionWarning` 表示导线穿过障碍包围盒,应优先改路径或设备位置;`ClearanceWarning` 表示导线没有穿过障碍,但低于安全间隙,通常需要微调路径或安全间隙参数。
批量诊断中的 `collision_samples[]` 也会带 `wire_object_label`。如果报告出现“碰撞示例”,可以先复制这个 Label 到树目录中查找对应导线,再结合 `collision_kind` 判断是硬碰撞还是安全间隙。碰撞样例还会带 `obstacle_parent_labels / obstacle_parent_names`,用于判断类似 `NAUO141` 这样的零件属于前门、柜体、安装板还是具体设备;确认是装配辅助件或可穿过结构后,再手动标记为忽略碰撞对象。
批量报告和汇总诊断里的 `top_collision_obstacles[]` 会按碰撞对象聚合高发对象,并保留 `name`、`label`、`collision_kind_counts`、`parent_labels` 和 `parent_names`。中文摘要会显示类似 `NAUO141(FRONT DOOR-R ASS'Y) 6 处`。如果高发对象属于柜体、门板、盖板或安装辅助结构,先确认导线是否真的应该穿过该区域;确认可忽略后再选择对应对象并标记为忽略碰撞。不要只因为对象名像 `NAUOxxx` 就直接全局忽略。
高发碰撞对象还会给出 `resolution_hint_code / resolution_hint_label`。`review_pass_through_structural_obstacle` 表示疑似柜体、门板、支架、盖板等结构件;处理方式是先在模型中定位该对象,确认它不是实际需要避让的设备实体后,选中对象点击 `选中对象忽略碰撞`,再重新生成布线。`review_device_or_layout_collision` 表示更像设备或安装区域穿模,应优先补线槽、`UserPath`、设备局部出线路径,或调整装配位置。
为了避免在树目录里手工查找 `NAUOxxx`,可以先点击 `选择高发碰撞对象`。系统会从最新批量布线诊断的 `top_collision_obstacles[]` 中查找对象,并在 FreeCAD 里选中这些高发碰撞对象。这个按钮只做定位和选择,不会自动忽略碰撞;确认对象确实是柜体、门板、支架或盖板等可穿越结构后,再点击 `选中对象忽略碰撞`
如果诊断里已经能看到 `parent_names / parent_labels`,也可以点击 `选择碰撞父装配`。系统会直接选择高发碰撞对象所属的父装配,例如前门总成或柜体总成;确认这些总成是可穿越结构后,再点击 `选中对象忽略碰撞`,可以一次影响其下层导入子件。
如果汇总诊断已经把高发碰撞对象标记为 `review_pass_through_structural_obstacle / 疑似结构件可确认忽略`,可以点击 `选择结构件碰撞父装配` 先定位检查。确认这些对象确实是柜体、门板、支架、盖板等结构件后,也可以直接点击 `确认结构件忽略碰撞`。该按钮只会把诊断判定为结构件候选的最近结构父装配标记为 `PassThrough`,不会沿父链继续标记 `QET Exchange Devices` 这类工程根组,也不会标记 `review_device_or_layout_collision` 这类疑似设备或布局碰撞。标记完成后需要重新点击 `生成布线连接`,再看碰撞数量是否减少;如果剩余高发对象变成真实设备,应优先补设备局部出线路径、调整 UserPath/线槽入口或检查装配位置。
结构件碰撞处理后,如果 `top_collision_obstacles[]` 剩余对象的 `resolution_hint_code``review_device_or_layout_collision`,可以点击 `选择设备碰撞对象`。该按钮只定位真实设备/布局碰撞对象,不会标记 `PassThrough`,也不会修改数据库。选中后优先检查这些设备附近是否缺少局部出线路径、线槽入口是否离端子过远、用户路径是否穿过设备包围盒,或设备本身是否装配到错误位置。
批量诊断的 `issue_codes` 会把碰撞进一步拆成 `structural_collision_candidates / 结构件碰撞候选``device_or_layout_collisions / 设备/布局碰撞`。前者适合先定位父装配并确认是否可标记 `PassThrough`后者不应直接忽略应回到设备、端子局部出线路径、UserPath 或装配位置处理。
如果碰撞对象是导入总成下的深层子零件,例如 `门板总成 -> Compound -> NAUOxxx`,也可以选择其父装配或中间 Compound 后点击 `选中对象忽略碰撞`。当前版本会沿父装配链递归识别 `PassThrough`,因此父装配被确认可穿越后,其下层导入子件不会继续作为导线障碍。这个操作只改变 FreeCAD 文档内的布线障碍语义,不写入 QET 数据库。
如果要反向查看“哪些导线正在碰撞”,点击 `选择碰撞导线`。系统会从最新批量布线诊断的 `collision_samples[]` 和带 `collision_warnings``route_samples[]` 中查找 RoutedConnection 导线对象并选中。现场排查时可以先点 `选择高发碰撞对象`,再点 `选择碰撞导线`,结合 3D 视图判断是结构件误报、路径离设备太近,还是需要补局部路径。
如果要判断某根线是否真正走了工程主路径,选中导线对象查看 `QetRouteQualityStatus`。`NormalPath` 表示没有使用布线面/辅助路径兜底;`FallbackPathWarning` 表示路线经过了 `RoutingRange``AuxiliaryPath`,可以继续查看 `QetRouteFallbackCarrierKinds``QetRouteFallbackCarrierLabels`。这个状态不是失败,但通常说明需要补线槽、黄色草图 `UserPath`、过线孔或设备局部路径。
### 14.2 第一版自动布线手测验收清单
第一版验收不要只看“模型里有没有线”,而要同时看诊断对象和单线属性。一次有效手测至少记录下面这些结果:
1. 面板状态摘要显示 `版本2026-06-08-runtime-routing-v4` 或更新版本。
2. `检查布线准备度` 能识别到 QET 导线来源、工程端子、路径网络和柜内边界;若有问题,`RoutingPreflight.QetDiagnosticIssueCodes` 能说明原因。
3. `检查布线路径网络` 不应出现空网络、路径对象几何无效、端子越出柜内边界或路径越出柜内边界;如果出现,应先修装配或路径源。
4. `生成布线连接` 后,`RoutingConnectionBatch.QetDiagnosticJson.runtime_version` 与面板版本一致。
5. 有导线任务时,`RoutingConnectionBatch.routed` 应大于 0如果为 0应优先看 `missing_endpoint_samples`、`missing_route_network_samples` 或 `error_samples`
6. 正常导线的单线 `QetRouteIssueCodes` 应为空;若存在问题码,应能归类到端子接入、碰撞、柜内越界、容量压力、路径质量或路径约束。
7. 已标记 `CabinetInterior` 时,优先要求 `QetRouteBoundaryStatus=InsideBoundary`;出现 `BoundaryWarning` 时,应补柜内主路径或修正边界。
8. 碰撞状态优先看 `QetRouteCollisionStatus``NoCollision` 为理想结果,`ClearanceWarning` 可作为间隙问题记录,`HardIntersectionWarning` 视为穿模问题。
9. 多根线共路时,检查 `QetRouteLaneIndex`、`QetRouteLaneOffsetMm` 和 `QetRouteCapacityStatus`,确认新增导线不会无诊断地贴到旧线上。
10. 最后点击 `汇总布线诊断`,把 `RoutingDiagnosticSummary.QetDiagnosticMessage` 和主要 `issue_codes` 作为本次手测结论。
如果上面 1、4、5 成立,且问题导线都能通过诊断字段定位原因,说明当前版本已经具备第一版可测闭环。若仍存在导线穿模、柜外线或未布通,应优先把对应导线的 `wire_object_label`、`QetRouteIssueCodes`、`QetRouteDiagnosticsJson` 和录屏时间点一起反馈。
---
## 15. 常见问题
@ -794,18 +1028,25 @@ FreeCADExchange 会生成 3D -> 2D 的回写结果。
常见原因:
- 设备、线槽、导轨还没有装配到机柜内,左侧树中有对象但 3D 位姿还不是柜内真实位置。
- 没有导入线槽。
- 线槽没有标记为线槽。
- 没有从线槽实体生成中心路径。
- 没有用草图/Draft 线创建用户主路径。
- 没有生成 `WireDuct`、`RoutingRange`、`UserPath` 或 `TerminalAccess` carrier。
- 端子离线槽太远,缺少过渡路径。
处理:
1. 选择线槽,点击 `标记为线槽`
2. 打开 `3D布线连接`
3. 点击 `从线槽实体生成中心路径`
4. 点击 `扫描端子/网络`
5. 再尝试生成布线连接。
1. 先把导轨、线槽和设备摆到机柜内真实位置。
2. 选择线槽,点击 `标记为线槽`
3. 如果没有线槽,先画草图或 Draft 线,再点击 `选中路径作为用户路径`
4. 打开 `3D布线连接`
5. 点击 `检查布线准备度`,确认有布线源。
6. 点击 `准备布线布局空间`
7. 点击 `生成布线路径网络`
8. 点击 `检查布线路径网络`
9. 再尝试生成布线连接。
### 15.4 为什么保存后 QET 看不到 3D 位姿?

@ -348,6 +348,6 @@ allow_floating_fallback = false
为避免第一版范围漂移,下面三项采用明确默认值,后续阶段再扩展:
1. 第一版读取 `wire_style_id` 仅用于诊断和后续扩展,不强制映射 FreeCAD 导线颜色和线宽
1. 第一版读取 `wire_style_id` 后会按 `wire_properties` 解析导线显示样式:能解析时映射 FreeCAD 导线颜色、线宽和线型,并把 `Resolved/Missing` 状态写入导线对象和批量诊断;缺少数据库或查不到样式时使用默认显示样式,不阻止布线
2. 第一版只在 FreeCAD 中保存和显示自动布线长度,不导出正式长度报表。
3. 第一版不提供 `AutoSuggested` 转锁定确认导线的完整工作流;用户需要固定路径时,先使用已有手动布线能力重新创建正式手动导线。

@ -0,0 +1,848 @@
# 三期 3D 功能任务拆解与开发顺序
更新时间2026-06-08
## 1. 文档目标
本文档根据当前任务表拆解三期 3D 建模和三维布线相关功能,明确:
- 每个任务包含哪些具体功能。
- FreeCAD 原生是否已经具备对应能力。
- 当前项目是否已经完成。
- 第一版是否必须做。
- 后续应该如何开发,以及开发顺序。
本文档只按当前正式路线评估:
```text
QET / 明图CAD
-> 2d_to_3d.json
-> FreeCADExchange
-> scene.FCStd
-> 3d_to_2d.json
```
不再把旧 ThreeD 模块作为正式完成依据。
## 2. 状态定义
| 状态 | 含义 |
| --- | --- |
| 已完成第一版 | 当前代码已经能支撑最小可用流程,但仍可能需要优化体验和边界情况。 |
| 部分完成 | 已有基础代码或 FreeCAD 原生能力,但还没有达到任务表描述的完整交付标准。 |
| 未完成 | 当前正式 FreeCAD 路线中还没有形成可用能力。 |
| FreeCAD 原生可用 | FreeCAD 已经提供通用 CAD 能力,第一版可以直接使用,不需要重复开发。 |
## 3. 三期 3D 建模功能交付
### 3.1 3D 数据模型与映射规范开发
任务描述:
> 基于 QET 设备、符号、端子、项目数据库,建立设备-3D资产-场景实例-端子连接点-2D图元映射关系定义 STEP/IGES/FCStd 资产、sidecar 元数据、设备参数、安装规则、连接点语义。
| 功能 | 当前完成情况 | 是否第一版必须 | 开发说明 | 完成标准 |
| --- | --- | --- | --- | --- |
| 设备实例与 3D 实例映射 | 已完成第一版 | 必须 | 使用 `project_uuid + element_uuid + instance_id`。 | QET 设备打开到 FreeCAD 后能稳定生成或复用同一个 3D 设备实例。 |
| 2D 端子与 3D 工程端子映射 | 已完成第一版 | 必须 | 第一版唯一核心依据是 `terminal_uuid`。 | 2D 端子在 FreeCAD 中能找到对应工程端子。 |
| 设备与 3D 资产映射 | 已完成第一版 | 必须 | QET 侧解析设备绑定资产FreeCAD 侧读取 `resolved_model_path`。 | QET 绑定的 FCStd/STEP 资产能被 FreeCAD 正确导入。 |
| 场景实例保存 | 部分完成 | 必须 | 第一版不把 3D 位姿放数据库,以 `scene.FCStd` 为准。 | 设备移动后保存 `scene.FCStd`,重新打开位置不丢。 |
| STEP/IGES/FCStd 资产定义 | 部分完成 | 必须 | STEP/IGES 作为几何输入FCStd 作为正式电气 3D 资产。 | FCStd 能保存模型几何、模板端子、工程端子和布线对象。 |
| sidecar 元数据 | 未作为主链路 | 非必须 | 当前正式路线优先 FCStd LCS不建议第一版依赖 sidecar。 | 如后续启用sidecar 只能作为 FCStd 之外的兼容补充。 |
| 设备参数 | 部分完成 | 非第一版必须 | 属于参数化设备库能力。 | 能用参数生成不同规格设备模型。 |
| 安装规则 | 部分完成 | 后续必须 | 如安装到导轨、安装板、柜体。 | 设备知道自己安装在哪个宿主上,移动宿主时能跟随或校验。 |
| 连接点语义 | 已改为端子语义 | 必须 | 正式叫“端子”,分模板端子和工程端子。 | 工程端子能被选中接线,带端子 UUID、位置、方向和可接线属性。 |
### 3.2 FreeCAD 参数化设备建模能力开发
任务描述:
> 基于 FreeCAD Part/PartDesign 建立电气元件参数化建模模板支持断路器、继电器、端子排、导轨、线槽、柜体等常用结构生成支持模型复用、尺寸参数配置、STEP/IGES 导出。
| 功能 | 当前完成情况 | 是否第一版必须 | 开发说明 | 完成标准 |
| --- | --- | --- | --- | --- |
| FreeCAD 手工建模 | FreeCAD 原生可用 | 非必须二开 | 直接使用 FreeCAD Part/PartDesign。 | CAD 人员能手工建模。 |
| STEP/IGES 模型导入 | FreeCAD 原生可用,项目已接入第一版 | 必须 | 用于把厂家模型导入后保存为 FCStd。 | STEP/STP/IGES 能导入并继续添加模板端子。 |
| 给模型添加模板端子 | 已完成第一版 | 必须 | 当前端子和布线的基础。 | 用户能在模型上创建模板端子并保存为 FCStd。 |
| 断路器/继电器/端子排参数化生成 | 未完整完成 | 非当前必须 | 属于后续设备库效率能力。 | 输入极数、宽度、高度、端子数等参数后自动生成模型。 |
| 导轨/线槽/柜体参数化生成 | 部分完成 | 后续建议 | 当前已有部分基础资产和布线载体能力。 | 能按长度、宽度、高度生成导轨、线槽、柜体模型。 |
| 模型复用 | 已完成第一版 | 必须 | 复用 FCStd 模板。 | 同一个 FCStd 可在不同工程中作为设备资产使用。 |
| 尺寸参数配置 | 部分完成 | 后续建议 | 可以用 FreeCAD Spreadsheet/Expression 或 Python 生成器。 | 不改代码即可生成不同规格模型。 |
| STEP/IGES 导出 | FreeCAD 原生可用 | 非当前必须 | 主要用于对外交付几何。 | 能导出标准 STEP/IGES 文件。 |
### 3.3 3D 资产绑定与导入管理开发
任务描述:
> 在 QET 设备库中支持绑定 FreeCAD 生成模型或外部 STEP/IGES/STL 资产;提供资产路径解析、版本记录、缺失诊断、重新加载、模型元数据读取能力。
| 功能 | 当前完成情况 | 是否第一版必须 | 开发说明 | 完成标准 |
| --- | --- | --- | --- | --- |
| QET 设备绑定 3D 资产 | 已完成第一版,主要在 QET 侧 | 必须 | FreeCAD 侧消费导出的模型路径。 | QET 设备能绑定 FCStd/STEP 资产。 |
| 资产路径解析 | 已完成第一版 | 必须 | `2d_to_3d.json` 提供 `resolved_model_path`。 | FreeCAD 能找到本地真实模型文件。 |
| FCStd 导入 | 已完成第一版 | 必须 | 当前正式电气 3D 资产格式。 | 导入后能读取模型、模板端子。 |
| STEP/IGES/STL 导入 | 部分完成 | 必须 STEP/IGESSTL 非重点 | STEP/IGES 只作为几何输入STL 不适合作为电气语义资产。 | 文件能进入 FreeCAD必要时转存 FCStd。 |
| 版本记录 | 部分完成 | 后续建议 | 至少记录文件 hash、更新时间、模板版本。 | 模型变更后能提示需要重新加载或重新生成端子。 |
| 缺失诊断 | 部分完成 | 必须 | 找不到模型文件时给出明确提示。 | 用户能知道哪个设备缺少 3D 模型文件。 |
| 重新加载 | 部分完成 | 后续建议 | 模型文件变更后刷新场景实例。 | 重新绑定或替换资产后FreeCAD 能更新对应设备。 |
| 模型元数据读取 | 已完成第一版 | 必须 | 当前重点读取模板端子 LCS。 | 能读取端子槽位、端子类型、坐标、方向、局部出线路径。 |
### 3.4 复杂设备结构装配开发
任务描述:
> 构建机柜、安装板、DIN 导轨、线槽、设备实例的 3D 场景装配能力;支持设备拖放、吸附、对齐、旋转、偏移、安装宿主绑定和装配约束保存。
| 功能 | 当前完成情况 | 是否第一版必须 | 开发说明 | 完成标准 |
| --- | --- | --- | --- | --- |
| 机柜导入 | 部分完成 | 必须 | 机柜可作为场景背景和布线范围。 | QET 选择机柜后 FreeCAD 能打开对应场景。 |
| 安装板、导轨、线槽导入 | 部分完成 | 必须 | 导轨和线槽后续作为安装/布线参考。 | 能在场景中识别并显示这些载体。 |
| 设备实例装配 | 部分完成 | 必须 | 第一版允许手动摆放。 | 设备实例能放入机柜场景并保存位置。 |
| 拖放、旋转、偏移 | FreeCAD 原生可用,项目部分接入 | 必须 | 第一版可依赖 FreeCAD 原生变换。 | 用户能移动和旋转设备,保存后不丢。 |
| 吸附、对齐 | 部分完成 | 后续建议 | 需要自定义电气柜装配命令。 | 设备能自动贴合导轨、安装板或线槽边界。 |
| 安装宿主绑定 | 未完整完成 | 后续必须 | 如设备绑定到某根 DIN 导轨。 | 宿主移动时设备关系可追踪,校验时知道设备安装位置是否合法。 |
| 装配约束保存 | 部分完成 | 后续必须 | 当前主要保存 FreeCAD 位姿,不是完整约束系统。 | 重新打开后不只是位置在,还能知道设备为什么在这里。 |
### 3.5 3D 视图导航功能开发
任务描述:
> 实现 3D 视图缩放、平移、旋转、前/顶/左/右等轴测视角切换、选择高亮、实例聚焦、相机状态保存。
| 功能 | 当前完成情况 | 是否第一版必须 | 开发说明 | 完成标准 |
| --- | --- | --- | --- | --- |
| 缩放、平移、旋转 | FreeCAD 原生可用 | 必须但不需要二开 | 使用 FreeCAD 自带 3D 视图。 | 用户能正常查看场景。 |
| 轴测视角切换 | FreeCAD 原生可用 | 必须但不需要二开 | 使用 FreeCAD 视图命令。 | 用户能切换前/顶/左/右/等轴测视角。 |
| 选择高亮 | FreeCAD 原生可用 | 必须但不需要二开 | 端子和导线对象需要可选择。 | 选择对象时能明显看到目标。 |
| 实例聚焦 | 部分完成 | 后续建议 | 可做“定位到设备/端子/导线”命令。 | 从任务或树节点能快速定位目标。 |
| 相机状态保存 | 未完整完成 | 非第一版必须 | 属于使用体验增强。 | 重新打开工程后恢复上次视角。 |
### 3.6 2D 到 3D 单向联动开发
任务描述:
> 根据 QET 原理图/布置图中的设备、端子、柜体、导轨、线槽信息,单向生成或更新 3D 场景实例。
| 功能 | 当前完成情况 | 是否第一版必须 | 开发说明 | 完成标准 |
| --- | --- | --- | --- | --- |
| QET 导出设备 | 已完成第一版 | 必须 | 导出 `devices[]`。 | FreeCAD 能得到设备实例列表。 |
| QET 导出端子 | 已完成第一版但需稳定 | 必须 | 导出 `devices[].terminals[]`。 | FreeCAD 能知道端子属于哪个设备实例。 |
| QET 导出资产路径 | 已完成第一版 | 必须 | 导出 `device_models[]`。 | FreeCAD 能找到设备 3D 模型。 |
| FreeCAD 生成/更新设备 | 已完成第一版 | 必须 | 按 `instance_id` 复用或创建设备组。 | 多次打开不会重复生成混乱设备。 |
| FreeCAD 生成/更新工程端子 | 已完成第一版 | 必须 | 依赖 FCStd 模板端子。 | 工程端子落在设备正确端子位置。 |
| 柜体、导轨、线槽联动 | 部分完成 | 后续必须 | 当前不应阻塞端子和手动布线。 | QET 中柜体/载体信息能稳定进入 FreeCAD 场景。 |
### 3.7 3D 布线基础能力开发
任务描述:
> 基于 3D 连接点和端子映射,支持线路路径采集、手动布线路径编辑、路径叠加显示、线槽/导轨空间参考,为后续自动布线预留接口。
| 功能 | 当前完成情况 | 是否第一版必须 | 开发说明 | 完成标准 |
| --- | --- | --- | --- | --- |
| 工程端子可被选中接线 | 已完成第一版 | 必须 | 手动布线只连接工程端子。 | 不能误选模板端子或普通几何作为接线端。 |
| 线路路径采集 | 已完成第一版 | 必须 | 起点、终点、手动路径点。 | 能记录一条导线的完整路径点。 |
| 手动布线路径编辑 | 已完成第一版 | 必须 | 添加路径点、更新导线。 | 用户能调整导线走向。 |
| 路径叠加显示 | 已完成第一版 | 必须 | 在 FreeCAD 场景中生成可见导线。 | 导线几何可见,能区分起终点。 |
| 线槽/导轨空间参考 | 部分完成 | 后续必须 | 自动布线需要,手动布线先可弱化。 | 线槽/导轨能作为布线候选路径或参考对象。 |
| 自动布线接口 | 已完成第一版 | 后续必须 | 为 qdj 自动布线提供端子、导线任务、路径网络基础。 | 自动布线可以复用工程端子和路径载体。 |
## 4. 三期三维布线功能交付
### 4.1 布线数据模型设计
任务描述:
> 定义端子、设备、线缆的数据结构与接口规范。
| 功能 | 当前完成情况 | 是否第一版必须 | 开发说明 | 完成标准 |
| --- | --- | --- | --- | --- |
| 设备数据结构 | 已完成第一版 | 必须 | `QETDevice_xxx` 设备组保存实例信息。 | 可按 `instance_id` 找到设备。 |
| 工程端子数据结构 | 已完成第一版 | 必须 | LCS 对象保存端子语义。 | 可按 `terminal_uuid` 找到工程端子。 |
| 导线任务数据结构 | 已完成第一版 | 必须 | 从 QET `wires[]` 导入。 | 任务能描述起点端子、终点端子、线号等。 |
| 已布导线数据结构 | 已完成第一版 | 必须 | FreeCAD 对象保存起终点、路径点、线长、状态。 | 保存后重开不丢,能参与回写。 |
| 线缆/多芯线数据结构 | 未完整完成 | 后续建议 | 比单根导线更复杂。 | 支持线缆、芯线、屏蔽层等关系。 |
### 4.2 基础数据解析开发
任务描述:
> 开发 3D 场景中设备、端子数据的读取与解析模块。
| 功能 | 当前完成情况 | 是否第一版必须 | 开发说明 | 完成标准 |
| --- | --- | --- | --- | --- |
| 读取设备实例 | 已完成第一版 | 必须 | 从 FreeCAD 文档中扫描 QET 设备组。 | 能列出当前场景设备。 |
| 读取工程端子 | 已完成第一版 | 必须 | 扫描设备下的工程端子组。 | 能按设备和端子 UUID 建索引。 |
| 读取模板端子 | 已完成第一版 | 必须 | 从 FCStd 模板 LCS 读取槽位。 | 工程端子能参考模板端子位置生成。 |
| 读取导线任务 | 已完成第一版 | 必须 | 从 `2d_to_3d.json``wires[]` 导入。 | 能得到待布线起点和终点。 |
| 端子数据稳定匹配 | 部分完成 | 必须 | 依赖 QET 提供稳定 `terminal_uuid`,可增加 `slot_name_hint`。 | 同一设备多个端子不会错位或串线。 |
### 4.3 智能连接识别算法开发
任务描述:
> 实现端子与线缆、设备接口的自动匹配识别逻辑。
| 功能 | 当前完成情况 | 是否第一版必须 | 开发说明 | 完成标准 |
| --- | --- | --- | --- | --- |
| 按 `terminal_uuid` 找端子 | 已完成第一版 | 必须 | 最稳定的识别方式。 | 给定端子 UUID 能找到唯一工程端子。 |
| 按槽位提示匹配模板端子 | 已完成第一版 | 必须 | 使用 `slot_name_hint``terminal_label` 辅助。 | P1/P2/A1/A2 等端子能落到正确模板槽位。 |
| 手动选择两个端子识别连接 | 已完成第一版 | 必须 | 手动布线场景使用。 | 用户选两个工程端子即可生成导线。 |
| 自动识别线缆与设备接口 | 部分完成 | 后续建议 | 属于自动布线和智能匹配。 | 系统能从导线任务自动找到起终点端子。 |
| 复杂接线关系纠错 | 未完成 | 后续建议 | 需要电气规则和 QET 数据完整支持。 | 能提示端子不匹配、线缆类型不匹配等。 |
### 4.4 更新 BOM 并生成取线表
任务描述:
> 自动更新线长、线表和 BOM 数据,生成生产用取线表。
| 功能 | 当前完成情况 | 是否第一版必须 | 开发说明 | 完成标准 |
| --- | --- | --- | --- | --- |
| 线长计算 | 部分完成 | 建议第一版保留字段 | 可根据导线路径点计算长度。 | 每根导线有可回写的长度。 |
| 线表生成 | 未完整完成 | 后续必须 | 需要 QET 侧线号、线型、颜色、截面积等数据。 | 能导出起点、终点、线号、长度、规格。 |
| BOM 更新 | 未完成 | 后续必须 | 需要接入 QET BOM 或物料系统。 | 导线、端子附件、线槽等物料可进入 BOM。 |
| 取线表 | 未完成 | 后续必须 | 面向生产下线。 | 可按柜体、线号、长度批量输出。 |
### 4.5 电气布线规则梳理
任务描述:
> 明确布线的电气规范,如线距、转角、分层规则等。
| 功能 | 当前完成情况 | 是否第一版必须 | 开发说明 | 完成标准 |
| --- | --- | --- | --- | --- |
| 基础线距/避障规则 | 部分完成 | 必须基础版 | 自动布线已有部分间距参数。 | 导线不会明显穿过设备或柜体。 |
| 转角规则 | 部分完成 | 建议 | 当前可用正交路径表达。 | 路径转角符合甲方要求。 |
| 分层规则 | 未完整完成 | 后续必须 | 如强弱电、不同电压等级分层。 | 不同线缆类别按规则走不同区域。 |
| 线槽容量规则 | 部分完成 | 后续必须 | 当前已有容量/复用思路,但未完整产品化。 | 超容量时提示或改道。 |
| 甲方规范配置 | 未完成 | 必须依赖甲方 | 规则需要甲方确认。 | 规则可配置、可验收。 |
### 4.6 路径规划算法开发
任务描述:
> 基于规则实现自动生成无碰撞、合规的布线路径。
| 功能 | 当前完成情况 | 是否第一版必须 | 开发说明 | 完成标准 |
| --- | --- | --- | --- | --- |
| 端子到端子路径生成 | 已完成第一版 | 后续自动布线必须 | 自动布线使用工程端子作为起终点。 | 能从起点端子生成到终点端子的 3D 路径。 |
| 正交路径生成 | 已完成第一版 | 必须基础版 | 符合柜内布线常见表达。 | 导线由水平/垂直/深度方向线段组成。 |
| 线槽/路径网络布线 | 已完成第一版 | 后续必须 | 依赖 RoutingNetwork。 | 导线能优先走线槽或用户定义路径。 |
| 避障 | 部分完成 | 后续必须 | 已有包围盒避障和碰撞检查。 | 不穿设备、不穿柜体、不超出布线区域。 |
| 合规评分与择优 | 部分完成 | 后续必须 | 当前仍需优化。 | 多条候选路径中选择更符合规则的一条。 |
### 4.7 算法性能优化与测试
任务描述:
> 优化路径生成效率,处理复杂场景下的布线稳定性问题。
| 功能 | 当前完成情况 | 是否第一版必须 | 开发说明 | 完成标准 |
| --- | --- | --- | --- | --- |
| 单根导线性能 | 部分完成 | 必须 | 当前先保证单根或少量导线可用。 | 单根导线生成不卡顿。 |
| 批量导线性能 | 未完整完成 | 后续必须 | 大量导线需要缓存和分批处理。 | 大工程批量布线耗时可接受。 |
| 复杂场景稳定性 | 未完整完成 | 后续必须 | 需要机柜、设备、线槽组合测试。 | 多设备、多线槽、多导线时不崩溃、不乱连。 |
| 自动测试用例 | 未完整完成 | 后续必须 | 需要固定样例工程。 | 每次修改后能跑回归测试。 |
### 4.8 手动调整功能开发
任务描述:
> 实现布线路径的拖拽、修改、撤销/重做等交互功能。
| 功能 | 当前完成情况 | 是否第一版必须 | 开发说明 | 完成标准 |
| --- | --- | --- | --- | --- |
| 设置起点端子 | 已完成第一版 | 必须 | 从选择对象中识别工程端子。 | 起点不是工程端子时给出提示。 |
| 设置终点并生成 | 已完成第一版 | 必须 | 连接两个工程端子。 | 生成导线对象并保存起终点 UUID。 |
| 添加手动路径点 | 已完成第一版 | 必须 | 控制导线走向。 | 导线按用户路径点生成。 |
| 修改已布导线 | 部分完成 | 必须 | 对已有导线重新生成。 | 调整后不重复生成错误导线。 |
| 拖拽路径点 | 部分完成 | 后续建议 | 更偏 UI 体验。 | 通过鼠标拖拽调整导线路径。 |
| 撤销/重做 | 部分依赖 FreeCAD 原生 | 后续建议 | 可先依赖 FreeCAD 文档操作栈。 | 用户误操作可以恢复。 |
### 4.9 实时错误检查逻辑开发
任务描述:
> 开发布线冲突、连接错误、电气规范违规的实时检测。
| 功能 | 当前完成情况 | 是否第一版必须 | 开发说明 | 完成标准 |
| --- | --- | --- | --- | --- |
| 非端子选择检查 | 已完成第一版 | 必须 | 手动布线时必须限制对象类型。 | 选错对象时不给生成导线。 |
| 起终点相同检查 | 已完成第一版 | 必须 | 避免自连接。 | 同一端子不能生成导线。 |
| 缺失端子检查 | 部分完成 | 必须 | 自动布线和任务导入都需要。 | 找不到端子时给出明确原因。 |
| 碰撞检查 | 部分完成 | 后续必须 | 当前已有碰撞/间隙诊断雏形。 | 导线与设备冲突时能提示。 |
| 电气规则违规检查 | 未完整完成 | 后续必须 | 依赖完整规则库。 | 强弱电混走、线径不匹配等可检测。 |
| 真正实时检查 | 部分完成 | 后续建议 | 当前更接近命令执行后检查。 | 用户调整路径时实时刷新错误提示。 |
### 4.10 整体联调与验收
任务描述:
> 全流程功能联调,修复 bug 并完成验收测试。
| 功能 | 当前完成情况 | 是否第一版必须 | 开发说明 | 完成标准 |
| --- | --- | --- | --- | --- |
| QET 到 FreeCAD 打开链路 | 已完成第一版 | 必须 | QET 3D 视图打开 FreeCAD。 | 能打开对应工程和场景文件。 |
| 设备导入链路 | 已完成第一版 | 必须 | FreeCAD 根据 QET 数据导入设备。 | 设备模型能正常显示。 |
| 端子生成链路 | 已完成第一版但需继续稳定 | 必须 | 模板端子生成工程端子。 | 工程端子位置准确,不错位。 |
| 手动布线链路 | 已完成第一版 | 必须 | 两个工程端子生成导线。 | 保存重开后导线不丢。 |
| 自动布线链路 | 部分完成 | 后续必须 | qdj 负责继续完善。 | 批量线缆能按规则生成路径。 |
| BOM/取线表链路 | 未完成 | 后续必须 | 需要 QET 侧数据和导线长度回写。 | 可生成生产可用线表。 |
| 验收测试 | 未完成 | 必须 | 需要固定工程和操作用例。 | 每项任务有可演示、可复现的验收步骤。 |
## 5. 当前 zwl 应优先负责的范围
当前 zwl 的重点不是整张任务表,而是下面这条 FreeCAD 端子和手动布线链路:
```text
FCStd 模板端子
-> 工程端子生成
-> 工程端子显示/选择
-> 手动连接两个工程端子
-> 生成 3D 导线
-> 保存到 scene.FCStd
-> 生成 3d_to_2d.json 回写
```
### 5.1 zwl 当前必须做
| 功能 | 说明 | 完成标准 |
| --- | --- | --- |
| 模板端子规范 | 定义设备 FCStd 内哪里可以接线。 | STEP/FCStd 模型能保存模板端子、槽位名和出线方向。 |
| 模型元数据读取 | FreeCAD 读取 FCStd 内模板端子。 | 导入设备后能读取模板端子位置和方向。 |
| 工程端子生成 | 从模板端子生成项目内可接线端子。 | 工程端子落在设备正确位置。 |
| 端子全局坐标计算 | 设备移动/旋转后仍能得到正确端子位置。 | 导线起终点跟随设备变化。 |
| 手动布线 | 选两个工程端子生成导线。 | 导线几何正确,起终点 UUID 正确。 |
| 手动路径点 | 支持用户控制导线路径。 | 路径点保存后重开不丢。 |
| 导线保存 | 导线对象保存到 `scene.FCStd`。 | 关闭重开工程,导线仍存在。 |
| 3D 回写 | 生成 `3d_to_2d.json`。 | 回写设备实例、端子绑定、导线基础数据。 |
| 基础错误检查 | 检查选错对象、缺失端子、起终点相同。 | 错误操作有明确提示,不生成错误导线。 |
### 5.2 zwl 当前不应作为主线做
| 功能 | 原因 |
| --- | --- |
| 完整参数化设备库 | 当前可先用已有 STEP/FCStd 资产加端子。 |
| 断路器/继电器/端子排通用参数模板 | 属于提高资产制作效率的后续能力。 |
| QET 设备库资产绑定界面 | 主要在 QET 侧。 |
| 完整复杂装配约束 | 当前只要求端子和导线稳定。 |
| 完整自定义 3D 视图导航 | FreeCAD 原生能力够第一版使用。 |
| 自动布线核心算法 | qdj 负责,依赖 zwl 的工程端子和手动布线基础。 |
| BOM/取线表 | 后续基于导线长度和端子回写再做。 |
| 完整电气规则库 | 需要甲方规则和整体设计。 |
## 6. 推荐开发顺序
### 阶段 1端子资产基础
目标:让一个普通 STEP 模型变成可接线的 FCStd 电气资产。
开发顺序:
1. 完善模板端子创建和校验。
2. 明确模板端子属性:槽位名、端子类型、是否可接线、出线方向。
3. 支持把模板端子的局部出线路径保存进 FCStd。
4. 保存为可复用 FCStd 设备模板。
验收:
- 打开一个 STEP 电流互感器模型。
- 添加 P1/P2 等模板端子。
- 保存为 FCStd。
- 重新打开后模板端子仍然存在,属性不丢。
### 阶段 2工程端子生成
目标QET 打开 FreeCAD 后,设备实例上有可用于接线的工程端子。
开发顺序:
1. 导入 QET 设备实例。
2. 读取设备 FCStd 内模板端子。
3. 根据 QET `terminal_uuid` 生成工程端子。
4. 如果 QET 没有端子 UUID支持生成 `local:*` 本地工程端子用于 3D 验证。
5. 设备移动/旋转后,工程端子全局坐标正确。
验收:
- 一个设备实例能生成工程端子。
- 两个同型号设备实例的工程端子分别落在各自设备上。
- 移动设备后,端子位置跟随变化。
### 阶段 3手动布线
目标:用户能连接两个工程端子。
开发顺序:
1. 选择工程端子作为起点。
2. 选择工程端子作为终点。
3. 根据端子全局坐标生成导线。
4. 支持添加手动路径点。
5. 保存导线属性:起点端子 UUID、终点端子 UUID、路径点、线长、状态。
6. 提供基础错误提示。
验收:
- 选两个端子能生成一根导线。
- 不能连接模板端子或普通几何。
- 起终点不能相同。
- 导线显示正确,不乱连到已有导线。
### 阶段 4保存和回写
目标3D 布线结果可以保存和交还 QET。
开发顺序:
1. 导线、端子、设备保存在 `scene.FCStd`
2. 重新打开工程时恢复端子和导线。
3. 生成 `3d_to_2d.json`
4. 回写设备实例、端子绑定、导线基础信息。
5. 跳过本地端子或无法可靠回写的端子,并给出说明。
验收:
- 保存关闭再打开,导线不丢。
- `3d_to_2d.json` 中能看到实例、端子和导线信息。
- QET 能消费回写结果或至少不报错。
### 阶段 5基础诊断和联调
目标:让 CAD 人员使用时不会因为错误操作把工程弄乱。
开发顺序:
1. 检查缺失模型。
2. 检查设备没有模板端子。
3. 检查端子 UUID 缺失。
4. 检查工程端子错位。
5. 检查导线起终点无效。
6. 输出清晰提示。
验收:
- 出错时能告诉用户是哪台设备、哪个端子、什么原因。
- 不产生半成品错误对象。
### 阶段 6装配和布线增强
目标:提升机柜内真实装配和走线质量。
开发顺序:
1. 设备吸附到导轨/安装板。
2. 保存安装宿主关系。
3. 线槽/导轨作为布线载体。
4. 导线优先沿线槽或用户路径走线。
5. 增加碰撞和越界检查。
验收:
- 设备能稳定放在导轨或安装板上。
- 导线能沿线槽走,不明显穿设备。
### 阶段 7自动布线、BOM 和取线表
目标:在手动布线稳定后,扩展自动化生产能力。
开发顺序:
1. qdj 基于工程端子和布线载体开发自动布线。
2. 引入甲方电气布线规则。
3. 批量生成导线。
4. 计算线长。
5. 回写线表、BOM、取线表。
6. 做大工程性能优化和验收测试。
验收:
- 一批导线能自动生成。
- 有冲突能提示。
- 能输出生产可用取线表。
## 7. 第一版最小交付清单
第一版不要求完成整张任务表,只要先完成下面这些:
1. FCStd 模板端子制作。
2. FCStd 模板端子读取。
3. 工程端子生成。
4. 工程端子选择。
5. 两个工程端子生成手动导线。
6. 手动路径点保存。
7. 导线保存到 `scene.FCStd`
8. 重新打开不丢端子和导线。
9. 生成 `3d_to_2d.json`
10. 基础错误提示。
完成这 10 项,就可以对外说:
> FreeCAD 端子显示、端子手动连线、保存回写第一版完成。
## 8. 甲方资料补充分析
资料来源:
- `D:\video\甲方视频\3D布线功能需求开发文档.docx`
- `D:\video\甲方视频\3D布线KYN28-12操作教程(终版).mp4`
- `D:\video\甲方视频\甲方布线操作.mp4`
- `D:\video\甲方视频\保护装置模型视频.mp4`
- `D:\video\甲方视频\面板装配、路径绘制、导轨配合038F3440.MP4`
- `D:\downloadWX\xwechat_files\wxid_pv577xuccot722_5d4a\msg\file\2026-06\20260601_10365403921E50.MP4`
甲方当前参考软件是 SOLIDWORKS Electrical 3D。设计时也应参考 EPLAN Pro Panel 的电气柜 3D 安装布局和布线思想。
### 8.1 甲方需求归纳
Word 需求文档中核心需求可以归纳为 6 类:
| 序号 | 甲方需求 | 对应我们系统的含义 | 当前优先级 |
| --- | --- | --- | --- |
| 1 | 三维模型读取与关系配合 | 导入柜体、安装板、面板、设备,并能做装配配合。 | 高 |
| 2 | 配合面定义与快速装配 | 设备能快速安装到导轨、安装板、面板等基准上,支持端子排、小型断路器批量插入。 | 中高 |
| 3 | 三维零件建模与电气脚点定义 | STEP/FCStd 设备需要补电气端子,端子带脚号、方向、可接线属性。 | 高 |
| 4 | 草图路径自定义与布线规则 | 用户可在 3D 空间画线段/曲线作为布线路径,导线可沿这些路径或区域走线。 | 高 |
| 5 | 取线表数据生成与导出 | 从布线结果生成线长、线缆规格、颜色、线耳、源/目标设备脚号等生产数据。 | 后续高 |
| 6 | 错误自检与线束高亮提示 | 检查缺失、未连接、路径冲突,选中线束时高亮并提示源/目标信息。 | 中高 |
### 8.2 从视频看到的实际操作意图
| 视频 | 观察到的重点 | 对我们开发的启发 |
| --- | --- | --- |
| `面板装配、路径绘制、导轨配合038F3440.MP4` | 安装板/面板是柜体结构的一部分,通过面、边、孔或基准与柜体配合;导轨、端子排、设备再装到面板上。 | FreeCAD 侧需要把安装板/面板作为“结构载体对象”,支持配合面、安装面、孔位参考和路径对象。 |
| `甲方布线操作.mp4` | 先插入设备、打开柜体或装配体,再做设备端子、路径绘制、布线效果展示。 | 我们应先确保设备、工程端子、路径对象、导线对象这四类对象的语义稳定。 |
| `3D布线KYN28-12操作教程(终版).mp4` | 有保存在线缆工程目录、装配完成状态、草图路径、端子排线槽配合、最终布线效果。 | 路径草图和线槽/导轨配合不是装饰几何,应成为布线网络或布线参考。 |
| `保护装置模型视频.mp4` | 保护装置模型带前面板、后部结构和端子区域。 | 设备模板端子不能只按包围盒猜点,必须由模板端子或连接点模式明确指定。 |
| `20260601_10365403921E50.MP4` | 显示机柜内端子排、导轨、线槽、局部装配和设备位置参考。 | 蓝色 CAD 参考线或草图线应区别于最终布线导线,作为安装/路径参考。 |
### 8.3 SW/EPLAN 对标结论
SOLIDWORKS Electrical 3D 的核心思想:
- 2D 电气原理图和 3D 柜体布局联动。
- 3D 中管理设备布局,并布置 wires、cables、harnesses。
- 通过 routing paths / ducts 引导线缆路径。
- routing 完成后更新线长,并可生成包含长度的报表。
- routing cables 时需要 origin / destinationrouting wires 时起终点来自电气图。
EPLAN Pro Panel 的核心思想:
- 在布局空间中放置柜体、安装板、安装导轨、电缆槽和设备。
- 机械元件可以有安装面,设备放在安装面或导轨上。
- 3D 部件放置上的连接点可以图形化显示,并显示连接点方向。
- 布线需要布线路径网络、布线范围、连接点方向和待布线连接。
- 已布线连接会计算长度,可用于生产和报表。
对我们项目的结论:
```text
安装板/面板/导轨/线槽
不只是普通模型
-> 应该成为 3D 装配和布线的结构载体
设备端子
不只是几何点
-> 应该成为带 terminal_uuid、槽位名、方向、脚号的工程端子
蓝色/彩色草图路径
不应该当作最终导线
-> 应该作为布线路径网络、布线范围或安装参考
最终导线
必须连接两个工程端子
-> 保存起点、终点、路径、线长、规格、状态
```
### 8.4 对当前 FreeCAD 开发的影响
| 功能 | 是否 FreeCAD 原生已有 | 我们要做什么 |
| --- | --- | --- |
| 导入柜体、安装板、导轨、线槽 | FreeCAD 原生能导入几何 | 给这些对象补业务类型,如 Cabinet、MountingPlate、DinRail、WireDuct、RoutingPath。 |
| 装配配合 | FreeCAD 有基础移动/约束能力,但不是电气柜业务规则 | 做轻量配合语义:安装面、宿主、局部坐标、吸附结果保存。 |
| 设备放置 | FreeCAD 能移动设备 | 增加“设备属于哪个安装面/导轨”的语义。 |
| 设备端子 | FreeCAD 没有电气端子语义 | 用 FCStd LCS 模板端子和工程端子实现。 |
| 连接点方向 | FreeCAD 有坐标系方向 | 规定 LCS 本地 +Z 为出线方向,并在布线时使用。 |
| 草图路径 | FreeCAD 能画草图/线段 | 把草图/边/线转换为 RoutingPath供布线算法使用。 |
| 布线范围 | FreeCAD 没有电气布线范围语义 | 需要新增 RoutingArea 或 CabinetRoutingZone 概念。 |
| 线长/取线表 | FreeCAD 没有 QET 生产数据 | FreeCAD 计算路径长度QET 或后处理生成生产取线表。 |
| 错误自检 | FreeCAD 没有电气规则检查 | 我们需要检查缺失端子、路径断开、碰撞、越界、线槽容量等。 |
### 8.5 需要 QET 侧配合的内容
以下内容如果涉及改 QET 代码,需要找 QET 对应开发者配合:
| QET 侧能力 | 为什么需要 | FreeCAD 侧依赖 |
| --- | --- | --- |
| 稳定导出设备 `element_uuid``instance_id` | FreeCAD 用它找到设备实例。 | 设备导入和回写。 |
| 稳定导出端子 `terminal_uuid` | FreeCAD 工程端子绑定 2D 端子的唯一依据。 | 工程端子生成、导线起终点绑定。 |
| 导出端子显示名或槽位提示 | 防止同一设备多个端子顺序错位。 | `slot_name_hint` / `terminal_label` 匹配模板端子。 |
| 导出导线任务 `wires[]` | 自动布线和待布线列表需要。 | 起点端子、终点端子、线号、线型。 |
| 导出线缆规格、颜色、截面积 | 取线表和导线显示需要。 | 线长、线色、线径、线耳。 |
| QET 设备库绑定 FCStd 资产 | FreeCAD 需要知道每个设备用哪个 3D 模型。 | 资产导入。 |
| QET 消费 `3d_to_2d.json` | FreeCAD 回写结果要进入 QET。 | 实例绑定、端子绑定、线长/布线结果。 |
| 取线表格式定义 | 最终要给终端取线机读取。 | FreeCAD 只提供线长和路径基础数据。 |
### 8.6 新增建议开发顺序
结合甲方资料,推荐把后续顺序调整为:
1. 端子模板和工程端子稳定。
2. 手动布线和保存回写稳定。
3. 草图/边/线转换为 `RoutingPath`
4. 安装板/面板/导轨/线槽标记为结构载体。
5. 设备和结构载体建立轻量配合关系。
6. 自动布线使用 `RoutingPath`、线槽、布线范围。
7. 错误自检和高亮。
8. 线长、线缆属性、取线表。
其中第 1、2 项是 zwl 当前主线;第 6 项已经在另一个会话中推进;第 8 项需要 QET 和生产数据格式共同确定。
### 8.7 第一版建议不要扩大到的范围
为了避免 FreeCAD 侧失控,第一版暂不建议做:
- 完整替代 SW 的机械装配 Mate 系统。
- 完整参数化设备库。
- 完整自动端子排生成和编号规则。
- 完整取线机格式导出。
- 完整 EPLAN 级规则库。
第一版只要把下面链路跑通:
```text
FCStd 电气资产
-> 模板端子
-> 工程端子
-> 结构载体/布线路径参考
-> 手动或自动布线
-> 线长和错误诊断
-> scene.FCStd + 3d_to_2d.json
```
### 8.8 甲方取线表样例分析
取线表样例:
- `D:\downloadWX\xwechat_files\wxid_pv577xuccot722_5d4a\msg\file\2026-04\PT2柜取线表.xlsx`
工作簿结构:
| 工作表 | 行列情况 | 说明 |
| --- | --- | --- |
| `线束组件编码` | 1 行 1 列 | 当前样例中只有标题。 |
| `取线表` | 528 行 52 列 | 真实取线表数据。 |
主要字段可以分成 5 类:
| 字段类别 | 代表字段 | 来源判断 | 说明 |
| --- | --- | --- | --- |
| 连接两端信息 | `号码管字符1`、`号码管方向1`、`线鼻子型号1`、`剥皮长度1`、`号码管字符2`、`号码管方向2`、`线鼻子型号2`、`剥皮长度2` | QET 为主FreeCAD 可补位置/区域 | 这些是电气连接和生产加工字段,不能只靠 3D 几何推断。 |
| 导线规格信息 | `导线型号`、`导线颜色`、`导线截面积mm2` | QET 为主 | 来自 2D 图纸、导线样式、线缆规则或物料数据。 |
| 线长信息 | `导线长度(mm)`、`加工总数` | FreeCAD 计算QET 汇总 | FreeCAD 按 3D 路径计算实际长度QET 或后处理计算生产数量和汇总。 |
| 生产数量信息 | `生产总数`、`单批数量`、`套数`、`加工总数` | QET/生产系统为主 | 依赖项目数量、批次、套数,不应由 FreeCAD 单独决定。 |
| 区域和线束编号 | `区域`、`始端区域`、`末端区域`、`分线束编号`、`总线束编号`、`小区域` | QET + FreeCAD | QET 知道柜体/图纸区域FreeCAD 可根据 3D 设备所在柜内区域辅助计算。 |
从样例看,取线表最终需要的不只是“线长”,而是一条完整生产记录:
```text
起点设备/端子/号码管/线鼻子
+ 终点设备/端子/号码管/线鼻子
+ 导线型号/颜色/截面积
+ 3D 路径计算线长
+ 所属区域/线束编号/生产数量
```
因此推荐职责边界:
| 职责 | FreeCAD 侧 | QET 侧 |
| --- | --- | --- |
| 端子空间位置 | 负责 | 消费结果或仅保存绑定 |
| 实际 3D 走线路径 | 负责 | 可读取回写 |
| 线长计算 | 负责 | 汇总、修正、导出 |
| 线号/号码管字符 | 不负责生成,最多回传关联端子 | 负责 |
| 导线型号/颜色/截面积 | 不负责主数据 | 负责 |
| 线鼻子/剥皮长度 | 不负责主数据 | 负责 |
| 区域/柜体/小区域 | 可根据 3D 空间辅助判断 | 负责主数据和最终分类 |
| 取线表格式导出 | 可提供基础 JSON | 负责最终 Excel/取线机格式 |
### 8.9 当前 JSON 交换样例分析
当前工程交换目录示例:
```text
D:\test\MT\电气工程4.0\电气工程414\电气工程414\.qet_freecad
2d_to_3d.json
3d_to_2d.json
```
当前 `2d_to_3d.json` 统计:
| 内容 | 数量 | 说明 |
| --- | --- | --- |
| `devices[]` | 86 | QET 导出的 2D 设备实例和端子上下文。 |
| `device_models[]` | 85 | QET 解析出的 3D 资产路径。 |
| `wires[]` | 75 | QET 导出的导线任务。 |
| `stale_devices[]` | 27 | 已失效或不在当前快照中的设备。 |
| `cabinet` | 1 | 当前图纸绑定的机柜上下文。 |
当前 `wires[]` 已有字段示例:
```json
{
"start_element_uuid": "...",
"start_terminal_uuid": "...",
"start_terminal_display": "12",
"end_element_uuid": "...",
"end_terminal_uuid": "...",
"end_terminal_display": "21",
"wire_id": "direction:...",
"wire_mark": "N4131",
"wire_mark_is_manual": false,
"wire_style_id": 3
}
```
这些字段已经足够支持“从 QET 导线任务找到 FreeCAD 工程端子并生成导线”。
但对取线表还不够,缺少:
| 取线表需要 | 当前 JSON 是否已有 | 建议责任 |
| --- | --- | --- |
| 导线型号 | 未直接看到,可能需通过 `wire_style_id` 回查 | QET 补充或回查 |
| 导线颜色 | 未直接看到,可能需通过 `wire_style_id` 回查 | QET 补充或回查 |
| 导线截面积 | 未直接看到,可能需通过 `wire_style_id` 回查 | QET 补充或回查 |
| 线鼻子型号 | 未看到 | QET 提供 |
| 剥皮长度 | 未看到 | QET 提供 |
| 号码管字符 | 部分可由 `wire_mark`、端子显示、设备信息组合 | QET 负责最终生成 |
| 3D 实际线长 | 当前 `2d_to_3d.json` 不应有,应该 FreeCAD 回写 | FreeCAD 计算 |
| 分线束编号/总线束编号 | 未看到 | QET/生产规则提供 |
| 起终点区域 | 当前有柜体上下文,但不完整 | QET 主导FreeCAD 可辅助空间判断 |
当前 `3d_to_2d.json` 统计:
| 内容 | 数量 | 说明 |
| --- | --- | --- |
| `instances[]` | 85 | FreeCAD 回写设备实例绑定。 |
| `terminals[]` | 138 | FreeCAD 回写端子绑定。 |
当前 `3d_to_2d.json` 尚未包含:
- 已布导线列表。
- 每根导线的 3D 路径点。
- 每根导线的实际长度。
- 碰撞/错误状态。
- 线缆/导线规格回传字段。
因此如果目标是最终生成甲方取线表,需要新增 FreeCAD 回写结构,建议命名为:
```json
{
"routed_wires": [
{
"wire_id": "string",
"wire_mark": "string",
"start_terminal_uuid": "string",
"end_terminal_uuid": "string",
"start_instance_id": "string",
"end_instance_id": "string",
"length_mm": 1234.5,
"route_status": "Routed",
"route_mode": "Manual",
"route_points": [],
"collision_count": 0,
"diagnostics": []
}
]
}
```
注意:`routed_wires[]` 只负责把 3D 布线结果回写给 QET。最终取线表中的导线型号、颜色、截面积、线鼻子、剥皮长度、号码管字符、区域、生产数量仍建议由 QET 根据主数据和生产规则生成。
### 8.10 后续需要 QET 开发者确认的问题
如果要从当前 3D 布线走到甲方取线表,需要 QET 侧确认:
1. `wire_style_id` 能否稳定回查导线型号、颜色、截面积。
2. 线鼻子型号、剥皮长度、导线半脱长度来自哪里。
3. 号码管字符当前生成规则是否已经在 QET 中存在。
4. 始端区域、末端区域、小区域、分线束编号、总线束编号的规则由谁生成。
5. QET 是否准备消费 FreeCAD 回写的 `routed_wires[]`
6. QET 最终是否负责导出甲方 Excel 取线表,还是 FreeCAD 直接导出。
推荐边界:
```text
FreeCAD
负责 3D 设备、端子、路径、线长、碰撞状态。
QET
负责 2D 电气连接、线号、线型、颜色、截面积、线鼻子、剥皮长度、区域、取线表格式。
```
## 9. 任务描述合理性修订标注
本章不覆盖原始任务表而是标注其中不够合理或容易误导开发范围的描述方便后续和项目负责人、QET 开发者、自动布线开发者对齐。
标注规则:
| 标记 | 含义 |
| --- | --- |
| 【建议修改】 | 原描述容易扩大范围或职责不清,建议修改。 |
| 【建议拆分】 | 一个任务里混入多类能力,建议拆成多个阶段。 |
| 【建议降级】 | 第一版不应承诺完整能力,只做基础版或依赖 FreeCAD 原生能力。 |
| 【职责需拆分】 | 该能力不能只由 FreeCAD 完成,需要 QET 或生产数据配合。 |
### 9.1 三期 3D 建模功能任务描述
| 原任务 | 合理性判断 | 建议改法 |
| --- | --- | --- |
| `3D 数据模型与映射规范开发`:基于 QET 设备、符号、端子、项目数据库,建立设备-3D资产-场景实例-端子连接点-2D图元映射关系定义 STEP/IGES/FCStd 资产、sidecar 元数据、设备参数、安装规则、连接点语义。 | 【建议拆分】这句话把第一版最小映射、资产格式、sidecar、参数、安装规则、连接点语义都混在一起范围过大。并且当前正式路线不建议用 `connectionPoint` 作为核心术语,也不建议第一版依赖 sidecar。 | 改为:`建立 QET 设备/端子与 FreeCAD 设备实例/工程端子的最小映射;定义 FCStd 电气资产、模板端子、工程端子和 3D 回写协议。STEP/IGES 仅作为几何输入sidecar、设备参数、安装规则作为后续扩展。` |
| `FreeCAD 参数化设备建模能力开发`:支持断路器、继电器、端子排、导轨、线槽、柜体等常用结构生成。 | 【建议降级】这相当于做一个完整参数化设备库,工作量很大,不应和端子/布线第一版绑死。 | 改为:`第一版支持 STEP/FCStd 资产加模板端子和少量基础载体模板;断路器、继电器、端子排、导轨、线槽、柜体的完整参数化生成作为设备库后续任务。` |
| `3D 资产绑定与导入管理开发`:支持绑定 FreeCAD 生成模型或外部 STEP/IGES/STL 资产。 | 【建议修改】STL 只有网格几何,不适合作为电气语义资产;正式电气资产应优先 FCStd。 | 改为:`优先支持 FCStd 电气资产STEP/IGES 作为几何输入STL/OBJ 只作为显示类或临时参考资产,不作为正式可接线设备资产。` |
| `3D 资产绑定与导入管理开发`:提供版本记录、缺失诊断、重新加载、模型元数据读取能力。 | 【职责需拆分】版本记录和资产绑定主要在 QET/设备库侧FreeCAD 侧负责读取模型语义和诊断导入状态。 | 改为:`QET 负责资产绑定、版本记录、路径解析FreeCAD 负责导入诊断、FCStd 模板端子读取、模型变更后的场景更新提示。` |
| `复杂设备结构装配开发`:支持设备拖放、吸附、对齐、旋转、偏移、安装宿主绑定和装配约束保存。 | 【建议修改】“装配约束”容易被理解为 SW Mate 级机械约束,第一版不现实。 | 改为:`第一版依赖 FreeCAD 原生移动/旋转,补充轻量电气装配语义:安装面、安装宿主、吸附结果、局部坐标。完整机械 Mate 系统不作为第一版目标。` |
| `3D 视图导航功能开发`:实现缩放、平移、旋转、视角切换、选择高亮。 | 【建议降级】这些是 FreeCAD 原生能力,不应作为大量二开任务。 | 改为:`复用 FreeCAD 原生视图能力;二开只做对象定位、端子/导线高亮、设备预览、错误对象聚焦。` |
| `2D 到 3D 单向联动开发`:根据原理图/布置图中的设备、端子、柜体、导轨、线槽信息,单向生成或更新 3D 场景实例。 | 【职责需拆分】设备、端子、导线任务可以由 QET 当前数据提供;导轨、线槽、安装板等结构载体未必来自 2D 原理图,需要明确数据源。 | 改为:`QET 第一版导出设备、端子、导线任务、3D 资产路径和柜体上下文FreeCAD 生成或更新设备、工程端子和导线任务。导轨、线槽、安装板可先由 FreeCAD 场景或机柜 FCStd 提供。` |
| `3D 布线基础能力开发`:基于 3D 连接点和端子映射。 | 【建议修改】当前正式术语应统一为“端子”,不要继续把 `连接点 connectionPoint` 作为核心。 | 改为:`基于 3D 工程端子和 2D terminal_uuid 映射,支持手动布线、路径点编辑、路径显示和自动布线接口。` |
### 9.2 三维布线功能任务描述
| 原任务 | 合理性判断 | 建议改法 |
| --- | --- | --- |
| `三期-3 维布线功能交付`:完成 3D 自动布线全链路功能开发,实现智能连接、规则化布线、手动调整与实时错误校验。 | 【建议拆分】自动布线全链路依赖端子、装配、布线路径、QET 导线任务、电气规则和取线表,不能作为一个单点任务承诺。 | 改为:`先完成工程端子、手动布线、路径保存和线长回写;再基于稳定端子和布线路径网络开发自动布线、规则检查和生产数据输出。` |
| `布线数据模型设计`:定义端子、设备、线缆的数据结构与接口规范。 | 基本合理,但应明确 FreeCAD/QET 边界。 | 改为:`QET 定义电气连接、线号、线型、规格FreeCAD 定义工程端子、布线路径、线长、碰撞状态和回写结构。` |
| `基础数据解析开发`:开发 3D 场景中设备、端子数据的读取与解析模块。 | 合理,但“端子数据没取对”这类问题通常不只在 FreeCAD可能是 QET 导出的端子和 FCStd 模板槽位不匹配。 | 增加说明:`解析模块需要同时校验 QET terminal_uuid、terminal_label/slot_name_hint 与 FCStd 模板端子槽位。` |
| `智能连接识别算法开发`:实现端子与线缆、设备接口的自动匹配识别逻辑。 | 【建议降级】第一版不应做复杂智能推断,必须先以 `terminal_uuid` 精确绑定为主。 | 改为:`第一版按 terminal_uuid 精确匹配slot_name_hint/terminal_label 只作模板槽位匹配提示;复杂智能识别作为后续。` |
| `更新BOM并生成取线表`:自动更新线长、线表和 BOM 数据,生成生产用取线表。 | 【职责需拆分】FreeCAD 只能可靠生成 3D 路径和线长;取线表中的线鼻子、剥皮长度、导线型号、颜色、区域、生产数量主要来自 QET/生产规则。 | 改为:`FreeCAD 回写 routed_wires[]包含线长、路径、起终点端子和状态QET 根据主数据生成 BOM、线表和取线表。` |
| `电气布线规则梳理`:明确布线的电气规范,如线距、转角、分层规则等,甲方提供。 | 合理,但它是算法开发的输入,不应等到后期才补。 | 调整为前置任务:`在自动布线前由甲方确认最小规则集:线距、转角、线槽优先级、强弱电分层、线槽容量、禁止区域。` |
| `路径规划算法开发`:基于规则实现自动生成无碰撞、合规的布线路径。 | 【建议修改】“无碰撞、合规”是理想目标,不应绝对承诺。实际应生成候选路径并给出诊断。 | 改为:`基于布线路径网络生成优选路径;尽量避障并标记碰撞/间隙/越界诊断,不能保证所有复杂场景自动无碰撞。` |
| `手动调整功能开发`:实现布线路径的拖拽、修改、撤销/重做等交互功能。 | 【建议降级】完整拖拽路径点和撤销重做 UI 工作量大,第一版可以先做路径点添加/删除/重新生成,并复用 FreeCAD 原生撤销。 | 改为:`第一版支持设置起点、终点、添加路径点、删除路径点、重新生成导线;拖拽路径点作为后续体验增强。` |
| `实时错误检查逻辑开发`:开发布线冲突、连接错误、电气规范违规的实时检测。 | 【建议降级】真正实时检测成本高,第一版可以做命令执行前后检查和批量诊断。 | 改为:`第一版做布线前置检查、生成后诊断和错误高亮;实时跟随鼠标/拖拽刷新作为后续。` |
| `整体联调与验收`:全流程功能联调,修复 bug 并完成验收测试。 | 合理,但需要明确验收样例。 | 增加说明:`验收必须基于甲方 KYN28/PT2 柜样例、真实 2d_to_3d.json、真实 FCStd 设备资产和取线表样例。` |
### 9.3 建议调整后的第一版任务口径
如果要让任务表更符合当前项目实际,建议第一版总目标改成:
```text
完成 QET 与 FreeCAD 的 3D 电气设计最小闭环:
1. QET 提供设备、端子、导线任务和 3D 资产路径。
2. FreeCAD 导入 FCStd 电气资产和柜体场景。
3. FreeCAD 从模板端子生成工程端子。
4. 用户可手动连接两个工程端子并编辑路径。
5. FreeCAD 保存 scene.FCStd并回写设备实例、端子绑定、已布导线、路径长度和诊断状态。
6. QET 根据回写结果和电气主数据生成取线表。
```
第一版不应承诺:
- 完整 SW Mate 级装配系统。
- 完整参数化设备库。
- 完整自动端子排生成。
- 完整 EPLAN 级自动布线规则库。
- FreeCAD 单独生成最终生产取线表。
这些可以作为第二阶段或第三阶段能力继续扩展。

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,6 +1,7 @@
import json
import traceback
import os
import sqlite3
from pathlib import Path
import FreeCAD as App
@ -405,6 +406,15 @@ def _optional_string(item, field_name, entry_label):
return value.strip() if isinstance(value, str) else ""
def _optional_text(item, field_name):
value = item.get(field_name, "")
if value is None:
return ""
if isinstance(value, str):
return value.strip()
return str(value).strip()
def _normalize_conductor_uuids(item, entry_label):
values = item.get("conductor_uuids", [])
if values is None:
@ -455,8 +465,10 @@ def _normalize_wires(payload):
"wire_id": wire_id,
"net_uuid": _optional_string(item, "net_uuid", entry_label),
"group_uuid": _optional_string(item, "group_uuid", entry_label),
"wire_label": _optional_string(item, "wire_label", entry_label),
"wire_mark": _optional_string(item, "wire_mark", entry_label),
"wire_mark_is_manual": wire_mark_is_manual,
"wire_style_id": _optional_text(item, "wire_style_id"),
"start_element_uuid": _optional_string(item, "start_element_uuid", entry_label),
"start_instance_id": _optional_string(item, "start_instance_id", entry_label),
"start_terminal_uuid": _optional_string(item, "start_terminal_uuid", entry_label),
@ -558,6 +570,68 @@ def _normalize_cabinet(payload):
return normalized
def _has_wire_properties_table(database_path):
try:
connection = sqlite3.connect(str(database_path))
except Exception:
return False
try:
row = connection.execute(
"""
SELECT name
FROM sqlite_master
WHERE type = 'table'
AND name = 'wire_properties'
LIMIT 1
"""
).fetchone()
return row is not None
except Exception:
return False
finally:
try:
connection.close()
except Exception:
pass
def _wire_style_database_path(payload, json_path):
for key in ("wire_style_database_path", "project_database_path", "database_path"):
value = payload.get(key, "")
if isinstance(value, str) and value.strip():
return value.strip()
try:
directory = Path(json_path).resolve().parent
except Exception:
return ""
candidates = []
search_dirs = []
for base in (directory, directory.parent, directory.parent.parent):
if base and base not in search_dirs:
search_dirs.append(base)
data_dir = base / "datafiles"
if data_dir not in search_dirs:
search_dirs.append(data_dir)
for base in (directory.parent, directory.parent.parent):
try:
for data_dir in base.glob("*/datafiles"):
if data_dir not in search_dirs:
search_dirs.append(data_dir)
except Exception:
pass
for search_dir in search_dirs:
for pattern in ("project-local.db", "project-local.sqlite", "*.sqlite", "*.sqlite3", "*.db"):
try:
candidates.extend(search_dir.glob(pattern))
except Exception:
pass
for candidate in sorted(set(candidates), key=lambda item: item.name.lower()):
# 不要求 QET 改协议FreeCAD 只在候选库确实含 wire_properties 时自动使用。
if _has_wire_properties_table(candidate):
return str(candidate)
return ""
def load_exchange_payload(json_path):
try:
raw_text = Path(json_path).read_text(encoding="utf-8")
@ -594,6 +668,9 @@ def load_exchange_payload(json_path):
"device_models": _normalize_device_models(payload),
"wires": _normalize_wires(payload),
}
wire_style_database_path = _wire_style_database_path(payload, json_path)
if wire_style_database_path:
normalized["wire_style_database_path"] = wire_style_database_path
return normalized
@ -623,6 +700,7 @@ def _build_summary(payload, json_path):
"missing_terminal_instances": missing_terminal_instances,
"cabinet": cabinet,
"scene_path": os.environ.get(ENV_SCENE_PATH, "").strip(),
"wire_style_database_path": payload.get("wire_style_database_path", ""),
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,5 +1,6 @@
import importlib
import json
import sqlite3
import sys
import tempfile
import types
@ -132,6 +133,132 @@ class ExchangeBootstrapWiringTest(unittest.TestCase):
self.assertEqual("wire-1", normalized["wires"][0]["wire_id"])
self.assertEqual("W001", normalized["wires"][0]["wire_mark"])
def test_load_exchange_payload_preserves_wire_label_and_style_id(self):
_install_fake_modules()
sys.modules.pop("ExchangeBootstrap", None)
bootstrap = importlib.import_module("ExchangeBootstrap")
payload = {
"schema_version": "1.2",
"project_uuid": "project-1",
"devices": [],
"terminals": [],
"device_models": [],
"wires": [
{
"wire_id": "wire-1",
"wire_label": "N4111",
"wire_style_id": 1,
"start_terminal_uuid": "terminal-a",
"end_terminal_uuid": "terminal-b",
}
],
}
with tempfile.TemporaryDirectory() as temp_dir:
path = Path(temp_dir) / "2d_to_3d.json"
path.write_text(json.dumps(payload), encoding="utf-8")
normalized = bootstrap.load_exchange_payload(str(path))
self.assertEqual("N4111", normalized["wires"][0]["wire_label"])
self.assertEqual("1", normalized["wires"][0]["wire_style_id"])
def test_load_exchange_payload_detects_wire_properties_database_next_to_json(self):
_install_fake_modules()
sys.modules.pop("ExchangeBootstrap", None)
bootstrap = importlib.import_module("ExchangeBootstrap")
payload = {
"schema_version": "1.2",
"project_uuid": "project-1",
"devices": [],
"terminals": [],
"device_models": [],
"wires": [],
}
with tempfile.TemporaryDirectory() as temp_dir:
path = Path(temp_dir) / "2d_to_3d.json"
db_path = Path(temp_dir) / "project-local.sqlite"
path.write_text(json.dumps(payload), encoding="utf-8")
connection = sqlite3.connect(str(db_path))
try:
connection.execute(
"""
CREATE TABLE wire_properties (
id INTEGER PRIMARY KEY,
project_uuid TEXT NOT NULL,
line_color TEXT
)
"""
)
connection.commit()
finally:
connection.close()
normalized = bootstrap.load_exchange_payload(str(path))
self.assertEqual(str(db_path), normalized["wire_style_database_path"])
def test_load_exchange_payload_detects_project_datafiles_wire_properties_database(self):
_install_fake_modules()
sys.modules.pop("ExchangeBootstrap", None)
bootstrap = importlib.import_module("ExchangeBootstrap")
payload = {
"schema_version": "1.2",
"project_uuid": "project-1",
"devices": [],
"terminals": [],
"device_models": [],
"wires": [{"wire_id": "wire-1", "wire_style_id": "1"}],
}
with tempfile.TemporaryDirectory() as temp_dir:
project_dir = Path(temp_dir) / "project-a"
exchange_dir = project_dir / ".qet_freecad"
data_dir = project_dir / "datafiles"
exchange_dir.mkdir(parents=True)
data_dir.mkdir(parents=True)
path = exchange_dir / "2d_to_3d.json"
db_path = data_dir / "project-local.db"
path.write_text(json.dumps(payload), encoding="utf-8")
connection = sqlite3.connect(str(db_path))
try:
connection.execute(
"""
CREATE TABLE wire_properties (
id INTEGER PRIMARY KEY,
project_uuid TEXT NOT NULL,
line_color TEXT
)
"""
)
connection.commit()
finally:
connection.close()
normalized = bootstrap.load_exchange_payload(str(path))
self.assertEqual(str(db_path), normalized["wire_style_database_path"])
def test_exchange_summary_includes_wire_style_database_path(self):
_install_fake_modules()
sys.modules.pop("ExchangeBootstrap", None)
bootstrap = importlib.import_module("ExchangeBootstrap")
payload = {
"project_uuid": "project-1",
"devices": [],
"terminals": [],
"device_models": [],
"wires": [],
"wire_style_database_path": "D:/project/project-local.sqlite",
}
summary = bootstrap._build_summary(payload, "D:/project/2d_to_3d.json")
self.assertEqual(
"D:/project/project-local.sqlite",
summary["wire_style_database_path"],
)
if __name__ == "__main__":
unittest.main()

Loading…
Cancel
Save