source: Evidence/Edd/Edd.cc@ 222

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