Program Listing for File service_server.h

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

// *****************************************************************************
//
// Copyright (c) 2015, 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_SERVICE_SERVER_H_
#define SWRI_ROSCPP_SERVICE_SERVER_H_

#include <rclcpp/rclcpp.hpp>
#include <diagnostic_updater/diagnostic_status_wrapper.hpp>
#include <swri_roscpp/service_server_impl.h>
#include <swri_roscpp/service_server_statistics.h>

namespace swri
{
class ServiceServer
{
 private:
  std::shared_ptr<ServiceServerImpl> impl_;

 public:
  ServiceServer();

  ServiceServer(std::shared_ptr<ServiceServerImpl>& impl);

  template<class S, class MReq, class MRes, class T>
  static ServiceServer createService(rclcpp::Node &nh,
                                     const std::string &service,
                                     bool(T::*srv_func)(const MReq &, const MRes &),
                                     T *obj);

  ServiceServer& operator=(const ServiceServer &other);

  // Reset all statistics, including message and timeout counts.
  void resetStatistics();

  // Returns the unmapped service name that was provided when the
  // service was created.
  const std::string& unmappedService() const;

  // Retrieve the statistics for all service calls handled by the
  // server.
  const ServiceServerStatistics& statistics() const;

  // The service statistics can be broken down to individual clients
  // if desired.
  void setInstrumentPerClient(bool enable);
  bool instrumentPerClient() const;
  std::vector<std::string> clientNames() const;
  ServiceServerStatistics clientStatistics(const std::string &name) const;

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

  // These flags determine which values are added to a diagnostic
  // status by the appendDiagnostics method.
  enum DIAGNOSTIC_FLAGS {
    DIAG_CONNECTION = 1 << 0,  // Include service names
    DIAG_SERVINGS   = 1 << 1,   // Servings counts/success rates
    DIAG_TIMING     = 1 << 2,  // Include service time
    DIAG_CLIENTS    = 1 << 3,  // Include client list, if applicable.

    DIAG_ALL        = ~0,       // Abbreviation to include all information
    DIAG_MOST       = DIAG_ALL ^ DIAG_CONNECTION
    // Abbreviation to include everything except connection info.
  };

  // This is a convenience method to add information about this
  // server to a diagnostic status in a standard way.
  //
  // The status is merged with a warning if service calls have failed.
  // The status is merged with an error if the most recent service
  // call failed.
  //
  // The flags parameter determines which values are added to the
  // status' key/value pairs.  This should be a bitwise combination of
  // the values defined in DIAGNOSTIC_FLAGS.
  void appendDiagnostics(diagnostic_updater::DiagnosticStatusWrapper &status,
                         const std::string &name,
                         const int flags);
};  // class ServiceServer

inline
ServiceServer::ServiceServer()
{
  // Setup an empty implementation so that we can assume impl_ is
  // non-null and avoid a lot of unnecessary NULL checks.
  impl_ = std::make_shared<ServiceServerImpl>();
}

inline
ServiceServer::ServiceServer(std::shared_ptr<ServiceServerImpl>& impl) :
impl_(impl)
{}

template<class S, class MReq, class MRes, class T>
inline
ServiceServer ServiceServer::createService(rclcpp::Node &nh,
                             const std::string &service,
                             bool(T::*srv_func)(const MReq &, const MRes &),
                             T *obj)
{
  std::shared_ptr<ServiceServerImpl> impl = std::make_shared<TypedServiceServerImpl<S, const MReq, const MRes, T> >(
      nh, service, srv_func, obj);
  return ServiceServer(impl);
}

inline
ServiceServer& ServiceServer::operator=(const ServiceServer &other)
{
  bool instrument_per_client = impl_->instrumentPerClient();
  bool log_calls = impl_->logCalls();
  impl_ = other.impl_;
  impl_->setInstrumentPerClient(instrument_per_client);
  impl_->setLogCalls(log_calls);
  return *this;
}

inline
void ServiceServer::resetStatistics()
{
  impl_->resetStatistics();
}

inline
const std::string& ServiceServer::unmappedService() const
{
  return impl_->unmappedService();
}

inline
const ServiceServerStatistics& ServiceServer::statistics() const
{
  return impl_->totalStats();
}

inline
void ServiceServer::setInstrumentPerClient(bool enable)
{
  impl_->setInstrumentPerClient(enable);
}

inline
bool ServiceServer::instrumentPerClient() const
{
  return impl_->instrumentPerClient();
}

inline
std::vector<std::string> ServiceServer::clientNames() const
{
  return impl_->clientNames();
}

inline
ServiceServerStatistics ServiceServer::clientStatistics(
  const std::string &name) const
{
  return impl_->clientStatistics(name);
}

inline
void ServiceServer::setLogCalls(bool enable)
{
  impl_->setLogCalls(enable);
}

inline
bool ServiceServer::logCalls() const
{
  return impl_->logCalls();
}

inline
void ServiceServer::appendDiagnostics(
  diagnostic_updater::DiagnosticStatusWrapper &status,
  const std::string &name,
  const int flags)
{
  const ServiceServerStatistics& stats = statistics();

  // Alias a type for easier access to DiagnosticStatus enumerations.
  typedef diagnostic_msgs::msg::DiagnosticStatus DS;

  if (stats.lastFailed()) {
    status.mergeSummaryf(DS::ERROR, "Last %s service called failed", name.c_str());
  } else if (stats.failed()) {
    status.mergeSummaryf(DS::ERROR, "%s service calls have failed.", name.c_str());
  }

  if (flags & DIAG_CONNECTION) {
    status.addf(name + "service name", "%s", unmappedService().c_str());
  }

  if (flags & DIAG_SERVINGS) {
    status.addf(name + "service calls", "%d failed / %d calls",
                stats.failed(), stats.servings());
  }

  if (flags & DIAG_TIMING) {
    status.addf(name + "service call time", "%.2f [s] (%.2f [s] - %.2f [s])",
                stats.meanTime().count() / 1000000000.0,
                stats.minTime().count() / 1000000000.0,
                stats.maxTime().count() / 1000000000.0);
  }

  if (flags & DIAG_CLIENTS) {
    std::vector<std::string> names = clientNames();

    for (size_t i = 0; i < names.size(); i++) {
      ServiceServerStatistics client_stats = clientStatistics(names[i]);

      if (flags & DIAG_SERVINGS) {
        status.addf(name + " calls from " + names[i], "%d failed / %d calls",
                    client_stats.failed(),
                    client_stats.servings());
      }

      if (flags & DIAG_TIMING) {
        status.addf(name + " calls from " + names[i] + " time", "%.2f [s] (%.2f [s] - %.2f [s])",
                client_stats.meanTime().count() / 1000000000.0,
                client_stats.minTime().count() / 1000000000.0,
                client_stats.maxTime().count() / 1000000000.0);
      }
    }
  }
}
}  // namespace swri
#endif  // SWRI_ROSCPP_SERVICE_SERVER_H_