DUNE-DAQ
DUNE Trigger and Data Acquisition software
Loading...
Searching...
No Matches
SNBDataHandlingModel.hxx
Go to the documentation of this file.
1// Declarations for SNBDataHandlingModel
2
3#include <folly/coro/BlockingWait.h>
4#include <folly/coro/Timeout.h>
5
6#include <typeinfo>
7
8namespace dunedaq {
9namespace snbmodules {
10
11template<class RDT, class RHT, class LBT, class RPT, class IDT>
12void
14{
15 // Setup request queues
16 // setup_request_queues(mcfg);
17 try {
18 for (auto input : mcfg->get_inputs()) {
19 if (input->get_data_type() == "DataRequest") {
21 }
22 }
23 for (auto output : mcfg->get_outputs()) {
24 if (output->get_data_type() == "TimeSync") {
27 m_timesync_connection_name = output->UID();
28 break;
29 }
30 }
31 } catch (const ers::Issue& excpt) {
32 throw datahandlinglibs::ResourceQueueError(ERS_HERE, "raw_input or frag_output", "SNBDataHandlingModel", excpt);
33 }
34
36
37 // Instantiate functionalities
39 m_error_registry->set_ers_metadata("DLH of SourceID[" + std::to_string(mcfg->get_source_id()) + "] ");
40 m_latency_buffer_impl.reset(new LBT());
43
47
48 // m_request_handler_impl->init(args);
49 // m_raw_processor_impl->init(args);
51 m_fake_trigger = false;
52 m_sourceid.id = mcfg->get_source_id();
53 m_sourceid.subsystem = RDT::subsystem;
57
60 ers::error(datahandlinglibs::ConfigurationError(
63 "Queue buffers (FixedRateQueue, BinarySearchQueue) expect in-order data and must use "
64 "post_processing_delay_ticks = 0."));
65 }
66 }
67
68 // Configure implementations:
69 m_raw_processor_impl->conf(mcfg);
70 // Configure the latency buffer before the request handler so the request handler can check for alignment
71 // restrictions
72 try {
74 } catch (const std::bad_alloc& be) {
76 datahandlinglibs::ConfigurationError(ERS_HERE, m_sourceid, "Latency Buffer can't be allocated with size!"));
77 }
78 m_request_handler_impl->conf(mcfg);
79}
80
81template<class RDT, class RHT, class LBT, class RPT, class IDT>
82void
83SNBDataHandlingModel<RDT, RHT, LBT, RPT, IDT>::conf(const appfwk::DAQModule::CommandData_t& /*args*/)
84{
85 // Configure and register consume callback
87 std::bind(&SNBDataHandlingModel<RDT, RHT, LBT, RPT, IDT>::consume_callback, this, std::placeholders::_1);
88
89 // Register callback
91 dmcbr->register_callback<IDT>(m_raw_data_callback_conf, m_consume_callback);
92
93 // Configure threads:
95 m_timesync_thread.set_name("timesync", m_sourceid.id);
96 }
98 m_postprocess_scheduler_thread.set_name("pprocsched", m_sourceid.id);
99 m_timekeeper = std::make_unique<folly::ThreadWheelTimekeeper>();
100 }
101}
102
103template<class RDT, class RHT, class LBT, class RPT, class IDT>
104void
105SNBDataHandlingModel<RDT, RHT, LBT, RPT, IDT>::start(const appfwk::DAQModule::CommandData_t& args)
106{
107 // Reset opmon variables
108 m_sum_payloads = 0;
109 m_num_payloads = 0;
110 m_sum_requests = 0;
111 m_num_requests = 0;
116
117 m_t0 = std::chrono::high_resolution_clock::now();
118
120
121 TLOG_DEBUG(TLVL_WORK_STEPS) << "Starting threads...";
122 m_raw_processor_impl->start(args);
123 m_request_handler_impl->start(args);
126 }
129 this);
130 }
131 // Register callback to receive and dispatch data requests
132 m_data_request_receiver->add_callback(
133 std::bind(&SNBDataHandlingModel<RDT, RHT, LBT, RPT, IDT>::dispatch_requests, this, std::placeholders::_1));
134}
135
136template<class RDT, class RHT, class LBT, class RPT, class IDT>
137void
138SNBDataHandlingModel<RDT, RHT, LBT, RPT, IDT>::stop(const appfwk::DAQModule::CommandData_t& args)
139{
140 TLOG_DEBUG(TLVL_WORK_STEPS) << "Stoppping threads...";
141
142 // Stop receiving data requests as first thing
143 m_data_request_receiver->remove_callback();
144 // Stop the other threads
145 m_request_handler_impl->stop(args);
147 while (!m_timesync_thread.get_readiness()) {
148 std::this_thread::sleep_for(std::chrono::milliseconds(10));
149 }
150 }
152 m_baton.post(); // In case the coroutine is still waiting when the consumer has stopped
153 while (!m_postprocess_scheduler_thread.get_readiness()) {
154 std::this_thread::sleep_for(std::chrono::milliseconds(10));
155 }
156 }
157 TLOG_DEBUG(TLVL_WORK_STEPS) << "Flushing latency buffer with occupancy: " << m_latency_buffer_impl->occupancy();
158 m_latency_buffer_impl->flush();
159 m_raw_processor_impl->stop(args);
160 m_raw_processor_impl->reset_last_daq_time();
161}
162
163template<class RDT, class RHT, class LBT, class RPT, class IDT>
164void
166{
169 ri.set_num_payloads(m_num_payloads.exchange(0));
170
172
173 auto now = std::chrono::high_resolution_clock::now();
174 int new_packets = m_stats_packet_count.exchange(0);
175 double seconds = std::chrono::duration_cast<std::chrono::microseconds>(now - m_t0).count() / 1000000.;
176 m_t0 = now;
177
178 // 08-May-2025, KAB: added a message to warn users when latency buffer inserts are failing.
179 int local_num_lb_insert_failures = m_num_lb_insert_failures.exchange(0);
180 if (local_num_lb_insert_failures != 0) {
181 ers::warning(datahandlinglibs::NonZeroLatencyBufferInsertFailures(
182 ERS_HERE, m_sourceid, local_num_lb_insert_failures, ri.num_payloads()));
183 }
184
185 ri.set_rate_payloads_consumed(new_packets / seconds / 1000.);
186 ri.set_num_lb_insert_failures(local_num_lb_insert_failures);
188 ri.set_num_requests(m_num_requests.exchange(0));
190 ri.set_last_daq_timestamp(m_raw_processor_impl->get_last_daq_time());
191 ri.set_newest_timestamp(m_raw_processor_impl->get_last_daq_time());
192 ri.set_oldest_timestamp(m_request_handler_impl->get_oldest_time());
193
194 this->publish(std::move(ri));
195}
196
197template<class RDT, class RHT, class LBT, class RPT, class IDT>
198void
200{
201 if constexpr (std::is_same_v<IDT, RDT>) {
202 process_item(std::move(payload));
203 } else {
204 auto transformed = transform_payload(payload);
205 for (auto& i : transformed) {
206 process_item(std::move(i));
207 }
208 }
209}
210
211template<class RDT, class RHT, class LBT, class RPT, class IDT>
212void
217
218template<class RDT, class RHT, class LBT, class RPT, class IDT>
219void
221{
222 m_raw_processor_impl->preprocess_item(&payload);
224 int64_t diff1 = payload.get_timestamp() - m_request_handler_impl->get_cutoff_timestamp();
225 if (diff1 <= 0) {
226 // m_request_handler_impl->increment_tardy_tp_count();
227 ers::warning(datahandlinglibs::DataPacketArrivedTooLate(ERS_HERE,
230 payload.get_timestamp(),
231 m_request_handler_impl->get_cutoff_timestamp(),
232 diff1,
233 (static_cast<double>(diff1) / 62500.0)));
234 }
235 }
236 while (m_latency_buffer_impl->isFull()) {
237 std::this_thread::sleep_for(std::chrono::milliseconds(1));
238 }
239
240 const RDT* written = nullptr;
242 if (!m_latency_buffer_impl->write(std::move(payload))) {
243 // TLOG_DEBUG(TLVL_TAKE_NOTE) << "***ERROR: Latency buffer insert failed! (Payload timestamp=" << payload.get_timestamp() << ")";
245 return;
246 }
247 written = m_latency_buffer_impl->back();
248 } else {
249 const auto [returned, result] = m_latency_buffer_impl->write_and_return(std::move(payload));
250 if (!result) {
251 // TLOG_DEBUG(TLVL_TAKE_NOTE) << "***ERROR: Latency buffer insert failed! (Payload timestamp=" << payload.get_timestamp() << ")";
253 return;
254 }
255 written = returned;
256 }
257
258 if (m_processing_delay_ticks == 0) {
259 m_raw_processor_impl->postprocess_item(written);
263 } else {
264 m_baton.post();
265 }
266}
267
268template<class RDT, class RHT, class LBT, class RPT, class IDT>
269void
274
275template<class RDT, class RHT, class LBT, class RPT, class IDT>
276folly::coro::Task<void>
278{
279
280 // TLOG_DEBUG(TLVL_WORK_STEPS) << "Postprocess schedule coroutine started...";
281 timestamp_t newest_ts = 0;
282 timestamp_t end_win_ts = 0;
283 bool first_cycle = true;
284 auto last_post_proc_time = std::chrono::system_clock::now();
285 auto now = last_post_proc_time;
286 std::chrono::milliseconds milliseconds;
287 RDT processed_element;
288
289 // Deferral of the post processing, to allow elements being reordered in the LB
290 // Basically, find data older than a certain timestamp and process all data since the last post-processed element up
291 // to that value
292 while (m_run_marker.load()) {
293 try {
294 co_await folly::coro::timeout(
295 m_baton.operator co_await(), std::chrono::milliseconds{ m_post_processing_delay_max_wait }, m_timekeeper.get());
296 m_baton.reset();
297 } catch (const folly::FutureTimeout&) {
299 }
300
301 if (m_latency_buffer_impl->occupancy() == 0) {
302 continue;
303 }
304
305 now = std::chrono::system_clock::now();
306 milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_post_proc_time);
307
308 if (static_cast<uint64_t>(milliseconds.count()) <= m_post_processing_delay_min_wait) {
309 continue;
310 }
311
312 last_post_proc_time = now;
313
314 // Get the LB boundaries
315 auto tail = m_latency_buffer_impl->back();
316 newest_ts = tail->get_timestamp();
317
318 if (first_cycle) {
319 auto head = m_latency_buffer_impl->front();
320 processed_element.set_timestamp(head->get_timestamp());
321 first_cycle = false;
322 // TLOG() << "***** First pass post processing *****";
323 }
324
325 if (newest_ts - processed_element.get_timestamp() > m_processing_delay_ticks) {
326 end_win_ts = newest_ts - m_processing_delay_ticks;
327 auto start_iter = m_latency_buffer_impl->lower_bound(processed_element, false);
328 processed_element.set_timestamp(end_win_ts);
329 auto end_iter = m_latency_buffer_impl->lower_bound(processed_element, false);
330
331 for (auto it = start_iter; it != end_iter; ++it) {
332 m_raw_processor_impl->postprocess_item(&(*it));
336 }
337 }
338 }
339}
340
341template<class RDT, class RHT, class LBT, class RPT, class IDT>
342void
344{
345 TLOG_DEBUG(TLVL_WORK_STEPS) << "TimeSync thread started...";
346 m_num_requests = 0;
347 m_sum_requests = 0;
348 uint64_t msg_seqno = 0;
349 timestamp_t prev_timestamp = 0;
350 auto once_per_run = true;
351 size_t zero_timestamp_count = 0;
352 size_t duplicate_timestamp_count = 0;
353 size_t total_timestamp_count = 0;
354 while (m_run_marker.load()) {
355 try {
356 auto timesyncmsg = dfmessages::TimeSync(m_raw_processor_impl->get_last_daq_time());
357 ++total_timestamp_count;
358 // daq_time is zero for the first received timesync, and may
359 // be the same as the previous daq_time if the data has
360 // stopped flowing. In both cases we don't send the TimeSync
361 if (timesyncmsg.daq_time != 0 && timesyncmsg.daq_time != prev_timestamp) {
362 prev_timestamp = timesyncmsg.daq_time;
363 timesyncmsg.run_number = m_run_number;
364 timesyncmsg.sequence_number = ++msg_seqno;
365 timesyncmsg.source_id = m_sourceid.id;
366 TLOG_DEBUG(TLVL_TIME_SYNCS) << "New timesync: daq=" << timesyncmsg.daq_time
367 << " wall=" << timesyncmsg.system_time << " run=" << timesyncmsg.run_number
368 << " seqno=" << timesyncmsg.sequence_number
369 << " source_id=" << timesyncmsg.source_id;
370 try {
371 dfmessages::TimeSync timesyncmsg_copy(timesyncmsg);
372 m_timesync_sender->send(std::move(timesyncmsg_copy), std::chrono::milliseconds(500));
373 } catch (ers::Issue& excpt) {
375 datahandlinglibs::TimeSyncTransmissionFailed(ERS_HERE, m_sourceid, m_timesync_connection_name, excpt));
376 }
377
378 if (m_fake_trigger) {
382 dr.trigger_timestamp = timesyncmsg.daq_time > 500 * us ? timesyncmsg.daq_time - 500 * us : 0;
383 auto width = 300000;
384 uint offset = 100;
388 dr.data_destination = "data_fragments_q";
389 TLOG_DEBUG(TLVL_WORK_STEPS) << "Issuing fake trigger based on timesync. " << " ts=" << dr.trigger_timestamp
390 << " window_begin=" << dr.request_information.window_begin
391 << " window_end=" << dr.request_information.window_end;
392 m_request_handler_impl->issue_request(dr);
393
396 }
397 } else {
398 if (timesyncmsg.daq_time == 0) {
399 ++zero_timestamp_count;
400 }
401 if (timesyncmsg.daq_time == prev_timestamp) {
402 ++duplicate_timestamp_count;
403 }
404 if (once_per_run) {
405 TLOG() << "Timesync with DAQ time 0 won't be sent out as it's an invalid sync.";
406 once_per_run = false;
407 }
408 }
409 } catch (const iomanager::TimeoutExpired& excpt) {
410 // ++m_timesyncqueue_timeout;
411 }
412 // Split up the 100ms sleep into 10 sleeps of 10ms, so we respond to "stop" quicker
413 for (size_t i = 0; i < 10; ++i) {
414 std::this_thread::sleep_for(std::chrono::milliseconds(10));
415 if (!m_run_marker.load()) {
416 break;
417 }
418 }
419 }
420 once_per_run = true;
421 TLOG_DEBUG(TLVL_WORK_STEPS) << "TimeSync thread joins... (timestamp count, zero/same/total = "
422 << zero_timestamp_count << "/" << duplicate_timestamp_count << "/"
423 << total_timestamp_count << ")";
424}
425
426template<class RDT, class RHT, class LBT, class RPT, class IDT>
427void
429{
430 if (data_request.request_information.component != m_sourceid) {
432 datahandlinglibs::RequestSourceIDMismatch(ERS_HERE, m_sourceid, data_request.request_information.component));
433 return;
434 }
435 TLOG_DEBUG(TLVL_QUEUE_POP) << "Received DataRequest" << " for trig/seq_number " << data_request.trigger_number << "."
436 << data_request.sequence_number << ", runno " << data_request.run_number
437 << ", trig timestamp " << data_request.trigger_timestamp
438 << ", SourceID: " << data_request.request_information.component << ", window begin/end "
439 << data_request.request_information.window_begin << "/"
440 << data_request.request_information.window_end
441 << ", dest: " << data_request.data_destination;
442 m_request_handler_impl->issue_request(data_request);
445}
446
447} // namespace snbmodules
448} // namespace dunedaq
@ TLVL_WORK_STEPS
#define ERS_HERE
@ TLVL_QUEUE_POP
@ TLVL_TIME_SYNCS
const dunedaq::appmodel::LatencyBuffer * get_latency_buffer() const
Get "latency_buffer" relationship value.
uint64_t get_post_processing_delay_ticks() const
Get "post_processing_delay_ticks" attribute value. Number of clock tick by which post processing of i...
const dunedaq::appmodel::DataProcessor * get_data_processor() const
Get "data_processor" relationship value.
uint64_t get_post_processing_delay_max_wait() const
Get "post_processing_delay_max_wait" attribute value. Maximum wait time (ms) before post processing c...
uint64_t get_post_processing_delay_min_wait() const
Get "post_processing_delay_min_wait" attribute value. Minimum time (ms) between consecutive post proc...
const dunedaq::appmodel::RequestHandler * get_request_handler() const
Get "request_handler" relationship value.
const dunedaq::appmodel::DataHandlerConf * get_module_configuration() const
Get "module_configuration" relationship value.
const dunedaq::appmodel::DataMoveCallbackConf * get_raw_data_callback() const
Get "raw_data_callback" relationship value. Configuration for raw data callback.
uint32_t get_source_id() const
Get "source_id" attribute value.
bool get_post_processing_enabled() const
Get "post_processing_enabled" attribute value.
const std::string & UID() const noexcept
const std::vector< const dunedaq::confmodel::Connection * > & get_inputs() const
Get "inputs" relationship value. List of connections to/from this module.
const std::vector< const dunedaq::confmodel::Connection * > & get_outputs() const
Get "outputs" relationship value. Output connections from this module.
static std::shared_ptr< DataMoveCallbackRegistry > get()
void register_node(ElementId name, NewNodePtr)
void publish(google::protobuf::Message &&, CustomOrigin &&co={}, OpMonLevel l=to_level(EntryOpMonLevel::kDefault)) const noexcept
std::atomic< num_lb_insert_failures_t > m_num_lb_insert_failures
void start(const appfwk::DAQModule::CommandData_t &args)
std::atomic< rawq_timeout_count_t > m_rawq_timeout_count
std::shared_ptr< RawDataProcessorType > m_raw_processor_impl
virtual std::vector< RDT > transform_payload(IDT &original) const
void init(const appmodel::DataHandlerModule *modconf)
Forward calls from the appfwk.
std::shared_ptr< timesync_sender_ct > m_timesync_sender
std::chrono::time_point< std::chrono::high_resolution_clock > m_t0
std::shared_ptr< RequestHandlerType > m_request_handler_impl
void conf(const appfwk::DAQModule::CommandData_t &args)
const appmodel::DataMoveCallbackConf * m_raw_data_callback_conf
std::unique_ptr< folly::ThreadWheelTimekeeper > m_timekeeper
void stop(const appfwk::DAQModule::CommandData_t &args)
std::shared_ptr< LatencyBufferType > m_latency_buffer_impl
void dispatch_requests(dfmessages::DataRequest &data_request)
std::atomic< num_post_processing_delay_max_waits_t > m_num_post_processing_delay_max_waits
std::shared_ptr< request_receiver_ct > m_data_request_receiver
std::unique_ptr< datahandlinglibs::FrameErrorRegistry > m_error_registry
void run_timesync()
Function that will be run in its own thread and sends periodic timesync messages by pushing them to t...
Base class for any user define issue.
Definition Issue.hpp:80
double offset
static int64_t now()
#define TLOG_DEBUG(lvl,...)
Definition Logging.hpp:112
#define TLOG(...)
Definition macro.hpp:22
Including Qt Headers.
Definition module.cpp:16
static std::shared_ptr< iomanager::SenderConcept< Datatype > > get_iom_sender(iomanager::ConnectionId const &id)
static std::shared_ptr< iomanager::ReceiverConcept< Datatype > > get_iom_receiver(iomanager::ConnectionId const &id)
void warning(const Issue &issue)
Definition ers.hpp:126
void error(const Issue &issue)
Definition ers.hpp:92
timestamp_t window_end
End of the data collection window.
SourceID component
The ID of the Requested Component.
timestamp_t window_begin
Start of the data collection window.
This message represents a request for data sent to a single component of the DAQ.
sequence_number_t sequence_number
Sequence Number of the request.
trigger_number_t trigger_number
Trigger number the request corresponds to.
timestamp_t trigger_timestamp
Timestamp of trigger.
run_number_t run_number
The current run number.
A synthetic message used to ensure that all elements of a DAQ system are synchronized.
Definition TimeSync.hpp:25