修改UI更新的定时器线程安全导致崩溃问题
This commit is contained in:
parent
1f82eb814f
commit
722e2ce9cc
@ -1491,7 +1491,7 @@ public async Task SafeCacheRefreshAsync()
|
||||
- `DatabaseDockPane/Models.cs` - 数据库操作示例
|
||||
- `ClashDetective` 相关示例 - 高级功能示例
|
||||
|
||||
碰撞检测模式说明
|
||||
### 碰撞检测模式说明
|
||||
|
||||
Hard(硬碰撞)
|
||||
|
||||
@ -1559,3 +1559,89 @@ public async Task SafeCacheRefreshAsync()
|
||||
collisionTest.SelectionA.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points;
|
||||
collisionTest.SelectionB.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points;
|
||||
```
|
||||
|
||||
### ModelGeometry 属性
|
||||
|
||||
ModelGeometry类公开了以下成员:
|
||||
|
||||
属性
|
||||
|
||||
| 名称 | 说明 |
|
||||
|----------------------------|----------------------------------------|
|
||||
| ActiveColor | 此几何体的当前(可见)颜色 |
|
||||
| ActiveTransform | 返回几何体当前活动的变换矩阵 |
|
||||
| ActiveTransparency | 此几何体的当前(可见)透明度 |
|
||||
| BoundingBox | 此几何体在世界坐标系中的包围盒 |
|
||||
| FragmentCount | 此几何体被分割成的片段数量 |
|
||||
| IsDisposed | 获取一个值,指示对象是否已被释放且不再可用(继承自NativeHandle) |
|
||||
| IsReadOnly | 是否只读(重写NativeHandle的IsReadOnly属性) |
|
||||
| IsSolid | 此几何体是否为实体?(包括所有形成封闭、流形外壳的三角形图元) |
|
||||
| Item | 模型层次结构中对应此几何体的项目 |
|
||||
| OriginalColor | 此几何体的原始颜色(设计文件中指定的) |
|
||||
| OriginalTransform | 返回几何体加载时的原始变换矩阵 |
|
||||
| OriginalTransparency | 此几何体的原始透明度(设计文件中指定的) |
|
||||
| PermanentColor | 几何体的永久颜色。可能是原始颜色或用户明确覆盖的颜色 |
|
||||
| PermanentOverrideTransform | 应用于模型几何体原始变换的覆盖变换 |
|
||||
| PermanentTransform | 模型几何体的永久变换。由原始变换与覆盖变换组合形成的变换 |
|
||||
| PermanentTransparency | 几何体的永久透明度。可能是原始透明度或用户明确覆盖的透明度 |
|
||||
| PrimitiveCount | 定义此几何体的图元(三角形、线、点)数量 |
|
||||
| PrimitiveTypes | 用于定义此几何体的图元类型 |
|
||||
|
||||
这些属性提供了访问和查询Navisworks模型几何体各种状态信息的接口,包括颜色、变换、透明度、包围盒和几何体结构等重要属性。
|
||||
|
||||
```csharp
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using System.Text;
|
||||
|
||||
using Autodesk.Navisworks.Api.Controls;
|
||||
|
||||
static public void OutputFirstGeometry()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Autodesk.Navisworks.Api.Application.ActiveDocument != null &&
|
||||
!Autodesk.Navisworks.Api.Application.ActiveDocument.IsClear)
|
||||
{
|
||||
ModelGeometry first =
|
||||
Autodesk.Navisworks.Api.Application.ActiveDocument.
|
||||
Models[0].RootItem.FindFirstGeometry();
|
||||
|
||||
if (first != null)
|
||||
{
|
||||
string text = string.Empty;
|
||||
text = string.Format("ActiveColor = {0}" +
|
||||
"\nActiveTransparency = {1}" +
|
||||
"\nBoundingBox = {2}" +
|
||||
"\nFragmentCount {3}" +
|
||||
"\nIsSolid {4}" +
|
||||
"\nItem {5}" +
|
||||
"\nOriginalColor {6}" +
|
||||
"\nOriginalTransparency {7}" +
|
||||
"\nPermanentColor {8}" +
|
||||
"\nPermanentTransparency {9}",
|
||||
first.ActiveColor.ToString(),
|
||||
first.ActiveTransparency,
|
||||
first.BoundingBox.ToString(),
|
||||
first.FragmentCount,
|
||||
first.IsSolid.ToString(),
|
||||
first.Item.ToString(),
|
||||
first.OriginalColor.ToString(),
|
||||
first.OriginalTransparency,
|
||||
first.PermanentColor.ToString(),
|
||||
first.PermanentTransparency);
|
||||
|
||||
MessageBox.Show(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MessageBox.Show(e.Message + "\n\n" + e.ToString());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -6,7 +6,6 @@ using Autodesk.Navisworks.Api;
|
||||
using Autodesk.Navisworks.Api.Clash;
|
||||
using ComApi = Autodesk.Navisworks.Api.Interop.ComApi;
|
||||
using ComApiBridge = Autodesk.Navisworks.Api.ComApi;
|
||||
using NavisworksTransport.UI.WPF;
|
||||
using NavisworksTransport.Utils;
|
||||
|
||||
namespace NavisworksTransport
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -48,19 +48,8 @@ namespace NavisworksTransport
|
||||
// 捕获任务调度器未观察到的异常
|
||||
System.Threading.Tasks.TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
|
||||
|
||||
// 尝试设置线程异常处理,但如果失败就跳过(可能控件已创建)
|
||||
try
|
||||
{
|
||||
System.Windows.Forms.Application.ThreadException += OnThreadException;
|
||||
System.Windows.Forms.Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
|
||||
LogManager.Info("[全局异常] WinForms异常处理已设置");
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
LogManager.Warning($"[全局异常] 无法设置WinForms异常处理模式(控件已创建): {ex.Message}");
|
||||
// 仍然可以添加事件处理器
|
||||
System.Windows.Forms.Application.ThreadException += OnThreadException;
|
||||
}
|
||||
// 直接注册WinForms线程异常处理器(不调用SetUnhandledExceptionMode,避免插件环境下的警告)
|
||||
System.Windows.Forms.Application.ThreadException += OnThreadException;
|
||||
|
||||
_isInitialized = true;
|
||||
|
||||
@ -319,6 +308,15 @@ namespace NavisworksTransport
|
||||
[DockPanePlugin(420, 700, FixedSize = false, AutoScroll = true)]
|
||||
public class Main : DockPanePlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造函数,提前初始化全局异常处理器
|
||||
/// </summary>
|
||||
public Main()
|
||||
{
|
||||
// 在任何控件创建之前初始化全局异常处理器
|
||||
GlobalExceptionHandler.Initialize();
|
||||
}
|
||||
|
||||
public override Control CreateControlPane()
|
||||
{
|
||||
try
|
||||
@ -374,4 +372,5 @@ namespace NavisworksTransport
|
||||
}, "销毁控制面板");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -743,11 +743,15 @@ namespace NavisworksTransport.Core
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_flushTimer != null)
|
||||
var timer = _flushTimer;
|
||||
if (timer != null)
|
||||
{
|
||||
_flushTimer = null; // 先置空,防止回调继续执行
|
||||
LogManager.Info("停止保底定时器");
|
||||
_flushTimer.Dispose();
|
||||
_flushTimer = null;
|
||||
|
||||
// 使用Change方法停止定时器,然后安全释放
|
||||
timer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
timer.Dispose();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@ -1133,9 +1133,9 @@ namespace NavisworksTransport.PathPlanning
|
||||
|
||||
LogManager.Info($"[包围盒障碍物处理] 输入统计 - 总模型项: {totalItems}, 将排除通道元素: {channelItemsSet.Count}");
|
||||
|
||||
// 使用并行处理提高性能,使用75%的CPU内核以平衡性能和稳定性
|
||||
// 使用并行处理提高性能,使用50%的CPU内核以平衡性能和稳定性
|
||||
var lockObject = new object();
|
||||
Parallel.ForEach(allItems, new ParallelOptions { MaxDegreeOfParallelism = Math.Max(1, Environment.ProcessorCount * 3 / 4) }, item =>
|
||||
Parallel.ForEach(allItems, new ParallelOptions { MaxDegreeOfParallelism = Math.Max(1, Environment.ProcessorCount / 2) }, item =>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@ -96,8 +96,7 @@ namespace NavisworksTransport.UI.WPF
|
||||
{
|
||||
LogManager.Info("=== 物流路径规划插件初始化开始 ===");
|
||||
|
||||
// 初始化全局异常处理
|
||||
GlobalExceptionHandler.Initialize();
|
||||
// 全局异常处理器已在Main构造函数中初始化,此处无需重复初始化
|
||||
|
||||
// 初始化路径规划管理器
|
||||
InitializePathPlanningManager();
|
||||
|
||||
@ -572,10 +572,29 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
try
|
||||
{
|
||||
// 先在UI线程上创建快照,确保线程安全
|
||||
var pointsSnapshot = await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||||
{
|
||||
return Points?.ToList() ?? new List<PathPointViewModel>();
|
||||
});
|
||||
|
||||
// 然后在后台线程计算
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
double totalLength = 0.0;
|
||||
var pointsSnapshot = Points.ToList();
|
||||
|
||||
if (pointsSnapshot.Count < 2)
|
||||
{
|
||||
// 在UI线程上更新总长度
|
||||
_uiStateManager.QueueUIUpdate(() =>
|
||||
{
|
||||
_totalLength = 0.0;
|
||||
OnPropertyChanged(nameof(TotalLength));
|
||||
OnPropertyChanged(nameof(SummaryInfo));
|
||||
});
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < pointsSnapshot.Count - 1; i++)
|
||||
{
|
||||
@ -617,27 +636,36 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
try
|
||||
{
|
||||
// 先在UI线程上创建快照,确保线程安全
|
||||
var pointsSnapshot = await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||||
{
|
||||
return Points?.ToList() ?? new List<PathPointViewModel>();
|
||||
});
|
||||
|
||||
var nameSnapshot = await _uiStateManager.ExecuteUIUpdateAsync(() => _name);
|
||||
|
||||
// 然后在后台线程验证
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
bool isValid = true;
|
||||
var validationMessages = new List<string>();
|
||||
|
||||
// 检查路径点数量
|
||||
if (Points.Count < 2)
|
||||
if (pointsSnapshot.Count < 2)
|
||||
{
|
||||
isValid = false;
|
||||
validationMessages.Add("路径至少需要2个点");
|
||||
}
|
||||
|
||||
// 检查路径名称
|
||||
if (string.IsNullOrWhiteSpace(_name))
|
||||
if (string.IsNullOrWhiteSpace(nameSnapshot))
|
||||
{
|
||||
isValid = false;
|
||||
validationMessages.Add("路径名称不能为空");
|
||||
}
|
||||
|
||||
// 检查重复点
|
||||
var duplicatePoints = Points.ToList()
|
||||
var duplicatePoints = pointsSnapshot
|
||||
.GroupBy(p => new { p.X, p.Y, p.Z })
|
||||
.Where(g => g.Count() > 1)
|
||||
.ToList();
|
||||
@ -657,7 +685,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
OnPropertyChanged(nameof(ValidationStatus));
|
||||
});
|
||||
|
||||
LogManager.Debug($"路径验证完成:{_name},结果:{(isValid ? "通过" : "失败")}");
|
||||
LogManager.Debug($"路径验证完成:{nameSnapshot},结果:{(isValid ? "通过" : "失败")}");
|
||||
return isValid;
|
||||
});
|
||||
}
|
||||
|
||||
@ -456,12 +456,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
AnimationDuration = 10.0;
|
||||
CurrentAnimationTime = 0.0;
|
||||
|
||||
// 设置初始状态
|
||||
CanStartAnimation = true;
|
||||
// 设置初始状态 - 修改: 默认状态应该是未激活,等待有效动画
|
||||
CanPauseAnimation = false;
|
||||
CanStopAnimation = false;
|
||||
StartAnimationButtonText = "开始动画";
|
||||
AnimationStatus = "动画状态: 就绪";
|
||||
AnimationProgress = 0;
|
||||
|
||||
// 初始化碰撞检测状态
|
||||
@ -479,6 +477,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
// 初始化动画管理器设置
|
||||
UpdatePathAnimationManagerSettings();
|
||||
|
||||
// 修改: 使用新的按钮状态更新方法来设置正确的初始状态
|
||||
UpdateAnimationButtonStates();
|
||||
|
||||
LogManager.Info("动画设置初始化完成");
|
||||
}
|
||||
|
||||
@ -719,24 +720,20 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
CurrentPathRoute = pathRoute;
|
||||
|
||||
// 更新按钮状态
|
||||
var hasValidPath = pathRoute != null && pathRoute.Points.Count >= 2;
|
||||
CanStartAnimation = hasValidPath;
|
||||
// 使用新的统一按钮状态更新方法
|
||||
UpdateAnimationButtonStates();
|
||||
|
||||
// 更新状态文本
|
||||
// 更新碰撞状态
|
||||
if (pathRoute == null)
|
||||
{
|
||||
AnimationStatus = "动画状态: 请选择路径";
|
||||
CollisionStatus = "请选择路径";
|
||||
}
|
||||
else if (pathRoute.Points.Count < 2)
|
||||
{
|
||||
AnimationStatus = "动画状态: 路径点数不足(需要至少2个点)";
|
||||
CollisionStatus = "路径点数不足";
|
||||
}
|
||||
else
|
||||
{
|
||||
AnimationStatus = "动画状态: 就绪";
|
||||
CollisionStatus = "就绪";
|
||||
}
|
||||
|
||||
@ -878,15 +875,27 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
|
||||
/// <summary>
|
||||
/// 执行清除移动物体命令
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// 执行清除移动物体命令
|
||||
/// 清除移动物体选择、停止当前动画并更新按钮状态
|
||||
/// </summary>
|
||||
private void ExecuteClearAnimatedObject()
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.Info("开始清除移动物体选择并恢复原始位置");
|
||||
LogManager.Info("开始清除移动物体选择、当前动画并恢复原始位置");
|
||||
|
||||
// 首先停止当前动画(如果正在播放)
|
||||
if (_pathAnimationManager != null && _pathAnimationManager.IsAnimating)
|
||||
{
|
||||
try
|
||||
{
|
||||
_pathAnimationManager.StopAnimation();
|
||||
LogManager.Info("已停止当前播放的动画");
|
||||
}
|
||||
catch (Exception stopEx)
|
||||
{
|
||||
LogManager.Warning($"停止当前动画时出现警告: {stopEx.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有选中的物体,则恢复到原始位置
|
||||
if (SelectedAnimatedObject != null)
|
||||
@ -918,7 +927,22 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
// 清理选择状态
|
||||
SelectedAnimatedObject = null;
|
||||
|
||||
LogManager.Info("移动物体选择已完全清除");
|
||||
// 重置动画进度和状态
|
||||
AnimationProgress = 0;
|
||||
CurrentAnimationTime = 0.0;
|
||||
|
||||
// 清理碰撞检测结果
|
||||
HasCollisionResults = false;
|
||||
CollisionStatus = "就绪";
|
||||
CollisionSummary = "尚未运行碰撞检测";
|
||||
|
||||
// 更新按钮状态 - 清除动画后应该重新评估按钮状态
|
||||
UpdateAnimationButtonStates();
|
||||
|
||||
// 更新生成动画的能力状态
|
||||
UpdateCanGenerateAnimation();
|
||||
|
||||
LogManager.Info("移动物体选择和当前动画已完全清除,按钮状态已更新");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -1047,9 +1071,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
// 使用PathAnimationManager设置物体动画(真正的物体移动动画)
|
||||
_pathAnimationManager.SetupAnimation(SelectedAnimatedObject, pathPoints, AnimationDuration);
|
||||
|
||||
// 更新状态
|
||||
CanStartAnimation = true;
|
||||
AnimationStatus = "动画已生成,可以开始播放";
|
||||
// 更新状态 - 使用新的按钮状态更新方法
|
||||
UpdateAnimationButtonStates();
|
||||
GenerationStatus = "动画生成成功";
|
||||
|
||||
var totalElapsed = (DateTime.Now - cacheStartTime).TotalMilliseconds;
|
||||
@ -1129,6 +1152,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
GenerationStatus = "可以生成动画";
|
||||
}
|
||||
|
||||
// 同时更新动画按钮状态,因为对象或路径的变化会影响"开始动画"按钮的可用性
|
||||
UpdateAnimationButtonStates();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -1146,6 +1172,78 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
CollisionDetectionFrequency = 10.0; // 默认值
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 统一更新动画按钮状态逻辑
|
||||
/// 根据当前的路径、动画对象和动画管理器状态来决定按钮的可用性
|
||||
/// </summary>
|
||||
private void UpdateAnimationButtonStates()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查是否有有效的动画可以播放
|
||||
var hasValidPath = CurrentPathRoute != null && CurrentPathRoute.Points.Count >= 2;
|
||||
var hasValidAnimatedObject = SelectedAnimatedObject != null;
|
||||
var hasValidAnimation = hasValidPath && hasValidAnimatedObject;
|
||||
|
||||
// 检查动画管理器当前状态
|
||||
var animationState = _pathAnimationManager?.CurrentState ?? NavisworksTransport.Core.Animation.AnimationState.Stopped;
|
||||
var isAnimating = _pathAnimationManager?.IsAnimating ?? false;
|
||||
|
||||
// 根据动画状态和条件更新按钮状态
|
||||
switch (animationState)
|
||||
{
|
||||
case NavisworksTransport.Core.Animation.AnimationState.Playing:
|
||||
// 播放中:开始按钮禁用,暂停和停止按钮可用
|
||||
CanStartAnimation = false;
|
||||
CanPauseAnimation = true;
|
||||
CanStopAnimation = true;
|
||||
AnimationStatus = "动画状态: 播放中";
|
||||
break;
|
||||
|
||||
case NavisworksTransport.Core.Animation.AnimationState.Paused:
|
||||
// 暂停中:开始按钮可用(显示为继续),暂停按钮禁用,停止按钮可用
|
||||
CanStartAnimation = hasValidAnimation;
|
||||
CanPauseAnimation = false;
|
||||
CanStopAnimation = true;
|
||||
StartAnimationButtonText = "继续播放";
|
||||
AnimationStatus = "动画状态: 已暂停";
|
||||
break;
|
||||
|
||||
default:
|
||||
// 停止或其他状态:根据是否有有效动画来决定开始按钮状态
|
||||
CanStartAnimation = hasValidAnimation;
|
||||
CanPauseAnimation = false;
|
||||
CanStopAnimation = false;
|
||||
StartAnimationButtonText = "开始动画";
|
||||
|
||||
// 更新状态文本
|
||||
if (!hasValidPath && !hasValidAnimatedObject)
|
||||
{
|
||||
AnimationStatus = "动画状态: 请选择路径和移动物体";
|
||||
}
|
||||
else if (!hasValidPath)
|
||||
{
|
||||
AnimationStatus = "动画状态: 请选择有效路径(至少2个点)";
|
||||
}
|
||||
else if (!hasValidAnimatedObject)
|
||||
{
|
||||
AnimationStatus = "动画状态: 请选择移动物体";
|
||||
}
|
||||
else
|
||||
{
|
||||
AnimationStatus = "动画状态: 就绪";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
LogManager.Debug($"按钮状态已更新: CanStart={CanStartAnimation}, CanPause={CanPauseAnimation}, CanStop={CanStopAnimation}, Status={AnimationStatus}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"更新动画按钮状态时发生错误: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化参数更新防抖定时器
|
||||
|
||||
Loading…
Reference in New Issue
Block a user