Skip to content
Snippets Groups Projects
JepByteStreamV1Tool.cxx 68.6 KiB
Newer Older
  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
*/


#include <numeric>
#include <set>
#include <utility>

#include "GaudiKernel/IInterface.h"
#include "GaudiKernel/MsgStream.h"
#include "GaudiKernel/StatusCode.h"

#include "ByteStreamCnvSvcBase/FullEventAssembler.h"

#include "TrigT1CaloEvent/CMMJetHits.h"
#include "TrigT1CaloEvent/CMMEtSums.h"
#include "TrigT1CaloEvent/JEMHits.h"
#include "TrigT1CaloEvent/JEMEtSums.h"
#include "TrigT1CaloEvent/JEPBSCollectionV1.h"
#include "TrigT1CaloEvent/JetElement.h"
#include "TrigT1CaloUtils/DataError.h"
#include "TrigT1CaloUtils/JetElementKey.h"
#include "TrigT1CaloToolInterfaces/IL1CaloMappingTool.h"

#include "CmmEnergySubBlock.h"
#include "CmmJetSubBlock.h"
#include "CmmSubBlock.h"
#include "JemJetElement.h"
#include "JemSubBlockV1.h"
#include "L1CaloErrorByteStreamTool.h"
#include "L1CaloSrcIdMap.h"
#include "L1CaloSubBlock.h"
#include "L1CaloUserHeader.h"
#include "ModifySlices.h"

#include "JepByteStreamV1Tool.h"

namespace LVL1BS {

// Interface ID

static const InterfaceID IID_IJepByteStreamV1Tool("JepByteStreamV1Tool", 1, 1);

const InterfaceID& JepByteStreamV1Tool::interfaceID()
{
  return IID_IJepByteStreamV1Tool;
}

// Constructor

JepByteStreamV1Tool::JepByteStreamV1Tool(const std::string& type,
    const std::string& name,
    const IInterface*  parent)
  : AthAlgTool(type, name, parent),
    m_jemMaps("LVL1::JemMappingTool/JemMappingTool"),
    m_errorTool("LVL1BS::L1CaloErrorByteStreamTool/L1CaloErrorByteStreamTool"),
    m_channels(44), m_crates(2), m_modules(16), 
    m_subDetector(eformat::TDAQ_CALO_JET_PROC_DAQ)
{
  declareInterface<JepByteStreamV1Tool>(this);

  declareProperty("JemMappingTool", m_jemMaps,
                  "Crate/Module/Channel to Eta/Phi/Layer mapping tool");
  declareProperty("ErrorTool", m_errorTool,
                  "Tool to collect errors for monitoring");

  declareProperty("CrateOffsetHw",  m_crateOffsetHw = 12,
                  "Offset of JEP crate numbers in bytestream");
  declareProperty("CrateOffsetSw",  m_crateOffsetSw = 0,
                  "Offset of JEP crate numbers in RDOs");
  declareProperty("SlinksPerCrate", m_slinks        = 4,
                  "The number of S-Links per crate");

  // Properties for reading bytestream only
  declareProperty("ROBSourceIDs",       m_sourceIDsProp,
                  "ROB fragment source identifiers");

  // Properties for writing bytestream only
  declareProperty("DataVersion",    m_version     = 1,
                  "Format version number in sub-block header");
  declareProperty("DataFormat",     m_dataFormat  = 1,
                  "Format identifier (0-1) in sub-block header");
  declareProperty("SimulSlices",    m_dfltSlices  = 1,
                  "The number of slices in the simulation");
  declareProperty("ForceSlices",    m_forceSlices = 0,
                  "If >0, the number of slices in bytestream");
  declareProperty("CrateMin",       m_crateMin = 0,
                  "Minimum crate number, allows partial output");
  declareProperty("CrateMax",       m_crateMax = m_crates - 1,
                  "Maximum crate number, allows partial output");

}

// Destructor

JepByteStreamV1Tool::~JepByteStreamV1Tool()
{
}

// Initialize


StatusCode JepByteStreamV1Tool::initialize()
{
  ATH_MSG_INFO ("Initializing " << name() << " - package version "
                 << PACKAGE_VERSION);

  ATH_CHECK( m_jemMaps.retrieve() );
  ATH_CHECK(  m_errorTool.retrieve() );

  return StatusCode::SUCCESS;
}

// Finalize

StatusCode JepByteStreamV1Tool::finalize()
{
  return StatusCode::SUCCESS;
}

// Conversion bytestream to jet elements

StatusCode JepByteStreamV1Tool::convert(
  const std::string& sgKey,
  const IROBDataProviderSvc::VROBFRAG& robFrags,
  DataVector<LVL1::JetElement>* const jeCollection) const
  JetElementData data (jeCollection);
  return convertBs(sgKey, robFrags, data);
}

// Conversion bytestream to jet hits

