423 lines
15 KiB
C#
423 lines
15 KiB
C#
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; }
|
||
}
|
||
} |