From c3bd3f89050474e706d481069b1210e9df466cb4 Mon Sep 17 00:00:00 2001
From: "P.-A. Loizeau" <p.-a.loizeau@gsi.de>
Date: Fri, 26 Mar 2021 16:05:23 +0100
Subject: [PATCH] [MQ] add optional monitoring histograms in
 CbmMQTsaMultiSampler

---
 MQ/source/CMakeLists.txt           |   6 +
 MQ/source/CbmMQTsaMultiSampler.cxx | 275 +++++++++++++++++++++--------
 MQ/source/CbmMQTsaMultiSampler.h   |  65 ++++---
 MQ/source/runTsaMultiSampler.cxx   |  22 ++-
 4 files changed, 257 insertions(+), 111 deletions(-)

diff --git a/MQ/source/CMakeLists.txt b/MQ/source/CMakeLists.txt
index f421ef3689..c8d90b5fd5 100644
--- a/MQ/source/CMakeLists.txt
+++ b/MQ/source/CMakeLists.txt
@@ -87,6 +87,12 @@ set(DEPENDENCIES
   fles_ipc
   CbmMQBase
   CbmFlibFlesTools
+  Core
+  Gpad
+  Hist
+  Net
+  RHTTP
+  RIO
 )
 GENERATE_EXECUTABLE()
 
diff --git a/MQ/source/CbmMQTsaMultiSampler.cxx b/MQ/source/CbmMQTsaMultiSampler.cxx
index ff968c8b73..45ebe988ab 100644
--- a/MQ/source/CbmMQTsaMultiSampler.cxx
+++ b/MQ/source/CbmMQTsaMultiSampler.cxx
@@ -8,23 +8,29 @@
 
 #include "CbmMQTsaMultiSampler.h"
 
-#include "CbmMQDefs.h"
-//#include "CbmFlesCanvasTools.h"
+#include "CbmFlesCanvasTools.h"
 #include "CbmFormatDecHexPrintout.h"
 
 #include "FairMQLogger.h"
 #include "FairMQProgOptions.h"  // device->fConfig
-//#include "RootSerializer.h"
+#include "BoostSerializer.h"
+#include "RootSerializer.h"
 
 #include "TimesliceInputArchive.hpp"
 #include "TimesliceMultiInputArchive.hpp"
 #include "TimesliceMultiSubscriber.hpp"
 #include "TimesliceSubscriber.hpp"
 
+#include <TCanvas.h>
+#include <TH1F.h>
+#include <TH1I.h>
+#include <TProfile.h>
+
 #include <boost/algorithm/string.hpp>
 #include <boost/archive/binary_oarchive.hpp>
 #include <boost/filesystem.hpp>
 #include <boost/regex.hpp>
+#include <boost/serialization/utility.hpp>
 
 namespace filesys = boost::filesystem;
 
@@ -58,7 +64,9 @@ CbmMQTsaMultiSampler::CbmMQTsaMultiSampler()
   , fTSCounter(0)
   , fMessageCounter(0)
   , fSource(nullptr)
