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

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