1 #include "canframemodel.h"
2
3 #include <QFile>
4 #include <QApplication>
5 #include <QPalette>
6 #include <QDateTime>
7 #include "utility.h"
8
~CANFrameModel()9 CANFrameModel::~CANFrameModel()
10 {
11 frames.clear();
12 filteredFrames.clear();
13 filters.clear();
14 busFilters.clear();
15 }
16
rowCount(const QModelIndex & parent) const17 int CANFrameModel::rowCount(const QModelIndex &parent) const
18 {
19 Q_UNUSED(parent);
20 if (filteredFrames.data())
21 {
22 int rows = filteredFrames.count();
23 return rows;
24 }
25
26 //just in case somehow data is invalid which I have seen before.
27 //But, this should not happen so issue a debugging message too
28 qDebug() << "Invalid data for filteredFrames. Returning 0.";
29 return 0;
30 }
31
totalFrameCount()32 int CANFrameModel::totalFrameCount()
33 {
34 int count;
35 count = frames.count();
36 return count;
37 }
38
columnCount(const QModelIndex & index) const39 int CANFrameModel::columnCount(const QModelIndex &index) const
40 {
41 Q_UNUSED(index);
42 return (int)Column::NUM_COLUMN;
43 }
44
CANFrameModel(QObject * parent)45 CANFrameModel::CANFrameModel(QObject *parent)
46 : QAbstractTableModel(parent)
47 {
48
49 if (QSysInfo::WordSize > 32)
50 {
51 qDebug() << "64 bit OS detected. Requesting a large preallocation";
52 preallocSize = 10000000;
53 }
54 else //if compiling for 32 bit you can't ask for gigabytes of preallocation so tone it down.
55 {
56 qDebug() << "32 bit OS detected. Requesting a much restricted prealloc";
57 preallocSize = 2000000;
58 }
59
60 frames.reserve(preallocSize);
61 filteredFrames.reserve(preallocSize); //the goal is to prevent a reallocation from ever happening
62
63 dbcHandler = DBCHandler::getReference();
64 interpretFrames = false;
65 overwriteDups = false;
66 useHexMode = true;
67 timeSeconds = false;
68 timeOffset = 0;
69 needFilterRefresh = false;
70 lastUpdateNumFrames = 0;
71 timeFormat = "MMM-dd HH:mm:ss.zzz";
72 sortDirAsc = false;
73 }
74
setHexMode(bool mode)75 void CANFrameModel::setHexMode(bool mode)
76 {
77 if (useHexMode != mode)
78 {
79 this->beginResetModel();
80 useHexMode = mode;
81 Utility::decimalMode = !useHexMode;
82 this->endResetModel();
83 }
84 }
85
setSecondsMode(bool mode)86 void CANFrameModel::setSecondsMode(bool mode)
87 {
88 if (Utility::secondsMode != mode)
89 {
90 this->beginResetModel();
91 Utility::secondsMode = mode;
92 this->endResetModel();
93 }
94 }
95
setSysTimeMode(bool mode)96 void CANFrameModel::setSysTimeMode(bool mode)
97 {
98 if (Utility::sysTimeMode != mode)
99 {
100 this->beginResetModel();
101 Utility::sysTimeMode = mode;
102 this->endResetModel();
103 }
104 }
105
setInterpretMode(bool mode)106 void CANFrameModel::setInterpretMode(bool mode)
107 {
108 //if the state of interpretFrames changes then we need to reset the model
109 //so that QT will refresh the view properly
110 if (interpretFrames != mode)
111 {
112 this->beginResetModel();
113 interpretFrames = mode;
114 this->endResetModel();
115 }
116 }
117
getInterpretMode()118 bool CANFrameModel::getInterpretMode()
119 {
120 return interpretFrames;
121 }
122
setTimeFormat(QString format)123 void CANFrameModel::setTimeFormat(QString format)
124 {
125 Utility::timeFormat = format;
126 beginResetModel(); //reset model to show new time format
127 endResetModel();
128 }
129
130 /*
131 * Scan all frames for the smallest timestamp and offset all timestamps so that smallest one is at 0
132 */
normalizeTiming()133 void CANFrameModel::normalizeTiming()
134 {
135 mutex.lock();
136 if (frames.count() == 0) return;
137 timeOffset = frames[0].timeStamp().microSeconds();
138 qint64 prevStamp = 0;
139
140 //find the absolute lowest timestamp in the whole time. Needed because maybe timestamp was reset in the middle.
141 for (int j = 0; j < frames.count(); j++)
142 {
143 if (frames[j].timeStamp().microSeconds() < timeOffset) timeOffset = frames[j].timeStamp().microSeconds();
144 }
145
146 for (int i = 0; i < frames.count(); i++)
147 {
148 qint64 thisStamp = frames[i].timeStamp().microSeconds() - timeOffset;
149 if (thisStamp <= prevStamp)
150 {
151 timeOffset -= prevStamp;
152 }
153 frames[i].setTimeStamp(QCanBusFrame::TimeStamp(0, thisStamp));
154 }
155
156 this->beginResetModel();
157 for (int i = 0; i < filteredFrames.count(); i++)
158 {
159 filteredFrames[i].setTimeStamp(QCanBusFrame::TimeStamp(0, filteredFrames[i].timeStamp().microSeconds() - timeOffset));
160 }
161 this->endResetModel();
162
163 mutex.unlock();
164 }
165
setOverwriteMode(bool mode)166 void CANFrameModel::setOverwriteMode(bool mode)
167 {
168 beginResetModel();
169 overwriteDups = mode;
170 recalcOverwrite();
171 endResetModel();
172 }
173
setFilterState(unsigned int ID,bool state)174 void CANFrameModel::setFilterState(unsigned int ID, bool state)
175 {
176 if (!filters.contains(ID)) return;
177 filters[ID] = state;
178 sendRefresh();
179 }
180
setBusFilterState(unsigned int BusID,bool state)181 void CANFrameModel::setBusFilterState(unsigned int BusID, bool state)
182 {
183 if (!busFilters.contains(BusID)) return;
184 busFilters[BusID] = state;
185 sendRefresh();
186 }
187
setAllFilters(bool state)188 void CANFrameModel::setAllFilters(bool state)
189 {
190 QMap<int, bool>::iterator it;
191 for (it = filters.begin(); it != filters.end(); ++it)
192 {
193 it.value() = state;
194 }
195 sendRefresh();
196 }
197
198 /*
199 * There is probably a more correct way to have done this but below are several functions that collectively implement
200 * quicksort on the columns and interpret the columns numerically. But, correct or not, this implementation is quite fast
201 * and sorts the columns properly.
202 */
getCANFrameVal(int row,Column col)203 uint64_t CANFrameModel::getCANFrameVal(int row, Column col)
204 {
205 uint64_t temp = 0;
206 if (row >= frames.count()) return 0;
207 CANFrame frame = frames[row];
208 switch (col)
209 {
210 case Column::TimeStamp:
211 if (overwriteDups) return frame.timedelta;
212 return frame.timeStamp().microSeconds();
213 case Column::FrameId:
214 return frame.frameId();
215 case Column::Extended:
216 if (frame.hasExtendedFrameFormat()) return 1;
217 return 0;
218 case Column::Remote:
219 if (overwriteDups) return frame.frameCount;
220 if (frame.frameType() == QCanBusFrame::RemoteRequestFrame) return 1;
221 return 0;
222 case Column::Direction:
223 if (frame.isReceived) return 1;
224 return 0;
225 case Column::Bus:
226 return static_cast<uint64_t>(frame.bus);
227 case Column::Length:
228 return static_cast<uint64_t>(frame.payload().length());
229 case Column::ASCII: //sort both the same for now
230 case Column::Data:
231 for (int i = 0; i < frame.payload().length(); i++) temp += (static_cast<uint64_t>(frame.payload()[i]) << (56 - (8 * i)));
232 //qDebug() << temp;
233 return temp;
234 case Column::NUM_COLUMN:
235 return 0;
236 }
237 return 0;
238 }
239
qSortCANFrameAsc(QVector<CANFrame> * frames,Column column,int lowerBound,int upperBound)240 void CANFrameModel::qSortCANFrameAsc(QVector<CANFrame> *frames, Column column, int lowerBound, int upperBound)
241 {
242 int p, i, j;
243 qDebug() << "Lower " << lowerBound << " Upper" << upperBound;
244 if (lowerBound < upperBound)
245 {
246 uint64_t piv = getCANFrameVal(lowerBound + (upperBound - lowerBound) / 2, column);
247 i = lowerBound - 1;
248 j = upperBound + 1;
249 for (;;){
250 do {
251 i++;
252 } while ((i < upperBound) && getCANFrameVal(i, column) < piv);
253
254 do
255 {
256 j--;
257 } while ((j > lowerBound) && getCANFrameVal(j, column) > piv);
258 if (i < j) {
259 CANFrame temp = frames->at(i);
260 frames->replace(i, frames->at(j));
261 frames->replace(j, temp);
262 }
263 else {p = j; break;}
264 }
265
266 qSortCANFrameAsc(frames, column, lowerBound, p);
267 qSortCANFrameAsc(frames, column, p+1, upperBound);
268 }
269 }
270
qSortCANFrameDesc(QVector<CANFrame> * frames,Column column,int lowerBound,int upperBound)271 void CANFrameModel::qSortCANFrameDesc(QVector<CANFrame> *frames, Column column, int lowerBound, int upperBound)
272 {
273 int p, i, j;
274 qDebug() << "Lower " << lowerBound << " Upper" << upperBound;
275 if (lowerBound < upperBound)
276 {
277 uint64_t piv = getCANFrameVal(lowerBound + (upperBound - lowerBound) / 2, column);
278 i = lowerBound - 1;
279 j = upperBound + 1;
280 for (;;){
281 do {
282 i++;
283 } while ((i < upperBound) && getCANFrameVal(i, column) > piv);
284
285 do
286 {
287 j--;
288 } while ((j > lowerBound) && getCANFrameVal(j, column) < piv);
289 if (i < j) {
290 CANFrame temp = frames->at(i);
291 frames->replace(i, frames->at(j));
292 frames->replace(j, temp);
293 }
294 else {p = j; break;}
295 }
296
297 qSortCANFrameDesc(frames, column, lowerBound, p);
298 qSortCANFrameDesc(frames, column, p+1, upperBound);
299 }
300 }
301
sortByColumn(int column)302 void CANFrameModel::sortByColumn(int column)
303 {
304 sortDirAsc = !sortDirAsc;
305 //beginResetModel();
306 if (sortDirAsc) qSortCANFrameAsc(&frames, Column(column), 0, frames.count()-1);
307 else qSortCANFrameDesc(&frames, Column(column), 0, frames.count()-1);
308 //endResetModel();
309 sendRefresh();
310 }
311
312 //End of custom sorting code
313
recalcOverwrite()314 void CANFrameModel::recalcOverwrite()
315 {
316 if (!overwriteDups) return; //no need to do a thing if mode is disabled
317
318 qDebug() << "recalcOverwrite called in model";
319
320 mutex.lock();
321 beginResetModel();
322
323 //Look at the current list of frames and turn it into just a list of unique IDs
324 QHash<uint64_t, CANFrame> overWriteFrames;
325 uint64_t idAugmented; //id in lower 29 bits, bus number shifted up 29 bits
326 foreach(CANFrame frame, frames)
327 {
328 if (frame.frameType() != frame.DataFrame) continue;
329
330 idAugmented = frame.frameId();
331 idAugmented = idAugmented + (frame.bus << 29ull);
332 if (!overWriteFrames.contains(idAugmented))
333 {
334 frame.timedelta = 0;
335 frame.frameCount = 1;
336 overWriteFrames.insert(idAugmented, frame);
337 }
338 else
339 {
340 frame.timedelta = frame.timeStamp().microSeconds() - overWriteFrames[idAugmented].timeStamp().microSeconds();
341 frame.frameCount = overWriteFrames[idAugmented].frameCount + 1;
342 overWriteFrames[idAugmented] = frame;
343 }
344 }
345 //Then replace the old list of frames with just the unique list
346 frames.clear();
347 frames.append(overWriteFrames.values().toVector());
348
349 filteredFrames.clear();
350 filteredFrames.reserve(preallocSize);
351
352 for (int i = 0; i < frames.count(); i++)
353 {
354 if (filters[frames[i].frameId()] && busFilters[frames[i].bus])
355 {
356 filteredFrames.append(frames[i]);
357 }
358 }
359
360 endResetModel();
361 mutex.unlock();
362 }
363
data(const QModelIndex & index,int role) const364 QVariant CANFrameModel::data(const QModelIndex &index, int role) const
365 {
366 QString tempString;
367 CANFrame thisFrame;
368 static bool rowFlip = false;
369 QVariant ts;
370
371 if (!index.isValid())
372 return QVariant();
373
374 if (index.row() >= (filteredFrames.count()))
375 return QVariant();
376
377 thisFrame = filteredFrames.at(index.row());
378
379 const unsigned char *data = reinterpret_cast<const unsigned char *>(thisFrame.payload().constData());
380 int dataLen = thisFrame.payload().count();
381
382 if (role == Qt::BackgroundColorRole)
383 {
384 if (dbcHandler != nullptr && interpretFrames)
385 {
386 DBC_MESSAGE *msg = dbcHandler->findMessage(thisFrame);
387 if (msg != nullptr)
388 {
389 return msg->bgColor;
390 }
391 }
392 rowFlip = (index.row() % 2);
393 if (rowFlip) return QApplication::palette().color(QPalette::Base);
394 else return QApplication::palette().color(QPalette::AlternateBase);
395 }
396
397 if (role == Qt::TextAlignmentRole)
398 {
399 switch(Column(index.column()))
400 {
401 case Column::TimeStamp:
402 return Qt::AlignRight;
403 case Column::FrameId:
404 case Column::Direction:
405 case Column::Extended:
406 case Column::Bus:
407 case Column::Remote:
408 case Column::Length:
409 return Qt::AlignHCenter;
410 default:
411 return Qt::AlignLeft;
412 }
413 }
414
415 if (role == Qt::TextColorRole)
416 {
417 if (dbcHandler != nullptr && interpretFrames)
418 {
419 DBC_MESSAGE *msg = dbcHandler->findMessage(thisFrame);
420 if (msg != nullptr)
421 {
422 return msg->fgColor;
423 }
424 }
425 return QApplication::palette().color(QPalette::WindowText);
426 }
427
428 if (role == Qt::DisplayRole) {
429 switch (Column(index.column()))
430 {
431 case Column::TimeStamp:
432 //Reformatting the output a bit with custom code
433 if (overwriteDups)
434 {
435 if (timeSeconds) return QString::number(thisFrame.timedelta / 1000000.0, 'f', 5);
436 return QString::number(thisFrame.timedelta);
437 }
438 else ts = Utility::formatTimestamp(thisFrame.timeStamp().microSeconds());
439 if (ts.type() == QVariant::Double) return QString::number(ts.toDouble(), 'f', 5); //never scientific notation, 5 decimal places
440 if (ts.type() == QVariant::LongLong) return QString::number(ts.toLongLong()); //never scientific notion, all digits shown
441 if (ts.type() == QVariant::DateTime) return ts.toDateTime().toString(timeFormat); //custom set format for dates and times
442 return Utility::formatTimestamp(thisFrame.timeStamp().microSeconds());
443 case Column::FrameId:
444 return Utility::formatCANID(thisFrame.frameId(), thisFrame.hasExtendedFrameFormat());
445 case Column::Extended:
446 return QString::number(thisFrame.hasExtendedFrameFormat());
447 case Column::Remote:
448 if (!overwriteDups) return QString::number(thisFrame.frameType() == QCanBusFrame::RemoteRequestFrame);
449 return QString::number(thisFrame.frameCount);
450 case Column::Direction:
451 if (thisFrame.isReceived) return QString(tr("Rx"));
452 return QString(tr("Tx"));
453 case Column::Bus:
454 return QString::number(thisFrame.bus);
455 case Column::Length:
456 return QString::number(dataLen);
457 case Column::ASCII:
458 if (thisFrame.frameId() >= 0x7FFFFFF0ull)
459 {
460 tempString.append("MARK ");
461 tempString.append(QString::number(thisFrame.frameId() & 0x7));
462 return tempString;
463 }
464 if (thisFrame.frameType() == QCanBusFrame::DataFrame) {
465 if (dataLen < 0) dataLen = 0;
466 //if (dLen > 8) dLen = 8;
467 for (int i = 0; i < dataLen; i++)
468 {
469 char byt = thisFrame.payload()[i];
470 //0x20 through 0x7E are printable characters. Outside of that range they aren't. So use dots instead
471 if (byt < 0x20) byt = 0x2E; //dot character
472 if (byt > 0x7E) byt = 0x2E;
473 tempString.append(QString::fromUtf8(&byt, 1));
474 }
475 }
476 if (thisFrame.frameType() == QCanBusFrame::ErrorFrame)
477 {
478 tempString = "ERROR";
479 }
480 return tempString;
481 case Column::Data:
482 if (dataLen < 0) dataLen = 0;
483 //if (useHexMode) tempString.append("0x ");
484 if (thisFrame.frameType() == QCanBusFrame::RemoteRequestFrame) {
485 return tempString;
486 }
487 for (int i = 0; i < dataLen; i++)
488 {
489 if (useHexMode) tempString.append( QString::number(data[i], 16).toUpper().rightJustified(2, '0'));
490 else tempString.append(QString::number(data[i], 10));
491 tempString.append(" ");
492 }
493 if (thisFrame.frameType() == thisFrame.ErrorFrame)
494 {
495 if (thisFrame.error() & thisFrame.TransmissionTimeoutError) tempString.append("\nTX Timeout");
496 if (thisFrame.error() & thisFrame.LostArbitrationError) tempString.append("\nLost Arbitration");
497 if (thisFrame.error() & thisFrame.ControllerError) tempString.append("\nController Error");
498 if (thisFrame.error() & thisFrame.ProtocolViolationError) tempString.append("\nProtocol Violation");
499 if (thisFrame.error() & thisFrame.TransceiverError) tempString.append("\nTransceiver Error");
500 if (thisFrame.error() & thisFrame.MissingAcknowledgmentError) tempString.append("\nMissing ACK");
501 if (thisFrame.error() & thisFrame.BusOffError) tempString.append("\nBus OFF");
502 if (thisFrame.error() & thisFrame.BusError) tempString.append("\nBus ERR");
503 if (thisFrame.error() & thisFrame.ControllerRestartError) tempString.append("\nController restart err");
504 if (thisFrame.error() & thisFrame.UnknownError) tempString.append("\nUnknown error type");
505 }
506 //TODO: technically the actual returned bytes for an error frame encode some more info. Not interpreting it yet.
507
508 //now, if we're supposed to interpret the data and the DBC handler is loaded then use it
509 if ( (dbcHandler != nullptr) && interpretFrames && (thisFrame.frameType() == thisFrame.DataFrame) )
510 {
511 DBC_MESSAGE *msg = dbcHandler->findMessage(thisFrame);
512 if (msg != nullptr)
513 {
514 tempString.append(" <" + msg->name + ">\n");
515 if (msg->comment.length() > 1) tempString.append(msg->comment + "\n");
516 for (int j = 0; j < msg->sigHandler->getCount(); j++)
517 {
518 QString sigString;
519 DBC_SIGNAL* sig = msg->sigHandler->findSignalByIdx(j);
520
521 if ( (sig->multiplexParent == nullptr) && sig->processAsText(thisFrame, sigString))
522 {
523 tempString.append(sigString);
524 tempString.append("\n");
525 if (sig->isMultiplexor)
526 {
527 qDebug() << "Multiplexor. Diving into the tree";
528 tempString.append(sig->processSignalTree(thisFrame));
529 }
530 }
531 else if (sig->isMultiplexed && overwriteDups) //wasn't in this exact frame but is in the message. Use cached value
532 {
533 bool isInteger = false;
534 if (sig->valType == UNSIGNED_INT || sig->valType == SIGNED_INT) isInteger = true;
535 tempString.append(sig->makePrettyOutput(sig->cachedValue.toDouble(), sig->cachedValue.toLongLong(), true, isInteger));
536 tempString.append("\n");
537 }
538 }
539 }
540 }
541 return tempString;
542 default:
543 return tempString;
544 }
545 }
546
547 return QVariant();
548 }
549
headerData(int section,Qt::Orientation orientation,int role) const550 QVariant CANFrameModel::headerData(int section, Qt::Orientation orientation,
551 int role) const
552 {
553 if (role != Qt::DisplayRole)
554 return QVariant();
555
556 if (orientation == Qt::Horizontal)
557 {
558 switch (Column(section))
559 {
560 case Column::TimeStamp:
561 if (overwriteDups) return QString(tr("Time Delta"));
562 return QString(tr("Timestamp"));
563 case Column::FrameId:
564 return QString(tr("ID"));
565 case Column::Extended:
566 return QString(tr("Ext"));
567 case Column::Remote:
568 if (!overwriteDups) return QString(tr("RTR"));
569 return QString(tr("Cnt"));
570 case Column::Direction:
571 return QString(tr("Dir"));
572 case Column::Bus:
573 return QString(tr("Bus"));
574 case Column::Length:
575 return QString(tr("Len"));
576 case Column::ASCII:
577 return QString(tr("ASCII"));
578 case Column::Data:
579 return QString(tr("Data"));
580 default:
581 return QString("");
582 }
583 }
584
585 else
586 return QString::number(section + 1);
587
588 return QVariant();
589 }
590
any_filters_are_configured(void)591 bool CANFrameModel::any_filters_are_configured(void)
592 {
593 for (auto const &val : filters)
594 {
595 if (val == true)
596 continue;
597 else
598 return true;
599 }
600 return false;
601 }
602
any_busfilters_are_configured(void)603 bool CANFrameModel::any_busfilters_are_configured(void)
604 {
605 for (auto const &val : busFilters)
606 {
607 if (val == true)
608 continue;
609 else
610 return true;
611 }
612 return false;
613 }
614
615
addFrame(const CANFrame & frame,bool autoRefresh=false)616 void CANFrameModel::addFrame(const CANFrame& frame, bool autoRefresh = false)
617 {
618 /*TODO: remove mutex */
619 mutex.lock();
620 CANFrame tempFrame;
621 tempFrame = frame;
622
623 tempFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, tempFrame.timeStamp().microSeconds() - timeOffset));
624
625 lastUpdateNumFrames++;
626
627 //if this ID isn't found in the filters list then add it and show it by default
628 if (!filters.contains(tempFrame.frameId()))
629 {
630 // if there are any filters already configured, leave the new filter disabled
631 if (any_filters_are_configured())
632 filters.insert(tempFrame.frameId(), false);
633 else
634 filters.insert(tempFrame.frameId(), true);
635 needFilterRefresh = true;
636 }
637
638 //if this BusID isn't found in the busFilters list then add it and show it by default
639 if (!busFilters.contains(tempFrame.bus))
640 {
641 // if there are any busFilters already configured, leave the new filter disabled
642 if (any_busfilters_are_configured())
643 busFilters.insert(tempFrame.bus, false);
644 else
645 busFilters.insert(tempFrame.bus, true);
646 needFilterRefresh = true;
647 }
648
649 if (!overwriteDups)
650 {
651 frames.append(tempFrame);
652 if (filters[tempFrame.frameId()] && busFilters[tempFrame.bus])
653 {
654 if (autoRefresh) beginInsertRows(QModelIndex(), filteredFrames.count(), filteredFrames.count());
655 tempFrame.frameCount = 1;
656 filteredFrames.append(tempFrame);
657 if (autoRefresh) endInsertRows();
658 }
659 }
660 else //yes, overwrite dups
661 {
662 bool found = false;
663 for (int i = 0; i < frames.count(); i++)
664 {
665 if ( (frames[i].frameId() == tempFrame.frameId()) && (frames[i].bus == tempFrame.bus) )
666 {
667 tempFrame.frameCount = frames[i].frameCount + 1;
668 tempFrame.timedelta = tempFrame.timeStamp().microSeconds() - frames[i].timeStamp().microSeconds();
669 frames.replace(i, tempFrame);
670 found = true;
671 break;
672 }
673 }
674 if (!found)
675 {
676 frames.append(tempFrame);
677 if (filters[tempFrame.frameId()] && busFilters[tempFrame.bus])
678 {
679 if (autoRefresh) beginInsertRows(QModelIndex(), filteredFrames.count(), filteredFrames.count());
680 tempFrame.frameCount = 1;
681 tempFrame.timedelta = 0;
682 filteredFrames.append(tempFrame);
683 if (autoRefresh) endInsertRows();
684 }
685 }
686 else
687 {
688 for (int j = 0; j < filteredFrames.count(); j++)
689 {
690 if ( (filteredFrames[j].frameId() == tempFrame.frameId()) && (filteredFrames[j].bus == tempFrame.bus) )
691 {
692 if (autoRefresh) beginResetModel();
693 filteredFrames.replace(j, tempFrame);
694 if (autoRefresh) endResetModel();
695 }
696 }
697 }
698 }
699
700 mutex.unlock();
701 }
702
703
addFrames(const CANConnection *,const QVector<CANFrame> & pFrames)704 void CANFrameModel::addFrames(const CANConnection*, const QVector<CANFrame>& pFrames)
705 {
706 foreach(const CANFrame& frame, pFrames)
707 {
708 addFrame(frame);
709 }
710 if (overwriteDups) //if in overwrite mode we'll update every time frames come in
711 {
712 beginResetModel();
713 endResetModel();
714 }
715 }
716
sendRefresh()717 void CANFrameModel::sendRefresh()
718 {
719 qDebug() << "Sending mass refresh";
720 QVector<CANFrame> tempContainer;
721 int count = frames.count();
722 for (int i = 0; i < count; i++)
723 {
724 if (filters[frames[i].frameId()] && busFilters[frames[i].bus])
725 {
726 tempContainer.append(frames[i]);
727 }
728 }
729 mutex.lock();
730 beginResetModel();
731 filteredFrames.clear();
732 filteredFrames.reserve(preallocSize);
733 filteredFrames.append(tempContainer);
734
735 lastUpdateNumFrames = 0;
736 endResetModel();
737 mutex.unlock();
738 }
739
sendRefresh(int pos)740 void CANFrameModel::sendRefresh(int pos)
741 {
742 beginInsertRows(QModelIndex(), pos, pos);
743 endInsertRows();
744 }
745
746 //issue a refresh for the last num entries in the model.
747 //used by the serial worker to do batch updates so it doesn't
748 //have to send thousands of messages per second
sendBulkRefresh()749 int CANFrameModel::sendBulkRefresh()
750 {
751 //int num = filteredFrames.count() - lastUpdateNumFrames;
752 if (lastUpdateNumFrames <= 0) return 0;
753
754 if (lastUpdateNumFrames == 0 && !overwriteDups) return 0;
755 if (filteredFrames.count() == 0) return 0;
756
757 qDebug() << "Bulk refresh of " << lastUpdateNumFrames;
758
759 beginResetModel();
760 endResetModel();
761
762 int num = lastUpdateNumFrames;
763 lastUpdateNumFrames = 0;
764
765 return num;
766 }
767
clearFrames()768 void CANFrameModel::clearFrames()
769 {
770 mutex.lock();
771 this->beginResetModel();
772 frames.clear();
773 filteredFrames.clear();
774 filters.clear();
775 busFilters.clear();
776 frames.reserve(preallocSize);
777 filteredFrames.reserve(preallocSize);
778 this->endResetModel();
779 lastUpdateNumFrames = 0;
780 mutex.unlock();
781
782 emit updatedFiltersList();
783 }
784
785 /*
786 * Since the getListReference function returns readonly
787 * you can't insert frames with it. Instead this function
788 * allows for a mass import of frames into the model
789 */
insertFrames(const QVector<CANFrame> & newFrames)790 void CANFrameModel::insertFrames(const QVector<CANFrame> &newFrames)
791 {
792 //not resetting the model here because the serial worker automatically does a bulk refresh every 1/4 second
793 //and that refresh will cause the view to update. If you do both it usually ends up thinking you have
794 //double the number of frames.
795 //beginResetModel();
796 mutex.lock();
797 int insertedFiltered = 0;
798 for (int i = 0; i < newFrames.count(); i++)
799 {
800 frames.append(newFrames[i]);
801 if (!filters.contains(newFrames[i].frameId()))
802 {
803 filters.insert(newFrames[i].frameId(), true);
804 needFilterRefresh = true;
805 }
806 if (filters[newFrames[i].frameId()])
807 {
808 busFilters.insert(newFrames[i].bus, true);
809 needFilterRefresh = true;
810 }
811 if (filters[newFrames[i].frameId()] && busFilters[newFrames[i].bus])
812 {
813 insertedFiltered++;
814 filteredFrames.append(newFrames[i]);
815 }
816 }
817 lastUpdateNumFrames = newFrames.count();
818 mutex.unlock();
819 //endResetModel();
820 //beginInsertRows(QModelIndex(), filteredFrames.count() + 1, filteredFrames.count() + insertedFiltered);
821 //endInsertRows();
822 if (needFilterRefresh) emit updatedFiltersList();
823 }
824
getIndexFromTimeID(unsigned int ID,double timestamp)825 int CANFrameModel::getIndexFromTimeID(unsigned int ID, double timestamp)
826 {
827 int bestIndex = -1;
828 int64_t intTimeStamp = static_cast<int64_t> (timestamp * 1000000l);
829 for (int i = 0; i < frames.count(); i++)
830 {
831 if ((frames[i].frameId() == ID))
832 {
833 if (frames[i].timeStamp().microSeconds() <= intTimeStamp) bestIndex = i;
834 else break; //drop out of loop as soon as we pass the proper timestamp
835 }
836 }
837 return bestIndex;
838 }
839
loadFilterFile(QString filename)840 void CANFrameModel::loadFilterFile(QString filename)
841 {
842 QFile *inFile = new QFile(filename);
843 QByteArray line;
844 int ID;
845
846 if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text))
847 return;
848
849 filters.clear();
850 busFilters.clear();
851
852 while (!inFile->atEnd()) {
853 line = inFile->readLine().simplified();
854 if (line.length() > 2)
855 {
856 QList<QByteArray> tokens = line.split(',');
857 ID = tokens[0].toInt(nullptr, 16);
858 if (tokens[1].toUpper() == "T") filters.insert(ID, true);
859 else filters.insert(ID, false);
860 }
861 }
862 inFile->close();
863
864 sendRefresh();
865
866 emit updatedFiltersList();
867 }
868
saveFilterFile(QString filename)869 void CANFrameModel::saveFilterFile(QString filename)
870 {
871 QFile *outFile = new QFile(filename);
872
873 if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text))
874 return;
875
876 QMap<int, bool>::const_iterator it;
877 for (it = filters.begin(); it != filters.end(); ++it)
878 {
879 outFile->write(QString::number(it.key(), 16).toUtf8());
880 outFile->putChar(',');
881 if (it.value()) outFile->putChar('T');
882 else outFile->putChar('F');
883 outFile->write("\n");
884 }
885 outFile->close();
886 }
887
needsFilterRefresh()888 bool CANFrameModel::needsFilterRefresh()
889 {
890 bool temp = needFilterRefresh;
891 needFilterRefresh = false;
892 return temp;
893 }
894
895 /*
896 *This used to not be const correct but it is now. So, there's little harm in
897 * allowing external code to peek at our frames. There's just no touching.
898 * This ability to get a direct read-only reference speeds up a variety of
899 * external code that needs to access frames directly and doesn't care about
900 * this model's normal output mechanism.
901 */
getListReference() const902 const QVector<CANFrame>* CANFrameModel::getListReference() const
903 {
904 return &frames;
905 }
906
getFilteredListReference() const907 const QVector<CANFrame>* CANFrameModel::getFilteredListReference() const
908 {
909 return &filteredFrames;
910 }
911
getFiltersReference() const912 const QMap<int, bool>* CANFrameModel::getFiltersReference() const
913 {
914 return &filters;
915 }
916
getBusFiltersReference() const917 const QMap<int, bool>* CANFrameModel::getBusFiltersReference() const
918 {
919 return &busFilters;
920 }
921