source: Evidence/Edd/Edd.cc@ 213

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