optimal
This commit is contained in:
@@ -1002,6 +1002,115 @@ TEST(ControlLoopAsyncPlanner, FailureToStartAPlanIsTreatedAsAFailedAttempt)
|
||||
<< join(fixture.states());
|
||||
}
|
||||
|
||||
// ================================================================================================
|
||||
// Preempt — goal mới thay goal cũ NGAY
|
||||
//
|
||||
// Bấm goal mới nghĩa là goal cũ không còn muốn nữa. Xếp hàng chờ robot đi hết chặng cũ là hành vi
|
||||
// không ai mong đợi, và mission layer cũng đã chốt "preempt ngay" (Q1, Phase 2).
|
||||
// ================================================================================================
|
||||
|
||||
TEST(ControlLoopPreempt, NewGoalWhileControllingReplansImmediately)
|
||||
{
|
||||
Fixture fixture;
|
||||
fixture.controller_.setScript({ControllerScript::kOk});
|
||||
|
||||
std::string reason;
|
||||
ASSERT_TRUE(fixture.loop_.submit(makeRequest(3.0), reason)) << reason;
|
||||
fixture.stepOnce(); // IDLE -> PLANNING
|
||||
fixture.stepOnce(); // -> CONTROLLING
|
||||
ASSERT_EQ(fixture.loop_.state(), NavigationState::kControlling);
|
||||
|
||||
const std::size_t plans_before = fixture.planner_.makePlanCount();
|
||||
|
||||
ASSERT_TRUE(fixture.loop_.submit(makeRequest(9.0), reason)) << reason;
|
||||
fixture.stepOnce();
|
||||
|
||||
EXPECT_EQ(fixture.loop_.state(), NavigationState::kPlanning)
|
||||
<< "goal mới nằm chờ thay vì thay goal cũ ngay";
|
||||
EXPECT_GT(fixture.planner_.makePlanCount(), plans_before) << "không lập plan lại cho goal mới";
|
||||
}
|
||||
|
||||
TEST(ControlLoopPreempt, NewGoalWhilePlanningReplansImmediately)
|
||||
{
|
||||
Fixture fixture;
|
||||
fixture.planner_.setLatencyCycles(50); // plan cũ còn lâu mới xong
|
||||
|
||||
std::string reason;
|
||||
ASSERT_TRUE(fixture.loop_.submit(makeRequest(3.0), reason)) << reason;
|
||||
fixture.stepOnce();
|
||||
ASSERT_EQ(fixture.loop_.state(), NavigationState::kPlanning);
|
||||
|
||||
ASSERT_TRUE(fixture.loop_.submit(makeRequest(9.0), reason)) << reason;
|
||||
fixture.stepOnce();
|
||||
|
||||
EXPECT_EQ(fixture.loop_.state(), NavigationState::kPlanning);
|
||||
EXPECT_GE(fixture.planner_.cancelCount(), 1u) << "lượt lập plan của goal cũ không bị huỷ";
|
||||
}
|
||||
|
||||
TEST(ControlLoopPreempt, OldMissionIsReportedPreemptedExactlyOnceUnderItsOwnId)
|
||||
{
|
||||
// Bất biến quan trọng nhất: báo kết quả ĐÚNG MỘT LẦN và ĐÚNG ID. Preempt báo kết quả chặng cũ và
|
||||
// nhận chặng mới trong cùng một cycle — dùng nhầm id thì mission layer mất dấu cả hai chặng.
|
||||
Fixture fixture;
|
||||
fixture.controller_.setScript({ControllerScript::kOk});
|
||||
|
||||
std::string reason;
|
||||
ASSERT_TRUE(fixture.loop_.submit(makeRequest(3.0, 11), reason)) << reason;
|
||||
fixture.stepOnce();
|
||||
fixture.stepOnce();
|
||||
ASSERT_EQ(fixture.loop_.state(), NavigationState::kControlling);
|
||||
|
||||
ASSERT_TRUE(fixture.loop_.submit(makeRequest(9.0, 22), reason)) << reason;
|
||||
fixture.stepOnce();
|
||||
|
||||
EXPECT_EQ(fixture.mission_.reportCountFor(11), 1u) << "chặng bị thay không được báo đúng một lần";
|
||||
EXPECT_EQ(fixture.mission_.reportCountFor(22), 0u) << "chặng MỚI bị báo kết quả ngay khi nhận";
|
||||
}
|
||||
|
||||
TEST(ControlLoopPreempt, PreemptedGoalStillFinishesTheNewOne)
|
||||
{
|
||||
// Preempt không được để lại trạng thái nửa vời: chặng mới phải chạy tới cùng như bình thường.
|
||||
Fixture fixture;
|
||||
fixture.controller_.setScript({ControllerScript::kOk, ControllerScript::kOk,
|
||||
ControllerScript::kGoalReached});
|
||||
|
||||
std::string reason;
|
||||
ASSERT_TRUE(fixture.loop_.submit(makeRequest(3.0, 11), reason)) << reason;
|
||||
fixture.stepOnce();
|
||||
fixture.stepOnce();
|
||||
|
||||
ASSERT_TRUE(fixture.loop_.submit(makeRequest(9.0, 22), reason)) << reason;
|
||||
fixture.run();
|
||||
|
||||
EXPECT_FALSE(fixture.hitLimit()) << join(fixture.states());
|
||||
EXPECT_STREQ(fixture.loop_.lastOutcome(), "SUCCEEDED");
|
||||
EXPECT_EQ(fixture.mission_.reportCountFor(22), 1u);
|
||||
}
|
||||
|
||||
TEST(ControlLoopPreempt, NewGoalDuringRecoveryCancelsTheRunningBehavior)
|
||||
{
|
||||
ControlLoopConfig config = baseConfig();
|
||||
config.state_machine.planner_patience = 0.05; // [s] vào recovery nhanh
|
||||
Fixture fixture(config);
|
||||
fixture.planner_.setScript({PlannerScript::kFail});
|
||||
fixture.recovery_.setScript({RecoveryScript::kRunning});
|
||||
|
||||
std::string reason;
|
||||
ASSERT_TRUE(fixture.loop_.submit(makeRequest(3.0), reason)) << reason;
|
||||
for (int i = 0; i < 6 && fixture.loop_.state() != NavigationState::kRecovering; ++i)
|
||||
{
|
||||
fixture.stepOnce();
|
||||
}
|
||||
ASSERT_EQ(fixture.loop_.state(), NavigationState::kRecovering) << join(fixture.states());
|
||||
|
||||
fixture.planner_.setScript({PlannerScript::kOk});
|
||||
ASSERT_TRUE(fixture.loop_.submit(makeRequest(9.0), reason)) << reason;
|
||||
fixture.stepOnce();
|
||||
|
||||
EXPECT_EQ(fixture.loop_.state(), NavigationState::kPlanning);
|
||||
EXPECT_GE(fixture.recovery_.cancelCount(), 1u) << "behavior đang chạy không được bảo dừng";
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
Reference in New Issue
Block a user