From d08c3ba454c6f383ce89a4d55dccb1df7404cd0a Mon Sep 17 00:00:00 2001
From: Dominik Smith <smith@th.physik.uni-frankfurt.de>
Date: Thu, 18 Mar 2021 15:41:18 +0100
Subject: [PATCH] Added files for BuildRawEvents FairMQ device. Updated
 CMakeLists.txt to include them.

---
 MQ/mcbm/CMakeLists.txt              |  31 ++
 MQ/mcbm/CbmDeviceBuildRawEvents.cxx | 552 ++++++++++++++++++++++++++++
 MQ/mcbm/CbmDeviceBuildRawEvents.h   | 135 +++++++
 MQ/mcbm/runBuildRawEvents.cxx       |  48 +++
 MQ/mcbm/startMQBuildRawEvents.sh.in | 264 +++++++++++++
 5 files changed, 1030 insertions(+)
 create mode 100644 MQ/mcbm/CbmDeviceBuildRawEvents.cxx
 create mode 100644 MQ/mcbm/CbmDeviceBuildRawEvents.h
 create mode 100644 MQ/mcbm/runBuildRawEvents.cxx
 create mode 100755 MQ/mcbm/startMQBuildRawEvents.sh.in

diff --git a/MQ/mcbm/CMakeLists.txt b/MQ/mcbm/CMakeLists.txt
index 3d0afdd0c4..99efb48b99 100644
--- a/MQ/mcbm/CMakeLists.txt
+++ b/MQ/mcbm/CMakeLists.txt
@@ -1,5 +1,6 @@
 configure_file(${CMAKE_CURRENT_SOURCE_DIR}/startMQMcbmPulserMonitor2020.sh.in ${CMAKE_BINARY_DIR}/bin/MQ/topologies/startMQMcbmPulserMonitor2020.sh)
 configure_file(${CMAKE_CURRENT_SOURCE_DIR}/startMQMcbmEvtBuilderWin2020.sh.in ${CMAKE_BINARY_DIR}/bin/MQ/topologies/startMQMcbmEvtBuilderWin2020.sh)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/startMQBuildRawEvents.sh.in ${CMAKE_BINARY_DIR}/bin/MQ/topologies/startMQBuildRawEvents.sh)
 
 set(INCLUDE_DIRECTORIES
     ${CMAKE_CURRENT_SOURCE_DIR}
@@ -10,6 +11,7 @@ set(INCLUDE_DIRECTORIES
     ${CBMROOT_SOURCE_DIR}/fles/mcbm2018/dataformat
     ${CBMROOT_SOURCE_DIR}/fles/mcbm2018/commonMQ
     ${CBMROOT_SOURCE_DIR}/fles/flestools
+    ${CBMROOT_SOURCE_DIR}/reco/eventbuilder/digis
     ${CBMDATA_DIR}
     ${CBMDATA_DIR}/global
     ${CBMDATA_DIR}/raw
@@ -45,6 +47,7 @@ include_directories(SYSTEM ${SYSTEM_INCLUDE_DIRECTORIES})
 set(LINK_DIRECTORIES
   ${ROOT_LIBRARY_DIR}
   ${FAIRROOT_LIBRARY_DIR}
+  ${KFParticle_LIB_DIR}
   ${FAIRMQ_LIBRARY_DIR}
   ${Boost_LIBRARY_DIRS}
 )
@@ -116,6 +119,34 @@ set(DEPENDENCIES
 #GENERATE_LIBRARY()
 GENERATE_EXECUTABLE()
 
+set(EXE_NAME BuildRawEvents)
+set(SRCS CbmDeviceBuildRawEvents.cxx runBuildRawEvents.cxx)
+
+set(DEPENDENCIES
+  ${DEPENDENCIES}
+  ${FAIR_LIBS}
+  ${BOOST_LIBS}
+  fles_ipc
+  KFParticle
+  CbmFlibMcbm2018
+  CbmFlibFlesTools
+  CbmEventBuilder
+  KF
+  L1
+  CbmBase
+  CbmData
+  CbmTofBase
+  CbmSimBase
+  Core
+  RIO
+  Net
+  Hist
+  RHTTP
+)
+#GENERATE_LIBRARY()
+GENERATE_EXECUTABLE()
+
+
 set(EXE_NAME McbmEventBuilderWin)
 set(SRCS CbmDeviceMcbmEventBuilderWin.cxx runMcbmEvtBuilderWin.cxx)
 
diff --git a/MQ/mcbm/CbmDeviceBuildRawEvents.cxx b/MQ/mcbm/CbmDeviceBuildRawEvents.cxx
new file mode 100644
index 0000000000..60d075eb86
--- /dev/null
+++ b/MQ/mcbm/CbmDeviceBuildRawEvents.cxx
@@ -0,0 +1,552 @@
+/**
+ * CbmDeviceBuildRawEvents.cxx
+ *
+ * @since 2020-05-24
+ * @author P.-A. Loizeau
+ */
+
+#include "CbmDeviceBuildRawEvents.h"
+
+/// CBM headers
+#include "CbmEvent.h"
+#include "CbmFlesCanvasTools.h"
+#include "CbmMQDefs.h"
+#include "CbmMatch.h"
+#include "CbmMvdDigi.h"
+
+#include "TimesliceMetaData.h"
+
+/// FAIRROOT headers
+#include "FairMQLogger.h"
+#include "FairMQProgOptions.h"  // device->fConfig
+#include "FairParGenericSet.h"
+#include "FairRunOnline.h"
+
+#include "BoostSerializer.h"
+
+#include "RootSerializer.h"
+
+/// FAIRSOFT headers (geant, boost, ...)
+#include "TCanvas.h"
+#include "TFile.h"
+#include "TH1.h"
+#include "TList.h"
+#include "TNamed.h"
+
+#include <boost/archive/binary_iarchive.hpp>
+#include <boost/serialization/utility.hpp>
+
+/// C/C++ headers
+#include <array>
+#include <iomanip>
+#include <stdexcept>
+#include <string>
+struct InitTaskError : std::runtime_error {
+  using std::runtime_error::runtime_error;
+};
+
+using namespace std;
+
+CbmDeviceBuildRawEvents::CbmDeviceBuildRawEvents() { fpAlgo = new CbmAlgoBuildRawEvents(); }
+
+void CbmDeviceBuildRawEvents::InitTask()
+try {
+  /// Read options from executable
+  LOG(info) << "Init options for CbmDeviceBuildRawEvents.";
+  fbFillHistos      = fConfig->GetValue<bool>("FillHistos");
+  fbIgnoreTsOverlap = fConfig->GetValue<bool>("IgnOverMs");
+
+  fsEvtOverMode   = fConfig->GetValue<std::string>("EvtOverMode");
+  fsRefDet        = fConfig->GetValue<std::string>("RefDet");
+  fvsAddDet       = fConfig->GetValue<std::vector<std::string>>("AddDet");
+  fvsDelDet       = fConfig->GetValue<std::vector<std::string>>("DelDet");
+  fvsSetTrigWin   = fConfig->GetValue<std::vector<std::string>>("SetTrigWin");
+  fvsSetTrigMinNb = fConfig->GetValue<std::vector<std::string>>("SetTrigMinNb");
+
+  fsChannelNameDataInput    = fConfig->GetValue<std::string>("TsNameIn");
+  fsChannelNameDataOutput   = fConfig->GetValue<std::string>("EvtNameOut");
+  fsChannelNameHistosInput  = fConfig->GetValue<std::string>("ChNameIn");
+  fsChannelNameHistosConfig = fConfig->GetValue<std::string>("ChNameHistCfg");
+  fsChannelNameCanvasConfig = fConfig->GetValue<std::string>("ChNameCanvCfg");
+  fsAllowedChannels[0]      = fsChannelNameDataInput;
+
+  fuPublishFreqTs  = fConfig->GetValue<uint32_t>("PubFreqTs");
+  fdMinPublishTime = fConfig->GetValue<double_t>("PubTimeMin");
+  fdMaxPublishTime = fConfig->GetValue<double_t>("PubTimeMax");
+
+  // Get the information about created channels from the device
+  // Check if the defined channels from the topology (by name)
+  // are in the list of channels which are possible/allowed
+  // for the device
+  // The idea is to check at initilization if the devices are
+  // properly connected. For the time beeing this is done with a
+  // nameing convention. It is not avoided that someone sends other
+  // data on this channel.
+  //logger::SetLogLevel("INFO");
+  int noChannel = fChannels.size();
+  LOG(info) << "Number of defined channels: " << noChannel;
+  for (auto const& entry : fChannels) {
+    LOG(info) << "Channel name: " << entry.first;
+    if (std::string::npos != entry.first.find(fsChannelNameDataInput)) {
+      if (!IsChannelNameAllowed(entry.first)) throw InitTaskError("Channel name does not match.");
+      OnData(entry.first, &CbmDeviceBuildRawEvents::HandleData);
+    }
+  }
+
+  /// FIXME: Disable clang formatting for now as it corrupts all alignment
+  /* clang-format off */
+
+  /// Initialize the Algorithm parameters
+  fpAlgo->SetFillHistos(fbFillHistos);
+  fpAlgo->SetIgnoreTsOverlap(fbIgnoreTsOverlap);
+  /// Extract Event Overlap Mode
+  EOverlapModeRaw mode =
+     ("NoOverlap"    == fsEvtOverMode ? EOverlapModeRaw::NoOverlap
+   : ("MergeOverlap" == fsEvtOverMode ? EOverlapModeRaw::MergeOverlap
+   : ("AllowOverlap" == fsEvtOverMode ? EOverlapModeRaw::AllowOverlap
+                                      : EOverlapModeRaw::NoOverlap)));
+  fpAlgo->SetEventOverlapMode(mode);
+  /// Extract refdet
+  RawEventBuilderDetector refDet = ("kT0"   == fsRefDet ? kRawEventBuilderDetT0
+                              : ("kSts"  == fsRefDet ? kRawEventBuilderDetMuch
+                              : ("kMuch" == fsRefDet ? kRawEventBuilderDetTrd
+                              : ("kTrd"  == fsRefDet ? kRawEventBuilderDetTrd
+                              : ("kTof"  == fsRefDet ? kRawEventBuilderDetTof
+                              : ("kRich" == fsRefDet ? kRawEventBuilderDetRich
+                              : ("kPsd"  == fsRefDet ? kRawEventBuilderDetPsd
+                                                     : kRawEventBuilderDetUndef)))))));
+  if (kRawEventBuilderDetUndef != refDet) {
+    fpAlgo->SetReferenceDetector(refDet);
+  } 
+  else {
+    LOG(info) << "CbmDeviceBuildRawEvents::InitTask => Trying to change "
+                 "reference to unsupported detector, ignored! "
+              << fsRefDet;
+  }
+
+  /// Extract detector to add if any
+  for (std::vector<std::string>::iterator itStrAdd = fvsAddDet.begin();
+       itStrAdd != fvsAddDet.end();
+       ++itStrAdd) {
+    RawEventBuilderDetector addDet = ("kT0"   == *itStrAdd ? kRawEventBuilderDetT0
+                                : ("kSts"  == *itStrAdd ? kRawEventBuilderDetSts
+                                : ("kMuch" == *itStrAdd ? kRawEventBuilderDetMuch
+                                : ("kTrd"  == *itStrAdd ? kRawEventBuilderDetTrd
+                                : ("kTof"  == *itStrAdd ? kRawEventBuilderDetTof
+                                : ("kRich" == *itStrAdd ? kRawEventBuilderDetRich
+                                : ("kPsd"  == *itStrAdd ? kRawEventBuilderDetPsd
+                                                        : kRawEventBuilderDetUndef)))))));
+    if (kRawEventBuilderDetUndef != addDet) {
+      fpAlgo->AddDetector(addDet);
+    } 
+    else {
+      LOG(info) << "CbmDeviceBuildRawEvents::InitTask => Trying to add "
+                   "unsupported detector, ignored! "
+                << (*itStrAdd);
+      continue;
+    }
+  } 
+
+     /// Extract detector to remove if any
+  for (std::vector<std::string>::iterator itStrRem = fvsDelDet.begin();
+       itStrRem != fvsDelDet.end();
+       ++itStrRem) {
+    RawEventBuilderDetector remDet = ("kT0"   == *itStrRem ? kRawEventBuilderDetT0
+                                : ("kSts"  == *itStrRem ? kRawEventBuilderDetSts
+                                : ("kMuch" == *itStrRem ? kRawEventBuilderDetMuch
+                                : ("kTrd"  == *itStrRem ? kRawEventBuilderDetTrd
+                                : ("kTof"  == *itStrRem ? kRawEventBuilderDetTof
+                                : ("kRich" == *itStrRem ? kRawEventBuilderDetRich
+                                : ("kPsd"  == *itStrRem ? kRawEventBuilderDetPsd
+                                                        : kRawEventBuilderDetUndef)))))));
+    if (kRawEventBuilderDetUndef != remDet) {
+      fpAlgo->RemoveDetector(remDet);
+    } 
+    else {
+      LOG(info) << "CbmDeviceBuildRawEvents::InitTask => Trying to remove "
+                   "unsupported detector, ignored! "
+                << (*itStrRem);
+      continue;
+    } 
+  }  
+     /// Extract Trigger window to add if any
+  for (std::vector<std::string>::iterator itStrTrigWin = fvsSetTrigWin.begin();
+       itStrTrigWin != fvsSetTrigWin.end();
+       ++itStrTrigWin) {
+    size_t charPosDel = (*itStrTrigWin).find(',');
+    if (std::string::npos == charPosDel) {
+      LOG(info)
+        << "CbmDeviceBuildRawEvents::InitTask => "
+        << "Trying to set trigger window with invalid option pattern, ignored! "
+        << " (Should be ECbmModuleId,dWinBeg,dWinEnd but instead found "
+        << (*itStrTrigWin) << " )";
+      continue;
+    } 
+
+    /// Detector Enum Tag
+    std::string sSelDet = (*itStrTrigWin).substr(0, charPosDel);
+    ECbmModuleId selDet = ("kT0"   == sSelDet ? ECbmModuleId::kT0
+                        : ("kSts"  == sSelDet ? ECbmModuleId::kSts
+                        : ("kMuch" == sSelDet ? ECbmModuleId::kMuch
+                        : ("kTrd"  == sSelDet ? ECbmModuleId::kTrd
+                        : ("kTof"  == sSelDet ? ECbmModuleId::kTof
+                        : ("kRich" == sSelDet ? ECbmModuleId::kRich
+                        : ("kPsd"  == sSelDet ? ECbmModuleId::kPsd
+                                              : ECbmModuleId::kNotExist)))))));
+    if (ECbmModuleId::kNotExist == selDet) {
+      LOG(info)
+        << "CbmDeviceBuildRawEvents::InitTask => "
+        << "Trying to set trigger window for unsupported detector, ignored! "
+        << sSelDet;
+      continue;
+    } 
+
+    /// Window beginning
+    charPosDel++;
+    std::string sNext = (*itStrTrigWin).substr(charPosDel);
+    charPosDel        = sNext.find(',');
+    if (std::string::npos == charPosDel) {
+      LOG(info)
+        << "CbmDeviceBuildRawEvents::InitTask => "
+        << "Trying to set trigger window with invalid option pattern, ignored! "
+        << " (Should be ECbmModuleId,dWinBeg,dWinEnd but instead found "
+        << (*itStrTrigWin) << " )";
+      continue;
+    } 
+    Double_t dWinBeg = std::stod(sNext.substr(0, charPosDel));
+
+    /// Window end
+    charPosDel++;
+    Double_t dWinEnd = std::stod(sNext.substr(charPosDel));
+
+    fpAlgo->SetTriggerWindow(selDet, dWinBeg, dWinEnd);
+  }  
+     /// Extract MinNb for trigger if any
+  for (std::vector<std::string>::iterator itStrMinNb = fvsSetTrigMinNb.begin();
+       itStrMinNb != fvsSetTrigMinNb.end();
+       ++itStrMinNb) {
+    size_t charPosDel = (*itStrMinNb).find(',');
+    if (std::string::npos == charPosDel) {
+      LOG(info)
+        << "CbmDeviceBuildRawEvents::InitTask => "
+        << "Trying to set trigger min Nb with invalid option pattern, ignored! "
+        << " (Should be ECbmModuleId,uMinNb but instead found " << (*itStrMinNb)
+        << " )";
+      continue;
+    }
+
+    /// Detector Enum Tag
+    std::string sSelDet = (*itStrMinNb).substr(0, charPosDel);
+    ECbmModuleId selDet = ("kT0"   == sSelDet ? ECbmModuleId::kT0
+                        : ("kSts"  == sSelDet ? ECbmModuleId::kSts
+                        : ("kMuch" == sSelDet ? ECbmModuleId::kMuch
+                        : ("kTrd"  == sSelDet ? ECbmModuleId::kTrd
+                        : ("kTof"  == sSelDet ? ECbmModuleId::kTof
+                        : ("kRich" == sSelDet ? ECbmModuleId::kRich
+                        : ("kPsd"  == sSelDet ? ECbmModuleId::kPsd
+                                              : ECbmModuleId::kNotExist)))))));
+    if (ECbmModuleId::kNotExist == selDet) {
+      LOG(info)
+        << "CbmDeviceBuildRawEvents::InitTask => "
+        << "Trying to set trigger min Nb for unsupported detector, ignored! "
+        << sSelDet;
+      continue;
+    }
+
+    /// Min number
+    charPosDel++;
+    UInt_t uMinNb = std::stoul((*itStrMinNb).substr(charPosDel));
+
+    fpAlgo->SetTriggerMinNumber(selDet, uMinNb);
+  } 
+
+  /// FIXME: Re-enable clang formatting after formatted lines
+  /* clang-format on */
+
+  /// Create input vectors
+  fvDigiT0   = new std::vector<CbmTofDigi>();
+  fvDigiSts  = new std::vector<CbmStsDigi>();
+  fvDigiMuch = new std::vector<CbmMuchBeamTimeDigi>();
+  fvDigiTrd  = new std::vector<CbmTrdDigi>();
+  fvDigiTof  = new std::vector<CbmTofDigi>();
+  fvDigiRich = new std::vector<CbmRichDigi>();
+  fvDigiPsd  = new std::vector<CbmPsdDigi>();
+
+  fTimeSliceMetaDataArray = new TClonesArray("TimesliceMetaData", 1);
+  if (NULL == fTimeSliceMetaDataArray) { throw InitTaskError("Failed creating the TS meta data TClonesarray "); }
+  fpAlgo->SetTimeSliceMetaDataArray(fTimeSliceMetaDataArray);
+
+  /// Digis storage
+  fpAlgo->SetT0Digis(fvDigiT0);
+  fpAlgo->SetStsDigis(fvDigiSts);
+  fpAlgo->SetMuchBeamTimeDigis(fvDigiMuch);
+  fpAlgo->SetTrdDigis(fvDigiTrd);
+  fpAlgo->SetTofDigis(fvDigiTof);
+  fpAlgo->SetRichDigis(fvDigiRich);
+  fpAlgo->SetPsdDigis(fvDigiPsd);
+
+  // Mvd currently not implemented in event builder
+  //std::vector<CbmMvdDigi>* pMvdDigi = new std::vector<CbmMvdDigi>();
+
+  /// Create output TClonesArray
+  fEvents = new TClonesArray("CbmEvent", 500);
+
+  /// Now that everything is set, initialize the Algorithm
+  if (kFALSE == fpAlgo->InitAlgo()) { throw InitTaskError("Failed to initilize the algorithm class."); }
+
+  /// Histograms management
+  if (kTRUE == fbFillHistos) {
+    /// Obtain vector of pointers on each histo from the algo (+ optionally desired folder)
+    std::vector<std::pair<TNamed*, std::string>> vHistos = fpAlgo->GetHistoVector();
+    /// Obtain vector of pointers on each canvas from the algo (+ optionally desired folder)
+    std::vector<std::pair<TCanvas*, std::string>> vCanvases = fpAlgo->GetCanvasVector();
+
+    /// 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()
+      //                   ;
+      fArrayHisto.Add(vHistos[uHisto].first);
+      std::pair<std::string, std::string> psHistoConfig(vHistos[uHisto].first->GetName(), vHistos[uHisto].second);
+      fvpsHistosFolder.push_back(psHistoConfig);
+
+      /// Serialize the vector of histo config into a single MQ message
+      FairMQMessagePtr messageHist(NewMessage());
+      Serialize<BoostSerializer<std::pair<std::string, std::string>>>(*messageHist, psHistoConfig);
+
+      /// Send message to the common histogram config messages queue
+      if (Send(messageHist, fsChannelNameHistosConfig) < 0) { throw InitTaskError("Problem sending histo config"); }
+      LOG(info) << "Config of hist  " << psHistoConfig.first.data() << " in folder " << psHistoConfig.second.data();
+    }
+
+    /// 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();
+      std::string sCanvConf = GenerateCanvasConfigString(vCanvases[uCanv].first);
+
+      std::pair<std::string, std::string> psCanvConfig(sCanvName, sCanvConf);
+
+      fvpsCanvasConfig.push_back(psCanvConfig);
+
+      /// Serialize the vector of canvas config into a single MQ message
+      FairMQMessagePtr messageCan(NewMessage());
+      Serialize<BoostSerializer<std::pair<std::string, std::string>>>(*messageCan, psCanvConfig);
+
+      /// Send message to the common canvas config messages queue
+      if (Send(messageCan, fsChannelNameCanvasConfig) < 0) { throw InitTaskError("Problem sending canvas config"); }
+
+      LOG(info) << "Config string of Canvas  " << psCanvConfig.first.data() << " is " << psCanvConfig.second.data();
+    }
+  }
+}
+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);
+}
+
+bool CbmDeviceBuildRawEvents::IsChannelNameAllowed(std::string channelName)
+{
+  for (auto const& entry : fsAllowedChannels) {
+    std::size_t pos1 = channelName.find(entry);
+    if (pos1 != std::string::npos) {
+      const vector<std::string>::const_iterator pos =
+        std::find(fsAllowedChannels.begin(), fsAllowedChannels.end(), entry);
+      const vector<std::string>::size_type idx = pos - fsAllowedChannels.begin();
+      LOG(info) << "Found " << entry << " in " << channelName;
+      LOG(info) << "Channel name " << channelName << " found in list of allowed channel names at position " << idx;
+      return true;
+    }
+  }
+  LOG(info) << "Channel name " << channelName << " not found in list of allowed channel names.";
+  LOG(error) << "Stop device.";
+  return false;
+}
+
+// handler is called whenever a message arrives on "data", with a reference to the message and a sub-channel index (here 0)
+bool CbmDeviceBuildRawEvents::HandleData(FairMQParts& parts, int /*index*/)
+{
+  fulNumMessages++;
+  LOG(debug) << "Received message number " << fulNumMessages << " with " << parts.Size() << " parts"
+             << ", size0: " << parts.At(0)->GetSize();
+
+  if (0 == fulNumMessages % 10000) LOG(info) << "Received " << fulNumMessages << " messages";
+
+  /// Extract unpacked data from input message
+  uint32_t uPartIdx = 0;
+
+  /// TS metadata
+  Deserialize<RootSerializer>(*parts.At(uPartIdx), fTsMetaData);
+  new ((*fTimeSliceMetaDataArray)[fTimeSliceMetaDataArray->GetEntriesFast()])
+    TimesliceMetaData(std::move(*fTsMetaData));
+  ++uPartIdx;
+
+  /// T0
+  std::string msgStrT0(static_cast<char*>(parts.At(uPartIdx)->GetData()), (parts.At(uPartIdx))->GetSize());
+  std::istringstream issT0(msgStrT0);
+  boost::archive::binary_iarchive inputArchiveT0(issT0);
+  inputArchiveT0 >> *fvDigiT0;
+  ++uPartIdx;
+
+  /// STS
+  std::string msgStrSts(static_cast<char*>(parts.At(uPartIdx)->GetData()), (parts.At(uPartIdx))->GetSize());
+  std::istringstream issSts(msgStrSts);
+  boost::archive::binary_iarchive inputArchiveSts(issSts);
+  inputArchiveSts >> *fvDigiSts;
+  ++uPartIdx;
+
+  /// MUCH
+  std::string msgStrMuch(static_cast<char*>(parts.At(uPartIdx)->GetData()), (parts.At(uPartIdx))->GetSize());
+  std::istringstream issMuch(msgStrMuch);
+  boost::archive::binary_iarchive inputArchiveMuch(issMuch);
+  inputArchiveMuch >> *fvDigiMuch;
+  ++uPartIdx;
+
+  /// TRD
+  std::string msgStrTrd(static_cast<char*>(parts.At(uPartIdx)->GetData()), (parts.At(uPartIdx))->GetSize());
+  std::istringstream issTrd(msgStrTrd);
+  boost::archive::binary_iarchive inputArchiveTrd(issTrd);
+  inputArchiveTrd >> *fvDigiTrd;
+  ++uPartIdx;
+
+  /// T0F
+  std::string msgStrTof(static_cast<char*>(parts.At(uPartIdx)->GetData()), (parts.At(uPartIdx))->GetSize());
+  std::istringstream issTof(msgStrTof);
+  boost::archive::binary_iarchive inputArchiveTof(issTof);
+  inputArchiveTof >> *fvDigiTof;
+  ++uPartIdx;
+
+  /// RICH
+  std::string msgStrRich(static_cast<char*>(parts.At(uPartIdx)->GetData()), (parts.At(uPartIdx))->GetSize());
+  std::istringstream issRich(msgStrRich);
+  boost::archive::binary_iarchive inputArchiveRich(issRich);
+  inputArchiveRich >> *fvDigiRich;
+  ++uPartIdx;
+
+  /// PSD
+  std::string msgStrPsd(static_cast<char*>(parts.At(uPartIdx)->GetData()), (parts.At(uPartIdx))->GetSize());
+  std::istringstream issPsd(msgStrPsd);
+  boost::archive::binary_iarchive inputArchivePsd(issPsd);
+  inputArchivePsd >> *fvDigiPsd;
+  ++uPartIdx;
+
+  /// Call Algo ProcessTs method
+  fpAlgo->ProcessTs();
+
+  /// Send events vector to ouput
+  if (!SendEvents(parts)) return false;
+
+  /// Clear metadata
+  fTimeSliceMetaDataArray->Clear();
+
+  /// Clear vectors
+  fvDigiT0->clear();
+  fvDigiSts->clear();
+  fvDigiMuch->clear();
+  fvDigiTrd->clear();
+  fvDigiTof->clear();
+  fvDigiRich->clear();
+  fvDigiPsd->clear();
+
+  /// Clear event vector after usage
+  fpAlgo->ClearEventVector();
+  fEvents->Clear("C");
+
+  /// Histograms management
+  if (kTRUE == fbFillHistos) {
+    /// 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();
+    }
+  }
+
+  return true;
+}
+
+bool CbmDeviceBuildRawEvents::SendEvents(FairMQParts& partsIn)
+{
+  /// Clear events TClonesArray before usage.
+  fEvents->Delete();
+
+  /// Get vector reference from algo
+  std::vector<CbmEvent*> vEvents = fpAlgo->GetEventVector();
+
+  /// Move CbmEvent from temporary vector to TClonesArray
+  for (CbmEvent* event : vEvents) {
+    LOG(debug) << "Vector: " << event->ToString();
+    new ((*fEvents)[fEvents->GetEntriesFast()]) CbmEvent(std::move(*event));
+    LOG(debug) << "TClonesArray: " << static_cast<CbmEvent*>(fEvents->At(fEvents->GetEntriesFast() - 1))->ToString();
+  }
+
+  /// Serialize the array of events into a single MQ message
+  FairMQMessagePtr message(NewMessage());
+  Serialize<RootSerializer>(*message, fEvents);
+
+  /// Add it at the end of the input composed message
+  FairMQParts partsOut(std::move(partsIn));
+  partsOut.AddPart(std::move(message));
+
+  if (Send(partsOut, fsChannelNameDataOutput) < 0) {
+    LOG(error) << "Problem sending data to " << fsChannelNameDataOutput;
+    return false;
+  }
+
+  return true;
+}
+
+bool CbmDeviceBuildRawEvents::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;
+  }
+
+  /// Reset the histograms after sending them (but do not reset the time)
+  fpAlgo->ResetHistograms(kFALSE);
+
+  return true;
+}
+
+CbmDeviceBuildRawEvents::~CbmDeviceBuildRawEvents()
+{
+  /// Clear metadata
+  fTimeSliceMetaDataArray->Clear();
+  delete fTsMetaData;
+
+  /// Clear vectors
+  fvDigiT0->clear();
+  fvDigiSts->clear();
+  fvDigiMuch->clear();
+  fvDigiTrd->clear();
+  fvDigiTof->clear();
+  fvDigiRich->clear();
+  fvDigiPsd->clear();
+
+  /// Clear events TClonesArray
+  fEvents->Delete();
+
+  delete fpRun;
+  delete fTimeSliceMetaDataArray;
+  delete fEvents;
+  delete fpAlgo;
+}
+
+void CbmDeviceBuildRawEvents::Finish() {}
diff --git a/MQ/mcbm/CbmDeviceBuildRawEvents.h b/MQ/mcbm/CbmDeviceBuildRawEvents.h
new file mode 100644
index 0000000000..b1f1b2fb97
--- /dev/null
+++ b/MQ/mcbm/CbmDeviceBuildRawEvents.h
@@ -0,0 +1,135 @@
+/**
+ * CbmDeviceBuildRawEvents.h
+ *
+ * @since 2020-05-24
+ * @author P.-A. Loizeau
+ */
+
+#ifndef CBMDEVICEBUILDRAWEVENTS_H_
+#define CBMDEVICEBUILDRAWEVENTS_H_
+
+/// CBM headers
+#include "CbmAlgoBuildRawEvents.h"
+#include "CbmMuchBeamTimeDigi.h"
+#include "CbmPsdDigi.h"
+#include "CbmRichDigi.h"
+#include "CbmStsDigi.h"
+#include "CbmTofDigi.h"
+#include "CbmTrdDigi.h"
+
+/// FAIRROOT headers
+#include "FairMQDevice.h"
+
+/// FAIRSOFT headers (geant, boost, ...)
+#include "Rtypes.h"
+#include "TMessage.h"
+#include "TObjArray.h"
+
+/// C/C++ headers
+#include <chrono>
+#include <map>
+#include <vector>
+
+class TList;
+class TClonesArray;
+class FairRunOnline;
+class TimesliceMetaData;
+
+class CbmDeviceBuildRawEvents : public FairMQDevice {
+public:
+  CbmDeviceBuildRawEvents();
+  virtual ~CbmDeviceBuildRawEvents();
+
+protected:
+  virtual void InitTask();
+  bool HandleData(FairMQParts&, int);
+  bool HandleCommand(FairMQMessagePtr&, int);
+
+private:
+  /// Constants
+
+  /// Control flags
+  Bool_t fbIgnoreTsOverlap = kFALSE;  //! Ignore data in Overlap part of the TS
+  Bool_t fbFillHistos      = kTRUE;   //! Switch ON/OFF filling of histograms
+
+  /// User settings parameters
+  /// Algo enum settings
+  std::string fsEvtOverMode                = "NoOverlap";
+  std::string fsRefDet                     = "kT0";
+  std::vector<std::string> fvsAddDet       = {};
+  std::vector<std::string> fvsDelDet       = {};
+  std::vector<std::string> fvsSetTrigWin   = {};
+  std::vector<std::string> fvsSetTrigMinNb = {};
+  /// message queues
+  std::string fsChannelNameDataInput    = "unpts_0";
+  std::string fsChannelNameDataOutput   = "events";
+  std::string fsChannelNameCommands     = "commands";
+  std::string fsChannelNameHistosInput  = "histogram-in";
+  std::string fsChannelNameHistosConfig = "histo-conf";
+  std::string fsChannelNameCanvasConfig = "canvas-conf";
+  /// Histograms management
+  uint32_t fuPublishFreqTs  = 100;
+  double_t fdMinPublishTime = 0.5;
+  double_t fdMaxPublishTime = 5.0;
+
+  /// List of MQ channels names
+  std::vector<std::string> fsAllowedChannels = {fsChannelNameDataInput};
+
+  /// Statistics & first TS rejection
+  uint64_t fulNumMessages                                = 0;
+  uint64_t fulTsCounter                                  = 0;
+  std::chrono::system_clock::time_point fLastPublishTime = std::chrono::system_clock::now();
+
+  /// Processing algos
+  CbmAlgoBuildRawEvents* fpAlgo = nullptr;
+
+  /// TS MetaData stable values storage
+  size_t fuNbCoreMsPerTs    = 0;        //!
+  size_t fuNbOverMsPerTs    = 0;        //!
+  Double_t fdMsSizeInNs     = 1280000;  //! Size of a single MS, [nanoseconds]
+  Double_t fdTsCoreSizeInNs = -1.0;     //! Total size of the core MS in a TS, [nanoseconds]
+  Double_t fdTsOverSizeInNs = -1.0;     //! Total size of the overlap MS in a TS, [nanoseconds]
+  Double_t fdTsFullSizeInNs = -1.0;     //! Total size of all MS in a TS, [nanoseconds]
+
+  /// Data reception
+  /// TS MetaData storage
+  TClonesArray* fTimeSliceMetaDataArray = nullptr;  //!
+  TimesliceMetaData* fTsMetaData        = nullptr;
+  /// Digis storage
+  std::vector<CbmTofDigi>* fvDigiT0            = nullptr;
+  std::vector<CbmStsDigi>* fvDigiSts           = nullptr;
+  std::vector<CbmMuchBeamTimeDigi>* fvDigiMuch = nullptr;
+  std::vector<CbmTrdDigi>* fvDigiTrd           = nullptr;
+  std::vector<CbmTofDigi>* fvDigiTof           = nullptr;
+  std::vector<CbmRichDigi>* fvDigiRich         = nullptr;
+  std::vector<CbmPsdDigi>* fvDigiPsd           = nullptr;
+  /// Data emission
+  TClonesArray* fEvents = nullptr;  //! output container of CbmEvents
+  //      std::vector< CbmEvent * > &        fEventVector;    //! vector with all created events
+
+  /// Internal data registration (for FairRootManager -> DigiManager links)
+  FairRunOnline* fpRun = nullptr;
+
+  /// 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 = {};
+
+  bool IsChannelNameAllowed(std::string channelName);
+  void Finish();
+  bool SendEvents(FairMQParts& partsIn);
+  bool SendHistograms();
+};
+
+// special class to expose protected TMessage constructor
+class CbmMQTMessage : public TMessage {
+public:
+  CbmMQTMessage(void* buf, Int_t len) : TMessage(buf, len) { ResetBit(kIsOwner); }
+};
+
+
+#endif /* CBMDEVICEBUILDRAWEVENTS_H_ */
diff --git a/MQ/mcbm/runBuildRawEvents.cxx b/MQ/mcbm/runBuildRawEvents.cxx
new file mode 100644
index 0000000000..f0487f28dd
--- /dev/null
+++ b/MQ/mcbm/runBuildRawEvents.cxx
@@ -0,0 +1,48 @@
+#include "CbmDeviceBuildRawEvents.h"
+
+#include <iomanip>
+#include <string>
+
+#include "runFairMQDevice.h"
+
+namespace bpo = boost::program_options;
+using namespace std;
+
+void addCustomOptions(bpo::options_description& options)
+{
+  options.add_options()("FillHistos", bpo::value<bool>()->default_value(true),
+                        "Fill histograms and send them to histo server if true");
+  options.add_options()("IgnTsOver", bpo::value<bool>()->default_value(false), "Ignore TS overlap if true");
+  options.add_options()("EvtOverMode", bpo::value<std::string>()->default_value("NoOverlap"),
+                        "Set the event overlap mode, use string matching an EOverlapMode ");
+  options.add_options()("RefDet", bpo::value<std::string>()->default_value("kT0"),
+                        "Set the reference (seed) detector, use string matching an ECbmModuleId ");
+  options.add_options()("AddDet", bpo::value<std::vector<std::string>>()->multitoken()->composing(),
+                        "Add a detector for digis selection, use string matching an ECbmModuleId ");
+  options.add_options()("DelDet", bpo::value<std::vector<std::string>>()->multitoken()->composing(),
+                        "Remove a detector for digis selection, use string matching an "
+                        "ECbmModuleId ");
+  options.add_options()("SetTrigWin", bpo::value<std::vector<std::string>>()->multitoken()->composing(),
+                        "Set trigger window for selected detector, use string matching "
+                        "ECbmModuleId,dWinBeg,dWinEnd e.g. kSts,-10.5,100.0");
+  options.add_options()("SetTrigMinNb", bpo::value<std::vector<std::string>>()->multitoken()->composing(),
+                        "Set minimum number of digis for selected detector, use string matching "
+                        "ECbmModuleId,uMinNb e.g. kTof,10");
+  options.add_options()("TsNameIn", bpo::value<std::string>()->default_value("unpts_0"),
+                        "MQ channel name for unpacked TS data");
+  options.add_options()("EvtNameOut", bpo::value<std::string>()->default_value("events"),
+                        "MQ channel name for built events");
+  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(100), "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");
+}
+
+FairMQDevicePtr getDevice(const FairMQProgOptions& /*config*/) { return new CbmDeviceBuildRawEvents(); }
diff --git a/MQ/mcbm/startMQBuildRawEvents.sh.in b/MQ/mcbm/startMQBuildRawEvents.sh.in
new file mode 100755
index 0000000000..0b94c5b453
--- /dev/null
+++ b/MQ/mcbm/startMQBuildRawEvents.sh.in
@@ -0,0 +1,264 @@
+#!/bin/bash
+$SIMPATH/bin/fairmq-shmmonitor --cleanup
+
+if [ $# -ge 1 ]; then
+  _nbmoni=$1
+  ((_pubfreqts = $_nbmoni*100 ))
+  _pubminsec=1.0
+  _pubmaxsec=10.0
+
+  if [ $# -ge 4 ]; then
+    _filename=""
+    _dirname=""
+    _hostname=$4
+
+    if [ $# -ge 5 ]; then
+      _pubfreqts=$5
+
+      if [ $# -ge 6 ]; then
+        _pubminsec=$6
+
+        if [ $# -ge 7 ]; then
+          _pubmaxsec=$7
+        fi
+      fi
+    fi
+  elif [ $# -ge 2 ]; then
+    _filename=$2
+    _hostname=""
+    if [ $# -eq 3 ]; then
+      _dirname=$3
+    else
+      _dirname=""
+    fi
+  else
+    echo 'Starting connection to local stream'
+    echo ' for other usages, please supply at least a filename.'
+    echo 'Possible usages are:'
+    echo 'startMQMcbmPulserMonitor2020.sh'
+    echo 'startMQMcbmPulserMonitor2020.sh <Nb Unp & Moni processes>'
+    echo 'startMQMcbmPulserMonitor2020.sh <Nb Unp & Moni processes> <full filename pattern list>'
+    echo 'startMQMcbmPulserMonitor2020.sh <Nb Unp & Moni processes> <filename pattern> <folder_path>'
+    echo 'startMQMcbmPulserMonitor2020.sh <Nb Unp & Moni processes> "" "" <hostname(s) list>'
+    echo 'startMQMcbmPulserMonitor2020.sh <Nb Unp & Moni processes> "" "" <hostname(s) list> <Hist publish freq. in TS>'
+    echo 'startMQMcbmPulserMonitor2020.sh <Nb Unp & Moni processes> "" "" <hostname(s) list> <Hist publish freq. in TS> <Min Hist pub. in s>'
+    echo 'startMQMcbmPulserMonitor2020.sh <Nb Unp & Moni processes> "" "" <hostname(s) list> <Hist publish freq. in TS> <Min Hist pub. in s> <Max Hist pub. in s>'
+    _filename=""
+    _dirname=""
+    _hostname="localhost"
+  fi
+else
+  echo 'Starting connection to local stream with 1 monitor process'
+  echo ' for other usages, please supply at least a filename.'
+  echo 'Possible usages are:'
+  echo 'startMQMcbmPulserMonitor2020.sh'
+  echo 'startMQMcbmPulserMonitor2020.sh <Nb Unp & Moni processes>'
+  echo 'startMQMcbmPulserMonitor2020.sh <Nb Unp & Moni processes> <full filename pattern list>'
+  echo 'startMQMcbmPulserMonitor2020.sh <Nb Unp & Moni processes> <filename pattern> <folder_path>'
+  echo 'startMQMcbmPulserMonitor2020.sh <Nb Unp & Moni processes> "" "" <hostname(s) list>'
+  echo 'startMQMcbmPulserMonitor2020.sh <Nb Unp & Moni processes> "" "" <hostname(s) list> <Hist publish freq. in TS>'
+  echo 'startMQMcbmPulserMonitor2020.sh <Nb Unp & Moni processes> "" "" <hostname(s) list> <Hist publish freq. in TS> <Min Hist pub. in s>'
+  echo 'startMQMcbmPulserMonitor2020.sh <Nb Unp & Moni processes> "" "" <hostname(s) list> <Hist publish freq. in TS> <Min Hist pub. in s> <Max Hist pub. in s>'
+  _filename=""
+  _dirname=""
+  _hostname="localhost"
+  _nbmoni=1
+  _pubfreqts=100
+  _pubminsec=1.0
+  _pubmaxsec=10.0
+fi
+
+_parfileSts=$VMCWORKDIR/macro/beamtime/mcbm2020/mStsPar.par
+_parfileMuch=$VMCWORKDIR/macro/beamtime/mcbm2020/mMuchPar.par
+_parfileTrdAsic=$VMCWORKDIR/parameters/trd/trd_v18q_mcbm.asic.par
+_parfileTrdDigi=$VMCWORKDIR/parameters/trd/trd_v18q_mcbm.digi.par
+_parfileTrdGas=$VMCWORKDIR/parameters/trd/trd_v18q_mcbm.gas.par
+_parfileTrdGain=$VMCWORKDIR/parameters/trd/trd_v18q_mcbm.gain.par
+_parfileTof=$VMCWORKDIR/macro/beamtime/mcbm2020/mTofPar.par
+_parfileRich=$VMCWORKDIR/macro/beamtime/mcbm2020/mRichPar.par
+_parfilePsd=$VMCWORKDIR/macro/beamtime/mcbm2020/mPsdPar.par
+
+LOGFILETAG=`hostname`
+LOGFILETAG+="_"
+LOGFILETAG+=`date +%Y_%m_%d_%H_%M_%S`
+LOGFILETAG+=".log"
+
+(( _paraBuffSz=100 ))
+(( _singBuffSz=_paraBuffSz*_nbmoni ))
+
+echo "Buffer size for parallel  devices $_paraBuffSz"
+echo "Buffer size for singleton devices $_singBuffSz"
+
+SAMPLER="MultiTsaSampler"
+SAMPLER+=" --id sampler1"
+#SAMPLER+=" --max-timeslices 0"
+#SAMPLER+=" --max-timeslices 10"
+#SAMPLER+=" --max-timeslices 100"
+SAMPLER+=" --max-timeslices 300"
+#SAMPLER+=" --max-timeslices 1000"
+SAMPLER+=" --severity info"
+#SAMPLER+=" --flib-port 10"
+if [ "$_hostname" != "" ]; then
+  SAMPLER+=" --flib-host $_hostname"
+elif [ "$_filename" != "" ]; then
+  SAMPLER+=" --filename $_filename"
+  if [ "$_dirname" != "" ]; then
+    SAMPLER+=" --dirname $_dirname"
+  fi
+fi
+SAMPLER+=" --high-water-mark 1000"
+SAMPLER+=" --no-split-ts 1"
+SAMPLER+=" --ChNameMissTs missedts"
+SAMPLER+=" --ChNameCmds commands"
+SAMPLER+=" --channel-config name=fullts,type=push,method=bind,address=tcp://127.0.0.1:11555"
+#SAMPLER+=" --channel-config name=fullts,type=push,method=bind,address=tcp://127.0.0.1:11555,sndBufSize=$_singBuffSz,rcvBuffSize=$_paraBuffSz"
+#SAMPLER+=" --transport shmem"
+SAMPLER+=" --transport zeromq"
+#SAMPLER+=" --transport nanomsg"
+SAMPLER+=" --channel-config name=missedts,type=pub,method=bind,address=tcp://127.0.0.1:11006"
+SAMPLER+=" --channel-config name=commands,type=pub,method=bind,address=tcp://127.0.0.1:11007"
+# Replaces log filename Xterm.log.hostname.yyyy.mm.dd.hh.mm.ss.XXXXXX
+# with ProcessName_hostname_yyyy_mm_dd_hh_mm_ss.log
+SAMPLER_LOG="sampler1_$LOGFILETAG"
+xterm -l -lf $SAMPLER_LOG -geometry 80x23+0+0 -hold -e @CMAKE_BINARY_DIR@/bin/MQ/source/$SAMPLER  &
+
+echo $SAMPLER
+
+_iMoni=0
+while (( _iMoni < _nbmoni )); do
+  (( _yOffset=200*_iMoni ))
+  (( _iMoni += 1 ))
+  (( _iPort = 11680 + _iMoni ))
+
+  UNPACKER="McbmUnpack"
+  UNPACKER+=" --id unp$_iMoni"
+  UNPACKER+=" --severity info"
+  UNPACKER+=" --IgnOverMs 1"
+  UNPACKER+=" --SetTimeOffs kSTS,-985"
+  UNPACKER+=" --SetTimeOffs kMUCH,-885"
+  UNPACKER+=" --SetTimeOffs kTRD,-25"
+  UNPACKER+=" --SetTimeOffs kTOF,25"
+  UNPACKER+=" --SetTimeOffs kRICH,-310"
+  UNPACKER+=" --SetTimeOffs kPSD,-240"
+  UNPACKER+=" --TsNameOut unpts$_iMoni"
+  UNPACKER+=" --channel-config name=fullts,type=pull,method=connect,address=tcp://127.0.0.1:11555"
+  #UNPACKER+=" --transport shmem"
+  UNPACKER+=" --transport zeromq"
+  #UNPACKER+=" --transport nanomsg"
+#  UNPACKER+=" --channel-config name=parameters,type=req,method=connect,transport=zeromq,address=tcp://127.0.0.1:11005"
+  UNPACKER+=" --channel-config name=parameters,type=req,method=connect,transport=zeromq,address=tcp://127.0.0.1:11005,rateLogging=0"
+  UNPACKER+=" --channel-config name=unpts$_iMoni,type=push,method=bind,transport=zeromq,address=tcp://127.0.0.1:$_iPort"
+#  UNPACKER+=" --channel-config name=unpts$_iMoni,type=push,method=bind,transport=zeromq,address=tcp://127.0.0.1:$_iPort,sndBufSize=$_paraBuffSz,rcvBuffSize=$_paraBuffSz"
+#  UNPACKER+=" --channel-config name=commands,type=sub,method=connect,transport=zeromq,address=tcp://127.0.0.1:11007"
+  #UNPACKER+=" --channel-config name=histogram-in,type=pub,method=connect,transport=zeromq,address=tcp://127.0.0.1:11666"
+  #UNPACKER+=" --channel-config name=histo-conf,type=pub,method=connect,transport=zeromq,address=tcp://127.0.0.1:11667,rateLogging=0"
+  #UNPACKER+=" --channel-config name=canvas-conf,type=pub,method=connect,transport=zeromq,address=tcp://127.0.0.1:11668,rateLogging=0"
+  # Replaces log filename Xterm.log.hostname.yyyy.mm.dd.hh.mm.ss.XXXXXX
+  # with ProcessName_hostname_yyyy_mm_dd_hh_mm_ss.log
+  UNPACKER_LOG="unp$_iMoni"
+  UNPACKER_LOG+="_$LOGFILETAG"
+  VALGRIND_UNP="valgrind -v --error-limit=no --suppressions=$ROOTSYS/share/root/etc/valgrind-root.supp --leak-check=full --show-reachable=yes --log-file=valgrind_unp_log.txt"
+  VALGHEAP_UNP="valgrind -v --tool=massif --massif-out-file=valgrind_unp_massif.out"
+  xterm -l -lf $UNPACKER_LOG -geometry 80x23+400+$_yOffset -hold -e @CMAKE_BINARY_DIR@/bin/MQ/mcbm/$UNPACKER &
+#  xterm -l -lf $UNPACKER_LOG -geometry 80x23+400+$_yOffset -hold -e $VALGRIND_UNP @CMAKE_BINARY_DIR@/bin/MQ/mcbm/$UNPACKER &
+#  xterm -l -lf $UNPACKER_LOG -geometry 80x23+400+$_yOffset -hold -e $VALGHEAP_UNP @CMAKE_BINARY_DIR@/bin/MQ/mcbm/$UNPACKER &
+
+  EVTBUILDER="BuildRawEvents"
+  EVTBUILDER+=" --id build$_iMoni"
+  EVTBUILDER+=" --severity info"
+  EVTBUILDER+=" --PubFreqTs $_pubfreqts"
+  EVTBUILDER+=" --PubTimeMin $_pubminsec"
+  EVTBUILDER+=" --PubTimeMax $_pubmaxsec"
+  EVTBUILDER+=" --FillHistos true"
+  EVTBUILDER+=" --IgnTsOver false"
+  EVTBUILDER+=" --EvtOverMode NoOverlap"
+  EVTBUILDER+=" --SetTrigWin kT0,-1,10" # To get T0 Digis (seed + close-by digis) in the event
+  EVTBUILDER+=" --SetTrigWin kSts,-50,100"
+  EVTBUILDER+=" --SetTrigWin kMuch,-150,50"
+  EVTBUILDER+=" --SetTrigWin kTrd,-250,100"
+  EVTBUILDER+=" --SetTrigWin kTof,-150,10"
+  EVTBUILDER+=" --SetTrigWin kRich,-150,20"
+  EVTBUILDER+=" --SetTrigWin kPsd,-50,10"
+  EVTBUILDER+=" --SetTrigMinNb kT0,1"
+  EVTBUILDER+=" --SetTrigMinNb kSts,0"
+  EVTBUILDER+=" --SetTrigMinNb kMuch,0"
+  EVTBUILDER+=" --SetTrigMinNb kTrd,0"
+  EVTBUILDER+=" --SetTrigMinNb kTof,10"
+  EVTBUILDER+=" --SetTrigMinNb kRich,0"
+  EVTBUILDER+=" --SetTrigMinNb kPsd,0"
+  EVTBUILDER+=" --TsNameIn unpts$_iMoni"
+  EVTBUILDER+=" --EvtNameOut events"
+  EVTBUILDER+=" --channel-config name=unpts$_iMoni,type=pull,method=connect,transport=zeromq,address=tcp://127.0.0.1:$_iPort"
+  #EVTBUILDER+=" --transport shmem"
+  EVTBUILDER+=" --transport zeromq"
+  #EVTBUILDER+=" --transport nanomsg"
+  EVTBUILDER+=" --channel-config name=events,type=push,method=connect,transport=zeromq,address=tcp://127.0.0.1:11556"
+#  EVTBUILDER+=" --channel-config name=commands,type=sub,method=connect,transport=zeromq,address=tcp://127.0.0.1:11007"
+  EVTBUILDER+=" --channel-config name=parameters,type=req,method=connect,transport=zeromq,address=tcp://127.0.0.1:11005,rateLogging=0"
+  EVTBUILDER+=" --channel-config name=histogram-in,type=pub,method=connect,transport=zeromq,address=tcp://127.0.0.1:11666"
+  EVTBUILDER+=" --channel-config name=histo-conf,type=pub,method=connect,transport=zeromq,address=tcp://127.0.0.1:11667,rateLogging=0"
+  EVTBUILDER+=" --channel-config name=canvas-conf,type=pub,method=connect,transport=zeromq,address=tcp://127.0.0.1:11668,rateLogging=0"
+  # Replaces log filename Xterm.log.hostname.yyyy.mm.dd.hh.mm.ss.XXXXXX
+  # with ProcessName_hostname_yyyy_mm_dd_hh_mm_ss.log
+  EVTBUILDER_LOG="build$_iMoni"
+  EVTBUILDER_LOG+="_$LOGFILETAG"
+  VALGRIND_EVT="valgrind -v --error-limit=no --suppressions=$ROOTSYS/share/root/etc/valgrind-root.supp --leak-check=full --show-reachable=yes --log-file=valgrind_evt_log.txt"
+  VALGHEAP_EVT="valgrind --tool=massif --massif-out-file=valgrind_evt_massif.out"
+  xterm -l -lf $EVTBUILDER_LOG -geometry 80x23+800+$_yOffset -hold -e @CMAKE_BINARY_DIR@/bin/MQ/mcbm/$EVTBUILDER &
+#  xterm -l -lf $EVTBUILDER_LOG -geometry 80x23+800+$_yOffset -hold -e $VALGRIND_EVT @CMAKE_BINARY_DIR@/bin/MQ/mcbm/$EVTBUILDER &
+#  xterm -l -lf $EVTBUILDER_LOG -geometry 80x23+800+$_yOffset -hold -e $VALGHEAP_EVT @CMAKE_BINARY_DIR@/bin/MQ/mcbm/$EVTBUILDER &
+
+done
+
+EVTSINK="McbmEventSink"
+EVTSINK+=" --id evtsink1"
+EVTSINK+=" --severity info"
+EVTSINK+=" --OutFileName mcbm_digis_events.root"
+EVTSINK+=" --FillHistos false"
+EVTSINK+=" --PubFreqTs $_pubfreqts"
+EVTSINK+=" --PubTimeMin $_pubminsec"
+EVTSINK+=" --PubTimeMax $_pubmaxsec"
+EVTSINK+=" --EvtNameIn events"
+EVTSINK+=" --channel-config name=events,type=pull,method=bind,transport=zeromq,address=tcp://127.0.0.1:11556"
+#EVTSINK+=" --channel-config name=events,type=pull,method=bind,transport=zeromq,address=tcp://127.0.0.1:11556,sndBufSize=$_paraBuffSz,rcvBuffSize=$_unpBufSz"
+EVTSINK+=" --channel-config name=missedts,type=sub,method=connect,transport=zeromq,address=tcp://127.0.0.1:11006"
+EVTSINK+=" --channel-config name=commands,type=sub,method=connect,transport=zeromq,address=tcp://127.0.0.1:11007"
+EVTSINK+=" --channel-config name=histogram-in,type=sub,method=bind,transport=zeromq,address=tcp://127.0.0.1:11666"
+EVTSINK+=" --channel-config name=histo-conf,type=sub,method=bind,transport=zeromq,address=tcp://127.0.0.1:11667,rateLogging=0"
+EVTSINK+=" --channel-config name=canvas-conf,type=sub,method=bind,transport=zeromq,address=tcp://127.0.0.1:11668,rateLogging=0"
+# Replaces log filename Xterm.log.hostname.yyyy.mm.dd.hh.mm.ss.XXXXXX
+# with ProcessName_hostname_yyyy_mm_dd_hh_mm_ss.log
+EVTSINK_LOG="evtsink1_$LOGFILETAG"
+VALGRIND_SINK="valgrind -v --error-limit=no --suppressions=$ROOTSYS/share/root/etc/valgrind-root.supp --leak-check=full --show-reachable=yes --log-file=valgrind_sink_log.txt"
+VALGHEAP_SINK="valgrind -v --tool=massif --massif-out-file=valgrind_sink_massif.out"
+VALGCPU_SINK="valgrind -v --tool=callgrind"
+xterm -l -lf $EVTSINK_LOG -geometry 80x23+1200+0 -hold -e @CMAKE_BINARY_DIR@/bin/MQ/mcbm/$EVTSINK &
+#xterm -l -lf $EVTSINK_LOG -geometry 80x23+1200+0 -hold -e $VALGRIND_SINK @CMAKE_BINARY_DIR@/bin/MQ/mcbm/$EVTSINK &
+#xterm -l -lf $EVTSINK_LOG -geometry 80x23+1200+0 -hold -e $VALGHEAP_SINK @CMAKE_BINARY_DIR@/bin/MQ/mcbm/$EVTSINK &
+#xterm -l -lf $EVTSINK_LOG -geometry 80x23+1200+0 -hold -e $VALGCPU_SINK @CMAKE_BINARY_DIR@/bin/MQ/mcbm/$EVTSINK &
+
+PARAMETERSERVER="parmq-server"
+PARAMETERSERVER+=" --id parmq-server"
+PARAMETERSERVER+=" --severity info"
+PARAMETERSERVER+=" --channel-name parameters"
+#PARAMETERSERVER+=" --channel-config name=parameters,type=rep,method=bind,transport=zeromq,address=tcp://127.0.0.1:11005"
+PARAMETERSERVER+=" --channel-config name=parameters,type=rep,method=bind,transport=zeromq,address=tcp://127.0.0.1:11005,rateLogging=0"
+PARAMETERSERVER+=" --first-input-name $_parfileSts;$_parfileMuch;$_parfileTrdAsic;$_parfileTrdDigi;$_parfileTrdGas;$_parfileTrdGain;$_parfileTof;$_parfileRich;$_parfilePsd"
+PARAMETERSERVER+=" --first-input-type ASCII"
+PARAMETERSERVER+=" --libs-to-load=libCbmFlibMcbm2018" # doesn't work due to runtime problem
+# Replaces log filename Xterm.log.hostname.yyyy.mm.dd.hh.mm.ss.XXXXXX
+# with ProcessName_hostname_yyyy_mm_dd_hh_mm_ss.log
+PARAMSRV_LOG="parmq_$LOGFILETAG"
+xterm -l -lf $PARAMSRV_LOG -geometry 80x23+1600+0 -hold -e @CMAKE_BINARY_DIR@/bin/MQ/parmq/$PARAMETERSERVER &
+
+HISTSERVER="MqHistoServer"
+HISTSERVER+=" --id server1"
+HISTSERVER+=" --severity info"
+HISTSERVER+=" --histport 8081"
+HISTSERVER+=" --channel-config name=histogram-in,type=sub,method=bind,transport=zeromq,address=tcp://127.0.0.1:11666"
+HISTSERVER+=" --channel-config name=histo-conf,type=sub,method=bind,transport=zeromq,address=tcp://127.0.0.1:11667,rateLogging=0"
+HISTSERVER+=" --channel-config name=canvas-conf,type=sub,method=bind,transport=zeromq,address=tcp://127.0.0.1:11668,rateLogging=0"
+# Replaces log filename Xterm.log.hostname.yyyy.mm.dd.hh.mm.ss.XXXXXX
+# with ProcessName_hostname_yyyy_mm_dd_hh_mm_ss.log
+HISTSRV_LOG="server1_$LOGFILETAG"
+xterm -l -lf $HISTSRV_LOG -geometry 80x23+2000+0 -hold -e @CMAKE_BINARY_DIR@/bin/MQ/histogramServer/$HISTSERVER &
-- 
GitLab