using System;
using System.Collections.Generic;
using SolidWorks.Interop.sldworks;
using SolidWorks.Interop.swconst;
namespace tools
{
/// <summary>
/// 遍历装配体及其子装配体特征树,删除配合特征(<see cref="Mate2"/>)且 <see cref="Feature.GetErrorCode2"/> 非零的配合。
/// </summary>
public static class delete_broken_mates
{
private sealed class ScanStats
{
public int AssemblyCount;
public int FeatureCount;
public int MateCount;
public int SuppressedMateCount;
public int BrokenMateCount;
public int NonMateErrorCount;
}
public static int Run(SldWorks swApp, ModelDoc2 swModel)
{
if (swApp == null || swModel == null)
{
Console.WriteLine("[delete_broken_mates] swApp/swModel 为空,退出。");
return 0;
}
if (swModel.GetType() != (int)swDocumentTypes_e.swDocASSEMBLY)
{
Console.WriteLine("[delete_broken_mates] 当前文档不是装配体,退出。");
return 0;
}
string docPath = "";
try { docPath = swModel.GetPathName() ?? ""; } catch { /* ignore */ }
string docTitle = "";
try { docTitle = swModel.GetTitle() ?? ""; } catch { /* ignore */ }
Console.WriteLine($"[delete_broken_mates] 开始扫描装配体(含子装配体): {docTitle} ({docPath})");
string originalActiveTitle = "";
try { originalActiveTitle = (swApp.ActiveDoc as ModelDoc2)?.GetTitle()?.Trim() ?? docTitle; }
catch { originalActiveTitle = docTitle; }
var stats = new ScanStats();
int deleted = 0;
deleted += ScanAndDeleteInDocument(swModel, "根装配体", stats);
var processedKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
string rootKey = GetDocumentKey(swModel);
if (!string.IsNullOrEmpty(rootKey))
{
processedKeys.Add(rootKey);
}
foreach ((ModelDoc2 subDoc, string label) in CollectSubAssemblyDocuments((AssemblyDoc)swModel, processedKeys))
{
ModelDoc2? workDoc = TryActivateAssemblyDocument(swApp, subDoc, label);
if (workDoc == null)
{
Console.WriteLine($"[delete_broken_mates] 无法激活子装配体文档,跳过: {label}");
continue;
}
deleted += ScanAndDeleteInDocument(workDoc, label, stats);
}
RestoreActiveDocument(swApp, originalActiveTitle);
Console.WriteLine(
$"[delete_broken_mates] 全部完成: 扫描装配体 {stats.AssemblyCount} 个, " +
$"特征 {stats.FeatureCount} 个, 配合 {stats.MateCount} 个(抑制 {stats.SuppressedMateCount} 个), " +
$"报错配合 {stats.BrokenMateCount} 个, 非配合报错特征 {stats.NonMateErrorCount} 个, " +
$"实际删除 {deleted} 个。");
return deleted;
}
private static int ScanAndDeleteInDocument(ModelDoc2 doc, string contextLabel, ScanStats stats)
{
stats.AssemblyCount++;
Console.WriteLine($"[delete_broken_mates] ── 扫描 {contextLabel} ──");
Console.WriteLine("[delete_broken_mates] 执行 EditRebuild3...");
doc.EditRebuild3();
var toDelete = new List<Feature>();
Feature root = (Feature)doc.FirstFeature();
CollectBrokenMatesRecursive(root, toDelete, stats, depth: 0);
Console.WriteLine($"[delete_broken_mates] {contextLabel} 扫描: 报错配合 {toDelete.Count} 个");
return DeleteMateFeatures(doc, toDelete, contextLabel);
}
private static List<(ModelDoc2 doc, string label)> CollectSubAssemblyDocuments(
AssemblyDoc rootAsm,
HashSet<string> processedKeys)
{
var result = new List<(ModelDoc2, string)>();
object[]? raw = rootAsm.GetComponents(false) as object[];
if (raw == null)
{
return result;
}
foreach (object obj in raw)
{
if (obj is not Component2 comp)
{
continue;
}
if (SafeIsSuppressed(comp))
{
continue;
}
TryResolveLightweightComponent(comp);
ModelDoc2? subDoc = TryGetAssemblyDoc(comp);
if (subDoc == null)
{
continue;
}
string key = GetDocumentKey(subDoc);
if (string.IsNullOrEmpty(key) || !processedKeys.Add(key))
{
continue;
}
result.Add((subDoc, BuildComponentLabel(comp, subDoc)));
}
return result;
}
private static ModelDoc2? TryActivateAssemblyDocument(SldWorks swApp, ModelDoc2 doc, string contextLabel)
{
string path = GetAssemblyPath(doc);
string title = "";
try { title = doc.GetTitle()?.Trim() ?? ""; } catch { /* ignore */ }
ModelDoc2? target = doc;
if (!string.IsNullOrEmpty(path))
{
target = swApp.GetOpenDocumentByName(path) as ModelDoc2 ?? target;
}
if (!string.IsNullOrEmpty(title))
{
target = swApp.GetOpenDocumentByName(title) as ModelDoc2 ?? target;
}
if (target == null)
{
return null;
}
if (!string.IsNullOrEmpty(path)
&& string.IsNullOrEmpty(GetAssemblyPath(target))
&& System.IO.File.Exists(path))
{
int openErrors = 0;
int openWarnings = 0;
ModelDoc2? opened = swApp.OpenDoc6(
path,
(int)swDocumentTypes_e.swDocASSEMBLY,
(int)swOpenDocOptions_e.swOpenDocOptions_Silent,
string.Empty,
ref openErrors,
ref openWarnings) as ModelDoc2;
if (opened != null)
{
target = opened;
Console.WriteLine($"[delete_broken_mates] 已静默打开子装配体: {contextLabel}");
}
else
{
Console.WriteLine(
$"[delete_broken_mates] OpenDoc6 失败 [{contextLabel}] errors={openErrors} warnings={openWarnings}");
}
}
string activateTitle = "";
try { activateTitle = target.GetTitle()?.Trim() ?? title; } catch { activateTitle = title; }
if (string.IsNullOrEmpty(activateTitle))
{
Console.WriteLine($"[delete_broken_mates] 子装配体无标题,跳过: {contextLabel}");
return null;
}
int activateErr = 0;
swApp.ActivateDoc3(
activateTitle,
false,
(int)swRebuildOnActivation_e.swDontRebuildActiveDoc,
ref activateErr);
ModelDoc2? active = swApp.ActiveDoc as ModelDoc2;
if (active != null && IsSameDocument(active, target))
{
Console.WriteLine($"[delete_broken_mates] 已激活子装配体: {contextLabel}");
return active;
}
Console.WriteLine(
$"[delete_broken_mates] ActivateDoc3 后活动文档不匹配 [{contextLabel}] activateErr={activateErr}");
return target;
}
private static void RestoreActiveDocument(SldWorks swApp, string originalTitle)
{
if (string.IsNullOrWhiteSpace(originalTitle))
{
return;
}
try
{
int activateErr = 0;
swApp.ActivateDoc3(
originalTitle.Trim(),
false,
(int)swRebuildOnActivation_e.swDontRebuildActiveDoc,
ref activateErr);
}
catch (Exception ex)
{
Console.WriteLine($"[delete_broken_mates] 恢复活动文档失败: {ex.Message}");
}
}
private static bool IsSameDocument(ModelDoc2 a, ModelDoc2 b)
{
if (a == b)
{
return true;
}
string pathA = GetAssemblyPath(a);
string pathB = GetAssemblyPath(b);
if (!string.IsNullOrEmpty(pathA) && !string.IsNullOrEmpty(pathB))
{
return pathA.Equals(pathB, StringComparison.OrdinalIgnoreCase);
}
string titleA = "";
string titleB = "";
try { titleA = a.GetTitle()?.Trim() ?? ""; } catch { /* ignore */ }
try { titleB = b.GetTitle()?.Trim() ?? ""; } catch { /* ignore */ }
return !string.IsNullOrEmpty(titleA)
&& titleA.Equals(titleB, StringComparison.OrdinalIgnoreCase);
}
private static string GetDocumentKey(ModelDoc2 doc)
{
string path = GetAssemblyPath(doc);
if (!string.IsNullOrEmpty(path))
{
return path;
}
try { return doc.GetTitle()?.Trim() ?? ""; }
catch { return ""; }
}
private static int DeleteMateFeatures(ModelDoc2 doc, List<Feature> toDelete, string contextLabel)
{
doc.ClearSelection2(true);
int deleted = 0;
foreach (Feature feat in toDelete)
{
try
{
if (feat == null)
{
continue;
}
string featName = feat.Name ?? "";
doc.ClearSelection2(true);
bool sel = feat.Select2(false, 0);
if (!sel)
{
Console.WriteLine(
$"[delete_broken_mates] [{contextLabel}] 无法选中配合特征,跳过: {featName}");
continue;
}
doc.EditDelete();
deleted++;
Console.WriteLine($"[delete_broken_mates] [{contextLabel}] 已删除配合: {featName}");
}
catch (Exception ex)
{
Console.WriteLine(
$"[delete_broken_mates] [{contextLabel}] 删除配合失败: {ex.Message}");
}
finally
{
doc.ClearSelection2(true);
}
}
return deleted;
}
private static ModelDoc2? TryGetAssemblyDoc(Component2 comp)
{
try
{
ModelDoc2? doc = comp.GetModelDoc2() as ModelDoc2;
if (doc == null)
{
return null;
}
return doc.GetType() == (int)swDocumentTypes_e.swDocASSEMBLY ? doc : null;
}
catch
{
return null;
}
}
private static void TryResolveLightweightComponent(Component2? component)
{
if (component == null)
{
return;
}
try
{
int sup = component.GetSuppression();
if (sup == (int)swComponentSuppressionState_e.swComponentLightweight)
{
component.SetSuppression2((int)swComponentSuppressionState_e.swComponentFullyResolved);
}
}
catch (Exception ex)
{
Console.WriteLine(
$"[delete_broken_mates] 轻化还原失败 [{component.Name2}]: {ex.Message}");
}
}
private static bool SafeIsSuppressed(Component2 comp)
{
try { return comp.IsSuppressed(); }
catch { return true; }
}
private static string GetAssemblyPath(ModelDoc2 asmDoc)
{
try { return asmDoc.GetPathName()?.Trim() ?? ""; }
catch { return ""; }
}
private static string BuildComponentLabel(Component2 comp, ModelDoc2 asmDoc)
{
string compName = "";
try { compName = comp.Name2?.Trim() ?? ""; } catch { /* ignore */ }
string path = GetAssemblyPath(asmDoc);
if (string.IsNullOrEmpty(path))
{
return string.IsNullOrEmpty(compName) ? "子装配体" : compName;
}
return string.IsNullOrEmpty(compName)
? path
: $"{compName} ({path})";
}
private static void CollectBrokenMatesRecursive(Feature feat, List<Feature> acc, ScanStats stats, int depth)
{
Feature f = feat;
while (f != null)
{
stats.FeatureCount++;
TryAddBrokenMate(f, acc, stats, depth);
Feature sub = (Feature)f.GetFirstSubFeature();
if (sub != null)
{
CollectBrokenMatesRecursive(sub, acc, stats, depth + 1);
}
f = depth == 0
? (Feature)f.GetNextFeature()
: (Feature)f.GetNextSubFeature();
}
}
private static void TryAddBrokenMate(Feature feat, List<Feature> acc, ScanStats stats, int depth)
{
try
{
string featName = feat.Name ?? "";
string featType = "";
try { featType = feat.GetTypeName2() ?? ""; } catch { /* ignore */ }
string indent = new string(' ', Math.Min(depth, 8) * 2);
bool suppressed = false;
try { suppressed = feat.IsSuppressed(); } catch { /* ignore */ }
int errCode = 0;
try { errCode = feat.GetErrorCode2(out _); } catch { errCode = 0; }
object? spec = null;
try { spec = feat.GetSpecificFeature2(); } catch { /* ignore */ }
if (spec is Mate2 mate)
{
stats.MateCount++;
string mateType = "";
try { mateType = ((swMateType_e)mate.Type).ToString(); } catch { /* ignore */ }
if (suppressed)
{
stats.SuppressedMateCount++;
Console.WriteLine(
$"{indent}[配合·抑制] {featName} type={mateType} err={errCode}");
return;
}
Console.WriteLine(
$"{indent}[配合] {featName} type={mateType} err={errCode} featType={featType}");
if (errCode != 0)
{
stats.BrokenMateCount++;
acc.Add(feat);
Console.WriteLine($"{indent} → 标记为报错配合 (err={errCode})");
}
return;
}
if (errCode != 0)
{
stats.NonMateErrorCount++;
Console.WriteLine(
$"{indent}[非配合·报错] {featName} featType={featType} err={errCode}");
}
}
catch (Exception ex)
{
Console.WriteLine($"[delete_broken_mates] 扫描特征异常: {ex.Message}");
}
}
}
}