NavisworksTransport/src/Core/ModelSplitterManager.cs
tian aaebbcad21 优化批量导出进度显示:统一使用Progress API,分离职责
核心改进:
1. ExportLayerToNwd专注导出逻辑,不再管理进度条
2. LayerManagementViewModel统一管理批量导出进度条
3. 状态栏只在开始/结束时显示,避免UI刷新同步问题

修改内容:

**ModelSplitterManager.cs**
- 移除ExportLayerToNwd内的Progress API相关代码
- 移除进度条声明、初始化、更新和清理代码
- 移除用户取消检查(由调用者管理)
- 保留核心导出逻辑:隔离、导出、恢复

**LayerManagementViewModel.cs**
- 在批量导出循环开始前创建Progress API进度条
- 循环中为每个文件更新进度条描述和百分比
- 显示:"正在导出第 X/Y 个分层:分层名"
- 移除循环中的状态栏进度更新(避免UI同步问题)
- 只在结束时更新状态栏最终结果
- finally块确保进度条被正确关闭

最终效果:
-  单文件导出:无进度条,快速完成
-  批量导出:统一的Progress API显示完整进度信息
-  状态栏:只显示开始/结束提示,不跟踪过程进度
-  职责清晰:导出逻辑与进度显示分离

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-13 12:14:20 +08:00

