Files
ParkingRobot/MultiWheelC.Tests/FleetMemberAgentTests.cs
T

241 lines
7.6 KiB
C#
Raw Normal View History

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