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