Program Listing for File topic_service_client.h

Return to documentation for file (/tmp/ws/src/marti_common/swri_roscpp/include/swri_roscpp/topic_service_client.h)

// *****************************************************************************
//
// Copyright (c) 2018, Southwest Research Institute® (SwRI®)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of Southwest Research Institute® (SwRI®) nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL SOUTHWEST RESEARCH INSTITUTE BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// *****************************************************************************
#ifndef SWRI_ROSCPP_TOPIC_SERVICE_CLIENT_H_
#define SWRI_ROSCPP_TOPIC_SERVICE_CLIENT_H_

#include <rclcpp/rclcpp.hpp>

#include <boost/thread/mutex.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>

#include <map>

namespace swri
{
template<class MReq, class MRes>
class TopicServiceClientRaw
{
private:
  typedef
  boost::mutex request_lock_;
  std::shared_ptr<rclcpp::Subscription<MRes> > response_sub_;
  std::shared_ptr<rclcpp::Publisher<MReq> > request_pub_;
  std::shared_ptr<MRes> response_;

  rclcpp::Duration timeout_;
  std::string name_;
  std::string service_name_;

  int sequence_;

public:
  TopicServiceClientRaw() :
    timeout_(std::chrono::duration<float>(4.0)),
    sequence_(0),
    node_(nullptr)
  {

  }

  void initialize(rclcpp::Node &nh,
                const std::string &service,
                const std::string &client_name = "")
  {
    node_ = &nh;
    //Converts using string stream instead of to_string so non C++ 11 nodes won't fail
    boost::uuids::random_generator gen;
    boost::uuids::uuid u = gen();
    std::string random_str = boost::uuids::to_string(u);
    name_ = client_name.length() ? client_name : (nh.get_name() + random_str);
    service_name_ = service;

    request_pub_ = nh.create_publisher<MReq>(service + "/request", 10);
    response_sub_ = nh.create_subscription<MRes>(service + "/response",
        10, std::bind(&TopicServiceClientRaw<MReq, MRes>::response_callback, this, std::placeholders::_1));
  }

  bool call(MReq& request, MRes& response)
  {
    boost::mutex::scoped_lock scoped_lock(request_lock_);

    // block for response
    request.srv_header.stamp = node_->now();
    request.srv_header.sequence = sequence_;
    request.srv_header.sender = name_;

    // Wait until we get a subscriber and publisher
    while (request_pub_->get_subscription_count() == 0 || response_sub_->get_publisher_count() == 0)
    {
      rclcpp::sleep_for(std::chrono::milliseconds(2));
      rclcpp::spin_some(node_->shared_from_this());

      if (node_->now() - request.srv_header.stamp > timeout_)
      {
        RCLCPP_ERROR(node_->get_logger(), "Topic service timeout exceeded");
        return false;
      }
    }
    response_.reset();
    request_pub_->publish(request);

    // Wait until we get a response
    while (!response_ && node_->now() - request.srv_header.stamp < timeout_)
    {
      rclcpp::sleep_for(std::chrono::milliseconds(2));
      rclcpp::spin_some(node_->shared_from_this());
    }

    sequence_++;
    if (response_)
    {
      response = *response_;
      response_.reset();
      return response.srv_header.result;
    }
    return false;
  }

  std::string getService()
  {
    return service_name_;
  }

  bool exists()
  {
    return request_pub_->get_subscription_count() > 0 && response_sub_->get_publisher_count() > 0;
  }

  // The service server can output a console log message when the
  // service is called if desired.
  void setLogCalls(bool enable);
  bool logCalls() const;
private:

  void response_callback(const std::shared_ptr<MRes> message)
  {
    RCLCPP_DEBUG(node_->get_logger(), "Got response for %s with sequence %i",
             message->srv_header.sender.c_str(), message->srv_header.sequence);

    if (message->srv_header.sender != name_)
    {
      RCLCPP_DEBUG(node_->get_logger(), "Got response from another client, ignoring..");
      return;
    }

    if (message->srv_header.sequence != sequence_)
    {
      RCLCPP_WARN(node_->get_logger(), "Got wrong sequence number, ignoring..");
      RCLCPP_DEBUG(node_->get_logger(), "message seq:%i vs current seq: %i", message->srv_header.sequence, sequence_);
      return;
    }

    response_ = message;
  }

  rclcpp::Node* node_;
};  // class TopicServiceClientRaw

template<class MReq>
class TopicServiceClient: public TopicServiceClientRaw<typename MReq:: Request, typename MReq:: Response>
{
public:
  bool call(MReq& req)
  {
    return TopicServiceClientRaw<typename MReq:: Request, typename MReq:: Response>::call(req.request, req.response);
  }

};  // class TopicServiceClient


}  // namespace swri
#endif  // SWRI_ROSCPP_TOPIC_SERVICE_SERVER_H_