artdaq  v3_00_03
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  struct sockaddr_in si_data_;
471  std::unique_ptr<RequestReceiver> requestReceiver_;
472 
473  RequestMode mode_;
474  Fragment::timestamp_t windowOffset_;
475  Fragment::timestamp_t windowWidth_;
476  Fragment::timestamp_t staleTimeout_;
477  Fragment::type_t expectedType_;
478  size_t maxFragmentCount_;
479  bool uniqueWindows_;
480  bool missing_request_;
481  std::chrono::steady_clock::time_point missing_request_time_;
482  std::chrono::steady_clock::time_point last_window_send_time_;
483  bool last_window_send_time_set_;
484  std::set<Fragment::sequence_id_t> windows_sent_ooo_;
485  size_t missing_request_window_timeout_us_;
486  size_t window_close_timeout_us_;
487 
488  bool useDataThread_;
489  size_t sleep_on_no_data_us_;
490  std::atomic<bool> data_thread_running_;
491  boost::thread dataThread_;
492 
493  std::condition_variable dataCondition_;
494  std::atomic<int> dataBufferDepthFragments_;
495  std::atomic<size_t> dataBufferDepthBytes_;
496  int maxDataBufferDepthFragments_;
497  size_t maxDataBufferDepthBytes_;
498 
499  bool useMonitoringThread_;
500  boost::thread monitoringThread_;
501  int64_t monitoringInterval_; // Microseconds
502  std::chrono::steady_clock::time_point lastMonitoringCall_;
503  bool isHardwareOK_;
504 
505  FragmentPtrs dataBuffer_;
506  FragmentPtrs newDataBuffer_;
507  std::mutex dataBufferMutex_;
508 
509  std::vector<artdaq::Fragment::fragment_id_t> fragment_ids_;
510 
511  // In order to support the state-machine related behavior, all
512  // CommandableFragmentGenerators must be able to remember a run number and a
513  // subrun number.
514  int run_number_, subrun_number_;
515 
516  // JCF, 8/28/14
517 
518  // Provide a user-adjustable timeout for the start transition
519  uint64_t timeout_;
520 
521  // JCF, 8/21/14
522 
523  // In response to a need to synchronize various components using
524  // different fragment generators in an experiment, keep a record
525  // of a timestamp (see Redmine Issue #6783 for more)
526 
527  uint64_t timestamp_;
528 
529  std::atomic<bool> should_stop_, exception_, force_stop_;
530  std::string latest_exception_report_;
531  std::atomic<size_t> ev_counter_;
532 
533  int board_id_;
534  std::string instance_name_for_metrics_;
535 
536  // Depending on what sleep_on_stop_us_ is set to, this gives the
537  // stopping thread the chance to gather the required lock
538 
539  int sleep_on_stop_us_;
540 
541  protected:
542 
543  // Obtain the next group of Fragments, if any are available. Return
544  // false if no more data are available, if we are 'stopped', or if
545  // we are not running in state-machine mode. Note that getNext_()
546  // must return n of each fragmentID declared by fragmentIDs_().
547  virtual bool getNext_(FragmentPtrs& output) = 0;
548 
549 
550  // Check any relavent hardware status registers. Return false if
551  // an error condition exists that should halt data-taking.
552  // This function should probably make MetricManager calls.
553  virtual bool checkHWStatus_();
554 
555  //
556  // State-machine related implementor interface below.
557  //
558 
559  // If a CommandableFragmentGenerator subclass is reading from a
560  // file, and start() is called, any run-, subrun-, and
561  // event-numbers in the data read from the file must be
562  // over-written by the specified run number, etc. After a call to
563  // StartCmd(), and until a call to StopCmd(), getNext_() is
564  // expected to return true as long as datataking is intended.
565  virtual void start() = 0;
566 
567  // On call to StopCmd, stopNoMutex() is called prior to StopCmd
568  // acquiring the mutex
569  virtual void stopNoMutex() = 0;
570 
571  // If a CommandableFragmentGenerator subclass is reading from a file, calling
572  // stop() should arrange that the next call to getNext_() returns
573  // false, rather than allowing getNext_() to read to the end of the
574  // file.
575  virtual void stop() = 0;
576 
577  // On call to PauseCmd, pauseNoMutex() is called prior to PauseCmd
578  // acquiring the mutex
579  virtual void pauseNoMutex();
580 
581  // If a CommandableFragmentGenerator subclass is reading from hardware, the
582  // implementation of pause() should tell the hardware to stop
583  // sending data.
584  virtual void pause();
585 
586  // The subrun number will be incremented *before* a call to
587  // resume. Subclasses are responsible for assuring that, after a
588  // call to resume, that getNext_() will return Fragments marked with
589  // the correct subrun number (and run number).
590  virtual void resume();
591 
592  // Let's say that the contract with the report() functions is that they
593  // return a non-empty string if they have something useful to report,
594  // but if they don't know how to handle a given request, they simply
595  // return an empty string and the ReportCmd() takes care of saying
596  // "the xyz command is not currently supported".
597  // For backward compatibility, we keep the report function that takes
598  // no arguments and add one that takes a "which" argument. In the
599  // ReportCmd function, we'll call the more specific one first.
600  virtual std::string report();
601 
602  virtual std::string reportSpecific(std::string const&);
603 
604  };
605 }
606 
607 #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.