Skip to content
Snippets Groups Projects
CbmDeviceUnpackTofMcbm2018.cxx 49.01 KiB
/* Copyright (C) 2018-2020 PI-UHd, GSI
   SPDX-License-Identifier: GPL-3.0-only
   Authors: Florian Uhlig, Norbert Herrmann [committer] */

/**
 * CbmDeviceUnpackTofMcbm2018.cxx
 *
 * @since 2018-04-24
 * @author F. Uhlig
 */

#include "CbmDeviceUnpackTofMcbm2018.h"

#include "CbmDefs.h"
#include "CbmMQDefs.h"
#include "CbmMcbm2018TofPar.h"
#include "CbmMcbm2018UnpackerAlgoTof.h"
//#include "CbmHistManager.h"
#include "CbmTbDaqBuffer.h"
#include "CbmTofAddress.h"
#include "CbmTofDetectorId_v21a.h"  // in cbmdata/tof
#include "CbmTofDigi.h"

#include "StorableTimeslice.hpp"

#include "FairMQLogger.h"
#include "FairMQProgOptions.h"  // device->fConfig
#include "FairParGenericSet.h"
#include "FairRuntimeDb.h"

#include "TH1.h"
#include "TH2.h"

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>

// include this header to serialize vectors
#include <boost/serialization/vector.hpp>

#include <array>
#include <iomanip>
#include <stdexcept>
#include <string>
struct InitTaskError : std::runtime_error {
  using std::runtime_error::runtime_error;
};

using namespace std;

//static Int_t iMess=0;
//const Int_t DetMask = 0x001FFFFF;

CbmDeviceUnpackTofMcbm2018::CbmDeviceUnpackTofMcbm2018()
  : fNumMessages(0)
  , fiSelectComponents(0)
  , fNumTint(0)
  , fEventHeader()
  , fiReqMode(0)
  , fiReqTint(0)
  , fiReqBeam(-1)
  , fiReqDigiAddr()
  , fiPulserMode(0)
  , fiPulMulMin(0)
  , fiPulTotMin(0)
  , fiPulTotMax(1000)
  , fuTotalMsNb(0)
  , fuOverlapMsNb(0)
  , fuCoreMs(0)
  , fdMsSizeInNs(0)
  , fdTsCoreSizeInNs(0)
  , fuMinNbGdpb(0)
  , fuNrOfGdpbs(0)
  , fuNrOfFeePerGdpb(0)
  , fuNrOfGet4PerFee(0)
  , fuNrOfChannelsPerGet4(0)
  , fuNrOfChannelsPerFee(0)
  , fuNrOfGet4(0)
  , fuNrOfGet4PerGdpb(0)
  , fuNrOfChannelsPerGdpb(0)
  , fuGdpbId(0)
  , fuGdpbNr(0)
  , fuGet4Id(0)
  , fuGet4Nr(0)
  , fMsgCounter(11, 0)  // length of enum MessageTypes initialized with 0
  , fGdpbIdIndexMap()
  , fvulCurrentEpoch()
  , fvbFirstEpochSeen()
  , fNofEpochs(0)
  , fulCurrentEpochTime(0.)
  //, fdMsIndex(0.)
  , fdToffTof(0.)
  , fiAddrRef(0)
  //, fuDiamondDpbIdx(3)
  //, fbEpochSuppModeOn( kTRUE )
  //, fbGet4M24b( kFALSE )
  //, fbGet4v20( kTRUE )
  //, fbMergedEpochsOn( kTRUE )
  , fUnpackPar(nullptr)
  , fdLastDigiTime(0.)
  , fdFirstDigiTimeDif(0.)
  //, fdEvTime0(0.)
  , fhRawTDigEvBmon(nullptr)
  , fhRawTDigRef0(nullptr)
  , fhRawTDigRef(nullptr)
  , fhRawTRefDig0(nullptr)
  , fhRawTRefDig1(nullptr)
  , fhRawDigiLastDigi(nullptr)
  , fhRawTotCh()
  , fhChCount()
  , fvbChanThere()
  , fhChanCoinc()
  , fhDetChanCoinc(nullptr)
  , fvuPadiToGet4()
  , fvuGet4ToPadi()
  , fvuElinkToGet4()
  , fvuGet4ToElink()
  , fviRpcType()
  , fviModuleId()
  , fviNrOfRpc()
  , fviRpcSide()
  , fviRpcChUId()
  , fBuffer(CbmTbDaqBuffer::Instance())
  , fUnpackerAlgo(nullptr)
{
  fUnpackerAlgo = new CbmMcbm2018UnpackerAlgoTof();
}

CbmDeviceUnpackTofMcbm2018::~CbmDeviceUnpackTofMcbm2018() { delete fUnpackerAlgo; }

