]> git.uio.no Git - u/mrichter/AliRoot.git/commitdiff
- added functionality to check CTP trigger classes within an analysis component
authorrichterm <richterm@f7af4fe6-9843-0410-8265-dc069ae4e863>
Wed, 1 Jul 2009 21:37:19 +0000 (21:37 +0000)
committerrichterm <richterm@f7af4fe6-9843-0410-8265-dc069ae4e863>
Wed, 1 Jul 2009 21:37:19 +0000 (21:37 +0000)
  bool EvaluateCTPTriggerClass checks a const char* expression of logic
  combinations of CTP trigger classes
- hanlde the ECS parameter data block on SOR and set CTP trigger classes
- first sketch of unit test for the above functionality, needs to be extended
  to really send the {ECSPARAM:PRIV} block
- long pending issue: comment warning on 'missing EOR' when a component has not
  received any event for processing. The check has to be replaced by a proper
  state handling

HLT/BASE/AliHLTComponent.cxx
HLT/BASE/AliHLTComponent.h
HLT/BASE/test/testAliHLTComponent_CTPTrigger.C [new file with mode: 0644]

index 1fc871814b67f045199dddb4547fc755a9d14b8a..5c14f07556019b332b8c193a0b4e0d2b44381b19 100644 (file)
@@ -36,6 +36,7 @@ using namespace std;
 #include "TObjectTable.h"
 #include "TClass.h"
 #include "TStopwatch.h"
+#include "TFormula.h"
 #include "AliHLTMemoryFile.h"
 #include "AliHLTMisc.h"
 #include <cassert>
@@ -90,6 +91,7 @@ AliHLTComponent::AliHLTComponent()
   fEventDoneDataSize(0),
   fCompressionLevel(ALIHLTCOMPONENT_DEFAULT_OBJECT_COMPRESSION)
   , fLastObjectSize(0)
+  , fpTriggerClasses(NULL)
 {
   // see header file for class documentation
   // or
@@ -130,6 +132,13 @@ AliHLTComponent::~AliHLTComponent()
   }
   if (fEventDoneData)
     delete [] reinterpret_cast<AliHLTUInt8_t*>( fEventDoneData );
+  fEventDoneData=NULL;
+
+  if (fpTriggerClasses) {
+    fpTriggerClasses->Delete();
+    delete fpTriggerClasses;
+  }
+  fpTriggerClasses=NULL;
 }
 
 AliHLTComponentHandler* AliHLTComponent::fgpComponentHandler=NULL;
@@ -264,11 +273,21 @@ int AliHLTComponent::Deinit()
   int iResult=0;
   iResult=DoDeinit();
   if (fpRunDesc) {
-    HLTWarning("did not receive EOR for run %d", fpRunDesc->fRunNo);
+    // TODO: the warning should be kept, but the condition is wrong since the
+    // AliHLTRunDesc is set before the SOR event in the SetRunDescription
+    // method. A couple of state flags should be defined but that is a bit more
+    // work to do. For the moment disable the warning (2009-07-01)
+    //HLTWarning("did not receive EOR for run %d", fpRunDesc->fRunNo);
     AliHLTRunDesc* pRunDesc=fpRunDesc;
     fpRunDesc=NULL;
     delete pRunDesc;
   }
+  if (fpTriggerClasses) {
+    fpTriggerClasses->Delete();
+    delete fpTriggerClasses;
+  }
+  fpTriggerClasses=NULL;
+
   fEventCount=0;
   return iResult;
 }
