Program Listing for File register_device_controller.hpp

Return to documentation for file (register_device_controller.hpp)

#ifndef DEVICE_REGISTER_CONTROLLER_H_
#define DEVICE_REGISTER_CONTROLLER_H_

#include <cstdint>
#include <memory>
#include <stdint.h>
#include <string>
#include <variant>

#include "device.hpp"
#include "elink.hpp"
#include "bus.hpp"
#include "fromhost_message.hpp"
#include "log.hpp"
#include "regmap_manager.hpp"
#include "register_cmd_parser.hpp"
#include "register_device_interface.hpp"
#include "felix/felix_client_thread.hpp"
#include "config/config_register.hpp"
#include "publisher.hpp"
#include "receiver.hpp"

#include "network/netio_evloop.hpp"
#include "network/utility.hpp"

#include "felix/felix_toflx.hpp"

template <class DEV>
class RegisterDeviceController
{

    public:

        RegisterDeviceController(
            std::variant<NetioEventLoop>&  evtloop,
            RegmapManager &                regmap,
            std::shared_ptr<DEV>           opened_device,
            std::shared_ptr<DEV>           opened_primary_device,
            const ConfigRegister &         config,
            unsigned int                   d_idx
        );

        //Cmd callbacks
        void cmd_connection_established(const std::string& s){LOG_INFO("Command connection established. Conn. info: %s", s.c_str());};
        void cmd_connection_closed(const std::string& s){LOG_INFO("Command connection closed. Conn. info: %s", s.c_str());};
        void process_message(const std::vector<ToFlxMessage>& messages);

        //Public for unit test
        std::string process_requests(std::vector<ReqData>& req_vec);

    private:
        //Shared resources
        std::variant<NetioEventLoop>& m_evtloop;
        RegisterDeviceInterface<DEV> m_devs;
        int m_dev_no{-1};
        uint64_t m_fid_reply{0};
        uint64_t m_fid_cmd{0};

        //Owned resources
        Bus m_bus_rep_tx;
        Bus m_bus_cmd_rx;
        bool m_do_reply;

        RegisterMsgParser m_parser;
        std::unique_ptr<Receiver>  m_cmd_receiver;
        std::unique_ptr<Publisher> m_reply_publisher;
};


template <class DEV>
RegisterDeviceController<DEV>::RegisterDeviceController(
    std::variant<NetioEventLoop> & evtloop,
    RegmapManager &                regmap,
    std::shared_ptr<DEV>           open_device,
    std::shared_ptr<DEV>           open_primary_device,
    const ConfigRegister &         c,
    unsigned int                   d_idx)
        : m_evtloop(evtloop), m_devs(regmap, open_device, open_primary_device), m_dev_no(c.dconfs.at(d_idx).dev_no),
          m_fid_reply(c.dconfs.at(d_idx).fid_reply), m_fid_cmd(c.dconfs.at(d_idx).fid_cmd),
          m_bus_rep_tx{c.bus_dir, c.bus_group, "register-pub-"+std::to_string(m_dev_no), c.verbose_bus},
          m_bus_cmd_rx{c.bus_dir, c.bus_group, "register-cmd-"+std::to_string(m_dev_no), c.verbose_bus},
          m_do_reply{c.enable_reply}
{

    constexpr static int net_pages{64};
    constexpr static int net_page_size{4096};
    constexpr static int net_page_timeout{0};

    auto fid_cmd = Elink(m_fid_cmd);
    auto fid_reply = Elink(m_fid_reply);

    if (c.enable_cmd) {
        int port_cmd = c.dconfs.at(d_idx).port_cmd;
        m_cmd_receiver = network::utility::create_buffered_receiver(c.evloop_type, c.local_ip, port_cmd, m_bus_cmd_rx, net_pages, net_page_size);
        m_cmd_receiver->declare({fid_cmd});
        m_cmd_receiver->set_conn_open_callback([this](const std::string& s){cmd_connection_established(s);});
        m_cmd_receiver->set_conn_close_callback([this](const std::string& s){cmd_connection_closed(s);});
        m_cmd_receiver->set_on_msg_callback([this](const std::vector<ToFlxMessage>& messages){process_message(messages);});
    }

    int port_reply = c.dconfs.at(d_idx).port_reply;
    m_reply_publisher = network::utility::create_buffered_publisher(c.evloop_type, c.local_ip, port_reply, m_bus_rep_tx, net_pages, net_page_size, net_page_size, net_page_timeout, UINT32_MAX);

    m_reply_publisher->declare({fid_reply});
    //evloop started by owner
}


