source: Evidence/Edd/Edd.cc@ 187

Last change on this file since 187 was 174, checked in by ogrimm, 15 years ago
Update to configuration handling: Dim request only made if configuration file was modified since last request
File size: 29.0 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
11February 2010, Oliver Grimm
12
13============================================================ */
14
15#include "Edd.h"
16
17Qt::GlobalColor LineColors[] = {Qt::black, Qt::blue, Qt::red, Qt::green, Qt::white,
18 Qt::darkRed, Qt::darkGreen, Qt::darkBlue, Qt::cyan,
19 Qt::darkCyan, Qt::magenta, Qt::darkMagenta,
20 Qt::gray, Qt::darkGray, Qt::lightGray};
21
22
23class EddDim *Handler;
24
25
26// History chooser function (opens plot for numeric data, TextHist for all other)
27QWidget *OpenHistory(char *Service, int Index) {
28
29 char *Name, *Format;
30 DimBrowser Browser;
31
32 Browser.getServices(Service);
33 if (Browser.getNextService(Name, Format) != DimSERVICE) return new EddPlot(Service, Index);
34
35 if (strlen(Format) == 1 && *Format != 'C') return new EddPlot(Service, Index);
36 else return new EddText(Service);
37}
38
39
40//////////////////////////////////////////
41// Text display for arbitary DIM service//
42//////////////////////////////////////////
43
44// Constructor
45EddLineDisplay::EddLineDisplay(QString Name, int Index, QWidget *P):
46 QLineEdit(P), ServiceName(Name), Index(Index) {
47
48 LastPlot = NULL;
49
50 // Widget properties
51 setReadOnly(true);
52 setMaximumWidth(100);
53 ShowAsTime = false;
54
55 // Connect to DIM handler
56 if (connect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), SLOT(Update(QString, int, QByteArray, QString, QString))) == false) {
57 printf("Failed connection for %s\n", Name.toAscii().data());
58 }
59
60 // Context menu
61 Menu = new QMenu(this);
62 Menu->addAction("Open new history", this, SLOT(MenuOpenHistory()));
63 Menu->addAction("Copy service", this, SLOT(MenuCopyService()));
64 Menu->addAction("Copy data", this, SLOT(MenuCopyData()));
65
66 // Subscribe to service
67 Handler->Subscribe(Name);
68}
69
70// Destructor
71EddLineDisplay::~EddLineDisplay() {
72
73 Handler->Unsubscribe(ServiceName);
74}
75
76// Update widget
77void EddLineDisplay::Update(QString Name, int Time, QByteArray Array, QString Format, QString Text) {
78
79 if (ServiceName != Name) return;
80
81 QPalette Pal = palette();
82
83 // Check if service available
84 if (Time == -1) {
85 setText("n/a");
86 setStatusTip(QString("%1: unavailable").arg(ServiceName));
87 Pal.setColor(QPalette::Base, Qt::lightGray);
88 }
89 else {
90 // Backgound colour determined by last byte
91 switch (Array[Array.size()]) {
92 case 0: Pal.setColor(QPalette::Base, Qt::white); break;
93 case 1: Pal.setColor(QPalette::Base, Qt::yellow); break;
94 case 2: Pal.setColor(QPalette::Base, Qt::red); break;
95 case 3: Pal.setColor(QPalette::Base, Qt::red); break;
96 default: break;
97 }
98
99 if (Format[0].toUpper() != 'C') Text = Text.section(' ', Index, Index);
100
101 if (!ShowAsTime) setText(Text);
102 else setText(QDateTime::fromTime_t(Text.toInt()).toString());
103 setCursorPosition(0);
104
105 // Update status tip
106 setStatusTip(QString("%1 (%4): Last update %2 Format '%3'").arg(ServiceName).arg( QDateTime::fromTime_t(Time).toString()).arg(Format).arg(Index));
107 }
108
109 setPalette(Pal);
110}
111
112// Open plot if mouse release within widget
113void EddLineDisplay::mouseReleaseEvent(QMouseEvent *Event) {
114
115 if (Event->button()!=Qt::LeftButton || !contentsRect().contains(Event->pos())) return;
116
117 // Check if last history plot still open, then raise
118 foreach (QWidget *Widget, QApplication::allWidgets()) {
119 if (Widget == LastPlot) {
120 Widget->activateWindow();
121 Widget->raise();
122 return;
123 }
124 }
125
126 // If not, open new plot
127 EddLineDisplay::MenuOpenHistory();
128}
129
130// Handling of mouse press event: Register start position for drag
131void EddLineDisplay::mousePressEvent(QMouseEvent *Event) {
132
133 if (Event->button() == Qt::LeftButton) dragStart = Event->pos();
134}
135
136// Handling of dragging (Drag and MimeData will be deleted by Qt)
137void EddLineDisplay::mouseMoveEvent(QMouseEvent *Event) {
138
139 if ((Event->buttons() & Qt::LeftButton) == 0) return;
140 if ((Event->pos()-dragStart).manhattanLength() < QApplication::startDragDistance()) return;
141
142 QDrag *Drag = new QDrag(this);
143 QMimeData *MimeData = new QMimeData;
144 QByteArray Data;
145 MimeData->setData("Edd/Service", Data.append(ServiceName + " " + QString::number(Index)));
146 Drag->setMimeData(MimeData);
147 Drag->exec();
148}
149
150//
151// Opening context menu
152//
153void EddLineDisplay::contextMenuEvent(QContextMenuEvent *Event) {
154
155 Menu->exec(Event->globalPos());
156}
157
158// Menu: Open history plot
159void EddLineDisplay::MenuOpenHistory() {
160
161 LastPlot = OpenHistory(ServiceName.toAscii().data(), Index);
162 if (LastPlot != NULL) LastPlot->show();
163}
164
165// Menu: Copy service name
166void EddLineDisplay::MenuCopyService() {
167
168 QMimeData *MimeData = new QMimeData;
169 QByteArray Data;
170 MimeData->setData("Edd/Service", Data.append(ServiceName + " " + QString::number(Index)));
171 QApplication::clipboard()->setMimeData(MimeData);
172}
173
174// Menu: Copy data
175void EddLineDisplay::MenuCopyData() {
176
177 QApplication::clipboard()->setText(text());
178}
179
180
181//////////////////////////////////
182// History plot for DIM service //
183//////////////////////////////////
184
185//
186// Constructor
187//
188EddPlot::EddPlot(QString DIMService, int Index, QWidget *P):
189 QwtPlot(P) {
190
191 Mutex = new QMutex(QMutex::Recursive);
192
193 // Widget properties
194 setAcceptDrops(true);
195 setAttribute(Qt::WA_DeleteOnClose);
196 setAutoReplot(false);
197 setCanvasBackground(QColor(Qt::yellow));
198 setAxisScaleDraw(QwtPlot::xBottom, new TimeScale());
199
200 Zoomer = new QwtPlotZoomer(QwtPlot::xBottom,QwtPlot::yLeft,canvas());
201 connect(Zoomer, SIGNAL(zoomed(const QwtDoubleRect &)), this, SLOT(HandleZoom(const QwtDoubleRect &)));
202 Panner = new QwtPlotPanner(canvas());
203 Panner->setMouseButton(Qt::LeftButton, Qt::ShiftModifier);
204 Grid = new QwtPlotGrid;
205 Grid->setMajPen(QPen(Qt::gray, 0, Qt::DotLine));
206 Grid->attach(this);
207 Legend = new QwtLegend();
208 Legend->setItemMode(QwtLegend::ClickableItem);
209 insertLegend(Legend, QwtPlot::TopLegend);
210
211 connect(this, SIGNAL(legendClicked (QwtPlotItem *)), SLOT(LegendClicked(QwtPlotItem *)));
212
213 // Connect to DIM handler
214 if (connect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), SLOT(Update(QString, int, QByteArray, QString, QString))) == false) {
215 printf("Failed connection for %s\n", DIMService.toAscii().data());
216 }
217
218 // Context menu
219 Menu = new QMenu(this);
220 YLogAction = Menu->addAction("y scale log", this, SLOT(UpdatePlot()));
221 YLogAction->setCheckable(true);
222 NormAction = Menu->addAction("Normalize", this, SLOT(UpdatePlot()));
223 NormAction->setCheckable(true);
224 StyleAction = Menu->addAction("Draw dots", this, SLOT(UpdatePlot()));
225 StyleAction->setCheckable(true);
226 Menu->addAction("Zoom out", this, SLOT(MenuZoomOut()));
227 Menu->addAction("Single trace", this, SLOT(MenuSingleTrace()));
228 Menu->addSeparator();
229 Menu->addAction("Save as ASCII", this, SLOT(MenuSaveASCII()));
230 Menu->addAction("Save plot", this, SLOT(MenuSave()));
231 Menu->addAction("Print plot", this, SLOT(MenuPrint()));
232 Menu->addSeparator();
233 Menu->addAction("Paste service", this, SLOT(MenuPasteService()));
234
235 // DIM client
236 if (!DIMService.isEmpty()) AddService(DIMService, Index);
237}
238
239//
240// Destructor (items with parent widget are automatically deleted)
241//
242EddPlot::~EddPlot() {
243
244 for (int i=0; i<Items.size(); i++) {
245 Handler->Unsubscribe(Items[i].Name);
246 delete Items[i].Signal;
247 }
248 delete Grid;
249 delete Mutex;
250}
251
252//
253// Add history service to plot
254//
255void EddPlot::AddService(QString Name, int Index) {
256
257 // Lock before accessing Items list
258 QMutexLocker Locker(Mutex);
259
260 // Check if already subscribed to service
261 for (int i=0; i<Items.size(); i++) {
262 if (Name == Items[i].Name && Index == Items[i].Index) {
263 QMessageBox::warning(this, "Edd Message",Name+" ("+QString::number(Index)+") already present",QMessageBox::Ok);
264 return;
265 }
266 }
267
268 // Generate new curve and subscribe to service
269 struct PlotItem New;
270
271 New.Name = Name;
272 New.Signal = new QwtPlotCurve;
273 New.Signal->attach(this);
274 New.Signal->setTitle(Name+"("+QString::number(Index)+")");
275 New.Signal->setPen(QColor(LineColors[Items.size() % (sizeof(LineColors)/sizeof(Qt::GlobalColor))]));
276 New.SizeLimit = 5000;
277 New.Index = Index;
278
279 Items.append(New);
280 Handler->Subscribe(Name);
281}
282
283// Update widget (must happen in GUI thread)
284void EddPlot::Update(QString Name, int Time, QByteArray, QString Format, QString Text) {
285
286 // Lock before accessing Items list
287 QMutexLocker Locker(Mutex);
288
289 // Determine which plot item this call belongs to
290 int ItemNo;
291 for (ItemNo=0; ItemNo<Items.size(); ItemNo++) if (Items[ItemNo].Name == Name) {
292
293 // Check if service available
294 if (Time == -1) {
295 setStatusTip(QString("%1: unavailable").arg(Name));
296 return;
297 }
298
299 // If size limit reached, clear buffer
300 if (Items[ItemNo].x.size() > Items[ItemNo].SizeLimit) {
301 Items[ItemNo].x.clear();
302 Items[ItemNo].y.clear();
303 }
304
305 // If buffer empty, request new history buffer
306 if (Items[ItemNo].x.isEmpty()) {
307 int Time, Size;
308 void *Data;
309
310 class EvidenceHistory *HistClass = Handler->GetHistory(Items[ItemNo].Name);
311
312 if (HistClass->GetHistory()) {
313 double Smallest = DBL_MAX, Largest = DBL_MIN;
314 double Number=0;
315 while (HistClass->Next(Time, Size, Data)) {
316 switch (Format[0].toUpper().toAscii()) {
317 case 'I':
318 case 'L': Number = *((int *) Data + Items[ItemNo].Index); break;
319 case 'S': Number = *((short *) Data + Items[ItemNo].Index); break;
320 case 'F': Number = *((float *) Data + Items[ItemNo].Index); break;
321 case 'D': Number = *((double *) Data + Items[ItemNo].Index); break;
322 case 'X': Number = *((long long *) Data + Items[ItemNo].Index); break;
323 default: break;
324 }
325 Items[ItemNo].x.append(Time);
326 Items[ItemNo].y.append(Number);
327
328 if (Largest < Items[ItemNo].y.last()) Largest = Items[ItemNo].y.last();
329 if (Smallest > Items[ItemNo].y.last()) Smallest = Items[ItemNo].y.last();
330 }
331
332 Items[ItemNo].Smallest = Smallest;
333 Items[ItemNo].Largest = Largest;
334
335 // Local buffer always at least twice as large as history
336 if (Items[ItemNo].SizeLimit < 2*Items[ItemNo].x.size()) {
337 Items[ItemNo].SizeLimit = 2*Items[ItemNo].x.size();
338 }
339 }
340 }
341
342 // Append data
343 QString Txt = Text;
344 Txt = Txt.section(' ', Items[ItemNo].Index, Items[ItemNo].Index);
345 Items[ItemNo].x.append(Time);
346 Items[ItemNo].y.append(atof(Txt.toAscii().data()));
347
348 // Update largest and smallest value
349 if (Items[ItemNo].y.last() > Items[ItemNo].Largest) Items[ItemNo].Largest = Items[ItemNo].y.last();
350 if (Items[ItemNo].y.last() < Items[ItemNo].Smallest) Items[ItemNo].Smallest = Items[ItemNo].y.last();
351
352 // Update status tip
353 QDateTime Timex = QDateTime::fromTime_t(Time);
354 setStatusTip(QString("%1: Last update %2 Format '%3'").arg(Name).arg(Timex.toString()).arg(Format));
355 }
356
357 UpdatePlot();
358}
359
360//
361// Update all curves in plot
362//
363void EddPlot::UpdatePlot() {
364
365 static QwtSymbol Symbol, Sym1;
366 Symbol.setStyle(QwtSymbol::Ellipse);
367 Symbol.setSize(4);
368
369 if (!YLogAction->isChecked()) {
370 setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine);
371 }
372 else setAxisScaleEngine(QwtPlot::yLeft, new QwtLog10ScaleEngine);
373
374 // Lock before accessing Items list
375 QMutexLocker Locker(Mutex);
376
377 for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
378
379 if (StyleAction->isChecked()) Items[ItemNo].Signal->setSymbol(Symbol);
380 else Items[ItemNo].Signal->setSymbol(Sym1);
381
382 int DataPoints = Items[ItemNo].x.size();
383 if (DataPoints == 0) continue;
384
385 // Normalize y scale if requested
386 double *y = new double [DataPoints];
387 for (int i=0; i<DataPoints; i++) {
388 y[i] = Items[ItemNo].y[i];
389
390 if (NormAction->isChecked()) {
391 if (Items[ItemNo].Smallest != Items[ItemNo].Largest) {
392 y[i] = (y[i] - Items[ItemNo].Smallest)/(Items[ItemNo].Largest-Items[ItemNo].Smallest);
393 }
394 else y[i] = 1;
395 }
396 }
397
398 // Plot data
399 Items[ItemNo].Signal->setData(Items[ItemNo].x.data(), y, DataPoints);
400 Items[ItemNo].Signal->show();
401 Zoomer->setZoomBase(Items[ItemNo].Signal->boundingRect());
402
403 delete[] y;
404 }
405 replot();
406}
407
408//
409// Reset graph axes to autoscale when fully unzoomed
410//
411void EddPlot::HandleZoom(const QwtDoubleRect &) {
412
413 if(Zoomer->zoomRectIndex() == 0) {
414 setAxisAutoScale(QwtPlot::xBottom);
415 setAxisAutoScale(QwtPlot::yLeft);
416 }
417}
418
419//
420// Drag and drop methods
421//
422
423void EddPlot::dragEnterEvent(QDragEnterEvent *Event) {
424
425 if (Event->mimeData()->hasFormat("Edd/Service")) Event->acceptProposedAction();
426}
427
428void EddPlot::dropEvent(QDropEvent *Event) {
429
430 QByteArray D(Event->mimeData()->data("Edd/Service"));
431 AddService(D.left(D.lastIndexOf(' ')), D.right(D.size()-D.lastIndexOf(' ')).toInt());
432}
433
434// Opening context menu
435void EddPlot::contextMenuEvent(QContextMenuEvent *Event) {
436
437 Menu->exec(Event->globalPos());
438}
439
440// Drag&Drop method
441void EddPlot::LegendClicked(QwtPlotItem *Item) {
442
443
444 QString D(Item->title().text());
445 D.replace('(',' ').chop(1);
446
447 QDrag *Drag = new QDrag(this);
448 QMimeData *MimeData = new QMimeData;
449 QByteArray Data;
450 MimeData->setData("Edd/Service", Data.append(D));
451 Drag->setMimeData(MimeData);
452 Drag->exec();
453}
454
455
456// Zoom completely out
457void EddPlot::MenuZoomOut() {
458
459 Zoomer->zoom(0);
460 UpdatePlot();
461}
462
463// Remove all items except last
464void EddPlot::MenuSingleTrace() {
465
466 // Lock before accessing Items list
467 QMutexLocker Locker(Mutex);
468
469 while (Items.size() > 1) {
470 Handler->Unsubscribe(Items.last().Name);
471 delete Items.last().Signal;
472 Items.takeLast();
473 }
474 UpdatePlot();
475}
476
477// Save data of plot as test
478void EddPlot::MenuSaveASCII() {
479 QString Filename = QFileDialog::getSaveFileName(this,
480 "Filename", ".", "Text files (*.txt *.ascii *.asc);;All files (*)");
481 if (Filename.length() <= 0) return;
482
483 QFile File(Filename);
484 if (!File.open(QFile::WriteOnly | QIODevice::Text | QFile::Truncate)) {
485 QMessageBox::warning(this, "Edd Message","Could not open file for writing.",QMessageBox::Ok);
486 return;
487 }
488
489 // Lock before accessing Items list
490 QMutexLocker Locker(Mutex);
491 QTextStream Stream(&File);
492
493 // Write x and y data for all signals to file
494 for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
495 Stream << QString("# ") + Items[ItemNo].Name + ".hist" << endl;
496 for (int i=0; i<Items[ItemNo].Signal->dataSize(); i++) {
497 Stream << (int) Items[ItemNo].x.at(i) << " " << Items[ItemNo].Signal->y(i) << endl;
498 }
499 }
500}
501
502// Print plot
503void EddPlot::MenuPrint() {
504
505 QPrinter *Printer = new QPrinter;
506 QPrintDialog *PrintDialog = new QPrintDialog(Printer, this);
507 if (PrintDialog->exec() == QDialog::Accepted) {
508 QPainter Painter(Printer);
509 QPixmap Pixmap = QPixmap::grabWidget(this);
510 Painter.drawPixmap(0, 0, Pixmap);
511 }
512 delete Printer; delete PrintDialog;
513}
514
515// Save plot as image
516void EddPlot::MenuSave() {
517
518 QString Filename = QFileDialog::getSaveFileName(this,
519 "Filename of image", "/home/ogrimm/ddd", "Image files (*.bmp *.jpg *.png *.ppm *.tiff *.xbm *.xpm);;All files (*)");
520 if (Filename.length()>0) {
521 QPixmap Pixmap = QPixmap::grabWidget(this);
522 if(!Pixmap.save(Filename)) {
523 QMessageBox::warning(this, "Edd Message","Could not write image file.",QMessageBox::Ok);
524 remove(Filename.toAscii().data());
525 }
526 }
527}
528
529// Add new service by pasting name
530void EddPlot::MenuPasteService() {
531
532 const QMimeData *D = QApplication::clipboard()->mimeData();
533 if (!D->hasFormat("Edd/Service")) return;
534
535 QByteArray E(D->data("Edd/Service"));
536 AddService(E.left(E.lastIndexOf(' ')), E.right(E.size()-E.lastIndexOf(' ')).toInt());
537}
538
539
540//////////////////////////////////////
541// History text box for DIM service //
542//////////////////////////////////////
543
544//
545// Constructor
546//
547EddText::EddText(QString Name, bool Pure, QWidget *P):
548 QTextEdit(P), Name(Name), Pure(Pure) {
549
550 // Widget properties
551 setReadOnly(true);
552 setAttribute(Qt::WA_DeleteOnClose);
553 setAutoFillBackground(true);
554 document()->setMaximumBlockCount(1000);
555 Accumulate = true;
556
557 // Connect to DIM handler
558 if (connect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), SLOT(Update(QString, int, QByteArray, QString, QString))) == false) {
559 printf("Failed connection for %s\n", Name.toAscii().data());
560 }
561
562 if (!Pure) {
563 // Get history for this service
564 int Time, Size;
565 void *Data;
566
567 class EvidenceHistory *HistClass = Handler->GetHistory(Name);
568
569 if (HistClass->GetHistory()) {
570 while (HistClass->Next(Time, Size, Data)) {
571 moveCursor (QTextCursor::Start);
572 insertPlainText(QString("(")+QDateTime::fromTime_t(Time).toString()+") ");
573 insertPlainText(QString((char *) Data) + "\n");
574 }
575 }
576 }
577
578 // DIM client
579 Handler->Subscribe(Name);
580}
581
582// Destructor
583EddText::~EddText() {
584
585 Handler->Unsubscribe(Name);
586}
587
588
589// Update widget (must happen in GUI thread)
590void EddText::Update(QString Name, int Time, QByteArray, QString Format, QString Text) {
591
592 if (this->Name != Name) return;
593 QPalette Pal = palette();
594
595 // Check if service available
596 if (Time == -1) {
597 setStatusTip(QString("%1: unavailable").arg(Name));
598 Pal.setColor(QPalette::Base, Qt::lightGray);
599 setPalette(Pal);
600 return;
601 }
602
603 Pal.setColor(QPalette::Base, Qt::white);
604 setPalette(Pal);
605 QDateTime Timex = QDateTime::fromTime_t(Time);
606
607 // Clear display in case text should not accumulate
608 if (Accumulate == false) clear();
609
610 if (!Pure) {
611 moveCursor(QTextCursor::Start);
612 insertPlainText(QString("(")+Timex.toString()+QString(") "));
613 insertPlainText(Text + "\n");
614 }
615 else if (Format == "C") insertPlainText(Text);
616
617 // Update status tip
618 setStatusTip(QString("%1: Last update %2 Format '%3'").arg(Name).arg(Timex.toString()).arg(Format));
619}
620
621
622/////////////////////////////
623// Interface to Dim system //
624/////////////////////////////
625EddDim::EddDim() {
626
627 Mutex = new QMutex(QMutex::Recursive);
628
629 MinuteVolume = 0;
630 TotalVolume = 0;
631
632 QTimer *Timer = new QTimer(this);
633 Timer->connect(Timer, SIGNAL(timeout()), this, SLOT(UpdateStatistics()));
634 Timer->start(10000);
635
636 // Connect to DIM handler
637 if (connect(this, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), SLOT(Update(QString, int, QByteArray, QString, QString))) == false) {
638 printf("Failed connection in EddDim()\n");
639 }
640}
641
642EddDim::~EddDim() {
643
644 for (int i=0; i<HistoryList.size(); i++) delete HistoryList[i].HistClass;
645 delete Mutex;
646}
647
648// Subscribe to DIM service
649void EddDim::Subscribe(QString Name) {
650
651 // Lock before accessing list
652 QMutexLocker Locker(Mutex);
653
654 // If already subscribed to service, increase usage count
655 for (int i=0; i<ServiceList.size(); i++) if (ServiceList[i].Name == Name) {
656 ServiceList[i].Count++;
657 // If service already reveived, reemit for new subscriber
658 if (!ServiceList[i].ByteArray.isEmpty()) {
659 YEP(Name, ServiceList[i].TimeStamp, ServiceList[i].ByteArray, ServiceList[i].Format, ServiceList[i].Text);
660 }
661 return;
662 }
663
664 // Create new entry in service list
665 struct Item New;
666 New.Name = Name;
667 New.ByteArray = QByteArray();
668 New.DIMService = new DimStampedInfo(Name.toAscii().data(), INT_MAX, NO_LINK, this);
669 New.Count = 1;
670 ServiceList.append(New);
671
672 return;
673}
674
675// Unsubsribe from DIM service
676void EddDim::Unsubscribe(QString Name) {
677
678 // Lock before accessing list
679 QMutexLocker Locker(Mutex);
680
681 for (int i=0; i<ServiceList.size(); i++) if (ServiceList[i].Name == Name) {
682 ServiceList[i].Count--;
683 if (ServiceList[i].Count == 0) {
684 delete ServiceList[i].DIMService;
685 ServiceList.removeAt(i);
686 return;
687 }
688 }
689}
690
691// Get history buffer
692class EvidenceHistory *EddDim::GetHistory(QString Name) {
693
694 // If history already available, return this
695 for (int i=0; i<HistoryList.size(); i++) if (HistoryList[i].Name == Name) {
696 HistoryList[i].Count++;
697 return HistoryList[i].HistClass;
698 }
699
700 struct HistItem New;
701 New.Name = Name;
702 New.HistClass = new EvidenceHistory(Name.toStdString());
703 New.Count = 1;
704 HistoryList.append(New);
705
706 return New.HistClass;
707
708}
709
710// Update throughput statistics
711void EddDim::UpdateStatistics() {
712
713 // Lock before accessing internal variables
714 QMutexLocker Locker(Mutex);
715
716 float Rate = MinuteVolume/1024.0 * 6;
717 float Total = TotalVolume/1024.0/1024.0;
718
719 YEP("Edd/Rate_kBMin", time(NULL), QByteArray::number(Rate), "F", QString::number(Rate));
720 YEP("Edd/Total_MB", time(NULL), QByteArray::number(Total), "F", QString::number(Total));
721 MinuteVolume = 0;
722}
723
724// Store service information for usage by Subscribe() and update statistics
725void EddDim::Update(QString Name, int Time, QByteArray Data, QString Format, QString Text) {
726
727 // Lock before accessing list
728 QMutexLocker Locker(Mutex);
729
730 for (int i=0; i<ServiceList.size(); i++) if (ServiceList[i].Name == Name) {
731 ServiceList[i].TimeStamp = Time;
732 ServiceList[i].ByteArray = Data;
733 ServiceList[i].Format = Format;
734 ServiceList[i].Text = Text;
735 }
736
737 // Update statistics only for Dim services
738 if (!Name.startsWith("Edd/")) {
739 TotalVolume += Data.size();
740 MinuteVolume += Data.size();
741 }
742}
743
744// Handling of DIM service update
745void EddDim::infoHandler() {
746
747 if (!EvidenceServer::ServiceOK(getInfo())) YEP(getInfo()->getName(), -1);
748 else {
749 char *Text = EvidenceServer::ToString(getInfo());
750 YEP(getInfo()->getName(), getInfo()->getTimestamp(), QByteArray((char *) getInfo()->getData(), getInfo()->getSize()), getInfo()->getFormat(), Text);
751 free(Text);
752 }
753}
754
755
756//
757// Main GUI (all widgets have ultimately Central as parent)
758//
759GUI::GUI() {
760
761 Handler = new EddDim();
762
763 // Set features of main window
764 Central = new QWidget(this);
765 setCentralWidget(Central);
766 setStatusBar(new QStatusBar(this));
767 setGeometry(100, 100, 800, 650);
768 setWindowTitle("Edd - Evidence Data Display");
769
770 EddLineDisplay *Value;
771 EddPlot *Graph;
772 EddText *Textout;
773 QString Text;
774
775 // TextBox for value
776
777
778 // Clock (updated every second)
779 //Clock = new QwtAnalogClock(Central);
780 //Clock->scaleDraw()->setPenWidth(3);
781 //Clock->setLineWidth(6);
782 //Clock->setFrameShadow(QwtDial::Sunken);
783 //Clock->setGeometry(0,0,10,10);
784 //Clock->setTime();
785
786 //QTimer *Timer = new QTimer(Clock);
787 //Timer->connect(Timer, SIGNAL(timeout()), Clock, SLOT(setCurrentTime()));
788 //Timer->start(1000);
789
790 MainWidget = new QWidget();
791 MainLayout = new QGridLayout(MainWidget);
792
793 Value = new EddLineDisplay("Alarm/Status");
794 Value->setMaximumWidth(200);
795 MainLayout->addWidget(Value, 0, 0, 1, 2);
796
797 Value = new EddLineDisplay("Alarm/MasterAlarm");
798 MainLayout->addWidget(Value, 0, 1, 1, 1);
799
800 Textout = new EddText("Alarm/Summary", true);
801 Textout->Accumulate = false;
802 Textout->setMaximumWidth(200);
803 Textout->setMaximumHeight(150);
804 MainLayout->addWidget(Textout, 1, 0, 1, 2);
805
806 Value = new EddLineDisplay("DColl/Status");
807 Value->setMaximumWidth(200);
808 MainLayout->addWidget(Value, 3, 0, 1, 2);
809
810 Value = new EddLineDisplay("DColl/DataSizekB");
811 MainLayout->addWidget(Value, 4, 0, 1, 1);
812
813 Value = new EddLineDisplay("DColl/LogSizekB");
814 MainLayout->addWidget(Value, 4, 1, 1, 1);
815
816 Value = new EddLineDisplay("DColl/CurrentFile");
817 Value->setMaximumWidth(400);
818 MainLayout->addWidget(Value, 5, 0, 1, 3);
819
820 Value = new EddLineDisplay("Config/Status");
821 Value->setMaximumWidth(200);
822 MainLayout->addWidget(Value, 6, 0, 1, 2);
823
824 Value = new EddLineDisplay("Config/ModifyTime");
825 Value->setMaximumWidth(200);
826 Value->ShowAsTime = true;
827 MainLayout->addWidget(Value, 7, 0, 1, 1);
828
829 QPushButton *Button = new QPushButton();
830 Button->setText("Start DIM browser");
831 connect(Button, SIGNAL(released()), SLOT(StartDIMBrowser()));
832 MainLayout->addWidget(Button, 7, 1, 1, 1);
833
834 Value = new EddLineDisplay("Edd/Rate_kBMin");
835 MainLayout->addWidget(Value, 8, 0, 1, 1);
836 Value = new EddLineDisplay("Edd/Total_MB");
837 MainLayout->addWidget(Value, 8, 1, 1, 1);
838
839 // Layout of all widgets
840 //MainLayout->addWidget(Value, 0, 0, 1, 1);
841 //MainLayout->addWidget(Clock, 0, 1, 1, 1);
842 //MainLayout->addWidget(Graph, 1, 0, 1, 2);
843 //MainLayout->setColumnStretch(1, 10);
844
845 //MainLayout->addWidget(Clock, 0, 1, 1, 1);
846 //MainLayout->addWidget(Graph1, 2, 0, 1, 2);
847
848 // Feedback page
849 FeedbackWidget = new QWidget();
850 FeedbackLayout = new QGridLayout(FeedbackWidget);
851 Graph = new EddPlot();
852 for (int i=0; i<36; i++) {
853 Value = new EddLineDisplay("Feedback/Average", i);
854 FeedbackLayout->addWidget(Value, i%9+1, 0+i/9, 1, 1);
855 Graph->AddService("Feedback/Average", i);
856 }
857 FeedbackLayout->addWidget(Graph, 0, 4, 11, 3);
858
859 //Graph = new EddPlot();
860 //for (int i=0; i<36; i++) {
861 //Text = Text.sprintf("Feedback/Sigma/ID%.2d/%.2d-%.3d",i/16, (i%16)/8, i%8);
862 //Graph->AddService(Text);
863 //}
864 //FeedbackLayout->addWidget(Graph, 10, 0, 10, 3);
865
866 Value = new EddLineDisplay("Feedback/Status");
867 Value->setMaximumWidth(200);
868 FeedbackLayout->addWidget(Value, 0, 0, 1, 3);
869 Value = new EddLineDisplay("Feedback/Count");
870 FeedbackLayout->addWidget(Value, 0, 3, 1, 1);
871
872 QWidget *Value1 = new QPushButton("Details");
873 FeedbackLayout->addWidget(Value1, 10, 0, 1, 1);
874 connect(Value1, SIGNAL(pressed()), this, SLOT(FeedbackDetails()));
875
876
877 // Bias voltage page
878 BiasWidget = new QWidget();
879 BiasLayout = new QGridLayout(BiasWidget);
880 Graph = new EddPlot();
881 for (int i=0; i<18; i++) {
882 Value = new EddLineDisplay("Bias/VOLT/ID00", i);
883 BiasLayout->addWidget(Value, i%9+1, 0+i/9, 1, 1);
884 Graph->AddService("Bias/VOLT/ID00", i);
885
886 Value = new EddLineDisplay("Bias/VOLT/ID00", i+32);
887 BiasLayout->addWidget(Value, i%9+1, 2+i/9, 1, 1);
888 Graph->AddService("Bias/VOLT/ID00",i+32);
889 }
890
891 BiasLayout->addWidget(Graph, 0, 4, 12, 3);
892 Value = new EddLineDisplay("Bias/Status");
893 Value->setMaximumWidth(200);
894 BiasLayout->addWidget(Value, 0, 0, 1, 3);
895
896 Textout = new EddText("Bias/Textout", true);
897 Textout->setFixedWidth(400);
898 BiasLayout->addWidget(Textout, 10, 0, 4, 4);
899
900 // Environment page
901 EnvironmentWidget = new QWidget();
902 EnvironmentLayout = new QGridLayout(EnvironmentWidget);
903 Value = new EddLineDisplay("ARDUINO/Status");
904 Value->setMaximumWidth(200);
905 EnvironmentLayout->addWidget(Value, 0, 0, 1, 3);
906
907 Graph = new EddPlot();
908 for (int i=0; i<10; i++) {
909 Value = new EddLineDisplay("ARDUINO/Data", i);
910 EnvironmentLayout->addWidget(Value, i%5+1, i/5, 1, 1);
911 Graph->AddService("ARDUINO/Data", i);
912 }
913 EnvironmentLayout->addWidget(Graph, 0, 3, 6, 4);
914
915 Value = new EddLineDisplay("SQM/NSB");
916 EnvironmentLayout->addWidget(Value, 6, 0, 1, 1);
917
918 // Tab widget
919 TabWidget = new QTabWidget(Central);
920 TabWidget->addTab(BiasWidget, "&Bias");
921 TabWidget->addTab(FeedbackWidget, "&Feedback");
922 TabWidget->addTab(EnvironmentWidget, "&Environment");
923 TabWidget->addTab(MainWidget, "Evidence");
924
925 // Menu bar
926 QMenu* Menu = menuBar()->addMenu("&Menu");
927 Menu->addAction("New history plot", this, SLOT(MenuNewHistory()));
928 Menu->addSeparator();
929 Menu->addAction("About", this, SLOT(MenuAbout()));
930 Menu->addSeparator();
931 QAction* QuitAction = Menu->addAction("Quit", qApp, SLOT(quit()));
932 QuitAction->setShortcut(Qt::CTRL + Qt::Key_Q);
933
934 // Show main window
935 show();
936}
937
938GUI::~GUI() {
939 delete Central;
940}
941
942
943void GUI::MenuAbout() {
944 QString Rev(SVN_REVISION);
945 Rev.remove(0,1).chop(2);
946
947 QMessageBox::about(this, "About Edd","Evidence Data Display\n\n"
948 "Written by Oliver Grimm, IPP, ETH Zurich\n"
949 "This version compiled "__DATE__" ("+Rev+")\n\n"
950 "Graphical user interface implemented with Qt and Qwt.\n"
951 "Evidence control system based on DIM (http://dim.web.cern.ch).\n\n"
952 "Comments to oliver.grimm@phys.ethz.ch.");
953}
954
955// Open request for new history plot
956void GUI::MenuNewHistory() {
957
958 QStringList List;
959 char *Name, *Format;
960 int Type;
961 bool OK;
962
963 // Find all services that are not history services and sort
964 getServices("*");
965 while ((Type = getNextService(Name, Format)) != 0) {
966 if (Type==DimSERVICE && strstr(Name, ".hist")==NULL) List.append(Name);
967 }
968 List.sort();
969
970 // Open dialog and open history window
971 QString Result = QInputDialog::getItem(this, "Edd Request",
972 "Enter DIM service name", List, 0, true, &OK);
973 if (OK && !Result.isEmpty()) {
974 Result = Result.trimmed();
975 if (Result.endsWith(".hist")) Result.chop(5);
976 QWidget *Hist = OpenHistory(Result.toAscii().data(), 0);
977 if (Hist != NULL) Hist->show();
978 }
979}
980
981// Feedback details page
982void GUI::FeedbackDetails() {
983
984 QWidget *Value;
985 QWidget *Widget = new QWidget();
986 QGridLayout *Layout = new QGridLayout(Widget);
987 EddPlot *Graph = new EddPlot();
988 for (int i=0; i<36; i++) {
989 Value = new EddLineDisplay("Feedback/Sigma", i);
990 Layout->addWidget(Value, i%9+1, 0+i/9, 1, 1);
991 Graph->AddService("Feedback/Sigma", i);
992 }
993 Layout->addWidget(Graph, 0, 4, 11, 3);
994
995 Widget->show();
996}
997
998// Start DIM Browser
999void GUI::StartDIMBrowser() {
1000
1001 QProcess::startDetached("did", QStringList(), QString(getenv("DIMDIR"))+"/linux/");
1002}
1003
1004
1005//---------------------------------------------------------------------
1006//************************ All functions ****************************
1007//-------------------------------------------------------------------
1008
1009// Quit application when clicking close button on window
1010void GUI::closeEvent(QCloseEvent *) {
1011 qApp->quit();
1012}
1013
1014
1015//---------------------------------------------------------------------
1016//**************************** Main program ***************************
1017//---------------------------------------------------------------------
1018
1019int main(int argc, char *argv[]) {
1020
1021 QApplication app(argc, argv);
1022 GUI MainWindow;
1023
1024 return app.exec();
1025}
Note: See TracBrowser for help on using the repository browser.