C++ YASMIN (Yet Another State MachINe)
Loading...
Searching...
No Matches
action_state.hpp
Go to the documentation of this file.
1// Copyright (C) 2023 Miguel Ángel González Santamarta
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program. If not, see <https://www.gnu.org/licenses/>.
15
16#ifndef YASMIN_ROS__ACTION_STATE_HPP_
17#define YASMIN_ROS__ACTION_STATE_HPP_
18
19#include <condition_variable>
20#include <functional>
21#include <memory>
22#include <string>
23
24#include <rclcpp/rclcpp.hpp>
25#include <rclcpp_action/rclcpp_action.hpp>
26
27#include "yasmin/blackboard.hpp"
28#include "yasmin/logs.hpp"
29#include "yasmin/state.hpp"
30#include "yasmin/types.hpp"
34
35using namespace std::placeholders;
36
37namespace yasmin_ros {
38
48template <typename ActionT> class ActionState : public yasmin::State {
50 using Goal = typename ActionT::Goal;
52 using Result = typename ActionT::Result::SharedPtr;
53
55 using Feedback = typename ActionT::Feedback;
58 typename rclcpp_action::Client<ActionT>::SendGoalOptions;
60 using ActionClient = typename rclcpp_action::Client<ActionT>::SharedPtr;
62 using GoalHandle = rclcpp_action::ClientGoalHandle<ActionT>;
67 std::function<std::string(yasmin::Blackboard::SharedPtr, Result)>;
70 std::shared_ptr<const Feedback>)>;
71
72public:
77
78
97 ActionState(const std::string &action_name,
99 const yasmin::Outcomes &outcomes, int wait_timeout = -1,
100 int response_timeout = -1, int maximum_retry = 3)
102 nullptr, nullptr, nullptr, wait_timeout, response_timeout,
103 maximum_retry) {}
104
125 ActionState(const std::string &action_name,
128 rclcpp::CallbackGroup::SharedPtr callback_group = nullptr,
129 int wait_timeout = -1, int response_timeout = -1,
130 int maximum_retry = 3)
132 nullptr, nullptr, callback_group, wait_timeout,
134
163
195
220 ActionState(const rclcpp::Node::SharedPtr &node,
221 const std::string &action_name,
226 rclcpp::CallbackGroup::SharedPtr callback_group = nullptr,
227 int wait_timeout = -1, int response_timeout = -1,
228 int maximum_retry = 3)
235
237 "The action succeeded");
239 "The action was aborted");
241 "The action was canceled");
242
243 if (this->wait_timeout > 0 || this->response_timeout > 0) {
244 this->outcomes.insert(basic_outcomes::TIMEOUT);
247 "The action server was not available in time");
248 }
249
250 if (outcomes.size() > 0) {
251 for (const std::string &outcome : outcomes) {
252 this->outcomes.insert(outcome);
253 }
254 }
255
256 if (node == nullptr) {
258 } else {
259 this->node_ = node;
260 }
261
263 this->node_, action_name, callback_group);
264
265 if (this->create_goal_handler == nullptr) {
266 throw std::invalid_argument("create_goal_handler is needed");
267 }
268 }
269
276 void cancel_state() override {
277
278 bool waiting_for_cancel = false;
279
280 {
281 std::lock_guard<std::mutex> lock(this->goal_handle_mutex);
282
283 if (this->goal_handle) {
284 auto future_cancel = this->action_client->async_cancel_goal(
285 this->goal_handle, std::bind(&ActionState::cancel_done, this));
286 waiting_for_cancel = true;
287 }
288 }
289
290 // Wait for cancellation to complete outside of the goal_handle_mutex lock
291 if (waiting_for_cancel) {
292 std::unique_lock<std::mutex> cancel_lock(this->action_cancel_mutex);
293 this->action_cancel_cond.wait(cancel_lock);
294 }
295
296 // Wake up the execute() method if it's waiting
298 }
299
306 void cancel_done() { this->action_cancel_cond.notify_all(); }
307
324 std::string execute(yasmin::Blackboard::SharedPtr blackboard) override {
325
326 std::unique_lock<std::mutex> lock(this->action_done_mutex);
327 int retry_count = 0;
328
329 Goal goal = this->create_goal_handler(blackboard);
330
331 // Wait for the action server to be available
332 YASMIN_LOG_INFO("Waiting for action '%s'", this->action_name.c_str());
333
334 while (!this->action_client->wait_for_action_server(
335 std::chrono::duration<int64_t, std::ratio<1>>(this->wait_timeout))) {
336
337 if (this->is_canceled()) {
339 }
340
341 YASMIN_LOG_WARN("Timeout reached, action '%s' is not available",
342 this->action_name.c_str());
343 if (retry_count < this->maximum_retry) {
344 retry_count++;
345 YASMIN_LOG_WARN("Retrying to connect to action '%s' "
346 "(%d/%d)",
347 this->action_name.c_str(), retry_count,
348 this->maximum_retry);
349 } else {
351 }
352 }
353
354 if (this->is_canceled()) {
356 }
357
358 // Prepare options for sending the goal
359 SendGoalOptions send_goal_options;
360 send_goal_options.goal_response_callback =
361 std::bind(&ActionState::goal_response_callback, this, _1);
362 send_goal_options.result_callback =
363 std::bind(&ActionState::result_callback, this, _1);
364
365 if (this->feedback_handler) {
366 send_goal_options.feedback_callback =
367 [this, blackboard](typename GoalHandle::SharedPtr,
368 std::shared_ptr<const Feedback> feedback) {
369 this->feedback_handler(blackboard, feedback);
370 };
371 }
372
373 // Send the goal to the action server
374 YASMIN_LOG_INFO("Sending goal to action '%s'", this->action_name.c_str());
375 this->action_client->async_send_goal(goal, send_goal_options);
376
377 if (this->response_timeout > 0) {
378 while (this->action_done_cond.wait_for(
379 lock, std::chrono::seconds(this->response_timeout)) ==
380 std::cv_status::timeout) {
381
382 if (this->is_canceled()) {
384 }
385
387 "Timeout reached while waiting for response from action '%s'",
388 this->action_name.c_str());
389 if (retry_count < this->maximum_retry) {
390 retry_count++;
391 YASMIN_LOG_WARN("Retrying to wait for action '%s' response (%d/%d)",
392 this->action_name.c_str(), retry_count,
393 this->maximum_retry);
394 } else {
396 }
397 }
398
399 } else {
400 this->action_done_cond.wait(lock);
401 }
402
403 if (this->is_canceled()) {
405 }
406
407 switch (this->action_status) {
408 case rclcpp_action::ResultCode::CANCELED:
410
411 case rclcpp_action::ResultCode::ABORTED:
413
414 case rclcpp_action::ResultCode::SUCCEEDED:
415 if (this->result_handler) {
416 return this->result_handler(blackboard, this->action_result);
417 }
419
420 default:
422 }
423 }
424
425protected:
427 rclcpp::Node::SharedPtr node_;
428
429private:
431 std::string action_name;
434
436 std::condition_variable action_done_cond;
440 std::condition_variable action_cancel_cond;
443
447 rclcpp_action::ResultCode action_status;
448
450 std::shared_ptr<GoalHandle> goal_handle;
453
460
467
468#if __has_include("rclcpp/version.h")
469#include "rclcpp/version.h"
470#if RCLCPP_VERSION_GTE(2, 4, 3) // Greater or equal to latest Foxy
478 void
479 goal_response_callback(const typename GoalHandle::SharedPtr &goal_handle) {
480 std::lock_guard<std::mutex> lock(this->goal_handle_mutex);
481 this->goal_handle = goal_handle;
482 }
483#else
492 std::shared_future<typename GoalHandle::SharedPtr> future) {
493 std::lock_guard<std::mutex> lock(this->goal_handle_mutex);
494 this->goal_handle = future.get();
495 }
496#endif
497#else
506 std::shared_future<typename GoalHandle::SharedPtr> future) {
507 std::lock_guard<std::mutex> lock(this->goal_handle_mutex);
508 this->goal_handle = future.get();
509 }
510#endif
511
519 void result_callback(const typename GoalHandle::WrappedResult &result) {
520 this->action_result = result.result;
521 this->action_status = result.code;
522 this->action_done_cond.notify_one();
523 }
524};
525
526} // namespace yasmin_ros
527
528#endif // YASMIN_ROS__ACTION_STATE_HPP_
std::shared_ptr< Blackboard > SharedPtr
Shared pointer type for Blackboard.
Definition blackboard.hpp:100
Represents a state in a state machine.
Definition state.hpp:49
Outcomes outcomes
The possible outcomes of this state.
Definition state.hpp:53
State(const Outcomes &outcomes)
Shared pointer type for State.
Definition state.cpp:64
bool is_canceled() const noexcept
Checks if the state has been canceled.
Definition state.cpp:89
virtual void cancel_state()
Cancels the current state execution.
Definition state.hpp:172
void set_outcome_description(const std::string &outcome, const std::string &description)
Sets a human-readable description for an outcome.
Definition state.cpp:164
void result_callback(const typename GoalHandle::WrappedResult &result)
Callback for handling the result of the action.
Definition action_state.hpp:519
int wait_timeout
Maximum time to wait for the action server.
Definition action_state.hpp:462
std::condition_variable action_done_cond
Condition variable for action completion.
Definition action_state.hpp:436
std::mutex action_cancel_mutex
Mutex for protecting action cancellation.
Definition action_state.hpp:442
rclcpp_action::ClientGoalHandle< ActionT > GoalHandle
Handle for the action goal.
Definition action_state.hpp:62
ActionState(const std::string &action_name, CreateGoalHandler create_goal_handler, const yasmin::Outcomes &outcomes, rclcpp::CallbackGroup::SharedPtr callback_group=nullptr, int wait_timeout=-1, int response_timeout=-1, int maximum_retry=3)
Construct an ActionState with a specific action name and goal handler.
Definition action_state.hpp:125
void cancel_done()
Notify that the action cancellation has completed.
Definition action_state.hpp:306
Result action_result
Shared pointer to the action result.
Definition action_state.hpp:445
typename rclcpp_action::Client< ActionT >::SendGoalOptions SendGoalOptions
Options for sending goals.
Definition action_state.hpp:57
std::string execute(yasmin::Blackboard::SharedPtr blackboard) override
Execute the action and return the outcome.
Definition action_state.hpp:324
void cancel_state() override
Cancel the current action state.
Definition action_state.hpp:276
std::condition_variable action_cancel_cond
Condition variable for action cancellation.
Definition action_state.hpp:440
typename ActionT::Feedback Feedback
Alias for the action feedback type.
Definition action_state.hpp:55
std::function< void(yasmin::Blackboard::SharedPtr, std::shared_ptr< const Feedback >)> FeedbackHandler
Function type for handling feedback.
Definition action_state.hpp:69
int response_timeout
Timeout for the action response.
Definition action_state.hpp:464
ActionState(const std::string &action_name, CreateGoalHandler create_goal_handler, const yasmin::Outcomes &outcomes, ResultHandler result_handler=nullptr, FeedbackHandler feedback_handler=nullptr, int wait_timeout=-1, int response_timeout=-1, int maximum_retry=3)
Construct an ActionState with outcomes and handlers.
Definition action_state.hpp:186
int maximum_retry
Maximum number of retries.
Definition action_state.hpp:466
ActionClient action_client
Shared pointer to the action client.
Definition action_state.hpp:433
typename rclcpp_action::Client< ActionT >::SharedPtr ActionClient
Shared pointer type for the action client.
Definition action_state.hpp:60
FeedbackHandler feedback_handler
Handler function for processing feedback.
Definition action_state.hpp:459
ActionState(const std::string &action_name, CreateGoalHandler create_goal_handler, ResultHandler result_handler=nullptr, FeedbackHandler feedback_handler=nullptr, int wait_timeout=-1, int response_timeout=-1, int maximum_retry=3)
Construct an ActionState with result and feedback handlers.
Definition action_state.hpp:155
typename ActionT::Goal Goal
Alias for the action goal type.
Definition action_state.hpp:50
rclcpp_action::ResultCode action_status
Status of the action execution.
Definition action_state.hpp:447
std::mutex goal_handle_mutex
Mutex for protecting access to the goal handle.
Definition action_state.hpp:452
std::shared_ptr< GoalHandle > goal_handle
Handle for the current goal.
Definition action_state.hpp:450
ActionState(const rclcpp::Node::SharedPtr &node, const std::string &action_name, CreateGoalHandler create_goal_handler, const yasmin::Outcomes &outcomes, ResultHandler result_handler=nullptr, FeedbackHandler feedback_handler=nullptr, rclcpp::CallbackGroup::SharedPtr callback_group=nullptr, int wait_timeout=-1, int response_timeout=-1, int maximum_retry=3)
Construct an ActionState with a specified node and action name.
Definition action_state.hpp:220
typename ActionT::Result::SharedPtr Result
Alias for the action result type.
Definition action_state.hpp:52
CreateGoalHandler create_goal_handler
Handler function for creating goals.
Definition action_state.hpp:455
rclcpp::Node::SharedPtr node_
Shared pointer to the ROS 2 node.
Definition action_state.hpp:427
void goal_response_callback(std::shared_future< typename GoalHandle::SharedPtr > future)
Callback for handling the goal response.
Definition action_state.hpp:505
std::mutex action_done_mutex
Mutex for protecting action completion.
Definition action_state.hpp:438
ActionState(const std::string &action_name, CreateGoalHandler create_goal_handler, const yasmin::Outcomes &outcomes, int wait_timeout=-1, int response_timeout=-1, int maximum_retry=3)
Shared pointer type for ActionState.
Definition action_state.hpp:97
std::string action_name
Name of the action to communicate with.
Definition action_state.hpp:431
std::function< Goal(yasmin::Blackboard::SharedPtr)> CreateGoalHandler
Function type for creating a goal.
Definition action_state.hpp:64
ResultHandler result_handler
Handler function for processing results.
Definition action_state.hpp:457
std::function< std::string(yasmin::Blackboard::SharedPtr, Result)> ResultHandler
Function type for handling results.
Definition action_state.hpp:66
static rclcpp_action::Client< ActionT >::SharedPtr get_or_create_action_client(rclcpp::Node::SharedPtr node, const std::string &action_name, rclcpp::CallbackGroup::SharedPtr callback_group=nullptr)
Get an existing action client from the cache or create a new one.
Definition ros_clients_cache.hpp:66
static YasminNode::SharedPtr get_instance()
Provides access to the singleton instance of YasminNode.
Definition yasmin_node.cpp:58
#define YASMIN_LOG_INFO(text,...)
Definition logs.hpp:169
#define YASMIN_LOG_WARN(text,...)
Definition logs.hpp:164
constexpr char SUCCEED[]
Constant representing a successful action outcome.
Definition basic_outcomes.hpp:39
constexpr char CANCEL[]
Constant representing a canceled action outcome.
Definition basic_outcomes.hpp:63
constexpr char TIMEOUT[]
Constant representing a timed-out action outcome.
Definition basic_outcomes.hpp:71
constexpr char ABORT[]
Constant representing an aborted action outcome.
Definition basic_outcomes.hpp:47
Definition action_state.hpp:37
Definition blackboard.hpp:34
StringSet Outcomes
Set of possible outcomes for states.
Definition types.hpp:77
#define YASMIN_PTR_ALIASES(ClassName)
Macro to define all pointer aliases for a class.
Definition types.hpp:52