diff --git a/src/Mod/FreeCADExchange/AutoRouting.py b/src/Mod/FreeCADExchange/AutoRouting.py index 46622ec..08ec063 100644 --- a/src/Mod/FreeCADExchange/AutoRouting.py +++ b/src/Mod/FreeCADExchange/AutoRouting.py @@ -1337,6 +1337,10 @@ def _route_segment_key(segment): def _route_segment_keys(result): route_track = result.get("route_track", {}) if isinstance(result, dict) else {} + return _route_track_segment_keys(route_track) + + +def _route_track_segment_keys(route_track): segments = route_track.get("segments", []) if isinstance(route_track, dict) else [] keys = [] for segment in segments or []: @@ -1346,6 +1350,35 @@ def _route_segment_keys(result): return keys +def _incoming_wire_uuids(wires): + wire_uuids = set() + for item in wires or []: + if not isinstance(item, dict): + continue + wire_uuid = _wire_item_value(item, "wire_id", "wire_uuid", "id") + if wire_uuid: + wire_uuids.add(wire_uuid) + return wire_uuids + + +def _existing_routed_segment_usage(doc, excluded_wire_uuids=None): + excluded_wire_uuids = set(excluded_wire_uuids or []) + usage = {} + for wire in list(WiringObjects.iter_routed_wire_objects(doc)): + if (getattr(wire, "RouteType", "") or "").strip() != "RoutedConnection": + continue + wire_uuid = (getattr(wire, "QetWireUuid", "") or "").strip() + if wire_uuid and wire_uuid in excluded_wire_uuids: + continue + try: + route_track = json.loads((getattr(wire, "QetRouteTrackJson", "") or "").strip() or "{}") + except Exception: + route_track = {} + for segment_key in _route_track_segment_keys(route_track): + usage[segment_key] = usage.get(segment_key, 0) + 1 + return usage + + def bind_wire_task_terminals_from_payload(doc, payload): """Bind local template terminals to QET terminal UUIDs without creating wires.""" if doc is None: @@ -1427,7 +1460,10 @@ def route_eplan_connections_from_payload(doc, payload, options=None, prepared_la missing_endpoint_uuids = set() lane_indexes_by_pair = {} lane_indexes_by_segment = {} - segment_usage_costs = {} + segment_usage_costs = _existing_routed_segment_usage( + doc, + excluded_wire_uuids=_incoming_wire_uuids(wires), + ) def add_status(status): key = str(status or "").strip() or "Unknown" diff --git a/tests/python/freecad_exchange_auto_routing_test.py b/tests/python/freecad_exchange_auto_routing_test.py index 3787c85..bb3f2d4 100644 --- a/tests/python/freecad_exchange_auto_routing_test.py +++ b/tests/python/freecad_exchange_auto_routing_test.py @@ -2035,6 +2035,68 @@ class AutoRoutingTest(unittest.TestCase): self.assertIn("Alternate Duct", second_labels) self.assertNotIn("Direct Duct", second_labels) + def test_route_eplan_connections_prefers_unused_segments_occupied_by_existing_wires(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, 20), app.Vector(100, 0, 20)], + label="Direct Duct", + project_uuid="project-1", + kind="WireDuct", + ) + routing_network.create_route_carrier( + doc, + [app.Vector(0, 0, 20), app.Vector(0, 40, 20)], + label="Left Bridge", + project_uuid="project-1", + kind="WireDuct", + ) + routing_network.create_route_carrier( + doc, + [app.Vector(0, 40, 20), app.Vector(100, 40, 20)], + label="Alternate Duct", + project_uuid="project-1", + kind="WireDuct", + ) + routing_network.create_route_carrier( + doc, + [app.Vector(100, 40, 20), app.Vector(100, 0, 20)], + label="Right Bridge", + project_uuid="project-1", + kind="WireDuct", + ) + auto_routing.route_eplan_connection_between_terminals( + doc, + start, + end, + wire_uuid="existing-wire", + ) + payload = { + "project_uuid": "project-1", + "wires": [ + { + "wire_id": "new-wire", + "start_terminal_uuid": "terminal-start", + "end_terminal_uuid": "terminal-end", + } + ], + } + + report = auto_routing.route_eplan_connections_from_payload(doc, payload) + + route_labels = [ + segment["carrier"]["label"] + for segment in report["routes"][0]["route_track"]["segments"] + ] + self.assertIn("Alternate Duct", route_labels) + self.assertNotIn("Direct Duct", route_labels) + def test_route_eplan_connections_report_includes_collision_samples(self): _install_fake_freecad() terminal_objects, _wiring_objects, routing_network, auto_routing = _reload_modules()