LCOV - code coverage report
Current view: top level - hsilibs/plugins - FakeHSIEventGeneratorModule.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 0.0 % 168 0
Test Date: 2025-12-21 13:07:08 Functions: 0.0 % 33 0

            Line data    Source code
       1              : /**
       2              :  * @file FakeHSIEventGeneratorModule.cpp FakeHSIEventGeneratorModule 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 "FakeHSIEventGeneratorModule.hpp"
      11              : 
      12              : #include "appmodel/FakeHSIEventGeneratorModule.hpp"
      13              : #include "confmodel/Connection.hpp"
      14              : #include "confmodel/DaqModule.hpp"
      15              : #include "confmodel/DetectorConfig.hpp"
      16              : #include "confmodel/Session.hpp"
      17              : #include "dfmessages/HSIEvent.hpp"
      18              : #include "iomanager/IOManager.hpp"
      19              : #include "logging/Logging.hpp"
      20              : #include "rcif/cmd/Nljs.hpp"
      21              : #include "utilities/Issues.hpp"
      22              : 
      23              : #include <chrono>
      24              : #include <cstdlib>
      25              : #include <string>
      26              : #include <thread>
      27              : #include <vector>
      28              : 
      29              : namespace dunedaq {
      30              : namespace hsilibs {
      31              : 
      32              : enum
      33              : {
      34              :   TLVL_ENTER_EXIT_METHODS = 5
      35              : };
      36              : 
      37            0 : FakeHSIEventGeneratorModule::FakeHSIEventGeneratorModule(const std::string& name)
      38              :   : HSIEventSender(name)
      39            0 :   , m_thread(std::bind(&FakeHSIEventGeneratorModule::do_hsi_work, this, std::placeholders::_1))
      40            0 :   , m_timestamp_estimator(nullptr)
      41            0 :   , m_random_generator()
      42            0 :   , m_uniform_distribution(0, UINT32_MAX)
      43            0 :   , m_clock_frequency(62500000)
      44            0 :   , m_trigger_rate(1)        // Hz
      45            0 :   , m_active_trigger_rate(1) // Hz
      46            0 :   , m_event_period(1e6)      // us
      47            0 :   , m_timestamp_offset(0)
      48            0 :   , m_hsi_device_id(0)
      49            0 :   , m_signal_emulation_mode(0)
      50            0 :   , m_mean_signal_multiplicity(0)
      51            0 :   , m_enabled_signals(0)
      52            0 :   , m_generated_counter(0)
      53            0 :   , m_last_generated_timestamp(0)
      54              : {
      55            0 :   register_command("conf", &FakeHSIEventGeneratorModule::do_configure);
      56            0 :   register_command("start", &FakeHSIEventGeneratorModule::do_start);
      57            0 :   register_command("stop_trigger_sources", &FakeHSIEventGeneratorModule::do_stop);
      58            0 :   register_command("scrap", &FakeHSIEventGeneratorModule::do_scrap);
      59            0 : }
      60              : 
      61              : void
      62            0 : FakeHSIEventGeneratorModule::init(std::shared_ptr<appfwk::ConfigurationManager> mcfg)
      63              : {
      64            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering init() method";
      65            0 :   HSIEventSender::init(mcfg);
      66              : 
      67            0 :   m_clock_frequency = mcfg->get_session()->get_detector_configuration()->get_clock_speed_hz();
      68            0 :   auto mdal =
      69            0 :     mcfg->get_dal<appmodel::FakeHSIEventGeneratorModule>(get_name()); // Only need generic DaqModule for output
      70              : 
      71            0 :   if (!mdal) {
      72            0 :     throw appfwk::CommandFailed(ERS_HERE, "init", get_name(), "Unable to retrieve configuration object");
      73              :   }
      74              : 
      75            0 :   for (auto con : mdal->get_outputs()) {
      76            0 :     if (con->get_data_type() == datatype_to_string<HSI_FRAME_STRUCT>()) {
      77              : 
      78            0 :       m_raw_hsi_data_sender = get_iom_sender<HSI_FRAME_STRUCT>(con->UID());
      79              :     }
      80              :   }
      81              : 
      82              :   // 07-Jul-2025, KAB: added the fetching of the TimeSync connection information
      83              :   // (and the assignment of the TimeSync Receiver) here, now that we have the
      84              :   // TimeSync connection defined in the configuration.  (Previously, the creation
      85              :   // of the TimeSync receiver was done in the 'start' method, and it used a hard-coded
      86              :   // wildcard in the connection name lookup.)
      87            0 :   for (auto con : mdal->get_inputs()) {
      88            0 :     if (con->get_data_type() == datatype_to_string<dfmessages::TimeSync>()) {
      89            0 :       m_timesync_receiver = get_iom_receiver<dfmessages::TimeSync>(con->UID());
      90              :     }
      91              :   }
      92              : 
      93            0 :   m_params = mdal->get_configuration();
      94            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting init() method";
      95            0 : }
      96              : 
      97              : // void
      98              : // FakeHSIEventGeneratorModule::get_info(opmonlib::InfoCollector& ci, int /*level*/)
      99              : // {
     100              : //   // send counters internal to the module
     101              : //   fakehsieventgeneratorinfo::Info module_info;
     102              : 
     103              : //   module_info.generated_hsi_events_counter = m_generated_counter.load();
     104              : //   module_info.sent_hsi_events_counter = m_sent_counter.load();
     105              : //   module_info.failed_to_send_hsi_events_counter = m_failed_to_send_counter.load();
     106              : //   module_info.last_generated_timestamp = m_last_generated_timestamp.load();
     107              : //   module_info.last_sent_timestamp = m_last_sent_timestamp.load();
     108              : 
     109              : //   ci.add(module_info);
     110              : // }
     111              : 
     112              : void
     113            0 : FakeHSIEventGeneratorModule::do_configure(const CommandData_t& /*obj*/)
     114              : {
     115            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering do_configure() method";
     116              : 
     117            0 :   if (m_params->get_trigger_rate() > 0) {
     118            0 :     m_trigger_rate.store(m_params->get_trigger_rate());
     119            0 :     m_active_trigger_rate.store(m_trigger_rate.load());
     120              :   } else {
     121            0 :     ers::fatal(InvalidTriggerRateValue(ERS_HERE, m_params->get_trigger_rate()));
     122              :   }
     123              : 
     124              :   // time between HSI events [us]
     125            0 :   m_event_period.store(1.e6 / m_active_trigger_rate.load());
     126            0 :   TLOG() << get_name() << " Setting trigger rate, event period [us] to: " << m_active_trigger_rate.load() << ", "
     127            0 :          << m_event_period.load();
     128              : 
     129              :   // offset in units of clock ticks, positive offset increases timestamp
     130            0 :   m_timestamp_offset = m_params->get_timestamp_offset();
     131            0 :   m_hsi_device_id = m_params->get_hsi_device_id();
     132            0 :   m_signal_emulation_mode = m_params->get_signal_emulation_mode();
     133            0 :   m_mean_signal_multiplicity = m_params->get_mean_signal_multiplicity();
     134            0 :   m_enabled_signals = m_params->get_enabled_signals();
     135              : 
     136              :   // configure the random distributions
     137            0 :   m_poisson_distribution = std::poisson_distribution<uint64_t>(m_mean_signal_multiplicity); // NOLINT(build/unsigned)
     138              : 
     139            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting do_configure() method";
     140            0 : }
     141              : 
     142              : void
     143            0 : FakeHSIEventGeneratorModule::do_start(const CommandData_t& obj)
     144              : {
     145            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering do_start() method";
     146            0 :   auto start_params = obj.get<rcif::cmd::StartParams>();
     147              : 
     148            0 :   m_timestamp_estimator.reset(new utilities::TimestampEstimatorTimeSync(start_params.run, m_clock_frequency));
     149              : 
     150            0 :   m_timesync_receiver->add_callback(
     151            0 :     std::bind(&utilities::TimestampEstimatorTimeSync::timesync_callback<dfmessages::TimeSync>,
     152            0 :               reinterpret_cast<utilities::TimestampEstimatorTimeSync*>(m_timestamp_estimator.get()),
     153              :               std::placeholders::_1));
     154              : 
     155            0 :   TLOG() << get_name() << " Using trigger rate, event period [us]: " << m_active_trigger_rate.load() << ", "
     156            0 :          << m_event_period.load();
     157              : 
     158            0 :   m_run_number.store(start_params.run);
     159              : 
     160              :   // 28-Sep-2023, KAB: added code to wait for the Sender connection to be ready.
     161              :   // This code needs to come *before* the Sender connection is first used, and
     162              :   // before any threads that use the Sender connection are start, and it needs
     163              :   // to come *after* any Receiver connections are created/started so that it
     164              :   // doesn't block other modules that are trying to connect to this one.
     165              :   // We retry for 10 seconds.  That seems like it should be plenty long enough
     166              :   // for the connection to be established but short enough so that it doesn't
     167              :   // annoy users if/when the connection readiness times out.
     168            0 :   if (!ready_to_send(std::chrono::milliseconds(1))) {
     169            0 :     bool ready = false;
     170            0 :     for (int loop_counter = 0; loop_counter < 10; ++loop_counter) {
     171            0 :       TLOG() << get_name() << " Waiting for the Sender for the " << m_hsievent_send_connection
     172            0 :              << " connection to be ready to send messages, loop_count=" << (loop_counter + 1);
     173            0 :       if (ready_to_send(std::chrono::milliseconds(1000))) {
     174              :         ready = true;
     175              :         break;
     176              :       }
     177              :     }
     178            0 :     if (ready) {
     179            0 :       TLOG() << get_name() << " The Sender for the " << m_hsievent_send_connection << " connection is now ready.";
     180              :     } else {
     181            0 :       throw(SenderReadyTimeout(ERS_HERE, get_name(), m_hsievent_send_connection));
     182              :     }
     183              :   }
     184              : 
     185            0 :   m_thread.start_working_thread("fake-tsd-gen");
     186            0 :   TLOG() << get_name() << " successfully started";
     187            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting do_start() method";
     188            0 : }
     189              : 
     190              : void
     191            0 : FakeHSIEventGeneratorModule::do_stop(const CommandData_t& /*args*/)
     192              : {
     193            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering do_stop() method";
     194            0 :   m_thread.stop_working_thread();
     195              : 
     196            0 :   m_timesync_receiver->remove_callback();
     197            0 :   TLOG() << get_name() << ": received " << m_timestamp_estimator->get_received_timesync_count()
     198            0 :          << " TimeSync messages.";
     199              : 
     200            0 :   m_timestamp_estimator.reset(nullptr); // Calls TimestampEstimatorTimeSync dtor
     201              : 
     202            0 :   m_active_trigger_rate.store(m_trigger_rate.load());
     203            0 :   m_event_period.store(1.e6 / m_active_trigger_rate.load());
     204            0 :   TLOG() << get_name() << " Updating trigger rate, event period [us] to: " << m_active_trigger_rate.load() << ", "
     205            0 :          << m_event_period.load();
     206              : 
     207            0 :   TLOG() << get_name() << " successfully stopped";
     208            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting do_stop() method";
     209            0 : }
     210              : 
     211              : void
     212            0 : FakeHSIEventGeneratorModule::do_scrap(const CommandData_t& /*args*/)
     213              : {
     214            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering do_scrap() method";
     215            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting do_scrap() method";
     216            0 : }
     217              : 
     218              : uint32_t // NOLINT(build/unsigned)
     219            0 : FakeHSIEventGeneratorModule::generate_signal_map()
     220              : {
     221              : 
     222            0 :   uint32_t signal_map = 0; // NOLINT(build/unsigned)
     223            0 :   switch (m_signal_emulation_mode) {
     224              :     case 0:
     225              :       // 0b11111111 11111111 11111111 11111111
     226              :       signal_map = UINT32_MAX;
     227              :       break;
     228              :     case 1:
     229            0 :       for (uint i = 0; i < 32; ++i)
     230            0 :         if (m_poisson_distribution(m_random_generator))
     231            0 :           signal_map = signal_map | (1UL << i);
     232              :       break;
     233            0 :     case 2:
     234            0 :       signal_map = m_uniform_distribution(m_random_generator);
     235            0 :       break;
     236            0 :     default:
     237            0 :       signal_map = 0;
     238              :   }
     239            0 :   TLOG_DEBUG(3) << "raw gen. map: " << std::bitset<32>(signal_map);
     240            0 :   return signal_map;
     241              : }
     242              : 
     243              : void
     244            0 : FakeHSIEventGeneratorModule::do_hsi_work(std::atomic<bool>& running_flag)
     245              : {
     246            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering generate_hsievents() method";
     247              : 
     248              :   // Wait for there to be a valid timestsamp estimate before we start
     249              :   // TODO put in tome sort of timeout? Stoyan Trilov stoyan.trilov@cern.ch
     250            0 :   if (m_timestamp_estimator.get() != nullptr && m_timestamp_estimator->wait_for_valid_timestamp(running_flag) ==
     251              :                                                   utilities::TimestampEstimatorBase::kInterrupted) {
     252            0 :     ers::error(utilities::FailedToGetTimestampEstimate(ERS_HERE));
     253            0 :     return;
     254              :   }
     255              : 
     256            0 :   m_generated_counter = 0;
     257            0 :   m_sent_counter = 0;
     258            0 :   m_last_generated_timestamp = 0;
     259            0 :   m_last_sent_timestamp = 0;
     260            0 :   m_failed_to_send_counter = 0;
     261              : 
     262            0 :   bool break_flag = false;
     263              : 
     264            0 :   auto prev_gen_time = std::chrono::steady_clock::now();
     265              : 
     266            0 :   while (!break_flag) {
     267              : 
     268              :     // emulate some signals
     269            0 :     uint32_t signal_map = generate_signal_map();           // NOLINT(build/unsigned)
     270            0 :     uint32_t trigger_map = signal_map & m_enabled_signals; // NOLINT(build/unsigned)
     271              : 
     272            0 :     TLOG_DEBUG(3) << "masked gen. map:" << std::bitset<32>(trigger_map);
     273              : 
     274              :     // if at least one active signal, send a HSIEvent
     275            0 :     if (trigger_map && m_timestamp_estimator.get() != nullptr) {
     276              : 
     277            0 :       dfmessages::timestamp_t ts = m_timestamp_estimator->get_timestamp_estimate();
     278              : 
     279            0 :       ts += m_timestamp_offset;
     280              : 
     281            0 :       ++m_generated_counter;
     282              : 
     283            0 :       m_last_generated_timestamp.store(ts);
     284              : 
     285            0 :       dfmessages::HSIEvent event =
     286            0 :         dfmessages::HSIEvent(m_hsi_device_id, trigger_map, ts, m_generated_counter, m_run_number);
     287            0 :       send_hsi_event(event);
     288              : 
     289              :       // Send raw HSI data to a DLH
     290            0 :       std::array<uint32_t, 7> hsi_struct;
     291            0 :       hsi_struct[0] = (0x1 << 6) | 0x1; // DAQHeader, frame version: 1, det id: 1
     292            0 :       hsi_struct[1] = ts;
     293            0 :       hsi_struct[2] = ts >> 32;
     294            0 :       hsi_struct[3] = signal_map;
     295            0 :       hsi_struct[4] = 0x0;
     296            0 :       hsi_struct[5] = trigger_map;
     297            0 :       hsi_struct[6] = m_generated_counter;
     298              : 
     299            0 :       TLOG_DEBUG(3) << get_name() << ": Formed HSI_FRAME_STRUCT " << std::hex << "0x" << hsi_struct[0] << ", 0x"
     300            0 :                     << hsi_struct[1] << ", 0x" << hsi_struct[2] << ", 0x" << hsi_struct[3] << ", 0x" << hsi_struct[4]
     301            0 :                     << ", 0x" << hsi_struct[5] << ", 0x" << hsi_struct[6] << "\n";
     302              : 
     303            0 :       send_raw_hsi_data(hsi_struct, m_raw_hsi_data_sender.get());
     304              :     }
     305              : 
     306              :     // sleep for the configured event period, if trigger ticks are not 0, otherwise do not send anything
     307            0 :     if (m_active_trigger_rate.load() > 0) {
     308            0 :       auto next_gen_time = prev_gen_time + std::chrono::microseconds(m_event_period.load());
     309              : 
     310              :       // check running_flag periodically
     311            0 :       auto flag_check_period = std::chrono::milliseconds(1);
     312            0 :       auto next_flag_check_time = prev_gen_time + flag_check_period;
     313              : 
     314            0 :       while (next_gen_time > next_flag_check_time + flag_check_period) {
     315            0 :         if (!running_flag.load()) {
     316            0 :           TLOG_DEBUG(0) << "while waiting to generate fake hsi event, negative run gatherer flag detected.";
     317            0 :           break_flag = true;
     318            0 :           break;
     319              :         }
     320            0 :         std::this_thread::sleep_until(next_flag_check_time);
     321            0 :         next_flag_check_time = next_flag_check_time + flag_check_period;
     322              :       }
     323            0 :       if (break_flag == false) {
     324            0 :         std::this_thread::sleep_until(next_gen_time);
     325              :       }
     326              : 
     327            0 :       prev_gen_time = next_gen_time;
     328              : 
     329              :     } else {
     330            0 :       std::this_thread::sleep_for(std::chrono::microseconds(250000));
     331            0 :       continue;
     332              :     }
     333              :   }
     334              : 
     335            0 :   std::ostringstream oss_summ;
     336            0 :   oss_summ << ": Exiting the generate_hsievents() method, generated " << m_generated_counter
     337            0 :            << " HSIEvent messages and successfully sent " << m_sent_counter << " copies. ";
     338            0 :   ers::info(dunedaq::hsilibs::ProgressUpdate(ERS_HERE, get_name(), oss_summ.str()));
     339            0 :   TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting do_work() method";
     340            0 : }
     341              : 
     342              : } // namespace hsilibs
     343              : } // namespace dunedaq
     344              : 
     345            0 : DEFINE_DUNE_DAQ_MODULE(dunedaq::hsilibs::FakeHSIEventGeneratorModule)
     346              : 
     347              : // Local Variables:
     348              : // c-basic-offset: 2
     349              : // End:
        

Generated by: LCOV version 2.0-1