]> git.uio.no Git - u/mrichter/AliRoot.git/blobdiff - HLT/rec/AliHLTReconstructor.cxx
The description of changes:
[u/mrichter/AliRoot.git] / HLT / rec / AliHLTReconstructor.cxx
index ea1036bfd9b04e06330b1a0ca40359c807329542..0b61f5a010091b7caaa37c45b66e1c69e390525b 100644 (file)
 
 #include <TSystem.h>
 #include <TObjString.h>
+#include "TFile.h"
+#include "TTree.h"
+#include "TObject.h"
+#include "TObjArray.h"
+#include "TClass.h"
+#include "TStreamerInfo.h"
 #include "AliHLTReconstructor.h"
 #include "AliLog.h"
+#include "AliRawReader.h"
 #include "AliESDEvent.h"
 #include "AliHLTSystem.h"
 #include "AliHLTOUTRawReader.h"
 #include "AliHLTOUTDigitReader.h"
 #include "AliHLTEsdManager.h"
+#include "AliHLTPluginBase.h"
+#include "AliHLTMisc.h"
+#include "AliCDBManager.h"
+#include "AliCDBEntry.h"
+#include "AliHLTMessage.h"
+#include "AliCentralTrigger.h"
+#include "AliTriggerConfiguration.h"
+#include "AliTriggerClass.h"
+#include "AliTriggerCluster.h"
+#include "AliDAQ.h"
+
+class AliCDBEntry;
 
 ClassImp(AliHLTReconstructor)
 
 AliHLTReconstructor::AliHLTReconstructor()
   : 
   AliReconstructor(),
-  AliHLTReconstructorBase(),
   fFctProcessHLTOUT(NULL),
-  fpEsdManager(NULL)
+  fpEsdManager(NULL),
+  fpPluginBase(new AliHLTPluginBase)
+  , fFlags(0)
+{ 
+  //constructor
+}
+
+AliHLTReconstructor::AliHLTReconstructor(const char* options)
+  : 
+  AliReconstructor(),
+  fFctProcessHLTOUT(NULL),
+  fpEsdManager(NULL),
+  fpPluginBase(new AliHLTPluginBase)
+  , fFlags(0)
 { 
   //constructor
+  if (options) Init(options);
 }
 
 AliHLTReconstructor::~AliHLTReconstructor()
 { 
   //destructor
 
-  AliHLTSystem* pSystem=GetInstance();
+  if (fpPluginBase) {
+  AliHLTSystem* pSystem=fpPluginBase->GetInstance();
   if (pSystem) {
-    AliDebug(0, Form("delete HLT system: status %#x", pSystem->GetStatusFlags()));
-    if (pSystem->CheckStatus(AliHLTSystem::kReady)) {
+    AliDebug(0, Form("terminate HLT system: status %#x", pSystem->GetStatusFlags()));
+    if (pSystem->CheckStatus(AliHLTSystem::kStarted)) {
       // send specific 'event' to execute the stop sequence
       pSystem->Reconstruct(0, NULL, NULL);
     }
   }
+  delete fpPluginBase;
+  }
+  fpPluginBase=NULL;
 
-  if (fpEsdManager) delete fpEsdManager;
+  if (fpEsdManager) AliHLTEsdManager::Delete(fpEsdManager);
   fpEsdManager=NULL;
 }
 
+void AliHLTReconstructor::Init(const char* options)
+{
+  // init the reconstructor
+  SetOption(options);
+  Init();
+}
+
 void AliHLTReconstructor::Init()
 {
   // init the reconstructor
-  AliHLTSystem* pSystem=GetInstance();
+  if (!fpPluginBase) {
+    AliError("internal memory error: can not get AliHLTSystem instance from plugin");
+    return;
+  }
+
+  AliHLTSystem* pSystem=fpPluginBase->GetInstance();
   if (!pSystem) {
     AliError("can not create AliHLTSystem object");
     return;
@@ -73,6 +121,8 @@ void AliHLTReconstructor::Init()
     return;
   }
 
+  TString esdManagerOptions;
+
   // the options scan has been moved to AliHLTSystem, the old code
   // here is kept to be able to run an older version of the HLT code
   // with newer AliRoot versions.
@@ -98,6 +148,13 @@ void AliHLTReconstructor::Init()
        }
       } else if (token.Contains("alilog=off")) {
        pSystem->SwitchAliLog(0);
+      } else if (token.CompareTo("ignore-hltout")==0) {
+       fFlags|=kAliHLTReconstructorIgnoreHLTOUT;
+      } else if (token.Contains("esdmanager=")) {
+       token.ReplaceAll("esdmanager=", "");
+       token.ReplaceAll(","," ");
+       token.ReplaceAll("'","");
+       esdManagerOptions=token;
       } else if (token.BeginsWith("lib") && token.EndsWith(".so")) {
        libs+=token;
        libs+=" ";
@@ -109,6 +166,19 @@ void AliHLTReconstructor::Init()
     delete pTokens;
   }
 
