903 lines
35 KiB
C#
903 lines
35 KiB
C#
using Autodesk.Navisworks.Api;
|
||
using System;
|
||
using System.IO;
|
||
using System.Threading.Tasks;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using ComApi = Autodesk.Navisworks.Api.Interop.ComApi;
|
||
using ComApiBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge;
|
||
|
||
namespace NavisworksTransport
|
||
{
|
||
/// <summary>
|
||
/// Navisworks文件导出器 - 负责将模型元素导出为独立的Navisworks文件
|
||
/// </summary>
|
||
public class NavisworksFileExporter
|
||
{
|
||
#region 枚举和常量
|
||
|
||
/// <summary>
|
||
/// 导出策略枚举
|
||
/// </summary>
|
||
public enum ExportStrategy
|
||
{
|
||
VisibilityControl, // 通过可见性控制
|
||
SelectionBased, // 基于选择集
|
||
CopyToNewDocument // 复制到新文档
|
||
}
|
||
|
||
/// <summary>
|
||
/// 导出配置
|
||
/// </summary>
|
||
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<string> StatusChanged;
|
||
public event EventHandler<int> ProgressChanged;
|
||
public event EventHandler<Exception> ErrorOccurred;
|
||
|
||
#endregion
|
||
|
||
#region 私有字段
|
||
|
||
private readonly VisibilityManager _visibilityManager;
|
||
private bool _isExporting = false;
|
||
|
||
#endregion
|
||
|
||
#region 构造函数
|
||
|
||
public NavisworksFileExporter()
|
||
{
|
||
_visibilityManager = new VisibilityManager();
|
||
LogManager.Info("[FileExporter] Navisworks文件导出器已初始化");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 公共方法
|
||
|
||
/// <summary>
|
||
/// 异步导出模型元素到文件
|
||
/// </summary>
|
||
/// <param name="items">要导出的模型元素</param>
|
||
/// <param name="outputPath">输出文件路径</param>
|
||
/// <param name="config">导出配置</param>
|
||
/// <returns>导出是否成功</returns>
|
||
public async Task<bool> 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}");
|
||
LogManager.Info($"[FileExporter] 创建备份: {config.CreateBackup}");
|
||
LogManager.Info($"[FileExporter] 超时时间: {config.TimeoutSeconds}秒");
|
||
|
||
OnStatusChanged($"开始导出 {items.Count} 个元素到 {Path.GetFileName(outputPath)}");
|
||
|
||
// 记录系统状态
|
||
LogManager.Info($"[FileExporter] 导出前内存使用: {GC.GetTotalMemory(false) / 1024 / 1024} MB");
|
||
LogManager.Info($"[FileExporter] 导出前进程内存: {System.Diagnostics.Process.GetCurrentProcess().WorkingSet64 / 1024 / 1024} MB");
|
||
|
||
// 验证输入参数
|
||
LogManager.Info("[FileExporter] 开始验证输入参数...");
|
||
ValidateExportParameters(items, outputPath);
|
||
LogManager.Info("[FileExporter] 输入参数验证完成");
|
||
|
||
// 创建输出目录
|
||
LogManager.Info("[FileExporter] 开始创建输出目录...");
|
||
EnsureOutputDirectory(outputPath);
|
||
LogManager.Info("[FileExporter] 输出目录创建完成");
|
||
|
||
// 根据策略执行导出
|
||
LogManager.Info($"[FileExporter] 开始执行导出,策略: {config.Strategy}");
|
||
bool success = await ExecuteExportAsync(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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 同步导出模型元素到文件
|
||
/// </summary>
|
||
/// <param name="items">要导出的模型元素</param>
|
||
/// <param name="outputPath">输出文件路径</param>
|
||
/// <param name="config">导出配置</param>
|
||
/// <returns>导出是否成功</returns>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 批量导出多个分组
|
||
/// </summary>
|
||
/// <param name="exportTasks">导出任务列表</param>
|
||
/// <param name="config">导出配置</param>
|
||
/// <returns>导出结果列表</returns>
|
||
public async Task<List<ExportResult>> BatchExportAsync(List<ExportTask> exportTasks, ExportConfiguration config = null)
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info($"[FileExporter] 开始批量导出,任务数量: {exportTasks.Count}");
|
||
|
||
var results = new List<ExportResult>();
|
||
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 数据结构
|
||
|
||
/// <summary>
|
||
/// 导出任务
|
||
/// </summary>
|
||
public class ExportTask
|
||
{
|
||
public string LayerName { get; set; }
|
||
public ModelItemCollection Items { get; set; }
|
||
public string OutputPath { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 导出结果
|
||
/// </summary>
|
||
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<bool> 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<bool> 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<bool> 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<bool> 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}");
|
||
}
|
||
|
||
// 关键修复:使用更安全的保存方法
|
||
bool success = false;
|
||
int retryCount = 0;
|
||
const int maxRetries = 3;
|
||
|
||
while (!success && retryCount < maxRetries)
|
||
{
|
||
try
|
||
{
|
||
retryCount++;
|
||
LogManager.Info($"[FileExporter] 尝试保存文档,第 {retryCount} 次");
|
||
|
||
// 确保文档处于稳定状态
|
||
if (document.Models.Count == 0)
|
||
{
|
||
throw new InvalidOperationException("文档中没有模型数据");
|
||
}
|
||
|
||
// 等待一小段时间确保状态稳定
|
||
System.Threading.Thread.Sleep(100);
|
||
|
||
// 根据文件格式选择保存方法
|
||
string extension = Path.GetExtension(outputPath).ToLower();
|
||
|
||
switch (extension)
|
||
{
|
||
case ".nwd":
|
||
// 使用最安全的NWD格式保存
|
||
document.SaveFile(outputPath);
|
||
break;
|
||
case ".nwf":
|
||
// NWF格式转换为NWD格式(更稳定)
|
||
LogManager.Warning("[FileExporter] NWF格式可能不稳定,转换为NWD格式");
|
||
string nwdPath = Path.ChangeExtension(outputPath, ".nwd");
|
||
document.SaveFile(nwdPath);
|
||
// 如果需要NWF格式,可以后续转换
|
||
if (File.Exists(nwdPath))
|
||
{
|
||
File.Move(nwdPath, outputPath);
|
||
}
|
||
break;
|
||
case ".nwc":
|
||
// NWC格式转换为NWD格式(更稳定)
|
||
LogManager.Warning("[FileExporter] NWC格式可能不稳定,转换为NWD格式");
|
||
string nwdPath2 = Path.ChangeExtension(outputPath, ".nwd");
|
||
document.SaveFile(nwdPath2);
|
||
if (File.Exists(nwdPath2))
|
||
{
|
||
File.Move(nwdPath2, outputPath);
|
||
}
|
||
break;
|
||
default:
|
||
// 默认使用NWD格式(最稳定)
|
||
document.SaveFile(outputPath);
|
||
break;
|
||
}
|
||
|
||
// 验证文件是否成功保存
|
||
if (File.Exists(outputPath))
|
||
{
|
||
var fileInfo = new FileInfo(outputPath);
|
||
if (fileInfo.Length > 0)
|
||
{
|
||
success = true;
|
||
LogManager.Info($"[FileExporter] 文档保存成功: {outputPath}, 大小: {fileInfo.Length} 字节");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning($"[FileExporter] 保存的文件大小为0: {outputPath}");
|
||
File.Delete(outputPath); // 删除空文件
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning($"[FileExporter] 保存后文件不存在: {outputPath}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[FileExporter] 第 {retryCount} 次保存尝试失败: {ex.Message}");
|
||
|
||
// 如果不是最后一次尝试,等待后重试
|
||
if (retryCount < maxRetries)
|
||
{
|
||
LogManager.Info($"[FileExporter] 等待 {retryCount * 1000}ms 后重试...");
|
||
System.Threading.Thread.Sleep(retryCount * 1000);
|
||
|
||
// 尝试清理可能的临时状态
|
||
try
|
||
{
|
||
if (File.Exists(outputPath))
|
||
{
|
||
var fileInfo = new FileInfo(outputPath);
|
||
if (fileInfo.Length == 0)
|
||
{
|
||
File.Delete(outputPath);
|
||
}
|
||
}
|
||
}
|
||
catch (Exception cleanupEx)
|
||
{
|
||
LogManager.Warning($"[FileExporter] 清理临时文件失败: {cleanupEx.Message}");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!success)
|
||
{
|
||
LogManager.Error($"[FileExporter] 经过 {maxRetries} 次尝试后仍然保存失败: {outputPath}");
|
||
}
|
||
|
||
return success;
|
||
}
|
||
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<ModelItem, bool> SaveCurrentVisibilityState()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("[FileExporter] 保存当前可见性状态");
|
||
|
||
var visibilityState = new Dictionary<ModelItem, bool>();
|
||
var document = Application.ActiveDocument;
|
||
|
||
if (document?.Models != null)
|
||
{
|
||
var allItems = document.Models.RootItemDescendantsAndSelf;
|
||
|
||
// 直接执行,避免异步复杂性(.NET Framework 4.6兼容)
|
||
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<ModelItem, bool>();
|
||
}
|
||
}
|
||
|
||
private void RestoreVisibilityState(Dictionary<ModelItem, bool> 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}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查一个模型项或其子项是否包含目标元素中的任何一个
|
||
/// </summary>
|
||
/// <param name="rootItem">要检查的根模型项</param>
|
||
/// <param name="targetItems">目标元素集合</param>
|
||
/// <returns>如果包含则返回true</returns>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 递归检查子项目,带深度限制
|
||
/// </summary>
|
||
/// <param name="item">当前检查的项目</param>
|
||
/// <param name="targetItems">目标元素集合</param>
|
||
/// <param name="currentDepth">当前深度</param>
|
||
/// <param name="maxDepth">最大深度</param>
|
||
/// <returns>如果包含则返回true</returns>
|
||
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
|
||
}
|
||
} |