1 /**************************************************************************
2 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
4 * Author: The ALICE Off-line Project. *
5 * Contributors are mentioned in the code where appropriate. *
7 * Permission to use, copy, modify and distribute this software and its *
8 * documentation strictly for non-commercial purposes is hereby granted *
9 * without fee, provided that the above copyright notice appears in all *
10 * copies and that both the copyright notice and this permission notice *
11 * appear in the supporting documentation. The authors make no claims *
12 * about the suitability of this software for any purpose. It is *
13 * provided "as is" without express or implied warranty. *
14 **************************************************************************/
18 Revision 1.18 2006/10/20 15:22:59 jgrosseo
19 o) Adding time out to the execution of the preprocessors: The Shuttle forks and the parent process monitors the child
20 o) Merging Collect, CollectAll, CollectNew function
21 o) Removing implementation of empty copy constructors (declaration still there!)
23 Revision 1.17 2006/10/05 16:20:55 jgrosseo
24 adapting to new CDB classes
26 Revision 1.16 2006/10/05 15:46:26 jgrosseo
27 applying to the new interface
29 Revision 1.15 2006/10/02 16:38:39 jgrosseo
32 storing of objects that failed to be stored to the grid before
33 interfacing of shuttle status table in daq system
35 Revision 1.14 2006/08/29 09:16:05 jgrosseo
38 Revision 1.13 2006/08/15 10:50:00 jgrosseo
39 effc++ corrections (alberto)
41 Revision 1.12 2006/08/08 14:19:29 jgrosseo
42 Update to shuttle classes (Alberto)
44 - Possibility to set the full object's path in the Preprocessor's and
45 Shuttle's Store functions
46 - Possibility to extend the object's run validity in the same classes
47 ("startValidity" and "validityInfinite" parameters)
48 - Implementation of the StoreReferenceData function to store reference
49 data in a dedicated CDB storage.
51 Revision 1.11 2006/07/21 07:37:20 jgrosseo
52 last run is stored after each run
54 Revision 1.10 2006/07/20 09:54:40 jgrosseo
55 introducing status management: The processing per subdetector is divided into several steps,
56 after each step the status is stored on disk. If the system crashes in any of the steps the Shuttle
57 can keep track of the number of failures and skips further processing after a certain threshold is
58 exceeded. These thresholds can be configured in LDAP.
60 Revision 1.9 2006/07/19 10:09:55 jgrosseo
61 new configuration, accesst to DAQ FES (Alberto)
63 Revision 1.8 2006/07/11 12:44:36 jgrosseo
64 adding parameters for extended validity range of data produced by preprocessor
66 Revision 1.7 2006/07/10 14:37:09 jgrosseo
67 small fix + todo comment
69 Revision 1.6 2006/07/10 13:01:41 jgrosseo
70 enhanced storing of last sucessfully processed run (alberto)
72 Revision 1.5 2006/07/04 14:59:57 jgrosseo
73 revision of AliDCSValue: Removed wrapper classes, reduced storage size per value by factor 2
75 Revision 1.4 2006/06/12 09:11:16 jgrosseo
76 coding conventions (Alberto)
78 Revision 1.3 2006/06/06 14:26:40 jgrosseo
79 o) removed files that were moved to STEER
80 o) shuttle updated to follow the new interface (Alberto)
82 Revision 1.2 2006/03/07 07:52:34 hristov
83 New version (B.Yordanov)
85 Revision 1.6 2005/11/19 17:19:14 byordano
86 RetrieveDATEEntries and RetrieveConditionsData added
88 Revision 1.5 2005/11/19 11:09:27 byordano
89 AliShuttle declaration added
91 Revision 1.4 2005/11/17 17:47:34 byordano
92 TList changed to TObjArray
94 Revision 1.3 2005/11/17 14:43:23 byordano
97 Revision 1.1.1.1 2005/10/28 07:33:58 hristov
98 Initial import as subdirectory in AliRoot
100 Revision 1.2 2005/09/13 08:41:15 byordano
101 default startTime endTime added
103 Revision 1.4 2005/08/30 09:13:02 byordano
106 Revision 1.3 2005/08/29 21:15:47 byordano
112 // This class is the main manager for AliShuttle.
113 // It organizes the data retrieval from DCS and call the
114 // interface methods of AliPreprocessor.
115 // For every detector in AliShuttleConfgi (see AliShuttleConfig),
116 // data for its set of aliases is retrieved. If there is registered
117 // AliPreprocessor for this detector then it will be used
118 // accroding to the schema (see AliPreprocessor).
119 // If there isn't registered AliPreprocessor than the retrieved
120 // data is stored automatically to the undelying AliCDBStorage.
121 // For detSpec is used the alias name.
124 #include "AliShuttle.h"
126 #include "AliCDBManager.h"
127 #include "AliCDBStorage.h"
128 #include "AliCDBId.h"
129 #include "AliCDBRunRange.h"
130 #include "AliCDBPath.h"
131 #include "AliCDBEntry.h"
132 #include "AliShuttleConfig.h"
133 #include "DCSClient/AliDCSClient.h"
135 #include "AliPreprocessor.h"
136 #include "AliShuttleStatus.h"
137 #include "AliShuttleLogbookEntry.h"
142 #include <TTimeStamp.h>
143 #include <TObjString.h>
144 #include <TSQLServer.h>
145 #include <TSQLResult.h>
151 #include <sys/types.h>
152 #include <sys/wait.h>
156 TString AliShuttle::fgkMainCDB("alien://folder=ShuttleCDB");
157 TString AliShuttle::fgkLocalCDB("local://LocalShuttleCDB");
158 TString AliShuttle::fgkMainRefStorage("alien://folder=ShuttleReference");
159 TString AliShuttle::fgkLocalRefStorage("local://LocalReferenceStorage");
161 Bool_t AliShuttle::fgkProcessDCS(kTRUE);
163 const char* AliShuttle::fgkShuttleTempDir = gSystem->ExpandPathName("$ALICE_ROOT/SHUTTLE/temp");
164 const char* AliShuttle::fgkShuttleLogDir = gSystem->ExpandPathName("$ALICE_ROOT/SHUTTLE/log");
166 //______________________________________________________________________________________________
167 AliShuttle::AliShuttle(const AliShuttleConfig* config,
168 UInt_t timeout, Int_t retries):
170 fTimeout(timeout), fRetries(retries),
181 // config: AliShuttleConfig used
182 // timeout: timeout used for AliDCSClient connection
183 // retries: the number of retries in case of connection error.
186 if (!fConfig->IsValid()) AliFatal("********** !!!!! Invalid configuration !!!!! **********");
187 for(int iSys=0;iSys<3;iSys++) {
189 fFESlist[iSys].SetOwner(kTRUE);
191 fPreprocessorMap.SetOwner(kTRUE);
193 fMonitoringMutex = new TMutex();
196 //______________________________________________________________________________________________
197 AliShuttle::~AliShuttle()
201 fPreprocessorMap.DeleteAll();
202 for(int iSys=0;iSys<3;iSys++)
204 fServer[iSys]->Close();
205 delete fServer[iSys];
214 if (fMonitoringMutex)
216 delete fMonitoringMutex;
217 fMonitoringMutex = 0;
221 //______________________________________________________________________________________________
222 void AliShuttle::RegisterPreprocessor(AliPreprocessor* preprocessor)
225 // Registers new AliPreprocessor.
226 // It uses GetName() for indentificator of the pre processor.
227 // The pre processor is registered it there isn't any other
228 // with the same identificator (GetName()).
231 const char* detName = preprocessor->GetName();
232 if(GetDetPos(detName) < 0)
233 AliFatal(Form("********** !!!!! Invalid detector name: %s !!!!! **********", detName));
235 if (fPreprocessorMap.GetValue(detName)) {
236 AliWarning(Form("AliPreprocessor %s is already registered!", detName));
240 fPreprocessorMap.Add(new TObjString(detName), preprocessor);
242 //______________________________________________________________________________________________
243 UInt_t AliShuttle::Store(const AliCDBPath& path, TObject* object,
244 AliCDBMetaData* metaData, Int_t validityStart, Bool_t validityInfinite)
246 // Stores a CDB object in the storage for offline reconstruction. Objects that are not needed for
247 // offline reconstruction, but should be stored anyway (e.g. for debugging) should NOT be stored
248 // using this function. Use StoreReferenceData instead!
249 // It calls WriteToCDB function which perform actual storage
251 return WriteToCDB(fgkMainCDB, fgkLocalCDB, path, object,
252 metaData, validityStart, validityInfinite);
256 //______________________________________________________________________________________________
257 UInt_t AliShuttle::StoreReferenceData(const AliCDBPath& path, TObject* object, AliCDBMetaData* metaData)
259 // Stores a CDB object in the storage for reference data. This objects will not be available during
260 // offline reconstrunction. Use this function for reference data only!
261 // It calls WriteToCDB function which perform actual storage
263 return WriteToCDB(fgkMainRefStorage, fgkLocalRefStorage, path, object, metaData);
267 //______________________________________________________________________________________________
268 UInt_t AliShuttle::WriteToCDB(const char* mainUri, const char* localUri,
269 const AliCDBPath& path, TObject* object, AliCDBMetaData* metaData,
270 Int_t validityStart, Bool_t validityInfinite)
272 // write object into the CDB. Parameters are passed by Store and StoreReferenceData functions.
273 // The parameters are:
274 // 1) Uri of the main storage (Grid)
275 // 2) Uri of the backup storage (Local)
276 // 3) the object's path.
277 // 4) the object to be stored
278 // 5) the metaData to be associated with the object
279 // 6) the validity start run number w.r.t. the current run,
280 // if the data is valid only for this run leave the default 0
281 // 7) specifies if the calibration data is valid for infinity (this means until updated),
282 // typical for calibration runs, the default is kFALSE
285 // 1 if stored in main (Grid) storage
286 // 2 if stored in backup (Local) storage
288 const char* cdbType = (mainUri == fgkMainCDB) ? "CDB" : "Reference";
290 Int_t firstRun = GetCurrentRun() - validityStart;
292 AliError("First valid run happens to be less than 0! Setting it to 0.");
297 if(validityInfinite) {
298 lastRun = AliCDBRunRange::Infinity();
300 lastRun = GetCurrentRun();
303 AliCDBId id(path, firstRun, lastRun, -1, -1);
305 if(! dynamic_cast<TObjString*> (metaData->GetProperty("RunUsed(TObjString)"))){
306 TObjString runUsed = Form("%d", GetCurrentRun());
307 metaData->SetProperty("RunUsed(TObjString)",&runUsed);
312 if (!(AliCDBManager::Instance()->GetStorage(mainUri))) {
313 AliError(Form("WriteToCDB - Cannot activate main %s storage", cdbType));
315 result = (UInt_t) AliCDBManager::Instance()->GetStorage(mainUri)
316 ->Put(object, id, metaData);
321 Log(fCurrentDetector,
322 Form("WriteToCDB - Problem with main %s storage. Putting <%s> into backup storage",
323 cdbType, path.GetPath().Data()));
325 // Set Grid version to current run number, to ease retrieval later
326 id.SetVersion(GetCurrentRun());
328 result = AliCDBManager::Instance()->GetStorage(localUri)
329 ->Put(object, id, metaData);
335 Log(fCurrentDetector, "WriteToCDB - Can't store data!");
343 //______________________________________________________________________________________________
344 AliShuttleStatus* AliShuttle::ReadShuttleStatus()
346 // Reads the AliShuttleStatus from the CDB
353 fStatusEntry = AliCDBManager::Instance()->GetStorage(AliShuttle::GetLocalCDB())
354 ->Get(Form("/SHUTTLE/STATUS/%s", fCurrentDetector.Data()), GetCurrentRun());
356 if (!fStatusEntry) return 0;
357 fStatusEntry->SetOwner(1);
359 AliShuttleStatus* status = dynamic_cast<AliShuttleStatus*> (fStatusEntry->GetObject());
361 AliError("Invalid object stored to CDB!");
368 //______________________________________________________________________________________________
369 Bool_t AliShuttle::WriteShuttleStatus(AliShuttleStatus* status)
371 // writes the status for one subdetector
378 Int_t run = GetCurrentRun();
380 AliCDBId id(AliCDBPath("SHUTTLE", "STATUS", fCurrentDetector), run, run);
382 fStatusEntry = new AliCDBEntry(status, id, new AliCDBMetaData);
383 fStatusEntry->SetOwner(1);
385 UInt_t result = AliCDBManager::Instance()->GetStorage(fgkLocalCDB)->Put(fStatusEntry);
388 AliError(Form("WriteShuttleStatus for %s, run %d failed", fCurrentDetector.Data(), run));
395 //______________________________________________________________________________________________
396 void AliShuttle::UpdateShuttleStatus(AliShuttleStatus::Status newStatus, Bool_t increaseCount)
398 // changes the AliShuttleStatus for the given detector and run to the given status
401 AliError("UNEXPECTED: fStatusEntry empty");
405 AliShuttleStatus* status = dynamic_cast<AliShuttleStatus*> (fStatusEntry->GetObject());
408 AliError("UNEXPECTED: status could not be read from current CDB entry");
412 TString actionStr = Form("UpdateShuttleStatus - %s: Changing state from %s to %s",
413 fCurrentDetector.Data(),
414 status->GetStatusName(),
415 status->GetStatusName(newStatus));
416 Log("SHUTTLE", actionStr);
417 SetLastAction(actionStr);
419 status->SetStatus(newStatus);
420 if (increaseCount) status->IncreaseCount();
422 AliCDBManager::Instance()->GetStorage(fgkLocalCDB)->Put(fStatusEntry);
424 //______________________________________________________________________________________________
425 Bool_t AliShuttle::ContinueProcessing()
427 // this function reads the AliShuttleStatus information from CDB and
428 // checks if the processing should be continued
429 // if yes it returns kTRUE and updates the AliShuttleStatus with nextStatus
431 AliShuttleLogbookEntry::Status entryStatus =
432 fLogbookEntry->GetDetectorStatus(fCurrentDetector);
434 if(entryStatus != AliShuttleLogbookEntry::kUnprocessed) {
435 Log("SHUTTLE", Form("ContinueProcessing - %s is %s",
436 fCurrentDetector.Data(),
437 fLogbookEntry->GetDetectorStatusName(entryStatus)));
441 // if we get here, according to Shuttle logbook subdetector is in UNPROCESSED state
442 AliShuttleStatus* status = ReadShuttleStatus();
445 Log("SHUTTLE", Form("ContinueProcessing - %s: Processing first time",
446 fCurrentDetector.Data()));
447 status = new AliShuttleStatus(AliShuttleStatus::kStarted);
448 return WriteShuttleStatus(status);
451 // The following two cases shouldn't happen if Shuttle Logbook was correctly updated.
452 // If it happens it may mean Logbook updating failed... let's do it now!
453 if (status->GetStatus() == AliShuttleStatus::kDone ||
454 status->GetStatus() == AliShuttleStatus::kFailed){
455 Log("SHUTTLE", Form("ContinueProcessing - %s is already %s. Updating Shuttle Logbook",
456 fCurrentDetector.Data(),
457 status->GetStatusName(status->GetStatus())));
458 UpdateShuttleLogbook(fCurrentDetector.Data(),
459 status->GetStatusName(status->GetStatus()));
463 if (status->GetStatus() == AliShuttleStatus::kStoreFailed) {
465 Form("ContinueProcessing - %s: Grid storage of one or more objects failed. Trying again now",
466 fCurrentDetector.Data()));
467 if(TryToStoreAgain()){
468 Log(fCurrentDetector.Data(), "ContinueProcessing - All objects successfully stored into OCDB");
469 UpdateShuttleStatus(AliShuttleStatus::kDone);
470 UpdateShuttleLogbook(fCurrentDetector.Data(), "DONE");
473 Form("ContinueProcessing - %s: Grid storage failed again",
474 fCurrentDetector.Data()));
479 // if we get here, there is a restart
482 if (status->GetCount() >= fConfig->GetMaxRetries()) {
484 Form("ContinueProcessing - %s failed %d times in status %s - Updating Shuttle Logbook",
485 fCurrentDetector.Data(),
486 status->GetCount(), status->GetStatusName()));
487 UpdateShuttleLogbook(fCurrentDetector.Data(), "FAILED");
491 Log("SHUTTLE", Form("ContinueProcessing - %s: restarting. Aborted before with %s. Retry number %d.",
492 fCurrentDetector.Data(),
493 status->GetStatusName(), status->GetCount()));
495 UpdateShuttleStatus(AliShuttleStatus::kStarted, kTRUE);
500 //______________________________________________________________________________________________
501 Bool_t AliShuttle::Process(AliShuttleLogbookEntry* entry)
504 // Makes data retrieval for all detectors in the configuration.
505 // entry: Shuttle logbook entry, contains run paramenters and status of detectors
506 // (Unprocessed, Inactive, Failed or Done).
507 // Returns kFALSE in case of error occured and kTRUE otherwise
510 if(!entry) return kFALSE;
512 fLogbookEntry = entry;
514 if(fLogbookEntry->IsDone()){
515 Log("SHUTTLE","Process - Shuttle is already DONE. Updating logbook");
516 UpdateShuttleLogbook("shuttle_done");
522 AliInfo(Form("\n\n \t\t\t^*^*^*^*^*^*^*^*^*^*^*^* run %d: START ^*^*^*^*^*^*^*^*^*^*^*^* \n",
525 fLogbookEntry->Print("all");
528 Bool_t hasError = kFALSE;
529 for(Int_t iSys=0;iSys<3;iSys++) fFESCalled[iSys]=kFALSE;
531 AliCDBStorage *mainCDBSto = AliCDBManager::Instance()->GetStorage(fgkMainCDB);
532 if(mainCDBSto) mainCDBSto->QueryCDB(GetCurrentRun());
533 AliCDBStorage *mainRefSto = AliCDBManager::Instance()->GetStorage(fgkMainRefStorage);
534 if(mainRefSto) mainRefSto->QueryCDB(GetCurrentRun());
536 // Loop on detectors in the configuration
537 TIter iter(fConfig->GetDetectors());
538 TObjString* aDetector = 0;
540 while ((aDetector = (TObjString*) iter.Next())) {
541 fCurrentDetector = aDetector->String();
543 if (!fConfig->HostProcessDetector(fCurrentDetector)) continue;
545 AliPreprocessor* aPreprocessor =
546 dynamic_cast<AliPreprocessor*> (fPreprocessorMap.GetValue(fCurrentDetector));
548 Log("SHUTTLE",Form("Process - %s: no preprocessor registered. Skipping",
549 fCurrentDetector.Data()));
553 if (ContinueProcessing() == kFALSE) continue;
555 AliInfo(Form("\n\n \t\t\t****** run %d - %s: START ******",
556 GetCurrentRun(), aDetector->GetName()));
563 Log("SHUTTLE", "ERROR: Forking failed");
568 AliInfo(Form("In parent process of %d - %s: Starting monitoring", GetCurrentRun(), aDetector->GetName()));
570 Long_t begin = time(0);
572 int status; // to be used with waitpid, on purpose an int (not Int_t)!
573 while (waitpid(pid, &status, WNOHANG) == 0)
575 Long_t expiredTime = time(0) - begin;
577 if (expiredTime > fConfig->GetPPTimeOut())
579 Log("SHUTTLE", Form("Process time out. Run time: %d seconds. Killing...", expiredTime));
585 gSystem->Sleep(1000);
589 if (expiredTime % 60 == 0)
590 Log("SHUTTLE", Form("Checked process. Run time: %d seconds.", expiredTime));
592 gSystem->Sleep(1000);
596 AliInfo(Form("In parent process of %d - %s: Client has terminated.", GetCurrentRun(), aDetector->GetName()));
598 if (WIFEXITED(status))
600 Int_t returnCode = WEXITSTATUS(status);
602 Log("SHUTTLE", Form("The return code is %d", returnCode));
611 AliInfo(Form("In client process of %d - %s", GetCurrentRun(), aDetector->GetName()));
613 UInt_t result = ProcessCurrentDetector();
615 Int_t returnCode = 0; // will be set to 1 in case of an error
619 AliInfo(Form("\n \t\t\t****** run %d - %s: PREPROCESSOR ERROR ****** \n\n",
620 GetCurrentRun(), aDetector->GetName()));
622 else if(result == 2) {
623 AliInfo(Form("\n \t\t\t****** run %d - %s: STORAGE ERROR ****** \n\n",
624 GetCurrentRun(), aDetector->GetName()));
626 AliInfo(Form("\n \t\t\t****** run %d - %s: DONE ****** \n\n",
627 GetCurrentRun(), aDetector->GetName()));
632 // Process successful: Update time_processed field in FES logbooks!
633 if(fFESCalled[kDAQ]) {
634 if (UpdateDAQTable() == kFALSE)
636 fFESlist[kDAQ].Clear();
638 //if(fFESCalled[kDCS]) {
639 // if (UpdateDCSTable(aDetector->GetName()) == kFALSE)
641 // fFESlist[kDCS].Clear();
643 //if(fFESCalled[kHLT]) {
644 // if (UpdateHLTTable(aDetector->GetName()) == kFALSE)
646 // fFESlist[kHLT].Clear();
650 AliInfo(Form("Client process of %d - %s is exiting now with %d.", GetCurrentRun(), aDetector->GetName(), returnCode));
652 // the client exits here
653 gSystem->Exit(returnCode);
655 AliError("We should never get here!!!");
659 AliInfo(Form("\n\n \t\t\t^*^*^*^*^*^*^*^*^*^*^*^* run %d: FINISH ^*^*^*^*^*^*^*^*^*^*^*^* \n",
662 //check if shuttle is done for this run, if so update logbook
663 TObjArray checkEntryArray;
664 checkEntryArray.SetOwner(1);
665 TString whereClause = Form("where run=%d",GetCurrentRun());
666 if(QueryShuttleLogbook(whereClause.Data(), checkEntryArray)) {
668 AliShuttleLogbookEntry* checkEntry = dynamic_cast<AliShuttleLogbookEntry*>
669 (checkEntryArray.At(0));
671 if(checkEntry && checkEntry->IsDone()){
672 Log("SHUTTLE","Process - Shuttle is DONE. Updating logbook");
673 UpdateShuttleLogbook("shuttle_done");
679 return hasError == kFALSE;
682 //______________________________________________________________________________________________
683 UInt_t AliShuttle::ProcessCurrentDetector()
686 // Makes data retrieval just for a specific detector (fCurrentDetector).
687 // Threre should be a configuration for this detector.
689 AliInfo(Form("Retrieving values for %s, run %d", fCurrentDetector.Data(), GetCurrentRun()));
691 UpdateShuttleStatus(AliShuttleStatus::kDCSStarted);
693 TString host(fConfig->GetDCSHost(fCurrentDetector));
694 Int_t port = fConfig->GetDCSPort(fCurrentDetector);
696 TIter iter(fConfig->GetDCSAliases(fCurrentDetector));
699 aliasMap.SetOwner(1);
701 Bool_t aDCSError = kFALSE;
704 while ((anAlias = (TObjString*) iter.Next())) {
705 TObjArray *valueSet = new TObjArray();
706 valueSet->SetOwner(1);
707 // TODO Test only... I've added a flag that allows to
708 // exclude DCS archive DB query
710 AliInfo("Querying DCS archive DB data...");
711 aDCSError = (GetValueSet(host, port, anAlias->String(), valueSet) == 0);
713 AliInfo(Form("Skipping DCS processing. Port = %d",port));
717 aliasMap.Add(anAlias->Clone(), valueSet);
719 Log(fCurrentDetector, Form("ProcessCurrentDetector - Error while retrieving alias %s",
720 anAlias->GetName()));
721 UpdateShuttleStatus(AliShuttleStatus::kDCSError, kTRUE);
722 aliasMap.DeleteAll();
727 // DCS Archive DB processing successful. Call Preprocessor!
728 UpdateShuttleStatus(AliShuttleStatus::kPPStarted);
730 AliPreprocessor* aPreprocessor =
731 dynamic_cast<AliPreprocessor*> (fPreprocessorMap.GetValue(fCurrentDetector));
733 aPreprocessor->Initialize(GetCurrentRun(), GetCurrentStartTime(), GetCurrentEndTime());
734 UInt_t aPPResult = aPreprocessor->Process(&aliasMap);
736 UInt_t returnValue = 0;
737 if (aPPResult == 0) { // Preprocessor error
738 UpdateShuttleStatus(AliShuttleStatus::kPPError);
740 } else if (fGridError == kFALSE) { // process and Grid storage ok!
741 UpdateShuttleStatus(AliShuttleStatus::kDone);
742 UpdateShuttleLogbook(fCurrentDetector, "DONE");
743 Log(fCurrentDetector.Data(),
744 "ProcessCurrentDetector - Preprocessor and Grid storage ended successfully");
746 } else { // Grid storage error (process ok, but object put in local storage)
747 UpdateShuttleStatus(AliShuttleStatus::kStoreFailed);
751 aliasMap.DeleteAll();
756 //______________________________________________________________________________________________
757 Bool_t AliShuttle::QueryShuttleLogbook(const char* whereClause,
760 // Query DAQ's Shuttle logbook and fills detector status object.
761 // Call QueryRunParameters to query DAQ logbook for run parameters.
763 // check connection, in case connect
764 if(!Connect(kDAQ)) return kFALSE;
767 sqlQuery = Form("select * from logbook_shuttle %s order by run", whereClause);
769 TSQLResult* aResult = fServer[kDAQ]->Query(sqlQuery);
771 AliError(Form("Can't execute query <%s>!", sqlQuery.Data()));
775 if(aResult->GetRowCount() == 0) {
776 if(sqlQuery.Contains("where shuttle_done=0")){
777 Log("SHUTTLE", "QueryShuttleLogbook - All runs in Shuttle Logbook are already DONE");
781 AliError("No entries in Shuttle Logbook match request");
787 // TODO Check field count!
788 const UInt_t nCols = 24;
789 if (aResult->GetFieldCount() != (Int_t) nCols) {
790 AliError("Invalid SQL result field number!");
798 while ((aRow = aResult->Next())) {
799 TString runString(aRow->GetField(0), aRow->GetFieldLength(0));
800 Int_t run = runString.Atoi();
802 AliShuttleLogbookEntry *entry = QueryRunParameters(run);
807 for(UInt_t ii = 0; ii < nCols; ii++)
808 entry->SetDetectorStatus(aResult->GetFieldName(ii), aRow->GetField(ii));
810 entries.AddLast(entry);
814 if(sqlQuery.Contains("where shuttle_done=0"))
815 Log("SHUTTLE", Form("QueryShuttleLogbook - Found %d unprocessed runs in Shuttle Logbook",
816 entries.GetEntriesFast()));
821 //______________________________________________________________________________________________
822 AliShuttleLogbookEntry* AliShuttle::QueryRunParameters(Int_t run)
825 // Retrieve run parameters written in the DAQ logbook and sets them into AliShuttleLogbookEntry object
828 // check connection, in case connect
833 sqlQuery.Form("select * from logbook where run=%d", run);
835 TSQLResult* aResult = fServer[kDAQ]->Query(sqlQuery);
837 AliError(Form("Can't execute query <%s>!", sqlQuery.Data()));
841 if (aResult->GetRowCount() == 0) {
842 Log("SHUTTLE", Form("QueryRunParameters - No entry in DAQ Logbook for run %d. Skipping", run));
847 if (aResult->GetRowCount() > 1) {
848 AliError(Form("More than one entry in DAQ Logbook for run %d. Skipping", run));
853 TSQLRow* aRow = aResult->Next();
856 AliError(Form("Could not retrieve row for run %d. Skipping", run));
861 AliShuttleLogbookEntry* entry = new AliShuttleLogbookEntry(run);
863 for (Int_t ii = 0; ii < aResult->GetFieldCount(); ii++)
864 entry->SetRunParameter(aResult->GetFieldName(ii), aRow->GetField(ii));
866 UInt_t startTime = entry->GetStartTime();
867 UInt_t endTime = entry->GetEndTime();
869 if (!startTime || !endTime || startTime > endTime) {
871 Form("QueryRunParameters - Invalid parameters for Run %d: startTime = %d, endTime = %d",
872 run, startTime, endTime));
885 //______________________________________________________________________________________________
886 Bool_t AliShuttle::TryToStoreAgain()
888 // Called in case the detector failed to store the object in Grid OCDB
889 // It tries to store the object again, if it does not find more recent and overlapping objects
890 // Calls underlying TryToStoreAgain(const char*) function twice, for OCDB and Reference storage.
892 AliInfo("Trying to store OCDB data again...");
893 Bool_t resultCDB = TryToStoreAgain(fgkMainCDB);
895 AliInfo("Trying to store reference data again...");
896 Bool_t resultRef = TryToStoreAgain(fgkMainRefStorage);
898 return resultCDB && resultRef;
901 //______________________________________________________________________________________________
902 Bool_t AliShuttle::TryToStoreAgain(TString& gridURI)
904 // Called by TryToStoreAgain(), performs actual storage retry
906 TObjArray* gridIds=0;
908 Bool_t result = kTRUE;
910 const char* type = 0;
912 if(gridURI == fgkMainCDB) {
914 backupURI = fgkLocalCDB;
915 } else if(gridURI == fgkMainRefStorage) {
917 backupURI = fgkLocalRefStorage;
919 AliError(Form("Invalid storage URI: %s", gridURI.Data()));
923 AliCDBManager* man = AliCDBManager::Instance();
925 AliCDBStorage *gridSto = man->GetStorage(gridURI);
927 Log(fCurrentDetector.Data(),
928 Form("TryToStoreAgain - cannot activate main %s storage", type));
932 gridIds = gridSto->GetQueryCDBList();
934 // get objects previously stored in local CDB
935 AliCDBStorage *backupSto = man->GetStorage(backupURI);
936 AliCDBPath aPath(GetOfflineDetName(fCurrentDetector.Data()),"*","*");
937 // Local objects were stored with current run as Grid version!
938 TList* localEntries = backupSto->GetAll(aPath.GetPath(), GetCurrentRun(), GetCurrentRun());
939 localEntries->SetOwner(1);
941 // loop on local stored objects
942 TIter localIter(localEntries);
943 AliCDBEntry *aLocEntry = 0;
944 while((aLocEntry = dynamic_cast<AliCDBEntry*> (localIter.Next()))){
945 aLocEntry->SetOwner(1);
946 AliCDBId aLocId = aLocEntry->GetId();
947 aLocEntry->SetVersion(-1);
948 aLocEntry->SetSubVersion(-1);
950 // loop on Grid valid Id's
951 Bool_t store = kTRUE;
952 TIter gridIter(gridIds);
953 AliCDBId* aGridId = 0;
954 while((aGridId = dynamic_cast<AliCDBId*> (gridIter.Next()))){
955 // If local object is valid up to infinity we store it anyway
956 // TODO This does not work! It may hide more recent objects...
957 if(aLocId.GetLastRun() == AliCDBRunRange::Infinity()) {
958 // TODO Check that it won't hide more recent files! how????
961 if(aGridId->GetPath() != aLocId.GetPath()) continue;
962 // skip all objects valid up to infinity
963 if(aGridId->GetLastRun() == AliCDBRunRange::Infinity()) continue;
964 // if we get here, it means there's already some more recent object stored on Grid!
970 Log(fCurrentDetector.Data(),
971 Form("TryToStoreAgain - A more recent object already exists in %s storage: <%s>",
972 type, aGridId->ToString().Data()));
973 // removing local filename...
974 // TODO maybe it's better not to remove it, it was not copied to the Grid!
976 backupSto->IdToFilename(aLocId, filename);
977 AliInfo(Form("Removing local file %s", filename.Data()));
978 gSystem->Exec(Form("rm %s",filename.Data()));
982 // If we get here, the file can be stored!
983 Bool_t storeOk = gridSto->Put(aLocEntry);
985 Log(fCurrentDetector.Data(),
986 Form("TryToStoreAgain - Object <%s> successfully put into %s storage",
987 aLocId.ToString().Data(), type));
989 // removing local filename...
991 backupSto->IdToFilename(aLocId, filename);
992 AliInfo(Form("Removing local file %s", filename.Data()));
993 gSystem->Exec(Form("rm %s", filename.Data()));
996 Log(fCurrentDetector.Data(),
997 Form("TryToStoreAgain - Grid %s storage of object <%s> failed again",
998 type, aLocId.ToString().Data()));
1002 localEntries->Clear();
1007 //______________________________________________________________________________________________
1008 Bool_t AliShuttle::GetValueSet(const char* host, Int_t port, const char* alias,
1009 TObjArray* valueSet)
1011 // Retrieve all "alias" data points from the DCS server
1012 // host, port: TSocket connection parameters
1013 // alias: name of the alias
1014 // valueSet: array of retrieved AliDCSValue's
1016 AliDCSClient client(host, port, fTimeout, fRetries);
1017 if (!client.IsConnected()) {
1021 Int_t result = client.GetAliasValues(alias,
1022 GetCurrentStartTime(), GetCurrentEndTime(), valueSet);
1025 Log(fCurrentDetector.Data(), Form("GetValueSet - Can't get '%s'! Reason: %s",
1026 alias, AliDCSClient::GetErrorString(result)));
1028 if (result == AliDCSClient::fgkServerError) {
1029 Log(fCurrentDetector.Data(), Form("GetValueSet - Server error: %s",
1030 client.GetServerError().Data()));
1039 //______________________________________________________________________________________________
1040 const char* AliShuttle::GetFile(Int_t system, const char* detector,
1041 const char* id, const char* source)
1043 // Get calibration file from file exchange servers
1044 // calls specific getter according to system index (kDAQ, kDCS, kHLT)
1048 return GetDAQFileName(detector, id, source);
1051 return GetDCSFileName(detector, id, source);
1054 return GetHLTFileName(detector, id, source);
1057 AliError(Form("No valid system index: %d",system));
1063 //______________________________________________________________________________________________
1064 TList* AliShuttle::GetFileSources(Int_t system, const char* detector, const char* id)
1066 // Get sources producing the condition file Id from file exchange servers
1067 // calls specific getter according to system index (kDAQ, kDCS, kHLT)
1071 return GetDAQFileSources(detector, id);
1074 return GetDCSFileSources(detector, id);
1077 return GetHLTFileSources(detector, id);
1080 AliError(Form("No valid system index: %d",system));
1086 //______________________________________________________________________________________________
1087 Bool_t AliShuttle::Connect(Int_t system)
1089 // Connect to MySQL Server of the system's FES logbook
1090 // DAQ Logbook, Shuttle Logbook and DAQ FES Logbook are on the same host
1092 // check connection: if already connected return
1093 if(fServer[system] && fServer[system]->IsConnected()) return kTRUE;
1095 TString aFESlbHost= Form("mysql://%s", fConfig->GetFESlbHost(system));
1097 fServer[system] = TSQLServer::Connect(aFESlbHost,
1098 fConfig->GetFESlbUser(system),
1099 fConfig->GetFESlbPass(system));
1100 if (!fServer[system] || !fServer[system]->IsConnected()) {
1101 AliError(Form("Can't establish connection to FES logbook for %s",
1102 AliShuttleInterface::GetSystemName(system)));
1103 if(fServer[system]) delete fServer[system];
1108 // TODO in the configuration should the table name be there too?
1109 TSQLResult* aResult=0;
1112 aResult = fServer[kDAQ]->GetTables("REFSYSLOG");
1115 //aResult = fServer[kDCS]->GetTables("REFSYSLOG");
1118 //aResult = fServer[kHLT]->GetTables("REFSYSLOG");
1128 //______________________________________________________________________________________________
1129 const char* AliShuttle::GetDAQFileName(const char* detector, const char* id, const char* source)
1131 // Retrieves a file from the DAQ FES.
1132 // First queris the DAQ logbook_fs for the DAQ file name, using the run, detector, id and source info
1133 // then calls RetrieveDAQFile(DAQfilename) for actual copy to local disk
1134 // run: current run being processed (given by Logbook entry fLogbookEntry)
1135 // detector: the Preprocessor name
1136 // id: provided as a parameter by the Preprocessor
1137 // source: provided by the Preprocessor through GetFileSources function
1139 // check connection, in case connect
1142 Log(detector, "GetDAQFileName - Couldn't connect to DAQ Logbook");
1146 // Query preparation
1147 TString sqlQueryStart = "select filePath from logbook_fs where";
1148 TString whereClause = Form("run=%d and detector=\"%s\" and fileId=\"%s\" and DAQsource=\"%s\"",
1149 GetCurrentRun(), detector, id, source);
1150 TString sqlQuery = Form("%s %s", sqlQueryStart.Data(), whereClause.Data());
1152 AliDebug(2, Form("SQL query: \n%s",sqlQuery.Data()));
1155 TSQLResult* aResult = 0;
1156 aResult = dynamic_cast<TSQLResult*> (fServer[kDAQ]->Query(sqlQuery));
1158 Log(detector, Form("GetDAQFileName - Can't execute SQL query for: id = %s, source = %s",
1163 if(aResult->GetRowCount() == 0)
1166 Form("GetDAQFileName - No entry in FES table for: id = %s, source = %s",
1172 if (aResult->GetRowCount() > 1) {
1174 Form("GetDAQFileName - More than one entry in FES table for: id = %s, source = %s",
1180 TSQLRow* aRow = dynamic_cast<TSQLRow*> (aResult->Next());
1183 Log(detector, Form("GetDAQFileName - Empty set result from query: id = %s, source = %s",
1189 TString filePath(aRow->GetField(0), aRow->GetFieldLength(0));
1194 AliDebug(2, Form("filePath = %s",filePath.Data()));
1196 // retrieved file is renamed to make it unique
1197 TString localFileName = Form("%s_%d_%s_%s.shuttle",
1198 detector, GetCurrentRun(), id, source);
1200 // file retrieval from DAQ FES
1201 Bool_t result = RetrieveDAQFile(filePath.Data(), localFileName.Data());
1203 Log(detector, Form("GetDAQFileName - Copy of file %s from DAQ FES failed", filePath.Data()));
1206 AliInfo(Form("File %s copied from DAQ FES into %s/%s",
1207 filePath.Data(), fgkShuttleTempDir, localFileName.Data()));
1211 fFESCalled[kDAQ]=kTRUE;
1212 TObjString *fileParams = new TObjString(Form("%s_!?!_%s", id, source));
1213 fFESlist[kDAQ].Add(fileParams);
1215 return localFileName.Data();
1219 //______________________________________________________________________________________________
1220 Bool_t AliShuttle::RetrieveDAQFile(const char* daqFileName, const char* localFileName)
1223 // check temp directory: trying to cd to temp; if it does not exist, create it
1224 AliDebug(2, Form("Copy file %s from DAQ FES into folder %s and rename it as %s",
1225 daqFileName,fgkShuttleTempDir, localFileName));
1227 void* dir = gSystem->OpenDirectory(fgkShuttleTempDir);
1229 if (gSystem->mkdir(fgkShuttleTempDir, kTRUE)) {
1230 AliError(Form("Can't open directory <%s>", fgkShuttleTempDir));
1235 gSystem->FreeDirectory(dir);
1238 TString baseDAQFESFolder = "DAQ";
1239 TString command = Form("scp %s@%s:%s/%s %s/%s",
1240 fConfig->GetFESUser(kDAQ),
1241 fConfig->GetFESHost(kDAQ),
1242 baseDAQFESFolder.Data(),
1247 AliDebug(2, Form("%s",command.Data()));
1249 UInt_t nRetries = 0;
1250 UInt_t maxRetries = 3;
1252 // copy!! if successful TSystem::Exec returns 0
1253 while(nRetries++ < maxRetries) {
1254 AliDebug(2, Form("Trying to copy file. Retry # %d", nRetries));
1255 if(gSystem->Exec(command.Data()) == 0) return kTRUE;
1262 //______________________________________________________________________________________________
1263 TList* AliShuttle::GetDAQFileSources(const char* detector, const char* id)
1265 // Retrieves a file from the DCS FES.
1267 // check connection, in case connect
1269 Log(detector, "GetDAQFileSources - Couldn't connect to DAQ Logbook");
1273 // Query preparation
1274 TString sqlQueryStart = "select DAQsource from logbook_fs where";
1275 TString whereClause = Form("run=%d and detector=\"%s\" and fileId=\"%s\"",
1276 GetCurrentRun(), detector, id);
1277 TString sqlQuery = Form("%s %s", sqlQueryStart.Data(), whereClause.Data());
1279 AliDebug(2, Form("SQL query: \n%s",sqlQuery.Data()));
1282 TSQLResult* aResult;
1283 aResult = fServer[kDAQ]->Query(sqlQuery);
1285 Log(detector, Form("GetDAQFileSources - Can't execute SQL query for id: %s", id));
1289 if (aResult->GetRowCount() == 0) {
1291 Form("GetDAQFileSources - No entry in FES table for id: %s", id));
1297 TList *list = new TList();
1300 while((aRow = aResult->Next())){
1302 TString daqSource(aRow->GetField(0), aRow->GetFieldLength(0));
1303 AliDebug(2, Form("daqSource = %s", daqSource.Data()));
1304 list->Add(new TObjString(daqSource));
1313 //______________________________________________________________________________________________
1314 const char* AliShuttle::GetDCSFileName(const char* /*detector*/, const char* /*id*/, const char* /*source*/){
1315 // Retrieves a file from the DCS FES.
1317 return "You're in DCS";
1321 //______________________________________________________________________________________________
1322 TList* AliShuttle::GetDCSFileSources(const char* /*detector*/, const char* /*id*/){
1323 // Retrieves a file from the DCS FES.
1329 //______________________________________________________________________________________________
1330 const char* AliShuttle::GetHLTFileName(const char* /*detector*/, const char* /*id*/, const char* /*source*/){
1331 // Retrieves a file from the HLT FES.
1333 return "You're in HLT";
1337 //______________________________________________________________________________________________
1338 TList* AliShuttle::GetHLTFileSources(const char* /*detector*/, const char* /*id*/){
1339 // Retrieves a file from the HLT FES.
1345 //______________________________________________________________________________________________
1346 Bool_t AliShuttle::UpdateDAQTable()
1348 // Update DAQ table filling time_processed field in all rows corresponding to current run and detector
1350 // check connection, in case connect
1352 Log(fCurrentDetector, "UpdateDAQTable - Couldn't connect to DAQ Logbook");
1356 TTimeStamp now; // now
1358 // Loop on FES list entries
1359 TIter iter(&fFESlist[kDAQ]);
1360 TObjString *aFESentry=0;
1361 while((aFESentry = dynamic_cast<TObjString*> (iter.Next()))){
1362 TString aFESentrystr = aFESentry->String();
1363 TObjArray *aFESarray = aFESentrystr.Tokenize("_!?!_");
1364 if(!aFESarray || aFESarray->GetEntries() != 2 ) {
1365 Log(fCurrentDetector, Form("UpdateDAQTable - error updating FES entry. Check string: <%s>",
1366 aFESentrystr.Data()));
1367 if(aFESarray) delete aFESarray;
1370 const char* fileId = ((TObjString*) aFESarray->At(0))->GetName();
1371 const char* daqSource = ((TObjString*) aFESarray->At(1))->GetName();
1372 TString whereClause = Form("where run=%d and detector=\"%s\" and fileId=\"%s\" and DAQsource=\"%s\";",
1373 GetCurrentRun(), fCurrentDetector.Data(), fileId, daqSource);
1377 TString sqlQuery = Form("update logbook_fs set time_processed=%d %s", now.GetSec(), whereClause.Data());
1379 AliDebug(2, Form("SQL query: \n%s",sqlQuery.Data()));
1382 TSQLResult* aResult;
1383 aResult = dynamic_cast<TSQLResult*> (fServer[kDAQ]->Query(sqlQuery));
1385 Log(fCurrentDetector, Form("UpdateDAQTable - Can't execute SQL query <%s>", sqlQuery.Data()));
1395 //______________________________________________________________________________________________
1396 Bool_t AliShuttle::UpdateShuttleLogbook(const char* detector, const char* status)
1398 // Update Shuttle logbook filling detector or shuttle_done column
1399 // ex. of usage: UpdateShuttleLogbook("PHOS", "DONE") or UpdateShuttleLogbook("shuttle_done")
1401 // check connection, in case connect
1403 Log("SHUTTLE", "UpdateShuttleLogbook - Couldn't connect to DAQ Logbook.");
1407 TString detName(detector);
1409 if(detName == "shuttle_done") {
1410 setClause = "set shuttle_done=1";
1412 TString statusStr(status);
1413 if(statusStr.Contains("done", TString::kIgnoreCase) ||
1414 statusStr.Contains("failed", TString::kIgnoreCase)){
1415 setClause = Form("set %s=\"%s\"", detector, status);
1418 Form("UpdateShuttleLogbook - Invalid status <%s> for detector %s",
1424 TString whereClause = Form("where run=%d", GetCurrentRun());
1426 TString sqlQuery = Form("update logbook_shuttle %s %s",
1427 setClause.Data(), whereClause.Data());
1429 AliDebug(2, Form("SQL query: \n%s",sqlQuery.Data()));
1432 TSQLResult* aResult;
1433 aResult = dynamic_cast<TSQLResult*> (fServer[kDAQ]->Query(sqlQuery));
1435 Log("SHUTTLE", Form("UpdateShuttleLogbook - Can't execute query <%s>", sqlQuery.Data()));
1443 //______________________________________________________________________________________________
1444 Int_t AliShuttle::GetCurrentRun() const
1446 // Get current run from logbook entry
1448 return fLogbookEntry ? fLogbookEntry->GetRun() : -1;
1451 //______________________________________________________________________________________________
1452 UInt_t AliShuttle::GetCurrentStartTime() const
1454 // get current start time
1456 return fLogbookEntry ? fLogbookEntry->GetStartTime() : 0;
1459 //______________________________________________________________________________________________
1460 UInt_t AliShuttle::GetCurrentEndTime() const
1462 // get current end time from logbook entry
1464 return fLogbookEntry ? fLogbookEntry->GetEndTime() : 0;
1467 //______________________________________________________________________________________________
1468 void AliShuttle::Log(const char* detector, const char* message)
1470 // Fill log string with a message
1472 void* dir = gSystem->OpenDirectory(fgkShuttleLogDir);
1474 if (gSystem->mkdir(fgkShuttleLogDir, kTRUE)) {
1475 AliError(Form("Can't open directory <%s>", fgkShuttleTempDir));
1480 gSystem->FreeDirectory(dir);
1483 TString toLog = Form("%s (%d): %s - ", TTimeStamp(time(0)).AsString("s"), getpid(), detector);
1484 if(GetCurrentRun()>=0 ) toLog += Form("run %d - ", GetCurrentRun());
1485 toLog += Form("%s", message);
1487 AliInfo(toLog.Data());
1490 fileName.Form("%s/%s.log", fgkShuttleLogDir, detector);
1491 gSystem->ExpandPathName(fileName);
1494 logFile.open(fileName, ofstream::out | ofstream::app);
1496 if (!logFile.is_open()) {
1497 AliError(Form("Could not open file %s", fileName.Data()));
1501 logFile << toLog.Data() << "\n";
1506 //______________________________________________________________________________________________
1507 Bool_t AliShuttle::Collect(Int_t run)
1510 // Collects conditions data for all UNPROCESSED run written to DAQ LogBook in case of run = -1 (default)
1511 // If a dedicated run is given this run is processed
1513 // In operational mode, this is the Shuttle function triggered by the EOR signal.
1517 Log("SHUTTLE","Collect - Shuttle called. Collecting conditions data for unprocessed runs");
1519 Log("SHUTTLE", Form("Collect - Shuttle called. Collecting conditions data for run %d", run));
1521 SetLastAction("Starting");
1523 TString whereClause("where shuttle_done=0");
1525 whereClause += Form(" and run=%d", run);
1527 TObjArray shuttleLogbookEntries;
1528 if (!QueryShuttleLogbook(whereClause, shuttleLogbookEntries)) {
1529 Log("SHUTTLE", "Collect - Can't retrieve entries from Shuttle logbook");
1533 if (!RetrieveConditionsData(shuttleLogbookEntries)) {
1534 Log("SHUTTLE", "Collect - Process of at least one run failed");
1541 //______________________________________________________________________________________________
1542 Bool_t AliShuttle::RetrieveConditionsData(const TObjArray& dateEntries)
1544 // Retrieve conditions data for all runs that aren't processed yet
1546 Bool_t hasError = kFALSE;
1548 TIter iter(&dateEntries);
1549 AliShuttleLogbookEntry* anEntry;
1551 while ((anEntry = (AliShuttleLogbookEntry*) iter.Next())){
1552 if (!Process(anEntry)){
1557 return hasError == kFALSE;
1560 //______________________________________________________________________________________________
1561 ULong_t AliShuttle::GetTimeOfLastAction() const
1565 fMonitoringMutex->Lock();
1567 tmp = fLastActionTime;
1569 fMonitoringMutex->UnLock();
1574 //______________________________________________________________________________________________
1575 const TString AliShuttle::GetLastAction() const
1577 // returns a string description of the last action
1581 fMonitoringMutex->Lock();
1585 fMonitoringMutex->UnLock();
1590 //______________________________________________________________________________________________
1591 void AliShuttle::SetLastAction(const char* action)
1593 // updates the monitoring variables
1595 fMonitoringMutex->Lock();
1597 fLastAction = action;
1598 fLastActionTime = time(0);
1600 fMonitoringMutex->UnLock();
1603 //______________________________________________________________________________________________
1604 const char* AliShuttle::GetRunParameter(const char* param)
1606 // returns run parameter read from DAQ logbook
1608 if(!fLogbookEntry) {
1609 AliError("No logbook entry!");
1613 return fLogbookEntry->GetRunParameter(param);