From 5f6de5b87457e4e60300c8614f844755a13d9538 Mon Sep 17 00:00:00 2001
From: "P.-A. Loizeau" <p.-a.loizeau@gsi.de>
Date: Wed, 17 Feb 2021 16:45:01 +0100
Subject: [PATCH] [mCBM 2020] Add support for processing of TS ranges based on
 spills - Add task finding the spill borders with the T0 + macros - In
 CbmMcbm2018Source, add support for selecting TS ranges for a chosen Spill
 range with 3 spill borders options - Fix TS metadata calculation against
 corrupted MS 0 and move it to make sure it is generated even when TS range
 selected

---
 fles/mcbm2018/CMakeLists.txt                 |   3 +
 fles/mcbm2018/CbmFlibMcbm2018LinkDef.h       |   3 +
 fles/mcbm2018/CbmMcbm2018Source.cxx          | 121 ++++-
 fles/mcbm2018/CbmMcbm2018Source.h            |  32 ++
 fles/mcbm2018/tasks/CbmMcbmSpillFindAlgo.cxx | 511 +++++++++++++++++++
 fles/mcbm2018/tasks/CbmMcbmSpillFindAlgo.h   | 163 ++++++
 fles/mcbm2018/tasks/CbmMcbmSpillFindTask.cxx | 216 ++++++++
 fles/mcbm2018/tasks/CbmMcbmSpillFindTask.h   |  77 +++
 macro/beamtime/mcbm2020/find_spills.C        | 118 +++++
 macro/beamtime/mcbm2020/find_spills_kronos.C |  62 +++
 10 files changed, 1291 insertions(+), 15 deletions(-)
 create mode 100644 fles/mcbm2018/tasks/CbmMcbmSpillFindAlgo.cxx
 create mode 100644 fles/mcbm2018/tasks/CbmMcbmSpillFindAlgo.h
 create mode 100644 fles/mcbm2018/tasks/CbmMcbmSpillFindTask.cxx
 create mode 100644 fles/mcbm2018/tasks/CbmMcbmSpillFindTask.h
 create mode 100644 macro/beamtime/mcbm2020/find_spills.C
 create mode 100644 macro/beamtime/mcbm2020/find_spills_kronos.C

diff --git a/fles/mcbm2018/CMakeLists.txt b/fles/mcbm2018/CMakeLists.txt
index 5bdf6aca..5a975a8e 100644
--- a/fles/mcbm2018/CMakeLists.txt
+++ b/fles/mcbm2018/CMakeLists.txt
@@ -130,6 +130,9 @@ Set(SRCS
 
    tasks/CbmMcbmCheckTimingAlgo.cxx
    tasks/CbmMcbmCheckTimingTask.cxx
+
+   tasks/CbmMcbmSpillFindAlgo.cxx
+   tasks/CbmMcbmSpillFindTask.cxx
 )
 
 If(_UINT8_T_EXIST)
diff --git a/fles/mcbm2018/CbmFlibMcbm2018LinkDef.h b/fles/mcbm2018/CbmFlibMcbm2018LinkDef.h
index c19aaf4e..f4570ee2 100644
--- a/fles/mcbm2018/CbmFlibMcbm2018LinkDef.h
+++ b/fles/mcbm2018/CbmFlibMcbm2018LinkDef.h
@@ -77,4 +77,7 @@
 #pragma link C++ class CbmMcbmCheckTimingAlgo + ;
 #pragma link C++ class CbmMcbmCheckTimingTask + ;
 
+#pragma link C++ class CbmMcbmSpillFindAlgo + ;
+#pragma link C++ class CbmMcbmSpillFindTask + ;
+
 #endif
diff --git a/fles/mcbm2018/CbmMcbm2018Source.cxx b/fles/mcbm2018/CbmMcbm2018Source.cxx
index 0db535ca..d7619d28 100644
--- a/fles/mcbm2018/CbmMcbm2018Source.cxx
+++ b/fles/mcbm2018/CbmMcbm2018Source.cxx
@@ -117,6 +117,76 @@ Bool_t CbmMcbm2018Source::Init() {
                   fTimeSliceMetaDataArray,
                   fbWriteOutput);
 
+  /// Single spill unpacking
+  if (0 <= fiUnpSpillIdxStart)
+  {
+    switch( fuFlagSpillStart )
+    {
+      case 0:
+      {
+        /// 0 = Break begin
+        if( fvuSpillBreakBegTs.size() - 1 <= fiUnpSpillIdxStop )
+        {
+          LOG(warning) << "Chosen last spill index larger than spills contained in chosen spill start vector: "
+                       << fiUnpSpillIdxStop << " VS " << fvuSpillBreakBegTs.size() - 1;
+          if( fiUnpSpillIdxStart < fvuSpillBreakBegTs.size() - 1 )
+          {
+             fiUnpSpillIdxStop = fvuSpillBreakBegTs.size() - 2;
+             LOG(warning) << "Using last possible spill instead as final one";
+          } // if( fiUnpSpillIdxStart < fvuSpillBreakBegTs.size() - 1 )
+          else LOG(fatal) << "Start index also too large, exiting";
+        } // if( fvuSpillBreakBegTs.size() - 1 <= fiUnpSpillIdxStop )
+
+        fuSpillBegTs = fvuSpillBreakBegTs[ fiUnpSpillIdxStart ];   //!
+        fuSpillEndTs = fvuSpillBreakBegTs[ fiUnpSpillIdxStop + 1 ];   //!
+        break;
+      }
+      case 1:
+      {
+        /// 1 = Break middle
+        if( fvuSpillBreakMidTs.size() - 1 <= fiUnpSpillIdxStop )
+        {
+          LOG(warning) << "Chosen last spill index larger than spills contained in chosen spill start vector: "
+                       << fiUnpSpillIdxStop << " VS " << fvuSpillBreakMidTs.size() - 1;
+          if( fiUnpSpillIdxStart < fvuSpillBreakMidTs.size() - 1 )
+          {
+             fiUnpSpillIdxStop = fvuSpillBreakMidTs.size() - 2;
+             LOG(warning) << "Using last possible spill instead as final one";
+          } // if( fiUnpSpillIdxStart < fvuSpillBreakMidTs.size() - 1 )
+          else LOG(fatal) << "Start index also too large, exiting";
+        } // if( fvuSpillBreakMidTs.size() - 1 <= fiUnpSpillIdxStop )
+
+        fuSpillBegTs = fvuSpillBreakMidTs[ fiUnpSpillIdxStart ];   //!
+        fuSpillEndTs = fvuSpillBreakMidTs[ fiUnpSpillIdxStop + 1 ];   //!
+        break;
+      }
+      case 2:
+      {
+        /// 2 = Break end
+        if( fvuSpillBreakEndTs.size() - 1 <= fiUnpSpillIdxStop )
+        {
+          LOG(warning) << "Chosen last spill index larger than spills contained in chosen spill start vector: "
+                       << fiUnpSpillIdxStop << " VS " << fvuSpillBreakEndTs.size() - 1;
+          if( fiUnpSpillIdxStart < fvuSpillBreakEndTs.size() - 1 )
+          {
+             fiUnpSpillIdxStop = fvuSpillBreakEndTs.size() - 2;
+             LOG(warning) << "Using last possible spill instead as final one";
+          } // if( fiUnpSpillIdxStart < fvuSpillBreakEndTs.size() - 1 )
+          else LOG(fatal) << "Start index also too large, exiting";
+        } // if( fvuSpillBreakEndTs.size() - 1 <= fiUnpSpillIdxStop )
+
+        fuSpillBegTs = fvuSpillBreakEndTs[ fiUnpSpillIdxStart ];   //!
+        fuSpillEndTs = fvuSpillBreakEndTs[ fiUnpSpillIdxStop + 1 ];   //!
+        break;
+      }
+      default:
+      {
+        LOG(fatal) << "Unknown spill start point option: " << fuFlagSpillStart;
+        break;
+      }
+    }  // switch( fuFlagSpillStart )
+  }  // if (0 <= fiUnpSpillIdxStart)
+
   return kTRUE;
 }
 
@@ -248,26 +318,22 @@ Int_t CbmMcbm2018Source::FillBuffer() {
           }  // for( auto it = it_list.first; it != it_list.second; ++it )
         }    // else of if( it == fUnpackers.end() )
       }      // for (size_t c {0}; c < ts.num_components(); c++)
-    }        // if( 1 == fTSCounter )
 
