artdaq  v3_05_00
CommandableFragmentGenerator.cc
1 #define TRACE_NAME (app_name + "_CommandableFragmentGenerator").c_str() // include these 2 first -
2 #include "artdaq/DAQdata/Globals.hh"
3 
4 #include "artdaq/Generators/CommandableFragmentGenerator.hh"
5 
6 #include <boost/exception/all.hpp>
7 #include <boost/throw_exception.hpp>
8 
9 #include <limits>
10 #include <iterator>
11 
12 #include "canvas/Utilities/Exception.h"
13 #include "cetlib_except/exception.h"
14 #include "fhiclcpp/ParameterSet.h"
15 
16 #include "artdaq-core/Utilities/SimpleLookupPolicy.hh"
17 #include "artdaq-core/Data/Fragment.hh"
18 #include "artdaq-core/Data/ContainerFragmentLoader.hh"
19 #include "artdaq-core/Utilities/ExceptionHandler.hh"
20 #include "artdaq-core/Utilities/TimeUtils.hh"
21 
22 #include <fstream>
23 #include <iomanip>
24 #include <iterator>
25 #include <iostream>
26 #include <iomanip>
27 #include <algorithm>
28 #include <sys/poll.h>
30 
31 #define TLVL_GETNEXT 10
32 #define TLVL_GETNEXT_VERBOSE 20
33 #define TLVL_CHECKSTOP 11
34 #define TLVL_EVCOUNTERINC 12
35 #define TLVL_GETDATALOOP 13
36 #define TLVL_GETDATALOOP_DATABUFFWAIT 21
37 #define TLVL_GETDATALOOP_VERBOSE 20
38 #define TLVL_WAITFORBUFFERREADY 15
39 #define TLVL_GETBUFFERSTATS 16
40 #define TLVL_CHECKDATABUFFER 17
41 #define TLVL_GETMONITORINGDATA 18
42 #define TLVL_APPLYREQUESTS 9
43 #define TLVL_SENDEMPTYFRAGMENTS 19
44 #define TLVL_CHECKWINDOWS 14
45 
47  : mutex_()
48  , requestReceiver_(nullptr)
49  , windowOffset_(0)
50  , windowWidth_(0)
51  , staleTimeout_(Fragment::InvalidTimestamp)
52  , expectedType_(Fragment::EmptyFragmentType)
53  , maxFragmentCount_(std::numeric_limits<size_t>::max())
54  , uniqueWindows_(true)
55  , windows_sent_ooo_()
56  , missing_request_window_timeout_us_(1000000)
57  , window_close_timeout_us_(2000000)
58  , useDataThread_(false)
59  , circularDataBufferMode_(false)
60  , sleep_on_no_data_us_(0)
61  , data_thread_running_(false)
62  , dataBufferDepthFragments_(0)
63  , dataBufferDepthBytes_(0)
64  , maxDataBufferDepthFragments_(1000)
65  , maxDataBufferDepthBytes_(1000)
66  , useMonitoringThread_(false)
67  , monitoringInterval_(0)
68  , lastMonitoringCall_()
69  , isHardwareOK_(true)
70  , dataBuffer_()
71  , newDataBuffer_()
72  , run_number_(-1)
73  , subrun_number_(-1)
74  , timeout_(std::numeric_limits<uint64_t>::max())
75  , timestamp_(std::numeric_limits<uint64_t>::max())
76  , should_stop_(false)
77  , exception_(false)
78  , force_stop_(false)
79  , latest_exception_report_("none")
80  , ev_counter_(1)
81  , board_id_(-1)
82  , instance_name_for_metrics_("FragmentGenerator")
83  , sleep_on_stop_us_(0)
84 {}
85 
87  : mutex_()
88  , requestReceiver_(nullptr)
89  , windowOffset_(ps.get<Fragment::timestamp_t>("request_window_offset", 0))
90  , windowWidth_(ps.get<Fragment::timestamp_t>("request_window_width", 0))
91  , staleTimeout_(ps.get<Fragment::timestamp_t>("stale_request_timeout", 0xFFFFFFFF))
92  , expectedType_(ps.get<Fragment::type_t>("expected_fragment_type", Fragment::type_t(Fragment::EmptyFragmentType)))
93  , uniqueWindows_(ps.get<bool>("request_windows_are_unique", true))
94  , windows_sent_ooo_()
95  , missing_request_window_timeout_us_(ps.get<size_t>("missing_request_window_timeout_us", 5000000))
96  , window_close_timeout_us_(ps.get<size_t>("window_close_timeout_us", 2000000))
97  , useDataThread_(ps.get<bool>("separate_data_thread", false))
98  , circularDataBufferMode_(ps.get<bool>("circular_buffer_mode", false))
99  , sleep_on_no_data_us_(ps.get<size_t>("sleep_on_no_data_us", 0))
100  , data_thread_running_(false)
101  , dataBufferDepthFragments_(0)
102  , dataBufferDepthBytes_(0)
103  , maxDataBufferDepthFragments_(ps.get<int>("data_buffer_depth_fragments", 1000))
104  , maxDataBufferDepthBytes_(ps.get<size_t>("data_buffer_depth_mb", 1000) * 1024 * 1024)
105  , useMonitoringThread_(ps.get<bool>("separate_monitoring_thread", false))
106  , monitoringInterval_(ps.get<int64_t>("hardware_poll_interval_us", 0))
107  , lastMonitoringCall_()
108  , isHardwareOK_(true)
109  , dataBuffer_()
110  , newDataBuffer_()
111  , run_number_(-1)
112  , subrun_number_(-1)
113  , timeout_(std::numeric_limits<uint64_t>::max())
114  , timestamp_(std::numeric_limits<uint64_t>::max())
115  , should_stop_(false)
116  , exception_(false)
117  , force_stop_(false)
118  , latest_exception_report_("none")
119  , ev_counter_(1)
120  , board_id_(-1)
121  , sleep_on_stop_us_(0)
122 {
123  board_id_ = ps.get<int>("board_id");
124  instance_name_for_metrics_ = "BoardReader." + boost::lexical_cast<std::string>(board_id_);
125 
126  fragment_ids_ = ps.get<std::vector<artdaq::Fragment::fragment_id_t>>("fragment_ids", std::vector<artdaq::Fragment::fragment_id_t>());
127 
128  TLOG(TLVL_TRACE) << "artdaq::CommandableFragmentGenerator::CommandableFragmentGenerator(ps)";
129  int fragment_id = ps.get<int>("fragment_id", -99);
130 
131  if (fragment_id != -99)
132  {
133  if (fragment_ids_.size() != 0)
134  {
135  latest_exception_report_ = "Error in CommandableFragmentGenerator: can't both define \"fragment_id\" and \"fragment_ids\" in FHiCL document";
136  throw cet::exception(latest_exception_report_);
137  }
138  else
139  {
140  fragment_ids_.emplace_back(fragment_id);
141  }
142  }
143 
144  sleep_on_stop_us_ = ps.get<int>("sleep_on_stop_us", 0);
145 
146  std::string modeString = ps.get<std::string>("request_mode", "ignored");
147  if (modeString == "single" || modeString == "Single")
148  {
149  mode_ = RequestMode::Single;
150  }
151  else if (modeString.find("buffer") != std::string::npos || modeString.find("Buffer") != std::string::npos)
152  {
153  mode_ = RequestMode::Buffer;
154  }
155  else if (modeString == "window" || modeString == "Window")
156  {
157  mode_ = RequestMode::Window;
158  }
159  else if (modeString.find("ignore") != std::string::npos || modeString.find("Ignore") != std::string::npos)
160  {
161  mode_ = RequestMode::Ignored;
162  }
163  TLOG(TLVL_DEBUG) << "Request mode is " << printMode_();
164 
165  if (mode_ != RequestMode::Ignored)
166  {
167  if (!useDataThread_)
168  {
169  latest_exception_report_ = "Error in CommandableFragmentGenerator: use_data_thread must be true when request_mode is not \"Ignored\"!";
170  throw cet::exception(latest_exception_report_);
171  }
172  requestReceiver_.reset(new RequestReceiver(ps));
173  }
174 }
175 
177 {
178  joinThreads();
179  requestReceiver_.reset(nullptr);
180 }
181 
183 {
184  should_stop_ = true;
185  force_stop_ = true;
186  TLOG(TLVL_DEBUG) << "Joining dataThread";
187  if (dataThread_.joinable()) dataThread_.join();
188  TLOG(TLVL_DEBUG) << "Joining monitoringThread";
189  if (monitoringThread_.joinable()) monitoringThread_.join();
190  TLOG(TLVL_DEBUG) << "joinThreads complete";
191 }
192 
194 {
195  bool result = true;
196 
197  if (check_stop()) usleep(sleep_on_stop_us_);
198  if (exception() || force_stop_) return false;
199 
200  if (!useMonitoringThread_ && monitoringInterval_ > 0)
201  {
202  TLOG(TLVL_GETNEXT) << "getNext: Checking whether to collect Monitoring Data";
203  auto now = std::chrono::steady_clock::now();
204 
205  if (TimeUtils::GetElapsedTimeMicroseconds(lastMonitoringCall_, now) >= static_cast<size_t>(monitoringInterval_))
206  {
207  TLOG(TLVL_GETNEXT) << "getNext: Collecting Monitoring Data";
208  isHardwareOK_ = checkHWStatus_();
209  TLOG(TLVL_GETNEXT) << "getNext: isHardwareOK_ is now " << std::boolalpha << isHardwareOK_;
210  lastMonitoringCall_ = now;
211  }
212  }
213 
214  try
215  {
216  std::lock_guard<std::mutex> lk(mutex_);
217  if (useDataThread_)
218  {
219  TLOG(TLVL_TRACE) << "getNext: Calling applyRequests";
220  result = applyRequests(output);
221  TLOG(TLVL_TRACE) << "getNext: Done with applyRequests result=" << std::boolalpha << result;
222  for (auto dataIter = output.begin(); dataIter != output.end(); ++dataIter)
223  {
224  TLOG(20) << "getNext: applyRequests() returned fragment with sequenceID = " << (*dataIter)->sequenceID()
225  << ", timestamp = " << (*dataIter)->timestamp() << ", and sizeBytes = " << (*dataIter)->sizeBytes();
226  }
227 
228  if (exception())
229  {
230  TLOG(TLVL_ERROR) << "Exception found in BoardReader with board ID " << board_id() << "; BoardReader will now return error status when queried";
231  throw cet::exception("CommandableFragmentGenerator") << "Exception found in BoardReader with board ID " << board_id() << "; BoardReader will now return error status when queried";
232  }
233  }
234  else
235  {
236  if (!isHardwareOK_)
237  {
238  TLOG(TLVL_ERROR) << "Stopping CFG because the hardware reports bad status!";
239  return false;
240  }
241  TLOG(TLVL_TRACE) << "getNext: Calling getNext_ w/ ev_counter()=" << ev_counter();
242  try
243  {
244  result = getNext_(output);
245  }
246  catch (...)
247  {
248  throw;
249  }
250  TLOG(TLVL_TRACE) << "getNext: Done with getNext_ - ev_counter() now " << ev_counter();
251  for (auto dataIter = output.begin(); dataIter != output.end(); ++dataIter)
252  {
253  TLOG(TLVL_GETNEXT_VERBOSE) << "getNext: getNext_() returned fragment with sequenceID = " << (*dataIter)->sequenceID()
254  << ", timestamp = " << (*dataIter)->timestamp() << ", and sizeBytes = " << (*dataIter)->sizeBytes();
255  }
256  }
257  }
258  catch (const cet::exception& e)
259  {
260  latest_exception_report_ = "cet::exception caught in getNext(): ";
261  latest_exception_report_.append(e.what());
262  TLOG(TLVL_ERROR) << "getNext: cet::exception caught: " << e;
263  set_exception(true);
264  return false;
265  }
266  catch (const boost::exception& e)
267  {
268  latest_exception_report_ = "boost::exception caught in getNext(): ";
269  latest_exception_report_.append(boost::diagnostic_information(e));
270  TLOG(TLVL_ERROR) << "getNext: boost::exception caught: " << boost::diagnostic_information(e);
271  set_exception(true);
272  return false;
273  }
274  catch (const std::exception& e)
275  {
276  latest_exception_report_ = "std::exception caught in getNext(): ";
277  latest_exception_report_.append(e.what());
278  TLOG(TLVL_ERROR) << "getNext: std::exception caught: " << e.what();
279  set_exception(true);
280  return false;
281  }
282  catch (...)
283  {
284  latest_exception_report_ = "Unknown exception caught in getNext().";
285  TLOG(TLVL_ERROR) << "getNext: unknown exception caught";
286  set_exception(true);
287  return false;
288  }
289 
290  if (!result)
291  {
292  TLOG(TLVL_DEBUG) << "getNext: Either getNext_ or applyRequests returned false, stopping";
293  }
294 
295  if (metricMan && !output.empty())
296  {
297  auto timestamp = output.front()->timestamp();
298 
299  if (output.size() > 1)
300  { // Only bother sorting if >1 entry
301  for (auto& outputfrag : output)
302  {
303  if (outputfrag->timestamp() > timestamp)
304  {
305  timestamp = outputfrag->timestamp();
306  }
307  }
308  }
309 
310  metricMan->sendMetric("Last Timestamp", timestamp, "Ticks", 1,
311  MetricMode::LastPoint, app_name);
312  }
313 
314  return result;
315 }
316 
318 {
319  TLOG(TLVL_CHECKSTOP) << "CFG::check_stop: should_stop=" << should_stop() << ", useDataThread_=" << useDataThread_ << ", exception status =" << int(exception());
320 
321  if (!should_stop()) return false;
322  if (!useDataThread_ || mode_ == RequestMode::Ignored) return true;
323  if (force_stop_) return true;
324 
325  // check_stop returns true if the CFG should stop. We should wait for the RequestReceiver to stop before stopping.
326  TLOG(TLVL_DEBUG) << "should_stop is true, force_stop_ is false, requestReceiver_->isRunning() is " << std::boolalpha << requestReceiver_->isRunning();
327  return !requestReceiver_->isRunning();
328 }
329 
331 {
332  if (fragment_ids_.size() != 1)
333  {
334  throw cet::exception("Error in CommandableFragmentGenerator: can't call fragment_id() unless member fragment_ids_ vector is length 1");
335  }
336  else
337  {
338  return fragment_ids_[0];
339  }
340 }
341 
343 {
344  if (force || mode_ == RequestMode::Ignored)
345  {
346  TLOG(TLVL_EVCOUNTERINC) << "ev_counter_inc: Incrementing ev_counter from " << ev_counter() << " by " << step;
347  return ev_counter_.fetch_add(step);
348  }
349  return ev_counter_.load();
350 } // returns the prev value
351 
352 void artdaq::CommandableFragmentGenerator::StartCmd(int run, uint64_t timeout, uint64_t timestamp)
353 {
354  TLOG(TLVL_TRACE) << "Start Command received.";
355  if (run < 0) throw cet::exception("CommandableFragmentGenerator") << "negative run number";
356 
357  timeout_ = timeout;
358  timestamp_ = timestamp;
359  ev_counter_.store(1);
360  windows_sent_ooo_.clear();
361  {
362  std::unique_lock<std::mutex> lock(dataBufferMutex_);
363  dataBufferDepthBytes_ = 0;
364  dataBufferDepthFragments_ = 0;
365  dataBuffer_.clear();
366  }
367  should_stop_.store(false);
368  force_stop_.store(false);
369  exception_.store(false);
370  run_number_ = run;
371  subrun_number_ = 1;
372  latest_exception_report_ = "none";
373 
374  start();
375 
376  std::unique_lock<std::mutex> lk(mutex_);
377  if (useDataThread_) startDataThread();
378  if (useMonitoringThread_) startMonitoringThread();
379  if (mode_ != RequestMode::Ignored)
380  {
381  requestReceiver_->SetRunNumber(static_cast<uint32_t>(run));
382  requestReceiver_->startRequestReception();
383  }
384  TLOG(TLVL_TRACE) << "Start Command complete.";
385 }
386 
387 void artdaq::CommandableFragmentGenerator::StopCmd(uint64_t timeout, uint64_t timestamp)
388 {
389  TLOG(TLVL_TRACE) << "Stop Command received.";
390 
391  timeout_ = timeout;
392  timestamp_ = timestamp;
393  if (requestReceiver_) {
394  TLOG(TLVL_DEBUG) << "Stopping Request reception BEGIN";
395  requestReceiver_->stopRequestReception();
396  TLOG(TLVL_DEBUG) << "Stopping Request reception END";
397  }
398 
399  stopNoMutex();
400  should_stop_.store(true);
401  std::unique_lock<std::mutex> lk(mutex_);
402  stop();
403 
404  joinThreads();
405  TLOG(TLVL_TRACE) << "Stop Command complete.";
406 }
407 
408 void artdaq::CommandableFragmentGenerator::PauseCmd(uint64_t timeout, uint64_t timestamp)
409 {
410  TLOG(TLVL_TRACE) << "Pause Command received.";
411  timeout_ = timeout;
412  timestamp_ = timestamp;
413  //if (requestReceiver_->isRunning()) requestReceiver_->stopRequestReceiverThread();
414 
415  pauseNoMutex();
416  should_stop_.store(true);
417  std::unique_lock<std::mutex> lk(mutex_);
418 
419  pause();
420 }
421 
422 void artdaq::CommandableFragmentGenerator::ResumeCmd(uint64_t timeout, uint64_t timestamp)
423 {
424  TLOG(TLVL_TRACE) << "Resume Command received.";
425  timeout_ = timeout;
426  timestamp_ = timestamp;
427 
428  subrun_number_ += 1;
429  should_stop_ = false;
430  {
431  std::unique_lock<std::mutex> lk(dataBufferMutex_);
432  dataBufferDepthBytes_ = 0;
433  dataBufferDepthFragments_ = 0;
434  dataBuffer_.clear();
435  }
436  // no lock required: thread not started yet
437  resume();
438 
439  std::unique_lock<std::mutex> lk(mutex_);
440  //if (useDataThread_) startDataThread();
441  //if (useMonitoringThread_) startMonitoringThread();
442  //if (mode_ != RequestMode::Ignored && !requestReceiver_->isRunning()) requestReceiver_->startRequestReceiverThread();
443  TLOG(TLVL_TRACE) << "Resume Command complete.";
444 }
445 
446 std::string artdaq::CommandableFragmentGenerator::ReportCmd(std::string const& which)
447 {
448  TLOG(TLVL_TRACE) << "Report Command received.";
449  std::lock_guard<std::mutex> lk(mutex_);
450 
451  // 14-May-2015, KAB: please see the comments associated with the report()
452  // methods in the CommandableFragmentGenerator.hh file for more information
453  // on the use of those methods in this method.
454 
455  // check if the child class has something meaningful for this request
456  std::string childReport = reportSpecific(which);
457  if (childReport.length() > 0) { return childReport; }
458 
459  // handle the requests that we can take care of at this level
460  if (which == "latest_exception")
461  {
462  return latest_exception_report_;
463  }
464 
465  // check if the child class has provided a catch-all report function
466  childReport = report();
467  if (childReport.length() > 0) { return childReport; }
468 
469  // if we haven't been able to come up with any report so far, say so
470  std::string tmpString = "The \"" + which + "\" command is not ";
471  tmpString.append("currently supported by the ");
472  tmpString.append(metricsReportingInstanceName());
473  tmpString.append(" fragment generator.");
474  TLOG(TLVL_TRACE) << "Report Command complete.";
475  return tmpString;
476 }
477 
478 // Default implemenetations of state functions
480 {
481 #pragma message "Using default implementation of CommandableFragmentGenerator::pauseNoMutex()"
482 }
483 
485 {
486 #pragma message "Using default implementation of CommandableFragmentGenerator::pause()"
487 }
488 
490 {
491 #pragma message "Using default implementation of CommandableFragmentGenerator::resume()"
492 }
493 
495 {
496 #pragma message "Using default implementation of CommandableFragmentGenerator::report()"
497  return "";
498 }
499 
501 {
502 #pragma message "Using default implementation of CommandableFragmentGenerator::reportSpecific(std::string)"
503  return "";
504 }
505 
507 {
508 #pragma message "Using default implementation of CommandableFragmentGenerator::checkHWStatus_()"
509  return true;
510 }
511 
512 bool artdaq::CommandableFragmentGenerator::metaCommand(std::string const&, std::string const&)
513 {
514 #pragma message "Using default implementation of CommandableFragmentGenerator::metaCommand(std::string, std::string)"
515  return true;
516 }
517 
519 {
520  if (dataThread_.joinable()) dataThread_.join();
521  TLOG(TLVL_INFO) << "Starting Data Receiver Thread";
522  try {
523  dataThread_ = boost::thread(&CommandableFragmentGenerator::getDataLoop, this);
524  }
525  catch (const boost::exception& e)
526  {
527  TLOG(TLVL_ERROR) << "Caught boost::exception starting Data Receiver thread: " << boost::diagnostic_information(e) << ", errno=" << errno;
528  std::cerr << "Caught boost::exception starting Data Receiver thread: " << boost::diagnostic_information(e) << ", errno=" << errno << std::endl;
529  exit(5);
530  }
531 }
532 
534 {
535  if (monitoringThread_.joinable()) monitoringThread_.join();
536  TLOG(TLVL_INFO) << "Starting Hardware Monitoring Thread";
537  try {
538  monitoringThread_ = boost::thread(&CommandableFragmentGenerator::getMonitoringDataLoop, this);
539  }
540  catch (const boost::exception& e)
541  {
542  TLOG(TLVL_ERROR) << "Caught boost::exception starting Hardware Monitoring thread: " << boost::diagnostic_information(e) << ", errno=" << errno;
543  std::cerr << "Caught boost::exception starting Hardware Monitoring thread: " << boost::diagnostic_information(e) << ", errno=" << errno << std::endl;
544  exit(5);
545  }
546 }
547 
549 {
550  switch (mode_)
551  {
552  case RequestMode::Single:
553  return "Single";
554  case RequestMode::Buffer:
555  return "Buffer";
556  case RequestMode::Window:
557  return "Window";
558  case RequestMode::Ignored:
559  return "Ignored";
560  }
561 
562  return "ERROR";
563 }
564 
565 
566 //
567 // The "useDataThread_" thread
568 //
570 {
571  data_thread_running_ = true;
572  while (!force_stop_)
573  {
574  if (!isHardwareOK_)
575  {
576  TLOG(TLVL_DEBUG) << "getDataLoop: isHardwareOK is " << isHardwareOK_ << ", aborting data thread";
577  data_thread_running_ = false;
578  return;
579  }
580 
581  TLOG(TLVL_GETDATALOOP) << "getDataLoop: calling getNext_";
582 
583  bool data = false;
584  auto startdata = std::chrono::steady_clock::now();
585 
586  try
587  {
588  data = getNext_(newDataBuffer_);
589  }
590  catch (...)
591  {
592  ExceptionHandler(ExceptionHandlerRethrow::no,
593  "Exception thrown by fragment generator in CommandableFragmentGenerator::getDataLoop; setting exception state to \"true\"");
594  set_exception(true);
595 
596  data_thread_running_ = false;
597  return;
598  }
599 
600  size_t newDataBufferDepthBytes = 0;
601  for (auto dataIter = newDataBuffer_.begin(); dataIter != newDataBuffer_.end(); ++dataIter)
602  {
603  TLOG(TLVL_GETDATALOOP_VERBOSE) << "getDataLoop: getNext_() returned fragment with timestamp = " << (*dataIter)->timestamp() << ", and sizeBytes = " << (*dataIter)->sizeBytes();
604  newDataBufferDepthBytes += (*dataIter)->sizeBytes();
605  }
606 
607  if (metricMan)
608  {
609  metricMan->sendMetric("Avg Data Acquisition Time", TimeUtils::GetElapsedTime(startdata), "s", 3, artdaq::MetricMode::Average);
610  }
611 
612  if (newDataBuffer_.size() == 0 && sleep_on_no_data_us_ > 0)
613  {
614  usleep(sleep_on_no_data_us_);
615  }
616 
617  TLOG(TLVL_GETDATALOOP_DATABUFFWAIT) << "Waiting for data buffer ready";
618  if (!waitForDataBufferReady()) return;
619  TLOG(TLVL_GETDATALOOP_DATABUFFWAIT) << "Done waiting for data buffer ready";
620 
621  TLOG(TLVL_GETDATALOOP) << "getDataLoop: processing data";
622  if (data && !force_stop_)
623  {
624  std::unique_lock<std::mutex> lock(dataBufferMutex_);
625  switch (mode_)
626  {
627  case RequestMode::Single:
628  // While here, if for some strange reason more than one event's worth of data is returned from getNext_...
629  while (newDataBuffer_.size() >= fragment_ids_.size())
630  {
631  dataBufferDepthBytes_ = 0;
632  dataBufferDepthFragments_ = 0;
633  dataBuffer_.clear();
634  auto it = newDataBuffer_.begin();
635  std::advance(it, fragment_ids_.size());
636  dataBuffer_.splice(dataBuffer_.end(), newDataBuffer_, newDataBuffer_.begin(), it);
637 
638  for (auto dbit = dataBuffer_.begin(); dbit != dataBuffer_.end(); ++dbit)
639  {
640  dataBufferDepthBytes_ += (*dbit)->sizeBytes();
641  }
642  }
643  break;
644  case RequestMode::Buffer:
645  case RequestMode::Ignored:
646  case RequestMode::Window:
647  default:
648  //dataBuffer_.reserve(dataBuffer_.size() + newDataBuffer_.size());
649  dataBufferDepthBytes_ += newDataBufferDepthBytes;
650  dataBuffer_.splice(dataBuffer_.end(), newDataBuffer_);
651  break;
652  }
653  getDataBufferStats();
654  }
655 
656  {
657  std::unique_lock<std::mutex> lock(dataBufferMutex_);
658  if (dataBuffer_.size() > 0)
659  {
660  dataCondition_.notify_all();
661  }
662  }
663  if (!data || force_stop_)
664  {
665  TLOG(TLVL_INFO) << "Data flow has stopped. Ending data collection thread";
666  std::unique_lock<std::mutex> lock(dataBufferMutex_);
667  data_thread_running_ = false;
668  if (requestReceiver_) requestReceiver_->ClearRequests();
669  newDataBuffer_.clear();
670  TLOG(TLVL_INFO) << "getDataLoop: Ending thread";
671  return;
672  }
673  }
674 }
675 
677 {
678  auto startwait = std::chrono::steady_clock::now();
679  auto first = true;
680  auto lastwaittime = 0ULL;
681 
682  {
683  std::unique_lock<std::mutex> lock(dataBufferMutex_);
684  getDataBufferStats();
685  }
686 
687  while (dataBufferIsTooLarge())
688  {
689  if (!circularDataBufferMode_)
690  {
691  if (should_stop())
692  {
693  TLOG(TLVL_DEBUG) << "Run ended while waiting for buffer to shrink!";
694  std::unique_lock<std::mutex> lock(dataBufferMutex_);
695  getDataBufferStats();
696  dataCondition_.notify_all();
697  data_thread_running_ = false;
698  return false;
699  }
700  auto waittime = TimeUtils::GetElapsedTimeMilliseconds(startwait);
701 
702  if (first || (waittime != lastwaittime && waittime % 1000 == 0))
703  {
704  TLOG(TLVL_WARNING) << "Bad Omen: Data Buffer has exceeded its size limits. "
705  << "(seq_id=" << ev_counter()
706  << ", frags=" << dataBufferDepthFragments_ << "/" << maxDataBufferDepthFragments_
707  << ", szB=" << dataBufferDepthBytes_ << "/" << maxDataBufferDepthBytes_ << ")";
708  TLOG(TLVL_TRACE) << "Bad Omen: Possible causes include requests not getting through or Ignored-mode BR issues";
709  first = false;
710  }
711  if (waittime % 5 && waittime != lastwaittime)
712  {
713  TLOG(TLVL_WAITFORBUFFERREADY) << "getDataLoop: Data Retreival paused for " << waittime << " ms waiting for data buffer to drain";
714  }
715  lastwaittime = waittime;
716  usleep(1000);
717  }
718  else
719  {
720  std::unique_lock<std::mutex> lock(dataBufferMutex_);
721  getDataBufferStats(); // Re-check under lock
722  if (dataBufferIsTooLarge())
723  {
724  if (dataBuffer_.begin() == dataBuffer_.end())
725  {
726  TLOG(TLVL_WARNING) << "Data buffer is reported as too large, but doesn't contain any Fragments! Possible corrupt memory!";
727  continue;
728  }
729  if (*dataBuffer_.begin())
730  {
731  TLOG(TLVL_WAITFORBUFFERREADY) << "waitForDataBufferReady: Dropping Fragment with timestamp " << (*dataBuffer_.begin())->timestamp() << " from data buffer (Buffer over-size, circular data buffer mode)";
732  }
733  dataBufferDepthBytes_ -= (*dataBuffer_.begin())->sizeBytes();
734  dataBuffer_.erase(dataBuffer_.begin());
735  getDataBufferStats();
736  }
737 
738  }
739  }
740  return true;
741 }
742 
744 {
745  return (maxDataBufferDepthFragments_ > 0 && dataBufferDepthFragments_ > maxDataBufferDepthFragments_) || (maxDataBufferDepthBytes_ > 0 && dataBufferDepthBytes_ > maxDataBufferDepthBytes_);
746 }
747 
749 {
751  dataBufferDepthFragments_ = dataBuffer_.size();
752 
753  if (metricMan)
754  {
755  TLOG(TLVL_GETBUFFERSTATS) << "getDataBufferStats: Sending Metrics";
756  metricMan->sendMetric("Buffer Depth Fragments", dataBufferDepthFragments_.load(), "fragments", 1, MetricMode::LastPoint);
757  metricMan->sendMetric("Buffer Depth Bytes", dataBufferDepthBytes_.load(), "bytes", 1, MetricMode::LastPoint);
758  }
759  TLOG(TLVL_GETBUFFERSTATS) << "getDataBufferStats: frags=" << dataBufferDepthFragments_.load() << "/" << maxDataBufferDepthFragments_
760  << ", sz=" << dataBufferDepthBytes_.load() << "/" << maxDataBufferDepthBytes_;
761 }
762 
764 {
765  std::unique_lock<std::mutex> lock(dataBufferMutex_);
766  dataCondition_.wait_for(lock, std::chrono::milliseconds(10));
767  if (dataBufferDepthFragments_ > 0)
768  {
769  if ((mode_ == RequestMode::Buffer || mode_ == RequestMode::Window))
770  {
771  // Eliminate extra fragments
772  getDataBufferStats();
773  while (dataBufferIsTooLarge())
774  {
775  TLOG(TLVL_CHECKDATABUFFER) << "checkDataBuffer: Dropping Fragment with timestamp " << (*dataBuffer_.begin())->timestamp() << " from data buffer (Buffer over-size)";
776  dataBufferDepthBytes_ -= (*dataBuffer_.begin())->sizeBytes();
777  dataBuffer_.erase(dataBuffer_.begin());
778  getDataBufferStats();
779  }
780  if (dataBuffer_.size() > 0)
781  {
782  TLOG(TLVL_CHECKDATABUFFER) << "Determining if Fragments can be dropped from data buffer";
783  Fragment::timestamp_t last = dataBuffer_.back()->timestamp();
784  Fragment::timestamp_t min = last > staleTimeout_ ? last - staleTimeout_ : 0;
785  for (auto it = dataBuffer_.begin(); it != dataBuffer_.end();)
786  {
787  if ((*it)->timestamp() < min)
788  {
789  TLOG(TLVL_CHECKDATABUFFER) << "checkDataBuffer: Dropping Fragment with timestamp " << (*it)->timestamp() << " from data buffer (timeout=" << staleTimeout_ << ", min=" << min << ")";
790  dataBufferDepthBytes_ -= (*it)->sizeBytes();
791  it = dataBuffer_.erase(it);
792  }
793  else
794  {
795  break;
796  }
797  }
798  getDataBufferStats();
799  }
800  }
801  else if (mode_ == RequestMode::Single && dataBuffer_.size() > fragment_ids_.size())
802  {
803  // Eliminate extra fragments
804  while (dataBuffer_.size() > fragment_ids_.size())
805  {
806  dataBufferDepthBytes_ -= (*dataBuffer_.begin())->sizeBytes();
807  dataBuffer_.erase(dataBuffer_.begin());
808  }
809  }
810  }
811 }
812 
814 {
815  while (!force_stop_)
816  {
817  if (should_stop() || monitoringInterval_ <= 0)
818  {
819  TLOG(TLVL_DEBUG) << "getMonitoringDataLoop: should_stop() is " << std::boolalpha << should_stop()
820  << " and monitoringInterval is " << monitoringInterval_ << ", returning";
821  return;
822  }
823  TLOG(TLVL_GETMONITORINGDATA) << "getMonitoringDataLoop: Determining whether to call checkHWStatus_";
824 
825  auto now = std::chrono::steady_clock::now();
826  if (TimeUtils::GetElapsedTimeMicroseconds(lastMonitoringCall_, now) >= static_cast<size_t>(monitoringInterval_))
827  {
828  isHardwareOK_ = checkHWStatus_();
829  TLOG(TLVL_GETMONITORINGDATA) << "getMonitoringDataLoop: isHardwareOK_ is now " << std::boolalpha << isHardwareOK_;
830  lastMonitoringCall_ = now;
831  }
832  usleep(monitoringInterval_ / 10);
833  }
834 }
835 
837 {
838  // We just copy everything that's here into the output.
839  TLOG(TLVL_APPLYREQUESTS) << "Mode is Ignored; Copying data to output";
840  std::move(dataBuffer_.begin(), dataBuffer_.end(), std::inserter(frags, frags.end()));
841  dataBufferDepthBytes_ = 0;
842  dataBuffer_.clear();
843 }
844 
846 {
847  // We only care about the latest request received. Send empties for all others.
848  auto requests = requestReceiver_->GetRequests();
849  while (requests.size() > 1)
850  {
851  // std::map is ordered by key => Last sequence ID in the map is the one we care about
852  requestReceiver_->RemoveRequest(requests.begin()->first);
853  requests.erase(requests.begin());
854  }
855  sendEmptyFragments(frags, requests);
856 
857  // If no requests remain after sendEmptyFragments, return
858  if (requests.size() == 0 || !requests.count(ev_counter())) return;
859 
860  if (dataBuffer_.size() > 0)
861  {
862  TLOG(TLVL_APPLYREQUESTS) << "Mode is Single; Sending copy of last event";
863  for (auto& fragptr : dataBuffer_)
864  {
865  // Return the latest data point
866  auto frag = fragptr.get();
867  auto newfrag = std::unique_ptr<artdaq::Fragment>(new Fragment(ev_counter(), frag->fragmentID()));
868  newfrag->resize(frag->size() - detail::RawFragmentHeader::num_words());
869  memcpy(newfrag->headerAddress(), frag->headerAddress(), frag->sizeBytes());
870  newfrag->setTimestamp(requests[ev_counter()]);
871  newfrag->setSequenceID(ev_counter());
872  frags.push_back(std::move(newfrag));
873  }
874  }
875  else
876  {
877  sendEmptyFragment(frags, ev_counter(), "No data for");
878  }
879  requestReceiver_->RemoveRequest(ev_counter());
880  ev_counter_inc(1, true);
881 }
882 
884 {
885  // We only care about the latest request received. Send empties for all others.
886  auto requests = requestReceiver_->GetRequests();
887  while (requests.size() > 1)
888  {
889  // std::map is ordered by key => Last sequence ID in the map is the one we care about
890  requestReceiver_->RemoveRequest(requests.begin()->first);
891  requests.erase(requests.begin());
892  }
893  sendEmptyFragments(frags, requests);
894 
895  // If no requests remain after sendEmptyFragments, return
896  if (requests.size() == 0 || !requests.count(ev_counter())) return;
897 
898  TLOG(TLVL_DEBUG) << "Creating ContainerFragment for Buffered Fragments";
899  frags.emplace_back(new artdaq::Fragment(ev_counter(), fragment_id()));
900  frags.back()->setTimestamp(requests[ev_counter()]);
901  ContainerFragmentLoader cfl(*frags.back());
902  cfl.set_missing_data(false); // Buffer mode is never missing data, even if there IS no data.
903 
904  // Buffer mode TFGs should simply copy out the whole dataBuffer_ into a ContainerFragment
905  // Window mode TFGs must do a little bit more work to decide which fragments to send for a given request
906  for (auto it = dataBuffer_.begin(); it != dataBuffer_.end();)
907  {
908  TLOG(TLVL_APPLYREQUESTS) << "ApplyRequests: Adding Fragment with timestamp " << (*it)->timestamp() << " to Container with sequence ID " << ev_counter();
909  cfl.addFragment(*it);
910  dataBufferDepthBytes_ -= (*it)->sizeBytes();
911  it = dataBuffer_.erase(it);
912  }
913  requestReceiver_->RemoveRequest(ev_counter());
914  ev_counter_inc(1, true);
915 }
916 
918 {
919  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsWindowMode BEGIN";
920 
921  auto requests = requestReceiver_->GetRequests();
922 
923  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsWindowMode: Starting request processing";
924  for (auto req = requests.begin(); req != requests.end();)
925  {
926  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsWindowMode: processing request with sequence ID " << req->first << ", timestamp " << req->second;
927 
928 
929  while (req->first < ev_counter() && requests.size() > 0)
930  {
931  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsWindowMode: Clearing passed request for sequence ID " << req->first;
932  requestReceiver_->RemoveRequest(req->first);
933  req = requests.erase(req);
934  }
935  if (requests.size() == 0) break;
936 
937  auto ts = req->second;
938  TLOG(TLVL_APPLYREQUESTS) << "applyRequests: Checking that data exists for request window " << req->first;
939  Fragment::timestamp_t min = ts > windowOffset_ ? ts - windowOffset_ : 0;
940  Fragment::timestamp_t max = min + windowWidth_;
941  TLOG(TLVL_APPLYREQUESTS) << "ApplyRequests: min is " << min << ", max is " << max
942  << " and first/last points in buffer are " << (dataBuffer_.size() > 0 ? dataBuffer_.front()->timestamp() : 0)
943  << "/" << (dataBuffer_.size() > 0 ? dataBuffer_.back()->timestamp() : 0)
944  << " (sz=" << dataBuffer_.size() << " [" << dataBufferDepthBytes_.load()
945  << "/" << maxDataBufferDepthBytes_ << "])";
946  bool windowClosed = dataBuffer_.size() > 0 && dataBuffer_.back()->timestamp() >= max;
947  bool windowTimeout = !windowClosed && TimeUtils::GetElapsedTimeMicroseconds(requestReceiver_->GetRequestTime(req->first)) > window_close_timeout_us_;
948  if (windowTimeout)
949  {
950  TLOG(TLVL_WARNING) << "applyRequests: A timeout occurred waiting for data to close the request window ({" << min << "-" << max
951  << "}, buffer={" << (dataBuffer_.size() > 0 ? dataBuffer_.front()->timestamp() : 0) << "-"
952  << (dataBuffer_.size() > 0 ? dataBuffer_.back()->timestamp() : 0)
953  << "} ). Time waiting: "
954  << TimeUtils::GetElapsedTimeMicroseconds(requestReceiver_->GetRequestTime(req->first)) << " us "
955  << "(> " << window_close_timeout_us_ << " us).";
956  }
957  if (windowClosed || !data_thread_running_ || windowTimeout)
958  {
959  TLOG(TLVL_DEBUG) << "applyRequests: Creating ContainerFragment for Window-requested Fragments";
960  frags.emplace_back(new artdaq::Fragment(req->first, fragment_id()));
961  frags.back()->setTimestamp(ts);
962  ContainerFragmentLoader cfl(*frags.back());
963 
964  // In the spirit of NOvA's MegaPool: (RS = Request start (min), RE = Request End (max))
965  // --- | Buffer Start | --- | Buffer End | ---
966  //1. RS RE | | | |
967  //2. RS | | RE | |
968  //3. RS | | | | RE
969  //4. | | RS RE | |
970  //5. | | RS | | RE
971  //6. | | | | RS RE
972  //
973  // 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.
974  // 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
975  // If the dataBuffer has size 0, then windowClosed will be false
976  if (!windowClosed || (dataBuffer_.size() > 0 && dataBuffer_.front()->timestamp() > min))
977  {
978  TLOG(TLVL_DEBUG) << "applyRequests: Request window starts before and/or ends after the current data buffer, setting ContainerFragment's missing_data flag!"
979  << " (requestWindowRange=[" << min << "," << max << "], "
980  << "buffer={" << (dataBuffer_.size() > 0 ? dataBuffer_.front()->timestamp() : 0) << "-"
981  << (dataBuffer_.size() > 0 ? dataBuffer_.back()->timestamp() : 0) << "}";
982  cfl.set_missing_data(true);
983  }
984 
985  // Buffer mode TFGs should simply copy out the whole dataBuffer_ into a ContainerFragment
986  // Window mode TFGs must do a little bit more work to decide which fragments to send for a given request
987  int fragCount = 0;
988  for (auto it = dataBuffer_.begin(); it != dataBuffer_.end();)
989  {
990  Fragment::timestamp_t fragT = (*it)->timestamp();
991  if (fragT < min || fragT > max || (fragT == max && windowWidth_ > 0))
992  {
993  ++it;
994  continue;
995  }
996 
997  TLOG(TLVL_APPLYREQUESTS) << "applyRequests: Adding (" << (++fragCount) << "th) Fragment with timestamp "
998  << (*it)->timestamp() << " and sizeBytes " << (*it)->sizeBytes()
999  << " to Container for sequence ID " << req->first;
1000  cfl.addFragment(*it);
1001 
1002  if (uniqueWindows_)
1003  {
1004  dataBufferDepthBytes_ -= (*it)->sizeBytes();
1005  it = dataBuffer_.erase(it);
1006  }
1007  else
1008  {
1009  ++it;
1010  }
1011  }
1012  requestReceiver_->RemoveRequest(req->first);
1013  checkOutOfOrderWindows(req->first);
1014  requestReceiver_->RemoveRequest(req->first);
1015  req = requests.erase(req);
1016  }
1017  else
1018  {
1019  ++req;
1020  }
1021  }
1022 }
1023 
1025 {
1026  if (check_stop() || exception())
1027  {
1028  return false;
1029  }
1030 
1031  // Wait for data, if in ignored mode, or a request otherwise
1032  if (mode_ == RequestMode::Ignored)
1033  {
1034  while (dataBufferDepthFragments_ <= 0)
1035  {
1036  if (check_stop() || exception() || !isHardwareOK_) return false;
1037  std::unique_lock<std::mutex> lock(dataBufferMutex_);
1038  dataCondition_.wait_for(lock, std::chrono::milliseconds(10), [this]() { return dataBufferDepthFragments_ > 0; });
1039  }
1040  }
1041  else
1042  {
1043  if ((check_stop() && requestReceiver_->size() == 0) || exception()) return false;
1044  checkDataBuffer();
1045 
1046  // Wait up to 1000 ms for a request...
1047  auto counter = 0;
1048 
1049  while (requestReceiver_->size() == 0 && counter < 100)
1050  {
1051  if (check_stop() || exception()) return false;
1052 
1053  checkDataBuffer();
1054 
1055  requestReceiver_->WaitForRequests(10); // milliseconds
1056  counter++;
1057  }
1058  }
1059 
1060  {
1061  std::unique_lock<std::mutex> dlk(dataBufferMutex_);
1062 
1063  switch (mode_)
1064  {
1065  case RequestMode::Single:
1066  applyRequestsSingleMode(frags);
1067  break;
1068  case RequestMode::Window:
1069  applyRequestsWindowMode(frags);
1070  break;
1071  case RequestMode::Buffer:
1072  applyRequestsBufferMode(frags);
1073  break;
1074  case RequestMode::Ignored:
1075  default:
1076  applyRequestsIgnoredMode(frags);
1077  break;
1078  }
1079 
1080  if (!data_thread_running_ || force_stop_)
1081  {
1082  TLOG(TLVL_INFO) << "Data thread has stopped; Clearing data buffer";
1083  dataBufferDepthBytes_ = 0;
1084  dataBuffer_.clear();
1085  }
1086 
1087  getDataBufferStats();
1088  }
1089 
1090  if (frags.size() > 0)
1091  TLOG(TLVL_APPLYREQUESTS) << "Finished Processing Event " << (*frags.begin())->sequenceID() << " for fragment_id " << fragment_id() << ".";
1092  return true;
1093 }
1094 
1095 bool artdaq::CommandableFragmentGenerator::sendEmptyFragment(artdaq::FragmentPtrs& frags, size_t seqId, std::string desc)
1096 {
1097  TLOG(TLVL_WARNING) << desc << " sequence ID " << seqId << ", sending empty fragment";
1098  for (auto fid : fragment_ids_)
1099  {
1100  auto frag = new Fragment();
1101  frag->setSequenceID(seqId);
1102  frag->setFragmentID(fid);
1103  frag->setSystemType(Fragment::EmptyFragmentType);
1104  frags.emplace_back(FragmentPtr(frag));
1105  }
1106  return true;
1107 }
1108 
1109 void artdaq::CommandableFragmentGenerator::sendEmptyFragments(artdaq::FragmentPtrs& frags, std::map<Fragment::sequence_id_t, Fragment::timestamp_t>& requests)
1110 {
1111  if (requests.size() > 0)
1112  {
1113  TLOG(TLVL_SENDEMPTYFRAGMENTS) << "Sending Empty Fragments for Sequence IDs from " << ev_counter() << " up to but not including " << requests.begin()->first;
1114  while (requests.begin()->first > ev_counter())
1115  {
1116  sendEmptyFragment(frags, ev_counter(), "Missed request for");
1117  ev_counter_inc(1, true);
1118  }
1119  }
1120 }
1121 
1122 void artdaq::CommandableFragmentGenerator::checkOutOfOrderWindows(artdaq::Fragment::sequence_id_t seq)
1123 {
1124  windows_sent_ooo_[seq] = std::chrono::steady_clock::now();
1125 
1126  auto it = windows_sent_ooo_.begin();
1127  while (it != windows_sent_ooo_.end())
1128  {
1129  if (seq == it->first && it->first == ev_counter())
1130  {
1131  TLOG(TLVL_CHECKWINDOWS) << "checkOutOfOrderWindows: Sequence ID matches ev_counter, incrementing ev_counter (" << ev_counter() << ")";
1132  ev_counter_inc(1, true);
1133  it = windows_sent_ooo_.erase(it);
1134  }
1135  else if (it->first <= ev_counter())
1136  {
1137  TLOG(TLVL_CHECKWINDOWS) << "checkOutOfOrderWindows: Data-taking has caught up to out-of-order window request " << it->first << ", removing from list. ev_counter=" << ev_counter();
1138  requestReceiver_->RemoveRequest(ev_counter());
1139  if (it->first == ev_counter()) ev_counter_inc(1, true);
1140  it = windows_sent_ooo_.erase(it);
1141  }
1142  else if (TimeUtils::GetElapsedTimeMicroseconds(it->second) > missing_request_window_timeout_us_)
1143  {
1144  TLOG(TLVL_CHECKWINDOWS) << "checkOutOfOrderWindows: Out-of-order window " << it->first << " has timed out, setting current sequence ID and removing from list";
1145  while (ev_counter() <= it->first)
1146  {
1147  if (ev_counter() < it->first) TLOG(TLVL_WARNING) << "Missed request for sequence ID " << ev_counter() << "! Will not send any data for this sequence ID!";
1148  requestReceiver_->RemoveRequest(ev_counter());
1149  ev_counter_inc(1, true);
1150  }
1151  windows_sent_ooo_.erase(windows_sent_ooo_.begin(), it);
1152  it = windows_sent_ooo_.erase(it);
1153  }
1154  else
1155  {
1156  TLOG(TLVL_CHECKWINDOWS) << "checkOutOfOrderWindows: Out-of-order window " << it->first << " waiting. Current event counter = " << ev_counter();
1157  ++it;
1158  }
1159  }
1160 }
1161 
int fragment_id() const
Get the current Fragment ID, if there is only one.
void applyRequestsSingleMode(artdaq::FragmentPtrs &frags)
Create fragments using data buffer for request mode Single. Precondition: dataBufferMutex_ and reques...
virtual bool checkHWStatus_()
Check any relavent hardware status registers. Return false if an error condition exists that should h...
virtual ~CommandableFragmentGenerator()
CommandableFragmentGenerator Destructor.
void applyRequestsBufferMode(artdaq::FragmentPtrs &frags)
Create fragments using data buffer for request mode Buffer. Precondition: dataBufferMutex_ and reques...
bool sendEmptyFragment(FragmentPtrs &frags, size_t sequenceId, std::string desc)
Send an EmptyFragmentType Fragment.
void getMonitoringDataLoop()
This function regularly calls checkHWStatus_(), and sets the isHardwareOK flag accordingly.
void startDataThread()
Function that launches the data thread (getDataLoop())
std::string ReportCmd(std::string const &which="")
Get a report about a user-specified run-time quantity.
virtual bool metaCommand(std::string const &command, std::string const &arg)
The meta-command is used for implementing user-specific commands in a CommandableFragmentGenerator.
bool dataBufferIsTooLarge()
Test the configured constraints on the data buffer.
void StopCmd(uint64_t timeout, uint64_t timestamp)
Stop the CommandableFragmentGenerator.
void applyRequestsWindowMode(artdaq::FragmentPtrs &frags)
Create fragments using data buffer for request mode Window. Precondition: dataBufferMutex_ and reques...
void StartCmd(int run, uint64_t timeout, uint64_t timestamp)
Start the CommandableFragmentGenerator.
virtual void pauseNoMutex()
On call to PauseCmd, pauseNoMutex() is called prior to PauseCmd acquiring the mutex ...
bool check_stop()
Routine used by applyRequests to make sure that all outstanding requests have been fulfilled before r...
void ResumeCmd(uint64_t timeout, uint64_t timestamp)
Resume the CommandableFragmentGenerator.
CommandableFragmentGenerator()
CommandableFragmentGenerator default constructor.
bool getNext(FragmentPtrs &output) overridefinal
getNext calls either applyRequests or getNext_ to get any data that is ready to be sent to the EventB...
bool waitForDataBufferReady()
Wait for the data buffer to drain (dataBufferIsTooLarge returns false), periodically reporting status...
size_t ev_counter_inc(size_t step=1, bool force=false)
Increment the event counter, if the current RequestMode allows it.
bool applyRequests(FragmentPtrs &frags)
See if any requests have been received, and add the corresponding data Fragment objects to the output...
void applyRequestsIgnoredMode(artdaq::FragmentPtrs &frags)
Create fragments using data buffer for request mode Ignored. Precondition: dataBufferMutex_ and reque...
void PauseCmd(uint64_t timeout, uint64_t timestamp)
Pause the CommandableFragmentGenerator.
void getDataLoop()
When separate_data_thread is set to true, this loop repeatedly calls getNext_ and adds returned Fragm...
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 ...
Receive data requests and make them available to CommandableFragmentGenerator or other interested par...
void startMonitoringThread()
Function that launches the monitoring thread (getMonitoringDataLoop())
virtual void pause()
If a CommandableFragmentGenerator subclass is reading from hardware, the implementation of pause() sh...
virtual void resume()
The subrun number will be incremented before a call to resume.
void checkDataBuffer()
Perform data buffer pruning operations. If the RequestMode is Single, removes all but the latest Frag...
virtual std::string report()
Let&#39;s say that the contract with the report() functions is that they return a non-empty string if the...
std::string printMode_()
Return the string representation of the current RequestMode.
void getDataBufferStats()
Calculate the size of the dataBuffer and report appropriate metrics.
void checkOutOfOrderWindows(Fragment::sequence_id_t seq)
Check the windows_sent_ooo_ map for sequence IDs that may be removed.
virtual std::string reportSpecific(std::string const &what)
Report the status of a specific quantity
void joinThreads()
Join any data-taking threads. Should be called when destructing CommandableFragmentGenerator.