11#pragma once
22
3- #include < capabilities2_runner/runner_base.hpp>
3+ #include < thread>
4+
5+ #include < rclcpp/rclcpp.hpp>
46#include < rclcpp_action/rclcpp_action.hpp>
7+ #include < action_msgs/srv/cancel_goal.hpp>
8+
9+ #include < capabilities2_runner/runner_base.hpp>
510
611namespace capabilities2_runner
712{
@@ -10,71 +15,136 @@ namespace capabilities2_runner
1015 * @brief action runner base class
1116 *
1217 * Create an action client to run an action based capability
13- *
1418 */
1519template <typename ActionT>
1620class ActionRunner : public RunnerBase
1721{
1822public:
19- ActionRunner () : RunnerBase()
20- {
21- }
22-
23- // helpers
24- // init action base members
25- void init_action (rclcpp::Node::SharedPtr node, const runner_opts& opts, const std::string& action_type)
26- {
27- // store node pointer and opts
28- init_base (node, opts);
29-
30- // create an action client
31- action_client_ = rclcpp_action::create_client<ActionT>(node_, get_action_name_by_type (action_type));
32-
33- // wait for action server
34- RCLCPP_INFO (node_->get_logger (), " %s waiting for action: %s" , run_config_.interface .c_str (),
35- get_action_name_by_type (action_type).c_str ());
36-
37- if (!action_client_->wait_for_action_server ())
38- {
39- RCLCPP_ERROR (node_->get_logger (), " %s failed to connect to action server" , run_config_.interface .c_str ());
40- throw runner_exception (" failed to connect to action server" );
41- }
42- }
43-
44- // find resource name by action type
45- std::string get_action_name_by_type (const std::string& action_type)
46- {
47- for (const auto & resource : run_config_.resources )
48- {
49- if (resource.resource_type == " action" )
50- {
51- if (resource.msg_type == action_type)
52- {
53- return resource.name ;
54- }
55- }
56- }
57-
58- throw runner_exception (" no action resource found: " + action_type);
59- }
60-
61- // get first action resource
62- std::string get_first_action_name ()
63- {
64- for (const auto & resource : run_config_.resources )
65- {
66- if (resource.resource_type == " action" )
67- {
68- return resource.name ;
69- }
70- }
71-
72- throw runner_exception (" no action resources found for interface: " + run_config_.interface );
73- }
23+
24+ /* *
25+ * @brief Constructor which needs to be empty due to plugin semantics
26+ */
27+ ActionRunner () : RunnerBase()
28+ {}
29+
30+ /* *
31+ * @brief Initializer function for initializing the action runner in place of constructor due to plugin semantics
32+ *
33+ * @param node shared pointer to the capabilities node. Allows to use ros node related functionalities
34+ * @param run_config runner configuration loaded from the yaml file
35+ * @param action_name action name used in the yaml file, used to load specific configuration from the run_config
36+ * @param on_started function pointer to trigger at the start of the action client in the runner
37+ * @param on_terminated function pointer to trigger at the termination of the action client in the runner
38+ */
39+ virtual void init_action (rclcpp::Node::SharedPtr node, const runner_opts& run_config, const std::string& action_name,
40+ std::function<void (const std::string&)> on_started = nullptr,
41+ std::function<void(const std::string&)> on_terminated = nullptr)
42+ {
43+ // initialize the runner base by storing node pointer and run config
44+ init_base (node, run_config);
45+
46+ // initialize the used for message timestamps
47+ clock_ = node_->get_clock ();
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 (), " %s waiting for action: %s" , run_config_.interface .c_str (), action_name.c_str ());
54+
55+ if (!action_client_->wait_for_action_server (std::chrono::seconds (3 )))
56+ {
57+ RCLCPP_ERROR (node_->get_logger (), " %s failed to connect to action server" , run_config_.interface .c_str ());
58+ throw runner_exception (" failed to connect to action server" );
59+ }
60+
61+ // goal response callback
62+ send_goal_options_.goal_response_callback =
63+ [this , on_started, on_terminated](const typename rclcpp_action::ClientGoalHandle<ActionT>::SharedPtr& goal_handle)
64+ {
65+ // store goal handle to be used with stop funtion
66+ goal_handle_ = goal_handle;
67+ };
68+
69+ // result callback
70+ send_goal_options_.result_callback =
71+ [this , on_started, on_terminated](const typename rclcpp_action::ClientGoalHandle<ActionT>::WrappedResult& wrapped_result)
72+ {
73+ if (wrapped_result.code == rclcpp_action::ResultCode::SUCCEEDED)
74+ {
75+ // publish event
76+ if (on_started)
77+ {
78+ on_started (run_config_.interface );
79+ }
80+ } else
81+ {
82+ // send terminated event
83+ if (on_terminated)
84+ {
85+ on_terminated (run_config_.interface );
86+ }
87+ }
88+ };
89+ }
90+
91+ virtual void stop (std::function<void (const std::string&)> on_stopped = nullptr) override
92+ {
93+ // if the node pointer is empty then throw an error
94+ // this means that the runner was not started and is being used out of order
95+
96+ if (!node_) throw runner_exception (" cannot stop runner that was not started" );
97+
98+ // throw an error if the action client is null
99+ // this can happen if the runner is not able to find the action resource
100+
101+ if (!action_client_) throw runner_exception (" cannot stop runner action that was not started" );
102+
103+ // stop runner using action client
104+ if (goal_handle_)
105+ {
106+ try
107+ {
108+ auto cancel_future = action_client_->async_cancel_goal (
109+ goal_handle_,
110+ [this , on_stopped](action_msgs::srv::CancelGoal_Response::SharedPtr response)
111+ {
112+ if (response->return_code != action_msgs::srv::CancelGoal_Response::ERROR_NONE)
113+ {
114+ // throw runner_exception("failed to cancel runner");
115+ }
116+
117+ // publish event
118+ if (on_stopped)
119+ {
120+ on_stopped (run_config_.interface );
121+ }
122+ }
123+ );
124+
125+ // wait for action to be stopped. hold the thread for 2 seconds to help keep callbacks in scope
126+ rclcpp::spin_until_future_complete (node_, cancel_future, std::chrono::seconds (2 ));
127+ }
128+ catch (const rclcpp_action::exceptions::UnknownGoalHandleError& e)
129+ {
130+ throw runner_exception (e.what ());
131+ }
132+ }
133+ }
74134
75135protected:
76- // action client
77- std::shared_ptr<rclcpp_action::Client<ActionT>> action_client_;
136+
137+ /* *< action client */
138+ std::shared_ptr<rclcpp_action::Client<ActionT>> action_client_;
139+
140+ /* *< rclcpp clock to get time */
141+ rclcpp::Clock::SharedPtr clock_;
142+
143+ /* *< Send Goal Option struct to link result_callback, feedback_callback and goal_response_callback with action client */
144+ typename rclcpp_action::Client<ActionT>::SendGoalOptions send_goal_options_;
145+
146+ /* *< goal handle parameter to capture goal response from goal_response_callback */
147+ typename rclcpp_action::ClientGoalHandle<ActionT>::SharedPtr goal_handle_;
78148};
79149
80150} // namespace capabilities2_runner
0 commit comments