void CbmDeviceUnpackTofMcbm2018::InitTask()
try {
  // 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 (!IsChannelNameAllowed(entry.first)) throw InitTaskError("Channel name does not match.");
    if (entry.first == "syscmd") {
      OnData(entry.first, &CbmDeviceUnpackTofMcbm2018::HandleMessage);
      continue;
    }
    //if(entry.first != "tofdigis") OnData(entry.first, &CbmDeviceUnpackTofMcbm2018::HandleData);
    if (entry.first != "tofdigis") OnData(entry.first, &CbmDeviceUnpackTofMcbm2018::HandleParts);
    else {
      fChannelsToSend[0].push_back(entry.first);
      LOG(info) << "Init to send data to channel " << fChannelsToSend[0][0];
    }
  }
  InitContainers();
  const Int_t iHeaderSize = 4;
  fEventHeader.resize(iHeaderSize);  // define size of eventheader int[]
  for (int i = 0; i < iHeaderSize; i++)
    fEventHeader[i] = 0;
  LOG(info) << "Read config";
  fiSelectComponents = fConfig->GetValue<uint64_t>("SelectComponents");
  fiReqMode          = fConfig->GetValue<uint64_t>("ReqMode");
  fiReqTint          = fConfig->GetValue<uint64_t>("ReqTint");
  fiReqBeam          = fConfig->GetValue<uint64_t>("ReqBeam");
  fiPulserMode       = fConfig->GetValue<int64_t>("PulserMode");
  fiPulMulMin        = fConfig->GetValue<uint64_t>("PulMulMin");
  fiPulTotMin        = fConfig->GetValue<uint64_t>("PulTotMin");
  fiPulTotMax        = fConfig->GetValue<uint64_t>("PulTotMax");
  fdToffTof          = fConfig->GetValue<double_t>("ToffTof");
  Int_t iRefModType  = fConfig->GetValue<int64_t>("RefModType");
  Int_t iRefModId    = fConfig->GetValue<int64_t>("RefModId");
  Int_t iRefCtrType  = fConfig->GetValue<int64_t>("RefCtrType");
  Int_t iRefCtrId    = fConfig->GetValue<int64_t>("RefCtrId");
  if (iRefModType > -1)
    fiAddrRef = CbmTofAddress::GetUniqueAddress(iRefModId, iRefCtrId, 0, 0, iRefModType, iRefCtrType);
  LOG(info) << " Using Reference counter address 0x" << std::hex << fiAddrRef;

  //    Int_t iMaxAsicInactive = fConfig->GetValue<uint64_t>("MaxAsicInactive");
  //    fUnpackerAlgo->SetMaxAsicInactive( iMaxAsicInactive );
  Int_t iReqDet       = 1;
  Int_t iNReq         = 0;
  const Int_t iMaxReq = 50;

  while (iNReq < iMaxReq) {  // FIXME, setup parameter hardwired!
    iReqDet = fConfig->GetValue<uint64_t>(Form("ReqDet%d", iNReq));
    if (iReqDet == 0) break;
    AddReqDigiAddr(iReqDet);
    iNReq++;
  }
  LOG(info) << "Setup request";
  if (fiReqMode > 0)
    if (iNReq == 0) {  // take all defined detectors
      for (UInt_t iGbtx = 0; iGbtx < fviNrOfRpc.size(); iGbtx++) {
        switch (fviRpcType[iGbtx]) {
          case 0:  // mTof modules
          case 1:  // eTof modules
            if (iGbtx % 2 == 0)
              for (Int_t iRpc = 0; iRpc < fviNrOfRpc[iGbtx]; iRpc++) {
                Int_t iAddr = CbmTofAddress::GetUniqueAddress(fviModuleId[iGbtx], iRpc, 0, 0, fviRpcType[iGbtx]);
                AddReqDigiAddr(iAddr);
              }
            break;

          case 4:
          case 9:  // HD 2-RPC boxes
            for (Int_t iRpc = 0; iRpc < 2; iRpc++) {
              Int_t iAddr = CbmTofAddress::GetUniqueAddress(fviModuleId[iGbtx], iRpc, 0, 0, fviRpcType[iGbtx]);
              AddReqDigiAddr(iAddr);
            }
            break;
          case 6:  // Buc box
            for (Int_t iRpc = 0; iRpc < 2; iRpc++) {
              Int_t iAddr = CbmTofAddress::GetUniqueAddress(fviModuleId[iGbtx], iRpc, 0, 0, fviRpcType[iGbtx]);
              AddReqDigiAddr(iAddr);
            }
            break;

          case 7:  // CERN box
            for (Int_t iRpc = 0; iRpc < 1; iRpc++) {
              Int_t iAddr = CbmTofAddress::GetUniqueAddress(fviModuleId[iGbtx], iRpc, 0, 0, 7);
              AddReqDigiAddr(iAddr);
            }
            break;
          case 8:  // ceramics
            for (Int_t iRpc = 0; iRpc < 8; iRpc++) {
              Int_t iAddr = CbmTofAddress::GetUniqueAddress(fviModuleId[iGbtx], iRpc, 0, 0, fviRpcType[iGbtx]);
              AddReqDigiAddr(iAddr);
            }
            break;
          case 5:  // add Diamond, single cell RPC
            Int_t iAddr = CbmTofAddress::GetUniqueAddress(fviModuleId[iGbtx], 0, 0, 0, 5);
            AddReqDigiAddr(iAddr);
            break;
        }
      }
    }

  LOG(info) << "ReqMode " << fiReqMode << " in " << fiReqTint << " ns "
            << " with " << fiReqDigiAddr.size() << " detectors out of " << fviNrOfRpc.size() << " GBTx, PulserMode "
            << fiPulserMode << " with Mul " << fiPulMulMin << ", TotMin " << fiPulTotMin;
  LOG(info) << Form("ReqBeam 0x%08x", (uint) fiReqBeam);
}
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 CbmDeviceUnpackTofMcbm2018::IsChannelNameAllowed(std::string channelName)
{
  for (auto const& entry : fAllowedChannels) {
    LOG(info) << "Inspect " << entry;
    std::size_t pos1 = channelName.find(entry);
    if (pos1 != std::string::npos) {
      const vector<std::string>::const_iterator pos =
        std::find(fAllowedChannels.begin(), fAllowedChannels.end(), entry);
      const vector<std::string>::size_type idx = pos - fAllowedChannels.begin();
      LOG(info) << "Found " << entry << " in " << channelName;
      LOG(info) << "Channel name " << channelName << " found in list of allowed channel names at position " << idx;
      return true;
    }
  }
  LOG(info) << "Channel name " << channelName << " not found in list of allowed channel names.";
  LOG(error) << "Stop device.";
  return false;
}

Bool_t CbmDeviceUnpackTofMcbm2018::InitContainers()
{
  LOG(info) << "Init parameter containers for CbmDeviceUnpackTofMcbm2018.";
  //  FairRuntimeDb* fRtdb = FairRuntimeDb::instance();

  // 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
  std::string message {"CbmMcbm2018TofPar,111"};
  LOG(info) << "Requesting parameter container CbmMcbm2018TofPar, sending message: " << message;
  FairMQMessagePtr req(NewSimpleMessage("CbmMcbm2018TofPar,111"));
  FairMQMessagePtr rep(NewMessage());

  if (Send(req, "parameters") > 0) {
    if (Receive(rep, "parameters") >= 0) {
      if (rep->GetSize() != 0) {
        CbmMqTMessage tmsg(rep->GetData(), rep->GetSize());
        fUnpackPar = dynamic_cast<CbmMcbm2018TofPar*>(tmsg.ReadObject(tmsg.GetClass()));
        LOG(info) << "Received unpack parameter from parmq server: " << fUnpackPar;
        fUnpackPar->Print();
      }
      else {
        LOG(error) << "Received empty reply. Parameter not available";
      }
    }
  }


  SetParContainers();

  Bool_t initOK = kTRUE;
  initOK &= fUnpackerAlgo->InitContainers();
  initOK &= ReInitContainers();  // needed for TInt parameters

  //  CreateHistograms();
  initOK &= fUnpackerAlgo->CreateHistograms();

  fvulCurrentEpoch.resize(fuNrOfGdpbs * fuNrOfGet4PerGdpb);
  fvbFirstEpochSeen.resize(fuNrOfGdpbs * fuNrOfGet4PerGdpb);
  fvbChanThere.resize(fviRpcChUId.size(), kFALSE);
  for (UInt_t i = 0; i < fuNrOfGdpbs; ++i) {
    for (UInt_t j = 0; j < fuNrOfGet4PerGdpb; ++j) {
      fvulCurrentEpoch[GetArrayIndex(i, j)]  = 0;
      fvbFirstEpochSeen[GetArrayIndex(i, j)] = kFALSE;
    }  // for( UInt_t j = 0; j < fuNrOfGet4PerGdpb; ++j )
  }    // for( UInt_t i = 0; i < fuNrOfGdpbs; ++i )

  fNumTint = 0;
  return initOK;
}

void CbmDeviceUnpackTofMcbm2018::SetParContainers()
{
  FairRuntimeDb* fRtdb = FairRuntimeDb::instance();

  TList* fParCList = fUnpackerAlgo->GetParList();

  LOG(info) << "Setting parameter containers for " << fParCList->GetEntries() << " entries ";

  for (Int_t iparC = 0; iparC < fParCList->GetEntries(); ++iparC) {
    FairParGenericSet* tempObj = (FairParGenericSet*) (fParCList->At(iparC));
    fParCList->Remove(tempObj);

    std::string sParamName {tempObj->GetName()};

    FairParGenericSet* newObj = dynamic_cast<FairParGenericSet*>(fRtdb->getContainer(sParamName.data()));
    LOG(info) << " - Get " << sParamName.data() << " at " << newObj;
    if (nullptr == newObj) {

      LOG(error) << "Failed to obtain parameter container " << sParamName << ", for parameter index " << iparC;
      return;
    }  // if( nullptr == newObj )
    if (iparC == 0) {
      newObj = (FairParGenericSet*) fUnpackPar;
      LOG(info) << " - Mod " << sParamName.data() << " to " << newObj;
    }
    fParCList->AddAt(newObj, iparC);
    //      delete tempObj;
  }  // for( Int_t iparC = 0; iparC < fParCList->GetEntries(); ++iparC )
}

void CbmDeviceUnpackTofMcbm2018::AddMsComponentToList(size_t component, UShort_t usDetectorId)
{
  fUnpackerAlgo->AddMsComponentToList(component, usDetectorId);
}

