1 #include "frameinfowindow.h"
2 #include "ui_frameinfowindow.h"
3 #include "mainwindow.h"
4 #include "helpwindow.h"
5 #include <QtDebug>
6 #include <vector>
7 #include "filterutility.h"
8 
9 const QColor FrameInfoWindow::byteGraphColors[8] = {Qt::blue, Qt::green,  Qt::black, Qt::red, //0 1 2 3
10                                                     Qt::gray, Qt::yellow, Qt::cyan,  Qt::darkMagenta}; //4 5 6 7
11 QPen FrameInfoWindow::bytePens[8];
12 
13 const int numIntervalHistBars = 20;
14 
FrameInfoWindow(const QVector<CANFrame> * frames,QWidget * parent)15 FrameInfoWindow::FrameInfoWindow(const QVector<CANFrame> *frames, QWidget *parent) :
16     QDialog(parent),
17     ui(new Ui::FrameInfoWindow)
18 {
19     ui->setupUi(this);
20     setWindowFlags(Qt::Window);
21 
22     readSettings();
23 
24     modelFrames = frames;
25 
26     // Using lambda expression to strip away the possible filter label before passing the ID to updateDetailsWindow
27     connect(ui->listFrameID, &QListWidget::currentTextChanged,
28         [this](QString itemText)
29             {
30             FrameInfoWindow::updateDetailsWindow(FilterUtility::getId(itemText));
31             } );
32 
33     connect(MainWindow::getReference(), &MainWindow::framesUpdated, this, &FrameInfoWindow::updatedFrames);
34     connect(ui->btnSave, &QAbstractButton::clicked, this, &FrameInfoWindow::saveDetails);
35 
36 
37     ui->graphHistogram->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes |
38                                     QCP::iSelectLegend | QCP::iSelectPlottables);
39 
40     ui->graphHistogram->xAxis->setRange(0, 63);
41     ui->graphHistogram->yAxis->setRange(0, 100);
42     QSharedPointer<QCPAxisTickerLog> graphHistoLogTicker(new QCPAxisTickerLog);
43     ui->graphHistogram->yAxis->setTicker(graphHistoLogTicker);
44     ui->graphHistogram->yAxis2->setTicker(graphHistoLogTicker);
45     ui->graphHistogram->yAxis->setNumberFormat("eb"); // e = exponential, b = beautiful decimal powers
46     ui->graphHistogram->yAxis->setNumberPrecision(0); //log ticker always picks powers of 10 so no need or use for precision
47 
48     ui->graphHistogram->axisRect()->setupFullAxesBox();
49 
50     ui->graphHistogram->xAxis->setLabel("Bits");
51     ui->graphHistogram->yAxis->setLabel("Instances");
52 
53     ui->graphHistogram->legend->setVisible(false);
54 
55     ui->graphBytes->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes |
56                                     QCP::iSelectLegend | QCP::iSelectPlottables);
57 
58     ui->graphBytes->xAxis->setRange(0, 63);
59     ui->graphBytes->yAxis->setRange(0, 265);
60     ui->graphBytes->axisRect()->setupFullAxesBox();
61 
62     ui->graphBytes->xAxis->setLabel("Time");
63     ui->graphBytes->yAxis->setLabel("Value");
64 
65     ui->graphBytes->legend->setVisible(false);
66 
67     ui->timeHistogram->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes |
68                                         QCP::iSelectLegend | QCP::iSelectPlottables);
69 
70     ui->timeHistogram->xAxis->setRange(0, numIntervalHistBars);
71     ui->timeHistogram->yAxis->setRange(0, 100);
72     QSharedPointer<QCPAxisTickerLog> logTicker(new QCPAxisTickerLog);
73     ui->timeHistogram->yAxis->setTicker(logTicker);
74     ui->timeHistogram->yAxis2->setTicker(logTicker);
75     ui->timeHistogram->yAxis->setScaleType(QCPAxis::stLogarithmic);
76     ui->timeHistogram->yAxis->setNumberFormat("eb"); // e = exponential, b = beautiful decimal powers
77     ui->timeHistogram->yAxis->setNumberPrecision(0); //log ticker always picks powers of 10 so no need or use for precision
78     ui->timeHistogram->axisRect()->setupFullAxesBox();
79 
80     ui->timeHistogram->xAxis->setLabel("Interval (ms)");
81     ui->timeHistogram->yAxis->setLabel("Occurrences");
82 
83     ui->timeHistogram->legend->setVisible(false);
84 
85     if (useOpenGL)
86     {
87         ui->graphHistogram->setAntialiasedElements(QCP::aeAll);
88         ui->graphHistogram->setOpenGl(true);
89         ui->graphBytes->setAntialiasedElements(QCP::aeAll);
90         ui->graphBytes->setOpenGl(true);
91         ui->timeHistogram->setAntialiasedElements(QCP::aeAll);
92         ui->timeHistogram->setOpenGl(true);
93     }
94     else
95     {
96         ui->graphHistogram->setOpenGl(false);
97         ui->graphHistogram->setAntialiasedElements(QCP::aeNone);
98         ui->graphBytes->setOpenGl(false);
99         ui->graphBytes->setAntialiasedElements(QCP::aeNone);
100         ui->timeHistogram->setOpenGl(false);
101         ui->timeHistogram->setAntialiasedElements(QCP::aeNone);
102     }
103 
104     // Prevent annoying accidental horizontal scrolling when filter list is populated with long interpreted message names
105     ui->listFrameID->horizontalScrollBar()->setEnabled(false);
106 
107     installEventFilter(this);
108 
109     for (int i = 0; i < 8; i++)
110     {
111         bytePens[i].setColor(byteGraphColors[i]);
112         bytePens[i].setWidth(1);
113     }
114 
115     dbcHandler = DBCHandler::getReference();
116 }
117 
showEvent(QShowEvent * event)118 void FrameInfoWindow::showEvent(QShowEvent* event)
119 {
120     QDialog::showEvent(event);
121     readSettings();
122     refreshIDList();
123     if (ui->listFrameID->count() > 0)
124     {
125         updateDetailsWindow(FilterUtility::getId(ui->listFrameID->item(0)));
126         ui->listFrameID->setCurrentRow(0);
127     }
128 }
129 
eventFilter(QObject * obj,QEvent * event)130 bool FrameInfoWindow::eventFilter(QObject *obj, QEvent *event)
131 {
132     if (event->type() == QEvent::KeyRelease) {
133         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
134         switch (keyEvent->key())
135         {
136         case Qt::Key_F1:
137             HelpWindow::getRef()->showHelp("framedetails.html");
138             break;
139         }
140         return true;
141     } else {
142         // standard event processing
143         return QObject::eventFilter(obj, event);
144     }
145 }
146 
~FrameInfoWindow()147 FrameInfoWindow::~FrameInfoWindow()
148 {
149     removeEventFilter(this);
150     delete ui;
151 }
152 
closeEvent(QCloseEvent * event)153 void FrameInfoWindow::closeEvent(QCloseEvent *event)
154 {
155     Q_UNUSED(event)
156     writeSettings();
157 }
158 
readSettings()159 void FrameInfoWindow::readSettings()
160 {
161     QSettings settings;
162 
163     if (settings.value("Main/FilterLabeling", false).toBool())
164     {
165         ui->listFrameID->setMinimumWidth(250);
166     }
167     else
168     {
169         ui->listFrameID->setMinimumWidth(120);
170     }
171 
172     if (settings.value("Main/SaveRestorePositions", false).toBool())
173     {
174         resize(settings.value("FrameInfo/WindowSize", QSize(794, 694)).toSize());
175         move(Utility::constrainedWindowPos(settings.value("FrameInfo/WindowPos", QPoint(50, 50)).toPoint()));
176     }
177     useOpenGL = settings.value("Main/UseOpenGL", false).toBool();
178 
179 }
180 
writeSettings()181 void FrameInfoWindow::writeSettings()
182 {
183     QSettings settings;
184 
185     if (settings.value("Main/SaveRestorePositions", false).toBool())
186     {
187         settings.setValue("FrameInfo/WindowSize", size());
188         settings.setValue("FrameInfo/WindowPos", pos());
189     }
190 }
191 
192 //remember, negative numbers are special -1 = all frames deleted, -2 = totally new set of frames.
updatedFrames(int numFrames)193 void FrameInfoWindow::updatedFrames(int numFrames)
194 {
195     if (numFrames == -1) //all frames deleted. Kill the display
196     {
197         //qDebug() << "Delete all frames in Info Window";
198         ui->listFrameID->clear();
199         ui->treeDetails->clear();
200         foundID.clear();
201         refreshIDList();
202     }
203     else if (numFrames == -2) //all new set of frames. Reset
204     {
205         //qDebug() << "All new set of frames in Info Window";
206         ui->listFrameID->clear();
207         ui->treeDetails->clear();
208         foundID.clear();
209         refreshIDList();
210         if (ui->listFrameID->count() > 0)
211         {
212             updateDetailsWindow(FilterUtility::getId(ui->listFrameID->item(0)));
213             ui->listFrameID->setCurrentRow(0);
214         }
215     }
216     else //just got some new frames. See if they are relevant.
217     {
218         //qDebug() << "Got frames in Info Window";
219         if (numFrames > modelFrames->count()) return;
220 
221         unsigned int currID = 0;
222         if (ui->listFrameID->currentItem())
223             currID = static_cast<unsigned int>(FilterUtility::getIdAsInt(ui->listFrameID->currentItem()));
224         bool thisID = false;
225         for (int x = modelFrames->count() - numFrames; x < modelFrames->count(); x++)
226         {
227             CANFrame thisFrame = modelFrames->at(x);
228             int32_t id = static_cast<int32_t>(thisFrame.frameId());
229             if (!foundID.contains(id))
230             {
231                 foundID.append(id);
232                 FilterUtility::createFilterItem(id, ui->listFrameID);
233             }
234 
235             if (currID == modelFrames->at(x).frameId())
236             {
237                 thisID = true;
238                 break;
239             }
240         }
241         if (thisID)
242         {
243             //the problem here is that it'll blast us out of the details as soon as this
244             //happens. The only way to do this properly is to actually traverse
245             //the details structure and change the text. We don't do that yet.
246             //so, the line is commented out. If people need to see the updated
247             //data they can click another ID and back and it'll be OK
248 
249             //updateDetailsWindow(ui->listFrameID->currentItem()->text());
250         }
251         //default is to sort in ascending order
252         ui->listFrameID->sortItems();
253         ui->lblFrameID->setText(tr("Frame IDs: (") + QString::number(ui->listFrameID->count()) + tr(" unique ids)"));
254     }
255 }
256 
updateDetailsWindow(QString newID)257 void FrameInfoWindow::updateDetailsWindow(QString newID)
258 {
259     int targettedID;
260     int minLen, maxLen, thisLen;
261     int64_t avgInterval;
262     int64_t minInterval;
263     int64_t maxInterval;
264     int64_t thisInterval;
265     int minData[8];
266     int maxData[8];
267     int dataHistogram[256][8];
268     int bitfieldHistogram[64];
269     QVector<double> histGraphX, histGraphY;
270     QVector<double> byteGraphX, byteGraphY[8];
271     QVector<double> timeGraphX, timeGraphY;
272     QHash<QString, QHash<QString, int>> signalInstances;
273     double maxY = -1000.0;
274     uint8_t changedBits[8];
275     uint8_t referenceBits[8];
276     QTreeWidgetItem *baseNode, *dataBase, *histBase, *tempItem;
277 
278     if (modelFrames->count() == 0) return;
279 
280     targettedID = static_cast<int>(Utility::ParseStringToNum(newID));
281 
282     qDebug() << "Started update details window with id " << targettedID;
283 
284     avgInterval = 0;
285 
286     if (targettedID > -1)
287     {
288 
289         frameCache.clear();
290         for (int i = 0; i < modelFrames->count(); i++)
291         {
292             CANFrame thisFrame = modelFrames->at(i);
293             if (thisFrame.frameId() == static_cast<uint32_t>(targettedID)) frameCache.append(thisFrame);
294         }
295 
296         const unsigned char *data = reinterpret_cast<const unsigned char *>(frameCache.at(0).payload().constData());
297         int dataLen = frameCache.at(0).payload().length();
298 
299         ui->treeDetails->clear();
300 
301         if (frameCache.count() == 0) return;
302 
303         baseNode = new QTreeWidgetItem();
304         baseNode->setText(0, QString("ID: ") + newID );
305 
306         if (frameCache[0].hasExtendedFrameFormat()) //if these frames seem to be extended then try for J1939 decoding
307         {
308             // ------- J1939 decoding ----------
309             J1939ID jid;
310             jid.src = targettedID & 0xFF;
311             jid.priority = targettedID >> 26;
312             jid.pgn = (targettedID >> 8) & 0x3FFFF; //18 bits
313             jid.pf = (targettedID >> 16) & 0xFF;
314             jid.ps = (targettedID >> 8) & 0xFF;
315 
316             tempItem = new QTreeWidgetItem();
317             tempItem->setText(0, tr("J1939 decoding"));
318             baseNode->addChild(tempItem);
319 
320             if (jid.pf > 0xEF)
321             {
322                 jid.isBroadcast = true;
323                 jid.dest = 0xFFFF;
324                 tempItem = new QTreeWidgetItem();
325                 tempItem->setText(0, tr("   Broadcast Frame"));
326                 baseNode->addChild(tempItem);
327             }
328             else
329             {
330                 jid.dest = jid.ps;
331                 tempItem = new QTreeWidgetItem();
332                 tempItem->setText(0, tr("   Destination ID: ") + Utility::formatNumber(static_cast<uint64_t>(jid.dest)));
333                 baseNode->addChild(tempItem);
334             }
335             tempItem = new QTreeWidgetItem();
336             tempItem->setText(0, tr("   SRC: ") + Utility::formatNumber(static_cast<uint64_t>(jid.src)));
337             baseNode->addChild(tempItem);
338 
339             tempItem = new QTreeWidgetItem();
340             tempItem->setText(0, tr("   PGN: ") + Utility::formatNumber(static_cast<uint64_t>(jid.pgn)) + "(" + QString::number(jid.pgn) + ")");
341             baseNode->addChild(tempItem);
342 
343             tempItem = new QTreeWidgetItem();
344             tempItem->setText(0, tr("   PF: ") + Utility::formatNumber(static_cast<uint64_t>(jid.pf)));
345             baseNode->addChild(tempItem);
346 
347             tempItem = new QTreeWidgetItem();
348             tempItem->setText(0, tr("   PS: ") + Utility::formatNumber(static_cast<uint64_t>(jid.ps)));
349             baseNode->addChild(tempItem);
350 
351             // ------- GMLAN 29bit decoding ----------
352             tempItem = new QTreeWidgetItem();
353             tempItem->setText(0, tr("GMLAN 29bit decoding"));
354             baseNode->addChild(tempItem);
355 
356             tempItem = new QTreeWidgetItem();
357             tempItem->setText(0, tr("   Priority bits: ") + Utility::formatNumber( (uint64_t)FilterUtility::getGMLanPriorityBits(targettedID)));
358             baseNode->addChild(tempItem);
359             tempItem = new QTreeWidgetItem();
360             tempItem->setText(0, tr("   Arbitration Id: ") + Utility::formatNumber( (uint64_t)FilterUtility::getGMLanArbitrationId(targettedID)));
361             baseNode->addChild(tempItem);
362             tempItem = new QTreeWidgetItem();
363             tempItem->setText(0, tr("   Sender Id: ") + Utility::formatNumber( (uint64_t)FilterUtility::getGMLanSenderId(targettedID)));
364             baseNode->addChild(tempItem);
365 
366         }
367 
368         tempItem = new QTreeWidgetItem();
369         tempItem->setText(0, tr("# of frames: ") + QString::number(frameCache.count(),10));
370         baseNode->addChild(tempItem);
371 
372         //clear out all the counters and accumulators
373         minLen = 8;
374         maxLen = 0;
375         minInterval = 0x7FFFFFFF;
376         maxInterval = 0;
377         for (int i = 0; i < 8; i++)
378         {
379             minData[i] = 256;
380             maxData[i] = -1;
381             for (int k = 0; k < 256; k++) dataHistogram[k][i] = 0;
382         }
383         for (int j = 0; j < 64; j++) bitfieldHistogram[j] = 0;
384         signalInstances.clear();
385 
386         data = reinterpret_cast<const unsigned char *>(frameCache.at(0).payload().constData());
387         dataLen = frameCache.at(0).payload().length();
388 
389         for (int c = 0; c < dataLen; c++)
390         {
391             changedBits[c] = 0;
392             referenceBits[c] = data[c];
393             //qDebug() << referenceBits[c];
394         }
395 
396         std::vector<int64_t> sortedIntervals;
397         int64_t intervalSum = 0;
398 
399         DBC_MESSAGE *msg = dbcHandler->findMessageForFilter(targettedID, nullptr);
400 
401         //then find all data points
402         for (int j = 0; j < frameCache.count(); j++)
403         {
404             data = reinterpret_cast<const unsigned char *>(frameCache.at(j).payload().constData());
405             dataLen = frameCache.at(j).payload().length();
406 
407             byteGraphX.append(j);
408             for (int bytcnt = 0; bytcnt < dataLen; bytcnt++)
409             {
410                 byteGraphY[bytcnt].append(data[bytcnt]);
411             }
412 
413             if (j != 0)
414             {
415                 //TODO - we try the interval whichever way doesn't go negative. But, we should probably sort the frame list before
416                 //starting so that the intervals are all correct.
417                 if (frameCache[j].timeStamp().microSeconds() > frameCache[j-1].timeStamp().microSeconds())
418                     thisInterval = (frameCache[j].timeStamp().microSeconds() - frameCache[j-1].timeStamp().microSeconds());
419                 else
420                     thisInterval = (frameCache[j-1].timeStamp().microSeconds() - frameCache[j].timeStamp().microSeconds());
421 
422                 sortedIntervals.push_back(thisInterval);
423                 intervalSum += thisInterval;
424                 if (thisInterval > maxInterval) maxInterval = thisInterval;
425                 if (thisInterval < minInterval) minInterval = thisInterval;
426                 avgInterval += thisInterval;
427             }
428             thisLen = dataLen;
429             if (thisLen > maxLen) maxLen = thisLen;
430             if (thisLen < minLen) minLen = thisLen;
431             for (int c = 0; c < thisLen; c++)
432             {
433                 unsigned char dat = data[c];
434                 if (minData[c] > dat) minData[c] = dat;
435                 if (maxData[c] < dat) maxData[c] = dat;
436                 dataHistogram[dat][c]++; //add one to count for this
437                 for (int l = 0; l < 8; l++)
438                 {
439                     int bit = dat & (1 << l);
440                     if (bit == (1 << l))
441                     {
442                         bitfieldHistogram[c * 8 + l]++;
443                     }
444                 }
445                 changedBits[c] |= referenceBits[c] ^ dat;
446             }
447 
448             //Search every signal in the selected message and give output of the range the signal took and
449             //how many messages contained each discrete value.
450             if (msg)
451             {
452                 int numSignals = msg->sigHandler->getCount();
453                 for (int i = 0; i < numSignals; i++)
454                 {
455                     DBC_SIGNAL *sig = msg->sigHandler->findSignalByIdx(i);
456                     if (sig)
457                     {
458                         if (sig->isSignalInMessage(frameCache.at(j)))
459                         {
460                             QString sigVal;
461                             if (sig->processAsText(frameCache.at(j), sigVal, false))
462                             {
463                                 signalInstances[sig->name][sigVal] = signalInstances[sig->name][sigVal] + 1;
464                             }
465                         }
466                     }
467                 }
468             }
469         }
470 
471         std::sort(sortedIntervals.begin(), sortedIntervals.end());
472         int64_t intervalStdDiv = 0, intervalPctl5 = 0, intervalPctl95 = 0, intervalMean = 0, intervalVariance = 0;
473 
474         int maxTimeCounter = -1;
475         if (sortedIntervals.size() > 0)
476         {
477             intervalMean = intervalSum / sortedIntervals.size();
478 
479             for(int l = 0; l < static_cast<int>(sortedIntervals.size()); l++) {
480                 intervalVariance += ((sortedIntervals[l] - intervalMean) * (sortedIntervals[l] - intervalMean));
481             }
482 
483             intervalVariance /= sortedIntervals.size();
484             intervalStdDiv = static_cast<int>(sqrt(intervalVariance));
485 
486             intervalPctl5 = sortedIntervals[static_cast<unsigned int>(floor(0.05 * sortedIntervals.size()))];
487             intervalPctl95 = sortedIntervals[static_cast<unsigned int>(floor(0.95 * sortedIntervals.size()))];
488 
489             uint64_t step = static_cast<unsigned int>(ceil((maxInterval - minInterval) / numIntervalHistBars));
490             qDebug() << "Step: " << step << " minInt: " << minInterval << " maxInt: " << maxInterval;
491             unsigned int index = 0;
492             int counter = 0;
493             for(int l = 0; l <= numIntervalHistBars; l++) {
494                 int64_t currentMax = maxInterval - ((numIntervalHistBars - l) * step);	// avoid missing the biggest value due to rounding errors
495                 qDebug() << "CurrentMax: " << currentMax;
496                 while(index < sortedIntervals.size()) {
497                     if(sortedIntervals[index] <= currentMax) {
498                         counter++;
499                         index++;
500                     }
501                     else {
502                         break;
503                     }
504                 }
505                 timeGraphX.append(currentMax / 1000.0);
506                 timeGraphY.append(counter);
507                 if(counter > maxTimeCounter) maxTimeCounter = counter;
508                 counter = 0;
509             }
510         }
511 
512         if (frameCache.count() > 1)
513             avgInterval = avgInterval / (frameCache.count() - 1);
514         else avgInterval = 0;
515 
516         tempItem = new QTreeWidgetItem();
517 
518         if (minLen < maxLen)
519             tempItem->setText(0, tr("Data Length: ") + QString::number(minLen) + tr(" to ") + QString::number(maxLen));
520         else
521             tempItem->setText(0, tr("Data Length: ") + QString::number(minLen));
522 
523         baseNode->addChild(tempItem);
524 
525         tempItem = new QTreeWidgetItem();
526         tempItem->setText(0, tr("Average inter-frame interval: ") + QString::number(avgInterval / 1000.0) + "ms");
527         baseNode->addChild(tempItem);
528         tempItem = new QTreeWidgetItem();
529         tempItem->setText(0, tr("Minimum inter-frame interval: ") + QString::number(minInterval / 1000.0) + "ms");
530         baseNode->addChild(tempItem);
531         tempItem = new QTreeWidgetItem();
532         tempItem->setText(0, tr("Maximum inter-frame interval: ") + QString::number(maxInterval / 1000.0) + "ms");
533         baseNode->addChild(tempItem);
534         tempItem = new QTreeWidgetItem();
535         tempItem->setText(0, tr("Inter-frame interval variation: ") + QString::number((maxInterval - minInterval) / 1000.0) + "ms");
536         baseNode->addChild(tempItem);
537         tempItem = new QTreeWidgetItem();
538         tempItem->setText(0, tr("Interval standard deviation: ") + QString::number(intervalStdDiv / 1000.0) + "ms");
539         baseNode->addChild(tempItem);
540         tempItem = new QTreeWidgetItem();
541         tempItem->setText(0, tr("Minimum range to fit 90% of inter-frame intervals: ") + QString::number((intervalPctl95 - intervalPctl5) / 1000.0) + "ms");
542         baseNode->addChild(tempItem);
543 
544         //display accumulated data for all the bytes in the message
545         for (int c = 0; c < maxLen; c++)
546         {
547             dataBase = new QTreeWidgetItem();
548             histBase = new QTreeWidgetItem();
549 
550             dataBase->setText(0, tr("Data Byte ") + QString::number(c));
551             baseNode->addChild(dataBase);
552 
553             tempItem = new QTreeWidgetItem();
554             QString builder;
555             builder = tr("Changed bits: 0x") + QString::number(changedBits[c], 16) + "  (" + Utility::formatByteAsBinary(changedBits[c]) + ")";
556             tempItem->setText(0, builder);
557             dataBase->addChild(tempItem);
558 
559             tempItem = new QTreeWidgetItem();
560             tempItem->setText(0, tr("Range: ") + Utility::formatNumber((unsigned int)minData[c]) + tr(" to ") + Utility::formatNumber((unsigned int)maxData[c]));
561             dataBase->addChild(tempItem);
562             histBase->setText(0, tr("Histogram"));
563             dataBase->addChild(histBase);
564 
565             for (int d = 0; d < 256; d++)
566             {
567                 if (dataHistogram[d][c] > 0)
568                 {
569                     tempItem = new QTreeWidgetItem();
570                     tempItem->setText(0, QString::number(d) + "/0x" + QString::number(d, 16) +" (" + Utility::formatByteAsBinary(static_cast<uint8_t>(d)) +") -> " + QString::number(dataHistogram[d][c]));
571                     histBase->addChild(tempItem);
572                 }
573             }
574         }
575 
576         dataBase = new QTreeWidgetItem();
577         dataBase->setText(0, tr("Bitfield Histogram"));
578         for (int c = 0; c < 8 * maxLen; c++)
579         {
580             tempItem = new QTreeWidgetItem();
581             tempItem->setText(0, QString::number(c) + " (Byte " + QString::number(c / 8) + " Bit "
582                             + QString::number(c % 8) + ") :" + QString::number(bitfieldHistogram[c]));
583 
584             dataBase->addChild(tempItem);
585             histGraphX.append(c);
586             histGraphY.append(bitfieldHistogram[c]);
587             if (bitfieldHistogram[c] > maxY) maxY = bitfieldHistogram[c];
588         }
589         baseNode->addChild(dataBase);
590 
591         QHash<QString, QHash<QString, int>>::const_iterator it = signalInstances.constBegin();
592         while (it != signalInstances.constEnd()) {
593             dataBase = new QTreeWidgetItem();
594             dataBase->setText(0, it.key());
595             QHash<QString,int>::const_iterator itVal = signalInstances[it.key()].constBegin();
596             while (itVal != signalInstances[it.key()].constEnd())
597             {
598                 tempItem = new QTreeWidgetItem();
599                 tempItem->setText(0, itVal.key() + ": " + QString::number(itVal.value()));
600                 dataBase->addChild(tempItem);
601                 ++itVal;
602             }
603             baseNode->addChild(dataBase);
604             ++it;
605         }
606 
607         ui->treeDetails->insertTopLevelItem(0, baseNode);
608 
609         ui->graphHistogram->clearGraphs();
610         ui->graphHistogram->addGraph();
611         ui->graphHistogram->graph()->setData(histGraphX, histGraphY);
612         ui->graphHistogram->graph()->setLineStyle(QCPGraph::lsStepLeft); //connect points with lines
613         QBrush graphBrush;
614         graphBrush.setColor(Qt::red);
615         graphBrush.setStyle(Qt::SolidPattern);
616         ui->graphHistogram->graph()->setPen(Qt::NoPen);
617         ui->graphHistogram->graph()->setBrush(graphBrush);
618         ui->graphHistogram->yAxis->setRange(0.8, maxY * 1.2);
619         ui->graphHistogram->yAxis->setScaleType(QCPAxis::stLogarithmic);
620         ui->graphHistogram->axisRect()->setupFullAxesBox();
621         ui->graphHistogram->replot();
622 
623         ui->graphBytes->clearGraphs();
624         for (int graphs = 0; graphs < 8; graphs++)
625         {
626             ui->graphBytes->addGraph();
627             ui->graphBytes->graph()->setData(byteGraphX, byteGraphY[graphs]);
628             ui->graphBytes->graph()->setPen(bytePens[graphs]);
629         }
630         ui->graphBytes->xAxis->setRange(0, byteGraphX.count());
631         ui->graphBytes->replot();
632 
633         ui->timeHistogram->clearGraphs();
634         ui->timeHistogram->addGraph();
635         ui->timeHistogram->graph()->setData(timeGraphX, timeGraphY);
636         ui->timeHistogram->graph()->setLineStyle(QCPGraph::lsStepLeft); //connect points with lines
637         //QBrush graphBrush;
638         graphBrush.setColor(Qt::red);
639         graphBrush.setStyle(Qt::SolidPattern);
640         ui->timeHistogram->graph()->setPen(Qt::NoPen);
641         ui->timeHistogram->graph()->setBrush(graphBrush);
642         //ui->timeHistogram->yAxis->setRange(0, maxTimeCounter * 1.1);
643         //ui->timeHistogram->xAxis->setRange(minInterval / 1000.0, maxInterval / 1000.0); //graph is in ms while intervals are in us
644         ui->timeHistogram->axisRect()->setupFullAxesBox();
645         ui->timeHistogram->rescaleAxes();
646         ui->timeHistogram->replot();
647     }
648     else
649     {
650     }
651 
652     QSettings settings;
653     if (settings.value("InfoCompare/AutoExpand", false).toBool())
654     {
655         ui->treeDetails->expandAll();
656     }
657 }
658 
refreshIDList()659 void FrameInfoWindow::refreshIDList()
660 {
661     int id;
662     for (int i = 0; i < modelFrames->count(); i++)
663     {
664         CANFrame thisFrame = modelFrames->at(i);
665         id = (int)thisFrame.frameId();
666         if (!foundID.contains(id))
667         {
668             foundID.append(id);
669             FilterUtility::createFilterItem(id, ui->listFrameID);
670         }
671     }
672     //default is to sort in ascending order
673     ui->listFrameID->sortItems();
674     ui->lblFrameID->setText(tr("Frame IDs: (") + QString::number(ui->listFrameID->count()) + tr(" unique ids)"));
675 }
676 
saveDetails()677 void FrameInfoWindow::saveDetails()
678 {
679     QString filename;
680     QFileDialog dialog(this);
681     QSettings settings;
682 
683     QStringList filters;
684     filters.append(QString(tr("Text File (*.txt)")));
685 
686     dialog.setFileMode(QFileDialog::AnyFile);
687     dialog.setNameFilters(filters);
688     dialog.setViewMode(QFileDialog::Detail);
689     dialog.setAcceptMode(QFileDialog::AcceptSave);
690     dialog.setDirectory(settings.value("FrameInfo/LoadSaveDirectory", dialog.directory().path()).toString());
691 
692     if (dialog.exec() == QDialog::Accepted)
693     {
694         settings.setValue("FrameInfo/LoadSaveDirectory", dialog.directory().path());
695         filename = dialog.selectedFiles()[0];
696         if (!filename.contains('.')) filename += ".txt";
697         if (dialog.selectedNameFilter() == filters[0])
698         {
699             QFile *outFile = new QFile(filename);
700 
701             if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text))
702             {
703                 delete outFile;
704                 return;
705             }
706 
707             //go through all IDs, recalculate the data, and then save it to file
708             for (int i = 0; i < ui->listFrameID->count(); i++)
709             {
710                 updateDetailsWindow(FilterUtility::getId(ui->listFrameID->item(i)));
711                 dumpNode(ui->treeDetails->invisibleRootItem(), outFile, 0);
712                 outFile->write("\n\n");
713             }
714 
715             outFile->close();
716             delete outFile;
717         }
718     }
719 }
720 
dumpNode(QTreeWidgetItem * item,QFile * file,int indent)721 void FrameInfoWindow::dumpNode(QTreeWidgetItem* item, QFile *file, int indent)
722 {
723     for (int i = 0; i < indent; i++) file->write("\t");
724     file->write(item->text(0).toUtf8());
725     file->write("\n");
726     for( int i = 0; i < item->childCount(); ++i )
727         dumpNode( item->child(i), file, indent + 1 );
728 }
729 
730 
731