DUNE-DAQ
DUNE Trigger and Data Acquisition software
Loading...
Searching...
No Matches
DataHandlingModel.hxx
Go to the documentation of this file.
1// Declarations for DataHandlingModel
2
3#include <folly/CancellationToken.h>
4#include <folly/coro/BlockingWait.h>
5#include <folly/coro/CurrentExecutor.h>
6#include <folly/coro/Timeout.h>
7#include <folly/futures/ThreadWheelTimekeeper.h>
8
9#include <typeinfo>
10
11namespace dunedaq {
12namespace datahandlinglibs {
13
14template<class RDT, class RHT, class LBT, class RPT, class IDT>
15void
17{
18 // Check if a callback is defined (TPs use IOManager Queues instead)
20 if (m_raw_data_callback_conf != nullptr) {
22 TLOG_DEBUG(TLVL_WORK_STEPS) << "DataHandlingModel operating in callback mode.";
23 } else {
24 TLOG_DEBUG(TLVL_WORK_STEPS) << "DataHandlingModel operating in message polling mode.";
25 }
26
27 try {
28 for (auto input : mcfg->get_inputs()) {
29 if (input->get_data_type() == "DataRequest") {
31 } else {
33
34 if (m_raw_data_callback_conf == nullptr) {
36 m_raw_receiver_timeout_ms = std::chrono::milliseconds(input->get_recv_timeout_ms());
37 }
38 }
39 }
40 for (auto output : mcfg->get_outputs()) {
41 if (output->get_data_type() == "TimeSync") {
44 m_timesync_connection_name = output->UID();
45 break;
46 }
47 }
48 } catch (const ers::Issue& excpt) {
49 throw ResourceQueueError(ERS_HERE, "raw_input or frag_output", "DataHandlingModel", excpt);
50 }
51
52 // Raw input connection sensibility check
53 if (m_raw_data_callback_conf == nullptr && m_raw_data_receiver == nullptr) {
54 ers::error(ConfigurationError(ERS_HERE, m_sourceid, "No callback configuration, and receiver is unset!"));
55 }
56
57 // Instantiate functionalities
59 m_error_registry->set_ers_metadata("DLH of SourceID[" + std::to_string(mcfg->get_source_id()) + "] ");
60 m_latency_buffer_impl.reset(new LBT());
63
67
68 // m_request_handler_impl->init(args);
69 // m_raw_processor_impl->init(args);
71 m_fake_trigger = false;
72 m_raw_receiver_sleep_us = std::chrono::microseconds::zero();
73 m_sourceid.id = mcfg->get_source_id();
74 m_sourceid.subsystem = RDT::subsystem;
78
80 if constexpr (ExpectsOrder<LBT>) {
81 ers::error(ConfigurationError(
84 "Queue buffers (FixedRateQueue, BinarySearchQueue) expect in-order data and must use "
85 "post_processing_delay_ticks = 0."));
86 }
87 }
88
89 // Configure implementations:
90 m_raw_processor_impl->conf(mcfg);
91 // Configure the latency buffer before the request handler so the request handler can check for alignment
92 // restrictions
93 try {
95 } catch (const std::bad_alloc& be) {
96 ers::error(ConfigurationError(ERS_HERE, m_sourceid, "Latency Buffer can't be allocated with size!"));
97 }
98 m_request_handler_impl->conf(mcfg);
99}
100
101template<class RDT, class RHT, class LBT, class RPT, class IDT>
102void
103DataHandlingModel<RDT, RHT, LBT, RPT, IDT>::conf(const appfwk::DAQModule::CommandData_t& /*args*/)
105 // Register callbacks if operating in that mode.
106 if (m_raw_data_callback_conf != nullptr) {
107 // Configure and register consume callback
109 std::bind(&DataHandlingModel<RDT, RHT, LBT, RPT, IDT>::consume_callback, this, std::placeholders::_1);
110
111 // Register callback
112 auto dmcbr = DataMoveCallbackRegistry::get();
113 dmcbr->register_callback<IDT>(m_raw_data_callback_conf, m_consume_callback);
114 }
115
116 // Configure threads:
117 m_consumer_thread.set_name("consumer", m_sourceid.id);
119 m_timesync_thread.set_name("timesync", m_sourceid.id);
120 }
122 m_postprocess_scheduler_thread.set_name("pprocsched", m_sourceid.id);
123 m_timekeeper = std::make_unique<folly::ThreadWheelTimekeeper>();
124 }
125}
126
127template<class RDT, class RHT, class LBT, class RPT, class IDT>
128void
129DataHandlingModel<RDT, RHT, LBT, RPT, IDT>::start(const appfwk::DAQModule::CommandData_t& args)
130{
131 // Reset opmon variables
132 m_sum_payloads = 0;
133 m_num_payloads = 0;
134 m_sum_requests = 0;
135 m_num_requests = 0;
140
141 m_t0 = std::chrono::high_resolution_clock::now();
142
144
145 TLOG_DEBUG(TLVL_WORK_STEPS) << "Starting threads...";
146 m_raw_processor_impl->start(args);
147 m_request_handler_impl->start(args);
148 if (m_raw_data_callback_conf == nullptr) {
150 }
153 }
156 this);
157 }
158 // Register callback to receive and dispatch data requests
159 m_data_request_receiver->add_callback(
160 std::bind(&DataHandlingModel<RDT, RHT, LBT, RPT, IDT>::dispatch_requests, this, std::placeholders::_1));
161}
162
163template<class RDT, class RHT, class LBT, class RPT, class IDT>
164void
165DataHandlingModel<RDT, RHT, LBT, RPT, IDT>::stop(const appfwk::DAQModule::CommandData_t& args)
166{
167 TLOG_DEBUG(TLVL_WORK_STEPS) << "Stoppping threads...";
168
169 // Stop receiving data requests as first thing
170 m_data_request_receiver->remove_callback();
171 // Stop the other threads
172 m_request_handler_impl->stop(args);
174 while (!m_timesync_thread.get_readiness()) {
175 std::this_thread::sleep_for(std::chrono::milliseconds(10));
176 }
177 }
178 if (m_raw_data_callback_conf == nullptr) {
179 while (!m_consumer_thread.get_readiness()) {
180 std::this_thread::sleep_for(std::chrono::milliseconds(10));
181 }
182 }
184 m_baton.post(); // In case the coroutine is still waiting when the consumer has stopped
185 while (!m_postprocess_scheduler_thread.get_readiness()) {
186 std::this_thread::sleep_for(std::chrono::milliseconds(10));
187 }
188 }
189 TLOG_DEBUG(TLVL_WORK_STEPS) << "Flushing latency buffer with occupancy: " << m_latency_buffer_impl->occupancy();
190 m_latency_buffer_impl->flush();
191 m_raw_processor_impl->stop(args);
192 m_raw_processor_impl->reset_last_daq_time();
193}
194
195template<class RDT, class RHT, class LBT, class RPT, class IDT>
196void
198{
201 ri.set_num_payloads(m_num_payloads.exchange(0));
202
204
205 auto now = std::chrono::high_resolution_clock::now();
206 int new_packets = m_stats_packet_count.exchange(0);
207 double seconds = std::chrono::duration_cast<std::chrono::microseconds>(now - m_t0).count() / 1000000.;
208 m_t0 = now;
209
210 // 08-May-2025, KAB: added a message to warn users when latency buffer inserts are failing.
211 int local_num_lb_insert_failures = m_num_lb_insert_failures.exchange(0);
212 if (local_num_lb_insert_failures != 0) {
214 NonZeroLatencyBufferInsertFailures(ERS_HERE, m_sourceid, local_num_lb_insert_failures, ri.num_payloads()));
215 }
216
217 ri.set_rate_payloads_consumed(new_packets / seconds / 1000.);
218 ri.set_num_lb_insert_failures(local_num_lb_insert_failures);
220 ri.set_num_requests(m_num_requests.exchange(0));
222 ri.set_last_daq_timestamp(m_raw_processor_impl->get_last_daq_time());
223 ri.set_newest_timestamp(m_raw_processor_impl->get_last_daq_time());
224 ri.set_oldest_timestamp(m_request_handler_impl->get_oldest_time());
225
226 this->publish(std::move(ri));
227}
228
229template<class RDT, class RHT, class LBT, class RPT, class IDT>
230void
232{
233 if constexpr (std::is_same_v<IDT, RDT>) {
234 process_item(std::move(payload));
235 } else {
236 auto transformed = transform_payload(payload);
237 for (auto& i : transformed) {
238 process_item(std::move(i));
239 }
240 }
241}
242
243template<class RDT, class RHT, class LBT, class RPT, class IDT>
244void
249
250template<class RDT, class RHT, class LBT, class RPT, class IDT>
251void
253{
254 m_raw_processor_impl->preprocess_item(&payload);
256 int64_t diff1 = payload.get_timestamp() - m_request_handler_impl->get_cutoff_timestamp();
257 if (diff1 <= 0) {
258 // m_request_handler_impl->increment_tardy_tp_count();
259 ers::warning(DataPacketArrivedTooLate(ERS_HERE,
262 payload.get_timestamp(),
263 m_request_handler_impl->get_cutoff_timestamp(),
264 diff1,
265 (static_cast<double>(diff1) / 62500.0)));
266 }
267 }
268
269 const RDT* written = nullptr;
270 if constexpr (ExpectsOrder<LBT>) {
271 if (!m_latency_buffer_impl->write(std::move(payload))) {
272 // TLOG_DEBUG(TLVL_TAKE_NOTE) << "***ERROR: Latency buffer insert failed! (Payload timestamp=" << payload.get_timestamp() << ")";
274 return;
275 }
276 written = m_latency_buffer_impl->back();
277 } else {
278 const auto [returned, result] = m_latency_buffer_impl->write_and_return(std::move(payload));
279 if (!result) {
280 // TLOG_DEBUG(TLVL_TAKE_NOTE) << "***ERROR: Latency buffer insert failed! (Payload timestamp=" << payload.get_timestamp() << ")";
282 return;
283 }
284 written = returned;
286
287 if (m_processing_delay_ticks == 0) {
288 m_raw_processor_impl->postprocess_item(written);
292 } else {
293 m_baton.post();
295}
296
297template<class RDT, class RHT, class LBT, class RPT, class IDT>
298void
301 folly::coro::blockingWait(postprocess_schedule());
302}
304template<class RDT, class RHT, class LBT, class RPT, class IDT>
305void
307{
308
309 TLOG_DEBUG(TLVL_WORK_STEPS) << "Consumer thread started...";
311 m_num_payloads = 0;
312 m_sum_payloads = 0;
315
316 while (m_run_marker.load()) {
317 // Try to acquire data
319 auto opt_payload = m_raw_data_receiver->try_receive(m_raw_receiver_timeout_ms);
320
321 if (opt_payload) {
322 IDT& payload = opt_payload.value();
323 transform_and_process(std::move(payload));
324 } else {
325 ++m_rawq_timeout_count;
326 // Protection against a zero sleep becoming a yield
327 if (m_raw_receiver_sleep_us != std::chrono::microseconds::zero())
328 std::this_thread::sleep_for(m_raw_receiver_sleep_us);
329 }
330 }
331 TLOG_DEBUG(TLVL_WORK_STEPS) << "Consumer thread joins... ";
332}
333
334template<class RDT, class RHT, class LBT, class RPT, class IDT>
335folly::coro::Task<void>
337{
338
339 TLOG_DEBUG(TLVL_WORK_STEPS) << "Postprocess schedule coroutine started...";
340 TLOG() << "***** Starting post-process coroutine with timout " << m_post_processing_delay_max_wait << " *****";
341
347
348 const auto wait_data = [this]() -> folly::coro::Task<void> {
349 // folly::coro::timeout cancels the task on timeout.
350 // Baton is not cancellable, so we attach a callback to resume the coroutine.
351 auto token = co_await folly::coro::co_current_cancellation_token;
352 folly::CancellationCallback cb(token, [this] { m_baton.post(); });
353 co_await m_baton; // Wait data
354 };
355
356 while (m_run_marker.load()) {
357 bool timeout = false;
358
360 try {
361 co_await folly::coro::timeout(
362 wait_data(), std::chrono::milliseconds{ m_post_processing_delay_max_wait }, m_timekeeper.get());
363
364 } catch (const folly::FutureTimeout&) {
365 timeout = true;
367 }
368 } else {
369 co_await m_baton;
370 }
371
372 m_baton.reset();
373
374 if (auto processed = sched_algo.run(timeout); processed > 0) {
375 m_num_payloads += processed;
376 m_sum_payloads += processed;
377 m_stats_packet_count += processed;
378 }
379 }
380}
381
382template<class RDT, class RHT, class LBT, class RPT, class IDT>
383void
385{
386 TLOG_DEBUG(TLVL_WORK_STEPS) << "TimeSync thread started...";
387 m_num_requests = 0;
388 m_sum_requests = 0;
389 uint64_t msg_seqno = 0;
390 timestamp_t prev_timestamp = 0;
391 auto once_per_run = true;
392 size_t zero_timestamp_count = 0;
393 size_t duplicate_timestamp_count = 0;
394 size_t total_timestamp_count = 0;
395 while (m_run_marker.load()) {
396 try {
397 auto timesyncmsg = dfmessages::TimeSync(m_raw_processor_impl->get_last_daq_time());
398 ++total_timestamp_count;
399 // daq_time is zero for the first received timesync, and may
400 // be the same as the previous daq_time if the data has
401 // stopped flowing. In both cases we don't send the TimeSync
402 if (timesyncmsg.daq_time != 0 && timesyncmsg.daq_time != prev_timestamp) {
403 prev_timestamp = timesyncmsg.daq_time;
404 timesyncmsg.run_number = m_run_number;
405 timesyncmsg.sequence_number = ++msg_seqno;
406 timesyncmsg.source_id = m_sourceid.id;
407 TLOG_DEBUG(TLVL_TIME_SYNCS) << "New timesync: daq=" << timesyncmsg.daq_time
408 << " wall=" << timesyncmsg.system_time << " run=" << timesyncmsg.run_number
409 << " seqno=" << timesyncmsg.sequence_number
410 << " source_id=" << timesyncmsg.source_id;
411 try {
412 dfmessages::TimeSync timesyncmsg_copy(timesyncmsg);
413 m_timesync_sender->send(std::move(timesyncmsg_copy), std::chrono::milliseconds(500));
414 } catch (ers::Issue& excpt) {
416 }
417
418 if (m_fake_trigger) {
422 dr.trigger_timestamp = timesyncmsg.daq_time > 500 * us ? timesyncmsg.daq_time - 500 * us : 0;
423 auto width = 300000;
424 uint offset = 100;
428 dr.data_destination = "data_fragments_q";
429 TLOG_DEBUG(TLVL_WORK_STEPS) << "Issuing fake trigger based on timesync. "
430 << " ts=" << dr.trigger_timestamp
431 << " window_begin=" << dr.request_information.window_begin
432 << " window_end=" << dr.request_information.window_end;
433 m_request_handler_impl->issue_request(dr);
434
437 }
438 } else {
439 if (timesyncmsg.daq_time == 0) {
440 ++zero_timestamp_count;
441 }
442 if (timesyncmsg.daq_time == prev_timestamp) {
443 ++duplicate_timestamp_count;
444 }
445 if (once_per_run) {
446 TLOG() << "Timesync with DAQ time 0 won't be sent out as it's an invalid sync.";
447 once_per_run = false;
448 }
449 }
450 } catch (const iomanager::TimeoutExpired& excpt) {
451 // ++m_timesyncqueue_timeout;
452 }
453 // Split up the 100ms sleep into 10 sleeps of 10ms, so we respond to "stop" quicker
454 for (size_t i = 0; i < 10; ++i) {
455 std::this_thread::sleep_for(std::chrono::milliseconds(10));
456 if (!m_run_marker.load()) {
457 break;
458 }
459 }
460 }
461 once_per_run = true;
462 TLOG_DEBUG(TLVL_WORK_STEPS) << "TimeSync thread joins... (timestamp count, zero/same/total = "
463 << zero_timestamp_count << "/" << duplicate_timestamp_count << "/"
464 << total_timestamp_count << ")";
465}
466
467template<class RDT, class RHT, class LBT, class RPT, class IDT>
468void
470{
471 if (data_request.request_information.component != m_sourceid) {
472 ers::error(RequestSourceIDMismatch(ERS_HERE, m_sourceid, data_request.request_information.component));
473 return;
474 }
475 TLOG_DEBUG(TLVL_QUEUE_POP) << "Received DataRequest"
476 << " for trig/seq_number " << data_request.trigger_number << "."
477 << data_request.sequence_number << ", runno " << data_request.run_number
478 << ", trig timestamp " << data_request.trigger_timestamp
479 << ", SourceID: " << data_request.request_information.component << ", window begin/end "
480 << data_request.request_information.window_begin << "/"
481 << data_request.request_information.window_end
482 << ", dest: " << data_request.data_destination;
483 m_request_handler_impl->issue_request(data_request);
486}
487
488} // namespace datahandlinglibs
489} // 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.
std::atomic< num_lb_insert_failures_t > m_num_lb_insert_failures
std::unique_ptr< FrameErrorRegistry > m_error_registry
std::shared_ptr< RequestHandlerType > m_request_handler_impl
void init(const appmodel::DataHandlerModule *modconf)
Forward calls from the appfwk.
std::shared_ptr< timesync_sender_ct > m_timesync_sender
void stop(const appfwk::DAQModule::CommandData_t &args)
virtual std::vector< RDT > transform_payload(IDT &original) const
void start(const appfwk::DAQModule::CommandData_t &args)
std::chrono::time_point< std::chrono::high_resolution_clock > m_t0
const appmodel::DataMoveCallbackConf * m_raw_data_callback_conf
std::shared_ptr< raw_receiver_ct > m_raw_data_receiver
void conf(const appfwk::DAQModule::CommandData_t &args)
void dispatch_requests(dfmessages::DataRequest &data_request)
void run_consume()
Function that will be run in its own thread to read the raw packets from the connection and add them ...
void run_timesync()
Function that will be run in its own thread and sends periodic timesync messages by pushing them to t...
std::atomic< num_post_processing_delay_max_waits_t > m_num_post_processing_delay_max_waits
std::atomic< rawq_timeout_count_t > m_rawq_timeout_count
std::shared_ptr< LatencyBufferType > m_latency_buffer_impl
std::shared_ptr< request_receiver_ct > m_data_request_receiver
std::shared_ptr< RawDataProcessorType > m_raw_processor_impl
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
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)
SourceID[" << sourceid << "] Command daqdataformats::SourceID Readout Initialization std::string initerror Configuration std::string conferror Configuration std::string conferror TimeSyncTransmissionFailed
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