LCOV - code coverage report
Current view: top level - daphnemodules/src - DaphneV3Interface.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 62.5 % 72 45
Test Date: 2026-10-11 15:05:20 Functions: 62.5 % 8 5

            Line data    Source code
       1              : /**
       2              :  * @file DaphneV3Interface.cpp
       3              :  *
       4              :  * Implementations of DaphneV3Interface's functions
       5              :  *
       6              :  * This is part of the DUNE DAQ Software Suite, copyright 2020.
       7              :  * Licensing/copyright details are in the COPYING file that you should have
       8              :  * received with this code.
       9              :  *
      10              :  */
      11              : 
      12              : #include "DaphneV3Interface.hpp"
      13              : 
      14              : #include "logging/Logging.hpp"
      15              : #include <fmt/format.h>
      16              : 
      17              : #include <regex>
      18              : #include <string>
      19              : #include <utility>
      20              : 
      21              : using namespace dunedaq::daphnemodules;
      22              : using namespace daphne;
      23              : 
      24            4 : DaphneV3Interface::DaphneV3Interface(std::string address, std::string routing, std::chrono::milliseconds timeout)
      25            4 :   : m_context(1)
      26            4 :   , m_socket(m_context, zmq::socket_type::dealer)
      27            4 :   , m_timeout(timeout)
      28              : {
      29              : 
      30            4 :   m_socket.set(zmq::sockopt::routing_id, routing);
      31            4 :   auto value = (int)m_timeout.count(); // NOLINT
      32            8 :   TLOG() << routing << " timeout set to " << value << " ms";
      33            4 :   m_socket.set(zmq::sockopt::rcvtimeo, value);
      34            4 :   m_socket.set(zmq::sockopt::sndtimeo, value);
      35            4 :   m_socket.set(zmq::sockopt::immediate, 1); // Don't queue messages to incomplete connections
      36              : 
      37              :   // find out if the address has a port with a regex
      38            4 :   static const std::regex ip_with_port(R"(^([^\/\s:]+)(?::(\d{1,5}))?$)");
      39            4 :   std::smatch string_values;
      40            4 :   if (!std::regex_match(address, string_values, ip_with_port)) {
      41            2 :     throw InvalidIPAddress(ERS_HERE, address);
      42              :   }
      43              : 
      44            6 :   m_connection = string_values[2].matched ? fmt::format("tcp://{}", address)
      45            2 :                                           : fmt::format("tcp://{}:{}", address, s_default_control_port);
      46            4 :   TLOG() << "Connecting to: " << m_connection;
      47              : 
      48            2 :   m_socket.connect(m_connection);
      49              : 
      50            2 :   auto add = string_values[1];
      51            2 :   auto port = string_values[2].matched ? std::stoi(string_values[2]) : s_default_control_port;
      52              : 
      53            2 :   try {
      54            2 :     if (!validate_connection()) {
      55            0 :       auto add = string_values[1];
      56            0 :       auto port = string_values[2].matched ? std::stoi(string_values[2]) : s_default_control_port;
      57            0 :       throw FailedPing(ERS_HERE, add, port);
      58              :     }
      59            2 :   } catch (const ers::Issue& e) {
      60            2 :     throw FailedPing(ERS_HERE, add, port, e);
      61            2 :   }
      62           16 : }
      63              : 
      64              : void
      65            0 : DaphneV3Interface::close()
      66              : {
      67              : 
      68            0 :   const std::lock_guard<std::mutex> lock(m_access_mutex);
      69            0 :   m_socket.set(zmq::sockopt::linger, 0);
      70            0 :   m_socket.close();
      71            0 : }
      72              : 
      73              : ControlEnvelopeV2
      74            0 : DaphneV3Interface::send(std::string&& message, daphne::MessageTypeV2 type)
      75              : {
      76              : 
      77            0 :   const std::lock_guard<std::mutex> lock(m_access_mutex);
      78              : 
      79            0 :   _send(std::move(message), type);
      80              : 
      81            0 :   return _receive();
      82            0 : }
      83              : 
      84              : void
      85            2 : DaphneV3Interface::_send(std::string&& message, daphne::MessageTypeV2 type)
      86              : {
      87              : 
      88            2 :   ControlEnvelopeV2 env;
      89            2 :   env.set_version(2);
      90            2 :   env.set_dir(DIR_REQUEST);
      91              : 
      92            2 :   env.set_type(type);
      93            2 :   env.set_payload(message);
      94              : 
      95              :   // additional information
      96            2 :   env.set_msg_id(m_message_counter++);
      97            4 :   env.set_timestamp_ns(
      98            2 :     std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count());
      99              : 
     100            2 :   std::string bytes = env.SerializeAsString();
     101              : 
     102            2 :   if (!m_socket.send(zmq::buffer(bytes), zmq::send_flags::none)) {
     103            2 :     throw FailedSend(ERS_HERE, MessageTypeV2_Name(type));
     104              :   }
     105            4 : }
     106              : 
     107              : ControlEnvelopeV2
     108            0 : DaphneV3Interface::_receive()
     109              : {
     110              : 
     111            0 :   zmq::message_t reply;
     112            0 :   if (!m_socket.recv(reply, zmq::recv_flags::none)) {
     113              :     // timeout or EAGAIN
     114            0 :     throw FailedReceive(ERS_HERE, m_connection);
     115              :   }
     116            0 :   if (reply.size() <= 0) {
     117            0 :     throw EmptyPayload(ERS_HERE, m_connection);
     118              :   }
     119              : 
     120            0 :   ControlEnvelopeV2 rep;
     121            0 :   if (!rep.ParseFromArray(reply.data(), static_cast<int>(reply.size()))) {
     122            0 :     throw FailedDecoding(ERS_HERE, rep.GetTypeName(), reply.to_string());
     123              :   }
     124              : 
     125            0 :   if (rep.version() != 2 || rep.dir() != DIR_RESPONSE) {
     126            0 :     ers::warning(UnexpectedDirection(ERS_HERE, rep.version(), Direction_Name(rep.dir()), reply.to_string()));
     127              :   }
     128              : 
     129            0 :   return rep;
     130            0 : }
     131              : 
     132              : bool
     133            2 : DaphneV3Interface::validate_connection()
     134              : {
     135            2 :   static const uint64_t good_value = 0xdeadbeef; // NOLINT
     136              : 
     137            2 :   TestRegRequest req; // empty
     138            2 :   auto reply = send<TestRegResponse>(
     139            2 :     req.SerializeAsString(), MessageTypeV2::MT2_READ_TEST_REG_REQ, MessageTypeV2::MT2_READ_TEST_REG_RESP);
     140              : 
     141            0 :   return reply.value() == good_value;
     142            2 : }
        

Generated by: LCOV version 2.0-1