Files
ParkingRobot/MultiWheelC.Tests/FleetMemberAgentTests.cs

241 lines
7.6 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
}
}