QwAnalysis
QwEventBuffer.cc
Go to the documentation of this file.
1 #include "QwEventBuffer.h"
2 
3 #include "QwOptions.h"
4 #include "QwEPICSEvent.h"
5 #include "VQwSubsystem.h"
6 #include "QwSubsystemArray.h"
7 
8 #include <TMath.h>
9 
10 #include <vector>
11 #include <glob.h>
12 
13 #include <csignal>
14 Bool_t globalEXIT;
15 void sigint_handler(int sig)
16 {
17  std::cout << "handling signal no. " << sig << " ";
18  std::cout << "(press ctrl-\\ to abort now)\n";
19  globalEXIT=1;
20 }
21 
22 
23 
24 #include "THaCodaFile.h"
25 #ifdef __CODA_ET
26 #include "THaEtClient.h"
27 #endif
28 
29 std::string QwEventBuffer::fDefaultDataFileStem = "QwRun_";
31 
32 const Int_t QwEventBuffer::kRunNotSegmented = -20;
33 const Int_t QwEventBuffer::kNoNextDataFile = -30;
35 
36 /// This is the ASCII character array 'NULL', and is used by the
37 /// DAQ to indicate a known empty buffer.
38 const UInt_t QwEventBuffer::kNullDataWord = 0x4e554c4c;
39 
40 
41 /// Default constructor
43  : fDataFileStem(fDefaultDataFileStem),
44  fDataFileExtension(fDefaultDataFileExtension),
45  fEvStreamMode(fEvStreamNull),
46  fEvStream(NULL),
47  fCurrentRun(-1),
48  fRunIsSegmented(kFALSE),
49  fPhysicsEventFlag(kFALSE),
50  fEvtNumber(0),
51  fNumPhysicsEvents(0)
52 {
53  // Set up the signal handler.
54  globalEXIT=0;
55  signal(SIGINT, sigint_handler);// ctrl+c
56  signal(SIGTERM, sigint_handler);// kill in shell // 15
57  // signal(SIGTSTP, sigint_handler);// ctrl+z // 20
58 
59  fDataDirectory = getenv("QW_DATA");
60  if (fDataDirectory.Length() == 0){
61  QwError << "ERROR: Can't get the data directory in the QwEventBuffer creator."
62  << QwLog::endl;
63  } else if (! fDataDirectory.EndsWith("/")) {
64  fDataDirectory.Append("/");
65  }
66 
67  fCleanParameter[0] = 0.0;
68  fCleanParameter[1] = 0.0;
69  fCleanParameter[2] = 0.0;
70 }
71 
72 /**
73  * Defines configuration options for QwEventBuffer class using QwOptions
74  * functionality.
75  *
76  * @param options Options object
77  */
79 {
80  // Define the execution options
81  options.AddDefaultOptions()
82  ("online", po::value<bool>()->default_bool_value(false),
83  "use online data stream");
84  options.AddDefaultOptions()
85  ("online.RunNumber", po::value<int>()->default_bool_value(0),
86  "Effective run number to be used by online system to find the parameter files");
87  options.AddDefaultOptions()
88  ("run,r", po::value<string>()->default_value("0:0"),
89  "run range in format #[:#]");
90  options.AddDefaultOptions()
91  ("runlist", po::value<string>()->default_value(""),
92  "run list file example \n[5253]\n 234\n 246\n 256\n 345:456\n 567:789\n [5259]\n [5260]\n 0:10000\n [5261:5270]\n 9000:10000\n- for run 5253 it will analyze three individual events, and two event ranges \n- for run 5259 it will analyze the entire run (all segments) \n- for run 5260 it will analyze the first 10000 events \n- for runs 5261 through 5270 it will analyze the events 9000 through 10000)");
93  options.AddDefaultOptions()
94  ("event,e", po::value<string>()->default_value("0:"),
95  "event range in format #[:#]");
96  options.AddDefaultOptions()
97  ("segment,s", po::value<string>()->default_value("0:"),
98  "run segment range in format #[:#]");
99  options.AddDefaultOptions()
100  ("chainfiles", po::value<bool>()->default_bool_value(false),
101  "chain file segments together, do not analyze them separately");
102  options.AddDefaultOptions()
103  ("codafile-stem", po::value<string>()->default_value(fDefaultDataFileStem),
104  "stem of the input CODA filename");
105  options.AddDefaultOptions()
106  ("codafile-ext", po::value<string>()->default_value(fDefaultDataFileExtension),
107  "extension of the input CODA filename");
108  // Options specific to the ET clients
109  options.AddOptions("ET system options")
110  ("ET.hostname", po::value<string>(),
111  "Name of the ET session's host machine --- Only used in online mode\nDefaults to the environment variable $HOSTNAME");
112  options.AddOptions("ET system options")
113  ("ET.session", po::value<string>(),
114  "ET session name --- Only used in online mode\nDefaults to the environment variable $SESSION");
115  options.AddOptions("ET system options")
116  ("ET.station", po::value<string>(),
117  "ET station name --- Only used in online mode");
118 }
119 
121 {
122  fOnline = options.GetValue<bool>("online");
123  if (fOnline){
124 #ifndef __CODA_ET
125  QwError << "Online mode will not work without the CODA libraries!"
126  << QwLog::endl;
127  exit(EXIT_FAILURE);
128 #else
129  if (options.HasValue("online.RunNumber")) {
130  fCurrentRun = options.GetValue<int>("online.RunNumber");
131  }
132  if (options.HasValue("ET.station")) {
133  fETStationName = options.GetValue<string>("ET.station");
134  } else {
135  fETStationName = "";
136  }
137  if (options.HasValue("ET.hostname")) {
138  fETHostname = options.GetValue<string>("ET.hostname");
139  } else {
140  fETHostname = getenv("HOSTNAME");
141  }
142  if (options.HasValue("ET.session")) {
143  fETSession = options.GetValue<string>("ET.session");
144  } else {
145  fETSession = getenv("SESSION");
146  }
147  if (fETHostname.Length() == 0 || fETSession.Length() == 0) {
148  TString tmp = "";
149  if (fETHostname == NULL)
150  tmp += " \"HOSTNAME\"";
151  if (fETSession == NULL){
152  if (tmp.Length() > 0)
153  tmp += " and";
154  tmp += " ET \"SESSION\"";
155  }
156  QwError << "The" << tmp
157  << " variable(s) is(are) not defined in your environment.\n"
158  << " This is needed to run the online analysis."
159  << QwLog::endl;
160  exit(EXIT_FAILURE);
161  }
162 #endif
163  }
164  fRunRange = options.GetIntValuePair("run");
165  fEventRange = options.GetIntValuePair("event");
166  fSegmentRange = options.GetIntValuePair("segment");
167  fRunListFileName = options.GetValue<string>("runlist");
168  fChainDataFiles = options.GetValue<bool>("chainfiles");
169  fDataFileStem = options.GetValue<string>("codafile-stem");
170  fDataFileExtension = options.GetValue<string>("codafile-ext");
171 
172  // Open run list file
173  if (fRunListFileName.size() > 0) {
175  fEventListFile = 0;
176  if (! GetNextRunRange()) {
177  QwWarning << "No run range found in run list file: " << fRunListFile->GetLine() << QwLog::endl;
178  }
179  } else {
180  fRunListFile = 0;
181  fEventListFile = 0;
182  }
183 }
184 
186 {
187  UInt_t nevents = fNumPhysicsEvents - fStartingPhysicsEvent;
188  if (nevents==0) nevents=1;
190  << "Analysis of run " << GetRunNumber() << QwLog::endl
191  << fNumPhysicsEvents << " physics events were processed"<< QwLog::endl
192  << "CPU time used: " << fRunTimer.CpuTime() << " s "
193  << "(" << 1000.0 * fRunTimer.CpuTime() / nevents << " ms per event)" << QwLog::endl
194  << "Real time used: " << fRunTimer.RealTime() << " s "
195  << "(" << 1000.0 * fRunTimer.RealTime() / nevents << " ms per event)" << QwLog::endl
196  << QwLog::endl;
197 }
198 
199 
200 
201 /// Read the next requested event range, return true if success
203  // If there is an event list, open the next section
204  if (fEventListFile && !fEventListFile->IsEOF()) {
205  std::string eventrange;
206  // Find next non-whitespace, non-comment, non-empty line, before EOF
207  do {
208  fEventListFile->ReadNextLine(eventrange);
211  } while (fEventListFile->LineIsEmpty() && !fEventListFile->IsEOF());
212  // If EOF return false; no next event range
213  if (fEventListFile->IsEOF()) return kFALSE;
214  // Parse the event range
216  QwMessage << "Next event range is " << eventrange << QwLog::endl;
217  return kTRUE;
218  }
219  return kFALSE;
220 }
221 
222 /// Read the next requested run range, return true if success
224  // Delete any open event list file before moving to next run
225  if (fEventListFile) delete fEventListFile;
226  // If there is a run list, open the next section
227  std::string runrange;
228  if (fRunListFile && !fRunListFile->IsEOF() &&
230  // Parse the run range
232  QwMessage << "Next run range is " << runrange << QwLog::endl;
233  // If there is no event range for this run range, set to default of 0:MAXINT
234  if (! GetNextEventRange()) {
235  QwWarning << "No valid event range in run list file: "
236  << fEventListFile->GetLine() << ". "
237  << "Assuming the full event range." << QwLog::endl;
239  }
240  return kTRUE;
241  }
242  return kFALSE;
243 }
244 
245 /// Get the next run in the active run range, proceed to next range if needed
247  // First run
248  if (fCurrentRun == -1) {
249  fCurrentRun = fRunRange.first;
250  return kTRUE;
251  // Run is in range
252  } else if (fCurrentRun < fRunRange.second) {
253  fCurrentRun++;
254  return kTRUE;
255  // Run is not in range, get new range
256  } else if (GetNextRunRange()) {
257  fCurrentRun = fRunRange.first;
258  return kTRUE;
259  }
260  return kFALSE;
261 }
262 
264 {
265  TString runlabel = Form("%d",fCurrentRun);
267  runlabel += Form(".%03d",*fRunSegmentIterator);
268  }
269  return runlabel;
270 }
271 
273 {
274  Int_t status = CODA_ERROR;
275  // Reset the physics event counter
277 
278  if (fOnline) {
279  // Online stream
281  } else {
282  // Offline data file
283  if (fRunIsSegmented)
284  // Segmented
285  status = OpenNextSegment();
286  else
287  // Not segmented
288  status = OpenDataFile(fCurrentRun);
289  }
290  return status;
291 }
292 
294 {
295  Int_t status = CODA_ERROR;
296  if (globalEXIT==1) {
297  // We want to exit, so don't open the next stream.
298  status = CODA_ERROR;
299  } else if (fOnline) {
300  /* Modify the call below for your ET system, if needed.
301  OpenETStream( ET host name , $SESSION , mode)
302  mode=0: wait forever
303  mode=1: timeout quickly
304  */
305  QwMessage << "Try to open the ET station with HOSTNAME=="
306  << fETHostname
307  << ", SESSION==" << fETSession << "."
308  << QwLog::endl;
310 
311  } else {
312  // Try to open the next data file for the current run,
313  // but only if we haven't hit the event limit.
314  if (fCurrentRun != -1 && !fChainDataFiles
315  && fEvtNumber <= fEventRange.second) {
316  status = OpenNextSegment();
317  }
318  while (status != CODA_OK && GetNextRunNumber()) {
319  status = OpenDataFile(fCurrentRun);
320  if (status == CODA_ERROR){
321  // The data file can't be opened.
322  // Get ready to process the next run.
323  QwError << "ERROR: Unable to find data files for run "
324  << fCurrentRun << ". Moving to the next run.\n"
325  << QwLog::endl;
326  }
327  }
328 
329  }
330  // Grab the starting event counter
332  // Start the timers.
333  fRunTimer.Reset();
334  fRunTimer.Start();
335  fStopwatch.Start();
336  return status;
337 }
338 
340 {
341  // Stop the timers.
342  fRunTimer.Stop();
343  fStopwatch.Stop();
344  QwWarning << "Starting CloseStream."
345  << QwLog::endl;
346  Int_t status = kFileHandleNotConfigured;
348  && (fRunIsSegmented && !fChainDataFiles) ){
349  // The run is segmented and we are not chaining the
350  // segments together in the event loop, so close
351  // the current segment.
352  status = CloseThisSegment();
353  } else if (fEvStreamMode==fEvStreamFile) {
354  status = CloseDataFile();
355  } else if (fEvStreamMode==fEvStreamFile){
356  status = CloseETStream();
357  }
358  return status;
359 }
360 
361 
362 
364 {
365  // This will return for read errors,
366  // non-physics events, and for physics
367  // events that are within the event range.
368  Int_t status = CODA_OK;
369  do {
370  status = GetEvent();
371  if (globalEXIT == 1) {
372  // QUESTION: Should we continue to loop once we've
373  // reached the maximum event, to allow access to
374  // non-physics events?
375  // For now, mock up EOF if we've reached the maximum event.
376  status = EOF;
377  }
378  if (fEvtNumber > fEventRange.second) {
379  do {
380  if (GetNextEventRange()) status = CODA_OK;
381  else status = EOF;
382  } while (fEvtNumber < fEventRange.first);
383  }
384  // While we're in a run segment which was not requested (which
385  // should happen only when reading the zeroth segment for startup
386  // information), pretend that there's an event cut causing us to
387  // ignore events. Read configuration events only from the first
388  // part of the file.
390  fEventRange.first = fEvtNumber + 1;
391  if (fEvtNumber > 1000) status = EOF;
392  }
393  } while (status == CODA_OK &&
394  IsPhysicsEvent() &&
395  (fEvtNumber < fEventRange.first
396  || fEvtNumber > fEventRange.second)
397  );
398  if (status == CODA_OK && IsPhysicsEvent()) fNumPhysicsEvents++;
399 
400  // Progress meter (this should probably produce less output in production)
401  int nevents = 10000;
402  if (IsPhysicsEvent() && fEvtNumber > 0 && fEvtNumber % nevents == 0) {
403  QwMessage << "Processing event " << fEvtNumber << " ";
404  fStopwatch.Stop();
405  double efficiency = 100.0 * fStopwatch.CpuTime() / fStopwatch.RealTime();
406  QwMessage << "(" << fStopwatch.CpuTime()*1e3/nevents << " ms per event with ";
407  QwMessage << efficiency << "% efficiency)";
408  fStopwatch.Reset();
409  fStopwatch.Start();
411  } else if (fEvtNumber > 0 && fEvtNumber % 100 == 0) {
412  QwVerbose << "Processing event " << fEvtNumber << QwLog::endl;
413  }
414 
415  return status;
416 }
417 
418 
420 {
421  Int_t status = kFileHandleNotConfigured;
422  ResetFlags();
424  status = GetFileEvent();
425  } else if (fEvStreamMode==fEvStreamET){
426  status = GetEtEvent();
427  }
428  if (status == CODA_OK){
429  DecodeEventIDBank((UInt_t*)(fEvStream->getEvBuffer()));
430  }
431  return status;
432 }
433 
435  Int_t status = CODA_OK;
436  // Try to get a new event. If the EOF occurs,
437  // and the run is segmented, try to open the
438  // next segment and read a new event; repeat
439  // if needed.
440  do {
441  status = fEvStream->codaRead();
442  if (fChainDataFiles && status == EOF){
444  // Crash out of the loop if we can't open the
445  // next segment!
446  if (OpenNextSegment()!=CODA_OK) break;
447  }
448  } while (fChainDataFiles && status == EOF);
449  return status;
450 }
451 
453  Int_t status = CODA_OK;
454  // Do we want to have any loop here to wait for a bad
455  // read to be cleared?
456  status = fEvStream->codaRead();
457  if (status == CODA_EXIT)
458  globalEXIT = 1;
459  return status;
460 }
461 
462 
463 Int_t QwEventBuffer::WriteEvent(int* buffer)
464 {
465  Int_t status = kFileHandleNotConfigured;
466  ResetFlags();
468  status = WriteFileEvent(buffer);
469  } else if (fEvStreamMode==fEvStreamET) {
470  QwMessage << "No support for writing to ET streams" << QwLog::endl;
471  status = CODA_ERROR;
472  }
473  return status;
474 }
475 
477 {
478  Int_t status = CODA_OK;
479  // fEvStream is of inherited type THaCodaData,
480  // but codaWrite is only defined for THaCodaFile.
481  status = ((THaCodaFile*)fEvStream)->codaWrite(buffer);
482  return status;
483 }
484 
485 
487 {
488  // Encode the data in the elements of the subsystem array
489  std::vector<UInt_t> buffer;
490  subsystems.EncodeEventData(buffer);
491 
492  // Add CODA event header
493  std::vector<UInt_t> header;
494  header.push_back((0x0001 << 16) | (0x10 << 8) | 0xCC);
495  // event type | event data type | event ID (0xCC for CODA event)
496  header.push_back(4); // size of header field
497  header.push_back((0xC000 << 16) | (0x01 << 8) | 0x00);
498  // bank type | bank data type (0x01 for uint32) | bank ID (0x00 for header event)
499  header.push_back(++fEvtNumber); // event number (initialized to 0,
500  // so increment before use to agree with CODA number)
501  header.push_back(1); // event class
502  header.push_back(0); // status summary
503 
504  // Copy the encoded event buffer into an array of integers,
505  // as expected by the CODA routines.
506  // Size of the event buffer in long words
507  int* codabuffer = new int[header.size() + buffer.size() + 1];
508  // First entry contains the buffer size
509  int k = 0;
510  codabuffer[k++] = header.size() + buffer.size();
511  for (size_t i = 0; i < header.size(); i++)
512  codabuffer[k++] = header.at(i);
513  for (size_t i = 0; i < buffer.size(); i++)
514  codabuffer[k++] = buffer.at(i);
515 
516  // Now write the buffer to the stream
517  Int_t status = WriteEvent(codabuffer);
518  // delete the buffer
519  delete[] codabuffer;
520  // and report success or fail
521  return status;
522 }
523 
524 
525 Int_t QwEventBuffer::EncodePrestartEvent(int runnumber, int runtype)
526 {
527  int buffer[5];
528  int localtime = (int) time(0);
529  buffer[0] = 4; // length
530  buffer[1] = ((kPRESTART_EVENT << 16) | (0x01 << 8) | 0xCC);
531  buffer[2] = localtime;
532  buffer[3] = runnumber;
533  buffer[4] = runtype;
534  ProcessPrestart(localtime, runnumber, runtype);
535  return WriteEvent(buffer);
536 }
538 {
539  int buffer[5];
540  int localtime = (int) time(0);
541  int eventcount = 0;
542  buffer[0] = 4; // length
543  buffer[1] = ((kGO_EVENT << 16) | (0x01 << 8) | 0xCC);
544  buffer[2] = localtime;
545  buffer[3] = 0; // (unused)
546  buffer[4] = eventcount;
547  ProcessGo(localtime, eventcount);
548  return WriteEvent(buffer);
549 }
551 {
552  int buffer[5];
553  int localtime = (int) time(0);
554  int eventcount = 0;
555  buffer[0] = 4; // length
556  buffer[1] = ((kPAUSE_EVENT << 16) | (0x01 << 8) | 0xCC);
557  buffer[2] = localtime;
558  buffer[3] = 0; // (unused)
559  buffer[4] = eventcount;
560  ProcessPause(localtime, eventcount);
561  return WriteEvent(buffer);
562 }
564 {
565  int buffer[5];
566  int localtime = (int) time(0);
567  int eventcount = 0;
568  buffer[0] = 4; // length
569  buffer[1] = ((kEND_EVENT << 16) | (0x01 << 8) | 0xCC);
570  buffer[2] = localtime;
571  buffer[3] = 0; // (unused)
572  buffer[4] = eventcount;
573  ProcessEnd(localtime, eventcount);
574  return WriteEvent(buffer);
575 }
576 
577 
579 }
580 
581 
583 {
584  UInt_t local_datatype;
585  UInt_t local_eventtype;
586 
587  fPhysicsEventFlag = kFALSE;
588 
589  if ( buffer[0] == 0 ){
590  /*****************************************************************
591  * This buffer is empty. *
592  *****************************************************************/
593  SetEventLength(1); // Pretend that there is one word.
594  SetWordsSoFar(1); // Mark that we've read the word already.
595  SetEventType(0);
596  fEvtTag = 0;
597  fBankDataType = 0;
598  fIDBankNum = 0;
599  fEvtNumber = 0;
600  fEvtClass = 0;
601  fStatSum = 0;
602  } else {
603  /*****************************************************************
604  * This buffer contains data; fill the event ID parameters. *
605  *****************************************************************/
606  // First word is the number of long-words in the buffer.
607  SetEventLength(buffer[0]+1);
608 
609  // Second word contains the event type, for CODA events.
610  fEvtTag = (buffer[1] & 0xFFFF0000) >> 16; // (bits(31-16));
611  local_datatype = (buffer[1] & 0xFF00) >> 8; // (bits(15-8));
612  fIDBankNum = (buffer[1] & 0xFF); // (bits(7-0));
613  if ( fIDBankNum == 0xCC) {
614  // This is a CODA event bank; the event type is equal to
615  // the event tag.
616  local_eventtype = fEvtTag;
617  SetEventType(local_eventtype);
618  fBankDataType = local_datatype;
619 
620  // local_eventtype is unsigned int and always positive
621  if (/* local_eventtype >= 0 && */ local_eventtype <= 15) {
622  // This is a physics event; record the event number, event
623  // classification, and status summary.
624  fEvtNumber = buffer[4];
625  fEvtClass = buffer[5];
626  fStatSum = buffer[6];
627  fPhysicsEventFlag = kTRUE;
628  // Now skip to the first ROC data bank.
629  SetWordsSoFar(7);
630  } else {
631  // This is not a physics event, but is still in the CODA
632  // event format. The first two words have been examined.
633  fEvtNumber = 0;
634  fEvtClass = 0;
635  fStatSum = 0;
636  SetWordsSoFar(2);
637  // Run this event through the Control event processing.
638  // If it is not a control event, nothing will happen.
640  }
641  } else {
642  // This is not an event in the CODA event bank format,
643  // but it still follows the CEBAF common event format.
644  // Arbitrarily set the event type to "fEvtTag".
645  // The first two words have been examined.
647  fEvtNumber = 0;
648  fEvtClass = 0;
649  fStatSum = 0;
650  SetWordsSoFar(2);
651  }
652  }
653  // std::cout<< Form("Length: %d; Tag: 0x%x; Bank ID num: 0x%x; ",
654  // fEvtLength, fEvtTag, fIDBankNum)
655  // << Form("Evt type: 0x%x; Evt number %d; Evt Class 0x%.8x; ",
656  // fEvtType, fEvtNumber, fEvtClass)
657  // << Form("Status Summary: 0x%.8x; Words so far %d",
658  // fStatSum, fWordsSoFar)
659  // << std::endl;
660 }
661 
662 
664 {
665  /// Passes the data for the configuration events into each subsystem
666  /// object. Each object is responsible for recognizing the configuration
667  /// data which it ought to decode.
668  /// NOTE TO DAQ PROGRAMMERS:
669  /// The configuration event for a ROC must have the same
670  /// subbank structure as the physics events for that ROC.
671  Bool_t okay = kTRUE;
672  UInt_t rocnum = fEvtType - 0x90;
673  QwMessage << "QwEventBuffer::FillSubsystemConfigurationData: "
674  << "Found configuration event for ROC"
675  << rocnum
676  << QwLog::endl;
677  QwMessage << Form("Evt type: 0x%x; Evt number %d; Evt Class 0x%.8x; ",
679  << QwLog::endl;
680  // Loop through the data buffer in this event.
681  UInt_t *localbuff = (UInt_t*)(fEvStream->getEvBuffer());
682  while ((okay = DecodeSubbankHeader(&localbuff[fWordsSoFar]))){
683  // If this bank has further subbanks, restart the loop.
684  if (fSubbankType == 0x10) {
685  QwMessage << "This bank has further subbanks, restart the loop" << QwLog::endl;
686  continue;
687  }
688  // If this bank only contains the word 'NULL' then skip
689  // this bank.
690  if (fFragLength==1 && localbuff[fWordsSoFar]==kNullDataWord){
691  fWordsSoFar += fFragLength;
692  QwMessage << "Skip this bank" << QwLog::endl;
693  continue;
694  }
695 
696  // Subsystems may be configured to accept data in formats
697  // other than 32 bit integer (banktype==1), but the
698  // bank type is not provided. Subsystems must be able
699  // to process their data knowing only the ROC and bank tags.
700  //
701  // After trying the data in each subsystem, bump the
702  // fWordsSoFar to move to the next bank.
703 
704  subsystems.ProcessConfigurationBuffer(rocnum, fSubbankTag,
705  &localbuff[fWordsSoFar],
706  fFragLength);
707  fWordsSoFar += fFragLength;
708  QwDebug << "QwEventBuffer::FillSubsystemConfigurationData: "
709  << "Ending loop: fWordsSoFar=="<<fWordsSoFar
710  <<QwLog::endl;
711  }
712  return okay;
713 }
714 
716 {
717  // Initialize local flag
718  Bool_t okay = kTRUE;
719 
720  // Reload the data buffer and decode the header again, this allows
721  // multiple calls to this function for different subsystem arrays.
722  UInt_t *localbuff = (UInt_t*)(fEvStream->getEvBuffer());
723  DecodeEventIDBank(localbuff);
724 
725  // Clear the old event information from the subsystems.
726  subsystems.ClearEventData();
727 
728  // Pass CODA run, segment, event number and type to the subsystem array.
729  subsystems.SetCodaRunNumber(fCurrentRun);
731  subsystems.SetCodaEventNumber(fEvtNumber);
732  subsystems.SetCodaEventType(fEvtType);
733 
734 
735 
736  // If this event type is masked for the subsystem array, return right away
737  if (((0x1 << (fEvtType - 1)) & subsystems.GetEventTypeMask()) == 0) {
738  return kTRUE;
739  }
740 
741  // Loop through the data buffer in this event.
742  while ((okay = DecodeSubbankHeader(&localbuff[fWordsSoFar]))){
743 
744  // If this bank has further subbanks, restart the loop.
745  if (fSubbankType == 0x10) continue;
746 
747  // If this bank only contains the word 'NULL' then skip
748  // this bank.
749  if (fFragLength == 1 && localbuff[fWordsSoFar]==kNullDataWord) {
750  fWordsSoFar += fFragLength;
751  continue;
752  }
753 
754  // if (fSubbankType == 0x85) {
755  // std::cout << "ProcessEventBuffer: , SubbankTag= "<< fSubbankTag<<" FragLength="<<fFragLength <<std::endl;
756  // }
757 
758 // QwDebug << "QwEventBuffer::FillSubsystemData: "
759 // << "Beginning loop: fWordsSoFar=="<<fWordsSoFar
760 // <<QwLog::endl;
761 
762  // Loop through the subsystems and try to store the data
763  // from this bank in each subsystem.
764  //
765  // Subsystems may be configured to accept data in formats
766  // other than 32 bit integer (banktype==1), but the
767  // bank type is not provided. Subsystems must be able
768  // to process their data knowing only the ROC and bank tags.
769  //
770  // After trying the data in each subsystem, bump the
771  // fWordsSoFar to move to the next bank.
772 
773  if( fROC == 0 && fSubbankTag==0x6101) {
774  //std::cout << "ProcessEventBuffer: ROC="<<fROC<<", SubbankTag="<< fSubbankTag<<", FragLength="<<fFragLength <<std::endl;
775  fCleanParameter[0]=localbuff[fWordsSoFar+fFragLength-4];//clean data
776  fCleanParameter[1]=localbuff[fWordsSoFar+fFragLength-3];//scan data 1
777  fCleanParameter[2]=localbuff[fWordsSoFar+fFragLength-2];//scan data 2
778  //std::cout << "ProcessEventBuffer: ROC="<<fROC<<", SubbankTag="<< fSubbankTag
779  // <<", FragLength="<<fFragLength << " " <<fCleanParameter[0]<< " " <<fCleanParameter[1]<< " " <<fCleanParameter[2]<<std::endl;
780 
781  }
782 
784 
786  &localbuff[fWordsSoFar],
787  fFragLength);
788  fWordsSoFar += fFragLength;
789 // QwDebug << "QwEventBuffer::FillSubsystemData: "
790 // << "Ending loop: fWordsSoFar=="<<fWordsSoFar
791 // <<QwLog::endl;
792  }
793  return okay;
794 }
795 
796 
797 // added all this method for QwEPICSEvent class
799 {
800  // QwVerbose << "QwEventBuffer::FillEPICSData: "
801 // << Form("Length: %d; Tag: 0x%x; Bank ID num: 0x%x; ",
802 // fEvtLength, fEvtTag, fIDBankNum)
803 // << Form("Evt type: 0x%x; Evt number %d; Evt Class 0x%.8x; ",
804 // fEvtType, fEvtNumber, fEvtClass)
805 // << Form("Status Summary: 0x%.8x; Words so far %d",
806 // fStatSum, fWordsSoFar)
807 // << QwLog::endl;
808 
809 
810  ///
811  Bool_t okay = kTRUE;
812  if (! IsEPICSEvent()){
813  okay = kFALSE;
814  return okay;
815  }
816  QwVerbose << "QwEventBuffer::FillEPICSData: "
817  << QwLog::endl;
818  // Loop through the data buffer in this event.
819  UInt_t *localbuff = (UInt_t*)(fEvStream->getEvBuffer());
820  if (fBankDataType==0x10){
821  while ((okay = DecodeSubbankHeader(&localbuff[fWordsSoFar]))){
822  // If this bank has further subbanks, restart the loop.
823  if (fSubbankType == 0x10) continue;
824  // If this bank only contains the word 'NULL' then skip
825  // this bank.
826  if (fFragLength==1 && localbuff[fWordsSoFar]==kNullDataWord){
827  fWordsSoFar += fFragLength;
828  continue;
829  }
830 
831  if (fSubbankType == 0x3){
832  // This is an ASCII string bank. Try to decode it and
833  // pass it to the EPICS class.
834  char* tmpchar = (Char_t*)&localbuff[fWordsSoFar];
835 
836  epics.ExtractEPICSValues(string(tmpchar), GetEventNumber());
837  QwVerbose << "test for GetEventNumber =" << GetEventNumber() << QwLog::endl;// always zero, wrong.
838 
839  }
840 
841 
842  fWordsSoFar += fFragLength;
843 
844 // QwDebug << "QwEventBuffer::FillEPICSData: "
845 // << "Ending loop: fWordsSoFar=="<<fWordsSoFar
846 // <<QwLog::endl;
847 // QwMessage<<"\nQwEventBuffer::FillEPICSData: fWordsSoFar = "<<fWordsSoFar<<QwLog::endl;
848 
849 
850  }
851  } else {
852  // Single bank in the event, use event headers.
853  if (fBankDataType == 0x3){
854  // This is an ASCII string bank. Try to decode it and
855  // pass it to the EPICS class.
856  Char_t* tmpchar = (Char_t*)&localbuff[fWordsSoFar];
857 
858  QwError << tmpchar << QwLog::endl;
859 
860  epics.ExtractEPICSValues(string(tmpchar), GetEventNumber());
861 
862  }
863 
864  }
865 
866  //std::cout<<"\nEpics data coming!! "<<fWordsSoFar<<std::endl;
867 
868  return okay;
869 }
870 
871 
872 Bool_t QwEventBuffer::DecodeSubbankHeader(UInt_t *buffer){
873  // This function will decode the header information from
874  // either a ROC bank or a subbank. It will also bump
875  // fWordsSoFar to be referring to the first word of
876  // the subbank's data.
877  //
878  // NOTE TO DAQ PROGRAMMERS:
879  // All internal subbank tags MUST be defined to
880  // be greater than 31.
881  Bool_t okay = kTRUE;
882  if (fWordsSoFar == fEvtLength){
883  // We have reached the end of this event.
884  okay = kFALSE;
885  } else {
886  fFragLength = buffer[0] - 1; // This is the number of words in the data block
887  fSubbankTag = (buffer[1]&0xFFFF0000)>>16; // Bits 16-31
888  fSubbankType = (buffer[1]&0xFF00)>>8; // Bits 8-15
889  fSubbankNum = (buffer[1]&0xFF); // Bits 0-7
890  if (fSubbankTag<=31){
891  // Subbank tags between 0 and 31 indicate this is
892  // a ROC bank.
893  fROC = fSubbankTag;
894  fSubbankTag = 0;
895  }
897  // Trouble, because we'll have too many words!
898  QwError << "fWordsSoFar+2+fFragLength=="<<fWordsSoFar+2+fFragLength
899  << " and fEvtLength==" << fEvtLength
900  << QwLog::endl;
901  okay = kFALSE;
902  }
903  fWordsSoFar += 2;
904  }
905  return okay;
906 }
907 
908 
909 const TString& QwEventBuffer::DataFile(const UInt_t run, const Short_t seg = -1)
910 {
911  TString basename = fDataFileStem + Form("%u.",run) + fDataFileExtension;
912  if(seg == -1){
913  fDataFile = fDataDirectory + basename;
914  } else {
915  fDataFile = fDataDirectory + basename + Form(".%d",seg);
916  }
917  return fDataFile;
918 }
919 
920 
922 {
923  glob_t globbuf;
924 
925  TString searchpath;
926  TString scanvalue;
927  Int_t local_segment;
928 
929  std::vector<Int_t> tmp_segments;
930  std::vector<Int_t> local_index;
931 
932  /* Clear and set up the fRunSegments vector. */
933  tmp_segments.clear();
934  fRunSegments.clear();
935  fRunSegments.resize(0);
936  fRunIsSegmented = kFALSE;
937 
938  searchpath = fDataFile;
939  glob(searchpath.Data(), GLOB_ERR, NULL, &globbuf);
940 
941  if (globbuf.gl_pathc == 1){
942  /* The base file name exists. *
943  * Do not look for file segments. */
944  fRunIsSegmented = kFALSE;
945 
946  } else {
947  /* The base file name does not exist. *
948  * Look for file segments. */
949  QwWarning << "File " << fDataFile << " does not exist!\n"
950  << " Trying to find run segments for run "
951  << fCurrentRun << "... ";
952 
953  searchpath.Append(".[0-9]*");
954  glob(searchpath.Data(), GLOB_ERR, NULL, &globbuf);
955 
956  if (globbuf.gl_pathc == 0){
957  /* There are no file segments and no base file *
958  * Produce and error message and exit. */
959  QwError << "\n There are no file segments either!!" << QwLog::endl;
960 
961  // This could mean a single gzipped file!
962  fRunIsSegmented = kFALSE;
963 
964  } else {
965  /* There are file segments. *
966  * Determine the segment numbers and fill fRunSegments *
967  * to indicate the existing file segments. */
968 
969  QwMessage << "OK" << QwLog::endl;
970  scanvalue = fDataFile + ".%d";
971 
972  /* Get the list of segment numbers in file listing *
973  * order. */
974  for (size_t iloop=0;iloop<globbuf.gl_pathc;++iloop){
975  /* Extract the segment numbers from the file name. */
976  sscanf(globbuf.gl_pathv[iloop], scanvalue.Data(), &local_segment);
977  tmp_segments.push_back(local_segment);
978  }
979  local_index.resize(tmp_segments.size(),0);
980  /* Get the list of segments sorted numerically in *
981  * increasing order. */
982  TMath::Sort(static_cast<int>(tmp_segments.size()),&(tmp_segments[0]),&(local_index[0]),
983  kFALSE);
984  /* Put the segments into numerical order in fRunSegments. Add *
985  * only those segments requested (though always add segment 0). */
986  QwMessage << " Found the segment(s): ";
987  size_t printed = 0;
988  for (size_t iloop=0; iloop<tmp_segments.size(); ++iloop){
989  local_segment = tmp_segments[local_index[iloop]];
990  if (printed++) QwMessage << ", ";
991  QwMessage << local_segment ;
992  if (local_segment == 0 ||
993  ( fSegmentRange.first <= local_segment &&
994  local_segment <= fSegmentRange.second ) ) {
995  fRunSegments.push_back(local_segment);
996  } else {
997  QwMessage << " (skipped)" ;
998  }
999  }
1000  QwMessage << "." << QwLog::endl;
1002 
1003  fRunIsSegmented = kTRUE;
1004 
1005  /* If the first requested segment hasn't been found,
1006  forget everything. */
1007  if ( local_segment < fSegmentRange.first ) {
1008  QwError << "First requested run segment "
1009  << fSegmentRange.first << " not found.\n";
1010  fRunSegments.pop_back();
1012  fRunIsSegmented = kTRUE; // well, it is true.
1013  }
1014  }
1015  }
1016  globfree(&globbuf);
1017  return fRunIsSegmented;
1018 }
1019 
1020 //------------------------------------------------------------
1021 
1023 {
1024  Int_t status = kFileHandleNotConfigured;
1025  Int_t last_runsegment;
1026  if (fRunIsSegmented){
1027  last_runsegment = *fRunSegmentIterator;
1029  if (fRunSegmentIterator <= fRunSegments.end()){
1030  QwMessage << "Closing run segment " << last_runsegment <<"."
1031  << QwLog::endl;
1032  status = CloseDataFile();
1033  }
1034  } else {
1035  // Don't try to close a nonsegmented file; we will explicitly
1036  // use CloseDataFile() later.
1037  }
1038  return status;
1039 }
1040 
1041 //------------------------------------------------------------
1042 
1044 {
1045  Int_t status;
1046  if (! fRunIsSegmented){
1047  /* We are processing a non-segmented run. *
1048  * We should not have entered this routine, but *
1049  * since we are here, don't do anything. */
1050  status = kRunNotSegmented;
1051 
1052  } else if (fRunSegments.size()==0){
1053  /* There are actually no file segments located. *
1054  * Return "kNoNextDataFile", but don't print an *
1055  * error message. */
1056  status = kNoNextDataFile;
1057 
1058  } else if (fRunSegmentIterator >= fRunSegments.begin() &&
1059  fRunSegmentIterator < fRunSegments.end() ) {
1060  QwMessage << "Trying to open run segment " << *fRunSegmentIterator << QwLog::endl;
1062 
1063  } else if (fRunSegmentIterator == fRunSegments.end() ) {
1064  /* We have reached the last run segment. */
1065  QwMessage << "There are no run segments remaining." << QwLog::endl;
1066  status = kNoNextDataFile;
1067 
1068  } else {
1069  QwError << "QwEventBuffer::OpenNextSegment(): Unrecognized error" << QwLog::endl;
1070  status = CODA_ERROR;
1071  }
1072  return status;
1073 }
1074 
1075 
1076 //------------------------------------------------------------
1077 //call this routine if we've selected the run segment by hand
1078 Int_t QwEventBuffer::OpenDataFile(UInt_t current_run, Short_t seg)
1079 {
1080  fCurrentRun = current_run;
1081 
1082  fRunSegments.clear();
1083  fRunIsSegmented = kTRUE;
1084 
1085  fRunSegments.push_back(seg);
1087  return OpenNextSegment();
1088 }
1089 
1090 //------------------------------------------------------------
1091 //call this routine if the run is not segmented
1092 Int_t QwEventBuffer::OpenDataFile(UInt_t current_run, const TString rw)
1093 {
1094  Int_t status;
1095  fCurrentRun = current_run;
1097  if (DataFileIsSegmented()){
1098  status = OpenNextSegment();
1099  } else {
1100  status = OpenDataFile(DataFile(fCurrentRun),rw);
1101  }
1102  return status;
1103 }
1104 
1105 
1106 
1107 //------------------------------------------------------------
1108 Int_t QwEventBuffer::OpenDataFile(const TString filename, const TString rw)
1109 {
1111  QwDebug << "QwEventBuffer::OpenDataFile: File handle doesn't exist.\n"
1112  << " Try to open a new file handle!"
1113  << QwLog::endl;
1114  fEvStream = new THaCodaFile();
1116  } else if (fEvStreamMode!=fEvStreamFile){
1117  QwError << "QwEventBuffer::OpenDataFile: The stream is not configured as an input\n"
1118  << " file stream! Can't deal with this!\n"
1119  << QwLog::endl;
1120  exit(1);
1121  }
1122  fDataFile = filename;
1123 
1124  if (rw.Contains("w",TString::kIgnoreCase)) {
1125  // If we open a file for write access, let's suppose
1126  // we've given the path we want to use.
1127  QwMessage << "Opening data file: " << fDataFile << QwLog::endl;
1128  } else {
1129  // Let's try to find the data file for read access.
1130  glob_t globbuf;
1131  glob(fDataFile.Data(), GLOB_ERR, NULL, &globbuf);
1132  if (globbuf.gl_pathc == 0){
1133  // Can't find the file; try in the "fDataDirectory".
1134  fDataFile = fDataDirectory + filename;
1135  glob(fDataFile.Data(), GLOB_ERR, NULL, &globbuf);
1136  }
1137  if (globbuf.gl_pathc == 0){
1138  // Can't find the file; try gzipped.
1139  fDataFile = filename + ".gz";
1140  glob(fDataFile.Data(), GLOB_ERR, NULL, &globbuf);
1141  }
1142  if (globbuf.gl_pathc == 0){
1143  // Can't find the file; try gzipped in the "fDataDirectory".
1144  fDataFile = fDataDirectory + filename + ".gz";
1145  glob(fDataFile.Data(), GLOB_ERR, NULL, &globbuf);
1146  }
1147  if (globbuf.gl_pathc == 1){
1148  QwMessage << "Opening data file: " << fDataFile << QwLog::endl;
1149  } else {
1150  fDataFile = filename;
1151  QwError << "Unable to find "
1152  << filename.Data() << " or "
1153  << (fDataDirectory + filename).Data() << QwLog::endl;
1154  }
1155  globfree(&globbuf);
1156  }
1157  return fEvStream->codaOpen(fDataFile, rw);
1158 }
1159 
1160 
1161 //------------------------------------------------------------
1163 {
1164  Int_t status = kFileHandleNotConfigured;
1166  status = fEvStream->codaClose();
1167  }
1168  return status;
1169 }
1170 
1171 //------------------------------------------------------------
1172 Int_t QwEventBuffer::OpenETStream(TString computer, TString session, int mode,
1173  const TString stationname)
1174 {
1175  Int_t status = CODA_OK;
1177 #ifdef __CODA_ET
1178  if (stationname != ""){
1179  fEvStream = new THaEtClient(computer, session, mode, stationname);
1180  } else {
1181  fEvStream = new THaEtClient(computer, session, mode);
1182  }
1184 #endif
1185  }
1186  return status;
1187 }
1188 
1189 //------------------------------------------------------------
1191 {
1192  Int_t status = kFileHandleNotConfigured;
1194  status = fEvStream->codaClose();
1195  }
1196  return status;
1197 }
void SetWordsSoFar(const ULong_t tmpwords)
enum QwEventBuffer::CodaStreamMode fEvStreamMode
QwParameterFile * fEventListFile
#define QwMessage
Predefined log drain for regular messages.
Definition: QwLog.h:50
TString fDataFile
Bool_t DataFileIsSegmented()
Bool_t DecodeSubbankHeader(UInt_t *buffer)
Bool_t globalEXIT
Bool_t GetNextRunRange()
Read the next requested run range, return true if success.
Bool_t FillSubsystemConfigurationData(QwSubsystemArray &subsystems)
Int_t EncodePauseEvent()
Definition of the pure virtual base class of all subsystems.
TString fETStationName
UInt_t fSubbankType
#define default_bool_value(b)
Definition: QwOptions.h:51
Int_t GetEtEvent()
Int_t GetSegmentNumber() const
Return CODA file segment number.
Definition: QwEventBuffer.h:82
Int_t CloseThisSegment()
TString fDataDirectory
Int_t ReOpenStream()
UInt_t fWordsSoFar
An options class.
Definition: QwOptions.h:133
void SetEventType(const UInt_t tmptype)
Int_t ProcessEvBuffer(const UInt_t event_type, const UInt_t roc_id, const UInt_t bank_id, UInt_t *buffer, UInt_t num_words)
Process the event buffer for events.
std::vector< Int_t > fRunSegments
void ProcessOptions(QwOptions &options)
Sets internal flags based on the QwOptions.
Int_t WriteFileEvent(int *buffer)
UInt_t fStartingPhysicsEvent
bool HasValue(const std::string &key)
Has this key been defined.
Definition: QwOptions.h:233
Int_t EncodeEndEvent()
Int_t OpenNextStream()
Opens the event stream (file or ET) based on the internal flags.
UInt_t GetEventTypeMask() const
Get event type mask.
po::options_description_easy_init AddDefaultOptions()
Add a default option.
Definition: QwOptions.h:159
UInt_t fSubbankNum
void SetCodaEventNumber(UInt_t evtnum)
Set the internal record of the CODA event number.
Int_t CloseStream()
Closes a currently open event stream.
std::pair< Int_t, Int_t > fRunRange
void TrimComment(const char commentchar)
Int_t GetFileEvent()
void sigint_handler(int sig)
#define QwVerbose
Predefined log drain for verbose messages.
Definition: QwLog.h:55
Int_t OpenETStream(TString computer, TString session, int mode, const TString stationname="")
UInt_t fSubbankTag
Int_t OpenNextSegment()
void EncodeEventData(std::vector< UInt_t > &buffer)
Encode the data in this event.
Int_t OpenDataFile(UInt_t current_run, Short_t seg)
Bool_t GetNextEventRange()
Read the next requested event range, return true if success.
TString fETSession
po::options_description_easy_init AddOptions(const std::string &blockname="Specialized options")
Add an option to a named block or create new block.
Definition: QwOptions.h:164
void SetCleanParameters(Double_t cleanparameter[3])
Set the internal record of the CODA event number.
Int_t GetEventNumber()
QwParameterFile * ReadNextSection(std::string &secname, const bool keep_header=false)
THaCodaData * fEvStream
void ExtractEPICSValues(const string &data, int event)
std::string fRunListFileName
Int_t GetNextEvent()
T GetValue(const std::string &key)
Get a templated value.
Definition: QwOptions.h:240
void ProcessPause(UInt_t local_time, UInt_t evt_count)
Int_t EncodePrestartEvent(int runnumber, int runtype=0)
void SetCodaRunNumber(UInt_t runnum)
Set the internal record of the CODA run number.
Double_t fCleanParameter[3]
Scan data/clean data from the green monster.
std::pair< int, int > GetIntValuePair(const std::string &key)
Get a pair of integer values.
Definition: QwOptions.cc:332
TString fDataFileExtension
Bool_t fRunIsSegmented
#define QwDebug
Predefined log drain for debugging output.
Definition: QwLog.h:60
static const UInt_t kNullDataWord
Definition: QwEventBuffer.h:48
void ProcessControlEvent(UInt_t evtype, UInt_t *buffer)
void DecodeEventIDBank(UInt_t *buffer)
Int_t WriteEvent(int *buffer)
QwParameterFile * fRunListFile
QwEventBuffer()
Default constructor.
std::string GetLine()
Int_t EncodeSubsystemData(QwSubsystemArray &subsystems)
void SetEventLength(const ULong_t tmplength)
Int_t GetRunNumber() const
Return CODA file run number.
Definition: QwEventBuffer.h:80
Bool_t ReadNextLine()
static std::string fDefaultDataFileStem
std::vector< Int_t >::iterator fRunSegmentIterator
static void DefineOptions(QwOptions &options)
std::pair< Int_t, Int_t > fSegmentRange
Bool_t fChainDataFiles
static std::pair< int, int > ParseIntRange(const std::string &separatorchars, const std::string &range)
Parse a range of integers as #:# where either can be missing.
Bool_t IsEPICSEvent()
void ProcessPrestart(UInt_t local_time, UInt_t local_runnumber, UInt_t local_runtype)
TStopwatch fRunTimer
Timer used for runlet processing loop.
Int_t EncodeGoEvent()
static const Int_t kNoNextDataFile
Definition: QwEventBuffer.h:45
static const Int_t kFileHandleNotConfigured
Definition: QwEventBuffer.h:46
Bool_t FillSubsystemData(QwSubsystemArray &subsystems)
void PrintRunTimes()
Int_t ProcessConfigurationBuffer(const UInt_t roc_id, const UInt_t bank_id, UInt_t *buffer, UInt_t num_words)
Process the event buffer for configuration events.
static std::string fDefaultDataFileExtension
void ProcessGo(UInt_t local_time, UInt_t evt_count)
static std::ostream & endl(std::ostream &)
End of the line.
Definition: QwLog.cc:299
Int_t CloseDataFile()
Int_t CloseETStream()
UInt_t fNumPhysicsEvents
UInt_t fBankDataType
An options class which parses command line, config file and environment.
Bool_t fPhysicsEventFlag
TString GetRunLabel() const
Returns a string like &lt;run#&gt; or &lt;run#&gt;.&lt;file#&gt;
Bool_t FillEPICSData(QwEPICSEvent &epics)
void TrimWhitespace(TString::EStripType head_tail=TString::kBoth)
TString fDataFileStem
Bool_t IsPhysicsEvent()
void SetCodaEventType(UInt_t evttype)
Set the internal record of the CODA event type.
#define QwWarning
Predefined log drain for warnings.
Definition: QwLog.h:45
std::pair< UInt_t, UInt_t > fEventRange
void SetCodaSegmentNumber(UInt_t segnum)
Set the internal record of the CODA segment number.
UInt_t fEvtNumber
CODA event number; only defined for physics events.
void ProcessEnd(UInt_t local_time, UInt_t evt_count)
const TString & DataFile(const UInt_t run, const Short_t seg)
TString fETHostname
Bool_t GetNextRunNumber()
Get the next run in the active run range, proceed to next range if needed.
static const Int_t kRunNotSegmented
Definition: QwEventBuffer.h:44
TStopwatch fStopwatch
Timer used for internal timing.
UInt_t fFragLength
#define QwError
Predefined log drain for errors.
Definition: QwLog.h:40