/* Copyright (C) 2022 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only Authors: Dominik Smith [committer] */ #include "UnpackMS.h" #include "AlgoFairloggerCompat.h" #include "CbmTofAddress.h" #include <cassert> #include <cmath> #include <sstream> #include <utility> #include <vector> using std::unique_ptr; using std::vector; namespace cbm::algo::bmon { UnpackMS::UnpackMS(const UnpackPar& pars) : fParams(pars) {} UnpackMS::~UnpackMS() = default; // ---- Algorithm execution --------------------------------------------- UnpackMS::Result_t UnpackMS::operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr, const uint64_t tTimeslice) const { // --- Output data Result_t result = {}; L_(debug) << "EQ ID 0x" << std::hex << msDescr.eq_id << std::dec; // --- Number of messages in microslice auto msSize = msDescr.size; if (msSize % sizeof(critof001::Message) != 0) { std::get<1>(result).fNumErrInvalidMsSize++; return result; } const uint32_t numMessages = msSize / sizeof(critof001::Message); if (numMessages < 2) { std::get<1>(result).fNumErrInvalidMsSize++; return result; } // --- Interpret MS content as sequence of SMX messages auto message = reinterpret_cast<const critof001::Message*>(msContent); const uint32_t maxDigis = numMessages - 2; // -2 for the TS_MSB and EPOCH messages std::get<0>(result).reserve(maxDigis); // --- The first message in the MS is expected to be of type EPOCH. if (message[0].getMessageType() != critof001::MSG_EPOCH) { std::get<1>(result).fNumErrInvalidFirstMessage++; return result; } { // --- Check that first epoch matches with the microslice index const uint64_t msStartEpoch = static_cast<uint64_t>(msDescr.idx / critof001::kuEpochInNs) % critof001::kulEpochCycleEp; if (message[0].getGdpbEpEpochNb() != msStartEpoch) { std::get<1>(result).fNumErrInvalidStartEpoch++; return result; } } TimeSpec time; // --- Current Timeslice start time in epoch units. Note that it is always a multiple of epochs // --- and the epoch is a multiple of ns. time.currentTsTime = static_cast<uint64_t>(tTimeslice / critof001::kuEpochInNs) % critof001::kulEpochCycleEp; ProcessEpochMessage(message[0], time); // --- The last message in the MS is expected to be EndOfMs. if (!message[numMessages - 1].isEndOfMs()) { std::get<1>(result).fNumErrInvalidLastMessage++; return result; } //Check if last message is "EndOfMs"!! Maybe loop to messageNr < numMessages - 1 // --- Message loop for (uint32_t messageNr = 1; messageNr < numMessages; messageNr++) { // --- Action depending on message type switch (message[messageNr].getMessageType()) { case critof001::MSG_HIT: { ProcessHitMessage(message[messageNr], time, std::get<0>(result), std::get<1>(result)); break; } case critof001::MSG_EPOCH: { if (critof001::kuChipIdMergedEpoch == message[messageNr].getGet4Idx()) { ProcessEpochMessage(message[messageNr], time); } // if this epoch message is a merged one valid for all chips else { /// FIXME: Should be checked in/forwarded to <some> monitor <task?>, here we just jump it std::get<1>(result).fNumErrInvalidAsicEpochs++; continue; } // if single chip epoch message break; } case critof001::MSG_SLOWC: { std::get<1>(result).fNumNonHitOrTsbMessage++; break; } case critof001::MSG_SYST: { std::get<1>(result).fNumNonHitOrTsbMessage++; break; } default: { std::get<1>(result).fNumNonHitOrTsbMessage++; break; } } //? Message type } //# Messages return result; } // -------------------------------------------------------------------------- // ----- Process hit message -------------------------------------------- inline void UnpackMS::ProcessHitMessage(const critof001::Message& message, const TimeSpec& time, vector<CbmBmonDigi>& digiVec, UnpackMonitorData& monitor) const { // IGNORES: // - Duplicate messages // - (fviRpcChUId.size() < uRemappedChannelNrInSys) // - successive digis with same time // (these are filtered in original version but not here) // also: does not apply new "remap digis" hack, and always includes timeslice overlap // --- Check eLink and get parameters const uint32_t elink = message.getGet4Idx(); if (elink >= fParams.fElinkParams.size()) { monitor.fNumErrElinkOutOfRange++; return; } const UnpackElinkPar& elinkPar = fParams.fElinkParams.at(elink); const uint32_t channel = message.getGdpbHitChanId(); const uint32_t channelUId = (elinkPar.fChannelUId)[channel]; L_(debug) << "GET4 Idx " << elink << " channel " << channel << " 0x" << std::hex << channelUId << std::dec; if (0 < channelUId) { double messageTime = message.getMsgFullTimeD(time.currentEpochInTs) - elinkPar.fTimeOffset; const double charge = (double) message.getGdpbHit32Tot(); //cast from uint32_t // --- Create output digi digiVec.emplace_back(channelUId, messageTime, charge); } else { L_(debug) << "Ignoring digi on unmapped channel"; } } // -------------------------------------------------------------------------- // ----- Process an epoch message --------------------------------------- inline void UnpackMS::ProcessEpochMessage(const critof001::Message& message, TimeSpec& time) const { const uint64_t epoch = message.getGdpbEpEpochNb(); // --- Calculate epoch relative to timeslice start time; correct for epoch cycles if (time.currentTsTime <= epoch) { time.currentEpochInTs = epoch - time.currentTsTime; } else { time.currentEpochInTs = epoch + critof001::kulEpochCycleEp - time.currentTsTime; } //Problem if MS spans multiple epoch cycles? } // -------------------------------------------------------------------------- } // namespace cbm::algo::bmon