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

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