修改了分层导出的bug,提高了性能。

This commit is contained in:
tian 2025-09-12 03:21:29 +08:00
parent c098fb9b1f
commit 468b3ef0e6
5 changed files with 322 additions and 105 deletions

View File

@ -690,7 +690,7 @@ namespace NavisworksTransport
OnStatusChanged("正在准备分层...");
// 优先从缓存获取分层结果,避免重复检测
string cacheKey = GenerateCacheKey(config);
string cacheKey = GenerateSmartTraversalCacheKey(config);
var previewResults = GetFromCache(config.Strategy, cacheKey);
if (previewResults == null || previewResults.Count == 0)
@ -741,7 +741,7 @@ namespace NavisworksTransport
try
{
// 实现具体的分层保存逻辑
await ProcessSingleLayerAsync(preview, config, cancellationToken);
await ProcessSingleLayerAsync(preview, config, cancellationToken, i + 1, layersToSave.Count);
successCount++;
LogManager.Info($"[分层管理器] 分层 {preview.LayerName} 处理成功 ({successCount}/{layersToSave.Count})");
}
@ -802,7 +802,7 @@ namespace NavisworksTransport
OnStatusChanged("正在准备分层...");
// 优先从缓存获取分层结果,避免重复检测
string cacheKey = GenerateCacheKey(config);
string cacheKey = GenerateSmartTraversalCacheKey(config);
var previewResults = GetFromCache(config.Strategy, cacheKey);
if (previewResults == null || previewResults.Count == 0)
@ -847,7 +847,7 @@ namespace NavisworksTransport
try
{
// 同步处理单个分层
ProcessSingleLayer(preview, config);
ProcessSingleLayer(preview, config, i + 1, layersToSave.Count);
successCount++;
LogManager.Info($"[分层管理器] 分层 {preview.LayerName} 处理成功 ({successCount}/{layersToSave.Count})");
}
@ -1078,7 +1078,8 @@ namespace NavisworksTransport
/// </summary>
private List<SplitPreviewResult> PreviewSplitWithSmartTraversal(SplitConfiguration config, IGroupingStrategy strategy)
{
var results = new List<SplitPreviewResult>();
// 使用字典按楼层名分组,自动合并同名楼层
var layerGroups = new Dictionary<string, SplitPreviewResult>();
try
{
@ -1089,7 +1090,7 @@ namespace NavisworksTransport
if (document?.Models?.Count == 0)
{
LogManager.Warning("[SimplifiedModelSplitter] 当前文档没有模型");
return results;
return new List<SplitPreviewResult>();
}
// 从每个模型的一级节点开始智能遍历
@ -1101,34 +1102,47 @@ namespace NavisworksTransport
foreach (ModelItem rootChild in model.RootItem.Children)
{
processedTopNodes++;
TraverseAndRecord(rootChild, strategy, config, results, 1);
// 统一使用 TraverseAndRecord 处理所有节点
// 它会自动检测节点是否有楼层属性并正确处理
TraverseAndRecord(rootChild, strategy, config, layerGroups, 1);
// 每处理10个顶级节点记录一次进度
if (processedTopNodes % 10 == 0)
{
LogManager.Info($"[分层管理器] 已处理 {processedTopNodes} 个顶级节点,找到 {results.Count} 个分层");
LogManager.Info($"[分层管理器] 已处理 {processedTopNodes} 个顶级节点,找到 {layerGroups.Count} 个分层");
}
}
}
}
LogManager.Info($"[分层管理器] ========== 智能遍历完成 ==========");
LogManager.Info($"[分层管理器] 处理了 {processedTopNodes} 个顶级节点,识别到 {results.Count} 个{strategy.GroupTypeName}分层");
LogManager.Info($"[分层管理器] 处理了 {processedTopNodes} 个顶级节点,识别到 {layerGroups.Count} 个{strategy.GroupTypeName}分层");
// 将字典转换为列表返回
var results = new List<SplitPreviewResult>(layerGroups.Values);
// 记录每个分层的详细信息
foreach (var layer in results)
{
var rootNodes = layer.Metadata["RootNodes"] as List<ModelItem>;
LogManager.Info($"[分层管理器] 楼层 '{layer.LayerName}': {rootNodes?.Count ?? 0} 个分支, {layer.Items.Count} 个节点");
}
return results;
}
catch (Exception ex)
{
LogManager.Error($"[分层管理器] 智能遍历失败: {ex.Message}", ex);
throw;
}
return results;
}
/// <summary>
/// 递归遍历节点并记录分层结果的核心方法
/// 递归遍历节点并记录分层结果的核心方法(支持同名楼层合并)
/// </summary>
private void TraverseAndRecord(ModelItem node, IGroupingStrategy strategy,
SplitConfiguration config, List<SplitPreviewResult> results, int currentDepth)
SplitConfiguration config, Dictionary<string, SplitPreviewResult> layerGroups, int currentDepth)
{
try
{
@ -1146,11 +1160,54 @@ namespace NavisworksTransport
LogManager.Info($"[分层管理器] 智能检测到属性类型: '{attributeName}', 值: '{attributeValue}'");
}
// 创建轻量化预览结果,停止该分支遍历
var layerResult = CreateLayerResult(layerValue, node, strategy.GroupTypeName, config.Strategy, detectedAttributeName);
results.Add(layerResult);
// 清理楼层名称
string sanitizedLayerName = SanitizeLayerName(layerValue);
// 检查是否已存在该楼层的结果
if (!layerGroups.ContainsKey(sanitizedLayerName))
{
// 第一次遇到这个楼层名,创建新结果
layerGroups[sanitizedLayerName] = new SplitPreviewResult
{
LayerName = sanitizedLayerName,
LayerAttribute = GetAttributeDescription(config.Strategy, layerValue, null),
Items = new ModelItemCollection(),
Metadata = new Dictionary<string, object>
{
["RootNodes"] = new List<ModelItem>(), // 存储多个根节点
["BranchCount"] = 0,
["GroupType"] = strategy.GroupTypeName,
["TraversalMode"] = "SmartTraversal",
["DetectedAttributeName"] = detectedAttributeName
}
};
LogManager.Info($"[分层管理器] 创建新楼层分组: '{sanitizedLayerName}'");
}
// 获取现有楼层结果
var layerResult = layerGroups[sanitizedLayerName];
// 展开并添加当前节点的所有子节点
LogManager.Info($"[分层管理器] 开始展开节点树: {node.DisplayName}");
var expandedNodes = ExpandNodeTree(node);
LogManager.Info($"[分层管理器] 节点树展开完成,共 {expandedNodes.Count} 个节点");
// 添加到楼层的节点集合
foreach (var item in expandedNodes)
{
layerResult.Items.Add(item);
}
// 记录这个分支的根节点
var rootNodes = layerResult.Metadata["RootNodes"] as List<ModelItem>;
rootNodes.Add(node);
// 更新分支计数和节点计数
layerResult.Metadata["BranchCount"] = rootNodes.Count;
layerResult.Metadata["ExpandedNodeCount"] = layerResult.Items.Count;
LogManager.Info($"[分层管理器] 合并到楼层 '{sanitizedLayerName}',现有 {rootNodes.Count} 个分支,共 {layerResult.Items.Count} 个节点");
LogManager.Info($"[分层管理器] 找到{strategy.GroupTypeName}分层: '{layerValue}' 于节点 '{node.DisplayName}' (深度{currentDepth})");
return; // 关键:停止遍历该分支
}
@ -1159,7 +1216,7 @@ namespace NavisworksTransport
{
foreach (ModelItem child in node.Children)
{
TraverseAndRecord(child, strategy, config, results, currentDepth + 1);
TraverseAndRecord(child, strategy, config, layerGroups, currentDepth + 1);
}
}
}
@ -1169,6 +1226,51 @@ namespace NavisworksTransport
}
}
/// <summary>
/// 展开节点树,收集所有子节点
/// </summary>
private ModelItemCollection ExpandNodeTree(ModelItem rootNode)
{
var allNodes = new ModelItemCollection();
allNodes.Add(rootNode);
// 使用队列进行广度优先遍历,收集所有子节点
var nodesToProcess = new Queue<ModelItem>();
nodesToProcess.Enqueue(rootNode);
int totalNodes = 1; // 包括根节点
try
{
while (nodesToProcess.Count > 0)
{
var current = nodesToProcess.Dequeue();
// 遍历当前节点的所有子节点
if (current.Children != null && current.Children.Count() > 0)
{
foreach (ModelItem child in current.Children)
{
allNodes.Add(child);
nodesToProcess.Enqueue(child);
totalNodes++;
// 每处理1000个节点记录一次进度
if (totalNodes % 1000 == 0)
{
LogManager.Info($"[分层管理器] 已展开 {totalNodes} 个节点...");
}
}
}
}
}
catch (Exception ex)
{
LogManager.Warning($"[分层管理器] 展开节点树时出现异常: {ex.Message},将使用部分展开的结果");
}
return allNodes;
}
/// <summary>
/// 创建轻量化分层预览结果 - 只包含必要信息,不做统计计算
/// </summary>
@ -1177,12 +1279,55 @@ namespace NavisworksTransport
/// </summary>
private SplitPreviewResult CreateLayerResult(string layerValue, ModelItem rootNode, string groupTypeName, SplitStrategy strategy, string detectedAttributeName = null)
{
// 性能优化:展开并缓存完整的节点树,避免导出时重复遍历
var allNodes = new ModelItemCollection();
allNodes.Add(rootNode);
// 使用队列进行广度优先遍历,收集所有子节点
var nodesToProcess = new Queue<ModelItem>();
nodesToProcess.Enqueue(rootNode);
int totalNodes = 1; // 包括根节点
LogManager.Info($"[分层管理器] 开始展开节点树: {rootNode.DisplayName}");
try
{
while (nodesToProcess.Count > 0)
{
var current = nodesToProcess.Dequeue();
// 遍历当前节点的所有子节点
if (current.Children != null && current.Children.Count() > 0)
{
foreach (ModelItem child in current.Children)
{
allNodes.Add(child);
nodesToProcess.Enqueue(child);
totalNodes++;
// 每处理1000个节点记录一次进度
if (totalNodes % 1000 == 0)
{
LogManager.Info($"[分层管理器] 已展开 {totalNodes} 个节点...");
}
}
}
}
}
catch (Exception ex)
{
LogManager.Warning($"[分层管理器] 展开节点树时出现异常: {ex.Message},将使用部分展开的结果");
}
LogManager.Info($"[分层管理器] 节点树展开完成,共 {totalNodes} 个节点");
var metadata = new Dictionary<string, object>
{
["LayerValue"] = layerValue,
["RootNodeReference"] = rootNode,
["GroupType"] = groupTypeName,
["TraversalMode"] = "SmartTraversal" // 标记使用智能遍历
["TraversalMode"] = "SmartTraversal",
["ExpandedNodeCount"] = totalNodes // 记录展开的节点数量,便于性能监控
};
// 如果检测到了具体的属性类型,保存到元数据中
@ -1195,7 +1340,7 @@ namespace NavisworksTransport
{
LayerName = SanitizeLayerName(layerValue),
LayerAttribute = GetAttributeDescription(strategy, layerValue, metadata),
Items = new ModelItemCollection { rootNode }, // 只保存根节点引用
Items = allNodes, // 保存完整的节点集合,而不只是根节点
Metadata = metadata
};
}
@ -1203,7 +1348,7 @@ namespace NavisworksTransport
/// <summary>
/// 处理单个分层(异步版本)
/// </summary>
private async Task ProcessSingleLayerAsync(SplitPreviewResult preview, SplitConfiguration config, CancellationToken cancellationToken)
private async Task ProcessSingleLayerAsync(SplitPreviewResult preview, SplitConfiguration config, CancellationToken cancellationToken, int currentIndex = 0, int totalCount = 0)
{
try
{
@ -1241,7 +1386,7 @@ namespace NavisworksTransport
/// <summary>
/// 处理单个分层(同步版本)
/// </summary>
private void ProcessSingleLayer(SplitPreviewResult preview, SplitConfiguration config)
private void ProcessSingleLayer(SplitPreviewResult preview, SplitConfiguration config, int currentIndex = 0, int totalCount = 0)
{
try
{
@ -1272,22 +1417,46 @@ namespace NavisworksTransport
LogManager.Info($"[分层管理器] 垃圾回收完成,当前内存: {GC.GetTotalMemory(false) / 1024 / 1024} MB");
}
// 更新状态:正在导出
string progressInfo = (currentIndex > 0 && totalCount > 0) ? $" ({currentIndex}/{totalCount})" : "";
OnStatusChanged($"正在导出{progressInfo}: {preview.LayerName}...");
// 使用TryExportToNwd API导出分层
bool success = ExportLayerToNwd(preview, outputPath, config);
if (success)
{
LogManager.Info($"[分层管理器] 分层 {preview.LayerName} 导出成功: {outputPath}");
// 获取文件大小信息
string fileSizeInfo = "";
try
{
if (File.Exists(outputPath))
{
var fileInfo = new FileInfo(outputPath);
double sizeMB = fileInfo.Length / (1024.0 * 1024.0);
fileSizeInfo = $" ({sizeMB:F1}MB)";
}
}
catch { }
// 更新状态:导出成功
string successProgressInfo = (currentIndex > 0 && totalCount > 0) ? $" ({currentIndex}/{totalCount})" : "";
OnStatusChanged($"导出成功{successProgressInfo}: {preview.LayerName}{fileSizeInfo}");
}
else
{
LogManager.Error($"[分层管理器] 分层 {preview.LayerName} 导出失败");
string failProgressInfo = (currentIndex > 0 && totalCount > 0) ? $" ({currentIndex}/{totalCount})" : "";
OnStatusChanged($"导出失败{failProgressInfo}: {preview.LayerName}");
throw new InvalidOperationException($"分层 {preview.LayerName} 导出失败");
}
}
catch (Exception ex)
{
LogManager.Error($"[分层管理器] 同步处理分层失败 {preview.LayerName}: {ex.Message}");
OnStatusChanged($"处理失败: {preview.LayerName} - {ex.Message}");
throw;
}
}
@ -1658,98 +1827,34 @@ namespace NavisworksTransport
try
{
// 采用智能隐藏策略:只操作顶级节点,避免遍历所有项目
var allTopLevelItems = new List<ModelItem>();
// 直接使用预览结果中的项目,这些已经是展开的完整节点树
// 不添加任何祖先路径,让 IsolateSpecificItems 自己处理
var itemsToIsolate = preview.Items;
// 只获取顶级节点,不遍历全部项目
foreach (Model model in document.Models)
LogManager.Info($"[分层管理器] 准备隔离显示 {itemsToIsolate.Count} 个节点");
// 使用 VisibilityManager.IsolateSpecificItems 隔离显示
// 这个方法会使用缓存优化,效率更高
var isolateResult = VisibilityManager.IsolateSpecificItems(itemsToIsolate);
if (!isolateResult.Success)
{
foreach (ModelItem topLevelItem in model.RootItem.Children)
{
allTopLevelItems.Add(topLevelItem);
}
throw new InvalidOperationException($"隔离显示失败: {isolateResult.Message}");
}
LogManager.Info($"[分层管理器] 收集到 {allTopLevelItems.Count} 个顶级节点");
// 智能隐藏策略:如果顶级节点包含要导出的项目,则保持可见;否则隐藏整个顶级分支
var itemsToHide = new ModelItemCollection();
int hiddenCount = 0;
foreach (ModelItem topLevelItem in allTopLevelItems)
{
bool shouldKeepTopLevel = false;
// 检查这个顶级节点本身是否在导出列表中
foreach (ModelItem exportItem in itemsToExport)
{
if (topLevelItem.Equals(exportItem))
{
shouldKeepTopLevel = true;
LogManager.Info($"[分层管理器] 顶级节点 {topLevelItem.DisplayName} 本身被选中,保持可见");
break;
}
}
// 如果顶级节点本身不在导出列表中,检查是否有任何导出项是它的后代
if (!shouldKeepTopLevel)
{
foreach (var exportItem in itemsToExport)
{
var current = exportItem;
while (current != null)
{
if (current == topLevelItem)
{
shouldKeepTopLevel = true;
LogManager.Info($"[分层管理器] 顶级节点 {topLevelItem.DisplayName} 包含导出项目,保持可见");
break;
}
current = current.Parent;
}
if (shouldKeepTopLevel) break;
}
}
// 如果这个顶级节点不需要保持可见,则隐藏它
if (!shouldKeepTopLevel)
{
try
{
itemsToHide.Add(topLevelItem);
hiddenCount++;
LogManager.Info($"[分层管理器] 隐藏顶级节点: {topLevelItem.DisplayName}");
}
catch (Exception ex)
{
LogManager.Warning($"[分层管理器] 添加隐藏项目失败: {topLevelItem.DisplayName} - {ex.Message}");
}
}
}
// 执行隐藏操作
if (itemsToHide.Count > 0)
{
try
{
document.Models.SetHidden(itemsToHide, true);
LogManager.Info($"[分层管理器] 成功隐藏 {hiddenCount} 个顶级节点,保留导出项目可见");
}
catch (Exception ex)
{
LogManager.Warning($"[分层管理器] 隐藏操作失败: {ex.Message}");
}
}
LogManager.Info($"[分层管理器] 隔离显示成功: {isolateResult.Message}");
LogManager.Info($"[分层管理器] 隐藏了 {isolateResult.HiddenCount} 个项目,保持 {isolateResult.TotalCount - isolateResult.HiddenCount} 个项目可见");
// 创建导出选项
var exportOptions = new Autodesk.Navisworks.Api.NwdExportOptions
{
ExcludeHiddenItems = true, // 只导出可见项目
EmbedXrefs = false,
PreventObjectPropertyExport = false
EmbedXrefs = config?.ExportOptions?.EmbedXrefs ?? false,
PreventObjectPropertyExport = config?.ExportOptions?.PreventObjectPropertyExport ?? false
};
LogManager.Info($"[分层管理器] 开始调用ExportToNwd API主线程");
LogManager.Info($"[分层管理器] 导出选项: ExcludeHiddenItems=true, EmbedXrefs={exportOptions.EmbedXrefs}, PreventObjectPropertyExport={exportOptions.PreventObjectPropertyExport}");
// 在主线程中执行导出
document.ExportToNwd(outputPath, exportOptions);
@ -1795,6 +1900,109 @@ namespace NavisworksTransport
return false;
}
}
/// <summary>
/// 递归处理节点可见性 - 精细化控制每个节点
/// </summary>
private void ProcessNodeVisibility(ModelItem node, HashSet<ModelItem> exportItemsSet,
ModelItemCollection itemsToHide,
ref int processedCount, ref int hiddenCount)
{
if (node == null) return;
processedCount++;
// 检查当前节点是否在导出集合中
bool shouldExport = exportItemsSet.Contains(node);
if (shouldExport)
{
// 这个节点要导出,不隐藏
// 继续处理其子节点(可能有些子节点不需要导出)
if (node.Children != null)
{
foreach (ModelItem child in node.Children)
{
ProcessNodeVisibility(child, exportItemsSet, itemsToHide,
ref processedCount, ref hiddenCount);
}
}
}
else
{
// 检查是否有任何子节点需要导出
bool hasExportChild = HasExportDescendant(node, exportItemsSet);
if (hasExportChild)
{
// 有子节点需要导出,继续递归处理子节点
// 但不隐藏当前节点(因为需要保持结构)
if (node.Children != null)
{
foreach (ModelItem child in node.Children)
{
ProcessNodeVisibility(child, exportItemsSet, itemsToHide,
ref processedCount, ref hiddenCount);
}
}
}
else
{
// 当前节点及其所有子节点都不需要导出,隐藏整个分支
try
{
itemsToHide.Add(node);
hiddenCount++;
if (hiddenCount <= 10 || hiddenCount % 1000 == 0)
{
LogManager.Debug($"[分层管理器] 隐藏节点: {node.DisplayName}");
}
}
catch (Exception ex)
{
LogManager.Warning($"[分层管理器] 添加隐藏节点失败: {ex.Message}");
}
// 不需要继续递归子节点,因为整个分支都隐藏了
}
}
}
/// <summary>
/// 检查节点是否有需要导出的后代
/// </summary>
private bool HasExportDescendant(ModelItem node, HashSet<ModelItem> exportItemsSet)
{
if (node?.Children == null || node.Children.Count() == 0)
return false;
// 使用队列进行广度优先搜索,避免深度递归
var queue = new Queue<ModelItem>();
foreach (ModelItem child in node.Children)
{
queue.Enqueue(child);
}
while (queue.Count > 0)
{
var current = queue.Dequeue();
if (exportItemsSet.Contains(current))
{
return true; // 找到需要导出的后代
}
if (current.Children != null)
{
foreach (ModelItem child in current.Children)
{
queue.Enqueue(child);
}
}
}
return false;
}
/// <summary>
/// 保存当前可见性状态(参考"保存当前选择项"的方法)