StatusCode JepByteStreamV1Tool::convert(
  const std::string& sgKey,
  const IROBDataProviderSvc::VROBFRAG& robFrags,
  DataVector<LVL1::JEMHits>* const hitCollection) const
  JetHitsData data (hitCollection);
  return convertBs(sgKey, robFrags, data);
}

// Conversion bytestream to energy sums

StatusCode JepByteStreamV1Tool::convert(
  const std::string& sgKey,
  const IROBDataProviderSvc::VROBFRAG& robFrags,
  DataVector<LVL1::JEMEtSums>* const etCollection) const
  EnergySumsData data (etCollection);
  return convertBs(sgKey, robFrags, data);
}

// Conversion bytestream to CMM hits

StatusCode JepByteStreamV1Tool::convert(
  const std::string& sgKey,
  const IROBDataProviderSvc::VROBFRAG& robFrags,
  DataVector<LVL1::CMMJetHits>* const hitCollection) const
  CmmHitsData data (hitCollection);
  return convertBs(sgKey, robFrags, data);
}

// Conversion bytestream to CMM energy sums

StatusCode JepByteStreamV1Tool::convert(
  const std::string& sgKey,
  const IROBDataProviderSvc::VROBFRAG& robFrags,
  DataVector<LVL1::CMMEtSums>* const etCollection) const
  CmmSumsData data (etCollection);
  return convertBs(sgKey, robFrags, data);
}

// Conversion of JEP container to bytestream