Bool_t CbmDeviceUnpackTofMcbm2018::ReInitContainers()
{
  LOG(info) << "ReInit parameter containers for CbmDeviceUnpackMcbm2018TofPar.";

  fuNrOfGdpbs = fUnpackPar->GetNrOfGdpbs();
  LOG(info) << "Nr. of Tof GDPBs: " << fuNrOfGdpbs;
  fuMinNbGdpb = 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;

  /// TODO: move these constants somewhere shared, e.g the parameter file
  fvuPadiToGet4.resize(fuNrOfChannelsPerFee);
  fvuGet4ToPadi.resize(fuNrOfChannelsPerFee);
  /* source: Monitor
   UInt_t uGet4topadi[32] = {
       4,  3,  2,  1,  // provided by Jochen
      24, 23, 22, 21,
       8,  7,  6,  5,
      28, 27, 26, 25,
      12, 11, 10,  9,
      32, 31, 30, 29,
      16, 15, 14, 13,
      20, 19, 18, 17 };
   */

  UInt_t uGet4topadi[32] = {4,  3,  2,  1,  // provided by Jochen
                            8,  7,  6,  5,  12, 11, 10, 9,  16, 15, 14, 13, 20, 19,
                            18, 17, 24, 23, 22, 21, 28, 27, 26, 25, 32, 31, 30, 29};

  UInt_t uPaditoget4[32] = {4,  3,  2,  1,  // provided by Jochen
                            12, 11, 10, 9, 20, 19, 18, 17, 28, 27, 26, 25, 32, 31,
                            30, 29, 8,  7, 6,  5,  16, 15, 14, 13, 24, 23, 22, 21};

  for (UInt_t uChan = 0; uChan < fuNrOfChannelsPerFee; ++uChan) {
    fvuPadiToGet4[uChan] = uPaditoget4[uChan] - 1;
    fvuGet4ToPadi[uChan] = uGet4topadi[uChan] - 1;
  }  // for( UInt_t uChan = 0; uChan < fuNrOfChannelsPerFee; ++uChan )

  /// TODO: move these constants somewhere shared, e.g the parameter file
  fvuElinkToGet4.resize(kuNbGet4PerGbtx);
  fvuGet4ToElink.resize(kuNbGet4PerGbtx);
  UInt_t kuElinkToGet4[kuNbGet4PerGbtx] = {27, 2,  7,  3,  31, 26, 30, 1,  33, 37, 32, 13, 9,  14,
                                           10, 15, 17, 21, 16, 35, 34, 38, 25, 24, 0,  6,  20, 23,
                                           18, 22, 28, 4,  29, 5,  19, 36, 39, 8,  12, 11};
  UInt_t kuGet4ToElink[kuNbGet4PerGbtx] = {24, 7,  1,  3,  31, 33, 25, 2,  37, 12, 14, 39, 38, 11,
                                           13, 15, 18, 16, 28, 34, 26, 17, 29, 27, 23, 22, 5,  0,
                                           30, 32, 6,  4,  10, 8,  20, 19, 35, 9,  21, 36};
  for (UInt_t uLinkAsic = 0; uLinkAsic < kuNbGet4PerGbtx; ++uLinkAsic) {
    fvuElinkToGet4[uLinkAsic] = kuElinkToGet4[uLinkAsic];
    fvuGet4ToElink[uLinkAsic] = kuGet4ToElink[uLinkAsic];
  }  // for( UInt_t uChan = 0; uChan < fuNrOfChannelsPerFee; ++uChan )

  UInt_t uNrOfGbtx = fUnpackPar->GetNrOfGbtx();
  fviRpcType.resize(uNrOfGbtx);
  fviModuleId.resize(uNrOfGbtx);
  fviNrOfRpc.resize(uNrOfGbtx);
  fviRpcSide.resize(uNrOfGbtx);

  for (UInt_t iGbtx = 0; iGbtx < uNrOfGbtx; ++iGbtx) {
    fviNrOfRpc[iGbtx]  = fUnpackPar->GetNrOfRpc(iGbtx);
    fviRpcType[iGbtx]  = fUnpackPar->GetRpcType(iGbtx);
    fviRpcSide[iGbtx]  = fUnpackPar->GetRpcSide(iGbtx);
    fviModuleId[iGbtx] = fUnpackPar->GetModuleId(iGbtx);
  }

  UInt_t uNrOfChannels = fuNrOfGet4 * fuNrOfChannelsPerGet4;
  LOG(info) << "Nr. of possible Tof channels: " << uNrOfChannels;

  //   CbmTofDetectorId* fTofId = new CbmTofDetectorId_v14a();
  fviRpcChUId.resize(uNrOfChannels);
  UInt_t iCh = 0;
  for (UInt_t iGbtx = 0; iGbtx < uNrOfGbtx; iGbtx++) {
    Int_t iModuleIdMap = fviModuleId[iGbtx];
    switch (fviRpcType[iGbtx]) {

      case 0:                         // CBM modules
        if (fviRpcSide[iGbtx] < 2) {  // mTof modules
          const Int_t RpcMap[5] = {4, 2, 0, 3, 1};
          for (Int_t iRpc = 0; iRpc < fviNrOfRpc[iGbtx]; iRpc++) {
            Int_t iStrMax = 32;
            Int_t iChNext = 1;

            for (Int_t iStr = 0; iStr < iStrMax; iStr++) {
              Int_t iStrMap = iStr;
              Int_t iRpcMap = RpcMap[iRpc];

              if (fviRpcSide[iGbtx] == 0) iStrMap = 31 - iStr;
              if (fviModuleId[iGbtx] > -1)
                fviRpcChUId[iCh] = CbmTofAddress::GetUniqueAddress(fviModuleId[iGbtx], iRpcMap, iStrMap,
                                                                   fviRpcSide[iGbtx], fviRpcType[iGbtx]);
              else
                fviRpcChUId[iCh] = 0;
              //	 LOG(debug)<<Form("Map Ch %d to Address 0x%08x",iCh,fviRpcChUId[iCh]);

              iCh += iChNext;
            }
          }
        }
        break;


      case 1:                         // STAR eTOF  modules
        if (fviRpcSide[iGbtx] < 2) {  // mTof modules
          const Int_t RpcMap[3] = {0, 1, 2};
          for (Int_t iRpc = 0; iRpc < fviNrOfRpc[iGbtx]; iRpc++) {
            Int_t iStrMax = 32;
            Int_t iChNext = 1;

            for (Int_t iStr = 0; iStr < iStrMax; iStr++) {
              Int_t iStrMap = iStr;
              Int_t iRpcMap = RpcMap[iRpc];

              if (fviRpcSide[iGbtx] == 0) iStrMap = 31 - iStr;
              if (fviModuleId[iGbtx] > -1)
                fviRpcChUId[iCh] = CbmTofAddress::GetUniqueAddress(fviModuleId[iGbtx], iRpcMap, iStrMap,
                                                                   fviRpcSide[iGbtx], fviRpcType[iGbtx]);
              else
                fviRpcChUId[iCh] = 0;
              //	 LOG(debug)<<Form("Map Ch %d to Address 0x%08x",iCh,fviRpcChUId[iCh]);
              iCh += iChNext;
            }
          }
        }
        iCh += 64;
        break;

      case 5:  // Diamond
      {
        LOG(info) << " Map diamond  at GBTX  -  iCh = " << iCh;
        for (UInt_t uFee = 0; uFee < fUnpackPar->GetNrOfFeePerGbtx(); ++uFee) {
          for (UInt_t uCh = 0; uCh < fUnpackPar->GetNrOfChannelsPerFee(); ++uCh) {
            if (uFee < 4 && (0 == uCh % 4 || uCh < 4)) {
              //  if(  0 == uCh )  {
              fviRpcChUId[iCh] = CbmTofAddress::GetUniqueAddress(
                fviModuleId[iGbtx], 0,
                uFee * fUnpackPar->GetNrOfChannelsPerFee() / 4 + uCh / 4 + 40 * fviRpcSide[iGbtx],
                //                           0, uFee + 10 * fviRpcSide[iGbtx],
                0, fviRpcType[iGbtx]);
              LOG(info) << Form("Map Bmon Ch %d to Address 0x%08x", iCh, fviRpcChUId[iCh]);
            }
            else
              fviRpcChUId[iCh] = 0;

            iCh++;
          }  // for( UInt_t uCh = 0; uCh < fUnpackPar->GetNrOfChannelsPerFee(); ++uCh )
        }    // for( UInt_t uFee = 0; uFee < fUnpackPar->GetNrOfFeePerGbtx(); ++uFee )
      } break;

      case 78:  // cern-20-gap + ceramic module
      {
        LOG(info) << " Map CERN 20 gap  at GBTX  -  iCh = " << iCh;
        // clang-format off
        const Int_t StrMap[32] = {0,  1,  2,  3,  4,  31, 5,  6,  7,  30, 8,
                                  9,  10, 29, 11, 12, 13, 14, 28, 15, 16, 17,
                                  18, 27, 26, 25, 24, 23, 22, 21, 20, 19};
        // clang-format on
        Int_t iModuleId   = 0;
        Int_t iModuleType = 7;
        Int_t iRpcMap     = 0;
        for (Int_t iFeet = 0; iFeet < 2; iFeet++) {
          for (Int_t iStr = 0; iStr < 32; iStr++) {
            Int_t iStrMap  = 31 - 12 - StrMap[iStr];
            Int_t iSideMap = iFeet;

            if (iStrMap < 20)
              fviRpcChUId[iCh] = CbmTofAddress::GetUniqueAddress(iModuleId, iRpcMap, iStrMap, iSideMap, iModuleType);
            else
              fviRpcChUId[iCh] = 0;
            iCh++;
          }
        }
      }
        [[fallthrough]];  // fall through is intended
      case 8:             // ceramics
      {
        Int_t iModuleId   = 0;
        Int_t iModuleType = 8;
        for (Int_t iRpc = 0; iRpc < 8; iRpc++) {
          fviRpcChUId[iCh] = CbmTofAddress::GetUniqueAddress(iModuleId, 7 - iRpc, 0, 0, iModuleType);
          iCh++;
        }
        iCh += (24 + 2 * 32);
      }
        LOG(info) << " Map end ceramics  box  at GBTX  -  iCh = " << iCh;

        break;

      case 4:
      case 9:  // Star2 boxes
      {
        LOG(info) << " Map Star2 box  at GBTX  -  iCh = " << iCh;
        const Int_t iRpc[5]  = {1, -1, 1, 0, 0};
        const Int_t iSide[5] = {1, -1, 0, 1, 0};
        for (Int_t iFeet = 0; iFeet < 5; iFeet++) {
          for (Int_t iStr = 0; iStr < 32; iStr++) {
            Int_t iStrMap  = iStr;
            Int_t iRpcMap  = iRpc[iFeet];
            Int_t iSideMap = iSide[iFeet];
            if (iSideMap == 0) iStrMap = 31 - iStr;
            switch (fviRpcSide[iGbtx]) {
              case 0:; break;
              case 1:; break;
              case 2:
                switch (iFeet) {
                  case 1:
                    iRpcMap  = iRpc[4];
                    iSideMap = iSide[4];
                    break;
                  case 4:
                    iRpcMap  = iRpc[1];
                    iSideMap = iSide[1];
                    break;
                  default:;
                }
                break;
            }
            if (iSideMap > -1)
              fviRpcChUId[iCh] =
                CbmTofAddress::GetUniqueAddress(fviModuleId[iGbtx], iRpcMap, iStrMap, iSideMap, fviRpcType[iGbtx]);
            else
              fviRpcChUId[iCh] = 0;

            iCh++;
          }
        }
      } break;

      case 6:  // Buc box
      {
        LOG(info) << " DevMap Buc box  at GBTX  -  iCh = " << iCh;
        const Int_t iRpc[5]  = {0, -1, 0, 1, 1};
        const Int_t iSide[5] = {1, -1, 0, 1, 0};
        for (Int_t iFeet = 0; iFeet < 5; iFeet++) {
          for (Int_t iStr = 0; iStr < 32; iStr++) {
            Int_t iStrMap  = iStr;
            Int_t iRpcMap  = iRpc[iFeet];
            Int_t iSideMap = iSide[iFeet];
            //if(iSideMap == 0)iStrMap=31-iStr;
            switch (fviRpcSide[iGbtx]) {
              case 0:; break;
              case 1:  // HD cosmic 2019, Buc2018, v18n
                iStrMap = 31 - iStr;
                iRpcMap = 1 - iRpcMap;
                break;
              case 2:  // v18m_cosmicHD
                //		 iStrMap=31-iStr;
                iSideMap = 1 - iSideMap;
                break;
              case 3:
                iStrMap  = 31 - iStr;
                iRpcMap  = 1 - iRpcMap;
                iSideMap = 1 - iSideMap;
                break;
              case 4:  // HD cosmic 2019, Buc2018, v18o
                iRpcMap = 1 - iRpcMap;
                break;
              case 5:  // HD cosmic 2020, Buc2018, v20a
                iStrMap = 31 - iStr;
                break;
              case 6:  //BUC special
              {
                switch (fviModuleId[iGbtx]) {
                  case 0: iRpcMap = 0; break;
                  case 1: iRpcMap = 1; break;
                }
                if (iFeet > 2) iModuleIdMap = 1;
              } break;
              case 7: {
                // clang-format off
                const Int_t iChMap[160]={
             	  127, 126,	125, 124,  12,  13,	 14,  15,	7,   6,	  5,   4,  28,	29,	 30,  31, 123, 122,	121, 120,	8,	 9,  10,  11, 107, 106,	105, 104, 108, 109,	110, 111,
                   39,  38,	 37,  36,  52,	53,	 54,  55,  63,	62,	 61,  60, 128, 129,	130, 131,  43,	42,	 41,  40, 148, 149,	150, 151,  59,	58,	 57,  56, 132, 133,	134, 135,
          		  139, 138,	137, 136, 140, 141, 142, 143,  99,	98,	 97,  96,  64,	65,	 66,  67, 103, 102,	101, 100,  84,	85,	 86,  87, 155, 154,	153, 152,  68,	69,	 70,  71,
          		  159, 158,	157, 156, 144, 145,	146, 147,  47,	46,	 45,  44,  76,	77,	 78,  79,  51,	50,	 49,  48,  20,	21,	 22,  23,  35,	34,	 33,  32, 116, 117,	118, 119,
          		   75,	74,	 73,  72,  92,	93,	 94,  95,  19,	18,	 17,  16,  80,	81,	 82,  83, 115, 114,	113, 112,  24,	25,	 26,  27,  91,	90,	 89,  88,	0,	 1,	 2,	3
                };
                // clang-format on
                Int_t iInd = iFeet * 32 + iStr;
                Int_t i    = 0;
                for (; i < 160; i++)
                  if (iInd == iChMap[i]) break;
                iStrMap        = i % 32;
                Int_t iFeetInd = (i - iStrMap) / 32;
                switch (iFeet) {
                  case 0:
                    iRpcMap  = 0;
                    iSideMap = 1;
                    break;
                  case 1:
                    iRpcMap  = 1;
                    iSideMap = 1;
                    break;
                  case 2:
                    iRpcMap  = 1;
                    iSideMap = 0;
                    break;
                  case 3:
                    iRpcMap  = 0;
                    iSideMap = 0;
                    break;
                  case 4: iSideMap = -1; break;
                }
                iModuleIdMap = fviModuleId[iGbtx];
                LOG(info) << "Buc of GBTX " << iGbtx
                          << Form(", Feet %1d, Str %2d, i %3d, FeetInd %1d, Rpc %1d, Side %1d, Str %2d ", iFeet, iStr,
                                  i, iFeetInd, iRpcMap, iSideMap, iStrMap);
              } break;
              default:;
            }
            if (iSideMap > -1)
              fviRpcChUId[iCh] =
                CbmTofAddress::GetUniqueAddress(iModuleIdMap, iRpcMap, iStrMap, iSideMap, fviRpcType[iGbtx]);
            else
              fviRpcChUId[iCh] = 0;

            iCh++;
          }
        }
      } break;

      case -1:
        LOG(info) << " Found unused GBTX link at iCh = " << iCh;
        iCh += 160;
        break;

      default: LOG(error) << "Invalid Type  specifier for Gbtx " << iGbtx << ": " << fviRpcType[iGbtx];
    }
  }

  for (UInt_t i = 0; i < uNrOfChannels; i = i + 8) {
    if (i % 64 == 0) LOG(info) << " Index " << i;
    LOG(info) << Form("0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x", fviRpcChUId[i], fviRpcChUId[i + 1],
                      fviRpcChUId[i + 2], fviRpcChUId[i + 3], fviRpcChUId[i + 4], fviRpcChUId[i + 5],
                      fviRpcChUId[i + 6], fviRpcChUId[i + 7]);
  }  // for( UInt_t i = 0; i < uNrOfChannels; ++i)

  return kTRUE;
}

