source: Evidence/Edd/Edd.cc@ 173

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