diff --git a/mainwindow.cpp b/mainwindow.cpp index 59a8d84..d5826c0 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -80,6 +80,7 @@ MainWindow::MainWindow(QWidget *parent) : fuzzingWindow = NULL; udsScanWindow = NULL; isoWindow = NULL; + snifferWindow = NULL; dbcHandler = new DBCHandler; bDirty = false; inhibitFilterUpdate = false; @@ -123,6 +124,7 @@ MainWindow::MainWindow(QWidget *parent) : connect(ui->actionFuzzing, &QAction::triggered, this, &MainWindow::showFuzzingWindow); connect(ui->actionUDS_Scanner, &QAction::triggered, this, &MainWindow::showUDSScanWindow); connect(ui->actionISO_TP_Decoder, &QAction::triggered, this, &MainWindow::showISOInterpreterWindow); + connect(ui->actionSniffer, &QAction::triggered, this, &MainWindow::showSnifferWindow); connect(CANConManager::getInstance(), &CANConManager::framesReceived, model, &CANFrameModel::addFrames); @@ -264,6 +266,12 @@ MainWindow::~MainWindow() isoWindow->close(); delete isoWindow; } + if (snifferWindow) + { + snifferWindow->close(); + delete snifferWindow; + snifferWindow = NULL; + } delete elapsedTime; @@ -291,6 +299,7 @@ void MainWindow::exitApp() if (fuzzingWindow) fuzzingWindow->close(); if (udsScanWindow) udsScanWindow->close(); if (isoWindow) isoWindow->close(); + if (snifferWindow) snifferWindow->close(); this->close(); } @@ -839,6 +848,14 @@ void MainWindow::showISOInterpreterWindow() isoWindow->show(); } +void MainWindow::showSnifferWindow() +{ + if (!snifferWindow) + snifferWindow = new SnifferWindow(this); + snifferWindow->show(); +} + + void MainWindow::showFrameSenderWindow() { if (!frameSenderWindow) diff --git a/mainwindow.h b/mainwindow.h index 191008a..a2925f5 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -25,6 +25,7 @@ #include "dbcloadsavewindow.h" #include "fuzzingwindow.h" #include "udsscanwindow.h" +#include "re/sniffer/snifferwindow.h" #include "isotp_interpreterwindow.h" class ConnectionWindow; @@ -67,6 +68,7 @@ private slots: void showFuzzingWindow(); void showUDSScanWindow(); void showISOInterpreterWindow(); + void showSnifferWindow(); void exitApp(); void handleSaveDecoded(); void connectionFailed(); @@ -137,6 +139,7 @@ private: FuzzingWindow *fuzzingWindow; UDSScanWindow *udsScanWindow; ISOTP_InterpreterWindow *isoWindow; + SnifferWindow* snifferWindow; //various private storage QLabel lbStatusConnected; diff --git a/mainwindow.ui b/mainwindow.ui index a4850ac..1c6437c 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -214,6 +214,7 @@ + @@ -395,6 +396,11 @@ ISO-TP Decoder + + + Sniffer + + diff --git a/re/sniffer/snifferitem.cpp b/re/sniffer/snifferitem.cpp new file mode 100644 index 0000000..0fe870b --- /dev/null +++ b/re/sniffer/snifferitem.cpp @@ -0,0 +1,86 @@ +#include +#include +#include "snifferitem.h" + + +SnifferItem::SnifferItem(const CANFrame& pFrame): + mNotch(0) +{ + mID = pFrame.ID; + mMarker = {0,0}; + mLastMarker = {0,0}; + /* that's dirty */ + update(pFrame); + update(pFrame); +} + + +SnifferItem::~SnifferItem() +{ + +} + +quint64 SnifferItem::getId() const +{ + return mID; +} + +float SnifferItem::getDelta() const +{ + return ((float)(mCurrentTime-mLastTime))/1000000; +} + +int SnifferItem::getData(uchar i) const +{ + return (i>=mCurrent.len) ? -1 : ((uchar*) &mCurrent.data)[i]; +} + +dc SnifferItem::dataChange(uchar i) const +{ + if( i= ((uchar*) &mLast.data)[i] ? dc::INC : dc::DEC; + } + + return dc::NO; +} + +int SnifferItem::elapsed() const +{ + return mTime.elapsed(); +} + +void SnifferItem::update(const CANFrame& pFrame) +{ + /* copy current to last */ + mLast = mCurrent; + mLastTime = mCurrentTime; + + /* copy new value */ + memcpy(&mCurrent.data, pFrame.data, 8); + mCurrent.len = pFrame.len; + mCurrentTime = pFrame.timestamp; + + /* update marker */ + mMarker.data |= mLast.data ^ mCurrent.data; + mMarker.len |= mLast.len ^ mCurrent.data; + + /* restart timeout */ + mTime.restart(); +} + +void SnifferItem::updateMarker() +{ + mLastMarker = mMarker; + mMarker = {0, 0}; +} + +void SnifferItem::notch(bool pNotch) +{ + if(pNotch) + mNotch |= mLastMarker.data; + else + mNotch = 0; +} diff --git a/re/sniffer/snifferitem.h b/re/sniffer/snifferitem.h new file mode 100644 index 0000000..cc06c3a --- /dev/null +++ b/re/sniffer/snifferitem.h @@ -0,0 +1,49 @@ +#ifndef SNIFFERITEM_H +#define SNIFFERITEM_H + +#include +#include +#include "can_structs.h" + +struct fstCan +{ + quint64 data; + int len; +}; + +enum dc +{ + NO, + INC, + DEC +}; + + +class SnifferItem +{ +public: + explicit SnifferItem(const CANFrame& pFrame); + virtual ~SnifferItem(); + + quint64 getId() const; + float getDelta() const; + int getData(uchar i) const; + dc dataChange(uchar) const; + int elapsed() const; + void update(const CANFrame& pFrame); + void updateMarker(); + void notch(bool); +private: + quint64 mID; + struct fstCan mLast; + struct fstCan mCurrent; + struct fstCan mLastMarker; + struct fstCan mMarker; + quint64 mNotch; + quint64 mLastTime; + quint64 mCurrentTime; + + QTime mTime; +}; + +#endif // SNIFFERITEM_H diff --git a/re/sniffer/sniffermodel.cpp b/re/sniffer/sniffermodel.cpp new file mode 100644 index 0000000..a762346 --- /dev/null +++ b/re/sniffer/sniffermodel.cpp @@ -0,0 +1,267 @@ +#include +#include "sniffermodel.h" +#include "snifferwindow.h" + + +SnifferModel::SnifferModel(QObject *parent) + : QAbstractItemModel(parent), + mFilter(false) +{ +} + +SnifferModel::~SnifferModel() +{ + clear(); +} + + +int SnifferModel::columnCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : tc::LAST+1; +} + + +int SnifferModel::rowCount(const QModelIndex &parent) const +{ + const QMap& map = mFilter ? mFilters : mMap; + return parent.isValid() ? 0 : map.size(); +} + + +QVariant SnifferModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + const QMap& map = mFilter ? mFilters : mMap; + + if(index.row()<=map.size()) + { + quint64 key = map.keys()[index.row()]; + SnifferItem* item = map[key]; + + int col = index.column(); + + switch(role) + { + case Qt::DisplayRole: + { + switch(col) + { + case tc::DELTA: + return QString::number(item->getDelta(), 'f'); + case tc::ID: + return QString("%1").arg(item->getId(), 5, 16, QLatin1Char(' ')).toUpper(); + default: + break; + } + if(tc::DATA_0<=col && col <=tc::DATA_7) + { + int data = item->getData(col-tc::DATA_0); + if(data>=0) + return QString("%1").arg(data, 2, 16, QLatin1Char('0')).toUpper(); + } + break; + } + case Qt::BackgroundRole: + { + if(tc::ID==col) + { + if(item->elapsed()>4000) + return QBrush(Qt::red); + } + else if(tc::DATA_0<=col && col<=tc::DATA_7) + { + dc change = item->dataChange(col-tc::DATA_0); + switch(change) + { + case dc::INC: + return QBrush(Qt::green); + case dc::DEC: + return QBrush(Qt::red); + default: + break; + } + } + break; + } + } + } + + return QVariant(); +} + + +Qt::ItemFlags SnifferModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return 0; + + return QAbstractItemModel::flags(index); +} + + +QVariant SnifferModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + { + switch(section) + { + case tc::DELTA: + return QString("Delta"); + case tc::ID: + return QString("ID"); + default: + break; + } + if(tc::DATA_0<=section && section <=tc::DATA_7) + return QString::number(section-tc::DATA_0); + } + + return QVariant(); +} + + +QModelIndex SnifferModel::index(int row, int column, const QModelIndex &parent) + const +{ + if (parent.isValid()) + return QModelIndex(); + + const QMap& map = mFilter ? mFilters : mMap; + + if(column>tc::LAST || row>=map.size()) + return QModelIndex(); + + return createIndex(row, column); +} + + +QModelIndex SnifferModel::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + + +void SnifferModel::clear() +{ + beginResetModel(); + qDeleteAll(mMap); + mMap.clear(); + mFilters.clear(); + endResetModel(); + + mFilter = false; +} + + +void SnifferModel::refresh() +{ + QMap::iterator i; + QVector toRemove; + SnifferItem* item; + + /* update markers */ + + + for (i = mMap.begin(); i != mMap.end(); ++i) + { + i.value()->updateMarker(); + if(i.value()->elapsed()>5000) + toRemove.append(i.key()); + } + + if(toRemove.size()) + { + beginResetModel(); + foreach(quint32 id, toRemove) + { + /* remove element */ + item = mMap.take(id); + mFilters.remove(id); + delete item; + /* send notification */ + emit idChange(id, false); + } + endResetModel(); + } + else + /* refresh data */ + dataChanged(createIndex(0, 0), + createIndex(rowCount()-1, columnCount()-1), QVector(Qt::DisplayRole)); +} + + +void SnifferModel::filter(fltType pType, int pId) +{ + beginResetModel(); + switch(pType) + { + case fltType::NONE: + /* erase everything */ + mFilter = true; + mFilters.clear(); + break; + case fltType::ADD: + /* add filter to list */ + mFilter = true; + mFilters[pId] = mMap[pId]; + break; + case fltType::REMOVE: + /* remove filter */ + if(!mFilter) + mFilters = mMap; + mFilter = true; + mFilters.remove(pId); + break; + case fltType::ALL: + /* stop filtering */ + mFilter = false; + mFilters.clear(); + break; + } + endResetModel(); +} + + +/***********************************************/ +/********** slots ****************/ +/***********************************************/ + +void SnifferModel::update(CANConnection*, QVector& pFrames) +{ + foreach(const CANFrame& frame, pFrames) + { + if(!mMap.contains(frame.ID)) + { + int index = std::distance(mMap.begin(), mMap.lowerBound(frame.ID)); + /* add the frame */ + beginInsertRows(QModelIndex(), index, index); + mMap[frame.ID] = new SnifferItem(frame); + endInsertRows(); + + emit idChange(frame.ID, true); + } + else + //updateData + mMap[frame.ID]->update(frame); + } +} + +void SnifferModel::notch() +{ + QMap& map = mFilter ? mFilters : mMap; + + foreach(SnifferItem* item, map) + item->notch(true); +} + +void SnifferModel::unNotch() +{ + QMap& map = mFilter ? mFilters : mMap; + + foreach(SnifferItem* item, map) + item->notch(false); +} + + diff --git a/re/sniffer/sniffermodel.h b/re/sniffer/sniffermodel.h new file mode 100644 index 0000000..7c3fc19 --- /dev/null +++ b/re/sniffer/sniffermodel.h @@ -0,0 +1,58 @@ +#ifndef SNIFFERMODEL_H +#define SNIFFERMODEL_H + +#include +#include +#include +#include + +#include "can_structs.h" +#include "connections/canconnection.h" +#include "snifferitem.h" + + +enum fltType +{ + ALL, + ADD, + REMOVE, + NONE +}; + +class SnifferModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit SnifferModel(QObject *parent = 0); + virtual ~SnifferModel(); + + /* from QAbstractItemModel */ + QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; + Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE; + int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + void refresh(); + void clear(); + void filter(fltType pType, int pId=0); + +public slots: + void update(CANConnection*, QVector&); + void notch(); + void unNotch(); + +signals: + void idChange(int, bool); + +private: + QMap mMap; + QMap mFilters; + bool mFilter; +}; + +#endif // SNIFFERMODEL_H diff --git a/re/sniffer/snifferwindow.cpp b/re/sniffer/snifferwindow.cpp new file mode 100644 index 0000000..cab2a88 --- /dev/null +++ b/re/sniffer/snifferwindow.cpp @@ -0,0 +1,132 @@ +#include +#include +#include "snifferwindow.h" +#include "ui_snifferwindow.h" +#include "connections/canconmanager.h" + +SnifferWindow::SnifferWindow(QWidget *parent) : + QDialog(parent), + ui(new Ui::snifferWindow), + mModel(this), + mTimer(this), + mFilter(false) +{ + ui->setupUi(this); + ui->treeView->setModel(&mModel); + + /* set column width */ + ui->treeView->setColumnWidth(tc::ID, 50); + ui->treeView->setColumnWidth(tc::LAST, 1); + for(int i=tc::DATA_0 ; i<=tc::DATA_7 ; i++) + ui->treeView->setColumnWidth(i, 30); + ui->treeView->header()->setDefaultAlignment(Qt::AlignCenter); + + /* activate sorting */ + ui->listWidget->setSortingEnabled(true); + + /* connect timer */ + connect(&mTimer, &QTimer::timeout, this, &SnifferWindow::update); + mTimer.setInterval(200); + + /* connect buttons */ + connect(ui->btNotch, &QPushButton::clicked, &mModel, &SnifferModel::notch); + connect(ui->btUnNotch, &QPushButton::clicked, &mModel, &SnifferModel::unNotch); + connect(ui->btAll, &QPushButton::clicked, this, &SnifferWindow::fltAll); + connect(ui->btNone, &QPushButton::clicked, this, &SnifferWindow::fltNone); + connect(&mModel, &SnifferModel::idChange, this, &SnifferWindow::idChange); + connect(ui->listWidget, &QListWidget::itemChanged, this, &SnifferWindow::itemChanged); + +} + +SnifferWindow::~SnifferWindow() +{ + closeEvent(NULL); + delete ui; +} + + +void SnifferWindow::showEvent(QShowEvent* event) +{ + QDialog::showEvent(event); + connect(CANConManager::getInstance(), &CANConManager::framesReceived, &mModel, &SnifferModel::update); + mTimer.start(); + + qDebug() << "show"; +} + + +void SnifferWindow::closeEvent(QCloseEvent *event) +{ + Q_UNUSED(event); + /* stop timer */ + mTimer.stop(); + /* disconnect reception of frames */ + disconnect(CANConManager::getInstance(), 0, this, 0); + /* clear model */ + mModel.clear(); + /* clean list */ + qDeleteAll(mMap); + mMap.clear(); + /* reset filtering */ + mFilter = false; +} + + +void SnifferWindow::update() +{ + mModel.refresh(); +} + + +void SnifferWindow::fltAll() +{ + filter(false); +} + + +void SnifferWindow::fltNone() +{ + filter(true); +} + +void SnifferWindow::filter(bool pFilter) +{ + mFilter = pFilter; + mModel.filter(mFilter ? fltType::NONE : fltType::ALL); + + foreach(QListWidgetItem* item, mMap) + item->setCheckState(mFilter ? Qt::Unchecked : Qt::Checked); +} + +void SnifferWindow::idChange(int pId, bool pAdd) +{ + QListWidgetItem* item; + + if(pAdd) + { + QString text = QString("0x") + QString("%1").arg(pId, 3, 16, QLatin1Char('0')).toUpper(); + item = new QListWidgetItem(text); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable); + item->setCheckState(mFilter ? Qt::Unchecked : Qt::Checked); + ui->listWidget->addItem(item); + mMap[pId] = item; + } + else + { + item = mMap.take(pId); + ui->listWidget->removeItemWidget(item); + delete item; + } +} + + +void SnifferWindow::itemChanged(QListWidgetItem * item) +{ + bool checked = (Qt::Checked == item->checkState()); + + if( !mFilter && checked ) + return; + + mModel.filter(checked ? fltType::ADD : fltType::REMOVE, mMap.key(item)); + mFilter = true; +} diff --git a/re/sniffer/snifferwindow.h b/re/sniffer/snifferwindow.h new file mode 100644 index 0000000..2d2cb1d --- /dev/null +++ b/re/sniffer/snifferwindow.h @@ -0,0 +1,56 @@ +#ifndef SNIFFER_H +#define SNIFFER_H + +#include +#include +#include "sniffermodel.h" + +namespace Ui { +class snifferWindow; +} + +enum tc +{ + DELTA = 0, + ID, + DATA_0, + DATA_1, + DATA_2, + DATA_3, + DATA_4, + DATA_5, + DATA_6, + DATA_7, + LAST +}; + + +class SnifferWindow : public QDialog +{ + Q_OBJECT + +public: + explicit SnifferWindow(QWidget *parent = 0); + ~SnifferWindow(); + + void showEvent(QShowEvent*); + void closeEvent(QCloseEvent*); + +public slots: + void update(); + void idChange(int, bool); + void fltAll(); + void fltNone(); + void itemChanged(QListWidgetItem*); + +private: + void filter(bool pFilter); + + Ui::snifferWindow* ui; + SnifferModel mModel; + QTimer mTimer; + QMap mMap; + bool mFilter; +}; + +#endif // SNIFFER_H diff --git a/snifferwindow.ui b/snifferwindow.ui new file mode 100644 index 0000000..44d36a0 --- /dev/null +++ b/snifferwindow.ui @@ -0,0 +1,188 @@ + + + snifferWindow + + + + 0 + 0 + 602 + 752 + + + + Sniffer + + + + QLayout::SetDefaultConstraint + + + + + QAbstractItemView::NoSelection + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Maximum + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + + 150 + 16777215 + + + + Notch + + + + + + + + 0 + 0 + + + + + 150 + 16777215 + + + + Unnotch + + + + + + + Qt::Vertical + + + QSizePolicy::Maximum + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + + 150 + 16777215 + + + + Filters: + + + + QLayout::SetDefaultConstraint + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 150 + 16777215 + + + + + + + + QLayout::SetDefaultConstraint + + + + + + 75 + 16777215 + + + + All + + + + + + + + 75 + 16777215 + + + + None + + + + + + + + + + + + + + + + +