483 lines
15 KiB
C#
483 lines
15 KiB
C#
using System;
|
||
using System.Collections.Concurrent;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using NavisworksTransport.Core;
|
||
|
||
namespace NavisworksTransport.Commands
|
||
{
|
||
/// <summary>
|
||
/// 命令执行器
|
||
/// 提供异步命令执行框架,与UIStateManager集成,支持命令队列和状态跟踪
|
||
/// </summary>
|
||
public class CommandExecutor
|
||
{
|
||
#region 字段和属性
|
||
|
||
private static CommandExecutor _instance;
|
||
private static readonly object _instanceLock = new object();
|
||
|
||
private readonly ConcurrentDictionary<string, IPathPlanningCommand> _runningCommands;
|
||
private readonly ConcurrentQueue<CommandExecutionRequest> _commandQueue;
|
||
private readonly SemaphoreSlim _executionSemaphore;
|
||
private readonly UIStateManager _uiStateManager;
|
||
private volatile bool _isProcessing = false;
|
||
private volatile bool _isDisposed = false;
|
||
|
||
/// <summary>
|
||
/// 获取CommandExecutor的单例实例
|
||
/// </summary>
|
||
public static CommandExecutor Instance
|
||
{
|
||
get
|
||
{
|
||
if (_instance == null)
|
||
{
|
||
lock (_instanceLock)
|
||
{
|
||
if (_instance == null)
|
||
{
|
||
_instance = new CommandExecutor();
|
||
}
|
||
}
|
||
}
|
||
return _instance;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当前正在运行的命令数量
|
||
/// </summary>
|
||
public int RunningCommandCount => _runningCommands.Count;
|
||
|
||
/// <summary>
|
||
/// 队列中等待执行的命令数量
|
||
/// </summary>
|
||
public int QueuedCommandCount => _commandQueue.Count;
|
||
|
||
/// <summary>
|
||
/// 最大并发执行命令数
|
||
/// </summary>
|
||
public int MaxConcurrentCommands { get; set; } = 3;
|
||
|
||
#endregion
|
||
|
||
#region 事件
|
||
|
||
/// <summary>
|
||
/// 命令开始执行事件
|
||
/// </summary>
|
||
public event EventHandler<CommandExecutionEventArgs> CommandStarted;
|
||
|
||
/// <summary>
|
||
/// 命令执行完成事件
|
||
/// </summary>
|
||
public event EventHandler<CommandCompletedEventArgs> CommandCompleted;
|
||
|
||
/// <summary>
|
||
/// 队列状态改变事件
|
||
/// </summary>
|
||
public event EventHandler<QueueStatusEventArgs> QueueStatusChanged;
|
||
|
||
#endregion
|
||
|
||
#region 构造函数
|
||
|
||
private CommandExecutor()
|
||
{
|
||
_runningCommands = new ConcurrentDictionary<string, IPathPlanningCommand>();
|
||
_commandQueue = new ConcurrentQueue<CommandExecutionRequest>();
|
||
_executionSemaphore = new SemaphoreSlim(MaxConcurrentCommands, MaxConcurrentCommands);
|
||
_uiStateManager = UIStateManager.Instance;
|
||
|
||
LogManager.Info("CommandExecutor 初始化完成");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 公共方法
|
||
|
||
/// <summary>
|
||
/// 执行单个命令(异步)
|
||
/// </summary>
|
||
public async Task<PathPlanningResult> ExecuteAsync(IPathPlanningCommand command, CancellationToken cancellationToken = default)
|
||
{
|
||
if (command == null)
|
||
{
|
||
return PathPlanningResult.Failure("命令对象不能为空");
|
||
}
|
||
|
||
if (_isDisposed)
|
||
{
|
||
return PathPlanningResult.Failure("CommandExecutor已被释放");
|
||
}
|
||
|
||
try
|
||
{
|
||
LogManager.Info($"开始执行命令: {command.DisplayName} (ID: {command.CommandId})");
|
||
|
||
// 等待执行槽位
|
||
await _executionSemaphore.WaitAsync(cancellationToken);
|
||
|
||
try
|
||
{
|
||
// 添加到运行命令列表
|
||
_runningCommands.TryAdd(command.CommandId, command);
|
||
|
||
// 触发命令开始事件
|
||
OnCommandStarted(command);
|
||
|
||
// 订阅命令状态改变事件
|
||
command.StatusChanged += OnCommandStatusChanged;
|
||
command.ProgressChanged += OnCommandProgressChanged;
|
||
|
||
// 执行命令
|
||
var result = await command.ExecuteAsync(cancellationToken);
|
||
|
||
// 触发命令完成事件
|
||
OnCommandCompleted(command, result);
|
||
|
||
LogManager.Info($"命令执行完成: {command.DisplayName}, 结果: {(result.IsSuccess ? "成功" : "失败")}");
|
||
|
||
return result;
|
||
}
|
||
finally
|
||
{
|
||
// 从运行命令列表中移除
|
||
_runningCommands.TryRemove(command.CommandId, out _);
|
||
|
||
// 取消订阅事件
|
||
command.StatusChanged -= OnCommandStatusChanged;
|
||
command.ProgressChanged -= OnCommandProgressChanged;
|
||
|
||
// 释放执行槽位
|
||
_executionSemaphore.Release();
|
||
}
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
LogManager.Info($"命令执行被取消: {command.DisplayName}");
|
||
return PathPlanningResult.Failure("命令执行已取消");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"执行命令时出现异常: {command.DisplayName}", ex);
|
||
return PathPlanningResult.Failure($"命令执行异常: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将命令加入队列异步执行
|
||
/// </summary>
|
||
public Task<PathPlanningResult> EnqueueAsync(IPathPlanningCommand command,
|
||
CommandPriority priority = CommandPriority.Normal,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
if (command == null)
|
||
{
|
||
return Task.FromResult(PathPlanningResult.Failure("命令对象不能为空"));
|
||
}
|
||
|
||
if (_isDisposed)
|
||
{
|
||
return Task.FromResult(PathPlanningResult.Failure("CommandExecutor已被释放"));
|
||
}
|
||
|
||
var request = new CommandExecutionRequest
|
||
{
|
||
Command = command,
|
||
Priority = priority,
|
||
CancellationToken = cancellationToken,
|
||
TaskCompletionSource = new TaskCompletionSource<PathPlanningResult>()
|
||
};
|
||
|
||
_commandQueue.Enqueue(request);
|
||
OnQueueStatusChanged();
|
||
|
||
// 启动处理队列(直接在主线程执行,避免后台线程导致的Navisworks Native对象生命周期问题)
|
||
ProcessQueueAsync();
|
||
|
||
LogManager.Info($"命令已加入队列: {command.DisplayName} (优先级: {priority})");
|
||
|
||
return request.TaskCompletionSource.Task;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 取消指定命令
|
||
/// </summary>
|
||
public bool CancelCommand(string commandId)
|
||
{
|
||
if (_runningCommands.TryGetValue(commandId, out var command))
|
||
{
|
||
command.Cancel();
|
||
LogManager.Info($"已发送取消请求: {command.DisplayName}");
|
||
return true;
|
||
}
|
||
|
||
LogManager.Warning($"未找到要取消的命令: {commandId}");
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 取消所有正在运行的命令
|
||
/// </summary>
|
||
public void CancelAllCommands()
|
||
{
|
||
var commands = _runningCommands.Values.ToArray();
|
||
foreach (var command in commands)
|
||
{
|
||
command.Cancel();
|
||
}
|
||
|
||
LogManager.Info($"已发送取消请求给 {commands.Length} 个命令");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定命令的状态
|
||
/// </summary>
|
||
public CommandExecutionStatus? GetCommandStatus(string commandId)
|
||
{
|
||
return _runningCommands.TryGetValue(commandId, out var command) ? (CommandExecutionStatus?)command.Status : null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取所有正在运行的命令信息
|
||
/// </summary>
|
||
public IEnumerable<CommandInfo> GetRunningCommands()
|
||
{
|
||
return _runningCommands.Values.Select(cmd => new CommandInfo
|
||
{
|
||
CommandId = cmd.CommandId,
|
||
DisplayName = cmd.DisplayName,
|
||
Description = cmd.Description,
|
||
Status = cmd.Status,
|
||
Progress = cmd.Progress
|
||
}).ToArray();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 释放资源
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
if (_isDisposed) return;
|
||
|
||
_isDisposed = true;
|
||
|
||
// 取消所有命令
|
||
CancelAllCommands();
|
||
|
||
// 等待所有命令完成(最多等待5秒)
|
||
var timeout = TimeSpan.FromSeconds(5);
|
||
var startTime = DateTime.Now;
|
||
|
||
while (_runningCommands.Count > 0 && DateTime.Now - startTime < timeout)
|
||
{
|
||
Thread.Sleep(100);
|
||
}
|
||
|
||
_executionSemaphore?.Dispose();
|
||
|
||
LogManager.Info("CommandExecutor 已释放");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 私有方法
|
||
|
||
/// <summary>
|
||
/// 处理命令队列
|
||
/// </summary>
|
||
private async Task ProcessQueueAsync()
|
||
{
|
||
if (_isProcessing || _isDisposed) return;
|
||
|
||
_isProcessing = true;
|
||
|
||
try
|
||
{
|
||
while (_commandQueue.TryDequeue(out var request) && !_isDisposed)
|
||
{
|
||
try
|
||
{
|
||
var result = await ExecuteAsync(request.Command, request.CancellationToken);
|
||
request.TaskCompletionSource.SetResult(result);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
var errorResult = PathPlanningResult.Failure($"队列执行命令失败: {ex.Message}", ex);
|
||
request.TaskCompletionSource.SetResult(errorResult);
|
||
}
|
||
|
||
OnQueueStatusChanged();
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
_isProcessing = false;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 事件处理
|
||
|
||
/// <summary>
|
||
/// 处理命令状态改变事件,更新UI
|
||
/// </summary>
|
||
private void OnCommandStatusChanged(object sender, CommandStatusChangedEventArgs e)
|
||
{
|
||
if (!(sender is IPathPlanningCommand command)) return;
|
||
|
||
// 使用UIStateManager安全更新UI
|
||
_uiStateManager.QueueUIUpdate(() =>
|
||
{
|
||
LogManager.Info($"命令状态改变: {command.DisplayName} - {e.PreviousStatus} → {e.CurrentStatus}");
|
||
}, UIUpdatePriority.Normal);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理命令进度改变事件,更新UI
|
||
/// </summary>
|
||
private void OnCommandProgressChanged(object sender, CommandProgressChangedEventArgs e)
|
||
{
|
||
if (!(sender is IPathPlanningCommand command)) return;
|
||
|
||
// 使用UIStateManager安全更新UI
|
||
_uiStateManager.QueueUIUpdate(() =>
|
||
{
|
||
// UI可以通过订阅CommandExecutor的事件来更新进度条
|
||
}, UIUpdatePriority.Normal);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 触发命令开始事件
|
||
/// </summary>
|
||
private void OnCommandStarted(IPathPlanningCommand command)
|
||
{
|
||
try
|
||
{
|
||
CommandStarted?.Invoke(this, new CommandExecutionEventArgs(command));
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error("触发命令开始事件时出现异常", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 触发命令完成事件
|
||
/// </summary>
|
||
private void OnCommandCompleted(IPathPlanningCommand command, PathPlanningResult result)
|
||
{
|
||
try
|
||
{
|
||
CommandCompleted?.Invoke(this, new CommandCompletedEventArgs(command, result));
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error("触发命令完成事件时出现异常", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 触发队列状态改变事件
|
||
/// </summary>
|
||
private void OnQueueStatusChanged()
|
||
{
|
||
try
|
||
{
|
||
QueueStatusChanged?.Invoke(this, new QueueStatusEventArgs(QueuedCommandCount, RunningCommandCount));
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error("触发队列状态改变事件时出现异常", ex);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
|
||
#region 辅助类和枚举
|
||
|
||
/// <summary>
|
||
/// 命令优先级
|
||
/// </summary>
|
||
public enum CommandPriority
|
||
{
|
||
Low = 0,
|
||
Normal = 1,
|
||
High = 2,
|
||
Critical = 3
|
||
}
|
||
|
||
/// <summary>
|
||
/// 命令执行请求
|
||
/// </summary>
|
||
internal class CommandExecutionRequest
|
||
{
|
||
public IPathPlanningCommand Command { get; set; }
|
||
public CommandPriority Priority { get; set; }
|
||
public CancellationToken CancellationToken { get; set; }
|
||
public TaskCompletionSource<PathPlanningResult> TaskCompletionSource { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 命令信息
|
||
/// </summary>
|
||
public class CommandInfo
|
||
{
|
||
public string CommandId { get; set; }
|
||
public string DisplayName { get; set; }
|
||
public string Description { get; set; }
|
||
public CommandExecutionStatus Status { get; set; }
|
||
public int Progress { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 命令执行事件参数
|
||
/// </summary>
|
||
public class CommandExecutionEventArgs : EventArgs
|
||
{
|
||
public IPathPlanningCommand Command { get; }
|
||
|
||
public CommandExecutionEventArgs(IPathPlanningCommand command)
|
||
{
|
||
Command = command;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 命令完成事件参数
|
||
/// </summary>
|
||
public class CommandCompletedEventArgs : EventArgs
|
||
{
|
||
public IPathPlanningCommand Command { get; }
|
||
public PathPlanningResult Result { get; }
|
||
|
||
public CommandCompletedEventArgs(IPathPlanningCommand command, PathPlanningResult result)
|
||
{
|
||
Command = command;
|
||
Result = result;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 队列状态事件参数
|
||
/// </summary>
|
||
public class QueueStatusEventArgs : EventArgs
|
||
{
|
||
public int QueuedCount { get; }
|
||
public int RunningCount { get; }
|
||
|
||
public QueueStatusEventArgs(int queuedCount, int runningCount)
|
||
{
|
||
QueuedCount = queuedCount;
|
||
RunningCount = runningCount;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
} |