StatusCode JepByteStreamV1Tool::convert(const LVL1::JEPBSCollectionV1* const jep,
                                        RawEventWrite* const re) const
{
  const bool debug = msgLvl(MSG::DEBUG);
  if (debug) msg(MSG::DEBUG);

  // Clear the event assembler
  FullEventAssembler<L1CaloSrcIdMap> fea;
  fea.clear();
  const uint16_t minorVersion = m_srcIdMap.minorVersionPreLS1();
  fea.setRodMinorVersion(minorVersion);

  // Pointer to ROD data vector

  FullEventAssembler<L1CaloSrcIdMap>::RODDATA* theROD = 0;

  // Jet element key provider
  LVL1::JetElementKey elementKey;

  // Jet element map
  ConstJetElementMap jeMap;
  setupJeMap(jep->JetElements(), jeMap, elementKey);

  // Jet hits map
  ConstJetHitsMap hitsMap;
  setupHitsMap(jep->JetHits(), hitsMap);

  // Energy sums map
  ConstEnergySumsMap etMap;
  setupEtMap(jep->EnergySums(), etMap);

  // CMM hits map
  ConstCmmHitsMap cmmHitsMap;
  setupCmmHitsMap(jep->CmmHits(), cmmHitsMap);

  // CMM energy sums map
  ConstCmmSumsMap cmmEtMap;
  setupCmmEtMap(jep->CmmSums(), cmmEtMap);

  // Loop over data

  const bool neutralFormat = m_dataFormat == L1CaloSubBlock::NEUTRAL;
  const int modulesPerSlink = m_modules / m_slinks;
  int timeslices       = 1;
  int trigJem          = 0;
  int timeslicesNew    = 1;
  int trigJemNew       = 0;
  for (int crate = m_crateMin; crate <= m_crateMax; ++crate) {
    const int hwCrate = crate + m_crateOffsetHw;

    for (int module = 0; module < m_modules; ++module) {

      // Pack required number of modules per slink

      if (module % modulesPerSlink == 0) {
        const int daqOrRoi = 0;
        const int slink = module / modulesPerSlink;
        if (debug) {
          msg() << "Treating crate " << hwCrate
        // Get number of JEM slices and triggered slice offset
        // for this slink
        if ( ! slinkSlices(crate, module, modulesPerSlink,
                           timeslices, trigJem,
                           jeMap,
                           hitsMap,
                           etMap,
                           cmmHitsMap,
                           cmmEtMap,
                           elementKey))
        {
          msg(MSG::ERROR) << "Inconsistent number of slices or "
                          << "triggered slice offsets in data for crate "
                          << hwCrate << " slink " << slink << endmsg;
          return StatusCode::FAILURE;
        timeslicesNew = (m_forceSlices) ? m_forceSlices : timeslices;
        trigJemNew    = ModifySlices::peak(trigJem, timeslices, timeslicesNew);
          msg() << "Data Version/Format: " << m_version
                << "Slices/offset: " << timeslices << " " << trigJem;
          if (timeslices != timeslicesNew) {
            msg() << " modified to " << timeslicesNew << " " << trigJemNew;
        }
        L1CaloUserHeader userHeader;
        userHeader.setJem(trigJemNew);
        const uint32_t rodIdJem = m_srcIdMap.getRodID(hwCrate, slink, daqOrRoi,
                                  m_subDetector);
        theROD = fea.getRodData(rodIdJem);
        theROD->push_back(userHeader.header());
      if (debug) msg() << "Module " << module << endmsg;

      // Create a sub-block for each slice (except Neutral format)

      // Vector for current JEM sub-blocks
      DataVector<JemSubBlockV1> jemBlocks;

      for (int slice = 0; slice < timeslicesNew; ++slice) {
        JemSubBlockV1* const subBlock = new JemSubBlockV1();
        subBlock->setJemHeader(m_version, m_dataFormat, slice,
                               hwCrate, module, timeslicesNew);
        jemBlocks.push_back(subBlock);
        if (neutralFormat) break;
      }

      // Find jet elements corresponding to each eta/phi pair and fill
      // sub-blocks

      for (int chan = 0; chan < m_channels; ++chan) {
        double eta = 0.;
        double phi = 0.;
        int layer = 0;
        if (m_jemMaps->mapping(crate, module, chan, eta, phi, layer)) {
          const LVL1::JetElement* const je = findJetElement(eta, phi, jeMap, elementKey);
          if (je ) {
            std::vector<int> emData;
            std::vector<int> hadData;
            std::vector<int> emErrors;
            std::vector<int> hadErrors;
            ModifySlices::data(je->emEnergyVec(),  emData,    timeslicesNew);
            ModifySlices::data(je->hadEnergyVec(), hadData,   timeslicesNew);
            ModifySlices::data(je->emErrorVec(),   emErrors,  timeslicesNew);
            ModifySlices::data(je->hadErrorVec(),  hadErrors, timeslicesNew);
            for (int slice = 0; slice < timeslicesNew; ++slice) {
              const LVL1::DataError emErrBits(emErrors[slice]);
              const LVL1::DataError hadErrBits(hadErrors[slice]);
              const int index = ( neutralFormat ) ? 0 : slice;
              JemSubBlockV1* const subBlock = jemBlocks[index];
              const JemJetElement jetEle(chan, emData[slice], hadData[slice],
                                         emErrBits.get(LVL1::DataError::Parity),
                                         hadErrBits.get(LVL1::DataError::Parity),
                                         emErrBits.get(LVL1::DataError::LinkDown) +
                                         (hadErrBits.get(LVL1::DataError::LinkDown) << 1));
              subBlock->fillJetElement(slice, jetEle);
      const LVL1::JEMHits* const hits = findJetHits(crate, module, hitsMap);
      if (hits) {
        std::vector<unsigned int> vec;
        ModifySlices::data(hits->JetHitsVec(), vec, timeslicesNew);
        for (int slice = 0; slice < timeslicesNew; ++slice) {
          const int index = ( neutralFormat ) ? 0 : slice;
          JemSubBlockV1* const subBlock = jemBlocks[index];
          subBlock->setJetHits(slice, vec[slice]);
      const LVL1::JEMEtSums* const et = findEnergySums(crate, module, etMap);
      if (et) {
        std::vector<unsigned int> exVec;
        std::vector<unsigned int> eyVec;
        std::vector<unsigned int> etVec;
        ModifySlices::data(et->ExVec(), exVec, timeslicesNew);
        ModifySlices::data(et->EyVec(), eyVec, timeslicesNew);
        ModifySlices::data(et->EtVec(), etVec, timeslicesNew);
        for (int slice = 0; slice < timeslicesNew; ++slice) {
          const int index = ( neutralFormat ) ? 0 : slice;
          JemSubBlockV1* const subBlock = jemBlocks[index];
          subBlock->setEnergySubsums(slice, exVec[slice], eyVec[slice],
                                     etVec[slice]);
      DataVector<JemSubBlockV1>::iterator pos;
      for (pos = jemBlocks.begin(); pos != jemBlocks.end(); ++pos) {
        JemSubBlockV1* const subBlock = *pos;
        if ( !subBlock->pack()) {
          msg(MSG::ERROR) << "JEM sub-block packing failed" << endmsg;
          return StatusCode::FAILURE;
        }
        if (debug) {
          msg() << "JEM sub-block data words: "
        }
        subBlock->write(theROD);
      }
    }

    // Append CMMs to last S-Link of the crate

    // Create a sub-block for each slice (except Neutral format)

    // Vector for current CMM-Energy sub-blocks
    DataVector<CmmEnergySubBlock> cmmEnergyBlocks;
    // Vector for current CMM-Jet sub-blocks
    DataVector<CmmJetSubBlock> cmmJetBlocks;

    const int summing = (crate == m_crates - 1) ? CmmSubBlock::SYSTEM
                        : CmmSubBlock::CRATE;
    for (int slice = 0; slice < timeslicesNew; ++slice) {
      CmmEnergySubBlock* const enBlock = new CmmEnergySubBlock();
      const int cmmEnergyVersion = 2; // with Missing-ET-Sig
      enBlock->setCmmHeader(cmmEnergyVersion, m_dataFormat, slice, hwCrate,
                            summing, CmmSubBlock::CMM_ENERGY,
                            CmmSubBlock::LEFT, timeslicesNew);
      cmmEnergyBlocks.push_back(enBlock);
      CmmJetSubBlock* const jetBlock = new CmmJetSubBlock();
      jetBlock->setCmmHeader(m_version, m_dataFormat, slice, hwCrate,
                             summing, CmmSubBlock::CMM_JET,
                             CmmSubBlock::RIGHT, timeslicesNew);
      cmmJetBlocks.push_back(jetBlock);
      if (neutralFormat) break;
    }

    // CMM-Energy

    int maxDataID = static_cast<int>(LVL1::CMMEtSums::MAXID);
    for (int dataID = 0; dataID < maxDataID; ++dataID) {
      int source = dataID;
      if (dataID >= m_modules) {
        if (summing == CmmSubBlock::CRATE &&
            dataID != LVL1::CMMEtSums::LOCAL) continue;
        // coverity[mixed_enums : FALSE]
        // coverity[switch_on_enum : FALSE]
        // coverity[first_enum_type : FALSE]
        switch (dataID) {
        case LVL1::CMMEtSums::LOCAL:
          source = CmmEnergySubBlock::LOCAL;
          break;
        case LVL1::CMMEtSums::REMOTE:
          source = CmmEnergySubBlock::REMOTE;
          break;
        case LVL1::CMMEtSums::TOTAL:
          source = CmmEnergySubBlock::TOTAL;
          break;
        case LVL1::CMMEtSums::MISSING_ET_MAP:
        case LVL1::CMMEtSums::SUM_ET_MAP:
        case LVL1::CMMEtSums::MISSING_ET_SIG_MAP:
          break;
        default:
          continue;
      const LVL1::CMMEtSums* const sums = findCmmSums(crate, dataID, cmmEtMap);
      if ( sums ) {
        std::vector<unsigned int> ex;
        std::vector<unsigned int> ey;
        std::vector<unsigned int> et;
        std::vector<int> exErr;
        std::vector<int> eyErr;
        std::vector<int> etErr;
        ModifySlices::data(sums->ExVec(), ex, timeslicesNew);
        ModifySlices::data(sums->EyVec(), ey, timeslicesNew);
        ModifySlices::data(sums->EtVec(), et, timeslicesNew);
        ModifySlices::data(sums->ExErrorVec(), exErr, timeslicesNew);
        ModifySlices::data(sums->EyErrorVec(), eyErr, timeslicesNew);
        ModifySlices::data(sums->EtErrorVec(), etErr, timeslicesNew);
        for (int slice = 0; slice < timeslicesNew; ++slice) {
          const LVL1::DataError exErrBits(exErr[slice]);
          const LVL1::DataError eyErrBits(eyErr[slice]);
          const LVL1::DataError etErrBits(etErr[slice]);
          int exError = exErrBits.get(LVL1::DataError::Parity);
          int eyError = eyErrBits.get(LVL1::DataError::Parity);
          int etError = etErrBits.get(LVL1::DataError::Parity);
          if (dataID == LVL1::CMMEtSums::LOCAL ||
              dataID == LVL1::CMMEtSums::REMOTE ||
              dataID == LVL1::CMMEtSums::TOTAL) {
            exError = (exError << 1) + exErrBits.get(LVL1::DataError::Overflow);
            eyError = (eyError << 1) + eyErrBits.get(LVL1::DataError::Overflow);
            etError = (etError << 1) + etErrBits.get(LVL1::DataError::Overflow);
          }
          const int index = ( neutralFormat ) ? 0 : slice;
          CmmEnergySubBlock* const subBlock = cmmEnergyBlocks[index];
          if (dataID == LVL1::CMMEtSums::MISSING_ET_MAP) {
            subBlock->setMissingEtHits(slice, et[slice]);
          } else if (dataID == LVL1::CMMEtSums::SUM_ET_MAP) {
            subBlock->setSumEtHits(slice, et[slice]);
          } else if (dataID == LVL1::CMMEtSums::MISSING_ET_SIG_MAP) {
            subBlock->setMissingEtSigHits(slice, et[slice]);
            subBlock->setSubsums(slice, source,
                                 ex[slice], ey[slice], et[slice],
                                 exError, eyError, etError);
    DataVector<CmmEnergySubBlock>::iterator pos;
    pos = cmmEnergyBlocks.begin();
    for (; pos != cmmEnergyBlocks.end(); ++pos) {
      CmmEnergySubBlock* const subBlock = *pos;
      if ( !subBlock->pack()) {
        msg(MSG::ERROR) << "CMM-Energy sub-block packing failed" << endmsg;
        return StatusCode::FAILURE;
        msg() << "CMM-Energy sub-block data words: "
      }
      subBlock->write(theROD);
    }

    // CMM-Jet

    maxDataID = static_cast<int>(LVL1::CMMJetHits::MAXID);
    for (int dataID = 0; dataID < maxDataID; ++dataID) {
      int source = dataID;
      if (dataID >= m_modules) {
        if (summing == CmmSubBlock::CRATE &&
            dataID != LVL1::CMMJetHits::LOCAL_MAIN &&
            dataID != LVL1::CMMJetHits::LOCAL_FORWARD) continue;
        // coverity[mixed_enums : FALSE]
        // coverity[switch_on_enum : FALSE]
        // coverity[first_enum_type : FALSE]
        switch (dataID) {
        case LVL1::CMMJetHits::LOCAL_MAIN:
          source = CmmJetSubBlock::LOCAL_MAIN;
          break;
        case LVL1::CMMJetHits::REMOTE_MAIN:
          source = CmmJetSubBlock::REMOTE_MAIN;
          break;
        case LVL1::CMMJetHits::TOTAL_MAIN:
          source = CmmJetSubBlock::TOTAL_MAIN;
          break;
        case LVL1::CMMJetHits::LOCAL_FORWARD:
          source = CmmJetSubBlock::LOCAL_FORWARD;
          break;
        case LVL1::CMMJetHits::REMOTE_FORWARD:
          source = CmmJetSubBlock::REMOTE_FORWARD;
          break;
        case LVL1::CMMJetHits::TOTAL_FORWARD:
          source = CmmJetSubBlock::TOTAL_FORWARD;
          break;
        case LVL1::CMMJetHits::ET_MAP:
          break;
        default:
          continue;
      const LVL1::CMMJetHits* const ch = findCmmHits(crate, dataID, cmmHitsMap);
      if ( ch ) {
        std::vector<unsigned int> hits;
        std::vector<int> errs;
        ModifySlices::data(ch->HitsVec(),  hits, timeslicesNew);
        ModifySlices::data(ch->ErrorVec(), errs, timeslicesNew);
        for (int slice = 0; slice < timeslicesNew; ++slice) {
          const LVL1::DataError errBits(errs[slice]);
          const int index = ( neutralFormat ) ? 0 : slice;
          CmmJetSubBlock* const subBlock = cmmJetBlocks[index];
          if (dataID == LVL1::CMMJetHits::ET_MAP) {
            subBlock->setJetEtMap(slice, hits[slice]);
            subBlock->setJetHits(slice, source, hits[slice],
                                 errBits.get(LVL1::DataError::Parity));
    DataVector<CmmJetSubBlock>::iterator jos;
    jos = cmmJetBlocks.begin();
    for (; jos != cmmJetBlocks.end(); ++jos) {
      CmmJetSubBlock* const subBlock = *jos;
      if ( !subBlock->pack()) {
        msg(MSG::ERROR) << "CMM-Jet sub-block packing failed" << endmsg;
        return StatusCode::FAILURE;
        msg() << "CMM-Jet sub-block data words: "

  return StatusCode::SUCCESS;
}

// Return reference to vector with all possible Source Identifiers

std::vector<uint32_t> JepByteStreamV1Tool::makeSourceIDs() const
  std::vector<uint32_t> sourceIDs;
  if (!m_sourceIDsProp.empty()) {
    sourceIDs = m_sourceIDsProp;
  }
  else {
    const int maxCrates = m_crates + m_crateOffsetHw;
    const int maxSlinks = m_srcIdMap.maxSlinks();
    for (int hwCrate = m_crateOffsetHw; hwCrate < maxCrates; ++hwCrate)
    {
      for (int slink = 0; slink < maxSlinks; ++slink)
      {
        const uint32_t rodId = m_srcIdMap.getRodID(hwCrate, slink, daqOrRoi,
                                                   m_subDetector);
        const uint32_t robId = m_srcIdMap.getRobID(rodId);
        sourceIDs.push_back(robId);
  return sourceIDs;
}

const std::vector<uint32_t>& JepByteStreamV1Tool::sourceIDs() const
{
  static const std::vector<uint32_t> sourceIDs = makeSourceIDs();
  return sourceIDs;
}

// Convert bytestream to given container type

StatusCode JepByteStreamV1Tool::convertBs(
  const std::string& sgKey,
  const IROBDataProviderSvc::VROBFRAG& robFrags,
  JepByteStreamToolData& data) const
  LocalData ld;

  // Check if overlap jet element channels wanted
  const std::string flag("Overlap");
  const std::string::size_type pos = sgKey.find(flag);
  ld.coreOverlap =
    (pos == std::string::npos || pos != sgKey.length() - flag.length()) ? 0 : 1;

  const bool debug = msgLvl(MSG::DEBUG);
  if (debug) msg(MSG::DEBUG);

  // JemSubBlock for unpacking
  JemSubBlockV1 jemSubBlock;
  // CmmEnergySubBlock for unpacking
  CmmEnergySubBlock cmmEnergySubBlock;
  // CmmJetSubBlock for unpacking
  CmmJetSubBlock cmmJetSubBlock;

   // Loop over ROB fragments

  int robCount = 0;
  std::set<uint32_t> dupCheck;
  ROBIterator rob    = robFrags.begin();
  ROBIterator robEnd = robFrags.end();
  for (; rob != robEnd; ++rob) {

    if (debug) {
      ++robCount;
      msg() << "Treating ROB fragment " << robCount << endmsg;
    }

    // Skip fragments with ROB status errors

    uint32_t robid = (*rob)->source_id();
    if ((*rob)->nstatus() > 0) {
      ROBPointer robData;
      (*rob)->status(robData);
      if (*robData != 0) {
        m_errorTool->robError(robid, *robData);
        if (debug) msg() << "ROB status error - skipping fragment" << endmsg;
        continue;
      }
    }

    // Skip duplicate fragments

    if (!dupCheck.insert(robid).second) {
      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_DUPLICATE_ROB);
      if (debug) msg() << "Skipping duplicate ROB fragment" << endmsg;
      continue;
    }

    // Unpack ROD data (slinks)

    RODPointer payloadBeg;
    RODPointer payload;
    RODPointer payloadEnd;
    (*rob)->rod_data(payloadBeg);
    payloadEnd = payloadBeg + (*rob)->rod_ndata();
    payload = payloadBeg;
    if (payload == payloadEnd) {
      if (debug) msg() << "ROB fragment empty" << endmsg;
      continue;
    }

    // Check identifier
    const uint32_t sourceID = (*rob)->rod_source_id();
    if (m_srcIdMap.getRobID(sourceID) != robid           ||
        m_srcIdMap.subDet(sourceID)   != m_subDetector   ||
        m_srcIdMap.daqOrRoi(sourceID) != 0               ||
        m_srcIdMap.slink(sourceID)    >= m_slinks        ||
        m_srcIdMap.crate(sourceID)    <  m_crateOffsetHw ||
        m_srcIdMap.crate(sourceID)    >= m_crateOffsetHw + m_crates) {
      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_ROD_ID);
      if (debug) {
        msg() << "Wrong source identifier in data: ROD "
              << MSG::hex << sourceID << "  ROB " << robid
      }
      continue;
    }

    // Check minor version
    const int minorVersion = (*rob)->rod_version() & 0xffff;
    if (minorVersion > m_srcIdMap.minorVersionPreLS1()) {
      if (debug) msg() << "Skipping post-LS1 data" << endmsg;
    const int rodCrate = m_srcIdMap.crate(sourceID);
      msg() << "Treating crate " << rodCrate
            << " slink " << m_srcIdMap.slink(sourceID) << endmsg;
    }

    // First word should be User Header
    if ( !L1CaloUserHeader::isValid(*payload) ) {
      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
      if (debug) msg() << "Invalid or missing user header" << endmsg;
      continue;
    }
    L1CaloUserHeader userHeader(*payload);
    userHeader.setVersion(minorVersion);
    const int headerWords = userHeader.words();
    if (headerWords != 1) {
      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
      if (debug) msg() << "Unexpected number of user header words: "
      continue;
    }
    for (int i = 0; i < headerWords; ++i) ++payload;
    // triggered slice offsets
    int trigJem = userHeader.jem();
    int trigCmm = userHeader.jepCmm();
    if (debug) {
      msg() << "Minor format version number: " << MSG::hex
            << minorVersion << MSG::dec << endmsg
            << "JEM triggered slice offset: " << trigJem << endmsg
            << "CMM triggered slice offset: " << trigCmm << endmsg;
    }
    if (trigJem != trigCmm) {
      const int newTrig = (trigJem > trigCmm) ? trigJem : trigCmm;
      trigJem = newTrig;
      trigCmm = newTrig;
      if (debug) msg() << "Changed both offsets to " << newTrig << endmsg;
    ld.rodErr = L1CaloSubBlock::ERROR_NONE;
      if (L1CaloSubBlock::wordType(*payload) != L1CaloSubBlock::HEADER) {
        if (debug) msg() << "Unexpected data sequence" << endmsg;
        ld.rodErr = L1CaloSubBlock::ERROR_MISSING_HEADER;
        break;
      }
      if (CmmSubBlock::cmmBlock(*payload)) {
        // CMMs
        if (CmmSubBlock::cmmType(*payload) == CmmSubBlock::CMM_JET) {
          cmmJetSubBlock.clear();
          payload = cmmJetSubBlock.read(payload, payloadEnd);
          if (cmmJetSubBlock.crate() != rodCrate) {
            if (debug) msg() << "Inconsistent crate number in ROD source ID"
            ld.rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
            break;
          if (data.m_collection == CMM_HITS) {
            decodeCmmJet(&cmmJetSubBlock, trigCmm, static_cast<CmmHitsData&>(data), ld);
            if (ld.rodErr != L1CaloSubBlock::ERROR_NONE) {
              if (debug) msg() << "decodeCmmJet failed" << endmsg;
          }
        } else if (CmmSubBlock::cmmType(*payload) == CmmSubBlock::CMM_ENERGY) {
          cmmEnergySubBlock.clear();
          payload = cmmEnergySubBlock.read(payload, payloadEnd);
          if (cmmEnergySubBlock.crate() != rodCrate) {
            if (debug) msg() << "Inconsistent crate number in ROD source ID"
            ld.rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
            break;
          if (data.m_collection == CMM_SUMS) {
            decodeCmmEnergy(&cmmEnergySubBlock, trigCmm, static_cast<CmmSumsData&>(data), ld);
            if (ld.rodErr != L1CaloSubBlock::ERROR_NONE) {
              if (debug) msg() << "decodeCmmEnergy failed" << endmsg;
        } else {
          if (debug) msg() << "Invalid CMM type in module field" << endmsg;
          ld.rodErr = L1CaloSubBlock::ERROR_MODULE_NUMBER;
          break;
        jemSubBlock.clear();
        payload = jemSubBlock.read(payload, payloadEnd);
        if (jemSubBlock.crate() != rodCrate) {
          if (debug) msg() << "Inconsistent crate number in ROD source ID"
          ld.rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
          break;
        if (data.m_collection == JET_ELEMENTS || data.m_collection == JET_HITS ||
            data.m_collection == ENERGY_SUMS) {
          decodeJem(&jemSubBlock, trigJem, data, ld);
          if (ld.rodErr != L1CaloSubBlock::ERROR_NONE) {
            if (debug) msg() << "decodeJem failed" << endmsg;
    if (ld.rodErr != L1CaloSubBlock::ERROR_NONE)
      m_errorTool->rodError(robid, ld.rodErr);
  }

  return StatusCode::SUCCESS;
}

// Unpack CMM-Energy sub-block

void JepByteStreamV1Tool::decodeCmmEnergy(CmmEnergySubBlock* subBlock,
                                          CmmSumsData& data,
                                          LocalData& ld) const
{
  const bool debug = msgLvl(MSG::DEBUG);
  if (debug) msg(MSG::DEBUG);

  const int hwCrate    = subBlock->crate();
  const int module     = subBlock->cmmPosition();
  const int firmware   = subBlock->cmmFirmware();
  const int summing    = subBlock->cmmSumming();
  const int timeslices = subBlock->timeslices();
  const int sliceNum   = subBlock->slice();
  if (debug) {
    msg() << "CMM-Energy: Crate " << hwCrate
          << "  Module "          << module
          << "  Firmware "        << firmware
          << "  Summing "         << summing
  }
  if (timeslices <= trigCmm) {
    if (debug) msg() << "Triggered CMM slice from header "
                       << "inconsistent with number of slices: "
                       << trigCmm << ", " << timeslices << endmsg;
    ld.rodErr = L1CaloSubBlock::ERROR_SLICES;
    return;
  }
  if (timeslices <= sliceNum) {
    if (debug) msg() << "Total slices inconsistent with slice number: "
                       << timeslices << ", " << sliceNum << endmsg;
    ld.rodErr = L1CaloSubBlock::ERROR_SLICES;
    return;
  }
  // Unpack sub-block
  if (subBlock->dataWords() && !subBlock->unpack()) {
    if (debug) {
      std::string errMsg(subBlock->unpackErrorMsg());
      msg() << "CMM-Energy sub-block unpacking failed: " << errMsg << endmsg;
    ld.rodErr = subBlock->unpackErrorCode();
    return;
  }

  // Retrieve required data

  const bool neutralFormat = subBlock->format() == L1CaloSubBlock::NEUTRAL;
  const int crate    = hwCrate - m_crateOffsetHw;
  const int swCrate  = crate   + m_crateOffsetSw;
  const int maxSid   = static_cast<int>(CmmEnergySubBlock::MAX_SOURCE_ID);
  LVL1::DataError derr;
  derr.set(LVL1::DataError::SubStatusWord, subBlock->subStatus());
  const int ssError = derr.error();
  const int sliceBeg = ( neutralFormat ) ? 0          : sliceNum;
  const int sliceEnd = ( neutralFormat ) ? timeslices : sliceNum + 1;
  for (int slice = sliceBeg; slice < sliceEnd; ++slice) {

    // Energy sums

    for (int source = 0; source < maxSid; ++source) {
      int dataID = source;
      if (source >= m_modules) {
        if (summing == CmmSubBlock::CRATE &&
            source != CmmEnergySubBlock::LOCAL) continue;
        switch (source) {
        case CmmEnergySubBlock::LOCAL:
          dataID = LVL1::CMMEtSums::LOCAL;
          break;
        case CmmEnergySubBlock::REMOTE:
          dataID = LVL1::CMMEtSums::REMOTE;
          break;
        case CmmEnergySubBlock::TOTAL:
          dataID = LVL1::CMMEtSums::TOTAL;
          break;
        default:
          continue;
        }
      }
      const unsigned int ex = subBlock->ex(slice, source);
      const unsigned int ey = subBlock->ey(slice, source);
      const unsigned int et = subBlock->et(slice, source);
      int exErr = subBlock->exError(slice, source);
      int eyErr = subBlock->eyError(slice, source);
      int etErr = subBlock->etError(slice, source);
      LVL1::DataError exErrBits(ssError);
      LVL1::DataError eyErrBits(ssError);
      LVL1::DataError etErrBits(ssError);
      if (dataID == LVL1::CMMEtSums::LOCAL ||
          dataID == LVL1::CMMEtSums::REMOTE ||
          dataID == LVL1::CMMEtSums::TOTAL) {
        exErrBits.set(LVL1::DataError::Overflow, exErr);
        exErrBits.set(LVL1::DataError::Parity,   exErr >> 1);
        eyErrBits.set(LVL1::DataError::Overflow, eyErr);
        eyErrBits.set(LVL1::DataError::Parity,   eyErr >> 1);
        etErrBits.set(LVL1::DataError::Overflow, etErr);
        etErrBits.set(LVL1::DataError::Parity,   etErr >> 1);
      } else {
        exErrBits.set(LVL1::DataError::Parity, exErr);
        eyErrBits.set(LVL1::DataError::Parity, eyErr);
        etErrBits.set(LVL1::DataError::Parity, etErr);
      }
      exErr = exErrBits.error();
      eyErr = eyErrBits.error();
      etErr = etErrBits.error();
      if (ex || ey || et || exErr || eyErr || etErr) {
        LVL1::CMMEtSums* sums = findCmmSums(data, crate, dataID);
        if ( ! sums ) {   // create new CMM energy sums
          ld.exVec.assign(timeslices, 0);
          ld.eyVec.assign(timeslices, 0);
          ld.etVec.assign(timeslices, 0);
          ld.exErrVec.assign(timeslices, 0);
          ld.eyErrVec.assign(timeslices, 0);
          ld.etErrVec.assign(timeslices, 0);
          ld.exVec[slice] = ex;
          ld.eyVec[slice] = ey;
          ld.etVec[slice] = et;
          ld.exErrVec[slice] = exErr;
          ld.eyErrVec[slice] = eyErr;
          ld.etErrVec[slice] = etErr;
            std::make_unique<LVL1::CMMEtSums>(swCrate, dataID, ld.etVec, ld.exVec, ld.eyVec,
                                              ld.etErrVec, ld.exErrVec, ld.eyErrVec, trigCmm);
          const int key = crate * 100 + dataID;
          data.m_cmmEtMap.insert(std::make_pair(key, sumsp.get()));
          data.m_cmmEtCollection->push_back(std::move(sumsp));
          ld.exVec = sums->ExVec();
          ld.eyVec = sums->EyVec();
          ld.etVec = sums->EtVec();
          ld.exErrVec = sums->ExErrorVec();
          ld.eyErrVec = sums->EyErrorVec();
          ld.etErrVec = sums->EtErrorVec();
          const int nsl = ld.exVec.size();
          if (timeslices != nsl) {
            if (debug) msg() << "Inconsistent number of slices in sub-blocks"
            ld.rodErr = L1CaloSubBlock::ERROR_SLICES;
            return;
          if (ld.exVec[slice] != 0 || ld.eyVec[slice] != 0 || ld.etVec[slice] != 0 ||
              ld.exErrVec[slice] != 0 || ld.eyErrVec[slice] != 0 ||
              ld.etErrVec[slice] != 0) {
            if (debug) msg() << "Duplicate data for slice " << slice << endmsg;
            ld.rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
            return;
          ld.exVec[slice] = ex;
          ld.eyVec[slice] = ey;
          ld.etVec[slice] = et;
          ld.exErrVec[slice] = exErr;
          ld.eyErrVec[slice] = eyErr;
          ld.etErrVec[slice] = etErr;
          sums->addEx(ld.exVec, ld.exErrVec);
          sums->addEy(ld.eyVec, ld.eyErrVec);
          sums->addEt(ld.etVec, ld.etErrVec);
        }
      }
    }

    // Hit maps - store as Et

    if (summing == CmmSubBlock::SYSTEM) {
      const unsigned int missEt = subBlock->missingEtHits(slice);
      if ( missEt || ssError ) {
        const int dataID = LVL1::CMMEtSums::MISSING_ET_MAP;
        LVL1::CMMEtSums* map = findCmmSums(data, crate, dataID);
          ld.etVec.assign(timeslices, 0);
          ld.etErrVec.assign(timeslices, 0);
          ld.etVec[slice]    = missEt;
          ld.etErrVec[slice] = ssError;
          auto mapp =
            std::make_unique<LVL1::CMMEtSums>(swCrate, dataID,
                                              ld.etVec, ld.etVec, ld.etVec,
                                              ld.etErrVec, ld.etErrVec, ld.etErrVec, trigCmm);
          const int key = crate * 100 + dataID;
          data.m_cmmEtMap.insert(std::make_pair(key, mapp.get()));
          data.m_cmmEtCollection->push_back(std::move(mapp));
          ld.etVec    = map->EtVec();
          ld.etErrVec = map->EtErrorVec();
          const int nsl = ld.etVec.size();
          if (timeslices != nsl) {
            if (debug) msg() << "Inconsistent number of slices in sub-blocks"
            ld.rodErr = L1CaloSubBlock::ERROR_SLICES;
            return;
          if (ld.etVec[slice] != 0 || ld.etErrVec[slice] != 0) {
            if (debug) msg() << "Duplicate data for slice " << slice << endmsg;
            ld.rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
            return;