1435 lines
52 KiB
C#
1435 lines
52 KiB
C#
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
|
||
{
|
||
/// <summary>
|
||
/// 碰撞报告显示状态枚举
|
||
/// </summary>
|
||
public enum CollisionReportStatus
|
||
{
|
||
/// <summary>正在生成报告</summary>
|
||
Generating,
|
||
/// <summary>报告已生成</summary>
|
||
Generated,
|
||
/// <summary>生成失败</summary>
|
||
Failed,
|
||
/// <summary>空报告</summary>
|
||
Empty
|
||
}
|
||
|
||
/// <summary>
|
||
/// 碰撞报告项目UI模型
|
||
/// </summary>
|
||
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; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 碰撞报告统计信息UI模型
|
||
/// </summary>
|
||
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; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 碰撞报告对话框ViewModel
|
||
/// </summary>
|
||
public class CollisionReportViewModel : ViewModelBase
|
||
{
|
||
#region 私有字段
|
||
|
||
private CollisionReportStatus _reportStatus;
|
||
private string _reportTitle;
|
||
private CollisionReportStatistics _statistics;
|
||
private ObservableCollection<CollisionReportItem> _animationCollisions;
|
||
private string _progressMessage;
|
||
private int _progressPercentage;
|
||
private bool _isGenerating;
|
||
private bool _hasCollisions;
|
||
private string _summaryMessage;
|
||
private string _recommendationMessage;
|
||
private string _movingObjectInfo;
|
||
private ObservableCollection<string> _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<ExcludedObjectRecord> _excludedObjects;
|
||
|
||
// 保存运动物体原始状态,用于碰撞查看后恢复
|
||
private ModelItemTransformHelper.ObjectStateSnapshot _savedAnimatedObjectState;
|
||
private ModelItem _currentAnimatedObject;
|
||
|
||
#endregion
|
||
|
||
#region 公共属性
|
||
|
||
/// <summary>
|
||
/// 报告状态
|
||
/// </summary>
|
||
public CollisionReportStatus ReportStatus
|
||
{
|
||
get => _reportStatus;
|
||
set => SetProperty(ref _reportStatus, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 报告标题
|
||
/// </summary>
|
||
public string ReportTitle
|
||
{
|
||
get => _reportTitle;
|
||
set => SetProperty(ref _reportTitle, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 统计信息
|
||
/// </summary>
|
||
public CollisionReportStatistics Statistics
|
||
{
|
||
get => _statistics;
|
||
set => SetProperty(ref _statistics, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 动画过程碰撞列表
|
||
/// </summary>
|
||
public ObservableCollection<CollisionReportItem> AnimationCollisions
|
||
{
|
||
get => _animationCollisions;
|
||
set => SetProperty(ref _animationCollisions, value);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 进度消息
|
||
/// </summary>
|
||
public string ProgressMessage
|
||
{
|
||
get => _progressMessage;
|
||
set => SetProperty(ref _progressMessage, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 进度百分比
|
||
/// </summary>
|
||
public int ProgressPercentage
|
||
{
|
||
get => _progressPercentage;
|
||
set => SetProperty(ref _progressPercentage, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否正在生成报告
|
||
/// </summary>
|
||
public bool IsGenerating
|
||
{
|
||
get => _isGenerating;
|
||
set
|
||
{
|
||
if (SetProperty(ref _isGenerating, value))
|
||
{
|
||
// 刷新命令状态
|
||
System.Windows.Input.CommandManager.InvalidateRequerySuggested();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否有碰撞
|
||
/// </summary>
|
||
public bool HasCollisions
|
||
{
|
||
get => _hasCollisions;
|
||
set
|
||
{
|
||
if (SetProperty(ref _hasCollisions, value))
|
||
{
|
||
// 刷新命令状态
|
||
System.Windows.Input.CommandManager.InvalidateRequerySuggested();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 总结消息
|
||
/// </summary>
|
||
public string SummaryMessage
|
||
{
|
||
get => _summaryMessage;
|
||
set => SetProperty(ref _summaryMessage, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 建议消息
|
||
/// </summary>
|
||
public string RecommendationMessage
|
||
{
|
||
get => _recommendationMessage;
|
||
set => SetProperty(ref _recommendationMessage, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 运动构件信息
|
||
/// </summary>
|
||
public string MovingObjectInfo
|
||
{
|
||
get => _movingObjectInfo;
|
||
set => SetProperty(ref _movingObjectInfo, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 被撞物体清单
|
||
/// </summary>
|
||
public ObservableCollection<string> CollidedObjectsList
|
||
{
|
||
get => _collidedObjectsList;
|
||
set => SetProperty(ref _collidedObjectsList, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 去重后的被撞物体总数
|
||
/// </summary>
|
||
public int UniqueCollidedObjectsCount
|
||
{
|
||
get => _uniqueCollidedObjectsCount;
|
||
set => SetProperty(ref _uniqueCollidedObjectsCount, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 路径名称
|
||
/// </summary>
|
||
public string PathName
|
||
{
|
||
get => _pathName;
|
||
set => SetProperty(ref _pathName, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试名称(来自检测记录)
|
||
/// </summary>
|
||
public string TestName
|
||
{
|
||
get => _testName;
|
||
set => SetProperty(ref _testName, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试时间(来自检测记录)
|
||
/// </summary>
|
||
public DateTime? TestTime
|
||
{
|
||
get => _testTime;
|
||
set => SetProperty(ref _testTime, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试时间显示字符串
|
||
/// </summary>
|
||
public string TestTimeDisplay => _testTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "未知时间";
|
||
|
||
/// <summary>
|
||
/// 动画帧率
|
||
/// </summary>
|
||
public int FrameRate
|
||
{
|
||
get => _frameRate;
|
||
set => SetProperty(ref _frameRate, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 动画时长
|
||
/// </summary>
|
||
public double Duration
|
||
{
|
||
get => _duration;
|
||
set => SetProperty(ref _duration, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检测容差
|
||
/// </summary>
|
||
public double DetectionTolerance
|
||
{
|
||
get => _detectionTolerance;
|
||
set => SetProperty(ref _detectionTolerance, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当前报告结果
|
||
/// </summary>
|
||
public CollisionReportResult CurrentReport
|
||
{
|
||
get => _currentReport;
|
||
set => SetProperty(ref _currentReport, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 截图预览图像源(兼容旧版本,返回当前选中的截图)
|
||
/// </summary>
|
||
public BitmapImage ScreenshotPreviewSource => GetScreenshotBitmap(SelectedScreenshot);
|
||
|
||
/// <summary>
|
||
/// 截图列表
|
||
/// </summary>
|
||
private ObservableCollection<CollisionReportScreenshot> _screenshots = new ObservableCollection<CollisionReportScreenshot>();
|
||
public ObservableCollection<CollisionReportScreenshot> Screenshots
|
||
{
|
||
get => _screenshots;
|
||
set
|
||
{
|
||
if (SetProperty(ref _screenshots, value))
|
||
{
|
||
OnPropertyChanged(nameof(HasScreenshots));
|
||
OnPropertyChanged(nameof(ScreenshotPreviewSource));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当前选中的截图
|
||
/// </summary>
|
||
private CollisionReportScreenshot _selectedScreenshot;
|
||
public CollisionReportScreenshot SelectedScreenshot
|
||
{
|
||
get => _selectedScreenshot;
|
||
set
|
||
{
|
||
if (SetProperty(ref _selectedScreenshot, value))
|
||
{
|
||
OnPropertyChanged(nameof(ScreenshotPreviewSource));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否有截图
|
||
/// </summary>
|
||
public bool HasScreenshots => Screenshots?.Count > 0;
|
||
|
||
/// <summary>
|
||
/// 截图数量
|
||
/// </summary>
|
||
public int ScreenshotCount => Screenshots?.Count ?? 0;
|
||
|
||
/// <summary>
|
||
/// 排除对象列表
|
||
/// </summary>
|
||
public ObservableCollection<ExcludedObjectRecord> ExcludedObjects
|
||
{
|
||
get => _excludedObjects;
|
||
set
|
||
{
|
||
if (SetProperty(ref _excludedObjects, value))
|
||
{
|
||
OnPropertyChanged(nameof(HasExcludedObjects));
|
||
OnPropertyChanged(nameof(ExcludedObjectsCount));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否有排除对象
|
||
/// </summary>
|
||
public bool HasExcludedObjects => ExcludedObjects?.Count > 0;
|
||
|
||
/// <summary>
|
||
/// 排除对象数量
|
||
/// </summary>
|
||
public int ExcludedObjectsCount => ExcludedObjects?.Count ?? 0;
|
||
|
||
/// <summary>
|
||
/// 获取截图的BitmapImage
|
||
/// </summary>
|
||
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 命令属性
|
||
|
||
/// <summary>
|
||
/// 导出报告命令
|
||
/// </summary>
|
||
public ICommand ExportReportCommand { get; }
|
||
|
||
/// <summary>
|
||
/// 关闭窗口命令
|
||
/// </summary>
|
||
public ICommand CloseCommand { get; }
|
||
|
||
/// <summary>
|
||
/// 添加截图命令
|
||
/// </summary>
|
||
public ICommand AddScreenshotCommand { get; }
|
||
|
||
/// <summary>
|
||
/// 删除截图命令
|
||
/// </summary>
|
||
public ICommand DeleteScreenshotCommand { get; }
|
||
|
||
/// <summary>
|
||
/// 选择截图命令
|
||
/// </summary>
|
||
public ICommand SelectScreenshotCommand { get; }
|
||
|
||
/// <summary>
|
||
/// 截图设置命令
|
||
/// </summary>
|
||
public ICommand ScreenshotSettingsCommand { get; }
|
||
|
||
/// <summary>
|
||
/// 高亮碰撞对象命令
|
||
/// </summary>
|
||
public ICommand HighlightCollisionCommand { get; }
|
||
|
||
#endregion
|
||
|
||
#region 截图设置属性
|
||
|
||
// 默认截图设置(0表示未初始化,将在构造函数中设置为当前视窗尺寸)
|
||
private int _screenshotWidth = 0;
|
||
private int _screenshotHeight = 0;
|
||
private ImageFormat _screenshotFormat = ImageFormat.Png;
|
||
|
||
/// <summary>
|
||
/// 截图宽度
|
||
/// </summary>
|
||
public int ScreenshotWidthSetting
|
||
{
|
||
get => _screenshotWidth;
|
||
set => SetProperty(ref _screenshotWidth, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 截图高度
|
||
/// </summary>
|
||
public int ScreenshotHeightSetting
|
||
{
|
||
get => _screenshotHeight;
|
||
set => SetProperty(ref _screenshotHeight, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 截图格式
|
||
/// </summary>
|
||
public ImageFormat ScreenshotFormatSetting
|
||
{
|
||
get => _screenshotFormat;
|
||
set => SetProperty(ref _screenshotFormat, value);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 构造函数
|
||
|
||
public CollisionReportViewModel()
|
||
{
|
||
// 初始化集合
|
||
AnimationCollisions = new ObservableCollection<CollisionReportItem>();
|
||
CollidedObjectsList = new ObservableCollection<string>();
|
||
ExcludedObjects = new ObservableCollection<ExcludedObjectRecord>();
|
||
|
||
Statistics = new CollisionReportStatistics();
|
||
|
||
// 初始化命令
|
||
ExportReportCommand = new RelayCommand(async () => await ExportReportAsync(), () => !IsGenerating);
|
||
CloseCommand = new RelayCommand(CloseWindow);
|
||
HighlightCollisionCommand = new RelayCommand<CollisionReportItem>(HighlightCollision, item => item?.CollisionData != null);
|
||
AddScreenshotCommand = new RelayCommand(ExecuteAddScreenshot, CanExecuteAddScreenshot);
|
||
DeleteScreenshotCommand = new RelayCommand<CollisionReportScreenshot>(ExecuteDeleteScreenshot, s => s != null);
|
||
SelectScreenshotCommand = new RelayCommand<CollisionReportScreenshot>(ExecuteSelectScreenshot);
|
||
ScreenshotSettingsCommand = new RelayCommand(ExecuteScreenshotSettings);
|
||
|
||
// 初始化状态
|
||
ReportStatus = CollisionReportStatus.Generating;
|
||
ReportTitle = "NavisworksTransport - 碰撞检测报告";
|
||
IsGenerating = false;
|
||
HasCollisions = false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化截图设置为当前视窗尺寸
|
||
/// </summary>
|
||
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 公共方法
|
||
|
||
/// <summary>
|
||
/// 初始化报告数据
|
||
/// </summary>
|
||
/// <param name="reportResult">报告结果</param>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新进度
|
||
/// </summary>
|
||
/// <param name="percentage">进度百分比</param>
|
||
/// <param name="message">进度消息</param>
|
||
public void UpdateProgress(int percentage, string message)
|
||
{
|
||
SafeExecute(() =>
|
||
{
|
||
ProgressPercentage = Math.Max(0, Math.Min(100, percentage));
|
||
ProgressMessage = message ?? "";
|
||
}, "更新报告生成进度", true);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 私有方法
|
||
|
||
/// <summary>
|
||
/// 从数据库加载测试信息(测试名称和时间)
|
||
/// </summary>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新统计信息
|
||
/// </summary>
|
||
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);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理碰撞数据
|
||
/// </summary>
|
||
private void ProcessCollisionData(List<CollisionResult> collisions)
|
||
{
|
||
if (collisions == null || collisions.Count == 0)
|
||
return;
|
||
|
||
var collisionItems = new List<CollisionReportItem>();
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建碰撞报告项目
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取ModelItem的完整层级路径
|
||
/// </summary>
|
||
private string GetModelItemFullPath(ModelItem item)
|
||
{
|
||
if (item == null) return "未知对象";
|
||
|
||
try
|
||
{
|
||
var pathParts = new List<string>();
|
||
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);
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 获取状态显示文本
|
||
/// </summary>
|
||
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 "未知";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取状态颜色
|
||
/// </summary>
|
||
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"; // 灰色 - 未知
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成总结和建议
|
||
/// </summary>
|
||
private void GenerateSummaryAndRecommendations(CollisionReportResult reportResult)
|
||
{
|
||
if (reportResult.TotalCollisions == 0)
|
||
{
|
||
SummaryMessage = "✅ 未发现碰撞问题,路径规划良好。";
|
||
RecommendationMessage = "当前路径设计合理,无需调整。建议定期进行碰撞检测以确保持续的安全性。";
|
||
}
|
||
else
|
||
{
|
||
SummaryMessage = $"⚠️ 发现 {reportResult.TotalCollisions} 个碰撞问题,需要采取措施处理。";
|
||
|
||
var recommendations = new List<string>
|
||
{
|
||
"1. 调整路径规划,避开碰撞区域",
|
||
"2. 检查物体尺寸和安全边距设置",
|
||
"3. 考虑增加临时障碍物标记",
|
||
"4. 重新评估物流流程设计",
|
||
"5. 建议进行更详细的碰撞检测分析"
|
||
};
|
||
|
||
RecommendationMessage = string.Join("\n", recommendations);
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 设置空报告状态
|
||
/// </summary>
|
||
private void SetEmptyReport()
|
||
{
|
||
ReportStatus = CollisionReportStatus.Empty;
|
||
IsGenerating = false;
|
||
HasCollisions = false;
|
||
SummaryMessage = "📄 暂无碰撞数据";
|
||
RecommendationMessage = "请先运行碰撞检测或动画播放以生成碰撞数据。";
|
||
Statistics = new CollisionReportStatistics { ReportType = "空报告" };
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置失败报告状态
|
||
/// </summary>
|
||
private void SetFailedReport(string errorMessage)
|
||
{
|
||
ReportStatus = CollisionReportStatus.Failed;
|
||
IsGenerating = false;
|
||
HasCollisions = false;
|
||
SummaryMessage = "❌ 报告生成失败";
|
||
RecommendationMessage = $"错误信息: {errorMessage}";
|
||
Statistics = new CollisionReportStatistics { ReportType = "生成失败" };
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 命令方法
|
||
|
||
/// <summary>
|
||
/// 导出报告
|
||
/// </summary>
|
||
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}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 自动导出报告到默认目录(关闭窗口时调用)
|
||
/// </summary>
|
||
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}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成导出内容
|
||
/// </summary>
|
||
private string GenerateExportContent(string fileExtension, string filePath = null)
|
||
{
|
||
if (fileExtension == ".html")
|
||
{
|
||
return GenerateHtmlReport(filePath);
|
||
}
|
||
else
|
||
{
|
||
return GenerateTextReport();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成文本报告
|
||
/// </summary>
|
||
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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加碰撞详细信息到文本报告
|
||
/// </summary>
|
||
private void AddCollisionDetailsToTextReport(StringBuilder report, string sectionTitle, ObservableCollection<CollisionReportItem> 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();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成HTML报告 - 统一使用 CollisionReportHtmlGenerator
|
||
/// </summary>
|
||
private string GenerateHtmlReport(string htmlFilePath = null)
|
||
{
|
||
// 确保 CurrentReport 的截图列表与 ViewModel 同步
|
||
if (CurrentReport != null && Screenshots != null)
|
||
{
|
||
CurrentReport.Screenshots = Screenshots.ToList();
|
||
}
|
||
|
||
return CollisionReportHtmlGenerator.GenerateHtmlReport(CurrentReport, htmlFilePath);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 高亮碰撞对象
|
||
/// </summary>
|
||
private void HighlightCollision(CollisionReportItem item)
|
||
{
|
||
try
|
||
{
|
||
if (item?.CollisionData == null)
|
||
return;
|
||
|
||
// 使用 ClashDetectiveResultsCategory(深红色),不是预计算高亮
|
||
var collisions = new List<CollisionResult> { item.CollisionData };
|
||
ModelHighlightHelper.ManageCollisionHighlightsByCategory(
|
||
ModelHighlightHelper.ClashDetectiveResultsCategory,
|
||
collisions,
|
||
clearOtherCategories: true);
|
||
LogManager.Info($"高亮碰撞对象: {item.Title}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"高亮碰撞对象失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 关闭窗口
|
||
/// </summary>
|
||
private void CloseWindow()
|
||
{
|
||
// 这个方法会由View中的代码调用
|
||
LogManager.Info("关闭碰撞报告窗口");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行添加新截图(使用默认设置,不弹出对话框)
|
||
/// </summary>
|
||
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<CollisionReportScreenshot>();
|
||
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);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查是否可以添加截图
|
||
/// </summary>
|
||
private bool CanExecuteAddScreenshot()
|
||
{
|
||
return CurrentReport != null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行截图设置 - 打开对话框修改截图参数
|
||
/// </summary>
|
||
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}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行删除截图
|
||
/// </summary>
|
||
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}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行选择截图
|
||
/// </summary>
|
||
private void ExecuteSelectScreenshot(CollisionReportScreenshot screenshot)
|
||
{
|
||
if (screenshot != null)
|
||
{
|
||
SelectedScreenshot = screenshot;
|
||
LogManager.Debug($"选中截图: {screenshot.FilePath}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 高亮并聚焦到碰撞项目中的被撞对象(Item2)
|
||
/// 同时将运动物体(Item1)移动到碰撞时的位置和朝向,还原碰撞场景
|
||
/// </summary>
|
||
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);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 恢复运动物体到原始状态(查看碰撞前保存的状态)
|
||
/// </summary>
|
||
public void RestoreAnimatedObjectToOriginalState()
|
||
{
|
||
CollisionSceneHelper.RestoreAnimatedObjectState(_currentAnimatedObject, _savedAnimatedObjectState);
|
||
|
||
// 清除保存的状态
|
||
_savedAnimatedObjectState = null;
|
||
_currentAnimatedObject = null;
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
} |