NavisworksTransport/src/Commands/CommandManager.cs

581 lines
22 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.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 CommandManager
{
#region
private static CommandManager _instance;
private static readonly object _instanceLock = new object();
private readonly ConcurrentDictionary<string, Func<IPathPlanningCommand>> _commandFactories;
private readonly CommandExecutor _commandExecutor;
private readonly UIStateManager _uiStateManager;
private volatile bool _isDisposed = false;
/// <summary>
/// 获取CommandManager的单例实例
/// </summary>
public static CommandManager Instance
{
get
{
if (_instance == null)
{
lock (_instanceLock)
{
if (_instance == null)
{
_instance = new CommandManager();
}
}
}
return _instance;
}
}
/// <summary>
/// 命令执行器
/// </summary>
public CommandExecutor Executor => _commandExecutor;
#endregion
#region
/// <summary>
/// 命令注册事件
/// </summary>
public event EventHandler<CommandRegistrationEventArgs> CommandRegistered;
/// <summary>
/// 命令执行开始事件
/// </summary>
public event EventHandler<CommandExecutionEventArgs> CommandExecutionStarted;
/// <summary>
/// 命令执行完成事件
/// </summary>
public event EventHandler<CommandCompletedEventArgs> CommandExecutionCompleted;
#endregion
#region
private CommandManager()
{
_commandFactories = new ConcurrentDictionary<string, Func<IPathPlanningCommand>>();
_commandExecutor = CommandExecutor.Instance;
_uiStateManager = UIStateManager.Instance;
// 订阅CommandExecutor事件
_commandExecutor.CommandStarted += OnCommandStarted;
_commandExecutor.CommandCompleted += OnCommandCompleted;
// 注册默认命令
RegisterDefaultCommands();
LogManager.Info("CommandManager 初始化完成");
}
#endregion
#region
/// <summary>
/// 注册命令工厂
/// </summary>
public void RegisterCommand<T>(string commandKey, Func<T> factory) where T : IPathPlanningCommand
{
if (string.IsNullOrEmpty(commandKey))
throw new ArgumentException("命令键不能为空", nameof(commandKey));
if (factory == null)
throw new ArgumentNullException(nameof(factory));
_commandFactories.AddOrUpdate(commandKey, () => factory(), (key, oldFactory) => () => factory());
OnCommandRegistered(commandKey, typeof(T));
LogManager.Info($"命令已注册: {commandKey} -> {typeof(T).Name}");
}
/// <summary>
/// 注册命令工厂(带参数)
/// </summary>
public void RegisterCommand<T>(string commandKey, Func<object[], T> factory) where T : IPathPlanningCommand
{
if (string.IsNullOrEmpty(commandKey))
throw new ArgumentException("命令键不能为空", nameof(commandKey));
if (factory == null)
throw new ArgumentNullException(nameof(factory));
// 存储一个包装函数,在创建时会需要参数
_commandFactories.AddOrUpdate(commandKey,
() => throw new InvalidOperationException($"命令 {commandKey} 需要参数,请使用 CreateCommand(string, params object[]) 方法"),
(key, oldFactory) => () => throw new InvalidOperationException($"命令 {commandKey} 需要参数,请使用 CreateCommand(string, params object[]) 方法"));
// 单独存储参数化工厂
_parameterizedFactories.AddOrUpdate(commandKey, factory, (key, oldFactory) => factory);
OnCommandRegistered(commandKey, typeof(T));
LogManager.Info($"参数化命令已注册: {commandKey} -> {typeof(T).Name}");
}
private readonly ConcurrentDictionary<string, Delegate> _parameterizedFactories = new ConcurrentDictionary<string, Delegate>();
/// <summary>
/// 注册默认命令
/// </summary>
private void RegisterDefaultCommands()
{
// 注册基本路径规划命令 - 暂时移除,避免构造函数参数问题
// RegisterCommand("SelectChannels", () => new SelectChannelsCommand());
// RegisterCommand("CreatePathRoute", () => new CreatePathRouteCommand());
// 注册自动路径规划命令
RegisterCommand("AutoPathPlanning", (object[] args) =>
{
if (args.Length < 2)
throw new ArgumentException("AutoPathPlanning 需要至少起点和终点参数");
var startPoint = args[0] as Autodesk.Navisworks.Api.Point3D;
var endPoint = args[1] as Autodesk.Navisworks.Api.Point3D;
if (startPoint == null || endPoint == null)
throw new ArgumentException("起点和终点必须是 Point3D 类型");
var parameters = new AutoPathPlanningParameters
{
StartPoint = startPoint,
EndPoint = endPoint,
ObjectWidthMeters = args.Length > 2 && args[2] is double v ? v : 1.0,
SafetyMarginMeters = args.Length > 3 && args[3] is double v1 ? v1 : 0.5,
GridSizeMeters = args.Length > 4 && args[4] is double v2 ? v2 : -1,
PathName = args.Length > 5 ? args[5]?.ToString() : null,
AutoDrawVisualization = args.Length <= 6 || !(args[6] is bool v3) || v3
};
return new AutoPathPlanningCommand(parameters);
});
// 注册快捷自动路径规划命令
RegisterCommand("AutoPathPlanningQuick", (object[] args) =>
{
if (args.Length < 2)
throw new ArgumentException("AutoPathPlanningQuick 需要至少起点和终点参数");
var startPoint = args[0] as Autodesk.Navisworks.Api.Point3D;
var endPoint = args[1] as Autodesk.Navisworks.Api.Point3D;
if (startPoint == null || endPoint == null)
throw new ArgumentException("起点和终点必须是 Point3D 类型");
var objectSize = args.Length > 2 && args[2] is double v ? v : 1.0;
var pathName = args.Length > 3 ? args[3]?.ToString() : null;
return AutoPathPlanningCommand.CreateQuick(startPoint, endPoint, objectSize, pathName);
});
// 注册模拟路径规划命令(保留用于测试)- 暂时注释掉避免类型推断问题
// RegisterCommand("MockAutoPathPlanning", (object[] args) => { return null; });
// 注册新的业务Commands
RegisterNewBusinessCommands();
}
/// <summary>
/// 注册新的业务Commands
/// </summary>
private void RegisterNewBusinessCommands()
{
LogManager.Info("开始注册新的业务Commands");
// 1. 注册设置物流属性命令
RegisterCommand("SetLogisticsAttribute", (object[] args) =>
{
if (args.Length < 2)
throw new ArgumentException("SetLogisticsAttribute 需要目标模型项和元素类型参数");
var targetItems = args[0] as Autodesk.Navisworks.Api.ModelItemCollection;
var elementType = args[1];
if (targetItems == null)
throw new ArgumentException("第一个参数必须是 ModelItemCollection 类型");
if (!Enum.TryParse<CategoryAttributeManager.LogisticsElementType>(elementType.ToString(), out var logisticsType))
throw new ArgumentException("第二个参数必须是有效的 LogisticsElementType");
var parameters = new SetLogisticsAttributeParameters
{
TargetItems = targetItems,
ElementType = logisticsType,
IsTraversable = args.Length > 2 && args[2] is bool ? (bool?)args[2] : null,
OverwriteExisting = args.Length <= 3 || !(args[3] is bool v) || v
};
return new SetLogisticsAttributeCommand(parameters);
});
// 2. 注册删除路径命令
RegisterCommand("DeletePath", (object[] args) =>
{
var parameters = new DeletePathParameters();
if (args.Length > 0)
{
if (args[0] is PathRoute route)
{
parameters.PathsToDelete.Add(route);
}
else if (args[0] is string pathId)
{
parameters.PathIds.Add(pathId);
}
else if (args[0] is bool deleteAll && deleteAll)
{
parameters.DeleteAll = true;
}
}
parameters.CreateBackup = args.Length <= 1 || !(args[1] is bool v) || v;
parameters.ForceDelete = args.Length > 2 && args[2] is bool v1 && v1;
return new DeletePathCommand(parameters);
});
// 3. 注册导入路径命令
RegisterCommand("ImportPath", (object[] args) =>
{
if (args.Length < 1)
throw new ArgumentException("ImportPath 需要文件路径参数");
var filePath = args[0]?.ToString();
if (string.IsNullOrEmpty(filePath))
throw new ArgumentException("文件路径不能为空");
var parameters = new ImportPathParameters
{
FilePath = filePath,
ImportFormat = args.Length > 1 && args[1] is ExportFormat format ? format : ExportFormat.Xml,
MergeWithExisting = args.Length <= 2 || !(args[2] is bool v) || v,
CreateBackup = args.Length <= 3 || !(args[3] is bool v1) || v1
};
return new ImportPathCommand(parameters);
});
// 4. 注册导出路径命令
RegisterCommand("ExportPath", (object[] args) =>
{
if (args.Length < 1)
throw new ArgumentException("ExportPath 需要输出文件路径参数");
var outputPath = args[0]?.ToString();
if (string.IsNullOrEmpty(outputPath))
throw new ArgumentException("输出文件路径不能为空");
var parameters = new ExportPathParameters
{
OutputFilePath = outputPath,
ExportAll = args.Length <= 1 || !(args[1] is bool v) || v,
ExportFormat = args.Length > 2 && args[2] is ExportFormat format ? format : ExportFormat.Xml,
OverwriteExisting = args.Length > 3 && args[3] is bool v1 && v1
};
return new ExportPathCommand(parameters);
});
// 5. 注册启动动画命令原第6项
RegisterCommand("StartAnimation", (object[] args) =>
{
if (args.Length < 1 || !(args[0] is PathRoute route))
throw new ArgumentException("StartAnimation 需要 PathRoute 参数");
var parameters = new StartAnimationParameters
{
TargetRoute = route,
AnimationSpeed = args.Length > 1 && args[1] is double v ? v : 1.0,
Loop = args.Length > 2 && args[2] is bool v1 && v1,
AutoStart = args.Length <= 3 || !(args[3] is bool v2) || v2
};
return new StartAnimationCommand(parameters);
});
LogManager.Info("新的业务Commands注册完成");
}
#endregion
#region
/// <summary>
/// 创建命令实例
/// </summary>
public IPathPlanningCommand CreateCommand(string commandKey)
{
if (!_commandFactories.TryGetValue(commandKey, out var factory))
{
throw new ArgumentException($"未注册的命令: {commandKey}");
}
try
{
return factory();
}
catch (Exception ex)
{
LogManager.Error($"创建命令失败: {commandKey}", ex);
throw new InvalidOperationException($"创建命令失败: {commandKey}", ex);
}
}
/// <summary>
/// 创建命令实例(带参数)
/// </summary>
public IPathPlanningCommand CreateCommand(string commandKey, params object[] parameters)
{
if (!_parameterizedFactories.TryGetValue(commandKey, out var factory))
{
// 尝试无参数创建
return CreateCommand(commandKey);
}
try
{
return (IPathPlanningCommand)factory.DynamicInvoke(new object[] { parameters });
}
catch (Exception ex)
{
LogManager.Error($"创建参数化命令失败: {commandKey}", ex);
throw new InvalidOperationException($"创建参数化命令失败: {commandKey}", ex);
}
}
/// <summary>
/// 执行命令(异步)
/// </summary>
public async Task<PathPlanningResult> ExecuteCommandAsync(string commandKey, CancellationToken cancellationToken = default)
{
try
{
var command = CreateCommand(commandKey);
return await _commandExecutor.ExecuteAsync(command, cancellationToken);
}
catch (Exception ex)
{
LogManager.Error($"执行命令失败: {commandKey}", ex);
return PathPlanningResult.Failure($"执行命令失败: {ex.Message}", ex);
}
}
/// <summary>
/// 执行命令(带参数,异步)
/// </summary>
public async Task<PathPlanningResult> ExecuteCommandAsync(string commandKey, object[] parameters, CancellationToken cancellationToken = default)
{
try
{
var command = CreateCommand(commandKey, parameters);
return await _commandExecutor.ExecuteAsync(command, cancellationToken);
}
catch (Exception ex)
{
LogManager.Error($"执行参数化命令失败: {commandKey}", ex);
return PathPlanningResult.Failure($"执行参数化命令失败: {ex.Message}", ex);
}
}
/// <summary>
/// 将命令加入队列执行
/// </summary>
public Task<PathPlanningResult> EnqueueCommandAsync(string commandKey,
CommandPriority priority = CommandPriority.Normal,
CancellationToken cancellationToken = default)
{
try
{
var command = CreateCommand(commandKey);
return _commandExecutor.EnqueueAsync(command, priority, cancellationToken);
}
catch (Exception ex)
{
LogManager.Error($"将命令加入队列失败: {commandKey}", ex);
return Task.FromResult(PathPlanningResult.Failure($"将命令加入队列失败: {ex.Message}", ex));
}
}
/// <summary>
/// 将命令加入队列执行(带参数)
/// </summary>
public Task<PathPlanningResult> EnqueueCommandAsync(string commandKey,
object[] parameters,
CommandPriority priority = CommandPriority.Normal,
CancellationToken cancellationToken = default)
{
try
{
var command = CreateCommand(commandKey, parameters);
return _commandExecutor.EnqueueAsync(command, priority, cancellationToken);
}
catch (Exception ex)
{
LogManager.Error($"将参数化命令加入队列失败: {commandKey}", ex);
return Task.FromResult(PathPlanningResult.Failure($"将参数化命令加入队列失败: {ex.Message}", ex));
}
}
#endregion
#region
/// <summary>
/// 获取已注册的命令列表
/// </summary>
public IEnumerable<string> GetRegisteredCommands()
{
return _commandFactories.Keys.ToArray();
}
/// <summary>
/// 检查命令是否已注册
/// </summary>
public bool IsCommandRegistered(string commandKey)
{
return _commandFactories.ContainsKey(commandKey);
}
/// <summary>
/// 取消注册命令
/// </summary>
public bool UnregisterCommand(string commandKey)
{
var removed = _commandFactories.TryRemove(commandKey, out _);
_parameterizedFactories.TryRemove(commandKey, out _);
if (removed)
{
LogManager.Info($"命令已取消注册: {commandKey}");
}
return removed;
}
/// <summary>
/// 获取运行状态统计
/// </summary>
public CommandManagerStatus GetStatus()
{
return new CommandManagerStatus
{
RegisteredCommandCount = _commandFactories.Count,
RunningCommandCount = _commandExecutor.RunningCommandCount,
QueuedCommandCount = _commandExecutor.QueuedCommandCount,
RegisteredCommands = GetRegisteredCommands().ToArray(),
RunningCommands = _commandExecutor.GetRunningCommands().ToArray()
};
}
#endregion
#region
private void OnCommandStarted(object sender, CommandExecutionEventArgs e)
{
// 使用UIStateManager安全地更新UI
_uiStateManager.QueueUIUpdate(() =>
{
CommandExecutionStarted?.Invoke(this, e);
}, UIUpdatePriority.Normal);
}
private void OnCommandCompleted(object sender, CommandCompletedEventArgs e)
{
// 使用UIStateManager安全地更新UI
_uiStateManager.QueueUIUpdate(() =>
{
CommandExecutionCompleted?.Invoke(this, e);
}, UIUpdatePriority.Normal);
}
private void OnCommandRegistered(string commandKey, Type commandType)
{
try
{
CommandRegistered?.Invoke(this, new CommandRegistrationEventArgs(commandKey, commandType));
}
catch (Exception ex)
{
LogManager.Error("触发命令注册事件时出现异常", ex);
}
}
#endregion
#region
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
if (_isDisposed) return;
_isDisposed = true;
// 取消订阅事件
_commandExecutor.CommandStarted -= OnCommandStarted;
_commandExecutor.CommandCompleted -= OnCommandCompleted;
// 释放CommandExecutor
_commandExecutor?.Dispose();
LogManager.Info("CommandManager 已释放");
}
#endregion
}
#region
/// <summary>
/// 命令管理器状态
/// </summary>
public class CommandManagerStatus
{
public int RegisteredCommandCount { get; set; }
public int RunningCommandCount { get; set; }
public int QueuedCommandCount { get; set; }
public string[] RegisteredCommands { get; set; }
public CommandInfo[] RunningCommands { get; set; }
}
/// <summary>
/// 命令注册事件参数
/// </summary>
public class CommandRegistrationEventArgs : EventArgs
{
public string CommandKey { get; }
public Type CommandType { get; }
public CommandRegistrationEventArgs(string commandKey, Type commandType)
{
CommandKey = commandKey;
CommandType = commandType;
}
}
#endregion
}