Files
ParkingRobot/MultiWheelC.Tests/FleetPreparationCoordinatorTests.cs
T

311 lines
9.9 KiB
C#

using System;
using MultiWheelC.Fleet;
using MyParking.Shared;
namespace MultiWheelC.Tests
{
internal static class FleetPreparationCoordinatorTests
{
private const double Tolerance = 1e-9;
public static void Run()
{
VerifyLeaderYawExample();
VerifyTailToTailUsesEquivalentAxis();
VerifyAllMembersMustBeReady();
VerifyStaleStatusIsIgnored();
VerifyMemberFaultIsLatched();
VerifyFaultAfterAuthorizationIsLatched();
VerifyPrematureActiveIsRejected();
VerifyCancelClearsPlan();
Console.WriteLine(
"FleetPreparationCoordinator测试通过。共8个场景。");
}
private static void VerifyLeaderYawExample()
{
var capture = FleetLayoutCapture.Capture(
new[]
{
new FleetMemberPose(
1,
new Pose2D(
-1.0,
0.0,
AngleMath.DegreesToRadians(20.0))),
new FleetMemberPose(
2,
new Pose2D(1.0, 0.0, 0.0))
},
leaderVehicleId: 1);
var coordinator =
new FleetPreparationCoordinator();
coordinator.StartRollingPreparation(
planId: 1,
capture.Layout,
motionDirectionInFleetRadians: 0.0);
AssertTargetDegrees(coordinator, 1, 0.0);
AssertTargetDegrees(coordinator, 2, 20.0);
}
private static void VerifyTailToTailUsesEquivalentAxis()
{
var coordinator =
new FleetPreparationCoordinator();
coordinator.StartRollingPreparation(
planId: 2,
CreateTailToTailLayout(),
motionDirectionInFleetRadians: 0.0);
AssertTargetDegrees(coordinator, 1, 0.0);
AssertTargetDegrees(coordinator, 2, 0.0);
}
private static void VerifyAllMembersMustBeReady()
{
var coordinator = CreateStartedCoordinator(3);
AssertState(
coordinator.ReportMemberStatus(
CreateStatus(
3,
1,
FleetMemberAgentState.Ready)),
FleetPreparationCoordinatorState
.WaitingForMembers,
"仅一辆车Ready");
AssertState(
coordinator.ReportMemberStatus(
CreateStatus(
3,
2,
FleetMemberAgentState.Ready)),
FleetPreparationCoordinatorState
.ReadyToActivate,
"全部成员Ready");
AssertState(
coordinator.ReportMemberStatus(
CreateStatus(
3,
1,
FleetMemberAgentState.Preparing)),
FleetPreparationCoordinatorState
.WaitingForMembers,
"成员失去Ready");
AssertState(
coordinator.ReportMemberStatus(
CreateStatus(
3,
1,
FleetMemberAgentState.Ready)),
FleetPreparationCoordinatorState
.ReadyToActivate,
"成员重新Ready");
if (coordinator.TryAuthorizeActivation(4))
{
throw new InvalidOperationException(
"错误任务编号不应获得激活授权。");
}
if (!coordinator.TryAuthorizeActivation(3))
{
throw new InvalidOperationException(
"全部成员Ready后没有获得激活授权。");
}
AssertState(
coordinator.State,
FleetPreparationCoordinatorState
.ActivationAuthorized,
"统一激活授权");
}
private static void VerifyStaleStatusIsIgnored()
{
var coordinator = CreateStartedCoordinator(5);
var state = coordinator.ReportMemberStatus(
CreateStatus(
4,
1,
FleetMemberAgentState.Ready));
AssertState(
state,
FleetPreparationCoordinatorState
.WaitingForMembers,
"旧任务状态报告");
}
private static void VerifyMemberFaultIsLatched()
{
var coordinator = CreateStartedCoordinator(6);
var state = coordinator.ReportMemberStatus(
CreateStatus(
6,
2,
FleetMemberAgentState.Faulted,
"舵轮未到位"));
AssertState(
state,
FleetPreparationCoordinatorState.Faulted,
"成员准备故障");
if (string.IsNullOrWhiteSpace(
coordinator.LastFailureReason))
{
throw new InvalidOperationException(
"成员准备故障没有保存原因。");
}
}
private static void VerifyFaultAfterAuthorizationIsLatched()
{
var coordinator = CreateStartedCoordinator(9);
coordinator.ReportMemberStatus(
CreateStatus(
9,
1,
FleetMemberAgentState.Ready));
coordinator.ReportMemberStatus(
CreateStatus(
9,
2,
FleetMemberAgentState.Ready));
coordinator.TryAuthorizeActivation(9);
var state = coordinator.ReportMemberStatus(
CreateStatus(
9,
2,
FleetMemberAgentState.Faulted,
"激活失败"));
AssertState(
state,
FleetPreparationCoordinatorState.Faulted,
"授权后的成员故障");
}
private static void VerifyPrematureActiveIsRejected()
{
var coordinator = CreateStartedCoordinator(7);
var state = coordinator.ReportMemberStatus(
CreateStatus(
7,
1,
FleetMemberAgentState.Active));
AssertState(
state,
FleetPreparationCoordinatorState.Faulted,
"成员提前运动");
}
private static void VerifyCancelClearsPlan()
{
var coordinator = CreateStartedCoordinator(8);
coordinator.Cancel();
AssertState(
coordinator.State,
FleetPreparationCoordinatorState.Idle,
"取消准备任务");
if (coordinator.CurrentPlanId != 0 ||
coordinator.Targets.Count != 0)
{
throw new InvalidOperationException(
"取消后没有清除准备任务数据。");
}
}
private static FleetPreparationCoordinator
CreateStartedCoordinator(long planId)
{
var coordinator =
new FleetPreparationCoordinator();
coordinator.StartRollingPreparation(
planId,
CreateTailToTailLayout(),
motionDirectionInFleetRadians: 0.0);
return coordinator;
}
private static FleetLayout CreateTailToTailLayout()
{
return new FleetLayout(
new[]
{
new VehicleLayout(
1,
new Pose2D(-1.0, 0.0, 0.0)),
new VehicleLayout(
2,
new Pose2D(1.0, 0.0, Math.PI))
});
}
private static FleetMemberPreparationStatus CreateStatus(
long planId,
int vehicleId,
FleetMemberAgentState state,
string failureReason = "")
{
return new FleetMemberPreparationStatus(
planId,
vehicleId,
state,
failureReason);
}
private static void AssertTargetDegrees(
FleetPreparationCoordinator coordinator,
int vehicleId,
double expectedDegrees)
{
if (!coordinator.TryGetTarget(
vehicleId,
out var target))
{
throw new InvalidOperationException(
$"没有找到车辆{vehicleId}的准备目标。");
}
AssertNear(
AngleMath.RadiansToDegrees(
target.MotionDirectionInBodyRadians),
expectedDegrees,
$"车辆{vehicleId}本地β");
}
private static void AssertState(
FleetPreparationCoordinatorState actual,
FleetPreparationCoordinatorState expected,
string scenario)
{
if (actual != expected)
{
throw new InvalidOperationException(
$"{scenario}状态错误:" +
$"actual={actual}, expected={expected}。");
}
}
private static void AssertNear(
double actual,
double expected,
string valueName)
{
if (Math.Abs(actual - expected) > Tolerance)
{
throw new InvalidOperationException(
$"{valueName}错误:" +
$"actual={actual:F9}, expected={expected:F9}。");
}
}
}
}