artdaq  v3_01_00
CommandableFragmentGenerator.hh
1 #ifndef artdaq_Application_CommandableFragmentGenerator_hh
2 #define artdaq_Application_CommandableFragmentGenerator_hh
3 
4 // Socket Includes
5 #include <arpa/inet.h>
6 #include <netinet/in.h>
7 #include <sys/types.h>
8 #include <sys/socket.h>
9 #include <unistd.h>
10 
11 #include <atomic>
12 #include <condition_variable>
13 #include <mutex>
14 #include <queue>
15 #include <chrono>
16 #include <array>
17 #include <list>
18 
19 #include "fhiclcpp/fwd.h"
20 #include "fhiclcpp/ParameterSet.h"
21 
22 #include "artdaq/DAQdata/Globals.hh"
23 #include "artdaq-core/Data/Fragment.hh"
24 #include "artdaq-core/Generators/FragmentGenerator.hh"
25 #include "artdaq-utilities/Plugins/MetricManager.hh"
26 #include "artdaq/DAQrate/RequestReceiver.hh"
27 
28 namespace artdaq
29 {
33  enum class RequestMode
34  {
35  Single,
36  Buffer,
37  Window,
38  Ignored
39  };
40 
83  class CommandableFragmentGenerator : public FragmentGenerator
84  {
85  public:
86 
93 
128  explicit CommandableFragmentGenerator(const fhicl::ParameterSet& ps);
129 
136 
137 
144  void joinThreads();
145 
151  bool getNext(FragmentPtrs& output) override final;
152 
153 
159  void applyRequestsIgnoredMode(artdaq::FragmentPtrs& frags);
160 
166  void applyRequestsSingleMode(artdaq::FragmentPtrs& frags);
167 
173  void applyRequestsBufferMode(artdaq::FragmentPtrs& frags);
174 
180  void applyRequestsWindowMode(artdaq::FragmentPtrs& frags);
181 
187  bool applyRequests(FragmentPtrs& output);
188 
196  bool sendEmptyFragment(FragmentPtrs& frags, size_t sequenceId, std::string desc);
197 
204  void sendEmptyFragments(FragmentPtrs& frags, std::map<Fragment::sequence_id_t, Fragment::timestamp_t>& requests);
205 
209  void startDataThread();
210 
214  void startMonitoringThread();
215 
220  void getDataLoop();
221 
226  bool waitForDataBufferReady();
227 
232  bool dataBufferIsTooLarge();
233 
237  void getDataBufferStats();
238 
244  void checkDataBuffer();
245 
249  void getMonitoringDataLoop();
250 
255  std::vector<Fragment::fragment_id_t> fragmentIDs() override
256  {
257  return fragment_ids_;
258  }
259 
260  //
261  // State-machine related interface below.
262  //
263 
278  void StartCmd(int run, uint64_t timeout, uint64_t timestamp);
279 
289  void StopCmd(uint64_t timeout, uint64_t timestamp);
290 
300  void PauseCmd(uint64_t timeout, uint64_t timestamp);
301 
310  void ResumeCmd(uint64_t timeout, uint64_t timestamp);
311 
322  std::string ReportCmd(std::string const& which = "");
323 
328  virtual std::string metricsReportingInstanceName() const
329  {
330  return instance_name_for_metrics_;
331  }
332 
333  // The following functions are not yet implemented, and their
334  // signatures may be subject to change.
335 
336  // John F., 12/6/13 -- do we want Reset and Shutdown commands?
337  // Kurt B., 15-Feb-2014. For the moment, I suspect that we don't
338  // want a Shutdown command. FragmentGenerator instances are
339  // Constructed at Initialization time, and they are destructed
340  // at Shutdown time. So, any shutdown operations that need to be
341  // done should be put in the FragmentGenerator child class
342  // destructors. If we find that want shutdown (or initialization)
343  // operations that are different from destruction (construction),
344  // then we'll have to add InitCmd and ShutdownCmd methods.
345 
346  // virtual void ResetCmd() final {}
347  // virtual void ShutdownCmd() final {}
348 
353  bool exception() const { return exception_.load(); }
354 
355 
362  virtual bool metaCommand(std::string const& command, std::string const& arg);
363 
364  protected:
365 
366  // John F., 12/6/13 -- need to figure out which of these getter
367  // functions should be promoted to "public"
368 
369  // John F., 1/21/15 -- after more than a year, there hasn't been a
370  // single complaint that a CommandableFragmentGenerator-derived
371  // class hasn't allowed its users to access these quantities, so
372  // they're probably fine as is
373 
378  int run_number() const { return run_number_; }
383  int subrun_number() const { return subrun_number_; }
388  uint64_t timeout() const { return timeout_; }
393  uint64_t timestamp() const { return timestamp_; }
394 
399  bool should_stop() const { return should_stop_.load(); }
400 
405  bool check_stop();
406 
411  int board_id() const { return board_id_; }
412 
418  int fragment_id() const;
419 
424  size_t ev_counter() const { return ev_counter_.load(); }
425 
432  size_t ev_counter_inc(size_t step = 1, bool force = false); // returns the prev value
433 
438  void set_exception(bool exception) { exception_.store(exception); }
439 
444  void metricsReportingInstanceName(std::string const& name)
445  {
446  instance_name_for_metrics_ = name;
447  }
448 
453  std::string printMode_();
454 
455  // John F., 12/10/13
456  // Is there a better way to handle mutex_ than leaving it a protected variable?
457 
458  // John F., 1/21/15
459  // Translation above is "should mutex_ be a private variable,
460  // accessible via a getter function". Probably, but at this point
461  // it's not worth breaking code by implementing this.
462 
463  std::mutex mutex_;
464 
465  private:
466  // FHiCL-configurable variables. Note that the C++ variable names
467  // are the FHiCL variable names with a "_" appended
468 
469  //Socket parameters
470  std::unique_ptr<RequestReceiver> requestReceiver_;
471 
472  RequestMode mode_;
473  Fragment::timestamp_t windowOffset_;
474  Fragment::timestamp_t windowWidth_;
475  Fragment::timestamp_t staleTimeout_;
476  Fragment::type_t expectedType_;
477  size_t maxFragmentCount_;
478  bool uniqueWindows_;
479  bool missing_request_;
480  std::chrono::steady_clock::time_point missing_request_time_;
481  std::chrono::steady_clock::time_point last_window_send_time_;
482  bool last_window_send_time_set_;
483  std::set<Fragment::sequence_id_t> windows_sent_ooo_;
484  size_t missing_request_window_timeout_us_;
485  size_t window_close_timeout_us_;
486 
487  bool useDataThread_;
488  size_t sleep_on_no_data_us_;
489  std::atomic<bool> data_thread_running_;
490  boost::thread dataThread_;
491 
492  std::condition_variable dataCondition_;
493  std::atomic<int> dataBufferDepthFragments_;
494  std::atomic<size_t> dataBufferDepthBytes_;
495  int maxDataBufferDepthFragments_;
496  size_t maxDataBufferDepthBytes_;
497 
498  bool useMonitoringThread_;
499  boost::thread monitoringThread_;
500  int64_t monitoringInterval_; // Microseconds
501  std::chrono::steady_clock::time_point lastMonitoringCall_;
502  bool isHardwareOK_;
503 
504  FragmentPtrs dataBuffer_;
505  FragmentPtrs newDataBuffer_;
506  std::mutex dataBufferMutex_;
507 
508  std::vector<artdaq::Fragment::fragment_id_t> fragment_ids_;
509 
510  // In order to support the state-machine related behavior, all
511  // CommandableFragmentGenerators must be able to remember a run number and a
512  // subrun number.
513  int run_number_, subrun_number_;
514 
515  // JCF, 8/28/14
516 
517  // Provide a user-adjustable timeout for the start transition
518  uint64_t timeout_;
519 
520  // JCF, 8/21/14
521 
522  // In response to a need to synchronize various components using
523  // different fragment generators in an experiment, keep a record
524  // of a timestamp (see Redmine Issue #6783 for more)
525 
526  uint64_t timestamp_;
527 
528  std::atomic<bool> should_stop_, exception_, force_stop_;
529  std::string latest_exception_report_;
530  std::atomic<size_t> ev_counter_;
531 
532  int board_id_;
533  std::string instance_name_for_metrics_;
534 
535  // Depending on what sleep_on_stop_us_ is set to, this gives the
536  // stopping thread the chance to gather the required lock
537 
538  int sleep_on_stop_us_;
539 
540  protected:
541 
542  // Obtain the next group of Fragments, if any are available. Return
543  // false if no more data are available, if we are 'stopped', or if
544  // we are not running in state-machine mode. Note that getNext_()
545  // must return n of each fragmentID declared by fragmentIDs_().
546  virtual bool getNext_(FragmentPtrs& output) = 0;
547 
548 
549  // Check any relavent hardware status registers. Return false if
550  // an error condition exists that should halt data-taking.
551  // This function should probably make MetricManager calls.
552  virtual bool checkHWStatus_();
553 
554  //
555  // State-machine related implementor interface below.
556  //
557 
558  // If a CommandableFragmentGenerator subclass is reading from a
559  // file, and start() is called, any run-, subrun-, and
560  // event-numbers in the data read from the file must be
561  // over-written by the specified run number, etc. After a call to
562  // StartCmd(), and until a call to StopCmd(), getNext_() is
563  // expected to return true as long as datataking is intended.
564  virtual void start() = 0;
565 
566  // On call to StopCmd, stopNoMutex() is called prior to StopCmd
567  // acquiring the mutex
568  virtual void stopNoMutex() = 0;
569 
570  // If a CommandableFragmentGenerator subclass is reading from a file, calling
571  // stop() should arrange that the next call to getNext_() returns
572  // false, rather than allowing getNext_() to read to the end of the
573  // file.
574  virtual void stop() = 0;
575 
576  // On call to PauseCmd, pauseNoMutex() is called prior to PauseCmd
577  // acquiring the mutex
578  virtual void pauseNoMutex();
579 
580  // If a CommandableFragmentGenerator subclass is reading from hardware, the
581  // implementation of pause() should tell the hardware to stop
582  // sending data.
583  virtual void pause();
584 
585  // The subrun number will be incremented *before* a call to
586  // resume. Subclasses are responsible for assuring that, after a
587  // call to resume, that getNext_() will return Fragments marked with
588  // the correct subrun number (and run number).
589  virtual void resume();
590 
591  // Let's say that the contract with the report() functions is that they
592  // return a non-empty string if they have something useful to report,
593  // but if they don't know how to handle a given request, they simply
594  // return an empty string and the ReportCmd() takes care of saying
595  // "the xyz command is not currently supported".
596  // For backward compatibility, we keep the report function that takes
597  // no arguments and add one that takes a "which" argument. In the
598  // ReportCmd function, we'll call the more specific one first.
599  virtual std::string report();
600 
601  virtual std::string reportSpecific(std::string const&);
602 
603  };
604 }
605 
606 #endif /* artdaq_Application_CommandableFragmentGenerator_hh */
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 subrun_number() const
Get the current Subrun number.
virtual ~CommandableFragmentGenerator()
CommandableFragmentGenerator Destructor.
RequestMode
The RequestMode enumeration contains the possible ways which CommandableFragmentGenerator responds to...
void applyRequestsBufferMode(artdaq::FragmentPtrs &frags)
Create fragments using data buffer for request mode Buffer. Precondition: dataBufferMutex_ and reques...
bool exception() const
Get the current value of the exception flag.
void metricsReportingInstanceName(std::string const &name)
Sets the name for metrics reporting.
std::mutex mutex_
Mutex used to ensure that multiple transition commands do not run at the same time.
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.
uint64_t timeout() const
Timeout of last command.
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.
std::vector< Fragment::fragment_id_t > fragmentIDs() override
Get the list of Fragment IDs handled by this CommandableFragmentGenerator.
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...
virtual std::string metricsReportingInstanceName() const
Get the name used when reporting metrics.
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 ...
bool should_stop() const
Get the current value of the should_stop flag.
CommandableFragmentGenerator is a FragmentGenerator-derived abstract class that defines the interface...
void startMonitoringThread()
Function that launches the monitoring thread (getMonitoringDataLoop())
size_t ev_counter() const
Get the current value of the event counter.
void checkDataBuffer()
Perform data buffer pruning operations. If the RequestMode is Single, removes all but the latest Frag...
int board_id() const
Gets the current board_id.
std::string printMode_()
Return the string representation of the current RequestMode.
void set_exception(bool exception)
Control the exception flag.
void getDataBufferStats()
Calculate the size of the dataBuffer and report appropriate metrics.
uint64_t timestamp() const
Timestamp of last command.
bool applyRequests(FragmentPtrs &output)
See if any requests have been received, and add the corresponding data Fragment objects to the output...
int run_number() const
Get the current Run number.
void joinThreads()
Join any data-taking threads. Should be called when destructing CommandableFragmentGenerator.