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 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); } } }