]> git.uio.no Git - u/mrichter/AliRoot.git/blob - HLT/rec/AliHLTReconstructor.cxx
cleanup of previous HLTOUT instance in case FillESD was switched off
[u/mrichter/AliRoot.git] / HLT / rec / AliHLTReconstructor.cxx
1 // $Id$
2
3 //**************************************************************************
4 //* This file is property of and copyright by the ALICE HLT Project        * 
5 //* ALICE Experiment at CERN, All rights reserved.                         *
6 //*                                                                        *
7 //* Primary Authors: Matthias Richter <Matthias.Richter@ift.uib.no>        *
8 //*                  for The ALICE HLT Project.                            *
9 //*                                                                        *
10 //* Permission to use, copy, modify and distribute this software and its   *
11 //* documentation strictly for non-commercial purposes is hereby granted   *
12 //* without fee, provided that the above copyright notice appears in all   *
13 //* copies and that both the copyright notice and this permission notice   *
14 //* appear in the supporting documentation. The authors make no claims     *
15 //* about the suitability of this software for any purpose. It is          *
16 //* provided "as is" without express or implied warranty.                  *
17 //**************************************************************************
18
19 //  @file   AliHLTReconstructor.cxx
20 //  @author Matthias Richter
21 //  @date   
22 //  @brief  Binding class for HLT reconstruction in AliRoot
23 //          Implements bot the interface to run HLT chains embedded into
24 //          AliReconstruction and the unpacking and treatment of HLTOUT
25
26 #include <TSystem.h>
27 #include <TObjString.h>
28 #include "TFile.h"
29 #include "TTree.h"
30 #include "TObject.h"
31 #include "TObjArray.h"
32 #include "TClass.h"
33 #include "TStreamerInfo.h"
34 #include "AliHLTReconstructor.h"
35 #include "AliLog.h"
36 #include "AliRawReader.h"
37 #include "AliESDEvent.h"
38 #include "AliHLTSystem.h"
39 #include "AliHLTOUTRawReader.h"
40 #include "AliHLTOUTDigitReader.h"
41 #include "AliHLTEsdManager.h"
42 #include "AliHLTPluginBase.h"
43 #include "AliHLTMisc.h"
44 #include "AliCDBManager.h"
45 #include "AliCDBEntry.h"
46 #include "AliHLTMessage.h"
47 #include "AliCentralTrigger.h"
48 #include "AliTriggerConfiguration.h"
49 #include "AliTriggerClass.h"
50 #include "AliTriggerCluster.h"
51 #include "AliDAQ.h"
52 #include "AliRunLoader.h"
53
54 class AliCDBEntry;
55
56 /** ROOT macro for the implementation of ROOT specific class methods */
57 ClassImp(AliHLTReconstructor)
58
59 AliHLTReconstructor::AliHLTReconstructor()
60   : AliReconstructor()
61   , fpEsdManager(NULL)
62   , fpPluginBase(new AliHLTPluginBase)
63   , fFlags(0)
64
65   //constructor
66 }
67
68 AliHLTReconstructor::AliHLTReconstructor(const char* options)
69   : AliReconstructor()
70   , fpEsdManager(NULL)
71   , fpPluginBase(new AliHLTPluginBase)
72   , fFlags(0)
73
74   //constructor
75   if (options) Init(options);
76 }
77
78 AliHLTReconstructor::~AliHLTReconstructor()
79
80   //destructor
81
82   if (fpEsdManager) AliHLTEsdManager::Delete(fpEsdManager);
83   fpEsdManager=NULL;
84
85   if (fpPluginBase) {
86   AliHLTSystem* pSystem=fpPluginBase->GetInstance();
87   if (pSystem) {
88     AliDebug(0, Form("terminate HLT system: status %#x", pSystem->GetStatusFlags()));
89     if (pSystem->CheckStatus(AliHLTSystem::kStarted)) {
90       // send specific 'event' to execute the stop sequence
91       pSystem->Reconstruct(0, NULL, NULL);
92     }
93   }
94   delete fpPluginBase;
95   }
96   fpPluginBase=NULL;
97
98 }
99
100 void AliHLTReconstructor::Init(const char* options)
101 {
102   // init the reconstructor
103   SetOption(options);
104   Init();
105 }
106
107 void AliHLTReconstructor::Init()
108 {
109   // init the reconstructor
110   if (!fpPluginBase) {
111     AliError("internal memory error: can not get AliHLTSystem instance from plugin");
112     return;
113   }
114
115   AliHLTSystem* pSystem=fpPluginBase->GetInstance();
116   if (!pSystem) {
117     AliError("can not create AliHLTSystem object");
118     return;
119   }
120   if (pSystem->CheckStatus(AliHLTSystem::kError)) {
121     AliError("HLT system in error state");
122     return;
123   }
124
125   TString esdManagerOptions;
126
127   // the options scan has been moved to AliHLTSystem, the old code
128   // here is kept to be able to run an older version of the HLT code
129   // with newer AliRoot versions.
130   TString option = GetOption();
131   TObjArray* pTokens=option.Tokenize(" ");
132   option="";
133   if (pTokens) {
134     int iEntries=pTokens->GetEntries();
135     for (int i=0; i<iEntries; i++) {
136       TString token=(((TObjString*)pTokens->At(i))->GetString());
137       if (token.Contains("loglevel=")) {
138         TString param=token.ReplaceAll("loglevel=", "");
139         if (param.IsDigit()) {
140           pSystem->SetGlobalLoggingLevel((AliHLTComponentLogSeverity)param.Atoi());
141         } else if (param.BeginsWith("0x") &&
142                    param.Replace(0,2,"",0).IsHex()) {
143           int severity=0;
144           sscanf(param.Data(),"%x", &severity);
145           pSystem->SetGlobalLoggingLevel((AliHLTComponentLogSeverity)severity);
146         } else {
147           AliWarning("wrong parameter for option \'loglevel=\', (hex) number expected");
148         }
149       } else if (token.Contains("alilog=off")) {
150         pSystem->SwitchAliLog(0);
151       } else if (token.CompareTo("ignore-hltout")==0) {
152         fFlags|=kAliHLTReconstructorIgnoreHLTOUT;
153       } else if (token.CompareTo("run-online-config")==0) {
154         fFlags|=kAliHLTReconstructorIgnoreHLTOUT;
155         if (option.Length()>0) option+=" ";
156         option+=token;
157       } else if (token.CompareTo("ignore-ctp")==0) {
158         fFlags|=kAliHLTReconstructorIgnoreCTP;
159       } else if (token.Contains("esdmanager=")) {
160         token.ReplaceAll("esdmanager=", "");
161         token.ReplaceAll(","," ");
162         token.ReplaceAll("'","");
163         esdManagerOptions=token;
164       } else {
165         if (option.Length()>0) option+=" ";
166         option+=token;
167       }
168     }
169     delete pTokens;
170   }
171
172   TString ecsParam;
173   TString ctpParam;
174   if ((fFlags&kAliHLTReconstructorIgnoreCTP)==0 &&
175       BuildCTPTriggerClassString(ctpParam)>=0) {
176     if (!ecsParam.IsNull()) ecsParam+=";";
177     ecsParam+="CTP_TRIGGER_CLASS=";
178     ecsParam+=ctpParam;
179   }
180
181   if (!ecsParam.IsNull()) {
182     option+=" ECS=";
183     option+=ecsParam;
184   }
185
186   if (!pSystem->CheckStatus(AliHLTSystem::kReady)) {
187     if (pSystem->ScanOptions(option.Data())<0) {
188       AliError("error setting options for HLT system");
189       return;
190     }
191     if ((pSystem->Configure())<0) {
192       AliError("error during HLT system configuration");
193       return;
194     }
195   }
196
197   fpEsdManager=AliHLTEsdManager::New();
198   if (fpEsdManager) {
199     fpEsdManager->SetOption(esdManagerOptions.Data());
200   }
201
202   AliHLTMisc::Instance().InitStreamerInfos(fgkCalibStreamerInfoEntry);
203 }
204
205 const char* AliHLTReconstructor::fgkCalibStreamerInfoEntry="HLT/Calib/StreamerInfo";
206
207 void AliHLTReconstructor::Reconstruct(AliRawReader* rawReader, TTree* /*clustersTree*/) const 
208 {
209   // reconstruction of real data without writing of ESD
210   // For each event, HLT reconstruction chains can be executed and
211   // added to the existing HLTOUT data
212   // The HLTOUT data is finally processed in FillESD
213
214   if (!fpPluginBase) {
215     AliError("internal memory error: can not get AliHLTSystem instance from plugin");
216     return;
217   }
218
219   int iResult=0;
220   AliHLTSystem* pSystem=fpPluginBase->GetInstance();
221
222   if (pSystem) {
223     AliHLTOUT* pHLTOUT=NULL;
224     pSystem->InvalidateHLTOUT(&pHLTOUT);
225     if (pHLTOUT) {
226       delete pHLTOUT;
227       pHLTOUT=NULL;
228     }
229     if (pSystem->CheckStatus(AliHLTSystem::kError)) {
230       AliError("HLT system in error state");
231       return;
232     }
233     if (!pSystem->CheckStatus(AliHLTSystem::kReady)) {
234       AliError("HLT system in wrong state");
235       return;
236     }
237
238     // init the HLTOUT instance for the current event
239     // not nice. Have to query the global run loader to get the current event no.
240     Int_t eventNo=-1;
241     AliRunLoader* runloader = AliRunLoader::Instance();
242     if (runloader) {
243       eventNo=runloader->GetEventNumber();
244     }
245     if (eventNo>=0) {
246       AliRawReader* input=NULL;
247       if ((fFlags&kAliHLTReconstructorIgnoreHLTOUT) == 0 ) {
248         input=rawReader;
249       }
250       pHLTOUT=new AliHLTOUTRawReader(input, eventNo, fpEsdManager);
251       if (pHLTOUT) {
252         if (pHLTOUT->Init()>=0) {
253           pSystem->InitHLTOUT(pHLTOUT);
254         } else {
255           AliError("error : initialization of HLTOUT handler failed");
256         }
257       } else {
258         AliError("memory allocation failed: can not create AliHLTOUT object");
259       }
260     } else {
261       AliError("can not get event number");
262     }
263
264     if ((iResult=pSystem->Reconstruct(1, NULL, rawReader))>=0) {
265     }
266   }
267 }
268
269 void AliHLTReconstructor::FillESD(AliRawReader* rawReader, TTree* /*clustersTree*/, 
270                                   AliESDEvent* esd) const
271 {
272   // reconstruct real data and fill ESD
273   if (!rawReader || !esd) {
274     AliError("missing raw reader or esd object");
275     return;
276   }
277
278   if (!fpPluginBase) {
279     AliError("internal memory error: can not get AliHLTSystem instance from plugin");
280     return;
281   }
282
283   AliHLTSystem* pSystem=fpPluginBase->GetInstance();
284
285   if (pSystem) {
286     if (pSystem->CheckStatus(AliHLTSystem::kError)) {
287       AliError("HLT system in error state");
288       return;
289     }
290     if (!pSystem->CheckStatus(AliHLTSystem::kReady)) {
291       AliError("HLT system in wrong state");
292       return;
293     }
294     pSystem->FillESD(-1, NULL, esd);
295
296     // the HLTOUT handler has either been created in the AliHLTReconstructor::Reconstruct
297     // step of this event or is created now. In either case the instance is deleted after
298     // the processing
299     AliHLTOUT* pHLTOUT=NULL;
300     pSystem->InvalidateHLTOUT(&pHLTOUT);
301     if (!pHLTOUT) {
302       AliRawReader* input=NULL;
303       if ((fFlags&kAliHLTReconstructorIgnoreHLTOUT) == 0 ) {
304         input=rawReader;
305       }
306       pHLTOUT=new AliHLTOUTRawReader(input, esd->GetEventNumberInFile(), fpEsdManager);
307     }
308     if (pHLTOUT) {
309       ProcessHLTOUT(pHLTOUT, esd, (pSystem->GetGlobalLoggingLevel()&kHLTLogDebug)!=0);
310       delete pHLTOUT;
311     } else {
312       AliError("error creating HLTOUT handler");
313     }
314   }
315 }
316
317 void AliHLTReconstructor::Reconstruct(TTree* /*digitsTree*/, TTree* /*clustersTree*/) const
318 {
319   // reconstruct simulated data
320
321   AliHLTSystem* pSystem=fpPluginBase->GetInstance();
322
323   if (pSystem) {
324     // create the HLTOUT instance in order to be available for other detector reconstruction
325     // first cleanup any existing instance
326     AliHLTOUT* pHLTOUT=NULL;
327     pSystem->InvalidateHLTOUT(&pHLTOUT);
328     if (pHLTOUT) {
329       delete pHLTOUT;
330       pHLTOUT=NULL;
331     }
332
333     // not nice. Have to query the global run loader to get the current event no.
334     // This is related to the missing AliLoader for HLT.
335     // Since AliReconstruction can not provide a digits tree, the file needs to be accessed
336     // explicitely, and the corresponding event needs to be selected.
337     Int_t eventNo=-1;
338     AliRunLoader* runloader = AliRunLoader::Instance();
339     if (runloader) {
340       eventNo=runloader->GetEventNumber();
341     }
342     if (eventNo>=0) {
343       const char* digitfile=NULL;
344       if ((fFlags&kAliHLTReconstructorIgnoreHLTOUT) == 0 ) {
345         digitfile="HLT.Digits.root";
346       }
347
348       pHLTOUT=new AliHLTOUTDigitReader(eventNo, fpEsdManager, digitfile);
349       if (pHLTOUT) {
350         if (pHLTOUT->Init()>=0) {
351           pSystem->InitHLTOUT(pHLTOUT);
352         } else {
353           AliError("error : initialization of HLTOUT handler failed");
354         }
355       } else {
356         AliError("memory allocation failed: can not create AliHLTOUT object");
357       }
358     } else {
359       AliError("can not get event number");
360     }
361
362     // all data processing happens in FillESD
363   }
364 }
365
366 void AliHLTReconstructor::FillESD(TTree* /*digitsTree*/, TTree* /*clustersTree*/, AliESDEvent* esd) const
367 {
368   // reconstruct simulated data and fill ESD
369
370   // later this is the place to extract the simulated HLT data
371   // for now it's only an user failure condition as he tries to run HLT reconstruction
372   // on simulated data 
373   TString option = GetOption();
374   if (!option.IsNull() && 
375       (option.Contains("config=") || option.Contains("chains="))) {
376     AliWarning(Form("You are trying to run a custom HLT chain on digits data.\n\n"
377                     "HLT reconstruction can be run embedded into AliReconstruction from\n"
378                     "raw data (real or simulated)). Reconstruction of digit data takes\n"
379                     "place in AliSimulation, appropriate input conversion is needed to\n"
380                     "feed data from the detector digits into the HLT chain.\n"
381                     "Consider running embedded into AliSimulation.\n"
382                     "        /***  run macro *****************************************/\n"
383                     "        AliSimulation sim;\n"
384                     "        sim.SetRunHLT(\"%s\");\n"
385                     "        sim.SetRunGeneration(kFALSE);\n"
386                     "        sim.SetMakeDigits(\"\");\n"
387                     "        sim.SetMakeSDigits(\"\");\n"
388                     "        sim.SetMakeDigitsFromHits(\"\");\n"
389                     "        sim.Run();\n"
390                     "        /*********************************************************/\n\n",
391                     option.Data()));
392   }
393   if (!fpPluginBase) {
394     AliError("internal memory error: can not get AliHLTSystem instance from plugin");
395     return;
396   }
397
398   AliHLTSystem* pSystem=fpPluginBase->GetInstance();
399   if (pSystem) {
400     if (pSystem->CheckStatus(AliHLTSystem::kError)) {
401       AliError("HLT system in error state");
402       return;
403     }
404     if (!pSystem->CheckStatus(AliHLTSystem::kReady)) {
405       AliError("HLT system in wrong state");
406       return;
407     }
408
409     // the HLTOUT handler has either been created in the AliHLTReconstructor::Reconstruct
410     // step of this event or is created now. In either case the instance is deleted after
411     // the processing
412     AliHLTOUT* pHLTOUT=NULL;
413     pSystem->InvalidateHLTOUT(&pHLTOUT);
414     if (!pHLTOUT) {
415       const char* digitfile=NULL;
416       if ((fFlags&kAliHLTReconstructorIgnoreHLTOUT) == 0 ) {
417         digitfile="HLT.Digits.root";
418       }
419       pHLTOUT=new AliHLTOUTDigitReader(esd->GetEventNumberInFile(), fpEsdManager, digitfile);
420     }
421
422     if (pHLTOUT) {
423       ProcessHLTOUT(pHLTOUT, esd, (pSystem->GetGlobalLoggingLevel()&kHLTLogDebug)!=0);
424       delete pHLTOUT;
425     } else {
426       AliError("error creating HLTOUT handler");
427     }
428   }
429 }
430
431 void AliHLTReconstructor::ProcessHLTOUT(AliHLTOUT* pHLTOUT, AliESDEvent* esd, bool bVerbose) const
432 {
433   // treatment of simulated or real HLTOUT data
434   if (!pHLTOUT) return;
435   if (!fpPluginBase) {
436     AliError("internal memory error: can not get AliHLTSystem instance from plugin");
437     return;
438   }
439
440   AliHLTSystem* pSystem=fpPluginBase->GetInstance();
441   if (!pSystem) {
442     AliError("error getting HLT system instance");
443     return;
444   }
445
446   if (pHLTOUT->Init()<0) {
447     AliError("error : initialization of HLTOUT handler failed");
448     return;
449   }
450
451   if (bVerbose)
452     PrintHLTOUTContent(pHLTOUT);
453
454   int blockindex=pHLTOUT->SelectFirstDataBlock(kAliHLTDataTypeStreamerInfo);
455   if (blockindex>=0) {
456     const AliHLTUInt8_t* pBuffer=NULL;
457     AliHLTUInt32_t size=0;
458     if (pHLTOUT->GetDataBuffer(pBuffer, size)>=0) {
459       TObject* pObject=AliHLTMessage::Extract(pBuffer, size);
460       if (pObject) {
461         TObjArray* pArray=dynamic_cast<TObjArray*>(pObject);
462         if (pArray) {
463           AliHLTMisc::Instance().InitStreamerInfos(pArray);
464         } else {
465           AliError(Form("wrong class type of streamer info list: expected TObjArray, but object is of type %s", pObject->Class()->GetName()));
466         }
467       } else {
468         AliError(Form("failed to extract object from data block of type %s", AliHLTComponent::DataType2Text(kAliHLTDataTypeStreamerInfo).c_str()));
469       }
470     } else {
471       AliError(Form("failed to get data buffer for block of type %s", AliHLTComponent::DataType2Text(kAliHLTDataTypeStreamerInfo).c_str()));
472     }
473   }
474
475   if (pSystem->ProcessHLTOUT(pHLTOUT, esd)<0) {
476     AliError("error processing HLTOUT");
477   }
478
479   if (bVerbose) {
480     AliInfo("HLT ESD content:");
481     esd->Print();
482   }
483   pHLTOUT->Reset();
484 }
485
486 void AliHLTReconstructor::ProcessHLTOUT(const char* digitFile, AliESDEvent* pEsd) const
487 {
488   // debugging/helper function to examine simulated data
489   if (!digitFile) return;
490
491   // read the number of events
492   TFile f(digitFile);
493   if (f.IsZombie()) return;
494   TTree* pTree=NULL;
495   f.GetObject("rawhltout", pTree);
496   if (!pTree) {
497     AliWarning(Form("can not find tree rawhltout in file %s", digitFile));
498     return ;
499   }
500   int nofEvents=pTree->GetEntries();
501   f.Close();
502   //delete pTree; OF COURSE NOT! its an object in the file
503   pTree=NULL;
504
505   for (int event=0; event<nofEvents; event++) {
506     AliHLTOUTDigitReader* pHLTOUT=new AliHLTOUTDigitReader(event, fpEsdManager, digitFile);
507     if (pHLTOUT) {
508       AliInfo(Form("event %d", event));
509       ProcessHLTOUT(pHLTOUT, pEsd, true);
510       delete pHLTOUT;
511     } else {
512       AliError("error creating HLTOUT handler");
513     }
514   }
515 }
516
517 void AliHLTReconstructor::ProcessHLTOUT(AliRawReader* pRawReader, AliESDEvent* pEsd) const
518 {
519   // debugging/helper function to examine simulated or real HLTOUT data
520   if (!pRawReader) return;
521
522   pRawReader->RewindEvents();
523   for (int event=0; pRawReader->NextEvent(); event++) {
524     AliHLTOUTRawReader* pHLTOUT=new AliHLTOUTRawReader(pRawReader, event, fpEsdManager);
525     if (pHLTOUT) {
526       AliInfo(Form("event %d", event));
527       // the two event fields contain: period - orbit - bunch crossing counter
528       //        id[0]               id[1]
529       // |32                0|32                0|
530       //
531       // |      28 bit    |       24 bit     | 12|
532       //        period          orbit         bcc
533       AliHLTUInt64_t eventId=0;
534       const UInt_t* rawreaderEventId=pRawReader->GetEventId();
535       if (rawreaderEventId) {
536         eventId=rawreaderEventId[0];
537         eventId=eventId<<32;
538         eventId|=rawreaderEventId[1];
539       }
540       AliInfo(Form("Event Id from rawreader:\t 0x%016llx", eventId));
541       ProcessHLTOUT(pHLTOUT, pEsd, true);
542       delete pHLTOUT;
543     } else {
544       AliError("error creating HLTOUT handler");
545     }
546   }
547 }
548
549 void AliHLTReconstructor::PrintHLTOUTContent(AliHLTOUT* pHLTOUT) const
550 {
551   // print the block specifications of the HLTOUT data blocks
552   if (!pHLTOUT) return;
553   int iResult=0;
554
555   AliInfo(Form("Event Id from hltout:\t 0x%016llx", pHLTOUT->EventId()));
556   for (iResult=pHLTOUT->SelectFirstDataBlock();
557        iResult>=0;
558        iResult=pHLTOUT->SelectNextDataBlock()) {
559     AliHLTComponentDataType dt=kAliHLTVoidDataType;
560     AliHLTUInt32_t spec=kAliHLTVoidDataSpec;
561     pHLTOUT->GetDataBlockDescription(dt, spec);
562     const AliHLTUInt8_t* pBuffer=NULL;
563     AliHLTUInt32_t size=0;
564     if (pHLTOUT->GetDataBuffer(pBuffer, size)>=0) {
565       pHLTOUT->ReleaseDataBuffer(pBuffer);
566       pBuffer=NULL; // just a dummy
567     }
568     AliInfo(Form("   %s  0x%x: size %d", AliHLTComponent::DataType2Text(dt).c_str(), spec, size));
569   }
570 }
571
572 int AliHLTReconstructor::BuildCTPTriggerClassString(TString& triggerclasses) const
573 {
574   // build the CTP trigger class string from the OCDB entry of the CTP trigger
575   int iResult=0;
576   
577   triggerclasses.Clear();
578   AliCentralTrigger* pCTP = new AliCentralTrigger();
579   AliTriggerConfiguration *config=NULL;
580   TString configstr("");
581   if (pCTP->LoadConfiguration(configstr) && 
582       (config = pCTP->GetConfiguration())!=NULL) {
583     const TObjArray& classesArray = config->GetClasses();
584     int nclasses = classesArray.GetEntriesFast();
585     for( int iclass=0; iclass < nclasses; iclass++ ) {
586       AliTriggerClass* trclass = NULL;
587       if (classesArray.At(iclass) && (trclass=dynamic_cast<AliTriggerClass*>(classesArray.At(iclass)))!=NULL) {
588         TString entry;
589         int trindex = TMath::Nint(TMath::Log2(trclass->GetMask()));
590         entry.Form("%02d:%s:", trindex, trclass->GetName());
591         AliTriggerCluster* cluster=NULL;
592         TObject* clusterobj=config->GetClusters().FindObject(trclass->GetCluster());
593         if (clusterobj && (cluster=dynamic_cast<AliTriggerCluster*>(clusterobj))!=NULL) {
594           TString detectors=cluster->GetDetectorsInCluster();
595           TObjArray* pTokens=detectors.Tokenize(" ");
596           if (pTokens) {
597             for (int dix=0; dix<pTokens->GetEntriesFast(); dix++) {
598               int id=AliDAQ::DetectorID(((TObjString*)pTokens->At(dix))->GetString());
599               if (id>=0) {
600                 TString detstr; detstr.Form("%s%02d", dix>0?"-":"", id);
601                 entry+=detstr;
602               } else {
603                 AliError(Form("invalid detector name extracted from trigger cluster: %s (%s)", ((TObjString*)pTokens->At(dix))->GetString().Data(), detectors.Data()));
604                 iResult=-EPROTO;
605                 break;
606               }
607             }
608             delete pTokens;
609           }
610         } else {
611           AliError(Form("can not find trigger cluster %s in config", trclass->GetCluster()?trclass->GetCluster()->GetName():"NULL"));
612           iResult=-EPROTO;
613           break;
614         }
615         if (!triggerclasses.IsNull()) triggerclasses+=",";
616         triggerclasses+=entry;
617       }
618     }
619   }
620
621   return iResult;
622 }