NavisworksTransport/src/Core/BatchQueueManager.cs

423 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Autodesk.Navisworks.Api;
using NavisworksTransport.Core.Animation;
using NavisworksTransport.Core.Collision;
using NavisworksTransport.Core.Models;
namespace NavisworksTransport.Core
{
/// <summary>
/// 批处理队列管理器 - 简化版只维护FIFO队列
/// </summary>
public class BatchQueueManager
{
private static BatchQueueManager _instance;
private static readonly object _lock = new object();
private PathDatabase _database;
private BatchCollisionExecutor _executor;
private readonly Queue<int> _queue;
private readonly object _queueLock = new object();
private BatchQueueItem _currentItem;
public event EventHandler<BatchQueueItemEventArgs> ItemAdded;
public event EventHandler<BatchQueueItemEventArgs> ItemStarted;
public event EventHandler<BatchQueueItemEventArgs> ItemCompleted;
public event EventHandler<BatchQueueProgressEventArgs> ItemProgress;
public bool IsExecuting => _currentItem != null;
public int QueueCount => _queue.Count;
public static BatchQueueManager Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
_instance = new BatchQueueManager();
}
}
}
return _instance;
}
}
private BatchQueueManager()
{
// 延迟初始化,等待 PathDatabase 就绪
_database = null;
_executor = null;
_queue = new Queue<int>();
}
/// <summary>
/// 设置PathPlanningManager用于获取数据库
/// </summary>
public void SetPathPlanningManager(PathPlanningManager pathPlanningManager)
{
_database = pathPlanningManager?.GetPathDatabase();
LogManager.Info("BatchQueueManager已设置PathPlanningManager");
}
/// <summary>
/// 确保 Executor 已初始化
/// </summary>
private void EnsureExecutorInitialized()
{
if (_executor == null)
{
var pathManager = PathPlanningManager.Instance;
_database = pathManager?.GetPathDatabase();
if (_database == null)
{
throw new InvalidOperationException("PathDatabase未初始化请确保文档已加载");
}
_executor = new BatchCollisionExecutor(
_database,
new BatchCollisionProcessor(),
UIStateManager.Instance
);
// 转换事件参数类型
_executor.TaskProgress += (s, e) =>
{
ItemProgress?.Invoke(s, new BatchQueueProgressEventArgs
{
ItemId = e.TaskId,
CompletedItems = e.CompletedItems,
TotalItems = e.TotalItems,
Percentage = e.Percentage
});
};
}
}
/// <summary>
/// 添加队列项
/// </summary>
public async Task<int> AddQueueItemAsync(BatchQueueItem item)
{
if (_database == null)
{
throw new InvalidOperationException("PathDatabase未初始化");
}
var itemId = await _database.CreateBatchQueueItemAsync(item);
item.Id = itemId;
lock (_queueLock)
{
_queue.Enqueue(itemId);
}
ItemAdded?.Invoke(this, new BatchQueueItemEventArgs { Item = item });
LogManager.Info($"[批处理队列] 已添加队列项: {item.PathRouteName}, ID: {itemId}");
return itemId;
}
/// <summary>
/// 处理队列(公共方法,供外部调用)
/// </summary>
public async Task ProcessQueueAsync()
{
while (true)
{
int itemId;
lock (_queueLock)
{
if (_queue.Count == 0)
{
break;
}
itemId = _queue.Dequeue();
}
try
{
EnsureExecutorInitialized();
var item = await _database.GetBatchQueueItemAsync(itemId);
_currentItem = item;
ItemStarted?.Invoke(this, new BatchQueueItemEventArgs { Item = item });
await ExecuteQueueItemAsync(item);
ItemCompleted?.Invoke(this, new BatchQueueItemEventArgs { Item = item });
}
catch (Exception ex)
{
LogManager.Error($"队列项执行失败 (Id: {itemId}): {ex.Message}");
// 更新状态为失败
if (_database != null)
{
var item = await _database.GetBatchQueueItemAsync(itemId);
if (item != null)
{
item.Status = BatchQueueStatus.Failed;
item.ErrorMessage = ex.Message;
item.EndTime = DateTime.Now;
await _database.UpdateBatchQueueItemAsync(item);
}
}
}
finally
{
_currentItem = null;
}
}
}
/// <summary>
/// 执行单个队列项
/// </summary>
private async Task ExecuteQueueItemAsync(BatchQueueItem item)
{
item.Status = BatchQueueStatus.Running;
item.StartTime = DateTime.Now;
await _database.UpdateBatchQueueItemAsync(item);
try
{
var pathRoute = await _database.GetPathRouteAsync(item.PathRouteId);
if (pathRoute == null)
{
throw new InvalidOperationException($"路径不存在 (Id: {item.PathRouteId})");
}
// 获取运动物体
ModelItem animatedObject = null;
bool isVirtualVehicle = item.IsVirtualVehicle;
if (!string.IsNullOrEmpty(item.VehicleObjectId))
{
animatedObject = FindModelItemById(item.VehicleObjectId);
isVirtualVehicle = false;
if (animatedObject == null)
{
throw new InvalidOperationException("未找到指定的运动物体");
}
LogManager.Info($"[批处理] 使用真实物体: {animatedObject.DisplayName}");
}
else if (isVirtualVehicle)
{
// 🔥 使用现有的虚拟车辆,不创建和清理
// ShowVirtualVehicle 会显示虚拟车辆并更新尺寸(如果已存在)
VirtualVehicleManager.Instance.ShowVirtualVehicle(
item.VirtualVehicleLength,
item.VirtualVehicleWidth,
item.VirtualVehicleHeight
);
animatedObject = VirtualVehicleManager.Instance.CurrentVirtualVehicle;
if (animatedObject == null)
{
throw new InvalidOperationException("虚拟车辆显示失败");
}
LogManager.Info($"[批处理] 使用虚拟车辆: {animatedObject.DisplayName}");
}
// 初始化碰撞检测缓存
LogManager.Info("[批处理] 初始化碰撞检测缓存...");
ClashDetectiveIntegration.InitializeCollisionDetectionCache(animatedObject);
// 在主线程执行Navisworks API调用
var frames = await UIStateManager.Instance.ExecuteUIUpdateAsync(() =>
{
// 预计算动画帧和碰撞
var config = new CollisionDetectionConfig
{
FrameRate = item.FrameRate,
DurationSeconds = item.DurationSeconds,
DetectionToleranceMeters = item.DetectionToleranceMeters,
VirtualVehicleLength = item.VirtualVehicleLength,
VirtualVehicleWidth = item.VirtualVehicleWidth,
VirtualVehicleHeight = item.VirtualVehicleHeight,
CollisionDetectionEnabled = true,
ReportGenerationEnabled = true
};
return _executor._processor.PrecomputeFrames(
pathRoute,
animatedObject,
isVirtualVehicle,
config.VirtualVehicleLength,
config.VirtualVehicleWidth,
config.VirtualVehicleHeight,
config.FrameRate,
config.DurationSeconds,
config.DetectionToleranceMeters,
null
);
});
// 检查预计算是否成功
if (frames == null || frames.Count == 0)
{
throw new InvalidOperationException("预计算动画帧失败:未生成任何帧");
}
// 提取碰撞结果
var collisions = frames.SelectMany(f => f.Collisions).ToList();
// 创建并运行ClashDetective测试直接在主线程执行避免异步切换导致的Navisworks Native对象生命周期问题
// 传入 pathPoints 参数,让 ClashDetective 测试完成后自动恢复到路径起点
var pathPoints = pathRoute.Points.Select(p => p.Position).ToList();
var testName = _executor._processor.CreateAndRunClashDetectiveTest(
collisions,
item.DetectionToleranceMeters,
pathRoute.Name,
pathRoute.Id,
animatedObject,
isVirtualVehicle,
item.FrameRate,
item.DurationSeconds,
item.VirtualVehicleLength,
item.VirtualVehicleWidth,
item.VirtualVehicleHeight,
pathPoints
);
// 🔥 使用 ClashDetective 确认的碰撞数,而不是预计算的碰撞数
item.CollisionCount = ClashDetectiveIntegration.Instance.ClashDetectiveCollisionCount;
// 保存测试名称
item.ClashDetectiveTestName = testName;
// 标记为完成
item.Status = BatchQueueStatus.Completed;
item.EndTime = DateTime.Now;
await _database.UpdateBatchQueueItemAsync(item);
LogManager.Info($"[批处理队列] 队列项执行完成: {item.PathRouteName}, 碰撞数: {item.CollisionCount}");
}
catch (Exception ex)
{
// 标记为失败
item.Status = BatchQueueStatus.Failed;
item.ErrorMessage = ex.Message;
item.EndTime = DateTime.Now;
await _database.UpdateBatchQueueItemAsync(item);
LogManager.Error($"[批处理队列] 队列项执行失败: {item.PathRouteName}, 错误: {ex.Message}");
throw; // 重新抛出异常,让上层处理
}
}
/// <summary>
/// 取消当前任务
/// </summary>
public void CancelCurrentItem()
{
EnsureExecutorInitialized();
_executor.Cancel();
}
/// <summary>
/// 获取队列列表
/// </summary>
public async Task<List<BatchQueueItem>> GetQueueItemsAsync(
BatchQueueStatus? statusFilter = null,
int limit = 50,
int offset = 0)
{
return await _database.GetBatchQueueItemsAsync(statusFilter, limit, offset);
}
/// <summary>
/// 获取单个队列项
/// </summary>
public async Task<BatchQueueItem> GetQueueItemAsync(int itemId)
{
return await _database.GetBatchQueueItemAsync(itemId);
}
/// <summary>
/// 删除队列项
/// </summary>
public async Task DeleteQueueItemAsync(int itemId)
{
await _database.DeleteBatchQueueItemAsync(itemId);
}
/// <summary>
/// 查找模型项
/// </summary>
private ModelItem FindModelItemById(string id)
{
try
{
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
if (doc == null) return null;
var model = doc.Models.RootItemDescendants.FirstOrDefault(
item => item.ToString() == id
);
return model;
}
catch (Exception ex)
{
LogManager.Error($"查找模型项失败 (Id: {id}): {ex.Message}");
return null;
}
}
}
/// <summary>
/// 批处理队列项事件参数
/// </summary>
public class BatchQueueItemEventArgs : EventArgs
{
public BatchQueueItem Item { get; set; }
}
/// <summary>
/// 批处理队列进度事件参数
/// </summary>
public class BatchQueueProgressEventArgs : EventArgs
{
public int ItemId { get; set; }
public int CompletedItems { get; set; }
public int TotalItems { get; set; }
public double Percentage { get; set; }
}
}