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 } }