贯通车队运行链并支持轨迹自动推导β
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MultiWheelC.Fleet;
|
||||
using MyParking.Shared;
|
||||
|
||||
namespace MultiWheelC.Tests
|
||||
{
|
||||
// 为同一测试进程中的各车辆端点提供共享FIFO消息队列。
|
||||
internal sealed class InMemoryFleetTransportNetwork
|
||||
{
|
||||
private readonly SharedState _sharedState;
|
||||
private readonly HashSet<int> _createdEndpointIds =
|
||||
new HashSet<int>();
|
||||
|
||||
public InMemoryFleetTransportNetwork(
|
||||
IReadOnlyList<int> vehicleIds,
|
||||
int leaderVehicleId)
|
||||
{
|
||||
_sharedState = new SharedState(
|
||||
vehicleIds,
|
||||
leaderVehicleId);
|
||||
}
|
||||
|
||||
public InMemoryFleetTransport CreateEndpoint(
|
||||
int vehicleId)
|
||||
{
|
||||
lock (_sharedState.SyncRoot)
|
||||
{
|
||||
if (!_sharedState.CommandQueues.ContainsKey(
|
||||
vehicleId))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(vehicleId),
|
||||
$"车辆{vehicleId}不属于当前内存车队网络。");
|
||||
}
|
||||
|
||||
if (!_createdEndpointIds.Add(vehicleId))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"车辆{vehicleId}的内存通信端点已经创建。");
|
||||
}
|
||||
}
|
||||
|
||||
return new InMemoryFleetTransport(
|
||||
_sharedState,
|
||||
vehicleId);
|
||||
}
|
||||
|
||||
internal sealed class SharedState
|
||||
{
|
||||
public SharedState(
|
||||
IReadOnlyList<int> vehicleIds,
|
||||
int leaderVehicleId)
|
||||
{
|
||||
if (vehicleIds == null)
|
||||
{
|
||||
throw new ArgumentNullException(
|
||||
nameof(vehicleIds));
|
||||
}
|
||||
|
||||
if (vehicleIds.Count == 0)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"内存车队网络至少需要一辆车。",
|
||||
nameof(vehicleIds));
|
||||
}
|
||||
|
||||
if (leaderVehicleId <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(leaderVehicleId),
|
||||
"主车编号必须大于零。");
|
||||
}
|
||||
|
||||
CommandQueues =
|
||||
new Dictionary<int, Queue<FleetCommand>>();
|
||||
|
||||
for (var index = 0;
|
||||
index < vehicleIds.Count;
|
||||
index++)
|
||||
{
|
||||
var vehicleId = vehicleIds[index];
|
||||
if (vehicleId <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(vehicleIds),
|
||||
$"第{index}辆车的编号必须大于零。");
|
||||
}
|
||||
|
||||
if (CommandQueues.ContainsKey(vehicleId))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"内存车队网络包含重复车号{vehicleId}。",
|
||||
nameof(vehicleIds));
|
||||
}
|
||||
|
||||
CommandQueues.Add(
|
||||
vehicleId,
|
||||
new Queue<FleetCommand>());
|
||||
}
|
||||
|
||||
if (!CommandQueues.ContainsKey(
|
||||
leaderVehicleId))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"主车{leaderVehicleId}不在车辆列表中。",
|
||||
nameof(leaderVehicleId));
|
||||
}
|
||||
|
||||
LeaderVehicleId = leaderVehicleId;
|
||||
}
|
||||
|
||||
public object SyncRoot { get; } = new object();
|
||||
|
||||
public int LeaderVehicleId { get; }
|
||||
|
||||
public Dictionary<int, Queue<FleetCommand>>
|
||||
CommandQueues { get; }
|
||||
|
||||
public Queue<FleetMemberReport> ReportQueue { get; } =
|
||||
new Queue<FleetMemberReport>();
|
||||
}
|
||||
}
|
||||
|
||||
// 单辆模拟车辆持有的通信端点;只负责消息路由,不解释控制语义。
|
||||
internal sealed class InMemoryFleetTransport : IFleetTransport
|
||||
{
|
||||
private readonly InMemoryFleetTransportNetwork.SharedState
|
||||
_sharedState;
|
||||
private readonly int _localVehicleId;
|
||||
|
||||
internal InMemoryFleetTransport(
|
||||
InMemoryFleetTransportNetwork.SharedState sharedState,
|
||||
int localVehicleId)
|
||||
{
|
||||
_sharedState = sharedState ??
|
||||
throw new ArgumentNullException(
|
||||
nameof(sharedState));
|
||||
_localVehicleId = localVehicleId;
|
||||
}
|
||||
|
||||
public void SendCommand(FleetCommand command)
|
||||
{
|
||||
if (_localVehicleId !=
|
||||
_sharedState.LeaderVehicleId)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"只有主车通信端点可以发送车队命令。");
|
||||
}
|
||||
|
||||
lock (_sharedState.SyncRoot)
|
||||
{
|
||||
if (command.TargetVehicleId ==
|
||||
FleetProtocol.BroadcastVehicleId)
|
||||
{
|
||||
foreach (var pair in
|
||||
_sharedState.CommandQueues)
|
||||
{
|
||||
// 主车本地命令由运行入口直接执行,不通过通信回环。
|
||||
if (pair.Key != _localVehicleId)
|
||||
{
|
||||
pair.Value.Enqueue(command);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_sharedState.CommandQueues.TryGetValue(
|
||||
command.TargetVehicleId,
|
||||
out var queue))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(command),
|
||||
$"目标车辆{command.TargetVehicleId}不存在。");
|
||||
}
|
||||
|
||||
queue.Enqueue(command);
|
||||
}
|
||||
}
|
||||
|
||||
public void SendReport(FleetMemberReport report)
|
||||
{
|
||||
if (report.VehicleId != _localVehicleId)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"车辆{_localVehicleId}不能发送属于车辆" +
|
||||
$"{report.VehicleId}的状态报告。",
|
||||
nameof(report));
|
||||
}
|
||||
|
||||
lock (_sharedState.SyncRoot)
|
||||
{
|
||||
_sharedState.ReportQueue.Enqueue(report);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryReceiveCommand(
|
||||
out FleetCommand command)
|
||||
{
|
||||
lock (_sharedState.SyncRoot)
|
||||
{
|
||||
var queue =
|
||||
_sharedState.CommandQueues[_localVehicleId];
|
||||
if (queue.Count == 0)
|
||||
{
|
||||
command = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
command = queue.Dequeue();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryReceiveReport(
|
||||
out FleetMemberReport report)
|
||||
{
|
||||
if (_localVehicleId !=
|
||||
_sharedState.LeaderVehicleId)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"只有主车通信端点可以接收成员状态报告。");
|
||||
}
|
||||
|
||||
lock (_sharedState.SyncRoot)
|
||||
{
|
||||
if (_sharedState.ReportQueue.Count == 0)
|
||||
{
|
||||
report = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
report =
|
||||
_sharedState.ReportQueue.Dequeue();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user