How to monitor the diagnostic

/*
 * Copyright 2025-present ifm electronic, gmbh
 * SPDX-License-Identifier: Apache-2.0
 * Demonstrates how to retrieve diagnostics using the O3RDiagnostic class
 * and handle diagnostics asynchronously using a callback function.
 */
#include "diagnostic.hpp"
#include <chrono>
#include <iostream>
#include <thread>

int main() {
  const std::string ip = "192.168.0.69"; // Replace with your O3R IP address
  auto o3r = std::make_shared<ifm3d::O3R>(ip);
  O3RDiagnostic diagnostic(o3r);

  // Retrieve and display active diagnostics
  try {
    auto active_diagnostics =
        diagnostic.GetDiagnosticFiltered({{"state", "active"}});
    std::cout << "Current active diagnostics:\n"
              << active_diagnostics.dump(4)
              << "\n"; // Pretty print with 4 spaces
  } catch (const std::exception &e) {
    std::cerr << "Error retrieving active diagnostics: " << e.what() << "\n";
  }

  // Loop for 5 seconds, displaying only the relevant groups
  std::cout << "Filtering diagnostics for 5 seconds...\n";
  auto start_time = std::chrono::steady_clock::now();
  while (std::chrono::steady_clock::now() - start_time <
         std::chrono::seconds(5)) {
    try {
      auto filtered_diagnostics =
          diagnostic.GetDiagnosticFiltered({{"state", "active"}});
      auto groups = filtered_diagnostics["groups"];

      // Display the groups status for existing ports and applications
      json filtered_groups;
      for (auto &[key, value] : groups.items()) {
        if (value != "not_available") {
          filtered_groups[key] = value;
        }
      }

      std::cout << "Current active diagnostic groups (filtered):\n"
                << filtered_groups.dump(4)
                << "\n"; // Pretty print with 4 spaces
    } catch (const std::exception &e) {
      std::cerr << "Error retrieving filtered diagnostics: " << e.what()
                << "\n";
    }

    std::this_thread::sleep_for(
        std::chrono::seconds(1)); // Wait 1 second before the next iteration
  }

  // Start asynchronous diagnostic monitoring
  diagnostic.StartAsyncDiagnostics();

  // Run asynchronous monitoring until interrupted
  try {
    std::cout << "Press Ctrl+C to stop diagnostic monitoring.\n";
    while (true) {
      std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
  } catch (...) {
    diagnostic.StopAsyncDiagnostics();
    std::cout << "Stopping diagnostic monitoring.\n";
  }

  return 0;
}
/*
 * Shared functionality for O3R diagnostics.
 */
#ifndef DIAGNOSTICS_HPP
#define DIAGNOSTICS_HPP

#include <chrono>
#include <ifm3d/common/json.hpp>
#include <ifm3d/device/o3r.h>
#include <ifm3d/fg.h>
#include <iostream>
#include <stdexcept>
#include <string>
#include <thread>

using ifm3d::json;

class O3RDiagnostic {
public:
  O3RDiagnostic(std::shared_ptr<ifm3d::O3R> o3r) : o3r_(o3r) {
    diag_fg_ = std::make_shared<ifm3d::FrameGrabber>(o3r_, 50009);
  }

  void StartAsyncDiagnostics() {
    diag_fg_->OnAsyncError(async_diagnostic_callback);
    std::cout << "Starting async diagnostic monitoring. Errors and "
                 "descriptions will be logged.\n";
    diag_fg_->Start({});
  }

  void StopAsyncDiagnostics() {
    std::cout << "Stopping async diagnostic monitoring.\n";
    diag_fg_->Stop();
  }

  json GetDiagnosticFiltered(const json &filter_mask = {}) {
    try {
      std::cout << "Polling O3R diagnostic data with filter: "
                << filter_mask.dump() << "\n";
      return o3r_->GetDiagnosticFiltered(filter_mask);
    } catch (const std::exception &e) {
      std::cerr << "Error when getting diagnostic data: " << e.what() << "\n";
      throw;
    }
  }

  static void async_diagnostic_callback(int id, const std::string &message) {
    json diagnostic = json::parse(message);

    // Extract severity levels
    const std::vector<std::string> severity_levels = {"critical", "major",
                                                      "minor", "info"};
    const std::string default_severity = "major";

    // Check if events exist
    if (!diagnostic.contains("events") || diagnostic["events"].empty()) {
      std::cout << "No diagnostic events found.\n";
      return;
    }

    // Extract events and filter by severity
    auto events = diagnostic["events"];
    for (const auto &event : events) {
      json filtered_diag = {{"id", event["id"]},
                            {"name", event["name"]},
                            {"severity", event["severity"]},
                            {"description", event["description"]},
                            {"source", event["source"]},
                            {"state", event["state"]},
                            {"timestamp", diagnostic["timestamp"]}};

      // Check severity and print critical diagnostics
      if (filtered_diag["severity"] == "critical") {
        std::cout << "⚠️ Critical diagnostic appeared! Stop the robot and "
                     "follow the handling strategy.\n";
      } // here you can add your own handling strategy and stopping the robot

      // Print JSON with indentation (pretty print)
      std::cout << "Received diagnostic message:\n"
                << filtered_diag.dump(4) << "\n"; // '4' is indentation level
    }
  }

private:
  std::shared_ptr<ifm3d::O3R> o3r_;
  std::shared_ptr<ifm3d::FrameGrabber> diag_fg_;
};

#endif // DIAGNOSTICS_HPP