LCOV - code coverage report
Current view: top level - fdreadoutlibs/src/daphne - DAPHNEFrameProcessor.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 0.0 % 144 0
Test Date: 2026-10-11 15:05:20 Functions: 0.0 % 19 0

            Line data    Source code
       1              : /**
       2              :  * @file DAPHNEFrameProcessor.hpp DAPHNE specific Task based raw processor
       3              :  * implementation
       4              :  *
       5              :  * This is part of the DUNE DAQ , copyright 2020.
       6              :  * Licensing/copyright details are in the COPYING file that you should have
       7              :  * received with this code.
       8              :  */
       9              : 
      10              : #include "fdreadoutlibs/daphne/DAPHNEFrameProcessor.hpp"
      11              : #include "fddetdataformats/DAPHNEFrame.hpp"
      12              : #include "trgdataformats/TriggerPrimitive.hpp"
      13              : 
      14              : #include "confmodel/GeoId.hpp"
      15              : 
      16              : #include <atomic>
      17              : #include <functional>
      18              : #include <memory>
      19              : #include <string>
      20              : 
      21              : using dunedaq::datahandlinglibs::logging::TLVL_BOOKKEEPING;
      22              : using dunedaq::datahandlinglibs::logging::TLVL_FRAME_RECEIVED;
      23              : 
      24              : DUNE_DAQ_TYPESTRING(dunedaq::trigger::TriggerPrimitiveTypeAdapter, "TriggerPrimitive")
      25            0 : DUNE_DAQ_TYPESTRING(std::vector<dunedaq::trigger::TriggerPrimitiveTypeAdapter>, "TriggerPrimitiveVector")
      26              : 
      27              : namespace dunedaq {
      28              : namespace fdreadoutlibs {
      29              : 
      30              : void
      31            0 : DAPHNEFrameProcessor::conf(const appmodel::DataHandlerModule* conf)
      32              : {
      33            0 :   TLOG() << "Looking for TP sink...";
      34              : 
      35            0 :   for (auto output : conf->get_outputs()) {
      36            0 :     TLOG() << "On outputs... (" << output->UID() << "," << output->get_data_type() << ")";
      37            0 :     try {
      38            0 :       if (output->get_data_type() == "TriggerPrimitiveVector") {
      39            0 :         TLOG() << "Found TP sink.";
      40            0 :         m_tp_sink = get_iom_sender<std::vector<trigger::TriggerPrimitiveTypeAdapter>>(output->UID());
      41            0 :         TLOG() << " SINK INITIALIZED for TriggerPrimitives with UID : " << output->UID();
      42              :       }
      43            0 :     } catch (const ers::Issue& excpt) {
      44            0 :       ers::error(datahandlinglibs::ResourceQueueError(ERS_HERE, "tp", "DefaultRequestHandlerModel", excpt));
      45            0 :     }
      46              :   }
      47              : 
      48            0 :   TLOG() << "Registering processing tasks...";
      49            0 :   inherited::add_preprocess_task(std::bind(&DAPHNEFrameProcessor::timestamp_check, this, std::placeholders::_1));
      50              : 
      51            0 :   auto dp = conf->get_module_configuration()->get_data_processor();
      52            0 :   if (dp == nullptr) {
      53              :     TLOG() << " PDS Data processor does not exist.";
      54              :   } else {
      55            0 :     auto proc_conf = dp->cast<appmodel::PDSRawDataProcessor>();
      56            0 :     if (proc_conf == nullptr) {
      57            0 :       TLOG() << "PDS RawDataProcessor does not exist.";
      58              :     } else {
      59            0 :       m_def_adc_intg_thresh = proc_conf->get_default_adc_intg_thresh();
      60              : 
      61            0 :       auto geo_id = conf->get_geo_id();
      62            0 :       if (geo_id != nullptr) {
      63            0 :         m_det_id = geo_id->get_detector_id();
      64            0 :         m_crate_id = geo_id->get_crate_id();
      65            0 :         m_slot_id = geo_id->get_slot_id();
      66            0 :         m_stream_id = geo_id->get_stream_id();
      67              :       }
      68              : 
      69            0 :       m_channel_map = dunedaq::detchannelmaps::make_pds_map(proc_conf->get_channel_map());
      70            0 :       const std::vector<unsigned int> channel_mask_vec = proc_conf->get_channel_mask();
      71              : 
      72            0 :       for (int chan = 0; chan < 48; chan++) { // 40 physical PDS channel 8 not. 0->7 contain light info, 8,9, additional
      73              :                                               // info. 10-17 light, 18,19 not etc...
      74            0 :         trgdataformats::channel_t off_channel = m_channel_map->get_offline_channel_from_det_crate_slot_stream_chan(
      75            0 :           m_det_id, m_crate_id, m_slot_id, m_stream_id, chan);
      76            0 :         if (std::find(channel_mask_vec.begin(), channel_mask_vec.end(), off_channel) != channel_mask_vec.end())
      77            0 :           m_channel_mask_set.insert(
      78              :             off_channel); // m_channel_mask will be a vector fille with random chanel which need to be masked.
      79              :       }
      80              : 
      81            0 :       if (m_post_processing_enabled) {
      82              :         // Extract TPs back as a pre-processing task, due to LatencyBuffer post-proc issues using SkipList.
      83            0 :         inherited::add_preprocess_task(std::bind(&DAPHNEFrameProcessor::extract_tps, this, std::placeholders::_1));
      84              :       }
      85            0 :     }
      86              :   }
      87              : 
      88            0 :   TLOG() << "Calling parent conf.";
      89            0 :   inherited::conf(conf);
      90            0 : }
      91              : 
      92              : void
      93            0 : DAPHNEFrameProcessor::start(const appfwk::DAQModule::CommandData_t& args)
      94              : {
      95              :   // Reset timestamp check
      96            0 :   m_previous_ts = 0;
      97            0 :   m_current_ts = 0;
      98            0 :   m_first_ts_missmatch = true;
      99            0 :   m_ts_error_ctr = 0;
     100              : 
     101              :   // Reset stats
     102            0 :   m_t0 = std::chrono::high_resolution_clock::now();
     103            0 :   m_num_new_tps.exchange(0);
     104              : 
     105            0 :   inherited::start(args);
     106            0 : }
     107              : void
     108            0 : DAPHNEFrameProcessor::stop(const appfwk::DAQModule::CommandData_t& args)
     109              : {
     110            0 :   inherited::stop(args);
     111            0 : }
     112              : /**
     113              :  * Pipeline Stage 1.: Check proper timestamp increments in DAPHNE frame
     114              :  * */
     115              : void
     116            0 : DAPHNEFrameProcessor::timestamp_check(frameptr fp)
     117              : {
     118              :   // Let Source Emulator deal with this
     119              :   /*
     120              :     // If EMU data, emulate perfectly incrementing timestamp
     121              :     if (inherited::m_emulator_mode) { // emulate perfectly incrementing timestamp
     122              :       // RS warning : not fixed rate!
     123              :       if (m_first_ts_fake) {
     124              :         fp->fake_timestamps(m_previous_ts, 16);
     125              :         m_first_ts_fake = false;
     126              :       } else {
     127              :         fp->fake_timestamps(m_previous_ts + 192, 16);
     128              :       }
     129              :     }*/
     130              : 
     131              :   // FIXME: This is a temporary fix to avoid frames with unphysical timestamp set to the far future to interfere with
     132              :   // the operations of the LB.
     133              :   // These frames are effectively "corrupted" or "invalid frames" and hould be handled as such.
     134              : 
     135            0 :   auto df_ptr = reinterpret_cast<dunedaq::fddetdataformats::DAPHNEFrame*>(fp);
     136              : 
     137            0 :   for (size_t i = 0; i < fp->get_num_frames(); ++i) {
     138              : 
     139            0 :     if (df_ptr[i].get_timestamp() > 0xFFFFFFFFFFFF0000 || df_ptr[i].get_timestamp() < 0xFFFF) {
     140            0 :       ers::warning(PDSUnphysicalFrameTimestamp(ERS_HERE, df_ptr[i].get_timestamp(), df_ptr[i].get_channel(), i));
     141            0 :       df_ptr[i].set_timestamp(0);
     142              :     }
     143              :   }
     144              : 
     145              :   // Acquire timestamp
     146            0 :   m_current_ts = fp->get_timestamp();
     147            0 :   uint64_t k_clock_frequency = 62500000; // NOLINT(build/unsigned)
     148            0 :   TLOG_DEBUG(TLVL_FRAME_RECEIVED) << "Received DAPHNE frame timestamp value of " << m_current_ts << " ticks (..."
     149            0 :                                   << std::fixed << std::setprecision(8)
     150            0 :                                   << (static_cast<double>(m_current_ts % (k_clock_frequency * 1000)) /
     151            0 :                                       static_cast<double>(k_clock_frequency))
     152            0 :                                   << " sec)"; // NOLINT
     153              : 
     154            0 :   if (m_ts_error_ctr > 1000) {
     155            0 :     if (!m_problem_reported) {
     156            0 :       std::cout << "*** Data Integrity ERROR *** Timestamp continuity is completely broken! "
     157            0 :                 << "Something is wrong with the FE source or with the configuration!\n";
     158            0 :       m_problem_reported = true;
     159              :     }
     160              :   }
     161              : 
     162            0 :   m_previous_ts = m_current_ts;
     163            0 :   m_last_processed_daq_ts = m_current_ts;
     164            0 : }
     165              : 
     166              : /**
     167              :  * Pipeline Stage 2.: Check DAPHNE headers for error flags
     168              :  * */
     169              : void
     170            0 : DAPHNEFrameProcessor::frame_error_check(frameptr /*fp*/)
     171              : {
     172              :   // check error fields
     173            0 : }
     174              : 
     175              : void
     176            0 : DAPHNEFrameProcessor::extract_tps(constframeptr fp)
     177              : {
     178              : 
     179            0 :   if (!fp || fp == nullptr) {
     180              :     return;
     181              :   }
     182              : 
     183            0 :   auto nonconstframeptr = const_cast<frameptr>(fp);
     184            0 :   auto df_ptr = reinterpret_cast<dunedaq::fddetdataformats::DAPHNEFrame*>((uint8_t*)nonconstframeptr); // NOLINT
     185            0 :   std::vector<trigger::TriggerPrimitiveTypeAdapter> ttpp;
     186              : 
     187            0 :   for (size_t i = 0; i < fp->get_num_frames(); i++) {
     188            0 :     for (size_t j = 0; j < fddetdataformats::DAPHNEFrame::PeakDescriptorData::max_peaks; j++) {
     189            0 :       if (df_ptr[i].peaks_data.is_found(j)) {
     190            0 :         int ch = m_channel_map->get_offline_channel_from_det_crate_slot_stream_chan(df_ptr[i].daq_header.det_id,
     191            0 :                                                                                     df_ptr[i].daq_header.crate_id,
     192            0 :                                                                                     df_ptr[i].daq_header.slot_id,
     193            0 :                                                                                     df_ptr[i].daq_header.link_id,
     194            0 :                                                                                     df_ptr[i].get_channel());
     195            0 :         if (std::binary_search(m_channel_mask_set.begin(), m_channel_mask_set.end(), ch))
     196            0 :           continue;
     197            0 :         if (df_ptr[i].peaks_data.get_adc_integral(j) < m_def_adc_intg_thresh)
     198            0 :           continue;
     199              : 
     200            0 :         trigger::TriggerPrimitiveTypeAdapter tpa;
     201            0 :         tpa.tp = peak_to_tp(df_ptr[i], j); // this is the trigger primitive
     202              :         // check for timestamps that are due to frame timestamps ~ ts=0, and ignore these peaks
     203            0 :         if (tpa.tp.time_start > 0xFFFFFFFFFFFF0000 || tpa.tp.time_start < 0xFFFF) {
     204            0 :           ers::warning(PDSPeakIgnored(ERS_HERE, tpa.tp.time_start, tpa.tp.channel, i, j));
     205            0 :           continue;
     206              :         }
     207              : 
     208            0 :         tpa.tp.detid = df_ptr->daq_header.det_id;
     209            0 :         ttpp.push_back(tpa);
     210              :       }
     211              :     }
     212              :   }
     213              : 
     214            0 :   int num_new_tps = ttpp.size();
     215            0 :   if (num_new_tps > 0) {
     216              : 
     217            0 :     const auto s_ts_begin = ttpp.front().tp.time_start;
     218            0 :     const auto channel_begin = ttpp.front().tp.channel;
     219            0 :     const auto s_ts_end = ttpp.back().tp.time_start;
     220            0 :     const auto channel_end = ttpp.back().tp.channel;
     221              : 
     222            0 :     if (!m_tp_sink->try_send(std::move(ttpp), iomanager::Sender::s_no_block)) {
     223            0 :       ers::warning(FailedToSendTPVector(ERS_HERE, s_ts_begin, channel_begin, s_ts_end, channel_end));
     224            0 :       m_tps_send_failed += num_new_tps;
     225              :     } else {
     226            0 :       m_num_new_tps += num_new_tps;
     227              :     }
     228              :   }
     229              : 
     230            0 :   return;
     231            0 : }
     232              : 
     233              : dunedaq::trgdataformats::TriggerPrimitive
     234            0 : DAPHNEFrameProcessor::peak_to_tp(dunedaq::fddetdataformats::DAPHNEFrame& frame, int i)
     235              : {
     236            0 :   dunedaq::trgdataformats::TriggerPrimitive tp;
     237              :   // TODO: add check on peak presence
     238            0 :   tp.time_start = frame.get_timestamp() + frame.peaks_data.get_sample_start(i);
     239            0 :   tp.samples_to_peak = frame.peaks_data.get_sample_max(i);
     240            0 :   tp.samples_over_threshold = frame.peaks_data.get_samples_over_baseline(i);
     241              :   // FIXME : hard-coded channel map
     242              :   // WARNING: slot ids in DAPHNEs are all 0!
     243            0 :   tp.channel = m_channel_map->get_offline_channel_from_det_crate_slot_stream_chan(frame.daq_header.det_id,
     244            0 :                                                                                   frame.daq_header.crate_id,
     245            0 :                                                                                   frame.daq_header.slot_id,
     246            0 :                                                                                   frame.daq_header.link_id,
     247            0 :                                                                                   frame.get_channel());
     248            0 :   tp.adc_integral = frame.peaks_data.get_adc_integral(i);
     249            0 :   tp.adc_peak = frame.peaks_data.get_adc_max(i);
     250            0 :   tp.detid = dunedaq::trgdataformats::TypeDefaults::s_invalid_detid;
     251            0 :   return tp;
     252              : }
     253              : 
     254              : void
     255            0 : DAPHNEFrameProcessor::generate_opmon_data()
     256              : {
     257              : 
     258              :   // right now, just fill some basic tp info...
     259            0 :   if (m_post_processing_enabled) {
     260            0 :     auto now = std::chrono::high_resolution_clock::now();
     261            0 :     int num_new_tps = m_num_new_tps.exchange(0);
     262            0 :     int new_tps_suppressed_too_long = 0; // not relevant for PDS TPs
     263            0 :     int new_tps_send_failed = m_tps_send_failed.exchange(0);
     264            0 :     double seconds = std::chrono::duration_cast<std::chrono::microseconds>(now - m_t0).count() / 1000000.;
     265            0 :     TLOG_DEBUG(TLVL_BOOKKEEPING) << "TP rate: " << std::to_string(num_new_tps / seconds / 1000.) << " [kHz]";
     266            0 :     TLOG_DEBUG(TLVL_BOOKKEEPING) << "Total new TPs: " << num_new_tps;
     267              : 
     268            0 :     datahandlinglibs::opmon::HitFindingInfo tp_info;
     269            0 :     tp_info.set_rate_tp_hits(num_new_tps / seconds / 1000.);
     270              : 
     271            0 :     tp_info.set_num_tps_sent(num_new_tps);
     272            0 :     tp_info.set_num_tps_suppressed_too_long(new_tps_suppressed_too_long);
     273            0 :     tp_info.set_num_tps_send_failed(new_tps_send_failed);
     274              : 
     275            0 :     publish(std::move(tp_info));
     276              : 
     277            0 :     m_t0 = now;
     278            0 :   }
     279              : 
     280            0 :   inherited::generate_opmon_data();
     281            0 : }
     282              : 
     283              : } // namespace fdreadoutlibs
     284              : } // namespace dunedaq
        

Generated by: LCOV version 2.0-1