From c4fc7a3b8aab4dd1b39932c5193ea43990072860 Mon Sep 17 00:00:00 2001 From: Zhaowenlong Date: Thu, 28 May 2026 19:15:53 +0800 Subject: [PATCH] feat: record freecad auto route diagnostics --- src/Mod/FreeCADExchange/AutoRouting.py | 39 ++++++++++++++++--- .../freecad_exchange_auto_routing_test.py | 32 +++++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/src/Mod/FreeCADExchange/AutoRouting.py b/src/Mod/FreeCADExchange/AutoRouting.py index db80ad7..6b3adee 100644 --- a/src/Mod/FreeCADExchange/AutoRouting.py +++ b/src/Mod/FreeCADExchange/AutoRouting.py @@ -102,6 +102,14 @@ def _point_payload(point): } +def _route_length(points): + total = 0.0 + normalized = [_vector(point) for point in points or []] + for index in range(len(normalized) - 1): + total += _distance(normalized[index], normalized[index + 1]) + return total + + def _is_finite_point(point): try: return all( @@ -514,26 +522,43 @@ def _set_string(obj, name, value, description="Auto-routing property"): TerminalObjects.ensure_string_property(obj, name, "QET Routing", description, value) -def _route_payload(route_data, collisions): +def _route_payload(route_data, collisions, wire_style_id=""): + points = route_data.get("points", []) return { "algorithm": route_data.get("algorithm", ""), - "points": [_point_payload(point) for point in route_data.get("points", [])], + "length_mm": _route_length(points), + "wire_style_id": str(wire_style_id or "").strip(), + "points": [_point_payload(point) for point in points], + "collision_count": len(collisions), "collisions": collisions, "network": route_data.get("network", {}), } -def _set_auto_metadata(wire, route_data, collisions): +def _set_auto_metadata(wire, route_data, collisions, wire_style_id=""): + length_mm = _route_length(route_data.get("points", [])) _set_string( wire, "QetAutoRouteAlgorithm", route_data.get("algorithm", ""), "Auto-routing algorithm used for this wire", ) + _set_string( + wire, + "QetAutoRouteLengthMm", + "{0:.3f}".format(length_mm), + "Auto route length in millimeters", + ) + _set_string( + wire, + "QetWireStyleId", + str(wire_style_id or "").strip(), + "QET wire style ID", + ) _set_string( wire, "QetAutoRouteDiagnosticsJson", - json.dumps(_route_payload(route_data, collisions), ensure_ascii=False), + json.dumps(_route_payload(route_data, collisions, wire_style_id=wire_style_id), ensure_ascii=False), "Auto-routing diagnostics", ) if route_data.get("network"): @@ -870,6 +895,7 @@ def route_between_terminals( group_uuid="", wire_mark="", wire_mark_is_manual=False, + wire_style_id="", ): if doc is None: raise AutoRoutingError("No FreeCAD document is available.") @@ -881,6 +907,7 @@ def route_between_terminals( raise AutoRoutingError("Start and end terminal must be different.") opts = _merged_options(options) + effective_wire_style_id = str(wire_style_id or opts.get("wire_style_id", "") or "").strip() start_uuid = (getattr(start_terminal, "QetTerminalUuid", "") or "").strip() end_uuid = (getattr(end_terminal, "QetTerminalUuid", "") or "").strip() project_uuid = _project_uuid(doc, start_terminal, end_terminal) @@ -937,7 +964,7 @@ def route_between_terminals( wire_mark=wire_mark, wire_mark_is_manual=wire_mark_is_manual, ) - _set_auto_metadata(wire, route_data, collisions) + _set_auto_metadata(wire, route_data, collisions, wire_style_id=effective_wire_style_id) routed_group = WiringObjects.ensure_routed_group(doc, project_uuid) if wire not in getattr(routed_group, "Group", []): @@ -1094,6 +1121,7 @@ def route_all_from_payload(doc, payload, options=None): group_uuid=_wire_item_value(item, "group_uuid"), wire_mark=_wire_item_value(item, "wire_mark"), wire_mark_is_manual=bool(item.get("wire_mark_is_manual", False)), + wire_style_id=_wire_item_value(item, "wire_style_id"), ) except Exception as exc: report["errors"].append(str(exc)) @@ -1175,6 +1203,7 @@ def _wire_tasks_payload(doc): "wire_label": (getattr(task, "QetWireLabel", "") or "").strip(), "wire_mark": (getattr(task, "QetWireMark", "") or "").strip(), "wire_mark_is_manual": bool(getattr(task, "QetWireMarkIsManual", False)), + "wire_style_id": (getattr(task, "QetWireStyleId", "") or "").strip(), "net_uuid": (getattr(task, "QetNetUuid", "") or "").strip(), "group_uuid": (getattr(task, "QetGroupUuid", "") or "").strip(), "start_element_uuid": (getattr(task, "QetStartElementUuid", "") or "").strip(), diff --git a/tests/python/freecad_exchange_auto_routing_test.py b/tests/python/freecad_exchange_auto_routing_test.py index 30e9613..07c4de3 100644 --- a/tests/python/freecad_exchange_auto_routing_test.py +++ b/tests/python/freecad_exchange_auto_routing_test.py @@ -1,4 +1,5 @@ import importlib +import json import sys import types import unittest @@ -270,6 +271,37 @@ class AutoRoutingTest(unittest.TestCase): self.assertEqual("Routed", result["route_status"]) self.assertTrue(any(point.y == 30.0 for point in result["points"])) + def test_auto_route_stores_length_and_wire_style_diagnostics(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)], + project_uuid="project-1", + kind="WireDuct", + ) + + result = auto_routing.route_between_terminals( + doc, + start, + end, + wire_uuid="wire-1", + wire_label="N4111", + options={"wire_style_id": "42"}, + ) + wire = result["wire"] + payload = json.loads(wire.QetAutoRouteDiagnosticsJson) + + self.assertGreater(float(wire.QetAutoRouteLengthMm), 0.0) + self.assertEqual("42", wire.QetWireStyleId) + self.assertEqual("42", payload["wire_style_id"]) + self.assertGreater(payload["length_mm"], 0.0) + def test_route_carrier_styles_make_generated_objects_distinguishable(self): _install_fake_freecad() terminal_objects, _wiring_objects, routing_network, _auto_routing = _reload_modules()