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

Last change on this file since 10068 was 10068, checked in by ogrimm, 12 years ago
Alarm has 5 s minimum period, updates to Edd
File size: 50.7 KB
Line 
1
2/* ============================================================
3
4Edd - Evidence Data Display
5
6Qt-based graphical user interface for the Evidence contron system
7
8EddLineDisplay changes its background colour in case it display
9a DIM status service
10
11April 2010, Oliver Grimm
12
13============================================================ */
14
15#include "Edd.h"
16
17Qt::GlobalColor LineColors[] = {Qt::black, Qt::blue, Qt::red, Qt::green, Qt::white,
18 Qt::darkRed, Qt::darkGreen, Qt::darkBlue, Qt::cyan,
19 Qt::darkCyan, Qt::magenta, Qt::darkMagenta,
20 Qt::gray, Qt::darkGray, Qt::lightGray};
21
22
23class EddDim *Handler;
24
25
26// History chooser function (opens plot for numeric data, TextHist for all other)
27QWidget *OpenHistory(char *Service, int Index) {
28
29 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 Active = false;
959
960 Tmpfile = tmpfile();
961 if(Tmpfile == NULL) {
962 QMessageBox::warning(this, "Edd Message", "Could not open temporary file.", QMessageBox::Ok);
963 }
964
965 // Open file with RawDataCTX
966 RD = new RawDataCTX(true);
967 ErrCode = CTX_NOTOPEN;
968
969 // Context menu
970 PhysPipeAction = new QAction("Plot physical pipeline", this);
971 PhysPipeAction->setCheckable(true);
972 connect(PhysPipeAction, SIGNAL(triggered()), SLOT(PlotTraces()));
973 Menu->insertAction(StripAction, PhysPipeAction);
974 Menu->removeAction(StripAction);
975
976 // Initial trace
977 AddTrace(0,0,0);
978
979 // Connect to DIM handler
980 if (connect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), SLOT(Update(QString, int, QByteArray, QString, QString))) == false) {
981 printf("Failed connection for %s\n", Name.toAscii().data());
982 }
983 SetActive(true);
984}
985
986// Destructor (items with parent widget are automatically deleted)
987EventScope::~EventScope() {
988
989 SetActive(false);
990 while (!List.isEmpty()) DeleteCurve(List.last().Signal);
991 delete RD;
992 if (Tmpfile != NULL) fclose(Tmpfile);
993}
994
995// Add trace
996void EventScope::AddTrace(int Board, int Chip, int Channel) {
997
998 struct ItemDetails N;
999
1000 N.Signal = NewCurve(QString::number(Board)+","+QString::number(Chip)+","+ QString::number(Channel)+ " (" + DRS_to_Pixel(Board, Chip, Channel).c_str() + ")");
1001 N.Board = Board;
1002 N.Chip = Chip;
1003 N.Channel = Channel;
1004 N.Trigger = new QwtPlotMarker();
1005 N.Trigger->setSymbol(QwtSymbol(QwtSymbol::Diamond, QBrush(N.Signal->pen().color()), N.Signal->pen(), QSize(10,10)));
1006 N.Trigger->attach(this);
1007
1008 if (List.isEmpty()) {
1009 QPen Pen = N.Signal->pen();
1010 Pen.setWidth(2);
1011 N.Signal->setPen(Pen);
1012 }
1013 List.append(N);
1014
1015 PlotTraces();
1016}
1017
1018// Update last trace (to reflect current setting of spin boxes in DAQ page)
1019void EventScope::UpdateFirst(int Board, int Chip, int Channel) {
1020
1021 if (List.isEmpty()) return;
1022
1023 List.first().Signal->setTitle(QString::number(Board)+","+QString::number(Chip)+","+ QString::number(Channel) + " (" + DRS_to_Pixel(Board, Chip, Channel).c_str() + ")");
1024 List.first().Board = Board;
1025 List.first().Chip = Chip;
1026 List.first().Channel = Channel;
1027
1028 PlotTraces();
1029}
1030
1031// Update event buffer
1032void EventScope::Update(QString Name, int Time, QByteArray Data, QString Format, QString) {
1033
1034 if (Name != this->Name) return;
1035
1036 // Check if service available
1037 if (!SetStatus(this, Name, Time, Format)) return;
1038 if (Data.size() < (int) sizeof(RunHeader)) return;
1039
1040 // Open tempory file and write event data to this file
1041 QTemporaryFile File;
1042 if (!File.open()) {
1043 QMessageBox::warning(this, "Edd Message","Could not open temporary file.",QMessageBox::Ok);
1044 return;
1045 }
1046 if (File.write(Data) == -1) {
1047 QMessageBox::warning(this, "Edd Message","Could not write data to temporary file.",QMessageBox::Ok);
1048 return;
1049 }
1050
1051 // Prepare temporary file for run header
1052 ftruncate(fileno(Tmpfile), 0);
1053 rewind(Tmpfile);
1054
1055 // Open file with RawDataCTX
1056 switch (ErrCode = RD->OpenDataFile(File.fileName().toAscii().data(), Tmpfile)) {
1057 case CTX_FOPEN: QMessageBox::warning(this, "Edd Message","Could not open file.",QMessageBox::Ok);
1058 return;
1059 case CTX_RHEADER: QMessageBox::warning(this, "Edd Message","Could not read run header.",QMessageBox::Ok);
1060 return;
1061 case CTX_BSTRUCT: QMessageBox::warning(this, "Edd Message","Could not read board structures.",QMessageBox::Ok);
1062 return;
1063 default: break;
1064 }
1065
1066 // Emit signal containing run header
1067 rewind(Tmpfile);
1068 QTextStream Stream(Tmpfile);
1069 emit(RunHeaderChanged(Stream.readAll()));
1070
1071 // Prepare temporary file for event header
1072 ftruncate(fileno(Tmpfile), 0);
1073 rewind(Tmpfile);
1074
1075 if (RD->ReadEvent(0, Tmpfile) != CTX_OK) {
1076 QMessageBox::warning(this, "Edd Warning","Could not read event.",QMessageBox::Ok);
1077 return;
1078 }
1079
1080 // Emit signal containing run header
1081 rewind(Tmpfile);
1082 emit(EventHeaderChanged(Stream.readAll()));
1083
1084 PlotTraces();
1085}
1086
1087// Update curves
1088void EventScope::PlotTraces() {
1089
1090 double x,y;
1091 unsigned int Cell, Trig;
1092
1093 // Only process if valid data in RawDataCTX class
1094 if (!Active || ErrCode != CTX_OK) return;
1095
1096 // Set x axis title
1097 if (PhysPipeAction->isChecked()) setAxisTitle(QwtPlot::xBottom, "Time from start of pipeline (ns)");
1098 else setAxisTitle(QwtPlot::xBottom, "Time from trigger minus one revolution (ns)");
1099
1100 // Loop through event data to update event scope
1101 RunHeader *R = RD->RHeader;
1102 for (int i=0; i<List.size(); i++) {
1103
1104 ClearCurve(i);
1105
1106 // Check if current event contains requested trace
1107 if (List[i].Board>=R->NBoards || List[i].Chip>=R->NChips || List[i].Channel>=R->NChannels) continue;
1108
1109 // Set trigger marker visibility
1110 List[i].Trigger->setVisible(PhysPipeAction->isChecked());
1111
1112 // Determine trigger cell
1113 Trig = *((int *) RD->Data + List[i].Board*R->NChips + List[i].Chip);
1114
1115 for (unsigned int j=0; j<R->Samples; j++) {
1116
1117 if (PhysPipeAction->isChecked()) Cell = (j + Trig) % 1024;
1118 else Cell = j;
1119
1120 x = j / RD->BStruct[List[i].Board].NomFreq;
1121 y = *((short *) (RD->Data + R->NBoards*R->NChips*sizeof(int)) +
1122 List[i].Board*R->NChips*R->NChannels*R->Samples + List[i].Chip*R->NChannels*R->Samples +
1123 List[i].Channel*R->Samples + Cell) * RD->BStruct[List[i].Board].ScaleFactor;
1124
1125 AddPoint(i, x, y);
1126
1127 // Set trigger point indicator
1128 if (Trig == j/*+R->Offset*/) List[i].Trigger->setValue(x, y);
1129 }
1130 }
1131
1132 UpdatePlot();
1133
1134 // Loop through event data for pixel display
1135 QVector<double> Pixel(R->NBoards*R->NChips*R->NChannels);
1136 int Count = 0;
1137
1138 for (unsigned int Board=0; Board<R->NBoards; Board++) {
1139 for (unsigned int Chip=0; Chip<R->NChips; Chip++) {
1140 for (unsigned int Channel=0; Channel<R->NChannels; Channel++) {
1141 Pixel[Count] = DBL_MIN;
1142
1143 for (unsigned int i=0; i<R->Samples; i++) {
1144 y = *((short *) (RD->Data + R->NBoards*R->NChips*sizeof(int)) +
1145 Board*R->NChips*R->NChannels*R->Samples + Chip*R->NChannels*R->Samples +
1146 Channel*R->Samples + i) * RD->BStruct[Board].ScaleFactor;
1147
1148 if (y > Pixel[Count]) Pixel[Count] = y;
1149 }
1150 Count++;
1151 }}}
1152
1153 emit(PixelData(Pixel));
1154}
1155
1156// Remove list entry
1157void EventScope::DeleteCurve(QwtPlotCurve *Curve) {
1158
1159 for (int i=0; i<List.size(); i++) if (List[i].Signal == Curve) {
1160 delete List[i].Trigger;
1161 List.removeAt(i);
1162 }
1163}
1164
1165// Set display active (if inactive, disconnect from server)
1166void EventScope::SetActive(bool State) {
1167
1168 if (State && !Active) Handler->Subscribe("drsdaq/EventData");
1169 if (!State && Active) Handler->Unsubscribe("drsdaq/EventData");
1170 Active = State;
1171}
1172
1173
1174//------------------------------------------------------------------
1175//**************************** Tab pages ***************************
1176//------------------------------------------------------------------
1177
1178//
1179// Environment page
1180//
1181TP_Environment::TP_Environment() {
1182
1183 QGridLayout *Layout = new QGridLayout(this);
1184 setAttribute(Qt::WA_DeleteOnClose);
1185
1186 // Status display
1187 EddLineDisplay *Line = new EddLineDisplay("ARDUINO/Message");
1188 Line->setMaximumWidth(200);
1189 Layout->addWidget(Line, 0, 0, 1, 2);
1190
1191 // Generate plot and data displays
1192 EddPlot *Plot = new EddPlot();
1193 for (int i=0; i<10; i++) {
1194 Line = new EddLineDisplay("ARDUINO/Data", i);
1195 Layout->addWidget(Line, i%5+1, i/5, 1, 1);
1196 Plot->AddService("ARDUINO/Data", i);
1197 }
1198 Layout->addWidget(Plot, 0, 2, 9, 7);
1199
1200 // Night sky monitor
1201 Line = new EddLineDisplay("SQM/Message");
1202 Line->setMaximumWidth(200);
1203 Layout->addWidget(Line, 6, 0, 1, 2);
1204
1205 Line = new EddLineDisplay("SQM/NSB");
1206 Layout->addWidget(Line, 7, 0, 1, 1);
1207}
1208
1209//
1210// Bias page
1211//
1212TP_Bias::TP_Bias() {
1213
1214 QGridLayout *Layout = new QGridLayout(this);
1215 setAttribute(Qt::WA_DeleteOnClose);
1216 EddLineDisplay *Line;
1217
1218 EddPlot *Plot = new EddPlot();
1219 Plot->setMinimumWidth(400);
1220 for (int i=0; i<18; i++) {
1221 Line = new EddLineDisplay("Bias/VOLT/ID00", i+64);
1222 Layout->addWidget(Line, i%9+1, 0+i/9, 1, 1);
1223 Plot->AddService("Bias/VOLT/ID00", i+64);
1224
1225 Line = new EddLineDisplay("Bias/VOLT/ID00", i+96);
1226 Layout->addWidget(Line, i%9+1, 2+i/9, 1, 1);
1227 Plot->AddService("Bias/VOLT/ID00",i+96);
1228 }
1229
1230 Layout->addWidget(Plot, 0, 4, 12, 3);
1231 Line = new EddLineDisplay("Bias/Message");
1232 Line->setMaximumWidth(200);
1233 Layout->addWidget(Line, 0, 0, 1, 3);
1234
1235 EddCommand *Command = new EddCommand("Bias/Command");
1236 Layout->addWidget(Command, 10, 0, 1, 4);
1237
1238 EddText *Text = new EddText("Bias/ConsoleOut", true);
1239 Text->setFixedWidth(400);
1240 Layout->addWidget(Text, 11, 0, 4, 4);
1241
1242 QWidget *Button = new QPushButton("Currents");
1243 Layout->addWidget(Button, 13, 4, 1, 1);
1244 connect(Button, SIGNAL(pressed()), SLOT(BiasCurrents()));
1245}
1246
1247void TP_Bias::BiasCurrents() {
1248
1249 QMainWindow *M = new QMainWindow;
1250 M->setCentralWidget(new QWidget);
1251 M->setStatusBar(new QStatusBar(M));
1252 M->setWindowTitle("Edd - Bias currents");
1253 M->setAttribute(Qt::WA_DeleteOnClose);
1254
1255 QGridLayout *Layout = new QGridLayout(M->centralWidget());
1256 EddLineDisplay *Line;
1257 EddPlot *Plot = new EddPlot();
1258
1259 for (int i=0; i<36; i++) {
1260 Line = new EddLineDisplay("Bias/MICROAMP/ID00", i+64);
1261 Line->setMaximumWidth(60);
1262 Layout->addWidget(Line, i%9, 0+i/9, 1, 1);
1263 Plot->AddService("Bias/MICROAMP/ID00", i+64);
1264 }
1265 Layout->addWidget(Plot, 0, 4, 30, 12);
1266
1267 M->show();
1268}
1269
1270//
1271// Feedback page
1272//
1273TP_Feedback::TP_Feedback() {
1274
1275 setAttribute(Qt::WA_DeleteOnClose);
1276 QGridLayout *Layout = new QGridLayout(this);
1277 EddLineDisplay *Line;
1278
1279 EddPlot *Plot = new EddPlot();
1280 for (int i=0; i<36; i++) {
1281 Line = new EddLineDisplay("Feedback/Average", i);
1282 Line->setMaximumWidth(60);
1283 Layout->addWidget(Line, i%9+2, 0+i/9, 1, 1);
1284 Plot->AddService("Feedback/Average", i);
1285 }
1286 Layout->addWidget(Plot, 0, 4, 12, 10);
1287
1288 Line = new EddLineDisplay("drsdaq/Message");
1289 Line->setMaximumWidth(200);
1290 Layout->addWidget(Line, 0, 0, 1, 2);
1291
1292 Line = new EddLineDisplay("Feedback/State");
1293 Line->setMaximumWidth(150);
1294 Layout->addWidget(Line, 1, 0, 1, 2);
1295 Line = new EddLineDisplay("Feedback/Count");
1296 Line->setMaximumWidth(60);
1297 Layout->addWidget(Line, 1, 2);
1298
1299 QWidget *Button = new QPushButton("Details");
1300 Layout->addWidget(Button, 12, 0, 1, 1);
1301 connect(Button, SIGNAL(pressed()), SLOT(FeedbackDetails()));
1302}
1303
1304void TP_Feedback::FeedbackDetails() {
1305
1306 QMainWindow *M = new QMainWindow;
1307 M->setCentralWidget(new QWidget);
1308 M->setStatusBar(new QStatusBar(M));
1309 M->setWindowTitle("Edd - Feedback Details");
1310 M->setAttribute(Qt::WA_DeleteOnClose);
1311
1312 QGridLayout *Layout = new QGridLayout(M->centralWidget());
1313 EddLineDisplay *Line;
1314 EddPlot *Plot = new EddPlot();
1315
1316 for (int i=0; i<36; i++) {
1317 Line = new EddLineDisplay("Feedback/Sigma", i);
1318 Line->setMaximumWidth(60);
1319 Layout->addWidget(Line, i%9, 0+i/9, 1, 1);
1320 Plot->AddService("Feedback/Sigma", i);
1321
1322 Line = new EddLineDisplay("Feedback/Target", i);
1323 Line->setMaximumWidth(60);
1324 Layout->addWidget(Line, i%9+10, 0+i/9, 1, 1);
1325
1326 Line = new EddLineDisplay("Feedback/Response", i);
1327 Line->setMaximumWidth(60);
1328 Layout->addWidget(Line, i%9+20, 0+i/9, 1, 1);
1329 }
1330 Layout->addWidget(Plot, 0, 4, 30, 12);
1331
1332 M->show();
1333}
1334
1335//
1336// Event scope page
1337//
1338TP_DAQ::TP_DAQ() {
1339
1340 EddLineDisplay *Line;
1341
1342 setAttribute(Qt::WA_DeleteOnClose);
1343 QGridLayout *Layout = new QGridLayout(this);
1344
1345 // Run-related information
1346 Line = new EddLineDisplay("drsdaq/RunNumber");
1347 Line->setMaximumWidth(100);
1348 Layout->addWidget(Line, 0, 1, 1, 1);
1349 Line = new EddLineDisplay("drsdaq/EventNumber");
1350 Line->setMaximumWidth(100);
1351 Layout->addWidget(Line, 0, 2, 1, 1);
1352 Line = new EddLineDisplay("drsdaq/RunSizeMB");
1353 Line->setMaximumWidth(100);
1354 Layout->addWidget(Line, 0, 3, 1, 1);
1355 Line = new EddLineDisplay("drsdaq/FileSizeMB");
1356 Line->setMaximumWidth(100);
1357 Layout->addWidget(Line, 0, 4, 1, 1);
1358 Line = new EddLineDisplay("drsdaq/FileName");
1359 Line->setMaximumWidth(200);
1360 Layout->addWidget(Line, 0, 5, 1, 1);
1361
1362 // Event scope
1363 Scope = new EventScope;
1364 Scope->setMinimumWidth(700);
1365
1366 // Text boxes for run and event header
1367 RunHeaderDisplay = new QPlainTextEdit();
1368 EventHeaderDisplay = new QPlainTextEdit();
1369 RunHeaderDisplay->setReadOnly(true);
1370 EventHeaderDisplay->setReadOnly(true);
1371
1372 // Tab widget
1373 QTabWidget *TabWidget = new QTabWidget();
1374 TabWidget->addTab(Scope, "&Signals");
1375 TabWidget->addTab(RunHeaderDisplay, "&Run Header");
1376 TabWidget->addTab(EventHeaderDisplay, "&Event Header");
1377 Layout->addWidget(TabWidget, 1, 1, 5, 5);
1378
1379 // Channel number
1380 Channel = new QSpinBox;
1381 connect(Channel, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int)));
1382 Channel->setToolTip("DRS channel number");
1383
1384 // Chip number
1385 Chip = new QSpinBox;
1386 connect(Chip, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int)));
1387 Chip->setToolTip("DRS chip number");
1388
1389 // Board number
1390 Board = new QSpinBox;
1391 connect(Board, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int)));
1392 Board->setToolTip("DRS board number");
1393
1394 // Pixel ID
1395 PixelID = new QLineEdit;
1396 PixelID->setMaximumWidth(60);
1397 connect(PixelID, SIGNAL(returnPressed()), SLOT(TranslatePixelID()));
1398 PixelID->setToolTip("Pixel identification");
1399
1400 // Layout of pixel addressing widgets
1401 QFormLayout *FormLayout = new QFormLayout();
1402 FormLayout->setRowWrapPolicy(QFormLayout::WrapAllRows);
1403 FormLayout->addRow("Channel", Channel);
1404 FormLayout->addRow("Chip", Chip);
1405 FormLayout->addRow("Board", Board);
1406 FormLayout->addRow("Pixel ID", PixelID);
1407 Layout->addLayout(FormLayout, 0, 0, 2, 1);
1408
1409 // Add trace permanently
1410 QPushButton *Button = new QPushButton("Keep trace");
1411 Button->setToolTip("Keep trace in display");
1412 Button->setMaximumWidth(80);
1413 Layout->addWidget(Button, 2, 0);
1414 connect(Button, SIGNAL(clicked()), SLOT(KeepCurrent()));
1415
1416 // Stop/start
1417 StartStopButton = new QPushButton("Stop");
1418 StartStopButton->setToolTip("Start/stop display");
1419 StartStopButton->setMaximumWidth(80);
1420 StartStopButton->setCheckable(true);
1421 QPalette Palette = StartStopButton->palette();
1422 Palette.setColor(QPalette::ButtonText, Qt::blue);
1423 Palette.setColor(QPalette::Button, Qt::green);
1424 StartStopButton->setPalette(Palette);
1425 StartStopButton->setFont(QFont("Times", 10, QFont::Bold));
1426 Layout->addWidget(StartStopButton, 4, 0);
1427 connect(StartStopButton, SIGNAL(toggled(bool)), SLOT(StartStop(bool)));
1428
1429 // Button to show event display
1430 QPushButton *PixDisplay = new QPushButton("Pixel display");
1431 PixDisplay->setFont(QFont("Times", 10, QFont::Bold));
1432 PixDisplay->setToolTip("Show event display window");
1433 PixDisplay->setMaximumWidth(80);
1434 Layout->addWidget(PixDisplay, 5, 0);
1435 connect(PixDisplay, SIGNAL(clicked()), SLOT(ShowPixelDisplay()));
1436
1437 // Event display window
1438 Display = new QWidget();
1439 Display->setWindowTitle("Edd - Event display");
1440
1441 Pixel = new QPushButton *[MAXPIXEL];
1442 int Count = 0;
1443 double x,y;
1444
1445 for (int ix=-22; ix<=22; ix++) for (int iy=-19; iy<=20; iy++) {
1446 if (Count == MAXPIXEL) break;
1447
1448 x = ix*0.866;
1449 y = iy - (ix%2==0 ? 0.5:0);
1450 if ((pow(x,2)+pow(y,2) >= 395) && !(abs(ix)==22 && iy==7)) continue;
1451
1452 Pixel[Count] = new QPushButton(Display);
1453 Pixel[Count]->setAutoFillBackground(true);
1454 Pixel[Count]->setGeometry((int) (x*12.5 + 250), (int) (y*12.5 + 250), 10, 10);
1455 Pixel[Count]->show();
1456 Count++;
1457 }
1458
1459 StartStop(false);
1460 connect(Scope, SIGNAL(PixelData(QVector<double>)), SLOT(SetPixelData(QVector<double>)));
1461}
1462
1463TP_DAQ::~TP_DAQ() {
1464
1465 delete[] Pixel;
1466}
1467
1468// Translate pixel ID to board, chip, channel
1469void TP_DAQ::TranslatePixelID() {
1470
1471 if (Scope->Pixel_to_DRSboard(PixelID->text().toStdString()) == 999999999) {
1472 QMessageBox::warning(this, "Edd Message","Pixel ID unknown.",QMessageBox::Ok);
1473 }
1474 else {
1475 Board->setValue(Scope->Pixel_to_DRSboard(PixelID->text().toStdString()));
1476 Chip->setValue(Scope->Pixel_to_DRSchip(PixelID->text().toStdString()));
1477 Channel->setValue(Scope->Pixel_to_DRSchannel(PixelID->text().toStdString()));
1478 }
1479}
1480
1481// Keep current trace
1482void TP_DAQ::KeepCurrent() {
1483
1484 Scope->AddTrace(Board->value(), Chip->value(), Channel->value());
1485}
1486
1487// Start/stop event acquisition
1488void TP_DAQ::StartStop(bool State) {
1489
1490 Scope->SetActive(!State);
1491 StartStopButton->setText(State ? "Start" : "Stop");
1492 if (!State) {
1493 connect(Scope, SIGNAL(RunHeaderChanged(QString)), RunHeaderDisplay, SLOT(setPlainText(QString)));
1494 connect(Scope, SIGNAL(EventHeaderChanged(QString)), EventHeaderDisplay, SLOT(setPlainText(QString)));
1495 }
1496 else {
1497 disconnect(Scope, SIGNAL(RunHeaderChanged(QString)), RunHeaderDisplay, SLOT(setPlainText(QString)));
1498 disconnect(Scope, SIGNAL(EventHeaderChanged(QString)), EventHeaderDisplay, SLOT(setPlainText(QString)));
1499 }
1500}
1501
1502// Update event scope
1503void TP_DAQ::UpdateScope(int) {
1504
1505 // Update pixel ID
1506 PixelID->setText(Scope->DRS_to_Pixel(Board->value(), Chip->value(), Channel->value()).c_str());
1507 // Update first trace
1508 Scope->UpdateFirst(Board->value(), Chip->value(), Channel->value());
1509}
1510
1511// Show/hide pixel display
1512void TP_DAQ::ShowPixelDisplay() {
1513
1514 Display->show();
1515 Display->raise();
1516}
1517
1518void TP_DAQ::SetPixelData(QVector<double> Data) {
1519
1520 QwtLinearColorMap Map;
1521
1522 for (int i=0; i<Data.size(); i++) {
1523 Pixel[i]->setPalette(QPalette(Map.color(QwtDoubleInterval(300, 400), Data[i])));
1524 }
1525}
1526
1527
1528//
1529// Evidence page
1530//
1531TP_Evidence::TP_Evidence() {
1532
1533 setAttribute(Qt::WA_DeleteOnClose);
1534 QGridLayout *Layout = new QGridLayout(this);
1535 EddLineDisplay *Line;
1536 EddText *Text;
1537
1538 Line = new EddLineDisplay("Alarm/Message");
1539 Line->setMaximumWidth(200);
1540 Layout->addWidget(Line, 0, 0, 1, 2);
1541
1542 QPushButton *Button = new QPushButton();
1543 Button->setText("ON/OFF");
1544 Button->setCheckable(true);
1545 connect(Button, SIGNAL(toggled(bool)), SLOT(ToggleAlarm(bool)));
1546 Layout->addWidget(Button, 0, 3, 1, 1);
1547
1548 Line = new EddLineDisplay("Alarm/MasterAlarm");
1549 Layout->addWidget(Line, 0, 1, 1, 1);
1550
1551 Text = new EddText("Alarm/Summary", true);
1552 Text->Accumulate = false;
1553 Text->setMaximumWidth(200);
1554 Text->setMaximumHeight(150);
1555 Layout->addWidget(Text, 1, 0, 1, 2);
1556
1557 Line = new EddLineDisplay("DColl/Message");
1558 Line->setMaximumWidth(200);
1559 Layout->addWidget(Line, 3, 0, 1, 2);
1560
1561 Line = new EddLineDisplay("DColl/DataSizeMB");
1562 Layout->addWidget(Line, 4, 0, 1, 1);
1563
1564 Line = new EddLineDisplay("DColl/LogSizeMB");
1565 Layout->addWidget(Line, 4, 1, 1, 1);
1566
1567 Line = new EddLineDisplay("DColl/CurrentFile");
1568 Line->setMaximumWidth(400);
1569 Layout->addWidget(Line, 5, 0, 1, 3);
1570
1571 Line = new EddLineDisplay("Config/Message");
1572 Line->setMaximumWidth(200);
1573 Layout->addWidget(Line, 6, 0, 1, 2);
1574
1575 Line = new EddLineDisplay("Config/ModifyTime");
1576 Line->setMaximumWidth(200);
1577 Line->ShowAsTime = true;
1578 Layout->addWidget(Line, 7, 0, 1, 1);
1579
1580 Button = new QPushButton();
1581 Button->setText("eLogBook");
1582 connect(Button, SIGNAL(released()), SLOT(StartELog()));
1583 Layout->addWidget(Button, 6, 1, 1, 1);
1584
1585 Button = new QPushButton();
1586 Button->setText("Start DIM browser");
1587 connect(Button, SIGNAL(released()), SLOT(StartDIMBrowser()));
1588 Layout->addWidget(Button, 7, 1, 1, 1);
1589
1590 Line = new EddLineDisplay("Edd/Rate_kBSec");
1591 Layout->addWidget(Line, 8, 0, 1, 1);
1592}
1593
1594// Toggle state of Alarm server
1595void TP_Evidence::ToggleAlarm(bool State) {
1596
1597 if (State) DimClient::sendCommandNB((char *) "Alarm/Switch", (char *) "off");
1598 else DimClient::sendCommandNB((char *) "Alarm/Switch", (char *) "on");
1599}
1600
1601// Start DIM Browser
1602void TP_Evidence::StartDIMBrowser() {
1603
1604 QProcess::startDetached("did", QStringList(), QString(getenv("DIMDIR"))+"/linux/");
1605}
1606
1607// Start eLogBook
1608void TP_Evidence::StartELog() {
1609
1610 QProcess::startDetached("firefox http://fact.ethz.ch/FACTelog/index.jsp");
1611}
1612
1613
1614//--------------------------------------------------------------------
1615//*************************** Main GUI *******************************
1616//--------------------------------------------------------------------
1617//
1618// All widgets have ultimately Central as parent.
1619//
1620GUI::GUI() {
1621
1622 Handler = new EddDim();
1623
1624 // Set features of main window
1625 Central = new QWidget(this);
1626 setCentralWidget(Central);
1627 setStatusBar(new QStatusBar(this));
1628 setWindowTitle("Edd - Evidence Data Display");
1629
1630 // Arrangement in tabs
1631 TabWidget = new QTabWidget(Central);
1632 TabWidget->setTabsClosable(true);
1633 connect(TabWidget, SIGNAL(tabCloseRequested(int)), SLOT(DetachTab(int)));
1634 TabWidget->addTab(new TP_DAQ, "Event scope");
1635 TabWidget->addTab(new TP_Bias, "Bias");
1636 TabWidget->addTab(new TP_Feedback, "Feedback");
1637 TabWidget->addTab(new TP_Environment, "Environment");
1638 TabWidget->addTab(new TP_Evidence, "Evidence");
1639
1640 // Menu bar
1641 QMenu* Menu = menuBar()->addMenu("&Menu");
1642 Menu->addAction("New history plot", this, SLOT(MenuNewHistory()));
1643 Menu->addSeparator();
1644 Menu->addAction("About", this, SLOT(MenuAbout()));
1645 Menu->addSeparator();
1646 QAction* QuitAction = Menu->addAction("Quit", qApp, SLOT(quit()));
1647 QuitAction->setShortcut(Qt::CTRL + Qt::Key_Q);
1648
1649 // Show main window
1650 resize(TabWidget->sizeHint()*1.1);
1651 show();
1652}
1653
1654GUI::~GUI() {
1655 delete Central;
1656}
1657
1658
1659void GUI::MenuAbout() {
1660 QString Rev(SVN_REVISION);
1661 Rev.remove(0,1).chop(2);
1662
1663 QMessageBox::about(this, "About Edd","Evidence Data Display\n\n"
1664 "Written by Oliver Grimm, IPP, ETH Zurich\n"
1665 "This version compiled "__DATE__" ("+Rev+")\n\n"
1666 "Graphical user interface implemented with Qt and Qwt.\n"
1667 "Evidence control system based on DIM (http://dim.web.cern.ch).\n\n"
1668 "Comments to oliver.grimm@phys.ethz.ch.");
1669}
1670
1671// Open request for new history plot
1672void GUI::MenuNewHistory() {
1673
1674 QStringList List;
1675 char *Name, *Format;
1676 int Type;
1677 bool OK;
1678
1679 // Find all DIM services and sort
1680 getServices("*");
1681 while ((Type = getNextService(Name, Format)) != 0) {
1682 if (Type==DimSERVICE) List.append(Name);
1683 }
1684 List.sort();
1685
1686 // Open dialog and open history window
1687 QString Result = QInputDialog::getItem(this, "Edd Request",
1688 "Enter DIM service name", List, 0, true, &OK);
1689 if (OK && !Result.isEmpty()) {
1690 Result = Result.trimmed();
1691 QWidget *Hist = OpenHistory(Result.toAscii().data(), 0);
1692 if (Hist != NULL) Hist->show();
1693 }
1694}
1695
1696// Open tab as separate window
1697void GUI::DetachTab(int Tab) {
1698
1699 QWidget *W = NULL;
1700 QMainWindow *M = new QMainWindow;
1701
1702 M->setCentralWidget(new QWidget(M));
1703 M->setStatusBar(new QStatusBar(M));
1704
1705 switch(Tab) {
1706 case 0: W = new TP_DAQ; break;
1707 case 1: W = new TP_Bias; break;
1708 case 2: W = new TP_Feedback; break;
1709 case 3: W = new TP_Environment; break;
1710 case 4: W = new TP_Evidence; break;
1711 default: break;
1712 }
1713
1714 if (W == NULL) {
1715 delete M->centralWidget();
1716 delete M;
1717 return;
1718 }
1719
1720 W->setParent(M);
1721 M->resize(size());
1722 M->setWindowTitle("Edd - " + TabWidget->tabText(Tab));
1723 M->show();
1724}
1725
1726// Quit application when clicking close button on window
1727void GUI::closeEvent(QCloseEvent *) {
1728 qApp->quit();
1729}
1730
1731
1732//**************************** Main program ***************************
1733int main(int argc, char *argv[]) {
1734
1735 QApplication app(argc, argv);
1736 GUI MainWindow;
1737
1738 return app.exec();
1739}
Note: See TracBrowser for help on using the repository browser.