LCOV - code coverage report
Current view: top level - dfmodules/plugins - TPStreamWriterModule.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 0.0 % 201 0
Test Date: 2026-08-30 15:04:40 Functions: 0.0 % 26 0

            Line data    Source code
       1              : /**
       2              :  * @file TPStreamWriterModule.cpp TPStreamWriterModule class implementation
       3              :  *
       4              :  * This is part of the DUNE DAQ Software Suite, copyright 2020.
       5              :  * Licensing/copyright details are in the COPYING file that you should have
       6              :  * received with this code.
       7              :  */
       8              : 
       9              : #include "TPStreamWriterModule.hpp"
      10              : #include "dfmodules/CommonIssues.hpp"
      11              : #include "dfmodules/TPBundleHandler.hpp"
      12              : #include "dfmodules/opmon/TPStreamWriter.pb.h"
      13              : 
      14              : #include "appmodel/DataStoreConf.hpp"
      15              : #include "appmodel/TPStreamWriterModule.hpp"
      16              : #include "confmodel/Connection.hpp"
      17              : #include "confmodel/Session.hpp"
      18              : #include "daqdataformats/Fragment.hpp"
      19              : #include "daqdataformats/Types.hpp"
      20              : #include "iomanager/IOManager.hpp"
      21              : #include "logging/Logging.hpp"
      22              : #include "rcif/cmd/Nljs.hpp"
      23              : 
      24              : #include "boost/date_time/posix_time/posix_time.hpp"
      25              : 
      26              : #include <chrono>
      27              : #include <memory>
      28              : #include <sstream>
      29              : #include <string>
      30              : #include <utility>
      31              : #include <vector>
      32              : 
      33              : enum
      34              : {
      35              :   TLVL_ENTER_EXIT_METHODS = 5,
      36              :   TLVL_CONFIG = 7,
      37              : };
      38              : 
      39              : namespace dunedaq {
      40              : namespace dfmodules {
      41              : 
      42            0 : TPStreamWriterModule::TPStreamWriterModule(const std::string& name)
      43              :   : dunedaq::appfwk::DAQModule(name)
      44            0 :   , m_thread(std::bind(&TPStreamWriterModule::do_work, this, std::placeholders::_1))
      45            0 :   , m_queue_timeout(100)
      46            0 :   , m_data_storage_is_enabled(true)
      47              : {
      48            0 :   register_command("conf", &TPStreamWriterModule::do_conf);
      49            0 :   register_command("start", &TPStreamWriterModule::do_start);
      50            0 :   register_command("stop", &TPStreamWriterModule::do_stop);
      51            0 :   register_command("scrap", &TPStreamWriterModule::do_scrap);
      52            0 : }
      53              : 
      54              : void
      55            0 : TPStreamWriterModule::init(std::shared_ptr<appfwk::ConfigurationManager> mcfg)
      56              : {
      57            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering init() method";
      58            0 :   auto mdal = mcfg->get_dal<appmodel::TPStreamWriterModule>(get_name());
      59            0 :   if (!mdal) {
      60            0 :     throw appfwk::CommandFailed(ERS_HERE, "init", get_name(), "Unable to retrieve configuration object");
      61              :   }
      62            0 :   assert(mdal->get_inputs().size() == 1);
      63            0 :   m_module_configuration = mcfg;
      64            0 :   m_tpset_source = iomanager::IOManager::get()->get_receiver<trigger::TPSet>(mdal->get_inputs()[0]->UID());
      65            0 :   m_writer_identifier = mdal->get_writer_identifier();
      66            0 :   m_tp_writer_conf = mdal->get_configuration();
      67            0 :   m_source_id = mdal->get_source_id();
      68            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting init() method";
      69            0 : }
      70              : 
      71              : void
      72            0 : TPStreamWriterModule::generate_opmon_data()
      73              : {
      74            0 :   opmon::TPStreamWriterInfo info;
      75              : 
      76            0 :   info.set_heartbeat_tpsets_received(m_heartbeat_tpsets.exchange(0));
      77            0 :   info.set_tpsets_with_tps_received(m_tpsets_with_tps.exchange(0));
      78            0 :   info.set_tps_received(m_tps_received.exchange(0));
      79            0 :   info.set_tps_written(m_tps_written.exchange(0));
      80            0 :   info.set_tps_discarded(m_tps_discarded.exchange(0));
      81            0 :   info.set_total_tps_received(m_total_tps_received.load());
      82            0 :   info.set_total_tps_written(m_total_tps_written.load());
      83            0 :   info.set_total_tps_discarded(m_total_tps_discarded.load());
      84            0 :   info.set_tardy_timeslice_max_seconds(m_tardy_timeslice_max_seconds.exchange(0.0));
      85            0 :   info.set_timeslices_written(m_timeslices_written.exchange(0));
      86            0 :   info.set_bytes_output(m_bytes_output.exchange(0));
      87              : 
      88            0 :   publish(std::move(info));
      89            0 : }
      90              : 
      91              : void
      92            0 : TPStreamWriterModule::do_conf(const CommandData_t&)
      93              : {
      94            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering do_conf() method";
      95            0 :   m_accumulation_interval_ticks = m_tp_writer_conf->get_tp_accumulation_interval();
      96            0 :   m_accumulation_inactivity_time_before_write = std::chrono::milliseconds(
      97            0 :     static_cast<int>(1000 * m_tp_writer_conf->get_tp_accumulation_inactivity_time_before_write_sec()));
      98            0 :   m_warn_user_when_tardy_tps_are_discarded = m_tp_writer_conf->get_warn_user_when_tardy_tps_are_discarded();
      99            0 :   m_accumulation_interval_seconds = ((double)m_accumulation_interval_ticks) / 62500000.0;
     100              : 
     101              :   // create the DataStore instance here
     102            0 :   try {
     103            0 :     m_data_writer = make_data_store(m_tp_writer_conf->get_data_store_params()->get_type(),
     104            0 :                                     m_tp_writer_conf->get_data_store_params()->UID(),
     105            0 :                                     m_module_configuration,
     106            0 :                                     m_writer_identifier);
     107            0 :     register_node("data_writer", m_data_writer);
     108            0 :   } catch (const ers::Issue& excpt) {
     109            0 :     throw UnableToConfigure(ERS_HERE, get_name(), excpt);
     110            0 :   }
     111              : 
     112              :   // ensure that we have a valid dataWriter instance
     113            0 :   if (m_data_writer.get() == nullptr) {
     114            0 :     throw InvalidDataWriterModule(ERS_HERE, get_name());
     115              :   }
     116              : 
     117            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting do_conf() method";
     118            0 : }
     119              : 
     120              : void
     121            0 : TPStreamWriterModule::do_start(const CommandData_t& payload)
     122              : {
     123            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering do_start() method";
     124            0 :   rcif::cmd::StartParams start_params = payload.get<rcif::cmd::StartParams>();
     125            0 :   m_data_storage_is_enabled = (!start_params.disable_data_storage);
     126            0 :   m_run_number = start_params.run;
     127            0 :   m_total_tps_received.store(0);
     128            0 :   m_total_tps_written.store(0);
     129            0 :   m_total_tps_discarded.store(0);
     130              : 
     131              :   // 06-Mar-2022, KAB: added this call to allow DataStore to prepare for the run.
     132              :   // I've put this call fairly early in this method because it could throw an
     133              :   // exception and abort the run start.  And, it seems sensible to avoid starting
     134              :   // threads, etc. if we throw an exception.
     135            0 :   if (m_data_storage_is_enabled) {
     136            0 :     try {
     137            0 :       m_data_writer->prepare_for_run(m_run_number, (start_params.production_vs_test == "TEST"));
     138            0 :     } catch (const ers::Issue& excpt) {
     139            0 :       throw UnableToStart(ERS_HERE, get_name(), m_run_number, excpt);
     140            0 :     }
     141              :   }
     142              : 
     143            0 :   m_thread.start_working_thread(get_name());
     144              : 
     145            0 :   TLOG() << get_name() << " successfully started for run number " << m_run_number;
     146            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting do_start() method";
     147            0 : }
     148              : 
     149              : void
     150            0 : TPStreamWriterModule::do_stop(const CommandData_t& /*payload*/)
     151              : {
     152            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering do_stop() method";
     153            0 :   m_thread.stop_working_thread();
     154              : 
     155              :   // 06-Mar-2022, KAB: added this call to allow DataStore to finish up with this run.
     156              :   // I've put this call fairly late in this method so that any draining of queues
     157              :   // (or whatever) can take place before we finalize things in the DataStore.
     158            0 :   if (m_data_storage_is_enabled) {
     159            0 :     try {
     160            0 :       m_data_writer->finish_with_run(m_run_number);
     161            0 :     } catch (const std::exception& excpt) {
     162            0 :       ers::error(ProblemDuringStop(ERS_HERE, get_name(), m_run_number, excpt));
     163            0 :     }
     164              :   }
     165              : 
     166            0 :   TLOG() << get_name() << " successfully stopped for run number " << m_run_number;
     167            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting do_stop() method";
     168            0 : }
     169              : 
     170              : void
     171            0 : TPStreamWriterModule::do_scrap(const CommandData_t& /*payload*/)
     172              : {
     173            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering do_scrap() method";
     174              : 
     175              :   // clear/reset the DataStore instance here
     176            0 :   m_data_writer.reset();
     177              : 
     178            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting do_scrap() method";
     179            0 : }
     180              : 
     181              : void
     182            0 : TPStreamWriterModule::do_work(std::atomic<bool>& running_flag)
     183              : {
     184            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering do_work() method";
     185              : 
     186            0 :   using namespace std::chrono;
     187            0 :   size_t n_tpset_received = 0;
     188            0 :   auto start_time = steady_clock::now();
     189            0 :   daqdataformats::timestamp_t first_timestamp = 0;
     190            0 :   daqdataformats::timestamp_t last_timestamp = 0;
     191              : 
     192            0 :   TPBundleHandler tp_bundle_handler(
     193            0 :     m_accumulation_interval_ticks, m_run_number, m_accumulation_inactivity_time_before_write);
     194              : 
     195            0 :   bool possible_pending_data = true;
     196            0 :   size_t largest_timeslice_number = 0;
     197            0 :   while (running_flag.load() || possible_pending_data) {
     198            0 :     trigger::TPSet tpset;
     199            0 :     try {
     200            0 :       tpset = m_tpset_source->receive(m_queue_timeout);
     201            0 :       ++n_tpset_received;
     202              : 
     203            0 :       if (tpset.type == trigger::TPSet::Type::kHeartbeat) {
     204            0 :         ++m_heartbeat_tpsets;
     205            0 :         continue;
     206              :       }
     207              : 
     208            0 :       TLOG_DEBUG(21) << "Number of TPs in TPSet is " << tpset.objects.size() << ", Source ID is " << tpset.origin
     209            0 :                      << ", seqno is " << tpset.seqno << ", start timestamp is " << tpset.start_time
     210            0 :                      << ", run number is " << tpset.run_number << ", slice id is "
     211            0 :                      << (tpset.start_time / m_accumulation_interval_ticks);
     212              : 
     213              :       // 30-Mar-2022, KAB: added test for matching run number.  This is to avoid getting
     214              :       // confused by TPSets that happen to be leftover in transit from one run to the
     215              :       // next (which we have observed in v2.10.x systems).
     216            0 :       if (tpset.run_number != m_run_number) {
     217            0 :         TLOG_DEBUG(22) << "Discarding TPSet with invalid run number " << tpset.run_number << " (current is "
     218            0 :                        << m_run_number << "),  Source ID is " << tpset.origin << ", seqno is " << tpset.seqno;
     219            0 :         continue;
     220            0 :       }
     221            0 :       ++m_tpsets_with_tps;
     222              : 
     223            0 :       size_t num_tps_in_tpset = tpset.objects.size();
     224            0 :       tp_bundle_handler.add_tpset(std::move(tpset));
     225            0 :       m_tps_received += num_tps_in_tpset;
     226            0 :       m_total_tps_received += num_tps_in_tpset;
     227            0 :       possible_pending_data = true;
     228            0 :     } catch (iomanager::ConnectionInstanceNotFound&) {
     229              :       // sleep for a little bit; and indicate no pending data, in case we never get a connection
     230              :       // and the run ends - we don't want to believe that there is pending data in that case.
     231            0 :       usleep(1000 * m_queue_timeout.count());
     232            0 :       possible_pending_data = false;
     233            0 :     } catch (iomanager::TimeoutExpired&) {
     234              :       // nothing special to do here, we'll simply let the rest of the code in this
     235              :       // while loop do its job
     236            0 :     }
     237              : 
     238            0 :     std::vector<std::unique_ptr<daqdataformats::TimeSlice>> list_of_timeslices;
     239            0 :     if (running_flag.load()) {
     240            0 :       list_of_timeslices = tp_bundle_handler.get_properly_aged_timeslices();
     241              :     } else {
     242            0 :       list_of_timeslices = tp_bundle_handler.get_all_remaining_timeslices();
     243            0 :       possible_pending_data = false;
     244              :     }
     245              : 
     246              :     // keep track of the largest timeslice number (for reporting on tardy ones)
     247            0 :     for (auto& timeslice_ptr : list_of_timeslices) {
     248            0 :       largest_timeslice_number = std::max(timeslice_ptr->get_header().timeslice_number, largest_timeslice_number);
     249              :     }
     250              : 
     251              :     // attempt to write out each TimeSlice
     252            0 :     for (auto& timeslice_ptr : list_of_timeslices) {
     253            0 :       daqdataformats::SourceID sid(daqdataformats::SourceID::Subsystem::kTRBuilder, m_source_id);
     254            0 :       timeslice_ptr->set_element_id(sid);
     255              : 
     256            0 :       if (m_data_storage_is_enabled) {
     257              : 
     258              :         // write the TSH and the fragments as a set of data blocks
     259            0 :         bool should_retry = true;
     260              :         size_t retry_wait_usec = 1000;
     261            0 :         do {
     262            0 :           should_retry = false;
     263            0 :           size_t number_of_tps =
     264            0 :             (timeslice_ptr->get_sum_of_fragment_payload_sizes() / sizeof(trgdataformats::TriggerPrimitive));
     265            0 :           try {
     266            0 :             m_data_writer->write(*timeslice_ptr);
     267            0 :             ++m_timeslices_written;
     268            0 :             m_bytes_output += timeslice_ptr->get_total_size_bytes();
     269            0 :             m_tps_written += number_of_tps;
     270            0 :             m_total_tps_written += number_of_tps;
     271            0 :           } catch (const RetryableDataStoreProblem& excpt) {
     272            0 :             should_retry = true;
     273            0 :             ers::error(DataWritingProblem(ERS_HERE,
     274            0 :                                           get_name(),
     275            0 :                                           timeslice_ptr->get_header().timeslice_number,
     276            0 :                                           timeslice_ptr->get_header().run_number,
     277            0 :                                           excpt));
     278            0 :             usleep(retry_wait_usec);
     279            0 :             retry_wait_usec = std::min(retry_wait_usec * 2, 1000000UL);
     280            0 :           } catch (const IgnorableDataStoreProblem& excpt) {
     281            0 :             int timeslice_number_diff = largest_timeslice_number - timeslice_ptr->get_header().timeslice_number;
     282            0 :             double seconds_too_late = m_accumulation_interval_seconds * timeslice_number_diff;
     283            0 :             m_tardy_timeslice_max_seconds = std::max(m_tardy_timeslice_max_seconds.load(), seconds_too_late);
     284            0 :             m_tps_discarded += number_of_tps;
     285            0 :             m_total_tps_discarded += number_of_tps;
     286            0 :             if (m_warn_user_when_tardy_tps_are_discarded) {
     287            0 :               std::ostringstream sid_list;
     288            0 :               bool first_frag = true;
     289            0 :               for (auto const& frag_ptr : timeslice_ptr->get_fragments_ref()) {
     290            0 :                 if (first_frag) {
     291              :                   first_frag = false;
     292              :                 } else {
     293            0 :                   sid_list << ",";
     294              :                 }
     295            0 :                 sid_list << frag_ptr->get_element_id().to_string();
     296              :               }
     297            0 :               ers::warning(TardyTPsDiscarded(
     298            0 :                 ERS_HERE, get_name(), sid_list.str(), timeslice_ptr->get_header().timeslice_number, seconds_too_late));
     299            0 :             }
     300            0 :           } catch (const std::exception& excpt) {
     301            0 :             m_tps_discarded += number_of_tps;
     302            0 :             m_total_tps_discarded += number_of_tps;
     303            0 :             ers::error(DataWritingProblem(ERS_HERE,
     304            0 :                                           get_name(),
     305            0 :                                           timeslice_ptr->get_header().timeslice_number,
     306            0 :                                           timeslice_ptr->get_header().run_number,
     307            0 :                                           excpt));
     308            0 :           }
     309            0 :         } while (should_retry && running_flag.load());
     310              :       } // if (m_data_storage_is_enabled) {
     311              :     }
     312              : 
     313            0 :     if (first_timestamp == 0) {
     314            0 :       first_timestamp = tpset.start_time;
     315              :     }
     316            0 :     last_timestamp = tpset.start_time;
     317            0 :   } // while(running)
     318              : 
     319            0 :   auto end_time = steady_clock::now();
     320            0 :   auto time_ms = duration_cast<milliseconds>(end_time - start_time).count();
     321            0 :   float rate_hz = 1e3 * static_cast<float>(n_tpset_received) / time_ms;
     322            0 :   float inferred_clock_frequency = 1e3 * (last_timestamp - first_timestamp) / time_ms;
     323              : 
     324            0 :   TLOG() << "Received " << n_tpset_received << " TPSets in " << time_ms << "ms. " << rate_hz
     325            0 :          << " TPSet/s. Inferred clock frequency " << inferred_clock_frequency << "Hz";
     326            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting do_work() method";
     327            0 : } // NOLINT Function length
     328              : 
     329              : } // namespace dfmodules
     330              : } // namespace dunedaq
     331              : 
     332            0 : DEFINE_DUNE_DAQ_MODULE(dunedaq::dfmodules::TPStreamWriterModule)
        

Generated by: LCOV version 2.0-1