]> git.uio.no Git - u/mrichter/AliRoot.git/blob - HLT/trigger/AliHLTGlobalTriggerComponent.cxx
make generation of monitoring trigger an optional feature
[u/mrichter/AliRoot.git] / HLT / trigger / AliHLTGlobalTriggerComponent.cxx
1 // $Id$
2 /**************************************************************************
3  * This file is property of and copyright by the ALICE HLT Project        *
4  * ALICE Experiment at CERN, All rights reserved.                         *
5  *                                                                        *
6  * Primary Authors: Artur Szostak <artursz@iafrica.com>                   *
7  *                  for The ALICE HLT Project.                            *
8  *                                                                        *
9  * Permission to use, copy, modify and distribute this software and its   *
10  * documentation strictly for non-commercial purposes is hereby granted   *
11  * without fee, provided that the above copyright notice appears in all   *
12  * copies and that both the copyright notice and this permission notice   *
13  * appear in the supporting documentation. The authors make no claims     *
14  * about the suitability of this software for any purpose. It is          *
15  * provided "as is" without express or implied warranty.                  *
16  **************************************************************************/
17
18 /// @file   AliHLTGlobalTriggerComponent.cxx
19 /// @author Artur Szostak <artursz@iafrica.com>
20 /// @date   26 Nov 2008
21 /// @brief  Implementation of the AliHLTGlobalTriggerComponent component class.
22 ///
23 /// The AliHLTGlobalTriggerComponentComponent class applies the global HLT trigger to all
24 /// trigger information produced by components deriving from AliHLTTrigger.
25
26 #include "AliHLTGlobalTriggerComponent.h"
27 #include "AliHLTGlobalTriggerDecision.h"
28 #include "AliHLTGlobalTrigger.h"
29 #include "AliHLTGlobalTriggerConfig.h"
30 #include "AliHLTTriggerMenu.h"
31 #include "AliHLTCTPData.h"
32 #include "AliCDBManager.h"
33 #include "AliCDBStorage.h"
34 #include "AliCDBEntry.h"
35 #include "TUUID.h"
36 #include "TMD5.h"
37 #include "TRandom3.h"
38 #include "TROOT.h"
39 #include "TSystem.h"
40 #include "TRegexp.h"
41 #include "TClonesArray.h"
42 #include "TObjString.h"
43 #include "TString.h"
44 #include "TInterpreter.h"
45 #include "TDatime.h"
46 #include "TClass.h"
47 #include "TNamed.h"
48 #include <fstream>
49 #include <cerrno>
50 #include <cassert>
51 #include <vector>
52 #include <algorithm>
53
54 ClassImp(AliHLTGlobalTriggerComponent)
55
56 const char* AliHLTGlobalTriggerComponent::fgkTriggerMenuCDBPath = "HLT/ConfigHLT/HLTGlobalTrigger";
57
58
59 namespace
60 {
61   /**
62    * This method is used as a comparison functor with the STL sort routines.
63    */
64   bool AliHLTDescendingNumbers(UInt_t a, UInt_t b)
65   {
66     return a > b;
67   }
68 } // end of namespace
69
70
71 AliHLTGlobalTriggerComponent::AliHLTGlobalTriggerComponent() :
72         AliHLTTrigger(),
73         fTrigger(NULL),
74         fDebugMode(false),
75         fRuntimeCompile(true),
76         fDeleteCodeFile(false),
77         fCodeFileName(),
78         fClassName(),
79         fCTPDecisions(NULL),
80         fBufferSizeConst(2*(sizeof(AliHLTGlobalTriggerDecision) + sizeof(AliHLTReadoutList))),
81         fBufferSizeMultiplier(1.),
82         fIncludePaths(TObjString::Class()),
83         fIncludeFiles(TObjString::Class()),
84         fLibStateAtLoad(),
85         fBits(0),
86         fDataEventsOnly(true)
87         , fMonitorPeriod(-1)
88 {
89   // Default constructor.
90   
91   ClearInfoForNewEvent(false);
92 }
93
94
95 AliHLTGlobalTriggerComponent::~AliHLTGlobalTriggerComponent()
96 {
97   // Default destructor.
98   
99   if (fTrigger != NULL) delete fTrigger;
100
101   if (fCTPDecisions) {
102     fCTPDecisions->Delete();
103     delete fCTPDecisions;
104   }
105 }
106
107
108 void AliHLTGlobalTriggerComponent::GetOutputDataTypes(AliHLTComponentDataTypeList& list) const
109 {
110   // Returns the kAliHLTDataTypeGlobalTrigger type as output.
111   list.push_back(kAliHLTDataTypeGlobalTrigger);
112 }
113
114
115 void AliHLTGlobalTriggerComponent::GetOutputDataSize(unsigned long& constBase, double& inputMultiplier)
116 {
117   // Returns the output data size estimate.
118
119   constBase = fBufferSizeConst;
120   inputMultiplier = fBufferSizeMultiplier;
121 }
122
123
124 Int_t AliHLTGlobalTriggerComponent::DoInit(int argc, const char** argv)
125 {
126   // Initialises the global trigger component.
127   
128   fDebugMode = false;
129   fClassName = "";
130   fCodeFileName = "";
131   fDeleteCodeFile = false;
132   const char* configFileName = NULL;
133   const char* codeFileName = NULL;
134   fIncludePaths.Clear();
135   fIncludeFiles.Clear();
136   SetBit(kIncludeInput);
137   fDataEventsOnly = true;
138   
139   for (int i = 0; i < argc; i++)
140   {
141     if (strcmp(argv[i], "-config") == 0)
142     {
143       if (configFileName != NULL)
144       {
145         HLTWarning("Trigger configuration macro was already specified."
146                    " Will replace previous value given by -config."
147         );
148       }
149       if (argc <= i+1)
150       {
151         HLTError("The trigger configuration macro filename was not specified." );
152         return -EINVAL;
153       }
154       configFileName = argv[i+1];
155       i++;
156       continue;
157     }
158     
159     if (strcmp(argv[i], "-includepath") == 0)
160     {
161       if (argc <= i+1)
162       {
163         HLTError("The include path was not specified." );
164         return -EINVAL;
165       }
166       try
167       {
168         new (fIncludePaths[fIncludePaths.GetEntriesFast()]) TObjString(argv[i+1]);
169       }
170       catch (const std::bad_alloc&)
171       {
172         HLTError("Could not allocate more memory for the fIncludePaths array.");
173         return -ENOMEM;
174       }
175       i++;
176       continue;
177     }
178     
179     if (strcmp(argv[i], "-include") == 0)
180     {
181       if (argc <= i+1)
182       {
183         HLTError("The include file name was not specified." );
184         return -EINVAL;
185       }
186       try
187       {
188         new (fIncludeFiles[fIncludeFiles.GetEntriesFast()]) TObjString(argv[i+1]);
189       }
190       catch (const std::bad_alloc&)
191       {
192         HLTError("Could not allocate more memory for the fIncludeFiles array.");
193         return -ENOMEM;
194       }
195       i++;
196       continue;
197     }
198     
199     if (strcmp(argv[i], "-debug") == 0)
200     {
201       if (fDebugMode == true)
202       {
203         HLTWarning("The debug flag was already specified. Ignoring this instance.");
204       }
205       fDebugMode = true;
206       continue;
207     }
208     
209     if (strcmp(argv[i], "-cint") == 0)
210     {
211       fRuntimeCompile = false;
212       continue;
213     }
214     
215     if (strcmp(argv[i], "-usecode") == 0)
216     {
217       if (codeFileName != NULL)
218       {
219         HLTWarning("Custom trigger code file was already specified."
220                    " Will replace previous value given by -usecode."
221         );
222       }
223       if (argc <= i+1)
224       {
225         HLTError("The custom trigger code filename was not specified." );
226         return -EINVAL;
227       }
228       codeFileName = argv[i+1];
229       if (argc <= i+2)
230       {
231         HLTError("The custom trigger class name was not specified." );
232         return -EINVAL;
233       }
234       fClassName = argv[i+2];
235       i += 2;
236       continue;
237     }
238
239     if (strcmp(argv[i], "-skipctp") == 0)
240     {
241       HLTInfo("Skipping CTP counters in trigger decision");
242       SetBit(kSkipCTP);
243       continue;
244     }
245
246     if (strcmp(argv[i], "-forward-input") == 0)
247     {
248       HLTInfo("Forwarding input objects and trigger decisions");
249       SetBit(kForwardInput);
250       SetBit(kIncludeShort);
251       SetBit(kIncludeInput, false);
252       continue;
253     }
254
255     if (strstr(argv[i], "-include-input") == argv[i])
256     {
257       SetBit(kForwardInput,false);
258       TString param=argv[i];
259       param.ReplaceAll("-include-input", "");
260       if (param.CompareTo("=none")==0) 
261       {
262         HLTInfo("skipping objects and trigger decisions");
263         SetBit(kIncludeShort, false);
264         SetBit(kIncludeInput, false);
265       }
266       else if (param.CompareTo("=short")==0) 
267       {
268         HLTInfo("including short info on objects and trigger decisions");
269         SetBit(kIncludeShort);
270         SetBit(kIncludeInput, false);
271       }
272       else if (param.CompareTo("=both")==0) 
273       {
274         HLTInfo("including input objects, trigger decisions and short info");
275         SetBit(kIncludeShort);
276         SetBit(kIncludeInput);
277       }
278       else if (param.CompareTo("=objects")==0 || param.IsNull())
279       {
280         HLTInfo("including input objects and trigger decisions");
281         SetBit(kIncludeShort, false);
282         SetBit(kIncludeInput);
283       }
284       else
285       {
286         HLTError("unknown parameter '%s' for argument '-include-input'", param.Data());
287       }
288       continue;
289     }
290
291     if (strcmp(argv[i], "-process-all-events") == 0)
292     {
293       fDataEventsOnly = false;
294       continue;
295     }
296
297     if (strstr(argv[i], "-monitoring") == argv[i])
298     {
299       TString param=argv[i];
300       param.ReplaceAll("-monitoring", "");
301       if (param.IsNull()) 
302       {
303         // -monitoring
304         // enable monitoring trigger for all events
305         fMonitorPeriod=0;
306       } else {
307         // -monitoring=n
308         // enable monitoring trigger once every n seconds
309         param.ReplaceAll("=", "");
310         if (param.IsDigit()) {
311           fMonitorPeriod=param.Atoi();
312         } else {
313           HLTError("expecting number as parameter for argument '-monitoring=', got %s, skipping monitoring trigger", param.Data());
314         }
315       }
316       continue;
317     }
318     
319     HLTError("Unknown option '%s'.", argv[i]);
320     return -EINVAL;
321   } // for loop
322   
323   const AliHLTTriggerMenu* menu = NULL;
324   if (configFileName != NULL)
325   {
326     TString cmd = ".x ";
327     cmd += configFileName;
328     gROOT->ProcessLine(cmd);
329     menu = AliHLTGlobalTriggerConfig::Menu();
330   }
331   
332   // Try load the trigger menu from the CDB if it is not loaded yet with the
333   // -config option
334   int result = -ENOENT;
335   if (menu == NULL)
336   {
337     result = LoadTriggerMenu(fgkTriggerMenuCDBPath, menu);
338   }
339   if (menu == NULL)
340   {
341     HLTError("No trigger menu configuration found or specified.");
342     return result;
343   }
344   
345   if (codeFileName == NULL)
346   {
347     result = GenerateTrigger(menu, fClassName, fCodeFileName, fIncludePaths, fIncludeFiles);
348     if (result == 0) fDeleteCodeFile = true;
349   }
350   else
351   {
352     result = LoadTriggerClass(codeFileName, fIncludePaths);
353     if (result == 0) fCodeFileName = codeFileName;
354   }
355   if (result != 0) return result;
356   
357   try
358   {
359     fTrigger = AliHLTGlobalTrigger::CreateNew(fClassName.Data());
360   }
361   catch (const std::bad_alloc&)
362   {
363     HLTError("Could not allocate memory for the AliHLTGlobalTrigger instance.");
364     return -ENOMEM;
365   }
366   if (fTrigger == NULL)
367   {
368     HLTError("Could not create a new instance of '%s'.", fClassName.Data());
369     return -EIO;
370   }
371   
372   fTrigger->FillFromMenu(*menu);
373   if (fTrigger->CallFailed()) return -EPROTO;
374
375   // setup the CTP accounting in AliHLTComponent
376   SetupCTPData();
377
378   // Set the default values from the trigger menu.
379   SetDescription(menu->DefaultDescription());
380   SetTriggerDomain(menu->DefaultTriggerDomain());
381   
382   return 0;
383 }
384
385
386 Int_t AliHLTGlobalTriggerComponent::DoDeinit()
387 {
388   // Cleans up the global trigger component.
389   
390   if (fTrigger != NULL)
391   {
392     delete fTrigger;
393     fTrigger = NULL;
394   }
395   
396   if (fCTPDecisions) {
397     fCTPDecisions->Delete();
398     delete fCTPDecisions;
399   }
400   fCTPDecisions=NULL;
401   
402   Int_t result = UnloadTriggerClass(fCodeFileName);
403   if (result != 0) return result;
404   
405   if (fDeleteCodeFile and !fCodeFileName.IsNull() && gSystem->AccessPathName(fCodeFileName)==0 && !fDebugMode) {
406     fCodeFileName.ReplaceAll(".cxx", "*");
407     TString command="rm "; command+=fCodeFileName;
408     gSystem->Exec(command);
409   }
410   fCodeFileName="";
411   fDeleteCodeFile=false;
412
413   return 0;
414 }
415
416
417 AliHLTComponent* AliHLTGlobalTriggerComponent::Spawn()
418 {
419   // Creates a new object instance.
420   AliHLTComponent* comp = NULL;
421   try
422   {
423     comp = new AliHLTGlobalTriggerComponent;
424   }
425   catch (const std::bad_alloc&)
426   {
427     HLTError("Could not allocate memory for a new instance of AliHLTGlobalTriggerComponent.");
428     return NULL;
429   }
430   return comp;
431 }
432
433
434 int AliHLTGlobalTriggerComponent::DoTrigger()
435 {
436   // This method will apply the global trigger decision.
437
438   if (fTrigger == NULL)
439   {
440     HLTFatal("Global trigger implementation object is NULL!");
441     return -EIO;
442   }
443
444   AliHLTUInt32_t eventType=0;
445   if (!IsDataEvent(&eventType)) {
446     if (eventType==gkAliEventTypeEndOfRun) PrintStatistics(fTrigger, kHLTLogImportant, 0);
447     if (fDataEventsOnly)
448     {
449       IgnoreEvent();  // dont generate any trigger decision.
450       return 0;
451     }
452   }
453
454   // Copy the trigger counters in case we need to set them back to their original
455   // value because the PushBack method fails with ENOSPC.
456   TArrayL64 originalCounters = fTrigger->GetCounters();
457   if (fTrigger->CallFailed()) return -EPROTO;
458   
459   fTrigger->NewEvent();
460   if (fTrigger->CallFailed()) return -EPROTO;
461   
462   // Fill in the input data.
463   const TObject* obj = GetFirstInputObject();
464   while (obj != NULL)
465   {
466     fTrigger->Add(obj, GetDataType(), GetSpecification());
467     if (fTrigger->CallFailed()) return -EPROTO;
468     obj = GetNextInputObject();
469   }
470
471   // add trigger decisions for every CTP class
472   const AliHLTCTPData* pCTPData=CTPData();
473   if (pCTPData) {
474     AddCTPDecisions(fTrigger, pCTPData, GetTriggerData());
475   }
476
477   // Calculate the global trigger result and trigger domain, then create and push
478   // back the new global trigger decision object.
479   TString description;
480   AliHLTTriggerDomain triggerDomain;
481   bool triggerResult = fTrigger->CalculateTriggerDecision(triggerDomain, description);
482   if (fTrigger->CallFailed()) return -EPROTO;
483   AliHLTGlobalTriggerDecision decision(
484       triggerResult,
485       // The following will cause the decision to be generated with default values
486       // (set in fTriggerDomain and fDescription) if the trigger result is false.
487       (triggerResult == true) ? triggerDomain : GetTriggerDomain(),
488       (triggerResult == true) ? description.Data() : GetDescription()
489     );
490
491   decision.SetCounters(fTrigger->GetCounters(), GetEventCount()+1);
492   if (fTrigger->CallFailed()) return -EPROTO;
493   
494   TClonesArray shortInfo(TNamed::Class(), GetNumberOfInputBlocks());
495   
496   // Add the input objects used to make the global decision.
497   obj = GetFirstInputObject();
498   while (obj != NULL)
499   {
500     const AliHLTTriggerDecision* intrig = dynamic_cast<const AliHLTTriggerDecision*>(obj);
501     
502     if (TestBit(kForwardInput)) Forward(obj);
503     
504     if (TestBit(kIncludeInput))
505     {
506       if (intrig != NULL)
507       {
508          decision.AddTriggerInput(*intrig);
509       }
510       else
511       {
512         // The const_cast should be safe in this case because the list of inputObjects
513         // will be added to the global decision with AddInputObjectRef, which only
514         // modifies the kCanDelete bit and nothing else.
515         // This is necessary since GetFirstInputObject only returns const objects.
516         decision.AddInputObjectRef( const_cast<TObject*>(obj) );
517       }
518     }
519     
520     if (TestBit(kIncludeShort))
521     {
522       int entries = shortInfo.GetEntriesFast();
523       try
524       {
525         new (shortInfo[entries]) TNamed(obj->GetName(), obj->GetTitle());
526       }
527       catch (const std::bad_alloc&)
528       {
529         HLTError("Could not allocate more memory for the short list of input objects.");
530         return -ENOMEM;
531       }
532       if (intrig != NULL)
533       {
534         shortInfo[entries]->SetBit(BIT(16)); // indicate that this is a trigger decision
535         shortInfo[entries]->SetBit(BIT(15), intrig->Result());
536       }
537     }
538
539     obj = GetNextInputObject();
540   }
541   if (TestBit(kIncludeShort)) decision.AddInputObjectRef(&shortInfo);
542   
543   // The const_cast should be safe in this case because AddInputObjectRef just
544   // modifies the kCanDelete bit and nothing else.
545   if (!TestBit(kSkipCTP) && CTPData()) decision.AddInputObjectRef(const_cast<AliHLTCTPData*>(CTPData()));
546   
547   static UInt_t lastTime=0;
548   TDatime time;
549   if (time.Get()-lastTime>60) {
550     lastTime=time.Get();
551     PrintStatistics(fTrigger, kHLTLogImportant);
552   }
553
554   // add readout filter to event done data
555   CreateEventDoneReadoutFilter(decision.TriggerDomain(), 3);
556
557   // add monitoring filter to event done data if enabled by setting
558   // a monitoring period >=0: -1 means off, 0 means for every event
559   // configured by argument '-monitoring[=n]'
560   if (fMonitorPeriod>=0) {
561     static UInt_t lastMonitorEvent=0;
562
563     AliHLTTriggerDomain monitoringFilter(decision.TriggerDomain());
564     if (decision.Result() &&
565         time.Get()-lastMonitorEvent>fMonitorPeriod) {
566       lastMonitorEvent=time.Get();
567       // add monitoring event command for triggered events
568       CreateEventDoneReadoutFilter(decision.TriggerDomain(), 5);
569     } else {
570       // empty filter list if events are not triggered
571       // or within the monitoring interval
572       monitoringFilter.Clear();
573     }
574     // add monitoring filter list
575     CreateEventDoneReadoutFilter(monitoringFilter, 4);
576   }
577
578   if (TriggerEvent(&decision) == -ENOSPC)
579   {
580     // Increase the estimated buffer space required if the PushBack methods in TriggerEvent
581     // returned the "no buffer space" error code. Also remember to set the trigger counters
582     // back to what they were, otherwise triggers will be double counted when we try to reprocess
583     // this event with larger buffers.
584     fBufferSizeConst += 1024*1024;
585     fBufferSizeMultiplier *= 2.;
586     fTrigger->SetCounters(originalCounters);
587     if (fTrigger->CallFailed()) return -EPROTO;
588     return -ENOSPC;
589   }
590   return 0;
591 }
592
593
594 int AliHLTGlobalTriggerComponent::Reconfigure(const char* cdbEntry, const char* chainId)
595 {
596   // Reconfigure the component by loading the trigger menu and recreating the
597   // trigger logic class.
598   
599   const char* path = fgkTriggerMenuCDBPath;
600   const char* id = "(unknown)";
601   if (cdbEntry != NULL) path = cdbEntry;
602   if (chainId != NULL and chainId[0] != '\0') id = chainId;
603   HLTInfo("Reconfiguring from '%s' for chain component '%s'.", path, id);
604   
605   const AliHLTTriggerMenu* menu = NULL;
606   int result = LoadTriggerMenu(path, menu);
607   if (result != 0) return result;
608   
609   TString className;
610   TString codeFileName;
611   result = GenerateTrigger(menu, className, codeFileName, fIncludePaths, fIncludeFiles);
612   if (result != 0) return result;
613   
614   AliHLTGlobalTrigger* trigger = NULL;
615   try
616   {
617     trigger = AliHLTGlobalTrigger::CreateNew(className.Data());
618   }
619   catch (const std::bad_alloc&)
620   {
621     HLTError("Could not allocate memory for the AliHLTGlobalTrigger instance.");
622     return -ENOMEM;
623   }
624   if (trigger == NULL)
625   {
626     HLTError("Could not create a new instance of '%s'.", className.Data());
627     // Make sure to cleanup after the new code file.
628     UnloadTriggerClass(codeFileName);
629     if (not codeFileName.IsNull() and gSystem->AccessPathName(codeFileName)==0 and not fDebugMode)
630     {
631       codeFileName.ReplaceAll(".cxx", "*");
632       TString command="rm "; command+=codeFileName;
633       gSystem->Exec(command);
634     }
635     return -EIO;
636   }
637   
638   if (fTrigger != NULL)
639   {
640     delete fTrigger;
641     fTrigger = NULL;
642   }
643   
644   fTrigger = trigger;
645   fTrigger->FillFromMenu(*menu);
646   if (fTrigger->CallFailed()) return -EPROTO;
647
648   // Set the default values from the trigger menu.
649   SetDescription(menu->DefaultDescription());
650   SetTriggerDomain(menu->DefaultTriggerDomain());
651   
652   // Cleanup the old class code.
653   UnloadTriggerClass(fCodeFileName);
654   if (fDeleteCodeFile and not fCodeFileName.IsNull() and gSystem->AccessPathName(fCodeFileName)==0 and not fDebugMode)
655   {
656     fCodeFileName.ReplaceAll(".cxx", "*");
657     TString command="rm "; command+=fCodeFileName;
658     gSystem->Exec(command);
659   }
660   fCodeFileName = codeFileName;
661   fDeleteCodeFile = true;  // Since we generated a new class.
662   
663   return 0;
664 }
665
666
667 int AliHLTGlobalTriggerComponent::LoadTriggerMenu(const char* cdbPath, const AliHLTTriggerMenu*& menu)
668 {
669   // Loads the trigger menu object from the CDB path.
670   
671   HLTDebug("Trying to load trigger menu from '%s'.", cdbPath);
672   if (AliCDBManager::Instance() == NULL)
673   {
674     HLTError("CDB manager object not found.");
675     return -EIO;
676   }
677   AliCDBStorage* store = AliCDBManager::Instance()->GetDefaultStorage();
678   if (store == NULL)
679   {
680     HLTError("Could not get the the default storage for the CDB.");
681     return -EIO;
682   }
683   Int_t version = store->GetLatestVersion(cdbPath, GetRunNo());
684   Int_t subVersion = store->GetLatestSubVersion(cdbPath, GetRunNo(), version);
685   AliCDBEntry* entry = AliCDBManager::Instance()->Get(cdbPath, GetRunNo(), version, subVersion);
686   if (entry == NULL)
687   {
688     HLTError("Could not get the CDB entry for \"%s\".", cdbPath);
689     return -EIO;
690   }
691   TObject* obj = entry->GetObject();
692   if (obj == NULL)
693   {
694     HLTError("Configuration object for \"%s\" is missing.", cdbPath);
695     return -ENOENT;
696   }
697   if (obj->IsA() != AliHLTTriggerMenu::Class())
698   {
699     HLTError("Wrong type for configuration object in \"%s\". Found a %s but we expect a AliHLTTriggerMenu.",
700              cdbPath, obj->ClassName()
701     );
702     return -EPROTO;
703   }
704   menu = static_cast<AliHLTTriggerMenu*>(obj);
705   return 0;
706 }
707
708
709 void AliHLTGlobalTriggerComponent::GenerateFileName(TString& name, TString& filename) const
710 {
711   // Creates a unique file name for the generated code.
712   
713   // Start by creating a new UUID. We cannot use the one automatically generated
714   // by ROOT because the algorithm used will not guarantee unique IDs when generating
715   // these UUIDs at a high rate in parallel.
716   TUUID uuid;
717   // We then use the generated UUID to form part of the random number seeds which
718   // will be used to generate a proper random UUID. For good measure we use a MD5
719   // hash also. Note that we want to use the TUUID class because it will combine the
720   // host address information into the UUID. Using gSystem->GetHostByName() apparently
721   // can cause problems on Windows machines with a firewall, because it always tries
722   // to contact a DNS. The TUUID class handles this case appropriately.
723   union
724   {
725     UChar_t buf[16];
726     UShort_t word[8];
727     UInt_t dword[4];
728   };
729   uuid.GetUUID(buf);
730   TMD5 md5;
731   md5.Update(buf, sizeof(buf));
732   md5.Final(buf);
733   dword[0] += gSystem->GetUid();
734   dword[1] += gSystem->GetGid();
735   dword[2] += gSystem->GetPid();
736   for (int i = 0; i < 4; ++i)
737   {
738     gRandom->SetSeed(dword[i]);
739     dword[i] = gRandom->Integer(0xFFFFFFFF);
740   }
741   md5.Update(buf, sizeof(buf));
742   md5.Final(buf);
743   // To keep to the standard we need to set the version and reserved bits.
744   word[3] = (word[3] & 0x0FFF) | 0x4000;
745   buf[8] = (buf[8] & 0x3F) | 0x80;
746
747   // Create the name of the new class and file.
748   char uuidstr[64];
749   sprintf(uuidstr, "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x",
750     dword[0], word[2], word[3], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]
751   );
752   name = "AliHLTGlobalTriggerImpl_";
753   name += uuidstr;
754   filename = name + ".cxx";
755
756   // For good measure, check that the file names are not used. If they are then regenerate.
757   fstream file(filename.Data(), ios_base::in);
758   if (file.good())
759   {
760     file.close();
761     GenerateFileName(name, filename);
762   }
763 }
764
765
766 int AliHLTGlobalTriggerComponent::GenerateTrigger(
767     const AliHLTTriggerMenu* menu, TString& name, TString& filename,
768     const TClonesArray& includePaths, const TClonesArray& includeFiles
769   )
770 {
771   // Generates the global trigger class that will implement the specified trigger menu.
772   // See header for more details.
773   
774   GenerateFileName(name, filename);
775   HLTDebug("Generating custom HLT trigger class named %s, in file %s, using trigger menu %p.",
776     name.Data(), filename.Data(), ((void*)menu)
777   );
778   
779   // Open a text file to write the code and generate the new class.
780   fstream code(filename.Data(), ios_base::out | ios_base::trunc);
781   if (not code.good())
782   {
783     HLTError("Could not open file '%s' for writing.", filename.Data());
784     return -EIO;
785   }
786   
787   TClonesArray symbols(AliHLTTriggerMenuSymbol::Class());
788   int result = BuildSymbolList(menu, symbols);
789   if (result != 0) return result;
790   
791   code << "#if !defined(__CINT__) || defined(__MAKECINT__)" << endl;
792   code << "#include <cstring>" << endl;
793   code << "#include \"TClass.h\"" << endl;
794   code << "#include \"TString.h\"" << endl;
795   code << "#include \"TClonesArray.h\"" << endl;
796   code << "#include \"AliHLTLogging.h\"" << endl;
797   code << "#include \"AliHLTGlobalTrigger.h\"" << endl;
798   code << "#include \"AliHLTGlobalTriggerDecision.h\"" << endl;
799   code << "#include \"AliHLTDomainEntry.h\"" << endl;
800   code << "#include \"AliHLTTriggerDomain.h\"" << endl;
801   code << "#include \"AliHLTReadoutList.h\"" << endl;
802   code << "#include \"AliHLTTriggerMenu.h\"" << endl;
803   code << "#include \"AliHLTTriggerMenuItem.h\"" << endl;
804   code << "#include \"AliHLTTriggerMenuSymbol.h\"" << endl;
805   
806   // Add any include files that were specified on the command line.
807   for (Int_t i = 0; i < includeFiles.GetEntriesFast(); i++)
808   {
809     TString file = static_cast<TObjString*>(includeFiles.UncheckedAt(i))->String();
810     code << "#include \"" << file.Data() << "\"" << endl;
811   }
812   
813   if (fDebugMode)
814   {
815     code << "#else" << endl;
816     code << "const char* gFunctionName = \"???\";" << endl;
817     code << "#define HLTDebug(msg) if (CheckFilter(kHLTLogDebug) && CheckGroup(Class_Name())) SendMessage(kHLTLogDebug, Class_Name(), ::gFunctionName, __FILE__, __LINE__, msg)" << endl;
818   }
819   code << "#endif" << endl;
820   
821   code << "class " << name << " :" << endl;
822   // Add appropriate #ifdef sections since we need to prevent inheritance from
823   // AliHLTGlobalTrigger. CINT does not seem to support multiple inheritance nor
824   // multiple levels of inheritance. Neither of the following schemes worked:
825   //
826   // 1)  class AliHLTGlobalTrigger : public AliHLTLogging {};
827   //     class AliHLTGlobalTriggerImpl_xyz : public AliHLTGlobalTrigger {};
828   //
829   // 2)  class AliHLTGlobalTrigger {};
830   //     class AliHLTGlobalTriggerImpl_xyz : public AliHLTGlobalTrigger, public AliHLTLogging {};
831   //
832   // Thus, we are forced to just inherit from AliHLTLogging when running in the CINT
833   // interpreter. But we anyway have to call the global trigger implementation class
834   // through the AliHLTGlobalTriggerWrapper so this is not such a problem.
835   code << "#if !defined(__CINT__) || defined(__MAKECINT__)" << endl;
836   code << "  public AliHLTGlobalTrigger," << endl;
837   code << "#endif" << endl;
838   code << "  public AliHLTLogging" << endl;
839   code << "{" << endl;
840   code << "public:" << endl;
841   
842   // Generate constructor method.
843   code << "  " << name << "() :" << endl;
844   code << "#if !defined(__CINT__) || defined(__MAKECINT__)" << endl;
845   code << "    AliHLTGlobalTrigger()," << endl;
846   code << "#endif" << endl;
847   code << "    AliHLTLogging()";
848   // Write the symbols in the trigger menu in the initialisation list.
849   for (Int_t i = 0; i < symbols.GetEntriesFast(); i++)
850   {
851     code << "," << endl;
852     AliHLTTriggerMenuSymbol* symbol = static_cast<AliHLTTriggerMenuSymbol*>( symbols.UncheckedAt(i) );
853     code << "    " << symbol->Name() << "()," << endl;
854     if (strcmp(symbol->ObjectClass(), "AliHLTTriggerDecision") == 0)
855     {
856       code << "    " << symbol->Name() << "TriggerDomain()," << endl;
857     }
858     code << "    " << symbol->Name() << "DomainEntry(kAliHLTAnyDataType)";
859   }
860   for (UInt_t i = 0; i < menu->NumberOfItems(); i++)
861   {
862     code << "," << endl << "    fMenuItemDescription" << i << "()";
863   }
864   code << endl << "  {" << endl;
865   if (fDebugMode)
866   {
867     code << "#ifdef __CINT__" << endl;
868     code << "    gFunctionName = \"" << name.Data() <<"\";" << endl;
869     code << "#endif" << endl;
870     code << "    SetLocalLoggingLevel(kHLTLogAll);" << endl;
871     code << "    HLTDebug(Form(\"Creating new instance at %p.\", this));" << endl;
872   }
873   code << "  }" << endl;
874   
875   code << "  virtual ~" << name << "() {" << endl;
876   if (fDebugMode)
877   {
878     code << "#ifdef __CINT__" << endl;
879     code << "    gFunctionName = \"~" << name.Data() << "\";" << endl;
880     code << "#endif" << endl;
881     code << "    HLTDebug(Form(\"Deleting instance at %p.\", this));" << endl;
882   }
883   code << "  }" << endl;
884   
885   // Generate the FillFromMenu method.
886   code << "  virtual void FillFromMenu(const AliHLTTriggerMenu& menu) {" << endl;
887   if (fDebugMode)
888   {
889     code << "#ifdef __CINT__" << endl;
890     code << "    gFunctionName = \"FillFromMenu\";" << endl;
891     code << "#endif" << endl;
892     code << "    HLTDebug(Form(\"Filling description entries from trigger menu for global trigger %p.\", this));" << endl;
893   }
894   code << "    fCounter.Set(menu.NumberOfItems());" << endl;
895   for (UInt_t i = 0; i < menu->NumberOfItems(); i++)
896   {
897     code << "    fMenuItemDescription" << i << " = (menu.Item(" << i
898          << ") != NULL) ? menu.Item(" << i << ")->Description() : \"\";" << endl;
899   }
900   if (fDebugMode)
901   {
902     code << "    HLTDebug(Form(\"Finished filling description entries from trigger menu.\"));" << endl;
903     code << "    HLTDebug(Form(\"Filling domain entries from trigger menu symbols for global trigger %p.\", this));" << endl;
904   }
905   code << "    for (Int_t i = 0; i < menu.SymbolArray().GetEntriesFast(); i++) {" << endl;
906   // 30 Oct 2009 - CINT sometimes evaluates the dynamic_cast incorrectly.
907   // Have to use the TClass system for extra protection.
908   code << "      if (menu.SymbolArray().UncheckedAt(i) == NULL) continue;" << endl;
909   code << "      if (menu.SymbolArray().UncheckedAt(i)->IsA() != AliHLTTriggerMenuSymbol::Class()) continue;" << endl;
910   code << "      const AliHLTTriggerMenuSymbol* symbol = dynamic_cast<const"
911            " AliHLTTriggerMenuSymbol*>(menu.SymbolArray().UncheckedAt(i));" << endl;
912   code << "      if (symbol == NULL) continue;" << endl;
913   for (Int_t i = 0; i < symbols.GetEntriesFast(); i++)
914   {
915     AliHLTTriggerMenuSymbol* symbol = static_cast<AliHLTTriggerMenuSymbol*>( symbols.UncheckedAt(i) );
916     code << "      if (strcmp(symbol->RealName(), \"" << symbol->RealName() << "\") == 0) {" << endl;
917     if (fDebugMode)
918     {
919       code << "        HLTDebug(Form(\"Assinging domain entry value corresponding with symbol '%s' to '%s'.\","
920               " symbol->RealName(), symbol->BlockType().AsString().Data()));" << endl;
921     }
922     code << "        " << symbol->Name() << "DomainEntry = symbol->BlockType();" << endl;
923     code << "        continue;" << endl;
924     code << "      }" << endl;
925   }
926   code << "    }" << endl;
927   // The following is an optimisation where symbols without any assignment operators
928   // are treated as constant and only initialised in FillFromMenu rather than reseting
929   // them in the NewEvent method.
930   // Note: we putting this initialisation into the constructor can lead to seg faults
931   // under CINT interpretation. Thus we must put it into the FillFromMenu method instead.
932   for (Int_t i = 0; i < symbols.GetEntriesFast(); i++)
933   {
934     AliHLTTriggerMenuSymbol* symbol = static_cast<AliHLTTriggerMenuSymbol*>( symbols.UncheckedAt(i) );
935     if (TString(symbol->AssignExpression()) != "") continue;
936     if (strcmp(symbol->ObjectClass(), "AliHLTTriggerDecision") == 0) continue;
937     // CINT has problems with the implicit equals operator for complex types, so if
938     // the type has an equals operater we need to write the operator call explicitly.
939     TClass* clas = TClass::GetClass(symbol->Type());
940     if (clas != NULL and clas->GetMethodAny("operator=") != NULL)
941     {
942       code << "    " << symbol->Name() << ".operator = (" << symbol->DefaultValue() << ");" << endl;
943     }
944     else
945     {
946       code << "    " << symbol->Name() << " = " << symbol->DefaultValue() << ";" << endl;
947     }
948   }
949   if (fDebugMode)
950   {
951     code << "    HLTDebug(Form(\"Finished filling domain entries from trigger menu symbols.\"));" << endl;
952   }
953   code << "  }" << endl;
954   
955   // Generate the NewEvent method.
956   code << "  virtual void NewEvent() {" << endl;
957   if (fDebugMode)
958   {
959     code << "#ifdef __CINT__" << endl;
960     code << "    gFunctionName = \"NewEvent\";" << endl;
961     code << "#endif" << endl;
962     code << "    HLTDebug(Form(\"New event for global trigger object %p, initialising variables to default values.\", this));" << endl;
963   }
964   // Write code to initialise the symbols in the trigger menu to their default values.
965   for (Int_t i = 0; i < symbols.GetEntriesFast(); i++)
966   {
967     AliHLTTriggerMenuSymbol* symbol = static_cast<AliHLTTriggerMenuSymbol*>( symbols.UncheckedAt(i) );
968     // The following is an optimisation. If the symbol does not have an assignment expression
969     // then it is effectively a constant symbol and can be initialised earlier and only once.
970     // In this case we initialise it in the FillFromMenu method instead.
971     if (TString(symbol->AssignExpression()) == "") continue;
972     // CINT has problems with the implicit equals operator for complex types, so if
973     // the type has an equals operater we need to write the operator call explicitly.
974     TClass* clas = TClass::GetClass(symbol->Type());
975     if (clas != NULL and clas->GetMethodAny("operator=") != NULL)
976     {
977       code << "    " << symbol->Name() << ".operator = (" << symbol->DefaultValue() << ");" << endl;
978     }
979     else
980     {
981       code << "    " << symbol->Name() << " = " << symbol->DefaultValue() << ";" << endl;
982     }
983     if (strcmp(symbol->ObjectClass(), "AliHLTTriggerDecision") == 0)
984     {
985       code << "    " << symbol->Name() << "TriggerDomain.Clear();" << endl;
986     }
987   }
988   if (fDebugMode)
989   {
990     code << "    HLTDebug(Form(\"Finished initialising variables.\"));" << endl;
991   }
992   code << "  }" << endl;
993   
994   // Generate the Add method.
995   bool haveAssignments = false;
996   for (Int_t i = 0; i < symbols.GetEntriesFast(); i++)
997   {
998     // First check if we have any symbols with assignment expressions.
999     // This is needed to get rid of the on the fly compilation warning about '_object_' not being used.
1000     AliHLTTriggerMenuSymbol* symbol = static_cast<AliHLTTriggerMenuSymbol*>( symbols.UncheckedAt(i) );
1001     TString expr = symbol->AssignExpression();
1002     if (expr == "") continue; // Skip entries that have no assignment expression.
1003     haveAssignments = true;
1004     break;
1005   }
1006   if (haveAssignments or fDebugMode)
1007   {
1008     code << "  virtual void Add(const TObject* _object_, const AliHLTComponentDataType& _type_, AliHLTUInt32_t _spec_) {" << endl;
1009   }
1010   else
1011   {
1012     code << "  virtual void Add(const TObject* /*_object_*/, const AliHLTComponentDataType& _type_, AliHLTUInt32_t _spec_) {" << endl;
1013   }
1014   if (fDebugMode)
1015   {
1016     code << "#ifdef __CINT__" << endl;
1017     code << "    gFunctionName = \"Add\";" << endl;
1018     code << "#endif" << endl;
1019   }
1020   code << "    AliHLTDomainEntry _type_spec_(_type_, _spec_);" << endl;
1021   if (fDebugMode)
1022   {
1023     code << "    HLTDebug(Form(\"Adding TObject %p, with class name '%s' from data block"
1024             " '%s', to global trigger object %p\", _object_, _object_->ClassName(),"
1025             " _type_spec_.AsString().Data(), this));" << endl;
1026     code << "    _object_->Print();" << endl;
1027     code << "    bool _object_assigned_ = false;" << endl;
1028   }
1029   for (Int_t i = 0; i < symbols.GetEntriesFast(); i++)
1030   {
1031     // Write code to check if the block type, specification and class name is correct.
1032     // Then write code to assign the variable from the input object.
1033     AliHLTTriggerMenuSymbol* symbol = static_cast<AliHLTTriggerMenuSymbol*>( symbols.UncheckedAt(i) );
1034     TString expr = symbol->AssignExpression();
1035     if (expr == "") continue; // Skip entries that have no assignment expression.
1036     bool isTrigDecision = strcmp(symbol->ObjectClass(), "AliHLTTriggerDecision") == 0;
1037     if (fDebugMode)
1038     {
1039       if (isTrigDecision)
1040       {
1041         code << "    HLTDebug(Form(\"Trying to match input object to class '"
1042              << symbol->ObjectClass() << "', trigger name '" << symbol->RealName()
1043              << "' and block type '%s'\", " << symbol->Name()
1044              << "DomainEntry.AsString().Data()));" << endl;
1045       }
1046       else
1047       {
1048         code << "    HLTDebug(Form(\"Trying to match input object to class '"
1049              << symbol->ObjectClass() << "' and block type '%s'\", "
1050              << symbol->Name() << "DomainEntry.AsString().Data()));" << endl;
1051       }
1052     }
1053     // 30 Oct 2009 - CINT sometimes evaluates the dynamic_cast incorrectly.
1054     // Have to use the TClass system for extra protection.
1055     code << "    const " << symbol->ObjectClass() << "* " << symbol->Name() << "_object_ = NULL;" << endl;
1056     code << "    if (_object_->InheritsFrom(" << symbol->ObjectClass() << "::Class())) " << symbol->Name()
1057          << "_object_ = dynamic_cast<const " << symbol->ObjectClass()
1058          << "*>(_object_);" << endl;
1059     code << "    if (" << symbol->Name() << "_object_ != NULL && ";
1060     if (isTrigDecision)
1061     {
1062       code << "strcmp(" << symbol->Name() << "_object_->Name(), \""
1063            << symbol->RealName() << "\") == 0 && ";
1064     }
1065     code << symbol->Name() << "DomainEntry == _type_spec_) {" << endl;
1066     TString fullname = symbol->Name();
1067     fullname += "_object_";
1068     expr.ReplaceAll("this", fullname);
1069     code << "      this->" << symbol->Name() << " = " << expr.Data() << ";" << endl;
1070     if (isTrigDecision)
1071     {
1072       code << "      this->" << symbol->Name() << "TriggerDomain = "
1073            << fullname.Data() << "->TriggerDomain();" << endl;
1074     }
1075     if (fDebugMode)
1076     {
1077       code << "      HLTDebug(Form(\"Added TObject %p with class name '%s' to variable "
1078            << symbol->Name() << "\", _object_, _object_->ClassName()));" << endl;
1079       code << "      _object_assigned_ = true;" << endl;
1080     }
1081     code << "    }" << endl;
1082   }
1083   if (fDebugMode)
1084   {
1085     code << "    if (! _object_assigned_) {" << endl;
1086     code << "      HLTDebug(Form(\"Did not assign TObject %p"
1087             " with class name '%s' to any variable.\", _object_, _object_->ClassName()));"
1088          << endl;
1089     code << "    }" << endl;
1090   }
1091   code << "  }" << endl;
1092   
1093   // Generate the CalculateTriggerDecision method.
1094   // This requires code to be generated that checks which items in the trigger menu
1095   // have their conditions asserted and then the trigger domain is generated from
1096   // those fired items.
1097   // The processing will start from the highest priority trigger group and stop
1098   // after at least one trigger from the current priority group being processed
1099   // is positive. For each priority group all the trigger menu items are checked.
1100   // Their combined trigger condition expression must be true for the trigger priority
1101   // group to be triggered positive. The full condition expression is formed by
1102   // concatenating the individual condition expressions. If no trailing operators are
1103   // used in the individual expressions then the default condition operator is placed
1104   // between two concatenated condition expressions.
1105   // If a trigger priority group has at least one trigger fired then the trigger domain
1106   // is calculated such that it will give the same result as the concatenated trigger
1107   // domain merging expressions for all the individual trigger menu items with
1108   // positive results. Again, if no trailing operators are used in the individual
1109   // merging expressions then the default domain operator is placed between two
1110   // expression fragments.
1111   code << "  virtual bool CalculateTriggerDecision(AliHLTTriggerDomain& _domain_, TString& _description_) {" << endl;
1112   if (fDebugMode)
1113   {
1114     code << "#ifdef __CINT__" << endl;
1115     code << "    gFunctionName = \"CalculateTriggerDecision\";" << endl;
1116     code << "#endif" << endl;
1117     code << "    HLTDebug(Form(\"Calculating global HLT trigger result with trigger object at %p.\", this));" << endl;
1118   }
1119   
1120   // Build a list of priorities used in the trigger menu.
1121   std::vector<UInt_t> priorities;
1122   for (UInt_t i = 0; i < menu->NumberOfItems(); i++)
1123   {
1124     const AliHLTTriggerMenuItem* item = menu->Item(i);
1125     bool priorityNotInList = std::find(priorities.begin(), priorities.end(), item->Priority()) == priorities.end();
1126     if (priorityNotInList) priorities.push_back(item->Priority());
1127   }
1128   std::sort(priorities.begin(), priorities.end(), AliHLTDescendingNumbers);
1129   // From the priority list, build the priority groups in the correct order,
1130   // i.e. highest priority first.
1131   // The priority group is a list of vectors of integers. The integers are the
1132   // index numbers into the trigger menu item list for the items which form part
1133   // of the priority group.
1134   std::vector<std::vector<Int_t> > priorityGroup;
1135   priorityGroup.insert(priorityGroup.begin(), priorities.size(), std::vector<Int_t>());
1136   for (size_t n = 0; n < priorities.size(); n++)
1137   {
1138     UInt_t priority = priorities[n];
1139     for (UInt_t i = 0; i < menu->NumberOfItems(); i++)
1140     {
1141       const AliHLTTriggerMenuItem* item = menu->Item(i);
1142       if (item->Priority() == priority) priorityGroup[n].push_back(i);
1143     }
1144   }
1145   
1146   for (size_t n = 0; n < priorityGroup.size(); n++)
1147   {
1148     if (fDebugMode)
1149     {
1150       code << "    HLTDebug(Form(\"Processing trigger priority group " << priorities[n] << "\"));" << endl;
1151     }
1152     code << "    ";
1153     if (n == 0) code << "UInt_t ";
1154     code << "_previous_match_ = 0xFFFFFFFF;" << endl;
1155     code << "    ";
1156     if (n == 0) code << "bool ";
1157     code << "_trigger_matched_ = false;" << endl;
1158     code << "    ";
1159     if (n == 0) code << "bool ";
1160     code << "_group_result_ = false;" << endl;
1161     std::vector<TString> conditionOperator;
1162     conditionOperator.insert(conditionOperator.begin(), priorityGroup[n].size(), TString(""));
1163     std::vector<TString> domainOperator;
1164     domainOperator.insert(domainOperator.begin(), priorityGroup[n].size(), TString(""));
1165     for (size_t m = 0; m < priorityGroup[n].size(); m++)
1166     {
1167       UInt_t i = priorityGroup[n][m];
1168       const AliHLTTriggerMenuItem* item = menu->Item(i);
1169       TString triggerCondition = item->TriggerCondition();
1170       TString mergeExpr = item->MergeExpression();
1171       // Replace the symbols found in the trigger condition and merging expressions
1172       // with appropriate compilable versions.
1173       for (Int_t j = 0; j < symbols.GetEntriesFast(); j++)
1174       {
1175         AliHLTTriggerMenuSymbol* symbol = static_cast<AliHLTTriggerMenuSymbol*>( symbols.UncheckedAt(j) );
1176         bool symbolNamesDifferent = strcmp(symbol->RealName(), symbol->Name()) != 0;
1177         if (strcmp(symbol->ObjectClass(), "AliHLTTriggerDecision") == 0)
1178         {
1179           TString newname = symbol->Name();
1180           newname += "TriggerDomain";
1181           mergeExpr.ReplaceAll(symbol->RealName(), newname);
1182         }
1183         else
1184         {
1185           if (symbolNamesDifferent) mergeExpr.ReplaceAll(symbol->RealName(), symbol->Name());
1186         }
1187         if (symbolNamesDifferent) triggerCondition.ReplaceAll(symbol->RealName(), symbol->Name());
1188       }
1189       // We allow the trigger conditions and merging expressions to have trailing operators.
1190       // Thus, we need to extract the operators and cleanup the expressions so that they will
1191       // compile. This means that we silently ignore the trailing operator if not needed.
1192       if (ExtractedOperator(triggerCondition, conditionOperator[m]))
1193       {
1194         // If the trailing operator is the same as the default operator then reset
1195         // the value in the operator list so that the default is used in the generated
1196         // code. This creates more compact code.
1197         if (conditionOperator[m] == menu->DefaultConditionOperator()) conditionOperator[m] = "";
1198       }
1199       if (ExtractedOperator(mergeExpr, domainOperator[m]))
1200       {
1201         if (domainOperator[m] == menu->DefaultDomainOperator()) domainOperator[m] = "";
1202       }
1203       if (fDebugMode)
1204       {
1205         code << "    HLTDebug(Form(\"Trying trigger condition " << i
1206              << " (Description = '%s').\", fMenuItemDescription" << i << ".Data()));"
1207              << endl;
1208       }
1209       code << "    ";
1210       if (n == 0 and m == 0) code << "bool ";
1211       code << "_item_result_ = false;" << endl;
1212       code << "    if (" << triggerCondition << ") {" << endl;
1213       code << "      ++fCounter[" << i << "];" << endl;
1214       const char* indentation = "";
1215       if (item->PreScalar() != 0)
1216       {
1217         indentation = "  ";
1218         code << "      if ((fCounter[" << i << "] % " << item->PreScalar() << ") == 1) {" << endl;
1219       }
1220       code << indentation << "      _item_result_ = true;" << endl;
1221       if (fDebugMode)
1222       {
1223         code << indentation << "      HLTDebug(Form(\"Matched trigger condition " << i
1224              << " (Description = '%s').\", fMenuItemDescription" << i << ".Data()));" << endl;
1225       }
1226       if (item->PreScalar() != 0)
1227       {
1228         code << "      }" << endl;
1229       }
1230       code << "    }" << endl;
1231       if (m == 0)
1232       {
1233         // Since this is the first item of the trigger group,
1234         // the generated trigger logic can be simplified a little.
1235         code << "    _group_result_ = _item_result_;" << endl;
1236         code << "    if (_item_result_) {" << endl;
1237         code << "      _domain_ = " << mergeExpr.Data() << ";" << endl;
1238         code << "      _description_ = fMenuItemDescription" << i << ";" << endl;
1239         code << "      _previous_match_ = " << i << ";" << endl;
1240         code << "      _trigger_matched_ = true;" << endl;
1241         code << "    }" << endl;
1242       }
1243       else
1244       {
1245         if (conditionOperator[m-1] == "")
1246         {
1247           code << "    _group_result_ = _group_result_ "
1248                << menu->DefaultConditionOperator() << " _item_result_;" << endl;
1249         }
1250         else
1251         {
1252           code << "    _group_result_ = _group_result_ "
1253                << conditionOperator[m-1] << " _item_result_;" << endl;
1254         }
1255         code << "    if (_item_result_) {" << endl;
1256         code << "      if (_trigger_matched_) {" << endl;
1257         bool switchWillBeEmpty = true;
1258         for (size_t k = 0; k < m; k++)
1259         {
1260           if (domainOperator[k] == "") continue;
1261           switchWillBeEmpty = false;
1262         }
1263         if (switchWillBeEmpty)
1264         {
1265           code << "        _domain_ = _domain_ " << menu->DefaultDomainOperator() << " "
1266                << mergeExpr.Data() << ";" << endl;
1267         }
1268         else
1269         {
1270           code << "        switch(_previous_match_) {" << endl;
1271           for (size_t k = 0; k < m; k++)
1272           {
1273             if (domainOperator[k] == "") continue;
1274             code << "        case " << k << ": _domain_ = _domain_ "
1275                  << domainOperator[k] << " " << mergeExpr.Data() << "; break;" << endl;
1276           }
1277           code << "        default: _domain_ = _domain_ "
1278                << menu->DefaultDomainOperator() << " " << mergeExpr.Data() << ";" << endl;
1279           code << "        }" << endl;
1280         }
1281         code << "        _description_ += \",\";" << endl;
1282         code << "        _description_ += fMenuItemDescription" << i << ";" << endl;
1283         code << "      } else {" << endl;
1284         code << "        _domain_ = " << mergeExpr.Data() << ";" << endl;
1285         code << "        _description_ = fMenuItemDescription" << i << ";" << endl;
1286         code << "      }" << endl;
1287         code << "      _previous_match_ = " << i << ";" << endl;
1288         code << "      _trigger_matched_ = true;" << endl;
1289         code << "    }" << endl;
1290       }
1291     }
1292     code << "    if (_group_result_) {" << endl;
1293     if (fDebugMode)
1294     {
1295       if (n < priorities.size() - 1)
1296       {
1297         code << "      HLTDebug(Form(\"Matched triggers in trigger priority group " << priorities[n]
1298              << ". Stopping processing here because all other trigger groups have lower priority.\"));" << endl;
1299       }
1300       else
1301       {
1302         code << "      HLTDebug(Form(\"Matched triggers in trigger priority group " << priorities[n] << ".\"));" << endl;
1303       }
1304     }
1305     code << "      return true;" << endl;
1306     code << "    }" << endl;
1307   }
1308   code << "    _domain_.Clear();" << endl;
1309   code << "    _description_ = \"\";" << endl;
1310   code << "    return false;" << endl;
1311   code << "  }" << endl;
1312   
1313   // Generate getter and setter methods for the counters.
1314   code << "  const TArrayL64& GetCounters() const { return fCounter; }" << endl;
1315   code << "  void SetCounters(const TArrayL64& counters) { fCounter = counters; }" << endl;
1316   
1317   code << "private:" << endl;
1318   // Add the symbols in the trigger menu to the list of private variables.
1319   for (Int_t i = 0; i < symbols.GetEntriesFast(); i++)
1320   {
1321     AliHLTTriggerMenuSymbol* symbol = static_cast<AliHLTTriggerMenuSymbol*>( symbols.UncheckedAt(i) );
1322     code << "  " << symbol->Type() << " " << symbol->Name() << ";" << endl;
1323     if (strcmp(symbol->ObjectClass(), "AliHLTTriggerDecision") == 0)
1324     {
1325       code << "  AliHLTTriggerDomain " << symbol->Name() << "TriggerDomain;" << endl;
1326     }
1327     code << "  AliHLTDomainEntry " << symbol->Name() << "DomainEntry;" << endl;
1328   }
1329   for (UInt_t i = 0; i < menu->NumberOfItems(); i++)
1330   {
1331     code << "  TString fMenuItemDescription" << i << ";" << endl;
1332   }
1333   code << "  TArrayL64 fCounter;" << endl;
1334   code << "#if !defined(__CINT__) || defined(__MAKECINT__)" << endl;
1335   code << "  ClassDef(" << name.Data() << ", 0)" << endl;
1336   code << "#else" << endl;
1337   code << "  virtual const char* Class_Name() const { return \"" << name.Data() << "\"; }" << endl;
1338   code << "#endif" << endl;
1339   code << "};" << endl;
1340   code << "#if !defined(__CINT__) || defined(__MAKECINT__)" << endl;
1341   code << "ClassImp(" << name.Data() << ")" << endl;
1342   code << "#endif" << endl;
1343   
1344   code.close();
1345   
1346   // Now we need to compile and load the new class.
1347   result = LoadTriggerClass(filename, includePaths);
1348   return result;
1349 }
1350
1351
1352 int AliHLTGlobalTriggerComponent::LoadTriggerClass(
1353     const char* filename, const TClonesArray& includePaths
1354   )
1355 {
1356   // Loads the code for a custom global trigger class implementation on the fly.
1357   
1358   HLTDebug("Loading HLT trigger class from file '%s'.", filename);
1359   
1360   TString compiler = gSystem->GetBuildCompilerVersion();
1361   if (fRuntimeCompile && (compiler.Contains("gcc") or compiler.Contains("icc")))
1362   {
1363     TString includePath;
1364 #if defined(PKGINCLUDEDIR)
1365     // this is especially for the HLT build system where the package is installed
1366     // in a specific directory including proper treatment of include files
1367     includePath.Form("-I%s", PKGINCLUDEDIR);
1368 #else
1369     // the default AliRoot behavior, all include files can be found in the
1370     // $ALICE_ROOT subfolders
1371     includePath = "-I${ALICE_ROOT}/include -I${ALICE_ROOT}/HLT/BASE -I${ALICE_ROOT}/HLT/trigger";
1372 #endif
1373     // Add any include paths that were specified on the command line.
1374     for (Int_t i = 0; i < includePaths.GetEntriesFast(); i++)
1375     {
1376       TString path = static_cast<TObjString*>(includePaths.UncheckedAt(i))->String();
1377       includePath += " -I";
1378       includePath += path;
1379     }
1380     HLTDebug("using include settings: %s", includePath.Data());
1381     gSystem->SetIncludePath(includePath);
1382     gSystem->SetFlagsOpt("-O3 -DNDEBUG");
1383     gSystem->SetFlagsDebug("-g3 -DDEBUG -D__DEBUG");
1384     
1385     int result = kTRUE;
1386     if (fDebugMode)
1387     {
1388       result = gSystem->CompileMacro(filename, "g");
1389     }
1390     else
1391     {
1392       result = gSystem->CompileMacro(filename, "O");
1393     }
1394     if (result != kTRUE)
1395     {
1396       HLTFatal("Could not compile and load global trigger menu implementation.");
1397       return -ENOENT;
1398     }
1399   }
1400   else
1401   {
1402     // Store the library state to be checked later in UnloadTriggerClass.
1403     fLibStateAtLoad = gSystem->GetLibraries();
1404     
1405     // If we do not support the compiler then try interpret the class instead.
1406     TString cmd = ".L ";
1407     cmd += filename;
1408     Int_t errorcode = TInterpreter::kNoError;
1409     gROOT->ProcessLine(cmd, &errorcode);
1410     if (errorcode != TInterpreter::kNoError)
1411     {
1412       HLTFatal("Could not load interpreted global trigger menu implementation"
1413                " (Interpreter error code = %d).",
1414                errorcode
1415       );
1416       return -ENOENT;
1417     }
1418   }
1419   
1420   return 0;
1421 }
1422
1423
1424 int AliHLTGlobalTriggerComponent::UnloadTriggerClass(const char* filename)
1425 {
1426   // Unloads the code previously loaded by LoadTriggerClass.
1427   
1428   HLTDebug("Unloading HLT trigger class in file '%s'.", filename);
1429   
1430   TString compiler = gSystem->GetBuildCompilerVersion();
1431   if (fRuntimeCompile && (compiler.Contains("gcc") or compiler.Contains("icc")))
1432   {
1433     // Generate the library name.
1434     TString libname = filename;
1435     Ssiz_t dotpos = libname.Last('.');
1436     if (0 <= dotpos and dotpos < libname.Length()) libname[dotpos] = '_';
1437     libname += ".";
1438     libname += gSystem->GetSoExt();
1439     
1440     // This is a workaround for a problem with unloading shared libraries in ROOT.
1441     // If the trigger logic library is loaded before the libAliHLTHOMER.so library
1442     // or any other library is loaded afterwards, then during the gInterpreter->UnloadFile
1443     // call all the subsequent libraries get unloded. This means that any objects created
1444     // from classes implemented in the libAliHLTHOMER.so library will generate segfaults
1445     // since the executable code has been unloaded.
1446     // We need to check if there are any more libraries loaded after the class we
1447     // are unloading and in that case don't unload the class.
1448     TString libstring = gSystem->GetLibraries();
1449     TString token, lastlib;
1450     Ssiz_t from = 0;
1451     Int_t numOfLibs = 0, posOfLib = -1;
1452     while (libstring.Tokenize(token, from, " "))
1453     {
1454       ++numOfLibs;
1455       lastlib = token;
1456       if (token.Contains(libname)) posOfLib = numOfLibs;
1457     }
1458     if (numOfLibs != posOfLib)
1459     {
1460       HLTWarning(Form("ROOT limitation! Cannot properly cleanup and unload the shared"
1461           " library '%s' since another library '%s' was loaded afterwards. Trying to"
1462           " unload this library will remove the others and lead to serious memory faults.",
1463           libname.Data(), lastlib.Data()
1464       ));
1465       return 0;
1466     }
1467     
1468     char* path = NULL;
1469     int result = 0;
1470     if ((path = gSystem->DynamicPathName(libname)) != NULL)
1471     {
1472       result = gInterpreter->UnloadFile(path);
1473       delete [] path;
1474     }
1475     if (result != TInterpreter::kNoError) return -ENOENT;
1476   }
1477   else
1478   {
1479     // This is again a workaround for the problem with unloading files in ROOT.
1480     // If the trigger logic class is loaded before the libAliHLTHOMER.so library
1481     // or any other library is loaded afterwards, then during the gInterpreter->UnloadFile
1482     // call all the subsequent libraries get unloded.
1483     // We need to check if the list of loaded libraries has changed since the last
1484     // call to LoadTriggerClass. If it has then don't unload the class.
1485     if (fLibStateAtLoad != gSystem->GetLibraries())
1486     {
1487       TString libstring = gSystem->GetLibraries();
1488       TString token;
1489       Ssiz_t from = 0;
1490       while (libstring.Tokenize(token, from, " "))
1491       {
1492         if (not fLibStateAtLoad.Contains(token)) break;
1493       }
1494       HLTWarning(Form("ROOT limitation! Cannot properly cleanup and unload the file"
1495           " '%s' since another library '%s' was loaded afterwards. Trying to unload"
1496           " this file will remove the other library and lead to serious memory faults.",
1497           filename, token.Data()
1498       ));
1499       return 0;
1500     }
1501   
1502     // If we did not compile the trigger logic then remove the interpreted class.
1503     TString cmd = ".U ";
1504     cmd += filename;
1505     Int_t errorcode = TInterpreter::kNoError;
1506     gROOT->ProcessLine(cmd, &errorcode);
1507     if (errorcode != TInterpreter::kNoError)
1508     {
1509       HLTFatal("Could not unload interpreted global trigger menu implementation"
1510                " (Interpreter error code = %d).",
1511                errorcode
1512       );
1513       return -ENOENT;
1514     }
1515   }
1516   
1517   return 0;
1518 }
1519
1520
1521 int AliHLTGlobalTriggerComponent::FindSymbol(const char* name, const TClonesArray& list)
1522 {
1523   // Searches for the named symbol in the given list.
1524   // See header for more details.
1525   
1526   for (int i = 0; i < list.GetEntriesFast(); i++)
1527   {
1528     const AliHLTTriggerMenuSymbol* symbol = dynamic_cast<const AliHLTTriggerMenuSymbol*>( list.UncheckedAt(i) );
1529     if (symbol == NULL) continue;
1530     if (strcmp(symbol->Name(), name) == 0) return i;
1531   }
1532   return -1;
1533 }
1534
1535
1536 int AliHLTGlobalTriggerComponent::BuildSymbolList(const AliHLTTriggerMenu* menu, TClonesArray& list)
1537 {
1538   // Builds the list of symbols to use in the custom global trigger menu
1539   // implementation class.
1540   // See header for more details.
1541   
1542   // Note: when we build the symbol list we must use the symbol name as returned
1543   // by the Name() method and not the RealName() method when using FindSymbol.
1544   // This is so that we avoid problems with the generated code not compiling
1545   // because names like "abc-xyz" and "abc_xyz" are synonymous.
1546   // Name() returns the converted C++ symbol name as used in the generated code.
1547   
1548   for (UInt_t i = 0; i < menu->NumberOfSymbols(); i++)
1549   {
1550     const AliHLTTriggerMenuSymbol* symbol = menu->Symbol(i);
1551     if (FindSymbol(symbol->Name(), list) != -1)
1552     {
1553       HLTError("Multiple symbols with the name '%s' defined in the trigger menu.", symbol->Name());
1554       return -EIO;
1555     }
1556     try
1557     {
1558       new (list[list.GetEntriesFast()]) AliHLTTriggerMenuSymbol(*symbol);
1559     }
1560     catch (const std::bad_alloc&)
1561     {
1562       HLTError("Could not allocate more memory for the symbols list when adding a trigger menu symbol.");
1563       return -ENOMEM;
1564     }
1565   }
1566   
1567   TRegexp exp("[_a-zA-Z][-_a-zA-Z0-9]*");
1568   TRegexp hexexp("x[a-fA-F0-9]+");
1569   for (UInt_t i = 0; i < menu->NumberOfItems(); i++)
1570   {
1571     const AliHLTTriggerMenuItem* item = menu->Item(i);
1572     TString str = item->TriggerCondition();
1573     Ssiz_t start = 0;
1574     do
1575     {
1576       Ssiz_t length = 0;
1577       Ssiz_t pos = exp.Index(str, &length, start);
1578       if (pos == kNPOS) break;
1579       start = pos+length;
1580       
1581       // Check if there is a numerical character before the found
1582       // regular expression. If so, then the symbol is not a valid one
1583       // and should be skipped.
1584       if (pos > 0)
1585       {
1586         bool notValid = false;
1587         switch (str[pos-1])
1588         {
1589         case '0': case '1': case '2': case '3': case '4':
1590         case '5': case '6': case '7': case '8': case '9':
1591           notValid = true;
1592           break;
1593         default:
1594           notValid = false;
1595           break;
1596         }
1597         if (notValid) continue;
1598       }
1599       TString s = str(pos, length);
1600       
1601       if (s == "and" or s == "and_eq" or s == "bitand" or s == "bitor" or
1602           s == "compl" or s == "not" or s == "not_eq" or s == "or" or
1603           s == "or_eq" or s == "xor" or s == "xor_eq" or s == "true" or
1604           s == "false"
1605          )
1606       {
1607         // Ignore iso646.h and other keywords.
1608         continue;
1609       }
1610
1611       // Need to create the symbols first and check if its name is in the list
1612       // before actually adding it to the symbols list.
1613       AliHLTTriggerMenuSymbol newSymbol;
1614       newSymbol.Name(s.Data());
1615       newSymbol.Type("bool");
1616       newSymbol.ObjectClass("AliHLTTriggerDecision");
1617       newSymbol.AssignExpression("this->Result()");
1618       newSymbol.DefaultValue("false");
1619       if (FindSymbol(newSymbol.Name(), list) == -1)
1620       {
1621         try
1622         {
1623           new (list[list.GetEntriesFast()]) AliHLTTriggerMenuSymbol(newSymbol);
1624         }
1625         catch (const std::bad_alloc&)
1626         {
1627           HLTError("Could not allocate more memory for the symbols list when adding a trigger name symbol.");
1628           return -ENOMEM;
1629         }
1630       }
1631     }
1632     while (start < str.Length());
1633   }
1634   
1635   return 0;
1636 }
1637
1638
1639 bool AliHLTGlobalTriggerComponent::ExtractedOperator(TString& expr, TString& op)
1640 {
1641   // Extracts the trailing operator from the expression.
1642   
1643   Ssiz_t i = 0;
1644   // First skip the trailing whitespace.
1645   bool whitespace = true;
1646   for (i = expr.Length()-1; i >= 0 and whitespace; i--)
1647   {
1648     switch (expr[i])
1649     {
1650     case ' ': case '\t': case '\r': case '\n':
1651       whitespace = true;
1652       break;
1653     default:
1654       whitespace = false;
1655     }
1656   }
1657   if (i < 0 or whitespace) return false;
1658   
1659   // Now find the first whitespace character before the trailing symbol.
1660   bool nonwhitespace = true;
1661   for (; i >= 0 and nonwhitespace; i--)
1662   {
1663     switch (expr[i])
1664     {
1665     case ' ': case '\t': case '\r': case '\n':
1666       nonwhitespace = false;
1667       break;
1668     default:
1669       nonwhitespace = true;
1670     }
1671   }
1672   if (i < 0 or nonwhitespace) return false;
1673   
1674   // Extract the last symbols and check if it is a valid operator.
1675   TString s = expr;
1676   s.Remove(0, i+2);
1677   if (s == "and" or s == "and_eq" or s == "bitand" or s == "bitor" or
1678       s == "compl" or s == "not" or s == "not_eq" or s == "or" or
1679       s == "or_eq" or s == "xor" or s == "xor_eq" or s == "&&" or
1680       s == "&=" or s == "&" or s == "|" or s == "~" or s == "!" or
1681       s == "!=" or s == "||" or s == "|=" or s == "^" or s == "^=" or
1682       s == "==" or s == "+" or s == "-" or s == "*" or s == "/" or
1683       s == "%" or s == ">" or s == "<" or s == ">=" or s == "<="
1684      )
1685   {
1686     expr.Remove(i+1);
1687     op = s;
1688     return true;
1689   }
1690   
1691   return false;
1692 }
1693
1694
1695 int AliHLTGlobalTriggerComponent::PrintStatistics(const AliHLTGlobalTrigger* pTrigger, AliHLTComponentLogSeverity level, int offset) const
1696 {
1697   // print some statistics
1698   int totalEvents=GetEventCount()+offset;
1699   const TArrayL64& counters = pTrigger->GetCounters();
1700   if (pTrigger->CallFailed()) return -EPROTO;
1701   for (int i = 0; i < counters.GetSize(); i++) {
1702     ULong64_t count = counters[i];
1703     float ratio=0;
1704     if (totalEvents>0) ratio=100*(float)count/totalEvents;
1705     HLTLog(level, "Item %d: total events: %d - counted events: %llu (%.1f%%)", i, totalEvents, count, ratio);
1706   }
1707   return 0;
1708 }
1709
1710 int AliHLTGlobalTriggerComponent::AddCTPDecisions(AliHLTGlobalTrigger* pTrigger, const AliHLTCTPData* pCTPData, const AliHLTComponentTriggerData* trigData)
1711 {
1712   // add trigger decisions for the valid CTP classes
1713   if (!pCTPData || !pTrigger) return 0;
1714
1715   AliHLTUInt64_t triggerMask=pCTPData->Mask();
1716   AliHLTUInt64_t bit0=0x1;
1717   if (!fCTPDecisions) {
1718     try
1719     {
1720       fCTPDecisions=new TClonesArray(AliHLTTriggerDecision::Class(), gkNCTPTriggerClasses);
1721     }
1722     catch (const std::bad_alloc&)
1723     {
1724       HLTError("Could not allocate memory for the CTP decisions array.");
1725       return -ENOMEM;
1726     }
1727     if (!fCTPDecisions) return -ENOMEM;
1728
1729     try
1730     {
1731       fCTPDecisions->ExpandCreate(gkNCTPTriggerClasses);
1732     }
1733     catch (const std::bad_alloc&)
1734     {
1735       HLTError("Could not allocate more memory for the CTP decisions array.");
1736       return -ENOMEM;
1737     }
1738     for (int i=0; i<gkNCTPTriggerClasses; i++) {
1739       const char* name=pCTPData->Name(i);
1740       if (triggerMask&(bit0<<i) && name) {
1741         AliHLTTriggerDecision* pDecision=dynamic_cast<AliHLTTriggerDecision*>(fCTPDecisions->At(i));
1742         assert(pDecision);
1743         if (!pDecision) {
1744           delete fCTPDecisions;
1745           fCTPDecisions=NULL;
1746           return -ENOENT;
1747         }
1748         pDecision->Name(name);
1749       }
1750     }
1751   }
1752
1753   for (int i=0; i<gkNCTPTriggerClasses; i++) {
1754     const char* name=pCTPData->Name(i);
1755     if ((triggerMask&(bit0<<i))==0 || name==NULL) continue;
1756     AliHLTTriggerDecision* pDecision=dynamic_cast<AliHLTTriggerDecision*>(fCTPDecisions->At(i));
1757     HLTDebug("updating CTP trigger decision %d %s (%p casted %p)", i, name, fCTPDecisions->At(i), pDecision);
1758     if (!pDecision) return -ENOENT;
1759
1760     bool result=false;
1761     // 13 March 2010 - Optimisation:
1762     // Dont use the EvaluateCTPTriggerClass method, which uses slow TFormula objects.
1763     AliHLTUInt64_t triggers = 0;
1764     if (trigData) triggers = pCTPData->ActiveTriggers(*trigData);
1765     else triggers = pCTPData->Triggers();
1766     result = (triggers&((AliHLTUInt64_t)0x1<<i)) ? true : false;
1767     //if (trigData) result=pCTPData->EvaluateCTPTriggerClass(name, *trigData);
1768     //else result=pCTPData->EvaluateCTPTriggerClass(name);
1769     pDecision->Result(result);
1770     pDecision->TriggerDomain().Clear();
1771     if (trigData) pDecision->TriggerDomain().Add(pCTPData->ReadoutList(*trigData));
1772     else pDecision->TriggerDomain().Add(pCTPData->ReadoutList());
1773
1774     pTrigger->Add(fCTPDecisions->At(i), kAliHLTDataTypeTriggerDecision, kAliHLTVoidDataSpec);
1775     if (pTrigger->CallFailed()) return -EPROTO;
1776   }
1777
1778   return 0;
1779 }