First version of the online reco + AliEVE attached to it. (Matevz and Cvetan)
[u/mrichter/AliRoot.git] / EVE / EveBase / AliEveEventManager.cxx
CommitLineData
d810d0de 1// $Id$
2// Main authors: Matevz Tadel & Alja Mrak-Tadel: 2006, 2007
5a5a1232 3
d810d0de 4/**************************************************************************
5 * Copyright(c) 1998-2008, ALICE Experiment at CERN, all rights reserved. *
6 * See http://aliceinfo.cern.ch/Offline/AliRoot/License.html for *
51346b82 7 * full copyright notice. *
d810d0de 8 **************************************************************************/
9
10#include "AliEveEventManager.h"
84aff7a4 11#include <TEveManager.h>
5a5a1232 12
13#include <AliRunLoader.h>
93845f6c 14#include <AliRun.h>
af885e0f 15#include <AliESDEvent.h>
3aecaefc 16#include <AliESDfriend.h>
c2c4b7a2 17#include <AliRawReaderRoot.h>
18#include <AliRawReaderFile.h>
19#include <AliRawReaderDate.h>
93845f6c 20#include <AliMagFMaps.h>
632d2b03 21#include <AliCDBManager.h>
22#include <AliHeader.h>
23#include <AliGeomManager.h>
5a5a1232 24
25#include <TFile.h>
26#include <TTree.h>
5a5a1232 27#include <TSystem.h>
d810d0de 28
a15e6d7d 29//==============================================================================
30//==============================================================================
31// AliEveEventManager
32//==============================================================================
5a5a1232 33
57ffa5fb 34//______________________________________________________________________________
5a5a1232 35//
4852ff6f 36// Provide interface for loading and navigating standard AliRoot data
37// (AliRunLoader) and ESDs.
38//
39// Missing support for raw-data. For now this is handled individually
40// by each sub-detector.
51346b82 41//
a15e6d7d 42// Also provides interface to magnetic-field and geometry. Mostly
43// intended as wrappers over standard AliRoot functionality for
44// convenient use from visualizateion macros.
5a5a1232 45
d810d0de 46ClassImp(AliEveEventManager)
5a5a1232 47
a15e6d7d 48AliEveEventManager* gAliEveEvent = 0;
5a5a1232 49
d810d0de 50Bool_t AliEveEventManager::fgAssertRunLoader = kFALSE;
c76ea574 51Bool_t AliEveEventManager::fgAssertESD = kFALSE;
c2c4b7a2 52Bool_t AliEveEventManager::fgAssertRaw = kFALSE;
5a5a1232 53
c2c4b7a2 54TString AliEveEventManager::fgESDFileName("AliESDs.root");
55TString AliEveEventManager::fgRawFileName("raw.root");
d810d0de 56TString AliEveEventManager::fgCdbUri("local://$ALICE_ROOT");
632d2b03 57
d810d0de 58AliMagF* AliEveEventManager::fgMagField = 0;
93845f6c 59
60
d810d0de 61AliEveEventManager::AliEveEventManager() :
84aff7a4 62 TEveEventManager(),
265ecb21 63
c2c4b7a2 64 fPath ( ), fEventId (-1),
265ecb21 65 fRunLoader (0),
90fa773e 66 fESDFile (0), fESDTree (0), fESD (0),
c2c4b7a2 67 fESDfriend (0), fESDfriendExists(kFALSE),
319f3084 68 fRawReader (0),
69 fAutoLoad(kFALSE),
70 fAutoLoadTime(5.),
71 fAutoLoadTimer(0),
72 fIsOnline(kFALSE)
c76ea574 73{
74 // Default constructor.
75}
5a5a1232 76
d810d0de 77AliEveEventManager::AliEveEventManager(TString path, Int_t ev) :
78 TEveEventManager("AliEVE AliEveEventManager"),
265ecb21 79
c76ea574 80 fPath (path), fEventId(-1),
265ecb21 81 fRunLoader (0),
90fa773e 82 fESDFile (0), fESDTree (0), fESD (0),
c2c4b7a2 83 fESDfriend (0), fESDfriendExists(kFALSE),
319f3084 84 fRawReader (0),
85 fAutoLoad(kFALSE),
86 fAutoLoadTime(5.),
87 fAutoLoadTimer(0),
88 fIsOnline(kFALSE)
5a5a1232 89{
c76ea574 90 // Constructor with event-directory URL and event-id.
91
5a5a1232 92 Open();
90fa773e 93 if (ev >= 0) GotoEvent(ev);
5a5a1232 94}
95
a15e6d7d 96AliEveEventManager::~AliEveEventManager()
97{
98 // Destructor.
99
319f3084 100 if (fAutoLoadTimer) delete fAutoLoadTimer;
a15e6d7d 101 // Somewhat unclear what to do here.
102 // In principle should close all data sources and deregister from
103 // TEveManager.
104}
105
57ffa5fb 106/******************************************************************************/
5a5a1232 107
c2c4b7a2 108void AliEveEventManager::SetESDFileName(const Text_t* esd)
109{
110 // Set file-name for opening ESD, default "AliESDs.root".
111
112 if (esd) fgESDFileName = esd;
113}
114
115void AliEveEventManager::SetRawFileName(const Text_t* raw)
116{
117 // Set file-name for opening of raw-data, default "raw.root"
118 if (raw) fgRawFileName = raw;
119}
120
121void AliEveEventManager::SetCdbUri(const Text_t* cdb)
122{
123 // Set path to CDB, default "local://$ALICE_ROOT".
124
125 if (cdb) fgCdbUri = cdb;
126}
127
128void AliEveEventManager::SetAssertElements(Bool_t assertRunloader,
129 Bool_t assertEsd,
130 Bool_t assertRaw)
131{
132 // Set global flags that detrmine which parts of the event-data must
133 // be present when the event is opened.
134
135 fgAssertRunLoader = assertRunloader;
136 fgAssertESD = assertEsd;
137 fgAssertRaw = assertRaw;
138}
139
140/******************************************************************************/
141
d810d0de 142void AliEveEventManager::Open()
5a5a1232 143{
c76ea574 144 // Open event-data from URL specified in fPath.
145 // Attempts to create AliRunLoader() and to open ESD with ESDfriends.
146 // Warning is reported if run-loader or ESD is not found.
147 // Global data-members fgAssertRunLoader and fgAssertESD can be set
148 // to throw exceptions instead.
149
a15e6d7d 150 static const TEveException kEH("AliEveEventManager::Open ");
5a5a1232 151
152 gSystem->ExpandPathName(fPath);
84aff7a4 153 if (fPath[0] != '/')
5a5a1232 154 fPath = Form("%s/%s", gSystem->WorkingDirectory(), fPath.Data());
155
632d2b03 156 Int_t runNo = -1;
157
a15e6d7d 158 TString gaPath(Form("%s/galice.root", fPath.Data()));
c2c4b7a2 159 // If i use open directly, we get fatal.
160 // Is this (AccessPathName check) ok for xrootd / alien?
a15e6d7d 161 if (gSystem->AccessPathName(gaPath, kReadPermission) == kFALSE)
90fa773e 162 {
a15e6d7d 163 fRunLoader = AliRunLoader::Open(gaPath);
a1896a82 164 if (fRunLoader)
5a5a1232 165 {
a15e6d7d 166 TString alicePath = fPath + "/";
167 fRunLoader->SetDirName(alicePath);
a1896a82 168
169 if (fRunLoader->LoadgAlice() != 0)
c2c4b7a2 170 Warning(kEH, "failed loading gAlice via run-loader.");
a1896a82 171
172 if (fRunLoader->LoadHeader() == 0)
173 {
c2c4b7a2 174 runNo = fRunLoader->GetHeader()->GetRun();
a1896a82 175 }
176 else
177 {
c2c4b7a2 178 Warning(kEH, "failed loading run-loader's header.");
179 delete fRunLoader;
180 fRunLoader = 0;
a1896a82 181 }
182 }
183 else // run-loader open failed
184 {
a15e6d7d 185 Warning(kEH, "failed opening ALICE run-loader from '%s'.", gaPath.Data());
5a5a1232 186 }
5a5a1232 187 }
a1896a82 188 else // galice not readable
189 {
a15e6d7d 190 Warning(kEH, "can not read '%s'.", gaPath.Data());
a1896a82 191 }
192 if (fRunLoader == 0)
193 {
84aff7a4 194 if (fgAssertRunLoader)
c2c4b7a2 195 throw (kEH + "Bootstraping of run-loader failed. Its precence was requested.");
a1896a82 196 else
a15e6d7d 197 Warning(kEH, "Bootstraping of run-loader failed.");
a1896a82 198 }
51346b82 199
5a5a1232 200
c2c4b7a2 201 TString esdPath(Form("%s/%s", fPath.Data(), fgESDFileName.Data()));
202 if ((fESDFile = TFile::Open(esdPath)))
90fa773e 203 {
c2c4b7a2 204 fESD = new AliESDEvent();
205 fESDTree = (TTree*) fESDFile->Get("esdTree");
206 if (fESDTree != 0)
90fa773e 207 {
c2c4b7a2 208 fESD->ReadFromTree(fESDTree);
209 runNo = fESD->GetESDRun()->GetRunNumber();
210
211 // Check if ESDfriends exists and attach the branch
212 TString p = Form("%s/AliESDfriends.root", fPath.Data());
213 if (gSystem->AccessPathName(p, kReadPermission) == kFALSE)
a1896a82 214 {
c2c4b7a2 215 fESDfriendExists = kTRUE;
216 fESDTree->SetBranchStatus ("ESDfriend*", 1);
217 fESDTree->SetBranchAddress("ESDfriend.", &fESDfriend);
753fdd1e 218 }
219 }
c2c4b7a2 220 else // esdtree == 0
a1896a82 221 {
5a5a1232 222 delete fESDFile; fESDFile = 0;
c2c4b7a2 223 Warning(kEH, "failed getting the esdTree.");
5a5a1232 224 }
a1896a82 225 }
226 else // esd not readable
227 {
a15e6d7d 228 Warning(kEH, "can not read ESD file '%s'.", esdPath.Data());
a1896a82 229 }
230 if (fESDTree == 0)
231 {
c76ea574 232 if (fgAssertESD)
90fa773e 233 {
c2c4b7a2 234 throw (kEH + "ESD not initialized. Its precence was requested.");
51346b82 235 } else {
a15e6d7d 236 Warning(kEH, "ESD not initialized.");
3aecaefc 237 }
5a5a1232 238 }
239
c2c4b7a2 240 TString rawPath(Form("%s/%s", fPath.Data(), fgRawFileName.Data()));
241 // If i use open directly, raw-reader reports an error but i have
242 // no way to detect it.
243 // Is this (AccessPathName check) ok for xrootd / alien?
244 if (gSystem->AccessPathName(rawPath, kReadPermission) == kFALSE)
245 {
246 if (fgRawFileName.EndsWith("/"))
247 {
248 fRawReader = new AliRawReaderFile(rawPath);
249 }
250 else if (fgRawFileName.EndsWith(".root"))
251 {
252 fRawReader = new AliRawReaderRoot(rawPath);
253 }
254 else if (!fgRawFileName.IsNull())
255 {
256 fRawReader = new AliRawReaderDate(rawPath);
257 }
258 }
259
260 if (fRawReader == 0)
261 {
262 if (fgAssertRaw)
263 {
264 throw (kEH + "raw-data not initialized. Its precence was requested.");
265 } else {
266 Warning(kEH, "raw-data not initialized.");
267 }
268 }
269
632d2b03 270 if (runNo < 0)
c2c4b7a2 271 {
272 if (fRawReader)
273 {
274 fRawReader->NextEvent();
275 runNo = fRawReader->GetRunNumber();
276 printf("Determining run-no from raw ... run=%d\n", runNo);
277 fRawReader->RewindEvents();
278 } else {
279 throw (kEH + "unknown run number.");
280 }
281 }
632d2b03 282
283 {
284 AliCDBManager* cdb = AliCDBManager::Instance();
285 cdb->SetDefaultStorage(fgCdbUri);
286 if (cdb->IsDefaultStorageSet() == kFALSE)
c2c4b7a2 287 throw (kEH + "CDB initialization failed.");
632d2b03 288 cdb->SetRun(runNo);
289 }
290
73c1c0ec 291 SetName(Form("Event %d", fEventId));
5a5a1232 292 SetTitle(fPath);
293}
294
319f3084 295void AliEveEventManager::SetEvent(AliRunLoader *runLoader, AliRawReader *rawReader, AliESDEvent *esd)
296{
297 // Set an event from an external source
298 // The method is used in the online visualisation
299 fRunLoader = runLoader;
300 fRawReader = rawReader;
301 fESD = esd;
302 fIsOnline = kTRUE;
303 SetTitle("Online event in memory");
304 SetName("Online Event");
305
306 ElementChanged();
307 AfterNewEventLoaded();
308}
309
d810d0de 310void AliEveEventManager::GotoEvent(Int_t event)
1eaa5849 311{
73c1c0ec 312 // Load data for specified event.
313 // If event is out of range an exception is thrown and old state
314 // is preserved.
315 // After successful loading of event, the virtual function
316 // AfterNewEventLoaded() is called. This executes commands that
317 // were registered via TEveEventManager::AddNewEventCommand().
318
a15e6d7d 319 static const TEveException kEH("AliEveEventManager::GotoEvent ");
1eaa5849 320
c2c4b7a2 321 if (event < 0) {
322 Error(kEH, "event must be non-negative.");
323 return;
324 }
325
1eaa5849 326 Int_t maxEvent = 0;
c2c4b7a2 327 if (fRunLoader) {
1eaa5849 328 maxEvent = fRunLoader->GetNumberOfEvents() - 1;
c2c4b7a2 329 } else if (fESDTree) {
1eaa5849 330 maxEvent = fESDTree->GetEntries() - 1;
c2c4b7a2 331 } else if (fRawReader) {
332 maxEvent = 10000000;
333 Info(kEH, "number of events unknown for raw-data, setting max-event id to 10M.");
334 } else {
335 throw (kEH + "neither RunLoader, ESD nor Raw loaded.");
336 }
84aff7a4 337 if (event < 0 || event > maxEvent)
c2c4b7a2 338 throw (kEH + Form("event %d not present, available range [%d, %d].",
339 event, 0, maxEvent));
1eaa5849 340
84aff7a4 341 TEveManager::TRedrawDisabler rd(gEve);
342 gEve->Redraw3D(kFALSE, kTRUE); // Enforce drop of all logicals.
d9e0d6c5 343
32e219c2 344 // !!! MT this is somewhat brutal; at least optionally, one could be
345 // a bit gentler, checking for objs owning their external refs and having
346 // additinal parents.
1eaa5849 347 DestroyElements();
1eaa5849 348
84aff7a4 349 if (fRunLoader) {
c2c4b7a2 350 if (fRunLoader->GetEvent(event) != 0)
351 throw (kEH + "failed getting required event.");
1eaa5849 352 }
353
84aff7a4 354 if (fESDTree) {
c2c4b7a2 355 if (fESDTree->GetEntry(event) <= 0)
356 throw (kEH + "failed getting required event from ESD.");
1eaa5849 357
2cea771a 358 if (fESDfriendExists)
1eaa5849 359 fESD->SetESDfriend(fESDfriend);
1eaa5849 360 }
90fa773e 361
c2c4b7a2 362 if (fRawReader)
363 {
364 Int_t rawEv = fEventId;
365 if (event < rawEv)
366 {
367 fRawReader->RewindEvents();
368 rawEv = -1;
369 }
370
371 while (rawEv < event)
372 {
373 if ( ! fRawReader->NextEvent())
374 {
375 fRawReader->RewindEvents();
376 fEventId = -1;
377 throw (kEH + Form("Error going to next raw-event from event %d.", rawEv));
378 }
379 ++rawEv;
380 }
381
382 printf ("Loaded raw-event %d.\n", rawEv);
383 }
384
385 fEventId = event;
386 SetName(Form("Event %d", fEventId));
319f3084 387 ElementChanged();
c2c4b7a2 388
90fa773e 389 AfterNewEventLoaded();
1eaa5849 390}
391
d810d0de 392void AliEveEventManager::Close()
5a5a1232 393{
73c1c0ec 394 // Close the event files.
395 // For the moment only ESD is closed. Needs to be investigated for
396 // AliRunLoader and Raw.
397
2cea771a 398 if (fESDTree) {
399 delete fESD; fESD = 0;
400 delete fESDfriend; fESDfriend = 0;
401
402 delete fESDTree; fESDTree = 0;
403 delete fESDFile; fESDFile = 0;
404 }
5a5a1232 405}
406
90fa773e 407
57ffa5fb 408/******************************************************************************/
c76ea574 409// Static convenience functions, mainly used from macros.
57ffa5fb 410/******************************************************************************/
5a5a1232 411
d810d0de 412AliRunLoader* AliEveEventManager::AssertRunLoader()
5a5a1232 413{
73c1c0ec 414 // Make sure AliRunLoader is initialized and return it.
415 // Throws exception in case run-loader is not available.
416 // Static utility for macros.
417
a15e6d7d 418 static const TEveException kEH("AliEveEventManager::AssertRunLoader ");
5a5a1232 419
a15e6d7d 420 if (gAliEveEvent == 0)
c2c4b7a2 421 throw (kEH + "ALICE event not ready.");
a15e6d7d 422 if (gAliEveEvent->fRunLoader == 0)
c2c4b7a2 423 throw (kEH + "AliRunLoader not initialised.");
a15e6d7d 424 return gAliEveEvent->fRunLoader;
5a5a1232 425}
426
d810d0de 427AliESDEvent* AliEveEventManager::AssertESD()
5a5a1232 428{
73c1c0ec 429 // Make sure AliESDEvent is initialized and return it.
430 // Throws exception in case ESD is not available.
431 // Static utility for macros.
432
a15e6d7d 433 static const TEveException kEH("AliEveEventManager::AssertESD ");
5a5a1232 434
a15e6d7d 435 if (gAliEveEvent == 0)
c2c4b7a2 436 throw (kEH + "ALICE event not ready.");
a15e6d7d 437 if (gAliEveEvent->fESD == 0)
c2c4b7a2 438 throw (kEH + "AliESD not initialised.");
a15e6d7d 439 return gAliEveEvent->fESD;
5a5a1232 440}
3aecaefc 441
d810d0de 442AliESDfriend* AliEveEventManager::AssertESDfriend()
3aecaefc 443{
73c1c0ec 444 // Make sure AliESDfriend is initialized and return it.
445 // Throws exception in case ESDfriend-loader is not available.
446 // Static utility for macros.
447
a15e6d7d 448 static const TEveException kEH("AliEveEventManager::AssertESDfriend ");
3aecaefc 449
a15e6d7d 450 if (gAliEveEvent == 0)
c2c4b7a2 451 throw (kEH + "ALICE event not ready.");
a15e6d7d 452 if (gAliEveEvent->fESDfriend == 0)
c2c4b7a2 453 throw (kEH + "AliESDfriend not initialised.");
a15e6d7d 454 return gAliEveEvent->fESDfriend;
3aecaefc 455}
93845f6c 456
c2c4b7a2 457AliRawReader* AliEveEventManager::AssertRawReader()
458{
459 // Make sure raw-reader is initialized and return it.
460
461 static const TEveException kEH("AliEveEventManager::AssertRawReader ");
462
463 if (gAliEveEvent == 0)
464 throw (kEH + "ALICE event not ready.");
465 if (gAliEveEvent->fRawReader == 0)
466 throw (kEH + "RawReader not ready.");
467
468 return gAliEveEvent->fRawReader;
469}
470
d810d0de 471AliMagF* AliEveEventManager::AssertMagField()
93845f6c 472{
73c1c0ec 473 // Make sure AliMagF is initialized and return it.
474 // Throws exception in case magnetic field is not available.
475 // Static utility for macros.
476
93845f6c 477 if (fgMagField == 0)
478 {
a15e6d7d 479 if (gAliEveEvent && gAliEveEvent->fRunLoader && gAliEveEvent->fRunLoader->GetAliRun())
480 fgMagField = gAliEveEvent->fRunLoader->GetAliRun()->Field();
93845f6c 481 else
482 fgMagField = new AliMagFMaps("Maps","Maps", 1, 1., 10., AliMagFMaps::k5kG);
483 }
484 return fgMagField;
485}
632d2b03 486
d810d0de 487TGeoManager* AliEveEventManager::AssertGeometry()
632d2b03 488{
73c1c0ec 489 // Make sure AliGeomManager is initialized and returns the
490 // corresponding TGeoManger.
647814a2 491 // gGeoManager is set to the return value.
492 // Throws exception if geometry can not be loaded or if it is not
493 // available and the TGeoManager is locked.
73c1c0ec 494 // Static utility for macros.
495
a15e6d7d 496 static const TEveException kEH("AliEveEventManager::AssertGeometry ");
632d2b03 497
498 if (AliGeomManager::GetGeometry() == 0)
499 {
647814a2 500 if (TGeoManager::IsLocked())
501 throw (kEH + "geometry is not loaded but TGeoManager is locked.");
502
af2e4ef5 503 gGeoManager = 0;
632d2b03 504 AliGeomManager::LoadGeometry();
505 if ( ! AliGeomManager::GetGeometry())
506 {
c2c4b7a2 507 throw (kEH + "can not load geometry.");
632d2b03 508 }
509 if ( ! AliGeomManager::ApplyAlignObjsFromCDB("ITS TPC TRD TOF PHOS HMPID EMCAL MUON FMD ZDC PMD T0 VZERO ACORDE"))
510 {
a15e6d7d 511 ::Warning(kEH, "mismatch of alignable volumes. Proceeding.");
c2c4b7a2 512 // throw (kEH + "could not apply align objs.");
632d2b03 513 }
c2c4b7a2 514
647814a2 515 // @@NEWROOT@@ Temporary fix.
c2c4b7a2 516 // In AliEve several simplified geometries can be loaded at a later stage.
647814a2 517 // Locking/unlocking is now handled properly in
518 // TEveManager::GetGeometry() but we're waiting for next root
519 // version due on 14.5.2008.
c2c4b7a2 520 TGeoManager::UnlockGeometry();
632d2b03 521 }
522
647814a2 523 gGeoManager = AliGeomManager::GetGeometry();
524 return gGeoManager;
632d2b03 525}
319f3084 526
527void AliEveEventManager::SetAutoLoad(Bool_t autoLoad)
528{
529 // Set the automatic event loading mode
530 fAutoLoad = autoLoad;
531 StartStopAutoLoadTimer();
532}
533
534void AliEveEventManager::SetAutoLoadTime(Double_t time)
535{
536 // Set the auto-load time in seconds
537 fAutoLoadTime = time;
538 StartStopAutoLoadTimer();
539}
540
541void AliEveEventManager::StartStopAutoLoadTimer()
542{
543 // Create if needed and start
544 // the automatic event loading timer
545 if (fAutoLoad) {
546 if (!fAutoLoadTimer) {
547 fAutoLoadTimer = new TTimer;
548 fAutoLoadTimer->Connect("Timeout()","AliEveEventManager",this,"NextEvent()");
549 }
550 fAutoLoadTimer->Start((Long_t)fAutoLoadTime*1000,kTRUE);
551 }
552 else {
553 if (fAutoLoadTimer) fAutoLoadTimer->Stop();
554 }
555}
556
557void AliEveEventManager::PrevEvent()
558{
559 // Loads previous event
560 // only in case of manual mode
561 if (!fIsOnline) {
562 GotoEvent(fEventId - 1);
563 StartStopAutoLoadTimer();
564 }
565}
566
567void AliEveEventManager::NextEvent()
568{
569 // Loads next event
570 // either in automatic (online) or
571 // manual mode
572
573 if (fIsOnline) {
574 if (fAutoLoadTimer) fAutoLoadTimer->Stop();
575
576 DestroyElements();
577
578 gSystem->ExitLoop();
579 }
580 else {
581 GotoEvent(fEventId + 1);
582 StartStopAutoLoadTimer();
583 }
584}
585
586const char* AliEveEventManager::GetEventInfo() const
587{
588 // Dumps the event-header contents
589
590 static TString eventInfo;
591
592 if (!fRawReader) return "No event information is available";
593
594 const UInt_t* id = fRawReader->GetEventId();
595 const UInt_t* pattern = fRawReader->GetTriggerPattern();
596 const UInt_t* attr = fRawReader->GetAttributes();
597 eventInfo.Form("Run#: %d\nEvent type: %d\nPeriod: %x\nOrbit: %x\nBC: %x\nTrigger: %x-%x\nDetectors: %x\nAttributes:%x-%x-%x",
598 fRawReader->GetRunNumber(),fRawReader->GetType(),
599 (((id)[0]>>4)&0x0fffffff),((((id)[0]<<20)&0xf00000)|(((id)[1]>>12)&0xfffff)),((id)[1]&0x00000fff),
600 pattern[0],pattern[1],
601 *fRawReader->GetDetectorPattern(),
602 attr[0],attr[1],attr[2]);
603
604 return eventInfo.Data();
605}
606