diff --git a/src/Mod/FreeCADExchange/RoutingNetwork.py b/src/Mod/FreeCADExchange/RoutingNetwork.py index 5ac5f4a..022df06 100644 --- a/src/Mod/FreeCADExchange/RoutingNetwork.py +++ b/src/Mod/FreeCADExchange/RoutingNetwork.py @@ -2278,6 +2278,19 @@ def _segment_usage_key(carrier, from_key, to_key): ) +def _carrier_capacity(carrier): + if carrier is None: + return 1 + for property_name in ("QetRouteCarrierCapacity", "QetWireCapacity"): + try: + value = int(float(getattr(carrier, property_name, 0) or 0)) + except Exception: + value = 0 + if value > 0: + return value + return 1 + + def shortest_path_with_carriers( network, start_key, @@ -2362,7 +2375,9 @@ def shortest_path_with_carriers( usage_cost = 0.0 if segment_usage_costs: usage_count = float(segment_usage_costs.get(_segment_usage_key(carrier, key, next_key), 0.0) or 0.0) - usage_cost = usage_count * float(segment_reuse_penalty or 0.0) + capacity = float(_carrier_capacity(carrier)) + excess_usage = max(usage_count - capacity + 1.0, 0.0) + usage_cost = excess_usage * float(segment_reuse_penalty or 0.0) next_state = (next_key, direction) next_cost = ( cost diff --git a/tests/python/freecad_exchange_auto_routing_test.py b/tests/python/freecad_exchange_auto_routing_test.py index bb3f2d4..953ce76 100644 --- a/tests/python/freecad_exchange_auto_routing_test.py +++ b/tests/python/freecad_exchange_auto_routing_test.py @@ -2035,6 +2035,79 @@ class AutoRoutingTest(unittest.TestCase): self.assertIn("Alternate Duct", second_labels) self.assertNotIn("Direct Duct", second_labels) + def test_route_eplan_connections_respects_route_segment_capacity_before_detouring(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") + _terminal(doc, terminal_objects, "TerminalStartA", "terminal-start-a", app.Vector(0, 0, 0)) + _terminal(doc, terminal_objects, "TerminalEndA", "terminal-end-a", app.Vector(100, 0, 0)) + _terminal(doc, terminal_objects, "TerminalStartB", "terminal-start-b", app.Vector(0, 0, 0)) + _terminal(doc, terminal_objects, "TerminalEndB", "terminal-end-b", app.Vector(100, 0, 0)) + _terminal(doc, terminal_objects, "TerminalStartC", "terminal-start-c", app.Vector(0, 0, 0)) + _terminal(doc, terminal_objects, "TerminalEndC", "terminal-end-c", app.Vector(100, 0, 0)) + direct = 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", + ) + direct.QetRouteCarrierCapacity = 2 + 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", + ) + payload = { + "project_uuid": "project-1", + "wires": [ + { + "wire_id": "wire-a", + "start_terminal_uuid": "terminal-start-a", + "end_terminal_uuid": "terminal-end-a", + }, + { + "wire_id": "wire-b", + "start_terminal_uuid": "terminal-start-b", + "end_terminal_uuid": "terminal-end-b", + }, + { + "wire_id": "wire-c", + "start_terminal_uuid": "terminal-start-c", + "end_terminal_uuid": "terminal-end-c", + }, + ], + } + + report = auto_routing.route_eplan_connections_from_payload(doc, payload) + + route_labels = [ + [segment["carrier"]["label"] for segment in route["route_track"]["segments"]] + for route in report["routes"] + ] + self.assertIn("Direct Duct", route_labels[0]) + self.assertIn("Direct Duct", route_labels[1]) + self.assertIn("Alternate Duct", route_labels[2]) + self.assertNotIn("Direct Duct", route_labels[2]) + 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()