/********************************************************************* * * Software License Agreement (BSD License) * * move_base2 — action handler mặc định: log rồi báo xong sau một khoảng thời gian. * * Author: DuongTD *********************************************************************/ #include #include #include #include #include #include #include namespace move_base2 { namespace { constexpr double kDefaultDuration = 0.0; // [s] 0 = xong ngay ở tick đầu. constexpr double kMaxDuration = 600.0; // [s] trần vệ sinh cho param cấu hình sai. constexpr double kDefaultTimeout = 30.0; // [s] } // namespace /** * @class NoopActionHandler * @brief Handler mặc định — không điều khiển thiết bị nào, chỉ log và đếm giờ. * * Có ba công dụng thật, không phải chỗ giữ chỗ: * 1. cho `actionType` chưa có thiết bị tương ứng (`wait`, hoặc action chỉ mang ý nghĩa ghi nhật ký) * chạy được mà không phải viết handler riêng; * 2. dựng một deployment chạy end-to-end trước khi phần cứng sẵn sàng; * 3. làm ví dụ tham chiếu cho contract — đặc biệt là **timeout tầng 1**. * * `duration = 0` nghĩa là xong ngay ở tick đầu. Đặt > 0 để mô phỏng một thiết bị chậm; đặt * `hang: true` để mô phỏng thiết bị không bao giờ trả lời — khi đó chỉ `timeout` cắt được, đúng * tình huống mà timeout tầng 1 sinh ra để xử lý. */ class NoopActionHandler final : public ActionHandler { public: NoopActionHandler() = default; static ActionHandler::Ptr create() { return std::make_shared(); } bool configure(const std::string& name, robot::NodeHandle& nh) override { name_ = name; nh.param("duration", duration_, kDefaultDuration); nh.param("timeout", timeout_, kDefaultTimeout); nh.param("hang", hang_, false); nh.param("action_types", action_types_, std::vector{"wait"}); if (!std::isfinite(duration_) || duration_ < 0.0 || duration_ > kMaxDuration) { robot::log_warning("[move_base2] '%s': duration=%.3f s ngoài [0, %.0f]; dùng %.3f s.", name_.c_str(), duration_, kMaxDuration, kDefaultDuration); duration_ = kDefaultDuration; } if (!std::isfinite(timeout_) || timeout_ < 0.0) { robot::log_warning("[move_base2] '%s': timeout=%.3f s không hợp lệ; dùng %.3f s.", name_.c_str(), timeout_, kDefaultTimeout); timeout_ = kDefaultTimeout; } if (hang_ && timeout_ <= 0.0) { // Chế độ mô phỏng thiết bị treo mà lại tắt timeout thì action sẽ chạy vĩnh viễn — đúng thứ // contract cấm. robot::log_error("[move_base2] '%s': hang=true nhưng timeout=%.3f s; handler sẽ không bao " "giờ kết thúc.", name_.c_str(), timeout_); return false; } if (!hang_ && timeout_ > 0.0 && duration_ > 0.0 && timeout_ <= duration_) { // Cấu hình này khiến action LUÔN hỏng vì timeout — gần như chắc chắn là gõ nhầm. robot::log_error("[move_base2] '%s': timeout=%.3f s <= duration=%.3f s; action sẽ luôn thất " "bại.", name_.c_str(), timeout_, duration_); return false; } if (action_types_.empty()) { robot::log_error("[move_base2] '%s': action_types rỗng — handler sẽ không bao giờ được gọi.", name_.c_str()); return false; } return true; } std::vector supportedActionTypes() const override { return action_types_; } bool start(const robot_protocol_msgs::Action& action, const robot::Time& now) override { started_at_ = now; action_id_ = action.actionId; robot::log_info("[move_base2] '%s': bắt đầu action '%s' (id '%s'), duration %.3f s.", name_.c_str(), action.actionType.c_str(), action.actionId.c_str(), duration_); return true; } ActionTick update(const robot::Time& now) override { ActionTick tick; const double elapsed = (now - started_at_).toSec(); // [s] // Timeout TẦNG 1 — trách nhiệm của chính handler, không dựa vào action_patience của state // machine (mặc định tắt). Ở đây ngưỡng là cấu hình vì handler này không nói chuyện với thiết bị // nào; handler thật thì suy ngưỡng từ hiểu biết về thiết bị của nó. if (timeout_ > 0.0 && elapsed >= timeout_) { tick.status = ActionTick::Status::kFailed; tick.message = "action '" + action_id_ + "' quá timeout"; return tick; } if (hang_) { // Mô phỏng thiết bị không bao giờ trả lời. Có để fault-injection trong test tích hợp: đây là // đúng tình huống mà timeout tầng 1 sinh ra để xử lý. tick.status = ActionTick::Status::kRunning; return tick; } if (elapsed >= duration_) { tick.status = ActionTick::Status::kSucceeded; tick.message = "action '" + action_id_ + "' hoàn tất"; return tick; } tick.status = ActionTick::Status::kRunning; return tick; } void cancel() override { // Không có thiết bị nào để đưa về trạng thái an toàn. Handler thật phải làm việc đó ở đây. robot::log_info("[move_base2] '%s': action '%s' bị huỷ.", name_.c_str(), action_id_.c_str()); } private: std::string name_; std::vector action_types_{"wait"}; double duration_ = kDefaultDuration; ///< [s] double timeout_ = kDefaultTimeout; ///< [s] 0 = không giới hạn bool hang_ = false; ///< true = không bao giờ hoàn tất (fault injection) robot::Time started_at_; std::string action_id_; }; } // namespace move_base2 BOOST_DLL_ALIAS(move_base2::NoopActionHandler::create, NoopActionHandler)