Line data Source code
1 : #include "tdemodules/AMCProtocolClient.hpp"
2 :
3 : #include <boost/lexical_cast.hpp>
4 : #include <fmt/ranges.h>
5 : #include "utilities.hpp"
6 :
7 : #include "logging/Logging.hpp"
8 :
9 : namespace dunedaq {
10 : namespace tdemodules {
11 :
12 :
13 0 : AMCProtocolClient::AMCProtocolClient(const std::string& server_ip, uint16_t port) :
14 0 : m_host(server_ip),
15 0 : m_port(port),
16 0 : m_timeout(50),
17 0 : m_io_context(),
18 0 : m_socket(m_io_context)
19 : {
20 :
21 0 : using boost::asio::ip::udp;
22 0 : udp::resolver resolver(m_io_context);
23 0 : m_server_endpoint= *resolver.resolve(udp::v4(), m_host, std::to_string(m_port)).begin();
24 :
25 0 : m_socket.open(udp::v4());
26 0 : m_socket.non_blocking(true);
27 :
28 0 : }
29 :
30 : std::vector<uint8_t>
31 0 : AMCProtocolClient::send_request(TFTPOpCode opcode, const std::vector<uint8_t>& payload) {
32 :
33 : // Build TFTP RRQ or WRQ packet
34 : // Switch to uin8_t
35 0 : std::vector<uint8_t> request;
36 : // request.push_back(static_cast<char>(opcode >> 8));
37 : // request.push_back(static_cast<char>(opcode & 0xFF));
38 : // append_bigendian(request, opcode);
39 0 : append_big_uint16(request, opcode);
40 0 : request.insert( request.end(), payload.begin(), payload.end());
41 :
42 0 : std::stringstream request_str;
43 0 : for (size_t i = 0; i < request.size(); ++i) {
44 0 : request_str << std::setfill('0') << std::setw(2) << std::hex << +request[i] << " ";
45 : }
46 0 : TLOG() << "sending request to AMC: " << request_str.str();
47 :
48 0 : m_socket.send_to(boost::asio::buffer(request), m_server_endpoint);
49 :
50 0 : std::vector<uint8_t> reply(516); // TFTP packets max ~516 bytes
51 :
52 0 : boost::asio::ip::udp::endpoint sender_endpoint;
53 0 : boost::system::error_code ec;
54 :
55 0 : size_t len = 0;
56 0 : for (int i = 0; i < m_timeout; ++i) { // wait up to ~5 seconds total
57 0 : std::this_thread::sleep_for(std::chrono::milliseconds(100));
58 0 : len = m_socket.receive_from(boost::asio::buffer(reply), sender_endpoint, 0, ec);
59 0 : if (!ec) 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 :
126 :
127 :
128 : } // namespace tdemodules
129 : } // namespace dunedaq
130 :
|