using System; using System.Numerics; using CommonUsage.Chassis; using MultiWheelC.Fleet; using MyParking.Shared; namespace MultiWheelC.Tests { internal static class FleetMemberAgentTests { private const long PlanId = 11; private const int VehicleId = 2; public static void Run() { VerifyActiveWatchdogExpires(); VerifyValidMotionCommandRefreshesDeadline(); VerifyRepeatedActivationDoesNotRefreshDeadline(); VerifyLateActivationCannotResumeMotion(); VerifyStopClearsWatchdog(); Console.WriteLine( "FleetMemberAgent本地命令看门狗测试通过,共5个场景。"); } private static void VerifyActiveWatchdogExpires() { var agent = CreateReadyAgent(); AssertTrue( agent.Activate( PlanId, commandReceivedTimeSeconds: 10.0, validForSeconds: 0.5), "成员车应当成功激活"); AssertTrue( agent.UpdateCommandWatchdog(10.5), "截止时刻仍应视为有效"); AssertFalse( agent.UpdateCommandWatchdog(10.501), "超过命令有效期后应停车"); AssertState( agent, FleetMemberAgentState.Faulted, "命令超时"); AssertTrue( !string.IsNullOrWhiteSpace( agent.LastFailureReason), "命令超时应保留故障原因"); } private static void VerifyValidMotionCommandRefreshesDeadline() { var agent = CreateReadyAgent(); agent.Activate( PlanId, commandReceivedTimeSeconds: 20.0, validForSeconds: 0.5); var accepted = agent.Execute( PlanId, new FleetMemberCommand( VehicleId, Twist2D.Zero), commandReceivedTimeSeconds: 20.4, validForSeconds: 0.7); AssertTrue( accepted, "有效速度命令应被接受"); AssertNear( agent.LastAcceptedCommandTimeSeconds, 20.4, "最近命令接收时间"); AssertNear( agent.CommandDeadlineSeconds, 21.1, "速度命令刷新后的截止时间"); AssertTrue( agent.UpdateCommandWatchdog(20.8), "刷新截止时间后车辆应保持Active"); } private static void VerifyRepeatedActivationDoesNotRefreshDeadline() { var agent = CreateReadyAgent(); agent.Activate( PlanId, commandReceivedTimeSeconds: 30.0, validForSeconds: 0.5); AssertTrue( agent.Activate( PlanId, commandReceivedTimeSeconds: 30.4, validForSeconds: 0.5), "未超时的重复激活命令应允许幂等确认"); AssertNear( agent.CommandDeadlineSeconds, 30.5, "重复激活不能替代运动命令刷新截止时间"); AssertFalse( agent.UpdateCommandWatchdog(30.501), "没有收到运动命令时仍应按最初激活期限停车"); } private static void VerifyLateActivationCannotResumeMotion() { var agent = CreateReadyAgent(); agent.Activate( PlanId, commandReceivedTimeSeconds: 40.0, validForSeconds: 0.5); AssertFalse( agent.Activate( PlanId, commandReceivedTimeSeconds: 40.6, validForSeconds: 0.5), "迟到的激活命令不能恢复已经失联的车辆"); AssertState( agent, FleetMemberAgentState.Faulted, "迟到激活命令"); } private static void VerifyStopClearsWatchdog() { var agent = CreateReadyAgent(); agent.Activate( PlanId, commandReceivedTimeSeconds: 50.0, validForSeconds: 0.5); agent.Stop(); AssertState( agent, FleetMemberAgentState.Idle, "正常停止"); AssertTrue( !agent.LastAcceptedCommandTimeSeconds.HasValue && !agent.CommandDeadlineSeconds.HasValue, "正常停止后应清除命令看门狗"); } private static FleetMemberAgent CreateReadyAgent() { var chassis = new MultiWheelChassis(); chassis.AddWheel(CreateWheel(-500f, 300f)); chassis.AddWheel(CreateWheel(-500f, -300f)); chassis.AddWheel(CreateWheel(500f, 300f)); chassis.AddWheel(CreateWheel(500f, -300f)); chassis.Initialize(); var agent = new FleetMemberAgent( new MultiWheelChassisAdapter( chassis, VehicleId), alignmentToleranceRadians: AngleMath.DegreesToRadians(1.0), alignmentStableSeconds: 0.0); AssertTrue( agent.BeginRollingPreparation( PlanId, motionDirectionInBodyRadians: 0.0), "滚动运动系准备命令应被接受"); AssertState( agent, FleetMemberAgentState.Preparing, "开始准备"); AssertTrue( agent.UpdatePreparation(0.01) == FleetMemberAgentState.Ready, "内存底盘舵轮应立即准备完成"); return agent; } private static SteerWheel CreateWheel( float xMillimeters, float yMillimeters) { var speed = 0f; var angle = 0f; return new SteerWheel( new Vector2( xMillimeters, yMillimeters), angleLowerLimit: -120f, angleUpperLimit: 120f, speedWriter: value => speed = value, speedReader: () => speed, angleWriter: value => angle = value, angleReader: () => angle); } private static void AssertState( FleetMemberAgent agent, FleetMemberAgentState expected, string scenario) { AssertTrue( agent.State == expected, $"{scenario}后的状态应为{expected}," + $"实际为{agent.State}。"); } private static void AssertNear( double? actual, double expected, string name) { AssertTrue( actual.HasValue && Math.Abs(actual.Value - expected) <= 1e-9, $"{name}不正确,期望{expected:F6}," + $"实际{actual?.ToString("F6") ?? "null"}。"); } 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); } } }