-  , fTime() {}
+  , fTime()
+  , fLastPublishTime {std::chrono::system_clock::now()}
+{}
 
 void CbmMQTsaMultiSampler::InitTask() try {
   // Get the values from the command line options (via fConfig)
@@ -73,14 +81,13 @@ void CbmMQTsaMultiSampler::InitTask() try {
   fbSendTsPerChannel    = fConfig->GetValue<bool>("send-ts-per-channel");
   fsChannelNameMissedTs = fConfig->GetValue<std::string>("ChNameMissTs");
   fsChannelNameCommands = fConfig->GetValue<std::string>("ChNameCmds");
-  /*
-    fuPublishFreqTs        = fConfig->GetValue< uint32_t >( "PubFreqTs" );
-    fdMinPublishTime       = fConfig->GetValue< double_t >( "PubTimeMin" );
-    fdMaxPublishTime       = fConfig->GetValue< double_t >( "PubTimeMax" );
-    fsChannelNameHistosInput  = fConfig->GetValue< std::string >( "ChNameIn" );
-    fsChannelNameHistosConfig = fConfig->GetValue< std::string >( "ChNameHistCfg" );
-    fsChannelNameCanvasConfig = fConfig->GetValue< std::string >( "ChNameCanvCfg" );
-*/
+  fuPublishFreqTs        = fConfig->GetValue< uint32_t >( "PubFreqTs" );
+  fdMinPublishTime       = fConfig->GetValue< double_t >( "PubTimeMin" );
+  fdMaxPublishTime       = fConfig->GetValue< double_t >( "PubTimeMax" );
+  fsChannelNameHistosInput  = fConfig->GetValue< std::string >( "ChNameIn" );
+  fsChannelNameHistosConfig = fConfig->GetValue< std::string >( "ChNameHistCfg" );
+  fsChannelNameCanvasConfig = fConfig->GetValue< std::string >( "ChNameCanvCfg" );
+
   if (fbNoSplitTs) {
     if (fbSendTsPerSysId) {
       if (fbSendTsPerChannel) {
@@ -198,10 +205,12 @@ void CbmMQTsaMultiSampler::InitTask() try {
   LOG(info) << "Number of defined output channels: " << noChannel;
   for (auto const& entry : fChannels) {
     /// Catches and ignores the channels for missing TS indices and commands
-    if (entry.first == fsChannelNameMissedTs
-        || entry.first == fsChannelNameCommands) {
+    /// Same for the histogram channels
+    if (entry.first == fsChannelNameMissedTs || entry.first == fsChannelNameCommands
+        || ( 0 < fuPublishFreqTs && ( entry.first == fsChannelNameHistosInput ||
+             entry.first == fsChannelNameHistosConfig || entry.first == fsChannelNameCanvasConfig ) ) ) {
       continue;
-    }  // if( entry.first == fsChannelNameMissedTs || entry.first == fsChannelNameCommands )
+    }  // if( entry.first == fsChannelNameMissedTs || entry.first == fsChannelNameCommands || histo channels name)
 
     LOG(info) << "Channel name: " << entry.first;
     if (!IsChannelNameAllowed(entry.first))
@@ -252,23 +261,80 @@ void CbmMQTsaMultiSampler::InitTask() try {
   else if (fbSendTsPerChannel) {
     LOG(info) << "Sending components in separate TS per channel";
   }  // else if( fbSendTsPerSysId && fbSendTsPerSysId ) of if( fbNoSplitTs )
-     /*
-  LOG(info) << "Histograms publication frequency in TS:    " << fuPublishFreqTs;
-  LOG(info) << "Histograms publication min. interval in s: " << fdMinPublishTime;
-  LOG(info) << "Histograms publication max. interval in s: " << fdMaxPublishTime;
-*/
 
-  /*
-   /// Histos creation and obtain pointer on them
-      /// Trigger histo creation on all associated algos
-   initOK &= CreateHistograms();
-
-   /// Add pointers to each histo in the histo array
-   /// Create histo config vector
-   /// ===> Use an std::vector< std::pair< std::string, std::string > > with < Histo name, Folder >
-   ///      and send it through a separate channel using the BoostSerializer
-   for( UInt_t uHisto = 0; uHisto < vHistos.size(); ++uHisto )
-   {
+  if( 0 < fuPublishFreqTs ) {
+    LOG(info) << "Histograms publication frequency in TS:    " << fuPublishFreqTs;
+    LOG(info) << "Histograms publication min. interval in s: " << fdMinPublishTime;
+    LOG(info) << "Histograms publication max. interval in s: " << fdMaxPublishTime;
+
+    /// Vector of pointers on each histo (+ optionally desired folder)
+    std::vector< std::pair< TNamed *, std::string > > vHistos = {};
+    /// Vector of pointers on each canvas (+ optionally desired folder)
+    std::vector< std::pair< TCanvas *, std::string > > vCanvases = {};
+
+    /// Histos creation and obtain pointer on them
+    fhTsRate       = new TH1I("TsRate", "TS rate; t [s]", 1800, 0., 1800.);
+    fhTsSize       = new TH1I("TsSize", "Size of TS; Size [MB]", 15000, 0., 15000.);
+    fhTsSizeEvo    = new TProfile( "TsSizeEvo", "Evolution of the TS Size; t [s]; Mean size [MB]", 1800, 0., 1800.);
+    fhTsMaxSizeEvo = new TH1F("TsMaxSizeEvo", "Evolution of maximal TS Size; t [s]; Max size [MB]", 1800, 0., 1800.);
+    fhMissedTS     = new TH1I("Missed_TS", "Missed TS", 2, 0., 2.);
+    fhMissedTSEvo  = new TProfile( "Missed_TS_Evo", "Missed TS evolution; t [s]", 1800, 0., 1800.);
+
+    /// Add histo pointers to the histo vector
+    vHistos.push_back( std::pair< TNamed *, std::string >( fhTsRate, "Sampler" ) );
+    vHistos.push_back( std::pair< TNamed *, std::string >( fhTsSize, "Sampler" ) );
+    vHistos.push_back( std::pair< TNamed *, std::string >( fhTsSizeEvo, "Sampler" ) );
+    vHistos.push_back( std::pair< TNamed *, std::string >( fhTsMaxSizeEvo, "Sampler" ) );
+    vHistos.push_back( std::pair< TNamed *, std::string >( fhMissedTS, "Sampler" ) );
+    vHistos.push_back( std::pair< TNamed *, std::string >( fhMissedTSEvo, "Sampler" ) );
+
+    /// Canvases creation
+    Double_t w = 10;
+    Double_t h = 10;
+    fcSummary = new TCanvas( "cSampSummary", "Sampler monitoring plots", w, h);
+    fcSummary->Divide(2, 3);
+
+    fcSummary->cd(1);
+    gPad->SetGridx();
+    gPad->SetGridy();
+    fhTsRate->Draw("hist");
+
+    fcSummary->cd(2);
+    gPad->SetGridx();
+    gPad->SetGridy();
+    gPad->SetLogx();
+    gPad->SetLogy();
+    fhTsSize->Draw("hist");
+
+    fcSummary->cd(3);
+    gPad->SetGridx();
+    gPad->SetGridy();
+    fhTsSizeEvo->Draw("hist");
+
+    fcSummary->cd(4);
+    gPad->SetGridx();
+    gPad->SetGridy();
+    fhTsMaxSizeEvo->Draw("hist");
+
+    fcSummary->cd(5);
+    gPad->SetGridx();
+    gPad->SetGridy();
+    fhMissedTS->Draw("hist");
+
+    fcSummary->cd(6);
+    gPad->SetGridx();
+    gPad->SetGridy();
+    fhMissedTSEvo->Draw("el");
+
+    /// Add canvas pointers to the canvas vector
+    vCanvases.push_back( std::pair< TCanvas *, std::string >( fcSummary, "canvases") );
+
+    /// Add pointers to each histo in the histo array
+    /// Create histo config vector
+    /// ===> Use an std::vector< std::pair< std::string, std::string > > with < Histo name, Folder >
+    ///      and send it through a separate channel using the BoostSerializer
+    for( UInt_t uHisto = 0; uHisto < vHistos.size(); ++uHisto )
+    {
 //         LOG(info) << "Registering  " << vHistos[ uHisto ].first->GetName()
 //                   << " in " << vHistos[ uHisto ].second.data()
 //                   ;
@@ -284,19 +350,18 @@ void CbmMQTsaMultiSampler::InitTask() try {
       /// Send message to the common histogram config messages queue
       if( Send( messageHist, fsChannelNameHistosConfig ) < 0 )
       {
-         LOG(error) << "Problem sending histo config";
-         return false;
+         LOG(fatal) << "Problem sending histo config";
       } // if( Send( messageHist, fsChannelNameHistosConfig ) < 0 )
 
       LOG(info) << "Config of hist  " << psHistoConfig.first.data()
                 << " in folder " << psHistoConfig.second.data() ;
-   } // for( UInt_t uHisto = 0; uHisto < vHistos.size(); ++uHisto )
+    } // for( UInt_t uHisto = 0; uHisto < vHistos.size(); ++uHisto )
 
-   /// Create canvas config vector
-   /// ===> Use an std::vector< std::pair< std::string, std::string > > with < Canvas name, config >
-   ///      and send it through a separate channel using the BoostSerializer
-   for( UInt_t uCanv = 0; uCanv < vCanvases.size(); ++uCanv )
-   {
+    /// Create canvas config vector
+    /// ===> Use an std::vector< std::pair< std::string, std::string > > with < Canvas name, config >
+    ///      and send it through a separate channel using the BoostSerializer
+    for( UInt_t uCanv = 0; uCanv < vCanvases.size(); ++uCanv )
+    {
 //         LOG(info) << "Registering  " << vCanvases[ uCanv ].first->GetName()
 //                   << " in " << vCanvases[ uCanv ].second.data();
       std::string sCanvName = (vCanvases[ uCanv ].first)->GetName();
@@ -313,20 +378,18 @@ void CbmMQTsaMultiSampler::InitTask() try {
       /// Send message to the common canvas config messages queue
       if( Send( messageCan, fsChannelNameCanvasConfig ) < 0 )
       {
-         LOG(error) << "Problem sending canvas config";
-         return false;
+         LOG(fatal) << "Problem sending canvas config";
       } // if( Send( messageCan, fsChannelNameCanvasConfig ) < 0 )
 
       LOG(info) << "Config string of Canvas  " << psCanvConfig.first.data()
                 << " is " << psCanvConfig.second.data() ;
-   } //  for( UInt_t uCanv = 0; uCanv < vCanvases.size(); ++uCanv )
-*/
+    } //  for( UInt_t uCanv = 0; uCanv < vCanvases.size(); ++uCanv )
+  }   // if( 0 < fuPublishFreqTs )
 
   fTime = std::chrono::steady_clock::now();
 } catch (InitTaskError& e) {
   LOG(error) << e.what();
-  // Wrapper defined in CbmMQDefs.h to support different FairMQ versions
-  cbm::mq::ChangeState(this, cbm::mq::Transition::ErrorFound);
+  ChangeState(fair::mq::Transition::ErrorFound);
 }
 
 bool CbmMQTsaMultiSampler::IsChannelNameAllowed(std::string channelName) {
@@ -374,7 +437,6 @@ bool CbmMQTsaMultiSampler::IsChannelNameAllowed(std::string channelName) {
 
 bool CbmMQTsaMultiSampler::ConditionalRun() {
 
-
   auto timeslice = fSource->get();
   if (timeslice) {
     if (fTSCounter < fMaxTimeslices) {
@@ -383,6 +445,38 @@ bool CbmMQTsaMultiSampler::ConditionalRun() {
       const fles::Timeslice& ts = *timeslice;
       uint64_t uTsIndex         = ts.index();
 
+      if( 0 < fuPublishFreqTs ) {
+        uint64_t uTsTime = ts.descriptor(0, 0).idx;
+        if( 0 == fuStartTime ) {
+          fuStartTime = uTsTime;
+        } // if( 0 == fuStartTime )
+        fdTimeToStart = static_cast< double_t >(uTsTime - fuStartTime)/1e9;
+        uint64_t uSizeMb = 0;
+
+        for( uint64_t uComp = 0; uComp < ts.num_components(); ++uComp ) {
+          uSizeMb += ts.size_component( uComp ) / (1024 * 1024);
+        } // for( uint_t uComp = 0; uComp < ts.num_components(); ++uComp )
+
+
+        fhTsRate->Fill( fdTimeToStart );
+        fhTsSize->Fill( uSizeMb );
+        fhTsSizeEvo->Fill( fdTimeToStart, uSizeMb );
+
+        /// Fill max size per s (assumes the histo binning is 1 second!)
+        if( 0. == fdLastMaxTime ) {
+          fdLastMaxTime = fdTimeToStart;
+          fdTsMaxSize = uSizeMb;
+        } // if( 0. == fdLastMaxTime )
+        else if( 1. <= fdTimeToStart - fdLastMaxTime ) {
+          fhTsMaxSizeEvo->Fill( fdLastMaxTime, fdTsMaxSize );
+          fdLastMaxTime = fdTimeToStart;
+          fdTsMaxSize = uSizeMb;
+        } // else if if( 1 <= fdTimeToStart - fdLastMaxTime )
+        else if( fdTsMaxSize < uSizeMb ) {
+          fdTsMaxSize = uSizeMb;
+        } // else if( fdTsMaxSize < uSizeMb )
+      } // if( 0 < fuPublishFreqTs )
+
       /// Missed TS detection (only if output channel name defined by user)
       if ((uTsIndex != (fuPrevTsIndex + 1))
           && (0 != fuPrevTsIndex && 0 != uTsIndex)
@@ -404,7 +498,18 @@ bool CbmMQTsaMultiSampler::ConditionalRun() {
 
           return false;
         }  // if( !SendMissedTsIdx( vulMissedIndices ) )
+
+        if( 0 < fuPublishFreqTs ) {
+          fhMissedTS->Fill(1, uTsIndex - fuPrevTsIndex);
+          fhMissedTSEvo->Fill(fdTimeToStart, 1, uTsIndex - fuPrevTsIndex);
+        } // if( 0 < fuPublishFreqTs )
+
       }  // if( ( uTsIndex != ( fuPrevTsIndex + 1 ) ) && ( 0 != fuPrevTsIndex && 0 != uTsIndex ) && "" != fsChannelNameMissedTs )
+      else if( 0 < fuPublishFreqTs ) {
+        fhMissedTS->Fill(0);
+        fhMissedTSEvo->Fill(fdTimeToStart, 0, 1);
+      } // else if( 0 < fuPublishFreqTs )
+
       fuPrevTsIndex = uTsIndex;
 
       if (fTSCounter % 10000 == 0) {
@@ -473,8 +578,23 @@ bool CbmMQTsaMultiSampler::ConditionalRun() {
           }  // if( !CreateAndSendComponent(ts, nrComp) )
         }  // for (unsigned int nrComp = 0; nrComp < ts.num_components(); ++nrComp)
       }  // else of if( fbSendTsPerSysId )
+
+      if( 0 < fuPublishFreqTs ) {
+        /// Send histograms periodically.
+        /// Use also runtime checker to trigger sending after M s if
+        /// processing too slow or delay sending if processing too fast
+        std::chrono::system_clock::time_point currentTime = std::chrono::system_clock::now();
+        std::chrono::duration<double_t> elapsedSeconds = currentTime - fLastPublishTime;
+        if ((fdMaxPublishTime < elapsedSeconds.count()) ||
+            (0 == fTSCounter % fuPublishFreqTs && fdMinPublishTime < elapsedSeconds.count())) {
+          SendHistograms();
+          fLastPublishTime = std::chrono::system_clock::now();
+        }  // if( ( fdMaxPublishTime < elapsedSeconds.count() ) || ( 0 == fTSCounter % fuPublishFreqTs && fdMinPublishTime < elapsedSeconds.count() ) )
+      } // if( 0 < fuPublishFreqTs )
+
       return true;
-    } else {
+    } // if (fTSCounter < fMaxTimeslices)
+    else {
       CalcRuntime();
 
       /// If command channel defined, send command to all "slaves"
@@ -489,8 +609,9 @@ bool CbmMQTsaMultiSampler::ConditionalRun() {
       }  // if( "" != fsChannelNameCommands )
 
       return false;
-    }
-  } else {
+    } // else of if (fTSCounter < fMaxTimeslices)
+  }   // if (timeslice)
+  else {
     CalcRuntime();
 
     /// If command channel defined, send command to all "slaves"
@@ -505,20 +626,7 @@ bool CbmMQTsaMultiSampler::ConditionalRun() {
     }  // if( "" != fsChannelNameCommands )
 
     return false;
-  }
-  /*
-   /// Send histograms each 100 time slices. Should be each ~1s
-   /// Use also runtime checker to trigger sending after M s if
-   /// processing too slow or delay sending if processing too fast
-   std::chrono::system_clock::time_point currentTime = std::chrono::system_clock::now();
-   std::chrono::duration<double_t> elapsedSeconds = currentTime - fLastPublishTime;
-   if( ( fdMaxPublishTime < elapsedSeconds.count() ) ||
-       ( 0 == fulNumMessages % fuPublishFreqTs && fdMinPublishTime < elapsedSeconds.count() ) )
-   {
-      SendHistograms();
-      fLastPublishTime = std::chrono::system_clock::now();
-   } // if( ( fdMaxPublishTime < elapsedSeconds.count() ) || ( 0 == fulNumMessages % fuPublishFreqTs && fdMinPublishTime < elapsedSeconds.count() ) )
-*/
+  } // else of if (timeslice)
 }
 
 bool CbmMQTsaMultiSampler::CreateAndSendComponent(const fles::Timeslice& ts,
@@ -892,26 +1000,37 @@ bool CbmMQTsaMultiSampler::SendCommand(std::string sCommand) {
   return true;
 }
 
-/*
 bool CbmMQTsaMultiSampler::SendHistograms()
 {
-   /// Serialize the array of histos into a single MQ message
-   FairMQMessagePtr message( NewMessage() );
-   Serialize<RootSerializer>( *message, &fArrayHisto );
-
-   /// Send message to the common histogram messages queue
-   if( Send( message, fsChannelNameHistosInput ) < 0 )
-   {
-      LOG(error) << "Problem sending data";
-      return false;
-   } // if( Send( message, fsChannelNameHistosInput ) < 0 )
+  /// Serialize the array of histos into a single MQ message
+  FairMQMessagePtr message( NewMessage() );
+  Serialize<RootSerializer>( *message, &fArrayHisto );
+
+  /// Send message to the common histogram messages queue
+  if( Send( message, fsChannelNameHistosInput ) < 0 )
+  {
+    LOG(error) << "Problem sending data";
+    return false;
+  } // if( Send( message, fsChannelNameHistosInput ) < 0 )
+
+  /// Reset the histograms after sending them (but do not reset the time)
+  ResetHistograms();
 
-   /// Reset the histograms after sending them (but do not reset the time)
-   ResetHistograms();
+  return true;
+}
 
-   return true;
+
+bool CbmMQTsaMultiSampler::ResetHistograms()
+{
+  fhTsRate->Reset();
+  fhTsSize->Reset();
+  fhTsSizeEvo->Reset();
+  fhTsMaxSizeEvo->Reset();
+  fhMissedTS->Reset();
+  fhMissedTSEvo->Reset();
+
+  return true;
 }
-*/
 
 CbmMQTsaMultiSampler::~CbmMQTsaMultiSampler() {}
 
diff --git a/MQ/source/CbmMQTsaMultiSampler.h b/MQ/source/CbmMQTsaMultiSampler.h
index 53c1cbdc4c..56e0723980 100644
--- a/MQ/source/CbmMQTsaMultiSampler.h
+++ b/MQ/source/CbmMQTsaMultiSampler.h
@@ -16,8 +16,15 @@
 
 #include "FairMQDevice.h"
 
+class TCanvas;
+class TH1F;
+class TH1I;
+class TProfile;
+#include <TObjArray.h>
+
 #include <ctime>
 #include <string>
+#include <utility>
 #include <vector>
 
 class CbmMQTsaMultiSampler : public FairMQDevice {
@@ -40,14 +47,14 @@ protected:
   bool fbNoSplitTs        = false;
   bool fbSendTsPerSysId   = false;
   bool fbSendTsPerChannel = false;
-  /*
-    std::string fsChannelNameHistosInput  = "histogram-in";
-    std::string fsChannelNameHistosConfig = "histo-conf";
-    std::string fsChannelNameCanvasConfig = "canvas-conf";
-    uint32_t    fuPublishFreqTs  = 100;
-    double_t    fdMinPublishTime =   0.5;
-    double_t    fdMaxPublishTime =   5;
-*/
+
+  std::string fsChannelNameHistosInput  = "histogram-in";
+  std::string fsChannelNameHistosConfig = "histo-conf";
+  std::string fsChannelNameCanvasConfig = "canvas-conf";
+  uint32_t    fuPublishFreqTs  = 0;
+  double_t    fdMinPublishTime = 0.5;
+  double_t    fdMaxPublishTime = 5;
+
   uint64_t fuPrevTsIndex = 0;
   uint64_t fTSCounter;
   uint64_t fMessageCounter;
@@ -73,12 +80,12 @@ private:
   bool SendData(const fles::StorableTimeslice&, std::string);
   bool SendMissedTsIdx(std::vector<uint64_t> vIndices);
   bool SendCommand(std::string sCommand);
-  /*
   bool SendHistograms();
-*/
+  bool ResetHistograms();
 
   fles::TimesliceSource* fSource;  //!
   std::chrono::steady_clock::time_point fTime;
+  std::chrono::system_clock::time_point fLastPublishTime;
 
 
   // The vector fAllowedChannels contain the list of defined channel names
@@ -115,21 +122,29 @@ private:
 
   std::string fsChannelNameMissedTs = "";
   std::string fsChannelNameCommands = "";
-  /*
-    /// Obtain vector of pointers on each histo from the algo (+ optionally desired folder)
-    std::vector< std::pair< TNamed *, std::string > > vHistos = fMonitorAlgo->GetHistoVector();
-    /// Obtain vector of pointers on each canvas from the algo (+ optionally desired folder)
-    std::vector< std::pair< TCanvas *, std::string > > vCanvases = fMonitorAlgo->GetCanvasVector();
-
-    /// Array of histograms to send to the histogram server
-    TObjArray fArrayHisto = {};
-    /// Vector of string pairs with ( HistoName, FolderPath ) to send to the histogram server
-    std::vector< std::pair< std::string, std::string > > fvpsHistosFolder = {};
-    /// Vector of string pairs with ( CanvasName, CanvasConfig ) to send to the histogram server
-    /// Format of Can config is "NbPadX(U);NbPadY(U);ConfigPad1(s);....;ConfigPadXY(s)"
-    /// Format of Pad config is "GrixX(b),GridY(b),LogX(b),LogY(b),LogZ(b),HistoName(s),DrawOptions(s)"
-    std::vector< std::pair< std::string, std::string > > fvpsCanvasConfig = {};
-*/
+
+  /// Array of histograms to send to the histogram server
+  TObjArray fArrayHisto = {};
+  /// Vector of string pairs with ( HistoName, FolderPath ) to send to the histogram server
+  std::vector< std::pair< std::string, std::string > > fvpsHistosFolder = {};
+  /// Vector of string pairs with ( CanvasName, CanvasConfig ) to send to the histogram server
+  /// Format of Can config is "NbPadX(U);NbPadY(U);ConfigPad1(s);....;ConfigPadXY(s)"
+  /// Format of Pad config is "GrixX(b),GridY(b),LogX(b),LogY(b),LogZ(b),HistoName(s),DrawOptions(s)"
+  std::vector< std::pair< std::string, std::string > > fvpsCanvasConfig = {};
+
+  /// Histograms
+  TH1I* fhTsRate          = nullptr;
+  TH1I* fhTsSize          = nullptr;
+  TProfile* fhTsSizeEvo   = nullptr;
+  TH1F* fhTsMaxSizeEvo    = nullptr;
+  TH1I* fhMissedTS        = nullptr;
+  TProfile* fhMissedTSEvo = nullptr;
+  TCanvas* fcSummary      = nullptr;
+  uint64_t fuStartTime    = 0;
+  double_t fdTimeToStart  = 0.;
+  double_t fdLastMaxTime  = 0.;
+  double_t fdTsMaxSize    = 0.;
+
 };
 
 #endif /* CBMMQTSASAMPLER_H_ */
diff --git a/MQ/source/runTsaMultiSampler.cxx b/MQ/source/runTsaMultiSampler.cxx
index 911a369523..dd421e17c0 100644
--- a/MQ/source/runTsaMultiSampler.cxx
+++ b/MQ/source/runTsaMultiSampler.cxx
@@ -41,14 +41,20 @@ void addCustomOptions(bpo::options_description& options) {
   options.add_options()("ChNameCmds",
                         bpo::value<std::string>()->default_value(""),
                         "MQ channel name for commands to slaves");
-  /*
-   options.add_options() ( "ChNameIn",      bpo::value< std::string >()->default_value( "histogram-in" ),
-                           "MQ channel name for histos");
-   options.add_options() ( "ChNameHistCfg", bpo::value< std::string >()->default_value( "histo-conf" ),
-                           "MQ channel name for histos config");
-   options.add_options() ( "ChNameCanvCfg", bpo::value< std::string >()->default_value( "canvas-conf" ),
-                           "MQ channel name for canvases config");
-*/
+
+  options.add_options()("PubFreqTs", bpo::value<uint32_t>()->default_value(0),
+                        "Histo publishing frequency in TS");
+  options.add_options()("PubTimeMin", bpo::value<double_t>()->default_value(1.0),
+                        "Minimal time between two publishing");
+  options.add_options()("PubTimeMax", bpo::value<double_t>()->default_value(10.0),
+                        "Maximal time between two publishing");
+  options.add_options()("ChNameIn", bpo::value< std::string >()->default_value( "histogram-in" ),
+                        "MQ channel name for histos");
+  options.add_options()("ChNameHistCfg", bpo::value< std::string >()->default_value( "histo-conf" ),
+                        "MQ channel name for histos config");
+  options.add_options()("ChNameCanvCfg", bpo::value< std::string >()->default_value( "canvas-conf" ),
+                        "MQ channel name for canvases config");
+
 }
 
 FairMQDevicePtr getDevice(const FairMQProgOptions& /*config*/) {
-- 
GitLab