artdaq  v3_00_01
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 #include "artdaq-core/Data/Fragment.hh"
22 #include "artdaq-core/Generators/FragmentGenerator.hh"
23 #include "artdaq-utilities/Plugins/MetricManager.hh"
24 #include "artdaq/DAQrate/detail/RequestMessage.hh"
25 #include "artdaq/DAQdata/Globals.hh"
26 
27 namespace artdaq
28 {
32  enum class RequestMode
33  {
34  Single,
35  Buffer,
36  Window,
37  Ignored
38  };
39 
82  class CommandableFragmentGenerator : public FragmentGenerator
83  {
84  public:
85 
92 
127  explicit CommandableFragmentGenerator(const fhicl::ParameterSet& ps);
128 
135 
136 
143  void joinThreads();
144 
150  bool getNext(FragmentPtrs& output) override final;
151 
152 
158  void applyRequestsIgnoredMode(artdaq::FragmentPtrs& frags);
159 
165  void applyRequestsSingleMode(artdaq::FragmentPtrs& frags);
166 
172  void applyRequestsBufferMode(artdaq::FragmentPtrs& frags);
173 
179  void applyRequestsWindowMode(artdaq::FragmentPtrs& frags);
180 
186  bool applyRequests(FragmentPtrs& output);
187 
191  void setupRequestListener();
192 
200  bool sendEmptyFragment(FragmentPtrs& frags, size_t sequenceId, std::string desc);
201 
207  void sendEmptyFragments(FragmentPtrs& frags);
208 
212  void startDataThread();
213 
217  void startMonitoringThread();
218 
223 
228  void getDataLoop();
229 
234  bool waitForDataBufferReady();
235 
240  bool dataBufferIsTooLarge();
241 
245  void getDataBufferStats();
246 
252  void checkDataBuffer();
253 
257  void getMonitoringDataLoop();
258 
262  void receiveRequestsLoop();
263 
268  std::vector<Fragment::fragment_id_t> fragmentIDs() override
269  {
270  return fragment_ids_;
271  }
272 
273  //
274  // State-machine related interface below.
275  //
276 
291  void StartCmd(int run, uint64_t timeout, uint64_t timestamp);
292 
302  void StopCmd(uint64_t timeout, uint64_t timestamp);
303 
313  void PauseCmd(uint64_t timeout, uint64_t timestamp);
314 
323  void ResumeCmd(uint64_t timeout, uint64_t timestamp);
324 
335  std::string ReportCmd(std::string const& which = "");
336 
341  virtual std::string metricsReportingInstanceName() const
342  {
343  return instance_name_for_metrics_;
344  }
345 
346  // The following functions are not yet implemented, and their
347  // signatures may be subject to change.
348 
349  // John F., 12/6/13 -- do we want Reset and Shutdown commands?
350  // Kurt B., 15-Feb-2014. For the moment, I suspect that we don't
351  // want a Shutdown command. FragmentGenerator instances are
352  // Constructed at Initialization time, and they are destructed
353  // at Shutdown time. So, any shutdown operations that need to be
354  // done should be put in the FragmentGenerator child class
355  // destructors. If we find that want shutdown (or initialization)
356  // operations that are different from destruction (construction),
357  // then we'll have to add InitCmd and ShutdownCmd methods.
358 
359  // virtual void ResetCmd() final {}
360  // virtual void ShutdownCmd() final {}
361 
366  bool exception() const { return exception_.load(); }
367 
368  protected:
369 
370  // John F., 12/6/13 -- need to figure out which of these getter
371  // functions should be promoted to "public"
372 
373  // John F., 1/21/15 -- after more than a year, there hasn't been a
374  // single complaint that a CommandableFragmentGenerator-derived
375  // class hasn't allowed its users to access these quantities, so
376  // they're probably fine as is
377 
382  int run_number() const { return run_number_; }
387  int subrun_number() const { return subrun_number_; }
392  uint64_t timeout() const { return timeout_; }
397  uint64_t timestamp() const { return timestamp_; }
398 
403  bool should_stop() const { return should_stop_.load(); }
404 
409  bool check_stop();
410 
415  int board_id() const { return board_id_; }
416 
422  int fragment_id() const;
423 
428  size_t ev_counter() const { return ev_counter_.load(); }
429 
436  size_t ev_counter_inc(size_t step = 1, bool force = false); // returns the prev value
437 
442  void set_exception(bool exception) { exception_.store(exception); }
443 
448  void metricsReportingInstanceName(std::string const& name)
449  {
450  instance_name_for_metrics_ = name;
451  }
452 
457  std::string printMode_();
458 
459  // John F., 12/10/13
460  // Is there a better way to handle mutex_ than leaving it a protected variable?
461 
462  // John F., 1/21/15
463  // Translation above is "should mutex_ be a private variable,
464  // accessible via a getter function". Probably, but at this point
465  // it's not worth breaking code by implementing this.
466 
467  std::mutex mutex_;
468 
469  private:
470  // FHiCL-configurable variables. Note that the C++ variable names
471  // are the FHiCL variable names with a "_" appended
472  int request_port_;
473  std::string request_addr_;
474 
475  //Socket parameters
476  struct sockaddr_in si_data_;
477  int request_socket_;
478  std::map<Fragment::sequence_id_t, Fragment::timestamp_t> requests_;
479  std::atomic<bool> request_stop_requested_;
480  std::chrono::steady_clock::time_point request_stop_timeout_;
481  std::atomic<bool> request_received_;
482  size_t end_of_run_timeout_ms_;
483  std::mutex request_mutex_;
484  boost::thread requestThread_;
485 
486  RequestMode mode_;
487  Fragment::timestamp_t windowOffset_;
488  Fragment::timestamp_t windowWidth_;
489  Fragment::timestamp_t staleTimeout_;
490  Fragment::type_t expectedType_;
491  size_t maxFragmentCount_;
492  bool uniqueWindows_;
493  bool missing_request_;
494  std::chrono::steady_clock::time_point missing_request_time_;
495  std::chrono::steady_clock::time_point last_window_send_time_;
496 
497  bool last_window_send_time_set_;
498  size_t missing_request_window_timeout_us_;
499  size_t window_close_timeout_us_;
500 
501  bool useDataThread_;
502  size_t sleep_on_no_data_us_;
503  std::atomic<bool> data_thread_running_;
504  boost::thread dataThread_;
505 
506  std::condition_variable requestCondition_;
507  std::condition_variable dataCondition_;
508  std::atomic<int> dataBufferDepthFragments_;
509  std::atomic<size_t> dataBufferDepthBytes_;
510  int maxDataBufferDepthFragments_;
511  size_t maxDataBufferDepthBytes_;
512 
513  bool useMonitoringThread_;
514  boost::thread monitoringThread_;
515  int64_t monitoringInterval_; // Microseconds
516  std::chrono::steady_clock::time_point lastMonitoringCall_;
517  bool isHardwareOK_;
518 
519  FragmentPtrs dataBuffer_;
520  FragmentPtrs newDataBuffer_;
521  std::mutex dataBufferMutex_;
522 
523  std::vector<artdaq::Fragment::fragment_id_t> fragment_ids_;
524 
525  // In order to support the state-machine related behavior, all
526  // CommandableFragmentGenerators must be able to remember a run number and a
527  // subrun number.
528  int run_number_, subrun_number_;
529 
530  // JCF, 8/28/14
531 
532  // Provide a user-adjustable timeout for the start transition
533  uint64_t timeout_;
534 
535  // JCF, 8/21/14
536 
537  // In response to a need to synchronize various components using
538  // different fragment generators in an experiment, keep a record
539  // of a timestamp (see Redmine Issue #6783 for more)
540 
541  uint64_t timestamp_;
542 
543  std::atomic<bool> should_stop_, exception_, force_stop_;
544  std::string latest_exception_report_;
545  std::atomic<size_t> ev_counter_;
546 
547  int board_id_;
548  std::string instance_name_for_metrics_;
549 
550  // Depending on what sleep_on_stop_us_ is set to, this gives the
551  // stopping thread the chance to gather the required lock
552 
553  int sleep_on_stop_us_;
554 
555  protected:
556 
557  // Obtain the next group of Fragments, if any are available. Return
558  // false if no more data are available, if we are 'stopped', or if
559  // we are not running in state-machine mode. Note that getNext_()
560  // must return n of each fragmentID declared by fragmentIDs_().
561  virtual bool getNext_(FragmentPtrs& output) = 0;
562 
563 
564  // Check any relavent hardware status registers. Return false if
565  // an error condition exists that should halt data-taking.
566  // This function should probably make MetricManager calls.
567  virtual bool checkHWStatus_();
568 
569  //
570  // State-machine related implementor interface below.
571  //
572 
573  // If a CommandableFragmentGenerator subclass is reading from a
574  // file, and start() is called, any run-, subrun-, and
575  // event-numbers in the data read from the file must be
576  // over-written by the specified run number, etc. After a call to
577  // StartCmd(), and until a call to StopCmd(), getNext_() is
578  // expected to return true as long as datataking is intended.
579  virtual void start() = 0;
580 
581  // On call to StopCmd, stopNoMutex() is called prior to StopCmd
582  // acquiring the mutex
583  virtual void stopNoMutex() = 0;
584 
585  // If a CommandableFragmentGenerator subclass is reading from a file, calling
586  // stop() should arrange that the next call to getNext_() returns
587  // false, rather than allowing getNext_() to read to the end of the
588  // file.
589  virtual void stop() = 0;
590 
591  // On call to PauseCmd, pauseNoMutex() is called prior to PauseCmd
592  // acquiring the mutex
593  virtual void pauseNoMutex();
594 
595  // If a CommandableFragmentGenerator subclass is reading from hardware, the
596  // implementation of pause() should tell the hardware to stop
597  // sending data.
598  virtual void pause();
599 
600  // The subrun number will be incremented *before* a call to
601  // resume. Subclasses are responsible for assuring that, after a
602  // call to resume, that getNext_() will return Fragments marked with
603  // the correct subrun number (and run number).
604  virtual void resume();
605 
606  // Let's say that the contract with the report() functions is that they
607  // return a non-empty string if they have something useful to report,
608  // but if they don't know how to handle a given request, they simply
609  // return an empty string and the ReportCmd() takes care of saying
610  // "the xyz command is not currently supported".
611  // For backward compatibility, we keep the report function that takes
612  // no arguments and add one that takes a "which" argument. In the
613  // ReportCmd function, we'll call the more specific one first.
614  virtual std::string report();
615 
616  virtual std::string reportSpecific(std::string const&);
617  };
618 }
619 
620 #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.
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.
bool should_stop() const
Get the current value of the should_stop flag.
void startRequestReceiverThread()
Function that launches the data request receiver thread (receiveRequestsLoop())
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 sendEmptyFragments(FragmentPtrs &frags)
This function is for Buffered and Single request modes, as they can only respond to one data request ...
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...
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.
int run_number() const
Get the current Run number.
void joinThreads()
Join any data-taking threads. Should be called when destructing CommandableFragmentGenerator.