source: Evidence/Edd/Edd.cc@ 228

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