SMACC2
smacc_action_client_base.hpp
Go to the documentation of this file.
1// Copyright 2021 RobosoftAI Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15/*****************************************************************************************************************
16 *
17 * Authors: Pablo Inigo Blasco, Brett Aldrich
18 *
19 ******************************************************************************************************************/
20
21#pragma once
22
26
27#include <optional>
28#include <rclcpp_action/rclcpp_action.hpp>
29
30namespace smacc2
31{
32namespace client_bases
33{
34using namespace smacc2::default_events;
35
36template <typename ActionType>
38{
39public:
40 // Inside this macro you can find the typedefs for Goal and other types
41 // ACTION_DEFINITION(ActionType);
42 typedef rclcpp_action::Client<ActionType> ActionClient;
43 // typedef actionlib::SimpleActionClient<ActionType> GoalHandle;
44
45 // typedef typename ActionClient::SimpleDoneCallback SimpleDoneCallback;
46 // typedef typename ActionClient::SimpleActiveCallback SimpleActiveCallback;
47 // typedef typename ActionClient::SimpleFeedbackCallback SimpleFeedbackCallback;
48
49 using Goal = typename ActionClient::Goal;
50 using Feedback = typename ActionClient::Feedback;
51 using GoalHandle = rclcpp_action::ClientGoalHandle<ActionType>;
53
54 using SendGoalOptions = typename ActionClient::SendGoalOptions;
56 std::function<void(std::shared_future<typename GoalHandle::SharedPtr>)>;
57 using FeedbackCallback = typename GoalHandle::FeedbackCallback;
58 using ResultCallback = typename GoalHandle::ResultCallback;
59 using CancelRequest = typename ActionType::Impl::CancelGoalService::Request;
60 using CancelResponse = typename ActionType::Impl::CancelGoalService::Response;
61 using CancelCallback = std::function<void(typename CancelResponse::SharedPtr)>;
62
63 std::string action_endpoint_;
64 SmaccActionClientBase(std::string actionServerName) : ISmaccActionClient()
65 {
66 action_endpoint_ = actionServerName;
67 }
68
70
72
73 virtual std::shared_ptr<rclcpp_action::ClientBase> getClientBase() override { return client_; }
74
75 void onInitialize() override
76 {
77 if (name_ == "") name_ = smacc2::demangleSymbol(typeid(*this).name());
78 this->client_ = rclcpp_action::create_client<ActionType>(getNode(), action_endpoint_);
79 // RCLCPP_INFO_STREAM(
80 // this->getLogger(),
81 // "Waiting for action server '" << name_ << "' of type: " << demangledTypeName<ActionType>());
82 //client_->wait_for_action_server();
83 }
84
85 static std::string getEventLabel()
86 {
87 auto type = TypeInfo::getTypeInfoFromType<ActionType>();
88 return type->getNonTemplatedTypeName();
89 }
90
91 std::optional<std::shared_future<typename GoalHandle::SharedPtr>> lastRequest_;
92 typename GoalHandle::SharedPtr goalHandle_;
93
96 // smacc2::SmaccSignal<void(const WrappedResult &)> onPreempted_;
97 // smacc2::SmaccSignal<void(const WrappedResult &)> onRejected_;
99
100 // event creation/posting factory functions
101 std::function<void(WrappedResult)> postSuccessEvent;
102 std::function<void(WrappedResult)> postAbortedEvent;
103 // std::function<void(WrappedResult)> postPreemptedEvent;
104 // std::function<void(WrappedResult)> postRejectedEvent;
105 std::function<void(WrappedResult)> postCancelledEvent;
106
107 std::function<void(const Feedback &)> postFeedbackEvent;
108
110 // SimpleActiveCallback active_cb;
112
113 template <typename EvType>
115 {
116 auto * ev = new EvType();
117 // ev->client = this;
118 // ev->resultMessage = *result;
119 RCLCPP_INFO(
120 getLogger(), "Action client Posting EVENT %s", demangleSymbol(typeid(ev).name()).c_str());
121 this->postEvent(ev);
122 }
123
124 template <typename TOrthogonal, typename TSourceObject>
126 {
127 // we create here all the event factory functions capturing the TOrthogonal
128 postSuccessEvent = [this](auto msg) {
129 this->postResultEvent<EvActionSucceeded<TSourceObject, TOrthogonal>>(msg);
130 };
131 postAbortedEvent = [this](auto msg) {
132 this->postResultEvent<EvActionAborted<TSourceObject, TOrthogonal>>(msg);
133 };
134
135 postCancelledEvent = [this](auto msg) {
136 this->postResultEvent<EvActionCancelled<TSourceObject, TOrthogonal>>(msg);
137 };
138 postFeedbackEvent = [this](auto msg) {
139 auto actionFeedbackEvent = new EvActionFeedback<Feedback, TOrthogonal>();
140 actionFeedbackEvent->client = this;
141 actionFeedbackEvent->feedbackMessage = msg;
142 this->postEvent(actionFeedbackEvent);
143 RCLCPP_DEBUG(getLogger(), "[%s] FEEDBACK EVENT", demangleType(typeid(*this)).c_str());
144 };
145
146 done_cb = [this](auto r) { this->onResult(r); };
147 // done_cb = boost::bind(&SmaccActionClientBase<ActionType>::onResult, this, _1, _2);
148 // active_cb;
149 feedback_cb = [this](auto client, auto feedback) { this->onFeedback(client, feedback); };
150 }
151
152 template <typename T>
153 boost::signals2::connection onSucceeded(void (T::*callback)(WrappedResult &), T * object)
154 {
155 return this->getStateMachine()->createSignalConnection(onSucceeded_, callback, object);
156 }
157
158 template <typename T>
159 boost::signals2::connection onSucceeded(std::function<void(WrappedResult &)> callback)
160 {
161 return this->getStateMachine()->createSignalConnection(onSucceeded_, callback);
162 }
163
164 template <typename T>
165 boost::signals2::connection onAborted(void (T::*callback)(WrappedResult &), T * object)
166 {
167 return this->getStateMachine()->createSignalConnection(onAborted_, callback, object);
168 }
169
170 template <typename T>
171 boost::signals2::connection onAborted(std::function<void(WrappedResult &)> callback)
172 {
173 return this->getStateMachine()->createSignalConnection(onAborted_, callback);
174 }
175
176 template <typename T>
177 boost::signals2::connection onCancelled(void (T::*callback)(WrappedResult &), T * object)
178 {
179 return this->getStateMachine()->createSignalConnection(onCancelled_, callback, object);
180 }
181
182 template <typename T>
183 boost::signals2::connection onCancelled(std::function<void(WrappedResult &)> callback)
184 {
185 return this->getStateMachine()->createSignalConnection(onCancelled_, callback);
186 }
187
188 /*
189 template <typename T>
190 boost::signals2::connection onPreempted(void (T::*callback)(WrappedResult &), T *object)
191 {
192 return this->getStateMachine()->createSignalConnection(onPreempted_, callback, object);
193 }
194
195 template <typename T>
196 boost::signals2::connection onPreempted(std::function<void(WrappedResult &)> callback)
197 {
198 return this->getStateMachine()->createSignalConnection(onPreempted_, callback);
199 }
200
201 template <typename T>
202 boost::signals2::connection onRejected(void (T::*callback)(WrappedResult &), T *object)
203 {
204 return this->getStateMachine()->createSignalConnection(onRejected_, callback, object);
205 }
206
207 template <typename T>
208 boost::signals2::connection onRejected(std::function<void(WrappedResult &)> callback)
209 {
210 return this->getStateMachine()->createSignalConnection(onRejected_, callback);
211 }
212 */
213
214 virtual bool cancelGoal() override
215 {
216 if (lastRequest_ && lastRequest_->valid())
217 {
218 rclcpp::spin_until_future_complete(getNode(), *lastRequest_);
219 auto req = lastRequest_->get();
220 RCLCPP_INFO_STREAM(
221 getLogger(), "[" << getName() << "] Cancelling goal. req id: "
222 << rclcpp_action::to_string(req->get_goal_id()));
223 auto cancelresult = client_->async_cancel_goal(req);
224
225 // wait actively
226 rclcpp::spin_until_future_complete(getNode(), cancelresult);
227 //lastRequest_.reset();
228 return true;
229 }
230 else
231 {
232 RCLCPP_ERROR(
233 getLogger(), "%s [at %s]: not connected with actionserver, skipping cancel goal ...",
234 getName().c_str(), getNamespace().c_str());
235 return false;
236 }
237 }
238
239 std::shared_future<typename GoalHandle::SharedPtr> sendGoal(Goal & goal)
240 {
241 // client_->sendGoal(goal, done_cb, active_cb, feedback_cb);
242 // std::shared_future<typename GoalHandle::SharedPtr>
243
244 SendGoalOptions options;
245
246 // GoalResponseCallback
247 // options.goal_response_callback;
248
250 // FeedbackCallback
251 options.feedback_callback = feedback_cb;
252
254 // ResultCallback result_callback;
255 // options.result_callback = done_cb;
256
257 options.result_callback =
258 [this](const typename rclcpp_action::ClientGoalHandle<ActionType>::WrappedResult & result) {
259 // TODO(#1652): a work around until rcl_action interface is updated
260 // if goal ids are not matched, the older goal call this callback so ignore the result
261 // if matched, it must be processed (including aborted)
262 RCLCPP_INFO_STREAM(getLogger(), getName() << ": Result callback, getting shared future");
263 goalHandle_ = lastRequest_->get();
264 RCLCPP_INFO_STREAM(getLogger(), getName() << ": Result CB Check goal id");
265 if (this->goalHandle_->get_goal_id() == result.goal_id)
266 {
267 // goal_result_available_ = true;
268 // result_ = result;
269 RCLCPP_INFO_STREAM(getLogger(), getName() << ": Result CB Goal id matches");
270 done_cb(result);
271 }
272 else
273 {
274 RCLCPP_INFO_STREAM(getLogger(), getName() << ": Result CB Goal id DOES NOT match");
275 }
276 };
277
278 // if (lastRequest_ && lastRequest_->valid())
279 // {
280 // RCLCPP_INFO_STREAM(getLogger(), getName() << ": checking previous request is really finished.");
281 // auto res = this->lastRequest_->get();
282 // RCLCPP_INFO_STREAM(getLogger(), getName() << ": okay");
283 // }
284 // else
285 // {
286 // RCLCPP_INFO_STREAM(getLogger(), getName() << ": no previous request.");
287 // }
288
289 RCLCPP_INFO_STREAM(getLogger(), getName() << ": async send goal.");
290 this->lastRequest_ = this->client_->async_send_goal(goal, options);
291
292 RCLCPP_INFO_STREAM(
293 getLogger(), "[" << getName() << "] Action goal sent to " << this->action_endpoint_
294 << "\": " << std::endl
295 << goal);
296
297 // if (client_->isServerConnected())
298 // {
299 // RCLCPP_INFO_STREAM(getLogger(), getName() << ": Goal sent:" << goal);
300
301 // RCLCPP_INFO_STREAM(getLogger(), getName() << ": Goal Id: " <<
302 // rclcpp_action::to_string(lastRequest_->get()->get_goal_id()));
303
304 RCLCPP_INFO_STREAM(
305 getLogger(), "[" << getName() << "] client ready clients: "
306 << this->client_->get_number_of_ready_clients());
307 RCLCPP_INFO_STREAM(
308 getLogger(),
309 "[" << getName() << "] Waiting it is ready? " << client_->action_server_is_ready());
310
311 // for (auto& gh: this->goal_handles_)
312 // {
313
314 // }
315
316 // RCLCPP_INFO_STREAM(getLogger(), getName() << ": spinning until completed");
317 // if (rclcpp::spin_until_future_complete(this->getNode(), lastRequest_, std::chrono::seconds(2))
318 // !=rclcpp::executor::FutureReturnCode::SUCCESS)
319 // {
320 // throw std::runtime_error("send_goal failed");
321 // }
322
323 // goalHandle_ = lastRequest_->get();
324 // if (!goalHandle_) {
325 // throw std::runtime_error("Goal was rejected by the action server");
326 // }
327
328 // }
329 // else
330 // {
331 // RCLCPP_ERROR(getLogger(),"%s [at %s]: not connected with actionserver, skipping goal request
332 // ...", getName().c_str(), getNamespace().c_str());
333 // //client_->waitForServer();
334 // }
335
336 return *lastRequest_;
337 }
338
339protected:
340 typename ActionClient::SharedPtr client_;
341
343 typename GoalHandle::SharedPtr /*goalhandle*/,
344 const std::shared_ptr<const Feedback> feedback_msg)
345 {
346 postFeedbackEvent(*feedback_msg);
347 }
348
349 void onResult(const WrappedResult & result_msg)
350 {
351 // auto *actionResultEvent = new EvActionResult<TDerived>();
352 // actionResultEvent->client = this;
353 // actionResultEvent->resultMessage = *result_msg;
354
355 // const auto &resultType = this->getState();
356 const auto & resultType = result_msg.code;
357
358 RCLCPP_INFO_STREAM(
359 getLogger(), "[" << this->getName() << "] request result of request ["
360 << rclcpp_action::to_string(result_msg.goal_id) << "]: " << (int)resultType);
361
362 if (resultType == rclcpp_action::ResultCode::SUCCEEDED)
363 {
364 RCLCPP_INFO(getLogger(), "[%s] request result: Success", this->getName().c_str());
365 onSucceeded_(result_msg);
366 postSuccessEvent(result_msg);
367 }
368 else if (resultType == rclcpp_action::ResultCode::ABORTED)
369 {
370 RCLCPP_INFO(getLogger(), "[%s] request result: Aborted", this->getName().c_str());
371 onAborted_(result_msg);
372 postAbortedEvent(result_msg);
373 }
374 else if (resultType == rclcpp_action::ResultCode::CANCELED)
375 {
376 RCLCPP_INFO(getLogger(), "[%s] request result: Cancelled", this->getName().c_str());
377 onCancelled_(result_msg);
378 postCancelledEvent(result_msg);
379 }
380 /*
381 else if (resultType == actionlib::SimpleClientGoalState::REJECTED)
382 {
383 RCLCPP_INFO(getLogger(),"[%s] request result: Rejected", this->getName().c_str());
384 onRejected_(result_msg);
385 postRejectedEvent(result_msg);
386 }
387 else if (resultType == actionlib::SimpleClientGoalState::PREEMPTED)
388 {
389 RCLCPP_INFO(getLogger(),"[%s] request result: Preempted", this->getName().c_str());
390 onPreempted_(result_msg);
391 postPreemptedEvent(result_msg);
392 }*/
393 else
394 {
395 RCLCPP_INFO(
396 getLogger(), "[%s] request result: NOT HANDLED TYPE: %d", this->getName().c_str(),
397 (int)resultType);
398 }
399 }
400};
401
402} // namespace client_bases
403
404} // namespace smacc2
rclcpp::Node::SharedPtr getNode()
Definition: client.cpp:60
ISmaccStateMachine * getStateMachine()
rclcpp::Logger getLogger()
boost::signals2::connection createSignalConnection(TSmaccSignal &signal, TMemberFunctionPrototype callback, TSmaccObjectType *object)
typename ActionType::Impl::CancelGoalService::Response CancelResponse
typename ActionType::Impl::CancelGoalService::Request CancelRequest
boost::signals2::connection onAborted(void(T::*callback)(WrappedResult &), T *object)
std::function< void(std::shared_future< typename GoalHandle::SharedPtr >)> GoalResponseCallback
std::function< void(typename CancelResponse::SharedPtr)> CancelCallback
typename ActionClient::SendGoalOptions SendGoalOptions
std::shared_future< typename GoalHandle::SharedPtr > sendGoal(Goal &goal)
void onFeedback(typename GoalHandle::SharedPtr, const std::shared_ptr< const Feedback > feedback_msg)
virtual std::shared_ptr< rclcpp_action::ClientBase > getClientBase() override
rclcpp_action::Client< ActionType > ActionClient
std::function< void(WrappedResult)> postCancelledEvent
smacc2::SmaccSignal< void(const WrappedResult &)> onSucceeded_
std::function< void(WrappedResult)> postSuccessEvent
boost::signals2::connection onSucceeded(std::function< void(WrappedResult &)> callback)
boost::signals2::connection onSucceeded(void(T::*callback)(WrappedResult &), T *object)
smacc2::SmaccSignal< void(const WrappedResult &)> onAborted_
boost::signals2::connection onCancelled(std::function< void(WrappedResult &)> callback)
rclcpp_action::ClientGoalHandle< ActionType > GoalHandle
std::function< void(WrappedResult)> postAbortedEvent
void onResult(const WrappedResult &result_msg)
std::function< void(const Feedback &)> postFeedbackEvent
smacc2::SmaccSignal< void(const WrappedResult &)> onCancelled_
boost::signals2::connection onCancelled(void(T::*callback)(WrappedResult &), T *object)
typename GoalHandle::ResultCallback ResultCallback
boost::signals2::connection onAborted(std::function< void(WrappedResult &)> callback)
std::optional< std::shared_future< typename GoalHandle::SharedPtr > > lastRequest_
typename GoalHandle::FeedbackCallback FeedbackCallback
Base::WrappedResult WrappedResult
std::string demangleType(const std::type_info *tinfo)
std::string demangleSymbol(const std::string &name)
void callback(const image_tools::ROSCvMatContainer &img)