void CbmDeviceUnpackTofMcbm2018::CreateHistograms()
{
  LOG(info) << "create Histos for " << fuNrOfGdpbs << " gDPBs ";

  fhRawTDigEvBmon =
    new TH1F(Form("Raw_TDig-EvBmon"), Form("Raw digi time difference to 1st digi ; time [ns]; cts"), 500, 0, 100.);
  //   fHM->Add( Form("Raw_TDig-EvBmon"), fhRawTDigEvBmon);

  fhRawTDigRef0 =
    new TH1F(Form("Raw_TDig-Ref0"), Form("Raw digi time difference to Ref ; time [ns]; cts"), 6000, -10000, 50000);
  //   fHM->Add( Form("Raw_TDig-Ref0"), fhRawTDigRef0);

  fhRawTDigRef =
    new TH1F(Form("Raw_TDig-Ref"), Form("Raw digi time difference to Ref ; time [ns]; cts"), 6000, -1000, 5000);
  //   fHM->Add( Form("Raw_TDig-Ref"), fhRawTDigRef);

  fhRawTRefDig0 = new TH1F(Form("Raw_TRef-Dig0"), Form("Raw Ref time difference to last digi  ; time [ns]; cts"), 9999,
                           -50000, 50000);
  //   fHM->Add( Form("Raw_TRef-Dig0"), fhRawTRefDig0);

  fhRawTRefDig1 =
    new TH1F(Form("Raw_TRef-Dig1"), Form("Raw Ref time difference to last digi  ; time [ns]; cts"), 9999, -5000, 5000);
  //   fHM->Add( Form("Raw_TRef-Dig1"), fhRawTRefDig1);

  fhRawDigiLastDigi = new TH1F(Form("Raw_Digi-LastDigi"),
                               Form("Raw Digi time difference to last digi  ; time [ns]; cts"), 9999, -5000, 5000);
  //                                 9999, -5000000, 5000000);
  //   fHM->Add( Form("Raw_Digi-LastDigi"), fhRawDigiLastDigi);

  fhRawTotCh.resize(fuNrOfGdpbs);
  fhChCount.resize(fuNrOfGdpbs);
  fhChanCoinc.resize(fuNrOfGdpbs * fuNrOfFeePerGdpb / 2);
  for (UInt_t uGdpb = 0; uGdpb < fuNrOfGdpbs; uGdpb++) {
    fhRawTotCh[uGdpb] = new TH2F(Form("Raw_Tot_gDPB_%02u", uGdpb), Form("Raw TOT gDPB %02u; channel; TOT [bin]", uGdpb),
                                 fuNrOfChannelsPerGdpb, 0., fuNrOfChannelsPerGdpb, 256, 0., 256.);
    //      fHM->Add( Form("Raw_Tot_gDPB_%02u", uGdpb), fhRawTotCh[ uGdpb ]);

    fhChCount[uGdpb] =
      new TH1I(Form("ChCount_gDPB_%02u", uGdpb), Form("Channel counts gDPB %02u; channel; Hits", uGdpb),
               fuNrOfChannelsPerGdpb, 0., fuNrOfChannelsPerGdpb);
    //      fHM->Add( Form("ChCount_gDPB_%02u", uGdpb), fhChCount[ uGdpb ]);
    /*
	for( UInt_t uLeftFeb = uGdpb*fuNrOfFebsPerGdpb / 2;
	uLeftFeb < (uGdpb + 1 )*fuNrOfFebsPerGdpb / 2;
	++uLeftFeb )
	{
	fhChanCoinc[ uLeftFeb ] = new TH2F( Form("fhChanCoinc_%02u", uLeftFeb),
	Form("Channels Coincidence %02; Left; Right", uLeftFeb),
	fuNrOfChannelsPerFee, 0., fuNrOfChannelsPerFee,
	fuNrOfChannelsPerFee, 0., fuNrOfChannelsPerFee );
	} // for( UInt_t uLeftFeb = 0; uLeftFeb < fuNrOfFebsPerGdpb / 2; uLeftFeb ++ )
      */
    fhChanCoinc[uGdpb] =
      new TH2F(Form("fhChanCoinc_%02u", uGdpb), Form("Channels Coincidence %02u; Left; Right", uGdpb),
               fuNrOfChannelsPerGdpb, 0., fuNrOfChannelsPerGdpb, fuNrOfChannelsPerGdpb, 0., fuNrOfChannelsPerGdpb);
  }  // for( UInt_t uGdpb = 0; uGdpb < fuMinNbGdpb; uGdpb ++)
  fhDetChanCoinc = new TH2F("fhDetChanCoinc", "Det Channels Coincidence; Left; Right", 32, 0., 32, 32, 0., 32);
}
// handler is called whenever a message arrives on "data", with a reference to the message and a sub-channel index (here 0)
bool CbmDeviceUnpackTofMcbm2018::HandleData(FairMQMessagePtr& msg, int /*index*/)
{
  // Don't do anything with the data
  // Maybe add an message counter which counts the incomming messages and add
  // an output
  fNumMessages++;
  LOG(debug) << "Received message number " << fNumMessages << " with size " << msg->GetSize();

  std::string msgStr(static_cast<char*>(msg->GetData()), msg->GetSize());
  std::istringstream iss(msgStr);
  boost::archive::binary_iarchive inputArchive(iss);

  fles::StorableTimeslice component {0};
  inputArchive >> component;

  //  CheckTimeslice(component);

  DoUnpack(component, 0);

  BuildTint(0);

  if (fNumMessages % 10000 == 0) LOG(info) << "Processed " << fNumMessages << " time slices";

  return true;
}

