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