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