source: trunk/FACT++/src/EventBuilderWrapper.h@ 17214

Last change on this file since 17214 was 16956, checked in by tbretz, 11 years ago
Added a check that the new night-as-int is never decreased; improved log output.
File size: 45.6 KB
Line 
1#ifndef FACT_EventBuilderWrapper
2#define FACT_EventBuilderWrapper
3
4#include <sstream>
5
6#if BOOST_VERSION < 104400
7#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 4))
8#undef BOOST_HAS_RVALUE_REFS
9#endif
10#endif
11#include <boost/thread.hpp>
12#include <boost/filesystem.hpp>
13#include <boost/date_time/posix_time/posix_time_types.hpp>
14
15#include "DimWriteStatistics.h"
16
17#include "DataCalib.h"
18#include "DataWriteRaw.h"
19
20#ifdef HAVE_FITS
21#include "DataWriteFits.h"
22#else
23#define DataWriteFits DataWriteFits2
24#endif
25
26#include "DataWriteFits2.h"
27
28#include "queue.h"
29
30namespace ba = boost::asio;
31namespace bs = boost::system;
32namespace fs = boost::filesystem;
33
34using ba::ip::tcp;
35
36using namespace std;
37
38// ========================================================================
39
40#include "EventBuilder.h"
41
42void StartEvtBuild();
43void CloseRunFile();
44
45// ========================================================================
46
47class EventBuilderWrapper
48{
49public:
50 // FIXME
51 static EventBuilderWrapper *This;
52
53 MessageImp &fMsg;
54
55private:
56 boost::thread fThreadMain;
57
58 enum
59 {
60 kCurrent = 0,
61 kTotal = 1,
62 kEventId = 2,
63 kTriggerId = 3,
64 };
65
66 FAD::FileFormat_t fFileFormat;
67
68 //uint32_t fMaxRun;
69 uint32_t fLastOpened;
70 uint32_t fLastClosed;
71 array<uint32_t,4> fNumEvts;
72
73 DimWriteStatistics fDimWriteStats;
74 DimDescribedService fDimRuns;
75 DimDescribedService fDimEvents;
76 DimDescribedService fDimRawData;
77 DimDescribedService fDimEventData;
78 DimDescribedService fDimFeedbackData;
79 DimDescribedService fDimFwVersion;
80 DimDescribedService fDimRunNumber;
81 DimDescribedService fDimStatus;
82 DimDescribedService fDimDNA;
83 DimDescribedService fDimTemperature;
84 DimDescribedService fDimPrescaler;
85 DimDescribedService fDimRefClock;
86 DimDescribedService fDimRoi;
87 DimDescribedService fDimDac;
88 DimDescribedService fDimDrsRuns;
89 DimDescribedService fDimDrsCalibration;
90 DimDescribedService fDimStatistics1;
91 //DimDescribedService fDimStatistics2;
92 DimDescribedService fDimFileFormat;
93 DimDescribedService fDimIncomplete;
94
95 Queue<pair<Time,GUI_STAT>> fQueueStatistics1;
96 Queue<tuple<Time,bool,FAD::EventHeader>> fQueueProcHeader;
97 Queue<pair<Time,array<uint32_t,4>>> fQueueEvents;
98 Queue<pair<Time,array<uint16_t,2>>> fQueueRoi;
99 Queue<vector<char>> fQueueRawData;
100 Queue<tuple<Time,uint32_t,array<float,1440*4>>> fQueueEventData;
101 Queue<tuple<Time, array<uint32_t,40>, array<int16_t,160>>> fQueueTempRefClk;
102
103 string fPath;
104 uint32_t fNightAsInt;
105 uint32_t fRunNumber;
106 int64_t fRunInProgress;
107
108 array<uint16_t,2> fVecRoi;
109 pair<float,array<float, 1440*4>> fMaxEvent; // Maximum event from applyCalib
110
111protected:
112 bool InitRunNumber(const string &path="")
113 {
114 if (!path.empty())
115 {
116 if (!DimWriteStatistics::DoesPathExist(path, fMsg))
117 {
118 fMsg.Error("Data path "+path+" does not exist!");
119 return false;
120 }
121
122 fPath = path;
123 fDimWriteStats.SetCurrentFolder(fPath);
124
125 fMsg.Info("Data path set to "+path+".");
126 }
127
128 // Get current night
129 const Time now;
130
131 const uint32_t night = now.NightAsInt();
132 if (night==fNightAsInt)
133 return true;
134
135 if (night<fNightAsInt)
136 {
137 fMsg.Warn("New night "+to_string(night)+" ["+now.GetAsStr()+"] before current night "+to_string(night)+"... keeping old one.");
138 fMsg.Warn("Please check the system clock.");
139 return true;
140 }
141
142 // Check for run numbers
143 fRunNumber = 1000;
144
145 while (--fRunNumber>0)
146 {
147 const string name = DataProcessorImp::FormFileName(fPath, night, fRunNumber, "");
148
149 if (access((name+"bin").c_str(), F_OK) == 0)
150 break;
151 if (access((name+"fits").c_str(), F_OK) == 0)
152 break;
153 if (access((name+"drs.fits").c_str(), F_OK) == 0)
154 break;
155 }
156
157 // This is now the first file which does not exist
158 fRunNumber++;
159 fLastOpened = 0;
160
161 // Check if we have exceeded the maximum
162 if (fRunNumber==1000)
163 {
164 fMsg.Error("You have a file with run-number 1000 in "+fPath+" ["+to_string(night)+"]");
165 return false;
166 }
167
168 ostringstream str;
169 if (fNightAsInt==0)
170 str << "First night...";
171 else
172 str << "Night has changed from " << fNightAsInt << " [" << now << "]... ";
173 str << " next run-number is " << night << "-" << setfill('0') << setw(3) << fRunNumber << " [" << (fPath.empty()?".":fPath) << "]";
174 fMsg.Message(str);
175
176 fNightAsInt = night;
177
178 return true;
179 }
180
181public:
182 EventBuilderWrapper(MessageImp &imp) : fMsg(imp),
183 fFileFormat(FAD::kNone), /*fMaxRun(0),*/ fLastOpened(0), fLastClosed(0),
184 fDimWriteStats ("FAD_CONTROL", imp),
185 fDimRuns ("FAD_CONTROL/RUNS", "I:2;C",
186 "Run files statistics"
187 "|stats[int]:last opened or closed run"
188 "|file[string]:filename of last opened file"),
189 fDimEvents ("FAD_CONTROL/EVENTS", "I:4",
190 "Event counts"
191 "|evtsCount[int]:Num evts cur. run, total (all run), evt ID, trig. Num"),
192 fDimRawData ("FAD_CONTROL/RAW_DATA", "S:1;S:1;I:1;I:1;S:1;I:1;I:2;I:40;S:1440;S:160;F",
193 "|roi[uint16]:number of samples per pixel"
194 "|roi_tm[uint16]:number of samples per time-marker channel"
195 "|num_fad[uint32]:event number from FADs"
196 "|num_ftm[uint32]:trigger number from FTM"
197 "|type[uint16]:trigger type from FTM"
198 "|num_boards[uint32]:number of active boards"
199 "|time[uint32]:PC time as unix time stamp"
200 "|time_board[uint32]:Time stamp of FAD boards"
201 "|start_pix[int16]:start sample of pixels"
202 "|start_tm[int16]:start sample of time marker channels"
203 "|adc[int16]:adc data"),
204 fDimEventData ("FAD_CONTROL/EVENT_DATA", "F:1440;F:1440;F:1440;F:1440", "|avg:|rms:|max:|pos"),
205 fDimFeedbackData("FAD_CONTROL/FEEDBACK_DATA", "F:1440", ""),
206 fDimFwVersion ("FAD_CONTROL/FIRMWARE_VERSION", "F:42",
207 "Firmware version number of fad boards"
208 "|firmware[float]:Version number of firmware, for each board. 40=min, 41=max"),
209 fDimRunNumber ("FAD_CONTROL/RUN_NUMBER", "I:42",
210 "Run numbers coming from FAD boards"
211 "|runNumbers[int]:current run number of each FAD board. 40=min, 41=max"),
212 fDimStatus ("FAD_CONTROL/STATUS", "S:42",
213 "Status of FAD boards"
214 "|status[bitpattern]:Status of each FAD board. Maybe buggy"),
215 fDimDNA ("FAD_CONTROL/DNA", "X:40",
216 "DNA of FAD boards"
217 "|DNA[hex]:Hex identifier of each FAD board"),
218 fDimTemperature ("FAD_CONTROL/TEMPERATURE", "S:1;F:160",
219 "DRS temperatures"
220 "|cnt[uint16]:Counter of averaged values"
221 "|temp[deg C]:average temp of all DRS chips"),
222 fDimPrescaler ("FAD_CONTROL/PRESCALER", "S:42",
223 "Trigger generator prescaler of fad boards"
224 "|prescaler[int]:Trigger generator prescaler value, for each board"),
225 fDimRefClock ("FAD_CONTROL/REFERENCE_CLOCK", "S:1;F:40",
226 "Reference clock of FAD boards"
227 "|cnt[uint16]:Counter of averaged values"
228 "|clk[Hz]:Averaged clock of ref clocks of FAD boards"),
229 fDimRoi ("FAD_CONTROL/REGION_OF_INTEREST", "S:2", "roi:|roi_rm:"),
230 fDimDac ("FAD_CONTROL/DAC", "S:336",
231 "DAC settings of each FAD board"
232 "|DAC[int]:DAC counts, sequentially DAC 0 board 0, 0/1, 0/2... (plus min max)"),
233 fDimDrsRuns ("FAD_CONTROL/DRS_RUNS", "I:1;I:3",
234 "|roi:Region of interest of secondary baseline"
235 "|run:Run numbers of DRS runs (0=none)"),
236 fDimDrsCalibration("FAD_CONTROL/DRS_CALIBRATION", "I:1;I:3;F:1474560;F:1474560;F:1474560;F:1474560;F:1474560;F:1474560;F:163840;F:163840",
237 "|roi:Region of interest of secondary baseline"
238 "|run:Run numbers of DRS runs (0=none)"),
239 fDimStatistics1 ("FAD_CONTROL/STATISTICS1", "I:5;X:3;I:1;I:2;C:40;I:40;I:40",
240 "Event Builder status for GUI display"
241 "|bufferInfo[int]:Events in buffer, incomp., comp., write, proc., tot."
242 "|memInfo[int]:total mem allocated, used mem, max memory"
243 "|deltaT[ms]:Time in ms for rates"
244 "|rateNew[int]:Number of new start events received"
245 "|numConn[int]:Number of connections per board"
246 "|rateBytes[int]:Bytes read during last cylce"
247 "|relBytes[int]:Relative number of total bytes received (received - released)"),
248 fDimFileFormat("FAD_CONTROL/FILE_FORMAT", "S:1", "|format[int]:Current file format"),
249 fDimIncomplete("FAD_CONTROL/INCOMPLETE", "X:1", "|incomplete[bits]:One bit per board"),
250 // It is important to instantiate them after the DimServices
251 fQueueStatistics1(std::bind(&EventBuilderWrapper::UpdateDimStatistics1, this, placeholders::_1)),
252 fQueueProcHeader( std::bind(&EventBuilderWrapper::procHeader, this, placeholders::_1)),
253 fQueueEvents( std::bind(&EventBuilderWrapper::UpdateDimEvents, this, placeholders::_1)),
254 fQueueRoi( std::bind(&EventBuilderWrapper::UpdateDimRoi, this, placeholders::_1)),
255 fQueueRawData( std::bind(&EventBuilderWrapper::UpdateDimRawData, this, placeholders::_1)),
256 fQueueEventData( std::bind(&EventBuilderWrapper::UpdateDimEventData, this, placeholders::_1)),
257 fQueueTempRefClk( std::bind(&EventBuilderWrapper::UpdateDimTempRefClk, this, placeholders::_1)),
258 fNightAsInt(0), fRunInProgress(-1),
259 fMaxEvent(make_pair(-FLT_MAX, array<float,1440*4>()))
260 {
261 if (This)
262 throw logic_error("EventBuilderWrapper cannot be instantiated twice.");
263
264 This = this;
265
266 fVecRoi.fill(0);
267
268 memset(fNumEvts.data(), 0, sizeof(fNumEvts));
269 fDimEvents.Update(fNumEvts);
270
271 for (size_t i=0; i<40; i++)
272 ConnectSlot(i, tcp::endpoint());
273 }
274
275 virtual ~EventBuilderWrapper()
276 {
277 Abort();
278
279 // FIXME: Used timed_join and abort afterwards
280 // What's the maximum time the eb need to abort?
281 fThreadMain.join();
282 }
283
284 map<uint32_t, FAD::RunDescription> fExpectedRuns;
285
286 mutex mtx_newrun;
287
288 uint32_t StartNewRun(int64_t maxtime, int64_t maxevt, const pair<string, FAD::Configuration> &ref)
289 {
290 if (maxtime<=0 || maxtime>24*60*60)
291 maxtime = 24*60*60;
292 if (maxevt<=0 || maxevt>INT32_MAX)
293 maxevt = INT32_MAX;
294
295 if (!InitRunNumber())
296 return 0;
297
298 const FAD::RunDescription descr =
299 {
300 uint32_t(maxtime),
301 uint32_t(maxevt),
302 fNightAsInt,
303 ref.first,
304 ref.second,
305 };
306
307 const lock_guard<mutex> lock(mtx_newrun);
308 fExpectedRuns[fRunNumber] = descr;
309 return fRunNumber++;
310 }
311
312 bool IsThreadRunning()
313 {
314 if (fThreadMain.get_id()==boost::this_thread::get_id())
315 return true;
316 return !fThreadMain.timed_join(boost::posix_time::microseconds(0));
317 }
318
319 void SetMaxMemory(unsigned int mb) const
320 {
321 g_maxMem = size_t(mb)*1000000;
322 }
323 void SetEventTimeout(uint16_t to) const
324 {
325 g_evtTimeout = to;
326 }
327
328 void StartThread(const vector<tcp::endpoint> &addr)
329 {
330 if (IsThreadRunning())
331 {
332 fMsg.Warn("Start - EventBuilder still running");
333 return;
334 }
335
336 //fLastMessage.clear();
337
338 for (size_t i=0; i<40; i++)
339 ConnectSlot(i, addr[i]);
340
341 fMsg.Message("Starting EventBuilder thread");
342
343 fThreadMain = boost::thread(StartEvtBuild);
344 }
345
346 void ConnectSlot(unsigned int i, const tcp::endpoint &addr)
347 {
348 if (i>39)
349 return;
350
351 fRunInProgress = -1;
352
353 if (addr==tcp::endpoint())
354 {
355 // In this order
356 g_port[i].sockDef = 0;
357
358 fDimIncomplete.setQuality(0);
359 fDimIncomplete.Update(uint64_t(0));
360 return;
361 }
362
363 struct sockaddr_in sockaddr; //IP for each socket
364 sockaddr.sin_family = AF_INET;
365 sockaddr.sin_addr.s_addr = htonl(addr.address().to_v4().to_ulong());
366 sockaddr.sin_port = htons(addr.port());
367 memcpy(&g_port[i].sockAddr, &sockaddr, sizeof(struct sockaddr_in));
368
369 // In this order
370 g_port[i].sockDef = 1;
371
372 fDimIncomplete.setQuality(0);
373 fDimIncomplete.Update(uint64_t(0));
374 }
375
376 void IgnoreSlot(unsigned int i)
377 {
378 if (i>39)
379 return;
380
381 if (g_port[i].sockAddr.sin_port==0)
382 return;
383
384 g_port[i].sockDef = -1;
385 }
386
387
388 void Abort()
389 {
390 fMsg.Message("Signal abort to EventBuilder thread...");
391 g_reset = 2;
392 }
393
394 void ResetThread(bool soft)
395 {
396 fMsg.Message("Signal reset to EventBuilder thread...");
397 g_reset = soft ? 101 : 102;
398 }
399
400 void Exit()
401 {
402 fMsg.Message("Signal exit to EventBuilder thread...");
403 g_reset = 1;
404 }
405
406 bool IsConnected(int i) const { return gi_NumConnect[i]==1; }
407 bool IsConnecting(int i) const { return gi_NumConnect[i]==0 && g_port[i].sockDef!=0; }
408 bool IsDisconnected(int i) const { return gi_NumConnect[i]==0 && g_port[i].sockDef==0; }
409 bool IsRunInProgress() const { return fRunInProgress>=0; }
410
411 void SetIgnore(int i, bool b) const { if (g_port[i].sockDef!=0) g_port[i].sockDef=b?-1:1; }
412 bool IsIgnored(int i) const { return g_port[i].sockDef==-1; }
413
414 void SetOutputFormat(FAD::FileFormat_t f)
415 {
416 const bool changed = f!=fFileFormat;
417
418 fFileFormat = f;
419 fDimFileFormat.Update(uint16_t(f));
420
421 string msg = "File format set to: ";
422 switch (f)
423 {
424 case FAD::kNone: msg += "kNone."; break;
425 case FAD::kDebug: msg += "kDebug."; break;
426 case FAD::kFits: msg += "kFits."; break;
427 case FAD::kCfitsio: msg += "kCfitsio"; break;
428 case FAD::kRaw: msg += "kRaw"; break;
429 case FAD::kCalib:
430 DataCalib::Restart();
431 DataCalib::Update(fDimDrsCalibration, fDimDrsRuns);
432 fMsg.Message("Resetted DRS calibration.");
433 return;
434 }
435
436 if (changed)
437 fMsg.Message(msg);
438 }
439
440 virtual int ResetSecondaryDrsBaseline()
441 {
442 if (DataCalib::ResetTrgOff(fDimDrsCalibration, fDimDrsRuns))
443 {
444 fFileFormat = FAD::kCalib;
445 fDimFileFormat.Update(uint16_t(fFileFormat));
446 fMsg.Message("Resetted DRS calibration for secondary baseline.");
447 }
448 else
449 fMsg.Warn("Could not reset DRS calibration of secondary baseline.");
450
451 return 0;
452 }
453
454 void LoadDrsCalibration(const char *fname)
455 {
456 if (!DataCalib::ReadFits(fname, fMsg))
457 return;
458 fMsg.Info("Successfully loaded DRS calibration from "+string(fname));
459 DataCalib::Update(fDimDrsCalibration, fDimDrsRuns);
460 }
461
462 virtual int CloseOpenFiles() { CloseRunFile(); fRunInProgress = -1; return 0; }
463
464
465 // -------------- Mapped event builder callbacks ------------------
466
467 void UpdateRuns(const string &fname="")
468 {
469 uint32_t values[2] =
470 {
471 fLastOpened,
472 fLastClosed
473 };
474
475 vector<char> data(sizeof(values)+fname.size()+1);
476 memcpy(data.data(), values, sizeof(values));
477 strcpy(data.data()+sizeof(values), fname.c_str());
478 fDimRuns.setQuality((bool)fFile);
479 fDimRuns.Update(data);
480
481 if (!fname.empty())
482 fDimWriteStats.FileOpened(fname);
483 }
484
485 shared_ptr<DataProcessorImp> fFile;
486
487 void UpdateDimEvents(const pair<Time,array<uint32_t,4>> &stat)
488 {
489 fDimEvents.setData(stat.second.data(), sizeof(uint32_t)*4);
490 fDimEvents.Update(stat.first);
491 }
492
493 bool runOpen(const EVT_CTRL2 &evt)
494 {
495 const uint32_t night = evt.runCtrl->night;
496 const uint32_t runid = evt.runNum>0 ? evt.runNum : time(NULL);
497
498 // If there is still an open file: close it
499 if (fFile)
500 runClose(*evt.runCtrl);
501
502 // Keep a copy of the currently valid drs calibration
503 // and associate it to the run control structure
504 evt.runCtrl->calib = make_shared<DrsCalibration>(DataCalib::GetCalibration());
505
506 /*
507 evt.runCtrl->calibInt.resize(1024*1440);
508
509 const int16_t *off = evt.runCtrl->zcalib.data();
510 int32_t *ptr = evt.runCtrl->calib.data();
511
512 const uint64_t num = evt.runCtrl->calib.fNumOffset;
513 for (int i=0; i<1024*1440)
514 ptr[i] = off[i]/num;
515 */
516
517 // FIMXE: Check if file already exists...
518
519 // Crate the file
520 DataProcessorImp *file = 0;
521 switch (fFileFormat)
522 {
523 case FAD::kNone: file = new DataDump(fPath, night, runid, fMsg); break;
524 case FAD::kDebug: file = new DataDebug(fPath, night, runid, fMsg); break;
525 case FAD::kCfitsio: file = new DataWriteFits(fPath, night, runid, fMsg); break;
526 case FAD::kFits: file = new DataWriteFits2(fPath, night, runid, fMsg); break;
527 case FAD::kRaw: file = new DataWriteRaw(fPath, night, runid, fMsg); break;
528 case FAD::kCalib: file = new DataCalib(fPath, night, runid, fDimDrsCalibration, fDimDrsRuns, fMsg); break;
529 }
530
531 try
532 {
533 // Try to open the file
534 FAD::RunDescription desc;
535 desc.name = evt.runCtrl->runType;
536
537 if (!file->Open(evt, desc))
538 return false;
539 }
540 catch (const exception &e)
541 {
542 fMsg.Error("Exception trying to open file: "+string(e.what()));
543 return false;
544 }
545
546 fLastOpened = runid;
547
548 // Signal that a file is open
549 fFile = shared_ptr<DataProcessorImp>(file);
550
551 // Now do all the calls which potentially block (dim)
552
553 // Time for update runs before time for update events
554 UpdateRuns(file->GetFileName());
555 fNumEvts[kEventId] = 0;
556 fNumEvts[kTriggerId] = 0;
557 fNumEvts[kCurrent] = 0;
558 fQueueEvents.emplace(Time(), fNumEvts);
559
560 ostringstream str;
561 str << "Opened: " << file->GetFileName() << " (" << file->GetRunId() << ")";
562 fMsg.Info(str);
563
564 return true;
565 }
566
567 bool runWrite(const EVT_CTRL2 &e)
568 {
569 /*
570 const size_t size = sizeof(EVENT)+1440*(evt.Roi+evt.RoiTM)*2;
571 vector evt(e.fEvent, e.fEvent+size);
572
573 const EVENT &evt = *reinterpret_cast<EVENT*>(evt.data());
574
575 int16_t *val = evt.Adc_Data;
576 const int16_t *off = e.runCtrl->zcalib.data();
577 for (const int16_t *start=evt.StartPix; start<evt.StartPix+1440; val+=1024, off+=1024, start++)
578 {
579 if (*start<0)
580 continue;
581
582 for (size_t i=0; i<roi; i++)
583 val[i] -= offset[(*start+i)%1024];
584 }*/
585
586 const EVENT &evt = *e.fEvent;
587 if (!fFile->WriteEvt(evt))
588 return false;
589
590 fNumEvts[kCurrent]++;
591 fNumEvts[kEventId] = evt.EventNum;
592 fNumEvts[kTriggerId] = evt.TriggerNum;
593 fNumEvts[kTotal]++;
594
595 static Time oldt(boost::date_time::neg_infin);
596 Time newt;
597 if (newt>oldt+boost::posix_time::seconds(1))
598 {
599 fQueueEvents.emplace(newt, fNumEvts);
600 oldt = newt;
601 }
602
603 return true;
604 }
605
606 void runClose(RUN_CTRL2 &run)
607 {
608 if (!fFile)
609 return;
610
611 // It can happen that runFinished was never called
612 // (e.g. runWrite failed)
613 if (fRunInProgress==fFile->GetRunId())
614 fRunInProgress = -1;
615
616 // Close the file
617 const bool rc = fFile->Close(NULL);
618
619 fLastClosed = fFile->GetRunId();
620
621 ostringstream str;
622 str << "Closed: " << fFile->GetFileName() << " (" << fFile->GetRunId() << ")";
623 if (!rc)
624 str << "... failed!";
625
626 // Signal that the file is closed
627
628 fFile.reset();
629
630 // Now do all the calls which can potentially block (dim)
631
632 CloseRun(fLastClosed);
633
634 // Time for update events before time for update runs
635 fQueueEvents.emplace(Time(), fNumEvts);
636 UpdateRuns();
637
638 // Do the potentially blocking call after all others
639 rc ? fMsg.Info(str) : fMsg.Error(str);
640
641 // If a Drs Calibration has just been finished, all following events
642 // should also be processed with this calibration.
643 // Note that this is a generally dangerous operation. Here, the previous
644 // DRS calibration shared_ptr gets freed and if it is the last in use,
645 // the memory will vanish. If another thread accesses that pointer,
646 // it _must_ make a copy of the shared_ptr first to ensure that
647 // the memory will stay in scope until the end of its operation.
648 const DrsCalibration &cal = DataCalib::GetCalibration();
649 if (!run.calib || run.calib->fStep != cal.fStep || run.calib->fRoi!=cal.fRoi)
650 run.calib = make_shared<DrsCalibration>(cal);
651 }
652
653 virtual void CloseRun(uint32_t /*runid*/) { }
654
655 void UpdateDimRoi(const pair<Time, array<uint16_t,2>> &roi)
656 {
657 fDimRoi.setData(roi.second.data(), sizeof(uint16_t)*2);
658 fDimRoi.Update(roi.first);
659 }
660
661 void UpdateDimTempRefClk(const tuple<Time, array<uint32_t,40>, array<int16_t,160>> &dat)
662 {
663 const auto delay = boost::posix_time::seconds(5);
664
665 const Time &tm = get<0>(dat);
666
667 const array<uint32_t,40> &clk = get<1>(dat);
668 const array<int16_t,160> &tmp = get<2>(dat);
669
670 // --------------- RefClock ---------------
671
672 // history, add current data to history
673 static list<pair<Time,array<uint32_t,40>>> listclk;
674 listclk.emplace_back(tm, clk);
675
676 // --------------- Temperatures ---------------
677
678 // history, add current data to history
679 static list<pair<Time,array<int16_t,160>>> listtmp;
680 listtmp.emplace_back(tm, tmp);
681
682 // ========== Update dim services once a second =========
683
684 static Time oldt(boost::date_time::neg_infin);
685 Time newt;
686
687 if (newt<oldt+delay)
688 return;
689
690 oldt = newt;
691
692 // --------------- RefClock ---------------
693
694 // remove expired data from history
695 while (1)
696 {
697 auto it=listclk.begin();
698 if (it==listclk.end() || it->first+delay>tm)
699 break;
700 listclk.pop_front();
701 }
702
703 // Structure for dim service
704 struct Clock
705 {
706 uint16_t num;
707 float val[40];
708 Clock() { memset(this, 0, sizeof(Clock)); }
709 } __attribute__((__packed__));
710
711 // Calculate average and fll structure
712 vector<uint16_t> clknum(40);
713
714 Clock avgclk;
715 avgclk.num = listclk.size();
716 for (auto it=listclk.begin(); it!=listclk.end(); it++)
717 for (int i=0; i<40; i++)
718 if (it->second[i]!=UINT32_MAX)
719 {
720 avgclk.val[i] += it->second[i];
721 clknum[i]++;
722 }
723 for (int i=0; i<40; i++)
724 avgclk.val[i] *= 2.048/clknum[i];
725
726 // Update dim service
727 fDimRefClock.setData(avgclk);
728 fDimRefClock.Update(tm);
729
730 listclk.clear();
731
732 // --------------- Temperatures ---------------
733
734 // remove expired data from history
735 while (1)
736 {
737 auto it=listtmp.begin();
738 if (it==listtmp.end() || it->first+delay>tm)
739 break;
740 listtmp.pop_front();
741 }
742
743 // Structure for dim service
744 struct Temp
745 {
746 uint16_t num;
747 float val[160];
748 Temp() { memset(this, 0, sizeof(Temp)); }
749 } __attribute__((__packed__));
750
751 // Calculate average and fll structure
752 vector<uint32_t> tmpnum(160);
753
754 Temp avgtmp;
755 avgtmp.num = listtmp.size();
756 for (auto it=listtmp.begin(); it!=listtmp.end(); it++)
757 for (int i=0; i<160; i++)
758 if (it->second[i]!=INT16_MIN)
759 {
760 avgtmp.val[i] += it->second[i];
761 tmpnum[i]++;
762 }
763 for (int i=0; i<160; i++)
764 avgtmp.val[i] /= tmpnum[i]*16;
765
766 // Update dim service
767 fDimTemperature.setData(avgtmp);
768 fDimTemperature.Update(tm);
769
770 listtmp.clear();
771 }
772
773 bool eventCheck(const EVT_CTRL2 &evt)
774 {
775 const EVENT *event = evt.fEvent;
776
777 const Time tm(evt.time);
778
779 const array<uint16_t,2> roi = {{ event->Roi, event->RoiTM }};
780
781 if (roi!=fVecRoi)
782 {
783 fQueueRoi.emplace(tm, roi);
784 fVecRoi = roi;
785 }
786
787 const FAD::EventHeader *beg = reinterpret_cast<const FAD::EventHeader*>(evt.FADhead);
788 const FAD::EventHeader *end = reinterpret_cast<const FAD::EventHeader*>(evt.FADhead)+40;
789
790 // FIMXE: Compare with target configuration
791
792 // Copy data to array
793 array<uint32_t,40> clk;
794 array<int16_t,160> tmp;
795
796 for (int i=0; i<40; i++)
797 clk[i] = UINT32_MAX;
798
799 for (int i=0; i<160; i++)
800 tmp[i] = INT16_MIN;
801
802 //fill(clk.data(), clk.data()+ 40, UINT32_MAX);
803 //fill(tmp.data(), tmp.data()+160, INT16_MIN);
804
805 for (const FAD::EventHeader *ptr=beg; ptr!=end; ptr++)
806 {
807 // FIXME: Compare with expectations!!!
808 if (ptr->fStartDelimiter==0)
809 {
810 if (ptr==beg)
811 beg++;
812 continue;
813 }
814
815 clk[ptr->Id()] = ptr->fFreqRefClock;
816 for (int i=0; i<4; i++)
817 tmp[ptr->Id()*4+i] = ptr->fTempDrs[i];
818
819 if (beg->fStatus != ptr->fStatus)
820 {
821 fMsg.Error("Inconsistency in FAD status detected.... closing run.");
822 return false;
823 }
824
825 if (beg->fRunNumber != ptr->fRunNumber)
826 {
827 fMsg.Error("Inconsistent run number detected.... closing run.");
828 return false;
829 }
830
831 /*
832 if (beg->fVersion != ptr->fVersion)
833 {
834 Error("Inconsist firmware version detected.... closing run.");
835 CloseRunFile(runNr, 0, 0);
836 break;
837 }
838 */
839 if (beg->fEventCounter != ptr->fEventCounter)
840 {
841 fMsg.Error("Inconsistent FAD event number detected.... closing run.");
842 return false;
843 }
844
845 if (beg->fTriggerCounter != ptr->fTriggerCounter)
846 {
847 fMsg.Error("Inconsistent FTM trigger number detected.... closing run.");
848 return false;
849 }
850
851 // FIXME: Check with first event!
852 if (beg->fAdcClockPhaseShift != ptr->fAdcClockPhaseShift)
853 {
854 fMsg.Error("Inconsistent phase shift detected.... closing run.");
855 return false;
856 }
857
858 // FIXME: Check with first event!
859 if (memcmp(beg->fDac, ptr->fDac, sizeof(beg->fDac)))
860 {
861 fMsg.Error("Inconsistent DAC values detected.... closing run.");
862 return false;
863 }
864
865 if (beg->fTriggerType != ptr->fTriggerType)
866 {
867 fMsg.Error("Inconsistent trigger type detected.... closing run.");
868 return false;
869 }
870 }
871
872 fQueueTempRefClk.emplace(tm, clk, tmp);
873
874 // check REFCLK_frequency
875 // check consistency with command configuration
876 // how to log errors?
877 // need gotNewRun/closedRun to know it is finished
878
879 return true;
880 }
881
882 Time fLastDimRawData;
883 Time fLastDimEventData;
884
885 void UpdateDimRawData(const vector<char> &v)
886 {
887 const EVENT *evt = reinterpret_cast<const EVENT*>(v.data());
888
889 fDimRawData.setData(v);
890 fDimRawData.setQuality(evt->TriggerType);
891 fDimRawData.Update(Time(evt->PCTime, evt->PCUsec));
892 }
893 void UpdateDimEventData(const tuple<Time,uint32_t,array<float, 1440*4>> &tup)
894 {
895 fDimEventData.setQuality(get<1>(tup));
896 fDimEventData.setData(get<2>(tup));
897 fDimEventData.Update(get<0>(tup));
898 }
899
900 void applyCalib(const EVT_CTRL2 &evt, const size_t &size)
901 {
902 const EVENT *event = evt.fEvent;
903 const int16_t *start = event->StartPix;
904
905 // Get the reference to the run associated information
906 RUN_CTRL2 &run = *evt.runCtrl;
907
908 if (size==1) // If there is more than one event waiting (including this one), throw them away
909 {
910 Time now;
911
912 // ------------------- Copy event data to new memory --------------------
913 // (to make it thread safe; a static buffer might improve memory handling)
914 const uint16_t roi = event->Roi;
915
916 // ------------------- Apply full DRS calibration ------------------------
917 // (Is that necessray, or would a simple offset correct do well already?)
918
919 // This is a very important step. Making a copy of the shared pointer ensures
920 // that another thread (here: runClose) can set a new shared_ptr with new
921 // data without this thread being affected. If we just did run.calib->Apply
922 // the shared_pointer in use here might vanash during the processing, the
923 // memory is freed and we access invalid memory. It is not important
924 // which memory we acces (the old or the new one) because it is just for
925 // display purpose anyway.
926 const shared_ptr<DrsCalibration> cal = run.calib;
927
928 // There seems to be a problem using std::array... maybe the size is too big?
929 // array<float, (1440+160)*1024> vec2;
930 vector<float> vec((1440+160)*roi);
931 cal->Apply(vec.data(), event->Adc_Data, start, roi);
932
933 // ------------------- Appy DRS-step correction --------------------------
934 for (auto it=run.prevStart.begin(); it!=run.prevStart.end(); it++)
935 {
936 DrsCalibrate::CorrectStep(vec.data(), 1440, roi, it->data(), start, roi+10);
937 DrsCalibrate::CorrectStep(vec.data(), 1440, roi, it->data(), start, 3);
938 }
939
940 // ------------------------- Remove spikes --------------------------------
941 DrsCalibrate::RemoveSpikes3(vec.data(), roi);
942
943 // -------------- Update raw data dim sevice (VERY SLOW) -----------------
944 if (fQueueRawData.empty() && now>fLastDimRawData+boost::posix_time::seconds(5))
945 {
946 vector<char> data1(sizeof(EVENT)+vec.size()*sizeof(float));
947 memcpy(data1.data(), event, sizeof(EVENT));
948 memcpy(data1.data()+sizeof(EVENT), vec.data(), vec.size()*sizeof(float));
949 fQueueRawData.emplace(data1);
950
951 fLastDimRawData = now;
952 }
953
954 // ------------------------- Basic statistics -----------------------------
955 DrsCalibrate::SlidingAverage(vec.data(), roi, 10);
956
957 // If this is a cosmic event
958 array<float, 1440*4> stats; // Mean, RMS, Max, Pos
959 const float max = DrsCalibrate::GetPixelStats(stats.data(), vec.data(), roi);
960 if (evt.trgTyp==0 && max>fMaxEvent.first)
961 fMaxEvent = make_pair(max, stats);
962
963 // ------------------ Update dim service (statistics) ---------------------
964
965 if (fQueueEventData.empty() && now>fLastDimEventData+boost::posix_time::microseconds(3141593))
966 {
967 fQueueEventData.emplace(evt.time, evt.trgTyp, evt.trgTyp==0 ? fMaxEvent.second : stats);
968 if (evt.trgTyp==0)
969 fMaxEvent.first = -FLT_MAX;
970
971 fLastDimEventData = now;
972 }
973
974 // === SendFeedbackData(PEVNT_HEADER *fadhd, EVENT *event)
975 //
976 // if (!ptr->HasTriggerLPext() && !ptr->HasTriggerLPint())
977 // return;
978 //
979 // vector<float> data2(1440); // Mean, RMS, Max, Pos, first, last
980 // DrsCalibrate::GetPixelMax(data2.data(), data.data(), event->Roi, 0, event->Roi-1);
981 //
982 // fDimFeedbackData.Update(data2);
983 }
984
985 // Keep the start cells of the last five events for further corrections
986 // As a performance improvement we could also just store the
987 // pointers to the last five events...
988 // What if a new run is started? Do we mind?
989 auto &l = run.prevStart; // History for start cells of previous events (for step calibration)
990
991 if (l.size()<5)
992 l.emplace_front();
993 else
994 {
995 auto it = l.end();
996 l.splice(l.begin(), l, --it);
997 }
998
999 memcpy(l.front().data(), start, 1440*sizeof(int16_t));
1000 }
1001
1002 bool IsRunWaiting()
1003 {
1004 const lock_guard<mutex> lock(mtx_newrun);
1005 return fExpectedRuns.find(fRunNumber-1)!=fExpectedRuns.end();
1006 }
1007
1008 uint32_t GetRunNumber() const
1009 {
1010 return fRunNumber;
1011 }
1012
1013 bool IncreaseRunNumber(uint32_t run)
1014 {
1015 if (!InitRunNumber())
1016 return false;
1017
1018 if (run<fRunNumber)
1019 {
1020 ostringstream msg;
1021 msg <<
1022 "Run number " << run << " smaller than next available "
1023 "run number " << fRunNumber << " in " << fPath << " [" << fNightAsInt << "]";
1024 fMsg.Error(msg);
1025 return false;
1026 }
1027
1028 fRunNumber = run;
1029
1030 return true;
1031 }
1032
1033 void gotNewRun(RUN_CTRL2 &run)
1034 {
1035 // This is to secure iteration over fExpectedRuns
1036 const lock_guard<mutex> lock(mtx_newrun);
1037
1038 map<uint32_t,FAD::RunDescription>::iterator it = fExpectedRuns.begin();
1039 while (it!=fExpectedRuns.end())
1040 {
1041 if (it->first<run.runId)
1042 {
1043 ostringstream str;
1044 str << "runOpen - Missed run " << it->first << ".";
1045 fMsg.Info(str);
1046
1047 // Increase the iterator first, it becomes invalid with the next call
1048 const auto is = it++;
1049 fExpectedRuns.erase(is);
1050 continue;
1051 }
1052
1053 if (it->first==run.runId)
1054 break;
1055
1056 it++;
1057 }
1058
1059 if (it==fExpectedRuns.end())
1060 {
1061 ostringstream str;
1062 str << "runOpen - Run " << run.runId << " wasn't expected (maybe manual triggers)";
1063 fMsg.Warn(str);
1064
1065 // This is not ideal, but the best we can do
1066 run.night = fNightAsInt;
1067
1068 return;
1069 }
1070
1071 const FAD::RunDescription &conf = it->second;
1072
1073 run.runType = conf.name;
1074 run.maxEvt = conf.maxevt;
1075 run.closeTime = conf.maxtime + run.openTime;
1076 run.night = conf.night;
1077
1078 fExpectedRuns.erase(it);
1079
1080 // Now signal the fadctrl (configuration process that a run is in progress)
1081 // Maybe this could be done earlier, but we are talking about a
1082 // negligible time scale here.
1083 fRunInProgress = run.runId;
1084 }
1085
1086 void runFinished()
1087 {
1088 // This is called when the last event of a run (run time exceeded or
1089 // max number of events exceeded) has been received.
1090 fRunInProgress = -1;
1091 }
1092
1093 //map<boost::thread::id, string> fLastMessage;
1094
1095 void factOut(int severity, const char *message)
1096 {
1097 ostringstream str;
1098 str << "EventBuilder: " << message;
1099
1100 /*
1101 string &old = fLastMessage[boost::this_thread::get_id()];
1102
1103 if (str.str()==old)
1104 return;
1105 old = str.str();
1106 */
1107
1108 fMsg.Update(str, severity);
1109 }
1110
1111/*
1112 void factStat(int64_t *stat, int len)
1113 {
1114 if (len!=7)
1115 {
1116 fMsg.Warn("factStat received unknown number of values.");
1117 return;
1118 }
1119
1120 vector<int64_t> data(1, g_maxMem);
1121 data.insert(data.end(), stat, stat+len);
1122
1123 static vector<int64_t> last(8);
1124 if (data==last)
1125 return;
1126 last = data;
1127
1128 fDimStatistics.Update(data);
1129
1130 // len ist die Laenge des arrays.
1131 // array[4] enthaelt wieviele bytes im Buffer aktuell belegt sind; daran
1132 // kannst Du pruefen, ob die 100MB voll sind ....
1133
1134 ostringstream str;
1135 str
1136 << "Wait=" << stat[0] << " "
1137 << "Skip=" << stat[1] << " "
1138 << "Del=" << stat[2] << " "
1139 << "Tot=" << stat[3] << " "
1140 << "Mem=" << stat[4] << "/" << g_maxMem << " "
1141 << "Read=" << stat[5] << " "
1142 << "Conn=" << stat[6];
1143
1144 fMsg.Info(str);
1145 }
1146 */
1147
1148 void UpdateDimStatistics1(const pair<Time,GUI_STAT> &stat)
1149 {
1150 fDimStatistics1.setData(&stat.second, sizeof(GUI_STAT));
1151 fDimStatistics1.Update(stat.first);
1152 }
1153
1154 void factStat(const GUI_STAT &stat)
1155 {
1156 fQueueStatistics1.emplace(Time(), stat);
1157 }
1158
1159 void factReportIncomplete(uint64_t rep)
1160 {
1161 fDimIncomplete.setQuality(1);
1162 fDimIncomplete.Update(rep);
1163 }
1164
1165 array<FAD::EventHeader, 40> fVecHeader;
1166
1167 template<typename T, class S>
1168 array<T, 42> Compare(const S *vec, const T *t)
1169 {
1170 const int offset = reinterpret_cast<const char *>(t) - reinterpret_cast<const char *>(vec);
1171
1172 const T *min = NULL;
1173 const T *val = NULL;
1174 const T *max = NULL;
1175
1176 array<T, 42> arr;
1177
1178 // bool rc = true;
1179 for (int i=0; i<40; i++)
1180 {
1181 const char *base = reinterpret_cast<const char*>(vec+i);
1182 const T *ref = reinterpret_cast<const T*>(base+offset);
1183
1184 arr[i] = *ref;
1185
1186 if (gi_NumConnect[i]==0)
1187 {
1188 arr[i] = 0;
1189 continue;
1190 }
1191
1192 if (!val)
1193 {
1194 min = ref;
1195 val = ref;
1196 max = ref;
1197 }
1198
1199 if (*ref<*min)
1200 min = ref;
1201
1202 if (*ref>*max)
1203 max = ref;
1204
1205 // if (*val!=*ref)
1206 // rc = false;
1207 }
1208
1209 arr[40] = val ? *min : 1;
1210 arr[41] = val ? *max : 0;
1211
1212 return arr;
1213 }
1214
1215 template<typename T>
1216 array<T, 42> CompareBits(const FAD::EventHeader *h, const T *t)
1217 {
1218 const int offset = reinterpret_cast<const char *>(t) - reinterpret_cast<const char *>(h);
1219
1220 T val = 0;
1221 T rc = 0;
1222
1223 array<T, 42> vec;
1224
1225 bool first = true;
1226
1227 for (int i=0; i<40; i++)
1228 {
1229 const char *base = reinterpret_cast<const char*>(&fVecHeader[i]);
1230 const T *ref = reinterpret_cast<const T*>(base+offset);
1231
1232 vec[i+2] = *ref;
1233
1234 if (gi_NumConnect[i]==0)
1235 {
1236 vec[i+2] = 0;
1237 continue;
1238 }
1239
1240 if (first)
1241 {
1242 first = false;
1243 val = *ref;
1244 rc = 0;
1245 }
1246
1247 rc |= val^*ref;
1248 }
1249
1250 vec[0] = rc;
1251 vec[1] = val;
1252
1253 return vec;
1254 }
1255
1256 template<typename T, size_t N>
1257 void Update(DimDescribedService &svc, const array<T, N> &data, const Time &t=Time(), int n=N)
1258 {
1259 svc.setData(const_cast<T*>(data.data()), sizeof(T)*n);
1260 svc.Update(t);
1261 }
1262
1263 template<typename T>
1264 void Print(const char *name, const pair<bool,array<T, 43>> &data)
1265 {
1266 cout << name << "|" << data.first << "|" << data.second[1] << "|" << data.second[0] << "<x<" << data.second[1] << ":";
1267 for (int i=0; i<40;i++)
1268 cout << " " << data.second[i+3];
1269 cout << endl;
1270 }
1271
1272 vector<uint> fNumConnected;
1273
1274 void procHeader(const tuple<Time,bool,FAD::EventHeader> &dat)
1275 {
1276 const Time &t = get<0>(dat);
1277 const bool changed = get<1>(dat);
1278 const FAD::EventHeader &h = get<2>(dat);
1279
1280 const FAD::EventHeader old = fVecHeader[h.Id()];
1281 fVecHeader[h.Id()] = h;
1282
1283 if (old.fVersion != h.fVersion || changed)
1284 {
1285 const array<uint16_t,42> ver = Compare(&fVecHeader[0], &fVecHeader[0].fVersion);
1286
1287 array<float,42> data;
1288 for (int i=0; i<42; i++)
1289 {
1290 ostringstream str;
1291 str << (ver[i]>>8) << '.' << (ver[i]&0xff);
1292 data[i] = stof(str.str());
1293 }
1294 Update(fDimFwVersion, data, t);
1295 }
1296
1297 if (old.fRunNumber != h.fRunNumber || changed)
1298 {
1299 const array<uint32_t,42> run = Compare(&fVecHeader[0], &fVecHeader[0].fRunNumber);
1300 fDimRunNumber.setData(&run[0], 42*sizeof(uint32_t));
1301 fDimRunNumber.Update(t);
1302 }
1303
1304 if (old.fTriggerGeneratorPrescaler != h.fTriggerGeneratorPrescaler || changed)
1305 {
1306 const array<uint16_t,42> pre = Compare(&fVecHeader[0], &fVecHeader[0].fTriggerGeneratorPrescaler);
1307 fDimPrescaler.setData(&pre[0], 42*sizeof(uint16_t));
1308 fDimPrescaler.Update(t);
1309 }
1310
1311 if (old.fDNA != h.fDNA || changed)
1312 {
1313 const array<uint64_t,42> dna = Compare(&fVecHeader[0], &fVecHeader[0].fDNA);
1314 Update(fDimDNA, dna, t, 40);
1315 }
1316
1317 if (old.fStatus != h.fStatus || changed)
1318 {
1319 const array<uint16_t,42> sts = CompareBits(&fVecHeader[0], &fVecHeader[0].fStatus);
1320 Update(fDimStatus, sts, t);
1321 }
1322
1323 if (memcmp(old.fDac, h.fDac, sizeof(h.fDac)) || changed)
1324 {
1325 array<uint16_t, FAD::kNumDac*42> dacs;
1326
1327 for (int i=0; i<FAD::kNumDac; i++)
1328 {
1329 const array<uint16_t, 42> dac = Compare(&fVecHeader[0], &fVecHeader[0].fDac[i]);
1330 memcpy(&dacs[i*42], &dac[0], sizeof(uint16_t)*42);
1331 }
1332
1333 Update(fDimDac, dacs, t);
1334 }
1335 }
1336
1337 void debugHead(const FAD::EventHeader &h)
1338 {
1339 const uint16_t id = h.Id();
1340 if (id>39)
1341 return;
1342
1343 if (fNumConnected.size()!=40)
1344 fNumConnected.resize(40);
1345
1346 const vector<uint> con(gi_NumConnect, gi_NumConnect+40);
1347
1348 const bool changed = con!=fNumConnected || !IsThreadRunning();
1349
1350 fNumConnected = con;
1351
1352 fQueueProcHeader.emplace(Time(), changed, h);
1353 }
1354};
1355
1356EventBuilderWrapper *EventBuilderWrapper::This = 0;
1357
1358// ----------- Event builder callbacks implementation ---------------
1359bool runOpen(const EVT_CTRL2 &evt)
1360{
1361 return EventBuilderWrapper::This->runOpen(evt);
1362}
1363
1364bool runWrite(const EVT_CTRL2 &evt)
1365{
1366 return EventBuilderWrapper::This->runWrite(evt);
1367}
1368
1369void runClose(RUN_CTRL2 &run)
1370{
1371 EventBuilderWrapper::This->runClose(run);
1372}
1373
1374bool eventCheck(const EVT_CTRL2 &evt)
1375{
1376 return EventBuilderWrapper::This->eventCheck(evt);
1377}
1378
1379void gotNewRun(RUN_CTRL2 &run)
1380{
1381 EventBuilderWrapper::This->gotNewRun(run);
1382}
1383
1384void runFinished()
1385{
1386 EventBuilderWrapper::This->runFinished();
1387}
1388
1389void applyCalib(const EVT_CTRL2 &evt, const size_t &size)
1390{
1391 EventBuilderWrapper::This->applyCalib(evt, size);
1392}
1393
1394void factOut(int severity, const char *message)
1395{
1396 EventBuilderWrapper::This->factOut(severity, message);
1397}
1398
1399void factStat(const GUI_STAT &stat)
1400{
1401 EventBuilderWrapper::This->factStat(stat);
1402}
1403
1404void factReportIncomplete(uint64_t rep)
1405{
1406 EventBuilderWrapper::This->factReportIncomplete(rep);
1407}
1408
1409// ------
1410
1411void debugHead(void *buf)
1412{
1413 const FAD::EventHeader &h = *reinterpret_cast<FAD::EventHeader*>(buf);
1414 EventBuilderWrapper::This->debugHead(h);
1415}
1416
1417#endif
Note: See TracBrowser for help on using the repository browser.