bool CbmDeviceUnpackTofMcbm2018::HandleParts(FairMQParts& parts, int /*index*/)
{
  // Don't do anything with the data
  // Maybe add an message counter which counts the incomming messages and add
  // an output
  fNumMessages++;
  LOG(debug) << "Received message number " << fNumMessages << " with " << parts.Size() << " parts";

  fles::StorableTimeslice ts {0};

  switch (fiSelectComponents) {
    case 0: {
      std::string msgStr(static_cast<char*>(parts.At(0)->GetData()), (parts.At(0))->GetSize());
      std::istringstream iss(msgStr);
      boost::archive::binary_iarchive inputArchive(iss);
      inputArchive >> ts;
      //CheckTimeslice(ts);
      if (1 == fNumMessages) {
        LOG(info) << "Initialize TS components list to " << ts.num_components();
        for (size_t c {0}; c < ts.num_components(); c++) {
          auto systemID = static_cast<int>(ts.descriptor(c, 0).sys_id);
          LOG(info) << "Found systemID: " << std::hex << systemID << std::dec;
          fUnpackerAlgo->AddMsComponentToList(c, systemID);  // TOF data
        }
      }
      DoUnpack(ts, 0);
    } break;
    case 1: {
      fles::StorableTimeslice component {0};

      uint ncomp = parts.Size();
      for (uint i = 0; i < ncomp; i++) {
        std::string msgStr(static_cast<char*>(parts.At(i)->GetData()), (parts.At(i))->GetSize());
        std::istringstream iss(msgStr);
        boost::archive::binary_iarchive inputArchive(iss);
        //fles::StorableTimeslice component{i};
        inputArchive >> component;

        //      CheckTimeslice(component);
        fUnpackerAlgo->AddMsComponentToList(0, 0x60);  // TOF data
        LOG(debug) << "HandleParts message " << fNumMessages << " with indx " << component.index();
        DoUnpack(component, 0);
      }
    } break;
    default:;
  }

  BuildTint(0);

  if (fNumMessages % 10000 == 0) LOG(info) << "Processed " << fNumMessages << " time slices";

  return true;
}

