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

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