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

            Line data    Source code
       1              : /**
       2              :  * @file HSIReadout.cpp HSIReadout 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 "HSIReadout.hpp"
      11              : 
      12              : #include "timing/HSIDesignInterface.hpp"
      13              : #include "timing/TimingIssues.hpp"
      14              : 
      15              : #include "hsilibs/opmon/hsi_readout_info.pb.cc"
      16              : 
      17              : #include "confmodel/Connection.hpp"
      18              : #include "confmodel/DaqModule.hpp"
      19              : #include "logging/Logging.hpp" // NOTE: if ISSUES ARE DECLARED BEFORE include logging/Logging.hpp, TLOG_DEBUG<<issue wont work.
      20              : #include "rcif/cmd/Nljs.hpp"
      21              : #include <chrono>
      22              : #include <cstdlib>
      23              : #include <memory>
      24              : #include <string>
      25              : #include <thread>
      26              : #include <vector>
      27              : 
      28              : namespace dunedaq {
      29            0 : ERS_DECLARE_ISSUE(hsilibs,
      30              :                   InvalidHSIEventHeader,
      31              :                   " Invalid hsi buffer event header: 0x" << std::hex << header,
      32              :                   ((uint32_t)header)) // NOLINT(build/unsigned)
      33            0 : ERS_DECLARE_ISSUE(hsilibs,
      34              :                   InvalidHSIEventTimestamp,
      35              :                   " Invalid hsi buffer event timestamp: 0x" << std::hex << timestamp,
      36              :                   ((uint64_t)timestamp)) // NOLINT(build/unsigned)
      37            0 : ERS_DECLARE_ISSUE(hsilibs,
      38              :                   InvalidNumberReadoutHSIWords,
      39              :                   " Invalid number of hsi words readout from buffer: 0x" << std::hex << n_words,
      40              :                   ((uint16_t)n_words)) // NOLINT(build/unsigned)
      41              : namespace hsilibs {
      42              : 
      43            0 : HSIReadout::HSIReadout(const std::string& name)
      44              :   : HSIEventSender(name)
      45            0 :   , m_thread(std::bind(&HSIReadout::do_hsi_work, this, std::placeholders::_1))
      46            0 :   , m_readout_period(1000)
      47            0 :   , m_hsi_device(nullptr)
      48            0 :   , m_readout_counter(0)
      49            0 :   , m_last_readout_timestamp(0)
      50              : 
      51              : {
      52            0 :   register_command("conf", &HSIReadout::do_configure);
      53            0 :   register_command("start", &HSIReadout::do_start);
      54            0 :   register_command("stop", &HSIReadout::do_stop);
      55            0 :   register_command("scrap", &HSIReadout::do_scrap);
      56            0 : }
      57              : 
      58              : void
      59            0 : HSIReadout::init(std::shared_ptr<appfwk::ConfigurationManager> mcfg)
      60              : {
      61            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering init() method";
      62            0 :   HSIEventSender::init(mcfg);
      63            0 :   auto mdal = mcfg->get_dal<appmodel::HSIReadout>(get_name());
      64              : 
      65            0 :   for (auto con : mdal->get_outputs()) {
      66            0 :     if (con->get_data_type() == datatype_to_string<HSI_FRAME_STRUCT>()) {
      67              : 
      68            0 :       m_raw_hsi_data_sender = get_iom_sender<HSI_FRAME_STRUCT>(con->UID());
      69              :     }
      70              :   }
      71            0 :   m_params = mdal->get_configuration();
      72              : 
      73            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting init() method";
      74            0 : }
      75              : 
      76              : void
      77            0 : HSIReadout::do_configure(const CommandData_t& /*data*/)
      78              : {
      79            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering do_configure() method";
      80              : 
      81            0 :   m_readout_period = m_params->get_readout_period();
      82              : 
      83            0 :   configure_uhal(m_params->get_uhal_log_level(), m_params->get_connections_file()); // configure hw ipbus connection
      84              : 
      85            0 :   if (m_params->get_hsi_device_name().empty()) {
      86            0 :     throw UHALDeviceNameIssue(ERS_HERE, "Device name for HSIReadout should not be empty");
      87              :   }
      88            0 :   m_hsi_device_name = m_params->get_hsi_device_name();
      89              : 
      90            0 :   try {
      91            0 :     m_hsi_device = std::make_unique<uhal::HwInterface>(m_connection_manager->getDevice(m_hsi_device_name));
      92            0 :   } catch (const uhal::exception::ConnectionUIDDoesNotExist& exception) {
      93            0 :     std::stringstream message;
      94            0 :     message << "UHAL device name not " << m_hsi_device_name << " in connections file";
      95            0 :     throw UHALDeviceNameIssue(ERS_HERE, message.str(), exception);
      96            0 :   }
      97              : 
      98            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting do_configure() method";
      99            0 : }
     100              : 
     101              : void
     102            0 : HSIReadout::do_start(const CommandData_t& data)
     103              : {
     104            0 :   TLOG() << get_name() << ": Entering do_start() method";
     105            0 :   auto start_params = data.get<rcif::cmd::StartParams>();
     106            0 :   m_run_number.store(start_params.run);
     107            0 :   m_thread.start_working_thread("read-hsi-events");
     108            0 :   TLOG() << get_name() << " successfully started";
     109            0 :   TLOG() << get_name() << ": Exiting do_start() method";
     110            0 : }
     111              : 
     112              : void
     113            0 : HSIReadout::do_stop(const CommandData_t& /*data*/)
     114              : {
     115            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering do_stop() method";
     116            0 :   m_thread.stop_working_thread();
     117            0 :   TLOG() << get_name() << " successfully stopped";
     118            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting do_stop() method";
     119            0 : }
     120              : 
     121              : void
     122            0 : HSIReadout::do_scrap(const CommandData_t& /*data*/)
     123              : {
     124            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering do_scrap() method";
     125            0 :   scrap_uhal();
     126            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting do_scrap() method";
     127            0 : }
     128              : 
     129              : void
     130            0 : HSIReadout::do_hsi_work(std::atomic<bool>& running_flag)
     131              : {
     132            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering do_hsievent_work() method";
     133              : 
     134            0 :   m_readout_counter = 0;
     135            0 :   m_sent_counter = 0;
     136            0 :   m_failed_to_send_counter = 0;
     137              : 
     138            0 :   m_last_readout_timestamp = 0;
     139            0 :   m_last_sent_timestamp = 0;
     140              : 
     141            0 :   auto hsi_design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
     142            0 :   auto hsi_node = hsi_design->get_hsi_node();
     143            0 :   auto ept_node = hsi_design->get_endpoint_node_plain(0);
     144              : 
     145            0 :   while (running_flag.load()) {
     146              : 
     147              :     // endpoint should be ready if already running
     148            0 :     auto hsi_endpoint_ready = ept_node->endpoint_ready();
     149            0 :     if (!hsi_endpoint_ready) {
     150            0 :       auto hsi_endpoint_state = ept_node->read_endpoint_state();
     151            0 :       ers::error(timing::EndpointNotReady(ERS_HERE, "HSI", hsi_endpoint_state));
     152              :     }
     153              : 
     154            0 :     auto hsi_emulation_mode = hsi_node.read_signal_source_mode();
     155              : 
     156            0 :     uhal::ValVector<uint32_t> hsi_words;
     157            0 :     try {
     158            0 :       uint16_t n_words_in_buffer; // NOLINT(build/unsigned)
     159              : 
     160            0 :       hsi_words = hsi_node.read_data_buffer(n_words_in_buffer, false, true);
     161            0 :       update_buffer_counts(n_words_in_buffer);
     162            0 :       TLOG_DEBUG(5) << get_name() << ": Number of words in HSI buffer: " << n_words_in_buffer;
     163            0 :     } catch (const uhal::exception::UdpTimeout& excpt) {
     164            0 :       ers::error(HSIReadoutNetworkIssue(ERS_HERE, excpt));
     165            0 :       std::this_thread::sleep_for(std::chrono::microseconds(m_readout_period));
     166            0 :       continue;
     167            0 :     }
     168              : 
     169            0 :     constexpr size_t n_words_per_hsi_buffer_event = timing::HSINode::hsi_buffer_event_words_number;
     170              :     // one or more complete events
     171            0 :     if (hsi_words.size() % n_words_per_hsi_buffer_event == 0 && hsi_words.size() > 0) {
     172            0 :       uint n_hsi_events = hsi_words.size() / n_words_per_hsi_buffer_event;
     173              : 
     174            0 :       TLOG_DEBUG(4) << get_name() << ": Have readout " << n_hsi_events << " HSIEvent(s) ";
     175              : 
     176            0 :       m_readout_counter.store(m_readout_counter.load() + n_hsi_events);
     177            0 :       for (uint i = 0; i < n_hsi_events; ++i) {
     178            0 :         std::array<uint32_t, n_words_per_hsi_buffer_event> raw_event;
     179              : 
     180            0 :         auto event_start = hsi_words.begin() + (i * n_words_per_hsi_buffer_event);
     181            0 :         std::copy_n(event_start, n_words_per_hsi_buffer_event, raw_event.begin());
     182              : 
     183            0 :         uint32_t header = raw_event.at(0);  // NOLINT(build/unsigned)
     184            0 :         uint32_t ts_low = raw_event.at(1);  // NOLINT(build/unsigned)
     185            0 :         uint32_t ts_high = raw_event.at(2); // NOLINT(build/unsigned)
     186            0 :         uint32_t data = raw_event.at(3);    // NOLINT(build/unsigned)
     187            0 :         uint32_t trigger = raw_event.at(4); // NOLINT(build/unsigned)
     188              : 
     189              :         // put together the timestamp
     190            0 :         uint64_t ts = ts_low | (static_cast<uint64_t>(ts_high) << 32); // NOLINT(build/unsigned)
     191              : 
     192              :         // bits 31-16 contain the HSI device ID
     193            0 :         uint32_t hsi_device_id = header >> 16; // NOLINT(build/unsigned)
     194              :         // bits 15-0 contain the sequence counter
     195            0 :         uint32_t counter = header & 0x0000ffff; // NOLINT(build/unsigned)
     196              : 
     197            0 :         if ((header >> 16) != 0xaa00) {
     198            0 :           ers::error(InvalidHSIEventHeader(ERS_HERE, header));
     199            0 :           continue;
     200              :         }
     201              : 
     202            0 :         if (!ts) {
     203            0 :           ers::warning(InvalidHSIEventTimestamp(ERS_HERE, ts));
     204            0 :           continue;
     205              :         }
     206              : 
     207            0 :         if (counter > 0 && counter % 60000 == 0) {
     208            0 :           TLOG_DEBUG(3) << "Sequence counter from firmware: " << counter;
     209              :         }
     210              : 
     211            0 :         TLOG_DEBUG(3) << get_name() << ": read out data: " << std::showbase << std::hex << header << ", " << ts << ", "
     212            0 :                       << data << ", " << std::bitset<32>(trigger) << ", "
     213            0 :                       << "ts: " << ts << "\n";
     214              : 
     215              :         // In lieu of propper HSI channel to signal mapping, fake signal map when HSI firmware+hardware is in emulation
     216              :         // mode.
     217              :         // TODO DAQ/HSI team 24/03/22 Put in place HSI channel to signal mapping.
     218              : 
     219            0 :         if (hsi_emulation_mode) {
     220            0 :           TLOG_DEBUG(3) << " HSI hardware is in emulation mode, faking (overwriting) signal map from firmware+hardware "
     221            0 :                            "to have (only) bit 7 high.";
     222            0 :           trigger = 1UL << 7;
     223              :         }
     224              : 
     225            0 :         dfmessages::HSIEvent event = dfmessages::HSIEvent(hsi_device_id, trigger, ts, counter, m_run_number);
     226              : 
     227            0 :         m_last_readout_timestamp.store(ts);
     228              : 
     229            0 :         send_hsi_event(event);
     230              : 
     231              :         // Send raw HSI data to a DLH
     232            0 :         std::array<uint32_t, 7> hsi_struct;
     233            0 :         hsi_struct[0] = (0x1 << 6) | 0x1; // DAQHeader, frame version: 1, det id: 1
     234            0 :         hsi_struct[1] = ts_low;
     235            0 :         hsi_struct[2] = ts_high;
     236            0 :         hsi_struct[3] = data;
     237            0 :         hsi_struct[4] = 0x0;
     238            0 :         hsi_struct[5] = trigger;
     239            0 :         hsi_struct[6] = counter;
     240              : 
     241            0 :         TLOG_DEBUG(3) << get_name() << ": Formed HSI_FRAME_STRUCT " << std::hex << "0x" << hsi_struct[0] << ", 0x"
     242            0 :                       << hsi_struct[1] << ", 0x" << hsi_struct[2] << ", 0x" << hsi_struct[3] << ", 0x" << hsi_struct[4]
     243            0 :                       << ", 0x" << hsi_struct[5] << ", 0x" << hsi_struct[6] << "\n";
     244              : 
     245            0 :         send_raw_hsi_data(hsi_struct, m_raw_hsi_data_sender.get());
     246              :       }
     247              :     }
     248              :     // empty buffer is ok
     249            0 :     else if (hsi_words.size() == 0) {
     250            0 :       TLOG_DEBUG(20) << "Empty HSI buffter";
     251              :     }
     252              :     // anything else is unexpected
     253              :     else {
     254            0 :       ers::error(InvalidNumberReadoutHSIWords(ERS_HERE, hsi_words.size()));
     255              :     }
     256            0 :     std::this_thread::sleep_for(std::chrono::microseconds(m_readout_period));
     257            0 :   }
     258            0 :   std::ostringstream oss_summ;
     259            0 :   oss_summ << ": Exiting the read_hsievents() method, read out " << m_readout_counter.load()
     260            0 :            << " HSIEvent messages and successfully sent " << m_sent_counter.load() << " copies. ";
     261            0 :   ers::info(hsilibs::ProgressUpdate(ERS_HERE, get_name(), oss_summ.str()));
     262            0 :   TLOG_DEBUG(2) << get_name() << ": Exiting do_work() method";
     263            0 : } // NOLINT
     264              : 
     265              : void
     266            0 : HSIReadout::update_buffer_counts(uint16_t new_count) // NOLINT(build/unsigned)
     267              : {
     268            0 :   std::unique_lock mon_data_lock(m_buffer_counts_mutex);
     269            0 :   if (m_buffer_counts.size() > 1000)
     270            0 :     m_buffer_counts.pop_front();
     271            0 :   m_buffer_counts.push_back(new_count);
     272            0 : }
     273              : 
     274              : double
     275            0 : HSIReadout::read_average_buffer_counts()
     276              : {
     277            0 :   std::unique_lock mon_data_lock(m_buffer_counts_mutex);
     278              : 
     279            0 :   double total_counts;
     280            0 :   uint32_t number_of_counts; // NOLINT(build/unsigned)
     281              : 
     282            0 :   total_counts = 0;
     283            0 :   number_of_counts = m_buffer_counts.size();
     284              : 
     285            0 :   if (number_of_counts) {
     286            0 :     for (uint i = 0; i < number_of_counts; ++i) { // NOLINT(build/unsigned)
     287            0 :       total_counts = total_counts + m_buffer_counts.at(i);
     288              :     }
     289            0 :     return total_counts / number_of_counts;
     290              :   } else {
     291              :     return 0;
     292              :   }
     293            0 : }
     294              : 
     295              : void
     296            0 : HSIReadout::generate_opmon_data()
     297              : {
     298              : 
     299            0 :   opmon::HSIReadoutInfo info;
     300              : 
     301            0 :   info.set_readout_hsi_events_counter(m_readout_counter.load());
     302            0 :   info.set_sent_hsi_events_counter(m_sent_counter.load());
     303            0 :   info.set_failed_to_send_hsi_events_counter(m_failed_to_send_counter.load());
     304            0 :   info.set_last_readout_timestamp(m_last_readout_timestamp.load());
     305            0 :   info.set_last_sent_timestamp(m_last_sent_timestamp.load());
     306            0 :   info.set_average_buffer_occupancy(read_average_buffer_counts());
     307              : 
     308            0 :   publish(std::move(info));
     309            0 : }
     310              : 
     311              : } // namespace hsilibs
     312              : } // namespace dunedaq
     313              : 
     314            0 : DEFINE_DUNE_DAQ_MODULE(dunedaq::hsilibs::HSIReadout)
     315              : 
     316              : // Local Variables:
     317              : // c-basic-offset: 2
     318              : // End:
        

Generated by: LCOV version 2.0-1