Program Listing for File approximate_epsilon_time.h

Return to documentation for file (include/message_filters/sync_policies/approximate_epsilon_time.h)

/*********************************************************************
* Software License Agreement (BSD License)
*
*  Copyright (c) 2022, Open Source Robotics Foundation, Inc.
*  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 the Willow Garage 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 THE
*  COPYRIGHT OWNER OR CONTRIBUTORS 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 MESSAGE_FILTERS__SYNC_POLICIES__APPROXIMATE_EPSILON_TIME_H_
#define MESSAGE_FILTERS__SYNC_POLICIES__APPROXIMATE_EPSILON_TIME_H_

#include <deque>
#include <string>
#include <tuple>

#include <rclcpp/rclcpp.hpp>

#include "message_filters/connection.h"
#include "message_filters/message_traits.h"
#include "message_filters/null_types.h"
#include "message_filters/signal9.h"
#include "message_filters/synchronizer.h"

namespace message_filters
{
namespace sync_policies
{

template<typename M0, typename M1, typename M2 = NullType, typename M3 = NullType, typename M4 = NullType,
         typename M5 = NullType, typename M6 = NullType, typename M7 = NullType, typename M8 = NullType>
class ApproximateEpsilonTime : public PolicyBase<M0, M1, M2, M3, M4, M5, M6, M7, M8>
{
public:
  typedef Synchronizer<ApproximateEpsilonTime> Sync;
  typedef PolicyBase<M0, M1, M2, M3, M4, M5, M6, M7, M8> Super;
  typedef typename Super::Messages Messages;
  typedef typename Super::Signal Signal;
  typedef typename Super::Events Events;
  typedef typename Super::RealTypeCount RealTypeCount;
  typedef typename Super::M0Event M0Event;
  typedef typename Super::M1Event M1Event;
  typedef typename Super::M2Event M2Event;
  typedef typename Super::M3Event M3Event;
  typedef typename Super::M4Event M4Event;
  typedef typename Super::M5Event M5Event;
  typedef typename Super::M6Event M6Event;
  typedef typename Super::M7Event M7Event;
  typedef typename Super::M8Event M8Event;
  typedef Events Tuple;

  ApproximateEpsilonTime(uint32_t queue_size, rclcpp::Duration epsilon)
  : parent_(nullptr)
  , queue_size_(queue_size)
  , epsilon_{epsilon}
  {
  }

  ApproximateEpsilonTime(const ApproximateEpsilonTime& e)
  : epsilon_{e.epsilon_}
  {
    *this = e;
  }

  ApproximateEpsilonTime& operator=(const ApproximateEpsilonTime& rhs)
  {
    parent_ = rhs.parent_;
    queue_size_ = rhs.queue_size_;
    events_ = rhs.events_;
    epsilon_ = rhs.epsilon_;

    return *this;
  }

  void initParent(Sync* parent)
  {
    parent_ = parent;
  }

  template<size_t i>
  void add(const typename std::tuple_element<i, Events>::type& evt)
  {
    RCUTILS_ASSERT(parent_);

    std::lock_guard<std::mutex> lock(mutex_);

    auto & events_of_this_type = std::get<i>(events_);
    if (0u == events_of_this_type.size()) {
      ++number_of_non_empty_events_;
    }
    events_of_this_type.push_back(evt);
    if (number_of_non_empty_events_ == RealTypeCount::value) {
      process();
    } else if (events_of_this_type.size() > queue_size_) {
      erase_beginning_of_vector<i>();
    }
  }

private:
  using TimeIndexPair = std::pair<rclcpp::Time, size_t>;

  template <size_t Is>
  TimeIndexPair
  get_older_timestamp_between(const TimeIndexPair & current)
  {
    namespace mt = message_filters::message_traits;
    using ThisEventType = typename std::tuple_element<Is, Events>::type;
    if constexpr (Is >= RealTypeCount::value) {
      return current;
    }
    const auto & events_of_this_type = std::get<Is>(events_);
    if (0u == events_of_this_type.size()) {
      // this condition should not happen
      return current;
    }
    auto candidate = mt::TimeStamp<typename ThisEventType::Message>::value(*events_of_this_type.at(0).getMessage());
    if (current.first > candidate) {
      return std::make_pair(candidate, Is);
    }
    return current;
  }

  template <size_t ... Is>
  TimeIndexPair
  get_older_timestamp_helper(std::index_sequence<Is...> const &)
  {
    TimeIndexPair older{
      rclcpp::Time(std::numeric_limits<int64_t>::max(), RCL_ROS_TIME), std::numeric_limits<size_t>::max()};
    ((older = get_older_timestamp_between<Is>(older)), ...);
    return older;
  }

  TimeIndexPair
  get_older_timestamp()
  {
    return get_older_timestamp_helper(std::make_index_sequence<9u>());
  }

  template <size_t Is>
  bool
  check_timestamp_within_epsilon(const TimeIndexPair & older)
  {
    namespace mt = message_filters::message_traits;
    using ThisEventType = typename std::tuple_element<Is, Events>::type;
    if constexpr (Is >= RealTypeCount::value) {
      return true;
    }
    if (Is == older.second) {
      return true;
    }
    const auto & events_of_this_type = std::get<Is>(events_);
    if (0u == events_of_this_type.size()) {
      // this condition should not happen
      return false;
    }
    auto ts = mt::TimeStamp<typename ThisEventType::Message>::value(*events_of_this_type.at(0).getMessage());
    if (older.first + epsilon_ >= ts) {
      return true;
    }
    return false;
  }

  template <size_t ... Is>
  bool
  check_all_timestamp_within_epsilon_helper(
    const TimeIndexPair & older, std::index_sequence<Is...> const &)
  {
    bool valid = true;
    ((valid &= check_timestamp_within_epsilon<Is>(older)), ...);
    return valid;
  }

  bool
  check_all_timestamp_within_epsilon(const TimeIndexPair & older)
  {
    return check_all_timestamp_within_epsilon_helper(
      older, std::make_index_sequence<9u>());
  }

  template<size_t Is>
  void
  erase_beginning_of_vector()
  {
    if constexpr (Is >= RealTypeCount::value) {
      return;
    }
    auto & this_vector = std::get<Is>(events_);
    if (this_vector.begin() != this_vector.end()) {
      this_vector.erase(this_vector.begin());
      if (this_vector.empty()) {
        --number_of_non_empty_events_;
      }
    }
  }

  template <size_t ... Is>
  void
  erase_beginning_of_vectors_helper(std::index_sequence<Is...> const &)
  {
    ((erase_beginning_of_vector<Is>()), ...);
  }

  void erase_beginning_of_vectors()
  {
    return erase_beginning_of_vectors_helper(std::make_index_sequence<9u>());
  }

  template<size_t Is>
  void
  erase_beginning_of_vector_if_on_sync_with_ts(rclcpp::Time timestamp)
  {
    namespace mt = message_filters::message_traits;
    using ThisEventType = typename std::tuple_element<Is, Events>::type;
    if constexpr (Is >= RealTypeCount::value) {
      return;
    }
    auto & this_vector = std::get<Is>(events_);
    if (this_vector.begin() == this_vector.end()) {
      return;
    }
    auto event_ts = mt::TimeStamp<typename ThisEventType::Message>::value(
      *this_vector.at(0).getMessage());
    if (timestamp + epsilon_ < event_ts) {
      return;
    }
    this_vector.erase(this_vector.begin());
    if (this_vector.empty()) {
      --number_of_non_empty_events_;
    }
  }

  template <size_t ... Is>
  void
  erase_old_events_if_on_sync_with_ts_helper(rclcpp::Time timestamp, std::index_sequence<Is...> const &)
  {
    ((erase_beginning_of_vector_if_on_sync_with_ts<Is>(timestamp)), ...);
  }

  void erase_old_events_if_on_sync_with_ts(rclcpp::Time timestamp)
  {
    return erase_old_events_if_on_sync_with_ts_helper(timestamp, std::make_index_sequence<9u>());
  }

  void signal()
  {
    if constexpr (RealTypeCount::value == 2) {
      parent_->signal(
        std::get<0>(events_).at(0), std::get<1>(events_).at(0),
        M2Event{}, M3Event{}, M4Event{}, M5Event{}, M6Event{}, M7Event{}, M8Event{});
    } else if constexpr (RealTypeCount::value == 3) {
      parent_->signal(
        std::get<0>(events_).at(0), std::get<1>(events_).at(0), std::get<2>(events_).at(0),
        M3Event{}, M4Event{}, M5Event{}, M6Event{}, M7Event{}, M8Event{});
    } else if constexpr (RealTypeCount::value == 4) {
      parent_->signal(
        std::get<0>(events_).at(0), std::get<1>(events_).at(0), std::get<2>(events_).at(0),
        std::get<3>(events_).at(0),
        M4Event{}, M5Event{}, M6Event{}, M7Event{}, M8Event{});
    } else if constexpr (RealTypeCount::value == 5) {
      parent_->signal(
        std::get<0>(events_).at(0), std::get<1>(events_).at(0), std::get<2>(events_).at(0),
        std::get<3>(events_).at(0), std::get<4>(events_).at(0),
        M5Event{}, M6Event{}, M7Event{}, M8Event{});
    } else if constexpr (RealTypeCount::value == 6) {
      parent_->signal(
        std::get<0>(events_).at(0), std::get<1>(events_).at(0), std::get<2>(events_).at(0),
        std::get<3>(events_).at(0), std::get<4>(events_).at(0), std::get<5>(events_).at(0),
        M6Event{}, M7Event{}, M8Event{});
    } else if constexpr (RealTypeCount::value == 7) {
      parent_->signal(
        std::get<0>(events_).at(0), std::get<1>(events_).at(0), std::get<2>(events_).at(0),
        std::get<3>(events_).at(0), std::get<4>(events_).at(0), std::get<5>(events_).at(0),
        std::get<6>(events_).at(0),
        M7Event{}, M8Event{});
    } else if constexpr (RealTypeCount::value == 8) {
      parent_->signal(
        std::get<0>(events_).at(0), std::get<1>(events_).at(0), std::get<2>(events_).at(0),
        std::get<3>(events_).at(0), std::get<4>(events_).at(0), std::get<5>(events_).at(0),
        std::get<6>(events_).at(0), std::get<7>(events_).at(0),
        M8Event{});
    } else if constexpr (RealTypeCount::value == 9) {
      parent_->signal(
        std::get<0>(events_).at(0), std::get<1>(events_).at(0), std::get<2>(events_).at(0),
        std::get<3>(events_).at(0), std::get<4>(events_).at(0), std::get<5>(events_).at(0),
        std::get<6>(events_).at(0), std::get<7>(events_).at(0), std::get<8>(events_).at(0));
    } else {
      static_assert("RealTypeCount::value should be >=2 and <=9");
    }
  }

  // assumes mutex_ is already locked
  void process()
  {
    while (number_of_non_empty_events_ == RealTypeCount::value) {
      auto old_ts = get_older_timestamp();
      if (check_all_timestamp_within_epsilon(old_ts)) {
        signal();
        erase_beginning_of_vectors();
      } else {
        erase_old_events_if_on_sync_with_ts(old_ts.first);
      }
    }
  }

  Sync* parent_;

  uint32_t queue_size_;
  rclcpp::Duration epsilon_;
  size_t number_of_non_empty_events_{0};
  using TupleOfVecOfEvents = std::tuple<
    std::vector<M0Event>, std::vector<M1Event>, std::vector<M2Event>,
    std::vector<M3Event>, std::vector<M4Event>, std::vector<M5Event>,
    std::vector<M6Event>, std::vector<M7Event>, std::vector<M8Event>>;
  TupleOfVecOfEvents events_;

  std::mutex mutex_;
};

}  // namespace sync_policies
}  // namespace message_filters

#endif  // MESSAGE_FILTERS__SYNC_POLICIES__APPROXIMATE_EPSILON_TIME_H_