text: #include "HLCReadyAggregator.hpp"
  
  /**
   * \file HLCReadyAggregator.cpp
   * \ingroup lcc
   */
  
  //The reader callback is initialized in the init list of the constructor; store all IDs in a map together with the current time in nanoseconds
  HLCReadyAggregator::HLCReadyAggregator() :
      async_hlc_reader(
          [&](std::vector<HLCHello>& samples){
              //Lock the mutex for thread-safe access
              std::lock_guard<std::mutex> lock(hlc_list_mutex);
  
              //Store new IDs / update receive time
              //Checks for on-/offline NUCs are performed in get_hlc_ids_string, which is called regularly by the UI
              for (auto& data : samples)
              {
                  auto id_string = data.source_id();
                  //Convert ID to uint8_t
                  try {
                      int id_int = std::stoi(id_string);
                      if (id_int < 0 || id_int > 255)
                      {
                          throw std::runtime_error("HLC ID is too small / too large");
                      }
  
                      uint8_t id_uint8 = static_cast<uint8_t>(id_int);
                      
                      hlc_map[id_uint8] = cpm::get_time_ns();
  
                      //Store whether the programs on the HLC are currently running (with a small risk that the order of msgs is not correct)
                      hlc_script_running[id_uint8] = data.script_running();
                      hlc_middleware_running[id_uint8] = data.middleware_running();
                  }
                  catch (const std::runtime_error& err)
                  {
                      cpm::Logging::Instance().write(2, "Error on converting HLC ID %s: %s", id_string.c_str(), err.what());
                  }
                  catch (...) {
                      cpm::Logging::Instance().write(2, "Error: Could not convert HLC ID %s to int in HLCReadyAggregator", id_string.c_str());
                  }
              }
          },
          "hlc_hello",
          true)
  {
  }
  
  std::vector<std::string> HLCReadyAggregator::get_hlc_ids_string()
  {
      auto ids_uint = get_hlc_ids_uint8_t();
  
      std::vector<std::string> valid_hlc_ids;
      for (auto entry : ids_uint)
      {
          //Convert ID to string (convert to int first, or uint8_t is interpreted as symbol)
          valid_hlc_ids.push_back(std::to_string(static_cast<int>(entry)));
      }
  
      return valid_hlc_ids;
  }
  
  std::vector<uint8_t> HLCReadyAggregator::get_hlc_ids_uint8_t()
  {
      //Lock the mutex for thread-safe access
      std::lock_guard<std::mutex> lock(hlc_list_mutex);
  
      //Only use IDs that are still up-to-date
      uint64_t current_time_ns = cpm::get_time_ns();
      std::vector<uint8_t> valid_hlc_ids;
  
      for (auto iterator = hlc_map.begin(); iterator != hlc_map.end();)
      {
          if (current_time_ns - iterator->second < time_to_live_ns)
          {
              valid_hlc_ids.push_back(iterator->first);
              ++iterator;
          }
          else
          {
              cpm::Logging::Instance().write(1, "HLC / NUC crashed / now offline / missed online message: %s", std::to_string(static_cast<int>(iterator->first)).c_str());
              iterator = hlc_map.erase(iterator);
          }
          
      }
  
      return valid_hlc_ids;
  }
  
  bool HLCReadyAggregator::script_running_on(uint8_t hlc_id)
  {
      std::lock_guard<std::mutex> lock(hlc_list_mutex);
  
      //Considered not running if no HLC msg has been received
      auto iterator = hlc_map.find(hlc_id);
      if (iterator == hlc_map.end())
      {
          return false;
      }
  
      //Considered not running if data is not up to date
      if (cpm::get_time_ns() - iterator->second >= time_to_live_ns)
      {
          return false;
      }
  
      //Else, obtain the actual value - which must exist if an entry in hlc_map exists 
      return hlc_script_running.at(hlc_id);
  }
  
  bool HLCReadyAggregator::middleware_running_on(uint8_t hlc_id)
  {
      std::lock_guard<std::mutex> lock(hlc_list_mutex);
  
      //Considered not running if no HLC msg has been received
      auto iterator = hlc_map.find(hlc_id);
      if (iterator == hlc_map.end())
          return false;
  
      //Considered not running if data is not up to date
      if (cpm::get_time_ns() - iterator->second >= time_to_live_ns)
      {
          return false;
      }
  
      //Else, obtain the actual value - which must exist if an entry in hlc_map exists 
      return hlc_middleware_running.at(hlc_id);
  }
  