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

Last change on this file since 13220 was 13197, checked in by tbretz, 13 years ago
Shortened some dim descriptions.
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
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
40extern "C" {
41 extern void StartEvtBuild();
42 extern int CloseRunFile(uint32_t runId, uint32_t closeTime, uint32_t maxEvt);
43}
44
45// ========================================================================
46
47class EventBuilderWrapper
48{
49public:
50 // FIXME
51 static EventBuilderWrapper *This;
52
53 MessageImp &fMsg;
54
55private:
56 boost::thread fThread;
57
58 enum CommandStates_t // g_runStat
59 {
60 kAbort = -2, // quit as soon as possible ('abort')
61 kExit = -1, // stop reading, quit when buffered events done ('exit')
62 kInitialize = 0, // 'initialize' (e.g. dim not yet started)
63 kHybernate = 1, // do nothing for long time ('hybernate') [wakeup within ~1sec]
64 kSleep = 2, // do nothing ('sleep') [wakeup within ~10msec]
65 kModeFlush = 10, // read data from camera, but skip them ('flush')
66 kModeTest = 20, // read data and process them, but do not write to disk ('test')
67 kModeFlag = 30, // read data, process and write all to disk ('flag')
68 kModeRun = 40, // read data, process and write selected to disk ('run')
69 };
70
71 enum
72 {
73 kCurrent = 0,
74 kTotal = 1,
75 kEventId = 2,
76 kTriggerId = 3,
77 };
78
79 FAD::FileFormat_t fFileFormat;
80
81 uint32_t fMaxRun;
82 uint32_t fLastOpened;
83 uint32_t fLastClosed;
84 uint32_t fNumEvts[4];
85
86 DimWriteStatistics fDimWriteStats;
87 DimDescribedService fDimRuns;
88 DimDescribedService fDimEvents;
89 DimDescribedService fDimRawData;
90 DimDescribedService fDimEventData;
91 DimDescribedService fDimFeedbackData;
92 DimDescribedService fDimFwVersion;
93 DimDescribedService fDimRunNumber;
94 DimDescribedService fDimStatus;
95 DimDescribedService fDimDNA;
96 DimDescribedService fDimTemperature;
97 DimDescribedService fDimPrescaler;
98 DimDescribedService fDimRefClock;
99 DimDescribedService fDimRoi;
100 DimDescribedService fDimDac;
101 DimDescribedService fDimDrsCalibration;
102 DimDescribedService fDimStatistics1;
103 DimDescribedService fDimStatistics2;
104 DimDescribedService fDimFileFormat;
105
106 bool fDebugStream;
107 bool fDebugRead;
108 bool fDebugLog;
109
110 string fPath;
111 uint32_t fRunNumber;
112
113protected:
114 int64_t InitRunNumber()
115 {
116 fRunNumber = 1000;
117
118 // Ensure that the night doesn't change during our check
119 const int check = Time().NightAsInt();
120
121 while (--fRunNumber>0)
122 {
123 const string name = DataProcessorImp::FormFileName(fPath, fRunNumber, "");
124
125 if (access((name+"bin").c_str(), F_OK) == 0)
126 break;
127 if (access((name+"fits").c_str(), F_OK) == 0)
128 break;
129 if (access((name+"drs.fits").c_str(), F_OK) == 0)
130 break;
131
132 }
133
134 if (check != Time().NightAsInt())
135 return InitRunNumber();
136
137 fRunNumber++;
138
139 if (fRunNumber==1000)
140 {
141 fMsg.Error("You have a file with run-number 1000 in "+fPath);
142 return -1;
143 }
144
145 ostringstream str;
146 str << "Set next run-number to " << fRunNumber << " determined from '" << (fPath.empty()?".":fPath) << "'";
147 fMsg.Message(str);
148
149 //fMsg.Info(" ==> TODO: Crosscheck with database!");
150
151 return check;
152 }
153
154 int64_t InitRunNumber(const string &path)
155 {
156 if (!DimWriteStatistics::DoesPathExist(path, fMsg))
157 {
158 fMsg.Error("Data path "+path+" does not exist!");
159 return -1;
160 }
161
162 //const fs::path fullPath = fs::system_complete(fs::path(path));
163
164 fPath = path;
165
166 fDimWriteStats.SetCurrentFolder(fPath);
167
168 return InitRunNumber();
169 }
170
171public:
172 EventBuilderWrapper(MessageImp &imp) : fMsg(imp),
173 fFileFormat(FAD::kNone), fMaxRun(0), fLastOpened(0), fLastClosed(0),
174 fDimWriteStats ("FAD_CONTROL", imp),
175 fDimRuns ("FAD_CONTROL/RUNS", "I:5;C",
176 "Run files statistics"
177 "|stats[int]:num of open run files, min/max run no, lastest opened or closed run"
178 "|file[string]:filename of last opened file"),
179 fDimEvents ("FAD_CONTROL/EVENTS", "I:4",
180 "Event counts"
181 "|evtsCount[int]:Num evts cur. run, total (all run), evt ID, trig. Num"),
182 fDimRawData ("FAD_CONTROL/RAW_DATA", "S:1;I:1;S:1;I:1;I:2;I:40;S:1440;S:160;F", ""),
183 fDimEventData ("FAD_CONTROL/EVENT_DATA", "F:1440;F:1440;F:1440;F:1440", ""),
184 fDimFeedbackData("FAD_CONTROL/FEEDBACK_DATA", "F:1440", ""),
185 fDimFwVersion ("FAD_CONTROL/FIRMWARE_VERSION", "F:42",
186 "Firmware version number of fad boards"
187 "|firmware[float]:Version number of firmware, for each board. 40=min, 41=max"),
188 fDimRunNumber ("FAD_CONTROL/RUN_NUMBER", "I:42",
189 "Run numbers coming from FAD boards"
190 "|runNumbers[int]:current run number of each FAD board. 40=min, 41=max"),
191 fDimStatus ("FAD_CONTROL/STATUS", "S:42",
192 "Status of FAD boards"
193 "|status[bitpattern]:Status of each FAD board. Maybe buggy"),
194 fDimDNA ("FAD_CONTROL/DNA", "X:40",
195 "DNA of FAD boards"
196 "|DNA[hex]:Hex identifier of each FAD board"),
197 fDimTemperature ("FAD_CONTROL/TEMPERATURE", "F:82",
198 "FADs temperatures"
199 "|temp[deg. C]:0 global min, 1-40 min, 41 global max, 42-81 max"),
200 fDimPrescaler ("FAD_CONTROL/PRESCALER", "S:42",
201 "Trigger generator prescaler of fad boards"
202 "|prescaler[int]:Trigger generator prescaler value, for each board"),
203 fDimRefClock ("FAD_CONTROL/REFERENCE_CLOCK", "I:42",
204 "Reference clock of FAD boards"
205 "|refClocks[t]:ref clocks of FAD boards. 40=min, 41=max"),
206 fDimRoi ("FAD_CONTROL/REGION_OF_INTEREST", "S:2", ""),
207 fDimDac ("FAD_CONTROL/DAC", "S:336",
208 "DAC settings of each FAD board"
209 "|DAC[int]:DAC counts, sequentially DAC 0 board 0, 0/1, 0/2... (plus min max)"),
210 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", ""),
211 fDimStatistics1 ("FAD_CONTROL/STATISTICS1", "I:3;I:5;X:4;I:3;I:3;I:40;I:1;I:2;C:40;I:40;I:40;X:40",
212 "Event Builder status for GUI display"
213 "|threadInfo[int]:Number of read, proc and writes"
214 "|bufferInfo[int]:Events in buffer, incomp., comp., tot., max past cycle, total"
215 "|memInfo[int]:total buf. mem, used mem, max used, max past cycle"
216 "|EvtCnt[int]:Number of events skipped, written, with errors"
217 "|badRoi[int]:Num boards with wrong ROI in event, run or board"
218 "|badRoiBoard[int]:Num boards with wrong ROI"
219 "|deltaT[ms]:Time in ms for rates"
220 "|rateNew[int]:Number of new start events received"
221 "|numConn[int]:Number of connections per board"
222 "|errConn[int]:IO errors per board"
223 "|rateBytes[int]:Bytes read this cycle"
224 "|totBytes[int]:Bytes read (counter)"),
225 fDimStatistics2 ("FAD_CONTROL/STATISTICS2", "I:1;I:280;X:40;I:40;I:4;I:4;I:2;I:2;I:3;C:40",
226 "Event Builder status, events oriented"
227 "|reset[int]:If increased, reset all counters"
228 "|numRead[int]:How often sucessful read from N sockets per loop"
229 "|gotByte[int]:number of bytes read per board"
230 "|gotErr[int]:number of com. errors per board"
231 "|evtStat[int]:number of evts read, completed, with errors, incomplete"
232 "|procStat[int]:num. of evts proc., w probs, acc. or rej. by SW trigger"
233 "|feedStat[int]:number of evts used or rejected by feedback system"
234 "|wrtStat[int]:number of evts written to disk, with errors"
235 "|runStat[int]:number of run opened, closed, with open or close errors"
236 "|numConn[int]:number of sockets successfully opened per board"),
237 fDimFileFormat ("FAD_CONTROL/FILE_FORMAT", "S:1", ""),
238 fDebugStream(false), fDebugRead(false), fDebugLog(false)
239 {
240 if (This)
241 throw logic_error("EventBuilderWrapper cannot be instantiated twice.");
242
243 This = this;
244
245 memset(fNumEvts, 0, sizeof(fNumEvts));
246 fDimEvents.Update(fNumEvts);
247
248 for (size_t i=0; i<40; i++)
249 ConnectSlot(i, tcp::endpoint());
250 }
251 virtual ~EventBuilderWrapper()
252 {
253 Abort();
254
255 // FIXME: Used timed_join and abort afterwards
256 // What's the maximum time the eb need to abort?
257 fThread.join();
258 //ffMsg.Info("EventBuilder stopped.");
259
260 for (vector<DataProcessorImp*>::iterator it=fFiles.begin(); it!=fFiles.end(); it++)
261 delete *it;
262 }
263
264 set<uint32_t> fIsRunStarted;
265 map<uint32_t, FAD::RunDescription> fExpectedRuns;
266
267 uint32_t StartNewRun(int64_t maxtime, int64_t maxevt, const pair<string, FAD::Configuration> &ref)
268 {
269 if (maxtime<=0 || maxtime>24*60*60)
270 maxtime = 24*60*60;
271 if (maxevt<=0 || maxevt>INT32_MAX)
272 maxevt = INT32_MAX;
273
274 const FAD::RunDescription descr =
275 {
276 uint32_t(maxtime),
277 uint32_t(maxevt),
278 ref.first,
279 ref.second,
280 };
281
282 // FIMXE: Maybe reset an event counter so that the mcp can count events?
283
284 //fMsg.Info(" ==> TODO: Set a limit on the size of fExpectedRuns!");
285
286 fExpectedRuns[fRunNumber] = descr;
287 fIsRunStarted.insert(fRunNumber);
288 return fRunNumber++;
289 }
290
291 bool IsThreadRunning()
292 {
293 return !fThread.timed_join(boost::posix_time::microseconds(0));
294 }
295
296 void SetMaxMemory(unsigned int mb) const
297 {
298 /*
299 if (mb*1000000<GetUsedMemory())
300 {
301 // ffMsg.Warn("...");
302 return;
303 }*/
304
305 g_maxMem = size_t(mb)*1000000;
306 }
307
308 void StartThread(const vector<tcp::endpoint> &addr)
309 {
310 if (IsThreadRunning())
311 {
312 fMsg.Warn("Start - EventBuilder still running");
313 return;
314 }
315
316 fLastMessage.clear();
317
318 for (size_t i=0; i<40; i++)
319 ConnectSlot(i, addr[i]);
320
321 g_runStat = kModeRun;
322 g_maxProc = 3;
323
324 fMsg.Message("Starting EventBuilder thread");
325
326 fThread = boost::thread(StartEvtBuild);
327 }
328 void ConnectSlot(unsigned int i, const tcp::endpoint &addr)
329 {
330 if (i>39)
331 return;
332
333 if (addr==tcp::endpoint())
334 {
335 DisconnectSlot(i);
336 return;
337 }
338
339 g_port[i].sockAddr.sin_family = AF_INET;
340 g_port[i].sockAddr.sin_addr.s_addr = htonl(addr.address().to_v4().to_ulong());
341 g_port[i].sockAddr.sin_port = htons(addr.port());
342 // In this order
343 g_port[i].sockDef = 1;
344 }
345 void DisconnectSlot(unsigned int i)
346 {
347 if (i>39)
348 return;
349
350 g_port[i].sockDef = 0;
351 // In this order
352 g_port[i].sockAddr.sin_family = AF_INET;
353 g_port[i].sockAddr.sin_addr.s_addr = 0;
354 g_port[i].sockAddr.sin_port = 0;
355 }
356 void IgnoreSlot(unsigned int i)
357 {
358 if (i>39)
359 return;
360 if (g_port[i].sockAddr.sin_port==0)
361 return;
362
363 g_port[i].sockDef = -1;
364 }
365
366
367 void Abort()
368 {
369 fMsg.Message("Signal abort to EventBuilder thread...");
370 g_runStat = kAbort;
371 }
372
373 void ResetThread(bool soft)
374 {
375 /*
376 if (g_reset > 0)
377
378 * suspend reading
379 * reset = g_reset;
380 * g_reset=0
381
382 * reset% 10
383 == 0 leave event Buffers as they are
384 == 1 let all buffers drain (write (incomplete) events)
385 > 1 flush all buffers (do not write buffered events)
386
387 * (reset/10)%10
388 > 0 close all sockets and destroy them (also free the
389 allocated read-buffers)
390 recreate before resuming operation
391 [ this is more than just close/open that can be
392 triggered by e.g. close/open the base-socket ]
393
394 * (reset/100)%10
395 > 0 close all open run-files
396
397 * (reset/1000)
398 sleep so many seconds before resuming operation
399 (does not (yet) take into account time left when waiting
400 for buffers getting empty ...)
401
402 * resume_reading
403
404 */
405 fMsg.Message("Signal reset to EventBuilder thread...");
406 g_reset = soft ? 101 : 102;
407 }
408
409 void Exit()
410 {
411 fMsg.Message("Signal exit to EventBuilder thread...");
412 g_runStat = kExit;
413 }
414
415 /*
416 void Wait()
417 {
418 fThread.join();
419 ffMsg.Message("EventBuilder stopped.");
420 }*/
421
422 void Hybernate() const { g_runStat = kHybernate; }
423 void Sleep() const { g_runStat = kSleep; }
424 void FlushMode() const { g_runStat = kModeFlush; }
425 void TestMode() const { g_runStat = kModeTest; }
426 void FlagMode() const { g_runStat = kModeFlag; }
427 void RunMode() const { g_runStat = kModeRun; }
428
429 // FIXME: To be removed
430 //void SetMode(int mode) const { g_runStat = mode; }
431
432 bool IsConnected(int i) const { return gi_NumConnect[i]==7; }
433 bool IsConnecting(int i) const { return !IsConnected(i) && !IsDisconnected(i); }
434 bool IsDisconnected(int i) const { return gi_NumConnect[i]<=0 && g_port[i].sockDef==0; }
435 int GetNumConnected(int i) const { return gi_NumConnect[i]; }
436 int GetNumFilesOpen() const { return fFiles.size(); }
437
438 /*
439 bool IsConnected(int i) const { return gi_NumConnect[i]>0; }
440 bool IsConnecting(int i) const { return !IsConnected(i) && !IsDisconnected(i); }
441 bool IsDisconnected(int i) const { return gi_NumConnect[i]<=0 && g_port[i].sockDef==0; }
442 int GetNumConnected(int i) const { return gi_NumConnect[i]; }
443 */
444
445 void SetIgnore(int i, bool b) const { if (g_port[i].sockDef!=0) g_port[i].sockDef=b?-1:1; }
446 bool IsIgnored(int i) const { return g_port[i].sockDef==-1; }
447
448 void SetOutputFormat(FAD::FileFormat_t f)
449 {
450 fFileFormat = f;
451 fDimFileFormat.Update(uint16_t(f));
452 if (fFileFormat==FAD::kCalib)
453 {
454 DataCalib::Restart();
455 DataCalib::Update(fDimDrsCalibration);
456 fMsg.Message("Resetted DRS calibration.");
457 }
458 }
459
460 virtual int ResetSecondaryDrsBaseline()
461 {
462 if (DataCalib::ResetTrgOff(fDimDrsCalibration))
463 {
464 fFileFormat = FAD::kCalib;
465 fDimFileFormat.Update(uint16_t(fFileFormat));
466 fMsg.Message("Resetted DRS calibration for secondary baseline.");
467 }
468 else
469 fMsg.Warn("Could not reset DRS calibration of secondary baseline.");
470
471 return 0;
472 }
473
474 void SetDebugLog(bool b) { fDebugLog = b; }
475
476 void SetDebugStream(bool b)
477 {
478 fDebugStream = b;
479 if (b)
480 return;
481
482 for (int i=0; i<40; i++)
483 {
484 if (!fDumpStream[i].is_open())
485 continue;
486
487 fDumpStream[i].close();
488
489 ostringstream name;
490 name << "socket_dump-" << setfill('0') << setw(2) << i << ".bin";
491 fMsg.Message("Closed file '"+name.str()+"'");
492 }
493 }
494
495 void SetDebugRead(bool b)
496 {
497 fDebugRead = b;
498 if (b || !fDumpRead.is_open())
499 return;
500
501 fDumpRead.close();
502 fMsg.Message("Closed file 'socket_events.txt'");
503 }
504
505// size_t GetUsedMemory() const { return gi_usedMem; }
506
507 void LoadDrsCalibration(const char *fname)
508 {
509 if (!DataCalib::ReadFits(fname, fMsg))
510 return;
511 fMsg.Info("Successfully loaded DRS calibration from "+string(fname));
512 DataCalib::Update(fDimDrsCalibration);
513 }
514
515 virtual int CloseOpenFiles() { CloseRunFile(0, 0, 0); return 0; }
516
517
518 /*
519 struct OpenFileToDim
520 {
521 int code;
522 char fileName[FILENAME_MAX];
523 };
524
525 SignalRunOpened(runid, filename);
526 // Send num open files
527 // Send runid, (more info about the run?), filename via dim
528
529 SignalEvtWritten(runid);
530 // Send num events written of newest file
531
532 SignalRunClose(runid);
533 // Send new num open files
534 // Send empty file-name if no file is open
535
536 */
537
538 // -------------- Mapped event builder callbacks ------------------
539
540 void UpdateRuns(const string &fname="")
541 {
542 uint32_t values[5] =
543 {
544 static_cast<uint32_t>(fFiles.size()),
545 0xffffffff,
546 0,
547 fLastOpened,
548 fLastClosed
549 };
550
551 for (vector<DataProcessorImp*>::const_iterator it=fFiles.begin();
552 it!=fFiles.end(); it++)
553 {
554 const DataProcessorImp *file = *it;
555
556 if (file->GetRunId()<values[1])
557 values[1] = file->GetRunId();
558
559 if (file->GetRunId()>values[2])
560 values[2] = file->GetRunId();
561 }
562
563 fMaxRun = values[2];
564
565 vector<char> data(sizeof(values)+fname.size()+1);
566 memcpy(data.data(), values, sizeof(values));
567 strcpy(data.data()+sizeof(values), fname.c_str());
568
569 fDimRuns.Update(data);
570 }
571
572 vector<DataProcessorImp*> fFiles;
573
574 FileHandle_t runOpen(uint32_t runid, RUN_HEAD *h, size_t)
575 {
576 //fMsg.Info(" ==> TODO: Update run configuration in database!");
577
578 map<uint32_t,FAD::RunDescription>::iterator it = fExpectedRuns.begin();
579 while (it!=fExpectedRuns.end())
580 {
581 if (it->first<runid)
582 {
583 ostringstream str;
584 str << "runOpen - Missed run " << it->first << ".";
585 fMsg.Info(str);
586
587 fExpectedRuns.erase(it++);
588 continue;
589 }
590 if (it->first==runid)
591 break;
592 it++;
593 }
594
595 FAD::RunDescription desc;
596
597 if (it==fExpectedRuns.end())
598 {
599 ostringstream str;
600 str << "runOpen - Run " << runid << " wasn't expected (maybe manual triggers)";
601 fMsg.Warn(str);
602 }
603 else
604 {
605 desc = it->second;
606 fExpectedRuns.erase(it);
607 }
608
609 // Check if file already exists...
610 DataProcessorImp *file = 0;
611 switch (fFileFormat)
612 {
613 case FAD::kNone: file = new DataDump(fPath, runid, fMsg); break;
614 case FAD::kDebug: file = new DataDebug(fPath, runid, fMsg); break;
615 case FAD::kCfitsio: file = new DataWriteFits(fPath, runid, fMsg); break;
616 case FAD::kFits: file = new DataWriteFits2(fPath, runid, fMsg); break;
617 case FAD::kRaw: file = new DataWriteRaw(fPath, runid, fMsg); break;
618 case FAD::kCalib: file = new DataCalib(fPath, runid, fDimDrsCalibration, fMsg); break;
619 }
620
621 try
622 {
623 if (!file->Open(h, desc))
624 return 0;
625 }
626 catch (const exception &e)
627 {
628 fMsg.Error("Exception trying to open file: "+string(e.what()));
629 return 0;
630 }
631
632 fFiles.push_back(file);
633
634 ostringstream str;
635 str << "Opened: " << file->GetFileName() << " (" << file->GetRunId() << ")";
636 fMsg.Info(str);
637
638 fDimWriteStats.FileOpened(file->GetFileName());
639
640 fLastOpened = runid;
641 UpdateRuns(file->GetFileName());
642
643 fNumEvts[kEventId] = 0;
644 fNumEvts[kTriggerId] = 0;
645
646 fNumEvts[kCurrent] = 0;
647 fDimEvents.Update(fNumEvts);
648 // fDimCurrentEvent.Update(uint32_t(0));
649
650 return reinterpret_cast<FileHandle_t>(file);
651 }
652
653 int runWrite(FileHandle_t handler, EVENT *e, size_t /*sz*/)
654 {
655 DataProcessorImp *file = reinterpret_cast<DataProcessorImp*>(handler);
656
657 if (!file->WriteEvt(e))
658 return -1;
659
660 if (file->GetRunId()==fMaxRun)
661 {
662 fNumEvts[kCurrent]++;
663 fNumEvts[kEventId] = e->EventNum;
664 fNumEvts[kTriggerId] = e->TriggerNum;
665 }
666
667 fNumEvts[kTotal]++;
668
669 static Time oldt(boost::date_time::neg_infin);
670 Time newt;
671 if (newt>oldt+boost::posix_time::seconds(1))
672 {
673 fDimEvents.Update(fNumEvts);
674 oldt = newt;
675 }
676
677
678 // ===> SignalEvtWritten(runid);
679 // Send num events written of newest file
680
681 /* close run runId (all all runs if runId=0) */
682 /* return: 0=close scheduled / >0 already closed / <0 does not exist */
683 //CloseRunFile(file->GetRunId(), time(NULL)+2) ;
684
685 return 0;
686 }
687
688 virtual void CloseRun(uint32_t /*runid*/) { }
689
690 int runClose(FileHandle_t handler, RUN_TAIL *tail, size_t)
691 {
692 //fMsg.Info(" ==> TODO: Update run configuration in database!");
693
694 DataProcessorImp *file = reinterpret_cast<DataProcessorImp*>(handler);
695
696 const vector<DataProcessorImp*>::iterator it = find(fFiles.begin(), fFiles.end(), file);
697 if (it==fFiles.end())
698 {
699 ostringstream str;
700 str << "File handler (" << handler << ") requested to close by event builder doesn't exist.";
701 fMsg.Fatal(str);
702 return -1;
703 }
704
705 fFiles.erase(it);
706
707 fLastClosed = file->GetRunId();
708 CloseRun(fLastClosed);
709 UpdateRuns();
710
711 fDimEvents.Update(fNumEvts);
712
713 const bool rc = file->Close(tail);
714 if (!rc)
715 {
716 // Error message
717 }
718
719 ostringstream str;
720 str << "Closed: " << file->GetFileName() << " (" << file->GetRunId() << ")";
721 fMsg.Info(str);
722
723 delete file;
724
725 // ==> SignalRunClose(runid);
726 // Send new num open files
727 // Send empty file-name if no file is open
728
729 return rc ? 0 : -1;
730 }
731
732 ofstream fDumpStream[40];
733
734 void debugStream(int isock, void *buf, int len)
735 {
736 if (!fDebugStream)
737 return;
738
739 const int slot = isock/7;
740 if (slot<0 || slot>39)
741 return;
742
743 if (!fDumpStream[slot].is_open())
744 {
745 ostringstream name;
746 name << "socket_dump-" << setfill('0') << setw(2) << slot << ".bin";
747
748 fDumpStream[slot].open(name.str().c_str(), ios::app);
749 if (!fDumpStream[slot])
750 {
751 ostringstream str;
752 str << "Open file '" << name << "': " << strerror(errno) << " (errno=" << errno << ")";
753 fMsg.Error(str);
754
755 return;
756 }
757
758 fMsg.Message("Opened file '"+name.str()+"' for writing.");
759 }
760
761 fDumpStream[slot].write(reinterpret_cast<const char*>(buf), len);
762 }
763
764 ofstream fDumpRead; // Stream to possibly dump docket events
765
766 void debugRead(int isock, int ibyte, uint32_t event, uint32_t ftmevt, uint32_t runno, int state, uint32_t tsec, uint32_t tusec)
767 {
768 // isock = socketID (0-279)
769 // ibyte = #bytes gelesen
770 // event = eventId (oder 0 wenn noch nicht bekannt)
771 // state : 1=finished reading data
772 // 0=reading data
773 // -1=start reading data (header)
774 // -2=start reading data,
775 // eventId not known yet (too little data)
776 // tsec, tusec = time when reading seconds, microseconds
777 //
778 if (!fDebugRead || ibyte==0)
779 return;
780
781 if (!fDumpRead.is_open())
782 {
783 fDumpRead.open("socket_events.txt", ios::app);
784 if (!fDumpRead)
785 {
786 ostringstream str;
787 str << "Open file 'socket_events.txt': " << strerror(errno) << " (errno=" << errno << ")";
788 fMsg.Error(str);
789
790 return;
791 }
792
793 fMsg.Message("Opened file 'socket_events.txt' for writing.");
794
795 fDumpRead << "# START: " << Time().GetAsStr() << endl;
796 fDumpRead << "# state time_sec time_usec socket slot runno event_id trigger_id bytes_received" << endl;
797 }
798
799 fDumpRead
800 << setw(2) << state << " "
801 << setw(8) << tsec << " "
802 << setw(9) << tusec << " "
803 << setw(3) << isock << " "
804 << setw(2) << isock/7 << " "
805 << runno << " "
806 << event << " "
807 << ftmevt << " "
808 << ibyte << endl;
809 }
810
811 array<uint16_t,2> fVecRoi;
812
813 int eventCheck(uint32_t runNr, PEVNT_HEADER *fadhd, EVENT *event, int /*iboard*/)
814 {
815 /*
816 fadhd[i] ist ein array mit den 40 fad-headers
817 (falls ein board nicht gelesen wurde, ist start_package_flag =0 )
818
819 event ist die Struktur, die auch die write routine erhaelt;
820 darin sind im header die 'soll-werte' fuer z.B. eventID
821 als auch die ADC-Werte (falls Du die brauchst)
822
823 Wenn die routine einen negativen Wert liefert, wird das event
824 geloescht (nicht an die write-routine weitergeleitet [mind. im Prinzip]
825 */
826
827 const array<uint16_t,2> roi = {{ event->Roi, event->RoiTM }};
828
829 if (roi!=fVecRoi)
830 {
831 Update(fDimRoi, roi);
832 fVecRoi = roi;
833 }
834
835 const FAD::EventHeader *beg = reinterpret_cast<FAD::EventHeader*>(fadhd);
836 const FAD::EventHeader *end = reinterpret_cast<FAD::EventHeader*>(fadhd)+40;
837
838 // FIMXE: Compare with target configuration
839
840 for (const FAD::EventHeader *ptr=beg; ptr!=end; ptr++)
841 {
842 // FIXME: Compare with expectations!!!
843 if (ptr->fStartDelimiter==0)
844 {
845 if (ptr==beg)
846 beg++;
847 continue;
848 }
849
850 if (beg->fStatus != ptr->fStatus)
851 {
852 fMsg.Error("Inconsistency in FAD status detected.... closing run.");
853 CloseRunFile(runNr, 0, 0);
854 return -1;
855 }
856
857 if (beg->fRunNumber != ptr->fRunNumber)
858 {
859 fMsg.Error("Inconsistent run number detected.... closing run.");
860 CloseRunFile(runNr, 0, 0);
861 return -1;
862 }
863
864 /*
865 if (beg->fVersion != ptr->fVersion)
866 {
867 Error("Inconsist firmware version detected.... closing run.");
868 CloseRunFile(runNr, 0, 0);
869 break;
870 }
871 */
872 if (beg->fEventCounter != ptr->fEventCounter)
873 {
874 fMsg.Error("Inconsistent FAD event number detected.... closing run.");
875 CloseRunFile(runNr, 0, 0);
876 return -1;
877 }
878
879 if (beg->fTriggerCounter != ptr->fTriggerCounter)
880 {
881 fMsg.Error("Inconsistent FTM trigger number detected.... closing run.");
882 CloseRunFile(runNr, 0, 0);
883 return -1;
884 }
885
886 if (beg->fAdcClockPhaseShift != ptr->fAdcClockPhaseShift)
887 {
888 fMsg.Error("Inconsistent phase shift detected.... closing run.");
889 CloseRunFile(runNr, 0, 0);
890 return -1;
891 }
892
893 if (memcmp(beg->fDac, ptr->fDac, sizeof(beg->fDac)))
894 {
895 fMsg.Error("Inconsistent DAC values detected.... closing run.");
896 CloseRunFile(runNr, 0, 0);
897 return -1;
898 }
899
900 if (beg->fTriggerType != ptr->fTriggerType)
901 {
902 fMsg.Error("Inconsistent trigger type detected.... closing run.");
903 CloseRunFile(runNr, 0, 0);
904 return -1;
905 }
906 }
907
908 // check REFCLK_frequency
909 // check consistency with command configuration
910 // how to log errors?
911 // need gotNewRun/closedRun to know it is finished
912
913 return 0;
914 }
915
916 void SendRawData(PEVNT_HEADER */*fadhd*/, EVENT *event)
917 {
918 // Currently we send any event no matter what its trigger id is...
919 // To be changed.
920 static Time oldt(boost::date_time::neg_infin);
921 Time newt;
922
923 // FIXME: Only send events if the have newer run-numbers
924 if (newt<oldt+boost::posix_time::seconds(1))
925 return;
926
927 oldt = newt;
928
929 vector<char> data(sizeof(EVENT)+event->Roi*sizeof(float)*(1440+160));
930 memcpy(data.data(), event, sizeof(EVENT));
931
932 float *vec = reinterpret_cast<float*>(data.data()+sizeof(EVENT));
933
934 DataCalib::Apply(vec, event->Adc_Data, event->StartPix, event->Roi);
935 fDimRawData.Update(data);
936
937 DrsCalibrate::RemoveSpikes(vec, event->Roi);
938
939 vector<float> data2(1440*4); // Mean, RMS, Max, Pos
940 DrsCalibrate::GetPixelStats(data2.data(), vec, event->Roi);
941
942 fDimEventData.Update(data2);
943 }
944
945 void SendFeedbackData(PEVNT_HEADER *fadhd, EVENT *event)
946 {
947 if (!DataCalib::IsValid())
948 return;
949
950 // Workaround to find a valid header.....
951 const FAD::EventHeader *beg = reinterpret_cast<FAD::EventHeader*>(fadhd);
952 const FAD::EventHeader *end = reinterpret_cast<FAD::EventHeader*>(fadhd)+40;
953
954 // FIMXE: Compare with target configuration
955
956 const FAD::EventHeader *ptr=beg;
957 for (; ptr!=end; ptr++)
958 {
959 if (ptr->fStartDelimiter==0)
960 continue;
961
962 if (!ptr->HasTriggerLPext() && !ptr->HasTriggerLPint())
963 return;
964 }
965
966 if (ptr->fStartDelimiter==0)
967 return;
968
969 vector<float> data(event->Roi*1440);
970 DataCalib::Apply(data.data(), event->Adc_Data, event->StartPix, event->Roi);
971
972 DrsCalibrate::RemoveSpikes(data.data(), event->Roi);
973
974 vector<float> data2(1440); // Mean, RMS, Max, Pos, first, last
975 DrsCalibrate::GetPixelMax(data2.data(), data.data(), event->Roi, 0, event->Roi-1);
976
977 fDimFeedbackData.Update(data2);
978 }
979
980 int subProcEvt(int threadID, PEVNT_HEADER *fadhd, EVENT *event, int16_t /*iboard*/, void */*buffer*/)
981 {
982 switch (threadID)
983 {
984 case 0:
985 SendRawData(fadhd, event);
986 return 1;
987 case 1:
988 SendFeedbackData(fadhd, event);
989 return 2;
990 }
991 return 100;
992 }
993
994
995 bool IsRunStarted() const
996 {
997 const set<uint32_t>::const_iterator it = fIsRunStarted.find(fRunNumber-1);
998 return it==fIsRunStarted.end();// ? true : it->second.started;
999 }
1000
1001 uint32_t GetRunNumber() const
1002 {
1003 return fRunNumber;
1004 }
1005
1006 void IncreaseRunNumber(uint32_t run)
1007 {
1008 if (run>fRunNumber)
1009 fRunNumber = run;
1010 }
1011
1012 void gotNewRun(uint32_t runnr, PEVNT_HEADER */*headers*/)
1013 {
1014 // This function is called even when writing is switched off
1015 set<uint32_t>::iterator it = fIsRunStarted.begin();
1016 while (it!=fIsRunStarted.end())
1017 {
1018 if (*it<runnr)
1019 {
1020 ostringstream str;
1021 str << "gotNewRun - Missed run " << *it << ".";
1022 fMsg.Info(str);
1023
1024 fIsRunStarted.erase(it++);
1025 continue;
1026 }
1027 if (*it==runnr)
1028 break;
1029 it++;
1030 }
1031 if (it==fIsRunStarted.end())
1032 {
1033 ostringstream str;
1034 str << "gotNewRun - Not waiting for run " << runnr << ".";
1035 fMsg.Warn(str);
1036 return;
1037 }
1038
1039 map<uint32_t,FAD::RunDescription>::iterator i2 = fExpectedRuns.find(runnr);
1040 if (i2==fExpectedRuns.end())
1041 {
1042 ostringstream str;
1043 str << "gotNewRun - Run " << runnr << " wasn't expected.";
1044 fMsg.Warn(str);
1045 return;
1046 }
1047
1048 CloseRunFile(runnr, time(NULL)+i2->second.maxtime, i2->second.maxevt);
1049 // return: 0=close scheduled / >0 already closed / <0 does not exist
1050
1051 // FIXME: Move configuration from expected runs to runs which will soon
1052 // be opened/closed
1053
1054 fIsRunStarted.erase(it);
1055 }
1056
1057 map<boost::thread::id, string> fLastMessage;
1058
1059 void factOut(int severity, int err, const char *message)
1060 {
1061 if (!fDebugLog && severity==99)
1062 return;
1063
1064 ostringstream str;
1065 //str << boost::this_thread::get_id() << " ";
1066 str << "EventBuilder(";
1067 if (err<0)
1068 str << "---";
1069 else
1070 str << err;
1071 str << "): " << message;
1072
1073 string &old = fLastMessage[boost::this_thread::get_id()];
1074
1075 if (str.str()==old)
1076 return;
1077 old = str.str();
1078
1079 fMsg.Update(str, severity);
1080 }
1081/*
1082 void factStat(int64_t *stat, int len)
1083 {
1084 if (len!=7)
1085 {
1086 fMsg.Warn("factStat received unknown number of values.");
1087 return;
1088 }
1089
1090 vector<int64_t> data(1, g_maxMem);
1091 data.insert(data.end(), stat, stat+len);
1092
1093 static vector<int64_t> last(8);
1094 if (data==last)
1095 return;
1096 last = data;
1097
1098 fDimStatistics.Update(data);
1099
1100 // len ist die Laenge des arrays.
1101 // array[4] enthaelt wieviele bytes im Buffer aktuell belegt sind; daran
1102 // kannst Du pruefen, ob die 100MB voll sind ....
1103
1104 ostringstream str;
1105 str
1106 << "Wait=" << stat[0] << " "
1107 << "Skip=" << stat[1] << " "
1108 << "Del=" << stat[2] << " "
1109 << "Tot=" << stat[3] << " "
1110 << "Mem=" << stat[4] << "/" << g_maxMem << " "
1111 << "Read=" << stat[5] << " "
1112 << "Conn=" << stat[6];
1113
1114 fMsg.Info(str);
1115 }
1116 */
1117
1118 void factStat(const EVT_STAT &stat)
1119 {
1120 fDimStatistics2.Update(stat);
1121 }
1122
1123 void factStat(const GUI_STAT &stat)
1124 {
1125 fDimStatistics1.Update(stat);
1126 }
1127
1128
1129 array<FAD::EventHeader, 40> fVecHeader;
1130
1131 template<typename T, class S>
1132 array<T, 42> Compare(const S *vec, const T *t)
1133 {
1134 const int offset = reinterpret_cast<const char *>(t) - reinterpret_cast<const char *>(vec);
1135
1136 const T *min = NULL;
1137 const T *val = NULL;
1138 const T *max = NULL;
1139
1140 array<T, 42> arr;
1141
1142 // bool rc = true;
1143 for (int i=0; i<40; i++)
1144 {
1145 const char *base = reinterpret_cast<const char*>(vec+i);
1146 const T *ref = reinterpret_cast<const T*>(base+offset);
1147
1148 arr[i] = *ref;
1149
1150 if (gi_NumConnect[i]!=7)
1151 {
1152 arr[i] = 0;
1153 continue;
1154 }
1155
1156 if (!val)
1157 {
1158 min = ref;
1159 val = ref;
1160 max = ref;
1161 }
1162
1163 if (*ref<*min)
1164 min = ref;
1165
1166 if (*ref>*max)
1167 max = ref;
1168
1169 // if (*val!=*ref)
1170 // rc = false;
1171 }
1172
1173 arr[40] = val ? *min : 1;
1174 arr[41] = val ? *max : 0;
1175
1176 return arr;
1177 }
1178
1179 template<typename T>
1180 array<T, 42> CompareBits(const FAD::EventHeader *h, const T *t)
1181 {
1182 const int offset = reinterpret_cast<const char *>(t) - reinterpret_cast<const char *>(h);
1183
1184 T val = 0;
1185 T rc = 0;
1186
1187 array<T, 42> vec;
1188
1189 bool first = true;
1190
1191 for (int i=0; i<40; i++)
1192 {
1193 const char *base = reinterpret_cast<const char*>(&fVecHeader[i]);
1194 const T *ref = reinterpret_cast<const T*>(base+offset);
1195
1196 vec[i+2] = *ref;
1197
1198 if (gi_NumConnect[i]!=7)
1199 {
1200 vec[i+2] = 0;
1201 continue;
1202 }
1203
1204 if (first)
1205 {
1206 first = false;
1207 val = *ref;
1208 rc = 0;
1209 }
1210
1211 rc |= val^*ref;
1212 }
1213
1214 vec[0] = rc;
1215 vec[1] = val;
1216
1217 return vec;
1218 }
1219
1220 template<typename T, size_t N>
1221 void Update(DimDescribedService &svc, const array<T, N> &data, int n=N)
1222 {
1223// svc.setQuality(vec[40]<=vec[41]);
1224 svc.setData(const_cast<T*>(data.data()), sizeof(T)*n);
1225 svc.Update();
1226 }
1227
1228 template<typename T>
1229 void Print(const char *name, const pair<bool,array<T, 43>> &data)
1230 {
1231 cout << name << "|" << data.first << "|" << data.second[1] << "|" << data.second[0] << "<x<" << data.second[1] << ":";
1232 for (int i=0; i<40;i++)
1233 cout << " " << data.second[i+3];
1234 cout << endl;
1235 }
1236
1237 vector<uint> fNumConnected;
1238
1239 void debugHead(int /*socket*/, const FAD::EventHeader &h)
1240 {
1241 const uint16_t id = h.Id();
1242 if (id>39)
1243 return;
1244
1245 if (fNumConnected.size()!=40)
1246 fNumConnected.resize(40);
1247
1248 const vector<uint> con(gi_NumConnect, gi_NumConnect+40);
1249
1250 const bool changed = con!=fNumConnected || !IsThreadRunning();
1251
1252 fNumConnected = con;
1253
1254 const FAD::EventHeader old = fVecHeader[id];
1255 fVecHeader[id] = h;
1256
1257 if (old.fVersion != h.fVersion || changed)
1258 {
1259 const array<uint16_t,42> ver = Compare(&fVecHeader[0], &fVecHeader[0].fVersion);
1260
1261 array<float,42> data;
1262 for (int i=0; i<42; i++)
1263 {
1264 ostringstream str;
1265 str << (ver[i]>>8) << '.' << (ver[i]&0xff);
1266 data[i] = stof(str.str());
1267 }
1268 Update(fDimFwVersion, data);
1269 }
1270
1271 if (old.fRunNumber != h.fRunNumber || changed)
1272 {
1273 const array<uint32_t,42> run = Compare(&fVecHeader[0], &fVecHeader[0].fRunNumber);
1274 fDimRunNumber.Update(run);
1275 }
1276
1277 if (old.fTriggerGeneratorPrescaler != h.fTriggerGeneratorPrescaler || changed)
1278 {
1279 const array<uint16_t,42> pre = Compare(&fVecHeader[0], &fVecHeader[0].fTriggerGeneratorPrescaler);
1280 fDimPrescaler.Update(pre);
1281 }
1282
1283 if (old.fDNA != h.fDNA || changed)
1284 {
1285 const array<uint64_t,42> dna = Compare(&fVecHeader[0], &fVecHeader[0].fDNA);
1286 Update(fDimDNA, dna, 40);
1287 }
1288
1289 if (old.fStatus != h.fStatus || changed)
1290 {
1291 const array<uint16_t,42> sts = CompareBits(&fVecHeader[0], &fVecHeader[0].fStatus);
1292 Update(fDimStatus, sts);
1293 }
1294
1295 if (memcmp(old.fDac, h.fDac, sizeof(h.fDac)) || changed)
1296 {
1297 array<uint16_t, FAD::kNumDac*42> dacs;
1298
1299 for (int i=0; i<FAD::kNumDac; i++)
1300 {
1301 const array<uint16_t, 42> dac = Compare(&fVecHeader[0], &fVecHeader[0].fDac[i]);
1302 memcpy(&dacs[i*42], &dac[0], sizeof(uint16_t)*42);
1303 }
1304
1305 Update(fDimDac, dacs);
1306 }
1307
1308 // -----------
1309
1310 static Time oldt(boost::date_time::neg_infin);
1311 Time newt;
1312
1313 if (newt>oldt+boost::posix_time::seconds(1))
1314 {
1315 oldt = newt;
1316
1317 // --- RefClock
1318
1319 const array<uint32_t,42> clk = Compare(&fVecHeader[0], &fVecHeader[0].fFreqRefClock);
1320 Update(fDimRefClock, clk);
1321
1322 // --- Temperatures
1323
1324 const array<int16_t,42> tmp[4] =
1325 {
1326 Compare(&fVecHeader[0], &fVecHeader[0].fTempDrs[0]), // 0-39:val, 40:min, 41:max
1327 Compare(&fVecHeader[0], &fVecHeader[0].fTempDrs[1]), // 0-39:val, 40:min, 41:max
1328 Compare(&fVecHeader[0], &fVecHeader[0].fTempDrs[2]), // 0-39:val, 40:min, 41:max
1329 Compare(&fVecHeader[0], &fVecHeader[0].fTempDrs[3]) // 0-39:val, 40:min, 41:max
1330 };
1331
1332 vector<int16_t> data;
1333 data.reserve(82);
1334 data.push_back(tmp[0][40]); // min: 0
1335 data.insert(data.end(), tmp[0].data(), tmp[0].data()+40); // val: 1-40
1336 data.push_back(tmp[0][41]); // max: 41
1337 data.insert(data.end(), tmp[0].data(), tmp[0].data()+40); // val: 42-81
1338
1339 for (int j=1; j<=3; j++)
1340 {
1341 const array<int16_t,42> &ref = tmp[j];
1342
1343 // Gloabl min
1344 if (ref[40]<data[0]) // 40=min
1345 data[0] = ref[40];
1346
1347 // Global max
1348 if (ref[41]>data[41]) // 41=max
1349 data[41] = ref[41];
1350
1351 for (int i=0; i<40; i++)
1352 {
1353 // min per board
1354 if (ref[i]<data[i+1]) // data: 1-40
1355 data[i+1] = ref[i]; // ref: 0-39
1356
1357 // max per board
1358 if (ref[i]>data[i+42]) // data: 42-81
1359 data[i+42] = ref[i]; // ref: 0-39
1360 }
1361
1362
1363 }
1364
1365 vector<float> deg(82); // 0: global min, 1-40: min
1366 for (int i=0; i<82; i++) // 41: global max, 42-81: max
1367 deg[i] = data[i]/16.;
1368 fDimTemperature.Update(deg);
1369 }
1370 }
1371};
1372
1373EventBuilderWrapper *EventBuilderWrapper::This = 0;
1374
1375// ----------- Event builder callbacks implementation ---------------
1376extern "C"
1377{
1378 FileHandle_t runOpen(uint32_t irun, RUN_HEAD *runhd, size_t len)
1379 {
1380 return EventBuilderWrapper::This->runOpen(irun, runhd, len);
1381 }
1382
1383 int runWrite(FileHandle_t fileId, EVENT *event, size_t len)
1384 {
1385 return EventBuilderWrapper::This->runWrite(fileId, event, len);
1386 }
1387
1388 int runClose(FileHandle_t fileId, RUN_TAIL *runth, size_t len)
1389 {
1390 return EventBuilderWrapper::This->runClose(fileId, runth, len);
1391 }
1392
1393 // -----
1394
1395 //void *runStart(uint32_t /*irun*/, RUN_HEAD */*runhd*/, size_t /*len*/)
1396 //{
1397 // return NULL;
1398 //}
1399
1400 int subProcEvt(int threadID, PEVNT_HEADER *fadhd, EVENT *event, int16_t mboard, void *runPtr)
1401 {
1402 return EventBuilderWrapper::This->subProcEvt(threadID, fadhd, event, mboard, runPtr);
1403 }
1404
1405 int runEnd(uint32_t, void */*runPtr*/)
1406 {
1407 return 0;
1408 }
1409
1410 // -----
1411
1412 int eventCheck(uint32_t runNr, PEVNT_HEADER *fadhd, EVENT *event, int mboard)
1413 {
1414 return EventBuilderWrapper::This->eventCheck(runNr, fadhd, event, mboard);
1415 }
1416
1417 void gotNewRun(uint32_t runnr, PEVNT_HEADER *headers)
1418 {
1419 return EventBuilderWrapper::This->gotNewRun(runnr, headers);
1420 }
1421
1422 // -----
1423
1424 void factOut(int severity, int err, const char *message)
1425 {
1426 EventBuilderWrapper::This->factOut(severity, err, message);
1427 }
1428
1429 void factStat(GUI_STAT stat)
1430 {
1431 EventBuilderWrapper::This->factStat(stat);
1432 }
1433
1434 void factStatNew(EVT_STAT stat)
1435 {
1436 EventBuilderWrapper::This->factStat(stat);
1437 }
1438
1439 // ------
1440
1441 void debugHead(int socket, int/*board*/, void *buf)
1442 {
1443 const FAD::EventHeader &h = *reinterpret_cast<FAD::EventHeader*>(buf);
1444 EventBuilderWrapper::This->debugHead(socket, h);
1445 }
1446
1447 void debugStream(int isock, void *buf, int len)
1448 {
1449 return EventBuilderWrapper::This->debugStream(isock, buf, len);
1450 }
1451
1452 void debugRead(int isock, int ibyte, int32_t event, int32_t ftmevt, int32_t runno, int state, uint32_t tsec, uint32_t tusec)
1453 {
1454 EventBuilderWrapper::This->debugRead(isock, ibyte, event, ftmevt, runno, state, tsec, tusec);
1455 }
1456}
1457
1458#endif
Note: See TracBrowser for help on using the repository browser.