/// \brief Implementation of a high performance DDL decoder for the muon tracking stations.
///
/// This file implementes the AliMUONTrackerDDLDecoder class, which contains
-/// the core logic for decoding the payload in DDL streams comming from the muon
+/// the core logic for decoding the payload in DDL streams coming from the muon
/// spectrometer's tracking chambers in a very efficient manner.
///
/// This implementation is derived from work done by Christian Finck for the
///
/// This class implements a high performance decoder for decoding DDL payload
/// data coming from the muon spectrometers tracking chambers.
-/// It has been implemented using the event driven paradigm, which allows us
-/// to minimise the number of method calls made in the inner loops of the algorithm
-/// and minimise the memory footprint.
+/// It has been implemented using the event driven paradigm with templates,
+/// which allows us to minimise the number of method calls made in the inner
+/// loops of the algorithm and minimise the memory footprint. At least for
+/// optimised production compilations.
/// The decoder class only contains the basic decoding and error checking logic.
/// It calls methods such as OnNewBlock, OnNewBusPatch, OnData etc in
/// the event handler during the decoding to return the decoded data.
/// The event handler class is nothing more than a callback interface to deliver
/// the next chunks of decoded data.
-/// To actually do something with the data one needs to implement a custom
+/// To actually do something with the data, one needs to implement a custom
/// event handler (callback) class by inheriting from AliMUONTrackerDDLDecoderEventHandler
/// and overriding the callback methods like so:
/// \code
/// class MyCustomHandler : public AliMUONTrackerDDLDecoderEventHandler
/// {
/// public:
-/// void OnData(UInt_t data)
+/// void OnData(UInt_t data, bool parityError)
/// {
-/// // I can do something with 'data' here.
+/// // I can do something with 'data' here and check if there was
+/// // a parity error with 'parityError'.
/// }
/// };
/// \endcode
///
-/// Once the custom handler is written then the decoder is in instantiated as
-/// shown below, to use your new custom handler. And to start decoding one needs
+/// Once the custom handler is written then the decoder is instantiated as
+/// shown below, to use your new custom handler. Also to start decoding one needs
/// to call the Decode() method of the decoder.
/// \code
/// AliMUONTrackerDDLDecoder<MyCustomHandler> myDecoder;
/// \endcode
///
/// Note that this class was written as a template on purpose. To maximise the
-/// compilers chance to make optimisations and inlining code must use a template.
-/// Depending on exactly what you do inside your handler the decoder will be
-/// significantly slower if run time polymorphism was used i.e. making the class
+/// compilers chance to make optimisations and inline the code we must use a template.
+/// Depending on exactly what you do inside your handler, the decoder could be
+/// significantly slower if run time polymorphism was used, i.e. making the class
/// AliMUONTrackerDDLDecoderEventHandler abstract and using virtual methods.
///
+/// There has been a change to the data format that the real detector generates.
+/// Two trailer words are added to the end of the DDL payload which indicated
+/// the end of data. The decoder is initialised by default to automatically
+/// check for these and deal with it correctly, if they exist or not.
+/// However, if you want to override this behaviour then set the flag
+/// fAutoDetectTrailer to false with AutoDetectTrailer(false). Then if you have
+/// data with the old data format you should set fCheckForTrailer to false with
+/// CheckForTrailer(false), otherwise for real data it should be
+/// fCheckForTrailer = true. Only when fAutoDetectTrailer is true will the
+/// fCheckForTrailer flag be ignored and no warnings will be generated for an
+/// incorrect data format.
+///
/// \author Artur Szostak <artursz@iafrica.com>
template <class EventHandler>
AliMUONTrackerDDLDecoder() :
fExitOnError(true), fTryRecover(false),
fSendDataOnParityError(false), fHadError(false),
+ fAutoDetectTrailer(true), fCheckForTrailer(true),
fMaxBlocks(2), fMaxDSPs(5), fMaxBusPatches(5), fHandler()
{}
/// Returns the "try to recover from errors" flag.
/// i.e. should the decoder try to recover from errors found in the
- /// payload headers.
+ /// payload headers or trailers.
bool TryRecover() const { return fTryRecover; }
/// Sets the "try to recover from errors" flag.
/// i.e. should the decoder try to recover from errors found in the
- /// payload headers.
+ /// payload headers or trailers.
void TryRecover(bool value) { fTryRecover = value; }
/// Returns the flag indicating if the raw data words in the bus patches
/// structure within the DDL payload.
void MaxBusPatches(UInt_t n) { fMaxBusPatches = n; }
+ /// Returns the value of the auto-detect trailer flag.
+ bool AutoDetectTrailer() const { return fAutoDetectTrailer; }
+
+ /// Sets the value of the auto-detect trailer flag.
+ void AutoDetectTrailer(bool value) { fAutoDetectTrailer = value; }
+
+ /// Returns the value of the flag to check for the end of DDL trailer.
+ bool CheckForTrailer() const { return fCheckForTrailer; }
+
+ /// Sets the value of the flag to check for the end of DDL trailer.
+ void CheckForTrailer(bool value) { fCheckForTrailer = value; }
+
/// This method decodes the DDL payload contained in the buffer.
bool Decode(const void* buffer, UInt_t bufferSize);
+ /// Returns the block marker key.
+ static UInt_t BlockDataKeyWord() { return fgkBlockDataKey; }
+
+ /// Returns the DSP marker key.
+ static UInt_t DspDataKeyWord() { return fgkDSPDataKey; }
+
+ /// Returns the bus patch marker key.
+ static UInt_t BusPatchDataKeyWord() { return fgkBusPatchDataKey; }
+
+ /// Returns the expected padding word value.
+ static UInt_t PaddingWord() { return fgkPaddingWord; }
+
+ /// Returns the expected end of DDL marker.
+ static UInt_t EndOfDDLWord() { return fgkEndOfDDL; }
+
private:
bool fExitOnError; ///< Indicates if we should exit on the very first error.
- bool fTryRecover; ///< Indicates if we should try recover from a corrupt structure header.
+ bool fTryRecover; ///< Indicates if we should try recover from a corrupt structure header or DDL trailer.
bool fSendDataOnParityError; ///< If set to true then we issue a OnData() event even if the data word had a parity error.
bool fHadError; ///< Indicates if we had an error decoding the data.
+ bool fAutoDetectTrailer; ///< Indicates if we should automatically check for the end of DDL trailer (Default = true).
+ bool fCheckForTrailer; ///< Indicates if we should check for the end of DDL trailer (Default = true). This flag is ignored if fAutoDetectTrailer is true.
UInt_t fMaxBlocks; ///< Maximum number of block structures allowed in a DDL stream.
UInt_t fMaxDSPs; ///< Maximum number of DSP structures allowed in a DDL stream.
UInt_t fMaxBusPatches; ///< Maximum number of bus patch structures allowed in a DDL stream.
static const UInt_t fgkDSPDataKey; ///< The key word expected to identify DSP structure headers.
static const UInt_t fgkBusPatchDataKey; ///< The key word expected to identify bus patch headers.
static const UInt_t fgkPaddingWord; ///< The expected format of the padding word in the DDL payload.
+ static const UInt_t fgkEndOfDDL; ///< The end of DDL trailer word.
};
//_____________________________________________________________________________
const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkBusPatchDataKey = 0xB000000B;
template <class EventHandler>
const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkPaddingWord = 0xBEEFFACE;
+template <class EventHandler>
+const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkEndOfDDL = 0xD000000D;
template <class EventHandler>
/// flag is set to true. There is also an optional flag fTryRecover which
/// can enable logic which will attempt to recover the header structures found
/// in the DDL payload if they are found to be inconsistent (assumed corrupt).
+ /// fTryRecover set to true will also enable recovery from a corrupt
+ /// DDL trailer marking the end of DDL payload.
///
/// \param buffer This is the pointer to the start of the memory buffer
/// containing the DDL payload. Remember that this must be the start of
/// \param start This is the pointer to the start of the buffer.
/// \param end This is the pointer to the first byte just past the
/// end of the buffer.
- /// \return If the blocks in the buffer were decoded without errors
- /// or we could recover from the errors, then true is returned.
- /// False is returned otherwise.
+ /// fHadError is set to true if there were any errors decoding the buffer
+ /// and the OnError method of the callback event handler is called for
+ /// each error.
const UChar_t* current = start;
+ const UInt_t* bufferStart = reinterpret_cast<const UInt_t*>(start);
+ const UInt_t* bufferEnd = reinterpret_cast<const UInt_t*>(end);
+ bool problemWithTrailer = false;
+
+ // The DDL payload normally has a 2 word trailer which contains the end of
+ // DDL markers 0xD000000D. But this is not the case for older simulated
+ // data so if we are auto-detecting the trailer then we need to carefully
+ // check if these words are there or not.
+ const UChar_t* endOfBlocks = end;
+ const UInt_t* trailerWords = reinterpret_cast<const UInt_t*>(end) - 2;
+ if (fAutoDetectTrailer)
+ {
+ if (trailerWords >= bufferStart and *trailerWords == fgkEndOfDDL
+ and *(trailerWords+1) == fgkEndOfDDL
+ )
+ {
+ // Found the trailer so reposition the end of blocks marker.
+ endOfBlocks = reinterpret_cast<const UChar_t*>(trailerWords);
+ }
+ // else assume we are dealing with the older data format.
+ }
+ else if (fCheckForTrailer)
+ {
+ if (trailerWords >= bufferStart and *trailerWords == fgkEndOfDDL
+ and *(trailerWords+1) == fgkEndOfDDL
+ )
+ {
+ // Found the trailer so reposition the end of blocks marker.
+ endOfBlocks = reinterpret_cast<const UChar_t*>(trailerWords);
+ }
+ else
+ {
+ if (trailerWords+1 >= bufferStart and *(trailerWords+1) == fgkEndOfDDL)
+ fHandler.OnError(EventHandler::kTooFewDDLTrailerWords, trailerWords+1);
+ else if (trailerWords >= bufferStart and *(trailerWords) == fgkEndOfDDL)
+ fHandler.OnError(EventHandler::kTooFewDDLTrailerWords, trailerWords);
+ else
+ fHandler.OnError(EventHandler::kNoDDLTrailerWords, end);
+
+ // Stop the decoding if so requested by the user, otherwise
+ // remember about the error so that we return false from the
+ // Decode() method and continue decoding.
+ fHadError = true;
+ if (fExitOnError) return;
+
+ // Mark that there was a problem with the trailer so that
+ // for subsequent errors we try to deal with this better.
+ problemWithTrailer = true;
+
+ // We can also try figure out how many trailer words there
+ // actually are and move the end of blocks marker back.
+
+ if (fTryRecover)
+ {
+ trailerWords = bufferEnd;
+ // There should only be a max of 2 trailer words.
+ if (*(trailerWords-1) == fgkEndOfDDL)
+ trailerWords--;
+ else if (*(trailerWords-1) == fgkEndOfDDL)
+ trailerWords--;
+ endOfBlocks = reinterpret_cast<const UChar_t*>(trailerWords);
+ }
+ }
+ }
UInt_t blockCount = 0; // Indicates the number of blocks decoded.
- while (current < end)
+ while (current < endOfBlocks)
{
// Mark the start of the block structure.
const UChar_t* blockStart = current;
const AliMUONBlockHeaderStruct* blockHeader
= reinterpret_cast<const AliMUONBlockHeaderStruct*>(blockStart);
current += sizeof(AliMUONBlockHeaderStruct);
- if (current > end)
+ if (current > endOfBlocks)
{
+ // We first check if we actually hit the end of DDL markers
+ // If we did then either we did not/could not recover from
+ // a corrupt trailer or we did not detect a correct trailer
+ // in auto-detect mode.
+ trailerWords = reinterpret_cast<const UInt_t*>(blockHeader);
+ // The "trailerWords+1 <= bufferEnd" checks that we are
+ // not reading beyond the end of the buffer.
+ if (trailerWords+1 <= bufferEnd and *trailerWords == fgkEndOfDDL)
+ {
+ // If we aready knew the trailer was corrupt then just
+ // return because the error was already announced.
+ if (problemWithTrailer) return;
+
+ if (fAutoDetectTrailer)
+ {
+ // If we got here then there is at least one correct trailer
+ // word, but since we did not detect a correct trailer then
+ // there must be only one. Announce the error and exit.
+ fHandler.OnError(EventHandler::kTooFewDDLTrailerWords, trailerWords);
+ fHadError = true;
+ return;
+ }
+ }
+
// So we only got part of a block header at the very end
// of the buffer. Nothing to do but report the error and exit.
if (blockCount == fMaxBlocks)
// If any of the above fail then we know there is a problem with
// the block header. It must be corrupted somehow.
if (blockHeader->fDataKey != fgkBlockDataKey
- or dataEnd > end or blockEnd > end or dataEnd != blockEnd)
+ or dataEnd > endOfBlocks or blockEnd > endOfBlocks or dataEnd != blockEnd)
{
// So let us see what exactly is wrong and report this.
if (blockCount == fMaxBlocks)
}
if (blockHeader->fDataKey != fgkBlockDataKey)
fHandler.OnError(EventHandler::kBadBlockKey, &blockHeader->fDataKey);
- if (blockEnd > end)
+ if (blockEnd > endOfBlocks)
fHandler.OnError(EventHandler::kBadBlockLength, &blockHeader->fLength);
- if (dataEnd > end)
+ if (dataEnd > endOfBlocks)
fHandler.OnError(EventHandler::kBadBlockTotalLength, &blockHeader->fTotalLength);
if (dataEnd != blockEnd)
fHandler.OnError(EventHandler::kBlockLengthMismatch, blockHeader);
RecoverResult result = TryRecoverStruct(
fgkBlockDataKey, sizeof(AliMUONBlockHeaderStruct),
blockHeader->fTotalLength, blockHeader->fLength,
- blockStart, end, dataEnd, blockEnd, current
+ blockStart, endOfBlocks, dataEnd, blockEnd, current
);
if (result == kContinueToNextStruct)
continue; // Try the next block at 'current'.