artdaq  3.13.00
FragmentBuffer.cc
1 #include "artdaq/DAQdata/Globals.hh"
2 #define TRACE_NAME (app_name + "_FragmentBuffer").c_str() // include these 2 first -
3 
4 #include "artdaq/DAQrate/FragmentBuffer.hh"
5 
6 #include <boost/exception/all.hpp>
7 #include <boost/throw_exception.hpp>
8 
9 #include <iterator>
10 #include <limits>
11 
12 #include "canvas/Utilities/Exception.h"
13 #include "cetlib_except/exception.h"
14 #include "fhiclcpp/ParameterSet.h"
15 
16 #include "artdaq-core/Data/ContainerFragmentLoader.hh"
17 #include "artdaq-core/Data/Fragment.hh"
18 #include "artdaq-core/Utilities/ExceptionHandler.hh"
19 #include "artdaq-core/Utilities/SimpleLookupPolicy.hh"
20 #include "artdaq-core/Utilities/TimeUtils.hh"
21 
22 #include <sys/poll.h>
23 #include <algorithm>
24 #include <fstream>
25 #include <iomanip>
26 #include <iostream>
27 #include <iterator>
29 
30 #define TLVL_ADDFRAGMENT 32
31 #define TLVL_CHECKSTOP 33
32 #define TLVL_WAITFORBUFFERREADY 34
33 #define TLVL_GETBUFFERSTATS 35
34 #define TLVL_CHECKDATABUFFER 36
35 #define TLVL_APPLYREQUESTS 37
36 #define TLVL_APPLYREQUESTS_VERBOSE 38
37 #define TLVL_SENDEMPTYFRAGMENTS 39
38 #define TLVL_CHECKWINDOWS 40
39 #define TLVL_EMPTYFRAGMENT 41
40 
41 artdaq::FragmentBuffer::FragmentBuffer(const fhicl::ParameterSet& ps)
42  : next_sequence_id_(1)
43  , requestBuffer_()
44  , bufferModeKeepLatest_(ps.get<bool>("buffer_mode_keep_latest", false))
45  , windowOffset_(ps.get<Fragment::timestamp_t>("request_window_offset", 0))
46  , windowWidth_(ps.get<Fragment::timestamp_t>("request_window_width", 0))
47  , staleTimeout_(ps.get<Fragment::timestamp_t>("stale_fragment_timeout", 0))
48  , expectedType_(ps.get<Fragment::type_t>("expected_fragment_type", Fragment::type_t(Fragment::EmptyFragmentType)))
49  , uniqueWindows_(ps.get<bool>("request_windows_are_unique", true))
50  , sendMissingFragments_(ps.get<bool>("send_missing_request_fragments", true))
51  , missing_request_window_timeout_us_(ps.get<size_t>("missing_request_window_timeout_us", 5000000))
52  , window_close_timeout_us_(ps.get<size_t>("window_close_timeout_us", 2000000))
53  , error_on_empty_(ps.get<bool>("error_on_empty_fragment", false))
54  , circularDataBufferMode_(ps.get<bool>("circular_buffer_mode", false))
55  , maxDataBufferDepthFragments_(ps.get<int>("data_buffer_depth_fragments", 1000))
56  , maxDataBufferDepthBytes_(ps.get<size_t>("data_buffer_depth_mb", 1000) * 1024 * 1024)
57  , systemFragmentCount_(0)
58  , should_stop_(false)
59 {
60  auto fragment_ids = ps.get<std::vector<artdaq::Fragment::fragment_id_t>>("fragment_ids", std::vector<artdaq::Fragment::fragment_id_t>());
61 
62  TLOG(TLVL_DEBUG + 33) << "artdaq::FragmentBuffer::FragmentBuffer(ps)";
63  int fragment_id = ps.get<int>("fragment_id", -99);
64 
65  if (fragment_id != -99)
66  {
67  if (fragment_ids.size() != 0)
68  {
69  auto report = "Error in FragmentBuffer: can't both define \"fragment_id\" and \"fragment_ids\" in FHiCL document";
70  TLOG(TLVL_ERROR) << report;
71  throw cet::exception("FragmentBufferConfig") << report;
72  }
73  else
74  {
75  fragment_ids.emplace_back(fragment_id);
76  }
77  }
78 
79  for (auto& id : fragment_ids)
80  {
81  dataBuffers_[id] = std::make_shared<DataBuffer>();
82  dataBuffers_[id]->DataBufferDepthBytes = 0;
83  dataBuffers_[id]->DataBufferDepthFragments = 0;
84  dataBuffers_[id]->HighestRequestSeen = 0;
85  dataBuffers_[id]->BufferFragmentKept = false;
86  }
87 
88  std::string modeString = ps.get<std::string>("request_mode", "ignored");
89  if (modeString == "single" || modeString == "Single")
90  {
91  mode_ = RequestMode::Single;
92  }
93  else if (modeString.find("buffer") != std::string::npos || modeString.find("Buffer") != std::string::npos)
94  {
95  mode_ = RequestMode::Buffer;
96  }
97  else if (modeString == "window" || modeString == "Window")
98  {
99  mode_ = RequestMode::Window;
100  }
101  else if (modeString.find("ignore") != std::string::npos || modeString.find("Ignore") != std::string::npos)
102  {
103  mode_ = RequestMode::Ignored;
104  }
105  else if (modeString.find("sequence") != std::string::npos || modeString.find("Sequence") != std::string::npos)
106  {
107  mode_ = RequestMode::SequenceID;
108  }
109  if (mode_ != RequestMode::Ignored && !ps.get<bool>("receive_requests", false))
110  {
111  TLOG(TLVL_WARNING) << "Request Mode was requested as " << modeString << ", but is being set to Ignored because \"receive_requests\" was not set to true";
112  mode_ = RequestMode::Ignored;
113  }
114  TLOG(TLVL_DEBUG + 32) << "Request mode is " << printMode_();
115 }
116 
118 {
119  TLOG(TLVL_INFO) << "Fragment Buffer Destructor; Clearing data buffers";
120  Reset(true);
121 }
122 
124 {
125  should_stop_ = stop;
126  next_sequence_id_ = 1;
127  for (auto& id : dataBuffers_)
128  {
129  std::lock_guard<std::mutex> dlk(id.second->DataBufferMutex);
130  id.second->DataBufferDepthBytes = 0;
131  id.second->DataBufferDepthFragments = 0;
132  id.second->BufferFragmentKept = false;
133  id.second->DataBuffer.clear();
134  }
135 
136  {
137  std::lock_guard<std::mutex> lk(systemFragmentMutex_);
138  systemFragments_.clear();
139  systemFragmentCount_ = 0;
140  }
141 }
142 
144 {
145  std::unordered_map<Fragment::fragment_id_t, FragmentPtrs> frags_by_id;
146  while (!frags.empty())
147  {
148  auto dataIter = frags.begin();
149  auto frag_id = (*dataIter)->fragmentID();
150 
151  if ((*dataIter)->type() == Fragment::EndOfRunFragmentType || (*dataIter)->type() == Fragment::EndOfSubrunFragmentType || (*dataIter)->type() == Fragment::InitFragmentType)
152  {
153  std::lock_guard<std::mutex> lk(systemFragmentMutex_);
154  systemFragments_.emplace_back(std::move(*dataIter));
155  systemFragmentCount_++;
156  frags.erase(dataIter);
157  continue;
158  }
159 
160  if (!dataBuffers_.count(frag_id))
161  {
162  throw cet::exception("FragmentIDs") << "Received Fragment with Fragment ID " << frag_id << ", which is not in the declared Fragment IDs list!";
163  }
164 
165  frags_by_id[frag_id].emplace_back(std::move(*dataIter));
166  frags.erase(dataIter);
167  }
168 
169  auto type_it = frags_by_id.begin();
170  while (type_it != frags_by_id.end())
171  {
172  auto frag_id = type_it->first;
173 
174  waitForDataBufferReady(frag_id);
175  auto dataBuffer = dataBuffers_[frag_id];
176  std::lock_guard<std::mutex> dlk(dataBuffer->DataBufferMutex);
177  switch (mode_)
178  {
179  case RequestMode::Single: {
180  auto dataIter = type_it->second.rbegin();
181  TLOG(TLVL_ADDFRAGMENT) << "Adding Fragment with Fragment ID " << frag_id << ", Sequence ID " << (*dataIter)->sequenceID() << ", and Timestamp " << (*dataIter)->timestamp() << " to buffer";
182  dataBuffer->DataBuffer.clear();
183  dataBuffer->DataBufferDepthBytes = (*dataIter)->sizeBytes();
184  dataBuffer->DataBuffer.emplace_back(std::move(*dataIter));
185  dataBuffer->DataBufferDepthFragments = 1;
186  type_it->second.clear();
187  }
188  break;
189  case RequestMode::Buffer:
190  case RequestMode::Ignored:
191  case RequestMode::Window:
192  case RequestMode::SequenceID:
193  default:
194  while (!type_it->second.empty())
195  {
196  auto dataIter = type_it->second.begin();
197  TLOG(TLVL_ADDFRAGMENT) << "Adding Fragment with Fragment ID " << frag_id << ", Sequence ID " << (*dataIter)->sequenceID() << ", and Timestamp " << (*dataIter)->timestamp() << " to buffer";
198 
199  dataBuffer->DataBufferDepthBytes += (*dataIter)->sizeBytes();
200  dataBuffer->DataBuffer.emplace_back(std::move(*dataIter));
201  type_it->second.erase(dataIter);
202  }
203  dataBuffer->DataBufferDepthFragments = dataBuffer->DataBuffer.size();
204  break;
205  }
206  getDataBufferStats(frag_id);
207  ++type_it;
208  }
209  dataCondition_.notify_all();
210 }
211 
213 {
214  TLOG(TLVL_CHECKSTOP) << "CFG::check_stop: should_stop=" << should_stop_.load();
215 
216  if (!should_stop_.load()) return false;
217  if (mode_ == RequestMode::Ignored)
218  {
219  return true;
220  }
221 
222  if (requestBuffer_ != nullptr)
223  {
224  // check_stop returns true if the CFG should stop. We should wait for the Request Buffer to report Request Receiver stopped before stopping.
225  TLOG(TLVL_DEBUG + 32) << "should_stop is true, requestBuffer_->isRunning() is " << std::boolalpha << requestBuffer_->isRunning();
226  if (!requestBuffer_->isRunning())
227  {
228  return true;
229  }
230  }
231  return false;
232 }
233 
235 {
236  switch (mode_)
237  {
238  case RequestMode::Single:
239  return "Single";
240  case RequestMode::Buffer:
241  return "Buffer";
242  case RequestMode::Window:
243  return "Window";
244  case RequestMode::Ignored:
245  return "Ignored";
246  case RequestMode::SequenceID:
247  return "SequenceID";
248  }
249 
250  return "ERROR";
251 }
252 
254 {
255  size_t count = 0;
256  for (auto& id : dataBuffers_) count += id.second->DataBufferDepthFragments;
257  count += systemFragmentCount_.load();
258  return count;
259 }
260 
261 bool artdaq::FragmentBuffer::waitForDataBufferReady(Fragment::fragment_id_t id)
262 {
263  if (!dataBuffers_.count(id))
264  {
265  TLOG(TLVL_ERROR) << "DataBufferError: "
266  << "Error in FragmentBuffer: Cannot wait for data buffer for ID " << id << " because it does not exist!";
267  throw cet::exception("DataBufferError") << "Error in FragmentBuffer: Cannot wait for data buffer for ID " << id << " because it does not exist!";
268  }
269  auto startwait = std::chrono::steady_clock::now();
270  auto first = true;
271  auto lastwaittime = 0ULL;
272  auto dataBuffer = dataBuffers_[id];
273 
274  while (dataBufferIsTooLarge(id))
275  {
276  if (!circularDataBufferMode_)
277  {
278  if (should_stop_.load())
279  {
280  TLOG(TLVL_DEBUG + 32) << "Run ended while waiting for buffer to shrink!";
281  getDataBufferStats(id);
282  dataCondition_.notify_all();
283  return false;
284  }
285  auto waittime = TimeUtils::GetElapsedTimeMilliseconds(startwait);
286 
287  if (first || (waittime != lastwaittime && waittime % 1000 == 0))
288  {
289  std::lock_guard<std::mutex> lk(dataBuffer->DataBufferMutex);
290  if (dataBufferIsTooLarge(id))
291  {
292  TLOG(TLVL_WARNING) << "Bad Omen: Data Buffer has exceeded its size limits. "
293  << "(seq_id=" << next_sequence_id_ << ", frag_id=" << id
294  << ", frags=" << dataBuffer->DataBufferDepthFragments << "/" << maxDataBufferDepthFragments_
295  << ", szB=" << dataBuffer->DataBufferDepthBytes << "/" << maxDataBufferDepthBytes_ << ")"
296  << ", timestamps=" << dataBuffer->DataBuffer.front()->timestamp() << "-" << dataBuffer->DataBuffer.back()->timestamp();
297  TLOG(TLVL_DEBUG + 33) << "Bad Omen: Possible causes include requests not getting through or Ignored-mode BR issues";
298 
299  if (metricMan)
300  {
301  metricMan->sendMetric("Bad Omen wait time", waittime / 1000.0, "s", 1, MetricMode::LastPoint);
302  }
303  }
304  first = false;
305  }
306  if (waittime % 5 && waittime != lastwaittime)
307  {
308  TLOG(TLVL_WAITFORBUFFERREADY) << "getDataLoop: Data Retreival paused for " << waittime << " ms waiting for data buffer to drain";
309  }
310  lastwaittime = waittime;
311  usleep(1000);
312  }
313  else
314  {
315  std::lock_guard<std::mutex> lk(dataBuffer->DataBufferMutex);
316  if (dataBufferIsTooLarge(id))
317  {
318  auto begin = dataBuffer->DataBuffer.begin();
319  if (begin == dataBuffer->DataBuffer.end())
320  {
321  TLOG(TLVL_WARNING) << "Data buffer is reported as too large, but doesn't contain any Fragments! Possible corrupt memory!";
322  continue;
323  }
324  if (*begin)
325  {
326  TLOG(TLVL_WAITFORBUFFERREADY) << "waitForDataBufferReady: Dropping Fragment with timestamp " << (*begin)->timestamp() << " from data buffer (Buffer over-size, circular data buffer mode)";
327 
328  dataBuffer->DataBufferDepthBytes -= (*begin)->sizeBytes();
329  dataBuffer->DataBuffer.erase(begin);
330  dataBuffer->DataBufferDepthFragments = dataBuffer->DataBuffer.size();
331  dataBuffer->BufferFragmentKept = false; // If any Fragments are removed from data buffer, then we know we don't have to ignore the first one anymore
332  }
333  }
334  }
335  }
336  return true;
337 }
338 
339 bool artdaq::FragmentBuffer::dataBufferIsTooLarge(Fragment::fragment_id_t id)
340 {
341  if (!dataBuffers_.count(id))
342  {
343  TLOG(TLVL_ERROR) << "DataBufferError: "
344  << "Error in FragmentBuffer: Cannot check size of data buffer for ID " << id << " because it does not exist!";
345  throw cet::exception("DataBufferError") << "Error in FragmentBuffer: Cannot check size of data buffer for ID " << id << " because it does not exist!";
346  }
347  auto dataBuffer = dataBuffers_[id];
348  return (maxDataBufferDepthFragments_ > 0 && dataBuffer->DataBufferDepthFragments.load() > maxDataBufferDepthFragments_) ||
349  (maxDataBufferDepthBytes_ > 0 && dataBuffer->DataBufferDepthBytes.load() > maxDataBufferDepthBytes_);
350 }
351 
352 void artdaq::FragmentBuffer::getDataBufferStats(Fragment::fragment_id_t id)
353 {
354  if (!dataBuffers_.count(id))
355  {
356  TLOG(TLVL_ERROR) << "DataBufferError: "
357  << "Error in FragmentBuffer: Cannot get stats of data buffer for ID " << id << " because it does not exist!";
358  throw cet::exception("DataBufferError") << "Error in FragmentBuffer: Cannot get stats of data buffer for ID " << id << " because it does not exist!";
359  }
360  auto dataBuffer = dataBuffers_[id];
361 
362  if (metricMan)
363  {
364  TLOG(TLVL_GETBUFFERSTATS) << "getDataBufferStats: Sending Metrics";
365  metricMan->sendMetric("Buffer Depth Fragments", dataBuffer->DataBufferDepthFragments.load(), "fragments", 1, MetricMode::LastPoint);
366  metricMan->sendMetric("Buffer Depth Bytes", dataBuffer->DataBufferDepthBytes.load(), "bytes", 1, MetricMode::LastPoint);
367 
368  auto bufferDepthFragmentsPercent = dataBuffer->DataBufferDepthFragments.load() * 100 / static_cast<double>(maxDataBufferDepthFragments_);
369  auto bufferDepthBytesPercent = dataBuffer->DataBufferDepthBytes.load() * 100 / static_cast<double>(maxDataBufferDepthBytes_);
370  metricMan->sendMetric("Fragment Buffer Full %Fragments", bufferDepthFragmentsPercent, "%", 3, MetricMode::LastPoint);
371  metricMan->sendMetric("Fragment Buffer Full %Bytes", bufferDepthBytesPercent, "%", 3, MetricMode::LastPoint);
372  metricMan->sendMetric("Fragment Buffer Full %", bufferDepthFragmentsPercent > bufferDepthBytesPercent ? bufferDepthFragmentsPercent : bufferDepthBytesPercent, "%", 1, MetricMode::LastPoint);
373  }
374  TLOG(TLVL_GETBUFFERSTATS) << "getDataBufferStats: frags=" << dataBuffer->DataBufferDepthFragments.load() << "/" << maxDataBufferDepthFragments_
375  << ", sz=" << dataBuffer->DataBufferDepthBytes.load() << "/" << maxDataBufferDepthBytes_;
376 }
377 
378 //-----------------------------------------------------------------------------
379 // P.Murat: return stat reports as a string
380 //-----------------------------------------------------------------------------
382  std::ostringstream oss;
383 
384  for (auto& it : dataBuffers_) {
385  Fragment::fragment_id_t id = it.first;
386  if (!dataBuffers_.count(id)) {
387  TLOG(TLVL_ERROR) << "DataBufferError: "
388  << "Error in FragmentBuffer: Cannot get stats of data buffer for ID "
389  << id << " because it does not exist!";
390  throw cet::exception("DataBufferError") << "Error in FragmentBuffer: Cannot get stats of data buffer for ID "
391  << id << " because it does not exist!";
392  }
393 
394  auto dataBuffer = dataBuffers_[id];
395 
396  TLOG(TLVL_GETBUFFERSTATS) << "getDataBufferStats: Sending Metrics";
397 
398  int nf = dataBuffer->DataBufferDepthFragments.load();
399  int nb = dataBuffer->DataBufferDepthBytes.load();
400  oss << std::endl << "fragment_id:" << id << " nfragments:" << nf << " nbytes:" << nb
401  << " max_nf:" << maxDataBufferDepthFragments_ << " max_nb:" << maxDataBufferDepthBytes_;
402 
403  TLOG(TLVL_GETBUFFERSTATS) << "getDataBufferStats: frags=" << dataBuffer->DataBufferDepthFragments.load() << "/" << maxDataBufferDepthFragments_
404  << ", sz=" << dataBuffer->DataBufferDepthBytes.load() << "/" << maxDataBufferDepthBytes_;
405  }
406 
407  return oss.str();
408 }
409 
410 void artdaq::FragmentBuffer::checkDataBuffer(Fragment::fragment_id_t id)
411 {
412  if (!dataBuffers_.count(id))
413  {
414  TLOG(TLVL_ERROR) << "DataBufferError: "
415  << "Error in FragmentBuffer: Cannot check data buffer for ID " << id << " because it does not exist!";
416  throw cet::exception("DataBufferError") << "Error in FragmentBuffer: Cannot check data buffer for ID " << id << " because it does not exist!";
417  }
418 
419  if (dataBuffers_[id]->DataBufferDepthFragments > 0 && mode_ != RequestMode::Single && mode_ != RequestMode::Ignored)
420  {
421  auto dataBuffer = dataBuffers_[id];
422  std::lock_guard<std::mutex> lk(dataBuffer->DataBufferMutex);
423 
424  // Eliminate extra fragments
425  while (dataBufferIsTooLarge(id))
426  {
427  auto begin = dataBuffer->DataBuffer.begin();
428  TLOG(TLVL_CHECKDATABUFFER) << "checkDataBuffer: Dropping Fragment with timestamp " << (*begin)->timestamp() << " from data buffer (Buffer over-size)";
429  dataBuffer->DataBufferDepthBytes -= (*begin)->sizeBytes();
430  dataBuffer->DataBuffer.erase(begin);
431  dataBuffer->DataBufferDepthFragments = dataBuffer->DataBuffer.size();
432  dataBuffer->BufferFragmentKept = false; // If any Fragments are removed from data buffer, then we know we don't have to ignore the first one anymore
433  }
434 
435  TLOG(TLVL_CHECKDATABUFFER) << "DataBufferDepthFragments is " << dataBuffer->DataBufferDepthFragments << ", DataBuffer.size is " << dataBuffer->DataBuffer.size();
436  if (dataBuffer->DataBufferDepthFragments > 0 && staleTimeout_ > 0)
437  {
438  TLOG(TLVL_CHECKDATABUFFER) << "Determining if Fragments can be dropped from data buffer";
439  Fragment::timestamp_t last = dataBuffer->DataBuffer.back()->timestamp();
440  Fragment::timestamp_t min = last > staleTimeout_ ? last - staleTimeout_ : 0;
441  for (auto it = dataBuffer->DataBuffer.begin(); it != dataBuffer->DataBuffer.end();)
442  {
443  if ((*it)->timestamp() < min)
444  {
445  TLOG(TLVL_CHECKDATABUFFER) << "checkDataBuffer: Dropping Fragment with timestamp " << (*it)->timestamp() << " from data buffer (timeout=" << staleTimeout_ << ", min=" << min << ")";
446  dataBuffer->DataBufferDepthBytes -= (*it)->sizeBytes();
447  dataBuffer->BufferFragmentKept = false; // If any Fragments are removed from data buffer, then we know we don't have to ignore the first one anymore
448  it = dataBuffer->DataBuffer.erase(it);
449  dataBuffer->DataBufferDepthFragments = dataBuffer->DataBuffer.size();
450  }
451  else
452  {
453  break;
454  }
455  }
456  }
457  }
458 }
459 
460 void artdaq::FragmentBuffer::applyRequestsIgnoredMode(artdaq::FragmentPtrs& frags)
461 {
462  // dataBuffersMutex_ is held by calling function
463  // We just copy everything that's here into the output.
464  TLOG(TLVL_APPLYREQUESTS) << "Mode is Ignored; Copying data to output";
465  for (auto& id : dataBuffers_)
466  {
467  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
468  if (id.second && !id.second->DataBuffer.empty() && id.second->DataBuffer.back()->sequenceID() >= next_sequence_id_)
469  {
470  next_sequence_id_ = id.second->DataBuffer.back()->sequenceID() + 1;
471  }
472  std::move(id.second->DataBuffer.begin(), id.second->DataBuffer.end(), std::inserter(frags, frags.end()));
473  id.second->DataBufferDepthBytes = 0;
474  id.second->DataBufferDepthFragments = 0;
475  id.second->BufferFragmentKept = false;
476  id.second->DataBuffer.clear();
477  }
478 }
479 
480 void artdaq::FragmentBuffer::applyRequestsSingleMode(artdaq::FragmentPtrs& frags)
481 {
482  // We only care about the latest request received. Send empties for all others.
483  auto requests = requestBuffer_->GetRequests();
484  while (requests.size() > 1)
485  {
486  // std::map is ordered by key => Last sequence ID in the map is the one we care about
487  requestBuffer_->RemoveRequest(requests.begin()->first);
488  requests.erase(requests.begin());
489  }
490  sendEmptyFragments(frags, requests);
491 
492  // If no requests remain after sendEmptyFragments, return
493  if (requests.size() == 0 || !requests.count(next_sequence_id_)) return;
494 
495  for (auto& id : dataBuffers_)
496  {
497  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
498  if (id.second->DataBufferDepthFragments > 0)
499  {
500  assert(id.second->DataBufferDepthFragments == 1);
501  TLOG(TLVL_APPLYREQUESTS) << "Mode is Single; Sending copy of last event (SeqID " << next_sequence_id_ << ")";
502  for (auto& fragptr : id.second->DataBuffer)
503  {
504  // Return the latest data point
505  auto frag = fragptr.get();
506  auto newfrag = std::unique_ptr<artdaq::Fragment>(new Fragment(next_sequence_id_, frag->fragmentID()));
507  newfrag->resize(frag->size() - detail::RawFragmentHeader::num_words());
508  memcpy(newfrag->headerAddress(), frag->headerAddress(), frag->sizeBytes());
509  newfrag->setTimestamp(requests[next_sequence_id_]);
510  newfrag->setSequenceID(next_sequence_id_);
511  frags.push_back(std::move(newfrag));
512  }
513  }
514  else
515  {
516  sendEmptyFragment(frags, next_sequence_id_, id.first, "No data for");
517  }
518  }
519  requestBuffer_->RemoveRequest(next_sequence_id_);
520  ++next_sequence_id_;
521 }
522 
523 void artdaq::FragmentBuffer::applyRequestsBufferMode(artdaq::FragmentPtrs& frags)
524 {
525  // We only care about the latest request received. Send empties for all others.
526  auto requests = requestBuffer_->GetRequests();
527  while (requests.size() > 1)
528  {
529  // std::map is ordered by key => Last sequence ID in the map is the one we care about
530  requestBuffer_->RemoveRequest(requests.begin()->first);
531  requests.erase(requests.begin());
532  }
533  sendEmptyFragments(frags, requests);
534 
535  // If no requests remain after sendEmptyFragments, return
536  if (requests.size() == 0 || !requests.count(next_sequence_id_)) return;
537 
538  for (auto& id : dataBuffers_)
539  {
540  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsBufferMode: Creating ContainerFragment for Buffered Fragments (SeqID " << next_sequence_id_ << ")";
541  frags.emplace_back(new artdaq::Fragment(next_sequence_id_, id.first));
542  frags.back()->setTimestamp(requests[next_sequence_id_]);
543  ContainerFragmentLoader cfl(*frags.back());
544  cfl.set_missing_data(false); // Buffer mode is never missing data, even if there IS no data.
545 
546  // If we kept a Fragment from the previous iteration, but more data has arrived, discard it
547  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
548  if (id.second->BufferFragmentKept && id.second->DataBufferDepthFragments > 1)
549  {
550  id.second->DataBufferDepthBytes -= id.second->DataBuffer.front()->sizeBytes();
551  id.second->DataBuffer.erase(id.second->DataBuffer.begin());
552  id.second->DataBufferDepthFragments = id.second->DataBuffer.size();
553  }
554 
555  // Buffer mode TFGs should simply copy out the whole dataBuffer_ into a ContainerFragment
556  FragmentPtrs fragsToAdd;
557  std::move(id.second->DataBuffer.begin(), --id.second->DataBuffer.end(), std::back_inserter(fragsToAdd));
558  id.second->DataBuffer.erase(id.second->DataBuffer.begin(), --id.second->DataBuffer.end());
559 
560  if (fragsToAdd.size() > 0)
561  {
562  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsBufferMode: Adding " << fragsToAdd.size() << " Fragments to Container (SeqID " << next_sequence_id_ << ")";
563  cfl.addFragments(fragsToAdd);
564  }
565  else
566  {
567  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsBufferMode: No Fragments to add (SeqID " << next_sequence_id_ << ")";
568  }
569 
570  if (id.second->DataBuffer.size() == 1)
571  {
572  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsBufferMode: Adding Fragment with timestamp " << id.second->DataBuffer.front()->timestamp() << " to Container with sequence ID " << next_sequence_id_;
573  cfl.addFragment(id.second->DataBuffer.front());
574  if (bufferModeKeepLatest_)
575  {
576  id.second->BufferFragmentKept = true;
577  id.second->DataBufferDepthBytes = id.second->DataBuffer.front()->sizeBytes();
578  id.second->DataBufferDepthFragments = id.second->DataBuffer.size(); // 1
579  }
580  else
581  {
582  id.second->DataBuffer.clear();
583  id.second->BufferFragmentKept = false;
584  id.second->DataBufferDepthBytes = 0;
585  id.second->DataBufferDepthFragments = 0;
586  }
587  }
588  }
589  requestBuffer_->RemoveRequest(next_sequence_id_);
590  ++next_sequence_id_;
591 }
592 
593 void artdaq::FragmentBuffer::applyRequestsWindowMode_CheckAndFillDataBuffer(artdaq::FragmentPtrs& frags, artdaq::Fragment::fragment_id_t id, artdaq::Fragment::sequence_id_t seq, artdaq::Fragment::timestamp_t ts)
594 {
595  auto dataBuffer = dataBuffers_[id];
596 
597  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsWindowMode_CheckAndFillDataBuffer: Checking that data exists for request window " << seq;
598  Fragment::timestamp_t min = ts > windowOffset_ ? ts - windowOffset_ : 0;
599  Fragment::timestamp_t max = ts + windowWidth_ > windowOffset_ ? ts + windowWidth_ - windowOffset_ : 1;
600 
601  TLOG(TLVL_APPLYREQUESTS) << "ApplyRequestsWindowsMode_CheckAndFillDataBuffer: min is " << min << ", max is " << max
602  << " and first/last points in buffer are " << (dataBuffer->DataBufferDepthFragments > 0 ? dataBuffer->DataBuffer.front()->timestamp() : 0)
603  << "/" << (dataBuffer->DataBufferDepthFragments > 0 ? dataBuffer->DataBuffer.back()->timestamp() : 0)
604  << " (sz=" << dataBuffer->DataBufferDepthFragments << " [" << dataBuffer->DataBufferDepthBytes.load()
605  << "/" << maxDataBufferDepthBytes_ << "])";
606  bool windowClosed = dataBuffer->DataBufferDepthFragments > 0 && dataBuffer->DataBuffer.back()->timestamp() >= max;
607  bool windowTimeout = !windowClosed && TimeUtils::GetElapsedTimeMicroseconds(requestBuffer_->GetRequestTime(seq)) > window_close_timeout_us_;
608  if (windowTimeout)
609  {
610  TLOG(TLVL_WARNING) << "applyRequestsWindowMode_CheckAndFillDataBuffer: A timeout occurred waiting for data to close the request window ({" << min << "-" << max
611  << "}, buffer={" << (dataBuffer->DataBufferDepthFragments > 0 ? dataBuffer->DataBuffer.front()->timestamp() : 0) << "-"
612  << (dataBuffer->DataBufferDepthFragments > 0 ? dataBuffer->DataBuffer.back()->timestamp() : 0)
613  << "} ). Time waiting: "
614  << TimeUtils::GetElapsedTimeMicroseconds(requestBuffer_->GetRequestTime(seq)) << " us "
615  << "(> " << window_close_timeout_us_ << " us).";
616  }
617  if (windowClosed || windowTimeout)
618  {
619  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsWindowMode_CheckAndFillDataBuffer: Creating ContainerFragment for Window-requested Fragments (SeqID " << seq << ")";
620  frags.emplace_back(new artdaq::Fragment(seq, id));
621  frags.back()->setTimestamp(ts);
622  ContainerFragmentLoader cfl(*frags.back());
623 
624  // In the spirit of NOvA's MegaPool: (RS = Request start (min), RE = Request End (max))
625  // --- | Buffer Start | --- | Buffer End | ---
626  // 1. RS RE | | | |
627  // 2. RS | | RE | |
628  // 3. RS | | | | RE
629  // 4. | | RS RE | |
630  // 5. | | RS | | RE
631  // 6. | | | | RS RE
632  //
633  // If RE (or RS) is after the end of the buffer, we wait for window_close_timeout_us_. If we're here, then that means that windowClosed is false, and the missing_data flag should be set.
634  // If RS (or RE) is before the start of the buffer, then missing_data should be set to true, as data is assumed to arrive in the buffer in timestamp order
635  // If the dataBuffer has size 0, then windowClosed will be false
636  if (!windowClosed || (dataBuffer->DataBufferDepthFragments > 0 && dataBuffer->DataBuffer.front()->timestamp() > min))
637  {
638  TLOG(TLVL_DEBUG + 32) << "applyRequestsWindowMode_CheckAndFillDataBuffer: Request window starts before and/or ends after the current data buffer, setting ContainerFragment's missing_data flag!"
639  << " (requestWindowRange=[" << min << "," << max << "], "
640  << "buffer={" << (dataBuffer->DataBufferDepthFragments > 0 ? dataBuffer->DataBuffer.front()->timestamp() : 0) << "-"
641  << (dataBuffer->DataBufferDepthFragments > 0 ? dataBuffer->DataBuffer.back()->timestamp() : 0) << "} (SeqID " << seq << ")";
642  cfl.set_missing_data(true);
643  }
644 
645  auto it = dataBuffer->DataBuffer.begin();
646  // Likely that it will be closer to the end...
647  if (windowTimeout)
648  {
649  it = dataBuffer->DataBuffer.end();
650  --it;
651  while (it != dataBuffer->DataBuffer.begin())
652  {
653  if ((*it)->timestamp() < min)
654  {
655  break;
656  }
657  --it;
658  }
659  }
660 
661  FragmentPtrs fragsToAdd;
662  // Do a little bit more work to decide which fragments to send for a given request
663  for (; it != dataBuffer->DataBuffer.end();)
664  {
665  Fragment::timestamp_t fragT = (*it)->timestamp();
666  if (fragT < min)
667  {
668  ++it;
669  continue;
670  }
671  if (fragT > max || (fragT == max && windowWidth_ > 0))
672  {
673  break;
674  }
675 
676  TLOG(TLVL_APPLYREQUESTS_VERBOSE) << "applyRequestsWindowMode_CheckAndFillDataBuffer: Adding Fragment with timestamp " << (*it)->timestamp() << " to Container (SeqID " << seq << ")";
677  if (uniqueWindows_)
678  {
679  dataBuffer->DataBufferDepthBytes -= (*it)->sizeBytes();
680  fragsToAdd.emplace_back(std::move(*it));
681  it = dataBuffer->DataBuffer.erase(it);
682  }
683  else
684  {
685  fragsToAdd.emplace_back(it->get());
686  ++it;
687  }
688  }
689 
690  if (fragsToAdd.size() > 0)
691  {
692  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsWindowMode_CheckAndFillDataBuffer: Adding " << fragsToAdd.size() << " Fragments to Container (SeqID " << seq << ")";
693  cfl.addFragments(fragsToAdd);
694 
695  // Don't delete Fragments which are still in the Fragment buffer
696  if (!uniqueWindows_)
697  {
698  for (auto& frag : fragsToAdd)
699  {
700  frag.release();
701  }
702  }
703  fragsToAdd.clear();
704  }
705  else
706  {
707  TLOG(error_on_empty_ ? TLVL_ERROR : TLVL_APPLYREQUESTS) << "applyRequestsWindowMode_CheckAndFillDataBuffer: No Fragments match request (SeqID " << seq << ", window " << min << " - " << max << ")";
708  }
709 
710  dataBuffer->DataBufferDepthFragments = dataBuffer->DataBuffer.size();
711  dataBuffer->WindowsSent[seq] = std::chrono::steady_clock::now();
712  if (seq > dataBuffer->HighestRequestSeen) dataBuffer->HighestRequestSeen = seq;
713  }
714 }
715 
716 void artdaq::FragmentBuffer::applyRequestsWindowMode(artdaq::FragmentPtrs& frags)
717 {
718  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsWindowMode BEGIN";
719 
720  auto requests = requestBuffer_->GetRequests();
721 
722  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsWindowMode: Starting request processing for " << requests.size() << " requests";
723  for (auto req = requests.begin(); req != requests.end();)
724  {
725  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsWindowMode: processing request with sequence ID " << req->first << ", timestamp " << req->second;
726 
727  while (req->first < next_sequence_id_ && requests.size() > 0)
728  {
729  TLOG(TLVL_APPLYREQUESTS_VERBOSE) << "applyRequestsWindowMode: Clearing passed request for sequence ID " << req->first;
730  requestBuffer_->RemoveRequest(req->first);
731  req = requests.erase(req);
732  }
733  if (requests.size() == 0) break;
734 
735  auto ts = req->second;
736  if (ts == Fragment::InvalidTimestamp)
737  {
738  TLOG(TLVL_ERROR) << "applyRequestsWindowMode: Received InvalidTimestamp in request " << req->first << ", cannot apply! Check that push-mode BRs are filling appropriate timestamps in their Fragments!";
739  req = requests.erase(req);
740  continue;
741  }
742 
743  for (auto& id : dataBuffers_)
744  {
745  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
746  if (!id.second->WindowsSent.count(req->first))
747  {
748  applyRequestsWindowMode_CheckAndFillDataBuffer(frags, id.first, req->first, req->second);
749  }
750  }
751  checkSentWindows(req->first);
752  ++req;
753  }
754 
755  // Check sent windows for requests that can be removed
756  std::set<artdaq::Fragment::sequence_id_t> seqs;
757  for (auto& id : dataBuffers_)
758  {
759  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
760  for (auto& seq : id.second->WindowsSent)
761  {
762  seqs.insert(seq.first);
763  }
764  }
765  for (auto& seq : seqs)
766  {
767  checkSentWindows(seq);
768  }
769 }
770 
772 {
773  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsSequenceIDMode BEGIN";
774 
775  auto requests = requestBuffer_->GetRequests();
776 
777  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsSequenceIDMode: Starting request processing";
778  for (auto req = requests.begin(); req != requests.end();)
779  {
780  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsSequenceIDMode: Checking that data exists for request SequenceID " << req->first;
781 
782  for (auto& id : dataBuffers_)
783  {
784  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
785  if (!id.second->WindowsSent.count(req->first))
786  {
787  TLOG(TLVL_APPLYREQUESTS_VERBOSE) << "Searching id " << id.first << " for Fragments with Sequence ID " << req->first;
788  for (auto it = id.second->DataBuffer.begin(); it != id.second->DataBuffer.end();)
789  {
790  auto seq = (*it)->sequenceID();
791  TLOG(TLVL_APPLYREQUESTS_VERBOSE) << "applyRequestsSequenceIDMode: Fragment SeqID " << seq << ", request ID " << req->first;
792  if (seq == req->first)
793  {
794  TLOG(TLVL_APPLYREQUESTS_VERBOSE) << "applyRequestsSequenceIDMode: Adding Fragment to output";
795  id.second->WindowsSent[req->first] = std::chrono::steady_clock::now();
796  id.second->DataBufferDepthBytes -= (*it)->sizeBytes();
797  frags.push_back(std::move(*it));
798  it = id.second->DataBuffer.erase(it);
799  id.second->DataBufferDepthFragments = id.second->DataBuffer.size();
800  }
801  else
802  {
803  ++it;
804  }
805  }
806  }
807  if (req->first > id.second->HighestRequestSeen) id.second->HighestRequestSeen = req->first;
808  }
809  checkSentWindows(req->first);
810  ++req;
811  }
812 
813  // Check sent windows for requests that can be removed
814  std::set<artdaq::Fragment::sequence_id_t> seqs;
815  for (auto& id : dataBuffers_)
816  {
817  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
818  for (auto& seq : id.second->WindowsSent)
819  {
820  seqs.insert(seq.first);
821  }
822  }
823  for (auto& seq : seqs)
824  {
825  checkSentWindows(seq);
826  }
827 }
828 
829 bool artdaq::FragmentBuffer::applyRequests(artdaq::FragmentPtrs& frags)
830 {
831  if (check_stop())
832  {
833  return false;
834  }
835 
836  // Wait for data, if in ignored mode, or a request otherwise
837  if (mode_ == RequestMode::Ignored)
838  {
839  auto start_time = std::chrono::steady_clock::now();
840  while (dataBufferFragmentCount_() == 0 && TimeUtils::GetElapsedTime(start_time) < 1.0)
841  {
842  if (check_stop()) return false;
843  std::unique_lock<std::mutex> lock(dataConditionMutex_);
844  dataCondition_.wait_for(lock, std::chrono::milliseconds(10), [this]() { return dataBufferFragmentCount_() > 0; });
845  }
846  }
847  else if (requestBuffer_ == nullptr)
848  {
849  TLOG(TLVL_ERROR) << "Request Buffer must be set (via SetRequestBuffer) before applyRequests/getData can be called!";
850  return false;
851  }
852  else
853  {
854  if ((check_stop() && requestBuffer_->size() == 0)) return false;
855 
856  std::unique_lock<std::mutex> lock(dataConditionMutex_);
857  dataCondition_.wait_for(lock, std::chrono::milliseconds(10));
858 
859  checkDataBuffers();
860 
861  // Wait up to 1000 ms for a request...
862  auto counter = 0;
863 
864  while (requestBuffer_->size() == 0 && counter < 100)
865  {
866  if (check_stop()) return false;
867 
868  checkDataBuffers();
869 
870  requestBuffer_->WaitForRequests(10); // milliseconds
871  counter++;
872  }
873  }
874 
875  if (systemFragmentCount_.load() > 0)
876  {
877  std::lock_guard<std::mutex> lk(systemFragmentMutex_);
878  TLOG(TLVL_INFO) << "Copying " << systemFragmentCount_.load() << " System Fragments into output";
879 
880  std::move(systemFragments_.begin(), systemFragments_.end(), std::inserter(frags, frags.end()));
881  systemFragments_.clear();
882  systemFragmentCount_ = 0;
883  }
884 
885  switch (mode_)
886  {
887  case RequestMode::Single:
888  applyRequestsSingleMode(frags);
889  break;
890  case RequestMode::Window:
891  applyRequestsWindowMode(frags);
892  break;
893  case RequestMode::Buffer:
894  applyRequestsBufferMode(frags);
895  break;
896  case RequestMode::SequenceID:
897  applyRequestsSequenceIDMode(frags);
898  break;
899  case RequestMode::Ignored:
900  default:
901  applyRequestsIgnoredMode(frags);
902  break;
903  }
904 
905  getDataBuffersStats();
906 
907  if (frags.size() > 0)
908  TLOG(TLVL_APPLYREQUESTS) << "Finished Processing requests, returning " << frags.size() << " fragments, current ev_counter is " << next_sequence_id_;
909  return true;
910 }
911 
912 bool artdaq::FragmentBuffer::sendEmptyFragment(artdaq::FragmentPtrs& frags, size_t seqId, Fragment::fragment_id_t fragmentId, std::string desc)
913 {
914  TLOG(TLVL_EMPTYFRAGMENT) << desc << " sequence ID " << seqId << ", sending empty fragment";
915  auto frag = new Fragment();
916  frag->setSequenceID(seqId);
917  frag->setFragmentID(fragmentId);
918  frag->setSystemType(Fragment::EmptyFragmentType);
919  frags.emplace_back(FragmentPtr(frag));
920  return true;
921 }
922 
923 void artdaq::FragmentBuffer::sendEmptyFragments(artdaq::FragmentPtrs& frags, std::map<Fragment::sequence_id_t, Fragment::timestamp_t>& requests)
924 {
925  if (requests.size() > 0)
926  {
927  TLOG(TLVL_SENDEMPTYFRAGMENTS) << "Sending Empty Fragments for Sequence IDs from " << next_sequence_id_ << " up to but not including " << requests.begin()->first;
928  while (requests.begin()->first > next_sequence_id_)
929  {
930  if (sendMissingFragments_)
931  {
932  for (auto& fid : dataBuffers_)
933  {
934  sendEmptyFragment(frags, next_sequence_id_, fid.first, "Missed request for");
935  }
936  }
937  ++next_sequence_id_;
938  }
939  }
940 }
941 
942 void artdaq::FragmentBuffer::checkSentWindows(artdaq::Fragment::sequence_id_t seq)
943 {
944  TLOG(TLVL_CHECKWINDOWS) << "checkSentWindows: Checking if request " << seq << " can be removed from request list";
945  bool seqComplete = true;
946  bool seqTimeout = false;
947  for (auto& id : dataBuffers_)
948  {
949  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
950  if (!id.second->WindowsSent.count(seq) || id.second->HighestRequestSeen < seq)
951  {
952  seqComplete = false;
953  }
954  if (id.second->WindowsSent.count(seq) && TimeUtils::GetElapsedTimeMicroseconds(id.second->WindowsSent[seq]) > missing_request_window_timeout_us_)
955  {
956  seqTimeout = true;
957  }
958  }
959  if (seqComplete)
960  {
961  TLOG(TLVL_CHECKWINDOWS) << "checkSentWindows: Request " << seq << " is complete, removing from requestBuffer_.";
962  requestBuffer_->RemoveRequest(seq);
963 
964  if (next_sequence_id_ == seq)
965  {
966  TLOG(TLVL_CHECKWINDOWS) << "checkSentWindows: Sequence ID matches ev_counter, incrementing ev_counter (" << next_sequence_id_ << ")";
967 
968  for (auto& id : dataBuffers_)
969  {
970  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
971  id.second->WindowsSent.erase(seq);
972  }
973 
974  ++next_sequence_id_;
975  }
976  }
977  if (seqTimeout)
978  {
979  TLOG(TLVL_CHECKWINDOWS) << "checkSentWindows: Sent Window history indicates that requests between " << next_sequence_id_ << " and " << seq << " have timed out.";
980  while (next_sequence_id_ <= seq)
981  {
982  if (next_sequence_id_ < seq) TLOG(TLVL_CHECKWINDOWS) << "Missed request for sequence ID " << next_sequence_id_ << "! Will not send any data for this sequence ID!";
983  requestBuffer_->RemoveRequest(next_sequence_id_);
984 
985  for (auto& id : dataBuffers_)
986  {
987  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
988  id.second->WindowsSent.erase(next_sequence_id_);
989  }
990 
991  ++next_sequence_id_;
992  }
993  }
994 }
void AddFragmentsToBuffer(FragmentPtrs frags)
Add Fragments to the FragmentBuffer.
size_t dataBufferFragmentCount_()
Get the total number of Fragments in all data buffers.
std::string printMode_()
Return the string representation of the current RequestMode.
bool applyRequests(FragmentPtrs &frags)
See if any requests have been received, and add the corresponding data Fragment objects to the output...
FragmentBuffer(const fhicl::ParameterSet &ps)
FragmentBuffer Constructor.
bool waitForDataBufferReady(Fragment::fragment_id_t id)
Wait for the data buffer to drain (dataBufferIsTooLarge returns false), periodically reporting status...
void applyRequestsIgnoredMode(artdaq::FragmentPtrs &frags)
Create fragments using data buffer for request mode Ignored. Precondition: dataBufferMutex_ and reque...
void applyRequestsWindowMode_CheckAndFillDataBuffer(artdaq::FragmentPtrs &frags, artdaq::Fragment::fragment_id_t id, artdaq::Fragment::sequence_id_t seq, artdaq::Fragment::timestamp_t ts)
bool check_stop()
Routine used by applyRequests to make sure that all outstanding requests have been fulfilled before r...
void Reset(bool stop)
Reset the FragmentBuffer (flushes all Fragments from buffers)
void checkSentWindows(Fragment::sequence_id_t seq)
Check the windows_sent_ooo_ map for sequence IDs that may be removed.
artdaq::Fragment::fragment_id_t fragment_id() const
Get the Fragment ID of this Fragment generator.
void checkDataBuffer(Fragment::fragment_id_t id)
Perform data buffer pruning operations for the given buffer. If the RequestMode is Single...
bool dataBufferIsTooLarge(Fragment::fragment_id_t id)
Test the configured constraints on the data buffer.
bool sendEmptyFragment(FragmentPtrs &frags, size_t sequenceId, Fragment::fragment_id_t fragmentId, std::string desc)
Send an EmptyFragmentType Fragment.
void applyRequestsBufferMode(artdaq::FragmentPtrs &frags)
Create fragments using data buffer for request mode Buffer. Precondition: dataBufferMutex_ and reques...
void applyRequestsWindowMode(artdaq::FragmentPtrs &frags)
Create fragments using data buffer for request mode Window. Precondition: dataBufferMutex_ and reques...
virtual ~FragmentBuffer()
FragmentBuffer Destructor.
void sendEmptyFragments(FragmentPtrs &frags, std::map< Fragment::sequence_id_t, Fragment::timestamp_t > &requests)
This function is for Buffered and Single request modes, as they can only respond to one data request ...
std::string getStatReport()
report statistics as a string
void applyRequestsSequenceIDMode(artdaq::FragmentPtrs &frags)
Create fragments using data buffer for request mode SequenceID. Precondition: dataBufferMutex_ and re...
void applyRequestsSingleMode(artdaq::FragmentPtrs &frags)
Create fragments using data buffer for request mode Single. Precondition: dataBufferMutex_ and reques...
void getDataBufferStats(Fragment::fragment_id_t id)
Calculate the size of the dataBuffer and report appropriate metrics.