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