2251 lines
95 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Autodesk.Navisworks.Api;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Threading;
using System.Linq;
using NavisApplication = Autodesk.Navisworks.Api.Application;
using NavisworksTransport.Core;
namespace NavisworksTransport
{
/// <summary>
/// 简化版模型分层管理器 - 为新UI设计的轻量级分层管理
/// 功能范围:楼层分析、属性设置、分层保存、选中保存
/// </summary>
public class ModelSplitterManager
{
#region
/// <summary>
/// 简化的分层策略
/// </summary>
public enum SplitStrategy
{
BySmartFloorDetection, // 智能检测楼层原ByFloor
ByFloorAttribute, // 按楼层属性原ByCustomFloor
ByZoneAttribute, // 按区域属性
BySubSystemAttribute // 按子系统属性
}
/// <summary>
/// 分层分组策略接口
/// </summary>
private interface IGroupingStrategy
{
string GroupTypeName { get; }
string UnclassifiedLabel { get; }
Dictionary<string, GroupItem> GroupItems(ModelItemCollection items, SplitConfiguration config);
/// <summary>
/// 提取单个节点的属性值,用于智能遍历
/// </summary>
string ExtractAttributeValue(ModelItem node);
}
/// <summary>
/// 分组项信息
/// </summary>
private class GroupItem
{
public string GroupName { get; set; }
public string OriginalName { get; set; }
public List<ModelItem> Items { get; set; } = new List<ModelItem>();
public Dictionary<string, object> Metadata { get; set; } = new Dictionary<string, object>();
}
/// <summary>
/// 楼层检测分组策略 - 使用统一的深度遍历结果
/// </summary>
private class FloorDetectionStrategy : IGroupingStrategy
{
private readonly FloorDetector _floorDetector;
public string GroupTypeName => "楼层";
public string UnclassifiedLabel => "未检测到楼层";
public FloorDetectionStrategy(FloorDetector floorDetector)
{
_floorDetector = floorDetector;
}
/// <summary>
/// 实现IGroupingStrategy接口的ExtractAttributeValue方法
/// 使用FloorDetector进行智能楼层检测
/// </summary>
public string ExtractAttributeValue(ModelItem node)
{
try
{
// 使用FloorDetector的通用属性检测逻辑
// 检查常见的楼层属性名称
string[] floorAttributes = { "Level", "Floor", "楼层", "层", "Story", "Storey", "Base Constraint" };
foreach (string attrName in floorAttributes)
{
string value = GetAttributeValueFromNode(node, attrName);
if (!string.IsNullOrEmpty(value))
{
return value;
}
}
return null;
}
catch (Exception ex)
{
LogManager.Warning($"智能楼层检测失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 从节点获取指定属性的值
/// </summary>
private string GetAttributeValueFromNode(ModelItem item, string attributeName)
{
try
{
// 使用 CategoryAttributeManager 的通用属性获取方法,简化代码
return CategoryAttributeManager.GetGeneralPropertyValue(item, attributeName);
}
catch
{
return null;
}
}
/// <summary>
/// 获取检测到的属性类型信息(属性名称和值)
/// </summary>
public (string attributeName, string attributeValue) ExtractAttributeInfo(ModelItem node)
{
try
{
// 使用FloorDetector的通用属性检测逻辑
// 检查常见的楼层属性名称
string[] floorAttributes = { "Level", "Floor", "楼层", "层", "Story", "Storey", "Base Constraint" };
foreach (string attrName in floorAttributes)
{
string value = GetAttributeValueFromNode(node, attrName);
if (!string.IsNullOrEmpty(value))
{
return (attrName, value);
}
}
return (null, null);
}
catch (Exception ex)
{
LogManager.Warning($"智能楼层属性检测失败: {ex.Message}");
return (null, null);
}
}
public Dictionary<string, GroupItem> GroupItems(ModelItemCollection items, SplitConfiguration config)
{
var result = new Dictionary<string, GroupItem>();
try
{
LogManager.Info($"[FloorDetectionStrategy] 开始智能楼层检测,输入模型项数={items?.Count ?? 0}");
if (items == null || items.Count == 0)
{
LogManager.Warning("[FloorDetectionStrategy] 输入模型项集合为空");
return result;
}
// 关键修复直接使用传入的items不再让FloorDetector重新获取深度项
// 调用 FloorDetector但传入 maxDepth = 0 来强制使用传入的items
var floors = _floorDetector.DetectFloorsFromGivenItems(items, null);
LogManager.Info($"[FloorDetectionStrategy] FloorDetector检测完成发现 {floors?.Count ?? 0} 个楼层");
if (floors != null && floors.Count > 0)
{
foreach (var floor in floors)
{
result[floor.FloorName] = new GroupItem
{
GroupName = floor.FloorName,
OriginalName = floor.FloorName,
Items = floor.Items?.ToList() ?? new List<ModelItem>(),
Metadata = new Dictionary<string, object>
{
["OriginalFloorName"] = floor.FloorName,
["FloorName"] = floor.FloorName,
["Elevation"] = floor.Elevation,
["DetectionMethod"] = "Floor",
["ItemsFromUnifiedDepth"] = true // 标记使用了统一深度遍历
}
};
LogManager.Info($"[FloorDetectionStrategy] 楼层 '{floor.FloorName}' 包含 {floor.Items?.Count ?? 0} 个模型项");
}
}
else
{
LogManager.Warning("[FloorDetectionStrategy] 未检测到任何楼层信息");
}
}
catch (Exception ex)
{
LogManager.Error($"[FloorDetectionStrategy] 楼层检测异常: {ex.Message}");
}
LogManager.Info($"[FloorDetectionStrategy] 楼层检测完成,返回 {result.Count} 个楼层分组");
return result;
}
}
/// <summary>
/// 属性分组策略基类
/// </summary>
private abstract class AttributeGroupingStrategy : IGroupingStrategy
{
protected readonly FloorAttributeManager _floorManager;
public abstract string GroupTypeName { get; }
public abstract string UnclassifiedLabel { get; }
protected AttributeGroupingStrategy()
{
_floorManager = new FloorAttributeManager();
}
protected abstract string GetAttributeValue(ModelItem item);
/// <summary>
/// 实现IGroupingStrategy接口的ExtractAttributeValue方法
/// 直接调用子类已实现的GetAttributeValue方法
/// </summary>
public string ExtractAttributeValue(ModelItem node)
{
return GetAttributeValue(node);
}
public Dictionary<string, GroupItem> GroupItems(ModelItemCollection items, SplitConfiguration config)
{
var result = new Dictionary<string, GroupItem>();
try
{
LogManager.Info($"[{GroupTypeName}AttributeStrategy] 开始属性分组,输入模型项数={items?.Count ?? 0},深度={config?.MaxDepth ?? -1}");
if (items == null || items.Count == 0)
{
LogManager.Warning($"[{GroupTypeName}AttributeStrategy] 输入模型项集合为空");
return result;
}
int processedCount = 0;
var attributeValueCounts = new Dictionary<string, int>();
foreach (var item in items)
{
try
{
processedCount++;
string attributeValue = GetAttributeValue(item);
string groupKey = string.IsNullOrEmpty(attributeValue) ? UnclassifiedLabel : attributeValue;
// 统计属性值分布
if (!attributeValueCounts.ContainsKey(groupKey))
attributeValueCounts[groupKey] = 0;
attributeValueCounts[groupKey]++;
if (!result.ContainsKey(groupKey))
{
result[groupKey] = new GroupItem
{
GroupName = groupKey,
OriginalName = attributeValue ?? UnclassifiedLabel,
Metadata = new Dictionary<string, object>
{
["DetectionMethod"] = GroupTypeName + "Attribute",
["ItemsFromUnifiedDepth"] = true, // 标记使用了统一深度遍历
["MaxDepth"] = config?.MaxDepth ?? -1
}
};
}
result[groupKey].Items.Add(item);
}
catch (Exception ex)
{
LogManager.Error($"[{GroupTypeName}AttributeStrategy] 处理单个模型项时出错: {ex.Message}");
}
}
// 记录属性值分布
LogManager.Info($"[{GroupTypeName}AttributeStrategy] 处理完成,共处理{processedCount}个模型项,发现{result.Count}个{GroupTypeName}分组");
foreach (var kvp in attributeValueCounts.OrderByDescending(x => x.Value))
{
LogManager.Info($"[{GroupTypeName}AttributeStrategy] {GroupTypeName}分组 '{kvp.Key}': {kvp.Value} 个模型项");
}
}
catch (Exception ex)
{
LogManager.Error($"[{GroupTypeName}AttributeStrategy] 属性分组异常: {ex.Message}");
}
return result;
}
}
/// <summary>
/// 楼层属性分组策略
/// </summary>
private class FloorAttributeStrategy : AttributeGroupingStrategy
{
public override string GroupTypeName => "楼层";
public override string UnclassifiedLabel => "未设置楼层";
protected override string GetAttributeValue(ModelItem item)
{
return _floorManager.GetFloorProperty(item, "楼层");
}
}
/// <summary>
/// 区域属性分组策略
/// </summary>
private class ZoneAttributeStrategy : AttributeGroupingStrategy
{
public override string GroupTypeName => "区域";
public override string UnclassifiedLabel => "未设置区域";
protected override string GetAttributeValue(ModelItem item)
{
return _floorManager.GetFloorProperty(item, "区域");
}
}
/// <summary>
/// 子系统属性分组策略
/// </summary>
private class SubSystemAttributeStrategy : AttributeGroupingStrategy
{
public override string GroupTypeName => "子系统";
public override string UnclassifiedLabel => "未设置子系统";
protected override string GetAttributeValue(ModelItem item)
{
return _floorManager.GetFloorProperty(item, "子系统");
}
}
/// <summary>
/// 简化的分层配置
/// </summary>
public class SplitConfiguration
{
/// <summary>
/// 分层策略
/// </summary>
public SplitStrategy Strategy { get; set; } = SplitStrategy.BySmartFloorDetection;
/// <summary>
/// 属性名称(用于自定义分层时使用)
/// </summary>
public string AttributeName { get; set; } = "Level";
/// <summary>
/// 输出目录
/// </summary>
public string OutputDirectory { get; set; } = "";
/// <summary>
/// 文件命名模式
/// </summary>
public string FileNamePattern { get; set; } = "{ProjectName}_{LayerName}";
/// <summary>
/// 遍历深度限制1=仅第一级2=到第二级3=到第三级0=所有级别)
/// </summary>
public int MaxDepth { get; set; } = 1;
/// <summary>
/// 导出选项配置 - 用户可配置的NWD导出选项
/// </summary>
public NwdExportUserOptions ExportOptions { get; set; } = new NwdExportUserOptions();
}
/// <summary>
/// 用户可配置的NWD导出选项 - 对应截图中的选项
/// </summary>
public class NwdExportUserOptions
{
/// <summary>
/// 嵌入 ReCap 和纹理数据 - 对应截图中的复选框
/// </summary>
public bool EmbedXrefs { get; set; } = false;
/// <summary>
/// 阻止导出对象特性 - 对应截图中的复选框
/// </summary>
public bool PreventObjectPropertyExport { get; set; } = false;
/// <summary>
/// Navisworks 版本 - 对应截图中的下拉框(暂时使用默认值)
/// </summary>
public string FileVersion { get; set; } = "2026";
}
/// <summary>
/// 分层预览结果
/// </summary>
public class SplitPreviewResult
{
public string LayerName { get; set; }
public string LayerAttribute { get; set; }
public ModelItemCollection Items { get; set; } = new ModelItemCollection();
public Dictionary<string, object> Metadata { get; set; } = new Dictionary<string, object>();
/// <summary>
/// 是否选中用于保存默认为true
/// </summary>
public bool IsSelectedForSave { get; set; } = true;
}
#endregion
#region
public event EventHandler<ProgressChangedEventArgs> ProgressChanged;
public event EventHandler<string> StatusChanged;
#endregion
#region
/// <summary>
/// 静态实例,方便其他类调用缓存清除功能
/// </summary>
public static ModelSplitterManager Instance { get; private set; }
private readonly FloorDetector _floorDetector;
// 楼层检测结果缓存
private readonly Dictionary<string, List<SplitPreviewResult>> _floorCache;
private readonly object _cacheLock;
#endregion
#region
public ModelSplitterManager()
{
// 设置静态实例
Instance = this;
_floorDetector = new FloorDetector();
// 初始化缓存
_floorCache = new Dictionary<string, List<SplitPreviewResult>>();
_cacheLock = new object();
// 验证Navisworks运行时环境
ValidateNavisworksRuntime();
LogManager.Info("[SimplifiedModelSplitter] 简化分层管理器已初始化,包含缓存机制和运行时验证");
}
/// <summary>
/// 验证Navisworks运行时环境 - 基于示例中的最佳实践
/// </summary>
private void ValidateNavisworksRuntime()
{
try
{
LogManager.Info("[SimplifiedModelSplitter] 开始验证Navisworks运行时环境...");
// 1. 检查线程模型 - 必须在STA线程中
var apartmentState = System.Threading.Thread.CurrentThread.GetApartmentState();
LogManager.Info($"[分层管理器] 当前线程状态: {apartmentState}");
if (apartmentState != System.Threading.ApartmentState.STA)
{
LogManager.Warning("[SimplifiedModelSplitter] 警告当前不在STA线程中这可能导致API调用失败");
LogManager.Warning("[SimplifiedModelSplitter] 建议:确保主线程标记为[STAThread]");
}
// 2. 检查Navisworks API可用性 - 适用于Navisworks 2026
try
{
// 尝试访问Navisworks Application来验证API是否可用
var app = NavisApplication.ActiveDocument;
LogManager.Info("[SimplifiedModelSplitter] Navisworks API可用性检查通过");
}
catch (Exception apiEx)
{
LogManager.Error($"[分层管理器] Navisworks API不可用: {apiEx.Message}");
LogManager.Error("[SimplifiedModelSplitter] 解决方案确保在Navisworks环境中运行此插件");
}
// 3. 检查文档状态
try
{
var document = NavisApplication.ActiveDocument;
if (document != null)
{
LogManager.Info($"[分层管理器] 活动文档: {document.FileName ?? ""}");
LogManager.Info($"[分层管理器] 文档模型数量: {document.Models?.Count ?? 0}");
}
else
{
LogManager.Warning("[SimplifiedModelSplitter] 当前没有活动文档");
}
}
catch (Exception docEx)
{
LogManager.Warning($"[分层管理器] 检查文档状态时出错: {docEx.Message}");
}
// 4. 内存状态检查
long memoryMB = GC.GetTotalMemory(false) / 1024 / 1024;
LogManager.Info($"[分层管理器] 当前内存使用: {memoryMB} MB");
LogManager.Info("[SimplifiedModelSplitter] Navisworks运行时环境验证完成");
}
catch (Exception ex)
{
LogManager.Error($"[分层管理器] 运行时环境验证失败: {ex.Message}");
}
}
#endregion
#region
/// <summary>
/// 清除缓存
/// </summary>
public void ClearCache()
{
lock (_cacheLock)
{
_floorCache.Clear();
LogManager.Info("[SimplifiedModelSplitter] 缓存已清除");
}
}
/// <summary>
/// 预览分层结果(简化版本,带缓存和进度报告)
/// </summary>
public List<SplitPreviewResult> PreviewSplit(SplitConfiguration config)
{
try
{
LogManager.Info($"[分层管理器] 开始智能遍历分层预览,策略: {config.Strategy},深度限制: {config.MaxDepth}");
// 步骤1初始化和缓存检查 (0-10%)
OnStatusChanged("正在初始化智能分层预览...");
OnProgressChanged(new ProgressChangedEventArgs(0, "准备智能遍历配置"));
// 生成缓存键(注意:智能遍历的缓存键需要区别于原有方式)
string cacheKey = GenerateSmartTraversalCacheKey(config);
LogManager.Info($"[分层管理器] 生成智能遍历缓存键: {cacheKey}");
OnProgressChanged(new ProgressChangedEventArgs(5, "检查缓存"));
// 检查缓存
List<SplitPreviewResult> cachedResults = GetFromCache(config.Strategy, cacheKey);
if (cachedResults != null && cachedResults.Count > 0)
{
LogManager.Info($"[分层管理器] 使用缓存结果,共 {cachedResults.Count} 个分层");
OnStatusChanged($"使用缓存结果,识别到 {cachedResults.Count} 个分层");
OnProgressChanged(new ProgressChangedEventArgs(100, $"从缓存加载 {cachedResults.Count} 个分层"));
return cachedResults;
}
// 步骤2验证文档状态 (10-15%)
OnStatusChanged("正在验证模型文档...");
OnProgressChanged(new ProgressChangedEventArgs(10, "验证Navisworks文档状态"));
var document = NavisApplication.ActiveDocument;
if (document?.Models?.Count == 0)
{
throw new InvalidOperationException("当前文档中没有模型");
}
OnProgressChanged(new ProgressChangedEventArgs(15, $"文档验证完成,包含 {document.Models.Count} 个模型"));
LogManager.Info($"[分层管理器] 文档验证完成,包含 {document.Models.Count} 个模型");
// 步骤3执行智能遍历分层分析 (15-90%)
OnStatusChanged($"正在执行{GetStrategyDisplayName(config.Strategy)}...");
OnProgressChanged(new ProgressChangedEventArgs(20, "开始智能遍历分析"));
var previewResults = new List<SplitPreviewResult>();
var strategy = CreateStrategy(config.Strategy);
// 关键改变:直接使用智能遍历,不再预收集所有节点
LogManager.Info($"[分层管理器] 使用智能遍历算法,策略: {strategy.GroupTypeName}");
previewResults = PreviewSplitWithSmartTraversal(config, strategy);
OnProgressChanged(new ProgressChangedEventArgs(90, $"智能分析完成,识别到 {previewResults.Count} 个分层"));
// 步骤4缓存结果和完成 (90-100%)
OnStatusChanged("正在保存分析结果...");
OnProgressChanged(new ProgressChangedEventArgs(95, "保存到缓存"));
// 缓存结果
SaveToCache(config.Strategy, cacheKey, previewResults);
// 完成
OnStatusChanged($"智能预览完成,识别到 {previewResults.Count} 个分层");
OnProgressChanged(new ProgressChangedEventArgs(100, $"预览完成,共 {previewResults.Count} 个分层"));
LogManager.Info($"[分层管理器] 智能遍历预览完成,共识别 {previewResults.Count} 个分层(深度: {config.MaxDepth}级),已缓存");
return previewResults;
}
catch (Exception ex)
{
LogManager.Error($"[分层管理器] 智能遍历预览失败: {ex.Message}", ex);
OnStatusChanged($"预览失败: {ex.Message}");
OnProgressChanged(new ProgressChangedEventArgs(0, $"预览失败: {ex.Message}"));
throw;
}
}
/// <summary>
/// 生成缓存键 - 使用统一的深度遍历
/// </summary>
private string GenerateCacheKey(SplitConfiguration config)
{
return $"{config.Strategy}_{config.MaxDepth}_{config.AttributeName ?? "Floor"}_" +
$"{GetItemsByDepthUnified(config.MaxDepth).Count}";
}
/// <summary>
/// 生成智能遍历专用的缓存键 - 区别于原有的预收集模式
/// </summary>
private string GenerateSmartTraversalCacheKey(SplitConfiguration config)
{
// 智能遍历的缓存键包含特殊标识,避免与原有缓存冲突
return $"SmartTraversal_{config.Strategy}_{config.MaxDepth}_{config.AttributeName ?? "Default"}_{DateTime.Now:yyyyMMdd}";
}
/// <summary>
/// 创建分层策略实例
/// </summary>
private IGroupingStrategy CreateStrategy(SplitStrategy strategy)
{
switch (strategy)
{
case SplitStrategy.BySmartFloorDetection:
return new FloorDetectionStrategy(_floorDetector);
case SplitStrategy.ByFloorAttribute:
return new FloorAttributeStrategy();
case SplitStrategy.ByZoneAttribute:
return new ZoneAttributeStrategy();
case SplitStrategy.BySubSystemAttribute:
return new SubSystemAttributeStrategy();
default:
throw new NotSupportedException($"不支持的分层策略: {strategy}");
}
}
/// <summary>
/// 从缓存获取结果
/// </summary>
private List<SplitPreviewResult> GetFromCache(SplitStrategy strategy, string cacheKey)
{
lock (_cacheLock)
{
// 只支持楼层缓存和自定义楼层缓存
var cache = _floorCache;
return cache.ContainsKey(cacheKey) ? cache[cacheKey] : null;
}
}
/// <summary>
/// 保存结果到缓存
/// </summary>
private void SaveToCache(SplitStrategy strategy, string cacheKey, List<SplitPreviewResult> results)
{
lock (_cacheLock)
{
// 只支持楼层缓存和自定义楼层缓存
var cache = _floorCache;
cache[cacheKey] = results;
LogManager.Info($"[分层管理器] 结果已缓存: {strategy}, 键: {cacheKey}, 数量: {results.Count}");
}
}
/// <summary>
/// 执行分层保存(异步,简化版本)
/// </summary>
public async Task ExecuteSplitAsync(SplitConfiguration config, CancellationToken cancellationToken = default)
{
try
{
LogManager.Info($"[分层管理器] 开始执行分层,策略: {config.Strategy}");
OnStatusChanged("正在准备分层...");
// 优先从缓存获取分层结果,避免重复检测
string cacheKey = GenerateSmartTraversalCacheKey(config);
var previewResults = GetFromCache(config.Strategy, cacheKey);
if (previewResults == null || previewResults.Count == 0)
{
LogManager.Info($"[分层管理器] 缓存中没有结果,重新预览分层");
previewResults = PreviewSplit(config);
if (previewResults.Count == 0)
{
throw new InvalidOperationException("没有可分层的内容");
}
}
else
{
LogManager.Info($"[分层管理器] 使用缓存的分层结果,共 {previewResults.Count} 个分层");
}
// 过滤出需要保存的分层
var layersToSave = previewResults.Where(p => p.IsSelectedForSave).ToList();
if (layersToSave.Count == 0)
{
LogManager.Warning("[SimplifiedModelSplitter] 没有选中任何分层进行保存");
OnStatusChanged("没有选中任何分层进行保存");
return;
}
LogManager.Info($"[分层管理器] 将保存 {layersToSave.Count}/{previewResults.Count} 个选中的分层");
// 确保输出目录存在
EnsureOutputDirectory(config.OutputDirectory);
// 逐个处理分层,增强错误处理
int successCount = 0;
var failedLayers = new List<string>();
for (int i = 0; i < layersToSave.Count; i++)
{
if (cancellationToken.IsCancellationRequested)
{
LogManager.Info($"[分层管理器] 用户取消分层操作,已处理 {successCount}/{layersToSave.Count} 个分层");
break;
}
var preview = layersToSave[i];
OnStatusChanged($"正在处理分层: {preview.LayerName} ({i + 1}/{layersToSave.Count})");
OnProgressChanged(new ProgressChangedEventArgs((i + 1) * 100 / layersToSave.Count,
$"处理分层 {i + 1}/{layersToSave.Count}: {preview.LayerName}"));
try
{
// 实现具体的分层保存逻辑
await ProcessSingleLayerAsync(preview, config, cancellationToken, i + 1, layersToSave.Count);
successCount++;
LogManager.Info($"[分层管理器] 分层 {preview.LayerName} 处理成功 ({successCount}/{layersToSave.Count})");
}
catch (Exception layerEx)
{
string errorMsg = $"分层 {preview.LayerName} 处理失败: {layerEx.Message}";
LogManager.Error($"[分层管理器] {errorMsg}");
failedLayers.Add(preview.LayerName);
// 继续处理其他分层,不因单个失败而中断整个进程
OnStatusChanged(errorMsg);
}
}
// 生成最终统计信息
string finalMessage = $"分层处理完成:成功 {successCount} 个,失败 {failedLayers.Count} 个";
if (layersToSave.Count < previewResults.Count)
{
int skippedCount = previewResults.Count - layersToSave.Count;
finalMessage += $",跳过 {skippedCount} 个未选中的分层";
}
if (failedLayers.Count > 0)
{
finalMessage += $"\n失败列表: {string.Join(", ", failedLayers)}";
LogManager.Warning($"[分层管理器] {finalMessage}");
}
else
{
LogManager.Info($"[分层管理器] {finalMessage}");
}
OnStatusChanged(finalMessage);
// 如果所有分层都失败,抛出异常
if (successCount == 0 && layersToSave.Count > 0)
{
throw new InvalidOperationException($"所有选中的分层处理都失败了,共 {layersToSave.Count} 个分层");
}
LogManager.Info($"[分层管理器] 分层执行完成:成功 {successCount}/{layersToSave.Count}");
}
catch (Exception ex)
{
LogManager.Error($"[分层管理器] 分层执行失败: {ex.Message}");
OnStatusChanged($"分层失败: {ex.Message}");
throw;
}
}
/// <summary>
/// 执行分层保存同步版本用于UI线程调用
/// </summary>
public void ExecuteSplit(SplitConfiguration config)
{
try
{
LogManager.Info($"[分层管理器] 开始执行分层,策略: {config.Strategy}");
OnStatusChanged("正在准备分层...");
// 优先从缓存获取分层结果,避免重复检测
string cacheKey = GenerateSmartTraversalCacheKey(config);
var previewResults = GetFromCache(config.Strategy, cacheKey);
if (previewResults == null || previewResults.Count == 0)
{
LogManager.Info($"[分层管理器] 缓存中没有结果,重新预览分层");
previewResults = PreviewSplit(config);
if (previewResults.Count == 0)
{
throw new InvalidOperationException("没有可分层的内容");
}
}
else
{
LogManager.Info($"[分层管理器] 使用缓存的分层结果,共 {previewResults.Count} 个分层");
}
// 过滤出需要保存的分层
var layersToSave = previewResults.Where(p => p.IsSelectedForSave).ToList();
if (layersToSave.Count == 0)
{
LogManager.Warning("[SimplifiedModelSplitter] 没有选中任何分层进行保存");
OnStatusChanged("没有选中任何分层进行保存");
return;
}
LogManager.Info($"[分层管理器] 将保存 {layersToSave.Count}/{previewResults.Count} 个选中的分层");
// 确保输出目录存在
EnsureOutputDirectory(config.OutputDirectory);
// 逐个处理分层,增强错误处理(同步版本)
int successCount = 0;
var failedLayers = new List<string>();
for (int i = 0; i < layersToSave.Count; i++)
{
var preview = layersToSave[i];
OnStatusChanged($"正在处理分层: {preview.LayerName} ({i + 1}/{layersToSave.Count})");
OnProgressChanged(new ProgressChangedEventArgs((i + 1) * 100 / layersToSave.Count,
$"处理分层 {i + 1}/{layersToSave.Count}: {preview.LayerName}"));
try
{
// 同步处理单个分层
ProcessSingleLayer(preview, config, i + 1, layersToSave.Count);
successCount++;
LogManager.Info($"[分层管理器] 分层 {preview.LayerName} 处理成功 ({successCount}/{layersToSave.Count})");
}
catch (Exception layerEx)
{
string errorMsg = $"分层 {preview.LayerName} 处理失败: {layerEx.Message}";
LogManager.Error($"[分层管理器] {errorMsg}");
failedLayers.Add(preview.LayerName);
// 继续处理其他分层,不因单个失败而中断整个进程
OnStatusChanged(errorMsg);
}
}
// 生成最终统计信息
string finalMessage = $"分层处理完成:成功 {successCount} 个,失败 {failedLayers.Count} 个";
if (layersToSave.Count < previewResults.Count)
{
int skippedCount = previewResults.Count - layersToSave.Count;
finalMessage += $",跳过 {skippedCount} 个未选中的分层";
}
if (failedLayers.Count > 0)
{
finalMessage += $"\n失败列表: {string.Join(", ", failedLayers)}";
LogManager.Warning($"[分层管理器] {finalMessage}");
}
else
{
LogManager.Info($"[分层管理器] {finalMessage}");
}
OnStatusChanged(finalMessage);
// 如果所有分层都失败,抛出异常
if (successCount == 0 && layersToSave.Count > 0)
{
throw new InvalidOperationException($"所有选中的分层处理都失败了,共 {layersToSave.Count} 个分层");
}
LogManager.Info($"[分层管理器] 分层执行完成:成功 {successCount}/{layersToSave.Count}");
}
catch (Exception ex)
{
LogManager.Error($"[分层管理器] 分层执行失败: {ex.Message}");
OnStatusChanged($"分层失败: {ex.Message}");
throw;
}
}
#endregion
#region
/// <summary>
/// 统一的深度遍历核心函数 - 精确控制深度逻辑
/// </summary>
/// <param name="maxDepth">最大深度1=仅第一级2=到第二级3=到第三级4=到第四级5=到第五级0=所有级别</param>
/// <returns>按深度筛选的模型项集合</returns>
private ModelItemCollection GetItemsByDepthUnified(int maxDepth)
{
try
{
LogManager.Info($"[分层管理器] ===== GetItemsByDepthUnified开始 ===== maxDepth={maxDepth}");
var document = NavisApplication.ActiveDocument;
if (document?.Models?.Count == 0)
{
LogManager.Warning("[SimplifiedModelSplitter] 当前文档没有模型");
return new ModelItemCollection();
}
var result = new ModelItemCollection();
if (maxDepth <= 0)
{
// 深度为0表示包含所有级别
result.AddRange(document.Models.RootItemDescendantsAndSelf);
LogManager.Info($"[分层管理器] maxDepth=0返回所有级别的项共{result.Count}个");
return result;
}
// 从文档的根模型开始精确的深度遍历
foreach (Model model in document.Models)
{
if (model.RootItem != null)
{
// 添加第一级节点(模型根的直接子节点)
if (maxDepth >= 1)
{
foreach (ModelItem firstLevelItem in model.RootItem.Children)
{
result.Add(firstLevelItem);
// 递归添加更深层次的节点
AddChildrenToDepth(firstLevelItem, result, 2, maxDepth);
}
}
}
}
LogManager.Info($"[分层管理器] ===== GetItemsByDepthUnified完成 ===== 返回{result.Count}个模型项(深度:{maxDepth}级)");
return result;
}
catch (Exception ex)
{
LogManager.Error($"[分层管理器] GetItemsByDepthUnified异常: {ex.Message}");
return new ModelItemCollection();
}
}
/// <summary>
/// 递归添加子节点到指定深度
/// </summary>
/// <param name="parentItem">父节点</param>
/// <param name="result">结果集合</param>
/// <param name="currentDepth">当前深度</param>
/// <param name="maxDepth">最大深度</param>
private void AddChildrenToDepth(ModelItem parentItem, ModelItemCollection result, int currentDepth, int maxDepth)
{
if (currentDepth > maxDepth || parentItem?.Children == null)
return;
foreach (ModelItem child in parentItem.Children)
{
result.Add(child);
// 如果还没有达到最大深度,继续遍历子项
if (currentDepth < maxDepth)
{
AddChildrenToDepth(child, result, currentDepth + 1, maxDepth);
}
}
}
/// <summary>
/// 智能遍历分层预览 - 边遍历边检查边停止的新算法
/// 替代原有的预收集模式,大幅提升预览性能
/// </summary>
private List<SplitPreviewResult> PreviewSplitWithSmartTraversal(SplitConfiguration config, IGroupingStrategy strategy)
{
// 使用字典按楼层名分组,自动合并同名楼层
var layerGroups = new Dictionary<string, SplitPreviewResult>();
try
{
LogManager.Info($"[分层管理器] ========== 开始智能遍历分层预览 ==========");
LogManager.Info($"[分层管理器] 策略: {strategy.GroupTypeName}, 深度限制: {config.MaxDepth}");
var document = NavisApplication.ActiveDocument;
if (document?.Models?.Count == 0)
{
LogManager.Warning("[SimplifiedModelSplitter] 当前文档没有模型");
return new List<SplitPreviewResult>();
}
// 从每个模型的一级节点开始智能遍历
int processedTopNodes = 0;
foreach (Model model in document.Models)
{
if (model.RootItem?.Children != null)
{
foreach (ModelItem rootChild in model.RootItem.Children)
{
processedTopNodes++;
// 统一使用 TraverseAndRecord 处理所有节点
// 它会自动检测节点是否有楼层属性并正确处理
TraverseAndRecord(rootChild, strategy, config, layerGroups, 1);
// 每处理10个顶级节点记录一次进度
if (processedTopNodes % 10 == 0)
{
LogManager.Info($"[分层管理器] 已处理 {processedTopNodes} 个顶级节点,找到 {layerGroups.Count} 个分层");
}
}
}
}
LogManager.Info($"[分层管理器] ========== 智能遍历完成 ==========");
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;
}
}
/// <summary>
/// 递归遍历节点并记录分层结果的核心方法(支持同名楼层合并)
/// </summary>
private void TraverseAndRecord(ModelItem node, IGroupingStrategy strategy,
SplitConfiguration config, Dictionary<string, SplitPreviewResult> layerGroups, int currentDepth)
{
try
{
// 检查当前节点的分层属性
string layerValue = strategy.ExtractAttributeValue(node);
if (!string.IsNullOrEmpty(layerValue))
{
// 找到分层属性,获取属性类型信息(仅对智能检测策略)
string detectedAttributeName = null;
if (strategy is FloorDetectionStrategy floorStrategy)
{
var (attributeName, attributeValue) = floorStrategy.ExtractAttributeInfo(node);
detectedAttributeName = attributeName;
LogManager.Info($"[分层管理器] 智能检测到属性类型: '{attributeName}', 值: '{attributeValue}'");
}
// 清理楼层名称
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];
// 只添加根节点,不展开子节点
// IsolateSpecificItems 会自动处理子节点(通过 Descendants
layerResult.Items.Add(node);
LogManager.Info($"[分层管理器] 添加根节点: {node.DisplayName}");
// 记录这个分支的根节点
var rootNodes = layerResult.Metadata["RootNodes"] as List<ModelItem>;
rootNodes.Add(node);
// 更新分支计数
layerResult.Metadata["BranchCount"] = rootNodes.Count;
layerResult.Metadata["EstimatedNodeCount"] = rootNodes.Count; // 只是根节点数量
LogManager.Info($"[分层管理器] 合并到楼层 '{sanitizedLayerName}',现有 {rootNodes.Count} 个根节点");
return; // 关键:停止遍历该分支
}
// 未找到分层属性且未达深度限制,继续遍历子节点
if (currentDepth < config.MaxDepth && node.Children != null)
{
foreach (ModelItem child in node.Children)
{
TraverseAndRecord(child, strategy, config, layerGroups, currentDepth + 1);
}
}
}
catch (Exception ex)
{
LogManager.Warning($"[分层管理器] 遍历节点失败: {node?.DisplayName ?? "null"}, 错误: {ex.Message}");
}
}
// ExpandNodeTree 方法已删除,不再需要展开节点树
// IsolateSpecificItems 会自动处理子节点(通过 Descendants
/// <summary>
/// 创建轻量化分层预览结果 - 只包含必要信息,不做统计计算
/// </summary>
/// <summary>
/// 创建轻量化分层预览结果 - 只包含必要信息,不做统计计算
/// </summary>
private SplitPreviewResult CreateLayerResult(string layerValue, ModelItem rootNode, string groupTypeName, SplitStrategy strategy, string detectedAttributeName = null)
{
// 性能优化:只保存根节点,不展开子节点
// IsolateSpecificItems 会自动处理子节点(通过 Descendants
var allNodes = new ModelItemCollection();
allNodes.Add(rootNode);
// 快速统计子节点数量(可选,用于显示信息)
int estimatedNodeCount = 1;
try
{
// 只统计直接子节点数量,不递归遍历
if (rootNode.Children != null && rootNode.Children.Count() > 0)
{
estimatedNodeCount += rootNode.Children.Count();
LogManager.Info($"[分层管理器] 节点 '{rootNode.DisplayName}' 包含 {rootNode.Children.Count()} 个直接子节点");
}
}
catch (Exception ex)
{
LogManager.Warning($"[分层管理器] 统计子节点时出现异常: {ex.Message}");
}
var metadata = new Dictionary<string, object>
{
["LayerValue"] = layerValue,
["RootNodeReference"] = rootNode,
["GroupType"] = groupTypeName,
["TraversalMode"] = "SmartTraversal",
["EstimatedNodeCount"] = estimatedNodeCount // 只是估算值,用于显示
};
// 如果检测到了具体的属性类型,保存到元数据中
if (!string.IsNullOrEmpty(detectedAttributeName))
{
metadata["DetectedAttributeName"] = detectedAttributeName;
}
return new SplitPreviewResult
{
LayerName = SanitizeLayerName(layerValue),
LayerAttribute = GetAttributeDescription(strategy, layerValue, metadata),
Items = allNodes, // 只包含根节点,导出时会自动处理子节点
Metadata = metadata
};
}
/// <summary>
/// 处理单个分层(异步版本)
/// </summary>
private async Task ProcessSingleLayerAsync(SplitPreviewResult preview, SplitConfiguration config, CancellationToken cancellationToken, int currentIndex = 0, int totalCount = 0)
{
try
{
LogManager.Info($"[分层管理器] 开始处理分层: {preview.LayerName}, 类型: {preview.LayerAttribute}");
// 检查取消请求
cancellationToken.ThrowIfCancellationRequested();
// 生成输出文件路径(使用新的智能文件名格式)
string fileName = GenerateFileName(preview.LayerName, config.Strategy, config);
string outputPath = Path.Combine(config.OutputDirectory, fileName);
LogManager.Info($"[分层管理器] 分层文件路径: {outputPath}");
// 直接在UI线程调用不使用Task.Run包装
bool success = ExportLayerToNwd(preview, outputPath, config);
if (success)
{
LogManager.Info($"[分层管理器] 分层 {preview.LayerName} 导出成功: {outputPath}");
}
else
{
LogManager.Error($"[分层管理器] 分层 {preview.LayerName} 导出失败");
throw new InvalidOperationException($"分层 {preview.LayerName} 导出失败");
}
}
catch (Exception ex)
{
LogManager.Error($"[分层管理器] 处理分层失败 {preview.LayerName}: {ex.Message}");
throw;
}
}
/// <summary>
/// 处理单个分层(同步版本)
/// </summary>
private void ProcessSingleLayer(SplitPreviewResult preview, SplitConfiguration config, int currentIndex = 0, int totalCount = 0)
{
try
{
LogManager.Info($"[分层管理器] 开始同步处理分层: {preview.LayerName}, 类型: {preview.LayerAttribute}");
if (preview.Items == null || preview.Items.Count == 0)
{
LogManager.Warning($"[分层管理器] 分层 {preview.LayerName} 没有包含模型元素,跳过导出");
return;
}
// 生成输出文件路径(使用新的智能文件名格式)
string fileName = GenerateFileName(preview.LayerName, config.Strategy, config);
string outputPath = Path.Combine(config.OutputDirectory, fileName);
LogManager.Info($"[分层管理器] 分层文件路径: {outputPath}");
// 检查模型项数量,对大量模型项进行特殊处理
if (preview.Items != null && preview.Items.Count > 100)
{
LogManager.Warning($"[分层管理器] 大量模型项检测:{preview.Items.Count}个,可能需要更多内存和时间");
// 强制垃圾回收,释放内存
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
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;
}
}
/// <summary>
/// 确保输出目录存在
/// </summary>
private void EnsureOutputDirectory(string directory)
{
if (string.IsNullOrEmpty(directory))
throw new ArgumentException("输出目录不能为空");
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
LogManager.Info($"[分层管理器] 创建输出目录: {directory}");
}
}
/// <summary>
/// 清理分层名称(支持中文字符)
/// </summary>
private string SanitizeLayerName(string layerName)
{
if (string.IsNullOrEmpty(layerName))
return "未命名分层";
// 移除文件名中的非法字符,保留中文字符
var invalidChars = Path.GetInvalidFileNameChars();
string sanitized = layerName;
foreach (char c in invalidChars)
{
sanitized = sanitized.Replace(c, '_');
}
// 移除其他特殊字符,保留字母、数字、下划线和中文字符
sanitized = System.Text.RegularExpressions.Regex.Replace(sanitized, @"[^\w\u4e00-\u9fa5\-\.]", "_");
// 移除多余的下划线
sanitized = System.Text.RegularExpressions.Regex.Replace(sanitized, @"_{2,}", "_");
// 移除首尾下划线
sanitized = sanitized.Trim('_', ' ');
// 限制长度
if (sanitized.Length > 50)
{
sanitized = sanitized.Substring(0, 50).TrimEnd('_');
}
return string.IsNullOrEmpty(sanitized) ? "未命名分层" : sanitized;
}
/// <summary>
/// 获取分层策略的属性描述
/// </summary>
/// <summary>
/// 获取具体的分层属性描述(实际属性值而非策略描述)
/// </summary>
/// <param name="strategy">分层策略</param>
/// <param name="layerName">分层名称</param>
/// <param name="metadata">元数据,可能包含实际的属性信息</param>
private string GetAttributeDescription(SplitStrategy strategy, string layerName, Dictionary<string, object> metadata = null)
{
try
{
switch (strategy)
{
case SplitStrategy.BySmartFloorDetection:
// 对于智能检测,直接返回元数据中检测到的真实属性名称
if (metadata != null && metadata.ContainsKey("DetectedAttributeName"))
{
return metadata["DetectedAttributeName"].ToString();
}
// 如果没有元数据,通过分层名称推断属性类型
return InferAttributeTypeFromLayerName(layerName);
case SplitStrategy.ByFloorAttribute:
return "楼层";
case SplitStrategy.ByZoneAttribute:
return "区域";
case SplitStrategy.BySubSystemAttribute:
return "子系统";
default:
return layerName; // 返回实际的分层名称
}
}
catch (Exception ex)
{
LogManager.Warning($"获取分层属性描述失败: {ex.Message}");
return layerName; // 失败时返回分层名称
}
}
/// <summary>
/// 从分层名称推断属性类型(用于智能检测)
/// </summary>
private string InferAttributeTypeFromLayerName(string layerName)
{
if (string.IsNullOrEmpty(layerName))
return "未知属性";
string lowerName = layerName.ToLower();
// 楼层相关关键词
if (lowerName.Contains("floor") || lowerName.Contains("level") ||
lowerName.Contains("楼") || lowerName.Contains("层") ||
lowerName.Contains("f1") || lowerName.Contains("f2") || lowerName.Contains("f3"))
{
return "楼层";
}
// 区域相关关键词
if (lowerName.Contains("zone") || lowerName.Contains("area") ||
lowerName.Contains("区") || lowerName.Contains("域") ||
lowerName.Contains("北") || lowerName.Contains("南") ||
lowerName.Contains("东") || lowerName.Contains("西"))
{
return "区域";
}
// 子系统相关关键词
if (lowerName.Contains("system") || lowerName.Contains("subsystem") ||
lowerName.Contains("系统") || lowerName.Contains("机电") ||
lowerName.Contains("hvac") || lowerName.Contains("电气") ||
lowerName.Contains("给排水") || lowerName.Contains("消防"))
{
return "子系统";
}
// 默认返回分层名称本身
return layerName;
}
/// <summary>
/// 估算文件大小
/// </summary>
private long EstimateFileSize(int itemCount)
{
// 简化的文件大小估算:每个元素约 1KB
return itemCount * 1024L;
}
/// <summary>
/// 生成文件名 - 新的智能格式根节点名称_分层属性_属性值_时间戳.nwd
/// </summary>
/// <param name="attributeValue">属性值(分层名称)</param>
/// <param name="strategy">分层策略</param>
/// <param name="config">配置信息</param>
/// <returns>文件名(不包括路径)</returns>
private string GenerateFileName(string attributeValue, SplitStrategy strategy, SplitConfiguration config)
{
try
{
// 获取根节点名称
string rootNodeName = GetRootNodeName();
// 获取分层属性类型名称
string attributeTypeName = GetAttributeTypeName(strategy);
// 清理属性值(原分层名称)
string sanitizedAttributeValue = SanitizeLayerName(attributeValue);
// 生成时间戳
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
// 新的统一格式根节点名称_分层属性_属性值_时间戳.nwd
string fileName = $"{rootNodeName}_{attributeTypeName}_{sanitizedAttributeValue}_{timestamp}.nwd";
// 确保文件名唯一性
fileName = EnsureUniqueFileName(fileName, config.OutputDirectory);
LogManager.Info($"[分层管理器] 生成智能文件名: {fileName}");
LogManager.Info($"[分层管理器] 文件名组成: 根节点='{rootNodeName}', 属性类型='{attributeTypeName}', 属性值='{sanitizedAttributeValue}', 时间戳='{timestamp}'");
return fileName;
}
catch (Exception ex)
{
LogManager.Error($"[分层管理器] 生成智能文件名失败: {ex.Message}");
// 失败时使用后备命名方式
string fallbackName = $"NavisworksModel_分层_{SanitizeLayerName(attributeValue)}_{DateTime.Now:yyyyMMdd_HHmmss}.nwd";
LogManager.Warning($"[分层管理器] 使用后备文件名: {fallbackName}");
return fallbackName;
}
}
/// <summary>
/// 获取根节点名称 - 新的智能文件名格式使用
/// 逻辑:文档文件名 → 第一个模型根项DisplayName → "NavisworksModel"
/// </summary>
/// <returns>根节点名称</returns>
private string GetRootNodeName()
{
try
{
var document = NavisApplication.ActiveDocument;
// 优先使用文档文件名(去除扩展名)
if (document != null && !string.IsNullOrEmpty(document.FileName))
{
string fileName = Path.GetFileNameWithoutExtension(document.FileName);
if (!string.IsNullOrEmpty(fileName))
{
return SanitizeLayerName(fileName);
}
}
// 其次尝试使用第一个模型根项的DisplayName
if (document?.Models != null && document.Models.Count > 0)
{
foreach (Model model in document.Models)
{
if (model.RootItem != null && !string.IsNullOrEmpty(model.RootItem.DisplayName))
{
return SanitizeLayerName(model.RootItem.DisplayName);
}
}
}
// 最后使用默认名称
return "NavisworksModel";
}
catch (Exception ex)
{
LogManager.Warning($"[分层管理器] 获取根节点名称失败: {ex.Message}");
return "NavisworksModel";
}
}
/// <summary>
/// 获取当前项目名称(保留兼容性)
/// </summary>
/// <returns>项目名称</returns>
private string GetCurrentProjectName()
{
return GetRootNodeName(); // 直接使用新的根节点名称方法
}
/// <summary>
/// 获取分层属性类型名称
/// </summary>
/// <param name="strategy">分层策略</param>
/// <returns>属性类型名称</returns>
private string GetAttributeTypeName(SplitStrategy strategy)
{
switch (strategy)
{
case SplitStrategy.BySmartFloorDetection:
return "楼层";
case SplitStrategy.ByFloorAttribute:
return "楼层";
case SplitStrategy.ByZoneAttribute:
return "区域";
case SplitStrategy.BySubSystemAttribute:
return "子系统";
default:
return "分层";
}
}
/// <summary>
/// 检查输出目录中是否存在同名文件并生成唯一文件名
/// </summary>
/// <param name="originalFileName">原始文件名</param>
/// <param name="outputDirectory">输出目录</param>
/// <returns>唯一的文件名</returns>
private string EnsureUniqueFileName(string originalFileName, string outputDirectory)
{
try
{
string filePath = Path.Combine(outputDirectory, originalFileName);
if (!File.Exists(filePath))
{
return originalFileName;
}
string nameWithoutExtension = Path.GetFileNameWithoutExtension(originalFileName);
string extension = Path.GetExtension(originalFileName);
int counter = 1;
string newFileName;
do
{
newFileName = $"{nameWithoutExtension}_{counter:D2}{extension}";
filePath = Path.Combine(outputDirectory, newFileName);
counter++;
} while (File.Exists(filePath) && counter < 100);
LogManager.Info($"[分层管理器] 文件名冲突,重命名为: {newFileName}");
return newFileName;
}
catch (Exception ex)
{
LogManager.Warning($"[分层管理器] 检查文件名唯一性失败: {ex.Message}");
return originalFileName;
}
}
/// <summary>
/// 使用正确的Navisworks API导出单个分层 - 基于示例中的最佳实践
/// </summary>
public bool ExportLayerToNwd(SplitPreviewResult preview, string outputPath, SplitConfiguration config)
{
if (preview == null)
{
LogManager.Error($"[分层管理器] 预览结果为null");
return false;
}
try
{
LogManager.Info($"[分层管理器] ========== ExportLayerToNwd开始 ==========");
LogManager.Info($"[分层管理器] 导出层: {preview.LayerName}");
LogManager.Info($"[分层管理器] 预览项数量: {preview.Items?.Count ?? 0}");
LogManager.Info($"[分层管理器] 输出路径: {outputPath}");
// 直接使用预览结果中的项目
if (preview.Items == null || preview.Items.Count == 0)
{
LogManager.Warning($"[分层管理器] 预览结果中没有模型项");
return false;
}
var itemsToExport = preview.Items;
LogManager.Info($"[分层管理器] 将导出 {itemsToExport.Count} 个模型项");
// 检查线程状态
var apartmentState = System.Threading.Thread.CurrentThread.GetApartmentState();
LogManager.Info($"[分层管理器] 当前线程状态: {apartmentState}");
// 核心修复:确保在主 UI 线程中执行 Navisworks API 调用
bool exportResult = false;
Exception exportException = null;
// 使用 Application.Current.Dispatcher.Invoke 确保在主线程中执行
System.Windows.Application.Current.Dispatcher.Invoke(() =>
{
try
{
LogManager.Info($"[分层管理器] 在主线程中执行导出操作");
var document = NavisApplication.ActiveDocument;
// 保存当前可见性状态(在主线程中)
LogManager.Info($"[分层管理器] 开始保存当前可见性状态");
var originalVisibilityState = SaveCurrentVisibilityState(document);
LogManager.Info($"[分层管理器] 已保存 {originalVisibilityState.Count} 个项目的可见性状态");
try
{
// 直接使用预览结果中的项目,这些已经是展开的完整节点树
var itemsToIsolate = preview.Items;
LogManager.Info($"[分层管理器] 准备隔离显示 {itemsToIsolate.Count} 个节点");
// 使用 VisibilityManager.IsolateSpecificItems 隔离显示
bool isolateSuccess = VisibilityHelper.IsolateSpecificItems(itemsToIsolate);
if (!isolateSuccess)
{
throw new InvalidOperationException("隔离显示失败");
}
LogManager.Info($"[分层管理器] 隔离显示成功");
// 创建导出选项
var exportOptions = new Autodesk.Navisworks.Api.NwdExportOptions
{
ExcludeHiddenItems = true, // 只导出可见项目
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);
LogManager.Info($"[分层管理器] ExportToNwd API调用完成主线程");
exportResult = true;
}
finally
{
// 恢复原始可见性状态(在主线程中)
try
{
RestoreVisibilityState(document, originalVisibilityState);
LogManager.Info($"[分层管理器] 已恢复原始可见性状态");
}
catch (Exception restoreEx)
{
LogManager.Error($"[分层管理器] 恢复可见性失败: {restoreEx.Message}");
}
}
}
catch (Exception ex)
{
exportException = ex;
}
});
// 检查导出结果
if (exportException != null)
{
LogManager.Error($"[分层管理器] 主线程导出异常: {exportException.Message}");
LogManager.Error($"[分层管理器] 异常堆栈: {exportException.StackTrace}");
return false;
}
LogManager.Info($"[分层管理器] ========== ExportLayerToNwd完成 ========== 成功导出到: {outputPath}");
return exportResult;
}
catch (Exception ex)
{
LogManager.Error($"[分层管理器] ExportLayerToNwd异常: {ex.Message}");
LogManager.Error($"[分层管理器] 异常堆栈: {ex.StackTrace}");
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>
/// 保存当前可见性状态(参考"保存当前选择项"的方法)
/// </summary>
private Dictionary<ModelItem, bool> SaveCurrentVisibilityState(Document document)
{
var visibilityState = new Dictionary<ModelItem, bool>();
try
{
// 获取所有顶级项目并记录它们的可见性状态
foreach (Model model in document.Models)
{
foreach (ModelItem topLevelItem in model.RootItem.Children)
{
try
{
// 记录是否隐藏IsHidden为true表示隐藏我们存储可见性所以取反
visibilityState[topLevelItem] = !topLevelItem.IsHidden;
}
catch
{
// 如果无法获取状态,默认为可见
visibilityState[topLevelItem] = true;
}
}
}
LogManager.Info($"[分层管理器] 保存可见性状态完成,记录了 {visibilityState.Count} 个顶级项目");
}
catch (Exception ex)
{
LogManager.Error($"[分层管理器] 保存可见性状态失败: {ex.Message}");
}
return visibilityState;
}
/// <summary>
/// 获取分层对应的顶级层节点(不展开子节点)
/// 根据API指南我们应该选择顶级节点让ExportToNwd自动处理其子树
/// </summary>
/// <summary>
/// 检查一个节点是否是顶级节点
/// </summary>
private bool IsTopLevelNode(ModelItem item)
{
try
{
// 顶级节点的特征父节点是RootItem或者父节点为null或者层级很浅
var parent = item.Parent;
if (parent == null)
{
return true; // 没有父节点,可能是根节点
}
// 检查父节点是否是模型的根项
var document = NavisApplication.ActiveDocument;
if (document?.Models != null)
{
foreach (Model model in document.Models)
{
if (parent == model.RootItem)
{
return true; // 父节点是模型根项,说明这是顶级节点
}
}
}
return false;
}
catch
{
return false;
}
}
/// <summary>
/// 查找一个节点的顶级父节点
/// </summary>
private ModelItem FindTopLevelParent(ModelItem item)
{
try
{
var current = item;
var document = NavisApplication.ActiveDocument;
// 向上遍历直到找到顶级节点
while (current != null)
{
if (IsTopLevelNode(current))
{
return current;
}
current = current.Parent;
}
return null;
}
catch
{
return null;
}
}
/// <summary>
/// 递归收集楼层顶层节点下的所有子节点
/// </summary>
/// <param name="floorTopLevelItems">楼层顶层节点集合</param>
/// <returns>包含所有子节点的完整模型项集合</returns>
private ModelItemCollection ExpandFloorItems(ModelItemCollection floorTopLevelItems)
{
try
{
LogManager.Info($"[分层管理器] ========== ExpandFloorItems开始 ==========");
LogManager.Info($"[分层管理器] 输入顶层节点数量: {floorTopLevelItems.Count}");
var expandedItems = new ModelItemCollection();
int totalCollected = 0;
foreach (ModelItem topLevelItem in floorTopLevelItems)
{
try
{
LogManager.Info($"[分层管理器] 开始展开顶层节点: '{topLevelItem.DisplayName}'");
// 添加顶层节点本身(如果它是具体的构件)
if (IsActualComponent(topLevelItem))
{
expandedItems.Add(topLevelItem);
totalCollected++;
}
// 递归收集所有子节点
var childItems = CollectAllChildItems(topLevelItem, 0, 10); // 限制最大深度为10
// 验证收集到的子项是否有效
var validChildItems = ValidateModelItems(childItems);
expandedItems.AddRange(validChildItems);
totalCollected += validChildItems.Count;
LogManager.Info($"[分层管理器] 顶层节点 '{topLevelItem.DisplayName}' 展开完成: 收集到{childItems.Count}个子项,有效{validChildItems.Count}个");
}
catch (Exception ex)
{
LogManager.Warning($"[分层管理器] 展开顶层节点失败: {topLevelItem?.DisplayName ?? "null"}, 错误: {ex.Message}");
}
}
LogManager.Info($"[分层管理器] ========== ExpandFloorItems完成 ========== 总计收集: {totalCollected}个模型项");
return expandedItems;
}
catch (Exception ex)
{
LogManager.Error($"[分层管理器] ExpandFloorItems异常: {ex.Message}");
LogManager.Error($"[分层管理器] 异常堆栈: {ex.StackTrace}");
return new ModelItemCollection();
}
}
/// <summary>
/// 递归收集模型项的所有子项
/// </summary>
/// <param name="parentItem">父级模型项</param>
/// <param name="currentDepth">当前递归深度</param>
/// <param name="maxDepth">最大递归深度</param>
/// <returns>所有子项的集合</returns>
private ModelItemCollection CollectAllChildItems(ModelItem parentItem, int currentDepth, int maxDepth)
{
var childItems = new ModelItemCollection();
try
{
// 防止递归过深
if (currentDepth >= maxDepth)
{
LogManager.Warning($"[分层管理器] 达到最大递归深度 {maxDepth},停止展开: {parentItem?.DisplayName}");
return childItems;
}
if (parentItem?.Children == null || parentItem.Children.Count() == 0)
{
return childItems;
}
foreach (ModelItem child in parentItem.Children)
{
try
{
// 添加子项本身
childItems.Add(child);
// 递归收集子项的子项
var grandChildren = CollectAllChildItems(child, currentDepth + 1, maxDepth);
if (grandChildren.Count > 0)
{
childItems.AddRange(grandChildren);
}
// 每收集100个项目记录一次进度只在顶层记录
if (currentDepth == 0 && childItems.Count % 100 == 0)
{
LogManager.Info($"[分层管理器] 已收集 {childItems.Count} 个子项...");
}
}
catch (Exception ex)
{
LogManager.Warning($"[分层管理器] 收集子项失败: {child?.DisplayName ?? "null"}, 错误: {ex.Message}");
}
}
}
catch (Exception ex)
{
LogManager.Error($"[分层管理器] CollectAllChildItems异常: {ex.Message}");
}
return childItems;
}
/// <summary>
/// 验证模型项集合的有效性,过滤掉无效的模型项
/// </summary>
/// <param name="items">要验证的模型项集合</param>
/// <returns>有效的模型项集合</returns>
private ModelItemCollection ValidateModelItems(ModelItemCollection items)
{
var validItems = new ModelItemCollection();
int nullCount = 0;
int invalidCount = 0;
try
{
LogManager.Info($"[分层管理器] 开始验证 {items.Count} 个模型项的有效性...");
foreach (ModelItem item in items)
{
try
{
// 检查模型项是否为null
if (item == null)
{
nullCount++;
continue;
}
// 检查模型项是否可以访问基本属性(验证其有效性)
string displayName = item.DisplayName; // 这会抛出异常如果项目无效
bool hasGeometry = item.HasGeometry; // 检查是否有几何体
// 如果能正常访问属性,则认为是有效的
validItems.Add(item);
}
catch (Exception ex)
{
invalidCount++;
LogManager.Warning($"[分层管理器] 发现无效模型项: {ex.Message}");
}
}
LogManager.Info($"[分层管理器] 模型项验证完成: 总数{items.Count}, 有效{validItems.Count}, 空项{nullCount}, 无效{invalidCount}");
return validItems;
}
catch (Exception ex)
{
LogManager.Error($"[分层管理器] 验证模型项时发生异常: {ex.Message}");
// 异常时返回原始集合,避免完全失败
return items;
}
}
/// <summary>
/// 判断模型项是否是实际的构件(而不是组织结构节点)
/// </summary>
/// <param name="item">模型项</param>
/// <returns>如果是实际构件返回true</returns>
private bool IsActualComponent(ModelItem item)
{
try
{
if (item == null) return false;
// 检查是否有几何体 - 有几何体的通常是实际构件
if (item.HasGeometry)
{
return true;
}
// 检查是否是叶子节点且有意义的名称
if (item.Children != null && item.Children.Count() == 0 && !string.IsNullOrEmpty(item.DisplayName))
{
string name = item.DisplayName.ToLower();
// 排除一些明显的组织节点
if (!name.Contains("layer") && !name.Contains("group") && !name.Contains("floor"))
{
return true;
}
}
return false;
}
catch (Exception ex)
{
LogManager.Warning($"[分层管理器] 判断构件类型失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 恢复可见性状态
/// </summary>
private void RestoreVisibilityState(Document document, Dictionary<ModelItem, bool> visibilityState)
{
try
{
LogManager.Info("[SimplifiedModelSplitter] 恢复可见性状态");
if (visibilityState == null || visibilityState.Count == 0)
{
LogManager.Warning("[SimplifiedModelSplitter] 没有可见性状态需要恢复,重置为全部可见");
document.Models.ResetAllHidden();
return;
}
// 使用成熟的可见性控制模式 - 分别收集要显示和隐藏的项目
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 (Exception ex)
{
LogManager.Warning($"[分层管理器] 处理项目可见性状态时出错: {ex.Message}");
}
}
// 首先重置所有项目为可见状态
document.Models.ResetAllHidden();
// 然后隐藏原来应该隐藏的项目
if (itemsToHide.Count > 0)
{
document.Models.SetHidden(itemsToHide, true);
LogManager.Info($"[分层管理器] 恢复隐藏 {itemsToHide.Count} 个项目");
}
LogManager.Info("[SimplifiedModelSplitter] 可见性状态恢复完成");
}
catch (Exception ex)
{
LogManager.Error($"[分层管理器] 恢复可见性状态失败: {ex.Message}");
// 失败时至少确保模型处于可见状态
try
{
document.Models.ResetAllHidden();
LogManager.Info("[SimplifiedModelSplitter] 已重置为全部可见状态");
}
catch (Exception resetEx)
{
LogManager.Error($"[分层管理器] 重置可见性也失败: {resetEx.Message}");
}
}
}
#endregion
#region
/// <summary>
/// 获取策略的显示名称
/// </summary>
/// <param name="strategy">分层策略</param>
/// <returns>显示名称</returns>
private string GetStrategyDisplayName(SplitStrategy strategy)
{
switch (strategy)
{
case SplitStrategy.BySmartFloorDetection:
return "智能楼层检测分析";
case SplitStrategy.ByFloorAttribute:
return "按楼层属性分析";
case SplitStrategy.ByZoneAttribute:
return "按区域属性分析";
case SplitStrategy.BySubSystemAttribute:
return "按子系统属性分析";
default:
return "分层分析";
}
}
#endregion
#region
protected virtual void OnProgressChanged(ProgressChangedEventArgs e)
{
ProgressChanged?.Invoke(this, e);
}
protected virtual void OnStatusChanged(string status)
{
StatusChanged?.Invoke(this, status);
}
#endregion
}
}