+  TString ecsParam;
+  TString ctpParam;
+  if (BuildCTPTriggerClassString(ctpParam)>=0) {
+    if (!ecsParam.IsNull()) ecsParam+=";";
+    ecsParam+="CTP_TRIGGER_CLASS=";
+    ecsParam+=ctpParam;
+  }
+
+  if (!ecsParam.IsNull()) {
+    option+=" ECS=";
+    option+=ecsParam;
+  }
+
   if (!libs.IsNull() &&
       (!pSystem->CheckStatus(AliHLTSystem::kLibrariesLoaded)) &&
       (pSystem->LoadComponentLibraries(libs.Data())<0)) {
@@ -136,33 +206,110 @@ void AliHLTReconstructor::Init()
   }
 
   gSystem->Load("libHLTinterface.so");
-  fFctProcessHLTOUT=gSystem->DynFindSymbol("libHLTinterface.so", "AliHLTSystemProcessHLTOUT");
+  fFctProcessHLTOUT=(void (*)())gSystem->DynFindSymbol("libHLTinterface.so", "AliHLTSystemProcessHLTOUT");
+
+  fpEsdManager=AliHLTEsdManager::New();
+  fpEsdManager->SetOption(esdManagerOptions.Data());
+
+  InitStreamerInfos();
+}
+
+const char* AliHLTReconstructor::fgkCalibStreamerInfoEntry="HLT/Calib/StreamerInfo";
+
+int AliHLTReconstructor::InitStreamerInfos()
+{
+  // init streamer infos for HLT reconstruction
+  // Root schema evolution is not enabled for AliHLTMessage and all streamed objects.
+  // Objects in the raw data payload rely on the availability of the correct stream info.
+  // The relevant streamer info for a specific run is stored in the OCDB.
+  // The method evaluates the following entries:
+  // - HLT/Calib/StreamerInfo
+  int iResult=0;
+
+  // to be activated later, this is supposed to go as patch into the v4-17-Release branch
+  // which doe snot have the AliHLTMisc implementation
+  //AliCDBEntry* pEntry=AliHLTMisc::Instance().LoadOCDBEntry(fgkCalibStreamerInfoEntry);
+  AliCDBEntry* pEntry=AliCDBManager::Instance()->Get(fgkCalibStreamerInfoEntry);
+  TObject* pObject=NULL;
+  //if (pEntry && (pObject=AliHLTMisc::Instance().ExtractObject(pEntry))!=NULL)
+  if (pEntry && (pObject=pEntry->GetObject())!=NULL)
+    {
+    TObjArray* pSchemas=dynamic_cast<TObjArray*>(pObject);
+    if (pSchemas) {
+      iResult=InitStreamerInfos(pSchemas);
+    } else {
+      AliError(Form("internal mismatch in OCDB entry %s: wrong class type", fgkCalibStreamerInfoEntry));
+    }
+  } else {
+    AliWarning(Form("missing HLT reco data (%s), skipping initialization of streamer info for TObjects in HLT raw data payload", fgkCalibStreamerInfoEntry));
+  }
+  return iResult;
+}
+
+int AliHLTReconstructor::InitStreamerInfos(TObjArray* pSchemas) const
+{
+  // init streamer infos for HLT reconstruction from an array of TStreamerInfo objects
 
-  fpEsdManager=new AliHLTEsdManager;
+  for (int i=0; i<pSchemas->GetEntriesFast(); i++) {
+    if (pSchemas->At(i)) {
+      TStreamerInfo* pSchema=dynamic_cast<TStreamerInfo*>(pSchemas->At(i));
+      if (pSchema) {
+       int version=pSchema->GetClassVersion();
+       TClass* pClass=TClass::GetClass(pSchema->GetName());
+       if (pClass) {
+         if (pClass->GetClassVersion()==version) {
+           AliDebug(0,Form("skipping schema definition %d version %d to class %s as this is the native version", i, version, pSchema->GetName()));
+           continue;
+         }
+         TObjArray* pInfos=pClass->GetStreamerInfos();
+         if (pInfos /*&& version<pInfos->GetEntriesFast()*/) {
+           if (pInfos->At(version)==NULL) {
+             TStreamerInfo* pClone=(TStreamerInfo*)pSchema->Clone();
+             if (pClone) {
+               pClone->SetClass(pClass);
+               pClone->BuildOld();
+               pInfos->AddAtAndExpand(pClone, version);
+               AliDebug(0,Form("adding schema definition %d version %d to class %s", i, version, pSchema->GetName()));
+             } else {
+               AliError(Form("skipping schema definition %d (%s), unable to create clone object", i, pSchema->GetName()));
+             }
+           } else {
+             TStreamerInfo* pInfo=dynamic_cast<TStreamerInfo*>(pInfos->At(version));
+             if (pInfo && pInfo->GetClassVersion()==version) {
+               AliDebug(0,Form("schema definition %d version %d already available in class %s, skipping ...", i, version, pSchema->GetName()));
+             } else {
+               AliError(Form("can not verify version for already existing schema definition %d (%s) version %d: version of existing definition is %d", i, pSchema->GetName(), version, pInfo?pInfo->GetClassVersion():-1));
+             }
+           }
+         } else {
+           AliError(Form("skipping schema definition %d (%s), unable to set version %d in info array of size %d", i, pSchema->GetName(), version, pInfos?pInfos->GetEntriesFast():-1));
+         }
+       } else {
+         AliError(Form("skipping schema definition %d (%s), unable to find class", i, pSchema->GetName()));
+       }
+      } else {
+       AliError(Form("skipping schema definition %d, not of TStreamerInfo", i));
+      }
+    }
+  }
+
+  return 0;
 }
 
