Hall-D Software  alpha
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
HDEVIOWriter.cc
Go to the documentation of this file.
1 // $Id$
2 //
3 // File: HDEVIOWriter.cc
4 //
5 
6 #include <unistd.h>
7 
8 #include <cstddef>
9 using namespace std;
10 
11 #include "HDEVIOWriter.h"
12 #include "hdbyte_swapout.h"
13 
14 #include <JANA/JApplication.h>
15 #include <JANA/JParameterManager.h>
16 using namespace jana;
17 
18 //---------------------------------
19 // HDEVIOWriter (Constructor)
20 //---------------------------------
21 HDEVIOWriter::HDEVIOWriter(string sink_name)
22 {
23  pthread_mutex_init(&output_deque_mutex, NULL);
24  pthread_mutex_init(&buff_pool_mutex,NULL);
25 
26  quit = false;
27 
28  // Initialize EVIO channel pointer to NULL (subclass will instantiate and open)
29  sink_type = kNoSink;
30  events_written_to_output = 0;
31  blocks_written_to_output = 0;
32  ofs_debug_output= NULL;
33 
34  MAX_OUTPUT_QUEUE_SIZE = 200; // in buffers/events
35  MAX_OUTPUT_BUFFER_SIZE = 0; // in words (0=AUTO)
36  MAX_HOLD_TIME = 2; // in seconds
37  NEVENTS_PER_BLOCK = 100; // suggested number of events per EVIO block
38  DEBUG_FILES = false;
39 
40  if(gPARMS){
41  // We want the default for MAX_OUTPUT_BUFFER_SIZE to be "AUTO" so that it can be set
42  // based on the ET system evnt size. This means the type of the config. variable
43  // must be a string.
44  string max_output_buffer = "AUTO";
45 
46  gPARMS->SetDefaultParameter("EVIOOUT:MAX_OUTPUT_QUEUE_SIZE" , MAX_OUTPUT_QUEUE_SIZE, "Maximum number of events output queue can have before processing threads start blocking.");
47  gPARMS->SetDefaultParameter("EVIOOUT:MAX_OUTPUT_BUFFER_SIZE", max_output_buffer, "Maximum number of words in output EVIO block. This may be overwritten by ET event size if writing to ET.");
48  gPARMS->SetDefaultParameter("EVIOOUT:MAX_HOLD_TIME", MAX_HOLD_TIME, "Maximum time in seconds to keep events in buffer before flushing them. This is to prevent farm from witholding events from ER when running very slow trigger rates. This should not be set lesst than 2.");
49  gPARMS->SetDefaultParameter("EVIOOUT:NEVENTS_PER_BLOCK", NEVENTS_PER_BLOCK, "Suggested number of events to write in single output block.");
50  gPARMS->SetDefaultParameter("EVIOOUT:DEBUG_FILES" , DEBUG_FILES, "Write input and output debug files in addition to the standard output.");
51 
52  // Check if user specified max max buffer size
53  if( max_output_buffer != "AUTO" ){
54  MAX_OUTPUT_BUFFER_SIZE = atoi(max_output_buffer.c_str());
55  }
56  }
57 
58  // Try opening the output for writing
59  try {
60 
61  // Check if we are outputting to ET or file
62  if(sink_name.substr(0,3) == "ET:"){
63 
64  // Connect to ET system. Throws exception if not successful.
65  ConnectToET(sink_name);
66  sink_type = kETSink;
67 
68  }else{
69  // Create EVIO file. Throws exception if not successful
70  jout << " Opening EVIO output file \"" << sink_name << "\"" << endl;
71  evioout = new ofstream(sink_name.c_str());
72  if(!evioout) throw JException("Unable to create ofstream object for output EVIO file");
73  if(!evioout->is_open()) throw JException("Unable to open output EVIO file");
74 
75  sink_type = kFileSink;
76  jout << "Opened file \"" << sink_name << "\" for writing EVIO events." << endl;
77  }
78 
79  } catch (exception &e) {
80 
81  // Unable to open output. Throw exception, informing user
82  jerr << e.what() << endl;
83  throw e;
84  }
85 
86  // Check if MAX_OUTPUT_BUFFER_SIZE is 0 meaning automatically set.
87  // At this point, it should have been set by the ET system size if
88  // we are writing to an ET system so if it is not, then we should
89  // set it to something reasonable.
90  if( MAX_OUTPUT_BUFFER_SIZE == 0 ) MAX_OUTPUT_BUFFER_SIZE = 250*1024; // = 1MB
91 
92  // Optionally open output file for debugging
93  if(DEBUG_FILES){
94  ofs_debug_output = new ofstream("hdevio_debug_output.evio");
95  if( !ofs_debug_output->is_open() ){
96  jerr << "Unable to open \"hdevio_debug_output.evio\"!" << endl;
97  delete ofs_debug_output;
98  ofs_debug_output = NULL;
99  }else{
100  jout << "Opened \"hdevio_debug_output.evio\" for debug output" << endl;
101  }
102  }
103 }
104 
105 //---------------------------------
106 // ~HDEVIOWriter (Destructor)
107 //---------------------------------
109 {
110 /*
111  // Write out any events that are still enqueued
112  if( !output_deque.empty() ){
113  uint32_t Nwords = 8; // include 8 header words for EVIO block
114  deque< vector<uint32_t>* >::iterator it;
115  for(it=output_deque.begin(); it!=output_deque.end(); it++){
116  Nwords += (*it)->size();
117  }
118  FlushOutput(Nwords, output_deque);
119  }
120 */
121  // Free up any memory used in the buffer pool
122  pthread_mutex_lock(&buff_pool_mutex);
123  for(uint32_t i=0; i<buff_pool.size(); i++) delete buff_pool[i];
124  buff_pool.clear();
125  pthread_mutex_unlock(&buff_pool_mutex);
126 
127  if(evioout){
128  // Write out just an EVIO block header to specify end-of-file
129  deque< vector<uint32_t>* > my_output_deque; // no data, just header
130  FlushOutput(8, my_output_deque);
131 
132  evioout->close();
133  delete evioout;
134  }
135 }
136 
137 
138 //----------------
139 // ConnectToET
140 //----------------
141 void HDEVIOWriter::ConnectToET(string sink_name)
142 {
143 #ifdef HAVE_ET
144 
145  /// Format for ET sink strings is:
146  ///
147  /// ET:session:host:port
148  ///
149  /// The session is used to form the filename of the ET
150  /// system. For example, if an session of "eb" is specified,
151  /// then a file named "/tmp/et_sys_eb" is assumed to be
152  /// what should be opened. If no session is specified (or
153  /// an empty session name) then "none" is used as the session.
154  ///
155  /// If the host is specified, then an attempt will be made
156  /// to open that system. If it is not specified, then
157  /// it will attempt to open an ET system on the local machine.
158  ///
159  /// If port is specified, it is used as the TCP port number
160  /// on the remote host to attach to. If the host is not
161  /// specified (i.e. by having two colons and therefore
162  /// an empty string) then the port is ignored. If the
163  /// port is omitted or specified as "0", then the default
164  /// port is used.
165  ///
166 
167  // Split sink name into session, host, etc...
168  vector<string> fields;
169  string str = sink_name;
170  size_t startpos=0, endpos=0;
171  while((endpos = str.find(":", startpos)) != str.npos){
172  size_t len = endpos-startpos;
173  fields.push_back(len==0 ? "":str.substr(startpos, len));
174  startpos = endpos+1;
175  }
176  if(startpos<str.length()) fields.push_back(str.substr(startpos, str.npos));
177 
178  string session = fields.size()>1 ? fields[1]:"";
179  string host = fields.size()>2 ? fields[2]:"";
180  int port = fields.size()>3 ? atoi(fields[3].c_str()):0;
181 
182  if(session == "") session = "none";
183  string fname = session.at(0)=='/' ? session:(string("/tmp/et_sys_") + session);
184 
185  // Report to user what we're doing
186  jout << " Opening ET system:" << endl;
187  jout << " session: " << session << endl;
188  jout << " system file: " << fname << endl;
189  if(host!=""){
190  jout << " host: "<<host << endl;
191  if(port !=0) jout << " port: " << port << endl;
192  }
193 
194  // connect to the ET system
195  et_openconfig openconfig;
196  et_open_config_init(&openconfig);
197  if(host != ""){
198  if(host.find("239.")==0){
199  cout<<__FILE__<<":"<<__LINE__<<" Configuring output ET for multicast" << endl;
200  et_open_config_setcast(openconfig, ET_MULTICAST);
201  et_open_config_addmulticast(openconfig, host.c_str());
202  et_open_config_sethost(openconfig, ET_HOST_ANYWHERE);
203  et_open_config_setport(openconfig, port);
204  struct timespec tspec={5,5};
205  et_open_config_settimeout(openconfig, tspec);
206  et_open_config_setwait(openconfig, ET_OPEN_WAIT);
207  }else{
208  cout<<__FILE__<<":"<<__LINE__<<" Configuring output ET for direct connection" << endl;
209  et_open_config_setcast(openconfig, ET_DIRECT);
210  et_open_config_setmode(openconfig, ET_HOST_AS_LOCAL); // ET_HOST_AS_LOCAL or ET_HOST_AS_REMOTE
211  et_open_config_sethost(openconfig, host.c_str());
212  et_open_config_setport(openconfig, ET_BROADCAST_PORT);
213  if(port != 0) et_open_config_setserverport(openconfig, port);
214  }
215  }
216  int status = et_open(&sys_id,fname.c_str(),openconfig);
217  if(status!=ET_OK){
218  cout<<__FILE__<<":"<<__LINE__<<" Problem opening ET system"<<endl;
219  cout<< et_perror(status);
220  exit(0);
221  return;
222  }
223 
224  // Attach to the Grand Central station
225  status=et_station_attach(sys_id, ET_GRANDCENTRAL, &att_id);
226  if(status!=ET_OK) {
227  et_close(sys_id);
228  jerr << "Unable to attach to Grand Central station " << endl;
229  cout<< et_perror(status);
230  exit(0);
231  return;
232  }
233 
234  jout << "...now connected to ET system: " << fname
235  << ", station: Grand Central " << " ( attach id=" << att_id <<")" << endl;
236 
237  // Make sure the size of event buffers we will allocate are at least as big
238  // as the event size used in the ET system
239  size_t eventsize;
240  et_system_geteventsize(sys_id, &eventsize);
241  uint32_t eventsize_words = (uint32_t)eventsize/4;
242  if( eventsize_words < MAX_OUTPUT_BUFFER_SIZE ){
243  jout<<" Events in ET system are smaller than currently set max buffer size:"<<endl;
244  jout<<" "<<eventsize_words<<" < "<<MAX_OUTPUT_BUFFER_SIZE<<endl;
245  jout<<" Setting MAX_OUTPUT_BUFFER_SIZE to "<<eventsize_words<<endl;
246  MAX_OUTPUT_BUFFER_SIZE = eventsize_words;
247  }else if(MAX_OUTPUT_BUFFER_SIZE==0){
248  jout<<" Auto-setting MAX_OUTPUT_BUFFER_SIZE to ET event size." << endl;
249  MAX_OUTPUT_BUFFER_SIZE = eventsize_words;
250  }
251 
252  jout<<" ET system event size in words:"<<eventsize_words<<" MAX_OUTPUT_BUFFER_SIZE:"<<MAX_OUTPUT_BUFFER_SIZE<<endl;
253 
254 #else
255  jerr << endl;
256  jerr << "You are attempting to connect to an ET system using a binary that" <<endl;
257  jerr << "was compiled without ET support. Please reconfigure and recompile" <<endl;
258  jerr << "To get ET support." << endl;
259  jerr << endl;
260  throw exception();
261 #endif
262 }
263 
264 //---------------------------------
265 // L3OutputThread (C-style wrapper for method)
266 //---------------------------------
267 void* HDEVIOOutputThread(void *evioout)
268 {
269  // C-style routine for launching pthread. This
270  // just calls the HDEVIOOutputThread() method of
271  // the given HDEVIOWriter object.
272  return ((HDEVIOWriter*)evioout)->HDEVIOOutputThread();
273 }
274 
275 //---------------------------------
276 // L3OutputThread
277 //---------------------------------
279 {
280  /// This is run in a dedicated thread and is responsible
281  /// for writing events to the ouput, one at a time.
282  /// EVIO buffers, one per event, are created by the
283  /// processing threads using the WriteEvent method of
284  /// this class. It stores them in a queue which this
285  /// thread monitors, writing out events as it finds
286  /// them. If the queue is empty, this thread will
287  /// sleep until either one becomes available, or the
288  /// thread is told to Quit.
289  time_t last_time = time(NULL);
290 
291  while(!quit){
292 
293  time_t t = time(NULL);
294 
295  // Lock output_deque_mutex
296  pthread_mutex_lock(&output_deque_mutex);
297 
298  // Count how many events will bring us up to
299  // MAX_OUTPUT_BUFFER_SIZE without going over. We'll
300  // need this to help decide if we're going to write
301  // out a block and if so, which events to write to it.
302  uint32_t Nbuffs = 0;
303  uint32_t Nwords = 8; // include 8 header words for EVIO block
304  deque< vector<uint32_t>* >::iterator it;
305  for(it=output_deque.begin(); it!=output_deque.end(); it++){
306  uint32_t N = (*it)->size();
307  if( (Nwords+N) > MAX_OUTPUT_BUFFER_SIZE) break;
308  Nbuffs++;
309  Nwords += N;
310 
311  //cout << " buff " << Nbuffs << " words = " << N << " total words = " << Nwords << endl;
312 
313  // If we've reached NEVENTS_PER_BLOCK then stop counting
314  if( Nbuffs >= NEVENTS_PER_BLOCK ) break;
315  }
316 
317  // Check if we've exceeded MAX_OUTPUT_BUFFER_SIZE by
318  // checking if there are more than Nbuffs buffers
319  bool flush_event = Nbuffs < output_deque.size();
320 
321  // Check if the number of events in the deque has
322  // reached NEVENTS_PER_BLOCK. (redundant with above check)
323  if(!flush_event) flush_event = output_deque.size() >= NEVENTS_PER_BLOCK;
324 
325  // Check if the amount of time that's passed since we
326  // last wrote an event exceeds MAX_HOLD_TIME.
327  if(!flush_event && !output_deque.empty()) flush_event = (t-last_time) >= MAX_HOLD_TIME;
328 
329  // If we're not ready to write an EVIO block, then go to sleep
330  // for a bit and try again.
331  if(!flush_event){
332  pthread_mutex_unlock(&output_deque_mutex);
333  if(quit) break; // don't go to sleep just as we're quitting
334  usleep(100);
335 
336  if(japp && japp->GetQuittingStatus()) quit=true;
337  continue;
338  }
339 
340  // Make sure we're writing out at least one event
341  // This should never happen, unless we're being passed some huge events...
342  if(flush_event && (Nbuffs==0))
343  Nbuffs = 1;
344 
345  // Make copy of first Nbuffs buffer pointers so we can do the
346  // expensive copying of their contents into the single output
347  // buffer outside of the output_deque_mutex lock.
348  deque< vector<uint32_t>* > my_output_deque(output_deque.begin(), output_deque.begin()+Nbuffs);
349  output_deque.erase(output_deque.begin(), output_deque.begin()+Nbuffs);
350 
351  // Unlock mutex so other threads can access output_deque
352  pthread_mutex_unlock(&output_deque_mutex);
353 
354  // Write the buffers to the output
355  FlushOutput(Nwords, my_output_deque);
356 
357  // Update record of the last time we flushed a block
358  last_time = t;
359  }
360 
361  // Write any remaining words to output
362  pthread_mutex_lock(&output_deque_mutex);
363  if( !output_deque.empty() ){
364  uint32_t Nwords = 8; // include 8 header words for EVIO block
365  deque< vector<uint32_t>* >::iterator it;
366  for(it=output_deque.begin(); it!=output_deque.end(); it++){
367  Nwords += (*it)->size();
368  }
369  FlushOutput(Nwords, output_deque);
370  }
371  pthread_mutex_unlock(&output_deque_mutex);
372 
373  return NULL;
374 }
375 
376 //---------------------------------
377 // FlushOutput
378 //---------------------------------
379 void HDEVIOWriter::FlushOutput(uint32_t Nwords, deque< vector<uint32_t>* > &my_output_deque)
380 {
381  /// Write the given buffer to the output channel (either
382  /// file or ET). This is called from the dedicated output
383  /// thread and should not be called from anywhere else.
384  /// If it is unable to write the buffer to the output for
385  /// any reason, then a JException is thrown.
386  /// The size of the buffer is taken from the first word
387  /// which is assumed to be the number of 32-bit words in
388  /// the buffer, not counting the leading length word. Thus,
389  /// a total of (buff[0]+1)*4 bytes is taken as the total
390  /// size of the buffer.
391 
392  // ---- Reuse the single output buffer ----
393  // Write EVIO block header
394  // -- the following comment copied from epicst2et.cc -----
395  // This is worth a note since it took me a couple of days to figure this
396  // out: The 6th word is the Bit Info/Version word. The documentation at
397  // the top of the BlockHeaderV4.java file describes the bits, but the numbers
398  // start from "1", not 0. Therefore to set "bit 10" we really need to add
399  // (1<<9). This bit turns out to be crucial since it tells the Event Recorder
400  // that there are no more events stacked in the ET event. Without it, the ER
401  // will assume there are more, encounter nonsense bytes, and then report that
402  // the buffer is not in EVIO 4 format. The (4<<10) bit should signify that
403  // this is "user data" though it is unclear if that affects anything. The
404  // final "4" indicates this is EVIO version 4.
405  //--------------------------------------------------------
406  uint32_t bitinfo = (1<<9) + (1<<10); // (1<<9)=Last event in ET stack, (1<<10)="Physics" payload
407  output_block.reserve(Nwords); // pre-allocate if needed for efficiency
408  output_block.resize(8);
409  output_block[0] = Nwords; // Number of 32 bit words in evio block, (already includes 8 for block header)
410  output_block[1] = ++blocks_written_to_output; // Block number
411  output_block[2] = 8; // Length of block header (words)
412  output_block[3] = my_output_deque.size(); // Event Count
413  output_block[4] = 0; // Reserved 1
414  output_block[5] = (bitinfo<<8) + 0x4; // 0x4=EVIO version 4
415  output_block[6] = 0; // Reserved 2
416  output_block[7] = 0xc0da0100; // Magic number
417 
418  //jout << "Writing out " << my_output_deque.size() << " events with " << Nwords << " words " << endl;
419 
420  // Write all event buffers into output buffer
421  deque< vector<uint32_t>* >::iterator it;
422  for(it=my_output_deque.begin(); it!=my_output_deque.end(); it++){
423  vector<uint32_t> *buff = *it;
424 
425  // For this to work correctly with CODA the event MUST be big endian.
426  // This is because the ER (and all other CODA components) are JAVA
427  // programs which insist on big endian as the data format regradless
428  // of the endianess of the processor used. Our computers all
429  // use Intel x86 based little endian processers. Thus, we must byte-swap
430  // To do this efficiencly, we do it during the copy of individual
431  // buffers to the primary output buffer.
432 
433 
434  uint32_t istart = output_block.size();
435  uint32_t len = buff->size();
436  output_block.resize(istart + len);
437  uint32_t *inbuff = &(*buff)[0];
438  uint32_t *outbuff = &output_block[istart];
439 
440  //cout << " block = " << len << endl;
441 
442  // copy and swap at same time
443  swap_bank_out(outbuff, inbuff, len);
444 
445 // output_block.insert(output_block.end(), buff->begin(), buff->end());
446 
447  // Return the buffer to the pool for recycling
448  ReturnBufferToPool(buff);
449  }
450 
451  // Write output buffer to output channel ET or file
452  uint32_t *buff = &output_block[0];
453  uint32_t buff_size_bytes = Nwords*sizeof(uint32_t);
454 
455  // Integrity check on overall length word
456  if( buff[0]*sizeof(uint32_t) != buff_size_bytes){
457  jerr << "EVIO output block header length does not match buffer size! " << endl;
458  jerr << " buff[0]=" << buff[0] << " words (=" << buff[0]*sizeof(uint32_t) << " bytes) != " << buff_size_bytes << endl;
459  throw JException("EVIO block header size corrupted");
460  }
461 
462  // Byte swap EVIO block header (we wait to do it here so that
463  // the above integrity check can be performed)
464  uint32_t tmpbuff[8];
465  swap_block_out(buff, 8, tmpbuff);
466  for(uint32_t i=0; i<8; i++) buff[i] = tmpbuff[i];
467 
468  // Write event to either ET buffer or EVIO file.
469  if(sink_type == kETSink){
470 #ifdef HAVE_ET
471  et_event *pe;
472 
473  int status = et_event_new(sys_id, att_id, &pe, ET_SLEEP, NULL, buff_size_bytes);
474  if(status != ET_OK){
475  jerr << "Unable to write new event to output (et_event_new returns "<<status<<")!" << endl;
476  jerr << " buff_size_bytes = " << buff_size_bytes << endl;
477  jerr << "First few words in case you are trying to debug:" << endl;
478  for(unsigned int j=0; j<3; j++){
479  char str[512];
480  for(unsigned int i=0; i<5; i++){
481  sprintf(str, " %08x", buff[i+j*5]);
482  jerr << str;
483  }
484  jerr << endl;
485  }
486  }else{
487 
488  // Get ET buffer of new event and copy ours into it
489  char *pdata;
490  et_event_getdata(pe, (void**)&pdata);
491  memcpy((char*)pdata, (char*)buff, buff_size_bytes);
492 
493  // Put event back into ET system
494  status = et_event_put(sys_id, att_id, pe);
495 
496  }
497 #endif
498  }else if(sink_type == kFileSink){
499 
500  // Write event to EVIO file
501  if(evioout) evioout->write((const char*)buff, buff_size_bytes);
502  //evWrite(evioHandle, buff);
503  //if(chan) chan->write(buff);
504  }
505 
506  // Optionally write buffer to output file
507  if(ofs_debug_output) ofs_debug_output->write((const char*)buff, buff_size_bytes);
508 }
509 
510 
511 //------------------
512 // GetBufferFromPool
513 //------------------
514 vector<uint32_t>* HDEVIOWriter::GetBufferFromPool(void)
515 {
516  /// Get a buffer from the buffer pool in a thread safe way.
517  /// This actually removes the buffer from the pool completely
518  /// so the caller gains ownership of the buffer.
519 
520  vector<uint32_t> *buffp = NULL;
521 
522  pthread_mutex_lock(&buff_pool_mutex);
523  if(buff_pool.empty()){
524  buffp = new vector<uint32_t>;
525  }else{
526  buffp = buff_pool.back();
527  buff_pool.pop_back();
528  }
529  pthread_mutex_unlock(&buff_pool_mutex);
530 
531  if(buffp==NULL) throw JException("Unable to get buffer from pool in JEventProcessor_L3proc");
532 
533  return buffp;
534 }
535 
536 //------------------
537 // ReturnBufferToPool
538 //------------------
539 void HDEVIOWriter::ReturnBufferToPool(vector<uint32_t> *buff)
540 {
541  /// Return a buffer to the pool.
542 
543  pthread_mutex_lock(&buff_pool_mutex);
544  buff_pool.push_back(buff);
545  pthread_mutex_unlock(&buff_pool_mutex);
546 }
547 
548 //---------------------------------
549 // AddBufferToOutput
550 //---------------------------------
551 void HDEVIOWriter::AddBufferToOutput(vector<uint32_t> *buff)
552 {
553  /// Add the given buffer to the list of buffers to
554  /// be written to the output. This will check that
555  /// the size of the output list has not grown too
556  /// large (default is 200 events) and if so, it
557  /// will block until the list emptys out a bit. This
558  /// Should apply back-pressure by having all processing
559  /// threads stop here until either the output catches
560  /// up, or we are told to quit.
561 
562  pthread_mutex_lock(&output_deque_mutex);
563 
564  while(output_deque.size() >= MAX_OUTPUT_QUEUE_SIZE){
565  // Release lock
566  pthread_mutex_unlock(&output_deque_mutex);
567 
568  // Sleep briefly
569  if(quit) return;
570  usleep(100);
571 
572  // Relock mutex
573  pthread_mutex_lock(&output_deque_mutex);
574  }
575 
576  // Add this buffer to list
577  output_deque.push_back(buff);
578 
579  // Release lock and wake up L3OutputThread
580  pthread_mutex_unlock(&output_deque_mutex);
581 }
582 
583 //---------------------------------
584 // Quit
585 //---------------------------------
587 {
588  quit=true;
589 }
590 
591 
592 
void swap_block_out(uint16_t *inbuff, uint16_t len, uint16_t *outbuff)
void * HDEVIOOutputThread(void *evioout)
char str[256]
char string[256]
sprintf(text,"Post KinFit Cut")
virtual ~HDEVIOWriter()
JApplication * japp
TEllipse * e
void FlushOutput(uint32_t Nwords, deque< vector< uint32_t > * > &my_output_deque)
void AddBufferToOutput(vector< uint32_t > *buff)
void ConnectToET(string sink_name)
uint32_t swap_bank_out(uint32_t *outbuff, uint32_t *inbuff, uint32_t len)
vector< uint32_t > * GetBufferFromPool(void)
void Quit(void)
void ReturnBufferToPool(vector< uint32_t > *buff)
void * HDEVIOOutputThread(void)
HDEVIOWriter(string sink_name)
Definition: HDEVIOWriter.cc:21