bool CbmDeviceUnpackTofMcbm2018::HandleMessage(FairMQMessagePtr& msg, int /*index*/)
{
  const char* cmd    = (char*) (msg->GetData());
  const char cmda[4] = {*cmd};
  LOG(info) << "Handle message " << cmd << ", " << cmd[0];
  cbm::mq::LogState(this);

  // only one implemented so far "Stop"

  if (strcmp(cmda, "STOP")) {
    LOG(info) << "STOP";
    fUnpackerAlgo->Finish();
    cbm::mq::ChangeState(this, cbm::mq::Transition::Ready);
    cbm::mq::LogState(this);
    cbm::mq::ChangeState(this, cbm::mq::Transition::DeviceReady);
    cbm::mq::LogState(this);
    cbm::mq::ChangeState(this, cbm::mq::Transition::Idle);
    cbm::mq::LogState(this);
    cbm::mq::ChangeState(this, cbm::mq::Transition::End);
    cbm::mq::LogState(this);
  }
  return true;
}

Bool_t CbmDeviceUnpackTofMcbm2018::DoUnpack(const fles::Timeslice& ts, size_t component)
{
  LOG(debug) << "Timeslice " << ts.index() << " contains " << ts.num_microslices(component)
             << " microslices of component " << component;

  if (kFALSE == fUnpackerAlgo->ProcessTs(ts)) {
    LOG(error) << "Failed processing TS " << ts.index() << " in unpacker algorithm class";
    return kTRUE;
  }  // if( kFALSE == fUnpackerAlgo->ProcessTs( ts ) )

  /// Copy the digis in the DaqBuffer
  std::vector<CbmTofDigi> vDigi = fUnpackerAlgo->GetVector();

  /*
   // time sort vDigis
   sort(vDigi.begin(), vDigi.end(),
        [](const CbmTofDigi & a, const CbmTofDigi & b) -> bool
        {
          return a.GetTime() < b.GetTime();
        });
   */

  LOG(debug) << "Insert " << vDigi.size() << " digis into DAQ buffer  with size " << fBuffer->GetSize();

  for (auto digi : vDigi) {
    // copy Digi for insertion into DAQ buffer
    CbmTofDigi* fDigi = new CbmTofDigi(digi);

    //if( (fDigi->GetAddress() & 0x000F00F ) != fiAddrRef )  fDigi->SetTime(fDigi->GetTime()+fdToffTof); // shift all Tof Times for v14a geometries
    if ((fDigi->GetAddress() & 0x000780F) != fiAddrRef)
      fDigi->SetTime(fDigi->GetTime() + fdToffTof);  // shift all Tof Times for V21a

    LOG(debug) << "BufferInsert digi " << Form("0x%08x at %012.2f", fDigi->GetAddress(), fDigi->GetTime())
               << Form(", first %012.2f, last %012.2f, size %u", fBuffer->GetTimeFirst(), fBuffer->GetTimeLast(),
                       fBuffer->GetSize());

    fBuffer->InsertData<CbmTofDigi>(fDigi);
  }
  vDigi.clear();
  fUnpackerAlgo->ClearVector();

  return kTRUE;
}

