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

Last change on this file since 13007 was 12998, checked in by tbretz, 13 years ago
Added out own FITS streamer to the list of possible file formats (5).
File size: 45.2 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;
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 run num., max run num., 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 counts of each FAD board"
209 "|DAC[int]:DAC counts, sequentally DAC 0 board 0, DAC 0 board 1... (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 if (beg->fEventCounter != ptr->fEventCounter)
872 {
873 Error("Inconsist run number detected.... closing run.");
874 CloseRunFile(runNr, 0, 0);
875 break;
876 }
877 if (beg->fTriggerCounter != ptr->fTriggerCounter)
878 {
879 Error("Inconsist trigger number detected.... closing run.");
880 CloseRunFile(runNr, 0, 0);
881 break;
882 }*/
883
884 if (beg->fAdcClockPhaseShift != ptr->fAdcClockPhaseShift)
885 {
886 fMsg.Error("Inconsistent phase shift detected.... closing run.");
887 CloseRunFile(runNr, 0, 0);
888 return -1;
889 }
890
891 if (memcmp(beg->fDac, ptr->fDac, sizeof(beg->fDac)))
892 {
893 fMsg.Error("Inconsistent DAC values detected.... closing run.");
894 CloseRunFile(runNr, 0, 0);
895 return -1;
896 }
897
898 if (beg->fTriggerType != ptr->fTriggerType)
899 {
900 fMsg.Error("Inconsistent trigger type detected.... closing run.");
901 CloseRunFile(runNr, 0, 0);
902 return -1;
903 }
904 }
905
906 // check REFCLK_frequency
907 // check consistency with command configuration
908 // how to log errors?
909 // need gotNewRun/closedRun to know it is finished
910
911 return 0;
912 }
913
914 void SendRawData(PEVNT_HEADER */*fadhd*/, EVENT *event)
915 {
916 // Currently we send any event no matter what its trigger id is...
917 // To be changed.
918 static Time oldt(boost::date_time::neg_infin);
919 Time newt;
920
921 // FIXME: Only send events if the have newer run-numbers
922 if (newt<oldt+boost::posix_time::seconds(1))
923 return;
924
925 oldt = newt;
926
927 vector<char> data(sizeof(EVENT)+event->Roi*sizeof(float)*(1440+160));
928 memcpy(data.data(), event, sizeof(EVENT));
929
930 float *vec = reinterpret_cast<float*>(data.data()+sizeof(EVENT));
931
932 DataCalib::Apply(vec, event->Adc_Data, event->StartPix, event->Roi);
933 fDimRawData.Update(data);
934
935 DrsCalibrate::RemoveSpikes(vec, event->Roi);
936
937 vector<float> data2(1440*4); // Mean, RMS, Max, Pos
938 DrsCalibrate::GetPixelStats(data2.data(), vec, event->Roi);
939
940 fDimEventData.Update(data2);
941 }
942
943 void SendFeedbackData(PEVNT_HEADER *fadhd, EVENT *event)
944 {
945 if (!DataCalib::IsValid())
946 return;
947
948 // Workaround to find a valid header.....
949 const FAD::EventHeader *beg = reinterpret_cast<FAD::EventHeader*>(fadhd);
950 const FAD::EventHeader *end = reinterpret_cast<FAD::EventHeader*>(fadhd)+40;
951
952 // FIMXE: Compare with target configuration
953
954 const FAD::EventHeader *ptr=beg;
955 for (; ptr!=end; ptr++)
956 {
957 if (ptr->fStartDelimiter==0)
958 continue;
959
960 if (!ptr->HasTriggerLPext() && !ptr->HasTriggerLPint())
961 return;
962 }
963
964 if (ptr->fStartDelimiter==0)
965 return;
966
967 vector<float> data(event->Roi*1440);
968 DataCalib::Apply(data.data(), event->Adc_Data, event->StartPix, event->Roi);
969
970 DrsCalibrate::RemoveSpikes(data.data(), event->Roi);
971
972 vector<float> data2(1440); // Mean, RMS, Max, Pos, first, last
973 DrsCalibrate::GetPixelMax(data2.data(), data.data(), event->Roi, 0, event->Roi-1);
974
975 fDimFeedbackData.Update(data2);
976 }
977
978 int subProcEvt(int threadID, PEVNT_HEADER *fadhd, EVENT *event, int16_t /*iboard*/, void */*buffer*/)
979 {
980 switch (threadID)
981 {
982 case 0:
983 SendRawData(fadhd, event);
984 return 1;
985 case 1:
986 SendFeedbackData(fadhd, event);
987 return 2;
988 }
989 return 100;
990 }
991
992
993 bool IsRunStarted() const
994 {
995 const set<uint32_t>::const_iterator it = fIsRunStarted.find(fRunNumber-1);
996 return it==fIsRunStarted.end();// ? true : it->second.started;
997 }
998
999 uint32_t GetRunNumber() const
1000 {
1001 return fRunNumber;
1002 }
1003
1004 void IncreaseRunNumber(uint32_t run)
1005 {
1006 if (run>fRunNumber)
1007 fRunNumber = run;
1008 }
1009
1010 void gotNewRun(uint32_t runnr, PEVNT_HEADER */*headers*/)
1011 {
1012 // This function is called even when writing is switched off
1013 set<uint32_t>::iterator it = fIsRunStarted.begin();
1014 while (it!=fIsRunStarted.end())
1015 {
1016 if (*it<runnr)
1017 {
1018 ostringstream str;
1019 str << "gotNewRun - Missed run " << *it << ".";
1020 fMsg.Info(str);
1021
1022 fIsRunStarted.erase(it++);
1023 continue;
1024 }
1025 if (*it==runnr)
1026 break;
1027 it++;
1028 }
1029 if (it==fIsRunStarted.end())
1030 {
1031 ostringstream str;
1032 str << "gotNewRun - Not waiting for run " << runnr << ".";
1033 fMsg.Warn(str);
1034 return;
1035 }
1036
1037 map<uint32_t,FAD::RunDescription>::iterator i2 = fExpectedRuns.find(runnr);
1038 if (i2==fExpectedRuns.end())
1039 {
1040 ostringstream str;
1041 str << "gotNewRun - Run " << runnr << " wasn't expected.";
1042 fMsg.Warn(str);
1043 return;
1044 }
1045
1046 CloseRunFile(runnr, time(NULL)+i2->second.maxtime, i2->second.maxevt);
1047 // return: 0=close scheduled / >0 already closed / <0 does not exist
1048
1049 // FIXME: Move configuration from expected runs to runs which will soon
1050 // be opened/closed
1051
1052 fIsRunStarted.erase(it);
1053 }
1054
1055 map<boost::thread::id, string> fLastMessage;
1056
1057 void factOut(int severity, int err, const char *message)
1058 {
1059 if (!fDebugLog && severity==99)
1060 return;
1061
1062 ostringstream str;
1063 //str << boost::this_thread::get_id() << " ";
1064 str << "EventBuilder(";
1065 if (err<0)
1066 str << "---";
1067 else
1068 str << err;
1069 str << "): " << message;
1070
1071 string &old = fLastMessage[boost::this_thread::get_id()];
1072
1073 if (str.str()==old)
1074 return;
1075 old = str.str();
1076
1077 fMsg.Update(str, severity);
1078 }
1079/*
1080 void factStat(int64_t *stat, int len)
1081 {
1082 if (len!=7)
1083 {
1084 fMsg.Warn("factStat received unknown number of values.");
1085 return;
1086 }
1087
1088 vector<int64_t> data(1, g_maxMem);
1089 data.insert(data.end(), stat, stat+len);
1090
1091 static vector<int64_t> last(8);
1092 if (data==last)
1093 return;
1094 last = data;
1095
1096 fDimStatistics.Update(data);
1097
1098 // len ist die Laenge des arrays.
1099 // array[4] enthaelt wieviele bytes im Buffer aktuell belegt sind; daran
1100 // kannst Du pruefen, ob die 100MB voll sind ....
1101
1102 ostringstream str;
1103 str
1104 << "Wait=" << stat[0] << " "
1105 << "Skip=" << stat[1] << " "
1106 << "Del=" << stat[2] << " "
1107 << "Tot=" << stat[3] << " "
1108 << "Mem=" << stat[4] << "/" << g_maxMem << " "
1109 << "Read=" << stat[5] << " "
1110 << "Conn=" << stat[6];
1111
1112 fMsg.Info(str);
1113 }
1114 */
1115
1116 void factStat(const EVT_STAT &stat)
1117 {
1118 fDimStatistics2.Update(stat);
1119 }
1120
1121 void factStat(const GUI_STAT &stat)
1122 {
1123 fDimStatistics1.Update(stat);
1124 }
1125
1126
1127 array<FAD::EventHeader, 40> fVecHeader;
1128
1129 template<typename T, class S>
1130 array<T, 42> Compare(const S *vec, const T *t)
1131 {
1132 const int offset = reinterpret_cast<const char *>(t) - reinterpret_cast<const char *>(vec);
1133
1134 const T *min = NULL;
1135 const T *val = NULL;
1136 const T *max = NULL;
1137
1138 array<T, 42> arr;
1139
1140 // bool rc = true;
1141 for (int i=0; i<40; i++)
1142 {
1143 const char *base = reinterpret_cast<const char*>(vec+i);
1144 const T *ref = reinterpret_cast<const T*>(base+offset);
1145
1146 arr[i] = *ref;
1147
1148 if (gi_NumConnect[i]!=7)
1149 {
1150 arr[i] = 0;
1151 continue;
1152 }
1153
1154 if (!val)
1155 {
1156 min = ref;
1157 val = ref;
1158 max = ref;
1159 }
1160
1161 if (*ref<*min)
1162 min = ref;
1163
1164 if (*ref>*max)
1165 max = ref;
1166
1167 // if (*val!=*ref)
1168 // rc = false;
1169 }
1170
1171 arr[40] = val ? *min : 1;
1172 arr[41] = val ? *max : 0;
1173
1174 return arr;
1175 }
1176
1177 template<typename T>
1178 array<T, 42> CompareBits(const FAD::EventHeader *h, const T *t)
1179 {
1180 const int offset = reinterpret_cast<const char *>(t) - reinterpret_cast<const char *>(h);
1181
1182 T val = 0;
1183 T rc = 0;
1184
1185 array<T, 42> vec;
1186
1187 bool first = true;
1188
1189 for (int i=0; i<40; i++)
1190 {
1191 const char *base = reinterpret_cast<const char*>(&fVecHeader[i]);
1192 const T *ref = reinterpret_cast<const T*>(base+offset);
1193
1194 vec[i+2] = *ref;
1195
1196 if (gi_NumConnect[i]!=7)
1197 {
1198 vec[i+2] = 0;
1199 continue;
1200 }
1201
1202 if (first)
1203 {
1204 first = false;
1205 val = *ref;
1206 rc = 0;
1207 }
1208
1209 rc |= val^*ref;
1210 }
1211
1212 vec[0] = rc;
1213 vec[1] = val;
1214
1215 return vec;
1216 }
1217
1218 template<typename T, size_t N>
1219 void Update(DimDescribedService &svc, const array<T, N> &data, int n=N)
1220 {
1221// svc.setQuality(vec[40]<=vec[41]);
1222 svc.setData(const_cast<T*>(data.data()), sizeof(T)*n);
1223 svc.Update();
1224 }
1225
1226 template<typename T>
1227 void Print(const char *name, const pair<bool,array<T, 43>> &data)
1228 {
1229 cout << name << "|" << data.first << "|" << data.second[1] << "|" << data.second[0] << "<x<" << data.second[1] << ":";
1230 for (int i=0; i<40;i++)
1231 cout << " " << data.second[i+3];
1232 cout << endl;
1233 }
1234
1235 vector<uint> fNumConnected;
1236
1237 void debugHead(int /*socket*/, const FAD::EventHeader &h)
1238 {
1239 const uint16_t id = h.Id();
1240 if (id>39)
1241 return;
1242
1243 if (fNumConnected.size()!=40)
1244 fNumConnected.resize(40);
1245
1246 const vector<uint> con(gi_NumConnect, gi_NumConnect+40);
1247
1248 const bool changed = con!=fNumConnected || !IsThreadRunning();
1249
1250 fNumConnected = con;
1251
1252 const FAD::EventHeader old = fVecHeader[id];
1253 fVecHeader[id] = h;
1254
1255 if (old.fVersion != h.fVersion || changed)
1256 {
1257 const array<uint16_t,42> ver = Compare(&fVecHeader[0], &fVecHeader[0].fVersion);
1258
1259 array<float,42> data;
1260 for (int i=0; i<42; i++)
1261 {
1262 ostringstream str;
1263 str << (ver[i]>>8) << '.' << (ver[i]&0xff);
1264 data[i] = stof(str.str());
1265 }
1266 Update(fDimFwVersion, data);
1267 }
1268
1269 if (old.fRunNumber != h.fRunNumber || changed)
1270 {
1271 const array<uint32_t,42> run = Compare(&fVecHeader[0], &fVecHeader[0].fRunNumber);
1272 fDimRunNumber.Update(run);
1273 }
1274
1275 if (old.fTriggerGeneratorPrescaler != h.fTriggerGeneratorPrescaler || changed)
1276 {
1277 const array<uint16_t,42> pre = Compare(&fVecHeader[0], &fVecHeader[0].fTriggerGeneratorPrescaler);
1278 fDimPrescaler.Update(pre);
1279 }
1280
1281 if (old.fDNA != h.fDNA || changed)
1282 {
1283 const array<uint64_t,42> dna = Compare(&fVecHeader[0], &fVecHeader[0].fDNA);
1284 Update(fDimDNA, dna, 40);
1285 }
1286
1287 if (old.fStatus != h.fStatus || changed)
1288 {
1289 const array<uint16_t,42> sts = CompareBits(&fVecHeader[0], &fVecHeader[0].fStatus);
1290 Update(fDimStatus, sts);
1291 }
1292
1293 if (memcmp(old.fDac, h.fDac, sizeof(h.fDac)) || changed)
1294 {
1295 array<uint16_t, FAD::kNumDac*42> dacs;
1296
1297 for (int i=0; i<FAD::kNumDac; i++)
1298 {
1299 const array<uint16_t, 42> dac = Compare(&fVecHeader[0], &fVecHeader[0].fDac[i]);
1300 memcpy(&dacs[i*42], &dac[0], sizeof(uint16_t)*42);
1301 }
1302
1303 Update(fDimDac, dacs);
1304 }
1305
1306 // -----------
1307
1308 static Time oldt(boost::date_time::neg_infin);
1309 Time newt;
1310
1311 if (newt>oldt+boost::posix_time::seconds(1))
1312 {
1313 oldt = newt;
1314
1315 // --- RefClock
1316
1317 const array<uint32_t,42> clk = Compare(&fVecHeader[0], &fVecHeader[0].fFreqRefClock);
1318 Update(fDimRefClock, clk);
1319
1320 // --- Temperatures
1321
1322 const array<int16_t,42> tmp[4] =
1323 {
1324 Compare(&fVecHeader[0], &fVecHeader[0].fTempDrs[0]), // 0-39:val, 40:min, 41:max
1325 Compare(&fVecHeader[0], &fVecHeader[0].fTempDrs[1]), // 0-39:val, 40:min, 41:max
1326 Compare(&fVecHeader[0], &fVecHeader[0].fTempDrs[2]), // 0-39:val, 40:min, 41:max
1327 Compare(&fVecHeader[0], &fVecHeader[0].fTempDrs[3]) // 0-39:val, 40:min, 41:max
1328 };
1329
1330 vector<int16_t> data;
1331 data.reserve(82);
1332 data.push_back(tmp[0][40]); // min: 0
1333 data.insert(data.end(), tmp[0].data(), tmp[0].data()+40); // val: 1-40
1334 data.push_back(tmp[0][41]); // max: 41
1335 data.insert(data.end(), tmp[0].data(), tmp[0].data()+40); // val: 42-81
1336
1337 for (int j=1; j<=3; j++)
1338 {
1339 const array<int16_t,42> &ref = tmp[j];
1340
1341 // Gloabl min
1342 if (ref[40]<data[0]) // 40=min
1343 data[0] = ref[40];
1344
1345 // Global max
1346 if (ref[41]>data[41]) // 41=max
1347 data[41] = ref[41];
1348
1349 for (int i=0; i<40; i++)
1350 {
1351 // min per board
1352 if (ref[i]<data[i+1]) // data: 1-40
1353 data[i+1] = ref[i]; // ref: 0-39
1354
1355 // max per board
1356 if (ref[i]>data[i+42]) // data: 42-81
1357 data[i+42] = ref[i]; // ref: 0-39
1358 }
1359
1360
1361 }
1362
1363 vector<float> deg(82); // 0: global min, 1-40: min
1364 for (int i=0; i<82; i++) // 41: global max, 42-81: max
1365 deg[i] = data[i]/16.;
1366 fDimTemperature.Update(deg);
1367 }
1368 }
1369};
1370
1371EventBuilderWrapper *EventBuilderWrapper::This = 0;
1372
1373// ----------- Event builder callbacks implementation ---------------
1374extern "C"
1375{
1376 FileHandle_t runOpen(uint32_t irun, RUN_HEAD *runhd, size_t len)
1377 {
1378 return EventBuilderWrapper::This->runOpen(irun, runhd, len);
1379 }
1380
1381 int runWrite(FileHandle_t fileId, EVENT *event, size_t len)
1382 {
1383 return EventBuilderWrapper::This->runWrite(fileId, event, len);
1384 }
1385
1386 int runClose(FileHandle_t fileId, RUN_TAIL *runth, size_t len)
1387 {
1388 return EventBuilderWrapper::This->runClose(fileId, runth, len);
1389 }
1390
1391 // -----
1392
1393 //void *runStart(uint32_t /*irun*/, RUN_HEAD */*runhd*/, size_t /*len*/)
1394 //{
1395 // return NULL;
1396 //}
1397
1398 int subProcEvt(int threadID, PEVNT_HEADER *fadhd, EVENT *event, int16_t mboard, void *runPtr)
1399 {
1400 return EventBuilderWrapper::This->subProcEvt(threadID, fadhd, event, mboard, runPtr);
1401 }
1402
1403 int runEnd(uint32_t, void */*runPtr*/)
1404 {
1405 return 0;
1406 }
1407
1408 // -----
1409
1410 int eventCheck(uint32_t runNr, PEVNT_HEADER *fadhd, EVENT *event, int mboard)
1411 {
1412 return EventBuilderWrapper::This->eventCheck(runNr, fadhd, event, mboard);
1413 }
1414
1415 void gotNewRun(uint32_t runnr, PEVNT_HEADER *headers)
1416 {
1417 return EventBuilderWrapper::This->gotNewRun(runnr, headers);
1418 }
1419
1420 // -----
1421
1422 void factOut(int severity, int err, const char *message)
1423 {
1424 EventBuilderWrapper::This->factOut(severity, err, message);
1425 }
1426
1427 void factStat(GUI_STAT stat)
1428 {
1429 EventBuilderWrapper::This->factStat(stat);
1430 }
1431
1432 void factStatNew(EVT_STAT stat)
1433 {
1434 EventBuilderWrapper::This->factStat(stat);
1435 }
1436
1437 // ------
1438
1439 void debugHead(int socket, int/*board*/, void *buf)
1440 {
1441 const FAD::EventHeader &h = *reinterpret_cast<FAD::EventHeader*>(buf);
1442 EventBuilderWrapper::This->debugHead(socket, h);
1443 }
1444
1445 void debugStream(int isock, void *buf, int len)
1446 {
1447 return EventBuilderWrapper::This->debugStream(isock, buf, len);
1448 }
1449
1450 void debugRead(int isock, int ibyte, int32_t event, int32_t ftmevt, int32_t runno, int state, uint32_t tsec, uint32_t tusec)
1451 {
1452 EventBuilderWrapper::This->debugRead(isock, ibyte, event, ftmevt, runno, state, tsec, tusec);
1453 }
1454}
1455
1456#endif
Note: See TracBrowser for help on using the repository browser.