LCOV - code coverage report
Current view: top level - tdemodules/src - AMCProtocolClient.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 0.0 % 66 0
Test Date: 2026-10-11 15:05:20 Functions: 0.0 % 5 0

            Line data    Source code
       1              : #include "tdemodules/AMCProtocolClient.hpp"
       2              : 
       3              : #include "utilities.hpp"
       4              : #include <boost/lexical_cast.hpp>
       5              : #include <fmt/ranges.h>
       6              : 
       7              : #include "logging/Logging.hpp"
       8              : 
       9              : namespace dunedaq {
      10              : namespace tdemodules {
      11              : 
      12            0 : AMCProtocolClient::AMCProtocolClient(const std::string& server_ip, uint16_t port)
      13            0 :   : m_host(server_ip)
      14            0 :   , m_port(port)
      15            0 :   , m_timeout(50)
      16            0 :   , m_io_context()
      17            0 :   , m_socket(m_io_context)
      18              : {
      19              : 
      20            0 :   using boost::asio::ip::udp;
      21            0 :   udp::resolver resolver(m_io_context);
      22            0 :   m_server_endpoint = *resolver.resolve(udp::v4(), m_host, std::to_string(m_port)).begin();
      23              : 
      24            0 :   m_socket.open(udp::v4());
      25            0 :   m_socket.non_blocking(true);
      26            0 : }
      27              : 
      28              : std::vector<uint8_t>
      29            0 : AMCProtocolClient::send_request(TFTPOpCode opcode, const std::vector<uint8_t>& payload)
      30              : {
      31              : 
      32              :   // Build TFTP RRQ or WRQ packet
      33              :   // Switch to uin8_t
      34            0 :   std::vector<uint8_t> request;
      35              :   // request.push_back(static_cast<char>(opcode >> 8));
      36              :   // request.push_back(static_cast<char>(opcode & 0xFF));
      37              :   // append_bigendian(request, opcode);
      38            0 :   append_big_uint16(request, opcode);
      39            0 :   request.insert(request.end(), payload.begin(), payload.end());
      40              : 
      41            0 :   std::stringstream request_str;
      42            0 :   for (size_t i = 0; i < request.size(); ++i) {
      43            0 :     request_str << std::setfill('0') << std::setw(2) << std::hex << +request[i] << " ";
      44              :   }
      45            0 :   TLOG() << "sending request to AMC: " << request_str.str();
      46              : 
      47            0 :   m_socket.send_to(boost::asio::buffer(request), m_server_endpoint);
      48              : 
      49            0 :   std::vector<uint8_t> reply(516); // TFTP packets max ~516 bytes
      50              : 
      51            0 :   boost::asio::ip::udp::endpoint sender_endpoint;
      52            0 :   boost::system::error_code ec;
      53              : 
      54            0 :   size_t len = 0;
      55            0 :   for (int i = 0; i < m_timeout; ++i) { // wait up to ~5 seconds total
      56            0 :     std::this_thread::sleep_for(std::chrono::milliseconds(100));
      57            0 :     len = m_socket.receive_from(boost::asio::buffer(reply), sender_endpoint, 0, ec);
      58            0 :     if (!ec)
      59              :       break;
      60              :   }
      61              : 
      62            0 :   if (ec) {
      63            0 :     throw AMCProtocolIssue(ERS_HERE, m_log_prefix, "TFTP receive error: " + ec.message());
      64              :   }
      65              : 
      66            0 :   if (len < 4) {
      67            0 :     throw AMCProtocolIssue(ERS_HERE, m_log_prefix, "Invalid TFTP reply: too short");
      68              :   }
      69            0 :   if (reply.size() < 4)
      70            0 :     throw std::runtime_error("Packet too short to be valid");
      71              : 
      72              :   // Parse returin code
      73            0 :   boost::endian::big_uint16_t rpl_opcode_be;
      74            0 :   std::memcpy(&rpl_opcode_be, reply.data(), sizeof(rpl_opcode_be));
      75              : 
      76              :   // A bit of overcasting here?
      77            0 :   TFTPOpCode rpl_opcode = static_cast<TFTPOpCode>(static_cast<uint16_t>(rpl_opcode_be));
      78              : 
      79            0 :   switch (rpl_opcode) {
      80            0 :     case TFTPOpCode::DAT: {
      81            0 :       if (reply.size() < sizeof(TFTP_Data_Header))
      82              :         throw AMCProtocolIssue(ERS_HERE, m_log_prefix, "Incomplete DATA packet");
      83              : 
      84            0 :       TFTP_Data_Header header;
      85            0 :       std::memcpy(&header, reply.data(), sizeof(header));
      86              : 
      87            0 :       TLOG() << "Received DATA packet:\n"
      88            0 :              << "  Block #: " << static_cast<uint16_t>(header.block) << "\n"
      89            0 :              << "  Payload size: " << reply.size() - sizeof(header) << " bytes\n";
      90              : 
      91              :       // Data starts at byte 3
      92            0 :       reply.resize(len);
      93              :       // Remove opcode
      94            0 :       reply.erase(reply.begin(), reply.begin() + 2);
      95              : 
      96            0 :       return reply;
      97              :     }
      98              : 
      99            0 :     case TFTPOpCode::ACK: {
     100            0 :       TFTP_Ack_Header header;
     101            0 :       std::memcpy(&header, reply.data(), sizeof(header));
     102              : 
     103            0 :       TLOG() << m_log_prefix << "Recieved Ack packet:\n" << static_cast<uint16_t>(header.block);
     104            0 :       return {};
     105              :     }
     106              : 
     107            0 :     case TFTPOpCode::ERR: {
     108            0 :       TFTP_Error_Header header;
     109            0 :       std::memcpy(&header, reply.data(), sizeof(header));
     110              : 
     111            0 :       std::string error_msg(reinterpret_cast<const char*>(reply.data() + sizeof(header)),
     112            0 :                             reply.size() - sizeof(header));
     113              : 
     114            0 :       ers::error(AMCResponseErr(ERS_HERE, m_log_prefix, static_cast<uint16_t>(header.error_code), error_msg));
     115            0 :       return {};
     116            0 :     }
     117              : 
     118            0 :     default:
     119            0 :       ers::error(AMCUnknownOpCode(ERS_HERE, m_log_prefix, static_cast<uint16_t>(opcode)));
     120              : 
     121            0 :       return {};
     122              :   }
     123            0 : }
     124              : 
     125              : } // namespace tdemodules
     126              : } // namespace dunedaq
        

Generated by: LCOV version 2.0-1