--- /dev/null
+/**************************************************************************
+ * This file is property of and copyright by the ALICE HLT Project *
+ * All rights reserved. *
+ * *
+ * Primary Authors: *
+ * Artur Szostak <artursz@iafrica.com> *
+ * *
+ * Permission to use, copy, modify and distribute this software and its *
+ * documentation strictly for non-commercial purposes is hereby granted *
+ * without fee, provided that the above copyright notice appears in all *
+ * copies and that both the copyright notice and this permission notice *
+ * appear in the supporting documentation. The authors make no claims *
+ * about the suitability of this software for any purpose. It is *
+ * provided "as is" without express or implied warranty. *
+ **************************************************************************/
+
+/* $Id$*/
+
+///
+/// \file AliMUONRawStreamTrackerHP.cxx
+/// \author Artur Szostak <artursz@iafrica.com>
+/// \date 29-11-2007
+/// \brief Implementation of the the high performance decoder AliMUONRawStreamTrackerHP.
+///
+
+//-----------------------------------------------------------------------------
+/// \ingroup raw
+/// \class AliMUONRawStreamTrackerHP
+/// \brief A high performance stream decoder for muon tracking DDL streams.
+///
+/// This is the raw stream class which interfaces between the high performance
+/// core decoder and the AliRawReader class.
+/// To gain the most out of the decoder, the Next() method which returns batches
+/// of decoded digit / channel information should be used. That is:
+/// \code
+/// UInt_t Next(const AliChannelInfo*& channels);
+/// \endcode
+///
+/// \author Artur Szostak <artursz@iafrica.com>
+//-----------------------------------------------------------------------------
+
+#include "AliMUONRawStreamTrackerHP.h"
+#include "AliRawReader.h"
+#include "AliLog.h"
+#include <cassert>
+
+/// \cond CLASSIMP
+ClassImp(AliMUONRawStreamTrackerHP)
+/// \endcond
+
+
+AliMUONRawStreamTrackerHP::AliMUONRawStreamTrackerHP() :
+ AliMUONVRawStreamTracker(),
+ fDecoder(),
+ fDDL(0),
+ fCurrentChannel(0),
+ fBufferSize(8192),
+ fBuffer(new UChar_t[8192]),
+ fHadError(kFALSE)
+{
+ ///
+ /// Default constructor.
+ ///
+}
+
+
+AliMUONRawStreamTrackerHP::AliMUONRawStreamTrackerHP(AliRawReader* rawReader) :
+ AliMUONVRawStreamTracker(rawReader),
+ fDecoder(),
+ fDDL(0),
+ fCurrentChannel(0),
+ fBufferSize(8192),
+ fBuffer(new UChar_t[8192]),
+ fHadError(kFALSE)
+{
+ ///
+ /// Constructor with AliRawReader as argument.
+ ///
+
+ fDecoder.GetHandler().SetRawStream(this);
+}
+
+
+AliMUONRawStreamTrackerHP::~AliMUONRawStreamTrackerHP()
+{
+ ///
+ /// Default destructor.
+ ///
+
+ if (fBuffer != NULL)
+ {
+ delete [] fBuffer;
+ }
+}
+
+
+void AliMUONRawStreamTrackerHP::First()
+{
+ /// Initialise or reset the iterator.
+ /// The first DDL will be found and decoded.
+
+ assert( GetReader() != NULL );
+
+ fDDL = 0;
+ NextDDL();
+}
+
+
+Bool_t AliMUONRawStreamTrackerHP::NextDDL()
+{
+ /// Reading the next tracker DDL and decode the payload with the
+ /// high performance decoder.
+ /// \return kTRUE if the next DDL was successfully read and kFALSE otherwise.
+
+ assert( GetReader() != NULL );
+
+ if (IsDone()) return kFALSE;
+
+ do
+ {
+ GetReader()->Reset();
+ GetReader()->Select("MUONTRK", fDDL, fDDL); // Select the DDL file to be read.
+ if (GetReader()->ReadHeader()) break;
+ AliDebug(3, Form("Skipping DDL %d which does not seem to be there", fDDL+1));
+ fDDL++;
+ }
+ while (fDDL < GetMaxDDL());
+
+ AliDebug(3, Form("DDL Number %d\n", fDDL));
+
+ Int_t dataSize = GetReader()->GetDataSize(); // in bytes
+ // Check if we have enough buffer space already in fBuffer. If we do then
+ // just continue reading otherwise we need to resize the buffer.
+ if (fBufferSize < dataSize)
+ {
+ if (fBuffer != NULL)
+ {
+ delete [] fBuffer;
+ fBuffer = NULL;
+ fBufferSize = 0;
+ }
+ try
+ {
+ fBuffer = new UChar_t[dataSize];
+ fBufferSize = dataSize;
+ }
+ catch (const std::bad_alloc&)
+ {
+ AliError("Could not allocate more buffer space. Cannot decode DDL.");
+ return kFALSE;
+ }
+ }
+
+ if (not GetReader()->ReadNext(fBuffer, dataSize))
+ {
+ return kFALSE;
+ }
+
+#ifndef R__BYTESWAP
+ Swap(fBuffer, dataSize); // Swap needed for mac power pc.
+#endif
+
+ bool result = false;
+ try
+ {
+ // Since we might allocate memory inside OnNewBuffer in the event
+ // handler we need to trap any memory allocation exception to be robust.
+ result = fDecoder.Decode(fBuffer, dataSize);
+ }
+ catch (const std::bad_alloc&)
+ {
+ AliError("Could not allocate more buffer space. Cannot decode DDL.");
+ return kFALSE;
+ }
+
+ fCurrentChannel = 0;
+ fDDL++; // Remember to increment index to next DDL.
+ return result;
+}
+
+
+Bool_t AliMUONRawStreamTrackerHP::IsDone() const
+{
+ /// Indicates whether the iteration is finished or not.
+ /// \return kTRUE if we already read all the digits and kFALSE if not.
+
+ return fDDL == GetMaxDDL();
+}
+
+
+Bool_t AliMUONRawStreamTrackerHP::Next(
+ Int_t& busPatchId, UShort_t& manuId, UChar_t& manuChannel,
+ UShort_t& adc
+ )
+{
+ /// Advance one step in the iteration. Returns false if finished.
+ /// [out] \param busPatchId This is filled with the bus patch ID of the digit.
+ /// [out] \param manuId This is filled with the MANU ID of the digit.
+ /// [out] \param manuChannel This is filled with the MANU channel ID of the digit.
+ /// [out] \param adc This is filled with the ADC signal value of the digit.
+ /// \return kTRUE if we read another digit and kFALSE if we have read all the
+ /// digits already, i.e. at the end of the iteration.
+
+ if (fCurrentChannel < fDecoder.GetHandler().ChannelCount())
+ {
+ const AliChannelInfo& channel = fDecoder.GetHandler().Channels()[fCurrentChannel];
+ busPatchId = channel.BusPatchId();
+ manuId = channel.ManuId();
+ manuChannel = channel.ChannelId();
+ adc = channel.ADC();
+ fCurrentChannel++;
+ return kTRUE;
+ }
+ else
+ {
+ if (NextDDL())
+ {
+ if (fCurrentChannel < fDecoder.GetHandler().ChannelCount())
+ {
+ const AliChannelInfo& channel = fDecoder.GetHandler().Channels()[fCurrentChannel];
+ busPatchId = channel.BusPatchId();
+ manuId = channel.ManuId();
+ manuChannel = channel.ChannelId();
+ adc = channel.ADC();
+ fCurrentChannel++;
+ return kTRUE;
+ }
+ }
+ }
+ return kFALSE;
+}
+
+
+UInt_t AliMUONRawStreamTrackerHP::Next(const AliChannelInfo*& channels)
+{
+ /// Returns the next batch of decoded channel data.
+ /// [out] \param channels This is filled with a pointer to an array of
+ /// channels / digits that were decoded. This method does not
+ /// modify 'channels' if zero is returned.
+ /// \return The number of elements in the array pointed to by 'channels'
+ /// is returned. If zero is returned then there are no more channels to read.
+
+ // Check if we are already at the end of the channels array. If so then we
+ // need to fetch the next non empty DDL.
+ if (fCurrentChannel >= fDecoder.GetHandler().ChannelCount())
+ {
+ do
+ {
+ if (not NextDDL()) return 0;
+ }
+ // Make sure to keep going even for empty DDL payloads:
+ while (fDecoder.GetHandler().ChannelCount() == 0);
+ }
+ channels = fDecoder.GetHandler().Channels() + fCurrentChannel;
+ return fDecoder.GetHandler().ChannelCount() - fCurrentChannel;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AliMUONRawStreamTrackerHP::AliDecoderEventHandler::AliDecoderEventHandler() :
+ fBusPatchId(0),
+ fChannelCount(0),
+ fMaxChannels(8192),
+ fChannelBuffer(new AliChannelInfo[8192]),
+ fRawStream(NULL),
+ fBufferStart(NULL)
+{
+ /// Default constructor initialises the internal buffer to store decoded
+ /// channel / digit data to 8192 elements.
+ /// This array will grow dynamically if needed.
+}
+
+
+AliMUONRawStreamTrackerHP::AliDecoderEventHandler::~AliDecoderEventHandler()
+{
+ /// Default destructor cleans up the allocated memory.
+
+ if (fChannelBuffer != NULL)
+ {
+ delete [] fChannelBuffer;
+ }
+}
+
+void AliMUONRawStreamTrackerHP::AliDecoderEventHandler::OnNewBuffer(
+ const void* buffer, UInt_t bufferSize
+ )
+{
+ /// This is called by the high performance decoder when a new DDL payload
+ /// is about to be decoded.
+ /// \param buffer The pointer to the buffer storing the DDL payload.
+ /// \param bufferSize The size of the buffer in bytes.
+
+ // remember the start of the buffer to be used in OnError.
+ fBufferStart = buffer;
+
+ // Reset the number of channels found.
+ fChannelCount = 0;
+
+ // Check if we will have enough space in the fChannelBuffer array.
+ // If we do not then we need to resize the array.
+ // bufferSize / sizeof(UInt_t) will be a safe over estimate of the
+ // number of channels that we will find.
+ UInt_t maxPossibleChannels = bufferSize / sizeof(UInt_t);
+ if (maxPossibleChannels > fMaxChannels)
+ {
+ if (fChannelBuffer != NULL)
+ {
+ delete [] fChannelBuffer;
+ fChannelBuffer = NULL;
+ fMaxChannels = 0;
+ }
+ fChannelBuffer = new AliChannelInfo[maxPossibleChannels];
+ fMaxChannels = maxPossibleChannels;
+ }
+}
+
+
+void AliMUONRawStreamTrackerHP::AliDecoderEventHandler::OnNewBusPatch(
+ const AliMUONBusPatchHeaderStruct* header, const void* /*data*/
+ )
+{
+ /// This is called by the high performance decoder when a new bus patch
+ /// is found within the DDL payload. All we do is remember the bus patch ID.
+ /// \param header The bus patch header structure.
+ /// \param data The bus patch data (not used in this method).
+
+ fBusPatchId = header->fBusPatchId;
+}
+
+
+void AliMUONRawStreamTrackerHP::AliDecoderEventHandler::OnData(UInt_t data)
+{
+ /// This is called by the high performance decoder when a new bus patch
+ /// is found within the DDL payload. All we do is remember the bus patch ID.
+ /// \param data The bus patch data (not used in this method).
+
+ assert( fChannelCount < fMaxChannels );
+
+ UShort_t manuId; UChar_t channelId; UShort_t adc;
+ UnpackADC(data, manuId, channelId, adc);
+ fChannelBuffer[fChannelCount] = AliChannelInfo(fBusPatchId, manuId, channelId, adc);
+ fChannelCount++;
+}
+
+
+void AliMUONRawStreamTrackerHP::AliDecoderEventHandler::OnError(
+ ErrorCode error, const void* location
+ )
+{
+ /// This is called by the high performance decoder when a error occurs
+ /// when trying to decode the DDL payload. This indicates corruption in
+ /// the data. This method converts the error code to a descriptive message
+ /// and log this with the raw reader.
+ /// \param error The error code indicating the problem.
+ /// \param location A pointer to the location within the DDL payload buffer
+ /// being decoded where the problem with the data was found.
+
+ assert( fRawStream != NULL );
+ assert( fRawStream->GetReader() != NULL );
+
+ const char* msg = ErrorCodeToMessage(error);
+ unsigned long pos = (unsigned long)location - (unsigned long)fBufferStart;
+
+ switch (error)
+ {
+ case kBadPaddingWord:
+ case kParityError:
+ fRawStream->GetReader()->AddMinorErrorLog(error, Form("%s [At byte: %d]", msg, pos));
+ break;
+ default:
+ fRawStream->GetReader()->AddMajorErrorLog(error, Form("%s [At byte: %d]", msg, pos));
+ break;
+ }
+}
+
--- /dev/null
+#ifndef ALIMUONTRACKERDDLDECODER_H
+#define ALIMUONTRACKERDDLDECODER_H
+/**************************************************************************
+ * This file is property of and copyright by the ALICE HLT Project *
+ * All rights reserved. *
+ * *
+ * Primary Authors: *
+ * Artur Szostak <artursz@iafrica.com> *
+ * *
+ * Permission to use, copy, modify and distribute this software and its *
+ * documentation strictly for non-commercial purposes is hereby granted *
+ * without fee, provided that the above copyright notice appears in all *
+ * copies and that both the copyright notice and this permission notice *
+ * appear in the supporting documentation. The authors make no claims *
+ * about the suitability of this software for any purpose. It is *
+ * provided "as is" without express or implied warranty. *
+ **************************************************************************/
+
+/* $Id$ */
+
+///
+/// \file AliMUONTrackerDDLDecoder.h
+/// \author Artur Szostak <artursz@iafrica.com>
+/// \date 28-11-2007
+/// \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
+/// spectrometer's tracking chambers in a very efficient manner.
+///
+/// This implementation is derived from work done by Christian Finck for the
+/// AliMUONPayloadTracker.
+///
+/// Note to maintainers: Please remember that this file is used by the online
+/// dHLT system. As an online system, the dHLT requires the fastest code possible
+/// in the decoders to satisfy its timing constraints. The performance impact
+/// must be checked before any proposed modification is made to this file.
+///
+
+#include "AliMUONTrackerDDLDecoderEventHandler.h"
+
+#include <cassert>
+#include <ostream>
+#include <Rtypes.h>
+
+
+/// \ingroup raw
+/// \class AliMUONTrackerDDLDecoder
+/// \brief A high performance decoder class for MUON tracking DDL data.
+///
+/// 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.
+/// 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
+/// 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)
+/// {
+/// // I can do something with 'data' here.
+/// }
+/// };
+/// \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
+/// to call the Decode() method of the decoder.
+/// \code
+/// AliMUONTrackerDDLDecoder<MyCustomHandler> myDecoder;
+/// muDecoder.Decoder(buffer, bufferSize);
+/// \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
+/// AliMUONTrackerDDLDecoderEventHandler abstract and using virtual methods.
+///
+/// \author Artur Szostak <artursz@iafrica.com>
+
+template <class EventHandler>
+class AliMUONTrackerDDLDecoder
+{
+public:
+
+ /// Default contructor.
+ AliMUONTrackerDDLDecoder() :
+ fExitOnError(true), fTryRecover(false),
+ fSendDataOnParityError(false), fHadError(false),
+ fMaxBlocks(2), fMaxDSPs(5), fMaxBusPatches(5), fHandler()
+ {}
+
+ /// Constant method to return the event handler instance.
+ const EventHandler& GetHandler() const { return fHandler; }
+
+ /// Returns the event handler instance.
+ EventHandler& GetHandler() { return fHandler; }
+
+ /// Returns the "exit on error" flag.
+ /// i.e. should the decoder stop on the very first error found.
+ bool ExitOnError() const { return fExitOnError; }
+
+ /// Sets the "exit on error" flag.
+ /// i.e. should the decoder stop on the very first error found.
+ void ExitOnError(bool value) { fExitOnError = value; }
+
+ /// Returns the "try to recover from errors" flag.
+ /// i.e. should the decoder try to recover from errors found in the
+ /// payload headers.
+ 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.
+ void TryRecover(bool value) { fTryRecover = value; }
+
+ /// Returns the flag indicating if the raw data words in the bus patches
+ /// that failed their parity tests (i.e. parity error / bit flip in the
+ /// raw data word) will be sent to the event handler anyway through OnData.
+ bool SendDataOnParityError() const { return fSendDataOnParityError; }
+
+ /// Sets the flag indicating if the raw data words in the bus patches
+ /// that failed their parity tests (i.e. parity error / bit flip in the
+ /// raw data word) will be sent to the event handler anyway through OnData.
+ void SendDataOnParityError(bool value) { fSendDataOnParityError = value; }
+
+ /// Returns the maximum block count expected in the DDL payload.
+ UInt_t MaxBlocks() const { return fMaxBlocks; }
+
+ /// Sets the maximum block count expected in the DDL payload.
+ void MaxBlocks(UInt_t n) { fMaxBlocks = n; }
+
+ /// Returns the maximum DSP header count expected in any given block
+ /// structure within the DDL payload.
+ UInt_t MaxDSPs() const { return fMaxDSPs; }
+
+ /// Sets the maximum DSP header count expected in any given block structure
+ /// within the DDL payload.
+ void MaxDSPs(UInt_t n) { fMaxDSPs = n; }
+
+ /// Returns the maximum number of bus patches expected in any given DSP
+ /// structure within the DDL payload.
+ UInt_t MaxBusPatches() const { return fMaxBusPatches; }
+
+ /// Sets the maximum number of bus patches expected in any given DSP
+ /// structure within the DDL payload.
+ void MaxBusPatches(UInt_t n) { fMaxBusPatches = n; }
+
+ /// This method decodes the DDL payload contained in the buffer.
+ bool Decode(const void* buffer, UInt_t bufferSize);
+
+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 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.
+ 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.
+ EventHandler fHandler; ///< The event handler which deals with parsing events.
+
+ bool DecodeBlockData(
+ const AliMUONBlockHeaderStruct* blockHeader,
+ const UChar_t* start, const UChar_t* end
+ );
+
+ bool DecodeDSPData(const UChar_t* start, const UChar_t* end);
+
+ bool DecodeBusPatchData(const UChar_t* start, const UChar_t* end);
+
+ /// Possible results that can be returned by the TryRecoverStruct method.
+ enum RecoverResult
+ {
+ kRecoverFailed, ///< The recovery failed. Cannot continue parsing.
+ kStructRecovered, ///< Indicates that we recovered from a corrupt structure header and can continue processing the given structure.
+ kContinueToNextStruct ///< Must continue parsing the next structure and ignore the current one.
+ };
+
+ RecoverResult TryRecoverStruct(
+ UInt_t expectedKey,
+ UInt_t headerSize,
+ UInt_t totalLength,
+ UInt_t length,
+ const UChar_t* structStart,
+ const UChar_t* bufferEnd,
+ const UChar_t*& dataEnd,
+ const UChar_t*& structEnd,
+ const UChar_t*& current
+ );
+
+ const UChar_t* FindKey(
+ UInt_t key, const UChar_t* start, const UChar_t* end
+ );
+
+ bool ParityIsOk(UInt_t data);
+
+ static const UInt_t fgkBlockDataKey; ///< The key word expected to identify block structure headers.
+ 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.
+};
+
+//_____________________________________________________________________________
+
+// The following are the structure header keys which are used to identify the kind
+// of structure header we are dealing with: block, DSP or bus patch header.
+template <class EventHandler>
+const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkBlockDataKey = 0xFC0000FC;
+template <class EventHandler>
+const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkDSPDataKey = 0xF000000F;
+template <class EventHandler>
+const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkBusPatchDataKey = 0xB000000B;
+template <class EventHandler>
+const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkPaddingWord = 0xBEEFFACE;
+
+
+inline const char* AliMUONTrackerDDLDecoderEventHandler::ErrorCodeToString(ErrorCode code)
+{
+ /// This is a utility method which converts an error code to a string
+ /// representation for printing purposes.
+ /// \param code The error code as received in OnError for example.
+ /// \return An ANSI string containing the name of the error code symbol.
+
+ switch (code)
+ {
+ case kNoError: return "kNoError";
+ case kBufferTooBig: return "kBufferTooBig";
+ case kTooManyBlocks: return "kTooManyBlocks";
+ case kTooManyDSPs: return "kTooManyDSPs";
+ case kTooManyBusPatches: return "kTooManyBusPatches";
+ case kNoBlockHeader: return "kNoBlockHeader";
+ case kBadBlockKey: return "kBadBlockKey";
+ case kBadBlockLength: return "kBadBlockLength";
+ case kBadBlockTotalLength: return "kBadBlockTotalLength";
+ case kBlockLengthMismatch: return "kBlockLengthMismatch";
+ case kNoDSPHeader: return "kNoDSPHeader";
+ case kBadDSPKey: return "kBadDSPKey";
+ case kBadDSPLength: return "kBadDSPLength";
+ case kBadDSPTotalLength: return "kBadDSPTotalLength";
+ case kDSPLengthMismatch: return "kDSPLengthMismatch";
+ case kNoBusPatchHeader: return "kNoBusPatchHeader";
+ case kBadBusPatchKey: return "kBadBusPatchKey";
+ case kBadBusPatchLength: return "kBadBusPatchLength";
+ case kBadBusPatchTotalLength: return "kBadBusPatchTotalLength";
+ case kBusPatchLengthMismatch: return "kBusPatchLengthMismatch";
+ case kGlitchFound: return "kGlitchFound";
+ case kBadPaddingWord: return "kBadPaddingWord";
+ case kParityError: return "kParityError";
+ default: return "INVALID";
+ }
+}
+
+
+inline const char* AliMUONTrackerDDLDecoderEventHandler::ErrorCodeToMessage(ErrorCode code)
+{
+ /// This is a utility method which converts an error code to user friendly
+ /// descriptive message useful for printing to the screen.
+ /// \param code The error code as received in OnError for example.
+ /// \return An ANSI string containing a descriptive message of the error.
+
+ switch (code)
+ {
+ case kNoError:
+ return "Decoding was successful.";
+ case kBufferTooBig:
+ return "The DDL raw data is larger than indicated by the headers;"
+ " extra bytes are probably just garbage.";
+ case kTooManyBlocks:
+ return "Too many block structures found.";
+ case kTooManyDSPs:
+ return "Too many DSP structures found in the block.";
+ case kTooManyBusPatches:
+ return "Too many bus patch structures found in the DSP structure.";
+ case kNoBlockHeader:
+ return "Missing a block header.";
+ case kBadBlockKey:
+ return "The block header key word does not contain the correct value.";
+ case kBadBlockLength:
+ return "The block length field points past the end of the raw data size.";
+ case kBadBlockTotalLength:
+ return "The total block length field points past the end of the"
+ " raw data size.";
+ case kBlockLengthMismatch:
+ return "The block length and total length fields do not correspond."
+ " One or both of these values is incorrect.";
+ case kNoDSPHeader:
+ return "Missing a DSP header.";
+ case kBadDSPKey:
+ return "The DSP header key word does not contain the correct value.";
+ case kBadDSPLength:
+ return "The DSP structure length field points past the end of the"
+ " block structure.";
+ case kBadDSPTotalLength:
+ return "The total DSP structure length field points past the end of"
+ " the block structure.";
+ case kDSPLengthMismatch:
+ return "The DSP structure length and total length fields do not"
+ " correspond. One or both of these values is incorrect.";
+ case kNoBusPatchHeader:
+ return "Missing a bus patch header.";
+ case kBadBusPatchKey:
+ return "The bus patch header key word does not contain the correct value.";
+ case kBadBusPatchLength:
+ return "The bus patch length field points past the end of the"
+ " DSP structure.";
+ case kBadBusPatchTotalLength:
+ return "The total bus patch length field points past the end of"
+ " the DSP structure.";
+ case kBusPatchLengthMismatch:
+ return "The bus patch length and total length fields do not correspond."
+ " One or both of these values is incorrect.";
+ case kGlitchFound:
+ return "Found a glitch. This means a 1 byte word has been randomly"
+ " inserted into the raw data by mistake.";
+ case kBadPaddingWord:
+ return "The padding word does not contain the correct value.";
+ case kParityError:
+ return "Found a parity error in the data word.";
+ default:
+ return "Unknown error code!";
+ }
+}
+
+
+inline std::ostream& operator << (std::ostream& os, AliMUONTrackerDDLDecoderEventHandler::ErrorCode code)
+{
+ /// This is the stream operator for std::ostream classes to be able to
+ /// easily write the error messages associated with the error codes generated
+ /// by the decoder to 'cout' or 'cerr' for example.
+
+ os << AliMUONTrackerDDLDecoderEventHandler::ErrorCodeToMessage(code);
+ return os;
+}
+
+
+template <class EventHandler>
+bool AliMUONTrackerDDLDecoder<EventHandler>::Decode(const void* buffer, UInt_t bufferSize)
+{
+ /// This method should be called to actually decode the DDL payload
+ /// contained in a memory buffer. The payload should be for a muon tracking
+ /// chamber DDL stream.
+ /// As the decoder progresses it will make method calls to the event handler
+ /// instance (which can be accessed with the GetHandler() method) to indicate
+ /// the start of the new block, DSP and bus patch headers. For every raw
+ /// data word the OnData method of the event handler is called.
+ ///
+ /// If an error occurs during the parse because the data is corrupt then
+ /// the OnError method is called indicating what the problem was.
+ /// Decoding will stop at this point unless the fExitOnError flag is set
+ /// to false. Also raw data words which contain a parity error are only
+ /// sent to the event handler with OnData if the fSendDataOnParityError
+ /// 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).
+ ///
+ /// \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
+ /// the payload and not the DDL stream. That is, this pointer should be
+ /// equal to: DDL start pointer + 8 * sizeof(UInt_t).
+ /// \param bufferSize This is the pointer to the first byte just past the
+ /// end of the block structure.
+ /// \return Returns false if there was any problem with decoding the data,
+ /// and true otherwise. Note: the data may have been partially decoded
+ /// even if false was returned, which would be indicated by at least one
+ /// call to the event handlers OnData method.
+
+ assert( buffer != NULL );
+
+ fHadError = false;
+
+ // We are basically implementing something like a recursive decent parser.
+ // So start by marking the current buffer position and end of buffer.
+ const UChar_t* current = reinterpret_cast<const UChar_t*>(buffer);
+ const UChar_t* end = current + bufferSize;
+
+ // Signal a new buffer event.
+ fHandler.OnNewBuffer(buffer, bufferSize);
+
+ UInt_t blockCount = 0; // Indicates the number of blocks decoded.
+ while (current < end)
+ {
+ // Mark the start of the block structure.
+ const UChar_t* blockStart = current;
+
+ // Get the block header, move the current pointer just past the end
+ // of the header and check that we have not overflowed the buffer.
+ const AliMUONBlockHeaderStruct* blockHeader
+ = reinterpret_cast<const AliMUONBlockHeaderStruct*>(blockStart);
+ current += sizeof(AliMUONBlockHeaderStruct);
+ if (current > end)
+ {
+ // 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)
+ // Special case where we got all the blocks we
+ // expected, so the remaining data must be rubbish.
+ fHandler.OnError(EventHandler::kBufferTooBig, blockHeader);
+ else
+ fHandler.OnError(EventHandler::kNoBlockHeader, blockHeader);
+ return false;
+ }
+
+ // The header fits the buffer so we can mark the data start and
+ // read from the header to find the end of data and block pointers.
+ const UChar_t* dataStart = current;
+ current += blockHeader->fLength * sizeof(UInt_t);
+ const UChar_t* dataEnd = current;
+ const UChar_t* blockEnd = blockStart
+ + blockHeader->fTotalLength * sizeof(UInt_t);
+
+ // Now we need to check for the following things:
+ // 1) Is the end of block or end of data pointer outside the buffer
+ // boundaries.
+ // 2) Are the values for these pointers the same.
+ // 3) Is the expected data key in the header present.
+ // 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)
+ {
+ // So let us see what exactly is wrong and report this.
+ if (blockCount == fMaxBlocks)
+ {
+ // Special case where we got all the blocks we
+ // expected, so the remaining data must be rubbish.
+ // Don't even bother trying to recover the data.
+ fHandler.OnError(EventHandler::kBufferTooBig, blockHeader);
+ return false;
+ }
+ if (blockHeader->fDataKey != fgkBlockDataKey)
+ fHandler.OnError(EventHandler::kBadBlockKey, &blockHeader->fDataKey);
+ if (blockEnd > end)
+ fHandler.OnError(EventHandler::kBadBlockLength, &blockHeader->fLength);
+ if (dataEnd > end)
+ fHandler.OnError(EventHandler::kBadBlockTotalLength, &blockHeader->fTotalLength);
+ if (dataEnd != blockEnd)
+ fHandler.OnError(EventHandler::kBlockLengthMismatch, blockHeader);
+
+ // Stop the decoding if so requested by the user, otherwise
+ // remember about the error so that we return false from this
+ // routine and continue decoding.
+ if (fExitOnError)
+ return false;
+ else
+ fHadError = true;
+
+ // Try to recover from the corrupt header.
+ RecoverResult result = TryRecoverStruct(
+ fgkBlockDataKey, sizeof(AliMUONBlockHeaderStruct),
+ blockHeader->fTotalLength, blockHeader->fLength,
+ blockStart, end, dataEnd, blockEnd, current
+ );
+ if (result == kContinueToNextStruct)
+ continue; // Try the next block at 'current'.
+ if (result == kRecoverFailed) return false;
+ }
+
+ // At this point we certainly have a valid block header, so we
+ // need to check if we have more blocks than we expected. If not
+ // then we can indicate we have another block and decode its data.
+ if (++blockCount > fMaxBlocks)
+ {
+ fHandler.OnError(EventHandler::kTooManyBlocks, current);
+
+ // In this case we stop the decoding because clearly
+ // something is seriously wrong with the data if we are
+ // getting more blocks than expected.
+ return false;
+ }
+
+ fHandler.OnNewBlock(blockHeader, dataStart);
+ if (DecodeBlockData(blockHeader, dataStart, dataEnd)) continue;
+
+ // At this point we had a problem decoding the block structure's
+ // data. Thus we should stop further decoding if so requested by
+ // the user. Note the fHadError flag is already marked inside
+ // DecodeBlockData.
+ if (fExitOnError) return false;
+ }
+
+ return not fHadError;
+}
+
+
+template <class EventHandler>
+bool AliMUONTrackerDDLDecoder<EventHandler>::DecodeBlockData(
+ const AliMUONBlockHeaderStruct* blockHeader,
+ const UChar_t* start, const UChar_t* end
+ )
+{
+ /// This method decodes a block structure's data payload. It unpacks the
+ /// DSP structures contained inside and then for each DSP it calls the
+ /// OnNewDSP method for the event handler to signal the start of each new
+ /// DSP structure.
+ /// \param blockHeader
+ /// \param start This is the pointer to the start of the block
+ /// structure's data.
+ /// \param end This is the pointer to the first byte just past the
+ /// end of the block structure.
+ /// \return If the block structure's data was decoded without errors
+ /// or we could recover from the errors, then true is returned.
+ /// False is returned otherwise.
+
+ const UChar_t* current = start;
+
+ UInt_t dspCount = 0; // Indicates the number of DSPs decoded.
+ while (current < end)
+ {
+ // Mark the start of the DSP structure.
+ const UChar_t* dspStart = current;
+
+ // Get the DSP header, move the current pointer just past the end
+ // of the header and check that we have not overflowed the buffer.
+ const AliMUONDSPHeaderStruct* dspHeader
+ = reinterpret_cast<const AliMUONDSPHeaderStruct*>(dspStart);
+ current += sizeof(AliMUONDSPHeaderStruct);
+ if (current > end)
+ {
+ // So we only got part of a DSP header at the very end of
+ // the block structure buffer. Nothing to do but report the
+ // error and exit. Set fHadError in case of further decoding.
+ fHandler.OnError(EventHandler::kNoDSPHeader, dspHeader);
+ fHadError = true;
+ return false;
+ }
+
+ // The header fits the buffer so we can mark the data start and
+ // read from the header to find the end of data and DSP pointers.
+ const UChar_t* dataStart = current;
+ current += dspHeader->fLength * sizeof(UInt_t);
+ const UChar_t* dataEnd = current;
+ const UChar_t* dspEnd = dspStart + dspHeader->fTotalLength * sizeof(UInt_t);
+
+ // Now we need to check for the following things:
+ // 1) Is the end of DSP or end of data pointer outside the buffer
+ // boundaries.
+ // 2) Are the values for these pointers the same.
+ // 3) Is the expected data key in the header present.
+ // If any of the above fail then we know there is a problem with
+ // the DSP header. It must be corrupted somehow.
+ if (dspHeader->fDataKey != fgkDSPDataKey
+ or dataEnd > end or dspEnd > end or dataEnd != dspEnd)
+ {
+ // So let us see what exactly is wrong and report this.
+ if (dspHeader->fDataKey != fgkDSPDataKey)
+ fHandler.OnError(EventHandler::kBadDSPKey, &dspHeader->fDataKey);
+ if (dspEnd > end)
+ fHandler.OnError(EventHandler::kBadDSPLength, &dspHeader->fLength);
+ if (dataEnd > end)
+ fHandler.OnError(EventHandler::kBadDSPTotalLength, &dspHeader->fTotalLength);
+ if (dataEnd != dspEnd)
+ fHandler.OnError(EventHandler::kDSPLengthMismatch, dspHeader);
+
+ // Indicate we had and error and stop the decoding if so
+ // requested by the user.
+ fHadError = true;
+ if (fExitOnError) return false;
+
+ // Try to recover from the corrupt header.
+ RecoverResult result = TryRecoverStruct(
+ fgkDSPDataKey, sizeof(AliMUONDSPHeaderStruct),
+ dspHeader->fTotalLength, dspHeader->fLength,
+ dspStart, end, dataEnd, dspEnd, current
+ );
+ if (result == kContinueToNextStruct)
+ continue; // Try the next DSP at 'current'.
+ if (result == kRecoverFailed) return false;
+ }
+
+ // At this point we certainly have a valid DSP header, so we
+ // need to check if we have more DSPs than we expected. If not
+ // then we can indicate we have another DSP and decode its data.
+ if (++dspCount > fMaxDSPs)
+ {
+ fHandler.OnError(EventHandler::kTooManyDSPs, current);
+
+ // In this case we stop further decoding of the block
+ // structure data because clearly something is seriously
+ // wrong if we are getting more DSPs than expected.
+ // Indicate that we had an error so the Decode() method
+ // returns false.
+ fHadError = true;
+ return false;
+ }
+
+ fHandler.OnNewDSP(dspHeader, dataStart);
+
+ // Check the error word in the header.
+ if (dspHeader->fErrorWord == (0x000000B1 | blockHeader->fDSPId)
+ or dspHeader->fErrorWord == (0x00000091 | blockHeader->fDSPId)
+ )
+ {
+ // An event with a glitch in the readout has been detected.
+ // It means that somewhere a 1 byte word has been randomly
+ // inserted and all the readout sequence is shifted until
+ // the next event.
+ fHandler.OnError(EventHandler::kGlitchFound, &dspHeader->fErrorWord);
+ fHadError = true;
+ if (fExitOnError) return false;
+
+ // Try recover by finding the very next DSP and continue
+ // decoding from there. Note: to achieve all we have to do
+ // is continue to the next iteration, because the logic
+ // will land up calling the FindKey method within the
+ // TryRecoverStruct method above.
+ if (fTryRecover) continue;
+ }
+
+ // Check if we are padding. If we are, then the bus patch data is
+ // actually 4 bytes smaller and the last word is a padding word.
+ if (dspHeader->fPaddingWord == 1)
+ {
+ dataEnd -= sizeof(UInt_t);
+
+ // Check the pad word is correct.
+ const UInt_t* padWord = reinterpret_cast<const UInt_t*>(dataEnd);
+ if (*padWord != fgkPaddingWord)
+ {
+ fHandler.OnError(EventHandler::kBadPaddingWord, padWord);
+ fHadError = true;
+ if (fExitOnError) return false;
+ }
+ }
+
+ if (DecodeDSPData(dataStart, dataEnd)) continue;
+
+ // At this point we had a problem decoding the DSP structure's
+ // data, thus we should stop further decoding if so requested by
+ // the user. Note the fHadError flag is already marked inside
+ // DecodeDSPData.
+ if (fExitOnError) return false;
+ }
+
+ return true;
+}
+
+
+template <class EventHandler>
+bool AliMUONTrackerDDLDecoder<EventHandler>::DecodeDSPData(
+ const UChar_t* start, const UChar_t* end
+ )
+{
+ /// This method decodes a DSP structure's data payload. It finds all the
+ /// bus patches found inside and for each it calls the OnNewBusPatch method
+ /// for the event handler to signal the start of each new bus patch.
+ /// \param start This is the pointer to the start of the DSP structure's data.
+ /// \param end This is the pointer to the first byte just past the
+ /// end of the DSP structure.
+ /// \return If the DSP structure's data was decoded without errors
+ /// or we could recover from the errors, then true is returned.
+ /// False is returned otherwise.
+
+ const UChar_t* current = start;
+
+ UInt_t busPatchCount = 0; // Indicates the number of bus patches decoded.
+ while (current < end)
+ {
+ // Mark the start of the bus patch structure.
+ const UChar_t* busPatchStart = current;
+
+ // Get the bus patch header, move the current pointer just past
+ // the end of the header and check that we have not overflowed
+ // the buffer.
+ const AliMUONBusPatchHeaderStruct* busPatchHeader
+ = reinterpret_cast<const AliMUONBusPatchHeaderStruct*>(busPatchStart);
+ current += sizeof(AliMUONBusPatchHeaderStruct);
+ if (current > end)
+ {
+ // So we only got part of a bus patch header at the very
+ // end of the DSP structure buffer. Nothing to do but
+ // report the error and exit. Set fHadError in case of
+ // further decoding.
+ fHandler.OnError(EventHandler::kNoBusPatchHeader, busPatchHeader);
+ fHadError = true;
+ return false;
+ }
+
+ // The header fits the buffer so we can mark the data start and
+ // read from the header to find the end of data and bus patch
+ // structure pointers.
+ const UChar_t* dataStart = current;
+ current += busPatchHeader->fLength * sizeof(UInt_t);
+ const UChar_t* dataEnd = current;
+ const UChar_t* busPatchEnd = busPatchStart
+ + busPatchHeader->fTotalLength * sizeof(UInt_t);
+
+ // Now we need to check for the following things:
+ // 1) Is the end of bus patch structure or end of data pointer
+ // outside the buffer boundaries.
+ // 2) Are the values for these pointers the same.
+ // 3) Is the expected data key in the header present.
+ // If any of the above fail then we know there is a problem with
+ // the bus patch header. It must be corrupted somehow.
+ if (busPatchHeader->fDataKey != fgkBusPatchDataKey
+ or dataEnd > end or busPatchEnd > end or dataEnd != busPatchEnd)
+ {
+ // So let us see what exactly is wrong and report this.
+ if (busPatchHeader->fDataKey != fgkBusPatchDataKey)
+ fHandler.OnError(EventHandler::kBadBusPatchKey, &busPatchHeader->fDataKey);
+ if (busPatchEnd > end)
+ fHandler.OnError(EventHandler::kBadBusPatchLength, &busPatchHeader->fLength);
+ if (dataEnd > end)
+ fHandler.OnError(EventHandler::kBadBusPatchTotalLength, &busPatchHeader->fTotalLength);
+ if (dataEnd != busPatchEnd)
+ fHandler.OnError(EventHandler::kBusPatchLengthMismatch, busPatchHeader);
+
+ // Indicate we had and error and stop the decoding if so
+ // requested by the user.
+ fHadError = true;
+ if (fExitOnError) return false;
+
+ // Try to recover from the corrupt header.
+ RecoverResult result = TryRecoverStruct(
+ fgkBusPatchDataKey, sizeof(AliMUONBusPatchHeaderStruct),
+ busPatchHeader->fTotalLength, busPatchHeader->fLength,
+ busPatchStart, end, dataEnd, busPatchEnd, current
+ );
+ if (result == kContinueToNextStruct)
+ continue; // Try the next bus patch at 'current'.
+ if (result == kRecoverFailed) return false;
+ }
+
+ // At this point we certainly have a valid bus patch header, so
+ // we need to check if we have more bus patches than we expected.
+ // If not then we can indicate we have another bus patch and
+ // decode its data.
+ if (++busPatchCount > fMaxBusPatches)
+ {
+ fHandler.OnError(EventHandler::kTooManyBusPatches, current);
+
+ // In this case we stop further decoding of the DSP
+ // structure's data because clearly something is seriously
+ // wrong if we are getting more bus patches than expected.
+ // Indicate that we had an error so the Decode() method
+ // returns false.
+ fHadError = true;
+ return false;
+ }
+
+ fHandler.OnNewBusPatch(busPatchHeader, dataStart);
+ if (DecodeBusPatchData(dataStart, dataEnd)) continue;
+
+ // At this point we had a problem decoding the bus patch data,
+ // thus we should stop further decoding if so requested by the
+ // user. Note the fHadError flag is already marked inside
+ // DecodeBusPatchData.
+ if (fExitOnError) return false;
+ }
+
+ return true;
+}
+
+
+template <class EventHandler>
+bool AliMUONTrackerDDLDecoder<EventHandler>::DecodeBusPatchData(
+ const UChar_t* start, const UChar_t* end
+ )
+{
+ /// This method decodes a single bus patch's data payload.
+ /// It will check the parity of the raw data words and send them
+ /// to the event handler instance with calls to OnData.
+ /// \param start This is the pointer to the start of the bus patch
+ /// structure's data.
+ /// \param end This is the pointer to the first byte just past the
+ /// end of the bus patch structure.
+ /// \return If the bus patch's data was decoded without errors
+ /// or we could recover from the errors, then true is returned.
+ /// False is returned otherwise.
+
+ // Assert that 'end' is always larger than start by n*sizeof(UInt_t)
+ // where n is a positive integer. This should be the case because we
+ // always add multiples of sizeof(UInt_t) to the 'current' pointer in
+ // all the DecodeXYZ methods.
+ assert( UInt_t(end - start) % 4 == 0 );
+
+ // Now step through all the data words and issue OnData events.
+ // We also need to check parity and signal OnError if it is not valid
+ // for any of the data words.
+ const UInt_t* data = reinterpret_cast<const UInt_t*>(start);
+ const UInt_t* dataEnd = reinterpret_cast<const UInt_t*>(end);
+ for (; data < dataEnd; data++)
+ {
+ if (ParityIsOk(*data))
+ {
+ fHandler.OnData(*data);
+ }
+ else
+ {
+ // Indicate we had a parity error and exit immediately
+ // if the user so requested.
+ fHandler.OnError(EventHandler::kParityError, data);
+ fHadError = true;
+ if (fExitOnError) return false;
+
+ if (fSendDataOnParityError)
+ fHandler.OnData(*data);
+ }
+ }
+
+ return true;
+}
+
+
+template <class EventHandler>
+typename AliMUONTrackerDDLDecoder<EventHandler>::RecoverResult
+AliMUONTrackerDDLDecoder<EventHandler>::TryRecoverStruct(
+ UInt_t expectedKey,
+ UInt_t headerSize,
+ UInt_t totalLength,
+ UInt_t length,
+ const UChar_t* structStart,
+ const UChar_t* bufferEnd,
+ const UChar_t*& dataEnd,
+ const UChar_t*& structEnd,
+ const UChar_t*& current
+ )
+{
+ /// This method attempts to recover from a corrupt structure header by
+ /// figuring out which of the structure size indicators is correct.
+ /// This is possible because each header has some redundant information.
+ /// The recovery procedure is only attempted if fTryRecover was set to
+ /// true. If the recovery procedure is successful then this method will
+ /// also update the pointers indicating the start of data, end of structure
+ /// and current parsing position with the correct values.
+ ///
+ /// [in] \param expectedKey This is the expected block key for the header
+ /// currently being processed.
+ /// [in] \param headerSize The expected header size as given by the sizeof
+ /// operator for example.
+ /// [in] \param totalLength The total length as given by the fTotalLength
+ /// field in the current header being handled.
+ /// [in] \param length The data length as given by the fLength field
+ /// in the current header being handled.
+ /// [in] \param structStart A pointer to the start of the structure header.
+ /// [in] \param bufferEnd A pointer to the first byte just past the end
+ /// of the buffer. This could be the pointer to the first byte
+ /// just past the end of the parent structure if we are dealing
+ /// with a DSP structure or bus patch. The parent structure for
+ /// the DSP is a block structure and for a bus patch it is a DSP.
+ /// [out] \param dataEnd This is the pointer to the first byte just past
+ /// the end of the structure being processed. It should be equal to
+ /// structStart + sizeof(structure header) + fLength, where fLength
+ /// is the field found in the structure's header itself. This value
+ /// will be corrected and updated if we could recover from the
+ /// corruption in the header.
+ /// [out] \param structEnd A pointer to the first byte just past the end of
+ /// the structure. This value should be set equal to
+ /// structStart + fTotalLength * sizeof(UInt_t), where fTotalLength
+ /// is the field found in the structure's header itself. This value
+ /// will be corrected and updated if we could recover from the
+ /// corruption in the header.
+ /// [out] \param current This is the pointer to the current location in
+ /// the DDL payload being parsed. It should in principle point
+ /// to the start of the structures data. This value will be
+ /// corrected and updated if we could recover from the corruption
+ /// in the header.
+ ///
+ /// \return Returns the result of the recovery attempt, which can be one
+ /// of the following:
+ /// kRecoverFailed - The recovery failed completely so the caller
+ /// cannot continue parsing any more structures. If the failure
+ /// is within a DSP then one could still continue parsing
+ /// from the next block. Similarly for bus patches, parsing could
+ /// continue from the next DSP structure.
+ /// kStructRecovered - Indicates that we recovered from a corrupt
+ /// structure header and can continue processing the data of the
+ /// structure in question.
+ /// kContinueToNextStruct - Either fTryRecover was set to false or we
+ /// could not recover from the corrupt header but we did find the
+ /// start of another header matching the expected key so parsing
+ /// can continue from the updated current position.
+
+ // Check if the user wants us to try and recover from a corrupt header.
+ if (not fTryRecover) return kContinueToNextStruct;
+
+ // If the user wants us to try recover, then try to recover what the
+ // correct values for dataEnd, structEnd and current were supposed to be.
+ // The recovery procedure is as follows: We have 4 conditions for a correct
+ // header:
+ // 1) The header key is what we expect.
+ // 2) The totalLength equals length + headerSize.
+ // 3) The word at dataEnd contains a valid key. (implies length is
+ // correct.)
+ // 4) The word at structEnd contains a valid key. (implies totalLength
+ // is correct.)
+ // If any 2 of these conditions hold then we know that only one of the
+ // header fields is corrupt and we have enough information to reconstruct
+ // the third field. Note that if conditions 3 and 4 are true then this
+ // implies 2 is also true. (not necessarily the other way around though.)
+ // The valid key mentioned above at dataEnd and structEnd should be:
+ // a) A bus patch key, DSP key or end of buffer if expectedKey indicates
+ // a buspatch.
+ // b) A DSP key, block structure key or end of buffer if expectedKey
+ // indicates a DSP.
+ // c) A block structure key or end of buffer if expectedKey indicates
+ // a DSP.
+ const UInt_t* headerKey = reinterpret_cast<const UInt_t*>(structStart);
+ bool headerKeyOk = (expectedKey == *headerKey);
+
+ bool lengthsMatch = (totalLength == length + headerSize);
+
+ bool lengthIsCorrect = false;
+ bool totalLengthIsCorrect = false;
+ const UInt_t* keyAtDataEnd = reinterpret_cast<const UInt_t*>(dataEnd);
+ const UInt_t* keyAtStructEnd = reinterpret_cast<const UInt_t*>(structEnd);
+
+ switch (expectedKey)
+ {
+ case fgkBlockDataKey:
+ if (dataEnd == bufferEnd)
+ {
+ // Are we at the end of the buffer?
+ lengthIsCorrect = true;
+ }
+ else
+ {
+ // Must check that we can read another 4 bytes before
+ // checking the key at dataEnd.
+ if (dataEnd + sizeof(UInt_t) <= bufferEnd)
+ {
+ if (*keyAtDataEnd == fgkBlockDataKey)
+ lengthIsCorrect = true;
+ }
+ }
+
+ if (structEnd == bufferEnd)
+ {
+ // Are we at the end of the buffer?
+ totalLengthIsCorrect = true;
+ }
+ else
+ {
+ // Must check that we can read another 4 bytes before
+ // checking the key at structEnd.
+ if (structEnd + sizeof(UInt_t) <= bufferEnd)
+ {
+ if (*keyAtStructEnd == fgkBlockDataKey)
+ totalLengthIsCorrect = true;
+ }
+ }
+
+ break;
+
+ case fgkDSPDataKey:
+ if (dataEnd == bufferEnd)
+ {
+ // Are we at the end of the buffer?
+ lengthIsCorrect = true;
+ }
+ else
+ {
+ // Must check that we can read another 4 bytes before
+ // checking the key at dataEnd.
+ if (dataEnd + sizeof(UInt_t) <= bufferEnd)
+ {
+ if (*keyAtDataEnd == fgkBlockDataKey
+ or *keyAtDataEnd == fgkDSPDataKey)
+ lengthIsCorrect = true;
+ }
+ }
+
+ if (structEnd == bufferEnd)
+ {
+ // Are we at the end of the buffer?
+ totalLengthIsCorrect = true;
+ }
+ else
+ {
+ // Must check that we can read another 4 bytes before
+ // checking the key at structEnd.
+ if (structEnd + sizeof(UInt_t) <= bufferEnd)
+ {
+ if (*keyAtStructEnd == fgkBlockDataKey
+ or *keyAtStructEnd == fgkDSPDataKey)
+ totalLengthIsCorrect = true;
+ }
+ }
+
+ break;
+
+ case fgkBusPatchDataKey:
+ if (dataEnd == bufferEnd)
+ {
+ // Are we at the end of the buffer?
+ lengthIsCorrect = true;
+ }
+ else
+ {
+ // Must check that we can read another 4 bytes before
+ // checking the key at dataEnd.
+ if (dataEnd + sizeof(UInt_t) <= bufferEnd)
+ {
+ if (*keyAtDataEnd == fgkDSPDataKey
+ or *keyAtDataEnd == fgkBusPatchDataKey)
+ lengthIsCorrect = true;
+ }
+ }
+
+ if (structEnd == bufferEnd)
+ {
+ // Are we at the end of the buffer?
+ totalLengthIsCorrect = true;
+ }
+ else
+ {
+ // Must check that we can read another 4 bytes before
+ // checking the key at structEnd.
+ if (structEnd + sizeof(UInt_t) <= bufferEnd)
+ {
+ if (*keyAtStructEnd == fgkDSPDataKey
+ or *keyAtStructEnd == fgkBusPatchDataKey)
+ totalLengthIsCorrect = true;
+ }
+ }
+
+ break;
+
+ default:
+ // lengthIsCorrect and totalLengthIsCorrect already set to false.
+ break;
+ }
+
+ if (headerKeyOk and lengthIsCorrect)
+ {
+ // totalLength was wrong, dataEnd is correct.
+ structEnd = dataEnd;
+ current = dataEnd;
+ return kStructRecovered;
+ }
+ if (headerKeyOk and totalLengthIsCorrect)
+ {
+ // Length was wrong, structEnd is correct.
+ dataEnd = structEnd;
+ current = structEnd;
+ return kStructRecovered;
+ }
+ if (lengthsMatch and lengthIsCorrect and totalLengthIsCorrect)
+ {
+ // The header's key was wrong but the lengths and pointers are OK.
+ return kStructRecovered;
+ }
+
+ // Could not recover the header from the available information, so find
+ // the next key in the stream that is the same as the currently expected
+ // one and continue decoding from there.
+ const UChar_t* location = FindKey(
+ expectedKey, structStart + sizeof(UInt_t), bufferEnd
+ );
+ if (location != NULL)
+ {
+ current = location;
+ return kContinueToNextStruct;
+ }
+
+ return kRecoverFailed;
+}
+
+
+template <class EventHandler>
+const UChar_t* AliMUONTrackerDDLDecoder<EventHandler>::FindKey(
+ UInt_t key, const UChar_t* start, const UChar_t* end
+ )
+{
+ /// Searches for the first occurrence of the key value in the buffer marked by
+ /// 'start' and 'end'. 'start' should point to the start of the buffer and 'end'
+ /// should point to 'start + bufferSize', i.e. just past the last byte of the
+ /// buffer. If the key was found then the pointer to that location is returned
+ /// otherwise NULL is returned.
+
+ const UChar_t* current = start;
+ while (current + sizeof(UInt_t) <= end)
+ {
+ UInt_t data = * reinterpret_cast<const UInt_t*>(current);
+ if (data == key) return current;
+ current++;
+ }
+ return NULL;
+}
+
+
+template <class EventHandler>
+bool AliMUONTrackerDDLDecoder<EventHandler>::ParityIsOk(UInt_t data)
+{
+ /// Optimised parity check addapted from:
+ /// http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel
+
+ // parity of the 32 bits must be zero if the last bit is equal
+ // to the parity of the first 31 bits.
+ // Reason: the parity bit xor the parity of the first 31 bits must give
+ // zero, unless there was a bit error.
+ data ^= data >> 16;
+ data ^= data >> 8;
+ data ^= data >> 4;
+ data &= 0xf;
+ data = ((0x6996 >> data) & 1);
+ return data == 0;
+}
+
+#endif // ALIMUONTRACKERDDLDECODER_H
--- /dev/null
+#ifndef ALIMUONTRACKERDDLDECODEREVENTHANDLER_H
+#define ALIMUONTRACKERDDLDECODEREVENTHANDLER_H
+/**************************************************************************
+ * This file is property of and copyright by the ALICE HLT Project *
+ * All rights reserved. *
+ * *
+ * Primary Authors: *
+ * Artur Szostak <artursz@iafrica.com> *
+ * *
+ * Permission to use, copy, modify and distribute this software and its *
+ * documentation strictly for non-commercial purposes is hereby granted *
+ * without fee, provided that the above copyright notice appears in all *
+ * copies and that both the copyright notice and this permission notice *
+ * appear in the supporting documentation. The authors make no claims *
+ * about the suitability of this software for any purpose. It is *
+ * provided "as is" without express or implied warranty. *
+ **************************************************************************/
+
+/* $Id$ */
+
+///
+/// \file AliMUONTrackerDDLDecoderEventHandler.h
+/// \author Artur Szostak <artursz@iafrica.com>
+/// \date 28-11-2007
+/// \brief Implementation of a high performance DDL decoder event handler
+/// for the muon tracking stations.
+///
+
+#include <cassert>
+#include <ostream>
+#include <Rtypes.h>
+
+
+// We use C binding for the structures because C is more uniform with its application
+// binary interface (ABI) between compilers.
+extern "C"
+{
+
+// The following structures are the headers found in the DDL payload from the
+// muon tracking chambers. The specification is defined in ALICE-INT-2005-012
+// (https://edms.cern.ch/file/591904/1/ALICE-INT-2005-012.pdf)
+
+/// The block header structure of the Tracker DDL payload.
+struct AliMUONBlockHeaderStruct
+{
+ UInt_t fDataKey; ///< Data key word for CRT header
+ UInt_t fTotalLength; ///< total length of block structure (w/o padding word)
+ UInt_t fLength; ///< length of raw data
+ UInt_t fDSPId; ///< DSP id
+ UInt_t fL0Trigger; ///< L0 trigger word
+ UInt_t fMiniEventId; ///< Bunch Crossing for mini-event id (see TDR chapter 8)
+ UInt_t fEventId1; ///< Event Id in bunch crossing
+ UInt_t fEventId2; ///< Event Id in orbit number
+};
+
+/// The DSP header structure of the Tracker DDL payload.
+struct AliMUONDSPHeaderStruct
+{
+ UInt_t fDataKey; ///< Data key word for FRT header
+ UInt_t fTotalLength; ///< total length of block structure
+ UInt_t fLength; ///< length of raw data
+ UInt_t fDSPId; ///< DSP id
+ UInt_t fBlkL1ATrigger; ///< L1 accept in Block Structure (CRT)
+ UInt_t fMiniEventId; ///< Mini Event Id in bunch crossing
+ UInt_t fL1ATrigger; ///< Number of L1 accept in DSP Structure (FRT)
+ UInt_t fL1RTrigger; ///< Number of L1 reject in DSP Structure (FRT)
+ UInt_t fPaddingWord; ///< padding dummy word for 64 bits transfer
+ UInt_t fErrorWord; ///< Error word
+};
+
+/// The bus patch header structure of the Tracker DDL payload.
+struct AliMUONBusPatchHeaderStruct
+{
+ UInt_t fDataKey; ///< Data key word for bus patch header
+ UInt_t fTotalLength; ///< total length of bus patch structure
+ UInt_t fLength; ///< length of raw data
+ UInt_t fBusPatchId; ///< bus patch id
+};
+
+} // extern "C"
+
+
+/// \ingroup raw
+/// \class AliMUONTrackerDDLDecoderEventHandler
+/// \brief Callback event handler class for the AliMUONTrackerDDLDecoder.
+///
+/// This class is the base class defining what methods the event handler for the
+/// high performance decoder should have. This handler actually does nothing.
+/// The user of this decoder will have to derive from this class a custom event
+/// handler that actually does something within the callback methods OnNewBusPatch,
+/// OnData, OnError etc...
+///
+/// \author Artur Szostak <artursz@iafrica.com>
+
+class AliMUONTrackerDDLDecoderEventHandler
+{
+public:
+
+ /// The only reason for a virtual destructor is to make -Weffc++ shutup.
+ /// This should not really be here.
+ virtual ~AliMUONTrackerDDLDecoderEventHandler() {}
+
+ /// All the possible error codes for the parsing.
+ enum ErrorCode
+ {
+ kNoError = 0, /// Decoding was successful.
+ // Offset our error codes to stay clear of any common codes in AliMUONRawStreamTracker:
+ kBufferTooBig = 10, /// The DDL raw data is larger than indicated by the headers; extra bytes are probably just garbage.
+ kTooManyBlocks = 11, /// Too many block structures found.
+ kTooManyDSPs = 12, /// Too many DSP structures found in the block.
+ kTooManyBusPatches = 13, /// Too many bus patch structures found in the DSP structure.
+ kNoBlockHeader = 14, /// Missing a block header.
+ kBadBlockKey = 15, /// The block header key word does not contain the correct value.
+ kBadBlockLength = 16, /// The block length field points past the end of the raw data size.
+ kBadBlockTotalLength = 17, /// The total block length field points past the end of the raw data size.
+ kBlockLengthMismatch = 18, /// The block length and total length fields do not correspond. One or both of these values is incorrect.
+ kNoDSPHeader = 19, /// Missing a DSP header.
+ kBadDSPKey = 20, /// The DSP header key word does not contain the correct value.
+ kBadDSPLength = 21, /// The DSP structure length field points past the end of the block structure.
+ kBadDSPTotalLength = 22, /// The total DSP structure length field points past the end of the block structure.
+ kDSPLengthMismatch = 23, /// The DSP structure length and total length fields do not correspond. One or both of these values is incorrect.
+ kNoBusPatchHeader = 24, /// Missing a bus patch header.
+ kBadBusPatchKey = 25, /// The bus patch header key word does not contain the correct value.
+ kBadBusPatchLength = 26, /// The bus patch length field points past the end of the DSP structure.
+ kBadBusPatchTotalLength = 27, /// The total bus patch length field points past the end of the DSP structure.
+ kBusPatchLengthMismatch = 28, /// The bus patch length and total length fields do not correspond. One or both of these values is incorrect.
+ // match up error codes with AliMUONRawStreamTracker:
+ kGlitchFound = 1, /// Found a glitch. This means a 1 byte word has been randomly inserted into the raw data by mistake.
+ kBadPaddingWord = 2, /// The padding word does not contain the correct value.
+ kParityError = 3 /// Found a parity error in the data word.
+ };
+
+ // The following methods should be overridden for specific processing to
+ // take place in your event handler.
+
+ /// The OnNewBuffer method will be called whenever a new buffer containing
+ /// a DDL payload is about to be processed.
+ /// The default behaviour of this method is to do nothing.
+ /// - param const void* The pointer to the start of the memory buffer storing
+ /// the DDL payload.
+ /// - param UInt_t The size in bytes of the memory buffer.
+ void OnNewBuffer(const void* /*buffer*/, UInt_t /*bufferSize*/) {}
+
+ /// OnNewBlock is called whenever a new block header is found in the payload.
+ /// The default behaviour of this method is to do nothing.
+ /// - param const AliMUONBlockHeaderStruct* This is a pointer to the block header as found in the
+ /// DDL payload.
+ /// - param const void* This is a pointer to the start of the block's contents.
+ /// Note: both pointers point into the memory buffer being parsed so the
+ /// contents must not be modified. On the other hand this is very efficient
+ /// because no memory copying is required.
+ void OnNewBlock(const AliMUONBlockHeaderStruct* /*header*/, const void* /*data*/) {}
+
+ /// OnNewDSP is called whenever a new DSP header is found in the payload.
+ /// Every DSP header recevied by a call to OnNewDSP is associated to the
+ /// block header received in the most recent call to OnNewBlock.
+ /// The default behaviour of this method is to do nothing.
+ /// - param const AliMUONDSPHeaderStruct* This is a pointer to the DSP header as found in the
+ /// DDL payload.
+ /// - param const void* This is a pointer to the start of the DSP's contents.
+ /// Note: both pointers point into the memory buffer being parsed so the
+ /// contents must not be modified. On the other hand this is very efficient
+ /// because no memory copying is required.
+ void OnNewDSP(const AliMUONDSPHeaderStruct* /*header*/, const void* /*data*/) {}
+
+ /// OnNewBusPatch is called whenever a new bus patch header is found in
+ /// the payload. Every bus patch recevied by a call to OnNewBusPatch is
+ /// associated to the DSP header received in the most recent call to OnNewDSP.
+ /// The default behaviour of this method is to do nothing.
+ /// - param const AliMUONBusPatchHeaderStruct* This is a pointer to the bus patch header as found
+ /// in the DDL payload.
+ /// - param const void* This is a pointer to the start of the bus patch's contents,
+ /// specifically the raw data words.
+ /// Note: both pointers point into the memory buffer being parsed so the
+ /// contents must not be modified. On the other hand this is very efficient
+ /// because no memory copying is required.
+ void OnNewBusPatch(const AliMUONBusPatchHeaderStruct* /*header*/, const void* /*data*/) {}
+
+ /// OnData is called for every raw data word found within a bus patch.
+ /// Every data ward recevied by a call to OnData is associated to the bus patch
+ /// header received in the most recent call to OnNewBusPatch.
+ /// The default behaviour of this method is to do nothing.
+ /// - param UInt_t This is the raw data word as found within the bus patch payload.
+ void OnData(UInt_t /*data*/) {}
+
+ /// Whenever a parsing error of the DDL payload is encountered because of
+ /// corruption of the raw data (eg. bit flips) the OnError method is called
+ /// imediately at the point this error is discovered.
+ /// The default behaviour of this method is to do nothing.
+ /// - param ErrorCode This is an error code indicating the kind of problem
+ /// encountered with the DDL payload.
+ /// - param const void* This is a pointer into the DDL payload memory buffer
+ /// indicating the exact location where the parsing error happened
+ /// or i.e. the location of the corruption.
+ /// Note that a relative offset in bytes from the start of the memory buffer
+ /// can be calculated by: storing the buffer pointer recevied in OnNewBuffer
+ /// earlier in fBufferStart for example, and then the offset is given by:
+ /// offset = (unsigned long)location - (unsigned long)fBufferStart;
+ void OnError(ErrorCode /*error*/, const void* /*location*/) {}
+
+ /// This is a utility method which will unpack the MANU ID, channel ID and
+ /// ADC signal value from a raw data word. It should normally be used in
+ /// OnData() to unpack these fields.
+ /// [in] \param data This is the raw data word found in the DDL payload.
+ /// [out] \param manuId This is filled with the unpacked MANU ID.
+ /// [out] \param channelId This is filled with the unpacked MANU channel ID.
+ /// [out] \param adc This is filled with the unpacked ADC signal.
+ static void UnpackADC(
+ UInt_t data,
+ UShort_t& manuId, UChar_t& channelId, UShort_t& adc
+ )
+ {
+ manuId = (UShort_t)(data >> 18) & 0x7FF;
+ channelId = (Char_t)(data >> 12) & 0x3F;
+ adc = (UShort_t)(data & 0xFFF);
+ }
+
+ /// This is a utility method which converts an error code to a string
+ /// respresentation for printing purposes.
+ /// \param code The error code as received in OnError for example.
+ /// \return An ANSI string containing the name of the error code symbol.
+ static const char* ErrorCodeToString(ErrorCode code);
+
+ /// This is a utility method which converts an error code to user friendly
+ /// descriptive message useful for printing to the screen.
+ /// \param code The error code as received in OnError for example.
+ /// \return An ANSI string containing a descriptive message of the error.
+ static const char* ErrorCodeToMessage(ErrorCode code);
+};
+
+#endif // ALIMUONTRACKERDDLDECODEREVENTHANDLER_H