From e0c33adeeec55d17f0eff51a4cbc34d2251221f9 Mon Sep 17 00:00:00 2001
From: P-A Loizeau <p.-a.loizeau@gsi.de>
Date: Thu, 2 Jul 2020 16:21:01 +0200
Subject: [PATCH] MQ: add example of the unpacking and event building chain for
 mCBM 2020 - Unpacker device running an instance of the unpacker algo of each
 of the participating detectors - Event builder device running an instance of
 CbmMcbm2019TimeWinEventBuilderAlgo - Event Sink acting as a parrallel stream
 aligner and merger + as a disk sink - Start script for a chain with a single
 MultiTsa source, parallel branches of Unpacker + EventBuilder, a single
 EventSink and an histogram server - Pulser monitor device (untested)

---
 MQ/CMakeLists.txt                          |   2 +
 MQ/mcbm/CMakeLists.txt                     | 160 ++++
 MQ/mcbm/CbmDeviceMcbmEventBuilderWin.cxx   | 703 ++++++++++++++++++
 MQ/mcbm/CbmDeviceMcbmEventBuilderWin.h     | 146 ++++
 MQ/mcbm/CbmDeviceMcbmEventSink.cxx         | 806 +++++++++++++++++++++
 MQ/mcbm/CbmDeviceMcbmEventSink.h           | 172 +++++
 MQ/mcbm/CbmDeviceMcbmMonitorPulser.cxx     | 408 +++++++++++
 MQ/mcbm/CbmDeviceMcbmMonitorPulser.h       | 276 +++++++
 MQ/mcbm/CbmDeviceMcbmUnpack.cxx            | 584 +++++++++++++++
 MQ/mcbm/CbmDeviceMcbmUnpack.h              | 113 +++
 MQ/mcbm/runMcbmEventSink.cxx               |  35 +
 MQ/mcbm/runMcbmEvtBuilderWin.cxx           |  49 ++
 MQ/mcbm/runMcbmMonitorPulser.cxx           |  39 +
 MQ/mcbm/runMcbmUnpack.cxx                  |  25 +
 MQ/mcbm/startMQMcbmEvtBuilderWin2020.sh.in | 264 +++++++
 MQ/mcbm/startMQMcbmPulserMonitor2020.sh.in | 160 ++++
 16 files changed, 3942 insertions(+)
 create mode 100644 MQ/mcbm/CMakeLists.txt
 create mode 100644 MQ/mcbm/CbmDeviceMcbmEventBuilderWin.cxx
 create mode 100644 MQ/mcbm/CbmDeviceMcbmEventBuilderWin.h
 create mode 100644 MQ/mcbm/CbmDeviceMcbmEventSink.cxx
 create mode 100644 MQ/mcbm/CbmDeviceMcbmEventSink.h
 create mode 100644 MQ/mcbm/CbmDeviceMcbmMonitorPulser.cxx
 create mode 100644 MQ/mcbm/CbmDeviceMcbmMonitorPulser.h
 create mode 100644 MQ/mcbm/CbmDeviceMcbmUnpack.cxx
 create mode 100644 MQ/mcbm/CbmDeviceMcbmUnpack.h
 create mode 100644 MQ/mcbm/runMcbmEventSink.cxx
 create mode 100644 MQ/mcbm/runMcbmEvtBuilderWin.cxx
 create mode 100644 MQ/mcbm/runMcbmMonitorPulser.cxx
 create mode 100644 MQ/mcbm/runMcbmUnpack.cxx
 create mode 100755 MQ/mcbm/startMQMcbmEvtBuilderWin2020.sh.in
 create mode 100755 MQ/mcbm/startMQMcbmPulserMonitor2020.sh.in

diff --git a/MQ/CMakeLists.txt b/MQ/CMakeLists.txt
index bb0aa9247f..3bc8ce8b40 100644
--- a/MQ/CMakeLists.txt
+++ b/MQ/CMakeLists.txt
@@ -10,6 +10,8 @@ add_subdirectory(sink)
 add_subdirectory(monitor)
 add_subdirectory(histoServer)
 
+add_subdirectory(mcbm)
+
 #add_subdirectory(sts)
 #add_subdirectory(test)
 
