source: fact/Evidence/Edd/Edd.cc@ 10117

Last change on this file since 10117 was 10112, checked in by ogrimm, 14 years ago
Added persistance mode to event scope
File size: 55.8 KB
Line 
1
2/* ============================================================
3
4Edd - Evidence Data Display
5
6Qt-based graphical user interface for the Evidence contron system
7
8EddLineDisplay changes its background colour in case it display
9a DIM status service
10
11April 2010, Oliver Grimm
12
13============================================================ */
14
15#include "Edd.h"
16
17Qt::GlobalColor LineColors[] = {Qt::black, Qt::blue, Qt::red, Qt::green, Qt::white,
18 Qt::darkRed, Qt::darkGreen, Qt::darkBlue, Qt::cyan,
19 Qt::darkCyan, Qt::magenta, Qt::darkMagenta,
20 Qt::gray, Qt::darkGray, Qt::lightGray};
21
22
23class EddDim *Handler;
24QString DRSBoard = "drsdaq";
25
26// History chooser function (opens plot for numeric data, TextHist for all other)
27QWidget *OpenHistory(char *Service, int Index) {
28
29 QString Format;
30 DimBrowser Browser;
31 class EvidenceHistory *Hist = Handler->GetHistory(Service);
32
33 // Check if history service available
34 if (Hist == NULL || Hist->GetFormat() == NULL) {
35 QMessageBox::warning(NULL, "Edd Message", QString("Could not retrieve history for service ") + Service ,QMessageBox::Ok);
36
37 // If service currently available, take its format
38 char *Name, *Fmt;
39
40 Browser.getServices(Service);
41 if (Browser.getNextService(Name, Fmt) != 0) Format = QString(Fmt);
42 else {
43 Handler->DropHistory(Service);
44 return NULL;
45 }
46 }
47
48 if (Format.isEmpty()) Format = Hist->GetFormat();
49 Handler->DropHistory(Service);
50
51 if (Format.size() == 1 && Format[0] != 'C') return new EddPlot(Service, Index);
52 else return new EddText(Service);
53}
54
55// Set status tip (returns true if service was available)
56bool SetStatus(QWidget *W, QString Name, int Time, QString Format, int Index) {
57
58 QString Status;
59
60 if (Index != -1) Name = Name + "(" + QString::number(Index) + ")";
61
62 if (Time == -1) Status = QString("%1: unavailable").arg(Name);
63 else Status = QString("%1: Last update %2 Format '%3'").arg(Name).arg(QDateTime::fromTime_t(Time).toString()).arg(Format);
64
65 W->setStatusTip(Status);
66
67 return(Time != -1);
68}
69
70
71//////////////////////////////////////////
72// Text display for arbitary DIM service//
73//////////////////////////////////////////
74
75EddLineDisplay::EddLineDisplay(QString Name, int Index, QWidget *P):
76 QLineEdit(P), ServiceName(Name), Index(Index) {
77
78 LastHist = NULL;
79
80 // Widget properties
81 setReadOnly(true);
82 setMaximumWidth(100);
83 ShowAsTime = false;
84 setFrame(false);
85 setAttribute(Qt::WA_DeleteOnClose);
86
87 // Connect to DIM handler
88 if (connect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), SLOT(Update(QString, int, QByteArray, QString, QString))) == false) {
89 printf("Failed connection for %s\n", Name.toAscii().data());
90 }
91
92 // Context menu
93 Menu = new QMenu(this);
94 Menu->addAction("Open new history", this, SLOT(MenuOpenHistory()));
95 Menu->addAction("Copy service", this, SLOT(MenuCopyService()));
96 Menu->addAction("Copy data", this, SLOT(MenuCopyData()));
97
98 // Subscribe to service
99 Handler->Subscribe(Name);
100}
101
102// Destructor
103EddLineDisplay::~EddLineDisplay() {
104
105 Handler->Unsubscribe(ServiceName);
106}
107
108// Update widget
109void EddLineDisplay::Update(QString Name, int Time, QByteArray, QString Format, QString Text) {
110
111 if (ServiceName != Name) return;
112
113 // Check if service available
114 QPalette Pal = palette();
115 if (!SetStatus(this, Name, Time, Format, Index)) {
116 setText("n/a");
117 Pal.setColor(QPalette::Base, Qt::lightGray);
118 setPalette(Pal);
119 return;
120 }
121 else Pal.setColor(QPalette::Base, Qt::white);
122
123 // Message service backgound colour determined by severity
124 if (Name.endsWith("/Message")) {
125 switch (Text.section(' ', 0, 0).toInt()) {
126 case 0: Pal.setColor(QPalette::Base, Qt::white); break;
127 case 1: Pal.setColor(QPalette::Base, Qt::yellow); break;
128 case 2: Pal.setColor(QPalette::Base, Qt::red); break;
129 case 3: Pal.setColor(QPalette::Base, Qt::red); break;
130 default: break;
131 }
132 Text = Text.section(' ', 1);
133 }
134 else if (Format[0].toUpper() != 'C' && Format != "I:1;C") Text = Text.section(' ', Index, Index);
135
136 if (!ShowAsTime) setText(Text);
137 else setText(QDateTime::fromTime_t(Text.toInt()).toString());
138
139 setCursorPosition(0);
140 setPalette(Pal);
141}
142
143// Open plot if mouse release within widget
144void EddLineDisplay::mouseReleaseEvent(QMouseEvent *Event) {
145
146 if (Event->button()!=Qt::LeftButton || !contentsRect().contains(Event->pos())) return;
147
148 // Check if last history plot still open, then raise
149 foreach (QWidget *Widget, QApplication::allWidgets()) {
150 if (Widget == LastHist) {
151 Widget->activateWindow();
152 Widget->raise();
153 return;
154 }
155 }
156
157 // If not, open new plot
158 EddLineDisplay::MenuOpenHistory();
159}
160
161// Handling of mouse press event: Register start position for drag
162void EddLineDisplay::mousePressEvent(QMouseEvent *Event) {
163
164 if (Event->button() == Qt::LeftButton) dragStart = Event->pos();
165}
166
167// Handling of dragging (Drag and MimeData will be deleted by Qt)
168void EddLineDisplay::mouseMoveEvent(QMouseEvent *Event) {
169
170 if ((Event->buttons() & Qt::LeftButton) == 0) return;
171 if ((Event->pos()-dragStart).manhattanLength() < QApplication::startDragDistance()) return;
172
173 QDrag *Drag = new QDrag(this);
174 QMimeData *MimeData = new QMimeData;
175 QByteArray Data;
176 MimeData->setData("Edd/Service", Data.append(ServiceName + " " + QString::number(Index)));
177 Drag->setMimeData(MimeData);
178 Drag->exec();
179}
180
181//
182// Opening context menu
183//
184void EddLineDisplay::contextMenuEvent(QContextMenuEvent *Event) {
185
186 Menu->exec(Event->globalPos());
187}
188
189// Menu: Open history plot
190void EddLineDisplay::MenuOpenHistory() {
191
192 LastHist = OpenHistory(ServiceName.toAscii().data(), Index);
193 if (LastHist != NULL) LastHist->show();
194}
195
196// Menu: Copy service name
197void EddLineDisplay::MenuCopyService() {
198
199 QMimeData *MimeData = new QMimeData;
200 QByteArray Data;
201 MimeData->setData("Edd/Service", Data.append(ServiceName + " " + QString::number(Index)));
202 QApplication::clipboard()->setMimeData(MimeData);
203}
204
205// Menu: Copy data
206void EddLineDisplay::MenuCopyData() {
207
208 QApplication::clipboard()->setText(text());
209}
210
211
212//////////////////////////////////////////
213// Sending string command to DIM server //
214//////////////////////////////////////////
215
216EddCommand::EddCommand(QString Name, QWidget *P): QLineEdit(P), Name(Name) {
217
218 setToolTip("Send command "+Name);
219 connect(this, SIGNAL(returnPressed()), SLOT(SendCommand()));
220}
221
222// Send command
223void EddCommand::SendCommand() {
224
225 DimClient::sendCommand(Name.toAscii().data(), text().toAscii().data());
226 clear();
227}
228
229
230//////////////////////////////////
231// History plot for DIM service //
232//////////////////////////////////
233
234EddPlot::EddPlot(QString Service, int Index, QWidget *P): EddBasePlot(P) {
235
236 // Widget properties
237 setAcceptDrops(true);
238 setAxisScaleDraw(QwtPlot::xBottom, new EddTimeScale);
239
240 // Update time range on plot when axis change (update() results in paintEvent())
241 connect(axisWidget(QwtPlot::xBottom), SIGNAL(scaleDivChanged()), SLOT(update()));
242
243 legend()->setItemMode(QwtLegend::ClickableItem);
244 connect(this, SIGNAL(legendClicked (QwtPlotItem *)), SLOT(LegendClicked(QwtPlotItem *)));
245
246 // Connect to DIM handler
247 if (connect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), SLOT(Update(QString, int, QByteArray, QString, QString))) == false) {
248 printf("Failed connection for %s\n", Service.toAscii().data());
249 }
250
251 // Additonal context menu items
252 QAction* Action = Menu->addAction("Paste service", this, SLOT(MenuPasteService()));
253 Menu->removeAction(Action);
254 Menu->insertAction(Menu->actions().value(1), Action);
255
256 // Maximum number if points in curve (will be increased in Update())
257 SizeLimit = 0;
258
259 // DIM client
260 if (!Service.isEmpty()) AddService(Service, Index);
261}
262
263// Destructor (items with parent widget are automatically deleted)
264EddPlot::~EddPlot() {
265
266 while (!List.isEmpty()) DeleteCurve(List.last().Signal);
267}
268
269// Add history service to plot
270void EddPlot::AddService(QString Name, int Index) {
271
272 // Check if curve already present on plot
273 for (int i=0; i<List.size(); i++) {
274 if (Name == List[i].Name && Index == List[i].Index) {
275 QMessageBox::warning(this, "Edd Message",Name+" ("+QString::number(Index)+") already present",QMessageBox::Ok);
276 return;
277 }
278 }
279
280 // Generate new curve and subscribe to service
281 struct ItemDetails N;
282
283 N.Name = Name;
284 N.Signal = NewCurve(Name+"("+QString::number(Index)+")");
285 N.Index = Index;
286 List.append(N);
287
288 Handler->Subscribe(Name);
289}
290
291// Update widget (must happen in GUI thread)
292void EddPlot::Update(QString Name, int Time, QByteArray, QString Format, QString Text) {
293
294 bool DoUpdate = false;
295
296 // Determine which plot item this call belongs to
297 int ItemNo;
298 for (ItemNo=0; ItemNo<List.size(); ItemNo++) if (List[ItemNo].Name == Name) {
299
300 DoUpdate = true;
301
302 // If size limit reached, clear buffer
303 if (List[ItemNo].Signal->dataSize() > SizeLimit) List[ItemNo].Signal->setData(QPolygonF());
304
305 // If buffer empty, request new history buffer
306 if (List[ItemNo].Signal->dataSize() == 0) {
307 int Count=0;
308 const struct EvidenceHistory::Item *R;
309 class EvidenceHistory *Hist;
310
311 if ((Hist = Handler->GetHistory(List[ItemNo].Name)) != NULL) {
312 double Number=0;
313 while ((R=Hist->Next()) != NULL) {
314 switch (*(Hist->GetFormat())) {
315 case 'I':
316 case 'L': Number = *((int *) R->Data + List[ItemNo].Index); break;
317 case 'S': Number = *((short *) R->Data + List[ItemNo].Index); break;
318 case 'F': Number = *((float *) R->Data + List[ItemNo].Index); break;
319 case 'D': Number = *((double *) R->Data + List[ItemNo].Index); break;
320 case 'X': Number = *((long long *) R->Data + List[ItemNo].Index); break;
321 default: break;
322 }
323 AddPoint(ItemNo, R->Time, Number);
324 Count++;
325 }
326
327 // Local buffer at least twice as large as longest history
328 if (SizeLimit < 2*Count) SizeLimit = 2*Count;
329 }
330 Handler->DropHistory(List[ItemNo].Name);
331 }
332
333 // Append data only if service available
334 if (SetStatus(this, Name, Time, Format)) {
335 QString Txt = Text;
336 Txt = Txt.section(' ', List[ItemNo].Index, List[ItemNo].Index);
337 AddPoint(ItemNo, Time, atof(Txt.toAscii().data()));
338 }
339 }
340
341 if (DoUpdate) UpdatePlot();
342}
343
344
345// Add text indicating time range to plot
346void EddPlot::paintEvent(QPaintEvent *) {
347
348 QString Text;
349 QFont Font;
350 QPainter Painter(this);
351
352 Text = QDateTime::fromTime_t((int) axisScaleDiv(QwtPlot::xBottom)->lowerBound()).toString("d-MMM-yyyy hh:mm:ss") + " to " + QDateTime::fromTime_t((int) axisScaleDiv(QwtPlot::xBottom)->upperBound()).toString("d-MMM-yyyy hh:mm:ss");
353
354 Font.setPointSize(6);
355 Painter.setFont(Font);
356 Painter.drawText(0, height(), Text);
357}
358
359// Drag and drop methods
360void EddPlot::dragEnterEvent(QDragEnterEvent *Event) {
361
362 if (Event->mimeData()->hasFormat("Edd/Service")) Event->acceptProposedAction();
363}
364
365void EddPlot::dropEvent(QDropEvent *Event) {
366
367 QByteArray D(Event->mimeData()->data("Edd/Service"));
368 AddService(D.left(D.lastIndexOf(' ')), D.right(D.size()-D.lastIndexOf(' ')).toInt());
369}
370
371void EddPlot::LegendClicked(QwtPlotItem *Item) {
372
373 QString D(Item->title().text());
374 D.replace('(',' ').chop(1);
375
376 QDrag *Drag = new QDrag(this);
377 QMimeData *MimeData = new QMimeData;
378 QByteArray Data;
379 MimeData->setData("Edd/Service", Data.append(D));
380 Drag->setMimeData(MimeData);
381 Drag->exec();
382}
383
384// Add new service by pasting name
385void EddPlot::MenuPasteService() {
386
387 const QMimeData *D = QApplication::clipboard()->mimeData();
388 if (!D->hasFormat("Edd/Service")) return;
389
390 QByteArray E(D->data("Edd/Service"));
391 AddService(E.left(E.lastIndexOf(' ')), E.right(E.size()-E.lastIndexOf(' ')).toInt());
392}
393
394// Remove list entry
395void EddPlot::DeleteCurve(QwtPlotCurve *Curve) {
396
397 for (int i=0; i<List.size(); i++) if (List[i].Signal == Curve) {
398 Handler->Unsubscribe(List[i].Name);
399 List.removeAt(i);
400 }
401}
402
403
404//////////////////
405// General plot //
406//////////////////
407
408// Constructor (all slots called in GUI thread, therefore no mutex necessary)
409EddBasePlot::EddBasePlot(QWidget *P): QwtPlot(P) {
410
411 // Widget properties
412 setAttribute(Qt::WA_DeleteOnClose);
413 setAutoReplot(false);
414 setCanvasBackground(EddPlotBackgroundColor);
415 setMargin(15);
416
417 // Plot navigation
418 Zoomer = new QwtPlotZoomer(QwtPlot::xBottom,QwtPlot::yLeft,canvas());
419 connect(Zoomer, SIGNAL(zoomed(const QwtDoubleRect &)), this, SLOT(HandleZoom(const QwtDoubleRect &)));
420 connect(Zoomer, SIGNAL(zoomed(const QwtDoubleRect &)), this, SLOT(ReDoStats()));
421
422 Magnifier = new QwtPlotMagnifier(canvas());
423 Magnifier->setMouseButton(Qt::NoButton,Qt::NoButton);
424 Magnifier->setZoomInKey(Qt::Key_M, Qt::NoModifier);
425 Magnifier->setZoomOutKey(Qt::Key_M, Qt::ShiftModifier);
426
427 Panner = new QwtPlotPanner(canvas());
428 Panner->setMouseButton(Qt::LeftButton, Qt::ShiftModifier);
429 connect(Panner, SIGNAL(panned(int, int)), this, SLOT(ReDoStats()));
430
431 Picker = new QwtPicker(QwtPicker::CornerToCorner|QwtPicker::RectSelection, QwtPicker::RectRubberBand, QwtPicker::AlwaysOff, this);
432 connect(Picker, SIGNAL(selected(const QwtPolygon &)), SLOT(MouseSelection(const QwtPolygon &)));
433
434 // Grid and legend
435 Grid = new QwtPlotGrid;
436 Grid->setMajPen(QPen(Qt::gray, 0, Qt::DotLine));
437 Grid->attach(this);
438
439 insertLegend(new QwtLegend(), QwtPlot::TopLegend, 0.3);
440
441 // Marker for statistics text
442 Stats = new QwtPlotMarker();
443 Stats->setLineStyle(QwtPlotMarker::NoLine);
444 Stats->setLabelAlignment(Qt::AlignLeft | Qt::AlignBottom);
445 Stats->hide();
446 Stats->attach(this);
447
448 // Context menu
449 Menu = new QMenu(this);
450 StripAction = Menu->addAction("Stripchart", this, SLOT(UpdatePlot()));
451 StripAction->setCheckable(true);
452 Menu->addSeparator();
453 YLogAction = Menu->addAction("y scale log", this, SLOT(UpdatePlot()));
454 YLogAction->setCheckable(true);
455 NormAction = Menu->addAction("Normalize", this, SLOT(UpdatePlot()));
456 NormAction->setCheckable(true);
457 StyleAction = Menu->addAction("Draw dots", this, SLOT(UpdatePlot()));
458 StyleAction->setCheckable(true);
459 StatisticsAction = Menu->addAction("Statistics", this, SLOT(ReDoStats()));
460 StatisticsAction->setCheckable(true);
461 Menu->addAction("Zoom out", this, SLOT(MenuZoomOut()));
462 Menu->addAction("Single trace", this, SLOT(MenuSingleTrace()));
463 Menu->addSeparator();
464 Menu->addAction("Save as ASCII", this, SLOT(MenuSaveASCII()));
465 Menu->addAction("Save plot", this, SLOT(MenuSave()));
466 Menu->addAction("Print plot", this, SLOT(MenuPrint()));
467 Menu->addAction("Plot help", this, SLOT(MenuPlotHelp()));
468}
469
470// Destructor (items with parent widget are automatically deleted)
471EddBasePlot::~EddBasePlot() {
472
473 for (int i=0; i<Items.size(); i++) delete Items[i].Signal;
474 delete Grid;
475}
476
477// Print statistics to plot if requested
478void EddBasePlot::ReDoStats() {
479
480 QString Text;
481 double Mean, Sigma;
482 unsigned long Count = 0;
483
484 // Set visibility of statistics box
485 Stats->setVisible(StatisticsAction->isChecked());
486
487 for (int i=0; i<Items.size(); i++) {
488 Mean = 0;
489 Sigma = 0;
490 Count = 0;
491
492 // Calculate mean and sigma for data points currently visible
493 for (int j=0; j<Items[i].y.size(); j++) {
494 if (!axisScaleDiv(QwtPlot::xBottom)->contains(Items[i].x[j])) continue;
495 Mean += Items[i].y[j];
496 Sigma += Items[i].y[j]*Items[i].y[j];
497 Count++;
498 }
499
500 if (Count == 0) Mean = 0;
501 else Mean /= Count;
502 if (Count < 2) Sigma = 0;
503 else Sigma = sqrt(Count/(Count-1)*(Sigma/Count-Mean*Mean));
504
505 // Prepare string
506 Text += Items[i].Signal->title().text() + ": m " + QString::number(Mean, 'f', 2) + ", s " + QString::number(Sigma, 'f', 2) + "\n";
507 }
508
509 // Replot once to get axis correct
510 replot();
511
512 // Print string to plot
513 QwtText text(Text);
514 text.setFont(QFont("Helvetica", 8));
515 Stats->setValue(axisScaleDiv(QwtPlot::xBottom)->upperBound(), axisScaleDiv(QwtPlot::yLeft)->upperBound());
516 Stats->setLabel(text);
517
518 // Replot again to update text
519 replot();
520}
521
522// Update all curves in plot
523void EddBasePlot::UpdatePlot() {
524
525 double Lower = axisScaleDiv(QwtPlot::xBottom)->lowerBound();
526 double Upper = axisScaleDiv(QwtPlot::xBottom)->upperBound();
527 double MaxTime = DBL_MIN;
528 static QwtSymbol Symbol, Sym1;
529 Symbol.setStyle(QwtSymbol::Ellipse);
530 Symbol.setSize(4);
531
532 // Select engine for linear or logarithmic scale
533 if (!YLogAction->isChecked()) {
534 setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine);
535 }
536 else setAxisScaleEngine(QwtPlot::yLeft, new QwtLog10ScaleEngine);
537
538 for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
539 // Determine current maximum value for strip chart plotting
540 if (Items[ItemNo].Signal->boundingRect().right() > MaxTime) {
541 MaxTime = Items[ItemNo].Signal->boundingRect().right();
542 }
543
544 // Set symbol if requested
545 if (StyleAction->isChecked()) Items[ItemNo].Signal->setSymbol(Symbol);
546 else Items[ItemNo].Signal->setSymbol(Sym1);
547
548 // Determine number of data points
549 int DataPoints = Items[ItemNo].x.size();
550 if (DataPoints == 0) continue;
551
552 // Normalize y scale if requested
553 double *y = new double [DataPoints];
554 for (int i=0; i<DataPoints; i++) {
555 y[i] = Items[ItemNo].y[i];
556
557 if (NormAction->isChecked()) {
558 if (Items[ItemNo].Smallest != Items[ItemNo].Largest) {
559 y[i] = (y[i] - Items[ItemNo].Smallest)/(Items[ItemNo].Largest-Items[ItemNo].Smallest);
560 }
561 else y[i] = 1;
562 }
563 }
564
565 // Plot data
566 Items[ItemNo].Signal->setData(Items[ItemNo].x.data(), y, DataPoints);
567 Items[ItemNo].Signal->show();
568 delete[] y;
569
570 // Generate bounding box of all curves for zoomer
571 BBox = BBox.unite(Items[ItemNo].Signal->boundingRect());
572 }
573
574 // Reset zoom base to include all data
575 Zoomer->setZoomBase(BBox);
576
577 // If plot is strip char, only move axis but keep range
578 if (StripAction->isChecked()) {
579 setCanvasBackground(EddPlotBackgroundColor.lighter(90));
580 setAxisScale(QwtPlot::xBottom, Lower+ BBox.right() - MaxTime, Upper + BBox.right() - MaxTime);
581 }
582 else setCanvasBackground(EddPlotBackgroundColor);
583
584 ReDoStats();
585}
586
587// Append curve to plot
588QwtPlotCurve *EddBasePlot::NewCurve(QwtText Title) {
589
590 struct PlotItem N;
591
592 N.Signal = new QwtPlotCurve;
593 N.Signal->attach(this);
594 N.Signal->setTitle(Title);
595 N.Signal->setPen(QColor(LineColors[Items.size() % (sizeof(LineColors)/sizeof(Qt::GlobalColor))]));
596 N.Largest = DBL_MIN;
597 N.Smallest = DBL_MAX;
598 Items.append(N);
599
600 return N.Signal;
601}
602
603// Clear curve data
604void EddBasePlot::ClearCurve(unsigned int Item) {
605
606 if (Item >= (unsigned int) Items.size()) return;
607
608 Items[Item].x.clear();
609 Items[Item].y.clear();
610}
611
612// Append data point
613void EddBasePlot::AddPoint(unsigned int Item, double x, double y) {
614
615 if (Item >= (unsigned int) Items.size()) return;
616
617 Items[Item].x.append(x);
618 Items[Item].y.append(y);
619
620 if (y > Items[Item].Largest) Items[Item].Largest = y;
621 if (y < Items[Item].Smallest) Items[Item].Smallest = y;
622}
623
624// Rescale plot in case selection has been made outside the canvas
625void EddBasePlot::MouseSelection(const QPolygon &P) {
626
627 QwtDoubleInterval xPlot, xMouse, yPlot, yMouse;
628
629 // Shift selected rectangle so that upper left corner is 0/0 on canvas
630 QRect R = P.boundingRect().translated(-plotLayout()->canvasRect().topLeft());
631
632 // Current axis intervals
633 xPlot = axisScaleDiv(QwtPlot::xBottom)->interval();
634 yPlot = axisScaleDiv(QwtPlot::yLeft)->interval();
635
636 // Selected axis intervals
637 xMouse = QwtDoubleInterval(invTransform(QwtPlot::xBottom, R.left()),
638 invTransform(QwtPlot::xBottom, R.right()));
639 yMouse = QwtDoubleInterval(invTransform(QwtPlot::yLeft, R.bottom()),
640 invTransform(QwtPlot::yLeft, R.top()));
641
642 // Selection region outside all axis?
643 if (R.right() < 0 && R.top() > plotLayout()->canvasRect().height()) return;
644
645 // Rescale both axis if selected rectangle encompasses canvas completely
646 if (P.boundingRect().contains(plotLayout()->canvasRect())) {
647 yMouse.setMaxValue(yMouse.maxValue() + yPlot.width());
648 yMouse.setMinValue(yMouse.minValue() - yPlot.width());
649 xMouse.setMinValue(xMouse.minValue() - xPlot.width());
650 xMouse.setMaxValue(xMouse.maxValue() + xPlot.width());
651
652 setAxisScale(QwtPlot::xBottom, xMouse.minValue(), xMouse.maxValue());
653 setAxisScale(QwtPlot::yLeft, yMouse.minValue(), yMouse.maxValue());
654 }
655
656 // Rescale y axis (increase range if selected rectangle larger than axis area)
657 if (R.right() < 0) {
658 if (yMouse.maxValue() > axisScaleDiv(QwtPlot::yLeft)->upperBound()) {
659 yMouse.setMaxValue(yMouse.maxValue() + yPlot.width());
660 }
661 if (yMouse.minValue() < axisScaleDiv(QwtPlot::yLeft)->lowerBound()) {
662 yMouse.setMinValue(yMouse.minValue() - yPlot.width());
663 }
664
665 setAxisScale(QwtPlot::yLeft, yMouse.minValue(), yMouse.maxValue());
666 }
667
668 // Rescale x axis (increase range if selected rectangle larger than axis area)
669 if (R.top() > plotLayout()->canvasRect().height()) {
670 if (xMouse.maxValue() > axisScaleDiv(QwtPlot::xBottom)->upperBound()) {
671 xMouse.setMaxValue(xMouse.maxValue() + xPlot.width());
672 }
673 if (xMouse.minValue() < axisScaleDiv(QwtPlot::xBottom)->lowerBound()) {
674 xMouse.setMinValue(xMouse.minValue() - xPlot.width());
675 }
676
677 setAxisScale(QwtPlot::xBottom, xMouse.minValue(), xMouse.maxValue());
678 }
679
680 // Draw new scales
681 replot();
682}
683
684// Reset graph axes to autoscale when fully unzoomed
685void EddBasePlot::HandleZoom(const QwtDoubleRect &) {
686
687 if(Zoomer->zoomRectIndex() == 0) {
688 setAxisAutoScale(QwtPlot::xBottom);
689 setAxisAutoScale(QwtPlot::yLeft);
690 }
691
692 UpdatePlot();
693}
694
695// Opening context menu
696void EddBasePlot::contextMenuEvent(QContextMenuEvent *Event) {
697
698 Menu->exec(Event->globalPos());
699}
700
701// Zoom completely out
702void EddBasePlot::MenuZoomOut() {
703
704 Zoomer->zoom(0);
705 UpdatePlot();
706}
707
708// Remove all items except one
709void EddBasePlot::MenuSingleTrace() {
710
711 while (Items.size() > 1) {
712 DeleteCurve(Items.last().Signal);
713 delete Items.last().Signal;
714 Items.takeLast();
715 }
716 UpdatePlot();
717}
718
719// Save data of plot as test
720void EddBasePlot::MenuSaveASCII() {
721 QString Filename = QFileDialog::getSaveFileName(this,
722 "Filename", ".", "Text files (*.txt *.ascii *.asc);;All files (*)");
723 if (Filename.length() <= 0) return;
724
725 QFile File(Filename);
726 if (!File.open(QFile::WriteOnly | QIODevice::Text | QFile::Truncate)) {
727 QMessageBox::warning(this, "Edd Message","Could not open file for writing.",QMessageBox::Ok);
728 return;
729 }
730
731 // Write x and y data for all signals to file
732 QTextStream Stream(&File);
733
734 for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
735 Stream << QString("# ") + Items[ItemNo].Signal->title().text() + ".hist" << endl;
736 for (int i=0; i<Items[ItemNo].Signal->dataSize(); i++) {
737 Stream << Items[ItemNo].x.at(i) << " " << Items[ItemNo].Signal->y(i) << endl;
738 }
739 }
740}
741
742// Print plot
743void EddBasePlot::MenuPrint() {
744
745 QPrinter *Printer = new QPrinter;
746 QPrintDialog *PrintDialog = new QPrintDialog(Printer, this);
747 if (PrintDialog->exec() == QDialog::Accepted) {
748 QPainter Painter(Printer);
749 QPixmap Pixmap = QPixmap::grabWidget(this);
750 Painter.drawPixmap(0, 0, Pixmap);
751 }
752 delete Printer; delete PrintDialog;
753}
754
755// Save plot as image
756void EddBasePlot::MenuSave() {
757
758 QString Filename = QFileDialog::getSaveFileName(this,
759 "Filename of image", "/home/ogrimm/ddd", "Image files (*.bmp *.jpg *.png *.ppm *.tiff *.xbm *.xpm);;All files (*)");
760 if (Filename.length()>0) {
761 QPixmap Pixmap = QPixmap::grabWidget(this);
762 if(!Pixmap.save(Filename)) {
763 QMessageBox::warning(this, "Edd Message","Could not write image file.",QMessageBox::Ok);
764 remove(Filename.toAscii().data());
765 }
766 }
767}
768
769// Help text
770void EddBasePlot::MenuPlotHelp() {
771
772 QMessageBox::about(this, "Edd - Plot navigation",
773 "Zoom\tMouse wheel\n"
774 "\tKeys m and shift-m\n"
775 "\tSelecting region with left mouse button\n"
776 "\tMiddle button zooms out one level\n"
777 "\tSelecting a range on an axis\n"
778 "\tSelecting whole canvas\n\n"
779 "Pan\tShift and left mouse button\n\n"
780 "ESC cancels selection\n"
781 "Cursor keys move mouse\n\n"
782 "Statistics are calculated over the current x axis extend");
783}
784
785//////////////////////////////////////
786// History text box for DIM service //
787//////////////////////////////////////
788
789// Constructor
790EddText::EddText(QString Name, bool Pure, QWidget *P):
791 QTextEdit(P), Name(Name), Pure(Pure) {
792
793 // Widget properties
794 setReadOnly(true);
795 setAttribute(Qt::WA_DeleteOnClose);
796 setAutoFillBackground(true);
797 document()->setMaximumBlockCount(1000);
798 Accumulate = true;
799
800 // Connect to DIM handler
801 if (connect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), SLOT(Update(QString, int, QByteArray, QString, QString))) == false) {
802 printf("Failed connection for %s\n", Name.toAscii().data());
803 }
804
805 if (!Pure) {
806 // Get history for this service
807 const struct EvidenceHistory::Item *R;
808 class EvidenceHistory *Hist;
809
810 if ((Hist = Handler->GetHistory(Name)) != NULL) {
811 while ((R=Hist->Next()) != NULL) {
812 moveCursor (QTextCursor::Start);
813 insertPlainText(QString("(")+QDateTime::fromTime_t(R->Time).toString()+") " +
814 QString::fromStdString(EvidenceServer::ToString(Hist->GetFormat(), (void *) R->Data, R->Size)) + "\n");
815 }
816 }
817 Handler->DropHistory(Name);
818 }
819
820 // DIM client
821 Handler->Subscribe(Name);
822}
823
824// Destructor
825EddText::~EddText() {
826
827 Handler->Unsubscribe(Name);
828}
829
830
831// Update widget (must happen in GUI thread)
832void EddText::Update(QString Name, int Time, QByteArray, QString Format, QString Text) {
833
834 if (this->Name != Name) return;
835 QPalette Pal = palette();
836
837 // Check if service available
838 if (!SetStatus(this, Name, Time, Format)) {
839 Pal.setColor(QPalette::Base, Qt::lightGray);
840 setPalette(Pal);
841 return;
842 }
843
844 Pal.setColor(QPalette::Base, Qt::white);
845 setPalette(Pal);
846 QDateTime Timex = QDateTime::fromTime_t(Time);
847
848 // Clear display in case text should not accumulate
849 if (Accumulate == false) clear();
850
851 if (!Pure) {
852 moveCursor(QTextCursor::Start);
853 insertPlainText(QString("(")+Timex.toString()+QString(") "));
854 insertPlainText(Text + "\n");
855 }
856 else if (Format == "C") insertPlainText(Text);
857}
858
859
860/////////////////////////////
861// Interface to Dim system //
862/////////////////////////////
863EddDim::EddDim() {
864
865 Mutex = new QMutex(QMutex::Recursive);
866 Volume = 0;
867
868 // Timer to calculate data rates
869 QTimer *Timer = new QTimer(this);
870 Timer->connect(Timer, SIGNAL(timeout()), this, SLOT(UpdateStatistics()));
871 Timer->start(10000);
872
873 // Connect to DIM handler
874 if (connect(this, SIGNAL(INT(QString, int, QByteArray, QString, QString)), SLOT(Update(QString, int, QByteArray, QString, QString))) == false) {
875 printf("Failed connection in EddDim()\n");
876 }
877}
878
879// Destructor
880EddDim::~EddDim() {
881
882 QList<QString> L = HistoryList.keys();
883 for(int i=0; i<L.size(); i++) delete HistoryList[L[i]].HistClass;
884
885 delete Mutex;
886}
887
888// Subscribe to DIM service
889void EddDim::Subscribe(QString Name) {
890
891 // Lock before accessing list
892 QMutexLocker Locker(Mutex);
893
894 // If already subscribed to service, increase usage count and reemit data for new subscriber
895 if (ServiceList.contains(Name)) {
896 ServiceList[Name].Count++;
897 YEP(Name, ServiceList[Name].TimeStamp, ServiceList[Name].ByteArray, ServiceList[Name].Format, ServiceList[Name].Text);
898 return;
899 }
900
901 // Create new entry in service list
902 ServiceList[Name].ByteArray = QByteArray();
903 ServiceList[Name].TimeStamp = -1;
904 ServiceList[Name].Count = 1;
905 ServiceList[Name].DIMService = new DimStampedInfo(Name.toAscii().data(), INT_MAX, NO_LINK, this);
906 return;
907}
908
909// Unsubsribe from DIM service
910void EddDim::Unsubscribe(QString Name) {
911
912 // Lock before accessing list
913 QMutexLocker Locker(Mutex);
914
915 if (ServiceList.contains(Name)) ServiceList[Name].Count--;
916
917 if (ServiceList[Name].Count == 0) {
918 delete ServiceList[Name].DIMService;
919 ServiceList.remove(Name);
920 return;
921 }
922}
923
924// Get history buffer
925class EvidenceHistory *EddDim::GetHistory(QString Name) {
926
927 // History already available (only request again if too old)
928 if (HistoryList.contains(Name)) {
929 HistoryList[Name].Count++;
930
931 if (time(NULL)-HistoryList[Name].LastUpdate < 5) {
932 HistoryList[Name].HistClass->Rewind();
933 return HistoryList[Name].HistClass;
934 }
935 HistoryList[Name].LastUpdate = time(NULL);
936 if (HistoryList[Name].HistClass->GetHistory()) return HistoryList[Name].HistClass;
937 else return NULL;
938 }
939
940 // Create new history class
941 HistoryList[Name].HistClass = new EvidenceHistory(Name.toStdString());
942 HistoryList[Name].Count = 1;
943 HistoryList[Name].LastUpdate = time(NULL);
944
945 if (HistoryList[Name].HistClass->GetHistory()) return HistoryList[Name].HistClass;
946 else return NULL;
947}
948
949// Reduce history usage counter
950void EddDim::DropHistory(QString Name) {
951
952 if (HistoryList.contains(Name)) HistoryList[Name].Count--;
953}
954
955// Update throughput statistics and clear up history memory
956void EddDim::UpdateStatistics() {
957
958 // Lock before accessing internal variables
959 QMutexLocker Locker(Mutex);
960
961 // Remove unused histories after not less than 5 seconds
962 QList<QString> L = HistoryList.keys();
963 for(int i=0; i<L.size(); i++) {
964 if ((HistoryList[L[i]].Count <= 0) && (time(NULL)-HistoryList[L[i]].LastUpdate) > 5) {
965 delete HistoryList[L[i]].HistClass;
966 HistoryList.remove(L[i]);
967 }
968 }
969
970 float Rate = Volume/1024.0/10;
971 Volume = 0;
972 YEP("Edd/Rate_kBSec", time(NULL), QByteArray::number(Rate), "F", QString::number(Rate));
973}
974
975// Store service information for usage by Subscribe(), update statistics and emit signal to widgets
976void EddDim::Update(QString Name, int Time, QByteArray Data, QString Format, QString Text) {
977
978 // Lock before accessing list
979 QMutexLocker Locker(Mutex);
980
981 // Store service data
982 if (ServiceList.contains(Name)) {
983 ServiceList[Name].TimeStamp = Time;
984 ServiceList[Name].ByteArray = Data;
985 ServiceList[Name].Format = Format;
986 ServiceList[Name].Text = Text;
987 }
988
989 // Update statistics only for actual Dim services
990 if (!Name.startsWith("Edd/")) Volume += Data.size();
991
992 // Emit signal to all widgets
993 YEP(Name, Time, Data, Format, Text);
994}
995
996// Handling of DIM service update
997// No locking allowed. Signal triggers only EddDim::Update() when the main event loop is idle.
998void EddDim::infoHandler() {
999
1000 if (!EvidenceServer::ServiceOK(getInfo())) INT(getInfo()->getName(), -1);
1001 else {
1002 INT(getInfo()->getName(), getInfo()->getTimestamp(), QByteArray((char *) getInfo()->getData(),
1003 getInfo()->getSize()), getInfo()->getFormat(), QString::fromStdString(EvidenceServer::ToString(getInfo()->getFormat(), getInfo()->getData(), getInfo()->getSize())));
1004 }
1005}
1006
1007//
1008//
1009// ====== FACT specific part ======
1010//
1011//
1012
1013////////////////////////
1014// Event oscilloscope //
1015////////////////////////
1016
1017// Constructor
1018EventScope::EventScope(QWidget *P): EddBasePlot(P), PixelMap("../../config/PixelMap.txt", false) {
1019
1020 Name = DRSBoard+"/EventData";
1021
1022 Tmpfile = tmpfile();
1023 if(Tmpfile == NULL) {
1024 QMessageBox::warning(this, "Edd Message", "Could not open temporary file.", QMessageBox::Ok);
1025 }
1026
1027 // Open file with RawDataCTX
1028 RD = new RawDataCTX(true);
1029 ErrCode = CTX_NOTOPEN;
1030
1031 // Context menu
1032 PhysPipeAction = new QAction("Physical pipeline", this);
1033 PhysPipeAction->setCheckable(true);
1034 connect(PhysPipeAction, SIGNAL(triggered()), SLOT(PlotTraces()));
1035 Menu->insertAction(StripAction, PhysPipeAction);
1036
1037 PersistanceAction = new QAction("Persistance", this);
1038 PersistanceAction->setCheckable(true);
1039 Menu->insertAction(StripAction, PersistanceAction);
1040 Menu->removeAction(StripAction);
1041
1042 // Initial trace
1043 AddTrace(0,0,0);
1044
1045 // Connect to DIM handler
1046 if (connect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), SLOT(Update(QString, int, QByteArray, QString, QString))) == false) {
1047 printf("Failed connection for %s\n", Name.toAscii().data());
1048 }
1049 SetActive(true);
1050}
1051
1052// Destructor (items with parent widget are automatically deleted)
1053EventScope::~EventScope() {
1054
1055 SetActive(false);
1056 while (!List.isEmpty()) DeleteCurve(List.last().Signal);
1057 delete RD;
1058 if (Tmpfile != NULL) fclose(Tmpfile);
1059}
1060
1061// Add trace
1062void EventScope::AddTrace(int Board, int Chip, int Channel) {
1063
1064 struct ItemDetails N;
1065
1066 N.Signal = NewCurve(QString::number(Board)+","+QString::number(Chip)+","+ QString::number(Channel)+ " (" + DRS_to_Pixel(Board, Chip, Channel).c_str() + ")");
1067 N.Board = Board;
1068 N.Chip = Chip;
1069 N.Channel = Channel;
1070 N.Trigger = new QwtPlotMarker();
1071 N.Trigger->setSymbol(QwtSymbol(QwtSymbol::Diamond, QBrush(N.Signal->pen().color()), N.Signal->pen(), QSize(10,10)));
1072 N.Trigger->attach(this);
1073
1074 if (List.isEmpty()) {
1075 QPen Pen = N.Signal->pen();
1076 Pen.setWidth(2);
1077 N.Signal->setPen(Pen);
1078 }
1079 List.append(N);
1080
1081 PlotTraces();
1082}
1083
1084// Update last trace (to reflect current setting of spin boxes in DAQ page)
1085void EventScope::UpdateFirst(int Board, int Chip, int Channel) {
1086
1087 if (List.isEmpty()) return;
1088
1089 // Clear in case persistance was activated
1090 ClearCurve(0);
1091
1092 List.first().Signal->setTitle(QString::number(Board)+","+QString::number(Chip)+","+ QString::number(Channel) + " (" + DRS_to_Pixel(Board, Chip, Channel).c_str() + ")");
1093 List.first().Board = Board;
1094 List.first().Chip = Chip;
1095 List.first().Channel = Channel;
1096
1097 PlotTraces();
1098}
1099
1100// Update event buffer
1101void EventScope::Update(QString Name, int Time, QByteArray Data, QString Format, QString) {
1102
1103 if (Name != this->Name) return;
1104
1105 // Check if service available
1106 if (!SetStatus(this, Name, Time, Format)) return;
1107 if (Data.size() < (int) sizeof(RunHeader)) return;
1108
1109 // Disconnect while processing to avoid queing of events
1110 disconnect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), this, SLOT(Update(QString, int, QByteArray, QString, QString)));
1111
1112 // Open tempory file and write event data to this file
1113 QTemporaryFile File;
1114 if (!File.open()) {
1115 QMessageBox::warning(this, "Edd Message","Could not open temporary file.",QMessageBox::Ok);
1116 return;
1117 }
1118 if (File.write(Data) == -1) {
1119 QMessageBox::warning(this, "Edd Message","Could not write data to temporary file.",QMessageBox::Ok);
1120 return;
1121 }
1122
1123 // Prepare temporary file for run header
1124 ftruncate(fileno(Tmpfile), 0);
1125 rewind(Tmpfile);
1126
1127 // Open file with RawDataCTX
1128 switch (ErrCode = RD->OpenDataFile(File.fileName().toAscii().data(), Tmpfile)) {
1129 case CTX_FOPEN: QMessageBox::warning(this, "Edd Message","Could not open file.",QMessageBox::Ok);
1130 return;
1131 case CTX_RHEADER: QMessageBox::warning(this, "Edd Message","Could not read run header.",QMessageBox::Ok);
1132 return;
1133 case CTX_BSTRUCT: QMessageBox::warning(this, "Edd Message","Could not read board structures.",QMessageBox::Ok);
1134 return;
1135 default: break;
1136 }
1137
1138 // Emit signal containing run header
1139 rewind(Tmpfile);
1140 QTextStream Stream(Tmpfile);
1141 emit(RunHeaderChanged(Stream.readAll()));
1142
1143 // Prepare temporary file for event header
1144 ftruncate(fileno(Tmpfile), 0);
1145 rewind(Tmpfile);
1146
1147 // Write event header text to file
1148 if (RD->ReadEvent(0, Tmpfile) != CTX_OK) {
1149 QMessageBox::warning(this, "Edd Warning","Could not read event.",QMessageBox::Ok);
1150 return;
1151 }
1152
1153 // Add trigger cells to file
1154 fprintf(Tmpfile, "\nTrigger cells:");
1155 int *TrigCells = (int *) RD->Data;
1156 for (unsigned int i=0; i<RD->RHeader->NBoards; i++) {
1157 fprintf(Tmpfile, "\n Board %d ", i);
1158 for (unsigned int j=0; j<RD->RHeader->NChips; j++) fprintf(Tmpfile, "%d ", *(TrigCells++));
1159 }
1160
1161 // Emit signal containing run header
1162 rewind(Tmpfile);
1163 emit(EventHeaderChanged(Stream.readAll()));
1164
1165 // Update display
1166 PlotTraces();
1167
1168 // Reconnect after processing
1169 connect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), SLOT(Update(QString, int, QByteArray, QString, QString)));
1170}
1171
1172// Update curves
1173void EventScope::PlotTraces() {
1174
1175 double x,y;
1176 unsigned int Cell, Trig;
1177 static int Last = 0;
1178
1179 // Only process if valid data in RawDataCTX class
1180 if (ErrCode != CTX_OK) return;
1181
1182 // Set x axis title
1183 if (PhysPipeAction->isChecked()) setAxisTitle(QwtPlot::xBottom, "Time from start of pipeline (ns)");
1184 else setAxisTitle(QwtPlot::xBottom, "Time from trigger minus one revolution (ns)");
1185
1186 // Loop through event data to update event scope
1187 RunHeader *R = RD->RHeader;
1188 for (int i=0; i<List.size(); i++) {
1189
1190 if (PersistanceAction->isChecked()) List[i].Signal->setStyle(QwtPlotCurve::Dots);
1191 else {
1192 ClearCurve(i);
1193 List[i].Signal->setStyle(QwtPlotCurve::Lines);
1194 }
1195
1196 // Check if current event contains requested trace
1197 if (List[i].Board>=R->NBoards || List[i].Chip>=R->NChips || List[i].Channel>=R->NChannels) continue;
1198
1199 // Set trigger marker visibility
1200 List[i].Trigger->setVisible(PhysPipeAction->isChecked());
1201
1202 // Determine trigger cell
1203 Trig = *((int *) RD->Data + List[i].Board*R->NChips + List[i].Chip);
1204
1205 // Calulate point of curve
1206 for (unsigned int j=0; j<R->Samples; j++) {
1207
1208 if (PhysPipeAction->isChecked()) Cell = (j - Trig) % 1024;
1209 else Cell = j;
1210
1211 x = j / RD->BStruct[List[i].Board].NomFreq;
1212 y = *((short *) (RD->Data + R->NBoards*R->NChips*sizeof(int)) +
1213 List[i].Board*R->NChips*R->NChannels*R->Samples + List[i].Chip*R->NChannels*R->Samples +
1214 List[i].Channel*R->Samples + Cell) * RD->BStruct[List[i].Board].ScaleFactor;
1215
1216 AddPoint(i, x, y);
1217
1218 // Set trigger point indicator
1219 if (Trig == j) List[i].Trigger->setValue(x, y);
1220 }
1221 }
1222
1223 // Limit update rate in persistance mode
1224 if (!PersistanceAction->isChecked() || time(NULL) > Last) {
1225 UpdatePlot();
1226 Last = time(NULL);
1227 }
1228
1229
1230 // Loop through event data for pixel display
1231 QVector<double> Pixel(R->NBoards*R->NChips*R->NChannels);
1232 int Count = 0;
1233
1234 for (unsigned int Board=0; Board<R->NBoards; Board++) {
1235 for (unsigned int Chip=0; Chip<R->NChips; Chip++) {
1236 for (unsigned int Channel=0; Channel<R->NChannels; Channel++) {
1237 Pixel[Count] = DBL_MIN;
1238
1239 for (unsigned int i=0; i<R->Samples; i++) {
1240 y = *((short *) (RD->Data + R->NBoards*R->NChips*sizeof(int)) +
1241 Board*R->NChips*R->NChannels*R->Samples + Chip*R->NChannels*R->Samples +
1242 Channel*R->Samples + i) * RD->BStruct[Board].ScaleFactor;
1243
1244 if (y > Pixel[Count]) Pixel[Count] = y;
1245 }
1246 Count++;
1247 }}}
1248
1249 emit(PixelData(Pixel));
1250}
1251
1252// Remove list entry
1253void EventScope::DeleteCurve(QwtPlotCurve *Curve) {
1254
1255 for (int i=0; i<List.size(); i++) if (List[i].Signal == Curve) {
1256 delete List[i].Trigger;
1257 List.removeAt(i);
1258 }
1259}
1260
1261// Set display active (if inactive, disconnect from server)
1262void EventScope::SetActive(bool State) {
1263
1264 static bool Active = false;
1265
1266 if (State && !Active) Handler->Subscribe(DRSBoard+"/EventData");
1267 if (!State && Active) Handler->Unsubscribe(DRSBoard+"/EventData");
1268 Active = State;
1269}
1270
1271
1272//------------------------------------------------------------------
1273//**************************** Tab pages ***************************
1274//------------------------------------------------------------------
1275
1276//
1277// Environment page
1278//
1279TP_Environment::TP_Environment() {
1280
1281 QGridLayout *Layout = new QGridLayout(this);
1282 setAttribute(Qt::WA_DeleteOnClose);
1283
1284 // Status display
1285 EddLineDisplay *Line = new EddLineDisplay("ARDUINO/Message");
1286 Line->setMaximumWidth(200);
1287 Layout->addWidget(Line, 0, 0, 1, 2);
1288
1289 // Generate plot and data displays
1290 EddPlot *Plot = new EddPlot();
1291 for (int i=0; i<10; i++) {
1292 Line = new EddLineDisplay("ARDUINO/Data", i);
1293 Layout->addWidget(Line, i%5+1, i/5, 1, 1);
1294 Plot->AddService("ARDUINO/Data", i);
1295 }
1296 Layout->addWidget(Plot, 0, 2, 9, 7);
1297
1298 // Night sky monitor
1299 Line = new EddLineDisplay("SQM/Message");
1300 Line->setMaximumWidth(200);
1301 Layout->addWidget(Line, 6, 0, 1, 2);
1302
1303 Line = new EddLineDisplay("SQM/NSB");
1304 Layout->addWidget(Line, 7, 0, 1, 1);
1305}
1306
1307//
1308// Bias page
1309//
1310TP_Bias::TP_Bias() {
1311
1312 QGridLayout *Layout = new QGridLayout(this);
1313 setAttribute(Qt::WA_DeleteOnClose);
1314 EddLineDisplay *Line;
1315
1316 EddPlot *Plot = new EddPlot();
1317 Plot->setMinimumWidth(400);
1318 for (int i=0; i<18; i++) {
1319 Line = new EddLineDisplay("Bias/VOLT/ID00", i+64);
1320 Layout->addWidget(Line, i%9+1, 0+i/9, 1, 1);
1321 Plot->AddService("Bias/VOLT/ID00", i+64);
1322
1323 Line = new EddLineDisplay("Bias/VOLT/ID00", i+96);
1324 Layout->addWidget(Line, i%9+1, 2+i/9, 1, 1);
1325 Plot->AddService("Bias/VOLT/ID00",i+96);
1326 }
1327
1328 Layout->addWidget(Plot, 0, 4, 12, 3);
1329 Line = new EddLineDisplay("Bias/Message");
1330 Line->setMaximumWidth(200);
1331 Layout->addWidget(Line, 0, 0, 1, 3);
1332
1333 EddCommand *Command = new EddCommand("Bias/Command");
1334 Layout->addWidget(Command, 10, 0, 1, 4);
1335
1336 EddText *Text = new EddText("Bias/ConsoleOut", true);
1337 Text->setFixedWidth(400);
1338 Layout->addWidget(Text, 11, 0, 4, 4);
1339
1340 QWidget *Button = new QPushButton("Currents");
1341 Layout->addWidget(Button, 13, 4, 1, 1);
1342 connect(Button, SIGNAL(pressed()), SLOT(BiasCurrents()));
1343}
1344
1345void TP_Bias::BiasCurrents() {
1346
1347 QMainWindow *M = new QMainWindow;
1348 M->setCentralWidget(new QWidget);
1349 M->setStatusBar(new QStatusBar(M));
1350 M->setWindowTitle("Edd - Bias currents");
1351 M->setAttribute(Qt::WA_DeleteOnClose);
1352
1353 QGridLayout *Layout = new QGridLayout(M->centralWidget());
1354 EddLineDisplay *Line;
1355 EddPlot *Plot = new EddPlot();
1356
1357 for (int i=0; i<36; i++) {
1358 Line = new EddLineDisplay("Bias/MICROAMP/ID00", i+64);
1359 Line->setMaximumWidth(60);
1360 Layout->addWidget(Line, i%9, 0+i/9, 1, 1);
1361 Plot->AddService("Bias/MICROAMP/ID00", i+64);
1362 }
1363 Layout->addWidget(Plot, 0, 4, 30, 12);
1364
1365 M->show();
1366}
1367
1368//
1369// Feedback page
1370//
1371TP_Feedback::TP_Feedback() {
1372
1373 setAttribute(Qt::WA_DeleteOnClose);
1374 QGridLayout *Layout = new QGridLayout(this);
1375 EddLineDisplay *Line;
1376
1377 EddPlot *Plot = new EddPlot();
1378 for (int i=0; i<36; i++) {
1379 Line = new EddLineDisplay("Feedback/Average", i);
1380 Line->setMaximumWidth(60);
1381 Layout->addWidget(Line, i%9+2, 0+i/9, 1, 1);
1382 Plot->AddService("Feedback/Average", i);
1383 }
1384 Layout->addWidget(Plot, 0, 4, 12, 10);
1385
1386 Line = new EddLineDisplay("Feedback/Message");
1387 Line->setMaximumWidth(200);
1388 Layout->addWidget(Line, 0, 0, 1, 2);
1389
1390 Line = new EddLineDisplay("Feedback/State");
1391 Line->setMaximumWidth(150);
1392 Layout->addWidget(Line, 1, 0, 1, 2);
1393 Line = new EddLineDisplay("Feedback/Count");
1394 Line->setMaximumWidth(60);
1395 Layout->addWidget(Line, 1, 2);
1396
1397 QWidget *Button = new QPushButton("Details");
1398 Layout->addWidget(Button, 12, 0, 1, 1);
1399 connect(Button, SIGNAL(pressed()), SLOT(FeedbackDetails()));
1400}
1401
1402void TP_Feedback::FeedbackDetails() {
1403
1404 QMainWindow *M = new QMainWindow;
1405 M->setCentralWidget(new QWidget);
1406 M->setStatusBar(new QStatusBar(M));
1407 M->setWindowTitle("Edd - Feedback Details");
1408 M->setAttribute(Qt::WA_DeleteOnClose);
1409
1410 QGridLayout *Layout = new QGridLayout(M->centralWidget());
1411 EddLineDisplay *Line;
1412 EddPlot *Plot = new EddPlot();
1413
1414 for (int i=0; i<36; i++) {
1415 Line = new EddLineDisplay("Feedback/Sigma", i);
1416 Line->setMaximumWidth(60);
1417 Layout->addWidget(Line, i%9, 0+i/9, 1, 1);
1418 Plot->AddService("Feedback/Sigma", i);
1419
1420 Line = new EddLineDisplay("Feedback/Target", i);
1421 Line->setMaximumWidth(60);
1422 Layout->addWidget(Line, i%9+10, 0+i/9, 1, 1);
1423
1424 Line = new EddLineDisplay("Feedback/Response", i);
1425 Line->setMaximumWidth(60);
1426 Layout->addWidget(Line, i%9+20, 0+i/9, 1, 1);
1427 }
1428 Layout->addWidget(Plot, 0, 4, 30, 12);
1429
1430 M->show();
1431}
1432
1433//
1434// Event scope page
1435//
1436TP_DAQ::TP_DAQ() {
1437
1438 EddLineDisplay *Line;
1439
1440 setAttribute(Qt::WA_DeleteOnClose);
1441 QGridLayout *Layout = new QGridLayout(this);
1442
1443 // Run-related information
1444 Line = new EddLineDisplay("drsdaq/RunNumber");
1445 Line->setMaximumWidth(100);
1446 Layout->addWidget(Line, 0, 1, 1, 1);
1447 Line = new EddLineDisplay("drsdaq/EventNumber");
1448 Line->setMaximumWidth(100);
1449 Layout->addWidget(Line, 0, 2, 1, 1);
1450 Line = new EddLineDisplay("drsdaq/RunSizeMB");
1451 Line->setMaximumWidth(100);
1452 Layout->addWidget(Line, 0, 3, 1, 1);
1453 Line = new EddLineDisplay("drsdaq/FileSizeMB");
1454 Line->setMaximumWidth(100);
1455 Layout->addWidget(Line, 0, 4, 1, 1);
1456 Line = new EddLineDisplay("drsdaq/FileName");
1457 Line->setMaximumWidth(200);
1458 Layout->addWidget(Line, 0, 5, 1, 1);
1459
1460 // Event scope
1461 Scope = new EventScope;
1462 Scope->setMinimumWidth(700);
1463
1464 // Text boxes for run and event header
1465 RunHeaderDisplay = new QPlainTextEdit();
1466 EventHeaderDisplay = new QPlainTextEdit();
1467 RunHeaderDisplay->setReadOnly(true);
1468 EventHeaderDisplay->setReadOnly(true);
1469
1470 // Tab widget
1471 QTabWidget *TabWidget = new QTabWidget();
1472 TabWidget->addTab(Scope, "&Signals");
1473 TabWidget->addTab(RunHeaderDisplay, "&Run Header");
1474 TabWidget->addTab(EventHeaderDisplay, "&Event Header");
1475 Layout->addWidget(TabWidget, 1, 1, 5, 5);
1476
1477 // Channel number
1478 Channel = new QSpinBox;
1479 connect(Channel, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int)));
1480 Channel->setToolTip("DRS channel number");
1481
1482 // Chip number
1483 Chip = new QSpinBox;
1484 connect(Chip, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int)));
1485 Chip->setToolTip("DRS chip number");
1486
1487 // Board number
1488 Board = new QSpinBox;
1489 connect(Board, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int)));
1490 Board->setToolTip("DRS board number");
1491
1492 // Pixel ID
1493 PixelID = new QLineEdit;
1494 PixelID->setMaximumWidth(60);
1495 connect(PixelID, SIGNAL(returnPressed()), SLOT(TranslatePixelID()));
1496 PixelID->setToolTip("Pixel identification");
1497
1498 // Layout of pixel addressing widgets
1499 QFormLayout *FormLayout = new QFormLayout();
1500 FormLayout->setRowWrapPolicy(QFormLayout::WrapAllRows);
1501 FormLayout->addRow("Channel", Channel);
1502 FormLayout->addRow("Chip", Chip);
1503 FormLayout->addRow("Board", Board);
1504 FormLayout->addRow("Pixel ID", PixelID);
1505 Layout->addLayout(FormLayout, 0, 0, 2, 1);
1506
1507 // Add trace permanently
1508 QPushButton *Button = new QPushButton("Keep trace");
1509 Button->setToolTip("Keep trace in display");
1510 Button->setMaximumWidth(80);
1511 Layout->addWidget(Button, 2, 0);
1512 connect(Button, SIGNAL(clicked()), SLOT(KeepCurrent()));
1513
1514 // Stop/start
1515 StartStopButton = new QPushButton("Stop");
1516 StartStopButton->setToolTip("Start/stop display");
1517 StartStopButton->setMaximumWidth(80);
1518 StartStopButton->setCheckable(true);
1519 QPalette Palette = StartStopButton->palette();
1520 Palette.setColor(QPalette::ButtonText, Qt::blue);
1521 Palette.setColor(QPalette::Button, Qt::green);
1522 StartStopButton->setPalette(Palette);
1523 StartStopButton->setFont(QFont("Times", 10, QFont::Bold));
1524 Layout->addWidget(StartStopButton, 4, 0);
1525 connect(StartStopButton, SIGNAL(toggled(bool)), SLOT(StartStop(bool)));
1526
1527 // Button to show event display
1528 QPushButton *PixDisplay = new QPushButton("Pixel display");
1529 PixDisplay->setFont(QFont("Times", 10, QFont::Bold));
1530 PixDisplay->setToolTip("Show event display window");
1531 PixDisplay->setMaximumWidth(80);
1532 Layout->addWidget(PixDisplay, 5, 0);
1533 connect(PixDisplay, SIGNAL(clicked()), SLOT(ShowPixelDisplay()));
1534
1535 // Event display window
1536 Display = new QWidget();
1537 Display->setWindowTitle("Edd - Event display");
1538
1539 Pixel = new QPushButton *[MAXPIXEL];
1540 int Count = 0;
1541 double x,y;
1542
1543 for (int ix=-22; ix<=22; ix++) for (int iy=-19; iy<=20; iy++) {
1544 if (Count == MAXPIXEL) break;
1545
1546 x = ix*0.866;
1547 y = iy - (ix%2==0 ? 0.5:0);
1548 if ((pow(x,2)+pow(y,2) >= 395) && !(abs(ix)==22 && iy==7)) continue;
1549
1550 Pixel[Count] = new QPushButton(Display);
1551 Pixel[Count]->setAutoFillBackground(true);
1552 Pixel[Count]->setGeometry((int) (x*12.5 + 250), (int) (y*12.5 + 250), 10, 10);
1553 Pixel[Count]->show();
1554 Count++;
1555 }
1556
1557 connect(Scope, SIGNAL(RunHeaderChanged(QString)), RunHeaderDisplay, SLOT(setPlainText(QString)));
1558 connect(Scope, SIGNAL(EventHeaderChanged(QString)), EventHeaderDisplay, SLOT(setPlainText(QString)));
1559
1560 StartStop(false);
1561 connect(Scope, SIGNAL(PixelData(QVector<double>)), SLOT(SetPixelData(QVector<double>)));
1562}
1563
1564TP_DAQ::~TP_DAQ() {
1565
1566 delete[] Pixel;
1567}
1568
1569// Translate pixel ID to board, chip, channel
1570void TP_DAQ::TranslatePixelID() {
1571
1572 if (Scope->Pixel_to_DRSboard(PixelID->text().toStdString()) == 999999999) {
1573 QMessageBox::warning(this, "Edd Message","Pixel ID unknown.",QMessageBox::Ok);
1574 }
1575 else {
1576 Board->setValue(Scope->Pixel_to_DRSboard(PixelID->text().toStdString()));
1577 Chip->setValue(Scope->Pixel_to_DRSchip(PixelID->text().toStdString()));
1578 Channel->setValue(Scope->Pixel_to_DRSchannel(PixelID->text().toStdString()));
1579 }
1580}
1581
1582// Keep current trace
1583void TP_DAQ::KeepCurrent() {
1584
1585 Scope->AddTrace(Board->value(), Chip->value(), Channel->value());
1586}
1587
1588// Start/stop event acquisition
1589void TP_DAQ::StartStop(bool State) {
1590
1591 Scope->SetActive(!State);
1592 StartStopButton->setText(State ? "Start" : "Stop");
1593 if (!State) connect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), Scope, SLOT(Update(QString, int, QByteArray, QString, QString)));
1594 else disconnect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), Scope, SLOT(Update(QString, int, QByteArray, QString, QString)));
1595}
1596
1597// Update event scope
1598void TP_DAQ::UpdateScope(int) {
1599
1600 // Update pixel ID
1601 PixelID->setText(Scope->DRS_to_Pixel(Board->value(), Chip->value(), Channel->value()).c_str());
1602 // Update first trace
1603 Scope->UpdateFirst(Board->value(), Chip->value(), Channel->value());
1604}
1605
1606// Show/hide pixel display
1607void TP_DAQ::ShowPixelDisplay() {
1608
1609 Display->show();
1610 Display->raise();
1611}
1612
1613void TP_DAQ::SetPixelData(QVector<double> Data) {
1614
1615 QwtLinearColorMap Map;
1616
1617 for (int i=0; i<Data.size(); i++) {
1618 Pixel[i]->setPalette(QPalette(Map.color(QwtDoubleInterval(300, 400), Data[i])));
1619 }
1620}
1621
1622
1623//
1624// Evidence page
1625//
1626TP_Evidence::TP_Evidence() {
1627
1628 setAttribute(Qt::WA_DeleteOnClose);
1629 QGridLayout *Layout = new QGridLayout(this);
1630 EddLineDisplay *Line;
1631 EddText *Text;
1632
1633 Line = new EddLineDisplay("Alarm/Message");
1634 Line->setMaximumWidth(200);
1635 Layout->addWidget(Line, 0, 0, 1, 2);
1636
1637 QPushButton *Button = new QPushButton();
1638 Button->setText("ON/OFF");
1639 Button->setCheckable(true);
1640 connect(Button, SIGNAL(toggled(bool)), SLOT(ToggleAlarm(bool)));
1641 Layout->addWidget(Button, 0, 3, 1, 1);
1642
1643 Line = new EddLineDisplay("Alarm/MasterAlarm");
1644 Layout->addWidget(Line, 0, 1, 1, 1);
1645
1646 Text = new EddText("Alarm/Summary", true);
1647 Text->Accumulate = false;
1648 Text->setMaximumWidth(200);
1649 Text->setMaximumHeight(150);
1650 Layout->addWidget(Text, 1, 0, 1, 2);
1651
1652 Line = new EddLineDisplay("DColl/Message");
1653 Line->setMaximumWidth(200);
1654 Layout->addWidget(Line, 3, 0, 1, 2);
1655
1656 Line = new EddLineDisplay("DColl/DataSizeMB");
1657 Layout->addWidget(Line, 4, 0, 1, 1);
1658
1659 Line = new EddLineDisplay("DColl/LogSizeMB");
1660 Layout->addWidget(Line, 4, 1, 1, 1);
1661
1662 Line = new EddLineDisplay("DColl/CurrentFile");
1663 Line->setMaximumWidth(400);
1664 Layout->addWidget(Line, 5, 0, 1, 3);
1665
1666 Line = new EddLineDisplay("Config/Message");
1667 Line->setMaximumWidth(200);
1668 Layout->addWidget(Line, 6, 0, 1, 2);
1669
1670 Line = new EddLineDisplay("Config/ModifyTime");
1671 Line->setMaximumWidth(200);
1672 Line->ShowAsTime = true;
1673 Layout->addWidget(Line, 7, 0, 1, 1);
1674
1675 Button = new QPushButton();
1676 Button->setText("eLogBook");
1677 connect(Button, SIGNAL(released()), SLOT(StartELog()));
1678 Layout->addWidget(Button, 6, 1, 1, 1);
1679
1680 Button = new QPushButton();
1681 Button->setText("Start DIM browser");
1682 connect(Button, SIGNAL(released()), SLOT(StartDIMBrowser()));
1683 Layout->addWidget(Button, 7, 1, 1, 1);
1684
1685 Line = new EddLineDisplay("Edd/Rate_kBSec");
1686 Layout->addWidget(Line, 8, 0, 1, 1);
1687}
1688
1689// Toggle state of Alarm server
1690void TP_Evidence::ToggleAlarm(bool State) {
1691
1692 if (State) DimClient::sendCommandNB((char *) "Alarm/Switch", (char *) "off");
1693 else DimClient::sendCommandNB((char *) "Alarm/Switch", (char *) "on");
1694}
1695
1696// Start DIM Browser
1697void TP_Evidence::StartDIMBrowser() {
1698
1699 QProcess::startDetached("did", QStringList(), QString(getenv("DIMDIR"))+"/linux/");
1700}
1701
1702// Start eLogBook
1703void TP_Evidence::StartELog() {
1704
1705 QProcess::startDetached("firefox http://fact.ethz.ch/FACTelog/index.jsp");
1706}
1707
1708
1709//--------------------------------------------------------------------
1710//*************************** Main GUI *******************************
1711//--------------------------------------------------------------------
1712//
1713// All widgets have ultimately Central as parent.
1714//
1715GUI::GUI() {
1716
1717 Handler = new EddDim();
1718
1719 // Set features of main window
1720 Central = new QWidget(this);
1721 setCentralWidget(Central);
1722 setStatusBar(new QStatusBar(this));
1723 setWindowTitle("Edd - Evidence Data Display");
1724
1725 // Arrangement in tabs
1726 TabWidget = new QTabWidget(Central);
1727 TabWidget->setTabsClosable(true);
1728 connect(TabWidget, SIGNAL(tabCloseRequested(int)), SLOT(DetachTab(int)));
1729 TabWidget->addTab(new TP_DAQ, "Event scope " + DRSBoard);
1730 TabWidget->addTab(new TP_Bias, "Bias");
1731 TabWidget->addTab(new TP_Feedback, "Feedback");
1732 TabWidget->addTab(new TP_Environment, "Environment");
1733 TabWidget->addTab(new TP_Evidence, "Evidence");
1734
1735 // Menu bar
1736 QMenu* Menu = menuBar()->addMenu("&Menu");
1737 Menu->addAction("New history plot", this, SLOT(MenuNewHistory()));
1738 Menu->addSeparator();
1739 Menu->addAction("About", this, SLOT(MenuAbout()));
1740 Menu->addSeparator();
1741 QAction* QuitAction = Menu->addAction("Quit", qApp, SLOT(quit()));
1742 QuitAction->setShortcut(Qt::CTRL + Qt::Key_Q);
1743
1744 // Show main window
1745 resize(TabWidget->sizeHint()*1.1);
1746 show();
1747
1748 // Set timer to regularly check the master alarm
1749 QTimer *Timer = new QTimer(this);
1750 connect(Timer, SIGNAL(timeout()), this, SLOT(CheckAlarm()));
1751 Timer->start(5000);
1752}
1753
1754GUI::~GUI() {
1755 delete Central;
1756}
1757
1758
1759void GUI::MenuAbout() {
1760 QString Rev(SVN_REVISION);
1761 Rev.remove(0,1).chop(2);
1762
1763 QMessageBox::about(this, "About Edd","Evidence Data Display\n\n"
1764 "Written by Oliver Grimm, IPP, ETH Zurich\n"
1765 "This version compiled "__DATE__" ("+Rev+")\n\n"
1766 "Graphical user interface implemented with Qt and Qwt.\n"
1767 "Evidence control system based on DIM (http://dim.web.cern.ch).\n\n"
1768 "Comments to oliver.grimm@phys.ethz.ch.");
1769}
1770
1771// Open request for new history plot
1772void GUI::MenuNewHistory() {
1773
1774 QStringList List;
1775 char *Name, *Format;
1776 int Type;
1777 bool OK;
1778
1779 // Find all DIM services and sort
1780 getServices("*");
1781 while ((Type = getNextService(Name, Format)) != 0) {
1782 if (Type==DimSERVICE) List.append(Name);
1783 }
1784 List.sort();
1785
1786 // Open dialog and open history window
1787 QString Result = QInputDialog::getItem(this, "Edd Request",
1788 "Enter DIM service name", List, 0, true, &OK);
1789 if (OK && !Result.isEmpty()) {
1790 Result = Result.trimmed();
1791 QWidget *Hist = OpenHistory(Result.toAscii().data(), 0);
1792 if (Hist != NULL) Hist->show();
1793 }
1794}
1795
1796// Open tab as separate window
1797void GUI::DetachTab(int Tab) {
1798
1799 QWidget *W = NULL;
1800 QMainWindow *M = new QMainWindow;
1801
1802 M->setCentralWidget(new QWidget(M));
1803 M->setStatusBar(new QStatusBar(M));
1804
1805 switch(Tab) {
1806 case 0: W = new TP_DAQ; break;
1807 case 1: W = new TP_Bias; break;
1808 case 2: W = new TP_Feedback; break;
1809 case 3: W = new TP_Environment; break;
1810 case 4: W = new TP_Evidence; break;
1811 default: break;
1812 }
1813
1814 if (W == NULL) {
1815 delete M->centralWidget();
1816 delete M;
1817 return;
1818 }
1819
1820 W->setParent(M);
1821 M->resize(size());
1822 M->setWindowTitle("Edd - " + TabWidget->tabText(Tab));
1823 M->show();
1824}
1825
1826// Check alarm level and if Alarm server is alive
1827void GUI::CheckAlarm() {
1828
1829 static int WarnedLevel = 0;
1830 static bool AlarmServerWarned = false;
1831
1832 // === Check service Alarm/MasterAlarm ===
1833 DimCurrentInfo MasterAlarm("Alarm/MasterAlarm", -1);
1834
1835 if (MasterAlarm.getInt() > WarnedLevel) {
1836 QSound::play(QApplication::applicationDirPath() + "/Error.wav");
1837
1838 // Construct warning message box
1839 QMessageBox Box;
1840 Box.setWindowTitle("Edd Alarm");
1841 Box.setText("Service 'Alarm/MasterAlarm' is at " + QString::number(MasterAlarm.getInt()));
1842 Box.setInformativeText("Warn again for the same alarm level?");
1843 Box.setIcon(QMessageBox::Warning);
1844 Box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
1845 Box.setDefaultButton(QMessageBox::No);
1846
1847 // Check if user wants to reset warned level
1848 if (Box.exec() == QMessageBox::Yes) WarnedLevel = 0;
1849 else WarnedLevel = MasterAlarm.getInt();
1850 }
1851
1852 // If MasterAlam decreased, lower also warned level
1853 if (MasterAlarm.getInt() < WarnedLevel && MasterAlarm.getInt() >=0 ) WarnedLevel = MasterAlarm.getInt();
1854
1855 // === Check Alarm server ===
1856 DimCurrentInfo ServerList("DIS_DNS/SERVER_LIST", NO_LINK);
1857 std::string Result = EvidenceServer::ToString((char *) "C", ServerList.getData(), ServerList.getSize());
1858
1859 // Warn if SERVER_LIST does not contain alarm server
1860 if (Result.find("Alarm@") == std::string::npos && !AlarmServerWarned) {
1861 QMessageBox Box;
1862 Box.setWindowTitle("Edd Alarm");
1863 Box.setText("Alarm server is unavailable or in error");
1864 Box.setIcon(QMessageBox::Critical);
1865 Box.setStandardButtons(QMessageBox::Ok);
1866 Box.setDefaultButton(QMessageBox::Ok);
1867 Box.exec();
1868
1869 AlarmServerWarned = true;
1870 }
1871
1872 if (Result.find("Alarm@") != std::string::npos) AlarmServerWarned = false;
1873}
1874
1875// Quit application when clicking close button on window
1876void GUI::closeEvent(QCloseEvent *) {
1877
1878 qApp->quit();
1879}
1880
1881
1882//**************************** Main program ***************************
1883int main(int argc, char *argv[]) {
1884
1885 if (argc > 1) DRSBoard = "FADctrl";
1886
1887 QApplication app(argc, argv);
1888 GUI MainWindow;
1889
1890 return app.exec();
1891}
Note: See TracBrowser for help on using the repository browser.