template <class DEV>
std::string RegisterDeviceController<DEV>::process_requests(std::vector<ReqData>& req_vec)
{
    for (auto& cmd_msg : req_vec) {
    //Further processing only if the command is correct
        if (cmd_msg.status_code == FelixClientThread::OK) {
            switch(cmd_msg.cmd) {
                case FelixClientThread::Cmd::GET:
                {
                    if (not m_devs.can_read(cmd_msg.resource_name)) {
                        LOG_ERR("Remote endpoint requested read of invalid register %s", cmd_msg.resource_name);
                        cmd_msg.status_code = FelixClientThread::ERROR_INVALID_REGISTER;
                        cmd_msg.status_message = std::string("Invalid register name '") + cmd_msg.resource_name + "'";
                    } else {
                        cmd_msg.value = std::to_string(m_devs.get_register(cmd_msg.resource_name));
                        LOG_DBG("Read %d, value %lu", cmd_msg.resource_name, cmd_msg.value);
                    }
                    break;
                }
                case FelixClientThread::Cmd::SET:
                {
                    if (not m_devs.can_write(cmd_msg.resource_name)) {
                        LOG_ERR("Remote endpoint requested invalid write of register %s", cmd_msg.resource_name);
                        cmd_msg.status_code = FelixClientThread::ERROR_NOT_AUTHORIZED;
                        cmd_msg.status_message = std::string("Not authorized to set register name '") + cmd_msg.resource_name + "'";
                    } else {
                        uint64_t value = std::stoul(cmd_msg.value, nullptr, 0);
                        m_devs.set_register(cmd_msg.resource_name, value);
                        LOG_DBG("Set %d to %lu", cmd_msg.resource_name, value);
                    }
                    break;
                }
                case FelixClientThread::Cmd::ECR_RESET:
                {
                    uint64_t value = std::stoul(cmd_msg.value, nullptr, 0);
                    m_devs.set_register("TTC_DEC_CTRL_XL1ID_SW", value);
                    m_devs.set_register("TTC_DEC_CTRL_XL1ID_RST", 0x1);
                    m_devs.set_register("TTC_DEC_CTRL_XL1ID_RST", 0x0);
                    break;
                }
                default:
                {
                    LOG_ERR("Command (code %d) correctly parsed cannot be processed", cmd_msg.cmd);
                    break;
                }
            }
        }
    }
    return m_parser.encode_replies(m_fid_reply, req_vec);
}


template <class DEV>
void RegisterDeviceController<DEV>::process_message(const std::vector<ToFlxMessage>& messages)
{
    for (const auto& msg : messages) {
        std::vector<ReqData> parsed_msgs = m_parser.parse_commands(reinterpret_cast<const char*>(msg.payload.data()), msg.payload.size());
        std::string reply_msg = "0"; //status byte
        reply_msg += process_requests(parsed_msgs);
        auto reply_ptr = reinterpret_cast<uint8_t*>(reply_msg.data());
        if (m_do_reply){
            m_reply_publisher->publish(m_fid_reply, reply_ptr, reply_msg.size());
            m_reply_publisher->flush(m_fid_reply);
        }
    }
}

#endif /* DEVICE_REGISTER_CONTROLLER_H */