Program Listing for File service.hpp

Return to documentation for file (include/turtlebot4_node/service.hpp)

/*
 * Copyright 2021 Clearpath Robotics, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author Roni Kreinin (rkreinin@clearpathrobotics.com)
 */

#ifndef TURTLEBOT4_NODE__SERVICE_HPP_
#define TURTLEBOT4_NODE__SERVICE_HPP_

#include <memory>
#include <future>
#include <string>

#include <rclcpp/rclcpp.hpp>

namespace turtlebot4
{

// Base templated service class
template<typename ServiceT>
class Turtlebot4BaseService
{
public:
  // Constructor and Destructor
  explicit Turtlebot4BaseService(
    std::shared_ptr<rclcpp::Node> & nh, std::string service,
    uint32_t timeout)
  : nh_(nh), service_(service), timeout_(timeout)
  {
    // Create service client
    client_ = nh_->create_client<ServiceT>(service);
  }
  virtual ~Turtlebot4BaseService() {}

  virtual void make_request(std::shared_ptr<typename ServiceT::Request> request) = 0;

protected:
  // Node handle
  std::shared_ptr<rclcpp::Node> nh_;
  // Service
  std::string service_;
  // Timer
  rclcpp::TimerBase::SharedPtr timer_;
  uint32_t timeout_;
  // Service client
  typename rclcpp::Client<ServiceT>::SharedPtr client_;

  // Service response callback
  virtual void response_callback(typename rclcpp::Client<ServiceT>::SharedFuture future) = 0;
};

// Templated service class for services with a success and message response
template<typename ServiceT>
class Turtlebot4Service : public Turtlebot4BaseService<ServiceT>
{
public:
  // Constructor and Destructor
  explicit Turtlebot4Service(
    std::shared_ptr<rclcpp::Node> & nh, std::string service,
    uint32_t timeout = 30)
  : Turtlebot4BaseService<ServiceT>(nh, service, timeout)
  {}

  virtual ~Turtlebot4Service() {}

  void make_request(std::shared_ptr<typename ServiceT::Request> request) override
  {
    // Cancel existing timers
    if (this->timer_ != nullptr) {
      this->timer_->cancel();
    }

    auto timeout = rclcpp::Clock().now().seconds() + this->timeout_;

    // Create timer to check for service without blocking
    this->timer_ = this->nh_->create_wall_timer(
      std::chrono::milliseconds(1000),
      [this, request, timeout]() -> void
      {
        // Check for service
        if (this->client_->wait_for_service(std::chrono::milliseconds(10))) {
          RCLCPP_INFO(
            this->nh_->get_logger(), "%s service available, sending request",
            this->service_.c_str());
          this->timer_->cancel();

          // Send request
          auto future_result = this->client_->async_send_request(
            request,
            std::bind(&Turtlebot4Service::response_callback, this, std::placeholders::_1));
        } else if (!rclcpp::ok()) {
          RCLCPP_ERROR(
            this->nh_->get_logger(), "Interrupted while waiting for the service %s.",
            this->service_.c_str());
          this->timer_->cancel();
        } else if (rclcpp::Clock().now().seconds() > timeout) {  // Timed out
          RCLCPP_ERROR(
            this->nh_->get_logger(), "Service %s unavailable.",
            this->service_.c_str());
          this->timer_->cancel();
        }
      });
  }

private:
  // Service response callback
  void response_callback(typename rclcpp::Client<ServiceT>::SharedFuture future) override
  {
    auto result = future.get();
    RCLCPP_INFO(
      this->nh_->get_logger(), "%s service got results: %s",
      this->service_.c_str(), result->success ? "Success" : "Failed");
    RCLCPP_INFO(this->nh_->get_logger(), result->message.c_str());
  }
};

// Templated service class for services with an empty response
template<typename ServiceT>
class Turtlebot4EmptyService : public Turtlebot4BaseService<ServiceT>
{
public:
  // Constructor and Destructor
  explicit Turtlebot4EmptyService(
    std::shared_ptr<rclcpp::Node> & nh, std::string service,
    uint32_t timeout = 30)
  : Turtlebot4BaseService<ServiceT>(nh, service, timeout)
  {}

  virtual ~Turtlebot4EmptyService() {}

  void make_request(std::shared_ptr<typename ServiceT::Request> request) override
  {
    // Cancel existing timers
    if (this->timer_ != nullptr) {
      this->timer_->cancel();
    }

    auto timeout = rclcpp::Clock().now().seconds() + this->timeout_;

    // Create timer to check for service without blocking
    this->timer_ = this->nh_->create_wall_timer(
      std::chrono::milliseconds(1000),
      [this, request, timeout]() -> void
      {
        // Check for service
        if (this->client_->wait_for_service(std::chrono::milliseconds(10))) {
          RCLCPP_INFO(
            this->nh_->get_logger(), "%s service available, sending request",
            this->service_.c_str());
          this->timer_->cancel();

          // Send request
          auto future_result = this->client_->async_send_request(
            request,
            std::bind(&Turtlebot4EmptyService::response_callback, this, std::placeholders::_1));
        } else if (!rclcpp::ok()) {
          RCLCPP_ERROR(
            this->nh_->get_logger(), "Interrupted while waiting for the service %s.",
            this->service_.c_str());
          this->timer_->cancel();
        } else if (rclcpp::Clock().now().seconds() > timeout) {  // Timed out
          RCLCPP_ERROR(
            this->nh_->get_logger(), "Service %s unavailable.",
            this->service_.c_str());
          this->timer_->cancel();
        }
      });
  }

private:
  // Service response callback
  void response_callback(typename rclcpp::Client<ServiceT>::SharedFuture future) override
  {
    (void)future;
    RCLCPP_INFO(
      this->nh_->get_logger(), "%s service completed.", this->service_.c_str());
  }
};

}  // namespace turtlebot4

#endif  // TURTLEBOT4_NODE__SERVICE_HPP_