|
|
|
@ -765,9 +765,44 @@ def _bbox_payload(obj, clearance=0.0):
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _collect_group_tree_ids(root):
|
|
|
|
|
|
|
|
excluded = set()
|
|
|
|
|
|
|
|
stack = [root]
|
|
|
|
|
|
|
|
while stack:
|
|
|
|
|
|
|
|
obj = stack.pop()
|
|
|
|
|
|
|
|
if obj is None or id(obj) in excluded:
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
excluded.add(id(obj))
|
|
|
|
|
|
|
|
stack.extend(list(getattr(obj, "Group", []) or []))
|
|
|
|
|
|
|
|
return excluded
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _expanded_obstacle_exclusion_ids(doc, exclude):
|
|
|
|
|
|
|
|
excluded = set(id(obj) for obj in (exclude or []) if obj is not None)
|
|
|
|
|
|
|
|
endpoint_instance_ids = {
|
|
|
|
|
|
|
|
(getattr(obj, "QetInstanceId", "") or "").strip()
|
|
|
|
|
|
|
|
for obj in (exclude or [])
|
|
|
|
|
|
|
|
if obj is not None and (getattr(obj, "QetInstanceId", "") or "").strip()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if not endpoint_instance_ids:
|
|
|
|
|
|
|
|
return excluded
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for obj in list(getattr(doc, "Objects", []) or []):
|
|
|
|
|
|
|
|
instance_id = (getattr(obj, "QetInstanceId", "") or "").strip()
|
|
|
|
|
|
|
|
parent_instance_ids = {
|
|
|
|
|
|
|
|
(getattr(parent, "QetInstanceId", "") or "").strip()
|
|
|
|
|
|
|
|
for parent in list(getattr(obj, "InList", []) or [])
|
|
|
|
|
|
|
|
if (getattr(parent, "QetInstanceId", "") or "").strip()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if instance_id in endpoint_instance_ids or parent_instance_ids.intersection(endpoint_instance_ids):
|
|
|
|
|
|
|
|
excluded.update(_collect_group_tree_ids(obj))
|
|
|
|
|
|
|
|
excluded.add(id(obj))
|
|
|
|
|
|
|
|
return excluded
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def collect_obstacles(doc, exclude=None, options=None):
|
|
|
|
def collect_obstacles(doc, exclude=None, options=None):
|
|
|
|
opts = _merged_options(options)
|
|
|
|
opts = _merged_options(options)
|
|
|
|
excluded = set(id(obj) for obj in (exclude or []) if obj is not None)
|
|
|
|
excluded = _expanded_obstacle_exclusion_ids(doc, exclude)
|
|
|
|
clearance = float(opts.get("obstacle_clearance", 0.0) or 0.0)
|
|
|
|
clearance = float(opts.get("obstacle_clearance", 0.0) or 0.0)
|
|
|
|
obstacles = []
|
|
|
|
obstacles = []
|
|
|
|
for obj in list(getattr(doc, "Objects", []) or []):
|
|
|
|
for obj in list(getattr(doc, "Objects", []) or []):
|
|
|
|
@ -843,24 +878,46 @@ def detect_collisions(points, obstacles):
|
|
|
|
return collisions
|
|
|
|
return collisions
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _detach_object_from_groups(doc, obj):
|
|
|
|
|
|
|
|
parents = list(getattr(obj, "InList", []) or [])
|
|
|
|
|
|
|
|
parents.extend(list(getattr(doc, "Objects", []) or []))
|
|
|
|
|
|
|
|
for parent in parents:
|
|
|
|
|
|
|
|
group = list(getattr(parent, "Group", []) or [])
|
|
|
|
|
|
|
|
if obj not in group:
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
if hasattr(parent, "removeObject"):
|
|
|
|
|
|
|
|
parent.removeObject(obj)
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
parent.Group = [child for child in group if child is not obj]
|
|
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
parent.Group = [child for child in group if child is not obj]
|
|
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _remove_existing_auto_routes(doc, start_uuid, end_uuid, wire_uuid=""):
|
|
|
|
def _remove_existing_auto_routes(doc, start_uuid, end_uuid, wire_uuid=""):
|
|
|
|
removed = 0
|
|
|
|
removed = 0
|
|
|
|
for obj in list(WiringObjects.iter_routed_wire_objects(doc)):
|
|
|
|
for obj in list(WiringObjects.iter_routed_wire_objects(doc)):
|
|
|
|
if (getattr(obj, "RouteType", "") or "").strip() != "AutoSuggested":
|
|
|
|
if (getattr(obj, "RouteType", "") or "").strip() != "AutoSuggested":
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
if wire_uuid and (getattr(obj, "QetWireUuid", "") or "").strip() != wire_uuid:
|
|
|
|
if wire_uuid:
|
|
|
|
continue
|
|
|
|
if (getattr(obj, "QetWireUuid", "") or "").strip() != wire_uuid:
|
|
|
|
same_direction = (
|
|
|
|
continue
|
|
|
|
(getattr(obj, "QetStartTerminalUuid", "") or "").strip() == start_uuid
|
|
|
|
else:
|
|
|
|
and (getattr(obj, "QetEndTerminalUuid", "") or "").strip() == end_uuid
|
|
|
|
same_direction = (
|
|
|
|
)
|
|
|
|
(getattr(obj, "QetStartTerminalUuid", "") or "").strip() == start_uuid
|
|
|
|
reverse_direction = (
|
|
|
|
and (getattr(obj, "QetEndTerminalUuid", "") or "").strip() == end_uuid
|
|
|
|
(getattr(obj, "QetStartTerminalUuid", "") or "").strip() == end_uuid
|
|
|
|
)
|
|
|
|
and (getattr(obj, "QetEndTerminalUuid", "") or "").strip() == start_uuid
|
|
|
|
reverse_direction = (
|
|
|
|
)
|
|
|
|
(getattr(obj, "QetStartTerminalUuid", "") or "").strip() == end_uuid
|
|
|
|
if not same_direction and not reverse_direction:
|
|
|
|
and (getattr(obj, "QetEndTerminalUuid", "") or "").strip() == start_uuid
|
|
|
|
continue
|
|
|
|
)
|
|
|
|
|
|
|
|
if not same_direction and not reverse_direction:
|
|
|
|
|
|
|
|
continue
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
|
|
|
|
_detach_object_from_groups(doc, obj)
|
|
|
|
doc.removeObject(obj.Name)
|
|
|
|
doc.removeObject(obj.Name)
|
|
|
|
removed += 1
|
|
|
|
removed += 1
|
|
|
|
except Exception:
|
|
|
|
except Exception:
|
|
|
|
@ -1076,7 +1133,7 @@ def format_terminal_binding_report(report):
|
|
|
|
return message
|
|
|
|
return message
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def route_all_from_payload(doc, payload, options=None):
|
|
|
|
def route_all_from_payload(doc, payload, options=None, prepared_layout=None):
|
|
|
|
if doc is None:
|
|
|
|
if doc is None:
|
|
|
|
raise AutoRoutingError("No FreeCAD document is available.")
|
|
|
|
raise AutoRoutingError("No FreeCAD document is available.")
|
|
|
|
if not isinstance(payload, dict):
|
|
|
|
if not isinstance(payload, dict):
|
|
|
|
@ -1107,6 +1164,8 @@ def route_all_from_payload(doc, payload, options=None):
|
|
|
|
"errors": [],
|
|
|
|
"errors": [],
|
|
|
|
"routes": [],
|
|
|
|
"routes": [],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if isinstance(prepared_layout, dict):
|
|
|
|
|
|
|
|
report["prepared_layout"] = prepared_layout
|
|
|
|
missing_endpoint_uuids = set()
|
|
|
|
missing_endpoint_uuids = set()
|
|
|
|
|
|
|
|
|
|
|
|
for index, item in enumerate(wires):
|
|
|
|
for index, item in enumerate(wires):
|
|
|
|
@ -1256,11 +1315,17 @@ def _clear_auto_route_batch_diagnostics(doc):
|
|
|
|
def _write_auto_route_batch_diagnostic(doc, report):
|
|
|
|
def _write_auto_route_batch_diagnostic(doc, report):
|
|
|
|
if doc is None or not isinstance(report, dict):
|
|
|
|
if doc is None or not isinstance(report, dict):
|
|
|
|
return None
|
|
|
|
return None
|
|
|
|
if not report.get("errors") and not report.get("missing_endpoint_uuids") and report.get("collision_warnings", 0) <= 0:
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
project_uuid = _project_uuid(doc)
|
|
|
|
project_uuid = _project_uuid(doc)
|
|
|
|
group = WiringObjects.ensure_diagnostic_group(doc, project_uuid)
|
|
|
|
group = WiringObjects.ensure_diagnostic_group(doc, project_uuid)
|
|
|
|
_clear_auto_route_batch_diagnostics(doc)
|
|
|
|
_clear_auto_route_batch_diagnostics(doc)
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
|
|
report.get("total_wires", 0) <= 0
|
|
|
|
|
|
|
|
and not report.get("routes")
|
|
|
|
|
|
|
|
and not report.get("errors")
|
|
|
|
|
|
|
|
and not report.get("missing_endpoint_uuids")
|
|
|
|
|
|
|
|
and report.get("collision_warnings", 0) <= 0
|
|
|
|
|
|
|
|
):
|
|
|
|
|
|
|
|
return None
|
|
|
|
diagnostic = doc.addObject("App::DocumentObjectGroup", _unique_name(doc, "QETAutoRouteDiagnostic"))
|
|
|
|
diagnostic = doc.addObject("App::DocumentObjectGroup", _unique_name(doc, "QETAutoRouteDiagnostic"))
|
|
|
|
diagnostic.Label = "QET Auto Route Diagnostic"
|
|
|
|
diagnostic.Label = "QET Auto Route Diagnostic"
|
|
|
|
_set_string(diagnostic, "QetDiagnosticKind", "AutoRouteBatch", "QET diagnostic kind")
|
|
|
|
_set_string(diagnostic, "QetDiagnosticKind", "AutoRouteBatch", "QET diagnostic kind")
|
|
|
|
@ -1317,9 +1382,9 @@ def bind_wire_task_terminals_from_tasks(doc):
|
|
|
|
return bind_wire_task_terminals_from_payload(doc, _wire_tasks_payload(doc))
|
|
|
|
return bind_wire_task_terminals_from_payload(doc, _wire_tasks_payload(doc))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def route_all_tasks(doc, options=None):
|
|
|
|
def route_all_tasks(doc, options=None, prepared_layout=None):
|
|
|
|
payload = _wire_tasks_payload(doc)
|
|
|
|
payload = _wire_tasks_payload(doc)
|
|
|
|
return route_all_from_payload(doc, payload, options=options)
|
|
|
|
return route_all_from_payload(doc, payload, options=options, prepared_layout=prepared_layout)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def prepare_eplan_style_layout(doc, project_uuid="", options=None):
|
|
|
|
def prepare_eplan_style_layout(doc, project_uuid="", options=None):
|
|
|
|
@ -1356,6 +1421,7 @@ def clear_auto_routes(doc):
|
|
|
|
if (getattr(obj, "RouteType", "") or "").strip() != "AutoSuggested":
|
|
|
|
if (getattr(obj, "RouteType", "") or "").strip() != "AutoSuggested":
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
|
|
|
|
_detach_object_from_groups(doc, obj)
|
|
|
|
doc.removeObject(obj.Name)
|
|
|
|
doc.removeObject(obj.Name)
|
|
|
|
removed += 1
|
|
|
|
removed += 1
|
|
|
|
except Exception:
|
|
|
|
except Exception:
|
|
|
|
@ -1398,10 +1464,9 @@ class CommandAutoRouteAll:
|
|
|
|
|
|
|
|
|
|
|
|
payload = getattr(App, "_qet_exchange_payload", None)
|
|
|
|
payload = getattr(App, "_qet_exchange_payload", None)
|
|
|
|
if isinstance(payload, dict) and payload.get("wires"):
|
|
|
|
if isinstance(payload, dict) and payload.get("wires"):
|
|
|
|
report = route_all_from_payload(doc, payload)
|
|
|
|
report = route_all_from_payload(doc, payload, prepared_layout=prepared_layout)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
report = route_all_tasks(doc)
|
|
|
|
report = route_all_tasks(doc, prepared_layout=prepared_layout)
|
|
|
|
report["prepared_layout"] = prepared_layout
|
|
|
|
|
|
|
|
if report.get("total_wires", 0) <= 0:
|
|
|
|
if report.get("total_wires", 0) <= 0:
|
|
|
|
_console_error("没有导线任务。一键自动布线需要 QET wires[] 或 QETWiring_01_Tasks。")
|
|
|
|
_console_error("没有导线任务。一键自动布线需要 QET wires[] 或 QETWiring_01_Tasks。")
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|