-    /// Apply TS throttling as set by user (default = 1 => no throttling)
-    if (0 == tsIndex % fuTsReduction) {
-      for (auto itUnp = fUnpackersToRun.begin(); itUnp != fUnpackersToRun.end();
-           ++itUnp) {
-        (*itUnp)->DoUnpack(ts, 0);
-      }  // for( auto itUnp = fUnpackersToRun.begin(); itUnp != fUnpackersToRun.end(); ++ itUnp )
-    }    // if( 0 == tsIndex % fuTsReduction )
-
-    /// Save the TimeSlice meta-data for access by higher level tasks
-    if (fTSCounter == 1) {
+      /// Compute and store the timeslice and microslices properties
       auto nMsInTs = ts.num_core_microslices();
-      if (nMsInTs > 1) {
+      if (nMsInTs > 2) {
         // This assumes that we have a component 0 and component independent ms/ts settings!
-        auto msDescA      = ts.descriptor(0, 0);
-        auto msDescB      = ts.descriptor(0, 1);
+        auto msDescA      = ts.descriptor(0, 1);
+        auto msDescB      = ts.descriptor(0, 2);
         auto msLength     = msDescB.idx - msDescA.idx;
         fTSLength         = msLength * nMsInTs;
         fTSOverlappLength = msLength * (ts.num_microslices(0) - nMsInTs);
+        LOG(info)
+            << "CbmMcbm2018Source::FillBuffer() - TS 1 - Calculated "
+            << "TimesliceMetaData information from microslices Metadata -> "
+            << "MS length found to be " << msLength << " ns, TS length "
+            << fTSLength << " ns, and TS overlap length "
+            << fTSOverlappLength << " ns";
       } else {
         LOG(warning)
           << "CbmMcbm2018Source::FillBuffer() - TS 1 - Calculate "
@@ -275,7 +341,32 @@ Int_t CbmMcbm2018Source::FillBuffer() {
              "TS duration can not be calculated with the given method. Hence, "
              "TimesliceMetaData duration values are filled with 0";
       }
-    }
+    }        // if( 1 == fTSCounter )
+
+    if (0 <= fiUnpSpillIdxStart )
+    {
+      if (tsIndex < fuSpillBegTs)
+      {
+        /// Jump all TS until reaching the first TS in the spill we want to unpack
+        continue;
+      } // if (tsIndex < fuSpillBegTs)
+      else if(fuSpillEndTs <= tsIndex)
+      {
+        /// Stop when reaching the first TS in the next spill
+        return 1;
+      } // else if
+    }  // if (0 <= fiUnpSpillIdxStart)
+
+
+    /// Apply TS throttling as set by user (default = 1 => no throttling)
+    if (0 == tsIndex % fuTsReduction) {
+      for (auto itUnp = fUnpackersToRun.begin(); itUnp != fUnpackersToRun.end();
+           ++itUnp) {
+        (*itUnp)->DoUnpack(ts, 0);
+      }  // for( auto itUnp = fUnpackersToRun.begin(); itUnp != fUnpackersToRun.end(); ++ itUnp )
+    }    // if( 0 == tsIndex % fuTsReduction )
+
+    /// Save the TimeSlice meta-data for access by higher level tasks
     new ((*fTimeSliceMetaDataArray)[fTimeSliceMetaDataArray->GetEntriesFast()])
       TimesliceMetaData(
         ts.descriptor(0, 0).idx, fTSLength, fTSOverlappLength, tsIndex);
diff --git a/fles/mcbm2018/CbmMcbm2018Source.h b/fles/mcbm2018/CbmMcbm2018Source.h
index d0d7e394..9d89d04c 100644
--- a/fles/mcbm2018/CbmMcbm2018Source.h
+++ b/fles/mcbm2018/CbmMcbm2018Source.h
@@ -77,6 +77,28 @@ public:
     fuTsReduction = uTsReduction;
   }
 
+  void UnpackSingleSpill( Int_t uSpillIdx, UInt_t uSpillStart = 1 ) {
+     fiUnpSpillIdxStart = uSpillIdx;
+     fiUnpSpillIdxStop = uSpillIdx;
+     fuFlagSpillStart = uSpillStart;
+  }
+
+  void UnpackSelectSpills( Int_t uSpillIdxStart,  Int_t uSpillIdxStop, UInt_t uSpillStart = 1 ) {
+     fiUnpSpillIdxStart = uSpillIdxStart;
+     fiUnpSpillIdxStop = uSpillIdxStop;
+     fuFlagSpillStart = uSpillStart;
+  }
+
+  void LoadTsListSpillBreakBegin( std::vector< ULong64_t > vTsBeg ) {
+     fvuSpillBreakBegTs.assign( vTsBeg.begin(), vTsBeg.end() );
+  }
+  void LoadTsListSpillBreakEnd( std::vector< ULong64_t > vTsEnd ) {
+     fvuSpillBreakEndTs.assign( vTsEnd.begin(), vTsEnd.end() );
+  }
+  void LoadTsListSpillBreakMiddle( std::vector< ULong64_t > vTsMid ) {
+     fvuSpillBreakMidTs.assign( vTsMid.begin(), vTsMid.end() );
+  }
+
   void SetSubscriberHwm(UInt_t val = 1) { fuSubscriberHwm = val; }
 
   void SetWriteOutputFlag(Bool_t bFlagIn) { fbWriteOutput = bFlagIn; }
@@ -105,6 +127,16 @@ private:
 
   UInt_t fuTsReduction;
 
+  Int_t fiUnpSpillIdxStart = -1;  //! >= 0 means unpack only from this spill
+  Int_t fiUnpSpillIdxStop  = -1;  //! >= 0 means unpack only up to this spill (included)
+  UInt_t fuFlagSpillStart  = 0;   //! 0 = Break begin, 1 = Break middle, 2 = Break end
+  UInt_t fuSpillBegTs      = 0;   //!
+  UInt_t fuSpillEndTs      = 0;   //!
+
+  std::vector< ULong64_t > fvuSpillBreakBegTs = {};  //!
+  std::vector< ULong64_t > fvuSpillBreakEndTs = {};  //!
+  std::vector< ULong64_t > fvuSpillBreakMidTs = {};  //!
+
   std::unique_ptr<fles::TimesliceSource> fSource;  //!
 
   UInt_t fuSubscriberHwm;
