Capabilities2 0.3.0
Loading...
Searching...
No Matches
action_runner.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <string>
4#include <iostream>
5#include <sstream>
6#include <vector>
7#include <map>
8#include <memory>
9#include <mutex>
10#include <condition_variable>
11
12#include <rclcpp/rclcpp.hpp>
13#include <rclcpp_action/rclcpp_action.hpp>
14#include <action_msgs/srv/cancel_goal.hpp>
15
17
19{
20
26template <typename ActionT>
28{
29public:
36
44 virtual void init_action(rclcpp::Node::SharedPtr node, const runner_opts& run_config, const std::string& action_name)
45 {
46 // initialize the runner base by storing node pointer and run config
47 init_base(node, run_config);
48
49 // create an action client
50 action_client_ = rclcpp_action::create_client<ActionT>(node_, action_name);
51
52 // wait for action server
53 RCLCPP_INFO(node_->get_logger(), "waiting for action: %s", action_name.c_str());
54
55 if (!action_client_->wait_for_action_server(std::chrono::seconds(1000)))
56 {
57 RCLCPP_ERROR(node_->get_logger(), "failed to connect to action: %s", action_name.c_str());
58 throw runner_exception("failed to connect to action server");
59 }
60
61 RCLCPP_INFO(node_->get_logger(), "connected with action: %s", action_name.c_str());
62 }
63
68 virtual void stop(const std::string& bond_id, const std::string& instance_id = "") override
69 {
70 // if the node pointer is empty then throw an error
71 // this means that the runner was not started and is being used out of order
72
73 if (!node_)
74 throw runner_exception("cannot stop runner that was not started");
75
76 // throw an error if the action client is null
77 // this can happen if the runner is not able to find the action resource
78
79 if (!action_client_)
80 throw runner_exception("cannot stop runner action that was not started");
81
82 // stop runner using action client
83 if (goal_handle_)
84 {
85 try
86 {
87 auto cancel_future = action_client_->async_cancel_goal(
88 goal_handle_, [this, &bond_id, &instance_id](action_msgs::srv::CancelGoal_Response::SharedPtr response) {
89 if (response->return_code != action_msgs::srv::CancelGoal_Response::ERROR_NONE)
90 {
91 // throw runner_exception("failed to cancel runner");
92 RCLCPP_ERROR(node_->get_logger(), "Runner cancellation failed.");
93 }
94
95 // emit stopped event
96 emit_stopped(bond_id, instance_id, param_on_stopped());
97 });
98
99 // wait for action to be stopped. hold the thread for 2 seconds to help keep callbacks in scope
100 // BUG: the line below does not work in jazzy build, so a workaround is used
101 auto timeout = std::chrono::steady_clock::now() + std::chrono::seconds(2);
102 while (std::chrono::steady_clock::now() < timeout)
103 {
104 // Check if the cancel operation is complete
105 if (cancel_future.wait_for(std::chrono::milliseconds(100)) == std::future_status::ready)
106 break;
107 }
108 }
109 catch (const rclcpp_action::exceptions::UnknownGoalHandleError& e)
110 {
111 RCLCPP_ERROR(node_->get_logger(), "failed to cancel goal: %s", e.what());
112 throw runner_exception(e.what());
113 }
114 }
115
116 RCLCPP_INFO(node_->get_logger(), "runner cleaned. stopping..");
117 }
118
119protected:
127 virtual void execution(capabilities2_events::EventParameters parameters, const std::string& thread_id) override
128 {
129 // split thread_id to get bond_id and instance_id (format: "bond_id/instance_id")
130 std::string bond_id = ThreadTriggerRunner::bond_from_thread_id(thread_id);
131 std::string instance_id = ThreadTriggerRunner::instance_from_thread_id(thread_id);
132
133 // generate a goal from parameters provided
134 goal_msg_ = generate_goal(parameters);
135 RCLCPP_INFO(node_->get_logger(), "goal generated for instance %s", instance_id.c_str());
136
137 std::mutex block_mutex;
138 std::condition_variable cv;
139 bool completed = false;
140 std::unique_lock<std::mutex> lock(block_mutex);
141
142 // trigger the action client with goal
143 send_goal_options_.goal_response_callback =
144 [this, &instance_id](const typename rclcpp_action::ClientGoalHandle<ActionT>::SharedPtr& goal_handle) {
145 if (goal_handle)
146 {
147 RCLCPP_INFO(node_->get_logger(), "goal accepted. Waiting for result for instance %s", instance_id.c_str());
148 }
149 else
150 {
151 RCLCPP_ERROR(node_->get_logger(), "goal rejected for instance %s", instance_id.c_str());
152 }
153
154 // store goal handle to be used with stop funtion
155 goal_handle_ = goal_handle;
156 };
157
158 send_goal_options_.feedback_callback = [this, &instance_id](
159 typename rclcpp_action::ClientGoalHandle<ActionT>::SharedPtr goal_handle,
160 const typename ActionT::Feedback::ConstSharedPtr feedback_msg) {
161 std::string feedback = generate_feedback(feedback_msg);
162
163 if (feedback != "")
164 {
165 RCLCPP_INFO(node_->get_logger(), "received feedback: %s for instance %s", feedback.c_str(), instance_id.c_str());
166 }
167 };
168
169 send_goal_options_.result_callback =
170 [this, &instance_id, &completed, &cv,
171 &bond_id, &instance_id](const typename rclcpp_action::ClientGoalHandle<ActionT>::WrappedResult& wrapped_result) {
172 RCLCPP_INFO(node_->get_logger(), "received result for instance %s", instance_id.c_str());
173 if (wrapped_result.code == rclcpp_action::ResultCode::SUCCEEDED)
174 {
175 RCLCPP_INFO(node_->get_logger(), "action succeeded for instance %s", instance_id.c_str());
176 // emit success event
177 emit_succeeded(bond_id, instance_id, param_on_success());
178 }
179 else
180 {
181 RCLCPP_ERROR(node_->get_logger(), "action failed for instance %s", instance_id.c_str());
182
183 // emit failed event
184 emit_failed(bond_id, instance_id, param_on_failure());
185 }
186
187 result_ = wrapped_result.result;
188 completed = true;
189 cv.notify_all();
190 };
191
193 RCLCPP_INFO(node_->get_logger(), "goal sent. Waiting for acceptance for instance %s", instance_id.c_str());
194
195 // Conditional wait
196 cv.wait(lock, [&completed] { return completed; });
197 RCLCPP_INFO(node_->get_logger(), "action complete. Thread closing for instance %s", instance_id.c_str());
198 }
199
211 virtual typename ActionT::Goal generate_goal(capabilities2_events::EventParameters& parameters) = 0;
212
224 virtual std::string generate_feedback(const typename ActionT::Feedback::ConstSharedPtr msg) = 0;
225
226protected:
228 typename rclcpp_action::Client<ActionT>::SharedPtr action_client_;
229
232 typename rclcpp_action::Client<ActionT>::SendGoalOptions send_goal_options_;
233
235 typename rclcpp_action::ClientGoalHandle<ActionT>::SharedPtr goal_handle_;
236
238 typename rclcpp_action::ClientGoalHandle<ActionT>::WrappedResult wrapped_result_;
239
241 typename ActionT::Result::SharedPtr result_;
242
244 typename ActionT::Goal goal_msg_;
245
247 std::shared_future<typename rclcpp_action::ClientGoalHandle<ActionT>::SharedPtr> goal_handle_future_;
248
250 std::shared_future<typename rclcpp_action::ClientGoalHandle<ActionT>::WrappedResult> result_future_;
251};
252
253} // namespace capabilities2_runner
action runner base class
Definition action_runner.hpp:28
virtual void stop(const std::string &bond_id, const std::string &instance_id="") override
stop function to cease functionality and shutdown
Definition action_runner.hpp:68
rclcpp_action::Client< ActionT >::SharedPtr action_client_
Definition action_runner.hpp:228
rclcpp_action::ClientGoalHandle< ActionT >::WrappedResult wrapped_result_
Definition action_runner.hpp:238
ActionRunner()
Constructor which needs to be empty due to plugin semantics.
Definition action_runner.hpp:33
std::shared_future< typename rclcpp_action::ClientGoalHandle< ActionT >::WrappedResult > result_future_
Definition action_runner.hpp:250
virtual ActionT::Goal generate_goal(capabilities2_events::EventParameters &parameters)=0
Generate a goal from parameters.
ActionT::Goal goal_msg_
Definition action_runner.hpp:244
rclcpp_action::ClientGoalHandle< ActionT >::SharedPtr goal_handle_
Definition action_runner.hpp:235
virtual void execution(capabilities2_events::EventParameters parameters, const std::string &thread_id) override
Trigger process to be executed.
Definition action_runner.hpp:127
rclcpp_action::Client< ActionT >::SendGoalOptions send_goal_options_
Definition action_runner.hpp:232
virtual void init_action(rclcpp::Node::SharedPtr node, const runner_opts &run_config, const std::string &action_name)
Initializer function for initializing the action runner in place of constructor due to plugin semanti...
Definition action_runner.hpp:44
ActionT::Result::SharedPtr result_
Definition action_runner.hpp:241
std::shared_future< typename rclcpp_action::ClientGoalHandle< ActionT >::SharedPtr > goal_handle_future_
Definition action_runner.hpp:247
virtual std::string generate_feedback(const typename ActionT::Feedback::ConstSharedPtr msg)=0
Generate a std::string from feedback message.
virtual capabilities2_events::EventParameters param_on_success()
Update on_success event parameters with new data if available.
Definition runner_base.hpp:271
void emit_succeeded(const std::string &bond_id, const std::string &instance_id, capabilities2_events::EventParameters parameters=capabilities2_events::EventParameters())
emit SUCCEEDED event
Definition runner_base.hpp:444
void emit_failed(const std::string &bond_id, const std::string &instance_id, capabilities2_events::EventParameters parameters=capabilities2_events::EventParameters())
emit FAILED event
Definition runner_base.hpp:457
void init_base(rclcpp::Node::SharedPtr node, const runner_opts &run_config)
Initializer function for initializing the base runner in place of constructor due to plugin semantics...
Definition runner_base.hpp:126
virtual capabilities2_events::EventParameters param_on_stopped()
Update on_stopped event parameters with new data if available.
Definition runner_base.hpp:241
virtual capabilities2_events::EventParameters param_on_failure()
Update on_failure event parameters with new data if available.
Definition runner_base.hpp:256
rclcpp::Node::SharedPtr node_
shared pointer to the capabilities node Allows to use ros node related functionalities
Definition runner_base.hpp:469
void emit_stopped(const std::string &bond_id, const std::string &instance_id, capabilities2_events::EventParameters parameters=capabilities2_events::EventParameters())
emit STOPPED event
Definition runner_base.hpp:431
add threaded trigger execution to the runner
Definition threadtrigger_runner.hpp:21
static const std::string bond_from_thread_id(const std::string &thread_id)
helper function to extract bond_id from thread_id
Definition threadtrigger_runner.hpp:30
static const std::string instance_from_thread_id(const std::string &thread_id)
helper function to extract instance_id from thread_id
Definition threadtrigger_runner.hpp:44
Definition action_runner.hpp:19
capability options for a capability runner given by interface and provider
Definition event_parameters.hpp:167
runner exception
Definition runner_base.hpp:24
runner options
Definition runner_base.hpp:56