View File

@ -186,7 +186,8 @@ namespace NavisworksTransport
private static string GenerateDocumentId(Document document)
{
if (document == null) return "empty";
return $"{document.FileName}_{document.Models?.Count ?? 0}_{document.CurrentSelection?.SelectedItems?.Count ?? 0}";
// 移除选中项计数,只保留文件名和模型数(选中项变化不应触发缓存清除)
return $"{document.FileName}_{document.Models?.Count ?? 0}";
}
/// <summary>

View File

@ -430,7 +430,8 @@ namespace NavisworksTransport.UI.WPF.Commands
EstimatedSize = "0 KB", // 不再显示文件大小
Status = "就绪", // 保持UI兼容性
IsSelectedForSave = result.IsSelectedForSave,
Items = result.Items
Items = result.Items,
Metadata = result.Metadata // 复制元数据包含RootNodes等优化信息
}).ToList();
LogManager.Info($"[PreviewSplitCommand] 预览完成,共 {previewItems.Count} 个分层");

View File

@ -110,6 +110,12 @@ namespace NavisworksTransport.UI.WPF.Models
/// </summary>
public ModelItemCollection Items { get; set; } = new ModelItemCollection();
/// <summary>
/// 元数据字典
/// 存储额外信息如RootNodes等优化数据
/// </summary>
public System.Collections.Generic.Dictionary<string, object> Metadata { get; set; } = new System.Collections.Generic.Dictionary<string, object>();
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)

View File

@ -1373,7 +1373,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
LayerName = item.LayerName,
LayerAttribute = item.LayerAttribute,
Items = item.Items,
IsSelectedForSave = item.IsSelectedForSave
IsSelectedForSave = item.IsSelectedForSave,
Metadata = item.Metadata // 重要复制元数据包含RootNodes等优化信息
}).ToList();
LogManager.Info($"[LayerManagementViewModel] 开始执行分层,输出目录: {selectedDirectory}");