Files
recovery_core/test/recovery_test_utils.h
2026-08-03 22:32:40 +07:00

214 lines
6.0 KiB
C++

/*********************************************************************
*
* Software License Agreement (BSD License)
*
* recovery_core — tiện ích dùng chung cho test.
*
* Author: DuongTD
*********************************************************************/
#ifndef RECOVERY_CORE_TEST_RECOVERY_TEST_UTILS_H_
#define RECOVERY_CORE_TEST_RECOVERY_TEST_UTILS_H_
#include <cmath>
#include <string>
#include <utility>
#include <vector>
#include <nav_test_harness/fake_clock.h>
#include <nav_test_harness/fake_collision_checker.h>
#include <nav_test_harness/fake_costmap.h>
#include <nav_test_harness/fake_pose_provider.h>
#include <recovery_core/recovery_behavior.h>
#include <recovery_core/recovery_context.h>
#include <recovery_core/recovery_math.h>
#include <recovery_core/recovery_registry.h>
namespace recovery_test
{
/// @brief Behavior trong @p registry mang tên @p name, hoặc nullptr.
inline recovery_core::RecoveryBehavior* findBehavior(const recovery_core::RecoveryRegistry& registry,
const std::string& name)
{
for (std::size_t i = 0; i < registry.size(); ++i)
{
if (registry.nameAt(i) == name)
{
return registry.at(i);
}
}
return nullptr;
}
/**
* @brief Nối `nav_test_harness::FakePoseProvider` vào cổng của recovery_core.
*
* Hai interface cố ý tách nhau: `recovery_core` không được phụ thuộc gói test harness, và
* `nav_test_harness` phục vụ nhiều gói khác nhau. Adapter mỏng ở đây chính là thứ `RecoveryRunner`
* sẽ làm với `move_base2::PosePort` ở Phase 4.
*/
class HarnessPoseProvider final : public recovery_core::PoseProvider
{
public:
explicit HarnessPoseProvider(nav_test_harness::FakePoseProvider* fake) : fake_(fake)
{
}
bool getRobotPose(robot_geometry_msgs::PoseStamped& pose) const override
{
return fake_ != nullptr && fake_->getRobotPose(pose);
}
private:
nav_test_harness::FakePoseProvider* fake_ = nullptr;
};
/// @brief Nối `nav_test_harness::FakeCollisionChecker` vào cổng của recovery_core.
class HarnessCollisionChecker final : public recovery_core::CollisionChecker
{
public:
explicit HarnessCollisionChecker(nav_test_harness::FakeCollisionChecker* fake) : fake_(fake)
{
}
double footprintCost(double x, double y, double theta) const override
{
return fake_ == nullptr ? -1.0 : fake_->footprintCost(x, y, theta);
}
private:
nav_test_harness::FakeCollisionChecker* fake_ = nullptr;
};
/// @brief Nguồn plan đơn giản do test bơm thẳng.
class StubPlanProvider final : public recovery_core::PlanProvider
{
public:
void setPlan(std::vector<robot_geometry_msgs::PoseStamped> plan)
{
plan_ = std::move(plan);
}
bool getGlobalPlan(std::vector<robot_geometry_msgs::PoseStamped>& out) const override
{
if (plan_.empty())
{
return false;
}
out = plan_;
return true;
}
private:
std::vector<robot_geometry_msgs::PoseStamped> plan_;
};
/**
* @brief Behavior giả, cho phép test điều khiển từng hook.
*
* Dùng để kiểm phần **base** (guard vòng đời, timeout, cưỡng chế họ output) mà không phụ thuộc vào
* hành vi của plugin thật.
*/
class MockBehavior final : public recovery_core::RecoveryBehavior
{
public:
explicit MockBehavior(recovery_core::RecoveryOutputType kind) : kind_(kind)
{
next_result = recovery_core::RecoveryResult::Running();
}
recovery_core::RecoveryOutputType outputKind() const override
{
return kind_;
}
// Núm điều khiển cho test.
bool configure_ok = true;
bool start_ok = true;
recovery_core::RecoveryResult next_result;
// Ghi nhận để assert.
int configure_calls = 0;
int start_calls = 0;
int update_calls = 0;
int cancel_calls = 0;
double last_dt = -1.0;
recovery_core::RecoveryGoal last_goal;
protected:
bool onConfigure(robot::NodeHandle& /*nh*/) override
{
++configure_calls;
return configure_ok;
}
bool onStart(const recovery_core::RecoveryGoal& goal) override
{
++start_calls;
last_goal = goal;
return start_ok;
}
recovery_core::RecoveryResult onUpdate(const robot::Time& /*now*/, double dt) override
{
++update_calls;
last_dt = dt;
return next_result;
}
recovery_core::RecoveryResult onCancel() override
{
++cancel_calls;
return recovery_core::RecoveryBehavior::onCancel();
}
private:
recovery_core::RecoveryOutputType kind_;
};
/**
* @brief Bộ đồ nghề đầy đủ cho một test plugin họ velocity.
*
* Gom costmap giả, pose giả, collision checker giả và đồng hồ giả, kèm hàm mô phỏng robot chạy
* theo đúng lệnh vận tốc mà behavior phát ra.
*/
struct VelocityRig
{
VelocityRig(double span_m = 8.0, double resolution = 0.05,
double footprint_length = 0.6, double footprint_width = 0.4)
: costmap(nav_test_harness::FakeCostmap::centered(span_m, resolution))
, checker(&costmap,
nav_test_harness::FakeCollisionChecker::rectangleFootprint(footprint_length,
footprint_width))
, pose_port(&pose)
, collision_port(&checker)
{
ctx.pose = &pose_port;
ctx.collision = &collision_port;
ctx.plan = &plan;
}
/// @brief Cho robot chạy theo @p command trong @p dt giây, cập nhật pose giả.
void applyCommand(const robot_geometry_msgs::Twist& command, double dt)
{
const double yaw = pose.rawPose().theta;
pose.moveBy(command.linear.x * std::cos(yaw) * dt, command.linear.x * std::sin(yaw) * dt,
command.angular.z * dt);
}
nav_test_harness::FakeCostmap costmap;
nav_test_harness::FakeCollisionChecker checker;
nav_test_harness::FakePoseProvider pose;
nav_test_harness::FakeClock clock;
StubPlanProvider plan;
HarnessPoseProvider pose_port;
HarnessCollisionChecker collision_port;
recovery_core::RecoveryContext ctx;
};
} // namespace recovery_test
#endif // RECOVERY_CORE_TEST_RECOVERY_TEST_UTILS_H_