diff --git a/MQ/mcbm/CMakeLists.txt b/MQ/mcbm/CMakeLists.txt
new file mode 100644
index 0000000000..ec45173159
--- /dev/null
+++ b/MQ/mcbm/CMakeLists.txt
@@ -0,0 +1,160 @@
+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)
+
+set(INCLUDE_DIRECTORIES
+    ${CMAKE_CURRENT_SOURCE_DIR}
+    ${CMAKE_SOURCE_DIR}/MQ/base
+    ${CBMROOT_SOURCE_DIR}/fles/mcbm2018/unpacker
+    ${CBMROOT_SOURCE_DIR}/fles/mcbm2018/tasks
+    ${CBMROOT_SOURCE_DIR}/fles/mcbm2018/parameter
+    ${CBMROOT_SOURCE_DIR}/fles/mcbm2018/dataformat
+    ${CBMROOT_SOURCE_DIR}/fles/mcbm2018/commonMQ
+    ${CBMROOT_SOURCE_DIR}/fles/flestools
+    ${CBMDATA_DIR}
+    ${CBMDATA_DIR}/global
+    ${CBMDATA_DIR}/raw
+    ${CBMDATA_DIR}/sts
+    ${CBMDATA_DIR}/much
+    ${CBMDATA_DIR}/rich
+    ${CBMDATA_DIR}/tof
+    ${CBMDATA_DIR}/psd
+    ${CBMDATA_DIR}/trd
+    ${CBMDATA_DIR}/mvd # Feint to avoid crash of DigiManager due to missing source pointer
+    ${CBMBASE_DIR}
+
+    ${CBMDETECTORBASE_DIR}/trd # required for parameter handling of the trd
+    ${CBMDETECTORBASE_DIR}/psd # required for fitting tools of the psd
+)
+
+Set(SYSTEM_INCLUDE_DIRECTORIES
+    ${SYSTEM_INCLUDE_DIRECTORIES}
+    ${ZeroMQ_INCLUDE_DIR}
+    ${Boost_INCLUDE_DIR}
+    ${FAIRROOT_INCLUDE_DIR}
+    ${FAIRMQ_INCLUDE_DIR}
+    ${FAIRMQ_INCLUDE_DIR}/options
+
+    ${IPC_INCLUDE_DIRECTORY}
+    ${CBMROOT_SOURCE_DIR}/external/cppzmq
+)
+
+include_directories(${INCLUDE_DIRECTORIES})
+include_directories(SYSTEM ${SYSTEM_INCLUDE_DIRECTORIES})
+
+set(LINK_DIRECTORIES
+  ${ROOT_LIBRARY_DIR}
+  ${FAIRROOT_LIBRARY_DIR}
+  ${FAIRMQ_LIBRARY_DIR}
+  ${Boost_LIBRARY_DIRS}
+)
+
+link_directories(${LINK_DIRECTORIES})
+
+set(EXECUTABLE_OUTPUT_PATH "${EXECUTABLE_OUTPUT_PATH}/MQ/mcbm")
+
+Set(BOOST_LIBS
+  ${Boost_SYSTEM_LIBRARY}
+  ${Boost_SERIALIZATION_LIBRARY}
+  ${Boost_PROGRAM_OPTIONS_LIBRARY}
+  ${Boost_LOG_LIBRARY}
+)
+If(UNIX AND NOT APPLE)
+  List(APPEND BOOST_LIBS pthread)
+EndIf()
+
+set(FAIR_LIBS
+  FairMQ
+)
+
+If(FAIRLOGGER_FOUND)
+  set(FAIR_LIBS
+      ${FAIR_LIBS}
+      FairLogger
+     )
+EndIf()
+
+set(EXE_NAME McbmUnpack)
+set(SRCS CbmDeviceMcbmUnpack.cxx runMcbmUnpack.cxx)
+
+set(DEPENDENCIES
+  ${DEPENDENCIES}
+  ${FAIR_LIBS}
+  ${BOOST_LIBS}
+  fles_ipc
+  CbmFlibMcbm2018
+  CbmFlibFlesTools
+  CbmBase
+  CbmData
+  Core
+  RIO
+  Net
+  Hist
+  RHTTP
+)
+#GENERATE_LIBRARY()
+GENERATE_EXECUTABLE()
+
+set(EXE_NAME McbmMonitorPulser)
+set(SRCS CbmDeviceMcbmMonitorPulser.cxx runMcbmMonitorPulser.cxx)
+
+set(DEPENDENCIES
+  ${DEPENDENCIES}
+  ${FAIR_LIBS}
+  ${BOOST_LIBS}
+  fles_ipc
+  CbmFlibMcbm2018
+  CbmFlibFlesTools
+  CbmBase
+  CbmData
+  Core
+  RIO
+  Net
+  Hist
+  RHTTP
+)
+#GENERATE_LIBRARY()
+GENERATE_EXECUTABLE()
+
+set(EXE_NAME McbmEventBuilderWin)
+set(SRCS CbmDeviceMcbmEventBuilderWin.cxx runMcbmEvtBuilderWin.cxx)
+
+set(DEPENDENCIES
+  ${DEPENDENCIES}
+  ${FAIR_LIBS}
+  ${BOOST_LIBS}
+  fles_ipc
+  CbmFlibMcbm2018
+  CbmFlibFlesTools
+  CbmBase
+  CbmData
+  Core
+  RIO
+  Net
+  Hist
+  RHTTP
+)
+#GENERATE_LIBRARY()
+GENERATE_EXECUTABLE()
+
+set(EXE_NAME McbmEventSink)
+set(SRCS CbmDeviceMcbmEventSink.cxx runMcbmEventSink.cxx)
+
+set(DEPENDENCIES
+  ${DEPENDENCIES}
+  ${FAIR_LIBS}
+  ${BOOST_LIBS}
+  fles_ipc
+  CbmFlibMcbm2018
+  CbmFlibFlesTools
+  CbmBase
+  CbmData
+  Core
+  RIO
+  Tree
+  Net
+  Hist
+  RHTTP
+)
+#GENERATE_LIBRARY()
+GENERATE_EXECUTABLE()
+
diff --git a/MQ/mcbm/CbmDeviceMcbmEventBuilderWin.cxx b/MQ/mcbm/CbmDeviceMcbmEventBuilderWin.cxx
new file mode 100644
index 0000000000..963fed8ec9
--- /dev/null
+++ b/MQ/mcbm/CbmDeviceMcbmEventBuilderWin.cxx
@@ -0,0 +1,703 @@
+/**
+ * CbmDeviceMcbmEventBuilderWin.cxx
+ *
+ * @since 2020-05-24
+ * @author P.-A. Loizeau
+ */
+
+#include "CbmDeviceMcbmEventBuilderWin.h"
+
+
+/// CBM headers
+#include "CbmMQDefs.h"
+
+#include "CbmEvent.h"
+#include "TimesliceMetaData.h"
+#include "CbmFlesCanvasTools.h"
+#include "CbmMvdDigi.h"
+#include "CbmMatch.h"
+
+/// FAIRROOT headers
+#include "FairRunOnline.h"
+#include "FairMQLogger.h"
+#include "FairMQProgOptions.h" // device->fConfig
+#include "FairParGenericSet.h"
+#include "RootSerializer.h"
+#include "BoostSerializer.h"
+
+/// FAIRSOFT headers (geant, boost, ...)
+#include "TNamed.h"
+#include "TList.h"
+#include "TCanvas.h"
+#include "TFile.h"
+#include "TH1.h"
+#include <boost/serialization/utility.hpp>
+#include <boost/archive/binary_iarchive.hpp>
+
+/// C/C++ headers
+#include <string>
+#include <iomanip>
+#include <array>
+
+#include <stdexcept>
+struct InitTaskError : std::runtime_error { using std::runtime_error::runtime_error; };
+
+using namespace std;
+
+//Bool_t bMcbm2018MonitorTaskT0ResetHistos = kFALSE;
+
+CbmDeviceMcbmEventBuilderWin::CbmDeviceMcbmEventBuilderWin()
+{
+   fpAlgo  = new CbmMcbm2019TimeWinEventBuilderAlgo();
+}
+
+void CbmDeviceMcbmEventBuilderWin::InitTask()
+try
+{
+   /// Read options from executable
+   LOG(info) << "Init options for CbmDeviceMcbmEventBuilderWin.";
+   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, &CbmDeviceMcbmEventBuilderWin::HandleData );
+      } // if( entry.first.find( "ts" )
+   } // for( auto const &entry : fChannels )
+
+//   InitContainers();
+
+   /// Initialize the Algorithm parameters
+   fpAlgo->SetFillHistos( fbFillHistos );
+   fpAlgo->SetIgnoreTsOverlap( fbIgnoreTsOverlap );
+      /// Extract Event Overlap Mode
+   EOverlapMode mode = ( "NoOverlap"    == fsEvtOverMode ? EOverlapMode::NoOverlap :
+                       ( "MergeOverlap" == fsEvtOverMode ? EOverlapMode::MergeOverlap :
+                       ( "AllowOverlap" == fsEvtOverMode ? EOverlapMode::AllowOverlap :
+                                                           EOverlapMode::NoOverlap
+                         ) ) );
+   fpAlgo->SetEventOverlapMode( mode );
+      /// Extract refdet
+   ECbmModuleId refDet = ( "kT0"   == fsRefDet ? ECbmModuleId::kT0 :
+                         ( "kSts"  == fsRefDet ? ECbmModuleId::kSts :
+                         ( "kMuch" == fsRefDet ? ECbmModuleId::kMuch :
+                         ( "kTrd"  == fsRefDet ? ECbmModuleId::kTrd :
+                         ( "kTof"  == fsRefDet ? ECbmModuleId::kTof :
+                         ( "kRich" == fsRefDet ? ECbmModuleId::kRich :
+                         ( "kPsd"  == fsRefDet ? ECbmModuleId::kPsd :
+                                                 ECbmModuleId::kNotExist
+                         ) ) ) ) ) ) );
+
+   fpAlgo->SetReferenceDetector( refDet );
+      /// Extract detector to add if any
+   for( std::vector< std::string >::iterator itStrAdd = fvsAddDet.begin(); itStrAdd != fvsAddDet.end(); ++itStrAdd )
+   {
+      ECbmModuleId addDet = ( "kT0"   == *itStrAdd ? ECbmModuleId::kT0 :
+                            ( "kSts"  == *itStrAdd ? ECbmModuleId::kSts :
+                            ( "kMuch" == *itStrAdd ? ECbmModuleId::kMuch :
+                            ( "kTrd"  == *itStrAdd ? ECbmModuleId::kTrd :
+                            ( "kTof"  == *itStrAdd ? ECbmModuleId::kTof :
+                            ( "kRich" == *itStrAdd ? ECbmModuleId::kRich :
+                            ( "kPsd"  == *itStrAdd ? ECbmModuleId::kPsd :
+                                                     ECbmModuleId::kNotExist
+                            ) ) ) ) ) ) );
+      if(  ECbmModuleId::kNotExist != addDet )
+      {
+         fpAlgo->AddDetector( addDet );
+      } // if(  ECbmModuleId::kNotExist != addDet )
+         else
+         {
+            LOG( info ) << "CbmDeviceMcbmEventBuilderWin::InitTask => Trying to add unsupported detector, ignored! "
+                        << (*itStrAdd);
+            continue;
+         } // else of if(  ECbmModuleId::kNotExist != addDet
+   } // for( std::vector< std::string >::iterator itStrAdd = fvsAddDet.begin(); itStrAdd != fvsAddDet.end(); ++itStrAdd )
+      /// Extract detector to add if any
+   for( std::vector< std::string >::iterator itStrRem = fvsDelDet.begin(); itStrRem != fvsDelDet.end(); ++itStrRem )
+   {
+      ECbmModuleId remDet = ( "kT0"   == *itStrRem ? ECbmModuleId::kT0 :
+                            ( "kSts"  == *itStrRem ? ECbmModuleId::kSts :
+                            ( "kMuch" == *itStrRem ? ECbmModuleId::kMuch :
+                            ( "kTrd"  == *itStrRem ? ECbmModuleId::kTrd :
+                            ( "kTof"  == *itStrRem ? ECbmModuleId::kTof :
+                            ( "kRich" == *itStrRem ? ECbmModuleId::kRich :
+                            ( "kPsd"  == *itStrRem ? ECbmModuleId::kPsd :
+                                                     ECbmModuleId::kNotExist
+                            ) ) ) ) ) ) );
+      if(  ECbmModuleId::kNotExist != remDet )
+      {
+         fpAlgo->RemoveDetector( remDet );
+      } // if(  ECbmModuleId::kNotExist != remDet )
+         else
+         {
+            LOG( info ) << "CbmDeviceMcbmEventBuilderWin::InitTask => Trying to remove unsupported detector, ignored! "
+                        << (*itStrRem);
+            continue;
+         } // else of if(  ECbmModuleId::kNotExist != remDet )
+   } // for( std::vector< std::string >::iterator itStrAdd = fvsAddDet.begin(); itStrAdd != fvsAddDet.end(); ++itStrAdd )
+      /// 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 ) << "CbmDeviceMcbmEventBuilderWin::InitTask => "
+                     << "Trying to set trigger window with invalid option pattern, ignored! "
+                     << " (Should be ECbmModuleId,dWinBeg,dWinEnd but instead found "
+                     << (*itStrTrigWin) << " )";
+         continue;
+      } // if( std::string::npos == charPosDel )
+
+      /// 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 ) << "CbmDeviceMcbmEventBuilderWin::InitTask => "
+                     << "Trying to set trigger window for unsupported detector, ignored! "
+                     << sSelDet;
+         continue;
+      } // if(  ECbmModuleId::kNotExist == selDet )
+
+      /// Window beginning
+      charPosDel++;
+      std::string sNext = (*itStrTrigWin).substr( charPosDel );
+      charPosDel = sNext.find( ',' );
+      if( std::string::npos == charPosDel )
+      {
+         LOG( info ) << "CbmDeviceMcbmEventBuilderWin::InitTask => "
+                     << "Trying to set trigger window with invalid option pattern, ignored! "
+                     << " (Should be ECbmModuleId,dWinBeg,dWinEnd but instead found "
+                     << (*itStrTrigWin) << " )";
+         continue;
+      } // if( std::string::npos == charPosDel )
+      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 );
+   } //       for( std::vector< std::string >::iterator itStrTrigWin = fvsSetTrigWin.begin(); itStrTrigWin != fvsSetTrigWin.end(); ++itStrTrigWin )
+      /// 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 ) << "CbmDeviceMcbmEventBuilderWin::InitTask => "
+                     << "Trying to set trigger min Nb with invalid option pattern, ignored! "
+                     << " (Should be ECbmModuleId,uMinNb but instead found "
+                     << (*itStrMinNb) << " )";
+         continue;
+      } // if( std::string::npos == charPosDel )
+
+      /// 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 ) << "CbmDeviceMcbmEventBuilderWin::InitTask => "
+                     << "Trying to set trigger min Nb for unsupported detector, ignored! "
+                     << sSelDet;
+         continue;
+      } // if(  ECbmModuleId::kNotExist == selDet )
+
+      /// Min number
+      charPosDel++;
+      UInt_t uMinNb = std::stoul( (*itStrMinNb).substr( charPosDel ) );
+
+      fpAlgo->SetTriggerMinNumber( selDet, uMinNb );
+   } //    for( std::vector< std::string >::iterator itStrMinNb = fvsSetTrigMinNb.begin(); itStrMinNb != fvsSetTrigMinNb.end(); ++itStrMinNb )
+
+   /// 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 >();
+
+   /// Register all input data members with the FairRoot manager
+   fpRun         = new FairRunOnline(0);
+	FairRootManager *ioman = nullptr ;
+	ioman = FairRootManager::Instance();
+   if( NULL == ioman )
+   {
+      throw InitTaskError( "No FairRootManager instance" );
+   }
+   fTimeSliceMetaDataArray = new TClonesArray("TimesliceMetaData", 1 );
+   if( NULL == fTimeSliceMetaDataArray )
+   {
+      throw InitTaskError( "Failed creating the TS meta data TClonesarray " );
+   }
+   ioman->Register( "TimesliceMetaData", "TS Meta Data", fTimeSliceMetaDataArray, kFALSE);
+      /// Digis storage
+   ioman->RegisterAny( "T0Digi",           fvDigiT0,   kFALSE);
+   ioman->RegisterAny( "StsDigi",          fvDigiSts,  kFALSE);
+   ioman->RegisterAny( "MuchBeamTimeDigi", fvDigiMuch, kFALSE);
+   ioman->RegisterAny( "TrdDigi",          fvDigiTrd,  kFALSE);
+   ioman->RegisterAny( "TofDigi",          fvDigiTof,  kFALSE);
+   ioman->RegisterAny( "RichDigi",         fvDigiRich, kFALSE);
+   ioman->RegisterAny( "PsdDigi",          fvDigiPsd,  kFALSE);
+      /// Feint to avoid crash of DigiManager due to missing source pointer
+      /// validity check in FairRootManager.h at line 461
+   std::vector< CbmMvdDigi > * pMvdDigi = new std::vector< CbmMvdDigi >();
+   ioman->RegisterAny( "MvdDigi",          pMvdDigi,   kFALSE);
+   std::vector< CbmMatch >   * pFakeMatch = new std::vector< CbmMatch >();
+   ioman->RegisterAny( "MvdDigiMatch",          pFakeMatch, kFALSE);
+   ioman->RegisterAny( "StsDigiMatch",          pFakeMatch, kFALSE);
+   ioman->RegisterAny( "MuchBeamTimeDigiMatch", pFakeMatch, kFALSE);
+   ioman->RegisterAny( "TrdDigiMatch",          pFakeMatch, kFALSE);
+   ioman->RegisterAny( "TofDigiMatch",          pFakeMatch, kFALSE);
+   ioman->RegisterAny( "RichDigiMatch",         pFakeMatch, kFALSE);
+   ioman->RegisterAny( "PsdDigiMatch",          pFakeMatch, kFALSE);
+
+   /// Create output TClonesArray
+   /// TODO: remove TObject from CbmEvent and switch to vectors!
+   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." );
+   } // if( kFALSE == fpAlgo->InitAlgo() )
+
+   /// 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" );
+         } // 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 )
+
+      /// 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" );
+         } // 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 )
+   } // if( kTRUE == fbFillHistos )
+
+} 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 CbmDeviceMcbmEventBuilderWin::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;
+      } // if (pos1!=std::string::npos)
+   } // for(auto const &entry : fsAllowedChannels)
+   LOG(info) << "Channel name " << channelName
+            << " not found in list of allowed channel names.";
+   LOG(error) << "Stop device.";
+   return false;
+}
+/*
+Bool_t CbmDeviceMcbmEventBuilderWin::InitContainers()
+{
+   LOG(info) << "Init parameter containers for CbmDeviceMcbmEventBuilderWin.";
+
+   if( kFALSE == InitParameters( fpAlgo ->GetParList() ) )
+      return kFALSE;
+
+   /// Need to add accessors for all options
+   fpAlgo ->SetIgnoreOverlapMs( fbIgnoreOverlapMs );
+
+   Bool_t initOK = fpAlgo->InitContainers();
+
+//   Bool_t initOK = fMonitorAlgo->ReInitContainers();
+
+  return initOK;
+}
+
+Bool_t CbmDeviceMcbmEventBuilderWin::InitParameters( TList* fParCList )
+{
+   for( int iparC = 0; iparC < fParCList->GetEntries(); iparC++ )
+   {
+      FairParGenericSet* tempObj = (FairParGenericSet*)( fParCList->At( iparC ) );
+      fParCList->Remove( tempObj );
+      std::string paramName{ tempObj->GetName() };
+      // NewSimpleMessage creates a copy of the data and takes care of its destruction (after the transfer takes place).
+      // Should only be used for small data because of the cost of an additional copy
+
+      // Her must come the proper Runid
+      std::string message = paramName + ",111";
+      LOG(info) << "Requesting parameter container " << paramName << ", sending message: " << message;
+
+      FairMQMessagePtr req( NewSimpleMessage(message) );
+      FairMQMessagePtr rep( NewMessage() );
+
+      FairParGenericSet* newObj = nullptr;
+
+      if( Send(req, "parameters") > 0 )
+      {
+         if( Receive( rep, "parameters" ) >= 0)
+         {
+            if( 0 !=  rep->GetSize() )
+            {
+               CbmMQTMessage tmsg( rep->GetData(), rep->GetSize() );
+               newObj = static_cast< FairParGenericSet* >( tmsg.ReadObject( tmsg.GetClass() ) );
+               LOG( info ) << "Received unpack parameter from the server:";
+               newObj->print();
+            } // if( 0 !=  rep->GetSize() )
+               else
+               {
+                  LOG( error ) << "Received empty reply. Parameter not available";
+                  return kFALSE;
+               } // else of if( 0 !=  rep->GetSize() )
+         } // if( Receive( rep, "parameters" ) >= 0)
+      } // if( Send(req, "parameters") > 0 )
+      fParCList->AddAt( newObj, iparC );
+      delete tempObj;
+   } // for( int iparC = 0; iparC < fParCList->GetEntries(); iparC++ )
+
+   return kTRUE;
+}
+*/
+// handler is called whenever a message arrives on "data", with a reference to the message and a sub-channel index (here 0)
+bool CbmDeviceMcbmEventBuilderWin::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
+      /// TODO: code order of vectors in the TS MetaData!!
+/*
+  std::string msgStrTsMeta( static_cast< char * >( parts.At( uPartIdx )->GetData() ),
+                            ( parts.At( uPartIdx ) )->GetSize() );
+  std::istringstream issTsMeta(msgStrTsMeta);
+  boost::archive::binary_iarchive inputArchiveTsMeta(issTsMeta);
+  inputArchiveTsMeta >> (*fTsMetaData);
+  ++uPartIdx;
+*/
+   Deserialize< RootSerializer >( *parts.At( uPartIdx ), fTsMetaData );
+   /// FIXME: Not if this is the proper way to insert the data
+   new( (*fTimeSliceMetaDataArray)[ fTimeSliceMetaDataArray->GetEntriesFast()
+//                                    ] ) TimesliceMetaData( *fTsMetaData ) ;
+                                    ] ) 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();
+//   delete fTsMetaData;
+
+   /// 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" );
+//   fEvents->Clear();
+
+   /// 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();
+      } // if( ( fdMaxPublishTime < elapsedSeconds.count() ) || ( 0 == fulNumMessages % fuPublishFreqTs && fdMinPublishTime < elapsedSeconds.count() ) )
+   } // if( kTRUE == fbFillHistos )
+
+   return true;
+}
+
+bool CbmDeviceMcbmEventBuilderWin::SendEvents( FairMQParts& partsIn )
+{
+   /// Clear events TClonesArray before usage.
+   fEvents->Delete();
+//   fEvents->Clear();
+
+   /// 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));
+//      new ( (*fEvents)[fEvents->GetEntriesFast()] ) CbmEvent( *event );
+      LOG(debug) << "TClonesArray: "
+                 << static_cast<CbmEvent*>(fEvents->At(fEvents->GetEntriesFast()-1))->ToString();
+   } // for( CbmEvent* event: vEvents )
+
+   /// 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
+   /// FIXME: use move or fix addition of new part to avoid full message copy
+   FairMQParts partsOut( std::move( partsIn ) );
+   partsOut.AddPart( std::move( message ) );
+
+//   /// Get vector from algo
+//   fEventVector = fpAlgo->GetEventVector();
+//
+//   /// Prepare serialized versions of the events vector
+//   std::stringstream ossEvents;
+//   boost::archive::binary_oarchive oaEvents(ossEvents);
+//   oaEvents << fpAlgo->GetEventVector();
+//   std::string* strMsgEvents = new std::string(ossEvents.str());
+//
+//   /// Create message
+//   FairMQMessagePtr msg( NewMessage( const_cast< char * >( strMsgEvents->c_str() ), // data
+//                                     strMsgEvents->length(), // size
+//                                     []( void * /*data*/, void* object ){ delete static_cast< std::string * >( object ); },
+//                                     strMsgEvents ) ); // object that manages the data
+
+   /// Send message
+//   if( Send( message, fsChannelNameDataOutput ) < 0 )
+   if( Send( partsOut, fsChannelNameDataOutput ) < 0 )
+   {
+      LOG(error) << "Problem sending data to " << fsChannelNameDataOutput;
+      return false;
+   }
+
+   return true;
+}
+
+bool CbmDeviceMcbmEventBuilderWin::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 )
+
+   /// Reset the histograms after sending them (but do not reset the time)
+   fpAlgo->ResetHistograms( kFALSE );
+
+   return true;
+}
+
+CbmDeviceMcbmEventBuilderWin::~CbmDeviceMcbmEventBuilderWin()
+{
+   /// 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 CbmDeviceMcbmEventBuilderWin::Finish()
+{
+}
diff --git a/MQ/mcbm/CbmDeviceMcbmEventBuilderWin.h b/MQ/mcbm/CbmDeviceMcbmEventBuilderWin.h
new file mode 100644
index 0000000000..7fff99b881
--- /dev/null
+++ b/MQ/mcbm/CbmDeviceMcbmEventBuilderWin.h
@@ -0,0 +1,146 @@
+/**
+ * CbmDeviceMcbmEventBuilderWin.h
+ *
+ * @since 2020-05-24
+ * @author P.-A. Loizeau
+ */
+
+#ifndef CBMDEVICEMCBMEVTBUILDERWIN_H_
+#define CBMDEVICEMCBMEVTBUILDERWIN_H_
+
+/// CBM headers
+#include "CbmMcbm2019TimeWinEventBuilderAlgo.h"
+#include "CbmStsDigi.h"
+#include "CbmMuchBeamTimeDigi.h"
+#include "CbmRichDigi.h"
+#include "CbmTrdDigi.h"
+#include "CbmTofDigi.h"
+#include "CbmPsdDigi.h"
+
+/// FAIRROOT headers
+#include "FairMQDevice.h"
+
+/// FAIRSOFT headers (geant, boost, ...)
+#include "TMessage.h"
+#include "Rtypes.h"
+#include "TObjArray.h"
+
+/// C/C++ headers
+#include <vector>
+#include <map>
+#include <chrono>
+
+class TList;
+class TClonesArray;
+class FairRunOnline;
+class TimesliceMetaData;
+
+class CbmDeviceMcbmEventBuilderWin: public FairMQDevice
+{
+   public:
+      CbmDeviceMcbmEventBuilderWin();
+      virtual ~CbmDeviceMcbmEventBuilderWin();
+
+   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 };
+
+      /// Parameters management
+//      TList* fParCList = nullptr;
+//      Bool_t InitParameters( TList* fParCList );
+
+      /// 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
+      CbmMcbm2019TimeWinEventBuilderAlgo  * 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);
+//      Bool_t InitContainers();
+      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 /* CBMDEVICEMCBMEVTBUILDERWIN_H_ */
diff --git a/MQ/mcbm/CbmDeviceMcbmEventSink.cxx b/MQ/mcbm/CbmDeviceMcbmEventSink.cxx
new file mode 100644
index 0000000000..1aeed9d887
--- /dev/null
+++ b/MQ/mcbm/CbmDeviceMcbmEventSink.cxx
@@ -0,0 +1,806 @@
+/**
+ * CbmDeviceMcbmEventSink.cxx
+ *
+ * @since 2020-05-24
+ * @author P.-A. Loizeau
+ */
+
+#include "CbmDeviceMcbmEventSink.h"
+
+
+/// CBM headers
+#include "CbmMQDefs.h"
+
+#include "CbmEvent.h"
+#include "TimesliceMetaData.h"
+#include "CbmFlesCanvasTools.h"
+
+/// FAIRROOT headers
+#include "FairRunOnline.h"
+#include "FairRootManager.h"
+#include "FairRootFileSink.h"
+#include "FairMQLogger.h"
+#include "FairMQProgOptions.h" // device->fConfig
+#include "FairParGenericSet.h"
+#include "RootSerializer.h"
+#include "BoostSerializer.h"
+
+/// FAIRSOFT headers (geant, boost, ...)
+#include "TNamed.h"
+#include "TList.h"
+#include "TCanvas.h"
+#include "TFile.h"
+#include "TH1.h"
+#include <boost/serialization/utility.hpp>
+#include <boost/archive/binary_iarchive.hpp>
+
+/// C/C++ headers
+#include <string>
+#include <iomanip>
+#include <array>
+#include <thread> // this_thread::sleep_for
+
+#include <stdexcept>
+struct InitTaskError : std::runtime_error { using std::runtime_error::runtime_error; };
+
+using namespace std;
+
+//Bool_t bMcbm2018MonitorTaskT0ResetHistos = kFALSE;
+
+CbmDeviceMcbmEventSink::CbmDeviceMcbmEventSink()
+{
+}
+
+void CbmDeviceMcbmEventSink::InitTask()
+try
+{
+   /// Read options from executable
+   LOG(info) << "Init options for CbmDeviceMcbmEventSink.";
+
+   fsOutputFileName          = fConfig->GetValue< std::string >( "OutFileName" );
+
+   fsChannelNameDataInput    = fConfig->GetValue< std::string >( "EvtNameIn" );
+   fsAllowedChannels[ 0 ] = fsChannelNameDataInput;
+
+   fbFillHistos              = fConfig->GetValue< bool >( "FillHistos" );
+   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" );
+
+   /// Associate the MissedTs Channel to the corresponding handler
+   OnData( fsChannelNameMissedTs, &CbmDeviceMcbmEventSink::HandleMissTsData );
+
+   /// Associate the command Channel to the corresponding handler
+   OnData( fsChannelNameCommands, &CbmDeviceMcbmEventSink::HandleCommand );
+
+   /// Associate the Event + Unp data Channel to the corresponding handler
+   // 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, &CbmDeviceMcbmEventSink::HandleData );
+      } // if( entry.first.find( "ts" )
+   } // for( auto const &entry : fChannels )
+
+//   InitContainers();
+
+   /// 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 >();
+
+   /// Prepare storage TClonesArrays
+      /// TS MetaData storage
+   fTimeSliceMetaDataArray = new TClonesArray("TimesliceMetaData", 1);
+   if( NULL == fTimeSliceMetaDataArray )
+   {
+      throw InitTaskError( "Failed creating the TS meta data TClonesarray " );
+   } // if( NULL == fTimeSliceMetaDataArray )
+      /// Events storage
+   /// TODO: remove TObject from CbmEvent and switch to vectors!
+   fEventsArray = new TClonesArray( "CbmEvent", 500 );
+   if( NULL == fEventsArray )
+   {
+      throw InitTaskError( "Failed creating the Events TClonesarray " );
+   } // if( NULL == fEventsArray )
+
+   /// Prepare root output
+   if( "" != fsOutputFileName )
+   {
+      fpRun = new FairRunOnline();
+      fpFairRootMgr = FairRootManager::Instance();
+      fpFairRootMgr->SetSink( new FairRootFileSink( fsOutputFileName ) );
+      if( nullptr == fpFairRootMgr->GetOutFile() )
+      {
+         throw InitTaskError( "Could not open root file" );
+      } // if( nullptr == fpFairRootMgr->GetOutFile() )
+   } // if( "" != fsOutputFileName )
+      else
+      {
+         throw InitTaskError( "Empty output filename!" );
+      } // else of if( "" != fsOutputFileName )
+
+   LOG(info) << "Init Root Output to " << fsOutputFileName;
+
+   fpFairRootMgr->InitSink();
+//      fEvtHeader = new FairEventHeader();
+//      fEvtHeader->SetRunId(iRunId);
+//      rootMgr->Register("EventHeader.", "Event", fEvtHeader, kTRUE);
+//      rootMgr->FillEventHeader(fEvtHeader);
+
+   /// Register all input data members with the FairRoot manager
+      /// TS MetaData
+   fpFairRootMgr->Register( "TimesliceMetaData", "TS Meta Data", fTimeSliceMetaDataArray, kTRUE);
+      /// Digis storage
+   fpFairRootMgr->RegisterAny( "T0Digi",           fvDigiT0,   kTRUE);
+   fpFairRootMgr->RegisterAny( "StsDigi",          fvDigiSts,  kTRUE);
+   fpFairRootMgr->RegisterAny( "MuchBeamTimeDigi", fvDigiMuch, kTRUE);
+   fpFairRootMgr->RegisterAny( "TrdDigi",          fvDigiTrd,  kTRUE);
+   fpFairRootMgr->RegisterAny( "TofDigi",          fvDigiTof,  kTRUE);
+   fpFairRootMgr->RegisterAny( "RichDigi",         fvDigiRich, kTRUE);
+   fpFairRootMgr->RegisterAny( "PsdDigi",          fvDigiPsd,  kTRUE);
+      /// CbmEvent
+   fpFairRootMgr->Register( "CbmEvent", "Cbm Event", fEventsArray, kTRUE);
+/*
+   TTree* outTree =new TTree(FairRootManager::GetTreeName(), "/cbmout", 99);
+   LOG(info) << "define Tree " << outTree->GetName();
+
+   fpFairRootMgr->GetSink()->SetOutTree(outTree);
+*/
+   fpFairRootMgr->WriteFolder();
+
+   LOG(info) << "Initialized outTree with rootMgr at " << fpFairRootMgr;
+
+   /// 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" );
+         } // 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 )
+
+      /// 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" );
+         } // 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 )
+*/
+   } // if( kTRUE == fbFillHistos )
+
+} 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 CbmDeviceMcbmEventSink::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;
+      } // if (pos1!=std::string::npos)
+   } // for(auto const &entry : fsAllowedChannels)
+   LOG(info) << "Channel name " << channelName
+            << " not found in list of allowed channel names.";
+   LOG(error) << "Stop device.";
+   return false;
+}
+/*
+Bool_t CbmDeviceMcbmEventSink::InitContainers()
+{
+   LOG(info) << "Init parameter containers for CbmDeviceMcbmEventSink.";
+
+   if( kFALSE == InitParameters( fpAlgo ->GetParList() ) )
+      return kFALSE;
+
+   /// Need to add accessors for all options
+   fpAlgo ->SetIgnoreOverlapMs( fbIgnoreOverlapMs );
+
+   Bool_t initOK = fpAlgo->InitContainers();
+
+//   Bool_t initOK = fMonitorAlgo->ReInitContainers();
+
+  return initOK;
+}
+
+Bool_t CbmDeviceMcbmEventSink::InitParameters( TList* fParCList )
+{
+   for( int iparC = 0; iparC < fParCList->GetEntries(); iparC++ )
+   {
+      FairParGenericSet* tempObj = (FairParGenericSet*)( fParCList->At( iparC ) );
+      fParCList->Remove( tempObj );
+      std::string paramName{ tempObj->GetName() };
+      // NewSimpleMessage creates a copy of the data and takes care of its destruction (after the transfer takes place).
+      // Should only be used for small data because of the cost of an additional copy
+
+      // Her must come the proper Runid
+      std::string message = paramName + ",111";
+      LOG(info) << "Requesting parameter container " << paramName << ", sending message: " << message;
+
+      FairMQMessagePtr req( NewSimpleMessage(message) );
+      FairMQMessagePtr rep( NewMessage() );
+
+      FairParGenericSet* newObj = nullptr;
+
+      if( Send(req, "parameters") > 0 )
+      {
+         if( Receive( rep, "parameters" ) >= 0)
+         {
+            if( 0 !=  rep->GetSize() )
+            {
+               CbmMQTMessage tmsg( rep->GetData(), rep->GetSize() );
+               newObj = static_cast< FairParGenericSet* >( tmsg.ReadObject( tmsg.GetClass() ) );
+               LOG( info ) << "Received unpack parameter from the server:";
+               newObj->print();
+            } // if( 0 !=  rep->GetSize() )
+               else
+               {
+                  LOG( error ) << "Received empty reply. Parameter not available";
+                  return kFALSE;
+               } // else of if( 0 !=  rep->GetSize() )
+         } // if( Receive( rep, "parameters" ) >= 0)
+      } // if( Send(req, "parameters") > 0 )
+      fParCList->AddAt( newObj, iparC );
+      delete tempObj;
+   } // for( int iparC = 0; iparC < fParCList->GetEntries(); iparC++ )
+
+   return kTRUE;
+}
+*/
+//--------------------------------------------------------------------//
+// handler is called whenever a message arrives on fsChannelNameMissedTs, with a reference to the message and a sub-channel index (here 0)
+bool CbmDeviceMcbmEventSink::HandleMissTsData( FairMQMessagePtr& msg, int /*index*/ )
+{
+   std::vector< uint64_t > vIndices;
+   std::string msgStrMissTs( static_cast< char * >( msg->GetData() ),
+                          msg->GetSize() );
+   std::istringstream issMissTs( msgStrMissTs );
+   boost::archive::binary_iarchive inputArchiveMissTs( issMissTs );
+   inputArchiveMissTs >> vIndices;
+
+   fvulMissedTsIndices.insert( fvulMissedTsIndices.end(), vIndices.begin(), vIndices.end() );
+
+   /// Check TS queue and process it if needed (in case it filled a hole!)
+   CheckTsQueues();
+
+   return true;
+}
+//--------------------------------------------------------------------//
+// handler is called whenever a message arrives on "data", with a reference to the message and a sub-channel index (here 0)
+bool CbmDeviceMcbmEventSink::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
+      /// TODO: code order of vectors in the TS MetaData!!
+/*
+  std::string msgStrTsMeta( static_cast< char * >( parts.At( uPartIdx )->GetData() ),
+                            ( parts.At( uPartIdx ) )->GetSize() );
+  std::istringstream issTsMeta(msgStrTsMeta);
+  boost::archive::binary_iarchive inputArchiveTsMeta(issTsMeta);
+  inputArchiveTsMeta >> (*fTsMetaData);
+  ++uPartIdx;
+*/
+   Deserialize< RootSerializer >( *parts.At( uPartIdx ), fTsMetaData );
+   LOG(debug) << "TS metadata extracted";
+
+   /// FIXME: Need to check if TS arrived in order (probably not!!!) + buffer!!!
+   if( fuPrevTsIndex + 1 == fTsMetaData->GetIndex() ||
+       ( 0 == fuPrevTsIndex && 0 == fulTsCounter && 0 == fTsMetaData->GetIndex() ) )
+   {
+      LOG(debug) << "TS direct to dump";
+      /// Fill all storage variables registers for data output
+      PrepareTreeEntry( parts );
+      /// Trigger FairRoot manager to dump Tree entry
+      DumpTreeEntry();
+      /// Update counters
+      fuPrevTsIndex = fTsMetaData->GetIndex();
+      fulTsCounter++;
+   } // if( fuPrevTsIndex + 1 == fTsMetaData->GetIndex() ||  ( 0 == fuPrevTsIndex && 0 == fulTsCounter ) )
+      else
+      {
+         LOG(debug) << "TS direct to storage";
+         /// If not consecutive to last TS sent,
+         fmFullTsStorage.emplace_hint( fmFullTsStorage.end(),
+                                       std::pair< uint64_t, CbmUnpackedTimeslice >( fTsMetaData->GetIndex(),
+                                                                           std::move( CbmUnpackedTimeslice( parts ) ) ) );
+      } // else of if( fuPrevTsIndex + 1 == fTsMetaData->GetIndex() ||  ( 0 == fuPrevTsIndex && 0 == fulTsCounter && 0 == fTsMetaData->GetIndex() )
+   LOG(debug) << "TS metadata checked";
+
+   /// Clear metadata => crashes, maybe not needed as due to move the pointer is invalidated?
+//   delete fTsMetaData;
+
+   /// Check TS queue and process it if needed (in case it filled a hole!)
+   CheckTsQueues();
+   LOG(debug) << "TS queues checked";
+
+   /// 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();
+      } // if( ( fdMaxPublishTime < elapsedSeconds.count() ) || ( 0 == fulNumMessages % fuPublishFreqTs && fdMinPublishTime < elapsedSeconds.count() ) )
+   } // if( kTRUE == fbFillHistos )
+
+   return true;
+}
+//--------------------------------------------------------------------//
+bool CbmDeviceMcbmEventSink::HandleCommand( FairMQMessagePtr& msg, int /*index*/ )
+{
+/*
+   std::string sCommand( static_cast< char * >( msg->GetData() ),
+                          msg->GetSize() );
+*/
+   std::string sCommand;
+   std::string msgStrCmd( static_cast< char * >( msg->GetData() ),
+                          msg->GetSize() );
+   std::istringstream issCmd( msgStrCmd );
+   boost::archive::binary_iarchive inputArchiveCmd( issCmd );
+   inputArchiveCmd >> sCommand;
+
+   std::string sCmdTag = sCommand;
+   size_t charPosDel = sCommand.find( ' ' );
+   if( std::string::npos != charPosDel )
+   {
+      sCmdTag = sCommand.substr( 0, charPosDel );
+   } // if( std::string::npos != charPosDel )
+
+   if( "EOF" == sCmdTag )
+   {
+      fbReceivedEof = true;
+
+      /// Extract the last TS index and global full TS count
+      if( std::string::npos == charPosDel )
+      {
+         LOG( fatal ) << "CbmDeviceMcbmEventSink::HandleCommand => "
+                     << "Incomplete EOF command received: "
+                     << sCommand;
+         return false;
+      } // if( std::string::npos == charPosDel )
+         /// Last TS index
+      charPosDel++;
+      std::string sNext = sCommand.substr( charPosDel );
+      charPosDel = sNext.find( ' ' );
+
+      if( std::string::npos == charPosDel )
+      {
+         LOG( fatal ) << "CbmDeviceMcbmEventSink::HandleCommand => "
+                     << "Incomplete EOF command received: "
+                     << sCommand;
+         return false;
+      } // if( std::string::npos == charPosDel )
+      fuLastTsIndex = std::stoul( sNext.substr( 0, charPosDel ) );
+         /// Total TS count
+      charPosDel++;
+      fuTotalTsCount = std::stoul( sNext.substr( charPosDel ) );
+
+      LOG( info ) << "CbmDeviceMcbmEventSink::HandleCommand => "
+                   << "Received EOF command with final TS index "
+                   << fuLastTsIndex << " and total nb TS " << fuTotalTsCount;
+      /// End of data: clean save of data + close file + send last state of histos if enabled
+      if( fuPrevTsIndex == fuLastTsIndex && fulTsCounter == fuTotalTsCount )
+      {
+         LOG( info ) << "CbmDeviceMcbmEventSink::HandleCommand => "
+                      << "Found final TS index "
+                      << fuLastTsIndex << " and total nb TS " << fuTotalTsCount;
+         Finish();
+      } // if( fuPrevTsIndex == fuLastTsIndex && fulTsCounter == fuTotalTsCount )
+   } // if( "EOF" == sCmdTag )
+   else if( "STOP" == sCmdTag )
+   {
+      /// TODO: different treatment in case of "BAD" ending compared to EOF?
+      /// Source failure: clean save of received data + close file + send last state of histos if enabled
+      Finish();
+   } // else if( "STOP" == sCmdTag )
+      else
+      {
+         LOG(warning) << "Unknown command received: " << sCmdTag << " => will be ignored!";
+      } // else if command not recognized
+
+   return true;
+}
+//--------------------------------------------------------------------//
+void CbmDeviceMcbmEventSink::CheckTsQueues()
+{
+   bool bHoleFoundInBothQueues = false;
+
+   std::map< uint64_t, CbmUnpackedTimeslice >::iterator itFullTs = fmFullTsStorage.begin();
+   std::vector< uint64_t >::iterator           itMissTs = fvulMissedTsIndices.begin();
+
+   while( !bHoleFoundInBothQueues )
+   {
+      /// Check if the first TS in the full TS queue is the next one
+      if( fmFullTsStorage.end() != itFullTs && fuPrevTsIndex + 1 == (*itFullTs).first )
+      {
+         /// Fill all storage variables registers for data output
+         PrepareTreeEntry( (*itFullTs).second );
+         /// Trigger FairRoot manager to dump Tree entry
+         DumpTreeEntry();
+
+         /// Update counters
+         fuPrevTsIndex = (*itFullTs).first;
+         fulTsCounter++;
+
+         /// Increment iterator
+         ++itFullTs;
+         continue;
+      } // if( fmFullTsStorage.end() != itFullTs && fuPrevTsIndex + 1 == (*itFullTs).first() )
+      /// Check if the first TS in the missed TS queue is the next one
+      if( fvulMissedTsIndices.end() != itMissTs && fuPrevTsIndex + 1 == (*itMissTs ) )
+      {
+         /// Prepare entry with only dummy TS metadata and empty storage variables
+         new( (*fTimeSliceMetaDataArray)[ fTimeSliceMetaDataArray->GetEntriesFast()
+                                          ] ) TimesliceMetaData( 0, 0, 0, (*itMissTs ) ) ;
+
+         /// Trigger FairRoot manager to dump Tree entry
+         DumpTreeEntry();
+
+         /// Update counters
+         fuPrevTsIndex = (*itMissTs);
+         fulMissedTsCounter++;
+
+         /// Increment iterator
+         ++itMissTs;
+         continue;
+      } // if( fvulMissedTsIndices.end() != itMissTs && fuPrevTsIndex + 1 == (*itMissTs ) )
+
+      /// Should be reached only if both queues at the end or hole found in both
+      bHoleFoundInBothQueues = true;
+   } // while( !bHoleFoundInBothQueues )
+
+   /// Delete the processed entries
+   fmFullTsStorage.erase(     fmFullTsStorage.begin(),     itFullTs );
+   fvulMissedTsIndices.erase( fvulMissedTsIndices.begin(), itMissTs );
+
+   /// End of data: clean save of data + close file + send last state of histos if enabled
+   if( fbReceivedEof && fuPrevTsIndex == fuLastTsIndex && fulTsCounter == fuTotalTsCount )
+   {
+      LOG( info ) << "CbmDeviceMcbmEventSink::CheckTsQueues => "
+                   << "Found final TS index "
+                   << fuLastTsIndex << " and total nb TS " << fuTotalTsCount;
+      Finish();
+   } // if( fbReceivedEof && fuPrevTsIndex == fuLastTsIndex && fulTsCounter == fuTotalTsCount )
+}
+//--------------------------------------------------------------------//
+void CbmDeviceMcbmEventSink::PrepareTreeEntry( CbmUnpackedTimeslice unpTs )
+{
+   /// FIXME: poor man solution with lots of data copy until we undertsnad how to properly deal
+   /// with FairMq messages ownership and memory managment
+
+   /// FIXME: Not sure if this is the proper way to insert the data
+   new( (*fTimeSliceMetaDataArray)[ fTimeSliceMetaDataArray->GetEntriesFast()
+                                    ] ) TimesliceMetaData( std::move( unpTs.fTsMetaData ) ) ;
+
+/*
+   /// Explicit copy version: safe but slow
+      /// T0
+   fvDigiT0->insert( fvDigiT0->end(), unpTs.fvDigiT0.begin(), unpTs.fvDigiT0.end() );
+      /// STS
+   fvDigiSts->insert( fvDigiSts->end(), unpTs.fvDigiSts.begin(), unpTs.fvDigiSts.end() );
+      /// MUCH
+   fvDigiMuch->insert( fvDigiMuch->end(), unpTs.fvDigiMuch.begin(), unpTs.fvDigiMuch.end() );
+      /// TRD
+   fvDigiTrd->insert( fvDigiTrd->end(), unpTs.fvDigiTrd.begin(), unpTs.fvDigiTrd.end() );
+      /// T0F
+   fvDigiTof->insert( fvDigiTof->end(), unpTs.fvDigiTof.begin(), unpTs.fvDigiTof.end() );
+      /// RICH
+   fvDigiRich->insert( fvDigiRich->end(), unpTs.fvDigiRich.begin(), unpTs.fvDigiRich.end() );
+      /// PSD
+   fvDigiPsd->insert( fvDigiPsd->end(), unpTs.fvDigiPsd.begin(), unpTs.fvDigiPsd.end() );
+*/
+   /// move version: safe but slow
+      /// T0
+   (*fvDigiT0) = std::move( unpTs.fvDigiT0 );
+      /// STS
+   (*fvDigiSts) = std::move( unpTs.fvDigiSts );
+      /// MUCH
+   (*fvDigiMuch) = std::move( unpTs.fvDigiMuch );
+      /// TRD
+   (*fvDigiTrd) = std::move( unpTs.fvDigiTrd );
+      /// T0F
+   (*fvDigiTof) = std::move( unpTs.fvDigiTof );
+      /// RICH
+   (*fvDigiRich) = std::move( unpTs.fvDigiRich );
+      /// PSD
+   (*fvDigiPsd) = std::move( unpTs.fvDigiPsd );
+
+   /// Extract CbmEvent TClonesArray from input message
+   fEventsArray->AbsorbObjects( &( unpTs.fEventsArray ) );
+}
+void CbmDeviceMcbmEventSink::DumpTreeEntry()
+{
+   // Unpacked digis + CbmEvent output to root file
+/*
+ * NH style
+//      fpFairRootMgr->FillEventHeader(fEvtHeader);
+//      LOG(info) << "Fill WriteOutBuffer with FairRootManager at " << fpFairRootMgr;
+//      fpOutRootFile->cd();
+      fpFairRootMgr->Fill();
+      fpFairRootMgr->StoreWriteoutBufferData( fpFairRootMgr->GetEventTime() );
+      //fpFairRootMgr->StoreAllWriteoutBufferData();
+      fpFairRootMgr->DeleteOldWriteoutBufferData();
+*/
+   /// FairRunOnline style
+   fpFairRootMgr->StoreWriteoutBufferData( fpFairRootMgr->GetEventTime() );
+   fpFairRootMgr->Fill();
+   fpFairRootMgr->DeleteOldWriteoutBufferData();
+
+   /// Clear metadata array
+   fTimeSliceMetaDataArray->Clear();
+
+   /// Clear vectors
+   fvDigiT0->clear();
+   fvDigiSts->clear();
+   fvDigiMuch->clear();
+   fvDigiTrd->clear();
+   fvDigiTof->clear();
+   fvDigiRich->clear();
+   fvDigiPsd->clear();
+
+   /// Clear event array
+//   fEventsArray->Delete();
+   fEventsArray->Clear( "C" );
+//   fEventsArray->Clear();
+}
+
+//--------------------------------------------------------------------//
+bool CbmDeviceMcbmEventSink::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 )
+
+   /// Reset the histograms after sending them (but do not reset the time)
+//   fpAlgo->ResetHistograms( kFALSE );
+
+   return true;
+}
+
+//--------------------------------------------------------------------//
+CbmDeviceMcbmEventSink::~CbmDeviceMcbmEventSink()
+{
+   /// FIXME: Add pointers check before delete
+
+   /// Close things properly if not alredy done
+   if( !fbFinishDone )
+      Finish();
+
+   /// Clear metadata
+   fTimeSliceMetaDataArray->Clear();
+   delete fTimeSliceMetaDataArray;
+   delete fTsMetaData;
+
+   /// Clear vectors
+   fvDigiT0->clear();
+   fvDigiSts->clear();
+   fvDigiMuch->clear();
+   fvDigiTrd->clear();
+   fvDigiTof->clear();
+   fvDigiRich->clear();
+   fvDigiPsd->clear();
+
+   /// Clear events TClonesArray
+   fEventsArray->Clear();
+   delete fEventsArray;
+
+   delete fpRun;
+}
+
+void CbmDeviceMcbmEventSink::Finish()
+{
+   // Clean closure of output to root file
+   fpFairRootMgr->Write();
+//   fpFairRootMgr->GetSource()->Close();
+   fpFairRootMgr->CloseSink();
+   LOG(info) << "File closed after saving "
+             << ( fulTsCounter + fulMissedTsCounter ) << " TS ("
+             << fulTsCounter << " full ones and "
+             << fulMissedTsCounter << " missed/empty ones)";
+
+   if( kTRUE == fbFillHistos )
+   {
+      SendHistograms();
+      fLastPublishTime = std::chrono::system_clock::now();
+   } // if( kTRUE == fbFillHistos )
+
+   ChangeState( fair::mq::Transition::Stop );
+   std::this_thread::sleep_for( std::chrono::milliseconds( 3000 ) );
+   ChangeState( fair::mq::Transition::End );
+
+   fbFinishDone = kTRUE;
+}
+
+CbmUnpackedTimeslice::CbmUnpackedTimeslice( FairMQParts & parts ) :
+   fEventsArray( "CbmEvent", 500 )
+{
+   /// Extract unpacked data from input message
+   uint32_t uPartIdx = 0;
+      /// TS metadata
+      /// TODO: code order of vectors in the TS MetaData!!
+/*
+  std::string msgStrTsMeta( static_cast< char * >( parts.At( uPartIdx )->GetData() ),
+                            ( parts.At( uPartIdx ) )->GetSize() );
+  std::istringstream issTsMeta(msgStrTsMeta);
+  boost::archive::binary_iarchive inputArchiveTsMeta(issTsMeta);
+  inputArchiveTsMeta >> (*fTsMetaData);
+  ++uPartIdx;
+*/
+   TObject* tempObjectMeta = nullptr;
+   RootSerializer().Deserialize( *parts.At( uPartIdx ), tempObjectMeta );
+   ++uPartIdx;
+
+   if( TString( tempObjectMeta->ClassName() ).EqualTo( "TimesliceMetaData") )
+   {
+      fTsMetaData = *( static_cast< TimesliceMetaData * >( tempObjectMeta ) );
+   } // if( TString( tempObject->ClassName() ).EqualTo( "TClonesArray") )
+
+      /// 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;
+
+   /// Extract CbmEvent TClonesArray from input message
+   TObject* tempObject = nullptr;
+   RootSerializer().Deserialize( *parts.At( uPartIdx ), tempObject);
+   ++uPartIdx;
+
+   if( TString( tempObject->ClassName() ).EqualTo( "TClonesArray") )
+   {
+      TClonesArray* arrayEventsIn = static_cast< TClonesArray * >( tempObject );
+
+      /// Copy data in registered TClonesArray (by taking ownership!)
+      fEventsArray.AbsorbObjects( arrayEventsIn );
+   } // if( TString( tempObject->ClassName() ).EqualTo( "TClonesArray") )
+}
diff --git a/MQ/mcbm/CbmDeviceMcbmEventSink.h b/MQ/mcbm/CbmDeviceMcbmEventSink.h
new file mode 100644
index 0000000000..f1a195b516
--- /dev/null
+++ b/MQ/mcbm/CbmDeviceMcbmEventSink.h
@@ -0,0 +1,172 @@
+/**
+ * CbmDeviceMcbmEventSink.h
+ *
+ * @since 2020-05-04
+ * @author P.-A. Loizeau
+ */
+
+#ifndef CBMDEVICEMCBMEVTSINK_H_
+#define CBMDEVICEMCBMEVTSINK_H_
+
+/// CBM headers
+#include "CbmStsDigi.h"
+#include "CbmMuchBeamTimeDigi.h"
+#include "CbmRichDigi.h"
+#include "CbmTrdDigi.h"
+#include "CbmTofDigi.h"
+#include "CbmPsdDigi.h"
+#include "CbmEvent.h"
+#include "TimesliceMetaData.h"
+
+/// FAIRROOT headers
+#include "FairMQDevice.h"
+
+/// FAIRSOFT headers (geant, boost, ...)
+#include "TMessage.h"
+#include "Rtypes.h"
+#include "TObjArray.h"
+#include "TClonesArray.h"
+
+/// C/C++ headers
+#include <vector>
+#include <map>
+#include <chrono>
+
+class TFile;
+class TList;
+class TClonesArray;
+//class TimesliceMetaData;
+class FairRunOnline;
+class FairRootManager;
+
+class CbmUnpackedTimeslice
+{
+   /// TODO: rename to CbmTsWithEvents
+   public:
+      CbmUnpackedTimeslice( FairMQParts & parts );
+
+      TimesliceMetaData                  fTsMetaData;
+      std::vector< CbmTofDigi >          fvDigiT0;
+      std::vector< CbmStsDigi >          fvDigiSts;
+      std::vector< CbmMuchBeamTimeDigi > fvDigiMuch;
+      std::vector< CbmTrdDigi >          fvDigiTrd;
+      std::vector< CbmTofDigi >          fvDigiTof;
+      std::vector< CbmRichDigi >         fvDigiRich;
+      std::vector< CbmPsdDigi >          fvDigiPsd;
+      TClonesArray                       fEventsArray;
+};
+
+class CbmDeviceMcbmEventSink: public FairMQDevice
+{
+   public:
+      CbmDeviceMcbmEventSink();
+      virtual ~CbmDeviceMcbmEventSink();
+
+   protected:
+      virtual void InitTask();
+      bool HandleMissTsData(FairMQMessagePtr&, int);
+      bool HandleData(FairMQParts&, int);
+      bool HandleCommand(FairMQMessagePtr&, int);
+
+   private:
+      /// Constants
+
+      /// Control flags
+      Bool_t fbFillHistos            = kFALSE;  //! Switch ON/OFF filling of histograms
+      Bool_t fbFinishDone            = kFALSE;  //! Keep track of whether the Finish was already called
+
+      /// User settings parameters
+         /// Algo enum settings
+      std::string fsOutputFileName = "mcbm_digis_events.root";
+         /// message queues
+      std::string fsChannelNameMissedTs     = "missedts";
+      std::string fsChannelNameDataInput    = "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 };
+
+      /// Parameters management
+//      TList* fParCList = nullptr;
+//      Bool_t InitParameters( TList* fParCList );
+
+      /// Statistics & missed TS detection
+      uint64_t fuPrevTsIndex      = 0;
+      uint64_t fulNumMessages     = 0;
+      uint64_t fulTsCounter       = 0;
+      uint64_t fulMissedTsCounter = 0;
+      std::chrono::system_clock::time_point fLastPublishTime = std::chrono::system_clock::now();
+
+      /// Control Commands reception
+      bool     fbReceivedEof  = false;
+      uint64_t fuLastTsIndex  = 0;
+      uint64_t fuTotalTsCount = 0;
+
+      /// Data reception
+         /// Event (TS) header
+//      FairEventHeader*        fEvtHeader;
+         /// 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;
+         /// CbmEvents
+      TClonesArray * fEventsArray = nullptr;    //! output container of CbmEvents
+//      std::vector< CbmEvent * > &        fEventVector;    //! vector with all created events
+
+      /// Storage for re-ordering
+         /// Missed TS vector
+      std::vector< uint64_t > fvulMissedTsIndices = {};
+         /// Buffered TS
+      std::map< uint64_t, CbmUnpackedTimeslice > fmFullTsStorage = {};
+
+      /// Data storage
+      FairRunOnline   * fpRun         = nullptr;
+      FairRootManager * fpFairRootMgr = nullptr ;
+      TFile           * fpOutRootFile = 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 = {};
+
+      /// Internal methods
+      bool IsChannelNameAllowed(std::string channelName);
+//      Bool_t InitContainers();
+      void CheckTsQueues();
+      void PrepareTreeEntry( CbmUnpackedTimeslice unpTs );
+      void DumpTreeEntry();
+      bool SendHistograms();
+      void Finish();
+};
+
+// special class to expose protected TMessage constructor
+class CbmMQTMessage : public TMessage
+{
+  public:
+    CbmMQTMessage(void* buf, Int_t len)
+        : TMessage(buf, len)
+    {
+        ResetBit(kIsOwner);
+    }
+};
+
+
+#endif /* CBMDEVICEMCBMEVTSINK_H_ */
diff --git a/MQ/mcbm/CbmDeviceMcbmMonitorPulser.cxx b/MQ/mcbm/CbmDeviceMcbmMonitorPulser.cxx
new file mode 100644
index 0000000000..fe4052e0c5
--- /dev/null
+++ b/MQ/mcbm/CbmDeviceMcbmMonitorPulser.cxx
@@ -0,0 +1,408 @@
+/**
+ * CbmDeviceMcbmMonitorPulser.cxx
+ *
+ * @since 2020-05-04
+ * @author P.-A Loizeau
+ */
+
+#include "CbmDeviceMcbmMonitorPulser.h"
+
+#include "TimesliceMetaData.h"
+#include "CbmMQDefs.h"
+
+//#include "CbmMcbm2018MonitorAlgoTof.h"
+#include "CbmFlesCanvasTools.h"
+
+#include "StorableTimeslice.hpp"
+
+#include "FairMQLogger.h"
+#include "FairMQProgOptions.h" // device->fConfig
+#include "FairParGenericSet.h"
+#include "RootSerializer.h"
+#include "BoostSerializer.h"
+
+#include "TNamed.h"
+#include "TList.h"
+#include "TCanvas.h"
+#include "TFile.h"
+#include "TH1.h"
+
+#include <string>
+#include <iomanip>
+#include <array>
+
+#include <boost/serialization/utility.hpp>
+#include <boost/archive/binary_iarchive.hpp>
+
+#include <stdexcept>
+struct InitTaskError : std::runtime_error { using std::runtime_error::runtime_error; };
+
+using namespace std;
+
+//Bool_t bMcbm2018MonitorTaskTofResetHistos = kFALSE;
+
+CbmDeviceMcbmMonitorPulser::CbmDeviceMcbmMonitorPulser()
+//   : fMonitorAlgo{ new CbmMcbm2018MonitorAlgoTof() }
+{
+}
+
+void CbmDeviceMcbmMonitorPulser::InitTask()
+try
+{
+   /// Read options from executable
+   LOG(info) << "Init options for CbmMqStarHistoServer.";
+
+   fbDebugMonitorMode     = fConfig->GetValue< bool >( "DebugMoni" );
+   fuHistoryHistoSize     = fConfig->GetValue< uint32_t >( "HistEvoSz" );
+   fuMinTotPulser         = fConfig->GetValue< uint32_t >( "PulsTotMin" );
+   fuMaxTotPulser         = fConfig->GetValue< uint32_t >( "PulsTotMax" );
+
+   fuPublishFreqTs        = fConfig->GetValue< uint32_t >( "PubFreqTs" );
+   fdMinPublishTime       = fConfig->GetValue< double_t >( "PubTimeMin" );
+   fdMaxPublishTime       = fConfig->GetValue< double_t >( "PubTimeMax" );
+   fsChannelNameDataInput    = fConfig->GetValue< std::string >( "TsNameIn" );
+   fsChannelNameHistosInput  = fConfig->GetValue< std::string >( "ChNameIn" );
+   fsChannelNameHistosConfig = fConfig->GetValue< std::string >( "ChNameHistCfg" );
+   fsChannelNameCanvasConfig = fConfig->GetValue< std::string >( "ChNameCanvCfg" );
+   fsAllowedChannels[ 0 ] = fsChannelNameDataInput;
+
+   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;
+
+   /// Set the Monitor Algo in Absolute time scale
+//   fMonitorAlgo->UseAbsoluteTime();
+
+   // 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, &CbmDeviceMcbmMonitorPulser::HandleData );
+      } // if( std::string::npos != entry.first.find( fsChannelNameDataInput ) )
+   } // for( auto const &entry : fChannels )
+   InitContainers();
+} 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 CbmDeviceMcbmMonitorPulser::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;
+      } // if (pos1!=std::string::npos)
+   } // for(auto const &entry : fsAllowedChannels)
+   LOG(info) << "Channel name " << channelName
+            << " not found in list of allowed channel names.";
+   LOG(error) << "Stop device.";
+   return false;
+}
+
+Bool_t CbmDeviceMcbmMonitorPulser::InitContainers()
+{
+   LOG(info) << "Init parameter containers for CbmDeviceMcbmMonitorPulser.";
+   Bool_t initOK = kTRUE;
+/*
+   fParCList = fMonitorAlgo->GetParList();
+
+   for( int iparC = 0; iparC < fParCList->GetEntries(); iparC++ ) {
+      FairParGenericSet* tempObj = (FairParGenericSet*)( fParCList->At( iparC ) );
+      fParCList->Remove( tempObj );
+      std::string paramName{ tempObj->GetName() };
+      // NewSimpleMessage creates a copy of the data and takes care of its destruction (after the transfer takes place).
+      // Should only be used for small data because of the cost of an additional copy
+
+      // Her must come the proper Runid
+      std::string message = paramName + ",111";
+      LOG(info) << "Requesting parameter container " << paramName << ", sending message: " << message;
+
+      FairMQMessagePtr req( NewSimpleMessage(message) );
+      FairMQMessagePtr rep( NewMessage() );
+
+      FairParGenericSet* newObj = nullptr;
+
+      if ( Send(req, "parameters") > 0 ) {
+         if ( Receive( rep, "parameters" ) >= 0) {
+            if ( rep->GetSize() != 0 ) {
+               CbmMQTMessage tmsg( rep->GetData(), rep->GetSize() );
+               newObj = static_cast< FairParGenericSet* >( tmsg.ReadObject( tmsg.GetClass() ) );
+               LOG( info ) << "Received unpack parameter from the server:";
+               newObj->print();
+            } else {
+               LOG( error ) << "Received empty reply. Parameter not available";
+            } // if (rep->GetSize() != 0)
+         } // if (Receive(rep, "parameters") >= 0)
+      } // if (Send(req, "parameters") > 0)
+      fParCList->AddAt( newObj, iparC );
+      delete tempObj;
+   } // for ( int iparC = 0; iparC < fParCList->GetEntries(); iparC++ )
+
+   /// Need to add accessors for all options
+   fMonitorAlgo->SetIgnoreOverlapMs( fbIgnoreOverlapMs );
+   fMonitorAlgo->SetDebugMonitorMode( fbDebugMonitorMode );
+   fMonitorAlgo->SetIgnoreCriticalErrors( fbIgnoreCriticalErrors );
+   fMonitorAlgo->SetHistoryHistoSize( fuHistoryHistoSize );
+   fMonitorAlgo->SetPulserTotLimits( fuMinTotPulser, fuMaxTotPulser );
+
+   Bool_t initOK = fMonitorAlgo->InitContainers();
+*/
+//   Bool_t initOK = fMonitorAlgo->ReInitContainers();
+
+//   CreateHistos();
+/*
+   /// Histos creation and obtain pointer on them
+      /// Trigger histo creation on all associated algos
+   initOK &= fMonitorAlgo->CreateHistograms();
+
+      /// 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();
+
+   /// 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 )
+      {
+         LOG(error) << "Problem sending histo config";
+         return false;
+      } // 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 )
+
+   /// 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 )
+      {
+         LOG(error) << "Problem sending canvas config";
+         return false;
+      } // 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 )
+*/
+  return initOK;
+}
+
+
+// handler is called whenever a message arrives on "data", with a reference to the message and a sub-channel index (here 0)
+bool CbmDeviceMcbmMonitorPulser::HandleData(FairMQParts& parts, int /*index*/)
+{
+   fulNumMessages++;
+
+   LOG(debug) << "Received message "<< fulNumMessages << " with "
+	     << parts.Size() << " parts" << ", size0: " << parts.At(0)->GetSize();
+
+   uint32_t uPartIdx = 0;
+
+   /// TODO: code order of vectors in the TS MetaData!!
+/*
+  std::string msgStrTsMeta( static_cast< char * >( parts.At( uPartIdx )->GetData() ),
+                            ( parts.At( uPartIdx ) )->GetSize() );
+  std::istringstream issTsMeta(msgStrTsMeta);
+  boost::archive::binary_iarchive inputArchiveTsMeta(issTsMeta);
+  inputArchiveTsMeta >> (*fTsMetaData);
+  ++uPartIdx;
+*/
+   Deserialize< RootSerializer >( *parts.At( uPartIdx ), fTsMetaData );
+   ++uPartIdx;
+
+   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;
+
+   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;
+
+   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;
+
+   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;
+
+   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;
+
+   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;
+
+   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;
+
+   /// Process data in Algo
+
+   /// Clear vectors
+   delete fTsMetaData;
+   fvDigiT0.clear();
+   fvDigiSts.clear();
+   fvDigiMuch.clear();
+   fvDigiTrd.clear();
+   fvDigiTof.clear();
+   fvDigiRich.clear();
+   fvDigiPsd.clear();
+
+/*
+   LOG(debug) << "Received message number "<<  fulNumMessages
+              << " with size " << msg->GetSize();
+
+   if( 0 == fulNumMessages % 10000 )
+      LOG(info) << "Received " << fulNumMessages << " messages";
+
+   std::string msgStr( static_cast<char*>( msg->GetData() ), msg->GetSize() );
+   std::istringstream iss( msgStr );
+   boost::archive::binary_iarchive inputArchive( iss );
+
+   /// Create an empty TS and fill it with the incoming message
+   fles::StorableTimeslice component{ 0 };
+   inputArchive >> component;
+
+   /// Process the Timeslice
+   DoUnpack(component, 0);
+
+   /// 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() ) )
+*/
+   return true;
+}
+
+bool CbmDeviceMcbmMonitorPulser::SendHistograms()
+{
+   /// Serialize the array of histos into a single MQ message
+   FairMQMessagePtr message( NewMessage() );
+   Serialize<RootSerializer>( *message, &fArrayHisto );
+
+  // test code to check if deserialization works
+/*
+  TObject* tempObject = nullptr;
+  Deserialize<RootDeserializer>(*message, tempObject);
+
+  if (TString(tempObject->ClassName()).EqualTo("TObjArray")) {
+   TObjArray* arrayHisto = static_cast<TObjArray*>(tempObject);
+   LOG(info) << "Array contains " << arrayHisto->GetEntriesFast()
+             << " entries";
+    for (Int_t i = 0; i < arrayHisto->GetEntriesFast(); i++) {
+      TObject* obj = arrayHisto->At(i);
+      LOG(info) << obj->GetName();
+      TH1* histogram = static_cast<TH1*>(obj);
+      LOG(info) << histogram->GetNbinsX();
+    }
+  }
+*/
+
+   /// 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)
+//   fMonitorAlgo->ResetHistograms( kFALSE );
+
+   return true;
+}
+
+
+CbmDeviceMcbmMonitorPulser::~CbmDeviceMcbmMonitorPulser()
+{
+}
+
+void CbmDeviceMcbmMonitorPulser::Finish()
+{
+}
diff --git a/MQ/mcbm/CbmDeviceMcbmMonitorPulser.h b/MQ/mcbm/CbmDeviceMcbmMonitorPulser.h
new file mode 100644
index 0000000000..8eb3ec00ac
--- /dev/null
+++ b/MQ/mcbm/CbmDeviceMcbmMonitorPulser.h
@@ -0,0 +1,276 @@
+/**
+ * CbmDeviceMcbmMonitorPulser.h
+ *
+ * @since 2020-05-04
+ * @author P.-A Loizeau
+ */
+
+#ifndef CBMDEVICEMCBMMONITORPULSER_H_
+#define CBMDEVICEMCBMMONITORPULSER_H_
+
+#include "CbmStsDigi.h"
+#include "CbmMuchBeamTimeDigi.h"
+#include "CbmRichDigi.h"
+#include "CbmTrdDigi.h"
+#include "CbmTofDigi.h"
+#include "CbmPsdDigi.h"
+
+#include "FairMQDevice.h"
+
+#include "TMessage.h"
+#include "Rtypes.h"
+#include "TObjArray.h"
+
+#include <vector>
+#include <map>
+#include <chrono>
+
+class TH1;
+class TH2;
+class TProfile;
+class TList;
+//class CbmMcbm2018MonitorAlgoTof;
+class TimesliceMetaData;
+
+class CbmDeviceMcbmMonitorPulser: public FairMQDevice
+{
+   public:
+      CbmDeviceMcbmMonitorPulser();
+      virtual ~CbmDeviceMcbmMonitorPulser();
+
+   protected:
+      virtual void InitTask();
+      bool HandleData(FairMQParts&, int);
+
+   private:
+      /// Constants
+/*********************** SHOULD GO IN ALGO ****************************/
+      static const UInt_t kuNbChanSMX      =        128;
+      static const UInt_t kuMaxNbStsDpbs   =          2;
+      static const UInt_t kuMaxNbMuchDpbs  =          6;
+      static const UInt_t kuMaxNbMuchAsics =         36;
+      static const UInt_t kuDefaultAddress = 0xFFFFFFFF;
+      static const UInt_t kuMaxChannelSts  =       3000;
+/*********************** SHOULD GO IN ALGO ****************************/
+
+      /// Control flags
+      Bool_t fbDebugMonitorMode      = kFALSE; //! Switch ON the filling of a additional set of histograms
+      Bool_t fbIgnoreCriticalErrors  = kTRUE; //! If ON not printout at all for critical errors
+      Bool_t fbComponentsAddedToList = kFALSE;
+
+      /// User settings parameters
+      std::string fsChannelNameDataInput    = "unpts_0";
+      std::string fsChannelNameCommands     = "commands";
+      std::string fsChannelNameHistosInput  = "histogram-in";
+      std::string fsChannelNameHistosConfig = "histo-conf";
+      std::string fsChannelNameCanvasConfig = "canvas-conf";
+      uint32_t    fuHistoryHistoSize = 3600;
+      uint32_t    fuMinTotPulser     =  185;
+      uint32_t    fuMaxTotPulser     =  195;
+      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 };
+
+      /// Parameters management
+/*
+      TList* fParCList = nullptr;
+*/
+
+      /// Statistics & first TS rejection
+      uint64_t fulNumMessages = 0;
+      uint64_t fulTsCounter   = 0;
+      std::chrono::system_clock::time_point fLastPublishTime = std::chrono::system_clock::now();
+
+      /// Data reception
+         /// TS MetaData storage
+      TimesliceMetaData * fTsMetaData = nullptr;
+         /// Digis storage
+      std::vector< CbmTofDigi >          fvDigiT0   = {};
+      std::vector< CbmStsDigi >          fvDigiSts  = {};
+      std::vector< CbmMuchBeamTimeDigi > fvDigiMuch = {};
+      std::vector< CbmTrdDigi >          fvDigiTrd  = {};
+      std::vector< CbmTofDigi >          fvDigiTof  = {};
+      std::vector< CbmRichDigi >         fvDigiRich = {};
+      std::vector< CbmPsdDigi >          fvDigiPsd  = {};
+
+      /// Processing algo
+//      CbmMcbm2018MonitorAlgoTof * fMonitorAlgo;
+
+      /// 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);
+      Bool_t InitContainers();
+      void Finish();
+      bool SendHistograms();
+
+/*********************** SHOULD GO IN ALGO ****************************/
+    void CheckInterSystemOffset();
+
+    template <class Digi> Int_t FillSystemOffsetHistos(TH1* histo,
+                                 TH2* histoEvo, TH2* histoEvoLong, TProfile * profMeanEvo,
+                                 TH2* histoAFCK,
+                                 const Double_t T0Time, const Int_t offsetRange, Int_t iStartDigi,
+                                 ECbmModuleId iDetId = ECbmModuleId::kLastModule
+                                  );
+
+    Int_t CalcNrBins(Int_t);
+    void CreateHistos();
+
+    /// Variables to store the previous digi time
+    Double_t fPrevTimeT0   = 0.;
+    Double_t fPrevTimeSts  = 0.;
+    Double_t fPrevTimeMuch = 0.;
+    Double_t fPrevTimeTrd  = 0.;
+    Double_t fPrevTimeTof  = 0.;
+    Double_t fPrevTimeRich = 0.;
+    Double_t fPrevTimePsd  = 0.;
+
+    /// Variables to store the first digi fitting the previous T0 hits
+    /// => Time-order means the time window for following one can only be in a later digi
+    Int_t fPrevT0FirstDigiSts  = 0;
+    Int_t fPrevT0FirstDigiMuch = 0;
+    Int_t fPrevT0FirstDigiTrd  = 0;
+    Int_t fPrevT0FirstDigiTof  = 0;
+    Int_t fPrevT0FirstDigiRich = 0;
+    Int_t fPrevT0FirstDigiPsd  = 0;
+
+    /// User settings: Data correction parameters
+        /// Charge cut
+    UInt_t fuMinTotPulserT0   = 182;
+    UInt_t fuMaxTotPulserT0   = 190;
+    UInt_t fuMinAdcPulserSts  =  90;
+    UInt_t fuMaxAdcPulserSts  = 100;
+    UInt_t fuMinAdcPulserMuch =   5;
+    UInt_t fuMaxAdcPulserMuch =  15;
+    UInt_t fuMinChargePulserTrd  = 0;
+    UInt_t fuMaxChargePulserTrd  = 70000;
+    UInt_t fuMinTotPulserTof  = 182;
+    UInt_t fuMaxTotPulserTof  = 190;
+    UInt_t fuMinTotPulserRich =  90;
+    UInt_t fuMaxTotPulserRich = 105;
+    UInt_t fuMinAdcPulserPsd  =  90;
+    UInt_t fuMaxAdcPulserPsd  = 100;
+        /// Channel selection
+    UInt_t fuStsAddress       = kuDefaultAddress;
+    UInt_t fuStsFirstCha      = kuMaxChannelSts;
+    UInt_t fuStsLastChan      = kuMaxChannelSts;
+    UInt_t fuMuchAsic         = kuMaxNbMuchAsics;
+    UInt_t fuMuchFirstCha     = kuNbChanSMX;
+    UInt_t fuMuchLastChan     = kuNbChanSMX;
+    UInt_t fuTrdAddress       = kuDefaultAddress;
+    UInt_t fuPsdAddress       = kuDefaultAddress;
+
+    //
+    Int_t fNrTs = 0;
+
+    Int_t fOffsetRange     = 1000;
+    Int_t fStsOffsetRange  = 1000;
+    Int_t fMuchOffsetRange = 1000;
+    Int_t fTrdOffsetRange  = 1000;
+    Int_t fTofOffsetRange  = 1000;
+    Int_t fRichOffsetRange = 1000;
+    Int_t fPsdOffsetRange  = 1000;
+
+    Int_t fBinWidth = 1;
+
+    TH1* fT0StsDiff  = nullptr;
+    TH1* fT0MuchDiff = nullptr;
+    TH1* fT0TrdDiff  = nullptr;
+    TH1* fT0TofDiff  = nullptr;
+    TH1* fT0RichDiff = nullptr;
+    TH1* fT0PsdDiff  = nullptr;
+    TH2* fT0PsdDiffCharge = nullptr;
+
+    TH2* fT0StsDiffEvo  = nullptr;
+    TH2* fT0MuchDiffEvo = nullptr;
+    TH2* fT0TrdDiffEvo  = nullptr;
+    TH2* fT0TofDiffEvo  = nullptr;
+    TH2* fT0RichDiffEvo = nullptr;
+    TH2* fT0PsdDiffEvo  = nullptr;
+
+    TH2* fT0StsDiffEvoLong  = nullptr;
+    TH2* fT0MuchDiffEvoLong = nullptr;
+    TH2* fT0TrdDiffEvoLong  = nullptr;
+    TH2* fT0TofDiffEvoLong  = nullptr;
+    TH2* fT0RichDiffEvoLong = nullptr;
+    TH2* fT0PsdDiffEvoLong  = nullptr;
+
+    Double_t fdStartTime = -1;
+    TProfile* fT0StsMeanEvo  = nullptr;
+    TProfile* fT0MuchMeanEvo = nullptr;
+    TProfile* fT0TrdMeanEvo  = nullptr;
+    TProfile* fT0TofMeanEvo  = nullptr;
+    TProfile* fT0RichMeanEvo = nullptr;
+    TProfile* fT0PsdMeanEvo  = nullptr;
+
+    TH1* fT0T0Diff     = nullptr;
+    TH1* fStsStsDiff   = nullptr;
+    TH1* fMuchMuchDiff = nullptr;
+    TH1* fTrdTrdDiff   = nullptr;
+    TH1* fTofTofDiff   = nullptr;
+    TH1* fRichRichDiff = nullptr;
+    TH1* fPsdPsdDiff   = nullptr;
+
+    TH2* fT0StsNb  = nullptr;
+    TH2* fT0MuchNb = nullptr;
+    TH2* fT0TrdNb  = nullptr;
+    TH2* fT0TofNb  = nullptr;
+    TH2* fT0RichNb = nullptr;
+    TH2* fT0PsdNb  = nullptr;
+
+    Int_t fiT0Nb   = 0;
+    Int_t fiStsNb  = 0;
+    Int_t fiMuchNb = 0;
+    Int_t fiTrdNb  = 0;
+    Int_t fiTofNb  = 0;
+    Int_t fiRichNb = 0;
+    Int_t fiPsdNb  = 0;
+
+    TH1* fT0Address = nullptr;
+    TH1* fT0Channel = nullptr;
+
+    TH2* fT0StsDpbDiff = nullptr;
+    TH2* fT0StsDpbDiffEvo[kuMaxNbStsDpbs];
+    TH1* fStsDpbCntsEvo[kuMaxNbStsDpbs];
+
+    TH2* fT0MuchRocDiff = nullptr;
+    TH2* fT0MuchAsicDiff = nullptr;
+    TH2* fT0MuchAsicDiffEvo[kuMaxNbMuchAsics];
+
+    TH2* fDigisPerAsicEvo = nullptr;
+    Double_t fdLastMuchDigi[kuMaxNbMuchAsics][ kuNbChanSMX ];
+    Double_t fdLastMuchDigiPulser[ kuMaxNbMuchAsics ][ kuNbChanSMX ];
+    TH2* fSameChanDigisDistEvo = nullptr;
+
+    Double_t fdLastT0DigiPulser = 0;
+
+    TH2* fDigiTimeEvoT0   = nullptr;
+    TH2* fDigiTimeEvoSts  = nullptr;
+    TH2* fDigiTimeEvoMuch = nullptr;
+    TH2* fDigiTimeEvoTof  = nullptr;
+/*********************** SHOULD GO IN ALGO ****************************/
+};
+
+// special class to expose protected TMessage constructor
+class CbmMQTMessage : public TMessage
+{
+  public:
+    CbmMQTMessage(void* buf, Int_t len)
+        : TMessage(buf, len)
+    {
+        ResetBit(kIsOwner);
+    }
+};
+
+
+#endif /* CBMDEVICEMCBMMONITORPULSER_H_ */
diff --git a/MQ/mcbm/CbmDeviceMcbmUnpack.cxx b/MQ/mcbm/CbmDeviceMcbmUnpack.cxx
new file mode 100644
index 0000000000..5a01f20552
--- /dev/null
+++ b/MQ/mcbm/CbmDeviceMcbmUnpack.cxx
@@ -0,0 +1,584 @@
+/**
+ * CbmDeviceMcbmUnpack.cxx
+ *
+ * @since 2020-05-04
+ * @author P.-A. Loizeau
+ */
+
+#include "CbmDeviceMcbmUnpack.h"
+#include "CbmMQDefs.h"
+
+#include "TimesliceMetaData.h"
+#include "CbmMcbm2018UnpackerAlgoSts.h"
+#include "CbmMcbm2018UnpackerAlgoMuch.h"
+#include "CbmMcbm2018UnpackerAlgoTrdR.h"
+#include "CbmMcbm2018UnpackerAlgoTof.h"
+#include "CbmMcbm2018UnpackerAlgoRich.h"
+#include "CbmMcbm2018UnpackerAlgoPsd.h"
+#include "CbmFlesCanvasTools.h"
+
+#include "StorableTimeslice.hpp"
+
+#include "FairMQLogger.h"
+#include "FairMQProgOptions.h" // device->fConfig
+#include "FairParGenericSet.h"
+#include "RootSerializer.h"
+#include "BoostSerializer.h"
+
+#include "TNamed.h"
+#include "TList.h"
+#include "TCanvas.h"
+#include "TFile.h"
+#include "TH1.h"
+
+#include <string>
+#include <iomanip>
+#include <array>
+
+#include <boost/serialization/utility.hpp>
+#include <boost/archive/binary_iarchive.hpp>
+
+#include <stdexcept>
+struct InitTaskError : std::runtime_error { using std::runtime_error::runtime_error; };
+
+using namespace std;
+
+Bool_t bMcbm2018MonitorTaskT0ResetHistos = kFALSE;
+
+CbmDeviceMcbmUnpack::CbmDeviceMcbmUnpack()
+{
+   fUnpAlgoSts  = new CbmMcbm2018UnpackerAlgoSts();
+   fUnpAlgoMuch = new CbmMcbm2018UnpackerAlgoMuch();
+   fUnpAlgoTrd  = new CbmMcbm2018UnpackerAlgoTrdR();
+   fUnpAlgoTof  = new CbmMcbm2018UnpackerAlgoTof();
+   fUnpAlgoRich = new CbmMcbm2018UnpackerAlgoRich();
+   fUnpAlgoPsd  = new CbmMcbm2018UnpackerAlgoPsd();
+}
+
+void CbmDeviceMcbmUnpack::InitTask()
+try
+{
+   /// Read options from executable
+   LOG(info) << "Init options for CbmDeviceMcbmUnpack.";
+   fbIgnoreOverlapMs         = fConfig->GetValue< bool >( "IgnOverMs" );
+   fvsSetTimeOffs            = fConfig->GetValue< std::vector< std::string >  >( "SetTrigWin" );
+   fsChannelNameDataInput    = fConfig->GetValue< std::string >( "TsNameIn" );
+   fsChannelNameDataOutput   = fConfig->GetValue< std::string >( "TsNameOut" );
+   /// TODO: option to set fuDigiMaskedIdT0 !!!!
+   fsAllowedChannels[ 0 ] = fsChannelNameDataInput;
+
+   // 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, &CbmDeviceMcbmUnpack::HandleData );
+      } // if( entry.first.find( "ts" )
+   } // for( auto const &entry : fChannels )
+   InitContainers();
+} 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 CbmDeviceMcbmUnpack::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;
+      } // if (pos1!=std::string::npos)
+   } // for(auto const &entry : fsAllowedChannels)
+   LOG(info) << "Channel name " << channelName
+            << " not found in list of allowed channel names.";
+   LOG(error) << "Stop device.";
+   return false;
+}
+
+Bool_t CbmDeviceMcbmUnpack::InitContainers()
+{
+   LOG(info) << "Init parameter containers for CbmDeviceMcbmUnpack.";
+
+   if( kFALSE == InitParameters( fUnpAlgoSts ->GetParList() ) )
+      return kFALSE;
+   if( kFALSE == InitParameters( fUnpAlgoMuch->GetParList() ) )
+      return kFALSE;
+   if( kFALSE == InitParameters( fUnpAlgoTrd ->GetParList() ) )
+      return kFALSE;
+   if( kFALSE == InitParameters( fUnpAlgoTof ->GetParList() ) )
+      return kFALSE;
+   if( kFALSE == InitParameters( fUnpAlgoRich->GetParList() ) )
+      return kFALSE;
+   if( kFALSE == InitParameters( fUnpAlgoPsd ->GetParList() ) )
+      return kFALSE;
+
+   /// Need to add accessors for all options
+   fUnpAlgoSts ->SetIgnoreOverlapMs( fbIgnoreOverlapMs );
+   fUnpAlgoMuch->SetIgnoreOverlapMs( fbIgnoreOverlapMs );
+   fUnpAlgoTrd ->SetIgnoreOverlapMs( fbIgnoreOverlapMs );
+   fUnpAlgoTof ->SetIgnoreOverlapMs( fbIgnoreOverlapMs );
+   fUnpAlgoRich->SetIgnoreOverlapMs( fbIgnoreOverlapMs );
+   fUnpAlgoPsd ->SetIgnoreOverlapMs( fbIgnoreOverlapMs );
+
+   /// Load time offsets
+   for( std::vector< std::string >::iterator itStrOffs = fvsSetTimeOffs.begin(); itStrOffs != fvsSetTimeOffs.end(); ++itStrOffs )
+   {
+      size_t charPosDel = (*itStrOffs).find( ',' );
+      if( std::string::npos == charPosDel )
+      {
+         LOG( info ) << "CbmDeviceMcbmUnpack::InitContainers => "
+                     << "Trying to set trigger window with invalid option pattern, ignored! "
+                     << " (Should be ECbmModuleId,dWinBeg,dWinEnd but instead found "
+                     << (*itStrOffs) << " )";
+      } // if( std::string::npos == charPosDel )
+
+      /// Detector Enum Tag
+      std::string sSelDet = (*itStrOffs).substr( 0, charPosDel );
+      /// Min number
+      charPosDel++;
+      Double_t dOffset = std::stod( (*itStrOffs).substr( charPosDel ) );
+
+      if( "kSTS"  == sSelDet )
+      {
+         fUnpAlgoSts ->SetTimeOffsetNs( dOffset );
+      } // if( "kSTS"  == sSelDet )
+      else if( "kMUCH" == sSelDet )
+      {
+         fUnpAlgoMuch->SetTimeOffsetNs( dOffset );
+      } // else if( "kMUCH" == sSelDet )
+      else if( "kTRD"  == sSelDet  )
+      {
+         fUnpAlgoTrd ->SetTimeOffsetNs( dOffset );
+      } // else if( "kTRD"  == sSelDet  )
+      else if( "kTOF"  == sSelDet )
+      {
+         fUnpAlgoTof ->SetTimeOffsetNs( dOffset );
+      } // else if( "kTOF"  == sSelDet )
+      else if( "kRICH" == sSelDet )
+      {
+         fUnpAlgoRich->SetTimeOffsetNs( dOffset );
+      } // else if( "kRICH" == sSelDet )
+      else if( "kPSD"  == sSelDet )
+      {
+         fUnpAlgoPsd ->SetTimeOffsetNs( dOffset );
+      } // else if( "kPSD"  == sSelDet )
+         else
+         {
+            LOG( info ) << "CbmDeviceMcbmUnpack::InitContainers => Trying to set time offset for unsupported detector, ignored! "
+                        << (sSelDet);
+            continue;
+         } // else of detector enum detection
+   } // for( std::vector< std::string >::iterator itStrAdd = fvsAddDet.begin(); itStrAdd != fvsAddDet.end(); ++itStrAdd )
+
+
+   /// Starting from first run on Tuesday 28/04/2020, STS uses bin sorter FW
+   fUnpAlgoSts->SetBinningFwFlag( kTRUE );
+   /// Starting from first run on Monday 04/05/2020, MUCH uses bin sorter FW
+   fUnpAlgoMuch->SetBinningFwFlag( kTRUE );
+
+   Bool_t initOK = fUnpAlgoSts->InitContainers();
+   initOK &= fUnpAlgoMuch->InitContainers();
+   initOK &= fUnpAlgoTrd ->InitContainers();
+   initOK &= fUnpAlgoTof ->InitContainers();
+   initOK &= fUnpAlgoRich->InitContainers();
+   initOK &= fUnpAlgoPsd ->InitContainers();
+
+   /// Special case for TRD vector initialization
+      /// Get address of vector from algo in a kind of loopback ^^'
+   initOK &= fUnpAlgoTrd->SetDigiOutputPointer( &( fUnpAlgoTrd->GetVector() ) );
+
+//   Bool_t initOK = fMonitorAlgo->ReInitContainers();
+
+  return initOK;
+}
+
+Bool_t CbmDeviceMcbmUnpack::InitParameters( TList* fParCList )
+{
+   for( int iparC = 0; iparC < fParCList->GetEntries(); iparC++ )
+   {
+      FairParGenericSet* tempObj = (FairParGenericSet*)( fParCList->At( iparC ) );
+      fParCList->Remove( tempObj );
+      std::string paramName{ tempObj->GetName() };
+      // NewSimpleMessage creates a copy of the data and takes care of its destruction (after the transfer takes place).
+      // Should only be used for small data because of the cost of an additional copy
+
+      // Her must come the proper Runid
+      std::string message = paramName + ",111";
+      LOG(info) << "Requesting parameter container " << paramName << ", sending message: " << message;
+
+      FairMQMessagePtr req( NewSimpleMessage(message) );
+      FairMQMessagePtr rep( NewMessage() );
+
+      FairParGenericSet* newObj = nullptr;
+
+      if( Send(req, "parameters") > 0 )
+      {
+         if( Receive( rep, "parameters" ) >= 0)
+         {
+            if( 0 !=  rep->GetSize() )
+            {
+               CbmMQTMessage tmsg( rep->GetData(), rep->GetSize() );
+               newObj = static_cast< FairParGenericSet* >( tmsg.ReadObject( tmsg.GetClass() ) );
+               LOG( info ) << "Received unpack parameter from the server:";
+               newObj->print();
+            } // if( 0 !=  rep->GetSize() )
+               else
+               {
+                  LOG( error ) << "Received empty reply. Parameter not available";
+                  return kFALSE;
+               } // else of if( 0 !=  rep->GetSize() )
+         } // if( Receive( rep, "parameters" ) >= 0)
+      } // if( Send(req, "parameters") > 0 )
+      fParCList->AddAt( newObj, iparC );
+      delete tempObj;
+   } // for( int iparC = 0; iparC < fParCList->GetEntries(); iparC++ )
+
+   return kTRUE;
+}
+
+// handler is called whenever a message arrives on "data", with a reference to the message and a sub-channel index (here 0)
+bool CbmDeviceMcbmUnpack::HandleData(FairMQMessagePtr& msg, int /*index*/)
+{
+   fulNumMessages++;
+   LOG(debug) << "Received message number "<<  fulNumMessages
+              << " with size " << msg->GetSize();
+
+   if( 0 == fulNumMessages % 10000 )
+      LOG(info) << "Received " << fulNumMessages << " messages";
+
+   std::string msgStr( static_cast<char*>( msg->GetData() ), msg->GetSize() );
+   std::istringstream iss( msgStr );
+   boost::archive::binary_iarchive inputArchive( iss );
+
+   /// Create an empty TS and fill it with the incoming message
+   fles::StorableTimeslice ts{ 0 };
+   inputArchive >> ts;
+
+   /// On first TS, extract the TS parameters from header (by definition stable over time)
+   if( -1.0 == fdTsCoreSizeInNs )
+   {
+      fuNbCoreMsPerTs = ts.num_core_microslices();
+      fuNbOverMsPerTs = ts.num_microslices( 0 ) - ts.num_core_microslices();
+      fdTsCoreSizeInNs = fdMsSizeInNs * ( fuNbCoreMsPerTs );
+      fdTsOverSizeInNs = fdMsSizeInNs * ( fuNbOverMsPerTs );
+      fdTsFullSizeInNs = fdTsCoreSizeInNs + fdTsOverSizeInNs;
+      LOG(info) << "Timeslice parameters: each TS has "
+                << fuNbCoreMsPerTs << " Core MS and "
+                << fuNbOverMsPerTs << " Overlap MS, for a core duration of "
+                << fdTsCoreSizeInNs << " ns and a full duration of "
+                << fdTsFullSizeInNs << " ns";
+   } // if( -1.0 == fdTsCoreSizeInNs )
+
+   fTsMetaData = new TimesliceMetaData( ts.descriptor( 0, 0).idx,
+                                        fdTsCoreSizeInNs, fdTsOverSizeInNs,
+                                        ts.index() );
+
+   /// Process the Timeslice
+   DoUnpack(ts, 0);
+
+   /// Send digi vectors to ouput
+   if( !SendUnpData() )
+      return false;
+
+   delete fTsMetaData;
+
+   /// Clear the digis vectors in case it was filled
+   fUnpAlgoSts ->ClearVector();
+   fUnpAlgoMuch->ClearVector();
+   fUnpAlgoTrd ->ClearVector();
+   fUnpAlgoTof ->ClearVector();
+   fUnpAlgoRich->ClearVector();
+   fUnpAlgoPsd ->ClearVector();
+
+   /// Clear the digis vectors in case it was filled
+   fUnpAlgoSts ->ClearErrorVector();
+   fUnpAlgoMuch->ClearErrorVector();
+   fUnpAlgoTrd ->ClearErrorVector();
+   fUnpAlgoTof ->ClearErrorVector();
+   fUnpAlgoRich->ClearErrorVector();
+   fUnpAlgoPsd ->ClearErrorVector();
+
+   return true;
+}
+
+bool CbmDeviceMcbmUnpack::SendUnpData()
+{
+
+   /// Prepare serialized versions of the TS Meta
+/*
+   std::stringstream ossTsMeta;
+   boost::archive::binary_oarchive oaTsMeta(ossTsMeta);
+   oaTsMeta << *(fTsMetaData);
+   std::string* strMsgTsMetaE = new std::string(ossTsMeta.str());
+*/
+   FairMQMessagePtr messTsMeta( NewMessage() );
+   Serialize< RootSerializer >( *messTsMeta, fTsMetaData );
+
+   std::stringstream ossSts;
+   boost::archive::binary_oarchive oaSts(ossSts);
+   oaSts << fUnpAlgoSts->GetVector();
+   std::string* strMsgSts = new std::string(ossSts.str());
+
+   std::stringstream ossMuch;
+   boost::archive::binary_oarchive oaMuch(ossMuch);
+   oaMuch << fUnpAlgoMuch->GetVector();
+   std::string* strMsgMuch = new std::string(ossMuch.str());
+
+   std::stringstream ossTrd;
+   boost::archive::binary_oarchive oaTrd(ossTrd);
+   oaTrd << fUnpAlgoTrd->GetVector();
+   std::string* strMsgTrd = new std::string(ossTrd.str());
+
+   /// Split TOF vector in TOF and T0
+   std::vector< CbmTofDigi > & vDigiTofT0 = fUnpAlgoTof->GetVector();
+   std::vector< CbmTofDigi > vDigiTof = {};
+   std::vector< CbmTofDigi > vDigiT0 = {};
+
+   for( auto digi: vDigiTofT0 )
+   {
+      if( fuDigiMaskedIdT0 == ( digi.GetAddress() & fuDigiMaskId ) )
+      {
+         /// Insert data in T0 output container
+         vDigiT0.emplace_back( digi );
+      } // if( fuDigiMaskedIdT0 == ( digi.GetAddress() & fuDigiMaskId ) )
+      else
+      {
+         /// Insert data in TOF output container
+         vDigiTof.emplace_back( digi );
+      } // else of if( fuDigiMaskedIdT0 == ( digi.GetAddress() & fuDigiMaskId ) )
+   } // for( auto digi: vDigi )
+
+   std::stringstream ossTof;
+   boost::archive::binary_oarchive oaTof(ossTof);
+   oaTof << vDigiTof;
+   std::string* strMsgTof = new std::string(ossTof.str());
+
+   std::stringstream ossT0;
+   boost::archive::binary_oarchive oaT0(ossT0);
+   oaT0 << vDigiT0;
+   std::string* strMsgT0 = new std::string(ossT0.str());
+
+   std::stringstream ossRich;
+   boost::archive::binary_oarchive oaRich(ossRich);
+   oaRich << fUnpAlgoRich->GetVector();
+   std::string* strMsgRich = new std::string(ossRich.str());
+
+   std::stringstream ossPsd;
+   boost::archive::binary_oarchive oaPsd(ossPsd);
+   oaPsd << fUnpAlgoPsd->GetVector();
+   std::string* strMsgPsd = new std::string(ossPsd.str());
+
+   FairMQParts parts;
+
+   parts.AddPart( std::move( messTsMeta ) );
+/*
+   parts.AddPart( NewMessage( const_cast< char * >( strMsgTsMetaE->c_str() ), // data
+                              strMsgTsMetaE->length(), // size
+                              []( void* , void* object ){ delete static_cast< std::string * >( object ); },
+                              strMsgTsMetaE
+                            )
+                ); // object that manages the data
+*/
+
+   parts.AddPart( NewMessage( const_cast< char * >( strMsgT0->c_str() ), // data
+                              strMsgT0->length(), // size
+                              []( void* , void* object ){ delete static_cast< std::string * >( object ); },
+                              strMsgT0
+                            )
+                ); // object that manages the data
+
+   parts.AddPart( NewMessage( const_cast< char * >( strMsgSts->c_str() ), // data
+                              strMsgSts->length(), // size
+                              []( void* , void* object ){ delete static_cast< std::string * >( object ); },
+                              strMsgSts
+                            )
+                ); // object that manages the data
+
+   parts.AddPart( NewMessage( const_cast< char * >( strMsgMuch->c_str() ), // data
+                              strMsgMuch->length(), // size
+                              []( void* , void* object ){ delete static_cast< std::string * >( object ); },
+                              strMsgMuch
+                            )
+                ); // object that manages the data
+
+   parts.AddPart( NewMessage( const_cast< char * >( strMsgTrd->c_str() ), // data
+                              strMsgTrd->length(), // size
+                              []( void* , void* object ){ delete static_cast< std::string * >( object ); },
+                              strMsgTrd
+                            )
+                ); // object that manages the data
+
+   parts.AddPart( NewMessage( const_cast< char * >( strMsgTof->c_str() ), // data
+                              strMsgTof->length(), // size
+                              []( void* , void* object ){ delete static_cast< std::string * >( object ); },
+                              strMsgTof
+                            )
+                ); // object that manages the data
+
+   parts.AddPart( NewMessage( const_cast< char * >( strMsgRich->c_str() ), // data
+                              strMsgRich->length(), // size
+                              []( void* , void* object ){ delete static_cast< std::string * >( object ); },
+                              strMsgRich
+                            )
+                ); // object that manages the data
+
+   parts.AddPart( NewMessage( const_cast< char * >( strMsgPsd->c_str() ), // data
+                              strMsgPsd->length(), // size
+                              []( void* , void* object ){ delete static_cast< std::string * >( object ); },
+                              strMsgPsd
+                            )
+                ); // object that manages the data
+
+  if( Send( parts, fsChannelNameDataOutput ) < 0 )
+  {
+    LOG(error) << "Problem sending data to " << fsChannelNameDataOutput;
+    return false;
+  }
+
+   return true;
+}
+
+
+CbmDeviceMcbmUnpack::~CbmDeviceMcbmUnpack()
+{
+   if( nullptr != fUnpAlgoSts )
+      delete fUnpAlgoSts;
+   if( nullptr != fUnpAlgoMuch )
+      delete fUnpAlgoMuch;
+   if( nullptr != fUnpAlgoTrd )
+      delete fUnpAlgoTrd;
+   if( nullptr != fUnpAlgoTof )
+      delete fUnpAlgoTof;
+   if( nullptr != fUnpAlgoRich )
+      delete fUnpAlgoRich;
+   if( nullptr != fUnpAlgoPsd )
+      delete fUnpAlgoPsd;
+}
+
+
+
+Bool_t CbmDeviceMcbmUnpack::DoUnpack(const fles::Timeslice& ts, size_t /*component*/)
+{
+   fulTsCounter++;
+
+   if( kFALSE == fbComponentsAddedToList )
+   {
+      for( uint32_t uCompIdx = 0; uCompIdx < ts.num_components(); ++uCompIdx )
+      {
+         switch( ts.descriptor( uCompIdx, 0 ).sys_id )
+         {
+            case kusSysIdSts:
+            {
+               fUnpAlgoSts ->AddMsComponentToList(uCompIdx, kusSysIdSts);
+               break;
+            } // case kusSysIdSts
+            case kusSysIdMuch:
+            {
+               fUnpAlgoMuch ->AddMsComponentToList(uCompIdx, kusSysIdMuch);
+               break;
+            } // case kusSysIdMuch
+            case kusSysIdTrd:
+            {
+               fUnpAlgoTrd ->AddMsComponentToList(uCompIdx, kusSysIdTrd);
+               break;
+            } // case kusSysIdTrd
+            case kusSysIdTof:
+            {
+               fUnpAlgoTof ->AddMsComponentToList(uCompIdx, kusSysIdTof);
+               break;
+            } // case kusSysIdTof
+            case kusSysIdT0:
+            {
+               fUnpAlgoTof ->AddMsComponentToList(uCompIdx, kusSysIdT0);
+               break;
+            } // case kusSysIdT0
+            case kusSysIdRich:
+            {
+               fUnpAlgoRich ->AddMsComponentToList(uCompIdx, kusSysIdRich);
+               break;
+            } // case kusSysIdRich
+            case kusSysIdPsd:
+            {
+               fUnpAlgoPsd ->AddMsComponentToList(uCompIdx, kusSysIdPsd);
+               break;
+            } // case kusSysIdPsd
+            default:
+               break;
+         } // switch( ts.descriptor( uCompIdx, 0 ).sys_id )
+      } // for( uint32_t uComp = 0; uComp < ts.num_components(); ++uComp )
+      fbComponentsAddedToList = kTRUE;
+   } // if( kFALSE == fbComponentsAddedToList )
+
+   if( kFALSE == fUnpAlgoSts->ProcessTs( ts ) )
+   {
+      LOG(error) << "Failed processing TS " << ts.index()
+                 << " in STS unpacker algorithm class";
+      return kTRUE;
+   } // if( kFALSE == fUnpAlgoSts->ProcessTs( ts ) )
+
+   if( kFALSE == fUnpAlgoMuch->ProcessTs( ts ) )
+   {
+      LOG(error) << "Failed processing TS " << ts.index()
+                 << " in MUCH unpacker algorithm class";
+      return kTRUE;
+   } // if( kFALSE == fUnpAlgoMuch->ProcessTs( ts ) )
+
+   if( kFALSE == fUnpAlgoTrd->ProcessTs( ts ) )
+   {
+      LOG(error) << "Failed processing TS " << ts.index()
+                 << " in TRD unpacker algorithm class";
+      return kTRUE;
+   } // if( kFALSE == fUnpAlgoTrd->ProcessTs( ts ) )
+
+   if( kFALSE == fUnpAlgoTof->ProcessTs( ts ) )
+   {
+      LOG(error) << "Failed processing TS " << ts.index()
+                 << " in TOF unpacker algorithm class";
+      return kTRUE;
+   } // if( kFALSE == fUnpAlgoTof->ProcessTs( ts ) )
+
+   if( kFALSE == fUnpAlgoRich->ProcessTs( ts ) )
+   {
+      LOG(error) << "Failed processing TS " << ts.index()
+                 << " in RICH unpacker algorithm class";
+      return kTRUE;
+   } // if( kFALSE == fUnpAlgoRich->ProcessTs( ts ) )
+
+   if( kFALSE == fUnpAlgoPsd->ProcessTs( ts ) )
+   {
+      LOG(error) << "Failed processing TS " << ts.index()
+                 << " in PSD unpacker algorithm class";
+      return kTRUE;
+   } // if( kFALSE == fUnpAlgoPsd->ProcessTs( ts ) )
+
+
+   if( 0 == fulTsCounter % 10000 )
+      LOG(info) << "Processed " << fulTsCounter << " time slices";
+
+   return kTRUE;
+}
+
+void CbmDeviceMcbmUnpack::Finish()
+{
+}
diff --git a/MQ/mcbm/CbmDeviceMcbmUnpack.h b/MQ/mcbm/CbmDeviceMcbmUnpack.h
new file mode 100644
index 0000000000..a8f360db45
--- /dev/null
+++ b/MQ/mcbm/CbmDeviceMcbmUnpack.h
@@ -0,0 +1,113 @@
+/**
+ * CbmDeviceMcbmUnpack.h
+ *
+ * @since 2020-05-04
+ * @author P.-A. Loizeau
+ */
+
+#ifndef CBMDEVICEMCBMUNPACK_H_
+#define CBMDEVICEMCBMUNPACK_H_
+
+#include "FairMQDevice.h"
+
+#include "Timeslice.hpp"
+
+#include "TMessage.h"
+#include "Rtypes.h"
+#include "TObjArray.h"
+
+#include <vector>
+#include <map>
+
+class TList;
+class CbmMcbm2018UnpackerAlgoSts;
+class CbmMcbm2018UnpackerAlgoMuch;
+class CbmMcbm2018UnpackerAlgoTrdR;
+class CbmMcbm2018UnpackerAlgoTof;
+class CbmMcbm2018UnpackerAlgoRich;
+class CbmMcbm2018UnpackerAlgoPsd;
+class TimesliceMetaData;
+
+class CbmDeviceMcbmUnpack: public FairMQDevice
+{
+   public:
+      CbmDeviceMcbmUnpack();
+      virtual ~CbmDeviceMcbmUnpack();
+
+   protected:
+      virtual void InitTask();
+      bool HandleData(FairMQMessagePtr&, int);
+      bool HandleCommand(FairMQMessagePtr&, int);
+
+   private:
+      /// Constants
+      static const uint16_t kusSysIdSts  = 0x10;
+      static const uint16_t kusSysIdMuch = 0x50;
+      static const uint16_t kusSysIdTrd  = 0x40;
+      static const uint16_t kusSysIdTof  = 0x60;
+      static const uint16_t kusSysIdT0   = 0x90;
+      static const uint16_t kusSysIdRich = 0x30;
+      static const uint16_t kusSysIdPsd  = 0x80;
+
+      /// Control flags
+      Bool_t fbIgnoreOverlapMs = false;      //! Ignore Overlap Ms: all fuOverlapMsNb MS at the end of timeslice
+      Bool_t fbComponentsAddedToList = kFALSE;
+
+      /// User settings parameters
+      std::string fsChannelNameDataInput  = "fullts";
+      std::string fsChannelNameDataOutput = "unpts_0";
+      std::string fsChannelNameCommands   = "commands";
+      UInt_t fuDigiMaskedIdT0             = 0x00005006;
+      UInt_t fuDigiMaskId                 = 0x0001FFFF;
+
+      /// List of MQ channels names
+      std::vector< std::string > fsAllowedChannels = { fsChannelNameDataInput };
+
+      /// Parameters management
+//      TList* fParCList = nullptr;
+      Bool_t InitParameters( TList* fParCList );
+
+      /// Statistics & first TS rejection
+      uint64_t fulNumMessages = 0;
+      uint64_t fulTsCounter   = 0;
+
+      /// Processing algos
+      CbmMcbm2018UnpackerAlgoSts  * fUnpAlgoSts  = nullptr;
+      CbmMcbm2018UnpackerAlgoMuch * fUnpAlgoMuch = nullptr;
+      CbmMcbm2018UnpackerAlgoTrdR * fUnpAlgoTrd  = nullptr;
+      CbmMcbm2018UnpackerAlgoTof  * fUnpAlgoTof  = nullptr;
+      CbmMcbm2018UnpackerAlgoRich * fUnpAlgoRich = nullptr;
+      CbmMcbm2018UnpackerAlgoPsd  * fUnpAlgoPsd  = nullptr;
+
+      /// Time offsets
+      std::vector< std::string > fvsSetTimeOffs   = {};
+
+      /// TS MetaData 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]
+      TimesliceMetaData * fTsMetaData;
+
+      bool IsChannelNameAllowed(std::string channelName);
+      Bool_t InitContainers();
+      Bool_t DoUnpack(const fles::Timeslice& ts, size_t component);
+      void Finish();
+      bool SendUnpData();
+};
+
+// special class to expose protected TMessage constructor
+class CbmMQTMessage : public TMessage
+{
+  public:
+    CbmMQTMessage(void* buf, Int_t len)
+        : TMessage(buf, len)
+    {
+        ResetBit(kIsOwner);
+    }
+};
+
+
+#endif /* CBMDEVICEMCBMUNPACK_H_ */
diff --git a/MQ/mcbm/runMcbmEventSink.cxx b/MQ/mcbm/runMcbmEventSink.cxx
new file mode 100644
index 0000000000..c844137d8c
--- /dev/null
+++ b/MQ/mcbm/runMcbmEventSink.cxx
@@ -0,0 +1,35 @@
+#include "runFairMQDevice.h"
+#include "CbmDeviceMcbmEventSink.h"
+
+#include <string>
+#include <iomanip>
+
+namespace bpo = boost::program_options;
+using namespace std;
+
+void addCustomOptions(bpo::options_description& options)
+{
+   options.add_options() ( "OutFileName",   bpo::value< std::string >()->default_value( "mcbm_digis_events.root" ),
+                           "Name (full or relative path) of the output .root file ");
+   options.add_options() ( "EvtNameIn",    bpo::value< std::string >()->default_value( "events" ),
+                           "MQ channel name for built events");
+   options.add_options() ( "FillHistos",    bpo::value< bool >()->default_value( false ),
+                           "Fill histograms and send them to histo server if true");
+   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 CbmDeviceMcbmEventSink();
+}
diff --git a/MQ/mcbm/runMcbmEvtBuilderWin.cxx b/MQ/mcbm/runMcbmEvtBuilderWin.cxx
new file mode 100644
index 0000000000..328d879b02
--- /dev/null
+++ b/MQ/mcbm/runMcbmEvtBuilderWin.cxx
@@ -0,0 +1,49 @@
+#include "runFairMQDevice.h"
+#include "CbmDeviceMcbmEventBuilderWin.h"
+
+#include <string>
+#include <iomanip>
+
+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 CbmDeviceMcbmEventBuilderWin();
+}
diff --git a/MQ/mcbm/runMcbmMonitorPulser.cxx b/MQ/mcbm/runMcbmMonitorPulser.cxx
new file mode 100644
index 0000000000..d1ca3fb514
--- /dev/null
+++ b/MQ/mcbm/runMcbmMonitorPulser.cxx
@@ -0,0 +1,39 @@
+#include "runFairMQDevice.h"
+#include "CbmDeviceMcbmMonitorPulser.h"
+
+#include <string>
+#include <iomanip>
+
+namespace bpo = boost::program_options;
+using namespace std;
+
+void addCustomOptions(bpo::options_description& options)
+{
+   options.add_options() ("DebugMoni",  bpo::value< bool >()->default_value( false ),
+                          "Debug Monitor Mode");
+   options.add_options() ("HistEvoSz",  bpo::value< uint32_t >()->default_value( 1800 ),
+                          "Size of evolution histos in seconds");
+   options.add_options() ("PulsTotMin", bpo::value< uint32_t >()->default_value(  185 ),
+                          "Minimal TOT for pulser cut");
+   options.add_options() ("PulsTotMax", bpo::value< uint32_t >()->default_value(  195 ),
+                          "Maximal TOT for pulser cut");
+   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");
+   options.add_options() ( "TsNameIn",      bpo::value< std::string >()->default_value( "unpts_0" ),
+                           "MQ channel name for TS data");
+   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*/)
+{
+    return new CbmDeviceMcbmMonitorPulser();
+}
diff --git a/MQ/mcbm/runMcbmUnpack.cxx b/MQ/mcbm/runMcbmUnpack.cxx
new file mode 100644
index 0000000000..c2fa51f483
--- /dev/null
+++ b/MQ/mcbm/runMcbmUnpack.cxx
@@ -0,0 +1,25 @@
+#include "runFairMQDevice.h"
+#include "CbmDeviceMcbmUnpack.h"
+
+#include <string>
+#include <iomanip>
+
+namespace bpo = boost::program_options;
+using namespace std;
+
+void addCustomOptions(bpo::options_description& options)
+{
+   options.add_options() ("IgnOverMs",  bpo::value< bool >()->default_value( true ),
+                          "Ignore overlap MS if true");
+   options.add_options() ( "SetTimeOffs",  bpo::value< std::vector< std::string > >()->multitoken()->composing(),
+                           "Set time offset in ns for selected detector, use string matching ECbmModuleId,dOffs e.g. kTof,-35.2");
+   options.add_options() ( "TsNameIn",      bpo::value< std::string >()->default_value( "fullts" ),
+                           "MQ channel name for raw TS data");
+   options.add_options() ( "TsNameOut",     bpo::value< std::string >()->default_value( "unpts_0" ),
+                           "MQ channel name for unpacked TS data");
+}
+
+FairMQDevicePtr getDevice(const FairMQProgOptions& /*config*/)
+{
+    return new CbmDeviceMcbmUnpack();
+}
diff --git a/MQ/mcbm/startMQMcbmEvtBuilderWin2020.sh.in b/MQ/mcbm/startMQMcbmEvtBuilderWin2020.sh.in
new file mode 100755
index 0000000000..561c5e7ba9
--- /dev/null
+++ b/MQ/mcbm/startMQMcbmEvtBuilderWin2020.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=/scratch/cbmroot_macro/macro/beamtime/mcbm2020/mStsPar.par
+_parfileMuch=/scratch/cbmroot_macro/macro/beamtime/mcbm2020/mMuchPar.par
+_parfileTrdAsic=/scratch/cbmroot_macro/parameters/trd/trd_v18q_mcbm.asic.par
+_parfileTrdDigi=/scratch/cbmroot_macro/parameters/trd/trd_v18q_mcbm.digi.par
+_parfileTrdGas=/scratch/cbmroot_macro/parameters/trd/trd_v18q_mcbm.gas.par
+_parfileTrdGain=/scratch/cbmroot_macro/parameters/trd/trd_v18q_mcbm.gain.par
+_parfileTof=/scratch/cbmroot_macro/macro/beamtime/mcbm2020/mTofPar.par
+_parfileRich=/scratch/cbmroot_macro/macro/beamtime/mcbm2020/mRichPar.par
+_parfilePsd=/scratch/cbmroot_macro/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="McbmEventBuilderWin"
+  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 &
diff --git a/MQ/mcbm/startMQMcbmPulserMonitor2020.sh.in b/MQ/mcbm/startMQMcbmPulserMonitor2020.sh.in
new file mode 100755
index 0000000000..6bab6e5f25
--- /dev/null
+++ b/MQ/mcbm/startMQMcbmPulserMonitor2020.sh.in
@@ -0,0 +1,160 @@
+#!/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=/scratch/cbmroot_macro/macro/beamtime/mcbm2020/mStsPar.par
+_parfileMuch=/scratch/cbmroot_macro/macro/beamtime/mcbm2020/mMuchPar.par
+_parfileTrdAsic=/scratch/cbmroot_macro/parameters/trd/trd_v18q_mcbm.asic.par
+_parfileTrdDigi=/scratch/cbmroot_macro/parameters/trd/trd_v18q_mcbm.digi.par
+_parfileTrdGas=/scratch/cbmroot_macro/parameters/trd/trd_v18q_mcbm.gas.par
+_parfileTrdGain=/scratch/cbmroot_macro/parameters/trd/trd_v18q_mcbm.gain.par
+_parfileTof=/scratch/cbmroot_macro/macro/beamtime/mcbm2020/mTofPar.par
+_parfileRich=/scratch/cbmroot_macro/macro/beamtime/mcbm2020/mRichPar.par
+_parfilePsd=/scratch/cbmroot_macro/macro/beamtime/mcbm2020/mPsdPar.par
+
+SAMPLER="MultiTsaSampler"
+SAMPLER+=" --id sampler1"
+SAMPLER+=" --max-timeslices 0"
+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+=" --channel-config name=fullts,type=push,method=bind,address=tcp://127.0.0.1:11555"
+#SAMPLER+=" --transport shmem"
+SAMPLER+=" --transport zeromq"
+#SAMPLER+=" --transport nanomsg"
+xterm -l -geometry 80x23+0+0 -hold -e @CMAKE_BINARY_DIR@/bin/MQ/source/$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+=" --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=pub,method=bind,transport=zeromq,address=tcp://127.0.0.1:$_iPort"
+  #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"
+  xterm -l -geometry 80x23+400+$_yOffset -hold -e @CMAKE_BINARY_DIR@/bin/MQ/mcbm/$UNPACKER &
+
+  MONITOR="McbmMonitorPulser"
+  MONITOR+=" --id mon$_iMoni"
+  MONITOR+=" --severity info"
+  MONITOR+=" --PubFreqTs $_pubfreqts"
+  MONITOR+=" --PubTimeMin $_pubminsec"
+  MONITOR+=" --PubTimeMax $_pubmaxsec"
+  MONITOR+=" --TsNameIn unpts$_iMoni"
+  MONITOR+=" --channel-config name=unpts$_iMoni,type=sub,method=connect,transport=zeromq,address=tcp://127.0.0.1:$_iPort"
+  #MONITOR+=" --transport shmem"
+  MONITOR+=" --transport zeromq"
+  #MONITOR+=" --transport nanomsg"
+  MONITOR+=" --channel-config name=parameters,type=req,method=connect,transport=zeromq,address=tcp://127.0.0.1:11005,rateLogging=0"
+  MONITOR+=" --channel-config name=histogram-in,type=pub,method=connect,transport=zeromq,address=tcp://127.0.0.1:11666"
+  MONITOR+=" --channel-config name=histo-conf,type=pub,method=connect,transport=zeromq,address=tcp://127.0.0.1:11667,rateLogging=0"
+  MONITOR+=" --channel-config name=canvas-conf,type=pub,method=connect,transport=zeromq,address=tcp://127.0.0.1:11668,rateLogging=0"
+  xterm -l -geometry 80x23+800+$_yOffset -hold -e @CMAKE_BINARY_DIR@/bin/MQ/mcbm/$MONITOR &
+done
+
+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
+xterm -geometry 80x23+1200+0 -hold -e @CMAKE_BINARY_DIR@/bin/MQ/parmq/$PARAMETERSERVER &
+
+HISTSERVER="MqHistoServer"
+HISTSERVER+=" --id server1"
+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"
+xterm -geometry 80x23+1400+0 -hold -e @CMAKE_BINARY_DIR@/bin/MQ/histogramServer/$HISTSERVER &
-- 
GitLab