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 */