void CbmDeviceUnpackTofMcbm2018::BuildTint(int iMode = 0)
{
  // iMode - sending condition
  // 0 (default)- build time interval only if last buffer entry is older the start + TSLength
  // 1 (finish), empty buffer without checking
  // Steering variables
  double TSLENGTH    = 1.E6;
  double fdMaxDeltaT = (double) fiReqTint;  // in ns

  LOG(debug) << "BuildTint: Buffer size " << fBuffer->GetSize() << ", DeltaT "
             << (fBuffer->GetTimeLast() - fBuffer->GetTimeFirst()) / 1.E9 << " s";
  CbmTbDaqBuffer::Data data;
  CbmTofDigi* digi;

  while (fBuffer->GetSize() > 0) {
    Double_t fTimeBufferLast = fBuffer->GetTimeLast();

    switch (iMode) {
      case 0:
        if (fTimeBufferLast - fBuffer->GetTimeFirst() < TSLENGTH) return;
        break;
      case 1:; break;
    }

    data = fBuffer->GetNextData(fTimeBufferLast);
    digi = boost::any_cast<CbmTofDigi*>(data.first);
    assert(digi);

    Double_t dTEnd    = digi->GetTime() + fdMaxDeltaT;
    Double_t dTEndMax = digi->GetTime() + 2 * fdMaxDeltaT;
    LOG(debug) << Form("Next event at %f until %f, max %f ", digi->GetTime(), dTEnd, dTEndMax);

    if (dTEnd > fTimeBufferLast) {
      LOG(warn) << Form("Remaining buffer < %f with %d entries is not "
                        "sufficient for digi ending at %f -> skipped ",
                        fTimeBufferLast, fBuffer->GetSize(), dTEnd);
      return;
    }

    LOG(debug) << "BuildTint0 with digi " << Form("0x%08x at %012.2f", digi->GetAddress(), digi->GetTime());

    Bool_t bDet[fiReqDigiAddr.size()][2];
    for (UInt_t i = 0; i < fiReqDigiAddr.size(); i++)
      for (Int_t j = 0; j < 2; j++)
        bDet[i][j] = kFALSE;  //initialize
    Bool_t bPul[fiReqDigiAddr.size()][2];
    for (UInt_t i = 0; i < fiReqDigiAddr.size(); i++)
      for (Int_t j = 0; j < 2; j++)
        bPul[i][j] = kFALSE;  //initialize
    Bool_t bBeam = kFALSE;

    std::vector<CbmTofDigi*> vdigi;
    UInt_t nDigi = 0;
    //const Int_t AddrMask=0x003FFFFF;
    const Int_t AddrMask = 0x001FFFFF;
    Bool_t bOut          = kFALSE;
    Int_t iBucMul        = 0;

    while (data.second != ECbmModuleId::kNotExist) {  // build digi array
      digi = boost::any_cast<CbmTofDigi*>(data.first);
      LOG(debug) << "GetNextData " << digi << ", " << data.second << ",  " << Form("%f %f", digi->GetTime(), dTEnd)
                 << ", Mul " << nDigi;
      assert(digi);

      if (nDigi == vdigi.size()) vdigi.resize(nDigi + 100);
      vdigi[nDigi++] = digi;

      Int_t iAddr = digi->GetAddress() & AddrMask;
      if (iAddr == 0x00003006 || iAddr == 0x0000b006) {
        iBucMul++;
        LOG(debug) << Form("Event %10d: BucMul %2d, addr 0x%08x, side %d, strip %2d, rpc %d", fEventHeader[0], iBucMul,
                           digi->GetAddress(), (Int_t) digi->GetSide(), (Int_t) digi->GetChannel(),
                           (Int_t) digi->GetRpc());
      }
      for (UInt_t i = 0; i < fiReqDigiAddr.size(); i++)
        if ((digi->GetAddress() & AddrMask) == fiReqDigiAddr[i]) {
          Int_t j    = ((CbmTofDigi*) digi)->GetSide();
          bDet[i][j] = kTRUE;
          if (fiReqDigiAddr[i] == (Int_t) fiReqBeam) {
            bBeam = kTRUE;
            LOG(debug) << "Found ReqBeam at index " << nDigi - 1 << ", req " << i;
          }
          if ((UInt_t) fiReqDigiAddr[i] == fiAddrRef) bDet[i][1] = kTRUE;  // diamond with pad readout
          // if ( (fiReqDigiAddr[i] & 0x0000F00F ) == 0x00008006) bDet[i][1]=kTRUE; // ceramic with pad readout
          Int_t str = ((CbmTofDigi*) digi)->GetChannel();

          switch (j) {  // treat both strip ends separately
            case 0:
              switch (fiPulserMode) {
                case 0:
                case 1:
                  if (str == 31)
                    if (digi->GetTot() > fiPulTotMin && digi->GetTot() < fiPulTotMax) bPul[i][0] = kTRUE;
                  if (str == 0) bPul[i][1] = kFALSE;
                  if ((UInt_t) fiReqDigiAddr[i] == fiAddrRef) {  //special mapping for MAr2019 diamond (Bmon)
                    if (str == 0) bPul[i][0] = kTRUE;
                    if (str == 40) bPul[i][1] = kTRUE;
                  }
                  break;
                case 2:
                  if (str == 0)
                    if (digi->GetTot() > fiPulTotMin && digi->GetTot() < fiPulTotMax) {
                      bPul[i][0] = kTRUE;
                      if ((fiReqDigiAddr[i] & 0x000FF00F) == 0x00078006) {
                        bPul[i][1] = kTRUE;   // ceramic with pad readout
                        bDet[i][1] = kFALSE;  // remove Hit flag
                      }
                      if (str == 31) bPul[i][1] = kFALSE;
                    }
                default:;
              }
              break;

            case 1:
              switch (fiPulserMode) {
                case 0:
                case 1:
                  if (str == 31) bPul[i][0] = kFALSE;
                  if (str == 0)
                    if (digi->GetTot() > fiPulTotMin && digi->GetTot() < fiPulTotMax) bPul[i][1] = kTRUE;
                  break;
                case 2:
                  if (str == 0) bPul[i][0] = kFALSE;
                  if (str == 31)
                    if (digi->GetTot() > fiPulTotMin && digi->GetTot() < fiPulTotMax) bPul[i][1] = kTRUE;
                  break;
                default:;
              }
              break;
            default:;
          }
        }
      //if(bOut) LOG(info)<<Form("Found 0x%08x, Req 0x%08x ", digi->GetAddress(), fiReqDigiAddr);
      if (dTEnd - digi->GetTime() < fdMaxDeltaT * 0.5) {
        if (digi->GetTime() + fdMaxDeltaT * 0.5 < dTEndMax) dTEnd = digi->GetTime() + fdMaxDeltaT * 0.5;
        else
          dTEnd = dTEndMax;
      };
      data = fBuffer->GetNextData(dTEnd);

    }  // end while

    LOG(debug) << nDigi << " digis associated to dTEnd = " << dTEnd << ":";
    //for(UInt_t iDigi=0; iDigi<nDigi; iDigi++) LOG(debug)<<Form(" 0x%08x",vdigi[iDigi]->GetAddress());
    for (UInt_t iDigi = 0; iDigi < nDigi; iDigi++)
      LOG(debug) << vdigi[iDigi]->ToString();

    UInt_t iDetMul = 0;
    if (fiReqDigiAddr.size() == 0) bOut = kTRUE;  // output everything
    else {
      if (fiReqMode == 0) {  // check for presence of requested detectors
        for (UInt_t i = 0; i < fiReqDigiAddr.size(); i++)
          if (bDet[i][0] == kFALSE || bDet[i][1] == kFALSE) break;
          else if (i == fiReqDigiAddr.size() - 1) {
            bOut    = kTRUE;
            iDetMul = i;
          }
      }
      else {  // check for presence of any known detector
        for (UInt_t i = 0; i < fiReqDigiAddr.size(); i++)
          if (bDet[i][0] == kTRUE && bDet[i][1] == kTRUE) { iDetMul++; }
        if (iDetMul >= fiReqMode) { bOut = kTRUE; }
      }
    }

    if (bOut && fiReqDigiAddr.size() > 1) {
      LOG(debug) << "Found Req coinc in event with " << nDigi << " digis in " << iDetMul
                 << " detectors, dTEnd = " << dTEnd;
    }

    // determine Pulser status
    UInt_t iPulMul = 0;  // Count Potential Pulser Signals
    for (UInt_t i = 0; i < fiReqDigiAddr.size(); i++) {
      if (bPul[i][0] == kTRUE && bPul[i][1] == kTRUE) iPulMul++;
    }

    if (fiPulserMode > 0 && iPulMul > fiPulMulMin) {
      LOG(debug) << "@Event " << fEventHeader[0] << ": iPulMul = " << iPulMul;
      bOut = kTRUE;
    }
    LOG(debug) << "Process Ev " << fEventHeader[0] << "  with iDetMul = " << iDetMul << ", iPulMul = " << iPulMul;

    fEventHeader[0]++;

    if ((Int_t) fiReqBeam > -1) {
      if (bBeam) { LOG(debug) << "Beam counter is present "; }
      else {
        LOG(debug) << "Beam counter is not present";
        bOut = kFALSE;  // request beam counter for event
      }
    }

    if (bOut) {
      fEventHeader[1] = iDetMul;
      fEventHeader[2] = fiReqMode;
      fEventHeader[3] = iPulMul;
      vdigi.resize(nDigi);
      const Int_t NDigiMax = 10000;
      if (nDigi > NDigiMax) {
        LOG(warn) << "Oversized event, truncated! ";
        for (UInt_t iDigi = NDigiMax; iDigi < nDigi; iDigi++)
          delete vdigi[iDigi];
        nDigi = 1;  //NDigiMax;
        vdigi.resize(nDigi);
      }
      SendDigis(vdigi, 0);

      for (UInt_t iDigi = 0; iDigi < nDigi; iDigi++)
        delete vdigi[iDigi];
    }
    else {
      LOG(debug) << " BuildTint cleanup of " << nDigi << " digis";
      for (UInt_t iDigi = 0; iDigi < nDigi; iDigi++) {
        //	vdigi[iDigi]->Delete();
      }
      LOG(debug) << " Digis deleted ";
      //vdigi.clear();
      //delete &vdigi;  // crashes, since local variable, will be done at return (?)
    }
  }
}

