NavisworksTransport/ModelSplitterManager.cs

1054 lines
40 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.Linq;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Text;
using ComApi = Autodesk.Navisworks.Api.Interop.ComApi;
using ComApiBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge;
using NavisApplication = Autodesk.Navisworks.Api.Application;
namespace NavisworksTransport
{
/// <summary>
/// 模型分层拆分管理器 - 负责将大型建筑模型按楼层或自定义属性拆分为多个独立文件
/// </summary>
public class ModelSplitterManager
{
#region
/// <summary>
/// 分层策略枚举
/// </summary>
public enum SplitStrategy
{
ByFloor, // 按楼层
ByCustomAttribute, // 按自定义属性
ByCategory, // 按类别
BySelection, // 按选择集
ByElevation // 按高程范围
}
/// <summary>
/// 分层配置类
/// </summary>
public class SplitConfiguration
{
public SplitStrategy Strategy { get; set; } = SplitStrategy.ByFloor;
public string AttributeName { get; set; } = "Level";
public string OutputDirectory { get; set; } = "";
public string FileNamePattern { get; set; } = "{ProjectName}_{LayerName}";
public bool IncludeEmptyLayers { get; set; } = false;
public bool PreserveOriginalStructure { get; set; } = true;
public double ElevationTolerance { get; set; } = 0.5;
public double MinFloorHeight { get; set; } = 2.5;
public int MaxLayersPerFile { get; set; } = 1;
public bool GenerateReport { get; set; } = true;
public bool CreateSubDirectories { get; set; } = true;
}
/// <summary>
/// 分层结果类
/// </summary>
public class SplitResult
{
public string LayerName { get; set; }
public ModelItemCollection Items { get; set; }
public string OutputFilePath { 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; }
public Dictionary<string, object> Metadata { get; set; }
public SplitResult()
{
Items = new ModelItemCollection();
Metadata = new Dictionary<string, object>();
ProcessTime = DateTime.Now;
}
}
/// <summary>
/// 楼层信息类
/// </summary>
public class FloorInfo
{
public string FloorName { get; set; }
public double Elevation { get; set; }
public BoundingBox3D Bounds { get; set; }
public ModelItemCollection Items { get; set; }
public int ItemCount => Items?.Count ?? 0;
public Dictionary<string, object> Properties { get; set; }
public FloorInfo()
{
Items = new ModelItemCollection();
Properties = new Dictionary<string, object>();
}
}
#endregion
#region
public event EventHandler<ProgressChangedEventArgs> ProgressChanged;
public event EventHandler<SplitResult> LayerProcessed;
public event EventHandler<List<SplitResult>> SplitCompleted;
public event EventHandler<string> StatusChanged;
public event EventHandler<Exception> ErrorOccurred;
#endregion
#region
private readonly FloorDetector _floorDetector;
private readonly AttributeGrouper _attributeGrouper;
private readonly NavisworksFileExporter _fileExporter;
private bool _isSplitting = false;
#endregion
#region
public ModelSplitterManager()
{
_floorDetector = new FloorDetector();
_attributeGrouper = new AttributeGrouper();
_fileExporter = new NavisworksFileExporter();
LogManager.Info("[ModelSplitter] 模型分层管理器已初始化");
}
#endregion
#region
/// <summary>
/// 预览分层结果
/// </summary>
public List<SplitResult> PreviewSplit(SplitConfiguration config)
{
try
{
LogManager.Info($"[ModelSplitter] 开始预览分层,策略: {config.Strategy}");
OnStatusChanged("正在分析模型结构...");
var allItems = GetAllModelItems();
if (allItems.Count == 0)
{
throw new InvalidOperationException("当前文档中没有模型元素");
}
var previewResults = new List<SplitResult>();
switch (config.Strategy)
{
case SplitStrategy.ByFloor:
previewResults = PreviewSplitByFloor(allItems, config);
break;
case SplitStrategy.ByCustomAttribute:
previewResults = PreviewSplitByAttribute(allItems, config);
break;
case SplitStrategy.ByCategory:
previewResults = PreviewSplitByCategory(allItems, config);
break;
case SplitStrategy.ByElevation:
previewResults = PreviewSplitByElevation(allItems, config);
break;
default:
throw new NotSupportedException($"不支持的分层策略: {config.Strategy}");
}
LogManager.Info($"[ModelSplitter] 预览完成,共识别 {previewResults.Count} 个分层");
OnStatusChanged($"预览完成,识别到 {previewResults.Count} 个分层");
return previewResults;
}
catch (Exception ex)
{
LogManager.Error($"[ModelSplitter] 预览分层失败: {ex.Message}");
OnErrorOccurred(ex);
throw;
}
}
/// <summary>
/// 执行分层拆分
/// </summary>
public async Task<List<SplitResult>> ExecuteSplitAsync(SplitConfiguration config)
{
if (_isSplitting)
{
throw new InvalidOperationException("分层操作正在进行中");
}
try
{
_isSplitting = true;
// 写入会话分隔符,标记新的分层操作开始
LogManager.WriteSessionSeparator();
LogManager.Info($"[ModelSplitter] ========== 开始执行分层拆分 ==========");
LogManager.Info($"[ModelSplitter] 分层策略: {config.Strategy}");
LogManager.Info($"[ModelSplitter] 输出目录: {config.OutputDirectory}");
LogManager.Info($"[ModelSplitter] 属性名称: {config.AttributeName}");
LogManager.Info($"[ModelSplitter] 文件命名模式: {config.FileNamePattern}");
LogManager.Info($"[ModelSplitter] 包含空分层: {config.IncludeEmptyLayers}");
LogManager.Info($"[ModelSplitter] 创建子目录: {config.CreateSubDirectories}");
LogManager.Info($"[ModelSplitter] 生成报告: {config.GenerateReport}");
OnStatusChanged("正在执行分层拆分...");
// 验证配置
LogManager.Info("[ModelSplitter] 开始验证配置...");
ValidateConfiguration(config);
LogManager.Info("[ModelSplitter] 配置验证完成");
// 创建输出目录
LogManager.Info("[ModelSplitter] 开始创建输出目录...");
EnsureOutputDirectory(config.OutputDirectory);
LogManager.Info("[ModelSplitter] 输出目录创建完成");
// 获取预览结果
LogManager.Info("[ModelSplitter] 开始获取预览结果...");
var previewResults = PreviewSplit(config);
LogManager.Info($"[ModelSplitter] 预览结果获取完成,共 {previewResults.Count} 个分层");
var results = new List<SplitResult>();
int processedCount = 0;
int totalCount = previewResults.Count;
// 记录系统状态
LogManager.Info($"[ModelSplitter] 系统内存使用: {GC.GetTotalMemory(false) / 1024 / 1024} MB");
LogManager.Info($"[ModelSplitter] 当前进程内存: {System.Diagnostics.Process.GetCurrentProcess().WorkingSet64 / 1024 / 1024} MB");
// 关键修复:添加内存管理和错误恢复机制
foreach (var preview in previewResults)
{
try
{
OnStatusChanged($"正在处理分层: {preview.LayerName} ({processedCount + 1}/{totalCount})");
OnProgressChanged(new ProgressChangedEventArgs(
(int)((double)processedCount / totalCount * 100),
$"处理分层 {processedCount + 1}/{totalCount}"));
// 在每个分层处理前确保Navisworks处于稳定状态
EnsureStableState();
var result = await ProcessSingleLayerAsync(preview, config);
results.Add(result);
OnLayerProcessed(result);
processedCount++;
LogManager.Info($"[ModelSplitter] 已处理分层: {result.LayerName}, 成功: {result.Success}");
// 在每个分层处理后,强制垃圾回收以释放内存
if (processedCount % 3 == 0) // 每处理3个分层进行一次垃圾回收
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}
catch (Exception ex)
{
LogManager.Error($"[ModelSplitter] 处理分层失败: {preview.LayerName}, 错误: {ex.Message}");
// 尝试恢复Navisworks状态
try
{
RecoverFromError();
}
catch (Exception recoverEx)
{
LogManager.Error($"[ModelSplitter] 状态恢复失败: {recoverEx.Message}");
}
var errorResult = new SplitResult
{
LayerName = preview.LayerName,
Items = preview.Items,
Success = false,
ErrorMessage = ex.Message
};
results.Add(errorResult);
// 如果连续失败超过一定次数,停止处理
int consecutiveFailures = results.Skip(Math.Max(0, results.Count - 3)).Count(r => !r.Success);
if (consecutiveFailures >= 3)
{
LogManager.Warning("[ModelSplitter] 连续失败次数过多,停止处理剩余分层");
OnStatusChanged("检测到连续失败,停止处理以保护系统稳定性");
break;
}
}
}
// 生成汇总报告
if (config.GenerateReport)
{
try
{
await GenerateSummaryReportAsync(results, config);
}
catch (Exception reportEx)
{
LogManager.Warning($"[ModelSplitter] 生成报告失败: {reportEx.Message}");
}
}
OnProgressChanged(new ProgressChangedEventArgs(100, "分层拆分完成"));
OnStatusChanged($"分层拆分完成,成功: {results.Count(r => r.Success)}, 失败: {results.Count(r => !r.Success)}");
OnSplitCompleted(results);
LogManager.Info($"[ModelSplitter] 分层拆分完成,总计: {results.Count}, 成功: {results.Count(r => r.Success)}");
return results;
}
catch (Exception ex)
{
LogManager.Error($"[ModelSplitter] 执行分层拆分失败: {ex.Message}");
// 尝试最终恢复
try
{
RecoverFromError();
}
catch (Exception recoverEx)
{
LogManager.Error($"[ModelSplitter] 最终状态恢复失败: {recoverEx.Message}");
}
OnErrorOccurred(ex);
throw;
}
finally
{
_isSplitting = false;
// 最终清理
try
{
FinalCleanup();
}
catch (Exception cleanupEx)
{
LogManager.Warning($"[ModelSplitter] 最终清理失败: {cleanupEx.Message}");
}
}
}
/// <summary>
/// 获取可用的楼层属性列表
/// </summary>
public List<string> GetAvailableFloorAttributes()
{
try
{
var allItems = GetAllModelItems();
return _floorDetector.GetAvailableFloorAttributes(allItems);
}
catch (Exception ex)
{
LogManager.Error($"[ModelSplitter] 获取楼层属性失败: {ex.Message}");
return new List<string>();
}
}
/// <summary>
/// 获取可用的自定义属性列表
/// </summary>
public List<string> GetAvailableCustomAttributes()
{
try
{
var allItems = GetAllModelItems();
return _attributeGrouper.GetAvailableAttributes(allItems);
}
catch (Exception ex)
{
LogManager.Error($"[ModelSplitter] 获取自定义属性失败: {ex.Message}");
return new List<string>();
}
}
#endregion
#region -
private List<SplitResult> PreviewSplitByFloor(ModelItemCollection allItems, SplitConfiguration config)
{
var floors = _floorDetector.DetectFloors(allItems, config.AttributeName);
var results = new List<SplitResult>();
foreach (var floor in floors)
{
if (!config.IncludeEmptyLayers && floor.ItemCount == 0)
continue;
var result = new SplitResult
{
LayerName = floor.FloorName,
Items = floor.Items,
ItemCount = floor.ItemCount,
OutputFilePath = GenerateOutputFilePath(floor.FloorName, config),
Metadata = new Dictionary<string, object>
{
["Elevation"] = floor.Elevation,
["Bounds"] = floor.Bounds,
["Properties"] = floor.Properties
}
};
results.Add(result);
}
return results;
}
private List<SplitResult> PreviewSplitByAttribute(ModelItemCollection allItems, SplitConfiguration config)
{
var groups = _attributeGrouper.GroupByAttribute(allItems, config.AttributeName);
var results = new List<SplitResult>();
foreach (var group in groups)
{
if (!config.IncludeEmptyLayers && group.Items.Count == 0)
continue;
var result = new SplitResult
{
LayerName = SanitizeLayerName(group.AttributeValue),
Items = group.Items,
ItemCount = group.Items.Count,
OutputFilePath = GenerateOutputFilePath(group.AttributeValue, config),
Metadata = group.Metadata
};
results.Add(result);
}
return results;
}
private List<SplitResult> PreviewSplitByCategory(ModelItemCollection allItems, SplitConfiguration config)
{
var categoryGroups = new Dictionary<string, ModelItemCollection>();
foreach (ModelItem item in allItems)
{
string category = GetItemCategory(item);
if (string.IsNullOrEmpty(category))
category = "未分类";
if (!categoryGroups.ContainsKey(category))
{
categoryGroups[category] = new ModelItemCollection();
}
categoryGroups[category].Add(item);
}
var results = new List<SplitResult>();
foreach (var kvp in categoryGroups)
{
if (!config.IncludeEmptyLayers && kvp.Value.Count == 0)
continue;
var result = new SplitResult
{
LayerName = kvp.Key,
Items = kvp.Value,
ItemCount = kvp.Value.Count,
OutputFilePath = GenerateOutputFilePath(kvp.Key, config)
};
results.Add(result);
}
return results;
}
private List<SplitResult> PreviewSplitByElevation(ModelItemCollection allItems, SplitConfiguration config)
{
var elevationGroups = new Dictionary<string, ModelItemCollection>();
foreach (ModelItem item in allItems)
{
var bounds = item.BoundingBox();
if (bounds.HasVolume)
{
double elevation = bounds.Min.Z;
string elevationRange = GetElevationRange(elevation, config.MinFloorHeight);
if (!elevationGroups.ContainsKey(elevationRange))
{
elevationGroups[elevationRange] = new ModelItemCollection();
}
elevationGroups[elevationRange].Add(item);
}
}
var results = new List<SplitResult>();
foreach (var kvp in elevationGroups.OrderBy(x => x.Key))
{
if (!config.IncludeEmptyLayers && kvp.Value.Count == 0)
continue;
var result = new SplitResult
{
LayerName = $"Elevation_{kvp.Key}",
Items = kvp.Value,
ItemCount = kvp.Value.Count,
OutputFilePath = GenerateOutputFilePath($"Elevation_{kvp.Key}", config)
};
results.Add(result);
}
return results;
}
#endregion
#region -
private async Task<SplitResult> ProcessSingleLayerAsync(SplitResult preview, SplitConfiguration config)
{
LogManager.Info($"[ModelSplitter] ========== 开始处理分层: {preview.LayerName} ==========");
LogManager.Info($"[ModelSplitter] 分层元素数量: {preview.ItemCount}");
LogManager.Info($"[ModelSplitter] 输出文件路径: {preview.OutputFilePath}");
var result = new SplitResult
{
LayerName = preview.LayerName,
Items = preview.Items,
ItemCount = preview.ItemCount,
OutputFilePath = preview.OutputFilePath,
Metadata = preview.Metadata
};
try
{
// 记录处理前的内存状态
LogManager.Info($"[ModelSplitter] 处理前内存使用: {GC.GetTotalMemory(false) / 1024 / 1024} MB");
// 创建子目录
if (config.CreateSubDirectories)
{
LogManager.Info("[ModelSplitter] 开始创建子目录...");
string layerDir = Path.Combine(config.OutputDirectory, SanitizeFileName(preview.LayerName));
Directory.CreateDirectory(layerDir);
result.OutputFilePath = Path.Combine(layerDir, Path.GetFileName(result.OutputFilePath));
LogManager.Info($"[ModelSplitter] 子目录创建完成: {layerDir}");
}
// 导出文件
LogManager.Info("[ModelSplitter] 开始导出文件...");
LogManager.Info($"[ModelSplitter] 目标文件: {result.OutputFilePath}");
bool exportSuccess = await _fileExporter.ExportModelItemsAsync(preview.Items, result.OutputFilePath);
LogManager.Info($"[ModelSplitter] 文件导出结果: {exportSuccess}");
if (exportSuccess && File.Exists(result.OutputFilePath))
{
result.Success = true;
result.FileSizeBytes = new FileInfo(result.OutputFilePath).Length;
LogManager.Info($"[ModelSplitter] 导出成功,文件大小: {result.FileSizeBytes / 1024.0 / 1024.0:F2} MB");
// 生成元数据文件
if (config.GenerateReport)
{
LogManager.Info("[ModelSplitter] 开始生成元数据文件...");
await GenerateLayerMetadataAsync(result, config);
LogManager.Info("[ModelSplitter] 元数据文件生成完成");
}
}
else
{
result.Success = false;
result.ErrorMessage = "文件导出失败";
LogManager.Error($"[ModelSplitter] 文件导出失败: exportSuccess={exportSuccess}, 文件存在={File.Exists(result.OutputFilePath)}");
}
// 记录处理后的内存状态
LogManager.Info($"[ModelSplitter] 处理后内存使用: {GC.GetTotalMemory(false) / 1024 / 1024} MB");
}
catch (Exception ex)
{
result.Success = false;
result.ErrorMessage = ex.Message;
LogManager.Error($"[ModelSplitter] 处理分层异常: {ex.Message}");
LogManager.Error($"[ModelSplitter] 异常堆栈: {ex.StackTrace}");
}
LogManager.Info($"[ModelSplitter] ========== 分层处理完成: {preview.LayerName}, 成功: {result.Success} ==========");
return result;
}
private async Task GenerateLayerMetadataAsync(SplitResult result, SplitConfiguration config)
{
try
{
string metadataPath = Path.ChangeExtension(result.OutputFilePath, ".json");
var metadata = new
{
LayerName = result.LayerName,
ItemCount = result.ItemCount,
FileSizeBytes = result.FileSizeBytes,
ProcessTime = result.ProcessTime,
Configuration = new
{
Strategy = config.Strategy.ToString(),
AttributeName = config.AttributeName,
ElevationTolerance = config.ElevationTolerance
},
Properties = result.Metadata
};
// 使用简单的JSON格式化避免依赖外部库
var jsonBuilder = new StringBuilder();
jsonBuilder.AppendLine("{");
jsonBuilder.AppendLine($" \"LayerName\": \"{EscapeJsonString(result.LayerName)}\",");
jsonBuilder.AppendLine($" \"ItemCount\": {result.ItemCount},");
jsonBuilder.AppendLine($" \"FileSizeBytes\": {result.FileSizeBytes},");
jsonBuilder.AppendLine($" \"ProcessTime\": \"{result.ProcessTime:yyyy-MM-dd HH:mm:ss}\",");
jsonBuilder.AppendLine(" \"Configuration\": {");
jsonBuilder.AppendLine($" \"Strategy\": \"{config.Strategy}\",");
jsonBuilder.AppendLine($" \"AttributeName\": \"{EscapeJsonString(config.AttributeName)}\",");
jsonBuilder.AppendLine($" \"ElevationTolerance\": {config.ElevationTolerance}");
jsonBuilder.AppendLine(" }");
jsonBuilder.AppendLine("}");
await Task.Run(() => File.WriteAllText(metadataPath, jsonBuilder.ToString()));
}
catch (Exception ex)
{
LogManager.Warning($"[ModelSplitter] 生成元数据文件失败: {ex.Message}");
}
}
private async Task GenerateSummaryReportAsync(List<SplitResult> results, SplitConfiguration config)
{
try
{
string reportPath = Path.Combine(config.OutputDirectory, "split_summary_report.html");
var html = await Task.Run(() => GenerateHtmlReport(results, config));
await Task.Run(() => File.WriteAllText(reportPath, html));
LogManager.Info($"[ModelSplitter] 汇总报告已生成: {reportPath}");
}
catch (Exception ex)
{
LogManager.Warning($"[ModelSplitter] 生成汇总报告失败: {ex.Message}");
}
}
#endregion
#region -
private ModelItemCollection GetAllModelItems()
{
var document = NavisApplication.ActiveDocument;
if (document?.Models == null || document.Models.Count == 0)
{
return new ModelItemCollection();
}
var allItems = new ModelItemCollection();
allItems.AddRange(document.Models.RootItemDescendantsAndSelf);
return allItems;
}
private void ValidateConfiguration(SplitConfiguration config)
{
if (string.IsNullOrEmpty(config.OutputDirectory))
{
throw new ArgumentException("输出目录不能为空");
}
if (config.Strategy == SplitStrategy.ByCustomAttribute && string.IsNullOrEmpty(config.AttributeName))
{
throw new ArgumentException("按自定义属性分层时,属性名称不能为空");
}
if (!Directory.Exists(Path.GetDirectoryName(config.OutputDirectory)))
{
throw new DirectoryNotFoundException($"输出目录的父目录不存在: {Path.GetDirectoryName(config.OutputDirectory)}");
}
}
private void EnsureOutputDirectory(string outputDirectory)
{
if (!Directory.Exists(outputDirectory))
{
Directory.CreateDirectory(outputDirectory);
LogManager.Info($"[ModelSplitter] 已创建输出目录: {outputDirectory}");
}
}
private string GenerateOutputFilePath(string layerName, SplitConfiguration config)
{
string sanitizedLayerName = SanitizeFileName(layerName);
string projectName = GetProjectName();
string fileName = config.FileNamePattern
.Replace("{ProjectName}", projectName)
.Replace("{LayerName}", sanitizedLayerName)
.Replace("{DateTime}", DateTime.Now.ToString("yyyyMMdd_HHmmss"));
return Path.Combine(config.OutputDirectory, fileName + ".nwd");
}
private string GetProjectName()
{
var document = Application.ActiveDocument;
if (document?.FileName != null)
{
return Path.GetFileNameWithoutExtension(document.FileName);
}
return "NavisworksModel";
}
private string SanitizeFileName(string fileName)
{
if (string.IsNullOrEmpty(fileName))
return "Unknown";
char[] invalidChars = Path.GetInvalidFileNameChars();
foreach (char c in invalidChars)
{
fileName = fileName.Replace(c, '_');
}
return fileName.Trim();
}
private string SanitizeLayerName(string layerName)
{
if (string.IsNullOrEmpty(layerName))
return "Unknown";
return layerName.Trim().Replace(" ", "_");
}
private string GetItemCategory(ModelItem item)
{
try
{
// 尝试从属性中获取类别信息
foreach (PropertyCategory category in item.PropertyCategories)
{
foreach (DataProperty property in category.Properties)
{
if (property.DisplayName.ToLower().Contains("category") ||
property.DisplayName.ToLower().Contains("type") ||
property.DisplayName.ToLower().Contains("family"))
{
return property.Value.ToString();
}
}
}
// 如果没有找到类别属性,使用显示名称的第一部分
string displayName = item.DisplayName;
if (!string.IsNullOrEmpty(displayName))
{
var parts = displayName.Split(':', '_', '-');
if (parts.Length > 0)
{
return parts[0].Trim();
}
}
return "未分类";
}
catch
{
return "未分类";
}
}
private string GetElevationRange(double elevation, double rangeSize)
{
double rangeStart = Math.Floor(elevation / rangeSize) * rangeSize;
double rangeEnd = rangeStart + rangeSize;
return $"{rangeStart:F1}_{rangeEnd:F1}";
}
private string EscapeJsonString(string input)
{
if (string.IsNullOrEmpty(input))
return "";
return input.Replace("\\", "\\\\")
.Replace("\"", "\\\"")
.Replace("\r", "\\r")
.Replace("\n", "\\n")
.Replace("\t", "\\t");
}
private string GenerateHtmlReport(List<SplitResult> results, SplitConfiguration config)
{
var html = $@"
<!DOCTYPE html>
<html>
<head>
<title>模型分层拆分报告</title>
<meta charset='utf-8'>
<style>
body {{ font-family: Arial, sans-serif; margin: 20px; }}
.header {{ background-color: #f0f0f0; padding: 15px; border-radius: 5px; }}
.summary {{ margin: 20px 0; }}
.results {{ margin-top: 20px; }}
table {{ border-collapse: collapse; width: 100%; }}
th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
th {{ background-color: #f2f2f2; }}
.success {{ color: green; }}
.error {{ color: red; }}
</style>
</head>
<body>
<div class='header'>
<h1>模型分层拆分报告</h1>
<p>生成时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}</p>
<p>分层策略: {config.Strategy}</p>
<p>输出目录: {config.OutputDirectory}</p>
</div>
<div class='summary'>
<h2>汇总信息</h2>
<p>总分层数: {results.Count}</p>
<p>成功: <span class='success'>{results.Count(r => r.Success)}</span></p>
<p>失败: <span class='error'>{results.Count(r => !r.Success)}</span></p>
<p>总文件大小: {results.Where(r => r.Success).Sum(r => r.FileSizeBytes) / 1024.0 / 1024.0:F2} MB</p>
</div>
<div class='results'>
<h2>详细结果</h2>
<table>
<tr>
<th>分层名称</th>
<th>元素数量</th>
<th>文件大小(MB)</th>
<th>状态</th>
<th>输出文件</th>
</tr>";
foreach (var result in results)
{
string statusClass = result.Success ? "success" : "error";
string status = result.Success ? "成功" : $"失败: {result.ErrorMessage}";
double fileSizeMB = result.FileSizeBytes / 1024.0 / 1024.0;
html += $@"
<tr>
<td>{result.LayerName}</td>
<td>{result.ItemCount}</td>
<td>{fileSizeMB:F2}</td>
<td class='{statusClass}'>{status}</td>
<td>{Path.GetFileName(result.OutputFilePath)}</td>
</tr>";
}
html += @"
</table>
</div>
</body>
</html>";
return html;
}
/// <summary>
/// 确保Navisworks处于稳定状态 - 使用最安全的方法
/// </summary>
private void EnsureStableState()
{
try
{
LogManager.Info("[ModelSplitter] 开始确保Navisworks稳定状态...");
var document = NavisApplication.ActiveDocument;
if (document != null)
{
LogManager.Info($"[ModelSplitter] 当前文档: {document.FileName ?? ""}");
LogManager.Info($"[ModelSplitter] 模型数量: {document.Models?.Count ?? 0}");
// 记录当前状态
if (document.CurrentSelection != null)
{
LogManager.Info($"[ModelSplitter] 当前选择项数量: {document.CurrentSelection.SelectedItems.Count}");
}
// 关键修复:完全跳过可见性重置,避免崩溃
LogManager.Info("[ModelSplitter] 跳过可见性重置以避免崩溃风险");
LogManager.Info("[ModelSplitter] 使用选择集策略替代可见性控制");
// 只进行安全的操作
try
{
// 清除当前选择,这是安全的操作
LogManager.Info("[ModelSplitter] 清除当前选择...");
document.CurrentSelection.Clear();
LogManager.Info("[ModelSplitter] 选择清除完成");
// 短暂等待让Navisworks稳定
LogManager.Info("[ModelSplitter] 等待系统稳定...");
System.Threading.Thread.Sleep(100);
LogManager.Info("[ModelSplitter] Navisworks稳定状态确保完成");
}
catch (Exception safeEx)
{
LogManager.Warning($"[ModelSplitter] 安全操作失败: {safeEx.Message}");
LogManager.Info("[ModelSplitter] 继续处理,忽略稳定状态错误");
}
}
else
{
LogManager.Warning("[ModelSplitter] 当前没有活动的Navisworks文档");
}
}
catch (Exception ex)
{
LogManager.Error($"[ModelSplitter] 确保稳定状态失败: {ex.Message}");
LogManager.Error($"[ModelSplitter] 稳定状态异常堆栈: {ex.StackTrace}");
LogManager.Info("[ModelSplitter] 继续处理,忽略稳定状态错误");
}
}
/// <summary>
/// 从错误中恢复
/// </summary>
private void RecoverFromError()
{
try
{
LogManager.Info("[ModelSplitter] 开始错误恢复...");
var document = NavisApplication.ActiveDocument;
if (document != null)
{
// 使用安全的可见性重置方法
LogManager.Info("[ModelSplitter] 错误恢复:重置可见性状态...");
try
{
document.Models.ResetAllHidden();
LogManager.Info("[ModelSplitter] 错误恢复ResetAllHidden成功");
}
catch (Exception resetEx)
{
LogManager.Warning($"[ModelSplitter] 错误恢复ResetAllHidden失败: {resetEx.Message}");
// 在错误恢复中,如果重置失败就跳过,不再尝试替代方法
LogManager.Info("[ModelSplitter] 错误恢复:跳过可见性重置");
}
// 清除选择
document.CurrentSelection.Clear();
// 强制刷新视图
document.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All);
// 等待恢复完成
System.Threading.Thread.Sleep(500);
}
// 强制垃圾回收
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
LogManager.Info("[ModelSplitter] 错误恢复完成");
}
catch (Exception ex)
{
LogManager.Error($"[ModelSplitter] 错误恢复失败: {ex.Message}");
}
}
/// <summary>
/// 最终清理
/// </summary>
private void FinalCleanup()
{
try
{
LogManager.Info("[ModelSplitter] 开始最终清理...");
var document = NavisApplication.ActiveDocument;
if (document != null)
{
// 使用安全的可见性重置方法
LogManager.Info("[ModelSplitter] 最终清理:重置可见性状态...");
try
{
document.Models.ResetAllHidden();
LogManager.Info("[ModelSplitter] 最终清理ResetAllHidden成功");
}
catch (Exception resetEx)
{
LogManager.Warning($"[ModelSplitter] 最终清理ResetAllHidden失败: {resetEx.Message}");
LogManager.Info("[ModelSplitter] 最终清理:跳过可见性重置");
}
// 清除选择
document.CurrentSelection.Clear();
// 刷新视图
document.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All);
}
// 最终垃圾回收
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
LogManager.Info("[ModelSplitter] 最终清理完成");
}
catch (Exception ex)
{
LogManager.Warning($"[ModelSplitter] 最终清理失败: {ex.Message}");
}
}
#endregion
#region
protected virtual void OnProgressChanged(ProgressChangedEventArgs e)
{
ProgressChanged?.Invoke(this, e);
}
protected virtual void OnLayerProcessed(SplitResult result)
{
LayerProcessed?.Invoke(this, result);
}
protected virtual void OnSplitCompleted(List<SplitResult> results)
{
SplitCompleted?.Invoke(this, results);
}
protected virtual void OnStatusChanged(string status)
{
StatusChanged?.Invoke(this, status);
}
protected virtual void OnErrorOccurred(Exception ex)
{
ErrorOccurred?.Invoke(this, ex);
}
#endregion
}
}