Program Listing for File client_manager.hpp

Return to documentation for file (include/rosbag2_test_common/client_manager.hpp)

// Copyright 2023 Sony Group Corporation.
//
// 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.

#ifndef ROSBAG2_TEST_COMMON__CLIENT_MANAGER_HPP_
#define ROSBAG2_TEST_COMMON__CLIENT_MANAGER_HPP_

#include <chrono>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "rcl/service_introspection.h"

#include "rclcpp/rclcpp.hpp"  // rclcpp must be included before the Windows specific includes.

namespace rosbag2_test_common
{
template<typename ServiceT>
class ClientManager : public rclcpp::Node
{
public:
  explicit ClientManager(
    std::string service_name,
    size_t number_of_clients = 1,
    bool service_event_contents = false,
    bool client_event_contents = true)
  : Node("service_client_manager_" + std::to_string(rclcpp::Clock().now().nanoseconds()),
      rclcpp::NodeOptions().start_parameter_services(false).start_parameter_event_publisher(
        false).enable_rosout(false)),
    service_name_(std::move(service_name)),
    number_of_clients_(number_of_clients),
    enable_service_event_contents_(service_event_contents),
    enable_client_event_contents_(client_event_contents)
  {
    // *INDENT-OFF*
    auto do_nothing_srv_callback =
      [this](const std::shared_ptr<rmw_request_id_t> request_header,
        const std::shared_ptr<typename ServiceT::Request> request,
        std::shared_ptr<typename ServiceT::Response> response) -> void
      {
        // Do nothing
        (void)request_header;
        (void)request;
        (void)response;
      };
    // *INDENT-ON*

    service_ = create_service<ServiceT>(service_name_, do_nothing_srv_callback);

    rcl_service_introspection_state_t introspection_state;
    if (enable_service_event_contents_) {
      introspection_state = RCL_SERVICE_INTROSPECTION_CONTENTS;
    } else {
      introspection_state = RCL_SERVICE_INTROSPECTION_OFF;
    }
    service_->configure_introspection(
      get_clock(), rclcpp::SystemDefaultsQoS(), introspection_state);

    if (enable_client_event_contents_) {
      introspection_state = RCL_SERVICE_INTROSPECTION_CONTENTS;
    } else {
      introspection_state = RCL_SERVICE_INTROSPECTION_OFF;
    }

    for (size_t i = 0; i < number_of_clients_; i++) {
      auto client = create_client<ServiceT>(service_name_);
      client->configure_introspection(
        get_clock(), rclcpp::SystemDefaultsQoS(), introspection_state);
      clients_.emplace_back(client);
    }
  }

  bool check_service_ready()
  {
    for (auto & client : clients_) {
      if (!client->service_is_ready()) {
        return false;
      }
    }
    return true;
  }

  bool wait_for_srvice_to_be_ready(std::chrono::duration<double> timeout = std::chrono::seconds(5))
  {
    using clock = std::chrono::system_clock;
    auto start = clock::now();
    while (!check_service_ready() && (clock::now() - start) < timeout) {
      std::this_thread::sleep_for(std::chrono::milliseconds(25));
    }
    return check_service_ready();
  }

  bool send_request(std::chrono::duration<double> timeout = std::chrono::seconds(5))
  {
    if (!check_service_ready()) {
      return false;
    }

    for (auto & client : clients_) {
      auto request = std::make_shared<typename ServiceT::Request>();
      auto result = client->async_send_request(request);
      // Wait for the result.
      if (rclcpp::executors::spin_node_until_future_complete(
          exec_, get_node_base_interface(), result, timeout) != rclcpp::FutureReturnCode::SUCCESS)
      {
        RCLCPP_INFO(this->get_logger(), "Failed to get response !");
        return false;
      }
    }
    return true;
  }

  using client_shared_ptr = typename rclcpp::Client<ServiceT>::SharedPtr;

private:
  rclcpp::executors::SingleThreadedExecutor exec_;
  typename rclcpp::Service<ServiceT>::SharedPtr service_;
  std::vector<client_shared_ptr> clients_;
  const std::string service_name_;
  size_t number_of_clients_;
  bool enable_service_event_contents_;
  bool enable_client_event_contents_;
};
}  // namespace rosbag2_test_common

#endif  // ROSBAG2_TEST_COMMON__CLIENT_MANAGER_HPP_