diff --git a/src/Mod/FreeCADExchange/AutoRouting.py b/src/Mod/FreeCADExchange/AutoRouting.py index 3ff04f4..9d23d76 100644 --- a/src/Mod/FreeCADExchange/AutoRouting.py +++ b/src/Mod/FreeCADExchange/AutoRouting.py @@ -876,14 +876,19 @@ def detect_collisions(points, obstacles, ignored_segment_indices=None): end = points[index + 1] for obstacle in obstacles: if _segment_intersects_bbox(start, end, obstacle["bbox"]): + raw_bbox = obstacle.get("raw_bbox") or obstacle.get("bbox") or {} + collision_kind = "HardIntersection" + if raw_bbox and not _segment_intersects_bbox(start, end, raw_bbox): + collision_kind = "ClearanceWarning" collisions.append( { "segment_index": index, "segment_start": _point_payload(start), "segment_end": _point_payload(end), + "collision_kind": collision_kind, "obstacle_name": obstacle.get("name", ""), "obstacle_label": obstacle.get("label", ""), - "obstacle_bbox": dict(obstacle.get("raw_bbox") or obstacle.get("bbox") or {}), + "obstacle_bbox": dict(raw_bbox), "collision_bbox": dict(obstacle.get("bbox", {}) or {}), } ) @@ -1366,12 +1371,21 @@ def format_eplan_connection_route_report(report): or collision_sample.get("obstacle_name") or "未知对象" ) - message += "\n碰撞示例:导线 {0} 碰到 {1}。".format( + wire_text = ( collision_sample.get("wire_label") or collision_sample.get("wire_uuid") - or "未知导线", - obstacle_text, + or "未知导线" ) + if collision_sample.get("collision_kind") == "ClearanceWarning": + message += "\n碰撞示例:导线 {0} 进入 {1} 的安全间隙。".format( + wire_text, + obstacle_text, + ) + else: + message += "\n碰撞示例:导线 {0} 碰到 {1}。".format( + wire_text, + obstacle_text, + ) auto_bound = report.get("auto_bound_terminals", 0) auto_created = report.get("auto_created_terminals", 0) if auto_bound or auto_created: diff --git a/tests/python/freecad_exchange_auto_routing_test.py b/tests/python/freecad_exchange_auto_routing_test.py index 29ef297..82baa0a 100644 --- a/tests/python/freecad_exchange_auto_routing_test.py +++ b/tests/python/freecad_exchange_auto_routing_test.py @@ -1074,6 +1074,36 @@ class AutoRoutingTest(unittest.TestCase): self.assertEqual("CollisionWarning", result["route_status"]) self.assertEqual("CollisionWarning", result["wire"].RouteStatus) self.assertEqual(1, result["collision_count"]) + self.assertEqual("HardIntersection", result["collisions"][0]["collision_kind"]) + + def test_eplan_connection_route_marks_clearance_warning_against_expanded_obstacle_bbox(self): + _install_fake_freecad() + terminal_objects, _wiring_objects, routing_network, auto_routing = _reload_modules() + app = sys.modules["FreeCAD"] + doc = FakeDocument() + terminal_objects.ensure_root_group(doc, "project-1") + start = _terminal(doc, terminal_objects, "TerminalStart", "terminal-start", app.Vector(0, 0, 0)) + end = _terminal(doc, terminal_objects, "TerminalEnd", "terminal-end", app.Vector(100, 0, 0)) + routing_network.create_route_carrier( + doc, + [app.Vector(0, 0, 100), app.Vector(100, 0, 100)], + project_uuid="project-1", + ) + obstacle = doc.addObject("Part::Feature", "NearObstacle") + obstacle.Shape = FakeShape(FakeBoundBox(40, 60, 3, 6, 90, 110)) + + result = auto_routing.route_eplan_connection_between_terminals( + doc, + start, + end, + options={"obstacle_clearance": 5.0}, + ) + + self.assertEqual("CollisionWarning", result["route_status"]) + self.assertEqual(1, result["collision_count"]) + self.assertEqual("ClearanceWarning", result["collisions"][0]["collision_kind"]) + self.assertEqual(3.0, result["collisions"][0]["obstacle_bbox"]["ymin"]) + self.assertEqual(-2.0, result["collisions"][0]["collision_bbox"]["ymin"]) def test_eplan_connection_route_ignores_terminal_exit_segment_collision(self): _install_fake_freecad() @@ -1432,6 +1462,7 @@ class AutoRoutingTest(unittest.TestCase): self.assertEqual("wire-1", report["collision_samples"][0]["wire_uuid"]) self.assertEqual("N4111", report["collision_samples"][0]["wire_label"]) self.assertEqual("MiddleObstacle", report["collision_samples"][0]["obstacle_name"]) + self.assertEqual("HardIntersection", report["collision_samples"][0]["collision_kind"]) self.assertEqual({"x": 0.0, "y": 0.0, "z": 100.0}, report["collision_samples"][0]["segment_start"]) self.assertEqual({"x": 100.0, "y": 0.0, "z": 100.0}, report["collision_samples"][0]["segment_end"]) self.assertEqual(40.0, report["collision_samples"][0]["obstacle_bbox"]["xmin"]) @@ -1501,6 +1532,27 @@ class AutoRoutingTest(unittest.TestCase): self.assertIn("首个错误:没有可用的线槽/路由路径网络", message) self.assertIn("缺失示例:terminal-a -> terminal-b", message) + def test_route_eplan_connections_report_calls_out_clearance_collision_kind(self): + _install_fake_freecad() + _terminal_objects, _wiring_objects, _routing_network, auto_routing = _reload_modules() + report = { + "routed": 1, + "collision_warnings": 1, + "skipped_missing_terminal": 0, + "collision_samples": [ + { + "wire_label": "N4111", + "obstacle_label": "柜体侧板", + "collision_kind": "ClearanceWarning", + } + ], + } + + message = auto_routing.format_eplan_connection_route_report(report) + + self.assertIn("安全间隙", message) + self.assertIn("柜体侧板", message) + def test_route_eplan_connections_report_ignores_non_numeric_status_counts(self): _install_fake_freecad() _terminal_objects, _wiring_objects, _routing_network, auto_routing = _reload_modules()