-void AliHLTReconstructor::Reconstruct(AliRawReader* /*rawReader*/, TTree* /*clustersTree*/) const 
+void AliHLTReconstructor::Reconstruct(AliRawReader* rawReader, TTree* /*clustersTree*/) const 
 {
   // reconstruction of real data without writing of ESD
   // For each event, HLT reconstruction chains can be executed and
   // added to the existing HLTOUT data
   // The HLTOUT data is finally processed in FillESD
-  AliHLTSystem* pSystem=GetInstance();
-}
-
-void AliHLTReconstructor::FillESD(AliRawReader* rawReader, TTree* /*clustersTree*/, 
-                                 AliESDEvent* esd) const
-{
-  // reconstruct real data and fill ESD
-  if (!rawReader || !esd) {
-    AliError("missing raw reader or esd object");
+  if (!fpPluginBase) {
+    AliError("internal memory error: can not get AliHLTSystem instance from plugin");
     return;
   }
 
-  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-  // code needs to be moved back to Reconstruct as soon es HLT loader is implemented
   int iResult=0;
-  AliHLTSystem* pSystem=GetInstance();
+  AliHLTSystem* pSystem=fpPluginBase->GetInstance();
+
   if (pSystem) {
     if (pSystem->CheckStatus(AliHLTSystem::kError)) {
       AliError("HLT system in error state");
@@ -175,7 +322,23 @@ void AliHLTReconstructor::FillESD(AliRawReader* rawReader, TTree* /*clustersTree
     if ((iResult=pSystem->Reconstruct(1, NULL, rawReader))>=0) {
     }
   }
-  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+}
+
+void AliHLTReconstructor::FillESD(AliRawReader* rawReader, TTree* /*clustersTree*/, 
+                                 AliESDEvent* esd) const
+{
+  // reconstruct real data and fill ESD
+  if (!rawReader || !esd) {
+    AliError("missing raw reader or esd object");
+    return;
+  }
+
+  if (!fpPluginBase) {
+    AliError("internal memory error: can not get AliHLTSystem instance from plugin");
+    return;
+  }
+
+  AliHLTSystem* pSystem=fpPluginBase->GetInstance();
 
   if (pSystem) {
     if (pSystem->CheckStatus(AliHLTSystem::kError)) {
@@ -188,9 +351,14 @@ void AliHLTReconstructor::FillESD(AliRawReader* rawReader, TTree* /*clustersTree
     }
     pSystem->FillESD(-1, NULL, esd);
 
-    AliHLTOUTRawReader* pHLTOUT=new AliHLTOUTRawReader(rawReader, esd->GetEventNumberInFile(), fpEsdManager);
+    AliRawReader* input=NULL;
+    if ((fFlags&kAliHLTReconstructorIgnoreHLTOUT) == 0 ) {
+      input=rawReader;
+    }
+    AliHLTOUTRawReader* pHLTOUT=new AliHLTOUTRawReader(input, esd->GetEventNumberInFile(), fpEsdManager);
     if (pHLTOUT) {
       ProcessHLTOUT(pHLTOUT, esd);
+      delete pHLTOUT;
     } else {
       AliError("error creating HLTOUT handler");
     }
@@ -203,6 +371,7 @@ void AliHLTReconstructor::Reconstruct(TTree* /*digitsTree*/, TTree* /*clustersTr
 
   // all reconstruction has been moved to FillESD
   //AliReconstructor::Reconstruct(digitsTree,clustersTree);
+  AliInfo("running digit data reconstruction");
 }
 
 void AliHLTReconstructor::FillESD(TTree* /*digitsTree*/, TTree* /*clustersTree*/, AliESDEvent* esd) const
@@ -215,7 +384,10 @@ void AliHLTReconstructor::FillESD(TTree* /*digitsTree*/, TTree* /*clustersTree*/
   TString option = GetOption();
   if (!option.IsNull() && 
       (option.Contains("config=") || option.Contains("chains="))) {
-    AliWarning(Form("HLT reconstruction of simulated data takes place in AliSimulation\n"
+    AliWarning(Form("HLT reconstruction can be run embedded into Alireconstruction from\n"
+                   "raw data (real or simulated)). Reconstruction of of digit data takes\n"
+                   "place in AliSimulation, appropriate input conversion is needed.\n"
+                   "Consider running embedded into AliSimulation."
                    "        /***  run macro *****************************************/\n"
                    "        AliSimulation sim;\n"
                    "        sim.SetRunHLT(\"%s\");\n"
@@ -226,7 +398,12 @@ void AliHLTReconstructor::FillESD(TTree* /*digitsTree*/, TTree* /*clustersTree*/
                    "        sim.Run();\n"
                    "        /*********************************************************/", option.Data()));
   }
-  AliHLTSystem* pSystem=GetInstance();
+  if (!fpPluginBase) {
+    AliError("internal memory error: can not get AliHLTSystem instance from plugin");
+    return;
+  }
+
+  AliHLTSystem* pSystem=fpPluginBase->GetInstance();
   if (pSystem) {
     if (pSystem->CheckStatus(AliHLTSystem::kError)) {
       AliError("HLT system in error state");
@@ -240,17 +417,23 @@ void AliHLTReconstructor::FillESD(TTree* /*digitsTree*/, TTree* /*clustersTree*/
     AliHLTOUTDigitReader* pHLTOUT=new AliHLTOUTDigitReader(esd->GetEventNumberInFile(), fpEsdManager);
     if (pHLTOUT) {
       ProcessHLTOUT(pHLTOUT, esd);
+      delete pHLTOUT;
     } else {
       AliError("error creating HLTOUT handler");
     }
   }
 }
 
-void AliHLTReconstructor::ProcessHLTOUT(AliHLTOUT* pHLTOUT, AliESDEvent* esd) const
+void AliHLTReconstructor::ProcessHLTOUT(AliHLTOUT* pHLTOUT, AliESDEvent* esd, bool bVerbose) const
 {
-  // treatmen of simulated or real HLTOUT data
+  // treatment of simulated or real HLTOUT data
   if (!pHLTOUT) return;
-  AliHLTSystem* pSystem=GetInstance();
+  if (!fpPluginBase) {
+    AliError("internal memory error: can not get AliHLTSystem instance from plugin");
+    return;
+  }
+
+  AliHLTSystem* pSystem=fpPluginBase->GetInstance();
   if (!pSystem) {
     AliError("error getting HLT system instance");
     return;
@@ -261,6 +444,30 @@ void AliHLTReconstructor::ProcessHLTOUT(AliHLTOUT* pHLTOUT, AliESDEvent* esd) co
     return;
   }
 
+  if (bVerbose)
+    PrintHLTOUTContent(pHLTOUT);
+
+  int blockindex=pHLTOUT->SelectFirstDataBlock(kAliHLTDataTypeStreamerInfo);
+  if (blockindex>=0) {
+    const AliHLTUInt8_t* pBuffer=NULL;
+    AliHLTUInt32_t size=0;
+    if (pHLTOUT->GetDataBuffer(pBuffer, size)>=0) {
+      TObject* pObject=AliHLTMessage::Extract(pBuffer, size);
+      if (pObject) {
+       TObjArray* pArray=dynamic_cast<TObjArray*>(pObject);
+       if (pArray) {
+         InitStreamerInfos(pArray);
+       } else {
+         AliError(Form("wrong class type of streamer info list: expected TObjArray, but object is of type %s", pObject->Class()->GetName()));
+       }
+      } else {
+       AliError(Form("failed to extract object from data block of type %s", AliHLTComponent::DataType2Text(kAliHLTDataTypeStreamerInfo).c_str()));
+      }
+    } else {
+      AliError(Form("failed to get data buffer for block of type %s", AliHLTComponent::DataType2Text(kAliHLTDataTypeStreamerInfo).c_str()));
+    }
+  }
+
   if (fFctProcessHLTOUT) {
     typedef int (*AliHLTSystemProcessHLTOUT)(AliHLTSystem* pInstance, AliHLTOUT* pHLTOUT, AliESDEvent* esd);
     AliHLTSystemProcessHLTOUT pFunc=(AliHLTSystemProcessHLTOUT)fFctProcessHLTOUT;
@@ -268,4 +475,143 @@ void AliHLTReconstructor::ProcessHLTOUT(AliHLTOUT* pHLTOUT, AliESDEvent* esd) co
       AliError("error processing HLTOUT");
     }
   }
+  pHLTOUT->Reset();
+}
+
+void AliHLTReconstructor::ProcessHLTOUT(const char* digitFile, AliESDEvent* pEsd) const
+{
+  // debugging/helper function to examine simulated data
+  if (!digitFile) return;
+
+  // read the number of events
+  TFile f(digitFile);
+  if (f.IsZombie()) return;
+  TTree* pTree=NULL;
+  f.GetObject("rawhltout", pTree);
+  if (!pTree) {
+    AliWarning(Form("can not find tree rawhltout in file %s", digitFile));
+    return ;
+  }
+  int nofEvents=pTree->GetEntries();
+  f.Close();
+  //delete pTree; OF COURSE NOT! its an object in the file
+  pTree=NULL;
+
+  for (int event=0; event<nofEvents; event++) {
+    AliHLTOUTDigitReader* pHLTOUT=new AliHLTOUTDigitReader(event, fpEsdManager, digitFile);
+    if (pHLTOUT) {
+      AliInfo(Form("event %d", event));
+      ProcessHLTOUT(pHLTOUT, pEsd, true);
+      delete pHLTOUT;
+    } else {
+      AliError("error creating HLTOUT handler");
+    }
+  }
+}
+
+void AliHLTReconstructor::ProcessHLTOUT(AliRawReader* pRawReader, AliESDEvent* pEsd) const
+{
+  // debugging/helper function to examine simulated or real HLTOUT data
+  if (!pRawReader) return;
+
+  pRawReader->RewindEvents();
+  for (int event=0; pRawReader->NextEvent(); event++) {
+    AliHLTOUTRawReader* pHLTOUT=new AliHLTOUTRawReader(pRawReader, event, fpEsdManager);
+    if (pHLTOUT) {
+      AliInfo(Form("event %d", event));
+      // the two event fields contain: period - orbit - bunch crossing counter
+      //        id[0]               id[1]
+      // |32                0|32                0|
+      //
+      // |      28 bit    |       24 bit     | 12|
+      //        period          orbit         bcc
+      AliHLTUInt64_t eventId=0;
+      const UInt_t* rawreaderEventId=pRawReader->GetEventId();
+      if (rawreaderEventId) {
+       eventId=rawreaderEventId[0];
+       eventId=eventId<<32;
+       eventId|=rawreaderEventId[1];
+      }
+      AliInfo(Form("Event Id from rawreader:\t 0x%016llx", eventId));
+      ProcessHLTOUT(pHLTOUT, pEsd, true);
+      delete pHLTOUT;
+    } else {
+      AliError("error creating HLTOUT handler");
+    }
+  }
+}
+
+void AliHLTReconstructor::PrintHLTOUTContent(AliHLTOUT* pHLTOUT) const
+{
+  // print the block specifications of the HLTOUT data blocks
+  if (!pHLTOUT) return;
+  int iResult=0;
+
+  AliInfo(Form("Event Id from hltout:\t 0x%016llx", pHLTOUT->EventId()));
+  for (iResult=pHLTOUT->SelectFirstDataBlock();
+       iResult>=0;
+       iResult=pHLTOUT->SelectNextDataBlock()) {
+    AliHLTComponentDataType dt=kAliHLTVoidDataType;
+    AliHLTUInt32_t spec=kAliHLTVoidDataSpec;
+    pHLTOUT->GetDataBlockDescription(dt, spec);
+    const AliHLTUInt8_t* pBuffer=NULL;
+    AliHLTUInt32_t size=0;
+    if (pHLTOUT->GetDataBuffer(pBuffer, size)>=0) {
+      pHLTOUT->ReleaseDataBuffer(pBuffer);
+      pBuffer=NULL; // just a dummy
+    }
+    AliInfo(Form("   %s  0x%x: size %d", AliHLTComponent::DataType2Text(dt).c_str(), spec, size));
+  }
+}
+
+int AliHLTReconstructor::BuildCTPTriggerClassString(TString& triggerclasses) const
+{
+  // build the CTP trigger class string from the OCDB entry of the CTP trigger
+  int iResult=0;
+  
+  triggerclasses.Clear();
+  AliCentralTrigger* pCTP = new AliCentralTrigger();
+  AliTriggerConfiguration *config=NULL;
+  TString configstr("");
+  if (pCTP->LoadConfiguration(configstr) && 
+      (config = pCTP->GetConfiguration())!=NULL) {
+    const TObjArray& classesArray = config->GetClasses();
+    int nclasses = classesArray.GetEntriesFast();
+    for( int iclass=0; iclass < nclasses; iclass++ ) {
+      AliTriggerClass* trclass = NULL;
+      if (classesArray.At(iclass) && (trclass=dynamic_cast<AliTriggerClass*>(classesArray.At(iclass)))!=NULL) {
+       TString entry;
+       int trindex = TMath::Nint(TMath::Log2(trclass->GetMask()));
+       entry.Form("%02d:%s:", trindex, trclass->GetName());
+       AliTriggerCluster* cluster=NULL;
+       TObject* clusterobj=config->GetClusters().FindObject(trclass->GetCluster());
+       if (clusterobj && (cluster=dynamic_cast<AliTriggerCluster*>(clusterobj))!=NULL) {
+         TString detectors=cluster->GetDetectorsInCluster();
+         TObjArray* pTokens=detectors.Tokenize(" ");
+         if (pTokens) {
+           for (int dix=0; dix<pTokens->GetEntriesFast(); dix++) {
+             int id=AliDAQ::DetectorID(((TObjString*)pTokens->At(dix))->GetString());
+             if (id>=0) {
+               TString detstr; detstr.Form("%s%02d", dix>0?"-":"", id);
+               entry+=detstr;
+             } else {
+               AliError(Form("invalid detector name extracted from trigger cluster: %s (%s)", ((TObjString*)pTokens->At(dix))->GetString().Data(), detectors.Data()));
+               iResult=-EPROTO;
+               break;
+             }
+           }
+           delete pTokens;
+         }
+       } else {
+         AliError(Form("can not find trigger cluster %s in config", trclass->GetCluster()));
+         iResult=-EPROTO;
+         break;
+       }
+       if (!triggerclasses.IsNull()) triggerclasses+=",";
+       triggerclasses+=entry;
+      }
+    }
+  }
+
+  return iResult;
 }