From e3029f2bd09e9e99cc062dc220f9aa5d74ca3815 Mon Sep 17 00:00:00 2001 From: Zhaowenlong Date: Mon, 1 Jun 2026 16:15:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=9D=A2=E6=9D=BF=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=AB=AF=E5=AD=90=E6=8E=A5=E5=85=A5=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/FreeCAD 3D自动布线设计方案.md | 19 +++++-- src/Mod/FreeCADExchange/AutoRoutingPanel.py | 44 +++++++++++++++++ .../freecad_exchange_auto_routing_test.py | 49 +++++++++++++++++++ 3 files changed, 109 insertions(+), 3 deletions(-) diff --git a/docs/FreeCAD 3D自动布线设计方案.md b/docs/FreeCAD 3D自动布线设计方案.md index 69fe0ef..429d3b3 100644 --- a/docs/FreeCAD 3D自动布线设计方案.md +++ b/docs/FreeCAD 3D自动布线设计方案.md @@ -89,6 +89,8 @@ terminal_uuid FreeCAD 的 `3D 布线连接` 面板提供“线槽桥接容差 mm”数值框,手动测试时可直接调整这个选项;生成布线路径网络、检查布线路径网络和生成布线连接都会读取当前面板值。 +同一面板还提供“端子接入最大距离 mm”和“端子出线长度 mm”。前者用于控制端子距离最近路由网络超过多少毫米时不再生成 `TerminalAccess`,避免设备还没摆放好时生成超长悬空接入线;后者用于控制端子沿 LCS 出线方向先走出的短线长度,避免导线从设备壳体内部或端子原点直接折返。 + ### 2.1 路由优先级 当前版本按下面优先级处理: @@ -270,6 +272,15 @@ src/Mod/FreeCADExchange/AutoRouting.py network-dijkstra-v1 ``` +端子接入有两个可调参数: + +```text +terminal_exit_length = 20.0 +terminal_access_max_distance = 1000.0 +``` + +`terminal_exit_length` 决定端子出线段长度;`terminal_access_max_distance` 决定端子出线点到最近路由网络的最大允许接入距离。两个参数都只保存在当前 FreeCAD 面板/调用选项中,不写数据库。 + ### 4.4 悬空线策略 当前版本默认: @@ -435,6 +446,7 @@ tests/python/freecad_exchange_auto_routing_test.py 28. 线槽源对象支持通过 `QetWireDuctEndMarginMm` 按对象调整中心路径端部缩进距离。 29. 自动布线支持通过 `adjoining_duct_tolerance` 调整相邻线槽端点自动桥接容差,并在网络结果中记录桥接段数量。 30. `3D 布线连接` 面板提供“线槽桥接容差 mm”设置,面板生成/检查/布线流程会使用该值。 +31. `3D 布线连接` 面板提供“端子接入最大距离 mm”和“端子出线长度 mm”设置,用于适配真实机柜里端子离线槽远近不同、设备端子方向不同的情况。 已完成 FreeCAD smoke: @@ -454,9 +466,10 @@ tests/manual/freecad_auto_routing_smoke.py 3. 清除布线连接 4. 清除走线路径 5. 点击“准备布线布局空间” -6. 可选:选中无法自动识别的线槽实体 -7. 点击“生成布线路径网络”;如果不选择,则使用整份文档自动识别 -8. 点击“生成布线连接” +6. 按当前机柜情况调整线槽桥接容差、端子接入最大距离、端子出线长度 +7. 可选:选中无法自动识别的线槽实体 +8. 点击“生成布线路径网络”;如果不选择,则使用整份文档自动识别 +9. 点击“生成布线连接” ``` 三个按钮的职责: diff --git a/src/Mod/FreeCADExchange/AutoRoutingPanel.py b/src/Mod/FreeCADExchange/AutoRoutingPanel.py index 5fd190d..d6a6b8d 100644 --- a/src/Mod/FreeCADExchange/AutoRoutingPanel.py +++ b/src/Mod/FreeCADExchange/AutoRoutingPanel.py @@ -85,6 +85,20 @@ class AutoRoutingController: tolerance = RoutingNetwork.DEFAULT_ADJOINING_DUCT_TOLERANCE self.options["adjoining_duct_tolerance"] = max(tolerance, 0.0) + def set_terminal_access_max_distance(self, value): + try: + max_distance = float(value) + except Exception: + max_distance = RoutingNetwork.DEFAULT_TERMINAL_ACCESS_MAX_DISTANCE + self.options["terminal_access_max_distance"] = max(max_distance, 0.0) + + def set_terminal_exit_length(self, value): + try: + exit_length = float(value) + except Exception: + exit_length = AutoRouting.DEFAULT_OPTIONS["terminal_exit_length"] + self.options["terminal_exit_length"] = max(exit_length, 0.0) + def summary(self): doc = _active_document() terminal_count = len(AutoRouting.index_terminals(doc)) @@ -212,6 +226,34 @@ class AutoRoutingTaskPanel: ) ) options_layout.addWidget(self.adjoining_duct_tolerance_spin) + options_layout.addWidget(QtWidgets.QLabel("端子接入最大距离 mm")) + self.terminal_access_max_distance_spin = QtWidgets.QDoubleSpinBox() + self.terminal_access_max_distance_spin.setRange(0.0, 100000.0) + self.terminal_access_max_distance_spin.setDecimals(1) + self.terminal_access_max_distance_spin.setSingleStep(50.0) + self.terminal_access_max_distance_spin.setValue( + float( + self.controller.routing_options().get( + "terminal_access_max_distance", + RoutingNetwork.DEFAULT_TERMINAL_ACCESS_MAX_DISTANCE, + ) + ) + ) + options_layout.addWidget(self.terminal_access_max_distance_spin) + options_layout.addWidget(QtWidgets.QLabel("端子出线长度 mm")) + self.terminal_exit_length_spin = QtWidgets.QDoubleSpinBox() + self.terminal_exit_length_spin.setRange(0.0, 1000.0) + self.terminal_exit_length_spin.setDecimals(1) + self.terminal_exit_length_spin.setSingleStep(5.0) + self.terminal_exit_length_spin.setValue( + float( + self.controller.routing_options().get( + "terminal_exit_length", + AutoRouting.DEFAULT_OPTIONS["terminal_exit_length"], + ) + ) + ) + options_layout.addWidget(self.terminal_exit_length_spin) self.generate_layout_button = QtWidgets.QPushButton("准备布线布局空间") self.generate_layout_button.setToolTip( @@ -280,6 +322,8 @@ class AutoRoutingTaskPanel: def _sync_options_from_widgets(self): self.controller.set_adjoining_duct_tolerance(self.adjoining_duct_tolerance_spin.value()) + self.controller.set_terminal_access_max_distance(self.terminal_access_max_distance_spin.value()) + self.controller.set_terminal_exit_length(self.terminal_exit_length_spin.value()) def generate_routing_paths(self): try: diff --git a/tests/python/freecad_exchange_auto_routing_test.py b/tests/python/freecad_exchange_auto_routing_test.py index 8dc20be..5276844 100644 --- a/tests/python/freecad_exchange_auto_routing_test.py +++ b/tests/python/freecad_exchange_auto_routing_test.py @@ -1499,6 +1499,55 @@ class AutoRoutingTest(unittest.TestCase): self.assertEqual(2, result["wire_duct_open_end_carriers"]) self.assertEqual(0, result["terminal_access_carriers"]) + def test_auto_routing_controller_exposes_terminal_access_max_distance(self): + _install_fake_freecad() + terminal_objects, _wiring_objects, _routing_network, _auto_routing = _reload_modules() + auto_routing_panel = importlib.import_module("AutoRoutingPanel") + app = sys.modules["FreeCAD"] + doc = FakeDocument() + app.ActiveDocument = doc + terminal_objects.ensure_root_group(doc, "project-1") + _terminal(doc, terminal_objects, "TerminalStart", "terminal-start", app.Vector(0, 0, 0)) + duct = doc.addObject("Part::Feature", "WireDuctFar") + duct.Label = "Wire Duct Far" + duct.Shape = FakeShape(FakeBoundBox(5000, 5100, -5, 5, 15, 25)) + + controller = auto_routing_panel.AutoRoutingController() + controller.set_terminal_access_max_distance(6000.0) + result = controller.generate_routing_paths() + + self.assertEqual(1, result["terminal_access_carriers"]) + self.assertEqual(6000.0, controller.routing_options()["terminal_access_max_distance"]) + + def test_auto_routing_controller_exposes_terminal_exit_length(self): + _install_fake_freecad() + terminal_objects, _wiring_objects, routing_network, _auto_routing = _reload_modules() + auto_routing_panel = importlib.import_module("AutoRoutingPanel") + app = sys.modules["FreeCAD"] + doc = FakeDocument() + app.ActiveDocument = doc + terminal_objects.ensure_root_group(doc, "project-1") + _terminal(doc, terminal_objects, "TerminalStart", "terminal-start", app.Vector(50, 0, 0)) + duct = doc.addObject("Part::Feature", "WireDuctA") + duct.Label = "Wire Duct A" + duct.Shape = FakeShape(FakeBoundBox(0, 100, -5, 5, 15, 25)) + + controller = auto_routing_panel.AutoRoutingController() + controller.set_terminal_exit_length(40.0) + controller.generate_routing_paths() + access_carriers = [ + carrier + for carrier in routing_network.collect_route_carriers(doc) + if getattr(carrier, "QetRouteCarrierKind", "") == "TerminalAccess" + ] + + self.assertEqual(1, len(access_carriers)) + self.assertEqual( + (50.0, 0.0, 40.0), + tuple(getattr(access_carriers[0].Points[0], axis) for axis in ("x", "y", "z")), + ) + self.assertEqual(40.0, controller.routing_options()["terminal_exit_length"]) + def test_route_eplan_connections_prepares_layout_space_like_eplan_route(self): _install_fake_freecad() terminal_objects, _wiring_objects, _routing_network, _auto_routing = _reload_modules()