artdaq  v3_00_01
CommandableFragmentGenerator.cc
1 #define TRACE_NAME "CommandableFragmentGenerator"
2 #include "tracemf.h"
3 
4 #include "artdaq/Application/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 
32  : mutex_()
33  , request_port_(3001)
34  , request_addr_("227.128.12.26")
35  , requests_()
36  , request_stop_requested_(false)
37  , request_received_(false)
38  , end_of_run_timeout_ms_(1000)
39  , windowOffset_(0)
40  , windowWidth_(0)
41  , staleTimeout_(Fragment::InvalidTimestamp)
42  , expectedType_(Fragment::EmptyFragmentType)
43  , maxFragmentCount_(std::numeric_limits<size_t>::max())
44  , uniqueWindows_(true)
45  , missing_request_(true)
46  , missing_request_time_()
47  , last_window_send_time_()
48  , last_window_send_time_set_(false)
49  , missing_request_window_timeout_us_(1000000)
50  , window_close_timeout_us_(2000000)
51  , useDataThread_(false)
52  , sleep_on_no_data_us_(0)
53  , data_thread_running_(false)
54  , dataBufferDepthFragments_(0)
55  , dataBufferDepthBytes_(0)
56  , maxDataBufferDepthFragments_(1000)
57  , maxDataBufferDepthBytes_(1000)
58  , useMonitoringThread_(false)
59  , monitoringInterval_(0)
60  , lastMonitoringCall_()
61  , isHardwareOK_(true)
62  , dataBuffer_()
63  , newDataBuffer_()
64  , run_number_(-1)
65  , subrun_number_(-1)
66  , timeout_(std::numeric_limits<uint64_t>::max())
67  , timestamp_(std::numeric_limits<uint64_t>::max())
68  , should_stop_(false)
69  , exception_(false)
70  , force_stop_(false)
71  , latest_exception_report_("none")
72  , ev_counter_(1)
73  , board_id_(-1)
74  , instance_name_for_metrics_("FragmentGenerator")
75  , sleep_on_stop_us_(0)
76 {}
77 
79  : mutex_()
80  , request_port_(ps.get<int>("request_port", 3001))
81  , request_addr_(ps.get<std::string>("request_address", "227.128.12.26"))
82  , requests_()
83  , request_stop_requested_(false)
84  , request_received_(false)
85  , end_of_run_timeout_ms_(ps.get<size_t>("end_of_run_quiet_timeout_ms", 1000))
86  , windowOffset_(ps.get<Fragment::timestamp_t>("request_window_offset", 0))
87  , windowWidth_(ps.get<Fragment::timestamp_t>("request_window_width", 0))
88  , staleTimeout_(ps.get<Fragment::timestamp_t>("stale_request_timeout", 0xFFFFFFFF))
89  , expectedType_(ps.get<Fragment::type_t>("expected_fragment_type", Fragment::type_t(Fragment::EmptyFragmentType)))
90  , uniqueWindows_(ps.get<bool>("request_windows_are_unique", true))
91  , missing_request_(false)
92  , missing_request_time_(decltype(missing_request_time_)::max())
93  , last_window_send_time_(decltype(last_window_send_time_)::max())
94  , last_window_send_time_set_(false)
95  , missing_request_window_timeout_us_(ps.get<size_t>("missing_request_window_timeout_us", 1000000))
96  , window_close_timeout_us_(ps.get<size_t>("window_close_timeout_us", 2000000))
97  , useDataThread_(ps.get<bool>("separate_data_thread", false))
98  , sleep_on_no_data_us_(ps.get<size_t>("sleep_on_no_data_us", 0))
99  , data_thread_running_(false)
100  , dataBufferDepthFragments_(0)
101  , dataBufferDepthBytes_(0)
102  , maxDataBufferDepthFragments_(ps.get<int>("data_buffer_depth_fragments", 1000))
103  , maxDataBufferDepthBytes_(ps.get<size_t>("data_buffer_depth_mb", 1000) * 1024 * 1024)
104  , useMonitoringThread_(ps.get<bool>("separate_monitoring_thread", false))
105  , monitoringInterval_(ps.get<int64_t>("hardware_poll_interval_us", 0))
106  , lastMonitoringCall_()
107  , isHardwareOK_(true)
108  , dataBuffer_()
109  , newDataBuffer_()
110  , run_number_(-1)
111  , subrun_number_(-1)
112  , timeout_(std::numeric_limits<uint64_t>::max())
113  , timestamp_(std::numeric_limits<uint64_t>::max())
114  , should_stop_(false)
115  , exception_(false)
116  , force_stop_(false)
117  , latest_exception_report_("none")
118  , ev_counter_(1)
119  , board_id_(-1)
120  , sleep_on_stop_us_(0)
121 {
122  board_id_ = ps.get<int>("board_id");
123  instance_name_for_metrics_ = "BoardReader." + boost::lexical_cast<std::string>(board_id_);
124 
125  fragment_ids_ = ps.get<std::vector<artdaq::Fragment::fragment_id_t>>("fragment_ids", std::vector<artdaq::Fragment::fragment_id_t>());
126 
127  TLOG_TRACE("CommandableFragmentGenerator") << "artdaq::CommandableFragmentGenerator::CommandableFragmentGenerator(ps)" << TLOG_ENDL;
128  int fragment_id = ps.get<int>("fragment_id", -99);
129 
130  if (fragment_id != -99)
131  {
132  if (fragment_ids_.size() != 0)
133  {
134  latest_exception_report_ = "Error in CommandableFragmentGenerator: can't both define \"fragment_id\" and \"fragment_ids\" in FHiCL document";
135  throw cet::exception(latest_exception_report_);
136  }
137  else
138  {
139  fragment_ids_.emplace_back(fragment_id);
140  }
141  }
142 
143  sleep_on_stop_us_ = ps.get<int>("sleep_on_stop_us", 0);
144 
145  dataBuffer_.emplace_back(FragmentPtr(new Fragment()));
146  (*dataBuffer_.begin())->setSystemType(Fragment::EmptyFragmentType);
147 
148  std::string modeString = ps.get<std::string>("request_mode", "ignored");
149  if (modeString == "single" || modeString == "Single")
150  {
151  mode_ = RequestMode::Single;
152  }
153  else if (modeString.find("buffer") != std::string::npos || modeString.find("Buffer") != std::string::npos)
154  {
155  mode_ = RequestMode::Buffer;
156  }
157  else if (modeString == "window" || modeString == "Window")
158  {
159  mode_ = RequestMode::Window;
160  }
161  else if (modeString.find("ignore") != std::string::npos || modeString.find("Ignore") != std::string::npos)
162  {
163  mode_ = RequestMode::Ignored;
164  }
165  TLOG_DEBUG("CommandableFragmentGenerator") << "Request mode is " << printMode_() << TLOG_ENDL;
166 
167  if (mode_ != RequestMode::Ignored)
168  {
169  if (!useDataThread_)
170  {
171  latest_exception_report_ = "Error in CommandableFragmentGenerator: use_data_thread must be true when request_mode is not \"Ignored\"!";
172  throw cet::exception(latest_exception_report_);
173  }
175  }
176 }
177 
179 {
180  request_socket_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
181  if (request_socket_ < 0)
182  {
183  throw art::Exception(art::errors::Configuration) << "CommandableFragmentGenerator: Error creating socket for receiving data requests! err=" << strerror(errno) << std::endl;
184  exit(1);
185  }
186 
187  struct sockaddr_in si_me_request;
188 
189  int yes = 1;
190  if (setsockopt(request_socket_, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
191  {
192  throw art::Exception(art::errors::Configuration) <<
193  "RequestedFragmentGenrator: Unable to enable port reuse on request socket, err=" << strerror(errno) << std::endl;
194  exit(1);
195  }
196  memset(&si_me_request, 0, sizeof(si_me_request));
197  si_me_request.sin_family = AF_INET;
198  si_me_request.sin_port = htons(request_port_);
199  si_me_request.sin_addr.s_addr = htonl(INADDR_ANY);
200  if (bind(request_socket_, (struct sockaddr *)&si_me_request, sizeof(si_me_request)) == -1)
201  {
202  throw art::Exception(art::errors::Configuration) <<
203  "CommandableFragmentGenerator: Cannot bind request socket to port " << request_port_ << ", err=" << strerror(errno) << std::endl;
204  exit(1);
205  }
206 
207  if (request_addr_ != "localhost")
208  {
209  struct ip_mreq mreq;
210  int sts = ResolveHost(request_addr_.c_str(), mreq.imr_multiaddr);
211  if (sts == -1)
212  {
213  throw art::Exception(art::errors::Configuration) << "Unable to resolve multicast request address, err=" << strerror(errno) << std::endl;
214  exit(1);
215  }
216  mreq.imr_interface.s_addr = htonl(INADDR_ANY);
217  if (setsockopt(request_socket_, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
218  {
219  throw art::Exception(art::errors::Configuration) <<
220  "CommandableFragmentGenerator: Unable to join multicast group, err=" << strerror(errno) << std::endl;
221  exit(1);
222  }
223  }
224 }
225 
227 {
228  joinThreads();
229 }
230 
232 {
233  should_stop_ = true;
234  force_stop_ = true;
235  TLOG_DEBUG("CommandableFragmentGenerator") << "Joining dataThread" << TLOG_ENDL;
236  if (dataThread_.joinable()) dataThread_.join();
237  TLOG_DEBUG("CommandableFragmentGenerator") << "Joining monitoringThread" << TLOG_ENDL;
238  if (monitoringThread_.joinable()) monitoringThread_.join();
239  TLOG_DEBUG("CommandableFragmentGenerator") << "Joining requestThread" << TLOG_ENDL;
240  if (requestThread_.joinable()) requestThread_.join();
241  if (request_socket_ != -1) close(request_socket_);
242 }
243 
245 {
246  bool result = true;
247 
248  if (check_stop()) usleep(sleep_on_stop_us_);
249  if (exception() || force_stop_) return false;
250 
251  if (!useMonitoringThread_ && monitoringInterval_ > 0)
252  {
253  TLOG_ARB(10, "CommandableFragmentGenerator") << "getNext: Checking whether to collect Monitoring Data" << TLOG_ENDL;
254  auto now = std::chrono::steady_clock::now();
255 
256  if (TimeUtils::GetElapsedTimeMicroseconds(lastMonitoringCall_, now) >= static_cast<size_t>(monitoringInterval_))
257  {
258  TLOG_ARB(10, "CommandableFragmentGenerator") << "getNext: Collecting Monitoring Data" << TLOG_ENDL;
259  isHardwareOK_ = checkHWStatus_();
260  TLOG_ARB(10, "CommandableFragmentGenerator") << "getNext: isHardwareOK_ is now " << std::boolalpha << isHardwareOK_ << TLOG_ENDL;
261  lastMonitoringCall_ = now;
262  }
263  }
264 
265  try
266  {
267  std::lock_guard<std::mutex> lk(mutex_);
268  if (useDataThread_)
269  {
270  TLOG_TRACE("CommandableFragmentGenerator") << "getNext: Calling applyRequests" << TLOG_ENDL;
271  result = applyRequests(output);
272  TLOG_TRACE("CommandableFragmentGenerator") << "getNext: Done with applyRequests" << TLOG_ENDL;
273 
274  if (exception())
275  {
276  throw cet::exception("CommandableFragmentGenerator") << "Exception found in BoardReader with board ID " << board_id() << "; BoardReader will now return error status when queried";
277  }
278  }
279  else
280  {
281  if (!isHardwareOK_)
282  {
283  TLOG_ERROR("CommandableFragmentGenerator") << "Stopping CFG because the hardware reports bad status!" << TLOG_ENDL;
284  return false;
285  }
286  TLOG_TRACE("CommandableFragmentGenerator") << "getNext: Calling getNext_ " << std::to_string(ev_counter()) << TLOG_ENDL;
287  try
288  {
289  result = getNext_(output);
290  }
291  catch (...)
292  {
293  throw;
294  }
295  TLOG_TRACE("CommandableFragmentGenerator") << "getNext: Done with getNext_ " << std::to_string(ev_counter()) << TLOG_ENDL;
296  }
297  }
298  catch (const cet::exception& e)
299  {
300  latest_exception_report_ = "cet::exception caught in getNext(): ";
301  latest_exception_report_.append(e.what());
302  TLOG_ERROR("CommandableFragmentGenerator") << "getNext: cet::exception caught: " << e << TLOG_ENDL;
303  set_exception(true);
304  return false;
305  }
306  catch (const boost::exception& e)
307  {
308  latest_exception_report_ = "boost::exception caught in getNext(): ";
309  latest_exception_report_.append(boost::diagnostic_information(e));
310  TLOG_ERROR("CommandableFragmentGenerator") << "getNext: boost::exception caught: " << boost::diagnostic_information(e) << TLOG_ENDL;
311  set_exception(true);
312  return false;
313  }
314  catch (const std::exception& e)
315  {
316  latest_exception_report_ = "std::exception caught in getNext(): ";
317  latest_exception_report_.append(e.what());
318  TLOG_ERROR("CommandableFragmentGenerator") << "getNext: std::exception caught: " << e.what() << TLOG_ENDL;
319  set_exception(true);
320  return false;
321  }
322  catch (...)
323  {
324  latest_exception_report_ = "Unknown exception caught in getNext().";
325  TLOG_ERROR("CommandableFragmentGenerator") << "getNext: unknown exception caught" << TLOG_ENDL;
326  set_exception(true);
327  return false;
328  }
329 
330  if (!result)
331  {
332  TLOG_DEBUG("getNext") << "stopped " << TLOG_ENDL;
333  }
334 
335  return result;
336 }
337 
339 {
340  TLOG_ARB(14, "CommandableFragmentGeneraotr") << "CFG::check_stop: should_stop=" << should_stop() << ", useDataThread_=" << useDataThread_ << ", requests_.size()=" << std::to_string(requests_.size()) << ", exception status =" << int(exception()) << TLOG_ENDL;
341 
342  if (!should_stop()) return false;
343  if (!useDataThread_ || mode_ == RequestMode::Ignored) return true;
344  if (force_stop_) return true;
345 
346  if (!request_received_)
347  {
348  TLOG_ERROR("CommandableFragmentGenerator") << "Stop request received by request-based CommandableFragmentGenerator, but no requests have been received." << std::endl
349  << "Check that UDP port " << request_port_ << " is open in the firewall config." << TLOG_ENDL;
350  return true;
351  }
352 
353  if (!request_stop_requested_) return false;
354 
355  return TimeUtils::GetElapsedTimeMilliseconds(request_stop_timeout_) > end_of_run_timeout_ms_;// && requests_.size() == 0;
356 }
357 
359 {
360  if (fragment_ids_.size() != 1)
361  {
362  throw cet::exception("Error in CommandableFragmentGenerator: can't call fragment_id() unless member fragment_ids_ vector is length 1");
363  }
364  else
365  {
366  return fragment_ids_[0];
367  }
368 }
369 
371 {
372  if (force || mode_ == RequestMode::Ignored)
373  {
374  return ev_counter_.fetch_add(step);
375  }
376  return ev_counter_.load();
377 } // returns the prev value
378 
379 void artdaq::CommandableFragmentGenerator::StartCmd(int run, uint64_t timeout, uint64_t timestamp)
380 {
381  if (run < 0) throw cet::exception("CommandableFragmentGenerator") << "negative run number";
382 
383  timeout_ = timeout;
384  timestamp_ = timestamp;
385  ev_counter_.store(1);
386  should_stop_.store(false);
387  exception_.store(false);
388  run_number_ = run;
389  subrun_number_ = 1;
390  latest_exception_report_ = "none";
391  dataBuffer_.clear();
392  requests_.clear();
393  last_window_send_time_set_ = false;
394 
395  start();
396 
397  std::unique_lock<std::mutex> lk(mutex_);
398  if (useDataThread_) startDataThread();
399  if (useMonitoringThread_) startMonitoringThread();
400  if (mode_ != RequestMode::Ignored) startRequestReceiverThread();
401 }
402 
403 void artdaq::CommandableFragmentGenerator::StopCmd(uint64_t timeout, uint64_t timestamp)
404 {
405  TLOG_DEBUG("CommandableFragmentGenerator") << "Stop Command received." << TLOG_ENDL;
406 
407  timeout_ = timeout;
408  timestamp_ = timestamp;
409 
410  stopNoMutex();
411  should_stop_.store(true);
412  std::unique_lock<std::mutex> lk(mutex_);
413  stop();
414 }
415 
416 void artdaq::CommandableFragmentGenerator::PauseCmd(uint64_t timeout, uint64_t timestamp)
417 {
418  timeout_ = timeout;
419  timestamp_ = timestamp;
420 
421  pauseNoMutex();
422  should_stop_.store(true);
423  std::unique_lock<std::mutex> lk(mutex_);
424 
425  pause();
426 }
427 
428 void artdaq::CommandableFragmentGenerator::ResumeCmd(uint64_t timeout, uint64_t timestamp)
429 {
430  timeout_ = timeout;
431  timestamp_ = timestamp;
432 
433  subrun_number_ += 1;
434  should_stop_ = false;
435 
436  dataBuffer_.clear();
437  requests_.clear();
438 
439  // no lock required: thread not started yet
440  resume();
441 
442  std::unique_lock<std::mutex> lk(mutex_);
443  if (useDataThread_) startDataThread();
444  if (useMonitoringThread_) startMonitoringThread();
445  if (mode_ != RequestMode::Ignored) startRequestReceiverThread();
446 }
447 
448 std::string artdaq::CommandableFragmentGenerator::ReportCmd(std::string const& which)
449 {
450  std::lock_guard<std::mutex> lk(mutex_);
451 
452  // 14-May-2015, KAB: please see the comments associated with the report()
453  // methods in the CommandableFragmentGenerator.hh file for more information
454  // on the use of those methods in this method.
455 
456  // check if the child class has something meaningful for this request
457  std::string childReport = reportSpecific(which);
458  if (childReport.length() > 0) { return childReport; }
459 
460  // handle the requests that we can take care of at this level
461  if (which == "latest_exception")
462  {
463  return latest_exception_report_;
464  }
465 
466  // check if the child class has provided a catch-all report function
467  childReport = report();
468  if (childReport.length() > 0) { return childReport; }
469 
470  // if we haven't been able to come up with any report so far, say so
471  std::string tmpString = "The \"" + which + "\" command is not ";
472  tmpString.append("currently supported by the ");
473  tmpString.append(metricsReportingInstanceName());
474  tmpString.append(" fragment generator.");
475  return tmpString;
476 }
477 
478 // Default implemenetations of state functions
479 void artdaq::CommandableFragmentGenerator::pauseNoMutex()
480 {
481 #pragma message "Using default implementation of CommandableFragmentGenerator::pauseNoMutex()"
482 }
483 
484 void artdaq::CommandableFragmentGenerator::pause()
485 {
486 #pragma message "Using default implementation of CommandableFragmentGenerator::pause()"
487 }
488 
489 void artdaq::CommandableFragmentGenerator::resume()
490 {
491 #pragma message "Using default implementation of CommandableFragmentGenerator::resume()"
492 }
493 
494 std::string artdaq::CommandableFragmentGenerator::report()
495 {
496 #pragma message "Using default implementation of CommandableFragmentGenerator::report()"
497  return "";
498 }
499 
500 std::string artdaq::CommandableFragmentGenerator::reportSpecific(std::string const&)
501 {
502 #pragma message "Using default implementation of CommandableFragmentGenerator::reportSpecific(std::string)"
503  return "";
504 }
505 
506 bool artdaq::CommandableFragmentGenerator::checkHWStatus_()
507 {
508 #pragma message "Using default implementation of CommandableFragmentGenerator::checkHWStatus_()"
509  return true;
510 }
511 
513 {
514  if (dataThread_.joinable()) dataThread_.join();
515  TLOG_INFO("CommandableFragmentGenerator") << "Starting Data Receiver Thread" << TLOG_ENDL;
516  dataThread_ = boost::thread(&CommandableFragmentGenerator::getDataLoop, this);
517 }
518 
520 {
521  if (monitoringThread_.joinable()) monitoringThread_.join();
522  TLOG_INFO("CommandableFragmentGenerator") << "Starting Hardware Monitoring Thread" << TLOG_ENDL;
523  monitoringThread_ = boost::thread(&CommandableFragmentGenerator::getMonitoringDataLoop, this);
524 }
525 
527 {
528  if (requestThread_.joinable()) requestThread_.join();
529  TLOG_INFO("CommandableFragmentGenerator") << "Starting Request Reception Thread" << TLOG_ENDL;
530  requestThread_ = boost::thread(&CommandableFragmentGenerator::receiveRequestsLoop, this);
531 }
532 
534 {
535  switch (mode_)
536  {
537  case RequestMode::Single:
538  return "Single";
539  case RequestMode::Buffer:
540  return "Buffer";
541  case RequestMode::Window:
542  return "Window";
543  case RequestMode::Ignored:
544  return "Ignored";
545  }
546 
547  return "ERROR";
548 }
549 
551 {
552  data_thread_running_ = true;
553  while (!force_stop_)
554  {
555  if (!isHardwareOK_)
556  {
557  TLOG_DEBUG("CommandableFragmentGenerator") << "getDataLoop: isHardwareOK is " << isHardwareOK_ << ", aborting data thread" << TLOG_ENDL;
558  data_thread_running_ = false;
559  return;
560  }
561 
562  TLOG_ARB(13, "CommandableFragmentGenerator") << "getDataLoop: calling getNext_" << TLOG_ENDL;
563 
564  bool data = false;
565  auto startdata = std::chrono::steady_clock::now();
566 
567  try
568  {
569  data = getNext_(newDataBuffer_);
570  }
571  catch (...)
572  {
573  ExceptionHandler(ExceptionHandlerRethrow::no,
574  "Exception thrown by fragment generator in CommandableFragmentGenerator::getDataLoop; setting exception state to \"true\"");
575  set_exception(true);
576 
577  data_thread_running_ = false;
578  return;
579  }
580 
581  if (metricMan)
582  {
583  metricMan->sendMetric("Avg Data Acquisition Time", TimeUtils::GetElapsedTime(startdata), "s", 3, artdaq::MetricMode::Average);
584  }
585 
586  if (newDataBuffer_.size() == 0 && sleep_on_no_data_us_ > 0)
587  {
588  usleep(sleep_on_no_data_us_);
589  }
590 
591  TLOG_ARB(15, "CommandableFragmentGenerator") << "Waiting for data buffer ready" << TLOG_ENDL;
592  if (!waitForDataBufferReady()) return;
593  TLOG_ARB(15, "CommandableFragmentGenerator") << "Done waiting for data buffer ready" << TLOG_ENDL;
594 
595  TLOG_ARB(13, "CommandableFragmentGenerator") << "getDataLoop: processing data" << TLOG_ENDL;
596  if (data && !force_stop_)
597  {
598  std::unique_lock<std::mutex> lock(dataBufferMutex_);
599  switch (mode_)
600  {
601  case RequestMode::Single:
602  // While here, if for some strange reason more than one event's worth of data is returned from getNext_...
603  while (newDataBuffer_.size() >= fragment_ids_.size())
604  {
605  dataBuffer_.clear();
606  auto it = newDataBuffer_.begin();
607  std::advance(it, fragment_ids_.size());
608  dataBuffer_.splice(dataBuffer_.end(), newDataBuffer_, newDataBuffer_.begin(), it);
609  }
610  break;
611  case RequestMode::Buffer:
612  case RequestMode::Ignored:
613  case RequestMode::Window:
614  default:
615  //dataBuffer_.reserve(dataBuffer_.size() + newDataBuffer_.size());
616  dataBuffer_.splice(dataBuffer_.end(), newDataBuffer_);
617  break;
618  }
619  getDataBufferStats();
620  }
621 
622  {
623  std::unique_lock<std::mutex> lock(dataBufferMutex_);
624  if (dataBuffer_.size() > 0)
625  {
626  dataCondition_.notify_all();
627  }
628  }
629  if (!data || force_stop_)
630  {
631  TLOG_INFO("CommandableFragmentGenerator") << "Data flow has stopped. Ending data collection thread" << TLOG_ENDL;
632  data_thread_running_ = false;
633  return;
634  }
635  }
636 }
637 
639 {
640  auto startwait = std::chrono::steady_clock::now();
641  auto first = true;
642  auto lastwaittime = 0ULL;
643  while (dataBufferIsTooLarge())
644  {
645  if (should_stop())
646  {
647  TLOG_DEBUG("CommandableFragmentGenerator") << "Run ended while waiting for buffer to shrink!" << TLOG_ENDL;
648  std::unique_lock<std::mutex> lock(dataBufferMutex_);
649  getDataBufferStats();
650  dataCondition_.notify_all();
651  data_thread_running_ = false;
652  return false;
653  }
654  auto waittime = TimeUtils::GetElapsedTimeMilliseconds(startwait);
655 
656  if (first || (waittime != lastwaittime && waittime % 1000 == 0))
657  {
658  TLOG_WARNING("CommandableFragmentGenerator") << "Bad Omen: Data Buffer has exceeded its size limits. Check the connection between the BoardReader and the EventBuilders! (seq_id=" << ev_counter() << ")" << TLOG_ENDL;
659  first = false;
660  }
661  if (waittime % 5 && waittime != lastwaittime)
662  {
663  TLOG_ARB(13, "CommandableFragmentGenerator") << "getDataLoop: Data Retreival paused for " << std::to_string(waittime) << " ms waiting for data buffer to drain" << TLOG_ENDL;
664  }
665  lastwaittime = waittime;
666  usleep(1000);
667  }
668  return true;
669 }
670 
672 {
673  return (maxDataBufferDepthFragments_ > 0 && dataBufferDepthFragments_ > maxDataBufferDepthFragments_) || (maxDataBufferDepthBytes_ > 0 && dataBufferDepthBytes_ > maxDataBufferDepthBytes_);
674 }
675 
677 {
679  dataBufferDepthFragments_ = dataBuffer_.size();
680  size_t acc = 0;
681  TLOG_ARB(15, "CommandableFragmentGenerator") << "getDataBufferStats: Calculating buffer size" << TLOG_ENDL;
682  for (auto i = dataBuffer_.begin(); i != dataBuffer_.end(); ++i)
683  {
684  if (i->get() != nullptr)
685  {
686  acc += (*i)->sizeBytes();
687  }
688  }
689  dataBufferDepthBytes_ = acc;
690 
691  if (metricMan)
692  {
693  TLOG_ARB(15, "CommandableFragmentGenerator") << "getDataBufferStats: Sending Metrics" << TLOG_ENDL;
694  metricMan->sendMetric("Buffer Depth Fragments", dataBufferDepthFragments_.load(), "fragments", 1, MetricMode::LastPoint);
695  metricMan->sendMetric("Buffer Depth Bytes", dataBufferDepthBytes_.load(), "bytes", 1, MetricMode::LastPoint);
696  }
697  TLOG_ARB(15, "CommandableFragmentGenerator") << "getDataBufferStats: frags=" << dataBufferDepthFragments_.load() << "/" << maxDataBufferDepthFragments_
698  << ", sz=" << std::to_string(dataBufferDepthBytes_.load()) << "/" << std::to_string(maxDataBufferDepthBytes_) << TLOG_ENDL;
699 }
700 
702 {
703  std::unique_lock<std::mutex> lock(dataBufferMutex_);
704  dataCondition_.wait_for(lock, std::chrono::milliseconds(10));
705  if (dataBufferDepthFragments_ > 0)
706  {
707  if ((mode_ == RequestMode::Buffer || mode_ == RequestMode::Window))
708  {
709  // Eliminate extra fragments
710  while (dataBufferIsTooLarge())
711  {
712  dataBuffer_.erase(dataBuffer_.begin());
713  getDataBufferStats();
714  }
715  if (dataBuffer_.size() > 0)
716  {
717  TLOG_ARB(17, "CommandableFragmentGenerator") << "Determining if Fragments can be dropped from data buffer" << TLOG_ENDL;
718  Fragment::timestamp_t last = dataBuffer_.back()->timestamp();
719  Fragment::timestamp_t min = last > staleTimeout_ ? last - staleTimeout_ : 0;
720  for (auto it = dataBuffer_.begin(); it != dataBuffer_.end();)
721  {
722  if ((*it)->timestamp() < min)
723  {
724  it = dataBuffer_.erase(it);
725  }
726  else
727  {
728  ++it;
729  }
730  }
731  getDataBufferStats();
732  }
733  }
734  else if (mode_ == RequestMode::Single && dataBuffer_.size() > fragment_ids_.size())
735  {
736  // Eliminate extra fragments
737  while (dataBuffer_.size() > fragment_ids_.size())
738  {
739  dataBuffer_.erase(dataBuffer_.begin());
740  }
741  }
742  }
743 }
744 
746 {
747  while (!force_stop_)
748  {
749  if (should_stop() || monitoringInterval_ <= 0)
750  {
751  TLOG_DEBUG("CommandableFragmentGenerator") << "getMonitoringDataLoop: should_stop() is " << std::boolalpha << should_stop()
752  << " and monitoringInterval is " << monitoringInterval_ << ", returning" << TLOG_ENDL;
753  return;
754  }
755  TLOG_ARB(12, "CommandableFragmentGenerator") << "getMonitoringDataLoop: Determining whether to call checkHWStatus_" << TLOG_ENDL;
756 
757  auto now = std::chrono::steady_clock::now();
758  if (TimeUtils::GetElapsedTimeMicroseconds(lastMonitoringCall_, now) >= static_cast<size_t>(monitoringInterval_))
759  {
760  isHardwareOK_ = checkHWStatus_();
761  TLOG_ARB(12, "CommandableFragmentGenerator") << "getMonitoringDataLoop: isHardwareOK_ is now " << std::boolalpha << isHardwareOK_ << TLOG_ENDL;
762  lastMonitoringCall_ = now;
763  }
764  usleep(monitoringInterval_ / 10);
765  }
766 }
767 
769 {
770  while (!force_stop_)
771  {
772  if (check_stop() || !isHardwareOK_ || exception())
773  {
774  TLOG_DEBUG("CommandableFragmentGenerator") << "receiveRequestsLoop: check_stop is " << std::boolalpha << check_stop()
775  << ", isHardwareOK_ is " << isHardwareOK_ << ", and exception state is " << exception() << ", aborting request reception thread." << TLOG_ENDL;
776  return;
777  }
778 
779  // Don't listen for requests when we're going to ignore them anyway
780  if (mode_ == RequestMode::Ignored) return;
781  TLOG_ARB(16, "CommandableFragmentGenerator") << "receiveRequestsLoop: Polling Request socket for new requests" << TLOG_ENDL;
782 
783  int ms_to_wait = 1000;
784  struct pollfd ufds[1];
785  ufds[0].fd = request_socket_;
786  ufds[0].events = POLLIN | POLLPRI;
787  int rv = poll(ufds, 1, ms_to_wait);
788 
789  // Continue loop if no message received or message does not have correct event ID
790  if (rv <= 0 || (ufds[0].revents != POLLIN && ufds[0].revents != POLLPRI)) continue;
791 
792  TLOG_ARB(11, "CommandableFragmentGenerator") << "Recieved packet on Request channel" << TLOG_ENDL;
793  detail::RequestHeader hdr_buffer;
794  recv(request_socket_, &hdr_buffer, sizeof(hdr_buffer), 0);
795  TLOG_ARB(11, "CommandableFragmentGenerator") << "Request header word: 0x" << std::hex << hdr_buffer.header << TLOG_ENDL;
796  if (!hdr_buffer.isValid()) continue;
797 
798  request_received_ = true;
799  if (hdr_buffer.mode == detail::RequestMessageMode::EndOfRun)
800  {
801  TLOG_INFO("CommandableFragmentGenerator") << "Received Request Message with the EndOfRun marker. (Re)Starting 1-second timeout for receiving all outstanding requests..." << TLOG_ENDL;
802  request_stop_timeout_ = std::chrono::steady_clock::now();
803  request_stop_requested_ = true;
804  }
805 
806  std::vector<detail::RequestPacket> pkt_buffer(hdr_buffer.packet_count);
807  recv(request_socket_, &pkt_buffer[0], sizeof(detail::RequestPacket) * hdr_buffer.packet_count, 0);
808  bool anyNew = false;
809  for (auto& buffer : pkt_buffer)
810  {
811  if (!buffer.isValid()) continue;
812  if (requests_.count(buffer.sequence_id) && requests_[buffer.sequence_id] != buffer.timestamp)
813  {
814  TLOG_ERROR("CommandableFragmentGenerator") << "Received conflicting request for SeqID "
815  << std::to_string(buffer.sequence_id) << "!"
816  << " Old ts=" << std::to_string(requests_[buffer.sequence_id])
817  << ", new ts=" << std::to_string(buffer.timestamp) << ". Keeping OLD!" << TLOG_ENDL;
818  }
819  else if (!requests_.count(buffer.sequence_id))
820  {
821  int delta = buffer.sequence_id - ev_counter();
822  TLOG_ARB(11, "CommandableFragmentGenerator") << "Recieved request for sequence ID " << std::to_string(buffer.sequence_id)
823  << " and timestamp " << std::to_string(buffer.timestamp) << " (delta: " << delta << ")" << TLOG_ENDL;
824  if (delta < 0)
825  {
826  TLOG_ARB(11, "CommandableFragmentGenerator") << "Already serviced this request! Ignoring..." << TLOG_ENDL;
827  }
828  else
829  {
830  std::unique_lock<std::mutex> tlk(request_mutex_);
831  requests_[buffer.sequence_id] = buffer.timestamp;
832  anyNew = true;
833  }
834  }
835  }
836  if (anyNew)
837  {
838  std::unique_lock<std::mutex> lock(request_mutex_);
839  requestCondition_.notify_all();
840  }
841  }
842 }
843 
845 {
846  // We just copy everything that's here into the output.
847  TLOG_ARB(9, "CommandableFragmentGenerator") << "Mode is Ignored; Copying data to output" << TLOG_ENDL;
848  std::move(dataBuffer_.begin(), dataBuffer_.end(), std::inserter(frags, frags.end()));
849  dataBuffer_.clear();
850 }
851 
853 {
854  // We only care about the latest request received. Send empties for all others.
855  sendEmptyFragments(frags);
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_ARB(9, "CommandableFragmentGenerator") << "Mode is Single; Sending copy of last event" << TLOG_ENDL;
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  requests_.clear();
880  ev_counter_inc(1, true);
881 }
882 
884 {
885  sendEmptyFragments(frags);
886 
887  // If no requests remain after sendEmptyFragments, return
888  if (requests_.size() == 0 || !requests_.count(ev_counter())) return;
889 
890  TLOG_DEBUG("CommandableFragmentGenerator") << "Creating ContainerFragment for Buffered Fragments" << TLOG_ENDL;
891  frags.emplace_back(new artdaq::Fragment(ev_counter(), fragment_id()));
892  frags.back()->setTimestamp(requests_[ev_counter()]);
893  ContainerFragmentLoader cfl(*frags.back());
894  cfl.set_missing_data(false); // Buffer mode is never missing data, even if there IS no data.
895 
896  // Buffer mode TFGs should simply copy out the whole dataBuffer_ into a ContainerFragment
897  // Window mode TFGs must do a little bit more work to decide which fragments to send for a given request
898  for (auto it = dataBuffer_.begin(); it != dataBuffer_.end();)
899  {
900  TLOG_ARB(9, "CommandableFragmentGenerator") << "ApplyRequests: Adding Fragment with timestamp " << std::to_string((*it)->timestamp()) << " to Container" << TLOG_ENDL;
901  cfl.addFragment(*it);
902  it = dataBuffer_.erase(it);
903  }
904  requests_.clear();
905  ev_counter_inc(1, true);
906 }
907 
909 {
910  if (!last_window_send_time_set_)
911  {
912  last_window_send_time_ = std::chrono::steady_clock::now();
913  last_window_send_time_set_ = true;
914  }
915 
916  bool now_have_desired_request = std::any_of(requests_.begin(), requests_.end(),
917  [this](decltype(requests_)::value_type& request) {
918  return request.first == ev_counter(); });
919 
920  if (missing_request_)
921  {
922  if (!now_have_desired_request && TimeUtils::GetElapsedTimeMicroseconds(missing_request_time_) > missing_request_window_timeout_us_)
923  {
924  TLOG_ERROR("CommandableFragmentGenerator") << "Data-taking has paused for " << TimeUtils::GetElapsedTimeMicroseconds(missing_request_time_) << " us "
925  << "(> " << std::to_string(missing_request_window_timeout_us_) << " us) while waiting for missing data request messages."
926  << " Sending Empty Fragments for missing requests!" << TLOG_ENDL;
927  sendEmptyFragments(frags);
928 
929  missing_request_ = false;
930  missing_request_time_ = decltype(missing_request_time_)::max();
931  }
932  else if (now_have_desired_request) {
933  missing_request_ = false;
934  missing_request_time_ = decltype(missing_request_time_)::max();
935  }
936  }
937 
938  for (auto req = requests_.begin(); req != requests_.end();)
939  {
940  auto ts = req->second;
941  if (req->first < ev_counter())
942  {
943  req = requests_.erase(req);
944  continue;
945  }
946  while (req->first > ev_counter() && request_stop_requested_ && TimeUtils::GetElapsedTime(request_stop_timeout_) > 1)
947  {
948  sendEmptyFragment(frags, ev_counter(), "Missing request for");
949  ev_counter_inc(1, true);
950  }
951  if (req->first > ev_counter())
952  {
953  if (!missing_request_)
954  {
955  missing_request_ = true;
956  missing_request_time_ = std::chrono::steady_clock::now();
957  }
958  ++req;
959  break; // We're missing the correct request
960  }
961  TLOG_ARB(9, "CommandableFragmentGenerator") << "ApplyRequests: Checking that data exists for request window " << std::to_string(req->first) << TLOG_ENDL;
962  Fragment::timestamp_t min = ts > windowOffset_ ? ts - windowOffset_ : 0;
963  Fragment::timestamp_t max = min + windowWidth_;
964  TLOG_ARB(9, "CommandableFragmentGenerator") << "ApplyRequests: min is " << std::to_string(min) << ", max is " << std::to_string(max)
965  << " and last point in buffer is " << std::to_string((dataBuffer_.size() > 0 ? dataBuffer_.back()->timestamp() : 0)) << " (sz=" << std::to_string(dataBuffer_.size()) << ")" << TLOG_ENDL;
966  bool windowClosed = dataBuffer_.size() > 0 && dataBuffer_.back()->timestamp() >= max;
967  bool windowTimeout = TimeUtils::GetElapsedTimeMicroseconds(last_window_send_time_) > window_close_timeout_us_;
968  if (windowTimeout)
969  {
970  TLOG_WARNING("CommandableFragmentGenerator") << "A timeout occurred waiting for data to close the request window (max=" << std::to_string(max)
971  << ", buffer=" << std::to_string((dataBuffer_.size() > 0 ? dataBuffer_.back()->timestamp() : 0))
972  << " (if no buffer in memory, this is shown as a 0)). Time waiting: "
973  << TimeUtils::GetElapsedTimeMicroseconds(last_window_send_time_) << " us "
974  << "(> " << std::to_string(window_close_timeout_us_) << " us)." << TLOG_ENDL;
975  }
976  if (windowClosed || !data_thread_running_ || windowTimeout)
977  {
978  TLOG_DEBUG("CommandableFragmentGenerator") << "Creating ContainerFragment for Buffered or Window-requested Fragments" << TLOG_ENDL;
979  frags.emplace_back(new artdaq::Fragment(ev_counter(), fragment_id()));
980  frags.back()->setTimestamp(ts);
981  ContainerFragmentLoader cfl(*frags.back());
982 
983  if (!windowClosed) cfl.set_missing_data(true);
984  if (dataBuffer_.size() > 0 && dataBuffer_.front()->timestamp() > min)
985  {
986  TLOG_DEBUG("CommandableFragmentGenerator") << "Request Window covers data that is either before data collection began or has fallen off the end of the buffer" << TLOG_ENDL;
987  cfl.set_missing_data(true);
988  }
989 
990  // Buffer mode TFGs should simply copy out the whole dataBuffer_ into a ContainerFragment
991  // Window mode TFGs must do a little bit more work to decide which fragments to send for a given request
992  for (auto it = dataBuffer_.begin(); it != dataBuffer_.end();)
993  {
994  Fragment::timestamp_t fragT = (*it)->timestamp();
995  if (fragT < min || fragT > max || (fragT == max && windowWidth_ > 0))
996  {
997  ++it;
998  continue;
999  }
1000 
1001  TLOG_ARB(9, "CommandableFragmentGenerator") << "ApplyRequests: Adding Fragment with timestamp " << std::to_string((*it)->timestamp()) << " to Container" << TLOG_ENDL;
1002  cfl.addFragment(*it);
1003 
1004  if (uniqueWindows_)
1005  {
1006  it = dataBuffer_.erase(it);
1007  }
1008  else
1009  {
1010  ++it;
1011  }
1012  }
1013  req = requests_.erase(req);
1014  ev_counter_inc(1, true);
1015  last_window_send_time_ = std::chrono::steady_clock::now();
1016  }
1017  else
1018  {
1019  // Wait for the window to be closed for the current event
1020  break;
1021  }
1022  }
1023 }
1024 
1026 {
1027  if (check_stop() || exception())
1028  {
1029  return false;
1030  }
1031 
1032  // Wait for data, if in ignored mode, or a request otherwise
1033  if (mode_ == RequestMode::Ignored)
1034  {
1035  while (dataBufferDepthFragments_ <= 0)
1036  {
1037  if (check_stop() || exception() || !isHardwareOK_) return false;
1038  std::unique_lock<std::mutex> lock(dataBufferMutex_);
1039  dataCondition_.wait_for(lock, std::chrono::milliseconds(10), [this]() { return dataBufferDepthFragments_ > 0; });
1040  }
1041  }
1042  else
1043  {
1044  if ((check_stop() && requests_.size() <= 0) || exception()) return false;
1045  checkDataBuffer();
1046 
1047  while (requests_.size() <= 0)
1048  {
1049  if (check_stop() || exception()) return false;
1050 
1051  checkDataBuffer();
1052 
1053  std::unique_lock<std::mutex> lock(request_mutex_);
1054  requestCondition_.wait_for(lock, std::chrono::milliseconds(10), [this]() { return requests_.size() > 0; });
1055  }
1056  }
1057 
1058  {
1059  std::unique_lock<std::mutex> dlk(dataBufferMutex_);
1060  std::unique_lock<std::mutex> rlk(request_mutex_);
1061 
1062  switch (mode_)
1063  {
1064  case RequestMode::Single:
1065  applyRequestsSingleMode(frags);
1066  break;
1067  case RequestMode::Window:
1068  applyRequestsWindowMode(frags);
1069  break;
1070  case RequestMode::Buffer:
1071  applyRequestsBufferMode(frags);
1072  break;
1073  case RequestMode::Ignored:
1074  default:
1075  applyRequestsIgnoredMode(frags);
1076  break;
1077  }
1078 
1079  getDataBufferStats();
1080  }
1081 
1082  if (frags.size() > 0)
1083  TLOG_ARB(9, "CommandableFragmentGenerator") << "Finished Processing Event " << std::to_string(ev_counter() + 1) << " for fragment_id " << fragment_id() << "." << TLOG_ENDL;
1084  return true;
1085 }
1086 
1087 bool artdaq::CommandableFragmentGenerator::sendEmptyFragment(artdaq::FragmentPtrs& frags, size_t seqId, std::string desc)
1088 {
1089  TLOG_WARNING("CommandableFragmentGenerator") << desc << " request " << seqId << ", sending empty fragment" << TLOG_ENDL;
1090  for (auto fid : fragment_ids_)
1091  {
1092  auto frag = new Fragment();
1093  frag->setSequenceID(seqId);
1094  frag->setFragmentID(fid);
1095  frag->setSystemType(Fragment::EmptyFragmentType);
1096  frags.emplace_back(FragmentPtr(frag));
1097  }
1098  return true;
1099 }
1100 
1102 {
1103  auto sequence_id = Fragment::InvalidSequenceID;
1104  auto timestamp = Fragment::InvalidTimestamp;
1105  // Map is ordered by sequence ID!
1106  TLOG_ARB(19, "CommandableFragmentGenerator") << "Sending Empty Fragments" << TLOG_ENDL;
1107  for (auto it = requests_.begin(); it != requests_.end();)
1108  {
1109  auto seq = it->first;
1110  auto ts = it->second;
1111 
1112  while (seq > ev_counter())
1113  {
1114  // Otherwise, this is just one we missed, send an empty
1115  sendEmptyFragment(frags, ev_counter(), "Missed request for");
1116  ev_counter_inc(1, true);
1117  }
1118 
1119  // Check if this is the one "true" request
1120  if (++it == requests_.end())
1121  {
1122  sequence_id = seq;
1123  timestamp = ts;
1124  break;
1125  }
1126  if (seq < ev_counter()) continue;
1127 
1128  }
1129  requests_.clear();
1130 
1131  if (sequence_id < ev_counter()) return; // No new requests received.
1132  requests_[sequence_id] = timestamp;
1133 }
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...
int ResolveHost(char const *host_in, in_addr &addr)
Convert a string hostname to a in_addr suitable for socket communication.
Definition: TCPConnect.cc:27
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.
End of Run mode (Used to end request processing on receiver)
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.
RequestMessageMode mode
Communicates additional information to the Request receiver.
bool isValid() const
Check the magic bytes of the packet.
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.
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.
Header of a RequestMessage. Contains magic bytes for validation and a count of expected RequestPacket...
void applyRequestsIgnoredMode(artdaq::FragmentPtrs &frags)
Create fragments using data buffer for request mode Ignored. Precondition: dataBufferMutex_ and reque...
uint32_t packet_count
The number of RequestPackets in this Request message.
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...
The RequestPacket contains information about a single data request.
void startRequestReceiverThread()
Function that launches the data request receiver thread (receiveRequestsLoop())
void startMonitoringThread()
Function that launches the monitoring thread (getMonitoringDataLoop())
void checkDataBuffer()
Perform data buffer pruning operations. If the RequestMode is Single, removes all but the latest Frag...
std::string printMode_()
Return the string representation of the current RequestMode.
void sendEmptyFragments(FragmentPtrs &frags)
This function is for Buffered and Single request modes, as they can only respond to one data request ...
void getDataBufferStats()
Calculate the size of the dataBuffer and report appropriate metrics.
bool applyRequests(FragmentPtrs &output)
See if any requests have been received, and add the corresponding data Fragment objects to the output...
void receiveRequestsLoop()
This function receives data request packets, adding new requests to the request list.
void setupRequestListener()
Opens the socket used to listen for data requests.
void joinThreads()
Join any data-taking threads. Should be called when destructing CommandableFragmentGenerator.