871 lines
19 KiB
C++
871 lines
19 KiB
C++
/*********************************************************************
|
|
*
|
|
* Software License Agreement (BSD License)
|
|
*
|
|
* move_base2 — hiện thực giả của các port, kịch bản hoá bằng chuỗi kết quả định sẵn.
|
|
*
|
|
* Đặt trong test/ của chính move_base2 chứ không đặt trong nav_test_harness: các fake này hiện thực
|
|
* port CỦA move_base2, nếu để trong harness thì harness phải phụ thuộc ngược vào move_base2 và
|
|
* chiều phụ thuộc một chiều bị phá vỡ. Phần fake thực sự dùng chung (đồng hồ, costmap, pose,
|
|
* kiểm va chạm, kịch bản) nằm ở nav_test_harness.
|
|
*
|
|
* Author: DuongTD
|
|
*********************************************************************/
|
|
#ifndef MOVE_BASE2_TEST_FAKE_PORTS_H_
|
|
#define MOVE_BASE2_TEST_FAKE_PORTS_H_
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <map>
|
|
#include <limits>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <robot/time.h>
|
|
|
|
#include <move_base2/ports/action_port.h>
|
|
#include <move_base2/ports/clock_port.h>
|
|
#include <move_base2/ports/controller_port.h>
|
|
#include <move_base2/ports/mission_port.h>
|
|
#include <move_base2/ports/planner_port.h>
|
|
#include <move_base2/ports/pose_port.h>
|
|
#include <move_base2/ports/recovery_port.h>
|
|
|
|
namespace move_base2
|
|
{
|
|
namespace testing
|
|
{
|
|
|
|
/// @brief Kết quả một lần lập plan trong kịch bản.
|
|
enum class PlannerScript
|
|
{
|
|
kOk, ///< Trả plan hợp lệ.
|
|
kFail, ///< makePlan trả false.
|
|
kEmpty ///< makePlan trả true nhưng plan rỗng — bẫy front()/back() trên vector rỗng.
|
|
};
|
|
|
|
/// @brief Kết quả một lần gọi controller trong kịch bản.
|
|
enum class ControllerScript
|
|
{
|
|
kOk, ///< Sinh lệnh hợp lệ.
|
|
kFail, ///< Không sinh được lệnh.
|
|
kGoalReached, ///< Báo đã tới đích.
|
|
kNaN, ///< Sinh lệnh chứa NaN — phải bị bộ trọng tài chặn.
|
|
kTooFast ///< Sinh lệnh vượt trần vận tốc — phải bị clamp.
|
|
};
|
|
|
|
/// @brief Kết quả một tick recovery trong kịch bản.
|
|
enum class RecoveryScript
|
|
{
|
|
kRunning,
|
|
kSucceeded,
|
|
kFailed
|
|
};
|
|
|
|
/// @brief Kết quả một tick action trong kịch bản (D8).
|
|
enum class ActionScript
|
|
{
|
|
kRunning,
|
|
kSucceeded,
|
|
kFailed
|
|
};
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
/// @brief Đồng hồ do test điều khiển, chuyển tiếp một FakeClock của harness qua ClockPort.
|
|
class FakeClockPort final : public ClockPort
|
|
{
|
|
public:
|
|
explicit FakeClockPort(double start_sec = 1000.0) : now_(start_sec)
|
|
{
|
|
}
|
|
|
|
robot::Time now() const override
|
|
{
|
|
return now_;
|
|
}
|
|
|
|
/// @param seconds [s] Lượng thời gian trôi. Giá trị âm bị bỏ qua.
|
|
void advance(double seconds)
|
|
{
|
|
if (seconds > 0.0)
|
|
{
|
|
now_ = robot::Time(now_.toSec() + seconds);
|
|
}
|
|
}
|
|
|
|
void setTime(double seconds)
|
|
{
|
|
now_ = robot::Time(seconds);
|
|
}
|
|
|
|
private:
|
|
robot::Time now_;
|
|
};
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
class FakePosePort final : public PosePort
|
|
{
|
|
public:
|
|
bool getRobotPose(robot_geometry_msgs::PoseStamped& pose) const override
|
|
{
|
|
++call_count_;
|
|
if (!available_)
|
|
{
|
|
return false;
|
|
}
|
|
pose = pose_;
|
|
return true;
|
|
}
|
|
|
|
/// @param x,y [m]
|
|
void setPosition(double x, double y)
|
|
{
|
|
pose_.header.frame_id = "map";
|
|
pose_.pose.position.x = x;
|
|
pose_.pose.position.y = y;
|
|
pose_.pose.orientation.w = 1.0;
|
|
}
|
|
|
|
/// @brief false = mô phỏng TF thiếu/stale.
|
|
void setAvailable(bool available)
|
|
{
|
|
available_ = available;
|
|
}
|
|
|
|
std::size_t callCount() const
|
|
{
|
|
return call_count_;
|
|
}
|
|
|
|
private:
|
|
robot_geometry_msgs::PoseStamped pose_;
|
|
bool available_ = true;
|
|
mutable std::size_t call_count_ = 0;
|
|
};
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
class FakePlannerPort final : public PlannerPort
|
|
{
|
|
public:
|
|
bool swapPlanner(const std::string& planner_name) override
|
|
{
|
|
if (!swap_succeeds_)
|
|
{
|
|
return false;
|
|
}
|
|
active_ = planner_name;
|
|
++swap_count_;
|
|
return true;
|
|
}
|
|
|
|
bool startPlan(const robot_geometry_msgs::PoseStamped& /*start*/,
|
|
const robot_geometry_msgs::PoseStamped& goal,
|
|
const robot_protocol_msgs::Order* order, std::uint64_t tag) override
|
|
{
|
|
if (in_flight_)
|
|
{
|
|
return false; // Đúng như PlannerRunner: một lượt tại một thời điểm.
|
|
}
|
|
|
|
++make_plan_count_;
|
|
saw_order_ = saw_order_ || order != nullptr;
|
|
|
|
in_flight_ = true;
|
|
pending_tag_ = tag;
|
|
pending_goal_ = goal;
|
|
cycles_left_ = latency_cycles_;
|
|
pending_action_ = nextAction();
|
|
return true;
|
|
}
|
|
|
|
bool isPlanning() const override
|
|
{
|
|
return in_flight_;
|
|
}
|
|
|
|
bool pollPlan(PlanResult& result) override
|
|
{
|
|
if (!in_flight_)
|
|
{
|
|
return false;
|
|
}
|
|
if (cycles_left_ > 0)
|
|
{
|
|
--cycles_left_;
|
|
return false; // Còn "đang tính" — bên gọi phải thấy kBusy.
|
|
}
|
|
|
|
in_flight_ = false;
|
|
result.tag = pending_tag_;
|
|
result.plan.clear();
|
|
|
|
switch (pending_action_)
|
|
{
|
|
case PlannerScript::kFail:
|
|
result.succeeded = false;
|
|
return true;
|
|
case PlannerScript::kEmpty:
|
|
// Contract nói thành công phải kèm plan không rỗng; fake cố ý vi phạm để kiểm guard của
|
|
// bên gọi.
|
|
result.succeeded = true;
|
|
return true;
|
|
case PlannerScript::kOk:
|
|
break;
|
|
}
|
|
|
|
result.succeeded = true;
|
|
result.plan.push_back(pending_goal_);
|
|
return true;
|
|
}
|
|
|
|
void cancelPlan() override
|
|
{
|
|
in_flight_ = false;
|
|
++cancel_count_;
|
|
}
|
|
|
|
std::string activePlanner() const override
|
|
{
|
|
return active_;
|
|
}
|
|
|
|
/**
|
|
* @brief Số cycle mà một lượt lập plan "mất" trước khi có kết quả.
|
|
*
|
|
* 0 (mặc định) = kết quả có ngay ở lần poll kế tiếp, tức đúng nhịp của bản lập plan đồng bộ cũ:
|
|
* kick ở cuối cycle N, state machine thấy plan ở cycle N+1. Nhờ vậy mọi test viết cho bản đồng bộ
|
|
* giữ nguyên ý nghĩa.
|
|
*/
|
|
void setLatencyCycles(std::size_t cycles)
|
|
{
|
|
latency_cycles_ = cycles;
|
|
}
|
|
|
|
std::size_t cancelCount() const
|
|
{
|
|
return cancel_count_;
|
|
}
|
|
|
|
void setScript(std::vector<PlannerScript> script)
|
|
{
|
|
script_ = std::move(script);
|
|
index_ = 0;
|
|
}
|
|
|
|
void setSwapSucceeds(bool succeeds)
|
|
{
|
|
swap_succeeds_ = succeeds;
|
|
}
|
|
|
|
std::size_t makePlanCount() const
|
|
{
|
|
return make_plan_count_;
|
|
}
|
|
|
|
std::size_t swapCount() const
|
|
{
|
|
return swap_count_;
|
|
}
|
|
|
|
bool sawOrder() const
|
|
{
|
|
return saw_order_;
|
|
}
|
|
|
|
private:
|
|
/// Hết kịch bản thì giữ kết quả cuối; kịch bản rỗng thì luôn thành công.
|
|
PlannerScript nextAction()
|
|
{
|
|
if (script_.empty())
|
|
{
|
|
return PlannerScript::kOk;
|
|
}
|
|
if (index_ >= script_.size())
|
|
{
|
|
return script_.back();
|
|
}
|
|
return script_[index_++];
|
|
}
|
|
|
|
std::vector<PlannerScript> script_;
|
|
std::size_t index_ = 0;
|
|
std::string active_;
|
|
bool swap_succeeds_ = true;
|
|
|
|
bool in_flight_ = false;
|
|
std::uint64_t pending_tag_ = 0;
|
|
robot_geometry_msgs::PoseStamped pending_goal_;
|
|
PlannerScript pending_action_ = PlannerScript::kOk;
|
|
std::size_t latency_cycles_ = 0;
|
|
std::size_t cycles_left_ = 0;
|
|
std::size_t cancel_count_ = 0;
|
|
|
|
std::size_t make_plan_count_ = 0;
|
|
std::size_t swap_count_ = 0;
|
|
bool saw_order_ = false;
|
|
};
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
class FakeControllerPort final : public ControllerPort
|
|
{
|
|
public:
|
|
bool swapPlanner(const std::string& planner_name) override
|
|
{
|
|
if (!swap_succeeds_)
|
|
{
|
|
return false;
|
|
}
|
|
active_ = planner_name;
|
|
return true;
|
|
}
|
|
|
|
void setTolerance(double xy_m, double yaw_rad) override
|
|
{
|
|
xy_tolerance_ = xy_m;
|
|
yaw_tolerance_ = yaw_rad;
|
|
}
|
|
|
|
bool setPlan(const std::vector<robot_geometry_msgs::PoseStamped>& plan) override
|
|
{
|
|
++set_plan_count_;
|
|
last_plan_size_ = plan.size();
|
|
return set_plan_succeeds_ && !plan.empty();
|
|
}
|
|
|
|
bool computeVelocityCommands(robot_geometry_msgs::Twist& cmd) override
|
|
{
|
|
++compute_count_;
|
|
switch (current_action_)
|
|
{
|
|
case ControllerScript::kFail:
|
|
return false;
|
|
case ControllerScript::kNaN:
|
|
cmd.linear.x = std::numeric_limits<double>::quiet_NaN();
|
|
cmd.angular.z = 0.0;
|
|
return true;
|
|
case ControllerScript::kTooFast:
|
|
cmd.linear.x = 99.0;
|
|
cmd.angular.z = 99.0;
|
|
return true;
|
|
case ControllerScript::kGoalReached:
|
|
case ControllerScript::kOk:
|
|
break;
|
|
}
|
|
cmd.linear.x = nominal_speed_;
|
|
cmd.angular.z = 0.0;
|
|
return true;
|
|
}
|
|
|
|
bool isGoalReached() override
|
|
{
|
|
// Lấy hành động cho cycle này ở đây vì đây là lời gọi ĐẦU TIÊN của một cycle controller, đúng
|
|
// thứ tự mà control loop dùng.
|
|
current_action_ = nextAction();
|
|
++goal_check_count_;
|
|
return current_action_ == ControllerScript::kGoalReached;
|
|
}
|
|
|
|
void setMeasuredVelocity(const robot_geometry_msgs::Twist& velocity) override
|
|
{
|
|
measured_velocity_ = velocity;
|
|
}
|
|
|
|
bool setTwistLinear(const robot_geometry_msgs::Vector3& linear) override
|
|
{
|
|
if (linear.x < 0.0)
|
|
{
|
|
limit_backward_ = linear.x; // [m/s], âm
|
|
}
|
|
else
|
|
{
|
|
limit_forward_ = linear.x; // [m/s]
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool setTwistAngular(const robot_geometry_msgs::Vector3& angular) override
|
|
{
|
|
limit_angular_ = angular.z; // [rad/s]
|
|
return true;
|
|
}
|
|
|
|
const robot_geometry_msgs::Twist& measuredVelocity() const
|
|
{
|
|
return measured_velocity_;
|
|
}
|
|
|
|
double limitForward() const
|
|
{
|
|
return limit_forward_;
|
|
}
|
|
|
|
double limitBackward() const
|
|
{
|
|
return limit_backward_;
|
|
}
|
|
|
|
double limitAngular() const
|
|
{
|
|
return limit_angular_;
|
|
}
|
|
|
|
std::string activeController() const override
|
|
{
|
|
return active_;
|
|
}
|
|
|
|
void setScript(std::vector<ControllerScript> script)
|
|
{
|
|
script_ = std::move(script);
|
|
index_ = 0;
|
|
}
|
|
|
|
void setSwapSucceeds(bool succeeds)
|
|
{
|
|
swap_succeeds_ = succeeds;
|
|
}
|
|
|
|
void setSetPlanSucceeds(bool succeeds)
|
|
{
|
|
set_plan_succeeds_ = succeeds;
|
|
}
|
|
|
|
/// @param speed [m/s] Tốc độ dài của lệnh khi kịch bản là kOk.
|
|
void setNominalSpeed(double speed)
|
|
{
|
|
nominal_speed_ = speed;
|
|
}
|
|
|
|
std::size_t setPlanCount() const
|
|
{
|
|
return set_plan_count_;
|
|
}
|
|
|
|
std::size_t computeCount() const
|
|
{
|
|
return compute_count_;
|
|
}
|
|
|
|
std::size_t goalCheckCount() const
|
|
{
|
|
return goal_check_count_;
|
|
}
|
|
|
|
std::size_t lastPlanSize() const
|
|
{
|
|
return last_plan_size_;
|
|
}
|
|
|
|
double xyTolerance() const
|
|
{
|
|
return xy_tolerance_;
|
|
}
|
|
|
|
double yawTolerance() const
|
|
{
|
|
return yaw_tolerance_;
|
|
}
|
|
|
|
private:
|
|
robot_geometry_msgs::Twist measured_velocity_;
|
|
double limit_forward_ = 0.0; ///< [m/s]
|
|
double limit_backward_ = 0.0; ///< [m/s], âm
|
|
double limit_angular_ = 0.0; ///< [rad/s]
|
|
|
|
ControllerScript nextAction()
|
|
{
|
|
if (script_.empty())
|
|
{
|
|
return ControllerScript::kOk;
|
|
}
|
|
if (index_ >= script_.size())
|
|
{
|
|
return script_.back();
|
|
}
|
|
return script_[index_++];
|
|
}
|
|
|
|
std::vector<ControllerScript> script_;
|
|
std::size_t index_ = 0;
|
|
ControllerScript current_action_ = ControllerScript::kOk;
|
|
|
|
std::string active_;
|
|
bool swap_succeeds_ = true;
|
|
bool set_plan_succeeds_ = true;
|
|
double nominal_speed_ = 0.3; ///< [m/s]
|
|
double xy_tolerance_ = 0.0; ///< [m]
|
|
double yaw_tolerance_ = 0.0; ///< [rad]
|
|
|
|
std::size_t set_plan_count_ = 0;
|
|
std::size_t compute_count_ = 0;
|
|
std::size_t goal_check_count_ = 0;
|
|
std::size_t last_plan_size_ = 0;
|
|
};
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
class FakeRecoveryPort final : public RecoveryPort
|
|
{
|
|
public:
|
|
explicit FakeRecoveryPort(std::size_t behavior_count = 2) : behavior_count_(behavior_count)
|
|
{
|
|
}
|
|
|
|
bool configure(robot::NodeHandle& /*nh*/) override
|
|
{
|
|
return true;
|
|
}
|
|
|
|
std::size_t behaviorCount() const override
|
|
{
|
|
return behavior_count_;
|
|
}
|
|
|
|
RecoveryOutputKind outputKind(std::size_t index) const override
|
|
{
|
|
if (index >= behavior_count_)
|
|
{
|
|
return RecoveryOutputKind::kNone;
|
|
}
|
|
const auto it = output_kinds_.find(index);
|
|
return it == output_kinds_.end() ? default_output_kind_ : it->second;
|
|
}
|
|
|
|
/// @brief Đặt họ output cho behavior thứ @p index (mặc định mọi behavior đều lái robot).
|
|
void setOutputKind(std::size_t index, RecoveryOutputKind kind)
|
|
{
|
|
output_kinds_[index] = kind;
|
|
}
|
|
|
|
void setDefaultOutputKind(RecoveryOutputKind kind)
|
|
{
|
|
default_output_kind_ = kind;
|
|
}
|
|
|
|
bool start(std::size_t index, RecoveryTrigger trigger) override
|
|
{
|
|
++start_count_;
|
|
last_start_index_ = index;
|
|
last_trigger_ = trigger;
|
|
started_indices_.push_back(index);
|
|
|
|
if (index >= behavior_count_ || !start_succeeds_)
|
|
{
|
|
return false;
|
|
}
|
|
active_ = true;
|
|
return true;
|
|
}
|
|
|
|
RecoveryTick update() override
|
|
{
|
|
++update_count_;
|
|
|
|
RecoveryTick tick;
|
|
switch (nextAction())
|
|
{
|
|
case RecoveryScript::kRunning:
|
|
tick.status = RecoveryTick::Status::kRunning;
|
|
tick.has_velocity = emits_velocity_;
|
|
tick.cmd.linear.x = recovery_speed_;
|
|
break;
|
|
case RecoveryScript::kSucceeded:
|
|
tick.status = RecoveryTick::Status::kSucceeded;
|
|
active_ = false;
|
|
break;
|
|
case RecoveryScript::kFailed:
|
|
tick.status = RecoveryTick::Status::kFailed;
|
|
active_ = false;
|
|
break;
|
|
}
|
|
return tick;
|
|
}
|
|
|
|
void cancel() override
|
|
{
|
|
++cancel_count_;
|
|
active_ = false;
|
|
}
|
|
|
|
std::string behaviorName(std::size_t index) const override
|
|
{
|
|
return index < behavior_count_ ? "fake_behavior_" + std::to_string(index) : std::string();
|
|
}
|
|
|
|
void setScript(std::vector<RecoveryScript> script)
|
|
{
|
|
script_ = std::move(script);
|
|
index_ = 0;
|
|
}
|
|
|
|
void setStartSucceeds(bool succeeds)
|
|
{
|
|
start_succeeds_ = succeeds;
|
|
}
|
|
|
|
/// @param speed [m/s] Vận tốc behavior phát khi đang chạy. Dấu âm nghĩa là lùi.
|
|
void setRecoveryVelocity(bool emits, double speed)
|
|
{
|
|
emits_velocity_ = emits;
|
|
recovery_speed_ = speed;
|
|
}
|
|
|
|
std::size_t startCount() const
|
|
{
|
|
return start_count_;
|
|
}
|
|
|
|
std::size_t updateCount() const
|
|
{
|
|
return update_count_;
|
|
}
|
|
|
|
std::size_t cancelCount() const
|
|
{
|
|
return cancel_count_;
|
|
}
|
|
|
|
std::size_t lastStartIndex() const
|
|
{
|
|
return last_start_index_;
|
|
}
|
|
|
|
RecoveryTrigger lastTrigger() const
|
|
{
|
|
return last_trigger_;
|
|
}
|
|
|
|
const std::vector<std::size_t>& startedIndices() const
|
|
{
|
|
return started_indices_;
|
|
}
|
|
|
|
bool active() const
|
|
{
|
|
return active_;
|
|
}
|
|
|
|
private:
|
|
RecoveryScript nextAction()
|
|
{
|
|
if (script_.empty())
|
|
{
|
|
return RecoveryScript::kSucceeded;
|
|
}
|
|
if (index_ >= script_.size())
|
|
{
|
|
return script_.back();
|
|
}
|
|
return script_[index_++];
|
|
}
|
|
|
|
std::size_t behavior_count_;
|
|
std::vector<RecoveryScript> script_;
|
|
std::size_t index_ = 0;
|
|
|
|
bool start_succeeds_ = true;
|
|
bool active_ = false;
|
|
bool emits_velocity_ = false;
|
|
double recovery_speed_ = -0.1; ///< [m/s], âm = lùi
|
|
|
|
/// Mặc định coi mọi behavior đều lái robot — giữ nguyên hành vi của các test viết trước khi
|
|
/// RecoveryPort có outputKind().
|
|
RecoveryOutputKind default_output_kind_ = RecoveryOutputKind::kVelocity;
|
|
std::map<std::size_t, RecoveryOutputKind> output_kinds_;
|
|
|
|
std::size_t start_count_ = 0;
|
|
std::size_t update_count_ = 0;
|
|
std::size_t cancel_count_ = 0;
|
|
std::size_t last_start_index_ = 0;
|
|
RecoveryTrigger last_trigger_ = RecoveryTrigger::kPlanningFailed;
|
|
std::vector<std::size_t> started_indices_;
|
|
};
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
class FakeActionPort final : public ActionPort
|
|
{
|
|
public:
|
|
bool configure(robot::NodeHandle& /*nh*/) override
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool start(const robot_protocol_msgs::Action& action) override
|
|
{
|
|
++start_count_;
|
|
started_action_types_.push_back(action.actionType);
|
|
if (!start_succeeds_)
|
|
{
|
|
return false;
|
|
}
|
|
active_ = true;
|
|
return true;
|
|
}
|
|
|
|
ActionTick update() override
|
|
{
|
|
++update_count_;
|
|
|
|
ActionTick tick;
|
|
switch (nextAction())
|
|
{
|
|
case ActionScript::kRunning:
|
|
tick.status = ActionTick::Status::kRunning;
|
|
break;
|
|
case ActionScript::kSucceeded:
|
|
tick.status = ActionTick::Status::kSucceeded;
|
|
active_ = false;
|
|
break;
|
|
case ActionScript::kFailed:
|
|
tick.status = ActionTick::Status::kFailed;
|
|
tick.message = "fake action failed";
|
|
active_ = false;
|
|
break;
|
|
}
|
|
return tick;
|
|
}
|
|
|
|
void cancel() override
|
|
{
|
|
++cancel_count_;
|
|
active_ = false;
|
|
}
|
|
|
|
void setScript(std::vector<ActionScript> script)
|
|
{
|
|
script_ = std::move(script);
|
|
index_ = 0;
|
|
}
|
|
|
|
void setStartSucceeds(bool succeeds)
|
|
{
|
|
start_succeeds_ = succeeds;
|
|
}
|
|
|
|
std::size_t startCount() const
|
|
{
|
|
return start_count_;
|
|
}
|
|
|
|
std::size_t updateCount() const
|
|
{
|
|
return update_count_;
|
|
}
|
|
|
|
std::size_t cancelCount() const
|
|
{
|
|
return cancel_count_;
|
|
}
|
|
|
|
/// @brief actionType của từng lần start, theo thứ tự — kiểm "actions đi nguyên vẹn, đúng thứ tự".
|
|
const std::vector<std::string>& startedActionTypes() const
|
|
{
|
|
return started_action_types_;
|
|
}
|
|
|
|
bool active() const
|
|
{
|
|
return active_;
|
|
}
|
|
|
|
private:
|
|
ActionScript nextAction()
|
|
{
|
|
if (script_.empty())
|
|
{
|
|
return ActionScript::kSucceeded;
|
|
}
|
|
if (index_ >= script_.size())
|
|
{
|
|
return script_.back();
|
|
}
|
|
return script_[index_++];
|
|
}
|
|
|
|
std::vector<ActionScript> script_;
|
|
std::size_t index_ = 0;
|
|
|
|
bool start_succeeds_ = true;
|
|
bool active_ = false;
|
|
|
|
std::size_t start_count_ = 0;
|
|
std::size_t update_count_ = 0;
|
|
std::size_t cancel_count_ = 0;
|
|
std::vector<std::string> started_action_types_;
|
|
};
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
class FakeMissionPort final : public MissionPort
|
|
{
|
|
public:
|
|
void setRequestCallback(RequestCallback callback) override
|
|
{
|
|
callback_ = std::move(callback);
|
|
}
|
|
|
|
void reportOutcome(std::uint64_t mission_sequence_id, NavigationOutcome outcome) override
|
|
{
|
|
reports_.emplace_back(mission_sequence_id, outcome);
|
|
}
|
|
|
|
bool hasActiveMission() const override
|
|
{
|
|
return active_;
|
|
}
|
|
|
|
void start() override
|
|
{
|
|
active_ = true;
|
|
}
|
|
|
|
void stop() override
|
|
{
|
|
active_ = false;
|
|
}
|
|
|
|
/// @brief Giả lập mission layer đẩy một chặng xuống.
|
|
void emit(const NavigationRequest& request)
|
|
{
|
|
if (callback_)
|
|
{
|
|
callback_(request);
|
|
}
|
|
}
|
|
|
|
const std::vector<std::pair<std::uint64_t, NavigationOutcome>>& reports() const
|
|
{
|
|
return reports_;
|
|
}
|
|
|
|
/// @brief Số lần đã báo kết quả cho một sequence id — bất biến là phải bằng 1.
|
|
std::size_t reportCountFor(std::uint64_t mission_sequence_id) const
|
|
{
|
|
std::size_t count = 0;
|
|
for (const auto& report : reports_)
|
|
{
|
|
if (report.first == mission_sequence_id)
|
|
{
|
|
++count;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
private:
|
|
RequestCallback callback_;
|
|
std::vector<std::pair<std::uint64_t, NavigationOutcome>> reports_;
|
|
bool active_ = false;
|
|
};
|
|
|
|
} // namespace testing
|
|
} // namespace move_base2
|
|
|
|
#endif // MOVE_BASE2_TEST_FAKE_PORTS_H_
|