/********************************************************************* * * Software License Agreement (BSD License) * * Nguồn mission từ VDA5050 Order (schema "vda5050.order"). * * Author: DuongTD *********************************************************************/ #ifndef MISSION_ADAPTERS_PLUGINS_VDA5050_SOURCE_ADAPTER_H_ #define MISSION_ADAPTERS_PLUGINS_VDA5050_SOURCE_ADAPTER_H_ #include #include #include #include #include #include #include #include #include namespace mission_plugins { /** * @class VDA5050SourceAdapter * @brief Cắt một VDA5050 Order thành các chặng, mỗi chặng kết thúc tại một node có action. * * Ba điểm conformance mà adapter này chịu trách nhiệm: * * 1. **base / horizon** — chỉ phần `released == true` được thực thi. Horizon là dự định của fleet * manager, chưa được phép chạy; robot đi vào đó là đi vào đoạn đường chưa ai cho phép. * 2. **orderId / orderUpdateId** — phân biệt yêu cầu MỚI (thay hàng đợi) với bản CẬP NHẬT của yêu * cầu đang chạy (nối tiếp). Nhầm hai thứ này thì mỗi lần fleet manager release thêm horizon, * robot lại huỷ và chạy lại chặng đang đi. * 3. **goal / start self-contained** — mission mang sẵn pose đích, consumer không phải tự đoán từ * `nodes.back()`. * * State là member, không phải static local: nhiều instance trong cùng tiến trình không được nhìn * thấy order của nhau. */ class VDA5050SourceAdapter : public mission_adapters::MissionSourceAdapter { public: /// @brief Factory được PluginRegistry nạp qua boost::dll::import_alias. static mission_adapters::MissionSourceAdapter::Ptr create(); /** * @brief Đọc param riêng của instance. * * Param (namespace ``): * - `global_frame` [string, mặc định "map"]: frame gán cho pose sinh ra từ nodePosition. * - `compound_actions` [map, optional]: action "phải dò rồi mới biết đích" — xem @ref Step. */ bool configure(const std::string& name, robot::NodeHandle& nh) override; std::string schema() const override; bool validate(const mission_adapters::MissionRequest& request, std::string& reason) const override; mission_adapters::ConversionResult convert(const mission_adapters::MissionRequest& request) override; private: /** * @struct Step * @brief Một bước trong chuỗi mở rộng của compound action. * * Engine hiểu đúng bốn từ khoá và **không biết** `charge`, `dock_target` hay `LiftFork` nghĩa là * gì — đó là điều kiện để cùng một engine dùng lại cho model robot khác: thêm model = thêm YAML * + một `.so` handler, không sửa dòng nào ở đây. * * Mỗi step phải có ĐÚNG MỘT trong bốn khoá đầu; sai cú pháp thì `configure()` từ chối lúc **boot**. */ struct Step { std::string action; ///< `action: ` -> chặng chỉ-action std::string move_to; ///< `move_to: ` -> chặng nav, frame cố định std::string move_to_param; ///< `move_to_param: `-> frame lấy từ actionParameters double move = std::numeric_limits::quiet_NaN(); ///< `move: ` tương đối std::string motion_hint; ///< `profile:` -> position | docking | go_straight | rotate std::string marker; ///< `marker:` -> khoá override planner, chỉ hợp lệ với docking }; /// @brief Chuỗi chặng thay cho một action. Bảng chỉ giữ CẤU TRÚC, dữ liệu tới từ order. using Steps = std::vector; /// @brief Nạp bảng `compound_actions`. Trả false nếu bảng có mà khai sai — lỗi nổ lúc boot. bool loadCompoundActions(const std::string& name, robot::NodeHandle& nh); /// @brief Tra bảng theo actionType. nullptr nếu là action thường. const Steps* findCompound(const std::string& action_type) const; /** * @brief Mở rộng action của một chặng thành chuỗi chặng, giữ nguyên thứ tự mảng JSON. * @param leg Chặng nav tới node; action compound sẽ được GỠ khỏi nó. * @param out Nơi nối thêm chặng sinh ra. * @return false nếu thiếu tham số cấu trúc — bên gọi phải bỏ CẢ order. * * Action thường tích vào bộ đệm; gặp compound thì xả bộ đệm rồi phát chuỗi. Bộ đệm ĐẦU TIÊN nằm * lại trên chặng nav, các bộ đệm sau thành chặng chỉ-action. Nhờ vậy `[MutedOn, charge, MutedOff]` * cho ra `MutedOff` **sau** chuỗi charge — gom hết vào chặng nav sẽ bật lại cảm biến an toàn * trước khi robot lùi vào trạm. */ bool expandCompound(const std::shared_ptr& leg, std::vector>& out) const; /// Số node đầu tiên có released == true. 0 nghĩa là không node nào được release. static size_t countReleasedNodes(const robot_protocol_msgs::Order& order); /// Số edge thuộc phần base, không vượt quá `released_node_count - 1`. static size_t countReleasedEdges(const robot_protocol_msgs::Order& order, size_t released_node_count); /** * @brief Phần order được phép thực thi. * @return số node base; 0 nếu order không dùng trường `released` (xem ghi chú trong .cpp). */ size_t executableNodeCount(const robot_protocol_msgs::Order& order) const; /// Dựng PoseStamped từ nodePosition (theta [rad] -> quaternion quanh trục z). robot_geometry_msgs::PoseStamped toPose(const robot_protocol_msgs::Node& node) const; std::string name_; /// Frame gán cho goal/start. VDA5050 `mapId` là danh tính bản đồ, KHÔNG phải frame TF. std::string global_frame_ = "map"; /// actionType -> chuỗi chặng. Rỗng = không action nào cần mở rộng, adapter chạy như trước. std::map compound_actions_; /// Order đang chạy — dùng để nhận ra order update so với order mới. std::string last_order_id_; std::uint32_t last_order_update_id_ = 0; /// Số node base đã chuyển đổi của order đang chạy; điểm bắt đầu cho phần release thêm. size_t converted_node_count_ = 0; }; } // namespace mission_plugins #endif // MISSION_ADAPTERS_PLUGINS_VDA5050_SOURCE_ADAPTER_H_