From 468b3ef0e685287424d9fa730496dd0a316a4b3a Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Fri, 12 Sep 2025 03:21:29 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E5=88=86=E5=B1=82?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E7=9A=84bug=EF=BC=8C=E6=8F=90=E9=AB=98?= =?UTF-8?q?=E4=BA=86=E6=80=A7=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Core/ModelSplitterManager.cs | 412 +++++++++++++----- src/Core/VisibilityManager.cs | 3 +- .../WPF/Commands/LayerManagementCommands.cs | 3 +- src/UI/WPF/Models/SplitPreviewItem.cs | 6 + .../ViewModels/LayerManagementViewModel.cs | 3 +- 5 files changed, 322 insertions(+), 105 deletions(-) diff --git a/src/Core/ModelSplitterManager.cs b/src/Core/ModelSplitterManager.cs index 68ebfdd..e9258a7 100644 --- a/src/Core/ModelSplitterManager.cs +++ b/src/Core/ModelSplitterManager.cs @@ -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 /// private List PreviewSplitWithSmartTraversal(SplitConfiguration config, IGroupingStrategy strategy) { - var results = new List(); + // 使用字典按楼层名分组,自动合并同名楼层 + var layerGroups = new Dictionary(); try { @@ -1089,7 +1090,7 @@ namespace NavisworksTransport if (document?.Models?.Count == 0) { LogManager.Warning("[SimplifiedModelSplitter] 当前文档没有模型"); - return results; + return new List(); } // 从每个模型的一级节点开始智能遍历 @@ -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(layerGroups.Values); + + // 记录每个分层的详细信息 + foreach (var layer in results) + { + var rootNodes = layer.Metadata["RootNodes"] as List; + LogManager.Info($"[分层管理器] 楼层 '{layer.LayerName}': {rootNodes?.Count ?? 0} 个分支, {layer.Items.Count} 个节点"); + } + + return results; } catch (Exception ex) { LogManager.Error($"[分层管理器] 智能遍历失败: {ex.Message}", ex); throw; } - - return results; } /// - /// 递归遍历节点并记录分层结果的核心方法 + /// 递归遍历节点并记录分层结果的核心方法(支持同名楼层合并) /// private void TraverseAndRecord(ModelItem node, IGroupingStrategy strategy, - SplitConfiguration config, List results, int currentDepth) + SplitConfiguration config, Dictionary 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 + { + ["RootNodes"] = new List(), // 存储多个根节点 + ["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; + 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 } } + /// + /// 展开节点树,收集所有子节点 + /// + private ModelItemCollection ExpandNodeTree(ModelItem rootNode) + { + var allNodes = new ModelItemCollection(); + allNodes.Add(rootNode); + + // 使用队列进行广度优先遍历,收集所有子节点 + var nodesToProcess = new Queue(); + 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; + } + /// /// 创建轻量化分层预览结果 - 只包含必要信息,不做统计计算 /// @@ -1177,12 +1279,55 @@ namespace NavisworksTransport /// 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(); + 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 { ["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 /// /// 处理单个分层(异步版本) /// - 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 /// /// 处理单个分层(同步版本) /// - 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(); + // 直接使用预览结果中的项目,这些已经是展开的完整节点树 + // 不添加任何祖先路径,让 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; } } + + /// + /// 递归处理节点可见性 - 精细化控制每个节点 + /// + private void ProcessNodeVisibility(ModelItem node, HashSet 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}"); + } + // 不需要继续递归子节点,因为整个分支都隐藏了 + } + } + } + + /// + /// 检查节点是否有需要导出的后代 + /// + private bool HasExportDescendant(ModelItem node, HashSet exportItemsSet) + { + if (node?.Children == null || node.Children.Count() == 0) + return false; + + // 使用队列进行广度优先搜索,避免深度递归 + var queue = new Queue(); + 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; + } /// /// 保存当前可见性状态(参考"保存当前选择项"的方法) diff --git a/src/Core/VisibilityManager.cs b/src/Core/VisibilityManager.cs index a93dd03..8dd82f1 100644 --- a/src/Core/VisibilityManager.cs +++ b/src/Core/VisibilityManager.cs @@ -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}"; } /// diff --git a/src/UI/WPF/Commands/LayerManagementCommands.cs b/src/UI/WPF/Commands/LayerManagementCommands.cs index 22aaf2a..cdee8dd 100644 --- a/src/UI/WPF/Commands/LayerManagementCommands.cs +++ b/src/UI/WPF/Commands/LayerManagementCommands.cs @@ -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} 个分层"); diff --git a/src/UI/WPF/Models/SplitPreviewItem.cs b/src/UI/WPF/Models/SplitPreviewItem.cs index d86a0fd..aaf2bfa 100644 --- a/src/UI/WPF/Models/SplitPreviewItem.cs +++ b/src/UI/WPF/Models/SplitPreviewItem.cs @@ -110,6 +110,12 @@ namespace NavisworksTransport.UI.WPF.Models /// public ModelItemCollection Items { get; set; } = new ModelItemCollection(); + /// + /// 元数据字典 + /// 存储额外信息,如RootNodes等优化数据 + /// + public System.Collections.Generic.Dictionary Metadata { get; set; } = new System.Collections.Generic.Dictionary(); + public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) diff --git a/src/UI/WPF/ViewModels/LayerManagementViewModel.cs b/src/UI/WPF/ViewModels/LayerManagementViewModel.cs index 7771fc1..10025ad 100644 --- a/src/UI/WPF/ViewModels/LayerManagementViewModel.cs +++ b/src/UI/WPF/ViewModels/LayerManagementViewModel.cs @@ -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}");