using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Input;
using Autodesk.Navisworks.Api;
using NavisworksTransport.Utils;
using NavisworksTransport.Commands;
using NavisworksTransport.Core;
using NavisworksTransport.Core.Animation;
using System.IO;
using System.Text;
using Microsoft.Win32;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
using NavisworksTransport.UI.WPF.Views;
using ImageFormat = System.Drawing.Imaging.ImageFormat;
namespace NavisworksTransport.UI.WPF.ViewModels
{
///
/// 碰撞报告显示状态枚举
///
public enum CollisionReportStatus
{
/// 正在生成报告
Generating,
/// 报告已生成
Generated,
/// 生成失败
Failed,
/// 空报告
Empty
}
///
/// 碰撞报告项目UI模型
///
public class CollisionReportItem
{
public int Index { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string StatusText { get; set; }
public string StatusColor { get; set; }
public string Details { get; set; }
public string Item1FullPath { get; set; }
public string Item2FullPath { get; set; }
public CollisionResult CollisionData { get; set; }
}
///
/// 碰撞报告统计信息UI模型
///
public class CollisionReportStatistics
{
public int TotalCollisions { get; set; }
public int AnimationCollisions { get; set; }
public int NewCollisions { get; set; }
public int ActiveCollisions { get; set; }
public int ReviewedCollisions { get; set; }
public int ApprovedCollisions { get; set; }
public int ResolvedCollisions { get; set; }
public string GenerationTime { get; set; }
public string ReportType { get; set; }
}
///
/// 碰撞报告对话框ViewModel
///
public class CollisionReportViewModel : ViewModelBase
{
#region 私有字段
private CollisionReportStatus _reportStatus;
private string _reportTitle;
private CollisionReportStatistics _statistics;
private ObservableCollection _animationCollisions;
private string _progressMessage;
private int _progressPercentage;
private bool _isGenerating;
private bool _hasCollisions;
private string _summaryMessage;
private string _recommendationMessage;
private string _movingObjectInfo;
private ObservableCollection _collidedObjectsList;
private int _uniqueCollidedObjectsCount;
private string _pathName;
private int _frameRate;
private double _duration;
private double _detectionTolerance;
private string _testName;
private DateTime? _testTime;
private CollisionReportResult _currentReport;
private ObservableCollection _excludedObjects;
// 保存运动物体原始状态,用于碰撞查看后恢复
private ModelItemTransformHelper.ObjectStateSnapshot _savedAnimatedObjectState;
private ModelItem _currentAnimatedObject;
#endregion
#region 公共属性
///
/// 报告状态
///
public CollisionReportStatus ReportStatus
{
get => _reportStatus;
set => SetProperty(ref _reportStatus, value);
}
///
/// 报告标题
///
public string ReportTitle
{
get => _reportTitle;
set => SetProperty(ref _reportTitle, value);
}
///
/// 统计信息
///
public CollisionReportStatistics Statistics
{
get => _statistics;
set => SetProperty(ref _statistics, value);
}
///
/// 动画过程碰撞列表
///
public ObservableCollection AnimationCollisions
{
get => _animationCollisions;
set => SetProperty(ref _animationCollisions, value);
}
///
/// 进度消息
///
public string ProgressMessage
{
get => _progressMessage;
set => SetProperty(ref _progressMessage, value);
}
///
/// 进度百分比
///
public int ProgressPercentage
{
get => _progressPercentage;
set => SetProperty(ref _progressPercentage, value);
}
///
/// 是否正在生成报告
///
public bool IsGenerating
{
get => _isGenerating;
set
{
if (SetProperty(ref _isGenerating, value))
{
// 刷新命令状态
System.Windows.Input.CommandManager.InvalidateRequerySuggested();
}
}
}
///
/// 是否有碰撞
///
public bool HasCollisions
{
get => _hasCollisions;
set
{
if (SetProperty(ref _hasCollisions, value))
{
// 刷新命令状态
System.Windows.Input.CommandManager.InvalidateRequerySuggested();
}
}
}
///
/// 总结消息
///
public string SummaryMessage
{
get => _summaryMessage;
set => SetProperty(ref _summaryMessage, value);
}
///
/// 建议消息
///
public string RecommendationMessage
{
get => _recommendationMessage;
set => SetProperty(ref _recommendationMessage, value);
}
///
/// 运动构件信息
///
public string MovingObjectInfo
{
get => _movingObjectInfo;
set => SetProperty(ref _movingObjectInfo, value);
}
///
/// 被撞物体清单
///
public ObservableCollection CollidedObjectsList
{
get => _collidedObjectsList;
set => SetProperty(ref _collidedObjectsList, value);
}
///
/// 去重后的被撞物体总数
///
public int UniqueCollidedObjectsCount
{
get => _uniqueCollidedObjectsCount;
set => SetProperty(ref _uniqueCollidedObjectsCount, value);
}
///
/// 路径名称
///
public string PathName
{
get => _pathName;
set => SetProperty(ref _pathName, value);
}
///
/// 测试名称(来自检测记录)
///
public string TestName
{
get => _testName;
set => SetProperty(ref _testName, value);
}
///
/// 测试时间(来自检测记录)
///
public DateTime? TestTime
{
get => _testTime;
set => SetProperty(ref _testTime, value);
}
///
/// 测试时间显示字符串
///
public string TestTimeDisplay => _testTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "未知时间";
///
/// 动画帧率
///
public int FrameRate
{
get => _frameRate;
set => SetProperty(ref _frameRate, value);
}
///
/// 动画时长
///
public double Duration
{
get => _duration;
set => SetProperty(ref _duration, value);
}
///
/// 检测容差
///
public double DetectionTolerance
{
get => _detectionTolerance;
set => SetProperty(ref _detectionTolerance, value);
}
///
/// 当前报告结果
///
public CollisionReportResult CurrentReport
{
get => _currentReport;
set => SetProperty(ref _currentReport, value);
}
///
/// 截图预览图像源(兼容旧版本,返回当前选中的截图)
///
public BitmapImage ScreenshotPreviewSource => GetScreenshotBitmap(SelectedScreenshot);
///
/// 截图列表
///
private ObservableCollection _screenshots = new ObservableCollection();
public ObservableCollection Screenshots
{
get => _screenshots;
set
{
if (SetProperty(ref _screenshots, value))
{
OnPropertyChanged(nameof(HasScreenshots));
OnPropertyChanged(nameof(ScreenshotPreviewSource));
}
}
}
///
/// 当前选中的截图
///
private CollisionReportScreenshot _selectedScreenshot;
public CollisionReportScreenshot SelectedScreenshot
{
get => _selectedScreenshot;
set
{
if (SetProperty(ref _selectedScreenshot, value))
{
OnPropertyChanged(nameof(ScreenshotPreviewSource));
}
}
}
///
/// 是否有截图
///
public bool HasScreenshots => Screenshots?.Count > 0;
///
/// 截图数量
///
public int ScreenshotCount => Screenshots?.Count ?? 0;
///
/// 排除对象列表
///
public ObservableCollection ExcludedObjects
{
get => _excludedObjects;
set
{
if (SetProperty(ref _excludedObjects, value))
{
OnPropertyChanged(nameof(HasExcludedObjects));
OnPropertyChanged(nameof(ExcludedObjectsCount));
}
}
}
///
/// 是否有排除对象
///
public bool HasExcludedObjects => ExcludedObjects?.Count > 0;
///
/// 排除对象数量
///
public int ExcludedObjectsCount => ExcludedObjects?.Count ?? 0;
///
/// 获取截图的BitmapImage
///
private BitmapImage GetScreenshotBitmap(CollisionReportScreenshot screenshot)
{
if (screenshot == null || string.IsNullOrEmpty(screenshot.FilePath) || !File.Exists(screenshot.FilePath))
{
return null;
}
try
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.UriSource = new Uri(screenshot.FilePath);
bitmap.EndInit();
bitmap.Freeze();
return bitmap;
}
catch (Exception ex)
{
LogManager.Error($"加载截图预览失败: {ex.Message}");
return null;
}
}
#endregion
#region 命令属性
///
/// 导出报告命令
///
public ICommand ExportReportCommand { get; }
///
/// 关闭窗口命令
///
public ICommand CloseCommand { get; }
///
/// 添加截图命令
///
public ICommand AddScreenshotCommand { get; }
///
/// 删除截图命令
///
public ICommand DeleteScreenshotCommand { get; }
///
/// 选择截图命令
///
public ICommand SelectScreenshotCommand { get; }
///
/// 截图设置命令
///
public ICommand ScreenshotSettingsCommand { get; }
///
/// 高亮碰撞对象命令
///
public ICommand HighlightCollisionCommand { get; }
#endregion
#region 截图设置属性
// 默认截图设置(0表示未初始化,将在构造函数中设置为当前视窗尺寸)
private int _screenshotWidth = 0;
private int _screenshotHeight = 0;
private ImageFormat _screenshotFormat = ImageFormat.Png;
///
/// 截图宽度
///
public int ScreenshotWidthSetting
{
get => _screenshotWidth;
set => SetProperty(ref _screenshotWidth, value);
}
///
/// 截图高度
///
public int ScreenshotHeightSetting
{
get => _screenshotHeight;
set => SetProperty(ref _screenshotHeight, value);
}
///
/// 截图格式
///
public ImageFormat ScreenshotFormatSetting
{
get => _screenshotFormat;
set => SetProperty(ref _screenshotFormat, value);
}
#endregion
#region 构造函数
public CollisionReportViewModel()
{
// 初始化集合
AnimationCollisions = new ObservableCollection();
CollidedObjectsList = new ObservableCollection();
ExcludedObjects = new ObservableCollection();
Statistics = new CollisionReportStatistics();
// 初始化命令
ExportReportCommand = new RelayCommand(async () => await ExportReportAsync(), () => !IsGenerating);
CloseCommand = new RelayCommand(CloseWindow);
HighlightCollisionCommand = new RelayCommand(HighlightCollision, item => item?.CollisionData != null);
AddScreenshotCommand = new RelayCommand(ExecuteAddScreenshot, CanExecuteAddScreenshot);
DeleteScreenshotCommand = new RelayCommand(ExecuteDeleteScreenshot, s => s != null);
SelectScreenshotCommand = new RelayCommand(ExecuteSelectScreenshot);
ScreenshotSettingsCommand = new RelayCommand(ExecuteScreenshotSettings);
// 初始化状态
ReportStatus = CollisionReportStatus.Generating;
ReportTitle = "NavisworksTransport - 碰撞检测报告";
IsGenerating = false;
HasCollisions = false;
}
///
/// 初始化截图设置为当前视窗尺寸
///
private void InitializeScreenshotSettings()
{
var generator = new Core.NavigationMapGenerator();
var viewportSize = generator.GetCurrentViewportSize();
_screenshotWidth = viewportSize.Width;
_screenshotHeight = viewportSize.Height;
LogManager.Info($"截图设置已初始化为当前视窗尺寸: {_screenshotWidth}x{_screenshotHeight}");
}
#endregion
#region 公共方法
///
/// 初始化报告数据
///
/// 报告结果
public void InitializeReport(CollisionReportResult reportResult)
{
if (reportResult == null)
{
SetEmptyReport();
return;
}
// 保存当前报告结果
CurrentReport = reportResult;
// 初始化截图设置(此时文档已准备好)
if (ScreenshotWidthSetting == 0 || ScreenshotHeightSetting == 0)
{
InitializeScreenshotSettings();
}
try
{
IsGenerating = true;
ReportStatus = CollisionReportStatus.Generating;
ProgressMessage = "正在解析报告数据...";
ProgressPercentage = 0;
// 清空现有数据
AnimationCollisions.Clear();
CollidedObjectsList.Clear();
Screenshots.Clear();
ExcludedObjects.Clear();
// 加载截图列表
if (reportResult.Screenshots?.Count > 0)
{
foreach (var screenshot in reportResult.Screenshots.OrderBy(s => s.SortOrder))
{
Screenshots.Add(screenshot);
}
// 默认选中第一张
SelectedScreenshot = Screenshots.FirstOrDefault();
LogManager.Info($"碰撞报告加载了 {Screenshots.Count} 张截图");
}
// 设置运动构件信息
MovingObjectInfo = reportResult.MovingObjectInfo;
// 设置被撞物体清单和计数
UniqueCollidedObjectsCount = reportResult.UniqueCollidedObjectsCount;
if (reportResult.CollidedObjectsList != null)
{
foreach (var objName in reportResult.CollidedObjectsList)
{
CollidedObjectsList.Add(objName);
}
}
// 加载排除对象列表
if (reportResult.ExcludedObjects?.Count > 0)
{
foreach (var excludedObj in reportResult.ExcludedObjects)
{
ExcludedObjects.Add(excludedObj);
}
LogManager.Info($"碰撞报告加载了 {ExcludedObjects.Count} 个排除对象");
}
// 设置动画参数
PathName = reportResult.PathName;
FrameRate = reportResult.FrameRate;
Duration = reportResult.Duration;
DetectionTolerance = reportResult.DetectionTolerance;
// 🔥 从数据库查询测试名称和时间
LoadTestInfoFromDatabase(reportResult.ResultId);
// 更新统计信息
UpdateStatistics(reportResult);
// 分类并添加碰撞项
ProcessCollisionData(reportResult.AllCollisions);
// 生成总结和建议
GenerateSummaryAndRecommendations(reportResult);
// 更新状态
HasCollisions = reportResult.TotalCollisions > 0;
ReportStatus = HasCollisions ? CollisionReportStatus.Generated : CollisionReportStatus.Empty;
ProgressMessage = "报告生成完成";
ProgressPercentage = 100;
LogManager.Info($"碰撞报告ViewModel初始化完成,发现 {reportResult.TotalCollisions} 个碰撞");
}
catch (Exception ex)
{
LogManager.Error($"初始化碰撞报告失败: {ex.Message}");
SetFailedReport(ex.Message);
}
finally
{
IsGenerating = false;
}
}
///
/// 更新进度
///
/// 进度百分比
/// 进度消息
public void UpdateProgress(int percentage, string message)
{
SafeExecute(() =>
{
ProgressPercentage = Math.Max(0, Math.Min(100, percentage));
ProgressMessage = message ?? "";
}, "更新报告生成进度", true);
}
#endregion
#region 私有方法
///
/// 从数据库加载测试信息(测试名称和时间)
///
private void LoadTestInfoFromDatabase(int resultId)
{
try
{
if (resultId <= 0)
{
LogManager.Debug("[LoadTestInfoFromDatabase] ResultId无效,跳过查询");
return;
}
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase == null)
{
LogManager.Warning("[LoadTestInfoFromDatabase] PathDatabase不可用");
return;
}
// 使用ResultId查询检测记录
var record = pathDatabase.GetDetectionResultById(resultId);
if (record != null)
{
TestName = record.TestName ?? "未命名测试";
TestTime = record.TestTime;
LogManager.Info($"[LoadTestInfoFromDatabase] 加载测试信息: Name={TestName}, Time={TestTimeDisplay}");
}
else
{
LogManager.Warning($"[LoadTestInfoFromDatabase] 未找到检测记录: ResultId={resultId}");
TestName = "未知测试";
TestTime = null;
}
}
catch (Exception ex)
{
LogManager.Error($"[LoadTestInfoFromDatabase] 加载测试信息失败: {ex.Message}");
TestName = "未知测试";
TestTime = null;
}
}
///
/// 更新统计信息
///
private void UpdateStatistics(CollisionReportResult reportResult)
{
Statistics = new CollisionReportStatistics
{
TotalCollisions = reportResult.TotalCollisions,
AnimationCollisions = reportResult.AnimationCollisions,
GenerationTime = $"{reportResult.GenerationTimeMs}ms",
ReportType = "综合碰撞报告"
};
// 计算状态统计
if (reportResult.AllCollisions?.Count > 0)
{
Statistics.NewCollisions = reportResult.AllCollisions.Count(c => c.Status == Autodesk.Navisworks.Api.Clash.ClashResultStatus.New);
Statistics.ActiveCollisions = reportResult.AllCollisions.Count(c => c.Status == Autodesk.Navisworks.Api.Clash.ClashResultStatus.Active);
Statistics.ReviewedCollisions = reportResult.AllCollisions.Count(c => c.Status == Autodesk.Navisworks.Api.Clash.ClashResultStatus.Reviewed);
Statistics.ApprovedCollisions = reportResult.AllCollisions.Count(c => c.Status == Autodesk.Navisworks.Api.Clash.ClashResultStatus.Approved);
Statistics.ResolvedCollisions = reportResult.AllCollisions.Count(c => c.Status == Autodesk.Navisworks.Api.Clash.ClashResultStatus.Resolved);
}
}
///
/// 处理碰撞数据
///
private void ProcessCollisionData(List collisions)
{
if (collisions == null || collisions.Count == 0)
return;
var collisionItems = new List();
int index = 1;
foreach (var collision in collisions)
{
var item = CreateCollisionReportItem(collision, index++);
collisionItems.Add(item);
}
// 更新UI集合(按序号排序保持顺序)
SafeExecute(() =>
{
foreach (var item in collisionItems.OrderBy(i => i.Index))
AnimationCollisions.Add(item);
}, "更新碰撞报告项目列表", true);
}
///
/// 创建碰撞报告项目
///
private CollisionReportItem CreateCollisionReportItem(CollisionResult collision, int index)
{
// 获取元素名称和全路径
var item1Name = ModelItemAnalysisHelper.GetSafeDisplayName(collision.Item1);
var item2Name = ModelItemAnalysisHelper.GetSafeDisplayName(collision.Item2);
var item1Path = GetModelItemFullPath(collision.Item1);
var item2Path = GetModelItemFullPath(collision.Item2);
var title = $"{item1Name} ↔ {item2Name}";
var item = new CollisionReportItem
{
Index = index,
Title = string.IsNullOrEmpty(title.Trim().Replace("↔", "").Trim()) ? "未命名碰撞" : title,
StatusText = GetStatusDisplayText(collision.Status),
StatusColor = GetStatusColor(collision.Status),
Details = $"位置: ({collision.Center.X:F2}, {collision.Center.Y:F2}, {collision.Center.Z:F2})",
Item1FullPath = $"🚛 运动物体: {item1Path}",
Item2FullPath = $"💥 被撞对象: {item2Path}",
CollisionData = collision
};
return item;
}
///
/// 获取ModelItem的完整层级路径
///
private string GetModelItemFullPath(ModelItem item)
{
if (item == null) return "未知对象";
try
{
var pathParts = new List();
var current = item;
int levels = 0;
while (current != null && levels < 20)
{
var name = ModelItemAnalysisHelper.GetSafeDisplayName(current);
if (!string.IsNullOrEmpty(name) && name != "未命名对象")
{
pathParts.Insert(0, name);
}
current = current.Parent;
levels++;
}
return pathParts.Count > 0 ? string.Join(" > ", pathParts) : "未知对象";
}
catch
{
return ModelItemAnalysisHelper.GetSafeDisplayName(item);
}
}
///
/// 获取状态显示文本
///
private string GetStatusDisplayText(Autodesk.Navisworks.Api.Clash.ClashResultStatus status)
{
switch (status)
{
case Autodesk.Navisworks.Api.Clash.ClashResultStatus.New:
return "新发现";
case Autodesk.Navisworks.Api.Clash.ClashResultStatus.Active:
return "活跃";
case Autodesk.Navisworks.Api.Clash.ClashResultStatus.Reviewed:
return "已审阅";
case Autodesk.Navisworks.Api.Clash.ClashResultStatus.Approved:
return "已批准";
case Autodesk.Navisworks.Api.Clash.ClashResultStatus.Resolved:
return "已解决";
default:
return "未知";
}
}
///
/// 获取状态颜色
///
private string GetStatusColor(Autodesk.Navisworks.Api.Clash.ClashResultStatus status)
{
switch (status)
{
case Autodesk.Navisworks.Api.Clash.ClashResultStatus.New:
return "#FF6B6B"; // 红色 - 新问题
case Autodesk.Navisworks.Api.Clash.ClashResultStatus.Active:
return "#FFA500"; // 橙色 - 活跃
case Autodesk.Navisworks.Api.Clash.ClashResultStatus.Reviewed:
return "#87CEEB"; // 天蓝色 - 已审阅
case Autodesk.Navisworks.Api.Clash.ClashResultStatus.Approved:
return "#98FB98"; // 浅绿色 - 已批准
case Autodesk.Navisworks.Api.Clash.ClashResultStatus.Resolved:
return "#90EE90"; // 绿色 - 已解决
default:
return "#D3D3D3"; // 灰色 - 未知
}
}
///
/// 生成总结和建议
///
private void GenerateSummaryAndRecommendations(CollisionReportResult reportResult)
{
if (reportResult.TotalCollisions == 0)
{
SummaryMessage = "✅ 未发现碰撞问题,路径规划良好。";
RecommendationMessage = "当前路径设计合理,无需调整。建议定期进行碰撞检测以确保持续的安全性。";
}
else
{
SummaryMessage = $"⚠️ 发现 {reportResult.TotalCollisions} 个碰撞问题,需要采取措施处理。";
var recommendations = new List
{
"1. 调整路径规划,避开碰撞区域",
"2. 检查物体尺寸和安全边距设置",
"3. 考虑增加临时障碍物标记",
"4. 重新评估物流流程设计",
"5. 建议进行更详细的碰撞检测分析"
};
RecommendationMessage = string.Join("\n", recommendations);
}
}
///
/// 设置空报告状态
///
private void SetEmptyReport()
{
ReportStatus = CollisionReportStatus.Empty;
IsGenerating = false;
HasCollisions = false;
SummaryMessage = "📄 暂无碰撞数据";
RecommendationMessage = "请先运行碰撞检测或动画播放以生成碰撞数据。";
Statistics = new CollisionReportStatistics { ReportType = "空报告" };
}
///
/// 设置失败报告状态
///
private void SetFailedReport(string errorMessage)
{
ReportStatus = CollisionReportStatus.Failed;
IsGenerating = false;
HasCollisions = false;
SummaryMessage = "❌ 报告生成失败";
RecommendationMessage = $"错误信息: {errorMessage}";
Statistics = new CollisionReportStatistics { ReportType = "生成失败" };
}
#endregion
#region 命令方法
///
/// 导出报告
///
private async Task ExportReportAsync()
{
try
{
var saveFileDialog = new SaveFileDialog
{
Title = "导出碰撞报告",
Filter = "HTML报告 (*.html)|*.html|碰撞报告 (*.txt)|*.txt|所有文件 (*.*)|*.*",
DefaultExt = "html",
FileName = $"NavisworksTransport_CollisionReport_{DateTime.Now:yyyyMMdd_HHmmss}"
};
if (saveFileDialog.ShowDialog() == true)
{
await Task.Run(() =>
{
var content = GenerateExportContent(Path.GetExtension(saveFileDialog.FileName).ToLower(), saveFileDialog.FileName);
File.WriteAllText(saveFileDialog.FileName, content, Encoding.UTF8);
});
LogManager.Info($"碰撞报告已导出到: {saveFileDialog.FileName}");
// 这里可以显示导出成功的提示
}
}
catch (Exception ex)
{
LogManager.Error($"导出碰撞报告失败: {ex.Message}");
}
}
///
/// 自动导出报告到默认目录(关闭窗口时调用)
///
public async Task AutoExportReportAsync()
{
try
{
if (CurrentReport == null || !HasCollisions) return;
// 生成默认文件名和路径
var sanitizedName = PathHelper.SanitizeFileName(CurrentReport.PathName ?? "未知路径");
var fileName = $"NavisworksTransport_CollisionReport_{sanitizedName}_{DateTime.Now:yyyyMMdd_HHmmss}.html";
var reportDir = PathHelper.GetReportDirectory();
var filePath = Path.Combine(reportDir, fileName);
await Task.Run(() =>
{
var content = GenerateHtmlReport(filePath);
File.WriteAllText(filePath, content, Encoding.UTF8);
});
LogManager.Info($"报告已自动导出到: {filePath}");
}
catch (Exception ex)
{
LogManager.Error($"自动导出报告失败: {ex.Message}");
}
}
///
/// 生成导出内容
///
private string GenerateExportContent(string fileExtension, string filePath = null)
{
if (fileExtension == ".html")
{
return GenerateHtmlReport(filePath);
}
else
{
return GenerateTextReport();
}
}
///
/// 生成文本报告
///
private string GenerateTextReport()
{
var report = new StringBuilder();
var now = DateTime.Now;
// 报告头部
report.AppendLine("========================================");
report.AppendLine(" NavisworksTransport 碰撞检测报告");
report.AppendLine("========================================");
report.AppendLine($"生成时间: {now:yyyy年MM月dd日 HH:mm:ss}");
report.AppendLine($"报告类型: {Statistics.ReportType}");
report.AppendLine();
// 检测记录信息
report.AppendLine("=== 检测记录 ===");
report.AppendLine($"测试名称: {TestName ?? "未命名"}");
report.AppendLine($"检测时间: {TestTimeDisplay}");
report.AppendLine($"路径名称: {PathName}");
report.AppendLine();
// 总体统计
report.AppendLine("=== 总体统计 ===");
report.AppendLine($"总碰撞数: {Statistics.TotalCollisions}");
report.AppendLine($"碰撞构件: {UniqueCollidedObjectsCount}个");
report.AppendLine($"检测点: {Statistics.AnimationCollisions}个");
report.AppendLine();
// 动画参数
report.AppendLine("=== 动画参数 ===");
report.AppendLine($"帧率: {FrameRate} FPS");
report.AppendLine($"时长: {Duration:F1} 秒");
report.AppendLine($"检测容差: {DetectionTolerance:F2} 米");
report.AppendLine();
// 运动构件信息
if (!string.IsNullOrEmpty(MovingObjectInfo))
{
report.AppendLine($"{MovingObjectInfo}");
}
report.AppendLine();
// 被撞物体清单
if (CollidedObjectsList?.Count > 0)
{
report.AppendLine("=== 碰撞构件清单 ===");
int index = 1;
foreach (var objName in CollidedObjectsList.Take(20)) // 限制显示前20个
{
report.AppendLine($" {index}. {objName}");
index++;
}
if (CollidedObjectsList.Count > 20)
{
report.AppendLine($" ... 还有 {CollidedObjectsList.Count - 20} 个构件");
}
report.AppendLine();
}
// 详细碰撞信息
if (HasCollisions)
{
AddCollisionDetailsToTextReport(report, "碰撞详情", AnimationCollisions);
}
// 总结和建议
report.AppendLine("=== 总结与建议 ===");
report.AppendLine(SummaryMessage);
report.AppendLine();
report.AppendLine("建议措施:");
report.AppendLine(RecommendationMessage);
report.AppendLine();
report.AppendLine("========================================");
report.AppendLine($"报告生成完成 - {now:HH:mm:ss}");
report.AppendLine("========================================");
return report.ToString();
}
///
/// 添加碰撞详细信息到文本报告
///
private void AddCollisionDetailsToTextReport(StringBuilder report, string sectionTitle, ObservableCollection items)
{
if (items?.Count > 0)
{
report.AppendLine($"--- {sectionTitle} ---");
foreach (var item in items.Take(10)) // 限制显示数量
{
report.AppendLine($"• {item.Title}");
report.AppendLine($" 状态: {item.StatusText}");
report.AppendLine($" {item.Details.Replace("\n", "\n ")}");
report.AppendLine();
}
if (items.Count > 10)
{
report.AppendLine($"... 还有 {items.Count - 10} 个{sectionTitle}未显示");
}
report.AppendLine();
}
}
///
/// 生成HTML报告 - 统一使用 CollisionReportHtmlGenerator
///
private string GenerateHtmlReport(string htmlFilePath = null)
{
// 确保 CurrentReport 的截图列表与 ViewModel 同步
if (CurrentReport != null && Screenshots != null)
{
CurrentReport.Screenshots = Screenshots.ToList();
}
return CollisionReportHtmlGenerator.GenerateHtmlReport(CurrentReport, htmlFilePath);
}
///
/// 高亮碰撞对象
///
private void HighlightCollision(CollisionReportItem item)
{
try
{
if (item?.CollisionData == null)
return;
// 使用 ClashDetectiveResultsCategory(深红色),不是预计算高亮
var collisions = new List { item.CollisionData };
ModelHighlightHelper.ManageCollisionHighlightsByCategory(
ModelHighlightHelper.ClashDetectiveResultsCategory,
collisions,
clearOtherCategories: true);
LogManager.Info($"高亮碰撞对象: {item.Title}");
}
catch (Exception ex)
{
LogManager.Error($"高亮碰撞对象失败: {ex.Message}");
}
}
///
/// 关闭窗口
///
private void CloseWindow()
{
// 这个方法会由View中的代码调用
LogManager.Info("关闭碰撞报告窗口");
}
///
/// 执行添加新截图(使用默认设置,不弹出对话框)
///
private void ExecuteAddScreenshot()
{
try
{
// 构建路径相关的截图目录(与GenerateCollisionReportCommand保持一致)
// 格式: screenshots/{RouteId前8位}_{PathName}/
string routeId = CurrentReport.RouteId ?? "unknown";
string pathName = CurrentReport.PathName ?? "collision";
string pathDirName = string.Format("{0}_{1}",
routeId.Length >= 8 ? routeId.Substring(0, 8) : routeId,
PathHelper.SanitizeFileName(pathName));
string pathScreenshotDir = System.IO.Path.Combine(
PathHelper.GetScreenshotDirectory(),
pathDirName);
// 使用路径相关的目录生成截图
string screenshotPath = PathHelper.GenerateSceneScreenshotToDirectory(
pathScreenshotDir,
pathName,
ScreenshotWidthSetting,
ScreenshotHeightSetting,
ScreenshotFormatSetting,
"collision"
);
if (screenshotPath != null)
{
// 创建截图对象
var screenshot = new CollisionReportScreenshot
{
FilePath = screenshotPath,
Format = PathHelper.ImageFormatToExtension(ScreenshotFormatSetting).ToUpper(),
Width = ScreenshotWidthSetting,
Height = ScreenshotHeightSetting,
SortOrder = Screenshots.Count
};
// 添加到列表
Screenshots.Add(screenshot);
SelectedScreenshot = screenshot;
// 同步到报告数据
if (CurrentReport.Screenshots == null)
CurrentReport.Screenshots = new List();
CurrentReport.Screenshots.Add(screenshot);
// 保存到数据库
try
{
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
// 使用 ResultId 精确关联到当前报告记录
if (pathDatabase != null && CurrentReport.ResultId > 0)
{
int dbId = pathDatabase.SaveCollisionReportScreenshot(
CurrentReport.ResultId,
screenshotPath,
screenshot.Format,
screenshot.Width,
screenshot.Height,
screenshot.SortOrder
);
screenshot.DatabaseId = dbId; // 保存数据库Id用于后续删除
LogManager.Info($"新截图已保存到数据库: DatabaseId={dbId}, ResultId={CurrentReport.ResultId}, Path={screenshotPath}");
}
else
{
LogManager.Warning($"无法保存截图到数据库: ResultId={CurrentReport.ResultId}, RouteId={CurrentReport.RouteId}");
}
}
catch (Exception dbEx)
{
LogManager.Error($"保存新截图到数据库失败: {dbEx.Message}");
}
OnPropertyChanged(nameof(HasScreenshots));
OnPropertyChanged(nameof(ScreenshotCount));
LogManager.Info($"已添加新截图,当前共 {Screenshots.Count} 张");
}
}
catch (Exception ex)
{
LogManager.Error($"添加截图失败: {ex.Message}");
System.Windows.MessageBox.Show($"添加截图失败: {ex.Message}", "错误",
System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
}
}
///
/// 检查是否可以添加截图
///
private bool CanExecuteAddScreenshot()
{
return CurrentReport != null;
}
///
/// 执行截图设置 - 打开对话框修改截图参数
///
private void ExecuteScreenshotSettings()
{
try
{
var dialog = new GenerateNavigationMapDialog();
dialog.Title = "截图设置";
// 设置当前值,默认优先使用当前视窗尺寸
dialog.SetInitialValues(ScreenshotWidthSetting, ScreenshotHeightSetting, ScreenshotFormatSetting, preferCurrentViewport: true);
DialogHelper.SetOwnerSafely(dialog);
if (dialog.ShowDialog() == true)
{
// 更新设置
ScreenshotWidthSetting = dialog.ImageWidth;
ScreenshotHeightSetting = dialog.ImageHeight;
ScreenshotFormatSetting = dialog.ImageFormat;
LogManager.Info($"截图设置已更新: {ScreenshotWidthSetting}x{ScreenshotHeightSetting}, {ScreenshotFormatSetting}");
}
}
catch (Exception ex)
{
LogManager.Error($"打开截图设置失败: {ex.Message}");
}
}
///
/// 执行删除截图
///
private void ExecuteDeleteScreenshot(CollisionReportScreenshot screenshot)
{
if (screenshot == null) return;
try
{
var result = System.Windows.MessageBox.Show(
"确定要删除这张截图吗?",
"确认删除",
System.Windows.MessageBoxButton.YesNo,
System.Windows.MessageBoxImage.Question);
if (result != System.Windows.MessageBoxResult.Yes) return;
// 删除物理文件(如果存在)
if (!string.IsNullOrEmpty(screenshot.FilePath) && System.IO.File.Exists(screenshot.FilePath))
{
try
{
System.IO.File.Delete(screenshot.FilePath);
LogManager.Debug($"已删除截图文件: {screenshot.FilePath}");
}
catch (Exception fileEx)
{
LogManager.Error($"删除截图文件失败: {fileEx.Message}");
// 继续删除数据库记录,不中断流程
}
}
// 从数据库删除(如果知道DatabaseId)
LogManager.Info($"开始删除截图,DatabaseId={screenshot.DatabaseId}, FilePath={screenshot.FilePath}");
if (screenshot.DatabaseId > 0)
{
try
{
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase != null)
{
// 删除前查询数量
var beforeScreenshots = pathDatabase.GetCollisionReportScreenshots(CurrentReport.ResultId);
var beforeCount = beforeScreenshots?.Count ?? 0;
var targetExists = beforeScreenshots?.Any(s => s.Id == screenshot.DatabaseId) ?? false;
pathDatabase.DeleteCollisionReportScreenshot(screenshot.DatabaseId);
// 删除后验证
var afterScreenshots = pathDatabase.GetCollisionReportScreenshots(CurrentReport.ResultId);
var afterCount = afterScreenshots?.Count ?? 0;
var targetStillExists = afterScreenshots?.Any(s => s.Id == screenshot.DatabaseId) ?? false;
LogManager.Info($"数据库删除验证: 删除前={beforeCount}张(目标存在:{targetExists}), 删除后={afterCount}张(目标仍存在:{targetStillExists}), 删除Id={screenshot.DatabaseId}");
}
else
{
LogManager.Error("无法获取PathDatabase,数据库删除失败");
}
}
catch (Exception dbEx)
{
LogManager.Error($"从数据库删除截图失败: {dbEx.Message}");
}
}
else
{
LogManager.Warning($"截图DatabaseId为0,跳过数据库删除");
}
// 从列表移除
Screenshots.Remove(screenshot);
// 从报告数据移除
CurrentReport.Screenshots?.Remove(screenshot);
// 更新选中项
if (SelectedScreenshot == screenshot)
{
SelectedScreenshot = Screenshots.FirstOrDefault();
}
// 重新排序并更新数据库
for (int i = 0; i < Screenshots.Count; i++)
{
Screenshots[i].SortOrder = i;
}
OnPropertyChanged(nameof(HasScreenshots));
OnPropertyChanged(nameof(ScreenshotCount));
LogManager.Info($"已删除截图,剩余 {Screenshots.Count} 张");
}
catch (Exception ex)
{
LogManager.Error($"删除截图失败: {ex.Message}");
}
}
///
/// 执行选择截图
///
private void ExecuteSelectScreenshot(CollisionReportScreenshot screenshot)
{
if (screenshot != null)
{
SelectedScreenshot = screenshot;
LogManager.Debug($"选中截图: {screenshot.FilePath}");
}
}
///
/// 高亮并聚焦到碰撞项目中的被撞对象(Item2)
/// 同时将运动物体(Item1)移动到碰撞时的位置和朝向,还原碰撞场景
///
public void HighlightAndFocusCollisionItem(CollisionReportItem collisionItem)
{
if (collisionItem?.CollisionData == null) return;
try
{
var collisionData = collisionItem.CollisionData;
// 首次查看碰撞时保存运动物体状态
if (collisionData.Item1 != null && collisionData.HasPositionInfo && collisionData.Item1Position != null)
{
if (_savedAnimatedObjectState == null || _currentAnimatedObject != collisionData.Item1)
{
_currentAnimatedObject = collisionData.Item1;
_savedAnimatedObjectState = CollisionSceneHelper.SaveAnimatedObjectState(collisionData.Item1);
}
}
// 🔥 关键修复:如果运动物体是虚拟物体,确保它是可见的
if (_currentAnimatedObject != null &&
VirtualObjectManager.Instance.IsVirtualObjectActive &&
VirtualObjectManager.Instance.CurrentVirtualObject == _currentAnimatedObject)
{
// 虚拟物体已存在但可能被隐藏,需要显示
// 使用默认尺寸 2x1x2 米(ShowVirtualObject 内部会处理尺寸更新)
VirtualObjectManager.Instance.ShowVirtualObject(2.0, 1.0, 2.0);
LogManager.Debug("[碰撞报告] 虚拟物体已确保可见");
}
// 使用通用方法移动并聚焦到碰撞位置
CollisionSceneHelper.MoveToCollisionAndFocus(collisionData, _currentAnimatedObject);
}
catch (Exception ex)
{
LogManager.Error($"聚焦到被撞对象失败: {ex.Message}", ex);
}
}
///
/// 恢复运动物体到原始状态(查看碰撞前保存的状态)
///
public void RestoreAnimatedObjectToOriginalState()
{
CollisionSceneHelper.RestoreAnimatedObjectState(_currentAnimatedObject, _savedAnimatedObjectState);
// 清除保存的状态
_savedAnimatedObjectState = null;
_currentAnimatedObject = null;
}
#endregion
}
}