Files
ParkingRobot/Simulation/Commands/SimulationCommandDispatcher.cs
T

151 lines
4.5 KiB
C#
Raw Normal View History

using System.Reflection;
using MyParking.Simulation.Core;
namespace MyParking.Simulation.Commands;
/// <summary>
/// 自动发现带SimulationAction特性的测试方法并分发网页命令。
/// </summary>
public sealed class SimulationCommandDispatcher
{
private readonly SimulationWorld _world;
private readonly IReadOnlyDictionary<string, RegisteredAction> _actions;
public SimulationCommandDispatcher(
SimulationWorld world)
{
_world = world;
_actions = DiscoverActions();
}
/// <summary>
/// 返回网页动态生成按钮所需的全部命令。
/// </summary>
public IReadOnlyList<SimulationActionDescriptor> GetActions()
{
return _actions.Values
.Select(action => action.Descriptor)
.OrderBy(action => action.Group)
.ThenBy(action => action.Order)
.ToArray();
}
/// <summary>
/// 对指定车辆执行一个已注册的测试方法。
/// </summary>
public CommandResult Execute(
int vehicleId,
string command)
{
if (!_actions.TryGetValue(
command,
out var registeredAction))
{
return new CommandResult(
false,
$"未注册仿真命令:{command}。");
}
try
{
return _world.WithVehicle(vehicleId, vehicle =>
{
var success = registeredAction.Handler(vehicle);
var message = success
? $"车辆{vehicleId}已执行:{registeredAction.Descriptor.DisplayName}。"
: $"车辆{vehicleId}暂时无法执行:{registeredAction.Descriptor.DisplayName}。";
return new CommandResult(success, message);
});
}
catch (KeyNotFoundException exception)
{
return new CommandResult(
false,
exception.Message);
}
}
private static IReadOnlyDictionary<string, RegisteredAction>
DiscoverActions()
{
var actions = new Dictionary<string, RegisteredAction>(
StringComparer.OrdinalIgnoreCase);
var methods = Assembly.GetExecutingAssembly()
.GetTypes()
.SelectMany(type => type.GetMethods(
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Static));
foreach (var method in methods)
{
var attribute =
method.GetCustomAttribute<SimulationActionAttribute>();
if (attribute == null)
continue;
ValidateMethod(method, attribute);
var handler =
(Func<SimulationVehicle, bool>)method.CreateDelegate(
typeof(Func<SimulationVehicle, bool>));
var descriptor = new SimulationActionDescriptor(
attribute.Key,
attribute.DisplayName,
attribute.Group,
attribute.Order);
if (!actions.TryAdd(
attribute.Key,
new RegisteredAction(descriptor, handler)))
{
throw new InvalidOperationException(
$"仿真命令Key重复:{attribute.Key}。");
}
}
return actions;
}
private static void ValidateMethod(
MethodInfo method,
SimulationActionAttribute attribute)
{
var parameters = method.GetParameters();
if (method.ReturnType != typeof(bool) ||
parameters.Length != 1 ||
parameters[0].ParameterType !=
typeof(SimulationVehicle))
{
throw new InvalidOperationException(
$"[{nameof(SimulationActionAttribute)}]方法" +
$"{method.DeclaringType?.FullName}.{method.Name}" +
"必须是static bool Xxx(SimulationVehicle vehicle)。");
}
if (string.IsNullOrWhiteSpace(attribute.Key) ||
string.IsNullOrWhiteSpace(attribute.DisplayName) ||
string.IsNullOrWhiteSpace(attribute.Group))
{
throw new InvalidOperationException(
$"仿真命令{method.Name}的特性参数不能为空。");
}
}
private sealed record RegisteredAction(
SimulationActionDescriptor Descriptor,
Func<SimulationVehicle, bool> Handler);
}
/// <summary>
/// 网页测试命令的执行结果。
/// </summary>
public sealed record CommandResult(
bool Success,
string Message);