LCOV - code coverage report
Current view: top level - ctbmodules/plugins - CTBModule.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 0.0 % 479 0
Test Date: 2026-10-11 15:05:20 Functions: 0.0 % 61 0

            Line data    Source code
       1              : /**
       2              :  * @file CTBModule.cpp CTBModule class
       3              :  * implementation
       4              :  *
       5              :  * This is part of the DUNE DAQ Software Suite, copyright 2020.
       6              :  * Licensing/copyright details are in the COPYING file that you should have
       7              :  * received with this code.
       8              :  */
       9              : 
      10              : #include "confmodel/GeoId.hpp"
      11              : 
      12              : #include "appmodel/CTBCalibrationStream.hpp"
      13              : #include "appmodel/CTBConf.hpp"
      14              : #include "appmodel/CTBCountLLT.hpp"
      15              : #include "appmodel/CTBHLT.hpp"
      16              : #include "appmodel/CTBLLT.hpp"
      17              : #include "appmodel/CTBMisc.hpp"
      18              : #include "appmodel/CTBRandomTrigger.hpp"
      19              : #include "appmodel/CTBReceiverSocket.hpp"
      20              : #include "appmodel/CTBSockets.hpp"
      21              : #include "appmodel/CTBoardConf.hpp"
      22              : 
      23              : #include "CTBModule.hpp"
      24              : #include "CTBModuleIssues.hpp"
      25              : 
      26              : #include "iomanager/IOManager.hpp"
      27              : #include "logging/Logging.hpp"
      28              : 
      29              : #include <chrono>
      30              : #include <map>
      31              : #include <memory>
      32              : #include <queue>
      33              : #include <string>
      34              : #include <thread>
      35              : #include <utility>
      36              : #include <vector>
      37              : 
      38              : /**
      39              :  * @brief Name used by TRACE TLOG calls from this source file
      40              :  */
      41              : #define TRACE_NAME "CTBModule" // NOLINT
      42              : enum
      43              : {
      44              :   TLVL_ENTER_EXIT_METHODS = 10,
      45              :   TLVL_CTB_MODULE = 15
      46              : };
      47              : 
      48              : constexpr uint16_t CTB_HSI_FRAME_VERSION = 0x1; // NOLINT
      49              : 
      50              : using namespace dunedaq;
      51              : using namespace ctbmodules;
      52              : 
      53            0 : CTBModule::CTBModule(const std::string& name)
      54              :   : hsilibs::HSIEventSender(name)
      55            0 :   , m_is_running(false)
      56            0 :   , m_stop_requested(false)
      57            0 :   , m_is_configured(false)
      58            0 :   , m_error_state(false)
      59            0 :   , m_total_hlt_counter(0)
      60            0 :   , m_ts_word_counter(0)
      61            0 :   , m_hlt_trigger_counter()
      62            0 :   , m_llt_trigger_counter()
      63            0 :   , m_control_ios()
      64            0 :   , m_receiver_ios()
      65            0 :   , m_control_socket(m_control_ios)
      66            0 :   , m_receiver_socket(m_receiver_ios)
      67            0 :   , m_thread_(std::bind(&CTBModule::do_hsi_work, this, std::placeholders::_1))
      68            0 :   , m_has_calibration_stream(false)
      69            0 :   , m_run_HLT_counter(0)
      70            0 :   , m_run_LLT_counter(0)
      71            0 :   , m_run_channel_status_counter(0)
      72            0 :   , m_num_control_messages_sent(0)
      73            0 :   , m_num_control_responses_received(0)
      74            0 :   , m_last_readout_hlt_timestamp(0)
      75              : {
      76            0 :   register_command("conf", &CTBModule::do_configure);
      77            0 :   register_command("start", &CTBModule::do_start);
      78            0 :   register_command("stop", &CTBModule::do_stop);
      79            0 : }
      80              : 
      81            0 : CTBModule::~CTBModule()
      82              : {
      83              :   // check if running. and in case stop the run
      84            0 :   if (m_is_running) {
      85            0 :     const CommandData_t stopobj;
      86            0 :     do_stop(stopobj);
      87            0 :   }
      88            0 :   m_control_socket.close();
      89            0 : }
      90              : 
      91              : void
      92            0 : CTBModule::init(std::shared_ptr<appfwk::ConfigurationManager> cfgMgr)
      93              : {
      94            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering init() method";
      95              : 
      96            0 :   HSIEventSender::init(cfgMgr);
      97              : 
      98            0 :   m_cfg = cfgMgr;
      99              : 
     100            0 :   auto mdal = cfgMgr->get_dal<appmodel::CTBModule>(get_name());
     101              : 
     102            0 :   if (!mdal) {
     103            0 :     throw ctbmodules::CTBConfigFailure(ERS_HERE, "Missing Module configuration for " + get_name());
     104              :   }
     105              : 
     106            0 :   m_module = mdal;
     107              : 
     108              :   // setting up connections
     109            0 :   auto iom = iomanager::IOManager::get();
     110              : 
     111            0 :   using hsi_frame_t = dunedaq::hsilibs::HSI_FRAME_STRUCT;
     112            0 :   for (auto con : m_module->get_outputs()) {
     113            0 :     if (con->get_data_type() == datatype_to_string<hsi_frame_t>()) {
     114            0 :       if (con->UID().find("HLT") != std::string::npos || con->UID().find("hlt") != std::string::npos) {
     115            0 :         m_hlt_hsi_data_sender = iom->get_sender<hsi_frame_t>(con->UID());
     116              :       }
     117            0 :       if (con->UID().find("LLT") != std::string::npos || con->UID().find("llt") != std::string::npos) {
     118            0 :         m_llt_hsi_data_sender = iom->get_sender<hsi_frame_t>(con->UID());
     119              :       }
     120              :     } // if data type is HSI Frame
     121              :   } // loop over outputs
     122              : 
     123            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting init() method";
     124            0 : }
     125              : 
     126              : void
     127            0 : CTBModule::do_configure(const CommandData_t&)
     128              : {
     129              : 
     130            0 :   TLOG_DEBUG(0) << get_name() << ": Configuring CTB";
     131              : 
     132            0 :   auto conf = m_module->get_configuration();
     133              : 
     134            0 :   m_receiver_port = m_module->get_board()->get_sockets()->get_receiver()->get_port();
     135            0 :   m_timeout = std::chrono::milliseconds(conf->get_connection_timeout_ms());
     136              : 
     137            0 :   auto hostname = conf->get_hostname();
     138            0 :   TLOG() << get_name() << ": Board receiver network location " << hostname << ':' << m_receiver_port << std::endl;
     139              : 
     140              :   // Initialise monitoring variables
     141            0 :   m_num_control_messages_sent = 0;
     142            0 :   m_num_control_responses_received = 0;
     143            0 :   m_ts_word_counter = 0;
     144              : 
     145            0 :   std::map<std::string, size_t> id_to_idx;
     146            0 :   for (size_t i = 0; i < m_hlt_range; i++)
     147            0 :     id_to_idx["HLT_" + std::to_string(i)] = i;
     148            0 :   for (size_t i = 0; i < m_llt_range; i++)
     149            0 :     id_to_idx["LLT_" + std::to_string(i)] = i;
     150              : 
     151              :   // configuring the board
     152            0 :   auto board = m_module->get_board();
     153            0 :   auto geo_id = board->get_geo_id();
     154            0 :   m_det = geo_id->get_detector_id();
     155            0 :   m_crate = geo_id->get_crate_id();
     156            0 :   m_slot = geo_id->get_slot_id();
     157              : 
     158            0 :   const auto& misc = board->get_misc();
     159            0 :   auto session = m_cfg->get_session();
     160              :   // HLTs
     161              :   // 0th HLT is random trigger that's not in HLT array
     162            0 :   if (!misc->get_randomtrigger_1()->is_excluded(*session))
     163            0 :     m_hlt_trigger_counter[0] = 0;
     164              : 
     165            0 :   auto hlts = board->get_HLTs();
     166            0 :   for (const auto& hlt : hlts) {
     167            0 :     if (!hlt->is_excluded(*session))
     168            0 :       m_hlt_trigger_counter[id_to_idx[hlt->UID()]] = 0;
     169              :   }
     170              : 
     171              :   // LLTs: Beam and CRT
     172              :   // 0th LLT is random trigger that's not in HLT array
     173            0 :   if (!misc->get_randomtrigger_2()->is_excluded(*session))
     174            0 :     m_llt_trigger_counter[0] = 0;
     175              : 
     176            0 :   auto beam_llts = board->get_beam_LLTs();
     177            0 :   for (const auto& llt : beam_llts) {
     178            0 :     if (!llt->is_excluded(*session))
     179            0 :       m_llt_trigger_counter[id_to_idx[llt->UID()]] = 0;
     180              :   }
     181              : 
     182            0 :   auto crt_llts = board->get_CRT_LLTs();
     183            0 :   for (const auto& llt : crt_llts) {
     184            0 :     if (!llt->is_excluded(*session))
     185            0 :       m_llt_trigger_counter[id_to_idx[llt->UID()]] = 0;
     186              :   }
     187              : 
     188              :   // network connection to ctb hardware control
     189            0 :   boost::asio::ip::tcp::resolver resolver(m_control_ios);
     190            0 :   boost::asio::ip::tcp::resolver::query query(hostname,
     191            0 :                                               std::to_string(conf->get_control_connection_port())); //"np04-ctb-1", 8991
     192            0 :   boost::asio::ip::tcp::resolver::iterator iter = resolver.resolve(query);
     193              : 
     194            0 :   m_endpoint = iter->endpoint();
     195            0 :   m_control_socket.connect(m_endpoint);
     196              : 
     197              :   // if necessary, set the calibration stream
     198            0 :   auto stream_conf = conf->get_calibration_stream();
     199            0 :   if (stream_conf) {
     200            0 :     m_has_calibration_stream = true;
     201            0 :     m_calibration_dir = stream_conf->get_directory();
     202            0 :     m_calibration_file_interval = std::chrono::duration_cast<decltype(m_calibration_file_interval)>(
     203            0 :       std::chrono::seconds(stream_conf->get_update_period_s()));
     204            0 :     ;
     205              :   }
     206              : 
     207              :   // at this point we have to find the hostname to tell the board what to get
     208            0 :   boost::asio::ip::tcp::resolver::query query_for_local(boost::asio::ip::host_name(), "");
     209            0 :   iter = resolver.resolve(query_for_local);
     210              : 
     211              :   // create the json string
     212            0 :   auto json_conf = m_module->get_board()->get_ctb_json(*session, iter->endpoint().address().to_string());
     213              : 
     214            0 :   auto json_dump = json_conf.dump();
     215              : 
     216            0 :   TLOG() << "Sending configuration: " << json_dump;
     217              : 
     218            0 :   send_config(json_dump);
     219            0 : }
     220              : 
     221              : void
     222            0 : CTBModule::do_start(const CommandData_t& startobj)
     223              : {
     224              : 
     225            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering do_start() method";
     226              : 
     227              :   // Set this to false early so it doesn't interfere with the start
     228            0 :   m_stop_requested.store(false);
     229              : 
     230            0 :   m_run_number.store(startobj.at("run").get<daqdataformats::run_number_t>());
     231              : 
     232            0 :   m_total_hlt_counter.store(0);
     233              : 
     234            0 :   TLOG_DEBUG(0) << get_name() << ": Sending start of run command";
     235            0 :   m_thread_.start_working_thread();
     236              : 
     237            0 :   if (m_has_calibration_stream) {
     238            0 :     std::stringstream run;
     239            0 :     run << "run" << m_run_number.load();
     240            0 :     SetCalibrationStream(run.str());
     241            0 :   }
     242              : 
     243            0 :   if (send_message("{\"command\":\"StartRun\"}")) {
     244            0 :     m_is_running.store(true);
     245            0 :     TLOG_DEBUG(1) << get_name() << ": successfully started";
     246              :   } else {
     247            0 :     throw CTBCommunicationError(ERS_HERE, "Unable to start CTB");
     248              :   }
     249              : 
     250            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting do_start() method";
     251            0 : }
     252              : 
     253              : void
     254            0 : CTBModule::do_stop(const CommandData_t& /*stopobj*/)
     255              : {
     256              : 
     257            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering do_stop() method";
     258              : 
     259            0 :   TLOG_DEBUG(0) << get_name() << ": Sending stop run command" << std::endl;
     260              : 
     261              :   // Give the do_work thread a chance to stop before stopping the CTB,
     262              :   // otherwise we end up reading from an empty buffer
     263            0 :   m_stop_requested.store(true);
     264            0 :   std::this_thread::sleep_for(std::chrono::milliseconds(2));
     265              : 
     266            0 :   if (send_message("{\"command\":\"StopRun\"}")) {
     267            0 :     TLOG_DEBUG(1) << get_name() << ": successfully stopped";
     268            0 :     m_is_running.store(false);
     269              :   } else {
     270            0 :     throw CTBCommunicationError(ERS_HERE, "Unable to stop CTB");
     271              :   }
     272            0 :   m_thread_.stop_working_thread();
     273              : 
     274            0 :   m_run_HLT_counter = 0;
     275            0 :   m_run_LLT_counter = 0;
     276            0 :   m_run_channel_status_counter = 0;
     277              : 
     278            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting do_stop() method";
     279            0 : }
     280              : 
     281              : void
     282            0 : CTBModule::do_hsi_work(std::atomic<bool>& running_flag)
     283              : {
     284            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering do_work() method";
     285              : 
     286            0 :   std::size_t n_bytes = 0;
     287            0 :   std::size_t n_words = 0;
     288              : 
     289            0 :   const size_t header_size = sizeof(content::tcp_header_t);
     290            0 :   const size_t word_size = content::word::word_t::size_bytes;
     291              : 
     292            0 :   TLOG_DEBUG(TLVL_CTB_MODULE) << get_name() << ": Header size: " << header_size << std::endl
     293            0 :                               << "Word size: " << word_size << std::endl;
     294              : 
     295              :   // connect to socket
     296            0 :   boost::asio::ip::tcp::acceptor acceptor(m_receiver_ios,
     297            0 :                                           boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), m_receiver_port));
     298            0 :   TLOG_DEBUG(0) << get_name() << ": Waiting for an incoming connection on port " << m_receiver_port << std::endl;
     299              : 
     300            0 :   std::future<void> accepting = async(std::launch::async, [&] { acceptor.accept(m_receiver_socket); });
     301              : 
     302            0 :   while (running_flag.load() && !m_stop_requested.load()) {
     303            0 :     if (accepting.wait_for(m_timeout) == std::future_status::ready) {
     304              :       break;
     305              :     }
     306              :   }
     307              : 
     308            0 :   TLOG_DEBUG(0) << get_name() << ": Connection received: start reading" << std::endl;
     309              : 
     310            0 :   content::tcp_header_t head;
     311            0 :   head.packet_size = 0;
     312            0 :   content::word::word_t temp_word;
     313            0 :   bool connection_closed = false;
     314            0 :   uint64_t ch_stat_beam, ch_stat_crt, ch_stat_pds; // NOLINT
     315            0 :   uint64_t prev_timestamp = 0;                     // NOLINT
     316            0 :   ts_payload prev_hlt, prev_llt, prev_ch_stat;
     317            0 :   ts_payload curr_hlt, curr_llt, curr_ch_stat;
     318              :   // buffers for word matching. buf_a are the trigger words, buf_b are corresponding payloads
     319            0 :   std::queue<content::word::trigger_t> match_buf_a_hlts, match_buf_a_llts;
     320            0 :   std::queue<ts_payload> match_buf_b_llts, match_buf_b_chstatus;
     321              : 
     322            0 :   while (running_flag.load() && !m_stop_requested.load()) {
     323              : 
     324            0 :     update_calibration_file();
     325              : 
     326            0 :     if (!read(head)) {
     327            0 :       connection_closed = true;
     328              :       break;
     329              :     }
     330              : 
     331            0 :     n_bytes = head.packet_size;
     332              :     // extract n_words
     333              : 
     334            0 :     n_words = n_bytes / word_size;
     335              :     // read n words as requested from the header
     336              : 
     337            0 :     update_buffer_counts(n_words);
     338              : 
     339            0 :     for (unsigned int i = 0; i < n_words; ++i) {
     340              : 
     341            0 :       if (!running_flag.load() || m_stop_requested.load()) {
     342              :         break;
     343              :       }
     344              : 
     345              :       // read a word
     346            0 :       if (!read(temp_word)) {
     347              :         connection_closed = true;
     348              :         break;
     349              :       }
     350              :       // put it in the calibration stream
     351            0 :       if (m_has_calibration_stream) {
     352            0 :         m_calibration_file.write(reinterpret_cast<const char*>(&temp_word), word_size); // NOLINT
     353            0 :         m_calibration_file.flush();
     354              :       } // word printing in calibration stream
     355              : 
     356              :       // check if it is a TS word and increment the counter
     357            0 :       if (IsTSWord(temp_word)) {
     358            0 :         ++m_ts_word_counter;
     359            0 :         TLOG_DEBUG(9) << "Received timestamp word! TS: " + temp_word.timestamp;
     360            0 :         prev_timestamp = temp_word.timestamp;
     361            0 :       } else if (IsFeedbackWord(temp_word)) {
     362            0 :         m_error_state.store(true);
     363            0 :         content::word::feedback_t* feedback = reinterpret_cast<content::word::feedback_t*>(&temp_word); // NOLINT
     364            0 :         TLOG_DEBUG(7) << "Received feedback word!";
     365              : 
     366            0 :         TLOG_DEBUG(8) << get_name() << ": Feedback word: " << std::endl
     367            0 :                       << std::hex << " \t Type -> " << feedback->word_type << std::endl
     368            0 :                       << " \t TS -> " << feedback->timestamp << std::endl
     369            0 :                       << " \t Code -> " << feedback->code << std::endl
     370            0 :                       << " \t Source -> " << feedback->source << std::endl
     371            0 :                       << " \t Padding -> " << feedback->padding << std::dec << std::endl;
     372            0 :       } else if (temp_word.word_type == content::word::t_gt) {
     373            0 :         TLOG_DEBUG(3) << "Received HLT word! TS: " + temp_word.timestamp;
     374            0 :         content::word::trigger_t* hlt_word = reinterpret_cast<content::word::trigger_t*>(&temp_word); // NOLINT
     375            0 :         curr_hlt = { hlt_word->timestamp, (hlt_word->trigger_word & 0x1FFFFFFFFFFFFFFF) };
     376            0 :         if (check_repeated_word(curr_hlt, prev_hlt, temp_word.word_type))
     377            0 :           continue;
     378            0 :         match_buf_a_hlts.push(*hlt_word);
     379              :         // Count the total HLTs and each specific one
     380            0 :         ++m_run_HLT_counter;
     381            0 :         ++m_total_hlt_counter;
     382            0 :         for (auto& hlt : m_hlt_trigger_counter) {
     383            0 :           if ((hlt_word->trigger_word >> hlt.first) & 0x1)
     384            0 :             ++hlt.second;
     385              :         }
     386            0 :         m_last_readout_hlt_timestamp = temp_word.timestamp;
     387            0 :         prev_hlt = curr_hlt;
     388            0 :       } else if (temp_word.word_type == content::word::t_lt) {
     389            0 :         TLOG_DEBUG(5) << "Received LLT word! TS: " + temp_word.timestamp;
     390            0 :         content::word::trigger_t* llt_word = reinterpret_cast<content::word::trigger_t*>(&temp_word); // NOLINT
     391            0 :         curr_llt = { llt_word->timestamp, (llt_word->trigger_word & 0xFFFFFFFF) };
     392            0 :         if (check_repeated_word(curr_llt, prev_llt, temp_word.word_type))
     393            0 :           continue;
     394            0 :         match_buf_a_llts.push(*llt_word);
     395            0 :         match_buf_b_llts.push(curr_llt);
     396              : 
     397            0 :         ++m_run_LLT_counter;
     398            0 :         for (auto& llt : m_llt_trigger_counter) {
     399            0 :           if ((llt_word->trigger_word >> llt.first) & 0x1)
     400            0 :             ++llt.second;
     401              :         }
     402            0 :         prev_llt = curr_llt;
     403            0 :       } else if (temp_word.word_type == content::word::t_ch) {
     404              : 
     405            0 :         content::word::ch_status_t* ch_stat_word = reinterpret_cast<content::word::ch_status_t*>(&temp_word); // NOLINT
     406              :         // The channel status only has 60b TS so complete the upper 4b from the TS Word. (fyi 60b rolls over >500yr
     407              :         // @ 62.5MHz)
     408            0 :         uint64_t corrected_ts = ((prev_timestamp & 0xF000000000000000) | ch_stat_word->timestamp); // NOLINT
     409            0 :         TLOG_DEBUG(6) << "Received Channel Status word! TS: " + corrected_ts;
     410            0 :         ch_stat_beam = ch_stat_word->get_beam();
     411            0 :         ch_stat_crt = ch_stat_word->get_crt();
     412            0 :         ch_stat_pds = ch_stat_word->get_pds();
     413            0 :         curr_ch_stat = { corrected_ts, ((ch_stat_pds << 48) | (ch_stat_crt << 16) | ch_stat_beam) };
     414            0 :         if (check_repeated_word(curr_ch_stat, prev_ch_stat, temp_word.word_type))
     415            0 :           continue;
     416            0 :         match_buf_b_chstatus.push(curr_ch_stat);
     417            0 :         prev_ch_stat = curr_ch_stat;
     418              : 
     419            0 :         ++m_run_channel_status_counter;
     420              :       } // else if on word types
     421              :       // do matching
     422            0 :       match_between_buffers(match_buf_a_hlts, match_buf_b_llts, prev_hlt.first, content::word::word_type::t_gt);
     423            0 :       match_between_buffers(match_buf_a_llts, match_buf_b_chstatus, prev_llt.first, content::word::word_type::t_lt);
     424              : 
     425              :     } // n_words loop
     426              : 
     427            0 :     if (connection_closed) {
     428              :       break;
     429              :     }
     430              :   }
     431              : 
     432              :   // Make sure CTB run stops before closing socket
     433            0 :   while (m_is_running.load()) {
     434            0 :     std::this_thread::sleep_for(std::chrono::microseconds(100));
     435              :   }
     436              : 
     437            0 :   boost::system::error_code closing_error;
     438              : 
     439            0 :   if (m_error_state.load()) {
     440              : 
     441            0 :     m_receiver_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send, closing_error);
     442              : 
     443            0 :     if (closing_error) {
     444            0 :       std::stringstream msg;
     445            0 :       msg << "Error in shutdown " << closing_error.message();
     446            0 :       ers::error(CTBCommunicationError(ERS_HERE, msg.str()));
     447            0 :     }
     448              :   }
     449              : 
     450            0 :   m_receiver_socket.close(closing_error);
     451              : 
     452            0 :   if (closing_error) {
     453            0 :     std::stringstream msg;
     454            0 :     msg << "Socket closing failed:: " << closing_error.message();
     455            0 :     ers::error(CTBCommunicationError(ERS_HERE, msg.str()));
     456            0 :   }
     457              : 
     458            0 :   TLOG_DEBUG(TLVL_CTB_MODULE) << get_name() << ": End of do_work loop: stop receiving data from the CTB";
     459              : 
     460            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting do_work() method";
     461              : 
     462            0 : } // NOLINT
     463              : 
     464              : bool
     465            0 : CTBModule::check_repeated_word(ts_payload& curr_word, ts_payload& prev_word, uint64_t wtype)
     466              : {                                           // NOLINT
     467            0 :   if (curr_word.first == prev_word.first) { // words with repeated timestamp. Not good!
     468            0 :     std::stringstream msg;
     469            0 :     msg << "Multiple words have the same timestamp, Using the first one. Word type: ";
     470            0 :     if (wtype == content::word::word_type::t_gt)
     471            0 :       msg << "HLT";
     472            0 :     else if (wtype == content::word::word_type::t_lt)
     473            0 :       msg << "LLT";
     474            0 :     else if (wtype == content::word::word_type::t_ch)
     475            0 :       msg << "Channel Status";
     476              :     else
     477            0 :       msg << wtype;
     478            0 :     msg << ", TS: " << curr_word.first << ".";
     479            0 :     if (curr_word.second !=
     480            0 :         prev_word.second) { // not only do we have repeated timestamp, they have different payload...
     481            0 :       msg << " Different payload!! Previous payload: 0x" << std::hex << prev_word.second << " Current payload: 0x"
     482            0 :           << curr_word.second;
     483            0 :       ers::warning(CTBRepeatedTimestampWarning(ERS_HERE, msg.str()));
     484              :     } else {
     485            0 :       msg << " Both have payload 0x" << std::hex << prev_word.second;
     486            0 :       TLOG() << msg.str();
     487              :     }
     488            0 :     return true;
     489            0 :   }
     490              :   return false;
     491              : }
     492              : 
     493              : void
     494            0 : CTBModule::send_matched_trigger_word(const content::word::trigger_t& word, uint64_t payload)
     495              : { // NOLINT
     496              :   // Send HSI data to a DLH
     497            0 :   std::array<uint32_t, 7> hsi_struct; // NOLINT
     498            0 :   bool is_hlt = word.IsHLT();
     499            0 :   hsi_struct[0] = (is_hlt << 26) | // link
     500            0 :                   (m_slot << 22) | (m_crate << 12) | (m_det << 6) | CTB_HSI_FRAME_VERSION;
     501            0 :   hsi_struct[1] = word.timestamp;                                 // ts low
     502            0 :   hsi_struct[2] = word.timestamp >> 32;                           // ts high
     503            0 :   hsi_struct[3] = payload;                                        // lower 32b
     504            0 :   hsi_struct[4] = payload >> 32;                                  // upper 32b (will be 0x0 for llt payloads)
     505            0 :   hsi_struct[5] = word.trigger_word;                              // trigger_map;
     506            0 :   hsi_struct[6] = is_hlt ? m_run_HLT_counter : m_run_LLT_counter; // m_generated_counter;
     507            0 :   int dbg_lvl = is_hlt ? 4 : 6;
     508            0 :   TLOG_DEBUG(dbg_lvl) << get_name() << ": Formed HSI_FRAME_STRUCT for " << (is_hlt ? "HLT" : "LLT") << std::hex << "0x"
     509            0 :                       << hsi_struct[0] << ", 0x" << hsi_struct[1] << ", 0x" << hsi_struct[2] << ", 0x" << hsi_struct[3]
     510            0 :                       << ", 0x" << hsi_struct[4] << ", 0x" << hsi_struct[5] << ", 0x" << hsi_struct[6] << "\n";
     511            0 :   if (is_hlt) {
     512            0 :     send_raw_hsi_data(hsi_struct, m_hlt_hsi_data_sender.get());
     513            0 :     dfmessages::HSIEvent event(m_det, word.trigger_word, word.timestamp, m_run_HLT_counter, m_run_number);
     514            0 :     send_hsi_event(event);
     515              :   } else {
     516            0 :     send_raw_hsi_data(hsi_struct, m_llt_hsi_data_sender.get());
     517              :   }
     518            0 : }
     519              : 
     520              : void
     521            0 : CTBModule::match_between_buffers(std::queue<content::word::trigger_t>& buf_a,
     522              :                                  std::queue<ts_payload>& buf_b,
     523              :                                  uint64_t timeout_reference,
     524              :                                  content::word::word_type buf_a_wtype)
     525              : { // NOLINT
     526            0 :   bool is_hlt = (buf_a_wtype == content::word::word_type::t_gt);
     527            0 :   while (buf_a.size() > 0) {
     528            0 :     content::word::trigger_t trigger = buf_a.front();
     529            0 :     auto trigger_ts = trigger.timestamp;
     530            0 :     auto trigger_word = trigger.trigger_word;
     531            0 :     if (timeout_reference > (trigger_ts + 100)) {
     532            0 :       std::stringstream msg;
     533            0 :       msg << "Time out while waiting for a match for the " << (is_hlt ? "HLT" : "LLT") << ": TS = " << trigger_ts
     534            0 :           << ", trigger word = " << std::hex << "0x" << trigger_word << " Timeout reference: " << std::dec
     535            0 :           << timeout_reference;
     536            0 :       ers::warning(CTBWordMatchWarning(ERS_HERE, msg.str()));
     537            0 :       buf_a.pop();
     538            0 :       continue;
     539            0 :     }
     540            0 :     if (buf_b.size() == 0)
     541              :       break; // no input to match yet. Return for now, wait for matching input to come
     542            0 :     while (buf_b.size() > 0) {
     543            0 :       auto input_ts = buf_b.front().first;
     544            0 :       if (input_ts < (trigger_ts - 1)) { // word is too early. No longer needed
     545            0 :         if (is_hlt)
     546            0 :           last_popped_llt = buf_b.front();
     547              :         else
     548            0 :           last_popped_chstatus = buf_b.front();
     549            0 :         buf_b.pop();
     550            0 :       } else if (input_ts == (trigger_ts - 1)) { // match is found
     551            0 :         send_matched_trigger_word(trigger, buf_b.front().second);
     552            0 :         buf_a.pop();
     553            0 :         if (is_hlt)
     554            0 :           last_popped_llt = buf_b.front();
     555              :         else
     556            0 :           last_popped_chstatus = buf_b.front();
     557            0 :         buf_b.pop();
     558            0 :         break;
     559              :       } else { // buf_b is already past the match window. No matching is found, error!
     560            0 :         if (is_hlt && (trigger_word == 0x1 || trigger_word == (0x1 << 16))) { // Fake HLTs, no matching is OK
     561            0 :           send_matched_trigger_word(trigger, 0);
     562              :         } else {
     563            0 :           std::stringstream msg;
     564            0 :           msg << "No match found for " << (is_hlt ? "HLT" : "LLT") << ": TS = " << trigger_ts << ", trigger word = 0x"
     565            0 :               << std::hex << trigger_word << " Adjacent input ts: " << std::dec
     566            0 :               << (is_hlt ? last_popped_llt.first : last_popped_chstatus.first) << " " << input_ts;
     567            0 :           ers::warning(CTBWordMatchWarning(ERS_HERE, msg.str()));
     568            0 :         }
     569            0 :         buf_a.pop();
     570            0 :         break;
     571              :       }
     572              :     } // end loop buf_b
     573              :   } // end loop buf_a
     574              :   // Don't let buf_b get too long (e.g. when LLT rate is high but HLT rate is low)
     575            0 :   while (buf_b.size() > 32) {
     576            0 :     if (is_hlt)
     577            0 :       last_popped_llt = buf_b.front();
     578              :     else
     579            0 :       last_popped_chstatus = buf_b.front();
     580            0 :     buf_b.pop();
     581              :   }
     582            0 : }
     583              : 
     584              : template<typename T>
     585              : bool
     586            0 : CTBModule::read(T& obj)
     587              : {
     588              : 
     589            0 :   boost::system::error_code receiving_error;
     590            0 :   boost::asio::read(m_receiver_socket, boost::asio::buffer(&obj, sizeof(T)), receiving_error);
     591              : 
     592            0 :   if (!receiving_error) {
     593              :     return true;
     594              :   }
     595              : 
     596            0 :   if (receiving_error == boost::asio::error::eof) {
     597            0 :     std::string error_message = "Socket closed: " + receiving_error.message();
     598            0 :     ers::error(CTBCommunicationError(ERS_HERE, error_message));
     599              :     return false;
     600            0 :   }
     601              : 
     602              :   if (receiving_error) {
     603            0 :     std::string error_message = "Read failure: " + receiving_error.message();
     604            0 :     ers::error(CTBCommunicationError(ERS_HERE, error_message));
     605              :     return false;
     606            0 :   }
     607              : 
     608              :   return true;
     609              : }
     610              : 
     611              : bool
     612            0 : CTBModule::IsTSWord(const content::word::word_t& w) noexcept
     613              : {
     614              : 
     615            0 :   if (w.word_type == content::word::t_ts) {
     616            0 :     return true;
     617              :   }
     618              :   return false;
     619              : }
     620              : 
     621              : bool
     622            0 : CTBModule::IsFeedbackWord(const content::word::word_t& w) noexcept
     623              : {
     624              : 
     625            0 :   if (w.word_type == content::word::t_fback) {
     626            0 :     return true;
     627              :   }
     628              :   return false;
     629              : }
     630              : 
     631              : void
     632            0 : CTBModule::init_calibration_file()
     633              : {
     634              : 
     635            0 :   if (!m_has_calibration_stream) {
     636            0 :     return;
     637              :   }
     638            0 :   std::array<char, 200> file_name;
     639            0 :   time_t rawtime = time(nullptr);
     640            0 :   struct tm timeinfo;
     641            0 :   localtime_r(&rawtime, &timeinfo);
     642            0 :   strftime(file_name.data(), file_name.size(), "%F_%H.%M.%S.calib", &timeinfo);
     643            0 :   std::string global_name = m_calibration_dir + m_calibration_prefix + file_name.data();
     644            0 :   m_calibration_file.open(global_name, std::ofstream::binary);
     645            0 :   m_last_calibration_file_update = std::chrono::steady_clock::now();
     646              :   // _calibration_file.setf ( std::ios::hex, std::ios::basefield );
     647              :   // _calibration_file.unsetf ( std::ios::showbase );
     648            0 :   TLOG_DEBUG(0) << get_name() << ": New Calibration Stream file: " << global_name << std::endl;
     649            0 : }
     650              : 
     651              : void
     652            0 : CTBModule::update_calibration_file()
     653              : {
     654              : 
     655            0 :   if (!m_has_calibration_stream) {
     656            0 :     return;
     657              :   }
     658              : 
     659            0 :   std::chrono::steady_clock::time_point check_point = std::chrono::steady_clock::now();
     660              : 
     661            0 :   if (check_point - m_last_calibration_file_update < m_calibration_file_interval) {
     662              :     return;
     663              :   }
     664              : 
     665            0 :   m_calibration_file.close();
     666            0 :   init_calibration_file();
     667              : }
     668              : 
     669              : bool
     670            0 : CTBModule::SetCalibrationStream(const std::string& prefix)
     671              : {
     672              : 
     673            0 :   if (m_calibration_dir.back() != '/') {
     674            0 :     m_calibration_dir += '/';
     675              :   }
     676            0 :   m_calibration_prefix = prefix;
     677            0 :   if (prefix.size() > 0) {
     678            0 :     m_calibration_prefix += '_';
     679              :   }
     680              :   // possibly we could check here if the directory is valid and  writable before assuming the calibration stream is
     681              :   // valid
     682            0 :   return true;
     683              : }
     684              : 
     685              : void
     686            0 : CTBModule::send_config(const std::string& config)
     687              : {
     688              : 
     689            0 :   if (m_is_configured.load()) {
     690              : 
     691            0 :     TLOG_DEBUG(1) << get_name() << ": Resetting before configuring" << std::endl;
     692            0 :     send_reset();
     693              :   }
     694              : 
     695            0 :   TLOG_DEBUG(1) << get_name() << ": Sending config" << std::endl;
     696              : 
     697            0 :   if (send_message(config)) {
     698              : 
     699            0 :     m_is_configured.store(true);
     700              : 
     701              :   } else {
     702            0 :     throw CTBCommunicationError(ERS_HERE, "Unable to configure CTB");
     703              :   }
     704            0 : }
     705              : 
     706              : void
     707            0 : CTBModule::send_reset()
     708              : {
     709              : 
     710            0 :   TLOG_DEBUG(1) << get_name() << ": Sending a reset" << std::endl;
     711              : 
     712            0 :   if (send_message("{\"command\":\"HardReset\"}")) {
     713              : 
     714            0 :     m_is_running.store(false);
     715            0 :     m_is_configured.store(false);
     716              : 
     717              :   } else {
     718            0 :     ers::error(CTBCommunicationError(ERS_HERE, "Unable to reset CTB"));
     719              :   }
     720            0 : }
     721              : 
     722              : bool
     723            0 : CTBModule::send_message(const std::string& msg)
     724              : {
     725              : 
     726              :   // add error options
     727              : 
     728            0 :   boost::system::error_code error;
     729            0 :   TLOG_DEBUG(1) << get_name() << ": Sending message: " << msg;
     730              : 
     731            0 :   m_num_control_messages_sent++;
     732              : 
     733            0 :   boost::asio::write(m_control_socket, boost::asio::buffer(msg), error);
     734            0 :   boost::array<char, 4096> reply_buf{ " " };
     735            0 :   m_control_socket.read_some(boost::asio::buffer(reply_buf), error);
     736            0 :   std::stringstream raw_answer(std::string(reply_buf.begin(), reply_buf.end()));
     737            0 :   TLOG_DEBUG(1) << get_name() << ": Unformatted answer: " << raw_answer.str();
     738              : 
     739            0 :   nlohmann::json answer;
     740            0 :   raw_answer >> answer;
     741            0 :   nlohmann::json& messages = answer["feedback"];
     742            0 :   TLOG_DEBUG(1) << get_name() << ": Received messages: " << messages.size();
     743              : 
     744            0 :   bool ret = true;
     745            0 :   for (nlohmann::json::size_type i = 0; i != messages.size(); ++i) {
     746              : 
     747            0 :     m_num_control_responses_received++;
     748              : 
     749            0 :     std::string type = messages[i]["type"].dump();
     750            0 :     if (type.find("error") != std::string::npos || type.find("Error") != std::string::npos ||
     751            0 :         type.find("ERROR") != std::string::npos) {
     752            0 :       ers::error(CTBMessage(ERS_HERE, messages[i]["message"].dump()));
     753            0 :       ret = false;
     754            0 :     } else if (type.find("warning") != std::string::npos || type.find("Warning") != std::string::npos ||
     755            0 :                type.find("WARNING") != std::string::npos) {
     756            0 :       ers::warning(CTBMessage(ERS_HERE, messages[i]["message"].dump()));
     757            0 :     } else if (type.find("info") != std::string::npos || type.find("Info") != std::string::npos ||
     758            0 :                type.find("INFO") != std::string::npos) {
     759            0 :       TLOG() << "Message from the board: " << messages[i]["message"].dump();
     760              :     } else {
     761            0 :       std::stringstream blob;
     762            0 :       blob << messages[i];
     763            0 :       TLOG() << get_name() << ": Unformatted from the board: " << blob.str();
     764            0 :     }
     765            0 :   }
     766              : 
     767            0 :   return ret;
     768            0 : }
     769              : 
     770              : void
     771            0 : CTBModule::update_buffer_counts(uint new_count) // NOLINT(build/unsigned)
     772              : {
     773            0 :   std::unique_lock mon_data_lock(m_buffer_counts_mutex);
     774            0 :   if (m_buffer_counts.size() > 1000)
     775            0 :     m_buffer_counts.pop_front();
     776            0 :   m_buffer_counts.push_back(new_count);
     777            0 : }
     778              : 
     779              : double
     780            0 : CTBModule::read_average_buffer_counts()
     781              : {
     782            0 :   std::unique_lock mon_data_lock(m_buffer_counts_mutex);
     783              : 
     784            0 :   double total_counts = 0;
     785            0 :   ;
     786            0 :   uint32_t number_of_counts = m_buffer_counts.size(); // NOLINT(build/unsigned)
     787              : 
     788            0 :   if (number_of_counts) {
     789            0 :     for (uint i = 0; i < number_of_counts; ++i) { // NOLINT(build/unsigned)
     790            0 :       total_counts = total_counts + m_buffer_counts.at(i);
     791              :     }
     792            0 :     return total_counts / number_of_counts;
     793              :   } else {
     794              :     return 0;
     795              :   }
     796            0 : }
     797              : 
     798              : void
     799            0 : CTBModule::generate_opmon_data()
     800              : {
     801            0 :   dunedaq::ctbmodules::opmon::CTBModuleInfo module_info;
     802              : 
     803            0 :   module_info.set_num_control_messages_sent(m_num_control_messages_sent.load());
     804            0 :   module_info.set_num_control_responses_received(m_num_control_responses_received.load());
     805            0 :   module_info.set_ctb_hardware_running(m_is_running.load());
     806            0 :   module_info.set_ctb_hardware_configured(m_is_configured.load());
     807              : 
     808            0 :   module_info.set_last_readout_timestamp(m_last_readout_hlt_timestamp.load());
     809            0 :   module_info.set_failed_to_send_hsi_events_counter(m_failed_to_send_counter.load());
     810            0 :   module_info.set_last_sent_timestamp(m_last_sent_timestamp.load());
     811            0 :   module_info.set_average_buffer_occupancy(read_average_buffer_counts());
     812              : 
     813            0 :   module_info.set_total_hlt_count(m_total_hlt_counter.load());
     814            0 :   module_info.set_ts_word_count(m_ts_word_counter.exchange(0));
     815              : 
     816            0 :   publish(std::move(module_info));
     817              : 
     818            0 :   for (auto& hlt : m_hlt_trigger_counter) {
     819            0 :     dunedaq::ctbmodules::opmon::TriggerInfo ti;
     820            0 :     ti.set_count(hlt.second.exchange(0));
     821            0 :     publish(std::move(ti), { { "trigger", "HLT_" + std::to_string(hlt.first) } });
     822            0 :   }
     823              : 
     824            0 :   for (auto& llt : m_llt_trigger_counter) {
     825            0 :     dunedaq::ctbmodules::opmon::TriggerInfo ti;
     826            0 :     ti.set_count(llt.second.exchange(0));
     827            0 :     publish(std::move(ti), { { "trigger", "LLT_" + std::to_string(llt.first) } });
     828            0 :   }
     829            0 : }
     830              : 
     831            0 : DEFINE_DUNE_DAQ_MODULE(dunedaq::ctbmodules::CTBModule)
     832              : 
     833              : // Local Variables:
     834              : // c-basic-offset: 2
     835              : // End:
        

Generated by: LCOV version 2.0-1