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") {
20 m_data_request_receiver = get_iom_receiver<dfmessages::DataRequest>(input->UID());
21 }
22 }
23 for (auto output : mcfg->get_outputs()) {
24 if (output->get_data_type() == "TimeSync") {
26 m_timesync_sender = get_iom_sender<dfmessages::TimeSync>(output->UID());
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=" <<
244 // payload.get_timestamp() << ")";
246 return;
247 }
248 written = m_latency_buffer_impl->back();
249 } else {
250 const auto [returned, result] = m_latency_buffer_impl->write_and_return(std::move(payload));
251 if (!result) {
252 // TLOG_DEBUG(TLVL_TAKE_NOTE) << "***ERROR: Latency buffer insert failed! (Payload timestamp=" <<
253 // payload.get_timestamp() << ")";
255 return;
256 }
257 written = returned;
258 }
259
260 if (m_processing_delay_ticks == 0) {
261 m_raw_processor_impl->postprocess_item(written);
265 } else {
266 m_baton.post();
267 }
268}
269
270template<class RDT, class RHT, class LBT, class RPT, class IDT>
271void
276
277template<class RDT, class RHT, class LBT, class RPT, class IDT>
278folly::coro::Task<void>
280{
281
282 // TLOG_DEBUG(TLVL_WORK_STEPS) << "Postprocess schedule coroutine started...";
283 timestamp_t newest_ts = 0;
284 timestamp_t end_win_ts = 0;
285 bool first_cycle = true;
286 auto last_post_proc_time = std::chrono::system_clock::now();
287 auto now = last_post_proc_time;
288 std::chrono::milliseconds milliseconds;
289 RDT processed_element;
290
291 // Deferral of the post processing, to allow elements being reordered in the LB
292 // Basically, find data older than a certain timestamp and process all data since the last post-processed element up
293 // to that value
294 while (m_run_marker.load()) {
295 try {
296 co_await folly::coro::timeout(
297 m_baton.operator co_await(), std::chrono::milliseconds{ m_post_processing_delay_max_wait }, m_timekeeper.get());
298 m_baton.reset();
299 } catch (const folly::FutureTimeout&) {
301 }
302
303 if (m_latency_buffer_impl->occupancy() == 0) {
304 continue;
305 }
306
307 now = std::chrono::system_clock::now();
308 milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_post_proc_time);
309
310 if (static_cast<uint64_t>(milliseconds.count()) <= m_post_processing_delay_min_wait) {
311 continue;
312 }
313
314 last_post_proc_time = now;
315
316 // Get the LB boundaries
317 auto tail = m_latency_buffer_impl->back();
318 newest_ts = tail->get_timestamp();
319
320 if (first_cycle) {
321 auto head = m_latency_buffer_impl->front();
322 processed_element.set_timestamp(head->get_timestamp());
323 first_cycle = false;
324 // TLOG() << "***** First pass post processing *****";
325 }
326
327 if (newest_ts - processed_element.get_timestamp() > m_processing_delay_ticks) {
328 end_win_ts = newest_ts - m_processing_delay_ticks;
329 auto start_iter = m_latency_buffer_impl->lower_bound(processed_element, false);
330 processed_element.set_timestamp(end_win_ts);
331 auto end_iter = m_latency_buffer_impl->lower_bound(processed_element, false);
332
333 for (auto it = start_iter; it != end_iter; ++it) {
334 m_raw_processor_impl->postprocess_item(&(*it));
338 }
339 }
340 }
341}
342
343template<class RDT, class RHT, class LBT, class RPT, class IDT>
344void
346{
347 TLOG_DEBUG(TLVL_WORK_STEPS) << "TimeSync thread started...";
348 m_num_requests = 0;
349 m_sum_requests = 0;
350 uint64_t msg_seqno = 0;
351 timestamp_t prev_timestamp = 0;
352 auto once_per_run = true;
353 size_t zero_timestamp_count = 0;
354 size_t duplicate_timestamp_count = 0;
355 size_t total_timestamp_count = 0;
356 while (m_run_marker.load()) {
357 try {
358 auto timesyncmsg = dfmessages::TimeSync(m_raw_processor_impl->get_last_daq_time());
359 ++total_timestamp_count;
360 // daq_time is zero for the first received timesync, and may
361 // be the same as the previous daq_time if the data has
362 // stopped flowing. In both cases we don't send the TimeSync
363 if (timesyncmsg.daq_time != 0 && timesyncmsg.daq_time != prev_timestamp) {
364 prev_timestamp = timesyncmsg.daq_time;
365 timesyncmsg.run_number = m_run_number;
366 timesyncmsg.sequence_number = ++msg_seqno;
367 timesyncmsg.source_id = m_sourceid.id;
368 TLOG_DEBUG(TLVL_TIME_SYNCS) << "New timesync: daq=" << timesyncmsg.daq_time
369 << " wall=" << timesyncmsg.system_time << " run=" << timesyncmsg.run_number
370 << " seqno=" << timesyncmsg.sequence_number
371 << " source_id=" << timesyncmsg.source_id;
372 try {
373 dfmessages::TimeSync timesyncmsg_copy(timesyncmsg);
374 m_timesync_sender->send(std::move(timesyncmsg_copy), std::chrono::milliseconds(500));
375 } catch (ers::Issue& excpt) {
377 datahandlinglibs::TimeSyncTransmissionFailed(ERS_HERE, m_sourceid, m_timesync_connection_name, excpt));
378 }
379
380 if (m_fake_trigger) {
384 dr.trigger_timestamp = timesyncmsg.daq_time > 500 * us ? timesyncmsg.daq_time - 500 * us : 0;
385 auto width = 300000;
386 uint offset = 100;
390 dr.data_destination = "data_fragments_q";
391 TLOG_DEBUG(TLVL_WORK_STEPS) << "Issuing fake trigger based on timesync. " << " ts=" << dr.trigger_timestamp
392 << " window_begin=" << dr.request_information.window_begin
393 << " window_end=" << dr.request_information.window_end;
394 m_request_handler_impl->issue_request(dr);
395
398 }
399 } else {
400 if (timesyncmsg.daq_time == 0) {
401 ++zero_timestamp_count;
402 }
403 if (timesyncmsg.daq_time == prev_timestamp) {
404 ++duplicate_timestamp_count;
405 }
406 if (once_per_run) {
407 TLOG() << "Timesync with DAQ time 0 won't be sent out as it's an invalid sync.";
408 once_per_run = false;
409 }
410 }
411 } catch (const iomanager::TimeoutExpired& excpt) {
412 // ++m_timesyncqueue_timeout;
413 }
414 // Split up the 100ms sleep into 10 sleeps of 10ms, so we respond to "stop" quicker
415 for (size_t i = 0; i < 10; ++i) {
416 std::this_thread::sleep_for(std::chrono::milliseconds(10));
417 if (!m_run_marker.load()) {
418 break;
419 }
420 }
421 }
422 once_per_run = true;
423 TLOG_DEBUG(TLVL_WORK_STEPS) << "TimeSync thread joins... (timestamp count, zero/same/total = "
424 << zero_timestamp_count << "/" << duplicate_timestamp_count << "/"
425 << total_timestamp_count << ")";
426}
427
428template<class RDT, class RHT, class LBT, class RPT, class IDT>
429void
431{
432 if (data_request.request_information.component != m_sourceid) {
434 datahandlinglibs::RequestSourceIDMismatch(ERS_HERE, m_sourceid, data_request.request_information.component));
435 return;
436 }
437 TLOG_DEBUG(TLVL_QUEUE_POP) << "Received DataRequest" << " for trig/seq_number " << data_request.trigger_number << "."
438 << data_request.sequence_number << ", runno " << data_request.run_number
439 << ", trig timestamp " << data_request.trigger_timestamp
440 << ", SourceID: " << data_request.request_information.component << ", window begin/end "
441 << data_request.request_information.window_begin << "/"
442 << data_request.request_information.window_end
443 << ", dest: " << data_request.data_destination;
444 m_request_handler_impl->issue_request(data_request);
447}
448
449} // namespace snbmodules
450} // namespace dunedaq
@ TLVL_QUEUE_POP
@ TLVL_TIME_SYNCS
#define ERS_HERE
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:76
double offset
#define TLOG_DEBUG(lvl,...)
Definition Logging.hpp:116
#define TLOG(...)
Definition macro.hpp:21
The DUNE-DAQ namespace.
void warning(const Issue &issue)
Definition ers.hpp:150
void error(const Issue &issue)
Definition ers.hpp:101
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