298 lines
8.8 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|