source: Evidence/Edd/Edd.cc@ 264

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