Skip to content

File liveness_monitor.hpp

File List > common > diagnostics > liveness_monitor.hpp

Go to the documentation of this file

// Copyright 2025 TIER IV, 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.

#pragma once

#include <diagnostic_updater/diagnostic_status_wrapper.hpp>
#include <diagnostic_updater/diagnostic_updater.hpp>
#include <rclcpp/clock.hpp>
#include <rclcpp/duration.hpp>
#include <rclcpp/node.hpp>
#include <rclcpp/rclcpp.hpp>
#include <rclcpp/time.hpp>
#include <rcpputils/thread_safety_annotations.hpp>

#include <diagnostic_msgs/msg/diagnostic_status.hpp>

#include <cstdint>
#include <memory>
#include <mutex>
#include <string>

namespace nebula::ros
{

class LivenessMonitor : public diagnostic_updater::DiagnosticTask
{
public:
  LivenessMonitor(
    const std::string & name, const rclcpp::Node * parent_node, const rclcpp::Duration & timeout)
  : DiagnosticTask(name), timeout_(timeout)
  {
    // select clock according to the use_sim_time paramter set to the parent
    bool use_sim_time = false;
    if (parent_node->has_parameter("use_sim_time")) {
      use_sim_time = parent_node->get_parameter("use_sim_time").as_bool();
    }
    if (use_sim_time) {
      clock_ = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
    } else {
      clock_ = std::make_shared<rclcpp::Clock>(RCL_STEADY_TIME);
    }

    last_tick_ = clock_->now();
  }

  void tick()
  {
    std::lock_guard lock(mutex_);
    last_tick_ = clock_->now();
  }

private:
  void run(diagnostic_updater::DiagnosticStatusWrapper & status) override
  {
    std::lock_guard lock(mutex_);

    rclcpp::Time now = clock_->now();
    rclcpp::Duration lateness = now - last_tick_;
    bool is_live = lateness < timeout_;

    using diagnostic_msgs::msg::DiagnosticStatus;
    uint8_t severity = DiagnosticStatus::OK;
    std::string message = "Alive";
    std::string value = "true";

    if (!is_live) {
      severity = DiagnosticStatus::ERROR;
      message = "Dead";
      value = "false";
    }

    status.summary(severity, message);
    status.add("Is alive", value);
    status.add("Last tick [s]", std::to_string(last_tick_.seconds()));
    status.add("Lateness [ms]", std::to_string(lateness.seconds() * 1000));
  }

  rclcpp::Clock::SharedPtr clock_;
  rclcpp::Duration timeout_;

  std::mutex mutex_;
  rclcpp::Time last_tick_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
};

}  // namespace nebula::ros