1584 lines
61 KiB
C#
1584 lines
61 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Collections.ObjectModel;
|
||
using System.Linq;
|
||
using System.Threading.Tasks;
|
||
using System.Windows.Input;
|
||
using System.Windows.Threading;
|
||
using Autodesk.Navisworks.Api;
|
||
using Autodesk.Navisworks.Api.Clash;
|
||
using NavisworksTransport.Commands;
|
||
using NavisworksTransport.Core.Animation;
|
||
using NavisworksTransport.Core;
|
||
using NavisworksTransport.UI.WPF.Commands;
|
||
using NavisworksTransport.UI.WPF.Collections;
|
||
using NavisworksTransport.UI.WPF.Models;
|
||
using NavisworksTransport.Utils;
|
||
|
||
namespace NavisworksTransport.UI.WPF.ViewModels
|
||
{
|
||
/// <summary>
|
||
/// 碰撞报告数据
|
||
/// </summary>
|
||
public class CollisionReportData
|
||
{
|
||
public bool IsValid { get; set; }
|
||
public string ErrorMessage { get; set; } = string.Empty;
|
||
public DateTime GeneratedTime { get; set; }
|
||
public string AnimatedObjectName { get; set; } = string.Empty;
|
||
public string PathName { get; set; } = string.Empty;
|
||
public int TotalTests { get; set; }
|
||
public int TotalCollisions { get; set; }
|
||
public List<CollisionTestInfo> Tests { get; set; } = new List<CollisionTestInfo>();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 碰撞测试信息
|
||
/// </summary>
|
||
public class CollisionTestInfo
|
||
{
|
||
public string TestName { get; set; } = string.Empty;
|
||
public string TestType { get; set; } = string.Empty;
|
||
public double Tolerance { get; set; }
|
||
public int CollisionCount { get; set; }
|
||
public string Status { get; set; } = string.Empty;
|
||
public List<CollisionDetailInfo> Collisions { get; set; } = new List<CollisionDetailInfo>();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 碰撞详细信息
|
||
/// </summary>
|
||
public class CollisionDetailInfo
|
||
{
|
||
public string CollisionId { get; set; } = string.Empty;
|
||
public string Object1Name { get; set; } = string.Empty;
|
||
public string Object1Position { get; set; } = string.Empty;
|
||
public string Object2Name { get; set; } = string.Empty;
|
||
public string Object2Position { get; set; } = string.Empty;
|
||
public string CollisionCenter { get; set; } = string.Empty;
|
||
public double Distance { get; set; }
|
||
public string Status { get; set; } = string.Empty;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 动画控制视图模型
|
||
/// 专门处理路径动画播放和碰撞检测功能
|
||
/// </summary>
|
||
public class AnimationControlViewModel : ViewModelBase, IDisposable
|
||
{
|
||
#region 私有字段
|
||
|
||
private readonly NavisworksTransport.Core.Animation.PathAnimationManager _pathAnimationManager;
|
||
private readonly NavisworksTransport.Core.Animation.LogisticsAnimationManager _logisticsAnimationManager;
|
||
private readonly ClashDetectiveIntegration _clashIntegration;
|
||
private readonly UIStateManager _uiStateManager;
|
||
|
||
// 动画参数相关字段
|
||
private ObservableCollection<int> _availableFrameRates;
|
||
private int _selectedFrameRate = 30;
|
||
private double _animationDuration = 10.0;
|
||
private double _currentAnimationTime = 0.0;
|
||
private bool _canStartAnimation = true;
|
||
private bool _canPauseAnimation = false;
|
||
private bool _canStopAnimation = false;
|
||
private string _animationStatus = "就绪";
|
||
private double _animationProgress = 0.0;
|
||
private string _startAnimationButtonText = "开始动画";
|
||
|
||
// 碰撞检测相关字段
|
||
private bool _hasCollisionResults = false;
|
||
private string _collisionStatus = "就绪";
|
||
private string _collisionSummary = "尚未运行碰撞检测";
|
||
|
||
// 碰撞检测参数字段
|
||
private double _collisionDetectionAccuracy = 0.1; // 检测精度(米)
|
||
private double _movementSpeed = 1.0; // 运动速度(米/秒)
|
||
private double _detectionGap = 0.05; // 检测间隙(米)
|
||
private int _animationFrameRate = 30; // 动画帧率(FPS)
|
||
private double _collisionDetectionFrequency = 10.0; // 检测频率(次/秒)
|
||
|
||
// 防抖机制相关字段
|
||
private DispatcherTimer _parameterUpdateTimer;
|
||
private readonly object _parameterUpdateLock = new object();
|
||
private volatile bool _hasParameterChanges = false;
|
||
|
||
// 当前选中路径
|
||
private PathRouteViewModel _currentPathRoute;
|
||
|
||
// 移动物体相关字段
|
||
private ModelItem _selectedAnimatedObject;
|
||
private string _selectedAnimatedObjectName = "未选择移动物体";
|
||
private bool _hasSelectedAnimatedObject = false;
|
||
private bool _canGenerateAnimation = false;
|
||
private string _generationStatus = "就绪";
|
||
|
||
|
||
#endregion
|
||
|
||
#region 公共属性
|
||
|
||
|
||
/// <summary>
|
||
/// 可用帧率集合
|
||
/// </summary>
|
||
public ObservableCollection<int> AvailableFrameRates
|
||
{
|
||
get => _availableFrameRates;
|
||
set => SetProperty(ref _availableFrameRates, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选中的帧率
|
||
/// </summary>
|
||
public int SelectedFrameRate
|
||
{
|
||
get => _selectedFrameRate;
|
||
set => SetProperty(ref _selectedFrameRate, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 动画持续时间(秒)
|
||
/// </summary>
|
||
public double AnimationDuration
|
||
{
|
||
get => _animationDuration;
|
||
set => SetProperty(ref _animationDuration, value);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 当前动画时间
|
||
/// </summary>
|
||
public double CurrentAnimationTime
|
||
{
|
||
get => _currentAnimationTime;
|
||
set => SetProperty(ref _currentAnimationTime, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否可以开始动画
|
||
/// </summary>
|
||
public bool CanStartAnimation
|
||
{
|
||
get => _canStartAnimation;
|
||
set => SetProperty(ref _canStartAnimation, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否可以暂停动画
|
||
/// </summary>
|
||
public bool CanPauseAnimation
|
||
{
|
||
get => _canPauseAnimation;
|
||
set => SetProperty(ref _canPauseAnimation, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否可以停止动画
|
||
/// </summary>
|
||
public bool CanStopAnimation
|
||
{
|
||
get => _canStopAnimation;
|
||
set => SetProperty(ref _canStopAnimation, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 动画状态
|
||
/// </summary>
|
||
public string AnimationStatus
|
||
{
|
||
get => _animationStatus;
|
||
set => SetProperty(ref _animationStatus, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 动画进度
|
||
/// </summary>
|
||
public double AnimationProgress
|
||
{
|
||
get => _animationProgress;
|
||
set => SetProperty(ref _animationProgress, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 开始动画按钮文本
|
||
/// </summary>
|
||
public string StartAnimationButtonText
|
||
{
|
||
get => _startAnimationButtonText;
|
||
set => SetProperty(ref _startAnimationButtonText, value);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 是否有碰撞检测结果
|
||
/// </summary>
|
||
public bool HasCollisionResults
|
||
{
|
||
get => _hasCollisionResults;
|
||
set => SetProperty(ref _hasCollisionResults, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 碰撞检测状态
|
||
/// </summary>
|
||
public string CollisionStatus
|
||
{
|
||
get => _collisionStatus;
|
||
set => SetProperty(ref _collisionStatus, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 碰撞检测摘要
|
||
/// </summary>
|
||
public string CollisionSummary
|
||
{
|
||
get => _collisionSummary;
|
||
set => SetProperty(ref _collisionSummary, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 碰撞检测精度(米)
|
||
/// </summary>
|
||
public double CollisionDetectionAccuracy
|
||
{
|
||
get => _collisionDetectionAccuracy;
|
||
set
|
||
{
|
||
// 限制精度到2位小数
|
||
var roundedValue = Math.Round(value, 2);
|
||
if (SetProperty(ref _collisionDetectionAccuracy, roundedValue))
|
||
{
|
||
UpdateCollisionDetectionFrequency();
|
||
ScheduleParameterUpdate();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 运动速度(米/秒)
|
||
/// </summary>
|
||
public double MovementSpeed
|
||
{
|
||
get => _movementSpeed;
|
||
set
|
||
{
|
||
// 限制精度到1位小数
|
||
var roundedValue = Math.Round(value, 1);
|
||
if (SetProperty(ref _movementSpeed, roundedValue))
|
||
{
|
||
UpdateCollisionDetectionFrequency();
|
||
ScheduleParameterUpdate();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检测间隙(米)
|
||
/// </summary>
|
||
public double DetectionGap
|
||
{
|
||
get => _detectionGap;
|
||
set
|
||
{
|
||
// 限制精度到2位小数
|
||
var roundedValue = Math.Round(value, 2);
|
||
if (SetProperty(ref _detectionGap, roundedValue))
|
||
{
|
||
ScheduleParameterUpdate();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 动画帧率(FPS)
|
||
/// </summary>
|
||
public int AnimationFrameRate
|
||
{
|
||
get => _animationFrameRate;
|
||
set
|
||
{
|
||
if (SetProperty(ref _animationFrameRate, value))
|
||
{
|
||
UpdatePathAnimationManagerSettings();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 碰撞检测频率(次/秒) - 计算得出
|
||
/// </summary>
|
||
public double CollisionDetectionFrequency
|
||
{
|
||
get => _collisionDetectionFrequency;
|
||
private set => SetProperty(ref _collisionDetectionFrequency, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当前选中的路径
|
||
/// </summary>
|
||
public PathRouteViewModel CurrentPathRoute
|
||
{
|
||
get => _currentPathRoute;
|
||
set
|
||
{
|
||
SetProperty(ref _currentPathRoute, value);
|
||
UpdateCanGenerateAnimation();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选中的移动物体
|
||
/// </summary>
|
||
public ModelItem SelectedAnimatedObject
|
||
{
|
||
get => _selectedAnimatedObject;
|
||
set
|
||
{
|
||
SetProperty(ref _selectedAnimatedObject, value);
|
||
UpdateAnimatedObjectInfo();
|
||
UpdateCanGenerateAnimation();
|
||
|
||
// ✨ 新功能:动画对象选择时预计算排除列表(线程安全版本)
|
||
_ = PrecomputeCollisionExclusionsAsync(value);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选中的移动物体名称
|
||
/// </summary>
|
||
public string SelectedAnimatedObjectName
|
||
{
|
||
get => _selectedAnimatedObjectName;
|
||
set => SetProperty(ref _selectedAnimatedObjectName, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否已选择移动物体
|
||
/// </summary>
|
||
public bool HasSelectedAnimatedObject
|
||
{
|
||
get => _hasSelectedAnimatedObject;
|
||
set => SetProperty(ref _hasSelectedAnimatedObject, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否可以生成动画
|
||
/// </summary>
|
||
public bool CanGenerateAnimation
|
||
{
|
||
get => _canGenerateAnimation;
|
||
set => SetProperty(ref _canGenerateAnimation, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成状态
|
||
/// </summary>
|
||
public string GenerationStatus
|
||
{
|
||
get => _generationStatus;
|
||
set => SetProperty(ref _generationStatus, value);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 命令
|
||
|
||
public ICommand StartAnimationCommand { get; private set; }
|
||
public ICommand PauseAnimationCommand { get; private set; }
|
||
public ICommand StopAnimationCommand { get; private set; }
|
||
public ICommand ViewCollisionReportCommand { get; private set; }
|
||
public ICommand SelectAnimatedObjectCommand { get; private set; }
|
||
public ICommand ClearAnimatedObjectCommand { get; private set; }
|
||
public ICommand GenerateAnimationCommand { get; private set; }
|
||
|
||
#endregion
|
||
|
||
#region 构造函数
|
||
|
||
public AnimationControlViewModel() : base()
|
||
{
|
||
try
|
||
{
|
||
// 初始化管理器
|
||
_pathAnimationManager = new NavisworksTransport.Core.Animation.PathAnimationManager();
|
||
_logisticsAnimationManager = new NavisworksTransport.Core.Animation.LogisticsAnimationManager();
|
||
_clashIntegration = ClashDetectiveIntegration.Instance;
|
||
_uiStateManager = UIStateManager.Instance;
|
||
|
||
// 订阅PathAnimationManager事件
|
||
_pathAnimationManager.ProgressChanged += OnAnimationProgressChanged;
|
||
_pathAnimationManager.StateChanged += OnAnimationStateChanged;
|
||
|
||
// 初始化集合
|
||
AvailableFrameRates = new ThreadSafeObservableCollection<int>();
|
||
|
||
// 初始化设置
|
||
InitializeAnimationSettings();
|
||
|
||
// 初始化命令
|
||
InitializeCommands();
|
||
|
||
// 初始化碰撞检测集成
|
||
InitializeClashIntegration();
|
||
|
||
// 初始化防抖定时器
|
||
InitializeParameterUpdateTimer();
|
||
|
||
LogManager.Info("AnimationControlViewModel初始化完成(含缓存管理)");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"AnimationControlViewModel初始化失败: {ex.Message}", ex);
|
||
throw;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 初始化方法
|
||
|
||
/// <summary>
|
||
/// 初始化动画设置
|
||
/// </summary>
|
||
private void InitializeAnimationSettings()
|
||
{
|
||
// 初始化可用帧率
|
||
AvailableFrameRates.Clear();
|
||
var frameRates = new[] { 15, 24, 30, 60 };
|
||
foreach (var rate in frameRates)
|
||
{
|
||
AvailableFrameRates.Add(rate);
|
||
}
|
||
SelectedFrameRate = 30; // 默认30fps
|
||
|
||
// 设置默认动画参数
|
||
AnimationDuration = 10.0;
|
||
CurrentAnimationTime = 0.0;
|
||
|
||
// 设置初始状态
|
||
CanStartAnimation = true;
|
||
CanPauseAnimation = false;
|
||
CanStopAnimation = false;
|
||
StartAnimationButtonText = "开始动画";
|
||
AnimationStatus = "动画状态: 就绪";
|
||
AnimationProgress = 0;
|
||
|
||
// 初始化碰撞检测状态
|
||
HasCollisionResults = false;
|
||
CollisionStatus = "就绪";
|
||
CollisionSummary = "尚未运行碰撞检测";
|
||
|
||
// 初始化碰撞检测参数
|
||
CollisionDetectionAccuracy = 0.1; // 0.1米
|
||
MovementSpeed = 1.0; // 1米/秒
|
||
DetectionGap = 0.05; // 0.05米
|
||
AnimationFrameRate = 30; // 30FPS
|
||
UpdateCollisionDetectionFrequency(); // 计算初始检测频率
|
||
|
||
// 初始化动画管理器设置
|
||
UpdatePathAnimationManagerSettings();
|
||
|
||
LogManager.Info("动画设置初始化完成");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化命令
|
||
/// </summary>
|
||
private void InitializeCommands()
|
||
{
|
||
StartAnimationCommand = new RelayCommand(async () => await ExecuteStartAnimationAsync(), () => CanStartAnimation);
|
||
PauseAnimationCommand = new RelayCommand(async () => await ExecutePauseAnimationAsync(), () => CanPauseAnimation);
|
||
StopAnimationCommand = new RelayCommand(async () => await ExecuteStopAnimationAsync(), () => CanStopAnimation);
|
||
ViewCollisionReportCommand = new RelayCommand(async () => await ExecuteViewCollisionReportAsync(), () => HasCollisionResults);
|
||
SelectAnimatedObjectCommand = new RelayCommand(ExecuteSelectAnimatedObject);
|
||
ClearAnimatedObjectCommand = new RelayCommand(ExecuteClearAnimatedObject, () => HasSelectedAnimatedObject);
|
||
GenerateAnimationCommand = new RelayCommand(ExecuteGenerateAnimation, () => CanGenerateAnimation);
|
||
|
||
LogManager.Info("动画控制命令初始化完成");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化碰撞检测集成
|
||
/// </summary>
|
||
private void InitializeClashIntegration()
|
||
{
|
||
try
|
||
{
|
||
_clashIntegration.Initialize();
|
||
_clashIntegration.CollisionDetected += OnCollisionDetected;
|
||
LogManager.Info("碰撞检测集成初始化完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"碰撞检测集成初始化失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 命令实现
|
||
|
||
/// <summary>
|
||
/// 开始动画命令
|
||
/// </summary>
|
||
private async Task ExecuteStartAnimationAsync()
|
||
{
|
||
if (!CanStartAnimation || CurrentPathRoute == null || CurrentPathRoute.Points.Count < 2)
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
AnimationStatus = "请先选择包含至少2个点的路径";
|
||
});
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 根据PathAnimationManager的状态决定操作
|
||
if (_pathAnimationManager.CurrentState == NavisworksTransport.Core.Animation.AnimationState.Paused)
|
||
{
|
||
// 从暂停状态恢复
|
||
_pathAnimationManager.ResumeAnimation();
|
||
LogManager.Info("从暂停状态恢复动画播放");
|
||
return; // 恢复动画后直接返回,不再执行StartAnimation
|
||
}
|
||
else if (_pathAnimationManager.IsAnimating)
|
||
{
|
||
LogManager.Warning("动画已在播放中");
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
AnimationStatus = "动画已在播放中";
|
||
CanStartAnimation = true;
|
||
CanPauseAnimation = false;
|
||
CanStopAnimation = false;
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 开始播放物体移动动画
|
||
// 首先重置进度(开始全新动画时)
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
AnimationProgress = 0;
|
||
CurrentAnimationTime = 0.0;
|
||
});
|
||
|
||
_pathAnimationManager.StartAnimation();
|
||
|
||
LogManager.Info($"开始物体移动动画播放: {CurrentPathRoute.Name}, 帧率: {SelectedFrameRate}fps");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
AnimationStatus = $"开始动画出错: {ex.Message}";
|
||
LogManager.Error($"开始动画异常: {ex.Message}", ex);
|
||
|
||
// 恢复按钮状态
|
||
CanStartAnimation = true;
|
||
CanPauseAnimation = false;
|
||
CanStopAnimation = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 暂停动画命令
|
||
/// </summary>
|
||
private async Task ExecutePauseAnimationAsync()
|
||
{
|
||
try
|
||
{
|
||
// 调用PathAnimationManager暂停动画
|
||
// UI状态会通过OnAnimationStateChanged事件自动更新
|
||
_pathAnimationManager.PauseAnimation();
|
||
|
||
LogManager.Info("暂停动画播放");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"暂停动画异常: {ex.Message}", ex);
|
||
|
||
// 错误时手动恢复UI状态
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
AnimationStatus = "暂停动画失败";
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止动画命令
|
||
/// </summary>
|
||
private async Task ExecuteStopAnimationAsync()
|
||
{
|
||
try
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
AnimationStatus = "动画状态: 已停止";
|
||
AnimationProgress = 0;
|
||
CurrentAnimationTime = 0.0;
|
||
|
||
CanStartAnimation = true;
|
||
CanPauseAnimation = false;
|
||
CanStopAnimation = false;
|
||
|
||
LogManager.Info("停止动画播放");
|
||
});
|
||
|
||
// 停止当前动画
|
||
_pathAnimationManager.StopAnimation();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"停止动画异常: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 查看碰撞报告命令
|
||
/// </summary>
|
||
private async Task ExecuteViewCollisionReportAsync()
|
||
{
|
||
if (!HasCollisionResults)
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CollisionStatus = "尚无碰撞检测结果可查看";
|
||
});
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CollisionStatus = "正在生成碰撞报告...";
|
||
});
|
||
|
||
LogManager.Info("开始生成碰撞检测详细报告");
|
||
|
||
// 在后台线程生成报告
|
||
var reportData = await Task.Run(() =>
|
||
{
|
||
try
|
||
{
|
||
return GenerateCollisionReport();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"生成碰撞报告失败: {ex.Message}");
|
||
return null;
|
||
}
|
||
});
|
||
|
||
if (reportData != null)
|
||
{
|
||
// 显示报告 - 可以通过多种方式显示
|
||
await ShowCollisionReport(reportData);
|
||
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CollisionStatus = "碰撞报告已生成";
|
||
LogManager.Info("碰撞报告生成完成并已显示");
|
||
});
|
||
}
|
||
else
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CollisionStatus = "生成碰撞报告失败";
|
||
});
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
CollisionStatus = "查看碰撞报告出错";
|
||
});
|
||
LogManager.Error($"查看碰撞报告异常: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 辅助方法
|
||
|
||
/// <summary>
|
||
/// 设置当前路径
|
||
/// </summary>
|
||
public void SetCurrentPath(PathRouteViewModel pathRoute)
|
||
{
|
||
CurrentPathRoute = pathRoute;
|
||
|
||
// 更新按钮状态
|
||
var hasValidPath = pathRoute != null && pathRoute.Points.Count >= 2;
|
||
CanStartAnimation = hasValidPath;
|
||
|
||
// 更新状态文本
|
||
if (pathRoute == null)
|
||
{
|
||
AnimationStatus = "动画状态: 请选择路径";
|
||
CollisionStatus = "请选择路径";
|
||
}
|
||
else if (pathRoute.Points.Count < 2)
|
||
{
|
||
AnimationStatus = "动画状态: 路径点数不足(需要至少2个点)";
|
||
CollisionStatus = "路径点数不足";
|
||
}
|
||
else
|
||
{
|
||
AnimationStatus = "动画状态: 就绪";
|
||
CollisionStatus = "就绪";
|
||
}
|
||
|
||
LogManager.Info($"AnimationControlViewModel当前路径更新: {pathRoute?.Name ?? "无"}, 点数: {pathRoute?.Points.Count ?? 0}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理动画进度变化事件
|
||
/// 直接使用Dispatcher更新UI,避免UIStateManager的超时机制导致UI积压
|
||
/// </summary>
|
||
private void OnAnimationProgressChanged(object sender, double progressPercent)
|
||
{
|
||
// 直接使用Dispatcher.BeginInvoke进行UI更新,避免UIStateManager的5秒超时导致积压
|
||
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||
{
|
||
// 直接使用传入的百分比数值
|
||
AnimationProgress = progressPercent;
|
||
CurrentAnimationTime = (AnimationProgress / 100.0) * AnimationDuration;
|
||
}));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理动画状态变化事件
|
||
/// 直接使用Dispatcher更新UI,避免UIStateManager的超时机制导致UI积压
|
||
/// </summary>
|
||
private void OnAnimationStateChanged(object sender, NavisworksTransport.Core.Animation.AnimationState state)
|
||
{
|
||
// 直接使用Dispatcher.BeginInvoke进行UI更新,避免UIStateManager的5秒超时导致积压
|
||
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||
{
|
||
switch (state)
|
||
{
|
||
case NavisworksTransport.Core.Animation.AnimationState.Playing:
|
||
CanStartAnimation = false;
|
||
CanPauseAnimation = true;
|
||
CanStopAnimation = true;
|
||
StartAnimationButtonText = "开始动画"; // 播放中时按钮显示为灰色的"开始动画"
|
||
AnimationStatus = "动画状态: 播放中";
|
||
break;
|
||
case NavisworksTransport.Core.Animation.AnimationState.Paused:
|
||
CanStartAnimation = true; // 暂停状态下可以继续
|
||
CanPauseAnimation = false;
|
||
CanStopAnimation = true;
|
||
StartAnimationButtonText = "继续播放"; // 暂停状态下显示"继续播放"
|
||
AnimationStatus = "动画状态: 已暂停";
|
||
break;
|
||
case NavisworksTransport.Core.Animation.AnimationState.Finished:
|
||
CanStartAnimation = true;
|
||
CanPauseAnimation = false;
|
||
CanStopAnimation = false;
|
||
StartAnimationButtonText = "开始动画"; // 完成后恢复"开始动画"
|
||
AnimationStatus = "动画状态: 已完成";
|
||
// 动画完成后更新碰撞检测状态
|
||
UpdateCollisionStatusAfterAnimation();
|
||
break;
|
||
case NavisworksTransport.Core.Animation.AnimationState.Stopped:
|
||
CanStartAnimation = true;
|
||
CanPauseAnimation = false;
|
||
CanStopAnimation = false;
|
||
StartAnimationButtonText = "开始动画"; // 停止后恢复"开始动画"
|
||
AnimationStatus = "动画状态: 已停止";
|
||
break;
|
||
default:
|
||
StartAnimationButtonText = "开始动画";
|
||
AnimationStatus = $"动画状态: {state}";
|
||
break;
|
||
}
|
||
}));
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 处理碰撞检测事件
|
||
/// </summary>
|
||
private async void OnCollisionDetected(object sender, CollisionDetectedEventArgs e)
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
HasCollisionResults = e.Results.Count > 0;
|
||
CollisionSummary = e.Results.Count > 0
|
||
? $"发现 {e.Results.Count} 个碰撞点"
|
||
: "未发现碰撞";
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行选择移动物体命令
|
||
/// </summary>
|
||
private void ExecuteSelectAnimatedObject()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("开始选择移动物体");
|
||
|
||
// 获取当前选中的模型项
|
||
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
var selectedItems = doc.CurrentSelection.SelectedItems;
|
||
|
||
if (selectedItems.Count == 0)
|
||
{
|
||
GenerationStatus = "请先在Navisworks中选择一个物体";
|
||
LogManager.Warning("用户未选择任何物体");
|
||
return;
|
||
}
|
||
|
||
if (selectedItems.Count > 1)
|
||
{
|
||
GenerationStatus = "请只选择一个物体作为移动对象";
|
||
LogManager.Warning($"用户选择了{selectedItems.Count}个物体,需要只选择一个");
|
||
return;
|
||
}
|
||
|
||
var selectedItem = selectedItems.First;
|
||
|
||
// 检查选中项是否包含几何体(直接包含或子项包含)
|
||
bool hasAnyGeometry = HasGeometryRecursive(selectedItem);
|
||
|
||
if (!hasAnyGeometry)
|
||
{
|
||
GenerationStatus = "选中的项目及其子项都不包含几何体,请选择其他物体";
|
||
LogManager.Warning("选中的项目及其子项都不包含几何体");
|
||
return;
|
||
}
|
||
|
||
LogManager.Info($"选中物体信息: DisplayName={selectedItem.DisplayName}, HasGeometry={selectedItem.HasGeometry}, ChildCount={selectedItem.Children.Count()}");
|
||
|
||
// 设置选中的移动物体
|
||
SelectedAnimatedObject = selectedItem;
|
||
GenerationStatus = "移动物体选择成功";
|
||
|
||
LogManager.Info($"移动物体选择成功: {SelectedAnimatedObject.DisplayName}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
GenerationStatus = "选择移动物体失败";
|
||
LogManager.Error($"选择移动物体时发生错误: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行清除移动物体命令
|
||
/// </summary>
|
||
/// <summary>
|
||
/// 执行清除移动物体命令
|
||
/// </summary>
|
||
private void ExecuteClearAnimatedObject()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("开始清除移动物体选择并恢复原始位置");
|
||
|
||
// 如果有选中的物体,则恢复到原始位置
|
||
if (SelectedAnimatedObject != null)
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info($"开始恢复物体 '{SelectedAnimatedObject.DisplayName}' 到原始位置");
|
||
|
||
// 使用ResetPermanentTransform清除所有增量变换,恢复到设计文件中的原始位置
|
||
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
var modelItems = new ModelItemCollection { SelectedAnimatedObject };
|
||
doc.Models.ResetPermanentTransform(modelItems);
|
||
|
||
LogManager.Info($"物体 '{SelectedAnimatedObject.DisplayName}' 已成功恢复到原始位置");
|
||
GenerationStatus = "已清除移动物体选择并恢复到原始位置";
|
||
}
|
||
catch (Exception restoreEx)
|
||
{
|
||
LogManager.Error($"恢复物体到原始位置时发生错误: {restoreEx.Message}");
|
||
GenerationStatus = $"已清除物体选择,但恢复位置失败: {restoreEx.Message}";
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LogManager.Info("没有选中的物体需要清除");
|
||
GenerationStatus = "已清除移动物体选择";
|
||
}
|
||
|
||
// 清理选择状态
|
||
SelectedAnimatedObject = null;
|
||
|
||
LogManager.Info("移动物体选择已完全清除");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"清除移动物体选择时发生错误: {ex.Message}");
|
||
GenerationStatus = $"清除失败: {ex.Message}";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 异步预计算碰撞排除列表
|
||
/// </summary>
|
||
/// <param name="animationObject">动画对象</param>
|
||
private async Task PrecomputeCollisionExclusionsAsync(ModelItem animationObject)
|
||
{
|
||
try
|
||
{
|
||
if (animationObject == null)
|
||
{
|
||
LogManager.Debug("[缓存预计算] 动画对象为空,跳过预计算");
|
||
return;
|
||
}
|
||
|
||
LogManager.Info($"[缓存预计算] 开始为新选择的动画对象预计算: {animationObject.DisplayName}");
|
||
|
||
// 在UI线程中更新开始状态
|
||
await System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||
{
|
||
GenerationStatus = "正在分析动画对象...";
|
||
}));
|
||
|
||
// 在UI线程中执行Navisworks API操作(线程安全)
|
||
bool success;
|
||
try
|
||
{
|
||
success = _logisticsAnimationManager.PrecomputeCollisionExclusions(animationObject);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[缓存预计算] 预计算失败: {ex.Message}", ex);
|
||
success = false;
|
||
}
|
||
|
||
// 异步更新UI状态
|
||
await System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||
{
|
||
if (success)
|
||
{
|
||
var cacheStats = _logisticsAnimationManager.GetCacheStats();
|
||
GenerationStatus = $"动画对象分析完成";
|
||
LogManager.Info($"[缓存预计算] 预计算成功 - {cacheStats}");
|
||
}
|
||
else
|
||
{
|
||
GenerationStatus = "动画对象分析失败,将使用实时计算";
|
||
LogManager.Warning("[缓存预计算] 预计算失败,碰撞检测将使用实时计算模式");
|
||
}
|
||
}));
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[缓存预计算] 异步预计算过程异常: {ex.Message}", ex);
|
||
await System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||
{
|
||
GenerationStatus = "动画对象分析异常";
|
||
}));
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行生成动画命令(同步执行,因为Navisworks API需要STA线程)
|
||
/// </summary>
|
||
private void ExecuteGenerateAnimation()
|
||
{
|
||
try
|
||
{
|
||
if (!CanGenerateAnimation)
|
||
{
|
||
GenerationStatus = "无法生成动画:缺少必要条件";
|
||
LogManager.Warning("尝试生成动画但条件不满足");
|
||
return;
|
||
}
|
||
|
||
GenerationStatus = "正在生成动画...";
|
||
LogManager.Info("开始生成动画");
|
||
|
||
// 🔥 新增:在生成动画阶段构建碰撞检测缓存
|
||
GenerationStatus = "正在构建碰撞检测缓存...";
|
||
LogManager.Info("[动画生成] 开始构建碰撞检测缓存");
|
||
|
||
var cacheStartTime = DateTime.Now;
|
||
try
|
||
{
|
||
// 构建全局缓存(与具体移动物体无关)
|
||
ClashDetectiveIntegration.ClearAllCaches();
|
||
|
||
GenerationStatus = "正在缓存几何对象列表...";
|
||
ClashDetectiveIntegration.BuildAllGeometryItemsCache();
|
||
|
||
GenerationStatus = "正在缓存通道对象...";
|
||
var clashIntegration = ClashDetectiveIntegration.Instance;
|
||
clashIntegration.BuildChannelObjectsCache();
|
||
|
||
var cacheElapsed = (DateTime.Now - cacheStartTime).TotalMilliseconds;
|
||
LogManager.Info($"[动画生成] 碰撞检测缓存构建完成,耗时: {cacheElapsed:F1}ms");
|
||
|
||
// 构建特定于动画对象的缓存
|
||
GenerationStatus = "正在分析动画对象...";
|
||
var success = _logisticsAnimationManager.PrecomputeCollisionExclusions(SelectedAnimatedObject);
|
||
|
||
if (!success)
|
||
{
|
||
LogManager.Warning("[动画生成] 动画对象分析失败,将使用实时计算模式");
|
||
}
|
||
}
|
||
catch (Exception cacheEx)
|
||
{
|
||
LogManager.Warning($"[动画生成] 缓存构建失败: {cacheEx.Message},将使用实时计算模式");
|
||
}
|
||
|
||
// 原有的动画生成逻辑
|
||
GenerationStatus = "正在生成路径动画...";
|
||
|
||
// 将PathRouteViewModel的点转换为Point3D列表
|
||
var pathPoints = CurrentPathRoute.Points.Select(p => new Point3D(p.X, p.Y, p.Z)).ToList();
|
||
|
||
// 使用PathAnimationManager设置物体动画(真正的物体移动动画)
|
||
_pathAnimationManager.SetupAnimation(SelectedAnimatedObject, pathPoints, AnimationDuration);
|
||
|
||
// 更新状态
|
||
CanStartAnimation = true;
|
||
AnimationStatus = "动画已生成,可以开始播放";
|
||
GenerationStatus = "动画生成成功";
|
||
|
||
var totalElapsed = (DateTime.Now - cacheStartTime).TotalMilliseconds;
|
||
LogManager.Info($"[动画生成] 动画生成完成,总耗时: {totalElapsed:F1}ms");
|
||
LogManager.Info($"动画生成成功: 物体={SelectedAnimatedObject.DisplayName}, 路径={CurrentPathRoute.Name}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
GenerationStatus = "动画生成失败";
|
||
AnimationStatus = "动画生成失败";
|
||
LogManager.Error($"生成动画时发生错误: {ex.Message}");
|
||
|
||
LogManager.Error($"动画生成失败,详细错误信息:{ex}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新移动物体信息
|
||
/// </summary>
|
||
private void UpdateAnimatedObjectInfo()
|
||
{
|
||
if (SelectedAnimatedObject != null)
|
||
{
|
||
SelectedAnimatedObjectName = SelectedAnimatedObject.DisplayName ?? "未命名物体";
|
||
HasSelectedAnimatedObject = true;
|
||
}
|
||
else
|
||
{
|
||
SelectedAnimatedObjectName = "未选择移动物体";
|
||
HasSelectedAnimatedObject = false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 递归检查ModelItem是否包含几何体
|
||
/// </summary>
|
||
private bool HasGeometryRecursive(ModelItem item)
|
||
{
|
||
if (item == null) return false;
|
||
|
||
// 如果当前项有几何体,直接返回true
|
||
if (item.HasGeometry) return true;
|
||
|
||
// 递归检查子项
|
||
foreach (ModelItem child in item.Children)
|
||
{
|
||
if (HasGeometryRecursive(child))
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新是否可以生成动画的状态
|
||
/// </summary>
|
||
private void UpdateCanGenerateAnimation()
|
||
{
|
||
var hasObject = SelectedAnimatedObject != null;
|
||
var hasPath = CurrentPathRoute != null && CurrentPathRoute.Points.Count >= 2;
|
||
|
||
CanGenerateAnimation = hasObject && hasPath;
|
||
|
||
if (!hasObject && !hasPath)
|
||
{
|
||
GenerationStatus = "请选择移动物体和动画路径";
|
||
}
|
||
else if (!hasObject)
|
||
{
|
||
GenerationStatus = "请选择移动物体";
|
||
}
|
||
else if (!hasPath)
|
||
{
|
||
GenerationStatus = "请选择有效的动画路径";
|
||
}
|
||
else
|
||
{
|
||
GenerationStatus = "可以生成动画";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新碰撞检测频率
|
||
/// </summary>
|
||
private void UpdateCollisionDetectionFrequency()
|
||
{
|
||
// 碰撞检测频率 = 运动速度 / 检测精度 (次/秒)
|
||
if (_collisionDetectionAccuracy > 0)
|
||
{
|
||
CollisionDetectionFrequency = _movementSpeed / _collisionDetectionAccuracy;
|
||
}
|
||
else
|
||
{
|
||
CollisionDetectionFrequency = 10.0; // 默认值
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化参数更新防抖定时器
|
||
/// </summary>
|
||
private void InitializeParameterUpdateTimer()
|
||
{
|
||
_parameterUpdateTimer = new DispatcherTimer
|
||
{
|
||
Interval = TimeSpan.FromMilliseconds(150) // 150毫秒防抖延迟
|
||
};
|
||
_parameterUpdateTimer.Tick += OnParameterUpdateTimerTick;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 定时器触发时执行参数更新
|
||
/// </summary>
|
||
private void OnParameterUpdateTimerTick(object sender, EventArgs e)
|
||
{
|
||
_parameterUpdateTimer.Stop();
|
||
lock (_parameterUpdateLock)
|
||
{
|
||
if (_hasParameterChanges)
|
||
{
|
||
_hasParameterChanges = false;
|
||
UpdatePathAnimationManagerSettings();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计划参数更新(防抖机制)
|
||
/// </summary>
|
||
private void ScheduleParameterUpdate()
|
||
{
|
||
lock (_parameterUpdateLock)
|
||
{
|
||
_hasParameterChanges = true;
|
||
_parameterUpdateTimer.Stop();
|
||
_parameterUpdateTimer.Start();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新PathAnimationManager的设置
|
||
/// </summary>
|
||
private void UpdatePathAnimationManagerSettings()
|
||
{
|
||
try
|
||
{
|
||
if (_pathAnimationManager != null)
|
||
{
|
||
_pathAnimationManager.SetAnimationFrameRate(_animationFrameRate);
|
||
_pathAnimationManager.SetCollisionDetectionAccuracy(_collisionDetectionAccuracy);
|
||
_pathAnimationManager.SetMovementSpeed(_movementSpeed);
|
||
_pathAnimationManager.SetDetectionGap(_detectionGap);
|
||
|
||
LogManager.Debug($"碰撞检测参数已更新: 帧率={_animationFrameRate}FPS, 精度={_collisionDetectionAccuracy:F2}m, 速度={_movementSpeed:F1}m/s, 间隙={_detectionGap:F2}m");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"更新动画管理器设置失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 动画完成后更新碰撞检测状态
|
||
/// </summary>
|
||
private void UpdateCollisionStatusAfterAnimation()
|
||
{
|
||
try
|
||
{
|
||
// 给一个短暂延迟,让CreateAllAnimationCollisionTests有时间完成
|
||
Task.Delay(1000).ContinueWith(_ => _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
// 检查ClashDetectiveIntegration是否有缓存结果
|
||
var clashIntegration = _clashIntegration;
|
||
if (clashIntegration != null)
|
||
{
|
||
// 通过检查是否有任何动画相关的碰撞测试来判断是否有结果
|
||
HasCollisionResults = true; // 假设动画完成后总是有结果可查看
|
||
CollisionStatus = "动画完成,碰撞检测已完成";
|
||
CollisionSummary = "请点击'查看碰撞报告'查看详细结果";
|
||
LogManager.Info("动画完成后碰撞状态已更新");
|
||
}
|
||
else
|
||
{
|
||
CollisionStatus = "碰撞检测未就绪";
|
||
CollisionSummary = "碰撞检测功能不可用";
|
||
LogManager.Warning("ClashDetectiveIntegration实例不可用");
|
||
}
|
||
}));
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"动画完成后更新碰撞状态失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成碰撞检测报告数据
|
||
/// </summary>
|
||
private CollisionReportData GenerateCollisionReport()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("开始从Clash Detective获取碰撞检测结果");
|
||
|
||
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
var documentClash = doc.GetClash();
|
||
|
||
if (documentClash == null)
|
||
{
|
||
LogManager.Warning("无法获取Clash Detective文档");
|
||
return new CollisionReportData { IsValid = false, ErrorMessage = "Clash Detective不可用" };
|
||
}
|
||
|
||
var reportData = new CollisionReportData
|
||
{
|
||
IsValid = true,
|
||
GeneratedTime = DateTime.Now,
|
||
AnimatedObjectName = SelectedAnimatedObject?.DisplayName ?? "未知对象",
|
||
PathName = CurrentPathRoute?.Name ?? "未知路径"
|
||
};
|
||
|
||
// 获取所有动画相关的碰撞测试
|
||
var animationTests = documentClash.TestsData.Tests.Cast<ClashTest>()
|
||
.Where(t => t.DisplayName.Contains("动画路径碰撞"))
|
||
.ToList();
|
||
|
||
LogManager.Info($"找到 {animationTests.Count} 个动画碰撞测试");
|
||
|
||
foreach (var test in animationTests)
|
||
{
|
||
var testInfo = new CollisionTestInfo
|
||
{
|
||
TestName = test.DisplayName,
|
||
TestType = test.TestType.ToString(),
|
||
Tolerance = test.Tolerance,
|
||
CollisionCount = test.Children.Count,
|
||
Status = test.Status.ToString()
|
||
};
|
||
|
||
// 获取每个碰撞的详细信息
|
||
foreach (var clashResult in test.Children.Cast<ClashResult>())
|
||
{
|
||
var collision = new CollisionDetailInfo
|
||
{
|
||
CollisionId = clashResult.Guid.ToString(),
|
||
Distance = clashResult.Distance,
|
||
Status = clashResult.Status.ToString()
|
||
};
|
||
|
||
// 获取碰撞对象信息
|
||
if (clashResult.CompositeItem1 != null)
|
||
{
|
||
collision.Object1Name = clashResult.CompositeItem1.DisplayName;
|
||
var point = clashResult.CompositeItem1.BoundingBox();
|
||
collision.Object1Position = $"({point.Center.X:F2}, {point.Center.Y:F2}, {point.Center.Z:F2})";
|
||
}
|
||
|
||
if (clashResult.CompositeItem2 != null)
|
||
{
|
||
collision.Object2Name = clashResult.CompositeItem2.DisplayName;
|
||
var point = clashResult.CompositeItem2.BoundingBox();
|
||
collision.Object2Position = $"({point.Center.X:F2}, {point.Center.Y:F2}, {point.Center.Z:F2})";
|
||
}
|
||
|
||
// 获取碰撞中心点
|
||
if (clashResult.Center != null)
|
||
{
|
||
collision.CollisionCenter = $"({clashResult.Center.X:F2}, {clashResult.Center.Y:F2}, {clashResult.Center.Z:F2})";
|
||
}
|
||
|
||
testInfo.Collisions.Add(collision);
|
||
}
|
||
|
||
reportData.Tests.Add(testInfo);
|
||
}
|
||
|
||
// 统计总体信息
|
||
reportData.TotalTests = reportData.Tests.Count;
|
||
reportData.TotalCollisions = reportData.Tests.Sum(t => t.CollisionCount);
|
||
|
||
LogManager.Info($"碰撞报告生成完成:{reportData.TotalTests}个测试,{reportData.TotalCollisions}个碰撞");
|
||
return reportData;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"生成碰撞报告异常: {ex.Message}");
|
||
return new CollisionReportData { IsValid = false, ErrorMessage = ex.Message };
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显示碰撞报告
|
||
/// </summary>
|
||
private async Task ShowCollisionReport(CollisionReportData reportData)
|
||
{
|
||
try
|
||
{
|
||
// 更新UI摘要信息
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
if (reportData.IsValid)
|
||
{
|
||
// 直接使用报告数据统计
|
||
CollisionSummary = $"动画检测: {reportData.TotalTests}个 | Clash Detective权威结果: {reportData.TotalCollisions}个";
|
||
}
|
||
else
|
||
{
|
||
CollisionSummary = $"报告生成失败:{reportData.ErrorMessage}";
|
||
}
|
||
});
|
||
|
||
// 如果报告有效且有碰撞数据,显示详细报告窗口
|
||
if (reportData.IsValid && reportData.TotalCollisions > 0)
|
||
{
|
||
// 调用ViewCollisionReportCommand显示详细报告窗口
|
||
try
|
||
{
|
||
// 创建综合碰撞报告命令(现在不需要传递参数了!)
|
||
var reportCommand = NavisworksTransport.Commands.ViewCollisionReportCommand.CreateComprehensive(autoHighlight: false);
|
||
|
||
// 执行命令,UI显示会在正确的线程中处理
|
||
var result = await reportCommand.ExecuteAsync();
|
||
|
||
if (result.IsSuccess)
|
||
{
|
||
LogManager.Info("碰撞报告窗口已成功显示");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning($"显示碰撞报告窗口失败: {result.ErrorMessage}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"调用碰撞报告命令失败: {ex.Message}");
|
||
}
|
||
}
|
||
else if (reportData.IsValid && reportData.TotalCollisions == 0)
|
||
{
|
||
// 没有碰撞时显示简单提示
|
||
LogManager.Info("未发现碰撞问题,路径规划良好");
|
||
System.Windows.MessageBox.Show(
|
||
"未发现碰撞问题,路径规划良好!",
|
||
"碰撞检测结果",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Information);
|
||
}
|
||
else
|
||
{
|
||
// 报告无效时的错误处理
|
||
LogManager.Error($"碰撞报告数据无效: {reportData.ErrorMessage}");
|
||
System.Windows.MessageBox.Show(
|
||
$"碰撞报告生成失败:{reportData.ErrorMessage}",
|
||
"错误",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Error);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"显示碰撞报告异常: {ex.Message}");
|
||
System.Windows.MessageBox.Show(
|
||
$"显示碰撞报告时发生异常:{ex.Message}",
|
||
"错误",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将碰撞报告详情输出到日志
|
||
/// </summary>
|
||
private void LogCollisionReportDetails(CollisionReportData reportData)
|
||
{
|
||
if (!reportData.IsValid)
|
||
{
|
||
LogManager.Error($"碰撞报告无效: {reportData.ErrorMessage}");
|
||
return;
|
||
}
|
||
|
||
var report = new System.Text.StringBuilder();
|
||
report.AppendLine("=== 碰撞检测详细报告 ===");
|
||
report.AppendLine($"生成时间: {reportData.GeneratedTime:yyyy-MM-dd HH:mm:ss}");
|
||
report.AppendLine($"动画对象: {reportData.AnimatedObjectName}");
|
||
report.AppendLine($"动画路径: {reportData.PathName}");
|
||
report.AppendLine($"总测试数: {reportData.TotalTests}");
|
||
report.AppendLine($"总碰撞数: {reportData.TotalCollisions}");
|
||
report.AppendLine();
|
||
|
||
foreach (var test in reportData.Tests)
|
||
{
|
||
report.AppendLine($"【测试】 {test.TestName}");
|
||
report.AppendLine($" 类型: {test.TestType}, 容差: {test.Tolerance:F3}, 状态: {test.Status}");
|
||
report.AppendLine($" 碰撞数量: {test.CollisionCount}");
|
||
|
||
foreach (var collision in test.Collisions)
|
||
{
|
||
report.AppendLine($" 碰撞 {collision.CollisionId}:");
|
||
report.AppendLine($" 对象1: {collision.Object1Name} 位置: {collision.Object1Position}");
|
||
report.AppendLine($" 对象2: {collision.Object2Name} 位置: {collision.Object2Position}");
|
||
report.AppendLine($" 碰撞中心: {collision.CollisionCenter}");
|
||
report.AppendLine($" 距离: {collision.Distance:F3}, 状态: {collision.Status}");
|
||
}
|
||
report.AppendLine();
|
||
}
|
||
|
||
LogManager.Info(report.ToString());
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 资源清理
|
||
|
||
private bool _disposed = false;
|
||
|
||
/// <summary>
|
||
/// 清理资源
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
Dispose(true);
|
||
GC.SuppressFinalize(this);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清理资源
|
||
/// </summary>
|
||
protected virtual void Dispose(bool disposing)
|
||
{
|
||
if (!_disposed)
|
||
{
|
||
if (disposing)
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("开始清理AnimationControlViewModel资源");
|
||
|
||
// 1. 优先清理防抖定时器,避免在清理过程中触发更新
|
||
if (_parameterUpdateTimer != null)
|
||
{
|
||
try
|
||
{
|
||
_parameterUpdateTimer.Stop();
|
||
_parameterUpdateTimer.Tick -= OnParameterUpdateTimerTick;
|
||
_parameterUpdateTimer = null;
|
||
LogManager.Debug("防抖定时器清理完成");
|
||
}
|
||
catch (Exception timerEx)
|
||
{
|
||
LogManager.Warning($"清理防抖定时器时出现警告: {timerEx.Message}");
|
||
}
|
||
}
|
||
|
||
// 2. 取消事件订阅(在停止动画之前,避免事件处理中访问已释放的对象)
|
||
if (_pathAnimationManager != null)
|
||
{
|
||
try
|
||
{
|
||
_pathAnimationManager.ProgressChanged -= OnAnimationProgressChanged;
|
||
_pathAnimationManager.StateChanged -= OnAnimationStateChanged;
|
||
LogManager.Debug("PathAnimationManager事件订阅取消完成");
|
||
}
|
||
catch (Exception eventEx)
|
||
{
|
||
LogManager.Warning($"取消PathAnimationManager事件订阅时出现警告: {eventEx.Message}");
|
||
}
|
||
}
|
||
|
||
if (_clashIntegration != null)
|
||
{
|
||
try
|
||
{
|
||
_clashIntegration.CollisionDetected -= OnCollisionDetected;
|
||
LogManager.Debug("碰撞检测事件订阅取消完成");
|
||
}
|
||
catch (Exception eventEx)
|
||
{
|
||
LogManager.Warning($"取消碰撞检测事件订阅时出现警告: {eventEx.Message}");
|
||
}
|
||
}
|
||
|
||
// 3. 直接清理动画管理器(PathAnimationManager.Dispose内部已经优化了重复调用)
|
||
// 不需要在这里再次调用StopAnimation,让PathAnimationManager自己处理
|
||
if (_pathAnimationManager != null)
|
||
{
|
||
try
|
||
{
|
||
// 检查Navisworks应用程序是否仍然可用
|
||
var app = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (app != null && app.Models != null)
|
||
{
|
||
_pathAnimationManager.Dispose();
|
||
LogManager.Debug("PathAnimationManager清理完成");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Info("Navisworks文档已不可用,跳过PathAnimationManager清理操作");
|
||
}
|
||
}
|
||
catch (Exception disposeEx)
|
||
{
|
||
LogManager.Warning($"清理PathAnimationManager时出现警告: {disposeEx.Message}");
|
||
}
|
||
}
|
||
|
||
// 4. 清理LogisticsAnimationManager缓存
|
||
if (_logisticsAnimationManager != null)
|
||
{
|
||
try
|
||
{
|
||
_logisticsAnimationManager.ClearExclusionCache();
|
||
_logisticsAnimationManager.Dispose();
|
||
LogManager.Debug("LogisticsAnimationManager缓存清理完成");
|
||
}
|
||
catch (Exception logisticsEx)
|
||
{
|
||
LogManager.Warning($"清理LogisticsAnimationManager缓存时出现警告: {logisticsEx.Message}");
|
||
}
|
||
}
|
||
|
||
LogManager.Info("AnimationControlViewModel资源清理完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"AnimationControlViewModel资源清理过程中发生异常: {ex.Message}");
|
||
// 即使清理过程中发生异常,也不应该抛出,因为这会干扰应用程序的正常关闭
|
||
}
|
||
}
|
||
|
||
_disposed = true;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
} |