Program Listing for File hardware_monitor_backend.hpp

Return to documentation for file (hardware_monitor_backend.hpp)

#ifndef HW_MON_BACKEND_H_
#define HW_MON_BACKEND_H_

#include <cstdint>
#include <memory>
#include <vector>
#include <fstream>
#include <nlohmann/json.hpp>
#include "flxcard/FlxCard.h"
#include "nlohmann/json_fwd.hpp"
#include "regmap_manager.hpp"
#include "util.hpp"
#include "log.hpp"


template <class DEV>
class HwMonBackend
{
public:
    using device_pair = std::pair<std::shared_ptr<DEV>, std::shared_ptr<DEV>>;

    HwMonBackend(const std::vector<device_pair> & device_pairs, const std::string& extras_file);

    std::string get_monitor_data();

private:
    std::vector<device_pair> m_device_pairs;
    std::vector<std::string> m_extra_registers;
    std::vector<unsigned int> m_mon_masks;
    nlohmann::json m_msg;
    RegmapManager m_register_checker;

    void card_monitor_data();
    nlohmann::json link_alignment(std::shared_ptr<DEV> primary);
    unsigned int make_mon_mask(unsigned int model);
    nlohmann::json mondata_json(const monitoring_data_t& mondata);
    nlohmann::json regdata_json(std::shared_ptr<DEV> d0, std::shared_ptr<DEV> d1);
};


template <class DEV>
HwMonBackend<DEV>::HwMonBackend(const std::vector<HwMonBackend<DEV>::device_pair> & device_pairs, const std::string& extras_file)
 : m_device_pairs(device_pairs)
{
    //determine mask depending on card type
    for (auto & p : m_device_pairs){
        unsigned int model = p.first->get_card_model();
        m_mon_masks.emplace_back(make_mon_mask(model));
    }

    //extra registers
    if (!extras_file.empty()){
        std::ifstream extras;
        extras.open(extras_file);
        for( std::string line; getline( extras, line ); ){
            if(m_register_checker.can_read(line)) {
                m_extra_registers.emplace_back(line);
                LOG_INFO("Register %s added to hardware monitoring.", line.c_str());
            } else {
                LOG_ERR("Requested register %s does not exist in the register map. Ignored.", line.c_str());
            }
        }
        extras.close();
    }
}


template <class DEV>
std::string HwMonBackend<DEV>::get_monitor_data()
{
    m_msg.clear();
    m_msg = nlohmann::json();
    m_msg["ts"] = Util::ISO8601TimeUTC();
    m_msg["host"] = Util::get_full_hostname();
    m_msg["cards"] = nlohmann::json();
    card_monitor_data();
    return m_msg.dump();
}


template <class DEV>
unsigned int HwMonBackend<DEV>::make_mon_mask(unsigned int model)
{
    unsigned int mask = 0x000;
    if (model == 709) {
        mask = FPGA_MONITOR;
    }
    else if (model == 712 || model == 711) {
        mask = FPGA_MONITOR | POD_MONITOR_LOS | POD_MONITOR_TEMP_VOLT | POD_MONITOR_POWER | POD_MONITOR_POWER_RX;
    }
    else if (model == 182 || model == 155) {
        mask = FPGA_MONITOR | FIREFLY_MONITOR;
    }
    else{
        LOG_ERR("Device model %u not recognised. Monitoring not possible.", model);
    }
    return mask;
}


template <class DEV>
nlohmann::json HwMonBackend<DEV>::link_alignment(std::shared_ptr<DEV> primary)
{
    nlohmann::json j = nlohmann::json::array();
    u_long aligned_data = primary->get_register("GBT_ALIGNMENT_DONE");
    u_long gbt_error_data = primary->get_register("GBT_ERROR");
    u_long number_channels = 2*primary->get_register("NUM_OF_CHANNELS");
    u_long mask, alignment, error, result;
    for(u_long ch = 0; ch < number_channels; ch++){
        mask = (1ul << ch);
        alignment = aligned_data & mask;
        error = gbt_error_data & mask;
        if (alignment == 0 ){
            result = 0;
        } else {
            result = (error == 0) ? 1 : 2;
        }
        j.emplace_back(result);
    }
    return j;
}


template <class DEV>
void HwMonBackend<DEV>::card_monitor_data()
{
    int idx = 0; //if range-based loops also had an index...
    for (const auto & p : m_device_pairs){
        auto primary = p.first;
        unsigned int mask = m_mon_masks.at(idx);
        monitoring_data_t mondata = primary->hw_get_monitoring_data(mask);
        nlohmann::json hwjson = mondata_json(mondata);
        nlohmann::json regjson = regdata_json(p.first, p.second);
        m_msg["cards"][std::to_string(idx)]["HWMON"] = hwjson;
        m_msg["cards"][std::to_string(idx)]["HWMON"]["FPGA"]["CLOCK_LOCK"] = primary->get_register("MMCM_MAIN_PLL_LOCK");
        m_msg["cards"][std::to_string(idx)]["HWMON"]["FPGA"]["BUSY"] = primary->get_register("TTC_DEC_CTRL_MASTER_BUSY");
        m_msg["cards"][std::to_string(idx)]["LINK"]["ALIGN"] = link_alignment(primary);
        m_msg["cards"][std::to_string(idx)]["REGS"]  = regjson;
        ++idx;
    }
}


template <class DEV>
nlohmann::json HwMonBackend<DEV>::mondata_json(const monitoring_data_t & mondata)
{
    nlohmann::json card_data;
    card_data["FPGA"] = nlohmann::json();
    card_data["FPGA"]["DNA"] = std::format("{:#0x}", mondata.fpga.dna);
    card_data["FPGA"]["Temp"] = std::lround(mondata.fpga.temperature);
    card_data["FPGA"]["Fan_rpm"] = mondata.fpga.fanspeed;
    card_data["FPGA"]["VCC_I"] = mondata.fpga.vccint;
    //transceivers TODO
    // RX optical power (minipod only)
    // TX optical power (minipod only)
    return card_data;
};


template <class DEV>
nlohmann::json HwMonBackend<DEV>::regdata_json(std::shared_ptr<DEV> d0, std::shared_ptr<DEV> d1)
{
    nlohmann::json reg_data;
    reg_data= nlohmann::json();
    for(const auto & reg : m_extra_registers) {
        reg_data[reg] = nlohmann::json::array();
        reg_data[reg].push_back(d0->get_register(reg.c_str()));
        if (d1 and m_register_checker.has_endpoint_1(reg)){
            reg_data[reg].push_back(d1->get_register(reg.c_str()));
        }
    }
    return reg_data;
};

#endif /* HW_MON_BACKEND_H_ */