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

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