source: fact/Evidence/GUI.cc@ 19715

Last change on this file since 19715 was 18023, checked in by daqct3, 10 years ago
Added copy/paste for time scale in Evidence GUI history plots
File size: 39.7 KB
Line 
1/* ============================================================
2
3Edd - Evidence Data Display
4
5Qt-based graphical user interface for the Evidence contron system
6
7EddLineDisplay changes its background colour in case it display
8a DIM status service
9
10April 2010, February 2012, Oliver Grimm
11
12============================================================ */
13
14#include "GUI.h"
15
16class EddDim *Handler;
17
18//
19// History chooser function (opens plot for numeric data, TextHist for all other)
20//
21void OpenHistory(char *Service, int FromIndex, int ToIndex) {
22
23 QString Format;
24 DimBrowser Browser;
25 struct EddDim::HistItem Hist = Handler->GetHistory(Service);
26 char *Name, *Fmt;
27
28 // Check if history service available
29 if (strcmp(Service, "Edd/Rate_kBSec") == 0) Format = "F";
30 else Format = Hist.Format;
31
32 // If service currently available, take its format
33 Browser.getServices(Service);
34 if (Browser.getNextService(Name, Fmt) != 0) Format = QString(Fmt);
35
36 // Open new window
37 QMainWindow *M = new QMainWindow;
38 M->setCentralWidget(new QWidget(M));
39 M->setStatusBar(new QStatusBar(M));
40 M->setAttribute(Qt::WA_DeleteOnClose);
41 M->setWindowTitle("Edd History");
42
43 QGridLayout *Layout = new QGridLayout(M->centralWidget());
44
45 // If service ends with 'C' make text display, otherwise numeric plot
46 if (Format.endsWith('C', Qt::CaseInsensitive)) Layout->addWidget(new EddText(Service), 0, 0);
47 else {
48 EddPlot *W = new EddPlot(Service, FromIndex);
49
50 // If service is array and no index given, try to find out how many indices exist and add all
51 if (FromIndex == -1 && Format.size() == 1) {
52 DimCurrentInfo X(Service, (char *) NULL, 0);
53
54 FromIndex = 0;
55 ToIndex = EvidenceServer::Tokenize(EvidenceServer::ToString(Format.toAscii().data(), X.getData(), X.getSize()), " ").size() - 1;
56 }
57
58 // Add all requested indices
59 for (int i=FromIndex+1; i<=ToIndex; i++) W->AddService(Service,i);
60
61 Layout->addWidget(W, 0, 0, 1, 5);
62 }
63
64 M->resize(400,450);
65 M->show();
66}
67
68//
69// Set status tip (returns true if service was available)
70//
71bool SetStatus(QWidget *W, QString Name, int Time, QString Format, int Index) {
72
73 QString Status;
74
75 if (Index != -1) Name = Name + ":" + QString::number(Index);
76
77 if (Time == -1) Status = QString("%1: unavailable").arg(Name);
78 else Status = QString("%1: Last update %2 Format '%3'").arg(Name).arg(QDateTime::fromTime_t(Time).toString()).arg(Format);
79
80 W->setStatusTip(Status);
81
82 return(Time != -1);
83}
84
85
86//////////////////////////////////////////
87// Text display for arbitary DIM service//
88//////////////////////////////////////////
89
90EddLineDisplay::EddLineDisplay(QString Name, int Index, QWidget *P):
91 QLineEdit(P), ServiceName(Name), Index(Index) {
92
93 LastHist = NULL;
94
95 // Widget properties
96 setReadOnly(true);
97 setMaximumWidth(100);
98 ShowAsTime = false;
99 setFrame(false);
100 setAttribute(Qt::WA_DeleteOnClose);
101 QPalette Pal = palette();
102 Pal.setColor(QPalette::Base, Qt::lightGray);
103 setPalette(Pal);
104
105 // Context menu
106 Menu = new QMenu(this);
107 Menu->addAction("Open new history", this, SLOT(MenuOpenHistory()));
108 Menu->addAction("Copy service", this, SLOT(MenuCopyService()));
109 Menu->addAction("Copy data", this, SLOT(MenuCopyData()));
110
111 // Subscribe to service
112 Handler->Subscribe(Name, this, Index);
113}
114
115// Destructor
116EddLineDisplay::~EddLineDisplay() {
117
118 Handler->Unsubscribe(ServiceName, this, Index);
119}
120
121// Update widget
122void EddLineDisplay::Update(const QString &, int Time, const QByteArray &, const QString &Format, const QString &Text, int) {
123
124 // Check if service available
125 QPalette Pal = palette();
126 if (!SetStatus(this, ServiceName, Time, Format, Index)) {
127 setText("n/a");
128 Pal.setColor(QPalette::Base, Qt::lightGray);
129 setPalette(Pal);
130 return;
131 }
132 else Pal.setColor(QPalette::Base, Qt::white);
133
134 // Message service backgound colour determined by severity
135 if (ServiceName.endsWith("/Message")) {
136 switch (Text.section(' ', 0, 0).toInt()) {
137 case 0: Pal.setColor(QPalette::Base, Qt::white); break;
138 case 1: Pal.setColor(QPalette::Base, Qt::yellow); break;
139 case 2: Pal.setColor(QPalette::Base, Qt::red); break;
140 case 3: Pal.setColor(QPalette::Base, Qt::red); break;
141 default: break;
142 }
143 setText(Text.section(' ', 1));
144 }
145 else if (!ShowAsTime) {
146 bool OK;
147 double Num = Text.toDouble(&OK);
148
149 if (OK) setText(QString::number(Num, 'g', 4));
150 else setText(Text);
151 }
152 else setText(QDateTime::fromTime_t(Text.toInt()).toString());
153
154 setCursorPosition(0);
155 setPalette(Pal);
156}
157
158// Open plot if mouse release within widget
159void EddLineDisplay::mouseReleaseEvent(QMouseEvent *Event) {
160
161 if (Event->button()!=Qt::LeftButton || !contentsRect().contains(Event->pos())) return;
162
163 // Check if last history plot still open, then raise
164 foreach (QWidget *Widget, QApplication::allWidgets()) {
165 if (Widget == LastHist) {
166 Widget->activateWindow();
167 Widget->raise();
168 return;
169 }
170 }
171
172 // If not, open new plot
173 MenuOpenHistory();
174}
175
176// Handling of mouse press event: Register start position for drag
177void EddLineDisplay::mousePressEvent(QMouseEvent *Event) {
178
179 if (Event->button() == Qt::LeftButton) dragStart = Event->pos();
180}
181
182// Handling of dragging (Drag and MimeData will be deleted by Qt)
183void EddLineDisplay::mouseMoveEvent(QMouseEvent *Event) {
184
185 if ((Event->buttons() & Qt::LeftButton) == 0) return;
186 if ((Event->pos()-dragStart).manhattanLength() < QApplication::startDragDistance()) return;
187
188 QDrag *Drag = new QDrag(this);
189 QMimeData *MimeData = new QMimeData;
190 QByteArray Data;
191 MimeData->setData("Edd/Service", Data.append(ServiceName + " " + QString::number(Index)));
192 Drag->setMimeData(MimeData);
193 Drag->exec();
194}
195
196//
197// Opening context menu
198//
199void EddLineDisplay::contextMenuEvent(QContextMenuEvent *Event) {
200
201 Menu->exec(Event->globalPos());
202}
203
204// Menu: Open history plot
205void EddLineDisplay::MenuOpenHistory() {
206
207 OpenHistory(ServiceName.toAscii().data(), Index);
208}
209
210// Menu: Copy service name
211void EddLineDisplay::MenuCopyService() {
212
213 QMimeData *MimeData = new QMimeData;
214 QByteArray Data;
215 MimeData->setData("Edd/Service", Data.append(ServiceName + " " + QString::number(Index)));
216 QApplication::clipboard()->setMimeData(MimeData);
217}
218
219// Menu: Copy data
220void EddLineDisplay::MenuCopyData() {
221
222 QApplication::clipboard()->setText(text());
223}
224
225
226//////////////////////////////////////////
227// Sending string command to DIM server //
228//////////////////////////////////////////
229
230EddCommand::EddCommand(QString Name, QWidget *P): QLineEdit(P), Name(Name) {
231
232 setToolTip("Send command "+Name);
233 setStatusTip(QString("Command %1").arg(Name));
234
235 connect(this, SIGNAL(returnPressed()), SLOT(SendCommand()));
236}
237
238// Get command format and returns NULL (not empty) QString if not available
239QString EddCommand::GetFormat() {
240
241 char *ItsName, *Format;
242 DimBrowser Browser;
243
244 Browser.getServices(Name.toAscii().data());
245
246 if (Browser.getNextService(ItsName, Format) != DimCOMMAND) return QString();
247 else return Format;
248}
249
250// Send command
251void EddCommand::SendCommand() {
252
253 QByteArray Data;
254
255 // Ignore empty commands
256 if (text().isEmpty()) return;
257
258 // Check if command available and retrieve format
259 QString Format = GetFormat();
260
261 if (Format.isNull()) {
262 QMessageBox::warning(this, "Edd Message", "Command " + Name + " currently unavailable", QMessageBox::Ok);
263 return;
264 }
265
266 // Command has no argument
267 if (Format.isEmpty()) {
268 DimClient::sendCommand(Name.toAscii().data(), NULL, 0);
269 return;
270 }
271
272 // Command has string as argument
273 if (Format == "C") {
274 DimClient::sendCommand(Name.toAscii().data(), text().toAscii().data());
275 return;
276 }
277
278 // Command has structure format, interpret data as hex
279 if (Format.size() > 1) {
280 Data = QByteArray::fromHex(text().toAscii());
281 }
282 else {
283 QDataStream Stream(&Data, QIODevice::WriteOnly);
284 std::vector<std::string> Values = EvidenceServer::Tokenize(text().toStdString());
285
286 for (unsigned int i=0; i<Values.size(); i++) {
287 switch (Format[0].toAscii()) {
288 case 'I':
289 case 'L': Stream << (int) atoi(Values[i].c_str()); break;
290 case 'S': Stream << (short) atoi(Values[i].c_str()); break;
291 case 'F': Stream << (float) atof(Values[i].c_str()); break;
292 case 'D': Stream << (double) atof(Values[i].c_str()); break;
293 case 'X': Stream << (long long) (atof(Values[i].c_str())); break;
294 }
295 }
296 }
297
298 DimClient::sendCommand(Name.toAscii().data(), Data.data(), Data.size());
299 clear();
300}
301
302// Opening context menu
303void EddCommand::contextMenuEvent(QContextMenuEvent *Event) {
304
305 QMenu *Menu = createStandardContextMenu();
306 Menu->addSeparator();
307 Menu->addAction("Command help", this, SLOT(MenuCommandHelp()));
308 Menu->exec(Event->globalPos());
309 delete Menu;
310}
311
312// Help text
313void EddCommand::MenuCommandHelp() {
314
315 QString Format = GetFormat();
316 QString Text = Name;
317
318 if (Format.isNull()) {
319 Text += " is currently unavailable";
320 }
321 else Text += " has format '"+Format+"'";
322
323 Text += "\n\nCommand arguments will be transmitted as string for format 'C',\n"
324 "interpreted as an array of numbers for all other single character formats\n"
325 "and as bytes in hexadecimal encoding for all more complex formats";
326
327 QMessageBox::about(this, "Edd - Command help", Text);
328}
329
330//////////////////////////////////
331// History plot for DIM service //
332//////////////////////////////////
333
334EddPlot::EddPlot(QString Service, int Index, QWidget *P): EddBasePlot(P) {
335
336 // Widget properties
337 setAcceptDrops(true);
338
339 // Horizontal time scale
340 setAxisScaleDraw(QwtPlot::xBottom, new EddDateScale());
341
342 // Additonal context menu items
343 QAction* Action = Menu->addAction("Paste", this, SLOT(MenuPaste()));
344 Menu->removeAction(Action);
345 Menu->insertAction(Menu->actions().value(1), Action);
346 Action = Menu->addAction("Copy time scale", this, SLOT(MenuCopyTimeScale()));
347 Menu->insertAction(Menu->actions().value(1), Action);
348
349 Action = Menu->addAction("Show last hour", this, SLOT(MenuShowLastHour()));
350 Menu->insertAction(Menu->actions().value(8), Action);
351 Action = Menu->addAction("Show last day", this, SLOT(MenuShowLastDay()));
352 Menu->insertAction(Menu->actions().value(8), Action);
353
354 Action = Menu->addAction("All as text", this, SLOT(MenuAllAsText()));
355 Menu->insertAction(Menu->actions().value(10), Action);
356
357 // Set timer update plot after new data added
358 SingleShot = new QTimer(this);
359 connect(SingleShot, SIGNAL(timeout()), this, SLOT(UpdatePlot()));
360 SingleShot->setSingleShot(true);
361
362 // DIM client
363 if (!Service.isEmpty()) AddService(Service, Index);
364}
365
366// Destructor (items with parent widget are automatically deleted)
367EddPlot::~EddPlot() {
368
369 while (!List.isEmpty()) RemoveService(List.last().Curve);
370}
371
372// Add service to plot
373void EddPlot::AddService(QString Name, int Index) {
374
375 if (Index < 0) Index = 0;
376
377 // Check if curve already present on plot
378 for (int i=0; i<List.size(); i++) {
379 if (Name == List[i].Name && Index == List[i].Index) {
380 QMessageBox::warning(this, "Edd Message",Name+" ("+QString::number(Index)+") already present",QMessageBox::Ok);
381 return;
382 }
383 }
384
385 // Generate new curve and subscribe to service
386 struct ItemDetails N;
387
388 N.Name = Name;
389 N.Index = Index;
390 N.Curve = NewCurve(Name+":"+QString::number(Index));
391 List.append(N);
392
393 // Get history
394 struct EddDim::HistItem Hist = Handler->GetHistory(Name);
395
396 for (int i=0; i<Hist.DataText.size(); i++) {
397 if (Hist.DataText[i].second.size() > N.Index) {
398 AddPoint(List.size()-1, double(Hist.DataText[i].first)*1000, Hist.DataText[i].second.at(N.Index).toFloat());
399 }
400 }
401
402 // Subscribe to service and start updating plot after 100 ms (to allow addition of other curves)
403 Handler->Subscribe(Name, this, Index);
404 SingleShot->start(100);
405}
406
407// Remove subscription
408void EddPlot::RemoveService(QwtPlotCurve *Curve) {
409
410 for (int i=0; i<List.size(); i++) if (List[i].Curve == Curve) {
411 Handler->Unsubscribe(List[i].Name, this, List[i].Index);
412 List.removeAt(i);
413 DeleteCurve(Curve);
414 break;
415 }
416}
417
418// Update widget (must happen in GUI thread)
419void EddPlot::Update(const QString &Name, int Time, const QByteArray &, const QString &Format, const QString &Text, int Index) {
420
421 for (int ItemNo=0; ItemNo<List.size(); ItemNo++) if (List[ItemNo].Name==Name && List[ItemNo].Index==Index) {
422
423 // Append data if service available
424 if (SetStatus(this, Name, Time, Format)) AddPoint(ItemNo, (double) Time*1000, atof(Text.toAscii().data()));
425 NewData = true;
426 }
427}
428
429// Drag and drop methods
430void EddPlot::dragEnterEvent(QDragEnterEvent *Event) {
431
432 if (Event->mimeData()->hasFormat("Edd/Service")) Event->acceptProposedAction();
433}
434
435void EddPlot::dropEvent(QDropEvent *Event) {
436
437 QByteArray D(Event->mimeData()->data("Edd/Service"));
438 AddService(D.left(D.lastIndexOf(' ')), D.right(D.size()-D.lastIndexOf(' ')).toInt());
439}
440
441// Menu: Copy time scale
442void EddPlot::MenuCopyTimeScale() {
443
444 double Min = axisInterval(QwtPlot::xBottom).minValue(), Max = axisInterval(QwtPlot::xBottom).maxValue();
445 QMimeData *MimeData = new QMimeData;
446 QByteArray Data;
447
448 Data.append((char *) &Min, sizeof(double));
449 Data.append((char *) &Max, sizeof(double));
450 MimeData->setData("Edd/TimeScale", Data);
451
452 QApplication::clipboard()->setMimeData(MimeData);
453}
454
455// Add new service by pasting name or paste time scale
456void EddPlot::MenuPaste() {
457
458 const QMimeData *D = QApplication::clipboard()->mimeData();
459
460 if (D->data("Edd/TimeScale").size() == 2*sizeof(double)) {
461 setAxisScale(QwtPlot::xBottom, *((double *) D->data("Edd/TimeScale").data()), *((double *) D->data("Edd/TimeScale").data() + 1));
462 replot();
463 return;
464 }
465
466 if (D->hasFormat("Edd/Service")) {
467 QByteArray E(D->data("Edd/Service"));
468 AddService(E.left(E.lastIndexOf(' ')), E.right(E.size()-E.lastIndexOf(' ')).toInt());
469 }
470}
471
472// Show last hour/last day
473void EddPlot::MenuShowLastHour() {
474
475 setAxisScale(QwtPlot::xBottom, (time(NULL)-60*60)*1000, (time(NULL)+60)*1000);
476 replot();
477}
478
479void EddPlot::MenuShowLastDay() {
480
481 setAxisScale(QwtPlot::xBottom, (time(NULL)-24*3600)*1000, (time(NULL)+3600)*1000);
482 replot();
483}
484
485void EddPlot::MenuAllAsText() {
486
487 QMainWindow *M = new QMainWindow;
488 M->setCentralWidget(new QWidget(M));
489 M->setStatusBar(new QStatusBar(M));
490 M->setAttribute(Qt::WA_DeleteOnClose);
491 M->setWindowTitle("Edd Services");
492
493 QGridLayout *Layout = new QGridLayout(M->centralWidget());
494
495 for (int i=0; i<List.size(); i++) {
496 Layout->addWidget(new EddLineDisplay(List[i].Name, List[i].Index), i/10, i%10);
497 }
498
499 M->resize(400,450);
500 M->show();
501}
502
503//
504// Reimplementation of QwtDateScaleDraw::label()
505//
506QwtText EddDateScale::label(double Value) const {
507
508 QString Format;
509
510 if (scaleDiv().range() > 30*24*60*60*1000.0) Format = "d-MMM\n yyyy";
511 else if (scaleDiv().range() > 1*24*60*60*1000.0) Format = " h 'h'\nd-MMM";
512 else if (scaleDiv().range() > 60*60*1000.0) Format = "hh:mm";
513 else Format = "hh:mm:ss";
514
515 return QwtDate::toString(toDateTime(Value), Format, QwtDate::FirstThursday);
516}
517
518//////////////////
519// General plot //
520//////////////////
521
522// Constructor (all slots called in GUI thread, therefore no mutex necessary)
523EddBasePlot::EddBasePlot(QWidget *P): QwtPlot(P) {
524
525 // Widget properties
526 setAttribute(Qt::WA_DeleteOnClose);
527 setAutoReplot(false);
528 setCanvasBackground(EddPlotBackgroundColor);
529 NewData = false;
530
531 // Plot navigation
532 Zoomer = new QwtPlotZoomer(QwtPlot::xBottom,QwtPlot::yLeft,canvas());
533 Zoomer->setTrackerMode(QwtPicker::AlwaysOff);
534 connect(Zoomer, SIGNAL(zoomed(const QRectF &)), this, SLOT(HandleZoom(const QRectF &)));
535 connect(Zoomer, SIGNAL(zoomed(const QRectF &)), this, SLOT(ReDoStats()));
536
537 Magnifier = new QwtPlotMagnifier(canvas());
538 Magnifier->setMouseButton(Qt::NoButton, Qt::NoModifier);
539 Magnifier->setZoomInKey(Qt::Key_M, Qt::NoModifier);
540 Magnifier->setZoomOutKey(Qt::Key_M, Qt::ShiftModifier);
541
542 Panner = new QwtPlotPanner(canvas());
543 Panner->setMouseButton(Qt::LeftButton, Qt::ShiftModifier);
544 connect(Panner, SIGNAL(panned(int, int)), this, SLOT(ReDoStats()));
545
546 Picker = new QwtPicker(QwtPicker::RectRubberBand, QwtPicker::AlwaysOff, this);
547 Picker->setStateMachine(new QwtPickerDragRectMachine);
548 connect(Picker, SIGNAL(selected(const QPolygon &)), SLOT(MouseSelection(const QPolygon &)));
549
550 // Grid and legend
551 Grid = new QwtPlotGrid;
552 Grid->setMajorPen(QPen(Qt::gray, 0, Qt::DotLine));
553 Grid->attach(this);
554
555 Legend = new EddLegend();
556 insertLegend(Legend, QwtPlot::TopLegend, 0.3);
557
558 // Marker for statistics text
559 Stats = new QwtPlotMarker();
560 Stats->setLineStyle(QwtPlotMarker::NoLine);
561 Stats->setLabelAlignment(Qt::AlignLeft | Qt::AlignBottom);
562 Stats->hide();
563 Stats->attach(this);
564
565 // Context menu
566 Menu = new QMenu(this);
567 StripAction = Menu->addAction("Stripchart", this, SLOT(UpdatePlot()));
568 StripAction->setCheckable(true);
569 Menu->addSeparator();
570 YLogAction = Menu->addAction("y scale log", this, SLOT(UpdatePlot()));
571 YLogAction->setCheckable(true);
572 NormAction = Menu->addAction("Normalize", this, SLOT(UpdatePlot()));
573 NormAction->setCheckable(true);
574 StyleAction = Menu->addAction("Draw dots", this, SLOT(UpdatePlot()));
575 StyleAction->setCheckable(true);
576 StatisticsAction = Menu->addAction("Statistics", this, SLOT(ReDoStats()));
577 StatisticsAction->setCheckable(true);
578 Menu->addAction("Zoom out", this, SLOT(MenuZoomOut()));
579 Menu->addSeparator();
580 Menu->addAction("Set update rate", this, SLOT(MenuSetUpdateRate()));
581 Menu->addSeparator();
582 Menu->addAction("Save as ASCII", this, SLOT(MenuSaveASCII()));
583 Menu->addAction("Save plot", this, SLOT(MenuSave()));
584 Menu->addAction("Print plot", this, SLOT(MenuPrint()));
585 Menu->addAction("Plot help", this, SLOT(MenuPlotHelp()));
586
587 // Set timer to regularly update plot if new data available
588 Timer = new QTimer(this);
589 connect(Timer, SIGNAL(timeout()), this, SLOT(UpdatePlot()));
590 Timer->start(2000);
591}
592
593// Destructor (items with parent widget are automatically deleted)
594EddBasePlot::~EddBasePlot() {
595
596 delete Grid;
597}
598
599// Print statistics to plot if requested
600void EddBasePlot::ReDoStats() {
601
602 QString Text;
603 double Mean, Sigma;
604 unsigned long Count = 0;
605
606 // Set visibility of statistics box
607 Stats->setVisible(StatisticsAction->isChecked());
608
609 for (int i=0; i<Items.size(); i++) {
610 Mean = 0;
611 Sigma = 0;
612 Count = 0;
613
614 // Calculate mean and sigma for data points currently visible
615 for (int j=0; j<Items[i].y.size(); j++) {
616 if (!axisScaleDiv(QwtPlot::xBottom).contains(Items[i].x[j])) continue;
617 Mean += Items[i].y[j];
618 Sigma += Items[i].y[j]*Items[i].y[j];
619 Count++;
620 }
621
622 if (Count == 0) Mean = 0;
623 else Mean /= Count;
624 if (Count < 2) Sigma = 0;
625 else Sigma = sqrt(Count/(Count-1)*(Sigma/Count-Mean*Mean));
626
627 // Prepare string
628 Text += Items[i].Signal->title().text() + ": m " + QString::number(Mean, 'f', 2) + ", s " + QString::number(Sigma, 'f', 2) + "\n";
629 }
630
631 // Replot once to get axis correct
632 replot();
633
634 // Print string to plot
635 QwtText text(Text);
636 text.setFont(QFont("Helvetica", 8));
637 Stats->setValue(axisScaleDiv(QwtPlot::xBottom).upperBound(), axisScaleDiv(QwtPlot::yLeft).upperBound());
638 Stats->setLabel(text);
639
640 // Replot again to update text
641 replot();
642}
643
644// Update all curves in plot
645void EddBasePlot::UpdatePlot() {
646
647 double Lower = axisScaleDiv(QwtPlot::xBottom).lowerBound();
648 double Upper = axisScaleDiv(QwtPlot::xBottom).upperBound();
649 double MaxTime = DBL_MIN;
650
651 // Only update if called by timer if new data is available
652 if (sender() == Timer && !NewData) return;
653 NewData = false;
654
655 // Select engine for linear or logarithmic scale
656 if (!YLogAction->isChecked()) {
657 setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine);
658 }
659 else setAxisScaleEngine(QwtPlot::yLeft, new QwtLogScaleEngine);
660
661 for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
662 // Determine current maximum value for strip chart plotting
663 if (Items[ItemNo].Signal->boundingRect().right() > MaxTime) {
664 MaxTime = Items[ItemNo].Signal->boundingRect().right();
665 }
666
667 // Set symbol if requested
668 if (StyleAction->isChecked()) {
669 QwtSymbol *Symbol = new QwtSymbol();
670 Symbol->setStyle(QwtSymbol::Ellipse);
671 Symbol->setSize(4);
672 Items[ItemNo].Signal->setSymbol(Symbol);
673 }
674 else Items[ItemNo].Signal->setSymbol(new QwtSymbol);
675
676 // Determine number of data points
677 int DataPoints = Items[ItemNo].x.size();
678 if (DataPoints == 0) continue;
679
680 // Normalize y scale if requested
681 double *y = new double [DataPoints];
682 for (int i=0; i<DataPoints; i++) {
683 y[i] = Items[ItemNo].y[i];
684
685 if (NormAction->isChecked()) {
686 if (Items[ItemNo].Smallest != Items[ItemNo].Largest) {
687 y[i] = (y[i] - Items[ItemNo].Smallest)/(Items[ItemNo].Largest-Items[ItemNo].Smallest);
688 }
689 else y[i] = 1;
690 }
691 }
692
693 // Plot data
694 Items[ItemNo].Signal->setSamples(Items[ItemNo].x.data(), y, DataPoints);
695 Items[ItemNo].Signal->show();
696 delete[] y;
697
698 // Generate bounding box of all curves for zoomer
699 BBox = BBox.unite(Items[ItemNo].Signal->boundingRect());
700 }
701
702 // Reset zoom base to include all data
703 Zoomer->setZoomBase(BBox);
704
705 // If plot is strip char, only move axis but keep range
706 if (StripAction->isChecked()) {
707 setCanvasBackground(EddPlotBackgroundColor.lighter(90));
708 setAxisScale(QwtPlot::xBottom, Lower+ BBox.right() - MaxTime, Upper + BBox.right() - MaxTime);
709 }
710 else setCanvasBackground(EddPlotBackgroundColor);
711
712 // Set footer
713 QwtText Text;
714 QFont Font;
715
716 Text = QDateTime::fromMSecsSinceEpoch(axisScaleDiv(QwtPlot::xBottom).lowerBound()).toString("d-MMM-yyyy hh:mm:ss") + " to " +
717 QDateTime::fromMSecsSinceEpoch(axisScaleDiv(QwtPlot::xBottom).upperBound()).toString("d-MMM-yyyy hh:mm:ss") +
718 (StripAction->isChecked() ? " (Stripchart)" : "");
719
720 Font.setPointSize(6);
721 Text.setFont(Font);
722 setFooter(Text); // For EddScope (not a history plot, no time scale) this action is removed
723
724 // Recalculate statistics
725 ReDoStats();
726}
727
728// Append curve to plot
729QwtPlotCurve *EddBasePlot::NewCurve(QwtText Title) {
730
731 static Qt::GlobalColor LineColors[] = {Qt::black, Qt::blue, Qt::red, Qt::green, Qt::white,
732 Qt::darkRed, Qt::darkGreen, Qt::darkBlue, Qt::cyan, Qt::darkCyan, Qt::magenta, Qt::darkMagenta,
733 Qt::gray, Qt::darkGray, Qt::lightGray};
734 struct PlotItem N;
735
736 N.Signal = new QwtPlotCurve(Title);
737 N.Signal->attach(this);
738 N.Signal->setPen(QColor(LineColors[Items.size() % (sizeof(LineColors)/sizeof(Qt::GlobalColor))]));
739 N.Largest = DBL_MIN;
740 N.Smallest = DBL_MAX;
741 Items.append(N);
742
743 QVariant ItemInfo = itemToInfo(N.Signal);
744 ((EddLegendLabel *) Legend->legendWidget(ItemInfo))->Curve = N.Signal;
745
746 // Context menu might delete curve and legend -> seg fault if using direct connection, as legend item deleted
747 connect(((EddLegendLabel *) Legend->legendWidget(ItemInfo)), SIGNAL(DeleteCurve(QwtPlotCurve *)), SLOT(RemoveService(QwtPlotCurve *)), Qt::QueuedConnection);
748
749 return N.Signal;
750}
751
752// Remove item
753void EddBasePlot::DeleteCurve(QwtPlotCurve *Curve) {
754
755 for (int i=0; i<Items.size(); i++) if (Items[i].Signal == Curve) {
756 delete Curve;
757 Items.takeAt(i);
758 break;
759 }
760 UpdatePlot();
761}
762
763// Clear curve data
764void EddBasePlot::ClearCurve(unsigned int Item) {
765
766 if (Item >= (unsigned int) Items.size()) return;
767
768 Items[Item].x.clear();
769 Items[Item].y.clear();
770}
771
772// Append data point
773void EddBasePlot::AddPoint(unsigned int Item, double x, double y) {
774
775 if (Item >= (unsigned int) Items.size()) return;
776
777 Items[Item].x.append(x);
778 Items[Item].y.append(y);
779
780 if (y > Items[Item].Largest) Items[Item].Largest = y;
781 if (y < Items[Item].Smallest) Items[Item].Smallest = y;
782}
783
784// Rescale plot in case selection has been made outside the canvas
785void EddBasePlot::MouseSelection(const QPolygon &P_integer) {
786
787 QwtInterval xPlot, xMouse, yPlot, yMouse;
788 QPolygonF P(P_integer);
789
790 // Shift selected rectangle so that upper left corner is 0/0 on canvas
791 QRectF R = P.boundingRect().translated(-plotLayout()->canvasRect().topLeft());
792
793 // Current axis intervals
794 xPlot = axisScaleDiv(QwtPlot::xBottom).interval();
795 yPlot = axisScaleDiv(QwtPlot::yLeft).interval();
796
797 // Selected axis intervals
798 xMouse = QwtInterval(invTransform(QwtPlot::xBottom, R.left()),
799 invTransform(QwtPlot::xBottom, R.right()));
800 yMouse = QwtInterval(invTransform(QwtPlot::yLeft, R.bottom()),
801 invTransform(QwtPlot::yLeft, R.top()));
802
803 // Selection region outside all axis?
804 if (R.right() < 0 && R.top() > plotLayout()->canvasRect().height()) return;
805
806 // If selected rectangle completely inside canvas taken care of by zoomer
807 if (plotLayout()->canvasRect().contains(P.boundingRect())) return;
808
809 // Rescale both axis if selected rectangle encompasses canvas completely
810 if (P.boundingRect().contains(plotLayout()->canvasRect())) {
811 yMouse.setMaxValue(yMouse.maxValue() + yPlot.width());
812 yMouse.setMinValue(yMouse.minValue() - yPlot.width());
813 xMouse.setMinValue(xMouse.minValue() - xPlot.width());
814 xMouse.setMaxValue(xMouse.maxValue() + xPlot.width());
815
816 setAxisScale(QwtPlot::xBottom, xMouse.minValue(), xMouse.maxValue());
817 setAxisScale(QwtPlot::yLeft, yMouse.minValue(), yMouse.maxValue());
818 }
819
820 // Rescale y axis (increase range if selected rectangle larger than axis area)
821 if (R.right() < 0) {
822 if (yMouse.maxValue() > axisScaleDiv(QwtPlot::yLeft).upperBound()) {
823 yMouse.setMaxValue(yMouse.maxValue() + yPlot.width());
824 }
825 if (yMouse.minValue() < axisScaleDiv(QwtPlot::yLeft).lowerBound()) {
826 yMouse.setMinValue(yMouse.minValue() - yPlot.width());
827 }
828
829 setAxisScale(QwtPlot::yLeft, yMouse.minValue(), yMouse.maxValue());
830 }
831
832 // Rescale x axis (increase range if selected rectangle larger than axis area)
833 if (R.top() > plotLayout()->canvasRect().height()) {
834 if (xMouse.maxValue() > axisScaleDiv(QwtPlot::xBottom).upperBound()) {
835 xMouse.setMaxValue(xMouse.maxValue() + xPlot.width());
836 }
837 if (xMouse.minValue() < axisScaleDiv(QwtPlot::xBottom).lowerBound()) {
838 xMouse.setMinValue(xMouse.minValue() - xPlot.width());
839 }
840
841 setAxisScale(QwtPlot::xBottom, xMouse.minValue(), xMouse.maxValue());
842 }
843
844 // Draw new scales
845 replot();
846}
847
848// Reset graph axes to autoscale when fully unzoomed
849void EddBasePlot::HandleZoom(const QRectF &) {
850
851 if(Zoomer->zoomRectIndex() == 0) {
852 setAxisAutoScale(QwtPlot::xBottom);
853 setAxisAutoScale(QwtPlot::yLeft);
854 }
855
856 UpdatePlot();
857}
858
859// Mouse events: Release click highlights curve if mouse did not move (which is zoom)
860void EddBasePlot::mousePressEvent(QMouseEvent *) {
861
862 MouseMoved = false;
863}
864
865void EddBasePlot::mouseMoveEvent(QMouseEvent *) {
866
867 MouseMoved = true;
868}
869
870void EddBasePlot::mouseReleaseEvent(QMouseEvent *Event) {
871
872 double Dist, MinDistance = std::numeric_limits<double>::infinity();
873 int Index = -1;
874
875 if (Event->button() != Qt::LeftButton || MouseMoved) return;
876
877 // Check which curve is closest
878 for (int i=0; i<Items.size(); i++) {
879 if (Items[i].Signal->closestPoint(canvas()->mapFromGlobal(QCursor::pos()), &Dist) != -1 && Dist < MinDistance) {
880 MinDistance = Dist;
881 Index = i;
882 }
883 }
884
885 // Toggle 'thick line' action
886 if (Index != -1) {
887 ((EddLegendLabel *) Legend->legendWidget(itemToInfo(Items[Index].Signal)))->ThickLineAction->toggle();
888 }
889}
890
891// Opening context menu
892void EddBasePlot::contextMenuEvent(QContextMenuEvent *Event) {
893
894 Menu->exec(Event->globalPos());
895}
896
897// Zoom completely out
898void EddBasePlot::MenuZoomOut() {
899
900 Zoomer->zoom(0);
901 UpdatePlot();
902}
903
904// Set maximum update rate of plot
905void EddBasePlot::MenuSetUpdateRate() {
906
907 bool OK;
908 double Rate;
909
910 Rate = QInputDialog::getDouble(this, "Edd Request",
911 "Minimum period between plot updates (sec)", (double) Timer->interval()/1000, 0, std::numeric_limits<double>::max(), 2, &OK);
912 if (OK) Timer->setInterval(Rate*1000);
913}
914
915// Save data of plot as test
916void EddBasePlot::MenuSaveASCII() {
917 QString Filename = QFileDialog::getSaveFileName(this,
918 "Filename", ".", "Text files (*.txt *.ascii *.asc);;All files (*)");
919 if (Filename.length() <= 0) return;
920
921 QFile File(Filename);
922 if (!File.open(QFile::WriteOnly | QIODevice::Text | QFile::Truncate)) {
923 QMessageBox::warning(this, "Edd Message","Could not open file for writing.",QMessageBox::Ok);
924 return;
925 }
926
927 // Write x and y data for all signals to file
928 QTextStream Stream(&File);
929 Stream.setRealNumberPrecision(14);
930
931 for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
932 Stream << QString("# ") + Items[ItemNo].Signal->title().text() + ".hist" << endl;
933 for (unsigned int i=0; i<Items[ItemNo].Signal->dataSize(); i++) {
934 //Stream << Items[ItemNo].x.at(i) << " " << Items[ItemNo].Signal->y(i) << endl;
935 Stream << Items[ItemNo].x.at(i) << " " << Items[ItemNo].Signal->sample(i).y() << endl;
936 }
937 }
938}
939
940// Print plot
941void EddBasePlot::MenuPrint() {
942
943 QPrinter *Printer = new QPrinter;
944 QPrintDialog *PrintDialog = new QPrintDialog(Printer, this);
945 if (PrintDialog->exec() == QDialog::Accepted) {
946 QPainter Painter(Printer);
947 QPixmap Pixmap = QPixmap::grabWidget(this);
948 Painter.drawPixmap(0, 0, Pixmap);
949 }
950 delete Printer; delete PrintDialog;
951}
952
953// Save plot as image
954void EddBasePlot::MenuSave() {
955
956 QString Filename = QFileDialog::getSaveFileName(this,
957 "Filename of image", "/home/ogrimm/ddd", "Image files (*.bmp *.jpg *.png *.ppm *.tiff *.xbm *.xpm);;All files (*)");
958 if (Filename.length()>0) {
959 QPixmap Pixmap = QPixmap::grabWidget(this);
960 if(!Pixmap.save(Filename)) {
961 QMessageBox::warning(this, "Edd Message","Could not write image file.",QMessageBox::Ok);
962 remove(Filename.toAscii().data());
963 }
964 }
965}
966
967// Help text
968void EddBasePlot::MenuPlotHelp() {
969
970 QMessageBox::about(this, "Edd - Plot help",
971 "Zoom\tMouse wheel\n"
972 "\tKeys m and shift-m\n"
973 "\tSelecting region with left mouse button\n"
974 "\tMiddle button zooms out one level\n"
975 "\tSelecting a range on an axis\n"
976 "\tSelecting whole canvas\n\n"
977 "Pan\tShift and left mouse button\n\n"
978 "ESC cancels selection\n"
979 "Clicking near a curve or on legend highlights\n"
980 "Cursor keys move mouse\n\n"
981 "Statistics are calculated over the current x axis extend\n\n"
982 "Items can be added to history plot by drag&drop from\n"
983 "DIM service displays or from other plots legends");
984}
985
986
987////////////////////////////////
988// Edd Legend and LegendLabel //
989////////////////////////////////
990
991// Reimplementation to return EddLegendLabel widget
992QWidget *EddLegend::createWidget(const QwtLegendData &) const {
993
994 return new EddLegendLabel();
995}
996
997// Constructor for EddLegendLabel class
998EddLegendLabel::EddLegendLabel() {
999
1000 // Context menu
1001 Menu = new QMenu(this);
1002 Menu->addAction("Open in new history", this, SLOT(MenuOpenHistory()));
1003 Menu->addAction("Copy service", this, SLOT(MenuCopyService()));
1004
1005 ThickLineAction = new QAction("Thick line", this);
1006 ThickLineAction->setCheckable(true);
1007 connect(ThickLineAction, SIGNAL(toggled(bool)), SLOT(MenuThickLine(bool)));
1008 Menu->addAction(ThickLineAction);
1009
1010 Menu->addAction("Remove curve", this, SLOT(MenuRemove()));
1011
1012 Curve = NULL;
1013}
1014
1015// Opening context menu
1016void EddLegendLabel::contextMenuEvent(QContextMenuEvent *Event) {
1017
1018 Menu->exec(Event->globalPos());
1019}
1020
1021// Handling of mouse press event: Register start position for drag
1022void EddLegendLabel::mousePressEvent(QMouseEvent *Event) {
1023
1024 if (Event->button() == Qt::LeftButton) dragStart = Event->pos();
1025}
1026
1027// Handling of dragging (Drag and MimeData will be deleted by Qt)
1028void EddLegendLabel::mouseMoveEvent(QMouseEvent *Event) {
1029
1030 if ((Event->buttons() & Qt::LeftButton) == 0) return;
1031 if ((Event->pos()-dragStart).manhattanLength() < QApplication::startDragDistance()) return;
1032
1033 QString D(text().text());
1034 D.replace(':',' ');
1035
1036 QDrag *Drag = new QDrag(this);
1037 QMimeData *MimeData = new QMimeData;
1038 QByteArray Data;
1039 MimeData->setData("Edd/Service", Data.append(D));
1040 Drag->setMimeData(MimeData);
1041 Drag->exec();
1042}
1043
1044// Handling of mouse release event: Mark line
1045void EddLegendLabel::mouseReleaseEvent(QMouseEvent *Event) {
1046
1047 if (Event->button() != Qt::LeftButton) return;
1048
1049 ThickLineAction->toggle();
1050 }
1051
1052// Menu: Open history plot
1053void EddLegendLabel::MenuOpenHistory() {
1054
1055 QString D(text().text());
1056 D.replace(':',' ');
1057 QStringList A = D.split(" ");
1058
1059 OpenHistory(A[0].toAscii().data(), A[1].toInt());
1060}
1061
1062// Menu: Copy service name
1063void EddLegendLabel::MenuCopyService() {
1064
1065 QString D(text().text());
1066 D.replace(':',' ');
1067
1068 QMimeData *MimeData = new QMimeData;
1069 QByteArray Data;
1070 MimeData->setData("Edd/Service", Data.append(D));
1071 QApplication::clipboard()->setMimeData(MimeData);
1072}
1073
1074// Menu: Thick line width
1075void EddLegendLabel::MenuThickLine(bool State) {
1076
1077 if (Curve == NULL) {
1078 printf("Warning: No QwtPlotCurve set in EddLegendLabel::MenuThickLine(), programming error\n");
1079 return;
1080 }
1081
1082 // Set pxel width of curve
1083 QPen Pen = Curve->pen();
1084 Pen.setWidth(State ? 4:1);
1085 Curve->setPen(Pen);
1086 Curve->plot()->replot();
1087
1088 // Highlight legend entry
1089 QFont Font = font();
1090 Font.setWeight(State ? QFont::Bold:QFont::Normal);
1091 setFont(Font);
1092}
1093
1094// Menu: Normal line width
1095void EddLegendLabel::MenuRemove() {
1096
1097 emit(DeleteCurve(Curve));
1098}
1099
1100//////////////////////////////////////
1101// History text box for DIM service //
1102//////////////////////////////////////
1103
1104// Constructor
1105EddText::EddText(QString Name, bool Pure, QWidget *P):
1106 QTextEdit(P), Name(Name), Pure(Pure) {
1107
1108 // Widget properties
1109 setReadOnly(true);
1110 setAttribute(Qt::WA_DeleteOnClose);
1111 setAutoFillBackground(true);
1112 document()->setMaximumBlockCount(1000);
1113 Accumulate = true;
1114
1115 // Get history for this service
1116 if (!Pure) {
1117 struct EddDim::HistItem Hist = Handler->GetHistory(Name);
1118
1119 for (int i=0; i<Hist.DataText.size(); i++) {
1120 moveCursor (QTextCursor::Start);
1121 insertPlainText(QString("(")+QDateTime::fromTime_t(Hist.DataText[i].first).toString()+") " +
1122 QString(Hist.DataRaw[i].second) + "\n");
1123 }
1124 }
1125
1126 // DIM client
1127 Handler->Subscribe(Name, this);
1128}
1129
1130// Destructor
1131EddText::~EddText() {
1132
1133 Handler->Unsubscribe(Name, this);
1134}
1135
1136
1137// Update widget (must happen in GUI thread)
1138void EddText::Update(const QString &, int Time, const QByteArray &, const QString &Format, const QString &Text, int) {
1139
1140 QPalette Pal = palette();
1141
1142 // Check if service available
1143 if (!SetStatus(this, Name, Time, Format)) {
1144 Pal.setColor(QPalette::Base, Qt::lightGray);
1145 setPalette(Pal);
1146 setText("n/a");
1147 return;
1148 }
1149
1150 Pal.setColor(QPalette::Base, Qt::white);
1151 setPalette(Pal);
1152 QDateTime Timex = QDateTime::fromTime_t(Time);
1153
1154 // Clear display in case text should not accumulate
1155 if (Accumulate == false) clear();
1156
1157 if (!Pure) {
1158 moveCursor(QTextCursor::Start);
1159 insertPlainText(QString("(")+Timex.toString()+QString(") "));
1160 insertPlainText(Text + "\n");
1161 }
1162 else if (Format == "C") insertPlainText(Text);
1163}
1164
1165
1166/////////////////////////////
1167// Interface to Dim system //
1168/////////////////////////////
1169EddDim::EddDim() {
1170
1171 Volume = 0;
1172 Period = 10;
1173 Mutex = new QMutex(QMutex::Recursive);
1174
1175 // Timer to calculate data rates
1176 QTimer *Timer = new QTimer(this);
1177 Timer->connect(Timer, SIGNAL(timeout()), this, SLOT(UpdateStatistics()));
1178 Timer->start(Period*1000);
1179
1180 // Connect to DIM handler
1181 if (connect(this, SIGNAL(INT(QString, int, QByteArray, QString)), SLOT(Update(QString, int, QByteArray, QString))) == false) {
1182 printf("Failed connection in EddDim()\n");
1183 }
1184}
1185
1186// Destructor
1187EddDim::~EddDim() {
1188
1189 delete Mutex;
1190}
1191
1192// Subscribe to DIM service
1193void EddDim::Subscribe(QString Name, class EddWidget *Instance, int Index) {
1194
1195 // Lock before accessing list
1196 QMutexLocker Locker(Mutex);
1197
1198 // Check if already subscribed to service
1199 if (ServiceList.contains(Name)) {
1200 ServiceList[Name].Subscribers.append(QPair<class EddWidget *, int>(Instance, Index));
1201
1202 if (Index>=0 && Index<ServiceList[Name].Items.size()) {
1203 Instance->Update(Name, ServiceList[Name].TimeStamp, ServiceList[Name].ByteArray, ServiceList[Name].Format, ServiceList[Name].Items[Index]);
1204 }
1205 else Instance->Update(Name, ServiceList[Name].TimeStamp, ServiceList[Name].ByteArray, ServiceList[Name].Format, ServiceList[Name].Text);
1206
1207 return;
1208 }
1209
1210 // Create new entry in service list
1211 ServiceList[Name].ByteArray = QByteArray();
1212 ServiceList[Name].TimeStamp = -1;
1213 ServiceList[Name].Subscribers.append(QPair<class EddWidget *, int>(Instance, Index));
1214 ServiceList[Name].DIMService = new DimStampedInfo(Name.toAscii().data(), INT_MAX, NO_LINK, this);
1215}
1216
1217
1218// Unsubscribe from DIM service
1219void EddDim::Unsubscribe(QString Name, class EddWidget *Instance, int Index) {
1220
1221 // Lock before accessing list
1222 QMutexLocker Locker(Mutex);
1223
1224 if (!ServiceList.contains(Name)) return;
1225
1226 QPair<class EddWidget *, int> P(Instance, Index);
1227
1228 if (ServiceList[Name].Subscribers.contains(P)) {
1229 ServiceList[Name].Subscribers.removeAt(ServiceList[Name].Subscribers.indexOf(P));
1230 }
1231
1232 // If no more needed, drop DIM subsription
1233 if (ServiceList[Name].Subscribers.isEmpty()) {
1234 delete ServiceList[Name].DIMService;
1235 ServiceList.remove(Name);
1236 }
1237}
1238
1239// Ignore service in update
1240void EddDim::Ignore(QString Name, bool Ignore) {
1241
1242 QMutexLocker Locker(&IgnoreMutex);
1243 IgnoreMap[Name] = Ignore;
1244}
1245
1246// Get history buffer
1247struct EddDim::HistItem EddDim::GetHistory(QString Name) {
1248
1249 // History already available?
1250 if (HistoryList.contains(Name)) return HistoryList[Name];
1251
1252 // Create history class to retrieve history data
1253 const struct EvidenceHistory::Item *R;
1254 class EvidenceHistory *Hist = new EvidenceHistory(Name.toStdString());
1255 Hist->GetHistory();
1256 HistoryList[Name].Time = time(NULL);
1257 HistoryList[Name].Format = Hist->GetFormat();
1258
1259 while ((R = Hist->Next()) != NULL) {
1260 HistoryList[Name].DataRaw.push_back(QPair<int, QByteArray>(R->Time, QByteArray(R->Data, R->Size)));
1261 HistoryList[Name].DataText.push_back(QPair<int, QStringList>(R->Time, QString::fromStdString(EvidenceServer::ToString(Hist->GetFormat(), R->Data, R->Size)).split(' ')));
1262 }
1263
1264 delete Hist;
1265
1266 return HistoryList[Name];
1267}
1268
1269
1270// Update throughput statistics and clear up history memory
1271void EddDim::UpdateStatistics() {
1272
1273 // Lock before accessing internal variables
1274 QMutexLocker Locker(Mutex);
1275
1276 // Remove unused histories after not less than 5 seconds
1277 QList<QString> L = HistoryList.keys();
1278 for(int i=0; i<L.size(); i++) {
1279 if ((time(NULL)-HistoryList[L[i]].Time) > 60) HistoryList.remove(L[i]);
1280 }
1281
1282 float Rate = Volume/1024.0/Period;
1283 Volume = 0;
1284
1285 // No unlock because Mutex is recursive
1286 Update("Edd/Rate_kBSec", time(NULL), QByteArray((char *) &Rate, sizeof(Rate)), "F");
1287}
1288
1289
1290// Store service information for usage by Subscribe(), update statistics and emit signal to widgets
1291void EddDim::Update(QString Name, int Time, QByteArray Data, QString Format) {
1292
1293 // Lock before accessing list
1294 QMutexLocker Locker(Mutex);
1295
1296 Volume += Data.size();
1297
1298 // Store service data and update all subscribers
1299 if (ServiceList.contains(Name)) {
1300 ServiceList[Name].TimeStamp = Time;
1301 ServiceList[Name].ByteArray = Data;
1302 ServiceList[Name].Format = Format;
1303 ServiceList[Name].Text = QString::fromStdString(EvidenceServer::ToString(Format.toAscii().data(), Data.data(), Data.size()));
1304 ServiceList[Name].Items = ServiceList[Name].Text.split(" ");
1305
1306 for (int i=0; i<ServiceList[Name].Subscribers.size(); i++) {
1307 QPair<class EddWidget *, int> P = ServiceList[Name].Subscribers[i];
1308
1309 if (P.second >=0 && P.second < ServiceList[Name].Items.size()) {
1310 P.first->Update(Name, Time, Data, Format, ServiceList[Name].Items[P.second], P.second);
1311 }
1312 else P.first->Update(Name, Time, Data, Format, ServiceList[Name].Text, P.second);
1313 }
1314 }
1315}
1316
1317// Handling of DIM service update (Data asynchronouly send to EddDim::Update())
1318void EddDim::infoHandler() {
1319
1320 QMutexLocker Locker(&IgnoreMutex);
1321 bool Ignore = IgnoreMap[getInfo()->getName()];
1322 Locker.unlock();
1323
1324 if (!EvidenceServer::ServiceOK(getInfo())) INT(getInfo()->getName(), -1);
1325 else if (!Ignore) INT(getInfo()->getName(), getInfo()->getTimestamp(), QByteArray((char *) getInfo()->getData(), getInfo()->getSize()), getInfo()->getFormat());
1326}
1327
1328
1329/////////////////////
1330// Open new window //
1331/////////////////////
1332
1333// Constructor
1334EddWindow::EddWindow(QString ButtonName, QString WindowName): QPushButton(ButtonName) {
1335
1336 M = new QMainWindow;
1337 M->setCentralWidget(new QWidget);
1338 M->setStatusBar(new QStatusBar(M));
1339 M->setWindowTitle(WindowName);
1340 L = new QGridLayout(M->centralWidget());
1341
1342 connect(this, SIGNAL(pressed()), M, SLOT(show()));
1343 connect(this, SIGNAL(pressed()), M, SLOT(raise()));
1344}
1345
1346// Return layout
1347QGridLayout *EddWindow::Layout() {
1348
1349 return L;
1350}
1351
1352// Return window
1353QMainWindow *EddWindow::Window() {
1354
1355 return M;
1356}
Note: See TracBrowser for help on using the repository browser.