diff --git a/.serena/cache/csharp/document_symbols_cache_v23-06-25.pkl b/.serena/cache/csharp/document_symbols_cache_v23-06-25.pkl index 0b29118..e1f17f2 100644 Binary files a/.serena/cache/csharp/document_symbols_cache_v23-06-25.pkl and b/.serena/cache/csharp/document_symbols_cache_v23-06-25.pkl differ diff --git a/NavisworksTransportPlugin.csproj b/NavisworksTransportPlugin.csproj index 67c32b8..fb302f4 100644 --- a/NavisworksTransportPlugin.csproj +++ b/NavisworksTransportPlugin.csproj @@ -218,7 +218,6 @@ - diff --git a/src/Core/ModelSplitterManager.cs b/src/Core/ModelSplitterManager.cs index b6e4897..0ca0d79 100644 --- a/src/Core/ModelSplitterManager.cs +++ b/src/Core/ModelSplitterManager.cs @@ -139,7 +139,6 @@ namespace NavisworksTransport private readonly FloorDetector _floorDetector; private readonly AttributeGrouper _attributeGrouper; - private readonly NavisworksFileExporter _fileExporter; private bool _isSplitting = false; #endregion @@ -150,7 +149,6 @@ namespace NavisworksTransport { _floorDetector = new FloorDetector(); _attributeGrouper = new AttributeGrouper(); - _fileExporter = new NavisworksFileExporter(); LogManager.Info("[ModelSplitter] 模型分层管理器已初始化"); } @@ -597,7 +595,60 @@ namespace NavisworksTransport LogManager.Info("[ModelSplitter] 开始导出文件..."); LogManager.Info($"[ModelSplitter] 目标文件: {result.OutputFilePath}"); - bool exportSuccess = await _fileExporter.ExportModelItemsAsync(preview.Items, result.OutputFilePath); + // 直接使用 Navisworks API 导出 + bool exportSuccess = false; + try + { + var document = NavisApplication.ActiveDocument; + + // 保存当前可见性状态并重置 + document.Models.ResetAllHidden(); + + // 隐藏不需要的项目 + var allItems = new ModelItemCollection(); + allItems.AddRange(document.Models.RootItemDescendantsAndSelf); + + var itemsToHide = new ModelItemCollection(); + foreach (ModelItem item in allItems) + { + bool shouldKeep = false; + foreach (ModelItem exportItem in preview.Items) + { + if (item.Equals(exportItem)) + { + shouldKeep = true; + break; + } + } + + if (!shouldKeep) + { + itemsToHide.Add(item); + } + } + + if (itemsToHide.Count > 0) + { + document.Models.SetHidden(itemsToHide, true); + } + + // 创建导出选项并导出 + var exportOptions = new Autodesk.Navisworks.Api.NwdExportOptions(); + exportOptions.ExcludeHiddenItems = true; + exportOptions.EmbedXrefs = false; + exportOptions.PreventObjectPropertyExport = false; + + document.ExportToNwd(result.OutputFilePath, exportOptions); + exportSuccess = true; + + // 恢复可见性 + document.Models.ResetAllHidden(); + } + catch (Exception ex) + { + LogManager.Error($"[ModelSplitter] 导出异常: {ex.Message}"); + exportSuccess = false; + } LogManager.Info($"[ModelSplitter] 文件导出结果: {exportSuccess}"); diff --git a/src/Core/SimplifiedModelSplitterManager.cs b/src/Core/SimplifiedModelSplitterManager.cs index 53b6748..ff791d4 100644 --- a/src/Core/SimplifiedModelSplitterManager.cs +++ b/src/Core/SimplifiedModelSplitterManager.cs @@ -110,7 +110,6 @@ namespace NavisworksTransport private readonly FloorDetector _floorDetector; private readonly AttributeGrouper _attributeGrouper; - private readonly NavisworksFileExporter _fileExporter; // 楼层检测结果缓存 private readonly Dictionary> _floorCache; @@ -125,7 +124,6 @@ namespace NavisworksTransport { _floorDetector = new FloorDetector(); _attributeGrouper = new AttributeGrouper(); - _fileExporter = new NavisworksFileExporter(); // 初始化缓存 _floorCache = new Dictionary>(); @@ -974,20 +972,158 @@ namespace NavisworksTransport var itemsToExport = preview.Items; LogManager.Info($"[SimplifiedModelSplitter] 将导出 {itemsToExport.Count} 个模型项"); - // 使用异步方法导出 - var exportTask = _fileExporter.ExportModelItemsAsync(itemsToExport, outputPath); - bool exportSuccess = exportTask.Result; + // 检查线程状态 + var apartmentState = System.Threading.Thread.CurrentThread.GetApartmentState(); + LogManager.Info($"[SimplifiedModelSplitter] 当前线程状态: {apartmentState}"); - if (exportSuccess) + // 核心修复:确保在主 UI 线程中执行 Navisworks API 调用 + bool exportResult = false; + Exception exportException = null; + + // 使用 Application.Current.Dispatcher.Invoke 确保在主线程中执行 + System.Windows.Application.Current.Dispatcher.Invoke(() => { - LogManager.Info($"[SimplifiedModelSplitter] ========== ExportLayerToNwd完成 ========== 成功导出到: {outputPath}"); - return true; - } - else + try + { + LogManager.Info($"[SimplifiedModelSplitter] 在主线程中执行导出操作"); + var document = NavisApplication.ActiveDocument; + + // 保存当前可见性状态(在主线程中) + LogManager.Info($"[SimplifiedModelSplitter] 开始保存当前可见性状态"); + var originalVisibilityState = SaveCurrentVisibilityState(document); + LogManager.Info($"[SimplifiedModelSplitter] 已保存 {originalVisibilityState.Count} 个项目的可见性状态"); + + try + { + // 采用智能隐藏策略:只操作顶级节点,避免遍历所有项目 + var allTopLevelItems = new List(); + + // 只获取顶级节点,不遍历全部项目 + foreach (Model model in document.Models) + { + foreach (ModelItem topLevelItem in model.RootItem.Children) + { + allTopLevelItems.Add(topLevelItem); + } + } + + LogManager.Info($"[SimplifiedModelSplitter] 收集到 {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($"[SimplifiedModelSplitter] 顶级节点 {topLevelItem.DisplayName} 本身被选中,保持可见"); + break; + } + } + + // 如果顶级节点本身不在导出列表中,检查是否有任何导出项是它的后代 + if (!shouldKeepTopLevel) + { + foreach (var exportItem in itemsToExport) + { + var current = exportItem; + while (current != null) + { + if (current == topLevelItem) + { + shouldKeepTopLevel = true; + LogManager.Info($"[SimplifiedModelSplitter] 顶级节点 {topLevelItem.DisplayName} 包含导出项目,保持可见"); + break; + } + current = current.Parent; + } + if (shouldKeepTopLevel) break; + } + } + + // 如果这个顶级节点不需要保持可见,则隐藏它 + if (!shouldKeepTopLevel) + { + try + { + itemsToHide.Add(topLevelItem); + hiddenCount++; + LogManager.Info($"[SimplifiedModelSplitter] 隐藏顶级节点: {topLevelItem.DisplayName}"); + } + catch (Exception ex) + { + LogManager.Warning($"[SimplifiedModelSplitter] 添加隐藏项目失败: {topLevelItem.DisplayName} - {ex.Message}"); + } + } + } + + // 执行隐藏操作 + if (itemsToHide.Count > 0) + { + try + { + document.Models.SetHidden(itemsToHide, true); + LogManager.Info($"[SimplifiedModelSplitter] 成功隐藏 {hiddenCount} 个顶级节点,保留导出项目可见"); + } + catch (Exception ex) + { + LogManager.Warning($"[SimplifiedModelSplitter] 隐藏操作失败: {ex.Message}"); + } + } + + // 创建导出选项 + var exportOptions = new Autodesk.Navisworks.Api.NwdExportOptions + { + ExcludeHiddenItems = true, // 只导出可见项目 + EmbedXrefs = false, + PreventObjectPropertyExport = false + }; + + LogManager.Info($"[SimplifiedModelSplitter] 开始调用ExportToNwd API(主线程)"); + + // 在主线程中执行导出 + document.ExportToNwd(outputPath, exportOptions); + + LogManager.Info($"[SimplifiedModelSplitter] ExportToNwd API调用完成(主线程)"); + exportResult = true; + } + finally + { + // 恢复原始可见性状态(在主线程中) + try + { + RestoreVisibilityState(document, originalVisibilityState); + LogManager.Info($"[SimplifiedModelSplitter] 已恢复原始可见性状态"); + } + catch (Exception restoreEx) + { + LogManager.Error($"[SimplifiedModelSplitter] 恢复可见性失败: {restoreEx.Message}"); + } + } + } + catch (Exception ex) + { + exportException = ex; + } + }); + + // 检查导出结果 + if (exportException != null) { - LogManager.Error($"[SimplifiedModelSplitter] 导出失败: {outputPath}"); + LogManager.Error($"[SimplifiedModelSplitter] 主线程导出异常: {exportException.Message}"); + LogManager.Error($"[SimplifiedModelSplitter] 异常堆栈: {exportException.StackTrace}"); return false; } + + LogManager.Info($"[SimplifiedModelSplitter] ========== ExportLayerToNwd完成 ========== 成功导出到: {outputPath}"); + return exportResult; } catch (Exception ex) { @@ -997,157 +1133,41 @@ namespace NavisworksTransport } } - /// - /// 根据分层结果获取该层包含的模型项 + /// 保存当前可见性状态(参考"保存当前选择项"的方法) /// - private ModelItemCollection GetLayerModelItems(SplitPreviewResult preview, SplitConfiguration config) + private Dictionary SaveCurrentVisibilityState(Document document) { + var visibilityState = new Dictionary(); + try { - LogManager.Info($"[SimplifiedModelSplitter] ========== GetLayerModelItems开始 =========="); - LogManager.Info($"[SimplifiedModelSplitter] 目标分层: {preview.LayerName}, 策略: {config.Strategy}"); - - // 优先使用预览结果中已缓存的模型项 - if (preview.Items != null && preview.Items.Count > 0) + // 获取所有顶级项目并记录它们的可见性状态 + foreach (Model model in document.Models) { - LogManager.Info($"[SimplifiedModelSplitter] 使用预览结果中的缓存模型项,数量: {preview.Items.Count}"); - - // 关键修复:展开楼层顶层节点,收集所有子节点 - LogManager.Info($"[SimplifiedModelSplitter] 开始展开楼层节点,收集完整的子节点内容..."); - var expandedItems = ExpandFloorItems(preview.Items); - LogManager.Info($"[SimplifiedModelSplitter] 楼层节点展开完成: 原始{preview.Items.Count}个 -> 展开后{expandedItems.Count}个"); - - return expandedItems; + foreach (ModelItem topLevelItem in model.RootItem.Children) + { + try + { + // 记录是否隐藏(IsHidden为true表示隐藏,我们存储可见性所以取反) + visibilityState[topLevelItem] = !topLevelItem.IsHidden; + } + catch + { + // 如果无法获取状态,默认为可见 + visibilityState[topLevelItem] = true; + } + } } - LogManager.Info($"[SimplifiedModelSplitter] 预览结果中没有模型项,开始重新检测"); - - var document = NavisApplication.ActiveDocument; - if (document?.Models?.Count == 0) - { - LogManager.Warning($"[SimplifiedModelSplitter] 文档无效或没有模型"); - return new ModelItemCollection(); - } - - var allItems = GetAllModelItems(config.MaxDepth); - LogManager.Info($"[SimplifiedModelSplitter] 获取到总模型项数: {allItems.Count},深度: {config.MaxDepth}"); - var layerItems = new ModelItemCollection(); - - switch (config.Strategy) - { - case SplitStrategy.ByFloor: - LogManager.Info($"[SimplifiedModelSplitter] 使用楼层策略,开始重新检测楼层"); - // 按楼层分层:使用FloorDetector重新检测并匹配层名 - var floors = _floorDetector.DetectFloors(allItems, null, config.MaxDepth); - LogManager.Info($"[SimplifiedModelSplitter] 检测到楼层数: {floors?.Count ?? 0}"); - - if (floors != null) - { - foreach (var floor in floors) - { - string sanitizedFloorName = SanitizeLayerName(floor.FloorName); - LogManager.Info($"[SimplifiedModelSplitter] 楼层匹配检查: 原始名称='{floor.FloorName}' -> 清理后='{sanitizedFloorName}', 目标='{preview.LayerName}', 匹配={sanitizedFloorName == preview.LayerName}"); - } - } - - var matchingFloor = floors?.FirstOrDefault(f => SanitizeLayerName(f.FloorName) == preview.LayerName); - if (matchingFloor != null) - { - LogManager.Info($"[SimplifiedModelSplitter] 找到匹配楼层: 原始名称='{matchingFloor.FloorName}', 清理后名称='{preview.LayerName}', 包含模型项: {matchingFloor.ItemCount}"); - - // 关键修复:直接使用FloorDetector检测结果,并展开所有子节点 - if (matchingFloor.Items != null && matchingFloor.Items.Count > 0) - { - LogManager.Info($"[SimplifiedModelSplitter] 直接使用FloorDetector的模型项结果,数量: {matchingFloor.Items.Count}"); - LogManager.Info($"[SimplifiedModelSplitter] 开始展开楼层节点,收集完整的子节点内容..."); - - // 展开楼层顶层节点,收集所有子节点 - var expandedItems = ExpandFloorItems(matchingFloor.Items); - LogManager.Info($"[SimplifiedModelSplitter] 楼层节点展开完成: 原始{matchingFloor.Items.Count}个 -> 展开后{expandedItems.Count}个"); - - layerItems = expandedItems; - } - else - { - LogManager.Info($"[SimplifiedModelSplitter] FloorDetector结果中没有模型项,使用原始名称重新查找: '{matchingFloor.FloorName}'"); - var foundItems = GetFloorItems(allItems, matchingFloor.FloorName, config.MaxDepth); - LogManager.Info($"[SimplifiedModelSplitter] 重新查找结果: {foundItems.Count}个模型项"); - - // 同样展开找到的楼层项目 - if (foundItems.Count > 0) - { - var expandedItems = ExpandFloorItems(foundItems); - LogManager.Info($"[SimplifiedModelSplitter] 重新查找的楼层项目展开完成: 原始{foundItems.Count}个 -> 展开后{expandedItems.Count}个"); - layerItems = expandedItems; - } - else - { - layerItems = foundItems; - } - } - } - else - { - LogManager.Warning($"[SimplifiedModelSplitter] 未找到匹配的楼层: {preview.LayerName}"); - } - break; - - case SplitStrategy.ByAttribute: - LogManager.Info($"[SimplifiedModelSplitter] 使用属性策略: {config.AttributeName}"); - // 按属性分层:使用AttributeGrouper重新分组并匹配层名 - var groups = _attributeGrouper.GroupByAttribute(allItems, config.AttributeName); - LogManager.Info($"[SimplifiedModelSplitter] 检测到属性组数: {groups?.Count ?? 0}"); - - if (groups != null) - { - foreach (var group in groups) - { - string sanitizedGroupName = SanitizeLayerName(group.GroupName); - LogManager.Info($"[SimplifiedModelSplitter] 属性组匹配检查: 原始名称='{group.GroupName}' -> 清理后='{sanitizedGroupName}', 目标='{preview.LayerName}', 匹配={sanitizedGroupName == preview.LayerName}"); - } - } - - var matchingGroup = groups?.FirstOrDefault(g => SanitizeLayerName(g.GroupName) == preview.LayerName); - if (matchingGroup != null) - { - LogManager.Info($"[SimplifiedModelSplitter] 找到匹配属性组: 原始名称='{matchingGroup.GroupName}', 清理后名称='{preview.LayerName}', 包含模型项: {matchingGroup.ItemCount}"); - - // 直接使用AttributeGrouper检测结果中的模型项,避免重新检测 - if (matchingGroup.Items != null && matchingGroup.Items.Count > 0) - { - LogManager.Info($"[SimplifiedModelSplitter] 直接使用AttributeGrouper的模型项结果,数量: {matchingGroup.Items.Count}"); - // 属性分组通常已经是完整的构件,不需要展开,但为了保持一致性,仍然检查 - var expandedItems = ExpandFloorItems(matchingGroup.Items); - LogManager.Info($"[SimplifiedModelSplitter] 属性组项目展开完成: 原始{matchingGroup.Items.Count}个 -> 展开后{expandedItems.Count}个"); - layerItems = expandedItems; - } - else - { - LogManager.Info($"[SimplifiedModelSplitter] AttributeGrouper结果中没有模型项,使用原始名称重新查找: '{matchingGroup.GroupName}'"); - layerItems = GetAttributeGroupItems(allItems, config.AttributeName, matchingGroup.GroupName); - } - } - else - { - LogManager.Warning($"[SimplifiedModelSplitter] 未找到匹配的属性组: {preview.LayerName}"); - } - break; - - default: - LogManager.Warning($"[SimplifiedModelSplitter] 不支持的分层策略: {config.Strategy}"); - break; - } - - LogManager.Info($"[SimplifiedModelSplitter] ========== GetLayerModelItems完成 ========== 获取到 {layerItems.Count} 个分层模型项"); - return layerItems; + LogManager.Info($"[SimplifiedModelSplitter] 保存可见性状态完成,记录了 {visibilityState.Count} 个顶级项目"); } catch (Exception ex) { - LogManager.Error($"[SimplifiedModelSplitter] 获取分层模型项失败: {ex.Message}"); - LogManager.Error($"[SimplifiedModelSplitter] 异常堆栈: {ex.StackTrace}"); - return new ModelItemCollection(); + LogManager.Error($"[SimplifiedModelSplitter] 保存可见性状态失败: {ex.Message}"); } + + return visibilityState; } /// @@ -1598,42 +1618,6 @@ namespace NavisworksTransport return false; } - /// - /// 保存当前可见性状态 - /// - private Dictionary SaveVisibilityState(Document document) - { - var visibilityState = new Dictionary(); - - try - { - // 这里需要保存所有模型项的可见性状态 - // 简化实现:记录隐藏的项目 - var allItems = new ModelItemCollection(); - allItems.AddRange(document.Models.RootItemDescendantsAndSelf); - - foreach (ModelItem item in allItems) - { - try - { - // Navisworks中检查项目是否隐藏的方法 - visibilityState[item] = !item.IsHidden; - } - catch - { - // 如果无法获取状态,默认为可见 - visibilityState[item] = true; - } - } - } - catch (Exception ex) - { - LogManager.Error($"[SimplifiedModelSplitter] 保存可见性状态失败: {ex.Message}"); - } - - return visibilityState; - } - /// /// 恢复可见性状态 /// @@ -1701,84 +1685,6 @@ namespace NavisworksTransport } } - /// - /// 隐藏除指定项目外的其他所有项目 - 使用成熟的可见性控制模式 - /// - private void HideOtherItems(Document document, ModelItemCollection keepVisible) - { - try - { - LogManager.Info($"[SimplifiedModelSplitter] 开始隐藏其他项目,保持可见: {keepVisible.Count} 个项目"); - - // 获取所有项目 - 使用安全的AddRange方法 - var allItems = new ModelItemCollection(); - allItems.AddRange(document.Models.RootItemDescendantsAndSelf); - LogManager.Info($"[SimplifiedModelSplitter] 总共 {allItems.Count} 个模型项目"); - - // 先重置所有项目为可见状态,确保从干净状态开始 - document.Models.ResetAllHidden(); - LogManager.Info("[SimplifiedModelSplitter] 已重置所有项目为可见状态"); - - // 收集需要隐藏的项目 - 不在keepVisible集合中的项目 - var itemsToHide = new ModelItemCollection(); - - foreach (ModelItem item in allItems) - { - try - { - // 检查该项目是否在keepVisible集合中 - bool shouldKeepVisible = false; - foreach (ModelItem visibleItem in keepVisible) - { - if (item.Equals(visibleItem)) - { - shouldKeepVisible = true; - break; - } - } - - // 如果不在保持可见的集合中,则需要隐藏 - if (!shouldKeepVisible) - { - itemsToHide.Add(item); - } - } - catch (Exception ex) - { - LogManager.Warning($"[SimplifiedModelSplitter] 检查项目可见性时出错: {ex.Message}"); - } - } - - LogManager.Info($"[SimplifiedModelSplitter] 需要隐藏 {itemsToHide.Count} 个项目"); - - // 执行隐藏操作 - 使用成熟的SetHidden方法 - if (itemsToHide.Count > 0) - { - document.Models.SetHidden(itemsToHide, true); - LogManager.Info($"[SimplifiedModelSplitter] 成功隐藏 {itemsToHide.Count} 个项目"); - } - else - { - LogManager.Info("[SimplifiedModelSplitter] 没有项目需要隐藏"); - } - } - catch (Exception ex) - { - LogManager.Error($"[SimplifiedModelSplitter] 隐藏其他项目失败: {ex.Message}"); - - // 异常处理:确保至少显示所有项目 - try - { - document.Models.ResetAllHidden(); - LogManager.Info("[SimplifiedModelSplitter] 异常恢复:重置为全部可见"); - } - catch (Exception resetEx) - { - LogManager.Error($"[SimplifiedModelSplitter] 异常恢复也失败: {resetEx.Message}"); - } - } - } - /// /// 简化的环境诊断方法 - 快速检查API是否可用 /// diff --git a/src/Utils/NavisworksFileExporter.cs b/src/Utils/NavisworksFileExporter.cs deleted file mode 100644 index b7650f9..0000000 --- a/src/Utils/NavisworksFileExporter.cs +++ /dev/null @@ -1,1014 +0,0 @@ -using Autodesk.Navisworks.Api; -using System; -using System.IO; -using System.Threading.Tasks; -using System.Collections.Generic; -using System.Linq; - -namespace NavisworksTransport -{ - /// - /// Navisworks文件导出器 - 负责将模型元素导出为独立的Navisworks文件 - /// - public class NavisworksFileExporter - { - #region 枚举和常量 - - /// - /// 导出策略枚举 - /// - public enum ExportStrategy - { - VisibilityControl, // 通过可见性控制 - SelectionBased, // 基于选择集 - CopyToNewDocument // 复制到新文档 - } - - /// - /// 导出配置 - /// - public class ExportConfiguration - { - public ExportStrategy Strategy { get; set; } = ExportStrategy.VisibilityControl; - public bool PreserveViewpoints { get; set; } = false; - public bool PreserveAnimations { get; set; } = false; - public bool PreserveClashTests { get; set; } = false; - public bool CompressOutput { get; set; } = true; - public string FileFormat { get; set; } = "nwd"; // nwd, nwf, nwc - public int TimeoutSeconds { get; set; } = 300; - public bool CreateBackup { get; set; } = true; - } - - #endregion - - #region 事件定义 - - public event EventHandler StatusChanged; - public event EventHandler ProgressChanged; - public event EventHandler ErrorOccurred; - - #endregion - - #region 私有字段 - - private readonly VisibilityManager _visibilityManager; - private bool _isExporting = false; - - #endregion - - #region 构造函数 - - public NavisworksFileExporter() - { - _visibilityManager = new VisibilityManager(); - LogManager.Info("[FileExporter] Navisworks文件导出器已初始化"); - } - - #endregion - - #region 公共方法 - - /// - /// 异步导出模型元素到文件 - /// - /// 要导出的模型元素 - /// 输出文件路径 - /// 导出配置 - /// 导出是否成功 - public async Task ExportModelItemsAsync(ModelItemCollection items, string outputPath, ExportConfiguration config = null) - { - if (_isExporting) - { - throw new InvalidOperationException("导出操作正在进行中"); - } - - try - { - _isExporting = true; - config = config ?? new ExportConfiguration(); - - LogManager.WriteSessionSeparator(); - LogManager.Info($"[FileExporter] ========== 开始文件导出 =========="); - LogManager.Info($"[FileExporter] 元素数量: {items.Count}"); - LogManager.Info($"[FileExporter] 输出路径: {outputPath}"); - LogManager.Info($"[FileExporter] 导出策略: {config.Strategy}"); - LogManager.Info($"[FileExporter] 文件格式: {config.FileFormat}"); - - OnStatusChanged($"开始导出 {items.Count} 个元素到 {Path.GetFileName(outputPath)}"); - - // 记录系统状态 - LogManager.Info($"[FileExporter] 导出前内存使用: {GC.GetTotalMemory(false) / 1024 / 1024} MB"); - - // 验证输入参数 - LogManager.Info("[FileExporter] 开始验证输入参数..."); - ValidateExportParameters(items, outputPath); - LogManager.Info("[FileExporter] 输入参数验证完成"); - - // 创建输出目录 - LogManager.Info("[FileExporter] 开始创建输出目录..."); - EnsureOutputDirectory(outputPath); - LogManager.Info("[FileExporter] 输出目录创建完成"); - - // 使用分层导出逻辑(参考保存当前选择项的验证方法) - LogManager.Info("[FileExporter] 开始分层导出逻辑..."); - bool success = await ExportWithLayerLogicAsync(items, outputPath, config); - LogManager.Info($"[FileExporter] 分层导出完成,结果: {success}"); - - if (success) - { - LogManager.Info($"[FileExporter] 导出成功: {outputPath}"); - OnStatusChanged("导出完成"); - } - else - { - LogManager.Error($"[FileExporter] 导出失败: {outputPath}"); - OnStatusChanged("导出失败"); - } - - return success; - } - catch (Exception ex) - { - LogManager.Error($"[FileExporter] 导出异常: {ex.Message}"); - OnErrorOccurred(ex); - return false; - } - finally - { - _isExporting = false; - } - } - - /// - /// 使用分层逻辑导出(参考保存当前选择项的验证方法) - /// 业务逻辑:选择当前层,隐藏其他层,导出,恢复隐藏 - /// - private async Task ExportWithLayerLogicAsync(ModelItemCollection items, string outputPath, ExportConfiguration config) - { - try - { - LogManager.Info("[FileExporter] 开始分层导出逻辑"); - - var document = Application.ActiveDocument; - if (document?.Models?.Count == 0) - { - LogManager.Error("[FileExporter] 没有活动文档或模型"); - return false; - } - - // 保存当前选择状态 - var originalSelection = new List(); - foreach (ModelItem item in document.CurrentSelection.SelectedItems) - { - originalSelection.Add(item); - } - LogManager.Info($"[FileExporter] 已保存原始选择状态,包含 {originalSelection.Count} 个元素"); - - try - { - // 1. 收集所有顶级节点(参考保存当前选择项的方法) - var allTopLevelItems = new List(); - foreach (Model model in document.Models) - { - foreach (ModelItem topLevelItem in model.RootItem.Children) - { - allTopLevelItems.Add(topLevelItem); - } - } - LogManager.Info($"[FileExporter] 收集到 {allTopLevelItems.Count} 个顶级节点"); - - // 2. 智能隐藏策略:隐藏不包含目标元素的顶级节点 - var itemsToHide = new ModelItemCollection(); - int hiddenCount = 0; - - foreach (ModelItem topLevelItem in allTopLevelItems) - { - bool shouldKeepTopLevel = false; - - // 检查是否有任何目标元素属于这个顶级节点 - foreach (ModelItem targetItem in items) - { - var current = targetItem; - while (current != null) - { - if (current == topLevelItem) - { - shouldKeepTopLevel = true; - LogManager.Info($"[FileExporter] 顶级节点 {topLevelItem.DisplayName} 包含目标元素,保持可见"); - break; - } - current = current.Parent; - } - if (shouldKeepTopLevel) break; - } - - // 如果这个顶级节点不需要保持可见,则隐藏它 - if (!shouldKeepTopLevel) - { - try - { - itemsToHide.Add(topLevelItem); - hiddenCount++; - LogManager.Info($"[FileExporter] 隐藏顶级节点: {topLevelItem.DisplayName}"); - } - catch (Exception ex) - { - LogManager.Warning($"[FileExporter] 添加隐藏项目失败: {topLevelItem.DisplayName} - {ex.Message}"); - } - } - } - - // 3. 执行隐藏操作 - if (itemsToHide.Count > 0) - { - try - { - document.Models.SetHidden(itemsToHide, true); - LogManager.Info($"[FileExporter] 成功隐藏 {hiddenCount} 个顶级节点"); - } - catch (Exception ex) - { - LogManager.Warning($"[FileExporter] 隐藏操作失败: {ex.Message}"); - } - } - - // 4. 确保选择目标元素 - document.CurrentSelection.Clear(); - foreach (ModelItem item in items) - { - try - { - document.CurrentSelection.Add(item); - } - catch (Exception ex) - { - LogManager.Warning($"[FileExporter] 添加选择元素失败: {item.DisplayName} - {ex.Message}"); - } - } - LogManager.Info($"[FileExporter] 已选择 {items.Count} 个目标元素"); - - // 5. 使用正确的导出方法 - OnStatusChanged("正在保存文件..."); - LogManager.Info("[FileExporter] 开始调用SaveDocument..."); - bool saveSuccess = SaveDocument(document, outputPath, config); - LogManager.Info($"[FileExporter] SaveDocument调用结果: {saveSuccess}"); - - // 6. 恢复所有隐藏项目的可见性(参考保存当前选择项的方法) - try - { - if (document?.Models != null && itemsToHide.Count > 0) - { - // 恢复所有顶级节点的可见性 - var allItems = new ModelItemCollection(); - foreach (Model model in document.Models) - { - foreach (ModelItem item in model.RootItem.Children) - { - allItems.Add(item); - } - } - - if (allItems.Count > 0) - { - document.Models.SetHidden(allItems, false); - LogManager.Info("[FileExporter] 已恢复所有项目可见性"); - } - } - } - catch (Exception ex) - { - LogManager.Warning($"[FileExporter] 恢复可见性失败: {ex.Message}"); - } - - return saveSuccess; - } - finally - { - // 7. 恢复原始选择 - try - { - document.CurrentSelection.Clear(); - foreach (ModelItem item in originalSelection) - { - try - { - document.CurrentSelection.Add(item); - } - catch (Exception ex) - { - LogManager.Warning($"[FileExporter] 恢复选择项时出错: {ex.Message}"); - } - } - LogManager.Info("[FileExporter] 已恢复原始选择"); - } - catch (Exception ex) - { - LogManager.Warning($"[FileExporter] 恢复选择失败: {ex.Message}"); - } - } - } - catch (Exception ex) - { - LogManager.Error($"[FileExporter] 分层导出逻辑异常: {ex.Message}"); - throw; - } - } - - /// - /// 同步导出模型元素到文件 - /// - /// 要导出的模型元素 - /// 输出文件路径 - /// 导出配置 - /// 导出是否成功 - public bool ExportModelItems(ModelItemCollection items, string outputPath, ExportConfiguration config = null) - { - try - { - var task = ExportModelItemsAsync(items, outputPath, config); - task.Wait(); - return task.Result; - } - catch (Exception ex) - { - LogManager.Error($"[FileExporter] 同步导出失败: {ex.Message}"); - return false; - } - } - - /// - /// 批量导出多个分组 - /// - /// 导出任务列表 - /// 导出配置 - /// 导出结果列表 - public async Task> BatchExportAsync(List exportTasks, ExportConfiguration config = null) - { - try - { - LogManager.Info($"[FileExporter] 开始批量导出,任务数量: {exportTasks.Count}"); - - var results = new List(); - int completedCount = 0; - - foreach (var task in exportTasks) - { - try - { - OnStatusChanged($"正在导出: {task.LayerName} ({completedCount + 1}/{exportTasks.Count})"); - OnProgressChanged((int)((double)completedCount / exportTasks.Count * 100)); - - bool success = await ExportModelItemsAsync(task.Items, task.OutputPath, config); - - var result = new ExportResult - { - LayerName = task.LayerName, - OutputPath = task.OutputPath, - Success = success, - ItemCount = task.Items.Count, - ProcessTime = DateTime.Now - }; - - if (success && File.Exists(task.OutputPath)) - { - result.FileSizeBytes = new FileInfo(task.OutputPath).Length; - } - else if (!success) - { - result.ErrorMessage = "导出失败"; - } - - results.Add(result); - completedCount++; - - LogManager.Info($"[FileExporter] 批量导出进度: {completedCount}/{exportTasks.Count}"); - } - catch (Exception ex) - { - var errorResult = new ExportResult - { - LayerName = task.LayerName, - OutputPath = task.OutputPath, - Success = false, - ErrorMessage = ex.Message, - ItemCount = task.Items.Count, - ProcessTime = DateTime.Now - }; - results.Add(errorResult); - - LogManager.Error($"[FileExporter] 批量导出任务失败: {task.LayerName}, 错误: {ex.Message}"); - } - } - - OnProgressChanged(100); - OnStatusChanged($"批量导出完成,成功: {results.Count(r => r.Success)}, 失败: {results.Count(r => !r.Success)}"); - - LogManager.Info($"[FileExporter] 批量导出完成,总计: {results.Count}, 成功: {results.Count(r => r.Success)}"); - return results; - } - catch (Exception ex) - { - LogManager.Error($"[FileExporter] 批量导出异常: {ex.Message}"); - throw; - } - } - - #endregion - - #region 数据结构 - - /// - /// 导出任务 - /// - public class ExportTask - { - public string LayerName { get; set; } - public ModelItemCollection Items { get; set; } - public string OutputPath { get; set; } - } - - /// - /// 导出结果 - /// - public class ExportResult - { - public string LayerName { get; set; } - public string OutputPath { get; set; } - public bool Success { get; set; } - public string ErrorMessage { get; set; } - public int ItemCount { get; set; } - public long FileSizeBytes { get; set; } - public DateTime ProcessTime { get; set; } - } - - #endregion - - #region 私有方法 - 导出执行 - - private async Task ExecuteExportAsync(ModelItemCollection items, string outputPath, ExportConfiguration config) - { - switch (config.Strategy) - { - case ExportStrategy.VisibilityControl: - return await ExportByVisibilityControlAsync(items, outputPath, config); - - case ExportStrategy.SelectionBased: - return await ExportBySelectionAsync(items, outputPath, config); - - case ExportStrategy.CopyToNewDocument: - return await ExportByCopyToNewDocumentAsync(items, outputPath, config); - - default: - throw new NotSupportedException($"不支持的导出策略: {config.Strategy}"); - } - } - - private Task ExportByVisibilityControlAsync(ModelItemCollection items, string outputPath, ExportConfiguration config) - { - try - { - LogManager.Info("[FileExporter] 使用纯选择集策略导出(完全避免可见性操作)"); - - var document = Application.ActiveDocument; - if (document == null) - { - throw new InvalidOperationException("没有活动的Navisworks文档"); - } - - // 关键修复:完全避免任何可见性操作,只使用选择集 - try - { - OnStatusChanged("正在准备导出..."); - LogManager.Info("[FileExporter] 开始纯选择集导出流程"); - - // 保存原始选择状态 - var originalSelection = new ModelItemCollection(); - originalSelection.CopyFrom(document.CurrentSelection.SelectedItems); - LogManager.Info($"[FileExporter] 已保存原始选择状态,包含 {originalSelection.Count} 个元素"); - - try - { - // 设置新的选择集 - OnStatusChanged("正在设置选择集..."); - LogManager.Info("[FileExporter] 清除当前选择..."); - document.CurrentSelection.Clear(); - - LogManager.Info($"[FileExporter] 开始逐个添加 {items.Count} 个目标元素到选择集..."); - - // 关键修复:逐个添加元素,避免批量操作导致的崩溃 - int addedCount = 0; - foreach (ModelItem item in items) - { - try - { - document.CurrentSelection.Add(item); - addedCount++; - - // 每添加50个元素记录一次进度 - if (addedCount % 50 == 0) - { - LogManager.Info($"[FileExporter] 已添加 {addedCount}/{items.Count} 个元素到选择集"); - } - } - catch (Exception addEx) - { - LogManager.Warning($"[FileExporter] 添加元素到选择集失败: {addEx.Message}"); - // 继续处理其他元素,不因单个元素失败而中断 - } - } - - LogManager.Info($"[FileExporter] 选择集添加完成,成功添加 {addedCount}/{items.Count} 个元素"); - - LogManager.Info("[FileExporter] 选择集设置完成"); - - // 等待状态稳定 - LogManager.Info("[FileExporter] 等待系统稳定..."); - System.Threading.Thread.Sleep(200); - - // 直接导出文件,不进行任何可见性操作 - OnStatusChanged("正在保存文件..."); - LogManager.Info("[FileExporter] 开始保存文档..."); - bool saveSuccess = SaveDocument(document, outputPath, config); - LogManager.Info($"[FileExporter] 文档保存结果: {saveSuccess}"); - - return Task.FromResult(saveSuccess); - } - finally - { - // 恢复原始选择状态 - try - { - LogManager.Info("[FileExporter] 开始恢复原始选择状态..."); - document.CurrentSelection.Clear(); - if (originalSelection.Count > 0) - { - document.CurrentSelection.AddRange(originalSelection); - LogManager.Info($"[FileExporter] 已恢复 {originalSelection.Count} 个原始选择元素"); - } - else - { - LogManager.Info("[FileExporter] 原始选择为空,保持清空状态"); - } - } - catch (Exception restoreEx) - { - LogManager.Warning($"[FileExporter] 恢复原始选择失败: {restoreEx.Message}"); - } - } - } - catch (Exception ex) - { - LogManager.Error($"[FileExporter] 选择集导出过程中发生异常: {ex.Message}"); - LogManager.Error($"[FileExporter] 异常堆栈: {ex.StackTrace}"); - - // 只进行最安全的清理操作 - try - { - document.CurrentSelection.Clear(); - LogManager.Info("[FileExporter] 异常恢复:已清除选择集"); - } - catch (Exception safeEx) - { - LogManager.Warning($"[FileExporter] 异常恢复中的安全操作也失败: {safeEx.Message}"); - } - throw; - } - } - catch (Exception ex) - { - LogManager.Error($"[FileExporter] 纯选择集导出失败: {ex.Message}"); - throw; - } - } - - private Task ExportBySelectionAsync(ModelItemCollection items, string outputPath, ExportConfiguration config) - { - try - { - LogManager.Info("[FileExporter] 使用选择集策略导出"); - - var document = Application.ActiveDocument; - if (document == null) - { - throw new InvalidOperationException("没有活动的Navisworks文档"); - } - - // 保存当前选择 - var originalSelection = new ModelItemCollection(); - originalSelection.CopyFrom(document.CurrentSelection.SelectedItems); - - try - { - // 设置新的选择集 - OnStatusChanged("正在设置选择集..."); - document.CurrentSelection.Clear(); - document.CurrentSelection.AddRange(items); - - // 等待选择更新 - System.Threading.Thread.Sleep(200); - - // 导出选中的元素 - OnStatusChanged("正在导出选中元素..."); - bool exportSuccess = ExportSelectedItems(document, outputPath, config); - - return Task.FromResult(exportSuccess); - } - finally - { - // 恢复原始选择 - document.CurrentSelection.Clear(); - if (originalSelection.Count > 0) - { - document.CurrentSelection.AddRange(originalSelection); - } - } - } - catch (Exception ex) - { - LogManager.Error($"[FileExporter] 选择集导出失败: {ex.Message}"); - throw; - } - } - - private async Task ExportByCopyToNewDocumentAsync(ModelItemCollection items, string outputPath, ExportConfiguration config) - { - try - { - LogManager.Info("[FileExporter] 使用新文档策略导出"); - - // 注意:Navisworks API不直接支持创建新文档并复制元素 - // 这里使用可见性控制作为替代方案 - LogManager.Warning("[FileExporter] 新文档策略不被直接支持,使用可见性控制替代"); - - return await ExportByVisibilityControlAsync(items, outputPath, config); - } - catch (Exception ex) - { - LogManager.Error($"[FileExporter] 新文档导出失败: {ex.Message}"); - throw; - } - } - - #endregion - - #region 私有方法 - 文件操作 - - private bool SaveDocument(Document document, string outputPath, ExportConfiguration config) - { - try - { - LogManager.Info($"[FileExporter] 开始保存文档到: {outputPath}"); - - // 创建备份 - if (config.CreateBackup && File.Exists(outputPath)) - { - string backupPath = outputPath + ".backup"; - File.Copy(outputPath, backupPath, true); - LogManager.Info($"[FileExporter] 已创建备份文件: {backupPath}"); - } - - // 使用正确的ExportToNwd API(参考文档5.2节) - try - { - LogManager.Info("[FileExporter] 开始使用ExportToNwd API保存文档"); - - // 确保文档处于稳定状态 - if (document.Models.Count == 0) - { - throw new InvalidOperationException("文档中没有模型数据"); - } - - // 等待一小段时间确保状态稳定 - System.Threading.Thread.Sleep(100); - - // 创建导出选项 - 参考保存当前选择项的正确做法 - var exportOptions = new Autodesk.Navisworks.Api.NwdExportOptions(); - exportOptions.ExcludeHiddenItems = true; // 只导出可见项目 - exportOptions.EmbedXrefs = false; - exportOptions.PreventObjectPropertyExport = false; - - // 使用ExportToNwd API导出 - document.ExportToNwd(outputPath, exportOptions); - - LogManager.Info("[FileExporter] ExportToNwd API调用完成"); - - // 验证文件是否成功保存 - if (File.Exists(outputPath)) - { - var fileInfo = new FileInfo(outputPath); - if (fileInfo.Length > 0) - { - LogManager.Info($"[FileExporter] 文档保存成功: {outputPath}, 大小: {fileInfo.Length} 字节"); - return true; - } - else - { - LogManager.Warning($"[FileExporter] 保存的文件大小为0: {outputPath}"); - File.Delete(outputPath); // 删除空文件 - return false; - } - } - else - { - LogManager.Warning($"[FileExporter] 保存后文件不存在: {outputPath}"); - return false; - } - } - catch (Exception ex) - { - LogManager.Error($"[FileExporter] ExportToNwd保存失败: {ex.Message}"); - return false; - } - } - catch (Exception ex) - { - LogManager.Error($"[FileExporter] 保存文档异常: {ex.Message}"); - return false; - } - } - - private bool ExportSelectedItems(Document document, string outputPath, ExportConfiguration config) - { - try - { - // 对于选择集导出,我们仍然需要使用可见性控制 - // 因为Navisworks不支持直接导出选中的元素 - - var selectedItems = document.CurrentSelection.SelectedItems; - var task = ExportByVisibilityControlAsync(selectedItems, outputPath, config); - task.Wait(); - return task.Result; - } - catch (Exception ex) - { - LogManager.Error($"[FileExporter] 导出选中元素失败: {ex.Message}"); - return false; - } - } - - private void SaveAsNwfFormat(Document document, string outputPath) - { - try - { - // NWF是Navisworks的工作文件格式 - // 需要使用COM API进行特殊处理 - LogManager.Info("[FileExporter] 保存为NWF格式"); - document.SaveFile(outputPath); - } - catch (Exception ex) - { - LogManager.Error($"[FileExporter] 保存NWF格式失败: {ex.Message}"); - throw; - } - } - - private void SaveAsNwcFormat(Document document, string outputPath) - { - try - { - // NWC是Navisworks的缓存文件格式 - LogManager.Info("[FileExporter] 保存为NWC格式"); - document.SaveFile(outputPath); - } - catch (Exception ex) - { - LogManager.Error($"[FileExporter] 保存NWC格式失败: {ex.Message}"); - throw; - } - } - - #endregion - - #region 私有方法 - 可见性状态管理 - - private Dictionary SaveCurrentVisibilityState() - { - try - { - LogManager.Info("[FileExporter] 保存当前可见性状态"); - - var visibilityState = new Dictionary(); - var document = Application.ActiveDocument; - - if (document?.Models != null) - { - // 安全获取所有模型项 - 使用AddRange避免挂起 - var allItems = new ModelItemCollection(); - allItems.AddRange(document.Models.RootItemDescendantsAndSelf); - - // 遍历ModelItemCollection而不是直接遍历RootItemDescendantsAndSelf - foreach (ModelItem item in allItems) - { - try - { - visibilityState[item] = !item.IsHidden; - } - catch - { - // 忽略无法访问的元素 - } - } - } - - LogManager.Info($"[FileExporter] 已保存 {visibilityState.Count} 个元素的可见性状态"); - return visibilityState; - } - catch (Exception ex) - { - LogManager.Error($"[FileExporter] 保存可见性状态失败: {ex.Message}"); - return new Dictionary(); - } - } - - private void RestoreVisibilityState(Dictionary visibilityState) - { - try - { - LogManager.Info("[FileExporter] 恢复可见性状态"); - - if (visibilityState == null || visibilityState.Count == 0) - { - LogManager.Warning("[FileExporter] 没有可见性状态需要恢复"); - return; - } - - // 直接执行,避免异步复杂性(.NET Framework 4.6兼容) - var itemsToShow = new ModelItemCollection(); - var itemsToHide = new ModelItemCollection(); - - foreach (var kvp in visibilityState) - { - try - { - if (kvp.Value) // 原来是可见的 - { - itemsToShow.Add(kvp.Key); - } - else // 原来是隐藏的 - { - itemsToHide.Add(kvp.Key); - } - } - catch - { - // 忽略无法访问的元素 - } - } - - // 批量恢复可见性 - 使用Document的SetHidden方法 - var document = Application.ActiveDocument; - if (document != null) - { - if (itemsToShow.Count > 0) - { - document.Models.SetHidden(itemsToShow, false); - } - - if (itemsToHide.Count > 0) - { - document.Models.SetHidden(itemsToHide, true); - } - } - - LogManager.Info("[FileExporter] 可见性状态恢复完成"); - } - catch (Exception ex) - { - LogManager.Error($"[FileExporter] 恢复可见性状态失败: {ex.Message}"); - } - } - - #endregion - - #region 私有方法 - 验证和辅助 - - private void ValidateExportParameters(ModelItemCollection items, string outputPath) - { - if (items == null || items.Count == 0) - { - throw new ArgumentException("要导出的模型元素集合不能为空", nameof(items)); - } - - if (string.IsNullOrEmpty(outputPath)) - { - throw new ArgumentException("输出路径不能为空", nameof(outputPath)); - } - - string directory = Path.GetDirectoryName(outputPath); - if (!string.IsNullOrEmpty(directory) && !Directory.Exists(Path.GetDirectoryName(directory))) - { - throw new DirectoryNotFoundException($"输出目录的父目录不存在: {Path.GetDirectoryName(directory)}"); - } - - var document = Application.ActiveDocument; - if (document?.Models == null || document.Models.Count == 0) - { - throw new InvalidOperationException("当前没有打开的Navisworks文档"); - } - } - - private void EnsureOutputDirectory(string outputPath) - { - string directory = Path.GetDirectoryName(outputPath); - if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) - { - Directory.CreateDirectory(directory); - LogManager.Info($"[FileExporter] 已创建输出目录: {directory}"); - } - } - - /// - /// 检查一个模型项或其子项是否包含目标元素中的任何一个 - /// - /// 要检查的根模型项 - /// 目标元素集合 - /// 如果包含则返回true - private bool ContainsAnyTargetItem(ModelItem rootItem, ModelItemCollection targetItems) - { - try - { - // 首先检查根项目本身 - if (targetItems.Contains(rootItem)) - { - return true; - } - - // 递归检查子项目(使用深度优先搜索,但限制深度避免栈溢出) - return ContainsAnyTargetItemRecursive(rootItem, targetItems, 0, 10); - } - catch (Exception ex) - { - LogManager.Error($"[FileExporter] 检查目标元素时出错: {ex.Message}"); - // 出错时保守处理,假设包含目标元素 - return true; - } - } - - /// - /// 递归检查子项目,带深度限制 - /// - /// 当前检查的项目 - /// 目标元素集合 - /// 当前深度 - /// 最大深度 - /// 如果包含则返回true - private bool ContainsAnyTargetItemRecursive(ModelItem item, ModelItemCollection targetItems, int currentDepth, int maxDepth) - { - try - { - // 防止递归过深 - if (currentDepth > maxDepth) - { - return false; - } - - // 检查当前项目的所有子项 - if (item.Children != null) - { - foreach (ModelItem child in item.Children) - { - // 检查子项目本身 - if (targetItems.Contains(child)) - { - return true; - } - - // 递归检查子项目的子项 - if (ContainsAnyTargetItemRecursive(child, targetItems, currentDepth + 1, maxDepth)) - { - return true; - } - } - } - - return false; - } - catch (Exception) - { - // 出错时保守处理 - return false; - } - } - - #endregion - - #region 事件触发方法 - - protected virtual void OnStatusChanged(string status) - { - StatusChanged?.Invoke(this, status); - } - - protected virtual void OnProgressChanged(int progress) - { - ProgressChanged?.Invoke(this, progress); - } - - protected virtual void OnErrorOccurred(Exception ex) - { - ErrorOccurred?.Invoke(this, ex); - } - - #endregion - } -} \ No newline at end of file