@@ -1454,6 +1473,7 @@ int AliHLTComponent::ProcessEvent( const AliHLTComponentEventData& evtData,
     int indexUpdtDCSEvent=-1;
     int indexSOREvent=-1;
     int indexEOREvent=-1;
+    int indexECSParamBlock=-1;
     for (unsigned int i=0; i<evtData.fBlockCnt && iResult>=0; i++) {
       if (fpInputBlocks[i].fDataType==kAliHLTDataTypeSOR) {
        indexSOREvent=i;
@@ -1504,6 +1524,8 @@ int AliHLTComponent::ProcessEvent( const AliHLTComponentEventData& evtData,
       } else if (fpInputBlocks[i].fDataType==kAliHLTDataTypeComponentTable) {
        forwardedBlocks.push_back(fpInputBlocks[i]);
        parentComponentTables.push_back(fpInputBlocks[i].fSpecification);
+      } else if (fpInputBlocks[i].fDataType==kAliHLTDataTypeECSParam) {
+       indexECSParamBlock=i;
       } else {
        // the processing function is called if there is at least one
        // non-steering data block. Steering blocks are not filtered out
@@ -1537,6 +1559,26 @@ int AliHLTComponent::ProcessEvent( const AliHLTComponentEventData& evtData,
       } else {
        iResult=-ENOMEM;
       }
+
+      if (indexECSParamBlock>=0) {
+       if (fpInputBlocks[indexECSParamBlock].fSize>0) {
+         const char* param=reinterpret_cast<const char*>(fpInputBlocks[indexECSParamBlock].fPtr);
+         TString paramString;
+         if (param[fpInputBlocks[indexECSParamBlock].fSize-1]!=0) {
+           HLTWarning("ECS parameter string not terminated");
+           paramString.Insert(0, param, fpInputBlocks[indexECSParamBlock].fSize);
+           paramString+="";
+         } else {
+           paramString=param;
+         }
+         ScanECSParam(paramString.Data());
+       } else {
+         HLTWarning("empty ECS parameter received");
+       }
+      } else {
+       // TODO: later on we might throw a warning here since the CTP trigger classes
+       // should be mandatory
+      }
     }
     if (indexEOREvent>=0) {
       bAddComponentTableEntry=true;
@@ -2192,3 +2234,131 @@ int AliHLTComponent::LoggingVarargs(AliHLTComponentLogSeverity severity,
 
   return iResult;
 }
+
+int AliHLTComponent::ScanECSParam(const char* ecsParam)
+{
+  // see header file for function documentation
+
+  // format of the parameter string from ECS
+  // <command>;<parameterkey>=<parametervalue>;<parameterkey>=<parametervalue>;...
+  // search for a subset of the parameterkeys
+  int iResult=0;
+  TString string=ecsParam;
+  TObjArray* parameter=string.Tokenize(";");
+  if (parameter) {
+    for (int i=0; i<parameter->GetEntries(); i++) {
+      TString entry=((TObjString*)parameter->At(i))->GetString();
+      HLTDebug("scanning ECS entry: %s", entry.Data());
+      TObjArray* entryParams=entry.Tokenize("=");
+      if (entryParams) {
+       if (entryParams->GetEntries()>1) {
+         if ((((TObjString*)entryParams->At(0))->GetString()).CompareTo("CTP_TRIGGER_CLASS")==0) {
+           int result=InitCTPTriggerClasses((((TObjString*)entryParams->At(1))->GetString()).Data());
+           if (iResult>=0 && result<0) iResult=result;
+         } else {
+           // TODO: scan the other parameters
+           // e.g. consistency check of run number
+         }
+       }
+       delete entryParams;
+      }
+    }
+    delete parameter;
+  }
+
+  return iResult;
+}
+
+int AliHLTComponent::InitCTPTriggerClasses(const char* ctpString)
+{
+  // see header file for function documentation
+  if (!ctpString) return -EINVAL;
+
+  if (fpTriggerClasses) {
+    fpTriggerClasses->Delete();
+  } else {
+    fpTriggerClasses=new TObjArray(gkNCTPTriggerClasses);
+  }
+  if (!fpTriggerClasses) return -ENOMEM;
+
+  // general format of the CTP_TRIGGER_CLASS parameter
+  // <bit position>:<Trigger class identifier string>:<detector-id-nr>-<detector-id-nr>-...,<bit position>:<Trigger class identifier string>:<detector-id-nr>-<detector-id-nr>-...,...
+  // the detector ids are ignored for the moment
+  HLTDebug(": %s", ctpString);
+  TString string=ctpString;
+  TObjArray* classEntries=string.Tokenize(",");
+  if (classEntries) {
+    for (int i=0; i<classEntries->GetEntries(); i++) {
+      TString entry=((TObjString*)classEntries->At(i))->GetString();
+      TObjArray* entryParams=entry.Tokenize(":");
+      if (entryParams) {
+       if (entryParams->GetEntries()==3 &&
+           (((TObjString*)entryParams->At(0))->GetString()).IsDigit()) {
+         int index=(((TObjString*)entryParams->At(0))->GetString()).Atoi();
+         if (index<gkNCTPTriggerClasses) {
+           fpTriggerClasses->AddAt(new TNamed("TriggerClass", (((TObjString*)entryParams->At(1))->GetString()).Data()), index);
+         } else {
+           // the trigger bitfield is fixed to 50 bits (gkNCTPTriggerClasses)
+           HLTError("invalid trigger class entry %s, index width of trigger bitfield", entry.Data());
+         }
+       } else {
+         HLTError("invalid trigger class entry %s", entry.Data());
+       }
+       delete entryParams;
+      }
+    }
+    delete classEntries;
+  }
+  return 0;
+}
+
+bool AliHLTComponent::EvaluateCTPTriggerClass(const char* expression, AliHLTComponentTriggerData& trigData) const
+{
+  // see header file for function documentation
+  if (!fpTriggerClasses) {
+    HLTError("trigger classes not initialized");
+    return false;
+  }
+
+  if (trigData.fDataSize != sizeof(AliHLTEventTriggerData)) {
+    HLTError("invalid trigger data size: %d expected %d", trigData.fDataSize, sizeof(AliHLTEventTriggerData));
+    return false;
+  }
+
+  // trigger mask is 50 bit wide and is stored in word 5 and 6 of the CDH
+  AliHLTEventTriggerData* evtData=reinterpret_cast<AliHLTEventTriggerData*>(trigData.fData);
+  AliHLTUInt64_t triggerMask=evtData->fCommonHeader[6];
+  triggerMask<<=32;
+  triggerMask|=evtData->fCommonHeader[5];
+
+  // use a TFormula to interprete the expression
+  // all classname are replaced by '[n]' which means the n'th parameter in the formula
+  // the parameters are set to 0 or 1 depending on the bit in the trigger mask
+  //
+  // TODO: this will most likely fail for class names like 'base', 'baseA', 'baseB'
+  // the class names must be fully unique, none must be contained as substring in
+  // another class name. Probably not needed for the moment but needs to be extended.
+  vector<Double_t> par;
+  TString condition=expression;
+  for (int i=0; i<gkNCTPTriggerClasses; i++) {
+    if (fpTriggerClasses->At(i)) {
+      TString className=fpTriggerClasses->At(i)->GetTitle();
+      //HLTDebug("checking trigger class %s", className.Data());
+      if (condition.Contains(className)) {
+       TString replace; replace.Form("[%d]", par.size());
+       //HLTDebug("replacing %s with %s in \"%s\"", className.Data(), replace.Data(), condition.Data());
+       condition.ReplaceAll(className, replace);
+       if (triggerMask&((AliHLTUInt64_t)0x1<<i)) par.push_back(1.0);
+       else par.push_back(0.0);
+      }
+    }
+  }
+
+  TFormula form("trigger expression", condition);
+  if (form.Compile()!=0) {
+    HLTError("invalid expression %s", expression);
+    return false;
+  }
+  if (form.EvalPar(&par[0], &par[0])>0.5) return true;
+  return false;
+}
index a9f3e1fbdf7e09b4c96bd2ad036093b89a19a891..5df88fa9aa50c3665ccb82d645fd4c980e864202 100644 (file)
@@ -1222,6 +1222,15 @@ class AliHLTComponent : public AliHLTLogging {
    */
   const char* GetChainId() const {return fChainId.c_str();}
 
+  /**
+   * Check whether a combination of trigger classes is fired.
+   * The expression can contain trigger class ids and logic operators
+   * like &&, ||, !, and ^, and may be grouped by parentheses.
+   * @param expression     a logic expression of trigger class ids
+   * @param trigData       the trigger data data
+   */
+  bool EvaluateCTPTriggerClass(const char* expression, AliHLTComponentTriggerData& trigData) const;
+
   /**
    * Check whether the current event is a valid data event.
    * @param pTgt    optional pointer to get the event type
@@ -1407,6 +1416,23 @@ class AliHLTComponent : public AliHLTLogging {
                              AliHLTUInt32_t offset,
                              const vector<AliHLTUInt32_t>& parents) const;
 
+  /**
+   * Scan the ECS parameter string.
+   * The framework provides both the parameters of CONFIGURE and ENGAGE
+   * in one string in a special data block kAliHLTDataTypeECSParam
+   * {ECSPARAM:PRIV}. The general format is
+   * <command>;<parameterkey>=<parametervalue>;<parameterkey>=<parametervalue>;...
+   */
+  int ScanECSParam(const char* ecsParam);
+
+  /**
+   * The trigger classes are determined from the trigger and propagated by
+   * ECS as part of the ENGAGE command parameter which is sent through the
+   * framework during the SOR event. This function treats the value of the
+   * parameter key CTP_TRIGGER_CLASS.
+   */
+  int InitCTPTriggerClasses(const char* ctpString);
+
   /** The global component handler instance */
   static AliHLTComponentHandler* fgpComponentHandler;              //! transient
 
@@ -1498,6 +1524,9 @@ class AliHLTComponent : public AliHLTLogging {
   /** size of last PushBack-serialized object */
   int fLastObjectSize;                                             //! transient
 
-  ClassDef(AliHLTComponent, 10)
+ /**  array of trigger class descriptors */
+  TObjArray* fpTriggerClasses;                                     //! transient
+
+  ClassDef(AliHLTComponent, 11)
 };
 #endif
diff --git a/HLT/BASE/test/testAliHLTComponent_CTPTrigger.C b/HLT/BASE/test/testAliHLTComponent_CTPTrigger.C
new file mode 100644 (file)
index 0000000..02fbe90
--- /dev/null
@@ -0,0 +1,372 @@
+// $Id$
+
+/**************************************************************************
+ * This file is property of and copyright by the ALICE HLT Project        * 
+ * ALICE Experiment at CERN, All rights reserved.                         *
+ *                                                                        *
+ * Primary Authors: Matthias Richter <Matthias.Richter@ift.uib.no>        *
+ *                  for The ALICE HLT Project.                            *
+ *                                                                        *
+ * 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.                  *
+ **************************************************************************/
+
+/** @file   testAliHLTComponent_CTPTrigger.C
+    @author Matthias Richter
+    @date   
+    @brief  Test program for default data types
+ */
+
+#ifndef __CINT__
+#include "TDatime.h"
+#include "TRandom.h"
+#include "AliHLTDataTypes.h"
+#include "AliHLTProcessor.h"
+#endif
+
+class AliHLTTestComponent : public AliHLTProcessor
+{
+public:
+  AliHLTTestComponent() {}
+  ~AliHLTTestComponent() {}
+
+  const char* GetComponentID() {return "TestComponent";};
+  void GetInputDataTypes(AliHLTComponentDataTypeList& list) {list.clear();}
+  AliHLTComponentDataType GetOutputDataType() {return kAliHLTAnyDataType;}
+  void GetOutputDataSize(unsigned long& constBase, double& inputMultiplier) {constBase=0; inputMultiplier=0;}
+  AliHLTComponent* Spawn() {return new AliHLTTestComponent;}
+
+  int InitTest(const char* param) {
+    return ScanECSParam(param);
+    //return InitCTPTriggerClasses(param);
+  }
+
+  bool Check(const char* expression, AliHLTComponentTriggerData* data) {
+    return EvaluateCTPTriggerClass(expression, *data);
+  }
+
+  class AliHLTCTPTriggerClass {
+  public:
+    AliHLTCTPTriggerClass() : fBit(~(unsigned)0), fClassName(""), fTrigger(false) {}
+    ~AliHLTCTPTriggerClass() {}
+
+    bool        Valid() {return fBit!=~(unsigned)0;}
+    unsigned    Bit() {return fBit;}
+    void        Bit(unsigned bit) {fBit=bit;}
+    bool        Trigger() {return fTrigger;}
+    void        Trigger(bool trigger) {fTrigger=trigger;}
+    const char* ClassName() {return fClassName.c_str();}    
+    void        ClassName(const char* classname) {fClassName=classname;}    
+  private:
+    unsigned fBit;
+    string   fClassName;
+    bool     fTrigger;
+  };
+protected:
+  int DoInit(int /*argc*/, const char** /*argv*/) {
+    return 0;
+  }
+
+  int DoDeinit() {
+    return 0;
+  }
+
+  int DoEvent( const AliHLTComponentEventData& /*evtData*/,
+              AliHLTComponentTriggerData& /*trigData*/) {
+    return 0;
+  }
+};
+
+class AliHLTTriggerDataAccess
+{
+public:
+  AliHLTTriggerDataAccess()
+    : fData(NULL)
+    , fEventData(NULL)
+    , fCDH(NULL)
+    , fMine(NULL)
+  {
+    unsigned size=sizeof(AliHLTComponentTriggerData) + sizeof(AliHLTEventTriggerData);
+    fMine=new Byte_t[size];
+    memset(fMine, 0, size);
+    AliHLTComponentTriggerData* data=reinterpret_cast<AliHLTComponentTriggerData*>(fMine);
+    data->fData=fMine+sizeof(AliHLTComponentTriggerData);
+    Set(data);
+  }
+
+  AliHLTTriggerDataAccess(AliHLTComponentTriggerData* pData)
+    : fData(NULL)
+    , fEventData(NULL)
+    , fCDH(NULL)
+    , fMine(NULL)
+  {
+    if (fMine) delete [] fMine;
+    fMine=NULL;
+    Set(pData);
+  }
+
+  ~AliHLTTriggerDataAccess(){
+    if (fMine) delete [] fMine;
+    fMine=NULL;
+    fData=NULL;
+    fEventData=NULL;
+    fCDH=NULL;
+  }
+
+  AliHLTComponentTriggerData* Data() {return fData;}
+
+  Long64_t              TriggerMask() {
+    Long64_t mask=0;
+    if (fCDH) {
+      mask=fCDH[6];
+      mask<<=32;
+      mask|=fCDH[5];
+    }
+    return mask;
+  }
+
+  int Set(AliHLTComponentTriggerData* data) {
+    fData=data;
+    fData->fDataSize=sizeof(AliHLTEventTriggerData);
+    fEventData=reinterpret_cast<AliHLTEventTriggerData*>(fData->fData);
+    fCDH=fEventData->fCommonHeader;
+    return 0;
+  }
+
+  int ResetCDH() {
+    if (fCDH) {
+      memset(fCDH, 0, 32);
+    }
+    return 0;
+  }
+
+  int TriggerBit(unsigned bit, bool set) {
+    if ((int)bit>=gkNCTPTriggerClasses) return -EINVAL;
+    if (!fCDH) return -ENODATA;
+
+    int word=5;
+    if (bit>=32) {
+      word++;
+      bit-=32;
+    }
+    if (set)
+      fCDH[word]|=(UInt_t)0x1<<bit;
+    else
+      fCDH[word]&=~((UInt_t)0x1<<bit);
+      
+    return bit;
+  }
+
+private:
+  AliHLTTriggerDataAccess(const AliHLTTriggerDataAccess&);
+  AliHLTTriggerDataAccess& operator=(const AliHLTTriggerDataAccess&);
+
+  AliHLTComponentTriggerData* fData;
+  AliHLTEventTriggerData*     fEventData;
+  AliHLTUInt32_t*             fCDH;
+  Byte_t*                     fMine;
+};
+
+/**
+ * Get a random number in the given range.
+ */
+int GetRandom(int min, int max)
+{
+  if (max-min<2) return min;
+  static TRandom rand;
+  static bool seedSet=false;
+  if (!seedSet) {
+    TDatime dt;
+    rand.SetSeed(dt.Get());
+    seedSet=true;
+  }
+  return min+rand.Integer(max-min);
+}
+
+/**
+ * Generate a random name of given length
+ */
+string GenerateTriggerClassName(int length)
+{
+  string tcn;
+  for (int i=0; i<length; i++) {
+    unsigned char c=GetRandom(48, 83);
+    if (c>57) c+=7;
+    tcn+=c;
+  }
+  return tcn;
+}
+
+/**
+ * Generate an array of trigger classes.
+ * The array has the specified size but the number antries which are actually
+ * filled is randomized.
+ */
+int GenerateTriggerClasses(int size, vector<AliHLTTestComponent::AliHLTCTPTriggerClass>& classes)
+{
+  classes.clear();
+  classes.resize(size);
+  unsigned count=GetRandom(4, size>16?size/2:size);
+  for (unsigned i=0; i<count; i++) {
+    int bit=0;
+    do {
+      bit=GetRandom(0, size);
+    } while (classes[bit].Valid());
+    classes[bit].Bit(bit);
+    classes[bit].ClassName((GenerateTriggerClassName(GetRandom(5,15))).c_str());
+  }
+  return classes.size();
+}
+
+int testAliHLTComponent_CTPTrigger()
+{
+  int iResult=0;
+  vector<AliHLTTestComponent::AliHLTCTPTriggerClass> triggerClasses;
+  if (GenerateTriggerClasses(GetRandom(5,gkNCTPTriggerClasses), triggerClasses)<=0) {
+    return -1;
+  }
+
+  TString parameter="CONFIGURE;CTP_TRIGGER_CLASS=";
+  vector<AliHLTTestComponent::AliHLTCTPTriggerClass>::iterator element=triggerClasses.begin();
+  while (element!=triggerClasses.end()) {
+    if (!element->Valid()) {
+      element=triggerClasses.erase(element);
+      continue;
+    }
+    if (!parameter.EndsWith("=")) parameter+=",";
+    parameter+=element->Bit(); parameter+=":";
+    parameter+=element->ClassName(); parameter+=":";
+    parameter+=0;
+    element++;
+  }
+  parameter+=";HLT_MODE=A;RUN_NO=0";
+
+  AliHLTTestComponent component;
+  component.SetGlobalLoggingLevel(kHLTLogDefault);
+  if ((iResult=component.InitTest(parameter.Data()))<0) return iResult;
+
+  AliHLTTriggerDataAccess trigData;
+  for (int cycle=0; cycle<500 && iResult>=0; cycle++) {
+    for (element=triggerClasses.begin();
+        element!=triggerClasses.end(); element++) {
+      element->Trigger(GetRandom(0,100)>50);
+    }
+
+    vector<AliHLTTestComponent::AliHLTCTPTriggerClass> shuffle;
+    shuffle.assign(triggerClasses.begin(), triggerClasses.end());
+    for (unsigned int trial=0; trial<2*triggerClasses.size() && iResult>=0; trial++) {
+      random_shuffle(shuffle.begin(), shuffle.end());
+
+      bool result=0;
+      bool trigger=0;
+      TString expression;
+      trigData.ResetCDH();
+      for (element=shuffle.begin();
+          element!=shuffle.end(); element++) {
+       trigData.TriggerBit(element->Bit(), element->Trigger());
+      }
+
+      // single class
+      for (element=shuffle.begin();
+          element!=shuffle.end() && iResult>=0 && trial<3;
+          element++) {
+       // is
+       result=element->Trigger();
+       expression=element->ClassName();
+       trigger=component.Check(expression.Data(), trigData.Data());
+       if (trigger!=result) {
+         cout << expression << ": " << element->Trigger()
+              << "->" << trigger 
+              << std::hex << "   (" << trigData.TriggerMask() << ")"
+              << endl;
+         cerr << "trigger does not match, expected " << result << endl;
+         iResult=-1;
+         break;
+       }
+
+       // is not
+       expression="!";
+       expression+=element->ClassName();
+       result=!result;
+       trigger=component.Check(expression.Data(), trigData.Data());
+       if (trigger!=result) {
+         cout << expression << ": " << element->Trigger()
+              << "->" << trigger 
+              << std::hex << "   (" << trigData.TriggerMask() << ")"
+              << endl;
+         cerr << "trigger does not match, expected " << result << endl;
+         iResult=-1;
+         break;
+       }
+      }
+
+      // OR
+      result=shuffle[0].Trigger() || shuffle[1].Trigger() || shuffle[2].Trigger();
+      expression.Form("%s || %s || %s",
+                     shuffle[0].ClassName(), shuffle[1].ClassName(), shuffle[2].ClassName());
+      trigger=component.Check(expression.Data(), trigData.Data());
+      if (trigger!=result) {
+       cout << expression << ": " << shuffle[0].Trigger() << shuffle[1].Trigger() << shuffle[2].Trigger() 
+            << "->" << trigger 
+            << std::hex << "   (" << trigData.TriggerMask() << ")"
+            << endl;
+       cerr << "trigger does not match, expected " << result << endl;
+       iResult=-1;
+       break;
+      }
+
+      // AND
+      result=shuffle[0].Trigger() && shuffle[1].Trigger() && shuffle[2].Trigger();
+      expression.Form("%s && %s && %s",
+                     shuffle[0].ClassName(), shuffle[1].ClassName(), shuffle[2].ClassName());
+
+      trigger=component.Check(expression.Data(), trigData.Data());
+      if (trigger!=result) {
+       cout << expression << ": " << shuffle[0].Trigger() << shuffle[1].Trigger() << shuffle[2].Trigger() 
+            << "->" << trigger 
+            << std::hex << "   (" << trigData.TriggerMask() << ")"
+            << endl;
+       cerr << "trigger does not match, expected " << result << endl;
+       iResult=-1;
+       break;
+      }
+
+      // mixed OR/AND
+      result=shuffle[0].Trigger() && (shuffle[1].Trigger() || shuffle[2].Trigger());
+      expression.Form("%s && (%s || %s)",
+                     shuffle[0].ClassName(), shuffle[1].ClassName(), shuffle[2].ClassName());
+
+      trigger=component.Check(expression.Data(), trigData.Data());
+      if (trigger!=result) {
+       cout << expression << ": " << shuffle[0].Trigger() << shuffle[1].Trigger() << shuffle[2].Trigger() 
+            << "->" << trigger 
+            << std::hex << "   (" << trigData.TriggerMask() << ")"
+            << endl;
+       cerr << "trigger does not match, expected " << result << endl;
+       iResult=-1;
+       break;
+      }
+    }
+  }
+  if (iResult<0) {
+    cerr << "check failed, dumping info" << endl;
+    cerr << "ECS param: " << parameter << endl;
+    for (element=triggerClasses.begin();
+        element!=triggerClasses.end(); element++) {
+      cerr << element->Trigger() << " " << element->Bit() << ": " << element->ClassName() << endl;
+    }
+  }
+  return iResult;
+}
+
+int main(int /*argc*/, const char** /*argv*/)
+{
+  int iResult=0;
+  iResult=testAliHLTComponent_CTPTrigger();
+  return iResult;
+}