3 //**************************************************************************
4 //* This file is property of and copyright by the *
5 //* ALICE Experiment at CERN, All rights reserved. *
7 //* Primary Authors: Matthias Richter <Matthias.Richter@ift.uib.no> *
8 //* for The ALICE HLT Project. *
10 //* Permission to use, copy, modify and distribute this software and its *
11 //* documentation strictly for non-commercial purposes is hereby granted *
12 //* without fee, provided that the above copyright notice appears in all *
13 //* copies and that both the copyright notice and this permission notice *
14 //* appear in the supporting documentation. The authors make no claims *
15 //* about the suitability of this software for any purpose. It is *
16 //* provided "as is" without express or implied warranty. *
17 //**************************************************************************
19 /// @file AliHLTOUTComponent.cxx
20 /// @author Matthias Richter
22 /// @brief The HLTOUT data sink component similar to HLTOUT nodes
23 /// @note Used in the AliRoot environment only.
27 #include "AliHLTOUTComponent.h"
28 #include "AliHLTOUT.h"
29 #include "AliHLTHOMERLibManager.h"
30 #include "AliHLTHOMERWriter.h"
31 #include "AliHLTErrorGuard.h"
32 #include "AliDAQ.h" // equipment Ids
33 #include "AliRawDataHeader.h" // Common Data Header
34 #include <TDatime.h> // seed for TRandom
35 #include <TRandom.h> // random int generation for DDL no
43 /** ROOT macro for the implementation of ROOT specific class methods */
44 ClassImp(AliHLTOUTComponent)
46 AliHLTOUTComponent::AliHLTOUTComponent(EType type)
50 , fIdFirstDDL(7680) // 0x1e<<8
54 , fDigitFileName("HLT.Digits.root")
57 , fppDigitArrays(NULL)
61 , fRoundRobinCounter(0)
63 // The HLTOUT data sink component which models the behavior of the HLTOUT
64 // nodes of the HLT cluster.
65 // The HLTOUT component is attached at the end of a chain. It stores all input
66 // block in the HOMER format, distributed over a number of DDL link. The data
67 // is stored in a digit file or in raw ddl files.
68 fIdFirstDDL=AliDAQ::DdlIDOffset("HLT");
69 fNofDDLs=AliDAQ::NumberOfDdls("HLT");
71 if (fType!=kGlobal && fType!=kDigits && fType!=kRaw) {
72 ALIHLTERRORGUARD(1, "invalid component type %d", fType);
76 int AliHLTOUTComponent::fgOptions=kWriteRawFiles|kWriteDigits;
78 AliHLTOUTComponent::~AliHLTOUTComponent()
81 if (fpLibManager) delete fpLibManager;
85 const char* AliHLTOUTComponent::GetComponentID()
87 // overloaded from AliHLTComponent: get component id
89 case kDigits: return "HLTOUTdigits";
90 case kRaw: return "HLTOUTraw";
98 void AliHLTOUTComponent::GetInputDataTypes( AliHLTComponentDataTypeList& list)
100 // overloaded from AliHLTComponent: indicate input data types
102 list.push_back(kAliHLTAnyDataType);
105 AliHLTComponent* AliHLTOUTComponent::Spawn()
107 // overloaded from AliHLTComponent: create instance
108 return new AliHLTOUTComponent(fType);
111 int AliHLTOUTComponent::DoInit( int argc, const char** argv )
113 // overloaded from AliHLTComponent: initialization
118 fOptions|=kWriteDigits; fOptions&=~kWriteRawFiles;
119 HLTInfo("initializing HLTOUT component for digits generation");
122 fOptions|=kWriteRawFiles; fOptions&=~kWriteDigits;
123 HLTInfo("initializing HLTOUT component for raw data generation");
130 if ((iResult=ConfigureFromArgumentString(argc, argv))<0) return iResult;
132 // Create a new library manager and allocate the appropriate number of
133 // HOMER writers for the HLTOUT component.
134 if (!fpLibManager) fpLibManager=new AliHLTHOMERLibManager;
137 for (writerNo=0; writerNo<fNofDDLs; writerNo++) {
138 AliHLTMonitoringWriter* pWriter=fpLibManager->OpenWriter();
140 HLTDebug("HOMER writer %p added", pWriter);
141 fWriters.push_back(pWriter);
143 HLTError("can not open HOMER writer");
155 int AliHLTOUTComponent::ScanConfigurationArgument(int argc, const char** argv)
157 // overloaded from AliHLTComponent: argument scan
158 if (argc<=0) return 0;
160 TString argument=argv[i];
164 // specify number of ddl links
165 if (argument.CompareTo("-links")==0) {
166 if (++i>=argc) return -EPROTO;
167 TString parameter(argv[i]);
168 parameter.Remove(TString::kLeading, ' '); // remove all blanks
169 if (parameter.IsDigit()) {
170 fNofDDLs=parameter.Atoi();
172 HLTError("wrong parameter for argument %s, number expected", argument.Data());
180 if (argument.CompareTo("-digitfile")==0) {
181 if (++i>=argc) return -EPROTO;
182 fDigitFileName=argv[i];
189 if (argument.Contains(key)) {
190 argument.ReplaceAll(key, "");
191 if (argument.IsNull()) {
192 fOptions|=kWriteRawFiles;
193 } else if (argument.CompareTo("=off")==0) {
194 fOptions&=~kWriteRawFiles;
195 } else if (argument.CompareTo("=on")==0) {
196 fOptions|=kWriteRawFiles;
198 HLTError("invalid parameter for argument %s: possible %s=off/%s=on", key, key, key);
207 if (argument.Contains(key)) {
208 argument.ReplaceAll(key, "");
209 if (argument.IsNull()) {
210 fOptions|=kWriteDigits;
211 } else if (argument.CompareTo("=off")==0) {
212 fOptions&=~kWriteDigits;
213 } else if (argument.CompareTo("=on")==0) {
214 fOptions|=kWriteDigits;
216 HLTError("invalid parameter for argument %s: possible %s=off/%s=on", key, key, key);
223 // -distribute-blocks
224 key="-distribute-blocks";
225 if (argument.CompareTo(key)==0) {
226 fRoundRobinCounter=-1;
235 int AliHLTOUTComponent::DoDeinit()
237 // overloaded from AliHLTComponent: cleanup
241 AliHLTMonitoringWriterPVector::iterator element=fWriters.begin();
242 while (element!= fWriters.end()) {
244 // wanted to have a dynamic_cast<AliHLTHOMERWriter*> here, but this results into
245 // undefined symbol when loading the library
246 if (*element!=NULL) {
248 fpLibManager->DeleteWriter((AliHLTHOMERWriter*)(*element));
250 HLTError("writer instance is NULL");
252 element=fWriters.erase(element);
266 fpDigitFile->Close();
271 if (fppDigitArrays) {
272 for (int i=0; i<fNofDDLs; i++) {
273 if (fppDigitArrays[i]) delete fppDigitArrays[i];
275 delete[] fppDigitArrays;
282 int AliHLTOUTComponent::DumpEvent( const AliHLTComponentEventData& evtData,
283 const AliHLTComponentBlockData* blocks,
284 AliHLTComponentTriggerData& /*trigData*/ )
286 // overloaded from AliHLTDataSink: event processing
288 HLTInfo("write %d output block(s)", evtData.fBlockCnt);
291 AliHLTUInt32_t eventType=gkAliEventTypeUnknown;
292 bool bIsDataEvent=IsDataEvent(&eventType);
294 homer_uint64 homerHeader[kCount_64b_Words];
295 HOMERBlockDescriptor homerDescriptor(homerHeader);
296 for (int n=0; n<(int)evtData.fBlockCnt; n++ ) {
297 if (blocks[n].fDataType==kAliHLTDataTypeEvent ||
298 blocks[n].fDataType==kAliHLTDataTypeSOR ||
299 blocks[n].fDataType==kAliHLTDataTypeEOR ||
300 blocks[n].fDataType==kAliHLTDataTypeComConf ||
301 blocks[n].fDataType==kAliHLTDataTypeUpdtDCS)
303 // the special events have to be ignored.
307 (blocks[n].fDataType!=kAliHLTDataTypeComponentTable))
309 // In simulation, there are no SOR and EOR events created. Thats
310 // why all data blocks of those events are currently ignored.
311 // Strictly speaking, components should not create output blocks
312 // on the SOR/EOR event
314 // Exeptions: some blocks are added, the buffer must be prepared and
315 // kept since the pointers will be invalid
316 // - kAliHLTDataTypeComponentTable component table entries
319 memset( homerHeader, 0, sizeof(homer_uint64)*kCount_64b_Words );
320 homerDescriptor.Initialize();
321 // for some traditional reason the TCPDumpSubscriber swaps the bytes
322 // of the data type id and data type origin. Actually I do not understand
323 // the corresponding code line
324 // homerBlock.SetType( blocks[n].fDataType.fID );
325 // this compiles in the PubSub framework and in addition does a byte swap
327 homer_uint64 origin=0;
328 memcpy(&id, blocks[n].fDataType.fID, sizeof(homer_uint64));
329 memcpy(((AliHLTUInt8_t*)&origin)+sizeof(homer_uint32), blocks[n].fDataType.fOrigin, sizeof(homer_uint32));
330 homerDescriptor.SetType(AliHLTOUT::ByteSwap64(id));
331 homerDescriptor.SetSubType1(AliHLTOUT::ByteSwap64(origin));
332 homerDescriptor.SetSubType2(blocks[n].fSpecification);
333 homerDescriptor.SetBlockSize(blocks[n].fSize);
335 writerNo=ShuffleWriters(fWriters, blocks[n].fSize);
337 assert(writerNo>=0 && writerNo<(int)fWriters.size());
338 // I'm puzzled by the different headers, buffers etc. used in the
339 // HOMER writer/data. In additional, there is no type check as there
340 // are void pointers used and names mixed.
341 // It seems that HOMERBlockDescriptor is just a tool to set the
342 // different fields in the homer header, which is an array of 64 bit
344 fWriters[writerNo]->AddBlock(homerHeader, blocks[n].fPtr);
349 if (iResult>=0 && !bIsDataEvent && fNofDDLs>=2) {
350 // data blocks from a special event are kept to be added to the
351 // following event. In the current implementation at least 2 DDLs
352 // are required to allow to keep the blocks of the SOR event and
353 // include it in the first event. If only one writer is available
354 // the blocks are ignored. For the moment this is not expexted to
355 // be a problem since components should not gererate anything on
356 // SOR/EOR. The only case is the list of AliHLTComponentTableEntry
357 // transmitted for component statistics in debug mode.
358 if (fReservedWriter>=0) {
359 HLTWarning("overriding previous buffer of non-data event data blocks");
361 const AliHLTUInt8_t* pBuffer=NULL;
363 // TODO: not yet clear whether it is smart to send the event id of
364 // this special event or if it should be set from the id of the
365 // following event where the data will be added
366 if (blockCount>0 && (bufferSize=FillOutputBuffer(evtData.fEventID, fWriters[writerNo], pBuffer))>0) {
367 fReservedWriter=writerNo;
368 fReservedData=bufferSize;
370 fWriters[writerNo]->Clear();
371 } else if (iResult>=0 && !bIsDataEvent && fNofDDLs<2 && blockCount>0) {
372 HLTWarning("ignoring %d block(s) for special event of type %d: at least 2 DDLs are required", blockCount, eventType);
375 if (iResult>=0 && bIsDataEvent) {
376 iResult=Write(GetEventCount());
379 if (fRoundRobinCounter>=0) {
380 if (++fRoundRobinCounter>=fNofDDLs) fRoundRobinCounter=0;
386 int AliHLTOUTComponent::Write(int eventNo)
388 // write digits and raw files for the current event
391 if (fWriters.size()==0) return 0;
393 if (fReservedWriter>=0) {
394 if (fOptions&kWriteDigits) WriteDigitArray(fReservedWriter, &fBuffer[0], fReservedData);
395 if (fOptions&kWriteRawFiles) WriteRawFile(eventNo, fReservedWriter, &fBuffer[0], fReservedData);
399 // search for the writer with the biggest data volume in order to allocate the
400 // output buffer of sufficient size
402 for (size_t i=0; i<fWriters.size(); i++) {
403 if ((int)i==fReservedWriter) continue;
406 if (sorted.size()==0 || fWriters[i]->GetTotalMemorySize()<=fWriters[sorted[0]]->GetTotalMemorySize()) {
409 sorted.insert(sorted.begin(), i);
415 vector<int>::iterator ddlno=sorted.begin();
416 while (ddlno!=sorted.end()) {
417 const AliHLTUInt8_t* pBuffer=NULL;
420 if ((bufferSize=FillOutputBuffer(eventNo, fWriters[*ddlno], pBuffer))>0) {
421 if (fOptions&kWriteDigits) WriteDigitArray(*ddlno, pBuffer, bufferSize);
422 if (fOptions&kWriteRawFiles &&
423 (fRoundRobinCounter<0 || fRoundRobinCounter==*ddlno))
424 WriteRawFile(eventNo, *ddlno, pBuffer, bufferSize);
426 fWriters[*ddlno]->Clear();
429 if (fOptions&kWriteDigits) WriteDigits(eventNo);
433 int AliHLTOUTComponent::ShuffleWriters(AliHLTMonitoringWriterPVector &list, AliHLTUInt32_t /*size*/)
435 /// get a writer for the next block
436 /// in round robin mode (like the online HLTOUT) all blocks of one event go to the same link
437 /// this is now also the default behavior of the HLTOUTComponent and indicated by
438 /// fRoundRobinCounter>=0
439 /// Writers are selected randomly otherwise.
440 if (fRoundRobinCounter>=0) {
441 if (fRoundRobinCounter==fReservedWriter) {
442 if (++fRoundRobinCounter>=fNofDDLs) fRoundRobinCounter=0;
443 if (fRoundRobinCounter==fReservedWriter) {
444 HLTWarning("there are not enough links to use a reserved writer, discarding data in reserved writer %d (total %d)",
445 fReservedWriter, fNofDDLs);
449 return fRoundRobinCounter;
453 assert(list.size()>0);
454 if (list.size()==0) return iResult;
457 for (i=0; i<list.size(); i++) {
458 if ((int)i==fReservedWriter) continue;
459 if (list[i]->GetTotalMemorySize()==0)
460 writers.push_back(i);
461 else if (iResult<0 ||
462 list[i]->GetTotalMemorySize()<list[iResult]->GetTotalMemorySize())
466 if (writers.size()>0) {
468 if (writers.size()>0) {
469 // shuffle among the empty writers
472 rand.SetSeed(dt.Get()*(iResult+1));
473 i=rand.Integer(writers.size()-1);
474 assert(i>0 && i<writers.size()-1);
478 // take the writer with the least data volume
484 int AliHLTOUTComponent::FillOutputBuffer(int eventNo, AliHLTMonitoringWriter* pWriter, const AliHLTUInt8_t* &pBuffer)
486 // prepare the output buffer for writing, consists of
490 // buffer is allocated internally and data is valid until next call
492 unsigned int bufferSize=0;
494 // space for common data header
495 bufferSize+=sizeof(AliRawDataHeader);
496 assert(sizeof(AliRawDataHeader)==32);
498 // space for HLT event header
499 bufferSize+=sizeof(AliHLTOUT::AliHLTOUTEventHeader);
501 // space for payload from the writer
502 if (pWriter) bufferSize+=pWriter->GetTotalMemorySize();
504 // payload data must be aligned to 32bit
505 bufferSize=(bufferSize+3)/4;
508 if (bufferSize>fBuffer.size())
509 fBuffer.resize(bufferSize);
511 // reset the last 32bit word, rest will be overwritten
512 memset(&fBuffer[bufferSize-4], 0, 4);
514 if (bufferSize<=fBuffer.size()) {
515 AliRawDataHeader* pCDH=reinterpret_cast<AliRawDataHeader*>(&fBuffer[0]);
516 AliHLTOUT::AliHLTOUTEventHeader* pHLTH=reinterpret_cast<AliHLTOUT::AliHLTOUTEventHeader*>(&fBuffer[sizeof(AliRawDataHeader)]);
517 *pCDH = AliRawDataHeader(); // Fill with default values.
518 memset(pHLTH, 0, sizeof(AliHLTOUT::AliHLTOUTEventHeader));
522 pWriter->Copy(&fBuffer[sizeof(AliRawDataHeader)+sizeof(AliHLTOUT::AliHLTOUTEventHeader)], 0, 0, 0, 0);
523 pHLTH->fLength=pWriter->GetTotalMemorySize();
524 // set status bit to indicate HLT payload
525 pCDH->fStatusMiniEventID|=0x1<<(AliHLTOUT::kCDHStatusFlagsOffset+AliHLTOUT::kCDHFlagsHLTPayload);
527 pHLTH->fLength+=sizeof(AliHLTOUT::AliHLTOUTEventHeader);
528 // pHLTH->fEventIDLow is already set to zero in memset above.
529 pHLTH->fEventIDLow = eventNo;
530 // version does not really matter since we do not add decision data
531 pHLTH->fVersion=AliHLTOUT::kVersion1;
533 pCDH->fSize=bufferSize;
534 pCDH->fStatusMiniEventID|=0x1<<(AliHLTOUT::kCDHStatusFlagsOffset + AliHLTOUT::kCDHFlagsHLTPayload);
537 iResult=(int)bufferSize;
546 int AliHLTOUTComponent::WriteDigitArray(int hltddl, const AliHLTUInt8_t* pBuffer, unsigned int bufferSize)
548 // wite a buffer to the associated digit array
550 assert(hltddl<fNofDDLs);
551 if (hltddl>=fNofDDLs) return -ERANGE;
553 if (!fppDigitArrays) {
554 fppDigitArrays=new TArrayC*[fNofDDLs];
555 if (fppDigitArrays) {
556 for (int i=0; i<fNofDDLs; i++) {
557 fppDigitArrays[i]=new TArrayC(0);
561 if (fppDigitArrays && fppDigitArrays[hltddl]) {
562 fppDigitArrays[hltddl]->Set(bufferSize, reinterpret_cast<const Char_t*>(pBuffer));
569 int AliHLTOUTComponent::WriteDigits(int /*eventNo*/)
571 // fill tree with digit arrays and write to file
572 // all links must be written, even in round robin mode, where all links but one
573 // do not contain any data blocks.
574 // This is a limitation of storing the links in a tree
577 fpDigitFile=new TFile(fDigitFileName, "RECREATE");
579 if (fpDigitFile && !fpDigitFile->IsZombie()) {
581 fpDigitTree=new TTree("rawhltout","HLTOUT raw data");
582 if (fpDigitTree && fppDigitArrays) {
583 for (int i=0; i<fNofDDLs; i++) {
584 const char* branchName=AliDAQ::DdlFileName("HLT", i);
585 if (fppDigitArrays[i]) fpDigitTree->Branch(branchName, "TArrayC", &fppDigitArrays[i], 32000/*just as the default*/, 0);
591 int res=fpDigitTree->Fill();
592 HLTDebug("writing digit tree: %d", res);
594 res=fpDigitTree->Write("",TObject::kOverwrite);
595 HLTDebug("writing digit tree: %d", res);
599 fpDigitTree->Write("",TObject::kOverwrite);
601 if (fppDigitArrays) for (int i=0; i<fNofDDLs; i++) {
602 if (fppDigitArrays[i]) fppDigitArrays[i]->Set(0);
606 const char* errorMsg="";
607 if (GetEventCount()==5) {
608 errorMsg=" (suppressing further error messages)";
610 if (GetEventCount()<5) {
611 HLTError("can not open HLT digit file %s%s", fDigitFileName.Data(), errorMsg);
618 int AliHLTOUTComponent::WriteRawFile(int eventNo, int hltddl, const AliHLTUInt8_t* pBuffer, unsigned int bufferSize)
620 // write buffer to raw file in the current directory
621 // creates the event raw directories in the current directory
623 const char* fileName=AliDAQ::DdlFileName("HLT", hltddl);
624 assert(fileName!=NULL);
626 filePath.Form("raw%d/", eventNo);
627 if (gSystem->AccessPathName(filePath)!=0) {
628 // note: AccessPathName return 0 if the path is existing
629 TString command="mkdir "; command+=filePath;
630 gSystem->Exec(command);
634 ios::openmode filemode=(ios::openmode)0;
635 ofstream rawfile(filePath.Data(), filemode);
636 if (rawfile.good()) {
637 if (pBuffer && bufferSize>0) {
638 rawfile.write(reinterpret_cast<const char*>(pBuffer), bufferSize);
640 HLTWarning("writing zero length raw data file %s");
642 HLTDebug("wrote %d byte(s) to file %s", bufferSize, filePath.Data());
644 HLTError("can not open file %s for writing", filePath.Data());
652 void AliHLTOUTComponent::SetGlobalOption(unsigned int options)
654 // set the global options
658 void AliHLTOUTComponent::ClearGlobalOption(unsigned int options)
660 // reset the global options
664 bool AliHLTOUTComponent::TestGlobalOption(unsigned int option)
667 return (fgOptions&option)!=0;