Files
ParkingRobot/MultiWheelC.Tests/FleetSafetySupervisorTests.cs

298 lines
8.8 KiB
C#

using System;
using System.Collections.Generic;
using MultiWheelC.Fleet;
using MyParking.Shared;
namespace MultiWheelC.Tests
{
internal static class FleetSafetySupervisorTests
{
private const long PlanId = 7;
private const double CurrentTimeSeconds = 10.0;
private const double CommunicationTimeoutSeconds = 0.5;
public static void Run()
{
VerifyHealthyFleetContinues();
VerifyMissingMemberStopsFleet();
VerifyCommunicationTimeoutStopsFleet();
VerifyUnavailableStateStopsFleet();
VerifyMemberFaultStopsFleet();
VerifyFailureCodeStopsFleet();
VerifyPlanMismatchStopsFleet();
VerifyStopIsLatchedUntilNewPlanStarts();
Console.WriteLine(
"FleetSafetySupervisor测试通过,共8个场景。");
}
private static void VerifyHealthyFleetContinues()
{
var supervisor = CreateStartedSupervisor();
var decision = supervisor.Evaluate(
CreateLayout(),
CreateHealthyStatuses(),
CurrentTimeSeconds);
AssertFalse(
decision.ShouldStop,
"成员状态健康时不应停车");
}
private static void VerifyMissingMemberStopsFleet()
{
var supervisor = CreateStartedSupervisor();
var decision = supervisor.Evaluate(
CreateLayout(),
new[] { CreateHealthyStatus(1) },
CurrentTimeSeconds);
AssertStopFromVehicle(
decision,
2,
"缺少成员报告");
}
private static void VerifyCommunicationTimeoutStopsFleet()
{
var supervisor = CreateStartedSupervisor();
var statuses = new[]
{
CreateHealthyStatus(1),
new FleetMemberSafetyStatus(
vehicleId: 2,
planId: PlanId,
isStateAvailable: true,
isFaulted: false,
failureCode: 0,
lastAcceptedReportTimeSeconds: 9.4)
};
var decision = supervisor.Evaluate(
CreateLayout(),
statuses,
CurrentTimeSeconds);
AssertStopFromVehicle(
decision,
2,
"通信超时");
}
private static void VerifyUnavailableStateStopsFleet()
{
var supervisor = CreateStartedSupervisor();
var statuses = new[]
{
CreateHealthyStatus(1),
new FleetMemberSafetyStatus(
vehicleId: 2,
planId: PlanId,
isStateAvailable: false,
isFaulted: false,
failureCode: 0,
lastAcceptedReportTimeSeconds: 9.9)
};
var decision = supervisor.Evaluate(
CreateLayout(),
statuses,
CurrentTimeSeconds);
AssertStopFromVehicle(
decision,
2,
"状态不可用");
}
private static void VerifyMemberFaultStopsFleet()
{
var supervisor = CreateStartedSupervisor();
var statuses = new[]
{
CreateHealthyStatus(1),
new FleetMemberSafetyStatus(
vehicleId: 2,
planId: PlanId,
isStateAvailable: true,
isFaulted: true,
failureCode: 0,
lastAcceptedReportTimeSeconds: 9.9)
};
var decision = supervisor.Evaluate(
CreateLayout(),
statuses,
CurrentTimeSeconds);
AssertStopFromVehicle(
decision,
2,
"成员故障状态");
}
private static void VerifyFailureCodeStopsFleet()
{
var supervisor = CreateStartedSupervisor();
var statuses = new[]
{
CreateHealthyStatus(1),
new FleetMemberSafetyStatus(
vehicleId: 2,
planId: PlanId,
isStateAvailable: true,
isFaulted: false,
failureCode: 42,
lastAcceptedReportTimeSeconds: 9.9)
};
var decision = supervisor.Evaluate(
CreateLayout(),
statuses,
CurrentTimeSeconds);
AssertStopFromVehicle(
decision,
2,
"成员故障码");
}
private static void VerifyPlanMismatchStopsFleet()
{
var supervisor = CreateStartedSupervisor();
var statuses = new[]
{
CreateHealthyStatus(1),
new FleetMemberSafetyStatus(
vehicleId: 2,
planId: PlanId - 1,
isStateAvailable: true,
isFaulted: false,
failureCode: 0,
lastAcceptedReportTimeSeconds: 9.9)
};
var decision = supervisor.Evaluate(
CreateLayout(),
statuses,
CurrentTimeSeconds);
AssertStopFromVehicle(
decision,
2,
"任务编号不一致");
}
private static void VerifyStopIsLatchedUntilNewPlanStarts()
{
var supervisor = CreateStartedSupervisor();
supervisor.Evaluate(
CreateLayout(),
new[] { CreateHealthyStatus(1) },
CurrentTimeSeconds);
var latchedDecision = supervisor.Evaluate(
CreateLayout(),
CreateHealthyStatuses(),
CurrentTimeSeconds);
AssertTrue(
latchedDecision.ShouldStop,
"故障恢复后停车决定仍应锁存");
supervisor.Start(PlanId + 1);
var recoveredDecision = supervisor.Evaluate(
CreateLayout(),
new[]
{
CreateHealthyStatus(1, PlanId + 1),
CreateHealthyStatus(2, PlanId + 1)
},
CurrentTimeSeconds);
AssertFalse(
recoveredDecision.ShouldStop,
"开始新任务后应清除旧任务停车锁存");
}
private static FleetSafetySupervisor
CreateStartedSupervisor()
{
var supervisor = new FleetSafetySupervisor(
CommunicationTimeoutSeconds);
supervisor.Start(PlanId);
return supervisor;
}
private static FleetLayout CreateLayout()
{
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 IReadOnlyList<FleetMemberSafetyStatus>
CreateHealthyStatuses()
{
return new[]
{
CreateHealthyStatus(1),
CreateHealthyStatus(2)
};
}
private static FleetMemberSafetyStatus CreateHealthyStatus(
int vehicleId,
long planId = PlanId)
{
return new FleetMemberSafetyStatus(
vehicleId,
planId,
isStateAvailable: true,
isFaulted: false,
failureCode: 0,
lastAcceptedReportTimeSeconds: 9.9);
}
private static void AssertStopFromVehicle(
FleetSafetyDecision decision,
int expectedVehicleId,
string scenario)
{
AssertTrue(
decision.ShouldStop,
$"{scenario}时应停车");
AssertTrue(
decision.SourceVehicleId == expectedVehicleId,
$"{scenario}的来源车辆不正确");
AssertTrue(
!string.IsNullOrWhiteSpace(decision.Reason),
$"{scenario}应提供停车原因");
}
private static void AssertTrue(
bool condition,
string message)
{
if (!condition)
{
throw new InvalidOperationException(message);
}
}
private static void AssertFalse(
bool condition,
string message)
{
AssertTrue(!condition, message);
}
}
}