bool CbmDeviceUnpackTofMcbm2018::SendDigis(std::vector<CbmTofDigi*> vdigi, int idx)
{
  LOG(debug) << "Send Digis for event " << fNumTint << " with size " << vdigi.size() << Form(" at %p ", &vdigi);
  LOG(debug) << "EventHeader: " << fEventHeader[0] << " " << fEventHeader[1] << " " << fEventHeader[2] << " "
             << fEventHeader[3];

  //  Int_t NDigi=vdigi.size();

  std::stringstream ossE;
  boost::archive::binary_oarchive oaE(ossE);
  oaE << fEventHeader;
  std::string* strMsgE = new std::string(ossE.str());

  std::stringstream oss;
  boost::archive::binary_oarchive oa(oss);
  oa << vdigi;
  std::string* strMsg = new std::string(oss.str());

  FairMQParts parts;
  parts.AddPart(NewMessage(
    const_cast<char*>(strMsgE->c_str()),  // data
    strMsgE->length(),                    // size
    [](void*, void* object) { delete static_cast<std::string*>(object); },
    strMsgE));  // object that manages the data

  parts.AddPart(NewMessage(
    const_cast<char*>(strMsg->c_str()),  // data
    strMsg->length(),                    // size
    [](void*, void* object) { delete static_cast<std::string*>(object); },
    strMsg));  // object that manages the data


  /*
  std::vector<CbmTofDigi>vTofDigi;
  vTofDigi.resize(vdigi.size());
  for (Int_t i=0; i<vdigi.size(); i++)   {
    CbmTofDigi *pdigi = (CbmTofDigi *) vdigi[i];
    CbmTofDigi digi = *pdigi;
    vTofDigi[i] = digi;
    LOG(debug) << vTofDigi[i].ToString()<<" bits "<<Form("0x%08x",vTofDigi[i].TestBits(0xFFFF));
  }
    FairMQMessagePtr msg(NewMessage(static_cast<std::vector<CbmTofDigi>*> (&vTofDigi), // data
                                  NDigi*sizeof(CbmTofDigi), // size
				  [](void* , void* object){ delete static_cast<CbmTofDigi*>(object); }
                                  )); // object that manages the data

  // transfer of TofDigi array, ... works
  CbmTofDigi aTofDigi[NDigi];
  //  const Int_t iNDigiOut=100;
  //  NDigi=TMath::Min(NDigi,iNDigiOut);
  //  std::array<CbmTofDigi,iNDigiOut> aTofDigi;
  for (Int_t i=0; i<NDigi; i++) {
    aTofDigi[i] = *vdigi[i];
    LOG(debug) << aTofDigi[i].ToString()<<" bits "<<Form("0x%08x",aTofDigi[i].TestBits(0xFFFF));
  }
  FairMQMessagePtr msg(NewMessage(static_cast<CbmTofDigi*> (&aTofDigi[0]), // data
                                  NDigi*sizeof(CbmTofDigi), // size
				  [](void* , void* object){ delete static_cast<CbmTofDigi*>(object); }
                                  )); // object that manages the data


  LOG(info) << "Send aTofDigi sizes "<<NDigi<<", "<<sizeof(CbmTofDigi)<<", msg size "<<msg->GetSize();

  // serialize the timeslice and create the message

  std::stringstream oss;
  boost::archive::binary_oarchive oa(oss);
  oa << vdigi;
  std::string* strMsg = new std::string(oss.str());

  LOG(debug) << "send strMsg with length " << strMsg->length()<<" "<<strMsg;
  FairMQMessagePtr msg(NewMessage(const_cast<char*>(strMsg->c_str()), // data
                                                    strMsg->length(), // size
                                                    [](void* , void* object){ delete static_cast<std::string*>(object); },
                                                    strMsg)); // object that manages the data
  */
  /*
  FairMQMessagePtr msg(NewMessage(static_cast<CbmTofDigi*> (vTofDigi.data()), // data
                                                vTofDigi.size()*sizeof(CbmTofDigi), // size
                                                [](void* , void* object){ delete static_cast<CbmTofDigi*>(object); }
                                                )); // object that manages the data
  */

  /* --------------------------------------- compiles but crashes .... ---------------------------------------------------
  const Int_t WSize=8;
  FairMQMessagePtr msg(NewMessage(static_cast<std::vector<CbmTofDigi>*> (&vTofDigi), // data
                                  vTofDigi.size()*sizeof(CbmTofDigi)*WSize, // size, FIXME, numerical value in code!
				  [](void* , void* object){ delete static_cast<std::vector<CbmTofDigi>*>(object); }
                                  )); // object that manages the data

  LOG(info) << "Send TofDigi sizes "<<vTofDigi.size()<<", "<<sizeof(CbmTofDigi)<<", msg size "<<msg->GetSize();
  int *pData = static_cast <int *>(vTofDigi.data());

  int *pData = static_cast <int *>(msg->GetData());
  const Int_t NBytes=4;
  for (int iData=0; iData<msg->GetSize()/NBytes; iData++) {
    LOG(info) << Form(" ind %d, poi %p, data: 0x%08x",iData,pData,*pData++);
  }
  */
  /*
    auto msg = NewMessageFor("my_channel", 0,
                         static_cast<void*>(vTofDigi.data()),
                         vTofDigi.size() * sizeof(CbmTofDigi),
			 FairMQNoCleanup, nullptr);
  */

  // TODO: Implement sending same data to more than one channel
  // Need to create new message (copy message??)
  /*
  if (fChannelsToSend[idx]>1) {
    LOG(info) << "Need to copy FairMessage ?";
  }
  */
  // in case of error or transfer interruption,
  // return false to go to IDLE state
  // successfull transfer will return number of bytes
  // transfered (can be 0 if sending an empty message).

  LOG(debug) << "Send data to channel " << idx << " " << fChannelsToSend[idx][0];


  //  if (Send(msg, fChannelsToSend[idx][0]) < 0) {
  if (Send(parts, fChannelsToSend[idx][0]) < 0) {
    LOG(error) << "Problem sending data " << fChannelsToSend[idx][0];
    return false;
  }
  /*
  LOG(info) << "Sent message # "<<  fNumTint << " at " << msg->GetData()
            << ", size " << msg->GetSize()<<" for "<<vTofDigi.size()<<" Digis at "
	    << vTofDigi.data() << ", "<<&vTofDigi;
  */
  fNumTint++;
  //if(fNumTint==100) FairMQStateMachine::ChangeState(PAUSE); //sleep(10000); // Stop for debugging ...
  /*
  LOG(info) << "Send message " << fNumTint << " with a size of "
            << msg->GetSize();
  */
  return true;
}

void CbmDeviceUnpackTofMcbm2018::AddReqDigiAddr(Int_t iAddr)
{
  UInt_t iNReq = fiReqDigiAddr.size();
  for (UInt_t i = 0; i < iNReq; i++)
    if (fiReqDigiAddr[i] == iAddr) return;  // det already present, avoid double counting
  fiReqDigiAddr.resize(iNReq + 1);          // hopefully the old entries are preserved ...
  fiReqDigiAddr[iNReq] = iAddr;
  LOG(info) << Form("Request Digi Address 0x%08x at index %d", iAddr, iNReq);
}

void CbmDeviceUnpackTofMcbm2018::SetIgnoreOverlapMs(Bool_t bFlagIn) { fUnpackerAlgo->SetIgnoreOverlapMs(bFlagIn); }

void CbmDeviceUnpackTofMcbm2018::SetTimeOffsetNs(Double_t dOffsetIn) { fUnpackerAlgo->SetTimeOffsetNs(dOffsetIn); }
void CbmDeviceUnpackTofMcbm2018::SetDiamondDpbIdx(UInt_t uIdx) { fUnpackerAlgo->SetDiamondDpbIdx(uIdx); }