diff --git a/fles/mcbm2018/tasks/CbmMcbmSpillFindAlgo.cxx b/fles/mcbm2018/tasks/CbmMcbmSpillFindAlgo.cxx
new file mode 100644
index 00000000..96a0e6f4
--- /dev/null
+++ b/fles/mcbm2018/tasks/CbmMcbmSpillFindAlgo.cxx
@@ -0,0 +1,511 @@
+// -----------------------------------------------------------------------------
+// -----                                                                   -----
+// -----                  CbmMcbmSpillFindAlgo                         -----
+// -----               Created 10.02.2019 by P.-A. Loizeau                 -----
+// -----                                                                   -----
+// -----------------------------------------------------------------------------
+
+#include "CbmMcbmSpillFindAlgo.h"
+
+#include "CbmFlesHistosTools.h"
+#include "CbmFormatMsHeaderPrintout.h"
+#include "CbmMcbm2018TofPar.h"
+#include "CbmTofAddress.h"
+#include "CbmTofDetectorId_v14a.h"  // in cbmdata/tof
+
+#include "FairLogger.h"
+#include "FairRootManager.h"
+#include "FairRun.h"
+#include "FairRunOnline.h"
+#include "FairRuntimeDb.h"
+
+#include "TCanvas.h"
+#include "TH1.h"
+#include "TH2.h"
+#include "TList.h"
+#include "TPaveStats.h"
+#include "TProfile.h"
+#include "TROOT.h"
+#include "TString.h"
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+
+#include <stdint.h>
+
+// -------------------------------------------------------------------------
+CbmMcbmSpillFindAlgo::CbmMcbmSpillFindAlgo() : CbmStar2019Algo() {}
+CbmMcbmSpillFindAlgo::~CbmMcbmSpillFindAlgo()
+{
+}
+
+// -------------------------------------------------------------------------
+Bool_t CbmMcbmSpillFindAlgo::Init()
+{
+  LOG(info) << "Initializing mCBM T0 2019 monitor algo";
+
+  return kTRUE;
+}
+void CbmMcbmSpillFindAlgo::Reset() {}
+void CbmMcbmSpillFindAlgo::Finish()
+{
+  /// If Spill is On, add a fake spill break to have the last spill
+  /// If Spill is Off, add a fake spill break end so that all modes include last spill
+  if (fbSpillOn)
+  {
+    fvuSpillBreakBegTs.push_back( fulCurrentTsIdx + 1 );
+    fvuSpillBreakEndTs.push_back( fulCurrentTsIdx + 1 );
+  } // if (fbSpillOn)
+  else fvuSpillBreakEndTs.push_back( fulCurrentTsIdx + 1 );
+
+  /// Fill the vector of spill break middle points
+  std::vector< ULong64_t >::iterator itBreakBeg = fvuSpillBreakBegTs.begin();
+  std::vector< ULong64_t >::iterator itBreakEnd = fvuSpillBreakEndTs.begin();
+
+  if( itBreakBeg != fvuSpillBreakBegTs.end() && itBreakEnd != fvuSpillBreakEndTs.end() && *itBreakEnd < *itBreakBeg ) {
+    fvuSpillBreakMidTs.push_back((*itBreakEnd + fulFirstTsIdx) / 2);
+    ++itBreakEnd;
+  }  // if( itBreakBeg != fvuSpillBreakBegTs.end() && itBreakEnd != fvuSpillBreakEndTs.end() && *itBreakEnd < *itBreakBeg )
+
+  while( itBreakBeg != fvuSpillBreakBegTs.end() && itBreakEnd != fvuSpillBreakEndTs.end() ) {
+    fvuSpillBreakMidTs.push_back((*itBreakBeg + *itBreakEnd) / 2);
+    ++itBreakBeg;
+    ++itBreakEnd;
+  }  // while( itBreakBeg != fvuSpillBreakBegTs.end() && itBreakEnd != fvuSpillBreakEndTs.end() )
+
+  if( itBreakBeg != fvuSpillBreakBegTs.end() ) {
+   fvuSpillBreakMidTs.push_back((*itBreakBeg + fulCurrentTsIdx) / 2);
+    ++itBreakBeg;
+  } // if( itBreakBeg != fvuSpillBreakBegTs.end() )
+
+  if( itBreakBeg != fvuSpillBreakBegTs.end() || itBreakEnd != fvuSpillBreakEndTs.end() ) {
+    LOG(warning) << "Size of spill breaks beginning or end did not match: "
+                 << fvuSpillBreakBegTs.size() << " VS " << fvuSpillBreakEndTs.size();
+  }  // if( itBreakBeg != fvuSpillBreakBegTs.end() || itBreakEnd != fvuSpillBreakEndTs.end() )
+
+  LOG(info) << "**********************************************";
+  LOG(info) << "TS index for beginning of spill breaks:";
+  for (ULong64_t uBeg : fvuSpillBreakBegTs) {
+    LOG(info)<< Form( "%9llu", uBeg );
+  }  // for (ULong64_t uBeg : fvuSpillBreakBegTs)
+  LOG(info) << "**********************************************";
+  LOG(info) << "TS index for ending of spill breaks:";
+  for (ULong64_t uEnd : fvuSpillBreakEndTs) {
+    LOG(info)<< Form( "%9llu", uEnd );
+  }  // for (ULong64_t uBeg : fvuSpillBreakBegTs)
+  LOG(info) << "**********************************************";
+  LOG(info) << "TS index for middle of spill breaks:";
+  for (ULong64_t uMid : fvuSpillBreakMidTs) {
+    LOG(info)<< Form( "%9llu", uMid );
+  }  // for (ULong64_t uBeg : fvuSpillBreakBegTs)
+  LOG(info) << "**********************************************";
+
+}
+
+// -------------------------------------------------------------------------
+Bool_t CbmMcbmSpillFindAlgo::InitContainers()
+{
+  LOG(info) << "Init parameter containers for CbmMcbmSpillFindAlgo";
+  Bool_t initOK = ReInitContainers();
+
+  return initOK;
+}
+Bool_t CbmMcbmSpillFindAlgo::ReInitContainers()
+{
+  LOG(info) << "**********************************************";
+  LOG(info) << "ReInit parameter containers for CbmMcbmSpillFindAlgo";
+
+  fUnpackPar = (CbmMcbm2018TofPar*) fParCList->FindObject("CbmMcbm2018TofPar");
+  if (nullptr == fUnpackPar) return kFALSE;
+
+  Bool_t initOK = InitParameters();
+
+  return initOK;
+}
+TList* CbmMcbmSpillFindAlgo::GetParList()
+{
+  if (nullptr == fParCList) fParCList = new TList();
+  fUnpackPar = new CbmMcbm2018TofPar("CbmMcbm2018TofPar");
+  fParCList->Add(fUnpackPar);
+
+  return fParCList;
+}
+Bool_t CbmMcbmSpillFindAlgo::InitParameters()
+{
+
+  fuNrOfGdpbs = fUnpackPar->GetNrOfGdpbs();
+  LOG(info) << "Nr. of Tof GDPBs: " << fuNrOfGdpbs;
+
+  fuNrOfFeePerGdpb = fUnpackPar->GetNrOfFeesPerGdpb();
+  LOG(info) << "Nr. of FEES per Tof GDPB: " << fuNrOfFeePerGdpb;
+
+  fuNrOfGet4PerFee = fUnpackPar->GetNrOfGet4PerFee();
+  LOG(info) << "Nr. of GET4 per Tof FEE: " << fuNrOfGet4PerFee;
+
+  fuNrOfChannelsPerGet4 = fUnpackPar->GetNrOfChannelsPerGet4();
+  LOG(info) << "Nr. of channels per GET4: " << fuNrOfChannelsPerGet4;
+
+  fuNrOfChannelsPerFee = fuNrOfGet4PerFee * fuNrOfChannelsPerGet4;
+  LOG(info) << "Nr. of channels per FEE: " << fuNrOfChannelsPerFee;
+
+  fuNrOfGet4 = fuNrOfGdpbs * fuNrOfFeePerGdpb * fuNrOfGet4PerFee;
+  LOG(info) << "Nr. of GET4s: " << fuNrOfGet4;
+
+  fuNrOfGet4PerGdpb = fuNrOfFeePerGdpb * fuNrOfGet4PerFee;
+  LOG(info) << "Nr. of GET4s per GDPB: " << fuNrOfGet4PerGdpb;
+
+  fuNrOfChannelsPerGdpb = fuNrOfGet4PerGdpb * fuNrOfChannelsPerGet4;
+  LOG(info) << "Nr. of channels per GDPB: " << fuNrOfChannelsPerGdpb;
+
+  fGdpbIdIndexMap.clear();
+  for (UInt_t i = 0; i < fuNrOfGdpbs; ++i) {
+    fGdpbIdIndexMap[fUnpackPar->GetGdpbId(i)] = i;
+    LOG(info) << "GDPB Id of TOF  " << i << " : " << std::hex << fUnpackPar->GetGdpbId(i) << std::dec;
+  }  // for( UInt_t i = 0; i < fuNrOfGdpbs; ++i )
+
+  return kTRUE;
+}
+// -------------------------------------------------------------------------
+
+void CbmMcbmSpillFindAlgo::AddMsComponentToList(size_t component, UShort_t usDetectorId)
+{
+  /// Check for duplicates and ignore if it is the case
+  for (UInt_t uCompIdx = 0; uCompIdx < fvMsComponentsList.size(); ++uCompIdx)
+    if (component == fvMsComponentsList[uCompIdx]) return;
+
+  /// Add to list
+  fvMsComponentsList.push_back(component);
+
+  LOG(info) << "CbmMcbmSpillFindAlgo::AddMsComponentToList => Component " << component << " with detector ID 0x"
+            << std::hex << usDetectorId << std::dec << " added to list";
+}
+// -------------------------------------------------------------------------
+
+Bool_t CbmMcbmSpillFindAlgo::ProcessTs(const fles::Timeslice& ts)
+{
+  fulCurrentTsIdx = ts.index();
+  fdTsStartTime   = static_cast<Double_t>(ts.descriptor(0, 0).idx);
+  if( fulCurrentTsIdx < fulFirstTsIdx )
+    fulFirstTsIdx = fulCurrentTsIdx;
+
+  /// Ignore First TS as first MS is typically corrupt
+  if (0 == fulCurrentTsIdx) return kTRUE;
+
+  /// 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);
+    fdTsFullSizeInNs = fdMsSizeInNs * (fuNbCoreMsPerTs + fuNbOverMsPerTs);
+    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";
+
+    /// Ignore overlap ms if flag set by user
+    fuNbMsLoop = fuNbCoreMsPerTs;
+    if (kFALSE == fbIgnoreOverlapMs) fuNbMsLoop += fuNbOverMsPerTs;
+    LOG(info) << "In each TS " << fuNbMsLoop << " MS will be looped over";
+  }  // if( -1.0 == fdTsCoreSizeInNs )
+
+  /// Compute time of TS core end
+  fdTsStopTimeCore = fdTsStartTime + fdTsCoreSizeInNs;
+  //      LOG(info) << Form( "TS %5d Start %12f Stop %12f", fulCurrentTsIdx, fdTsStartTime, fdTsStopTimeCore );
+
+  /// Loop over core microslices (and overlap ones if chosen)
+  for (fuMsIndex = 0; fuMsIndex < fuNbMsLoop; fuMsIndex++) {
+    /// Loop over registered components
+    for (UInt_t uMsCompIdx = 0; uMsCompIdx < fvMsComponentsList.size(); ++uMsCompIdx) {
+      UInt_t uMsComp = fvMsComponentsList[uMsCompIdx];
+
+      if (kFALSE == ProcessMs(ts, uMsComp, fuMsIndex)) {
+        LOG(error) << "Failed to process ts " << fulCurrentTsIdx << " MS " << fuMsIndex << " for component " << uMsComp;
+        return kFALSE;
+      }  // if( kFALSE == ProcessMs( ts, uMsCompIdx, fuMsIndex ) )
+    }    // for( UInt_t uMsCompIdx = 0; uMsCompIdx < fvMsComponentsList.size(); ++uMsCompIdx )
+  }  // for( fuMsIndex = 0; fuMsIndex < uNbMsLoop; fuMsIndex ++ )
+
+  /// Fill plots if in monitor mode
+  if (fbMonitorMode) {
+    if (kFALSE == FillHistograms()) {
+      LOG(error) << "Failed to fill histos in ts " << fulCurrentTsIdx;
+      return kFALSE;
+    }  // if( kFALSE == FillHistograms() )
+  }    // if( fbMonitorMode )
+
+  return kTRUE;
+}
+
+Bool_t CbmMcbmSpillFindAlgo::ProcessMs(const fles::Timeslice& ts, size_t uMsCompIdx, size_t uMsIdx)
+{
+  auto msDescriptor        = ts.descriptor(uMsCompIdx, uMsIdx);
+  fuCurrentEquipmentId     = msDescriptor.eq_id;
+  const uint8_t* msContent = reinterpret_cast<const uint8_t*>(ts.content(uMsCompIdx, uMsIdx));
+
+  uint32_t uSize  = msDescriptor.size;
+  fulCurrentMsIdx = msDescriptor.idx;
+  fdMsTime        = (1e-9) * static_cast<double>(fulCurrentMsIdx);
+  LOG(debug) << "Microslice: " << fulCurrentMsIdx << " from EqId " << std::hex << fuCurrentEquipmentId << std::dec
+             << " has size: " << uSize;
+
+  if (0 == fvbMaskedComponents.size()) fvbMaskedComponents.resize(ts.num_components(), kFALSE);
+
+  fuCurrDpbId = static_cast<uint32_t>(fuCurrentEquipmentId & 0xFFFF);
+
+  /*
+ * Should be only for first detected TS
+ */
+  if (fulCurrentTsIdx < 10 && 0 == uMsIdx) {
+    LOG(INFO) << "---------------------------------------------------------------";
+    LOG(INFO) << "Component " << uMsCompIdx << " TS Idx " << fulCurrentTsIdx;
+    LOG(INFO) << "hi hv eqid flag si sv idx/start        crc      size     offset";
+    LOG(INFO) << Form("%02x %02x %04x %04x %02x %02x %016lx %08x %08x %016lx",
+                      static_cast<unsigned int>(msDescriptor.hdr_id), static_cast<unsigned int>(msDescriptor.hdr_ver),
+                      msDescriptor.eq_id, msDescriptor.flags, static_cast<unsigned int>(msDescriptor.sys_id),
+                      static_cast<unsigned int>(msDescriptor.sys_ver), static_cast<unsigned long>(msDescriptor.idx),
+                      msDescriptor.crc, msDescriptor.size, static_cast<unsigned long>(msDescriptor.offset));
+  }  // if( fulCurrentTsIdx < 10 && 0 == uMsIdx )
+     /*
+*/
+
+  /// Check if this sDPB ID was declared in parameter file and stop there if not
+  auto it = fGdpbIdIndexMap.find(fuCurrDpbId);
+  if (it == fGdpbIdIndexMap.end()) {
+    if (kFALSE == fvbMaskedComponents[uMsCompIdx]) {
+      LOG(info) << "---------------------------------------------------------------";
+      /*
+          LOG(info) << "hi hv eqid flag si sv idx/start        crc      size     offset";
+          LOG(info) << Form( "%02x %02x %04x %04x %02x %02x %016llx %08x %08x %016llx",
+                            static_cast<unsigned int>(msDescriptor.hdr_id),
+                            static_cast<unsigned int>(msDescriptor.hdr_ver), msDescriptor.eq_id, msDescriptor.flags,
+                            static_cast<unsigned int>(msDescriptor.sys_id),
+                            static_cast<unsigned int>(msDescriptor.sys_ver), msDescriptor.idx, msDescriptor.crc,
+                            msDescriptor.size, msDescriptor.offset );
+*/
+      LOG(info) << FormatMsHeaderPrintout(msDescriptor);
+      LOG(warning) << "Could not find the gDPB index for AFCK id 0x" << std::hex << fuCurrDpbId << std::dec
+                   << " in timeslice " << fulCurrentTsIdx << " in microslice " << uMsIdx << " component " << uMsCompIdx
+                   << "\n"
+                   << "If valid this index has to be added in the TOF "
+                      "parameter file in the DbpIdArray field";
+      fvbMaskedComponents[uMsCompIdx] = kTRUE;
+    }  // if( kFALSE == fvbMaskedComponents[ uMsComp ] )
+    else
+      return kTRUE;
+
+    /// Try to get it from the second message in buffer (first is epoch cycle without gDPB ID)
+    /// TODO!!!!
+
+    return kFALSE;
+  }  // if( it == fGdpbIdIndexMap.end() )
+  else
+    fuCurrDpbIdx = fGdpbIdIndexMap[fuCurrDpbId];
+
+  /// Spill Detection
+  if (0 == fuCurrDpbIdx) {
+    /// Check only every user defined interval (0.5s per default)
+    if (fdSpillCheckInterval < fdMsTime - fdLastSecondTime) {
+      /// Spill Off detection
+      if (fbSpillOn && fuCountsLastInterval < fuOffSpillCountLimit) {
+        fbSpillOn = kFALSE;
+        fuCurrentSpillIdx++;
+        fdStartTimeSpill   = fdMsTime;
+        if( 0 < fvuSpillBreakBegTs.size() )
+        {
+           fhSpillDuration->Fill( fulCurrentTsIdx - fvuSpillBreakBegTs.back() );
+        } // if( 0 < fvuSpillBreakBegTs.size() )
+        fvuSpillBreakBegTs.push_back( fulCurrentTsIdx );
+      }  // if( fbSpillOn && fuCountsLastInterval < fuOffSpillCountLimit )
+      else if (!fbSpillOn && fuOffSpillCountLimit < fuCountsLastInterval) {
+        fbSpillOn = kTRUE;
+        if( 0 < fvuSpillBreakBegTs.size() )
+        {
+           fhSpillBreakDuration->Fill( fuCurrentSpillIdx, fulCurrentTsIdx - fvuSpillBreakBegTs.back() );
+        } // if( 0 < fvuSpillBreakBegTs.size() )
+        if( 0 < fvuSpillBreakEndTs.size() )
+        {
+           fhHitsPerSpill->Fill( fuCurrentSpillIdx, fuCountsLastSpill );
+           fhSpillDuration->Fill( fuCurrentSpillIdx, fulCurrentTsIdx - fvuSpillBreakEndTs.back() );
+        } // if( 0 < fvuSpillBreakEndTs.size() )
+        fvuSpillBreakEndTs.push_back( fulCurrentTsIdx );
+        fuCountsLastSpill = 0;
+      }  // else if (fuOffSpillCountLimit < fuCountsLastInterval)
+
+      fuCountsLastInterval = 0;
+      fdLastSecondTime   = fdMsTime;
+    }  // if( fdSpillCheckInterval < fdMsTime - fdLastSecondTime )
+  }    // if( 0 == fuCurrDpbIdx )
+
+  /// Save start time of first valid MS )
+  if (fdStartTime < 0) fdStartTime = fdMsTime;
+  /// Reset the histograms if reached the end of the evolution histos range
+  else if (fuHistoryHistoSize < fdMsTime - fdStartTime) {
+    ResetHistograms();
+    fdStartTime = fdMsTime;
+  }  // else if( fuHistoryHistoSize < fdMsTime - fdStartTime )
+
+  // If not integer number of message in input buffer, print warning/error
+  if (0 != (uSize % kuBytesPerMessage))
+    LOG(error) << "The input microslice buffer does NOT "
+               << "contain only complete nDPB messages!";
+
+  // Compute the number of complete messages in the input microslice buffer
+  uint32_t uNbMessages = (uSize - (uSize % kuBytesPerMessage)) / kuBytesPerMessage;
+
+  // Prepare variables for the loop on contents
+  Int_t messageType       = -111;
+  ULong64_t ulNbHitsTs    = 0;
+  const uint64_t* pInBuff = reinterpret_cast<const uint64_t*>(msContent);
+  for (uint32_t uIdx = 0; uIdx < uNbMessages; uIdx++) {
+    // Fill message
+    uint64_t ulData = static_cast<uint64_t>(pInBuff[uIdx]);
+
+    /// Catch the Epoch cycle block which is always the first 64b of the MS
+    if (0 == uIdx) {
+      continue;
+    }  // if( 0 == uIdx )
+
+    gdpbv100::Message mess(ulData);
+    /// Get message type
+    messageType = mess.getMessageType();
+
+    fuGet4Id = mess.getGdpbGenChipId();
+    fuGet4Nr = fuGet4Id / 2;
+    // UInt_t uChannelT0 = ( fuGet4Id < 32 ) ? ( fuGet4Id / 8 ) : (fuGet4Id / 8 - 1); /// December 2018 mapping
+    // UInt_t uChannelT0 = fuGet4Id / 2 + 4 * fuCurrDpbIdx;  /// 2019 mapping with 320/640 Mb/s FW
+
+    if (fuNrOfGet4PerGdpb <= fuGet4Id && !mess.isStarTrigger() && (gdpbv100::kuChipIdMergedEpoch != fuGet4Id))
+      LOG(warning) << "Message with Get4 ID too high: " << fuGet4Id << " VS " << fuNrOfGet4PerGdpb
+                   << " set in parameters.";
+
+    switch (messageType) {
+      case gdpbv100::MSG_HIT: {
+        if (mess.getGdpbHitIs24b()) {
+          LOG(error) << "This event builder does not support 24b hit message!!!.";
+          continue;
+        }  // if( getGdpbHitIs24b() )
+        else {
+          /// Spill detection
+
+          /// Do not fill the pulser hits to keep counts low for channel 0
+          /// => Check channel != first GET4 per board + TOT
+          UInt_t uTot = mess.getGdpbHit32Tot();
+          if (0 != fuGet4Nr || uTot < fuMinTotPulser || fuMaxTotPulser < uTot) {
+            fuCountsLastInterval++;
+            fuCountsLastSpill++;
+            ulNbHitsTs++;
+          }  // if (0 != fuGet4Nr || uTot < fuMinTotPulser || fuMaxTotPulser < uTot) {
+        }  // else of if( getGdpbHitIs24b() )
+        break;
+      }  // case gdpbv100::MSG_HIT:
+      case gdpbv100::MSG_EPOCH: {
+        break;
+      }  // case gdpbv100::MSG_EPOCH:
+      case gdpbv100::MSG_SLOWC: {
+        break;
+      }  // case gdpbv100::MSG_SLOWC:
+      case gdpbv100::MSG_SYST: {
+        break;
+      }  // case gdpbv100::MSG_SYST:
+      case gdpbv100::MSG_STAR_TRI_A:
+      case gdpbv100::MSG_STAR_TRI_B:
+      case gdpbv100::MSG_STAR_TRI_C:
+      case gdpbv100::MSG_STAR_TRI_D: {
+        break;
+      }  // case gdpbv100::MSG_STAR_TRI_A-D
+      default:
+        LOG(error) << "Message type " << std::hex << std::setw(2) << static_cast<uint16_t>(messageType)
+                   << " not included in Get4 unpacker.";
+    }  // switch( mess.getMessageType() )
+  }    // for (uint32_t uIdx = 0; uIdx < uNbMessages; uIdx ++)
+  fhHitsEvo->Fill( fdTsStartTime*1e-9, ulNbHitsTs );
+
+  /// Fill histograms
+  FillHistograms();
+
+  return kTRUE;
+}
+// -------------------------------------------------------------------------
+Bool_t CbmMcbmSpillFindAlgo::CreateHistograms()
+{
+  std::string sFolder = "SpillFinder";
+
+  LOG(info) << "create Histos for T0 monitoring ";
+
+  /// Logarithmic bining
+  uint32_t iNbBinsLog = 0;
+  /// Parameters are NbDecadesLog, NbStepsDecade, NbSubStepsInStep
+  std::vector<double> dBinsLogVector = GenerateLogBinArray(4, 9, 1, iNbBinsLog);
+  double* dBinsLog                   = dBinsLogVector.data();
+  //   double * dBinsLog = GenerateLogBinArray( 4, 9, 1, iNbBinsLog );
+
+  /*******************************************************************/
+  fhHitsEvo      = new TH1I("hHitsEvo", "Hit count evo; Time [s]; Hits Count []", fuHistoryHistoSize * 50, 0.,
+                            fuHistoryHistoSize);
+  fhHitsPerSpill = new TH1I("hHitsPerSpill", "Hit count per spill; Spill; Hits Count []", 2000, 0., 2000);
+  fhSpillBreakDuration = new TH1I("hSpillBreakDuration", "Spill break duration; Spill; Duration [TS]", 2000, 0., 2000);
+  fhSpillDuration = new TH1I("hSpillDuration", "Spill duration; Spill; Duration [TS]", 2000, 0., 2000);
+
+  /// Add pointers to the vector with all histo for access by steering class
+  AddHistoToVector(fhHitsEvo, sFolder);
+  AddHistoToVector(fhHitsPerSpill, sFolder);
+  AddHistoToVector(fhSpillBreakDuration, sFolder);
+  AddHistoToVector(fhSpillDuration, sFolder);
+
+  /*******************************************************************/
+  /// Cleanup array of log bins
+  //   delete dBinsLog;
+
+  /*******************************************************************/
+
+  /// Canvases
+  Double_t w = 10;
+  Double_t h = 10;
+
+  /*******************************************************************/
+/*
+  /// Map of hits over T0 detector and same vs time in run
+  fcHitMaps = new TCanvas("cHitMaps", "Hit maps", w, h);
+  fcHitMaps->Divide(2);
+
+  fcHitMaps->cd(1);
+  gPad->SetGridx();
+  gPad->SetGridy();
+  gPad->SetLogy();
+  fhChannelMap->Draw();
+
+  fcHitMaps->cd(2);
+  gPad->SetGridx();
+  gPad->SetGridy();
+  gPad->SetLogz();
+  fhHitMapEvo->Draw("colz");
+
+  AddCanvasToVector(fcHitMaps, "canvases");
+*/
+  /*******************************************************************/
+
+  /*******************************************************************/
+
+  return kTRUE;
+}
+Bool_t CbmMcbmSpillFindAlgo::FillHistograms()
+{
+
+  return kTRUE;
+}
+Bool_t CbmMcbmSpillFindAlgo::ResetHistograms(Bool_t bResetTime)
+{
+  fuCurrentSpillIdx  = 0;
+  fhHitsEvo->Reset();
+  fhHitsPerSpill->Reset();
+  fhSpillBreakDuration->Reset();
+  fhSpillDuration->Reset();
+
+  if (kTRUE == bResetTime) {
+    /// Also reset the Start time for the evolution plots!
+    fdStartTime = -1.0;
+  }  // if( kTRUE == bResetTime )
+
+  return kTRUE;
+}
+// -------------------------------------------------------------------------
diff --git a/fles/mcbm2018/tasks/CbmMcbmSpillFindAlgo.h b/fles/mcbm2018/tasks/CbmMcbmSpillFindAlgo.h
new file mode 100644
index 00000000..21dcbca9
--- /dev/null
+++ b/fles/mcbm2018/tasks/CbmMcbmSpillFindAlgo.h
@@ -0,0 +1,163 @@
+// -----------------------------------------------------------------------------
+// -----                                                                   -----
+// -----                  CbmMcbmSpillFindAlgo                         -----
+// -----               Created 10.02.2019 by P.-A. Loizeau                 -----
+// -----                                                                   -----
+// -----------------------------------------------------------------------------
+
+#ifndef CbmMcbmSpillFindAlgo_H
+#define CbmMcbmSpillFindAlgo_H
+
+#include "CbmStar2019Algo.h"
+
+// Data
+#include "CbmTofDigi.h"
+
+#include "gDpbMessv100.h"
+
+// CbmRoot
+
+// C++11
+#include <chrono>
+
+// C/C++
+#include <map>
+#include <vector>
+
+class CbmMcbm2018TofPar;
+/*
+class TCanvas;
+class THttpServer;
+*/
+class TH1;
+class TH2;
+class TProfile;
+
+class CbmMcbmSpillFindAlgo : public CbmStar2019Algo<CbmTofDigi> {
+public:
+  CbmMcbmSpillFindAlgo();
+  ~CbmMcbmSpillFindAlgo();
+
+  virtual Bool_t Init();
+  virtual void Reset();
+  virtual void Finish();
+
+  Bool_t InitContainers();
+  Bool_t ReInitContainers();
+  TList* GetParList();
+
+  Bool_t InitParameters();
+
+  Bool_t ProcessTs(const fles::Timeslice& ts);
+  Bool_t ProcessTs(const fles::Timeslice& ts, size_t /*component*/) { return ProcessTs(ts); }
+  Bool_t ProcessMs(const fles::Timeslice& ts, size_t uMsCompIdx, size_t uMsIdx);
+
+  void AddMsComponentToList(size_t component, UShort_t usDetectorId);
+
+  Bool_t CreateHistograms();
+  Bool_t FillHistograms();
+  Bool_t ResetHistograms(Bool_t bResetTime = kTRUE);
+
+  inline void SetMonitorMode(Bool_t bFlagIn = kTRUE) { fbMonitorMode = bFlagIn; }
+  inline void SetHistoryHistoSize(UInt_t inHistorySizeSec = 1800) { fuHistoryHistoSize = inHistorySizeSec; }
+  inline void SetPulserTotLimits(UInt_t uMin, UInt_t uMax)
+  {
+    fuMinTotPulser = uMin;
+    fuMaxTotPulser = uMax;
+  }
+  inline void SetSpillThreshold(UInt_t uCntLimit) { fuOffSpillCountLimit = uCntLimit; }
+  inline void SetSpillCheckIntervalSec(Double_t dInterval) { fdSpillCheckInterval = dInterval; }
+
+private:
+  /// Control flags
+  Bool_t fbMonitorMode                    = kFALSE;  //! Switch ON the filling of a minimal set of histograms
+  Bool_t fbDebugMonitorMode               = kFALSE;  //! Switch ON the filling of a additional set of histograms
+  std::vector<Bool_t> fvbMaskedComponents = {};
+
+  /// Settings from parameter file
+  CbmMcbm2018TofPar* fUnpackPar = nullptr;  //!
+    /// Readout chain dimensions and mapping
+  UInt_t fuNrOfGdpbs                       = 0;   //! Total number of GDPBs in the system
+  std::map<UInt_t, UInt_t> fGdpbIdIndexMap = {};  //! gDPB ID to index map
+  UInt_t fuNrOfFeePerGdpb                  = 0;   //! Number of FEBs per GDPB
+  UInt_t fuNrOfGet4PerFee                  = 0;   //! Number of GET4s per FEE
+  UInt_t fuNrOfChannelsPerGet4             = 0;   //! Number of channels in each GET4
+  UInt_t fuNrOfChannelsPerFee              = 0;   //! Number of channels in each FEE
+  UInt_t fuNrOfGet4                        = 0;   //! Total number of Get4 chips in the system
+  UInt_t fuNrOfGet4PerGdpb                 = 0;   //! Number of GET4s per GDPB
+  UInt_t fuNrOfChannelsPerGdpb             = 0;   //! Number of channels per GDPB
+
+  /// User settings: Data correction parameters
+  UInt_t   fuMinTotPulser       = 90;
+  UInt_t   fuMaxTotPulser       = 100;
+  UInt_t   fuOffSpillCountLimit = 100;
+  Double_t fdSpillCheckInterval = 0.5;
+
+  /// Constants
+  static const Int_t kiMaxNbFlibLinks   = 32;
+  static const UInt_t kuBytesPerMessage = 8;
+  static const UInt_t kuNbChanDiamond   = 8;
+
+  /// Running indices
+  /// TS/MS info
+  ULong64_t fulFirstTsIdx   = 9999999999999; //! First TS index, forward point set ~30 years...
+  ULong64_t fulCurrentTsIdx = 0;
+  ULong64_t fulCurrentMsIdx = 0;
+  Double_t fdTsStartTime    = -1.0;  //! Time in ns of current TS from the index of the first MS first component
+  Double_t fdTsStopTimeCore =
+    -1.0;                    //! End Time in ns of current TS Core from the index of the first MS first component
+  Double_t fdMsTime = -1.0;  //! Start Time in ns of current MS from its index field in header
+  UInt_t fuMsIndex  = 0;     //! Index of current MS within the TS
+                             /// Current data properties
+  std::map<gdpbv100::MessageTypes, UInt_t> fmMsgCounter = {};
+  UInt_t fuCurrentEquipmentId = 0;   //! Current equipment ID, tells from which DPB the current MS is originating
+  UInt_t fuCurrDpbId          = 0;   //! Temp holder until Current equipment ID is properly filled in MS
+  UInt_t fuCurrDpbIdx         = 0;   //! Index of the DPB from which the MS currently unpacked is coming
+  Int_t fiRunStartDateTimeSec = -1;  //! Start of run time since "epoch" in s, for the plots with date as X axis
+  Int_t fiBinSizeDatePlots    = -1;  //! Bin size in s for the plots with date as X axis
+  UInt_t fuGet4Id = 0;  //! running number (0 to fuNrOfGet4PerGdpb) of the Get4 chip of a unique GDPB for current message
+  UInt_t fuGet4Nr = 0;  //! running number (0 to fuNrOfGet4) of the Get4 chip in the system for current message
+
+  /// Starting state book-keeping
+  Double_t fdStartTime = -1.0; //! Time of first valid hit (epoch available), used as reference for evolution plots
+  Double_t fdStartTimeMsSz = 0.0; //! Time of first microslice, used as reference for evolution plots
+  std::chrono::steady_clock::time_point ftStartTimeUnix = std::chrono::steady_clock::
+    now(); //! Time of run Start from UNIX system, used as reference for long evolution plots against reception time
+
+  /// Spill detection
+  Bool_t fbSpillOn            = kTRUE;
+  UInt_t fuCurrentSpillIdx    = 0;
+  Double_t fdStartTimeSpill   = -1.0;
+  Double_t fdLastSecondTime   = -1.0;
+  UInt_t fuCountsLastInterval = 0;
+  UInt_t fuCountsLastSpill = 0;
+  std::vector< ULong64_t > fvuSpillBreakBegTs = {};
+  std::vector< ULong64_t > fvuSpillBreakEndTs = {};
+  std::vector< ULong64_t > fvuSpillBreakMidTs = {};
+
+  /// Histograms related variables
+  UInt_t fuHistoryHistoSize = 3600; /** Size in seconds of the evolution histograms **/
+
+  /// Histograms
+  TH1* fhHitsEvo            = nullptr;
+  TH1* fhHitsPerSpill       = nullptr;
+  TH1* fhSpillBreakDuration = nullptr;
+  TH1* fhSpillDuration      = nullptr;
+
+  /// Canvases
+  /*
+  TCanvas* fcSummary            = nullptr;
+  TCanvas* fcHitMaps            = nullptr;
+  TCanvas* fcGenCntsPerMs       = nullptr;
+  TCanvas* fcSpillCounts        = nullptr;
+  TCanvas* fcSpillCountsHori    = nullptr;
+  TCanvas* fcSpillDpbCountsHori = nullptr;
+*/
+
+  CbmMcbmSpillFindAlgo(const CbmMcbmSpillFindAlgo&);
+  CbmMcbmSpillFindAlgo operator=(const CbmMcbmSpillFindAlgo&);
+
+  ClassDef(CbmMcbmSpillFindAlgo, 1)
+};
+
+#endif
diff --git a/fles/mcbm2018/tasks/CbmMcbmSpillFindTask.cxx b/fles/mcbm2018/tasks/CbmMcbmSpillFindTask.cxx
new file mode 100644
index 00000000..cd2548be
--- /dev/null
+++ b/fles/mcbm2018/tasks/CbmMcbmSpillFindTask.cxx
@@ -0,0 +1,216 @@
+// -----------------------------------------------------------------------------
+// -----                                                                   -----
+// -----                     CbmMcbmSpillFindTask                      -----
+// -----               Created 10.02.2019 by P.-A. Loizeau                 -----
+// -----                                                                   -----
+// -----------------------------------------------------------------------------
+
+#include "CbmMcbmSpillFindTask.h"
+
+#include "CbmMcbm2018TofPar.h"
+#include "CbmMcbmSpillFindAlgo.h"
+
+#include "FairLogger.h"
+#include "FairParGenericSet.h"
+#include "FairRootManager.h"
+#include "FairRun.h"
+#include "FairRunOnline.h"
+#include "FairRuntimeDb.h"
+
+#include "TCanvas.h"
+#include "THttpServer.h"
+#include "TList.h"
+#include "TROOT.h"
+#include "TString.h"
+#include <TFile.h>
+
+#include <chrono>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+
+#include <stdint.h>
+
+Bool_t bMcbmSpillFindResetHistos = kFALSE;
+
+CbmMcbmSpillFindTask::CbmMcbmSpillFindTask()
+  : CbmMcbmUnpack()
+  , fbMonitorMode(kTRUE)
+  , fbDebugMonitorMode(kFALSE)
+  , fuHistoryHistoSize(3600)
+  , fsHistoFileName("data/HistosMonitorT0.root")
+  , fuMinTotPulser(90)
+  , fuMaxTotPulser(100)
+  , fuOffSpillCountLimit(200)
+  , fulTsCounter(0)
+  , fMonitorAlgo(nullptr)
+{
+  fMonitorAlgo = new CbmMcbmSpillFindAlgo();
+}
+
+CbmMcbmSpillFindTask::~CbmMcbmSpillFindTask() { delete fMonitorAlgo; }
+
+Bool_t CbmMcbmSpillFindTask::Init()
+{
+  LOG(info) << "CbmMcbmSpillFindTask::Init";
+  LOG(info) << "Initializing mCBM T0 2019 Monitor";
+
+  return kTRUE;
+}
+
+void CbmMcbmSpillFindTask::SetParContainers()
+{
+  LOG(info) << "Setting parameter containers for " << GetName();
+
+  TList* parCList = fMonitorAlgo->GetParList();
+
+  for (Int_t iparC = 0; iparC < parCList->GetEntries(); ++iparC) {
+    FairParGenericSet* tempObj = (FairParGenericSet*) (parCList->At(iparC));
+    parCList->Remove(tempObj);
+
+    std::string sParamName {tempObj->GetName()};
+    FairParGenericSet* newObj =
+      dynamic_cast<FairParGenericSet*>(FairRun::Instance()->GetRuntimeDb()->getContainer(sParamName.data()));
+
+    if (nullptr == newObj) {
+      LOG(error) << "Failed to obtain parameter container " << sParamName << ", for parameter index " << iparC;
+      return;
+    }  // if( nullptr == newObj )
+
+    parCList->AddAt(newObj, iparC);
+    //      delete tempObj;
+  }  // for( Int_t iparC = 0; iparC < parCList->GetEntries(); ++iparC )
+}
+
+Bool_t CbmMcbmSpillFindTask::InitContainers()
+{
+  LOG(info) << "Init parameter containers for " << GetName();
+
+  /// Control flags
+  CbmMcbm2018TofPar* pUnpackPar =
+    dynamic_cast<CbmMcbm2018TofPar*>(FairRun::Instance()->GetRuntimeDb()->getContainer("CbmMcbm2018TofPar"));
+  if (nullptr == pUnpackPar) {
+    LOG(error) << "Failed to obtain parameter container CbmMcbm2018TofPar";
+    return kFALSE;
+  }  // if( nullptr == pUnpackPar )
+
+  Bool_t initOK = fMonitorAlgo->InitContainers();
+
+  /// Transfer parameter values set from calling macro
+  fMonitorAlgo->SetMonitorMode(fbMonitorMode);
+  fMonitorAlgo->SetHistoryHistoSize(fuHistoryHistoSize);
+  fMonitorAlgo->SetPulserTotLimits(fuMinTotPulser, fuMaxTotPulser);
+  fMonitorAlgo->SetSpillThreshold(fuOffSpillCountLimit);
+  fMonitorAlgo->SetSpillCheckIntervalSec(fdSpillCheckInterval);
+
+  /// Histos creation, obtain pointer on them and add them to the HTTP server
+  /// 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();
+
+  /// Register the histos in the HTTP server
+  THttpServer* server = FairRunOnline::Instance()->GetHttpServer();
+  if (nullptr != server) {
+    for (UInt_t uHisto = 0; uHisto < vHistos.size(); ++uHisto) {
+      //         LOG(info) << "Registering  " << vHistos[ uHisto ].first->GetName()
+      //                   << " in " << vHistos[ uHisto ].second.data();
+      server->Register(Form("/%s", vHistos[uHisto].second.data()), vHistos[uHisto].first);
+    }  // for( UInt_t uHisto = 0; uHisto < vHistos.size(); ++uHisto )
+
+    for (UInt_t uCanv = 0; uCanv < vCanvases.size(); ++uCanv) {
+      //         LOG(info) << "Registering  " << vCanvases[ uCanv ].first->GetName()
+      //                   << " in " << vCanvases[ uCanv ].second.data();
+      server->Register(Form("/%s", vCanvases[uCanv].second.data()),
+                       gROOT->FindObject((vCanvases[uCanv].first)->GetName()));
+    }  //  for( UInt_t uCanv = 0; uCanv < vCanvases.size(); ++uCanv )
+
+    server->RegisterCommand("/Reset_MoniT0_Hist", "bMcbmSpillFindResetHistos=kTRUE");
+    server->Restrict("/Reset_MoniT0_Hist", "allow=admin");
+  }  // if( nullptr != server )
+
+  return initOK;
+}
+
+Bool_t CbmMcbmSpillFindTask::ReInitContainers()
+{
+  LOG(info) << "ReInit parameter containers for " << GetName();
+  Bool_t initOK = fMonitorAlgo->ReInitContainers();
+
+  return initOK;
+}
+
+void CbmMcbmSpillFindTask::AddMsComponentToList(size_t component, UShort_t usDetectorId)
+{
+  fMonitorAlgo->AddMsComponentToList(component, usDetectorId);
+}
+
+Bool_t CbmMcbmSpillFindTask::DoUnpack(const fles::Timeslice& ts, size_t /*component*/)
+{
+  if (fbMonitorMode && bMcbmSpillFindResetHistos) {
+    LOG(info) << "Reset T0 Monitor histos ";
+    fMonitorAlgo->ResetHistograms();
+    bMcbmSpillFindResetHistos = kFALSE;
+  }  // if( fbMonitorMode && bMcbmSpillFindResetHistos )
+
+  if (kFALSE == fMonitorAlgo->ProcessTs(ts)) {
+    LOG(error) << "Failed processing TS " << ts.index() << " in unpacker algorithm class";
+    return kTRUE;
+  }  // if( kFALSE == fMonitorAlgo->ProcessTs( ts ) )
+
+  /// Cleqr the digis vector in case it was filled
+  std::vector<CbmTofDigi> vDigi = fMonitorAlgo->GetVector();
+  fMonitorAlgo->ClearVector();
+
+  if (0 == fulTsCounter % 1000) LOG(info) << "Processed " << fulTsCounter << "TS";
+  fulTsCounter++;
+
+  return kTRUE;
+}
+
+void CbmMcbmSpillFindTask::Reset() {}
+
+void CbmMcbmSpillFindTask::Finish()
+{
+  fMonitorAlgo->Finish();
+
+  /// Obtain vector of pointers on each histo from the algo (+ optionally desired folder)
+  std::vector<std::pair<TNamed*, std::string>> vHistos = fMonitorAlgo->GetHistoVector();
+
+  /// Save old global file and folder pointer to avoid messing with FairRoot
+  TFile* oldFile     = gFile;
+  TDirectory* oldDir = gDirectory;
+
+  TFile* histoFile = nullptr;
+
+  // open separate histo file in recreate mode
+  histoFile = new TFile(fsHistoFileName, "RECREATE");
+  histoFile->cd();
+
+  /// Save the histograms in a file
+  for (UInt_t uHisto = 0; uHisto < vHistos.size(); ++uHisto) {
+    /// Make sure we end up in chosen folder
+    TString sFolder = vHistos[uHisto].second.data();
+    if (nullptr == gDirectory->Get(sFolder)) gDirectory->mkdir(sFolder);
+    gDirectory->cd(sFolder);
+
+    /// Write plot
+    vHistos[uHisto].first->Write();
+
+    histoFile->cd();
+  }  // for( UInt_t uHisto = 0; uHisto < vHistos.size(); ++uHisto )
+
+  /// Restore old global file and folder pointer to avoid messing with FairRoot
+  gFile      = oldFile;
+  gDirectory = oldDir;
+
+  histoFile->Close();
+}
+
+void CbmMcbmSpillFindTask::SetIgnoreOverlapMs(Bool_t bFlagIn) { fMonitorAlgo->SetIgnoreOverlapMs(bFlagIn); }
+
+
+ClassImp(CbmMcbmSpillFindTask)
diff --git a/fles/mcbm2018/tasks/CbmMcbmSpillFindTask.h b/fles/mcbm2018/tasks/CbmMcbmSpillFindTask.h
new file mode 100644
index 00000000..45d63988
--- /dev/null
+++ b/fles/mcbm2018/tasks/CbmMcbmSpillFindTask.h
@@ -0,0 +1,77 @@
+// -----------------------------------------------------------------------------
+// -----                                                                   -----
+// -----                     CbmMcbmSpillFindTaskzz                      -----
+// -----               Created 10.02.2019 by P.-A. Loizeau                 -----
+// -----                                                                   -----
+// -----------------------------------------------------------------------------
+
+#ifndef CbmMcbmSpillFindTask_H
+#define CbmMcbmSpillFindTask_H
+
+#include "CbmMcbmSpillFindAlgo.h"
+#include "CbmMcbmUnpack.h"
+
+#include "Timeslice.hpp"
+
+#include "TString.h"
+
+class CbmMcbmSpillFindTask : public CbmMcbmUnpack {
+public:
+  CbmMcbmSpillFindTask();
+  CbmMcbmSpillFindTask(const CbmMcbmSpillFindTask&) = delete;
+  CbmMcbmSpillFindTask operator=(const CbmMcbmSpillFindTask&) = delete;
+
+  virtual ~CbmMcbmSpillFindTask();
+
+  virtual Bool_t Init();
+  virtual Bool_t DoUnpack(const fles::Timeslice& ts, size_t component);
+  virtual void Reset();
+
+  virtual void Finish();
+
+  void SetParContainers();
+
+  Bool_t InitContainers();
+
+  Bool_t ReInitContainers();
+
+  /// Temp until we change from CbmMcbmUnpack to something else
+  void AddMsComponentToList(size_t component, UShort_t usDetectorId);
+  void SetNbMsInTs(size_t /*uCoreMsNb*/, size_t /*uOverlapMsNb*/) {};
+
+  /// Algo settings setters
+  inline void SetMonitorMode(Bool_t bFlagIn = kTRUE) { fbMonitorMode = bFlagIn; }
+  void SetIgnoreOverlapMs(Bool_t bFlagIn = kTRUE);
+  inline void SetHistoryHistoSize(UInt_t inHistorySizeSec = 1800) { fuHistoryHistoSize = inHistorySizeSec; }
+  inline void SetHistoFilename(TString sNameIn) { fsHistoFileName = sNameIn; }
+  inline void SetPulserTotLimits(UInt_t uMin, UInt_t uMax)
+  {
+    fuMinTotPulser = uMin;
+    fuMaxTotPulser = uMax;
+  }
+  inline void SetSpillThreshold(UInt_t uCntLimit) { fuOffSpillCountLimit = uCntLimit; }
+  inline void SetSpillCheckIntervalSec(Double_t dInterval) { fdSpillCheckInterval = dInterval; }
+
+private:
+  /// Control flags
+  Bool_t fbMonitorMode;       //! Switch ON the filling of a minimal set of histograms
+  Bool_t fbDebugMonitorMode;  //! Switch ON the filling of a additional set of histograms
+
+  /// User settings parameters
+  UInt_t fuHistoryHistoSize;
+  TString fsHistoFileName;
+  UInt_t fuMinTotPulser;
+  UInt_t fuMaxTotPulser;
+  UInt_t fuOffSpillCountLimit;
+  Double_t fdSpillCheckInterval = 0.5;
+
+  /// Statistics & first TS rejection
+  uint64_t fulTsCounter;
+
+  /// Processing algo
+  CbmMcbmSpillFindAlgo* fMonitorAlgo;
+
+  ClassDef(CbmMcbmSpillFindTask, 1)
+};
+
+#endif
diff --git a/macro/beamtime/mcbm2020/find_spills.C b/macro/beamtime/mcbm2020/find_spills.C
new file mode 100644
index 00000000..388fce1e
--- /dev/null
+++ b/macro/beamtime/mcbm2020/find_spills.C
@@ -0,0 +1,118 @@
+/** @file MCBM spill detection with T0
+ ** @author Pierre-Alain Loizeau <p.-a.loizeau@gsi.de>
+ ** @date 16.02.2021
+ ** ROOT macro to read tsa files which have been produced in mCBM and use the T0 detector to
+ ** find the spill breaks beginning, middle and end TS index
+ */
+// In order to call later Finish, we make this global
+FairRunOnline* run = NULL;
+
+Bool_t find_spills(TString inFile       = "",
+                   UInt_t uRunId        = 0,
+                   TString sOutDir      = "data",
+                   TString sHostname        = "localhost",
+                   Int_t iServerHttpPort    = 8080,
+                   Int_t iServerRefreshRate = 100)
+{
+  TString srcDir = gSystem->Getenv("VMCWORKDIR");
+
+  // --- Specify number of events to be produced.
+  // --- -1 means run until the end of the input file.
+  Int_t nEvents = -1;
+  // --- Specify output file name (this is just an example)
+  TString runId   = TString::Format("%u", uRunId);
+  TString parFile = "data/spill_finder_params_" + runId + ".root";
+
+  // --- Set log output levels
+  FairLogger::GetLogger();
+  gLogger->SetLogScreenLevel("INFO");
+  //gLogger->SetLogScreenLevel("DEBUG");
+  gLogger->SetLogVerbosityLevel("MEDIUM");
+
+  // --- Define parameter files
+  TList* parFileList = new TList();
+  TString paramDir   = srcDir + "/macro/beamtime/mcbm2020/";
+
+  TString paramFileTof       = paramDir + "mT0Par.par";
+  TObjString* parTofFileName = new TObjString(paramFileTof);
+  parFileList->Add(parTofFileName);
+
+  // --- Set debug level
+  gDebug = 0;
+
+  std::cout << std::endl;
+
+  // ========================================================================
+  // ========================================================================
+  std::cout << std::endl;
+  std::cout << ">>> SpillFinder: Initialising..." << std::endl;
+  CbmMcbmSpillFindTask* spill_finder_t0 = new CbmMcbmSpillFindTask();
+
+  spill_finder_t0->SetIgnoreOverlapMs();
+  if (0 < uRunId)
+    spill_finder_t0->SetHistoFilename( Form("data/HistosSpillFinder_%03u.root", uRunId));
+  spill_finder_t0->SetPulserTotLimits(180, 210);  // for runs  >  86
+  spill_finder_t0->SetSpillCheckIntervalSec( 0.05 ); // ~every 4 TS
+  spill_finder_t0->SetSpillThreshold( 40 ); // ~10 hits per TS
+
+  // --- Source task
+  CbmMcbm2018Source* source = new CbmMcbm2018Source();
+
+  if ("" != inFile) {
+    source->SetFileName(inFile);
+  }  // if( "" != inFile )
+  else {
+    source->SetHostName(sHostname);
+  }  // else of if( "" != inFile )
+
+  // Use kHodo since there is no entry for T0 in the enum yet
+  source->AddUnpacker(spill_finder_t0, 0x90, ECbmModuleId::kHodo);  //gDPB T0
+
+  source->SetSubscriberHwm(1000);
+
+  // --- Run
+  run = new FairRunOnline(source);
+  run->ActivateHttpServer(iServerRefreshRate,
+                          iServerHttpPort);  // refresh each 100 events
+  /// To avoid the server sucking all Histos from gROOT when no output file is used
+  /// ===> Need to explicitely add the canvases to the server in the task!
+  run->GetHttpServer()->GetSniffer()->SetScanGlobalDir(kFALSE);
+  run->SetAutoFinish(kFALSE);
+
+
+  // -----   Runtime database   ---------------------------------------------
+  FairRuntimeDb* rtdb       = run->GetRuntimeDb();
+  FairParAsciiFileIo* parIn = new FairParAsciiFileIo();
+  parIn->open(parFileList, "in");
+  rtdb->setFirstInput(parIn);
+
+  run->Init();
+
+  // --- Start run
+  TStopwatch timer;
+  timer.Start();
+  std::cout << ">>> SpillFinder: Starting run..." << std::endl;
+  run->Run(-1, 0);  // process  full file(s)!
+
+  run->Finish();
+
+  timer.Stop();
+
+  std::cout << "Processed " << std::dec << source->GetTsCount() << " timeslices"
+            << std::endl;
+
+  // --- End-of-run info
+  Double_t rtime = timer.RealTime();
+  Double_t ctime = timer.CpuTime();
+  std::cout << std::endl << std::endl;
+  std::cout << ">>> SpillFinder: Macro finished successfully." << std::endl;
+  std::cout << ">>> SpillFinder: Real time " << rtime << " s, CPU time " << ctime
+            << " s" << std::endl;
+  std::cout << std::endl;
+
+  /// --- Screen output for automatic tests
+  std::cout << " Test passed" << std::endl;
+  std::cout << " All ok " << std::endl;
+
+  return kTRUE;
+}
diff --git a/macro/beamtime/mcbm2020/find_spills_kronos.C b/macro/beamtime/mcbm2020/find_spills_kronos.C
new file mode 100644
index 00000000..138396fb
--- /dev/null
+++ b/macro/beamtime/mcbm2020/find_spills_kronos.C
@@ -0,0 +1,62 @@
+/** @file MCBM DATA unpacking
+ ** @author Florian Uhlig <f.uhlig@gsi.de>
+ ** @date 20.06.2016
+ ** Modified by P.-A. Loizeau
+ ** @date 30.01.2019
+ ** ROOT macro to read tsa files which have been produced with the new data transport
+ ** Convert data into cbmroot format.
+ ** Uses CbmMcbm2018Source as source task.
+ */
+#include "find_spills.C"
+
+/// FIXME: Disable clang formatting to keep easy parameters overview
+/* clang-format off */
+Bool_t find_spills_kronos(UInt_t uRunIdx       = 28,
+                          TString sOutDir      = "data")
+{
+  /// FIXME: Re-enable clang formatting after parameters initial values setting
+  /* clang-format on */
+
+  UInt_t uRunId = 0;
+  if (99999 != uRunIdx) {
+    std::vector<UInt_t> vuListRunId = {
+      692, 698, 702, 704, 705, 706, 707,            //  7 =>  0 -  6
+      744, 750, 759, 760, 761, 762, 799,            //  7 =>  7 - 13
+      811, 812, 816, 817, 819,                      //  5 => 14 - 18
+      820, 821, 822, 824, 826, 827, 828, 829,       //  8 => 19 - 26
+      830, 831, 836,                                //  3 => 27 - 29
+      841, 846, 849,                                //  3 => 30 - 32
+      850, 851, 852, 854, 855, 856, 857, 858, 859,  //  9 => 33 - 41
+      860, 861, 862, 863, 864, 865, 866             //  7 => 42 - 48
+                                                    /*
+ /// With runs < 1 min due to missmatch!
+             811, 812, 816, 817, 818, 819,                     //  6 => 14 - 19
+             820, 821, 822, 824, 826, 827, 828, 829,           //  8 => 20 - 27
+             830, 831, 836, 839,                               //  4 => 28 - 31
+             840, 841, 842, 844, 845, 846, 848, 849,           //  8 => 32 - 39
+             850, 851, 852, 854, 855, 856, 857, 858, 859,      //  9 => 40 - 48
+             860, 861, 862, 863, 864, 865, 866                 //  7 => 49 - 55
+*/
+    };
+    if (vuListRunId.size() <= uRunIdx) {
+      std::cout << "Run index is out of range, not doing anything" << std::endl;
+      return kFALSE;
+    }
+    uRunId = vuListRunId[uRunIdx];
+  }  // if( 99999 != uRunIdx )
+
+  if (uRunId < 692) return kFALSE;
+
+  TString inFile = Form("/lustre/cbm/users/ploizeau/mcbm2020/data/%3u_pn02_*.tsa;", uRunId);
+  inFile += Form("/lustre/cbm/users/ploizeau/mcbm2020/data/%3u_pn04_*.tsa;", uRunId);
+  inFile += Form("/lustre/cbm/users/ploizeau/mcbm2020/data/%3u_pn05_*.tsa;", uRunId);
+  inFile += Form("/lustre/cbm/users/ploizeau/mcbm2020/data/%3u_pn06_*.tsa;", uRunId);
+  inFile += Form("/lustre/cbm/users/ploizeau/mcbm2020/data/%3u_pn08_*.tsa;", uRunId);
+  inFile += Form("/lustre/cbm/users/ploizeau/mcbm2020/data/%3u_pn10_*.tsa;", uRunId);
+  inFile += Form("/lustre/cbm/users/ploizeau/mcbm2020/data/%3u_pn11_*.tsa;", uRunId);
+  inFile += Form("/lustre/cbm/users/ploizeau/mcbm2020/data/%3u_pn12_*.tsa;", uRunId);
+  inFile += Form("/lustre/cbm/users/ploizeau/mcbm2020/data/%3u_pn13_*.tsa;", uRunId);
+  inFile += Form("/lustre/cbm/users/ploizeau/mcbm2020/data/%3u_pn15_*.tsa", uRunId);
+
+  return find_spills(inFile, uRunId, sOutDir);
+}
-- 
GitLab