artdaq  v2_03_02
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Pages
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 <thread>
16 #include <chrono>
17 #include <array>
18 #include <list>
19 
20 #include "fhiclcpp/fwd.h"
21 #include "fhiclcpp/ParameterSet.h"
22 #include "artdaq-core/Data/Fragment.hh"
23 #include "artdaq-core/Generators/FragmentGenerator.hh"
24 #include "artdaq-utilities/Plugins/MetricManager.hh"
25 #include "artdaq/DAQrate/detail/RequestMessage.hh"
26 #include "artdaq/DAQdata/Globals.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 
124  explicit CommandableFragmentGenerator(const fhicl::ParameterSet& ps);
125 
132 
138  bool getNext(FragmentPtrs& output) override final;
139 
145  bool applyRequests(FragmentPtrs& output);
146 
150  void setupRequestListener();
151 
159  bool sendEmptyFragment(FragmentPtrs& frags, size_t sequenceId, std::string desc);
160 
166  void sendEmptyFragments(FragmentPtrs& frags);
167 
171  void startDataThread();
172 
176  void startMonitoringThread();
177 
182 
187  void getDataLoop();
188 
193  bool dataBufferIsTooLarge();
194 
198  void getDataBufferStats();
199 
205  void checkDataBuffer();
206 
210  void getMonitoringDataLoop();
211 
215  void receiveRequestsLoop();
216 
221  std::vector<Fragment::fragment_id_t> fragmentIDs() override
222  {
223  return fragment_ids_;
224  }
225 
226  //
227  // State-machine related interface below.
228  //
229 
244  void StartCmd(int run, uint64_t timeout, uint64_t timestamp);
245 
255  void StopCmd(uint64_t timeout, uint64_t timestamp);
256 
266  void PauseCmd(uint64_t timeout, uint64_t timestamp);
267 
276  void ResumeCmd(uint64_t timeout, uint64_t timestamp);
277 
288  std::string ReportCmd(std::string const& which = "");
289 
294  virtual std::string metricsReportingInstanceName() const
295  {
296  return instance_name_for_metrics_;
297  }
298 
299  // The following functions are not yet implemented, and their
300  // signatures may be subject to change.
301 
302  // John F., 12/6/13 -- do we want Reset and Shutdown commands?
303  // Kurt B., 15-Feb-2014. For the moment, I suspect that we don't
304  // want a Shutdown command. FragmentGenerator instances are
305  // Constructed at Initialization time, and they are destructed
306  // at Shutdown time. So, any shutdown operations that need to be
307  // done should be put in the FragmentGenerator child class
308  // destructors. If we find that want shutdown (or initialization)
309  // operations that are different from destruction (construction),
310  // then we'll have to add InitCmd and ShutdownCmd methods.
311 
312  // virtual void ResetCmd() final {}
313  // virtual void ShutdownCmd() final {}
314 
319  bool exception() const { return exception_.load(); }
320 
321  protected:
322 
323  // John F., 12/6/13 -- need to figure out which of these getter
324  // functions should be promoted to "public"
325 
326  // John F., 1/21/15 -- after more than a year, there hasn't been a
327  // single complaint that a CommandableFragmentGenerator-derived
328  // class hasn't allowed its users to access these quantities, so
329  // they're probably fine as is
330 
335  int run_number() const { return run_number_; }
340  int subrun_number() const { return subrun_number_; }
345  uint64_t timeout() const { return timeout_; }
350  uint64_t timestamp() const { return timestamp_; }
351 
356  bool should_stop() const { return should_stop_.load(); }
357 
362  bool check_stop();
363 
368  int board_id() const { return board_id_; }
369 
375  int fragment_id() const;
376 
381  size_t ev_counter() const { return ev_counter_.load(); }
382 
389  size_t ev_counter_inc(size_t step = 1, bool force = false); // returns the prev value
390 
395  void set_exception(bool exception) { exception_.store(exception); }
396 
401  void metricsReportingInstanceName(std::string const& name)
402  {
403  instance_name_for_metrics_ = name;
404  }
405 
410  std::string printMode_();
411 
412  // John F., 12/10/13
413  // Is there a better way to handle mutex_ than leaving it a protected variable?
414 
415  // John F., 1/21/15
416  // Translation above is "should mutex_ be a private variable,
417  // accessible via a getter function". Probably, but at this point
418  // it's not worth breaking code by implementing this.
419 
420  std::mutex mutex_;
421 
422  private:
423  // FHiCL-configurable variables. Note that the C++ variable names
424  // are the FHiCL variable names with a "_" appended
425  int request_port_;
426  std::string request_addr_;
427 
428  //Socket parameters
429  struct sockaddr_in si_data_;
430  int request_socket_;
431  std::map<Fragment::sequence_id_t, Fragment::timestamp_t> requests_;
432  std::atomic<bool> request_stop_requested_;
433  std::chrono::steady_clock::time_point request_stop_timeout_;
434  size_t end_of_run_timeout_ms_;
435  std::mutex request_mutex_;
436  std::thread requestThread_;
437 
438  RequestMode mode_;
439  Fragment::timestamp_t windowOffset_;
440  Fragment::timestamp_t windowWidth_;
441  Fragment::timestamp_t staleTimeout_;
442  size_t maxFragmentCount_;
443  bool uniqueWindows_;
444 
445  bool useDataThread_;
446  std::atomic<bool> data_thread_running_;
447  std::thread dataThread_;
448 
449  std::condition_variable requestCondition_;
450  std::condition_variable dataCondition_;
451  std::atomic<int> dataBufferDepthFragments_;
452  std::atomic<size_t> dataBufferDepthBytes_;
453  int maxDataBufferDepthFragments_;
454  size_t maxDataBufferDepthBytes_;
455 
456  bool useMonitoringThread_;
457  std::thread monitoringThread_;
458  int64_t monitoringInterval_; // Microseconds
459  std::chrono::steady_clock::time_point lastMonitoringCall_;
460  bool isHardwareOK_;
461 
462  FragmentPtrs dataBuffer_;
463  FragmentPtrs newDataBuffer_;
464  std::mutex dataBufferMutex_;
465 
466  std::vector<artdaq::Fragment::fragment_id_t> fragment_ids_;
467 
468  // In order to support the state-machine related behavior, all
469  // CommandableFragmentGenerators must be able to remember a run number and a
470  // subrun number.
471  int run_number_, subrun_number_;
472 
473  // JCF, 8/28/14
474 
475  // Provide a user-adjustable timeout for the start transition
476  uint64_t timeout_;
477 
478  // JCF, 8/21/14
479 
480  // In response to a need to synchronize various components using
481  // different fragment generators in an experiment, keep a record
482  // of a timestamp (see Redmine Issue #6783 for more)
483 
484  uint64_t timestamp_;
485 
486  std::atomic<bool> should_stop_, exception_;
487  std::string latest_exception_report_;
488  std::atomic<size_t> ev_counter_;
489 
490  int board_id_;
491  std::string instance_name_for_metrics_;
492 
493  // Depending on what sleep_on_stop_us_ is set to, this gives the
494  // stopping thread the chance to gather the required lock
495 
496  int sleep_on_stop_us_;
497 
498  // Obtain the next group of Fragments, if any are available. Return
499  // false if no more data are available, if we are 'stopped', or if
500  // we are not running in state-machine mode. Note that getNext_()
501  // must return n of each fragmentID declared by fragmentIDs_().
502  virtual bool getNext_(FragmentPtrs& output) = 0;
503 
504 
505  // Check any relavent hardware status registers. Return false if
506  // an error condition exists that should halt data-taking.
507  // This function should probably make MetricManager calls.
508  virtual bool checkHWStatus_();
509 
510  //
511  // State-machine related implementor interface below.
512  //
513 
514  // If a CommandableFragmentGenerator subclass is reading from a
515  // file, and start() is called, any run-, subrun-, and
516  // event-numbers in the data read from the file must be
517  // over-written by the specified run number, etc. After a call to
518  // StartCmd(), and until a call to StopCmd(), getNext_() is
519  // expected to return true as long as datataking is intended.
520  virtual void start() = 0;
521 
522  // On call to StopCmd, stopNoMutex() is called prior to StopCmd
523  // acquiring the mutex
524  virtual void stopNoMutex() = 0;
525 
526  // If a CommandableFragmentGenerator subclass is reading from a file, calling
527  // stop() should arrange that the next call to getNext_() returns
528  // false, rather than allowing getNext_() to read to the end of the
529  // file.
530  virtual void stop() = 0;
531 
532  // On call to PauseCmd, pauseNoMutex() is called prior to PauseCmd
533  // acquiring the mutex
534  virtual void pauseNoMutex();
535 
536  // If a CommandableFragmentGenerator subclass is reading from hardware, the
537  // implementation of pause() should tell the hardware to stop
538  // sending data.
539  virtual void pause();
540 
541  // The subrun number will be incremented *before* a call to
542  // resume. Subclasses are responsible for assuring that, after a
543  // call to resume, that getNext_() will return Fragments marked with
544  // the correct subrun number (and run number).
545  virtual void resume();
546 
547  // Let's say that the contract with the report() functions is that they
548  // return a non-empty string if they have something useful to report,
549  // but if they don't know how to handle a given request, they simply
550  // return an empty string and the ReportCmd() takes care of saying
551  // "the xyz command is not currently supported".
552  // For backward compatibility, we keep the report function that takes
553  // no arguments and add one that takes a "which" argument. In the
554  // ReportCmd function, we'll call the more specific one first.
555  virtual std::string report();
556 
557  virtual std::string reportSpecific(std::string const&);
558  };
559 }
560 
561 #endif /* artdaq_Application_CommandableFragmentGenerator_hh */
int fragment_id() const
Get the current Fragment ID, if there is only one.
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...
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 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...
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 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.