192 BT::NodeStatus tick() override final;
194 void halt() override final;
197 bool dynamic_client_instance_ = false;
198 std::shared_ptr<ServiceClientInstance> client_instance_;
199 typename ServiceClient::SharedFuture future_;
201 rclcpp::Time time_request_sent_;
202 BT::NodeStatus on_feedback_state_change_;
203 typename Response::SharedPtr response_;
210template <class ServiceT>
211inline
RosServiceNode<ServiceT>::ServiceClientInstance::ServiceClientInstance(
212 rclcpp::Node::SharedPtr node, rclcpp::CallbackGroup::SharedPtr group, const std::
string & service_name)
214 service_client = node->create_client<ServiceT>(service_name, rclcpp::ServicesQoS(), group);
218template <
class ServiceT>
220 const std::string & instance_name,
const Config & config, Context context)
221: RosActionNodeBase(instance_name, config, context)
226 if (
const BT::Expected<std::string> expected_name = context_.getTopicName(
this)) {
227 createClient(expected_name.value());
232 dynamic_client_instance_ = true;
236template <
class ServiceT>
237inline BT::NodeStatus RosServiceNode<ServiceT>::tick()
241 return BT::NodeStatus::FAILURE;
246 if (dynamic_client_instance_ && client_instance_) {
247 dynamic_client_instance_ =
false;
252 if (status() == BT::NodeStatus::IDLE && dynamic_client_instance_) {
253 const BT::Expected<std::string> expected_name = context_.getTopicName(
this);
255 createClient(expected_name.value());
257 throw exceptions::RosNodeError(
258 context_.getFullyQualifiedTreeNodeName(
this) +
259 " - Cannot create the service client because the service name couldn't be resolved using "
260 "the expression specified by the node's registration parameters (" +
261 NodeRegistrationOptions::PARAM_NAME_ROS2TOPIC +
": " + context_.getRegistrationOptions().topic +
262 "). Error message: " + expected_name.error());
266 if (!client_instance_) {
267 throw exceptions::RosNodeError(context_.getFullyQualifiedTreeNodeName(
this) +
" - client_instance_ is nullptr.");
270 auto & service_client = client_instance_->service_client;
272 auto check_status = [
this](BT::NodeStatus status) {
273 if (!isStatusCompleted(status)) {
274 throw exceptions::RosNodeError(
275 context_.getFullyQualifiedTreeNodeName(
this) +
" - The callback must return either SUCCESS or FAILURE.");
281 if (status() == BT::NodeStatus::IDLE) {
282 setStatus(BT::NodeStatus::RUNNING);
284 on_feedback_state_change_ = BT::NodeStatus::RUNNING;
287 typename Request::SharedPtr request = std::make_shared<Request>();
289 if (!setRequest(request)) {
290 return check_status(onFailure(INVALID_REQUEST));
294 if (!service_client->service_is_ready()) {
295 return onFailure(SERVICE_UNREACHABLE);
298 const auto future_and_request_id =
299 service_client->async_send_request(request, [
this](
typename ServiceClient::SharedFuture response) {
300 if (response.wait_for(std::chrono::seconds(0)) != std::future_status::ready) {
301 throw exceptions::RosNodeError(
302 this->context_.getFullyQualifiedTreeNodeName(
this) +
" - Response not ready in response callback.");
304 this->response_ = response.get();
306 future_ = future_and_request_id.future;
307 request_id_ = future_and_request_id.request_id;
308 time_request_sent_ = context_.getCurrentTime();
310 RCLCPP_DEBUG(logger_,
"%s - Service request sent.", context_.getFullyQualifiedTreeNodeName(
this).c_str());
311 return BT::NodeStatus::RUNNING;
314 if (status() == BT::NodeStatus::RUNNING) {
318 if ((context_.getCurrentTime() - time_request_sent_) > context_.getRegistrationOptions().request_timeout) {
320 client_instance_->service_client->remove_pending_request(request_id_);
321 return check_status(onFailure(SERVICE_TIMEOUT));
323 return BT::NodeStatus::RUNNING;
324 }
else if (future_.valid()) {
328 RCLCPP_DEBUG(logger_,
"%s - Service response received.", context_.getFullyQualifiedTreeNodeName(
this).c_str());
332 return check_status(onResponseReceived(response_));
334 return BT::NodeStatus::RUNNING;
337template <
class ServiceT>
338inline void RosServiceNode<ServiceT>::halt()
340 if (status() == BT::NodeStatus::RUNNING) {
345template <
class ServiceT>
351template <
class ServiceT>
354 return BT::NodeStatus::SUCCESS;
357template <
class ServiceT>
360 const std::string msg = context_.getFullyQualifiedTreeNodeName(
this) +
" - Unexpected error " +
361 std::to_string(error) +
": " +
toStr(error) +
".";
362 RCLCPP_ERROR_STREAM(logger_, msg);
363 throw exceptions::RosNodeError(msg);
366template <
class ServiceT>
369 if (service_name.empty()) {
370 throw exceptions::RosNodeError(
371 context_.getFullyQualifiedTreeNodeName(
this) +
372 " - Argument service_name is empty when trying to create the client.");
377 client_instance_ && service_name == client_instance_->name &&
378 client_instance_->service_client->service_is_ready()) {
382 rclcpp::Node::SharedPtr node = context_.getRosNode();
383 rclcpp::CallbackGroup::SharedPtr group = context_.getWaitablesCallbackGroup();
385 throw exceptions::RosNodeError(
386 context_.getFullyQualifiedTreeNodeName(
this) +
387 " - The weak pointer to the ROS 2 callback group expired. The tree node doesn't "
388 "take ownership of it.");
393 service_name, [&] {
return std::make_shared<ServiceClientInstance>(node, group, service_name); });
395 bool found = client_instance_->service_client->wait_for_service(context_.getRegistrationOptions().wait_timeout);
397 std::string msg = context_.getFullyQualifiedTreeNodeName(
this) +
" - Service with name '" + client_instance_->name +
398 "' is not reachable.";
399 if (context_.getRegistrationOptions().allow_unreachable) {
400 RCLCPP_WARN_STREAM(logger_, msg);
402 RCLCPP_ERROR_STREAM(logger_, msg);
403 throw exceptions::RosNodeError(msg);
409template <
class ServiceT>
412 if (client_instance_)
return client_instance_->name;