Merge pull request #45 from collin80/WIP2

Update WIP to current state of WIP2
This commit is contained in:
Collin Kidder
2016-03-14 22:01:53 -04:00
71 changed files with 6840 additions and 1353 deletions
+28 -2
View File
@@ -1,6 +1,6 @@
# SavvyCAN
QT based cross platform canbus tool
(C) 2015 EVTV and Collin Kidder
(C) 2015-2016 EVTV and Collin Kidder
A QT5 based cross platform tool which can be used to load, save, and capture canbus frames.
This tool is designed to help with visualization, reverse engineering, debugging, and
@@ -8,12 +8,17 @@ capturing of canbus frames.
Really requires at a resolution of at least 1024x768. Fully multi-monitor capable.
Currently canbus capture requires a CANDue board from EVTV:
You are highly recommended to use the CANDue board from EVTV:
(http://store.evtv.me/proddetail.php?prod=ArduinoDueCANBUS&cat=23)
The CANDue board must be running the GVRET firmware which can also be found
within the collin80 repos.
It is very soon to be possible to use any SocketCAN compatible device
under LINUX. There may, however, be some loss of some functionality as
some functions of SavvyCAN are designed for use directly with the
EVTVDue and CANDue 2.0 boards.
It should, however, be noted that use of a capture device is not required to make use
of this program. It can load and save in several formats:
@@ -27,3 +32,24 @@ of this program. It can load and save in several formats:
5. Generic CSV file (ID,D0 D1 D2 D3 D4 D5 D6 D7)
6. Vector Trace files
7. IXXAT Minilog files
8. CAN-DO Logs
9. Vehicle Spy log files
Requires QScintilla library available at:
https://www.riverbankcomputing.com/software/qscintilla/download
Also uses QCustomPlot available at:
http://www.qcustomplot.com/
However, this source code is integrated into the source for SavvyCAN and one isn't required to download it separately.
The project now requires (yes, requires) at least QT 5.6.0 because of
a dependency on QtSerialBus which is still an optional feature. You
will need to download it when installing QT.
+31 -5
View File
@@ -6,11 +6,11 @@
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets serialport printsupport
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets serialport printsupport qml
CONFIG(release, debug|release):DEFINES += QT_NO_DEBUG_OUTPUT
CONFIG += c++11
CONFIG += c++11 qscintilla2
TARGET = SavvyCAN
TEMPLATE = app
@@ -37,7 +37,18 @@ SOURCES += main.cpp\
mainsettingsdialog.cpp \
firmwareuploaderwindow.cpp \
discretestatewindow.cpp \
connectionwindow.cpp
connectionwindow.cpp \
scriptingwindow.cpp \
scriptcontainer.cpp \
canfilter.cpp \
rangestatewindow.cpp \
dbc_classes.cpp \
dbcloadsavewindow.cpp \
fuzzingwindow.cpp \
udsscanwindow.cpp \
can_structs.cpp \
isotp_interpreterwindow.cpp \
isotp_decoder.cpp
HEADERS += mainwindow.h \
can_structs.h \
@@ -63,7 +74,16 @@ HEADERS += mainwindow.h \
mainsettingsdialog.h \
firmwareuploaderwindow.h \
discretestatewindow.h \
connectionwindow.h
connectionwindow.h \
scriptingwindow.h \
scriptcontainer.h \
canfilter.h \
rangestatewindow.h \
dbcloadsavewindow.h \
fuzzingwindow.h \
udsscanwindow.h \
isotp_interpreterwindow.h \
isotp_decoder.h
FORMS += mainwindow.ui \
graphingwindow.ui \
@@ -79,7 +99,13 @@ FORMS += mainwindow.ui \
mainsettingsdialog.ui \
firmwareuploaderwindow.ui \
discretestatewindow.ui \
connectionwindow.ui
connectionwindow.ui \
scriptingwindow.ui \
rangestatewindow.ui \
dbcloadsavewindow.ui \
fuzzingwindow.ui \
udsscanwindow.ui \
isotp_interpreterwindow.ui
DISTFILES +=
+2
View File
@@ -0,0 +1,2 @@
#include "can_structs.h"
+18 -1
View File
@@ -1,14 +1,16 @@
#ifndef CAN_STRUCTS_H
#define CAN_STRUCTS_H
#include <QVector>
#include <stdint.h>
struct CANFrame
class CANFrame
{
public:
int ID;
int bus;
bool extended;
bool isReceived; //did we receive this or send it?
int len;
unsigned char data[8];
uint64_t timestamp;
@@ -25,5 +27,20 @@ public:
int priority;
bool isBroadcast;
};
//the same as the CANFrame struct but with arbitrary data size.
struct ISOTP_MESSAGE
{
public:
int ID;
int bus;
bool extended;
bool isReceived;
int len; //# of bytes this message should have (as reported)
int actualSize; //# we actually got
QVector<unsigned char> data;
uint64_t timestamp;
};
#endif // CAN_STRUCTS_H
+12 -3
View File
@@ -13,6 +13,7 @@ CANDataGrid::CANDataGrid(QWidget *parent) :
memset(data, 0, 8);
memset(refData, 0, 8);
memset(usedData, 0, 8);
}
CANDataGrid::~CANDataGrid()
@@ -45,7 +46,7 @@ void CANDataGrid::paintEvent(QPaintEvent *event)
int x, y;
unsigned char prevByte, thisByte;
bool thisBit, prevBit;
QBrush blackBrush, whiteBrush, redBrush, greenBrush;
QBrush blackBrush, whiteBrush, redBrush, greenBrush, grayBrush;
QPainter painter(this);
QRect viewport = painter.viewport();
@@ -60,6 +61,7 @@ void CANDataGrid::paintEvent(QPaintEvent *event)
whiteBrush = QBrush(Qt::white);
redBrush = QBrush(Qt::red);
greenBrush = QBrush(Qt::green);
grayBrush = QBrush(QColor(230,230,230));
//the whole thing is broken up into 100 chunks which are allocated as such:
//The entirety of the upper row is taken up by "BITS"
@@ -109,7 +111,7 @@ void CANDataGrid::paintEvent(QPaintEvent *event)
thisBit = false;
prevBit = false;
if ((thisByte & (1 << (7-x))) == (1 << (7-x))) thisBit = true;
if ((prevByte & (1 << (7-x))) == (1 << (7-x))) prevBit = true;
if ((prevByte & (1 << (7-x))) == (1 << (7-x))) prevBit = true;
if (thisBit)
{
@@ -130,7 +132,8 @@ void CANDataGrid::paintEvent(QPaintEvent *event)
}
else
{
painter.setBrush(whiteBrush);
if ((usedData[y] & (1 << (7-x))) == (1 << (7-x))) painter.setBrush(grayBrush);
else painter.setBrush(whiteBrush);
}
}
@@ -180,3 +183,9 @@ void CANDataGrid::updateData(unsigned char *newData, bool bUpdate = true)
memcpy(data, newData, 8); //on a 64 bit processor this is probably optimized to a single instruction
if (bUpdate) this->update();
}
void CANDataGrid::setUsed(unsigned char *newData, bool bUpdate = false)
{
memcpy(usedData, newData, 8);
if (bUpdate) this->update();
}
+2
View File
@@ -17,6 +17,7 @@ public:
void paintEvent(QPaintEvent *event);
void setReference(unsigned char *, bool);
void updateData(unsigned char *, bool);
void setUsed(unsigned char *, bool);
void saveImage(QString filename, int width, int height);
protected:
@@ -29,6 +30,7 @@ private:
Ui::CANDataGrid *ui;
unsigned char refData[8];
unsigned char data[8];
unsigned char usedData[8];
QPoint upperLeft, gridSize;
};
+27
View File
@@ -0,0 +1,27 @@
#include "canfilter.h"
CANFilter::CANFilter()
{
ID = 0;
mask = 0;
bus = -1;
}
void CANFilter::setFilter(uint32_t id, uint32_t mask, int bus)
{
this->ID = id;
this->mask = mask;
this->bus = bus;
}
bool CANFilter::checkFilter(uint32_t id, int bus)
{
if (bus == -1 || bus == this->bus)
{
uint32_t result = id & this->mask;
if (result == this->ID) return true;
}
return false;
}
+19
View File
@@ -0,0 +1,19 @@
#ifndef CANFILTER_H
#define CANFILTER_H
#include <stdint.h>
class CANFilter
{
public:
CANFilter();
void setFilter(uint32_t id, uint32_t mask, int bus);
bool checkFilter(uint32_t id, int bus);
private:
uint32_t ID;
uint32_t mask;
int bus;
};
#endif // CANFILTER_H
+83 -42
View File
@@ -6,7 +6,12 @@
int CANFrameModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return filteredFrames.count();
if (filteredFrames.data()) return filteredFrames.count();
//just in case somehow data is invalid which I have seen before.
//But, this should not happen so issue a debugging message too
qDebug() << "Invalid data for filteredFrames. Returning 0.";
return 0;
}
int CANFrameModel::totalFrameCount()
@@ -17,7 +22,7 @@ int CANFrameModel::totalFrameCount()
int CANFrameModel::columnCount(const QModelIndex &index) const
{
Q_UNUSED(index);
return 6;
return 7;
}
CANFrameModel::CANFrameModel(QObject *parent)
@@ -115,6 +120,8 @@ void CANFrameModel::recalcOverwrite()
{
if (!overwriteDups) return; //no need to do a thing if mode is disabled
qDebug() << "recalcOverwrite called in model";
int lastUnique = 0;
bool found;
@@ -159,7 +166,7 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const
if (!index.isValid())
return QVariant();
if (index.row() >= filteredFrames.count())
if (index.row() >= (filteredFrames.count()))
return QVariant();
if (role == Qt::DisplayRole) {
@@ -168,7 +175,7 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const
{
case 0: //timestamp
if (!timeSeconds) return QString::number(thisFrame.timestamp);
else return QString::number(thisFrame.timestamp / 1000000.0f);
else return QString::number((double)thisFrame.timestamp / 1000000.0, 'f', 6);
break;
case 1: //id
return Utility::formatNumber(thisFrame.ID);
@@ -176,13 +183,17 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const
case 2: //ext
return QString::number(thisFrame.extended);
break;
case 3: //bus
case 3: //direction
if (thisFrame.isReceived) return QString(tr("Rx"));
else return QString(tr("Tx"));
break;
case 4: //bus
return QString::number(thisFrame.bus);
break;
case 4: //len
case 5: //len
return QString::number(thisFrame.len);
break;
case 5: //data
case 6: //data
for (int i = 0; i < thisFrame.len; i++)
{
tempString.append(Utility::formatNumber(thisFrame.data[i]));
@@ -191,16 +202,19 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const
//now, if we're supposed to interpret the data and the DBC handler is loaded then use it
if (dbcHandler != NULL && interpretFrames)
{
DBC_MESSAGE *msg = dbcHandler->findMsgByID(thisFrame.ID);
DBC_MESSAGE *msg = dbcHandler->findMessage(thisFrame);
if (msg != NULL)
{
tempString.append("\r\n");
tempString.append(msg->name + " " + msg->comment + "\r\n");
for (int j = 0; j < msg->msgSignals.length(); j++)
for (int j = 0; j < msg->sigHandler->getCount(); j++)
{
tempString.append(dbcHandler->processSignal(thisFrame, msg->msgSignals.at(j)));
tempString.append("\r\n");
QString sigString;
if (msg->sigHandler->findSignalByIdx(j)->processAsText(thisFrame, sigString))
{
tempString.append(sigString);
tempString.append("\r\n");
}
}
}
}
@@ -234,12 +248,15 @@ QVariant CANFrameModel::headerData(int section, Qt::Orientation orientation,
return QString(tr("Ext"));
break;
case 3:
return QString(tr("Bus"));
return QString(tr("Dir"));
break;
case 4:
return QString(tr("Len"));
return QString(tr("Bus"));
break;
case 5:
return QString(tr("Len"));
break;
case 6:
return QString(tr("Data"));
break;
}
@@ -251,25 +268,27 @@ QVariant CANFrameModel::headerData(int section, Qt::Orientation orientation,
return QVariant();
}
void CANFrameModel::addFrame(CANFrame &frame, bool autoRefresh = false)
void CANFrameModel::addFrame(const CANFrame &frame, bool autoRefresh = false)
{
mutex.lock();
frame.timestamp -= timeOffset;
CANFrame tempFrame;
tempFrame = frame;
tempFrame.timestamp -= timeOffset;
//if this ID isn't found in the filters list then add it and show it by default
if (!filters.contains(frame.ID))
if (!filters.contains(tempFrame.ID))
{
filters.insert(frame.ID, true);
filters.insert(tempFrame.ID, true);
needFilterRefresh = true;
}
if (!overwriteDups)
{
frames.append(frame);
if (filters[frame.ID])
frames.append(tempFrame);
if (filters[tempFrame.ID])
{
if (autoRefresh) beginInsertRows(QModelIndex(), filteredFrames.count() + 1, filteredFrames.count() + 1);
filteredFrames.append(frame);
filteredFrames.append(tempFrame);
if (autoRefresh) endInsertRows();
}
}
@@ -278,20 +297,20 @@ void CANFrameModel::addFrame(CANFrame &frame, bool autoRefresh = false)
bool found = false;
for (int i = 0; i < frames.count(); i++)
{
if (frames[i].ID == frame.ID)
if (frames[i].ID == tempFrame.ID)
{
frames.replace(i, frame);
frames.replace(i, tempFrame);
found = true;
break;
}
}
if (!found)
{
frames.append(frame);
if (filters[frame.ID])
frames.append(tempFrame);
if (filters[tempFrame.ID])
{
if (autoRefresh) beginInsertRows(QModelIndex(), filteredFrames.count() + 1, filteredFrames.count() + 1);
filteredFrames.append(frame);
filteredFrames.append(tempFrame);
if (autoRefresh) endInsertRows();
}
}
@@ -299,10 +318,10 @@ void CANFrameModel::addFrame(CANFrame &frame, bool autoRefresh = false)
{
for (int j = 0; j < filteredFrames.count(); j++)
{
if (filteredFrames[j].ID == frame.ID)
if (filteredFrames[j].ID == tempFrame.ID)
{
if (autoRefresh) beginResetModel();
filteredFrames.replace(j, frame);
filteredFrames.replace(j, tempFrame);
if (autoRefresh) endResetModel();
}
}
@@ -314,18 +333,23 @@ void CANFrameModel::addFrame(CANFrame &frame, bool autoRefresh = false)
void CANFrameModel::sendRefresh()
{
mutex.lock();
qDebug() << "Sending mass refresh";
QVector<CANFrame> tempContainer;
beginResetModel();
filteredFrames.clear();
for (int i = 0; i < frames.count(); i++)
int count = frames.count();
for (int i = 0; i < count; i++)
{
if (filters[frames[i].ID])
{
filteredFrames.append(frames[i]);
tempContainer.append(frames[i]);
}
}
filteredFrames.clear();
filteredFrames.append(tempContainer);
lastUpdateNumFrames = filteredFrames.count();
endResetModel();
mutex.unlock();
}
void CANFrameModel::sendRefresh(int pos)
@@ -343,23 +367,34 @@ void CANFrameModel::sendBulkRefresh(int num)
//of how many by tracking the number we last knew about as opposed to how many rows there
//are now.
num = filteredFrames.count() - lastUpdateNumFrames;
if (num < 0) return;
//qDebug() << "Num: " << num;
if (num == 0 && !overwriteDups) return;
if (filteredFrames.count() == 0) return;
lastUpdateNumFrames += num; //done this way to avoid asking for filteredFrames.count() again
//qDebug() << "Bulk refresh of " << num;
if (num == 0) return;
if (filteredFrames.count() == 0) return;
qDebug() << "Bulk refresh of " << num;
if (!overwriteDups)
{
mutex.lock();
if (num > filteredFrames.count()) num = filteredFrames.count();
beginInsertRows(QModelIndex(), filteredFrames.count() - num, filteredFrames.count() - 1);
endInsertRows();
//qDebug() << "From " << (filteredFrames.count() - num) << " to " << (filteredFrames.count() - 1);
//beginInsertRows(QModelIndex(), filteredFrames.count() - num, filteredFrames.count() - 1);
//endInsertRows();
beginResetModel();
endResetModel();
mutex.unlock();
}
else
{
sendRefresh();
}
beginResetModel();
endResetModel();
}
}
void CANFrameModel::clearFrames()
@@ -383,6 +418,11 @@ void CANFrameModel::clearFrames()
*/
void CANFrameModel::insertFrames(const QVector<CANFrame> &newFrames)
{
//not resetting the model here because the serial worker automatically does a bulk refresh every 1/4 second
//and that refresh will cause the view to update. If you do both it usually ends up thinking you have
//double the number of frames.
//beginResetModel();
mutex.lock();
int insertedFiltered = 0;
for (int i = 0; i < newFrames.count(); i++)
{
@@ -398,9 +438,10 @@ void CANFrameModel::insertFrames(const QVector<CANFrame> &newFrames)
filteredFrames.append(newFrames[i]);
}
}
beginInsertRows(QModelIndex(), filteredFrames.count() + 1, filteredFrames.count() + insertedFiltered);
endInsertRows();
mutex.unlock();
//endResetModel();
//beginInsertRows(QModelIndex(), filteredFrames.count() + 1, filteredFrames.count() + insertedFiltered);
//endInsertRows();
if (needFilterRefresh) emit updatedFiltersList();
}
+1 -1
View File
@@ -23,7 +23,7 @@ public:
int columnCount(const QModelIndex &) const;
int totalFrameCount();
void addFrame(CANFrame &, bool);
void addFrame(const CANFrame &, bool);
void sendRefresh();
void sendRefresh(int);
void sendBulkRefresh(int);
+2 -2
View File
@@ -1,12 +1,12 @@
#ifndef CONFIG
#define CONFIG
#define VERSION 140
#define VERSION 152
//try to keep this in sync.
//SavvyCAN will complain if you connect a GVRET board with a revision
//less than this number.
#define CURRENT_GVRET_VER 325 //330 is the newest but it is WIP
#define CURRENT_GVRET_VER 331
#endif // CONFIG
+88 -4
View File
@@ -1,5 +1,6 @@
#include "connectionwindow.h"
#include "ui_connectionwindow.h"
#include <QtNetwork/QNetworkInterface>
ConnectionWindow::ConnectionWindow(QWidget *parent) :
QDialog(parent),
@@ -7,7 +8,20 @@ ConnectionWindow::ConnectionWindow(QWidget *parent) :
{
ui->setupUi(this);
getSerialPorts();
settings = new QSettings();
int temp = settings->value("Main/DefaultConnectionType", 0).toInt();
if (temp == 0) currentConnType = ConnectionType::GVRET_SERIAL;
if (temp == 1) currentConnType = ConnectionType::KVASER;
if (temp == 2) currentConnType = ConnectionType::SOCKETCAN;
currentPortName = settings->value("Main/DefaultConnectionPort", "").toString();
currentSpeed1 = -1;
currentSpeed2 = -1;
ui->ckSingleWire->setChecked(settings->value("Main/SingleWireMode", false).toBool());
ui->cbSpeed0->addItem(tr("<Default>"));
ui->cbSpeed0->addItem(tr("Disabled"));
@@ -25,11 +39,23 @@ ConnectionWindow::ConnectionWindow(QWidget *parent) :
ui->cbSpeed1->addItem(tr("1000000"));
ui->cbSpeed1->addItem(tr("33333"));
#ifdef Q_OS_LINUX
ui->rbSocketCAN->setEnabled(true);
#endif
#ifdef Q_OS_WIN
ui->rbKvaser->setEnabled(true);
#endif
connect(ui->btnOK, SIGNAL(clicked(bool)), this, SLOT(handleOKButton()));
connect(ui->rbGVRET, SIGNAL(toggled(bool)), this, SLOT(handleConnTypeChanged()));
connect(ui->rbKvaser, SIGNAL(toggled(bool)), this, SLOT(handleConnTypeChanged()));
connect(ui->rbSocketCAN, SIGNAL(toggled(bool)), this, SLOT(handleConnTypeChanged()));
}
ConnectionWindow::~ConnectionWindow()
{
delete settings;
delete ui;
}
@@ -37,6 +63,11 @@ void ConnectionWindow::showEvent(QShowEvent* event)
{
QDialog::showEvent(event);
qDebug() << "Show connectionwindow";
handleConnTypeChanged();
}
void ConnectionWindow::handleConnTypeChanged()
{
if (ui->rbGVRET->isChecked()) getSerialPorts();
if (ui->rbKvaser->isChecked()) getKvaserPorts();
if (ui->rbSocketCAN->isChecked()) getSocketcanPorts();
@@ -45,10 +76,36 @@ void ConnectionWindow::showEvent(QShowEvent* event)
void ConnectionWindow::handleOKButton()
{
QString conn;
int connType = 0;
if (ui->rbGVRET->isChecked()) conn = "GVRET";
if (ui->rbKvaser->isChecked()) conn = "KVASER";
if (ui->rbSocketCAN->isChecked()) conn = "SOCKETCAN";
if (ui->rbGVRET->isChecked())
{
conn = "GVRET";
connType = 0;
currentConnType = ConnectionType::GVRET_SERIAL;
}
if (ui->rbKvaser->isChecked())
{
conn = "KVASER";
connType = 1;
currentConnType = ConnectionType::KVASER;
}
if (ui->rbSocketCAN->isChecked())
{
conn = "SOCKETCAN";
connType = 2;
currentConnType = ConnectionType::SOCKETCAN;
}
currentPortName = getPortName();
currentSpeed1 = getSpeed0();
currentSpeed2 = getSpeed1();
settings->setValue("Main/DefaultConnectionPort", currentPortName);
settings->setValue("Main/DefaultConnectionType", connType);
settings->setValue("Main/SingleWireMode", ui->ckSingleWire->isChecked());
emit updateConnectionSettings(conn, getPortName(), getSpeed0(), getSpeed1());
@@ -63,6 +120,7 @@ void ConnectionWindow::getSerialPorts()
for (int i = 0; i < ports.count(); i++)
{
ui->cbPort->addItem(ports[i].portName());
if (currentPortName == ports[i].portName()) ui->cbPort->setCurrentIndex(i);
}
}
@@ -73,13 +131,28 @@ void ConnectionWindow::getKvaserPorts()
void ConnectionWindow::getSocketcanPorts()
{
QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
QString interfaceName;
ui->cbPort->clear();
foreach (QNetworkInterface interface, interfaces)
{
interfaceName = interface.name().toLower();
qDebug() << "Interface: " << interface.name();
if (interfaceName.contains("can"))
{
ui->cbPort->addItem(interfaceName);
}
}
}
void ConnectionWindow::setSpeeds(int speed0, int speed1)
{
bool found = false;
qDebug() << speed0 << "X" << speed1;
for (int i = 0; i < ui->cbSpeed0->count(); i++)
{
if (ui->cbSpeed0->itemText(i).toInt() == speed0)
@@ -161,3 +234,14 @@ ConnectionType::ConnectionType ConnectionWindow::getConnectionType()
if (ui->rbKvaser->isChecked()) return ConnectionType::KVASER;
if (ui->rbSocketCAN->isChecked()) return ConnectionType::SOCKETCAN;
}
void ConnectionWindow::setCAN1SWMode(bool mode)
{
ui->ckSingleWire->setChecked(mode);
}
bool ConnectionWindow::getCAN1SWMode()
{
if (ui->ckSingleWire->checkState() == Qt::Checked) return true;
return false;
}
+9
View File
@@ -4,6 +4,7 @@
#include <QSerialPortInfo>
#include <QDialog>
#include <QDebug>
#include <QSettings>
namespace Ui {
class ConnectionWindow;
@@ -31,19 +32,27 @@ public:
int getSpeed1();
QString getPortName(); //name of port to connect to
ConnectionType::ConnectionType getConnectionType();
bool getCAN1SWMode();
signals:
void updateConnectionSettings(QString connectionType, QString port, int speed0, int speed1);
public slots:
void setSpeeds(int speed0, int speed1);
void setCAN1SWMode(bool mode);
private slots:
void handleOKButton();
void handleConnTypeChanged();
private:
Ui::ConnectionWindow *ui;
QList<QSerialPortInfo> ports;
QSettings *settings;
ConnectionType::ConnectionType currentConnType;
QString currentPortName;
int currentSpeed1, currentSpeed2;
void getSerialPorts();
void getKvaserPorts();
+7
View File
@@ -96,6 +96,13 @@
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="ckSingleWire">
<property name="text">
<string>Set CAN1 to Single Wire Mode</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnOK">
<property name="text">
+203
View File
@@ -0,0 +1,203 @@
#include "dbc_classes.h"
#include "dbchandler.h"
#include "utility.h"
DBC_MESSAGE::DBC_MESSAGE()
{
sigHandler = new DBCSignalHandler;
}
/*
The way that the DBC file format works is kind of weird... For intel format signals you count up
from the start bit to the end bit which is (startbit + signallength - 1). At each point
bits are numbered in a sawtooth manner. What that means is that the very first bit is 0 and you count up
from there all of the way to 63 with each byte being 8 bits so bit 0 is the lowest bit in the first byte
and 8 is the lowest bit in the next byte up. The whole thing looks like this:
Bits
7 6 5 4 3 2 1 0
0 7 6 5 4 3 2 1 0
b 1 15 14 13 12 11 10 9 8
y 2 23 22 21 20 19 18 17 16
t 3 31 30 29 28 27 26 25 24
e 4 39 38 37 36 35 34 33 32
s 5 47 46 45 44 43 42 41 40
6 55 54 53 52 51 50 49 48
7 63 62 61 60 59 58 57 56
For intel format you start at the start bit and keep counting up. If you have a signal size of 8
and start at bit 12 then the bits are 12, 13, 14, 15, 16, 17, 18, 19 which spans across two bytes.
In this format each bit is worth twice as much as the last and you just keep counting up.
Bit 12 is worth 1, 13 is worth 2, 14 is worth 4, etc all of the way to bit 19 is worth 128.
Motorola format turns most everything on its head. You count backward from the start bit but
only within the current byte. If you are about to exit the current byte you go one higher and then keep
going backward as before. Using the same example as for intel, start bit of 12 and a signal length of 8.
So, the bits are 12, 11, 10, 9, 8, 23, 22, 21. Yes, that's confusing. They now go in reverse value order too.
Bit 12 is worth 128, 11 is worth 64, etc until bit 21 is worth 1.
*/
bool DBC_SIGNAL::processAsText(const CANFrame &frame, QString &outString)
{
int64_t result = 0;
bool isSigned = false;
double endResult;
if (valType == STRING)
{
QString buildString;
int startByte = signalSize / 8;
int bytes = signalSize / 8;
for (int x = 0; x < bytes; x++) buildString.append(frame.data[startByte + x]);
outString = buildString;
return true;
}
//if this is a multiplexed signal then we have to see if it is even found in the current message
if (isMultiplexed)
{
if (parentMessage->multiplexorSignal != NULL)
{
int val;
if (!parentMessage->multiplexorSignal->processAsInt(frame, val)) return false;
if (val != multiplexValue) return false; //signal not found in this message
}
else return false;
}
if (valType == SIGNED_INT) isSigned = true;
if (valType == SIGNED_INT || valType == UNSIGNED_INT)
{
result = Utility::processIntegerSignal(frame.data, startBit, signalSize, intelByteOrder, isSigned);
endResult = ((double)result * factor) + bias;
result = (int64_t)endResult;
}
else if (valType == SP_FLOAT)
{
//The theory here is that we force the integer signal code to treat this as
//a 32 bit unsigned integer. This integer is then cast into a float in such a way
//that the bytes that make up the integer are instead treated as having made up
//a 32 bit single precision float. That's evil incarnate but it is very fast and small
//in terms of new code.
result = Utility::processIntegerSignal(frame.data, startBit, 32, false, false);
endResult = (*((float *)(&result)) * factor) + bias;
}
else //double precision float
{
//like the above, this is rotten and evil and wrong in so many ways. Force
//calculation of a 64 bit integer and then cast it into a double.
result = Utility::processIntegerSignal(frame.data, 0, 64, false, false);
endResult = (*((double *)(&result)) * factor) + bias;
}
QString outputString;
outputString = name + ": ";
if (valList.count() > 0) //if this is a value list type then look it up and display the proper string
{
for (int x = 0; x < valList.count(); x++)
{
if (valList.at(x).value == result) outputString += valList.at(x).descript;
}
}
else //otherwise display the actual number and unit (if it exists)
{
outputString += QString::number(endResult) + unitName;
}
outString = outputString;
return true;
}
//Works quite a bit like the above version but this one is cut down and only will return int32_t which is perfect for
//uses like calculating a multiplexor value or if you know you are going to get an integer returned
//from a signal and you want to use it as-is and not have to convert back from a string. Use with caution though
//as this basically assumes the signal is an integer.
//The call syntax is different from the more generic processSignal. Instead of returning the value we return
//true or false to show whether the function succeeded. The variable to fill out is passed by reference.
bool DBC_SIGNAL::processAsInt(const CANFrame &frame, int32_t &outValue)
{
int32_t result = 0;
bool isSigned = false;
if (valType == STRING || valType == SP_FLOAT || valType == DP_FLOAT)
{
return false;
}
//if this is a multiplexed signal then we have to see if it is even found in the current message
if (isMultiplexed)
{
if (parentMessage->multiplexorSignal != NULL)
{
int val;
if (!parentMessage->multiplexorSignal->processAsInt(frame, val)) return false;
if (val != multiplexValue) return false; //signal not found in this message
}
else return false;
}
if (valType == SIGNED_INT) isSigned = true;
result = Utility::processIntegerSignal(frame.data, startBit, signalSize, intelByteOrder, isSigned);
double endResult = ((double)result * factor) + bias;
result = (int32_t)endResult;
outValue = result;
return true;
}
//Another cut down version that will only return double precision data. This can be used on any of the types
//except STRING. Useful for when you know you'll need floating point data and don't want to incur a conversion
//back and forth to double or float. Such a use is the graphing window.
//Similar syntax to processSignalInt but with double instead.
bool DBC_SIGNAL::processAsDouble(const CANFrame &frame, double &outValue)
{
int64_t result = 0;
bool isSigned = false;
double endResult;
if (valType == STRING)
{
return false;
}
//if this is a multiplexed signal then we have to see if it is even found in the current message
if (isMultiplexed)
{
if (parentMessage->multiplexorSignal != NULL)
{
int val;
if (!parentMessage->multiplexorSignal->processAsInt(frame, val)) return false;
if (val != multiplexValue) return false; //signal not found in this message
}
else return false;
}
if (valType == SIGNED_INT) isSigned = true;
if (valType == SIGNED_INT || valType == UNSIGNED_INT)
{
result = Utility::processIntegerSignal(frame.data, startBit, signalSize, intelByteOrder, isSigned);
endResult = ((double)result * factor) + bias;
result = (int64_t)endResult;
}
else if (valType == SP_FLOAT)
{
//The theory here is that we force the integer signal code to treat this as
//a 32 bit unsigned integer. This integer is then cast into a float in such a way
//that the bytes that make up the integer are instead treated as having made up
//a 32 bit single precision float. That's evil incarnate but it is very fast and small
//in terms of new code.
result = Utility::processIntegerSignal(frame.data, startBit, 32, false, false);
endResult = (*((float *)(&result)) * factor) + bias;
}
else //double precision float
{
//like the above, this is rotten and evil and wrong in so many ways. Force
//calculation of a 64 bit integer and then cast it into a double.
result = Utility::processIntegerSignal(frame.data, 0, 64, false, false);
endResult = (*((double *)(&result)) * factor) + bias;
}
outValue = endResult;
return true;
}
+19 -3
View File
@@ -3,6 +3,7 @@
#include <QString>
#include <QStringList>
#include "can_structs.h"
/*classes to encapsulate data from a DBC file. Really, the stuff of interest
are the nodes, messages, signals, attributes, and comments.
@@ -54,35 +55,50 @@ public:
QList<DBC_ATTRIBUTE> attributes;
};
class DBC_MESSAGE; //forward reference so that DBC_SIGNAL can compile before we get to real definition of DBC_MESSAGE
class DBC_SIGNAL
{
public:
public: //TODO: this is sloppy. It shouldn't all be public!
QString name;
int startBit;
int signalSize;
bool intelByteOrder; //true is obviously little endian. False is big endian
bool isMultiplexor;
bool isMultiplexed;
int multiplexValue;
DBC_SIG_VAL_TYPE valType;
double factor;
double bias;
double min;
double max;
DBC_NODE *receiver;
DBC_NODE *receiver; //it is fast to have a pointer but dangerous... Make sure to walk the whole tree and delete everything so nobody has stale references.
DBC_MESSAGE *parentMessage;
QString unitName;
QString comment;
QList<DBC_ATTRIBUTE> attributes;
QList<DBC_VAL> valList;
bool processAsText(const CANFrame &frame, QString &outString);
bool processAsInt(const CANFrame &frame, int32_t &outValue);
bool processAsDouble(const CANFrame &frame, double &outValue);
};
class DBCSignalHandler; //forward declaration to keep from having to include dbchandler.h in this file and thus create a loop
class DBC_MESSAGE
{
public:
DBC_MESSAGE();
int ID;
QString name;
QString comment;
int len;
DBC_NODE *sender;
QList<DBC_ATTRIBUTE> attributes;
QList<DBC_SIGNAL> msgSignals;
DBCSignalHandler *sigHandler;
DBC_SIGNAL* multiplexorSignal;
};
+483 -290
View File
@@ -3,21 +3,262 @@
#include <QFile>
#include <QRegularExpression>
#include <QDebug>
#include <QMessageBox>
#include <QFileDialog>
#include "utility.h"
DBCHandler::DBCHandler(QObject *parent) : QObject(parent)
DBC_SIGNAL* DBCSignalHandler::findSignalByIdx(int idx)
{
if (sigs.count() == 0) return NULL;
if (idx < 0) return NULL;
if (idx >= sigs.count()) return NULL;
return &sigs[idx];
}
void DBCHandler::loadDBCFile(QString filename)
DBC_SIGNAL* DBCSignalHandler::findSignalByName(QString name)
{
QFile *inFile = new QFile(filename);
if (sigs.count() == 0) return NULL;
for (int i = 0; i < sigs.count(); i++)
{
if (sigs[i].name.compare(name, Qt::CaseInsensitive) == 0)
{
return &sigs[i];
}
}
return NULL;
}
bool DBCSignalHandler::addSignal(DBC_SIGNAL &sig)
{
sigs.append(sig);
return true;
}
bool DBCSignalHandler::removeSignal(DBC_SIGNAL *sig)
{
//if (sigs.removeAll(*sig) > 0) return true;
return false;
}
bool DBCSignalHandler::removeSignal(int idx)
{
if (sigs.count() == 0) return false;
if (idx < 0) return false;
if (idx >= sigs.count()) return false;
sigs.removeAt(idx);
return true;
}
bool DBCSignalHandler::removeSignal(QString name)
{
bool foundSome = false;
if (sigs.count() == 0) return false;
for (int i = sigs.count() - 1; i >= 0; i--)
{
if (sigs[i].name.compare(name, Qt::CaseInsensitive) == 0)
{
sigs.removeAt(i);
foundSome = true;
}
}
return foundSome;
}
void DBCSignalHandler::removeAllSignals()
{
sigs.clear();
}
int DBCSignalHandler::getCount()
{
return sigs.count();
}
DBC_MESSAGE* DBCMessageHandler::findMsgByID(int id)
{
if (messages.count() == 0) return NULL;
for (int i = 0; i < messages.count(); i++)
{
if (messages[i].ID == id)
{
return &messages[i];
}
}
return NULL;
}
DBC_MESSAGE* DBCMessageHandler::findMsgByIdx(int idx)
{
if (messages.count() == 0) return NULL;
if (idx < 0) return NULL;
if (idx >= messages.count()) return NULL;
return &messages[idx];
}
DBC_MESSAGE* DBCMessageHandler::findMsgByName(QString name)
{
if (messages.count() == 0) return NULL;
for (int i = 0; i < messages.count(); i++)
{
if (messages[i].name.compare(name, Qt::CaseInsensitive) == 0)
{
return &messages[i];
}
}
return NULL;
}
bool DBCMessageHandler::addMessage(DBC_MESSAGE &msg)
{
messages.append(msg);
return true;
}
bool DBCMessageHandler::removeMessage(DBC_MESSAGE *msg)
{
//if (messages.removeAll(*msg) > 0) return true;
return false;
}
bool DBCMessageHandler::removeMessageByIndex(int idx)
{
if (messages.count() == 0) return false;
if (idx < 0) return false;
if (idx >= messages.count()) return false;
messages.removeAt(idx);
return true;
}
bool DBCMessageHandler::removeMessage(int ID)
{
bool foundSome = false;
if (messages.count() == 0) return false;
for (int i = messages.count() - 1; i >= 0; i--)
{
if (messages[i].ID == ID)
{
messages.removeAt(i);
foundSome = true;
}
}
return foundSome;
}
bool DBCMessageHandler::removeMessage(QString name)
{
bool foundSome = false;
if (messages.count() == 0) return false;
for (int i = messages.count() - 1; i >= 0; i--)
{
if (messages[i].name.compare(name, Qt::CaseInsensitive) == 0)
{
messages.removeAt(i);
foundSome = true;
}
}
return foundSome;
}
void DBCMessageHandler::removeAllMessages()
{
messages.clear();
}
int DBCMessageHandler::getCount()
{
return messages.count();
}
DBCFile::DBCFile()
{
messageHandler = new DBCMessageHandler;
}
DBCFile::DBCFile(const DBCFile& cpy)
{
messageHandler = new DBCMessageHandler;
for (int i = 0 ; i < cpy.messageHandler->getCount() ; i++)
messageHandler->addMessage(*cpy.messageHandler->findMsgByIdx(i));
fileName = cpy.fileName;
filePath = cpy.filePath;
assocBuses = cpy.assocBuses;
dbc_nodes.clear();
dbc_nodes.append(cpy.dbc_nodes);
}
DBCFile& DBCFile::operator=(const DBCFile& cpy)
{
if (this != &cpy) // protect against invalid self-assignment
{
messageHandler = cpy.messageHandler;
fileName = cpy.fileName;
filePath = cpy.filePath;
assocBuses = cpy.assocBuses;
dbc_nodes.clear();
dbc_nodes.append(cpy.dbc_nodes);
}
return *this;
}
DBC_NODE* DBCFile::findNodeByIdx(int idx)
{
if (idx < 0) return NULL;
if (idx >= dbc_nodes.count()) return NULL;
return &dbc_nodes[idx];
}
DBC_NODE* DBCFile::findNodeByName(QString name)
{
if (dbc_nodes.length() == 0) return NULL;
for (int i = 0; i < dbc_nodes.length(); i++)
{
if (dbc_nodes[i].name.compare(name, Qt::CaseInsensitive) == 0)
{
return &dbc_nodes[i];
}
}
return NULL;
}
QString DBCFile::getFullFilename()
{
return filePath + fileName;
}
QString DBCFile::getFilename()
{
return fileName;
}
QString DBCFile::getPath()
{
return filePath;
}
int DBCFile::getAssocBus()
{
return assocBuses;
}
void DBCFile::setAssocBus(int bus)
{
if (bus < -1) return;
if (bus > 1) return;
assocBuses = bus;
}
void DBCFile::loadFile(QString fileName)
{
QFile *inFile = new QFile(fileName);
QString line;
QRegularExpression regex;
QRegularExpressionMatch match;
DBC_MESSAGE *currentMessage = NULL;
int numSigFaults = 0, numMsgFaults = 0;
qDebug() << "DBC File: " << filename;
qDebug() << "DBC File: " << fileName;
if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text))
{
@@ -27,7 +268,7 @@ void DBCHandler::loadDBCFile(QString filename)
qDebug() << "Starting DBC load";
dbc_nodes.clear();
dbc_messages.clear();
messageHandler->removeAllMessages();
DBC_NODE falseNode;
falseNode.name = "Vector__XXX";
@@ -36,9 +277,9 @@ void DBCHandler::loadDBCFile(QString filename)
while (!inFile->atEnd()) {
line = QString(inFile->readLine().simplified());
if (line.startsWith("BO_ "))
if (line.startsWith("BO_ ")) //defines a message
{
//qDebug() << "Found a BO line";
qDebug() << "Found a BO line";
regex.setPattern("^BO\\_ (\\w+) (\\w+) *: (\\w+) (\\w+)");
match = regex.match(line);
//captured 1 = the ID in decimal
@@ -52,16 +293,55 @@ void DBCHandler::loadDBCFile(QString filename)
msg.name = match.captured(2);
msg.len = match.captured(3).toInt();
msg.sender = findNodeByName(match.captured(4));
dbc_messages.append(msg);
currentMessage = &dbc_messages.last();
messageHandler->addMessage(msg);
currentMessage = messageHandler->findMsgByID(msg.ID);
}
else numMsgFaults++;
}
if (line.startsWith("SG_ "))
if (line.startsWith("SG_ ")) //defines a signal
{
//qDebug() << "Found a SG line";
regex.setPattern("^SG\\_ (\\w+) : (\\d+)\\|(\\d+)@(\\d+)([\\+|\\-]) \\(([0-9.+\\-eE]+),([0-9.+\\-eE]+)\\) \\[([0-9.+\\-eE]+)\\|([0-9.+\\-eE]+)\\] \\\"(.*)\\\" (.*)");
int offset = 0;
bool isMultiplexor = false;
bool isMultiplexed = false;
DBC_SIGNAL sig;
sig.multiplexValue = 0;
sig.isMultiplexed = false;
sig.isMultiplexor = false;
qDebug() << "Found a SG line";
regex.setPattern("^SG\\_ *(\\w+) *M *: *(\\d+)\\|(\\d+)@(\\d+)([\\+|\\-]) \\(([0-9.+\\-eE]+),([0-9.+\\-eE]+)\\) \\[([0-9.+\\-eE]+)\\|([0-9.+\\-eE]+)\\] \\\"(.*)\\\" (.*)");
match = regex.match(line);
if (match.hasMatch())
{
qDebug() << "Multiplexor signal";
isMultiplexor = true;
sig.isMultiplexor = true;
}
else
{
regex.setPattern("^SG\\_ *(\\w+) *m(\\d+) *: *(\\d+)\\|(\\d+)@(\\d+)([\\+|\\-]) \\(([0-9.+\\-eE]+),([0-9.+\\-eE]+)\\) \\[([0-9.+\\-eE]+)\\|([0-9.+\\-eE]+)\\] \\\"(.*)\\\" (.*)");
match = regex.match(line);
if (match.hasMatch())
{
qDebug() << "Multiplexed signal";
isMultiplexed = true;
sig.isMultiplexed = true;
sig.multiplexValue = match.captured(2).toInt();
offset = 1;
}
else
{
regex.setPattern("^SG\\_ *(\\w+) *: *(\\d+)\\|(\\d+)@(\\d+)([\\+|\\-]) \\(([0-9.+\\-eE]+),([0-9.+\\-eE]+)\\) \\[([0-9.+\\-eE]+)\\|([0-9.+\\-eE]+)\\] \\\"(.*)\\\" (.*)");
match = regex.match(line);
sig.isMultiplexed = false;
sig.isMultiplexor = false;
}
}
//captured 1 is the signal name
//captured 2 would be multiplex value if this is a multiplex signal. Then offset the rest of these by 1
//captured 2 is the starting bit
//captured 3 is the length in bits
//captured 4 is the byte order / value type
@@ -75,14 +355,13 @@ void DBCHandler::loadDBCFile(QString filename)
if (match.hasMatch())
{
DBC_SIGNAL sig;
sig.name = match.captured(1);
sig.startBit = match.captured(2).toInt();
sig.signalSize = match.captured(3).toInt();
int val = match.captured(4).toInt();
sig.startBit = match.captured(2 + offset).toInt();
sig.signalSize = match.captured(3 + offset).toInt();
int val = match.captured(4 + offset).toInt();
if (val < 2)
{
if (match.captured(5) == "+") sig.valType = UNSIGNED_INT;
if (match.captured(5 + offset) == "+") sig.valType = UNSIGNED_INT;
else sig.valType = SIGNED_INT;
}
switch (val)
@@ -103,23 +382,26 @@ void DBCHandler::loadDBCFile(QString filename)
sig.valType = STRING;
break;
}
sig.factor = match.captured(6).toDouble();
sig.bias = match.captured(7).toDouble();
sig.min = match.captured(8).toDouble();
sig.max = match.captured(9).toDouble();
sig.unitName = match.captured(10);
if (match.captured(11).contains(','))
sig.factor = match.captured(6 + offset).toDouble();
sig.bias = match.captured(7 + offset).toDouble();
sig.min = match.captured(8 + offset).toDouble();
sig.max = match.captured(9 + offset).toDouble();
sig.unitName = match.captured(10 + offset);
if (match.captured(11 + offset).contains(','))
{
QString tmp = match.captured(11).split(',')[0];
sig.receiver = findNodeByName(tmp);
}
else sig.receiver = findNodeByName(match.captured(11));
currentMessage->msgSignals.append(sig);
else sig.receiver = findNodeByName(match.captured(11 + offset));
sig.parentMessage = currentMessage;
currentMessage->sigHandler->addSignal(sig);
if (isMultiplexor) currentMessage->multiplexorSignal = currentMessage->sigHandler->findSignalByName(sig.name);
}
else numSigFaults++;
}
if (line.startsWith("BU_:"))
if (line.startsWith("BU_:")) //line specifies the nodes on this canbus
{
//qDebug() << "Found a BU line";
qDebug() << "Found a BU line";
regex.setPattern("^BU\\_\\:(.*)");
match = regex.match(line);
//captured 1 = a list of node names separated by spaces. No idea how many yet
@@ -141,7 +423,7 @@ void DBCHandler::loadDBCFile(QString filename)
}
if (line.startsWith("CM_ SG_ "))
{
//qDebug() << "Found an SG comment line";
qDebug() << "Found an SG comment line";
regex.setPattern("^CM\\_ SG\\_ *(\\w+) *(\\w+) *\\\"(.*)\\\";");
match = regex.match(line);
//captured 1 is the ID to match against to get to the message
@@ -150,10 +432,10 @@ void DBCHandler::loadDBCFile(QString filename)
if (match.hasMatch())
{
//qDebug() << "Comment was: " << match.captured(3);
DBC_MESSAGE *msg = findMsgByID(match.captured(1).toInt());
DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toInt());
if (msg != NULL)
{
DBC_SIGNAL *sig = findSignalByName(msg, match.captured(2));
DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(match.captured(2));
if (sig != NULL)
{
sig->comment = match.captured(3);
@@ -163,7 +445,7 @@ void DBCHandler::loadDBCFile(QString filename)
}
if (line.startsWith("CM_ BO_ "))
{
//qDebug() << "Found a BO comment line";
qDebug() << "Found a BO comment line";
regex.setPattern("^CM\\_ BO\\_ *(\\w+) *\\\"(.*)\\\";");
match = regex.match(line);
//captured 1 is the ID to match against to get to the message
@@ -171,7 +453,7 @@ void DBCHandler::loadDBCFile(QString filename)
if (match.hasMatch())
{
//qDebug() << "Comment was: " << match.captured(2);
DBC_MESSAGE *msg = findMsgByID(match.captured(1).toInt());
DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toInt());
if (msg != NULL)
{
msg->comment = match.captured(2);
@@ -180,7 +462,7 @@ void DBCHandler::loadDBCFile(QString filename)
}
if (line.startsWith("CM_ BU_ "))
{
//qDebug() << "Found a BU comment line";
qDebug() << "Found a BU comment line";
regex.setPattern("^CM\\_ BU\\_ *(\\w+) *\\\"(.*)\\\";");
match = regex.match(line);
//captured 1 is the Node name
@@ -198,7 +480,7 @@ void DBCHandler::loadDBCFile(QString filename)
//VAL_ (1090) (VCUPresentParkLightOC) (1 "Error present" 0 "Error not present") ;
if (line.startsWith("VAL_ "))
{
//qDebug() << "Found a value definition line";
qDebug() << "Found a value definition line";
regex.setPattern("^VAL\\_ (\\w+) (\\w+) (.*);");
match = regex.match(line);
//captured 1 is the ID to match against
@@ -207,10 +489,10 @@ void DBCHandler::loadDBCFile(QString filename)
if (match.hasMatch())
{
//qDebug() << "Data was: " << match.captured(3);
DBC_MESSAGE *msg = findMsgByID(match.captured(1).toInt());
DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toInt());
if (msg != NULL)
{
DBC_SIGNAL *sig = findSignalByName(msg, match.captured(2));
DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(match.captured(2));
if (sig != NULL)
{
QString tokenString = match.captured(3);
@@ -260,16 +542,28 @@ void DBCHandler::loadDBCFile(QString filename)
}
*/
}
if (numSigFaults > 0 || numMsgFaults > 0)
{
QMessageBox msgBox;
QString msg = "DBC file loaded with errors!\n";
msg += "Number of faulty message entries: " + QString::number(numMsgFaults) + "\n";
msg += "Number of faulty signal entries: " + QString::number(numSigFaults) + "\n\n";
msg += "Faulty entries have not been loaded.";
msgBox.setText(msg);
msgBox.exec();
}
inFile->close();
delete inFile;
QStringList fileList = fileName.split('/');
this->fileName = fileList[fileList.length() - 1]; //whoops... same name as parameter in this function.
filePath = fileName.left(fileName.length() - this->fileName.length());
assocBuses = -1;
}
/*Yes, this is really hard to follow and all of the sections are mixed up in code
* believe it or not I think this is actually the easiest, simplest way to do it.
*/
void DBCHandler::saveDBCFile(QString filename)
void DBCFile::saveFile(QString fileName)
{
QFile *outFile = new QFile(filename);
QFile *outFile = new QFile(fileName);
QString nodesOutput, msgOutput, commentsOutput, valuesOutput;
if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text))
@@ -330,27 +624,36 @@ void DBCHandler::saveDBCFile(QString filename)
nodesOutput.append("\n");
outFile->write(nodesOutput.toUtf8());
for (int x = 0; x < dbc_messages.count(); x++)
for (int x = 0; x < messageHandler->getCount(); x++)
{
DBC_MESSAGE msg = dbc_messages[x];
msgOutput.append("BO_ " + QString::number(msg.ID) + " " + msg.name + ": " + QString::number(msg.len) +
" " + msg.sender->name + "\n");
if (msg.comment.length() > 0)
DBC_MESSAGE *msg = messageHandler->findMsgByIdx(x);
msgOutput.append("BO_ " + QString::number(msg->ID) + " " + msg->name + ": " + QString::number(msg->len) +
" " + msg->sender->name + "\n");
if (msg->comment.length() > 0)
{
commentsOutput.append("CM_ BO_ " + QString::number(msg.ID) + " \"" + msg.comment + "\";\n");
commentsOutput.append("CM_ BO_ " + QString::number(msg->ID) + " \"" + msg->comment + "\";\n");
}
for (int s = 0; s < msg.msgSignals.count(); s++)
for (int s = 0; s < msg->sigHandler->getCount(); s++)
{
DBC_SIGNAL sig = msg.msgSignals[s];
msgOutput.append(" SG_ " + sig.name + " : " + QString::number(sig.startBit) + "|" + QString::number(sig.signalSize) + "@");
switch (sig.valType)
DBC_SIGNAL *sig = msg->sigHandler->findSignalByIdx(s);
msgOutput.append(" SG_ " + sig->name);
if (sig->isMultiplexor) msgOutput.append(" M");
if (sig->isMultiplexed)
{
msgOutput.append(" m" + QString::number(sig->multiplexValue));
}
msgOutput.append(" : " + QString::number(sig->startBit) + "|" + QString::number(sig->signalSize) + "@");
switch (sig->valType)
{
case UNSIGNED_INT:
if (sig.intelByteOrder) msgOutput.append("1+");
if (sig->intelByteOrder) msgOutput.append("1+");
else msgOutput.append("0+");
break;
case SIGNED_INT:
if (sig.intelByteOrder) msgOutput.append("1-");
if (sig->intelByteOrder) msgOutput.append("1-");
else msgOutput.append("0-");
break;
case SP_FLOAT:
@@ -366,19 +669,19 @@ void DBCHandler::saveDBCFile(QString filename)
msgOutput.append("0-");
break;
}
msgOutput.append(" (" + QString::number(sig.factor) + "," + QString::number(sig.bias) + ") [" +
QString::number(sig.min) + "|" + QString::number(sig.max) + "] \"" + sig.unitName
+ "\" " + sig.receiver->name + "\n");
if (sig.comment.length() > 0)
msgOutput.append(" (" + QString::number(sig->factor) + "," + QString::number(sig->bias) + ") [" +
QString::number(sig->min) + "|" + QString::number(sig->max) + "] \"" + sig->unitName
+ "\" " + sig->receiver->name + "\n");
if (sig->comment.length() > 0)
{
commentsOutput.append("CM_ SG_ " + QString::number(msg.ID) + " " + sig.name + " \"" + sig.comment + "\";\n");
commentsOutput.append("CM_ SG_ " + QString::number(msg->ID) + " " + sig->name + " \"" + sig->comment + "\";\n");
}
if (sig.valList.count() > 0)
if (sig->valList.count() > 0)
{
valuesOutput.append("VAL_ " + QString::number(msg.ID) + " " + sig.name);
for (int v = 0; v < sig.valList.count(); v++)
valuesOutput.append("VAL_ " + QString::number(msg->ID) + " " + sig->name);
for (int v = 0; v < sig->valList.count(); v++)
{
DBC_VAL val = sig.valList[v];
DBC_VAL val = sig->valList[v];
valuesOutput.append(" " + QString::number(val.value) + " \"" + val.descript +"\"");
}
valuesOutput.append(";\n");
@@ -396,250 +699,140 @@ void DBCHandler::saveDBCFile(QString filename)
outFile->close();
delete outFile;
QStringList fileList = fileName.split('/');
this->fileName = fileList[fileList.length() - 1]; //whoops... same name as parameter in this function.
filePath = fileName.left(fileName.length() - this->fileName.length());
}
DBC_NODE *DBCHandler::findNodeByName(QString name)
void DBCHandler::saveDBCFile(int idx)
{
if (dbc_nodes.length() == 0) return NULL;
for (int i = 0; i < dbc_nodes.length(); i++)
if (loadedFiles.count() == 0) return;
if (idx < 0) return;
if (idx >= loadedFiles.count()) return;
QString filename;
QFileDialog dialog;
QStringList filters;
filters.append(QString(tr("DBC File (*.dbc)")));
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
dialog.setAcceptMode(QFileDialog::AcceptSave);
dialog.selectFile(loadedFiles[idx].getFullFilename());
if (dialog.exec() == QDialog::Accepted)
{
if (dbc_nodes[i].name.compare(name, Qt::CaseInsensitive) == 0)
{
return &dbc_nodes[i];
}
}
return NULL;
}
DBC_NODE *DBCHandler::findNodeByIdx(int idx)
{
if (idx < 0) return NULL;
if (idx >= dbc_nodes.count()) return NULL;
return &dbc_nodes[idx];
}
DBC_MESSAGE *DBCHandler::findMsgByID(int id)
{
if (dbc_messages.length() == 0) return NULL;
for (int i = 0; i < dbc_messages.length(); i++)
{
if (dbc_messages[i].ID == id)
{
return &dbc_messages[i];
}
}
return NULL;
}
DBC_MESSAGE *DBCHandler::findMsgByIdx(int idx)
{
if (dbc_messages.length() == 0) return NULL;
if (idx < 0) return NULL;
if (idx >= dbc_messages.count()) return NULL;
return &dbc_messages[idx];
}
DBC_MESSAGE *DBCHandler::findMsgByName(QString name)
{
if (dbc_messages.length() == 0) return NULL;
for (int i = 0; i < dbc_messages.length(); i++)
{
if (dbc_messages[i].name.compare(name, Qt::CaseInsensitive) == 0)
{
return &dbc_messages[i];
}
}
return NULL;
}
DBC_SIGNAL *DBCHandler::findSignalByName(DBC_MESSAGE *msg, QString name)
{
if (msg == NULL) return NULL;
if (msg->msgSignals.length() == 0) return NULL;
for (int i = 0; i < msg->msgSignals.length(); i++)
{
if (msg->msgSignals[i].name.compare(name, Qt::CaseInsensitive) == 0)
{
return &msg->msgSignals[i];
}
}
return NULL;
}
DBC_SIGNAL *DBCHandler::findSignalByIdx(DBC_MESSAGE *msg, int idx)
{
if (msg == NULL) return NULL;
if (msg->msgSignals.length() == 0) return NULL;
if (idx < 0) return NULL;
if (idx >= msg->msgSignals.count()) return NULL;
return &msg->msgSignals[idx];
}
//Dumps the messages, signals, values structs out in order to debugging console. Used only for debugging
//not really meant for general consumption.
void DBCHandler::listDebugging()
{
for (int i = 0; i < dbc_messages.length(); i++)
{
DBC_MESSAGE msg = dbc_messages.at(i);
qDebug() << " ";
qDebug() << "Msg ID: " << msg.ID << " Name: " << msg.name;
for (int j = 0; j < msg.msgSignals.length(); j++)
{
DBC_SIGNAL sig;
sig = msg.msgSignals.at(j);
qDebug() << " Signal Name: " << sig.name;
qDebug() << " Start bit: " << sig.startBit;
qDebug() << " Bit Length: " << sig.signalSize;
if (sig.valList.length() > 1) qDebug() << " Values: ";
for (int k = 0; k < sig.valList.length(); k++)
{
DBC_VAL val = sig.valList.at(k);
qDebug() << " " << val.value << " Description: " << val.descript;
}
}
filename = dialog.selectedFiles()[0];
if (!filename.contains('.')) filename += ".dbc";
loadedFiles[idx].saveFile(filename);
}
}
int DBCHandler::createBlankFile()
{
DBCFile newFile;
loadedFiles.append(newFile);
return loadedFiles.count();
}
//the only reason to even bother sending the index is to see if
//the user wants to replace an already loaded DBC.
//Otherwise add a new one. Well, always add a new one.
//If a valid index is passed we'll remove that one and then commence
//adding. Otherwise, just go straight to adding.
DBCFile* DBCHandler::loadDBCFile(int idx)
{
if (idx > -1 && idx < loadedFiles.count()) removeDBCFile(idx);
QString filename;
QFileDialog dialog;
QStringList filters;
filters.append(QString(tr("DBC File (*.dbc)")));
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
if (dialog.exec() == QDialog::Accepted)
{
filename = dialog.selectedFiles()[0];
//right now there is only one file type that can be loaded here so just do it.
DBCFile newFile;
newFile.loadFile(filename);
loadedFiles.append(newFile);
}
return &loadedFiles.last();
}
void DBCHandler::removeDBCFile(int idx)
{
if (loadedFiles.count() == 0) return;
if (idx < 0) return;
if (idx >= loadedFiles.count()) return;
loadedFiles.removeAt(idx);
}
void DBCHandler::removeAllFiles()
{
loadedFiles.clear();
}
void DBCHandler::swapFiles(int pos1, int pos2)
{
if (loadedFiles.count() < 2) return;
if (pos1 < 0) return;
if (pos1 >= loadedFiles.count()) return;
if (pos2 < 0) return;
if (pos2 >= loadedFiles.count()) return;
loadedFiles.swap(pos1, pos2);
}
/*
The way that the DBC file format works is kind of weird... For intel format signals you count up
from the start bit to the end bit which is (startbit + signallength - 1). At each point
bits are numbered in a sawtooth manner. What that means is that the very first bit is 0 and you count up
from there all of the way to 63 with each byte being 8 bits so bit 0 is the lowest bit in the first byte
and 8 is the lowest bit in the next byte up. The whole thing looks like this:
Bits
7 6 5 4 3 2 1 0
0 7 6 5 4 3 2 1 0
b 1 15 14 13 12 11 10 9 8
y 2 23 22 21 20 19 18 17 16
t 3 31 30 29 28 27 26 25 24
e 4 39 38 37 36 35 34 33 32
s 5 47 46 45 44 43 42 41 40
6 55 54 53 52 51 50 49 48
7 63 62 61 60 59 58 57 56
For intel format you start at the start bit and keep counting up. If you have a signal size of 8
and start at bit 12 then the bits are 12, 13, 14, 15, 16, 17, 18, 19 which spans across two bytes.
In this format each bit is worth twice as much as the last and you just keep counting up.
Bit 12 is worth 1, 13 is worth 2, 14 is worth 4, etc all of the way to bit 19 is worth 128.
Motorola format turns most everything on its head. You count backward from the start bit but
only within the current byte. If you are about to exit the current byte you go one higher and then keep
going backward as before. Using the same example as for intel, start bit of 12 and a signal length of 8.
So, the bits are 12, 11, 10, 9, 8, 23, 22, 21. Yes, that's confusing. They now go in reverse value order too.
Bit 12 is worth 128, 11 is worth 64, etc until bit 21 is worth 1.
* Convenience function that encapsulates a whole lot of the details.
* You give it a canbus frame and it'll tell you whether there is a loaded DBC file that can
* interpret that frame for you.
* Returns NULL if there is no message definition that matches.
*/
QString DBCHandler::processSignal(const CANFrame &frame, const DBC_SIGNAL &sig)
DBC_MESSAGE* DBCHandler::findMessage(const CANFrame &frame)
{
int64_t result = 0;
int bit;
int sigSize;
if (sig.valType == STRING)
for(int i = 0; i < loadedFiles.count(); i++)
{
QString buildString;
int startByte = sig.signalSize / 8;
int bytes = sig.signalSize / 8;
for (int x = 0; x < bytes; x++) buildString.append(frame.data[startByte + x]);
return buildString;
}
sigSize = sig.signalSize;
if (sig.intelByteOrder)
{
bit = sig.startBit;
for (int bitpos = 0; bitpos < sigSize; bitpos++)
if (loadedFiles[i].getAssocBus() == -1 || frame.bus == loadedFiles[i].getAssocBus())
{
if (frame.data[bit / 8] & (1 << (bit % 8)))
result += (1ULL << bitpos);
bit++;
DBC_MESSAGE* msg = loadedFiles[i].messageHandler->findMsgByID(frame.ID);
if (msg != NULL) return msg;
}
}
else //motorola / big endian mode
{
bit = sig.startBit;
for (int bitpos = 0; bitpos < sigSize; bitpos++)
{
if (frame.data[bit / 8] & (1 << (bit % 8)))
result += (1ULL << (sigSize - bitpos - 1));
if ((bit % 8) == 0)
bit += 15;
else bit--;
}
}
if (sig.valType == SIGNED_INT)
{
int mask = (1 << (sig.signalSize - 1));
if ((result & mask) == mask) //is the highest bit possible for this signal size set?
{
/*
* if so we need to also set every bit higher in the result int too.
* This leads to the below two lines that are nasty. Here's the theory behind that...
* If the value is signed and the highest bit is set then it is negative. To create
* a negative value out of this even though the variable result is 64 bit we have to
* run 1's all of the way up to bit 63 in result. -1 is all ones for whatever size integer
* you have. So, it's 64 1's in this case.
* signedMask is done this way:
* first you take the signal size and shift 1 up that far. Then subtract one. Lets
* see that for a 16 bit signal:
* (1 << 16) - 1 = the first 16 bits set as 1's. So far so good. We then negate the whole
* thing which flips all bits. Thus signedMask ends up with 1's everwhere that the signal
* doesn't take up in the 64 bit signed integer result. Then, result has an OR operation on
* it with the old value and -1 masked so that the the 1 bits from -1 don't overwrite bits from the
* actual signal. This extends the sign bits out so that the integer result reads as the proper negative
* value. We dont need to do any of this if the sign bit wasn't set.
*/
int signedMask = ~((1 << sig.signalSize) - 1);
result = (-1 & signedMask) | result;
}
}
double endResult = ((double)result * sig.factor) + sig.bias;
result = (int) endResult;
QString outputString;
outputString = sig.name + ": ";
if (sig.valList.count() > 0) //if this is a value list type then look it up and display the proper string
{
for (int x = 0; x < sig.valList.count(); x++)
{
if (sig.valList.at(x).value == result) outputString += sig.valList.at(x).descript;
}
}
else //otherwise display the actual number and unit (if it exists)
{
outputString += QString::number(endResult) + sig.unitName;
}
return outputString;
return NULL;
}
//given a byte it will reverse the bit order in that byte
unsigned char DBCHandler::reverseBits(unsigned char b) {
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}
unsigned char DBCHandler::processByte(unsigned char input, int start, int end)
int DBCHandler::getFileCount()
{
unsigned char output = 0, size = end - start + 1;
//first knock it down so that bottom is is start
output = input >> start;
//then mask off all bits above the proper ending
output &= ((1 << size) - 1);
return output;
return loadedFiles.count();
}
DBCFile* DBCHandler::getFileByIdx(int idx)
{
if (loadedFiles.count() == 0) return NULL;
if (idx < 0) return NULL;
if (idx >= loadedFiles.count()) return NULL;
return &loadedFiles[idx];
}
DBCFile* DBCHandler::getFileByName(QString name)
{
if (loadedFiles.count() == 0) return NULL;
for (int i = 0; i < loadedFiles.count(); i++)
{
if (loadedFiles[i].getFilename().compare(name, Qt::CaseInsensitive) == 0)
{
return &loadedFiles[i];
}
}
return NULL;
}
+69 -19
View File
@@ -6,40 +6,90 @@
#include "can_structs.h"
/*
* For ease of quick testing and development this is all run together.
* It should be decoupled once the functionality is confirmed.
* TODO:
* Finish coding up the decoupled design
*
*/
class DBCHandler : public QObject
class DBCSignalHandler: public QObject
{
Q_OBJECT
public:
explicit DBCHandler(QObject *parent = 0);
void loadDBCFile(QString);
void saveDBCFile(QString);
void listDebugging();
QString processSignal(const CANFrame &frame, const DBC_SIGNAL &sig);
DBC_NODE *findNodeByName(QString name);
DBC_NODE *findNodeByIdx(int idx);
DBC_SIGNAL *findSignalByName(QString name);
DBC_SIGNAL *findSignalByIdx(int idx);
bool addSignal(DBC_SIGNAL &sig);
bool removeSignal(DBC_SIGNAL *sig);
bool removeSignal(int idx);
bool removeSignal(QString name);
void removeAllSignals();
int getCount();
private:
QList<DBC_SIGNAL> sigs; //signals is a reserved word or I'd have used that
};
class DBCMessageHandler: public QObject
{
Q_OBJECT
public:
DBC_MESSAGE *findMsgByID(int id);
DBC_MESSAGE *findMsgByIdx(int idx);
DBC_MESSAGE *findMsgByName(QString name);
bool addMessage(DBC_MESSAGE &msg);
bool removeMessage(DBC_MESSAGE *msg);
bool removeMessageByIndex(int idx);
bool removeMessage(int ID);
bool removeMessage(QString name);
void removeAllMessages();
int getCount();
private:
QList<DBC_MESSAGE> messages;
};
DBC_SIGNAL *findSignalByName(DBC_MESSAGE *msg, QString name);
DBC_SIGNAL *findSignalByIdx(DBC_MESSAGE *msg, int idx);
//technically there should be a node handler too but I'm sort of treating nodes as second class
//citizens since they aren't really all that important (to me anyway)
class DBCFile: public QObject
{
Q_OBJECT
public:
DBCFile();
DBCFile(const DBCFile& cpy);
DBCFile& operator=(const DBCFile& cpy);
DBC_NODE *findNodeByName(QString name);
DBC_NODE *findNodeByIdx(int idx);
void saveFile(QString);
void loadFile(QString);
QString getFullFilename();
QString getFilename();
QString getPath();
int getAssocBus();
void setAssocBus(int bus);
DBCMessageHandler *messageHandler;
QList<DBC_NODE> dbc_nodes;
QList<DBC_MESSAGE> dbc_messages;
private:
QString fileName;
QString filePath;
int assocBuses; //-1 = all buses, 0 = first bus, 1 = second bus
};
signals:
public slots:
class DBCHandler: public QObject
{
Q_OBJECT
public:
DBCFile* loadDBCFile(int);
void saveDBCFile(int);
void removeDBCFile(int);
void removeAllFiles();
void swapFiles(int pos1, int pos2);
DBC_MESSAGE* findMessage(const CANFrame &frame);
int getFileCount();
DBCFile* getFileByIdx(int idx);
DBCFile* getFileByName(QString name);
int createBlankFile();
private:
unsigned char reverseBits(unsigned char);
unsigned char processByte(unsigned char, int, int);
QList<DBCFile> loadedFiles;
};
#endif // DBCHANDLER_H
+151
View File
@@ -0,0 +1,151 @@
#include "dbcloadsavewindow.h"
#include "ui_dbcloadsavewindow.h"
DBCLoadSaveWindow::DBCLoadSaveWindow(DBCHandler *handler, const QVector<CANFrame> *frames, QWidget *parent) :
QDialog(parent),
ui(new Ui::DBCLoadSaveWindow)
{
dbcHandler = handler;
referenceFrames = frames;
ui->setupUi(this);
QStringList header;
header << "Filename" << "Associated Bus";
ui->tableFiles->setColumnCount(2);
ui->tableFiles->setHorizontalHeaderLabels(header);
ui->tableFiles->setColumnWidth(0, 355);
ui->tableFiles->setColumnWidth(1, 125);
connect(ui->btnEdit, &QAbstractButton::clicked, this, &DBCLoadSaveWindow::editFile);
connect(ui->btnLoad, &QAbstractButton::clicked, this, &DBCLoadSaveWindow::loadFile);
connect(ui->btnMoveDown, &QAbstractButton::clicked, this, &DBCLoadSaveWindow::moveDown);
connect(ui->btnMoveUp, &QAbstractButton::clicked, this, &DBCLoadSaveWindow::moveUp);
connect(ui->btnRemove, &QAbstractButton::clicked, this, &DBCLoadSaveWindow::removeFile);
connect(ui->btnSave, &QAbstractButton::clicked, this, &DBCLoadSaveWindow::saveFile);
connect(ui->btnNewDBC, &QAbstractButton::clicked, this, &DBCLoadSaveWindow::newFile);
connect(ui->tableFiles, &QTableWidget::cellChanged, this, &DBCLoadSaveWindow::cellChanged);
connect(ui->tableFiles, &QTableWidget::cellDoubleClicked, this, &DBCLoadSaveWindow::cellDoubleClicked);
editorWindow = new DBCMainEditor(handler, frames);
}
DBCLoadSaveWindow::~DBCLoadSaveWindow()
{
delete ui;
}
void DBCLoadSaveWindow::newFile()
{
int idx = dbcHandler->createBlankFile();
idx = ui->tableFiles->rowCount();
ui->tableFiles->insertRow(ui->tableFiles->rowCount());
ui->tableFiles->setItem(idx, 0, new QTableWidgetItem("UNNAMEDFILE"));
ui->tableFiles->setItem(idx, 1, new QTableWidgetItem("-1"));
}
void DBCLoadSaveWindow::loadFile()
{
DBCFile *file = dbcHandler->loadDBCFile(-1);
int idx = ui->tableFiles->rowCount();
ui->tableFiles->insertRow(ui->tableFiles->rowCount());
ui->tableFiles->setItem(idx, 0, new QTableWidgetItem(file->getFullFilename()));
ui->tableFiles->setItem(idx, 1, new QTableWidgetItem("-1"));
}
void DBCLoadSaveWindow::saveFile()
{
int idx = ui->tableFiles->currentRow();
if (idx < 0) return;
dbcHandler->saveDBCFile(idx);
//then update the list to show the new file name (if it changed)
ui->tableFiles->setItem(idx, 0, new QTableWidgetItem(dbcHandler->getFileByIdx(idx)->getFullFilename()));
}
void DBCLoadSaveWindow::removeFile()
{
int idx = ui->tableFiles->currentRow();
if (idx < 0) return;
dbcHandler->removeDBCFile(idx);
ui->tableFiles->removeRow(idx);
}
void DBCLoadSaveWindow::moveUp()
{
int idx = ui->tableFiles->currentRow();
if (idx < 1) return;
dbcHandler->swapFiles(idx - 1, idx);
swapTableRows(true);
}
void DBCLoadSaveWindow::moveDown()
{
int idx = ui->tableFiles->currentRow();
if (idx < 0) return;
if (idx > (dbcHandler->getFileCount() - 2)) return;
dbcHandler->swapFiles(idx, idx + 1);
swapTableRows(false);
}
void DBCLoadSaveWindow::editFile()
{
int idx = ui->tableFiles->currentRow();
if (idx < 0) return;
editorWindow->setFileIdx(idx);
editorWindow->show();
}
void DBCLoadSaveWindow::cellChanged(int row, int col)
{
if (col == 1) //the bus column
{
DBCFile *file = dbcHandler->getFileByIdx(row);
int bus = ui->tableFiles->item(row, col)->text().toInt();
if (bus > -2 && bus < 2)
{
file->setAssocBus(bus);
}
}
}
void DBCLoadSaveWindow::cellDoubleClicked(int row, int col)
{
editorWindow->setFileIdx(row);
editorWindow->show();
}
void DBCLoadSaveWindow::swapTableRows(bool up)
{
int idx = ui->tableFiles->currentRow();
const int destIdx = (up ? idx-1 : idx+1);
Q_ASSERT(destIdx >= 0 && destIdx < ui->tableFiles->rowCount());
// take whole rows
QList<QTableWidgetItem*> sourceItems = takeRow(idx);
QList<QTableWidgetItem*> destItems = takeRow(destIdx);
// set back in reverse order
setRow(idx, destItems);
setRow(destIdx, sourceItems);
}
QList<QTableWidgetItem*> DBCLoadSaveWindow::takeRow(int row)
{
QList<QTableWidgetItem*> rowItems;
for (int col = 0; col < ui->tableFiles->columnCount(); ++col)
{
rowItems << ui->tableFiles->takeItem(row, col);
}
return rowItems;
}
void DBCLoadSaveWindow::setRow(int row, const QList<QTableWidgetItem*>& rowItems)
{
for (int col = 0; col < ui->tableFiles->columnCount(); ++col)
{
ui->tableFiles->setItem(row, col, rowItems.at(col));
}
}
+44
View File
@@ -0,0 +1,44 @@
#ifndef DBCLOADSAVEWINDOW_H
#define DBCLOADSAVEWINDOW_H
#include <QDialog>
#include <QTableWidget>
#include "dbchandler.h"
#include "dbcmaineditor.h"
namespace Ui {
class DBCLoadSaveWindow;
}
class DBCLoadSaveWindow : public QDialog
{
Q_OBJECT
public:
explicit DBCLoadSaveWindow(DBCHandler *handler, const QVector<CANFrame> *frames, QWidget *parent = 0);
~DBCLoadSaveWindow();
private slots:
void loadFile();
void saveFile();
void removeFile();
void moveUp();
void moveDown();
void editFile();
void cellChanged(int row, int col);
void cellDoubleClicked(int row, int col);
void newFile();
private:
Ui::DBCLoadSaveWindow *ui;
DBCHandler *dbcHandler;
const QVector<CANFrame> *referenceFrames;
DBCMainEditor *editorWindow;
void swapTableRows(bool up);
QList<QTableWidgetItem*> takeRow(int row);
void setRow(int row, const QList<QTableWidgetItem*>& rowItems);
};
#endif // DBCLOADSAVEWINDOW_H
+81
View File
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DBCLoadSaveWindow</class>
<widget class="QDialog" name="DBCLoadSaveWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>506</width>
<height>665</height>
</rect>
</property>
<property name="windowTitle">
<string>DBC File Manager</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTableWidget" name="tableFiles"/>
</item>
<item>
<widget class="QPushButton" name="btnNewDBC">
<property name="text">
<string>Create new DBC</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btnLoad">
<property name="text">
<string>Load</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnRemove">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnMoveUp">
<property name="text">
<string>Move Up</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="btnSave">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnEdit">
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnMoveDown">
<property name="text">
<string>Move Down</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+42 -34
View File
@@ -54,8 +54,8 @@ void DBCMainEditor::showEvent(QShowEvent* event)
inhibitCellChanged = true;
refreshNodesTable();
if (dbcHandler->dbc_nodes.count() > 0)
refreshMessagesTable(&dbcHandler->dbc_nodes.at(0));
if (dbcFile->dbc_nodes.count() > 0)
refreshMessagesTable(&dbcFile->dbc_nodes.at(0));
currRow = 0;
inhibitCellChanged = false;
@@ -67,6 +67,13 @@ DBCMainEditor::~DBCMainEditor()
delete sigEditor;
}
void DBCMainEditor::setFileIdx(int idx)
{
if (idx < 0 || idx > dbcHandler->getFileCount() - 1) return;
dbcFile = dbcHandler->getFileByIdx(idx);
fileIdx = idx;
}
void DBCMainEditor::closeEvent(QCloseEvent *event)
{
Q_UNUSED(event);
@@ -123,9 +130,9 @@ void DBCMainEditor::deleteCurrentNode()
if (nodeName.length() > 0 && nodeName.compare("Vector__XXX", Qt::CaseInsensitive) != 0)
{
ui->NodesTable->removeRow(thisRow);
dbcHandler->dbc_nodes.removeAt(thisRow);
dbcFile->dbc_nodes.removeAt(thisRow);
inhibitCellChanged = true;
refreshMessagesTable(&dbcHandler->dbc_nodes[0]);
refreshMessagesTable(&dbcFile->dbc_nodes[0]);
ui->NodesTable->selectRow(0);
inhibitCellChanged = false;
}
@@ -137,7 +144,7 @@ void DBCMainEditor::deleteCurrentMessage()
if (ui->MessagesTable->item(thisRow, 0)->text().length() > 0)
{
ui->MessagesTable->removeRow(thisRow);
dbcHandler->dbc_messages.removeAt(thisRow);
dbcFile->messageHandler->removeMessageByIndex(thisRow);
}
}
@@ -156,7 +163,7 @@ void DBCMainEditor::onCellChangedNode(int row,int col)
QString newName = ui->NodesTable->item(row, col)->text().simplified().replace(' ', '_');
qDebug() << "new name: " << newName;
if (newName.length() == 0) return;
if (dbcHandler->findNodeByName(newName) != NULL) //duplicates an existing node!
if (dbcFile->findNodeByName(newName) != NULL) //duplicates an existing node!
{
QMessageBox msg;
msg.setParent(0);
@@ -165,8 +172,8 @@ void DBCMainEditor::onCellChangedNode(int row,int col)
return;
}
newNode.name = newName;
dbcHandler->dbc_nodes.append(newNode);
qDebug() << "# of nodes now " << dbcHandler->dbc_nodes.count();
dbcFile->dbc_nodes.append(newNode);
qDebug() << "# of nodes now " << dbcFile->dbc_nodes.count();
QTableWidgetItem *widgetName = new QTableWidgetItem(newName);
inhibitCellChanged = true;
ui->NodesTable->setItem(row, col, widgetName);
@@ -178,7 +185,7 @@ void DBCMainEditor::onCellChangedNode(int row,int col)
{
if (col == 0)
{
DBC_NODE *oldNode = dbcHandler->findNodeByIdx(row);
DBC_NODE *oldNode = dbcFile->findNodeByIdx(row);
QString nodeName = ui->NodesTable->item(row, col)->text().simplified().replace(' ', '_');
if (oldNode == NULL) return;
if (row != 0) oldNode->name = nodeName;
@@ -192,7 +199,7 @@ void DBCMainEditor::onCellChangedNode(int row,int col)
{
QString nodeName = ui->NodesTable->item(row, 0)->text().simplified().replace(' ', '_');
qDebug() << "searching for node " << nodeName;
DBC_NODE *thisNode = dbcHandler->findNodeByName(nodeName);
DBC_NODE *thisNode = dbcFile->findNodeByName(nodeName);
if (thisNode == NULL) return;
thisNode->comment = ui->NodesTable->item(row, col)->text().simplified();
qDebug() << "New comment: " << thisNode->comment;
@@ -213,7 +220,7 @@ void DBCMainEditor::onCellChangedMessage(int row,int col)
int msgLen;
QString msgComment;
DBC_MESSAGE newMsg;
DBC_NODE *node = dbcHandler->findNodeByIdx(ui->NodesTable->currentRow());
DBC_NODE *node = dbcFile->findNodeByIdx(ui->NodesTable->currentRow());
DBC_MESSAGE *msg;
if (node == NULL)
{
@@ -224,14 +231,14 @@ void DBCMainEditor::onCellChangedMessage(int row,int col)
msgID = Utility::ParseStringToNum(ui->MessagesTable->item(row, 0)->text());
qDebug() << "Msg ID of edited: " << msgID;
msg = dbcHandler->findMsgByID(msgID);
msg = dbcFile->messageHandler->findMsgByID(msgID);
switch(col)
{
case 0: //msg id
if (row == ui->MessagesTable->rowCount() - 1) //new record
{
if (dbcHandler->findMsgByID(msgID) != NULL)
if (dbcFile->messageHandler->findMsgByID(msgID) != NULL)
{
QMessageBox msg;
msg.setParent(0);
@@ -251,7 +258,7 @@ void DBCMainEditor::onCellChangedMessage(int row,int col)
break;
}
}
dbcHandler->dbc_messages.append(newMsg);
dbcFile->messageHandler->addMessage(newMsg);
}
else //editing an existing record
{
@@ -270,7 +277,7 @@ void DBCMainEditor::onCellChangedMessage(int row,int col)
if (msgName.length() == 0) return;
if (row == ui->MessagesTable->rowCount() - 1) //new record
{
if (dbcHandler->findMsgByName(msgName) != NULL)
if (dbcFile->messageHandler->findMsgByName(msgName) != NULL)
{
QMessageBox msg;
msg.setParent(0);
@@ -281,7 +288,7 @@ void DBCMainEditor::onCellChangedMessage(int row,int col)
newMsg.ID = -1;
newMsg.name = msgName;
newMsg.sender = node;
dbcHandler->dbc_messages.append(newMsg);
dbcFile->messageHandler->addMessage(newMsg);
}
else
{
@@ -304,7 +311,7 @@ void DBCMainEditor::onCellChangedMessage(int row,int col)
newMsg.name = "";
newMsg.len = msgLen;
newMsg.sender = node;
dbcHandler->dbc_messages.append(newMsg);
dbcFile->messageHandler->addMessage(newMsg);
}
else //editing an existing record
{
@@ -328,7 +335,7 @@ void DBCMainEditor::onCellChangedMessage(int row,int col)
newMsg.len = 0;
newMsg.comment = msgComment;
newMsg.sender = node;
dbcHandler->dbc_messages.append(newMsg);
dbcFile->messageHandler->addMessage(newMsg);
}
else //editing an existing record
{
@@ -363,7 +370,7 @@ void DBCMainEditor::onCellClickedNode(int row, int col)
nodeName = item->text();
qDebug() << "Trying to find node with name " << nodeName;
DBC_NODE *node = dbcHandler->findNodeByName(nodeName);
DBC_NODE *node = dbcFile->findNodeByName(nodeName);
//qDebug() << "Address of node: " << (int)node;
inhibitCellChanged = true;
refreshMessagesTable(node);
@@ -376,12 +383,13 @@ void DBCMainEditor::onCellClickedMessage(int row, int col)
if (col == 3) //3 is the signals field. If clicked we go to the signals dialog
{
QString idString = ui->MessagesTable->item(row, 0)->text();
DBC_MESSAGE *message = dbcHandler->findMsgByID(Utility::ParseStringToNum(idString));
DBC_MESSAGE *message = dbcFile->messageHandler->findMsgByID(Utility::ParseStringToNum(idString));
sigEditor->setMessageRef(message);
sigEditor->setFileIdx(fileIdx);
sigEditor->exec(); //blocks this window from being active until we're done
//now update the displayed # of signals
inhibitCellChanged = true;
QTableWidgetItem *replacement = new QTableWidgetItem(QString::number(message->msgSignals.count()));
inhibitCellChanged = true;
QTableWidgetItem *replacement = new QTableWidgetItem(QString::number(message->sigHandler->getCount()));
ui->MessagesTable->setItem(row, col, replacement);
inhibitCellChanged = false;
}
@@ -394,17 +402,17 @@ void DBCMainEditor::refreshNodesTable()
int rowIdx;
if (dbcHandler->findNodeByName("Vector__XXX") == NULL)
if (dbcFile->findNodeByName("Vector__XXX") == NULL)
{
DBC_NODE newNode;
newNode.name = "Vector__XXX";
newNode.comment = "Default node if no other node is specified";
dbcHandler->dbc_nodes.append(newNode);
dbcFile->dbc_nodes.append(newNode);
}
for (int x = 0; x < dbcHandler->dbc_nodes.count(); x++)
for (int x = 0; x < dbcFile->dbc_nodes.count(); x++)
{
DBC_NODE node = dbcHandler->dbc_nodes.at(x);
DBC_NODE node = dbcFile->dbc_nodes.at(x);
QTableWidgetItem *nodeName = new QTableWidgetItem(node.name);
QTableWidgetItem *nodeComment = new QTableWidgetItem(node.comment);
rowIdx = ui->NodesTable->rowCount();
@@ -428,17 +436,17 @@ void DBCMainEditor::refreshMessagesTable(const DBC_NODE *node)
if (node != NULL)
{
for (int x = 0; x < dbcHandler->dbc_messages.count(); x++)
for (int x = 0; x < dbcFile->messageHandler->getCount(); x++)
{
DBC_MESSAGE msg = dbcHandler->dbc_messages.at(x);
if (msg.sender == node)
DBC_MESSAGE *msg = dbcFile->messageHandler->findMsgByIdx(x);
if (msg->sender == node)
{
//many of these are simplistic first versions just to test functionality.
QTableWidgetItem *msgID = new QTableWidgetItem(Utility::formatNumber(msg.ID));
QTableWidgetItem *msgName = new QTableWidgetItem(msg.name);
QTableWidgetItem *msgLen = new QTableWidgetItem(QString::number(msg.len));
QTableWidgetItem *msgSignals = new QTableWidgetItem(QString::number(msg.msgSignals.count()));
QTableWidgetItem *msgComment = new QTableWidgetItem(msg.comment);
QTableWidgetItem *msgID = new QTableWidgetItem(Utility::formatNumber(msg->ID));
QTableWidgetItem *msgName = new QTableWidgetItem(msg->name);
QTableWidgetItem *msgLen = new QTableWidgetItem(QString::number(msg->len));
QTableWidgetItem *msgSignals = new QTableWidgetItem(QString::number(msg->sigHandler->getCount()));
QTableWidgetItem *msgComment = new QTableWidgetItem(msg->comment);
rowIdx = ui->MessagesTable->rowCount();
ui->MessagesTable->insertRow(rowIdx);
+3
View File
@@ -18,6 +18,7 @@ class DBCMainEditor : public QDialog
public:
explicit DBCMainEditor(DBCHandler *handler, const QVector<CANFrame> *frames, QWidget *parent = 0);
~DBCMainEditor();
void setFileIdx(int idx);
private slots:
void onCellChangedNode(int,int);
@@ -35,6 +36,8 @@ private:
const QVector<CANFrame> *referenceFrames;
DBCSignalEditor *sigEditor;
int currRow;
DBCFile *dbcFile;
int fileIdx;
bool inhibitCellChanged;
void refreshNodesTable();
+144 -20
View File
@@ -33,11 +33,6 @@ DBCSignalEditor::DBCSignalEditor(DBCHandler *handler, QWidget *parent) :
ui->comboType->addItem("DOUBLE PRECISION");
ui->comboType->addItem("STRING");
for (int x = 0; x < dbcHandler->dbc_nodes.count(); x++)
{
ui->comboReceiver->addItem(dbcHandler->dbc_nodes[x].name);
}
connect(ui->signalsList, SIGNAL(currentRowChanged(int)), this, SLOT(clickSignalList(int)));
connect(ui->bitfield, SIGNAL(gridClicked(int,int)), this, SLOT(bitfieldClicked(int,int)));
connect(ui->signalsList, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomMenuSignals(QPoint)));
@@ -52,6 +47,8 @@ DBCSignalEditor::DBCSignalEditor(DBCHandler *handler, QWidget *parent) :
{
if (currentSignal == NULL) return;
currentSignal->intelByteOrder = ui->cbIntelFormat->isChecked();
if (currentSignal->valType == SP_FLOAT || currentSignal->valType == DP_FLOAT)
currentSignal->intelByteOrder = false;
fillSignalForm(currentSignal);
});
@@ -59,7 +56,7 @@ DBCSignalEditor::DBCSignalEditor(DBCHandler *handler, QWidget *parent) :
[=]()
{
if (currentSignal == NULL) return;
currentSignal->receiver = dbcHandler->findNodeByName(ui->comboReceiver->currentText());
currentSignal->receiver = dbcFile->findNodeByName(ui->comboReceiver->currentText());
});
connect(ui->comboType, &QComboBox::currentTextChanged,
[=]()
@@ -75,14 +72,21 @@ DBCSignalEditor::DBCSignalEditor(DBCHandler *handler, QWidget *parent) :
break;
case 2:
currentSignal->valType = SP_FLOAT;
currentSignal->intelByteOrder = false;
if (currentSignal->startBit > 39) currentSignal->startBit = 39;
currentSignal->signalSize = 32;
break;
case 3:
currentSignal->valType = DP_FLOAT;
currentSignal->intelByteOrder = false;
currentSignal->startBit = 7; //has to be!
currentSignal->signalSize = 64;
break;
case 4:
currentSignal->valType = STRING;
break;
break;
}
fillSignalForm(currentSignal);
});
connect(ui->txtBias, &QLineEdit::editingFinished,
[=]()
@@ -143,7 +147,8 @@ DBCSignalEditor::DBCSignalEditor(DBCHandler *handler, QWidget *parent) :
temp = Utility::ParseStringToNum(ui->txtBitLength->text());
if (temp < 0) return;
if (temp > 63) return;
currentSignal->signalSize = temp;
if (currentSignal->valType != SP_FLOAT && currentSignal->valType != DP_FLOAT)
currentSignal->signalSize = temp;
fillSignalForm(currentSignal);
});
connect(ui->txtName, &QLineEdit::editingFinished,
@@ -154,6 +159,52 @@ DBCSignalEditor::DBCSignalEditor(DBCHandler *handler, QWidget *parent) :
//need to update the list too.
ui->signalsList->currentItem()->setText(currentSignal->name);
});
connect(ui->txtMultiplexValue, &QLineEdit::editingFinished,
[=]()
{
if (currentSignal == NULL) return;
int temp;
temp = Utility::ParseStringToNum(ui->txtMultiplexValue->text());
//TODO: could look up the multiplexor and ensure that the value is within a range that the multiplexor could return
currentSignal->multiplexValue = temp;
});
connect(ui->rbMultiplexed, &QRadioButton::toggled,
[=](bool state)
{
if (state) //signal is now set as a multiplexed signal
{
currentSignal->isMultiplexed = true;
currentSignal->isMultiplexor = false;
//if the set multiplexor for the message was this signal then clear it
if (dbcMessage->multiplexorSignal == currentSignal) dbcMessage->multiplexorSignal = NULL;
}
});
connect(ui->rbMultiplexor, &QRadioButton::toggled,
[=](bool state)
{
if (state) //signal is now set as a multiplexed signal
{
//don't allow this signal to be a multiplexor if there is already one for this message.
//if (dbcMessage->multiplexorSignal != currentSignal && dbcMessage->multiplexorSignal != NULL) return; //I spoke too soon above...
currentSignal->isMultiplexed = false;
currentSignal->isMultiplexor = true;
//we just set that this is the multiplexor so update the message to show that as well.
dbcMessage->multiplexorSignal = currentSignal;
}
});
connect(ui->rbNotMulti, &QRadioButton::toggled,
[=](bool state)
{
if (state) //signal is now set as a multiplexed signal
{
currentSignal->isMultiplexed = false;
currentSignal->isMultiplexor = false;
if (dbcMessage->multiplexorSignal == currentSignal) dbcMessage->multiplexorSignal = NULL;
}
});
}
DBCSignalEditor::~DBCSignalEditor()
@@ -167,6 +218,17 @@ void DBCSignalEditor::closeEvent(QCloseEvent *event)
writeSettings();
}
void DBCSignalEditor::setFileIdx(int idx)
{
if (idx < 0 || idx > dbcHandler->getFileCount() - 1) return;
dbcFile = dbcHandler->getFileByIdx(idx);
for (int x = 0; x < dbcFile->dbc_nodes.count(); x++)
{
ui->comboReceiver->addItem(dbcFile->dbc_nodes[x].name);
}
}
void DBCSignalEditor::readSettings()
{
QSettings settings;
@@ -200,9 +262,9 @@ void DBCSignalEditor::showEvent(QShowEvent* event)
refreshSignalsList();
currentSignal = NULL;
if (dbcMessage->msgSignals.count() > 0)
if (dbcMessage->sigHandler->getCount() > 0)
{
currentSignal = &dbcMessage->msgSignals[0];
currentSignal = dbcMessage->sigHandler->findSignalByIdx(0);
fillSignalForm(currentSignal);
fillValueTable(currentSignal);
}
@@ -270,12 +332,17 @@ void DBCSignalEditor::addNewSignal()
newSig.intelByteOrder = true;
newSig.max = 0.0;
newSig.min = 0.0;
newSig.receiver = &dbcHandler->dbc_nodes[0];
newSig.receiver = dbcFile->findNodeByIdx(0);
newSig.signalSize = 1;
newSig.startBit = 0;
newSig.valType = UNSIGNED_INT;
newSig.isMultiplexed = false;
newSig.isMultiplexor = false;
newSig.multiplexValue = 0;
newSig.parentMessage = dbcMessage;
ui->signalsList->addItem(newName);
dbcMessage->msgSignals.append(newSig);
dbcMessage->sigHandler->addSignal(newSig);
if (dbcMessage->sigHandler->getCount() == 1) clickSignalList(0);
}
void DBCSignalEditor::deleteCurrentSignal()
@@ -284,10 +351,10 @@ void DBCSignalEditor::deleteCurrentSignal()
if (currIdx > -1)
{
delete(ui->signalsList->item(currIdx));
dbcMessage->msgSignals.removeAt(currIdx);
dbcMessage->sigHandler->removeSignal(currIdx);
currentSignal = NULL;
currIdx = ui->signalsList->currentRow();
if (currIdx > -1) currentSignal = &dbcMessage->msgSignals[currIdx];
if (currIdx > -1) currentSignal = dbcMessage->sigHandler->findSignalByIdx(currIdx);
fillSignalForm(currentSignal);
fillValueTable(currentSignal);
}
@@ -306,10 +373,10 @@ void DBCSignalEditor::deleteCurrentValue()
void DBCSignalEditor::refreshSignalsList()
{
ui->signalsList->clear();
for (int x = 0; x < dbcMessage->msgSignals.count(); x++)
for (int x = 0; x < dbcMessage->sigHandler->getCount(); x++)
{
DBC_SIGNAL sig = dbcMessage->msgSignals.at(x);
ui->signalsList->addItem(sig.name);
DBC_SIGNAL *sig = dbcMessage->sigHandler->findSignalByIdx(x);
ui->signalsList->addItem(sig->name);
}
}
@@ -317,6 +384,8 @@ void DBCSignalEditor::fillSignalForm(DBC_SIGNAL *sig)
{
unsigned char bitpattern[8];
generateUsedBits();
if (sig == NULL)
{
ui->txtName->setText("");
@@ -327,6 +396,10 @@ void DBCSignalEditor::fillSignalForm(DBC_SIGNAL *sig)
ui->txtMinVal->setText("");
ui->txtScale->setText("");
ui->txtUnitName->setText("");
ui->txtMultiplexValue->setText("");
ui->rbMultiplexed->setChecked(false);
ui->rbMultiplexor->setChecked(false);
ui->rbNotMulti->setChecked(true);
memset(bitpattern, 0, 8); //clear it out
ui->bitfield->setReference(bitpattern, false);
ui->bitfield->updateData(bitpattern, true);
@@ -338,11 +411,16 @@ void DBCSignalEditor::fillSignalForm(DBC_SIGNAL *sig)
ui->txtName->setText(sig->name);
ui->txtBias->setText(QString::number(sig->bias));
ui->txtBitLength->setText(QString::number(sig->signalSize));
ui->txtMultiplexValue->setText(QString::number(sig->multiplexValue));
ui->txtComment->setText(sig->comment);
ui->txtMaxVal->setText(QString::number(sig->max));
ui->txtMinVal->setText(QString::number(sig->min));
ui->txtScale->setText(QString::number(sig->factor));
ui->txtUnitName->setText(sig->unitName);
ui->rbMultiplexed->setChecked(sig->isMultiplexed);
ui->rbMultiplexor->setChecked(sig->isMultiplexor);
ui->rbNotMulti->setChecked( !(sig->isMultiplexor | sig->isMultiplexed) );
qDebug() << sig->isMultiplexor << "*" << sig->isMultiplexed;
memset(bitpattern, 0, 8); //clear it out first.
@@ -379,7 +457,6 @@ void DBCSignalEditor::fillSignalForm(DBC_SIGNAL *sig)
}
}
ui->bitfield->updateData(bitpattern, true);
ui->cbIntelFormat->setChecked(sig->intelByteOrder);
@@ -446,12 +523,12 @@ void DBCSignalEditor::clickSignalList(int row)
{
if (row < 0) return;
//qDebug() << ui->signalsList->item(row)->text();
DBC_SIGNAL *thisSig = dbcHandler->findSignalByName(dbcMessage, ui->signalsList->item(row)->text());
DBC_SIGNAL *thisSig = dbcMessage->sigHandler->findSignalByName(ui->signalsList->item(row)->text());
if (thisSig == NULL) return;
currentSignal = thisSig;
fillSignalForm(thisSig);
fillValueTable(thisSig);
}
void DBCSignalEditor::bitfieldClicked(int x, int y)
@@ -459,5 +536,52 @@ void DBCSignalEditor::bitfieldClicked(int x, int y)
int bit = (7 - x) + (y * 8);
if (currentSignal == NULL) return;
currentSignal->startBit = bit;
if (currentSignal->valType == SP_FLOAT)
{
if (currentSignal->startBit > 31) currentSignal->startBit = 39;
}
if (currentSignal->valType == DP_FLOAT)
currentSignal->startBit = 7;
fillSignalForm(currentSignal);
}
void DBCSignalEditor::generateUsedBits()
{
uint8_t usedBits[8] = {0,0,0,0,0,0,0,0};
int startBit, endBit;
for (int x = 0; x < dbcMessage->sigHandler->getCount(); x++)
{
DBC_SIGNAL *sig = dbcMessage->sigHandler->findSignalByIdx(x);
startBit = sig->startBit;
if (sig->intelByteOrder)
{
endBit = startBit + sig->signalSize - 1;
if (startBit < 0) startBit = 0;
if (endBit > 63) endBit = 63;
for (int y = startBit; y <= endBit; y++)
{
int byt = y / 8;
usedBits[byt] |= 1 << (y % 8);
}
}
else //big endian / motorola format
{
//much more irritating than the intel version...
int size = sig->signalSize;
while (size > 0)
{
int byt = startBit / 8;
usedBits[byt] |= 1 << (startBit % 8);
size--;
if ((startBit % 8) == 0) startBit += 15;
else startBit--;
if (startBit > 63) startBit = 63;
}
}
}
ui->bitfield->setUsed(usedBits, false);
}
+3
View File
@@ -17,6 +17,7 @@ public:
explicit DBCSignalEditor(DBCHandler *handler, QWidget *parent = 0);
void setMessageRef(DBC_MESSAGE *msg);
void showEvent(QShowEvent*);
void setFileIdx(int idx);
~DBCSignalEditor();
private slots:
@@ -34,11 +35,13 @@ private:
DBCHandler *dbcHandler;
DBC_MESSAGE *dbcMessage;
DBC_SIGNAL *currentSignal;
DBCFile *dbcFile;
bool inhibitCellChanged;
void refreshSignalsList();
void fillSignalForm(DBC_SIGNAL *sig);
void fillValueTable(DBC_SIGNAL *sig);
void generateUsedBits();
void closeEvent(QCloseEvent *event);
void readSettings();
+45 -3
View File
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>884</width>
<height>791</height>
<height>915</height>
</rect>
</property>
<property name="windowTitle">
@@ -118,7 +118,7 @@
</property>
</widget>
</item>
<item row="12" column="0">
<item row="14" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Comment:</string>
@@ -153,9 +153,51 @@
<item row="10" column="1">
<widget class="QComboBox" name="comboReceiver"/>
</item>
<item row="12" column="1">
<item row="14" column="1">
<widget class="QLineEdit" name="txtComment"/>
</item>
<item row="12" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Multiplexing</string>
</property>
</widget>
</item>
<item row="12" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QRadioButton" name="rbNotMulti">
<property name="text">
<string>Not multiplexed</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbMultiplexed">
<property name="text">
<string>Multiplexed</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbMultiplexor">
<property name="text">
<string>Multiplexor</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="13" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Multiplex Value</string>
</property>
</widget>
</item>
<item row="13" column="1">
<widget class="QLineEdit" name="txtMultiplexValue"/>
</item>
</layout>
</widget>
</item>
+172 -22
View File
@@ -14,9 +14,47 @@ DiscreteStateWindow::DiscreteStateWindow(const QVector<CANFrame> *frames, QWidge
timer = new QTimer();
timer->setInterval(100);
isRealtime = ui->rbRealtime->isChecked();
typeChanged();
connect(ui->btnStart, SIGNAL(clicked(bool)), this, SLOT(handleStartButton()));
connect(timer, SIGNAL(timeout()), this, SLOT(handleTick()));
connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int)));
connect(ui->rbLogged, SIGNAL(clicked(bool)), this, SLOT(typeChanged()));
connect(ui->rbRealtime, SIGNAL(clicked(bool)), this, SLOT(typeChanged()));
connect(ui->btnAll, &QAbstractButton::clicked,
[=]()
{
for (int i = 0; i < ui->listID->count(); i++)
{
QListWidgetItem *item = ui->listID->item(i);
item->setCheckState(Qt::Checked);
idFilters[Utility::ParseStringToNum(item->text())] = true;
}
});
connect(ui->btnNone, &QAbstractButton::clicked,
[=]()
{
for (int i = 0; i < ui->listID->count(); i++)
{
QListWidgetItem *item = ui->listID->item(i);
item->setCheckState(Qt::Unchecked);
idFilters[Utility::ParseStringToNum(item->text())] = false;
}
});
connect(ui->listID, &QListWidget::itemChanged,
[=](QListWidgetItem *item)
{
bool isChecked = false;
int id = Utility::ParseStringToNum(item->text());
if (item->checkState() == Qt::Checked) isChecked = true;
idFilters[id] = isChecked;
});
refreshFilterList();
}
DiscreteStateWindow::~DiscreteStateWindow()
@@ -33,26 +71,89 @@ DiscreteStateWindow::~DiscreteStateWindow()
delete ui;
}
void DiscreteStateWindow::typeChanged()
{
if (ui->rbLogged->isChecked())
{
ui->spinFreq->setEnabled(false);
ui->spinIterations->setEnabled(false);
ui->lblStatus->setEnabled(false);
ui->spinMaxBits->setEnabled(true);
ui->spinMinBits->setEnabled(true);
ui->listID->setEnabled(true);
ui->btnAll->setEnabled(true);
ui->btnNone->setEnabled(true);
isRealtime = false;
}
else
{
ui->spinFreq->setEnabled(true);
ui->spinIterations->setEnabled(true);
ui->lblStatus->setEnabled(true);
ui->spinMaxBits->setEnabled(false);
ui->spinMinBits->setEnabled(false);
ui->listID->setEnabled(false);
ui->btnAll->setEnabled(false);
ui->btnNone->setEnabled(false);
isRealtime = true;
}
}
void DiscreteStateWindow::updatedFrames(int numFrames)
{
CANFrame thisFrame;
if (numFrames == -1) //all frames deleted. Kill the display
{
ui->listID->clear();
idFilters.clear();
}
else if (numFrames == -2) //all new set of frames. Reset
{
refreshFilterList();
}
else //just got some new frames. See if they are relevant.
{
if (numFrames > modelFrames->count()) return;
for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
{
thisFrame = modelFrames->at(i);
if (!idFilters.contains(thisFrame.ID))
{
idFilters.insert(thisFrame.ID, true);
QListWidgetItem* listItem = new QListWidgetItem(Utility::formatNumber(thisFrame.ID), ui->listID);
listItem->setFlags(listItem->flags() | Qt::ItemIsUserCheckable); // set checkable flag
listItem->setCheckState(Qt::Checked); //default all filters to be set active
}
if (operatingState == DWStates::IDLE) stateFrames[0]->append(thisFrame);
else stateFrames[currToggleState + 1]->append(thisFrame);
}
}
}
void DiscreteStateWindow::refreshFilterList()
{
int id;
idFilters.clear();
ui->listID->clear();
for (int i = 0; i < modelFrames->length(); i++)
{
id = modelFrames->at(i).ID;
if (!idFilters.contains(id))
{
idFilters.insert(id, true);
QListWidgetItem* listItem = new QListWidgetItem(Utility::formatNumber(id), ui->listID);
listItem->setFlags(listItem->flags() | Qt::ItemIsUserCheckable); // set checkable flag
listItem->setCheckState(Qt::Checked); //default all filters to be set active
}
}
ui->listID->sortItems();
}
void DiscreteStateWindow::showEvent(QShowEvent* event)
{
QDialog::showEvent(event);
@@ -96,7 +197,7 @@ void DiscreteStateWindow::updateStateLabel()
switch(operatingState)
{
case DWStates::IDLE:
ui->lblStatus->setText("WAIT");
ui->lblStatus->setText("IDLE");
pal = ui->lblStatus->palette();
pal.setColor(QPalette::WindowText, Qt::red);
ui->lblStatus->setPalette(pal);
@@ -108,7 +209,7 @@ void DiscreteStateWindow::updateStateLabel()
ui->lblStatus->setPalette(pal);
break;
case DWStates::COUNTDOWN_WAITING:
ui->lblStatus->setText("Return to resting state");
ui->lblStatus->setText("Wait....");
pal = ui->lblStatus->palette();
pal.setColor(QPalette::WindowText, Qt::red);
ui->lblStatus->setPalette(pal);
@@ -119,32 +220,45 @@ void DiscreteStateWindow::updateStateLabel()
pal.setColor(QPalette::WindowText, Qt::green);
ui->lblStatus->setPalette(pal);
break;
case DWStates::DONE:
ui->lblStatus->setText("DONE");
pal = ui->lblStatus->palette();
pal.setColor(QPalette::WindowText, Qt::green);
ui->lblStatus->setPalette(pal);
break;
}
}
void DiscreteStateWindow::handleStartButton()
{
operatingState = DWStates::COUNTDOWN_SIGNAL;
ticksPerStateChange = ticksUntilStateChange = ui->spinFreq->value() * 10;
numToggleStates = ui->spinStates->value();
numIterations = ui->spinIterations->value();
currToggleState = 0;
currIteration = 0;
for (int i = 0; i < stateFrames.count(); i++)
if (isRealtime)
{
stateFrames[i]->clear();
delete(stateFrames[i]);
}
operatingState = DWStates::COUNTDOWN_SIGNAL;
for (int j = 0; j <= numToggleStates; j++)
ticksPerStateChange = ticksUntilStateChange = ui->spinFreq->value() * 10;
numToggleStates = ui->spinStates->value();
numIterations = ui->spinIterations->value();
currToggleState = 0;
currIteration = 0;
for (int i = stateFrames.count() - 1; i > 0; i--)
{
stateFrames[i]->clear();
stateFrames.removeAt(i);
}
for (int j = 0; j <= numToggleStates; j++)
{
stateFrames.append(new QVector<CANFrame>());
}
timer->start();
}
else
{
stateFrames.append(new QVector<CANFrame>());
calculateResults();
}
timer->start();
}
void DiscreteStateWindow::handleTick()
@@ -167,11 +281,11 @@ void DiscreteStateWindow::handleTick()
{
ticksUntilStateChange = ticksPerStateChange;
currIteration++;
if (currIteration == numIterations)
if (currIteration > numIterations)
{
operatingState = DWStates::IDLE;
timer->stop();
//call to calculate our findings here.
calculateResults();
}
else operatingState = DWStates::COUNTDOWN_SIGNAL;
}
@@ -183,7 +297,7 @@ void DiscreteStateWindow::handleTick()
ticksUntilStateChange = ticksPerStateChange;
operatingState = DWStates::COUNTDOWN_WAITING;
currToggleState++;
if (currToggleState == numToggleStates) currToggleState = 0;
if (currToggleState > numToggleStates) currToggleState = 0;
}
break;
}
@@ -192,5 +306,41 @@ void DiscreteStateWindow::handleTick()
void DiscreteStateWindow::calculateResults()
{
int minBits, maxBits;
if (isRealtime)
{
}
else //use already loaded frames from main cache
{
//basic overview: run through all ID filters and see if it is enabled.
//If so add it to a giant list of messages by ID where each ID has its own
//list of messages
//Then, for each ID start a loop that runs from largest bits to smallest bits for scan
//For each value run through the algorithm for all messages. At the end list any matches
//the simplest approach seems to be to grab that number of bits and then record every
//unique value. IF the # of unique values is the same as the number of states then
//we've got a match.It should be noted that the # of states must be at least 2 - the idle
//state is 1 and then a second state at the minimum. Turn signals might be 3 states then
minBits = ui->spinMinBits->value();
maxBits = ui->spinMaxBits->value();
QHash<int, bool>::const_iterator it;
QList<CANFrame> frameCache;
for (it = idFilters.begin(); it != idFilters.end(); ++it)
{
if (it.value())
{
frameCache.clear();
for (int i = 0; i < modelFrames->count(); i++)
{
if (modelFrames->at(i).ID == it.key()) frameCache.append(modelFrames->at(i));
}
for (int bits = maxBits; bits >= minBits; bits--)
{
QList<int> values;
}
}
}
}
}
+6 -1
View File
@@ -15,7 +15,8 @@ enum DiscreteWindowState
IDLE,
COUNTDOWN_SIGNAL,
COUNTDOWN_WAITING,
GETTING_SIGNAL
GETTING_SIGNAL,
DONE
};
}
@@ -33,6 +34,7 @@ private slots:
void updatedFrames(int);
void handleStartButton();
void handleTick();
void typeChanged();
private:
Ui::DiscreteStateWindow *ui;
@@ -46,7 +48,10 @@ private:
int currToggleState;
int numIterations;
int currIteration;
bool isRealtime;
QHash<int, bool> idFilters;
void refreshFilterList();
void closeEvent(QCloseEvent *event);
void readSettings();
void writeSettings();
+213 -96
View File
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
<width>749</width>
<height>795</height>
</rect>
</property>
<property name="windowTitle">
@@ -31,63 +31,232 @@
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1" alignment="Qt::AlignHCenter">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Number of States:</string>
</property>
</widget>
</item>
<item row="0" column="0" alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Toggle Frequency (Seconds)</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QSpinBox" name="spinFreq">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>30</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
</item>
<item row="1" column="1">
<item>
<widget class="QSpinBox" name="spinStates">
<property name="minimum">
<number>1</number>
<number>2</number>
</property>
<property name="maximum">
<number>20</number>
</property>
</widget>
</item>
<item row="0" column="2" alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Iterations</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QSpinBox" name="spinIterations">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>10</number>
<number>64</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QRadioButton" name="rbRealtime">
<property name="text">
<string>Realtime</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbLogged">
<property name="text">
<string>From logged data</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="2,2">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Toggle Frequency (Seconds)</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinFreq">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>30</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Iterations</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinIterations">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>10</number>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="lblStatus">
<property name="palette">
<palette>
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>120</red>
<green>120</green>
<blue>120</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="font">
<font>
<family>Nirmala UI</family>
<pointsize>26</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>WAIT</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Min Bits</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinMinBits">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>8</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Max Bits</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinMaxBits">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>8</number>
</property>
<property name="value">
<number>8</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>IDs to Scan</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listID"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QPushButton" name="btnAll">
<property name="text">
<string>All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnNone">
<property name="text">
<string>None</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout"/>
</item>
<item>
<widget class="QPushButton" name="btnStart">
<property name="text">
@@ -95,58 +264,6 @@
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="lblStatus">
<property name="palette">
<palette>
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>120</red>
<green>120</green>
<blue>120</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="font">
<font>
<family>Nirmala UI</family>
<pointsize>26</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>WAIT</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
+6 -6
View File
@@ -58,11 +58,11 @@ void FileComparatorWindow::writeSettings()
void FileComparatorWindow::loadInterestedFile()
{
interestedFrames.clear();
QString result = FrameFileIO::loadFrameFile(&interestedFrames);
if (result.length() > 0)
QString resultingFileName;
if (FrameFileIO::loadFrameFile(resultingFileName, &interestedFrames))
{
ui->lblFirstFile->setText(result);
interestedFilename = result;
ui->lblFirstFile->setText(resultingFileName);
interestedFilename = resultingFileName;
if (interestedFrames.count() > 0 && referenceFrames.count() > 0) calculateDetails();
}
}
@@ -70,8 +70,8 @@ void FileComparatorWindow::loadInterestedFile()
void FileComparatorWindow::loadReferenceFile()
{
//secondFileFrames.clear();
QString result = FrameFileIO::loadFrameFile(&referenceFrames);
if (result.length() > 0)
QString resultingFileName;
if (FrameFileIO::loadFrameFile(resultingFileName, &referenceFrames))
{
ui->lblRefFrames->setText(QString::number(referenceFrames.length()));
if (interestedFrames.count() > 0 && referenceFrames.count() > 0) calculateDetails();
+1
View File
@@ -370,6 +370,7 @@ void FlowViewWindow::updatedFrames(int numFrames)
}
else //just got some new frames. See if they are relevant.
{
if (numFrames > modelFrames->count()) return;
int refID = frameCache[0].ID;
bool needRefresh = false;
for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
+1 -1
View File
@@ -799,7 +799,7 @@
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Trigger Values</string>
<string>Data Seek Values</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
+640 -30
View File
@@ -7,18 +7,121 @@ FrameFileIO::FrameFileIO()
}
QString FrameFileIO::loadFrameFile(QVector<CANFrame>* frameCache)
bool FrameFileIO::saveFrameFile(QString &fileName, const QVector<CANFrame>* frameCache)
{
QString filename;
QFileDialog dialog(qApp->activeWindow());
bool result = false;
QStringList filters;
filters.append(QString(tr("GVRET Logs (*.csv *.CSV)")));
filters.append(QString(tr("CRTD Logs (*.txt *.TXT)")));
filters.append(QString(tr("Generic ID/Data CSV (*.csv *.CSV)")));
filters.append(QString(tr("BusMaster Log (*.log *.LOG)")));
filters.append(QString(tr("Microchip Log (*.can *.CAN)")));
filters.append(QString(tr("Vector Trace Files (*.trace *.TRACE)")));
filters.append(QString(tr("IXXAT MiniLog (*.csv *.CSV)")));
filters.append(QString(tr("CAN-DO Log (*.can *.avc *.evc *.qcc *.CAN *.AVC *.EVC *.QCC)")));
filters.append(QString(tr("Vehicle Spy (*.csv *.CSV)")));
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
dialog.setAcceptMode(QFileDialog::AcceptSave);
if (dialog.exec() == QDialog::Accepted)
{
filename = dialog.selectedFiles()[0];
QProgressDialog progress(qApp->activeWindow());
progress.setWindowModality(Qt::WindowModal);
progress.setLabelText("Saving file...");
progress.setCancelButton(0);
progress.setRange(0,0);
progress.setMinimumDuration(0);
progress.show();
qApp->processEvents();
if (dialog.selectedNameFilter() == filters[0])
{
if (!filename.contains('.')) filename += ".csv";
result = saveNativeCSVFile(filename, frameCache);
}
if (dialog.selectedNameFilter() == filters[1])
{
if (!filename.contains('.')) filename += ".txt";
result = saveCRTDFile(filename, frameCache);
}
if (dialog.selectedNameFilter() == filters[2])
{
if (!filename.contains('.')) filename += ".csv";
result = saveGenericCSVFile(filename, frameCache);
}
if (dialog.selectedNameFilter() == filters[3])
{
if (!filename.contains('.')) filename += ".log";
result = saveLogFile(filename, frameCache);
}
if (dialog.selectedNameFilter() == filters[4])
{
if (!filename.contains('.')) filename += ".log";
result = saveMicrochipFile(filename, frameCache);
}
if (dialog.selectedNameFilter() == filters[5])
{
if (!filename.contains('.')) filename += ".trace";
result = saveTraceFile(filename, frameCache);
}
if (dialog.selectedNameFilter() == filters[6])
{
if (!filename.contains('.')) filename += ".csv";
result = saveIXXATFile(filename, frameCache);
}
if (dialog.selectedNameFilter() == filters[7])
{
if (!filename.contains('.')) filename += ".can";
result = saveCANDOFile(filename, frameCache);
}
if (dialog.selectedNameFilter() == filters[8])
{
if (!filename.contains('.')) filename += ".csv";
result = saveVehicleSpyFile(filename, frameCache);
}
progress.cancel();
if (result)
{
QStringList fileList = filename.split('/');
fileName = fileList[fileList.length() - 1];
return true;
}
return false;
}
return false;
}
bool FrameFileIO::loadFrameFile(QString &fileName, QVector<CANFrame>* frameCache)
{
QString filename;
QFileDialog dialog;
bool result = false;
QStringList filters;
filters.append(QString(tr("CRTD Logs (*.txt)")));
filters.append(QString(tr("GVRET Logs (*.csv)")));
filters.append(QString(tr("Generic ID/Data CSV (*.csv)")));
filters.append(QString(tr("BusMaster Log (*.log)")));
filters.append(QString(tr("Microchip Log (*.can)")));
filters.append(QString(tr("GVRET Logs (*.csv *.CSV)")));
filters.append(QString(tr("CRTD Logs (*.txt *.TXT)")));
filters.append(QString(tr("Generic ID/Data CSV (*.csv *.CSV)")));
filters.append(QString(tr("BusMaster Log (*.log *.LOG)")));
filters.append(QString(tr("Microchip Log (*.can *.CAN)")));
filters.append(QString(tr("Vector trace files (*.trace *.TRACE)")));
filters.append(QString(tr("IXXAT MiniLog (*.csv *.CSV)")));
filters.append(QString(tr("CAN-DO Log (*.avc *.can *.evc *.qcc *.AVC *.CAN *.EVC *.QCC)")));
filters.append(QString(tr("Vehicle Spy (*.csv *.CSV)")));
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilters(filters);
@@ -38,23 +141,93 @@ QString FrameFileIO::loadFrameFile(QVector<CANFrame>* frameCache)
qApp->processEvents();
if (dialog.selectedNameFilter() == filters[0]) result = loadCRTDFile(filename, frameCache);
if (dialog.selectedNameFilter() == filters[1]) result = loadNativeCSVFile(filename, frameCache);
if (dialog.selectedNameFilter() == filters[0]) result = loadNativeCSVFile(filename, frameCache);
if (dialog.selectedNameFilter() == filters[1]) result = loadCRTDFile(filename, frameCache);
if (dialog.selectedNameFilter() == filters[2]) result = loadGenericCSVFile(filename, frameCache);
if (dialog.selectedNameFilter() == filters[3]) result = loadLogFile(filename, frameCache);
if (dialog.selectedNameFilter() == filters[4]) result = loadMicrochipFile(filename, frameCache);
if (dialog.selectedNameFilter() == filters[5]) result = loadTraceFile(filename, frameCache);
if (dialog.selectedNameFilter() == filters[6]) result = loadIXXATFile(filename, frameCache);
if (dialog.selectedNameFilter() == filters[7]) result = loadCANDOFile(filename, frameCache);
if (dialog.selectedNameFilter() == filters[8]) result = loadVehicleSpyFile(filename, frameCache);
progress.cancel();
if (result)
{
QStringList fileList = filename.split('/');
filename = fileList[fileList.length() - 1];
return filename;
fileName = fileList[fileList.length() - 1];
return true;
}
else return QString("");
else return false;
}
return QString("");
return false;
}
//2,2550.368293675,0.003818174999651092,67371008,F,F,HS CAN $119,HS CAN,,119,F,F,00,00,00,00,00,00,0D,8B,,,
//Line,Abs Time(Sec),Rel Time (Sec),Status,Er,Tx,Description,Network,Node,Arb ID,Remote,Xtd,B1,B2,B3,B4,B5,B6,B7,B8,Value,Trigger,Signals
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
bool FrameFileIO::loadVehicleSpyFile(QString filename, QVector<CANFrame> *frames)
{
QFile *inFile = new QFile(filename);
CANFrame thisFrame;
QByteArray line;
int lineCounter = 0;
bool pastHeader = false;
if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text))
{
delete inFile;
return false;
}
while (!inFile->atEnd() && !pastHeader)
{
line = inFile->readLine().simplified().toUpper();
if (line.startsWith("LINE")) lineCounter++;
if (lineCounter == 2) pastHeader = true;
}
while (!inFile->atEnd()) {
lineCounter++;
if (lineCounter > 100)
{
qApp->processEvents();
lineCounter = 0;
}
line = inFile->readLine().simplified().toUpper();
QList<QByteArray> tokens = line.split(',');
thisFrame.bus = 0;
thisFrame.timestamp = tokens[1].toDouble() * 1000000.0;
if (tokens[5].startsWith("T")) thisFrame.isReceived = false;
else thisFrame.isReceived = true;
thisFrame.ID = tokens[9].toInt(NULL, 16);
if (tokens[11].startsWith("T")) thisFrame.extended = true;
else thisFrame.extended = false;
thisFrame.len = 0;
for (int i = 0; i < 8; i++)
{
if (tokens[12 + i].length() > 0)
{
thisFrame.data[i] = tokens[12 + i].toInt(NULL, 16);
thisFrame.len++;
}
else break;
}
frames->append(thisFrame);
}
inFile->close();
delete inFile;
return true;
}
bool FrameFileIO::saveVehicleSpyFile(QString filename, const QVector<CANFrame> *frames)
{
return true;
}
//CRTD format from Mark Webb-Johnson / OVMS project
@@ -92,7 +265,7 @@ bool FrameFileIO::loadCRTDFile(QString filename, QVector<CANFrame>* frames)
return false;
}
line = inFile->readLine(); //read out the header first and discard it.
line = inFile->readLine().toUpper(); //read out the header first and discard it.
while (!inFile->atEnd()) {
lineCounter++;
@@ -119,11 +292,14 @@ bool FrameFileIO::loadCRTDFile(QString filename, QVector<CANFrame>* frames)
}
//qDebug() << "decimal places " << decimalPlaces;
thisFrame.timestamp = (int64_t)(tokens[0].toDouble() * multiplier);
if (tokens[1] == "R11" || tokens[1] == "R29")
char firstChar = tokens[1].left(1)[0];
if (firstChar == 'R' || firstChar == 'T')
{
thisFrame.ID = tokens[2].toInt(NULL, 16);
if (tokens[1] == "R29") thisFrame.extended = true;
if (tokens[1] == "R29" || tokens[1] == "T29") thisFrame.extended = true;
else thisFrame.extended = false;
if (firstChar == 'T') thisFrame.isReceived = false;
else thisFrame.isReceived = true;
thisFrame.bus = 0;
thisFrame.len = tokens.length() - 3;
for (int d = 0; d < thisFrame.len; d++)
@@ -169,11 +345,15 @@ bool FrameFileIO::saveCRTDFile(QString filename, const QVector<CANFrame>* frames
outFile->write(QString::number(frames->at(c).timestamp / 1000000.0, 'f', 6).toUtf8());
outFile->putChar(' ');
if (frames->at(c).isReceived) outFile->putChar('R');
else outFile->putChar('T');
if (frames->at(c).extended)
{
outFile->write("R29 ");
outFile->write("29 ");
}
else outFile->write("R11 ");
else outFile->write("11 ");
outFile->write(QString::number(frames->at(c).ID, 16).toUpper().rightJustified(8, '0').toUtf8());
outFile->putChar(' ');
@@ -197,6 +377,7 @@ bool FrameFileIO::loadNativeCSVFile(QString filename, QVector<CANFrame>* frames)
QFile *inFile = new QFile(filename);
CANFrame thisFrame;
QByteArray line;
int fileVersion = 1;
long long timeStamp = Utility::GetTimeMS();
int lineCounter = 0;
@@ -206,7 +387,8 @@ bool FrameFileIO::loadNativeCSVFile(QString filename, QVector<CANFrame>* frames)
return false;
}
line = inFile->readLine(); //read out the header first and discard it.
line = inFile->readLine().toUpper(); //read out the header first and discard it.
if (line.at(23) == 'D') fileVersion = 2; //Dir is found starting at position 23 if this is a V2 file
while (!inFile->atEnd()) {
lineCounter++;
@@ -234,11 +416,27 @@ bool FrameFileIO::loadNativeCSVFile(QString filename, QVector<CANFrame>* frames)
thisFrame.ID = tokens[1].toInt(NULL, 16);
if (tokens[2].toUpper().contains("TRUE")) thisFrame.extended = 1;
else thisFrame.extended = 0;
thisFrame.bus = tokens[3].toInt();
thisFrame.len = tokens[4].toInt();
for (int c = 0; c < 8; c++) thisFrame.data[c] = 0;
for (int d = 0; d < thisFrame.len; d++)
thisFrame.data[d] = tokens[5 + d].toInt(NULL, 16);
if (fileVersion == 1)
{
thisFrame.isReceived = true;
thisFrame.bus = tokens[3].toInt();
thisFrame.len = tokens[4].toInt();
for (int c = 0; c < 8; c++) thisFrame.data[c] = 0;
for (int d = 0; d < thisFrame.len; d++)
thisFrame.data[d] = tokens[5 + d].toInt(NULL, 16);
}
else if (fileVersion == 2)
{
if (tokens[3].at(0) == 'R') thisFrame.isReceived = true;
else thisFrame.isReceived = false;
thisFrame.bus = tokens[4].toInt();
thisFrame.len = tokens[5].toInt();
for (int c = 0; c < 8; c++) thisFrame.data[c] = 0;
for (int d = 0; d < thisFrame.len; d++)
thisFrame.data[d] = tokens[6 + d].toInt(NULL, 16);
}
frames->append(thisFrame);
}
}
@@ -258,7 +456,7 @@ bool FrameFileIO::saveNativeCSVFile(QString filename, const QVector<CANFrame>* f
return false;
}
outFile->write("Time Stamp,ID,Extended,Bus,LEN,D1,D2,D3,D4,D5,D6,D7,D8");
outFile->write("Time Stamp,ID,Extended,Dir,Bus,LEN,D1,D2,D3,D4,D5,D6,D7,D8");
outFile->write("\n");
for (int c = 0; c < frames->count(); c++)
@@ -279,6 +477,9 @@ bool FrameFileIO::saveNativeCSVFile(QString filename, const QVector<CANFrame>* f
if (frames->at(c).extended) outFile->write("true,");
else outFile->write("false,");
if (frames->at(c).isReceived) outFile->write("Rx,");
else outFile->write("Tx,");
outFile->write(QString::number(frames->at(c).bus).toUtf8());
outFile->putChar(44);
@@ -447,7 +648,7 @@ bool FrameFileIO::loadLogFile(QString filename, QVector<CANFrame>* frames)
lineCounter = 0;
}
line = inFile->readLine();
line = inFile->readLine().toUpper();
if (line.startsWith("***")) continue;
if (line.length() > 1)
{
@@ -456,6 +657,8 @@ bool FrameFileIO::loadLogFile(QString filename, QVector<CANFrame>* frames)
timeStamp = (timeToks[0].toInt() * (1000ul * 1000ul * 60ul * 60ul)) + (timeToks[1].toInt() * (1000ul * 1000ul * 60ul))
+ (timeToks[2].toInt() * (1000ul * 1000ul)) + (timeToks[3].toInt() * 100ul);
thisFrame.timestamp = timeStamp;
if (tokens[1].at(0) == 'R') thisFrame.isReceived = true;
else thisFrame.isReceived = false;
thisFrame.ID = tokens[3].right(tokens[3].length() - 2).toInt(NULL, 16);
if (tokens[4] == "s") thisFrame.extended = false;
else thisFrame.extended = true;
@@ -511,7 +714,8 @@ bool FrameFileIO::saveLogFile(QString filename, const QVector<CANFrame>* frames)
tempStamp = timestamp.addMSecs(frames->at(c).timestamp / 1000);
outFile->write(tempStamp.toString("h:m:s:z").toUtf8());
outFile->write(" Rx ");
if (frames->at(c).isReceived) outFile->write(" Rx ");
else outFile->write(" Tx ");
outFile->write(QString::number(frames->at(c).bus).toUtf8() + " ");
outFile->write(QString::number(frames->at(c).ID, 16).toUpper().rightJustified(8, '0').toUtf8());
if (frames->at(c).extended) outFile->write(" x ");
@@ -532,6 +736,226 @@ bool FrameFileIO::saveLogFile(QString filename, const QVector<CANFrame>* frames)
return true;
}
//"00:01:03.03","223","Std","","00 00 00 00 49 00 00 01 "
bool FrameFileIO::loadIXXATFile(QString filename, QVector<CANFrame>* frames)
{
QFile *inFile = new QFile(filename);
CANFrame thisFrame;
QByteArray line;
uint64_t timeStamp = Utility::GetTimeMS();
int lineCounter = 0;
if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text))
{
delete inFile;
return false;
}
for (int i = 0; i < 7; i++) line = inFile->readLine(); //read out the header first and discard it.
while (!inFile->atEnd()) {
lineCounter++;
if (lineCounter > 100)
{
qApp->processEvents();
lineCounter = 0;
}
line = inFile->readLine().toUpper();
if (line.length() > 1)
{
QList<QByteArray> tokens = line.split(',');
QString timePortion = unQuote(tokens[0]);
QStringList timeToks = timePortion.split(':');
timeStamp = (timeToks[0].toInt() * (1000ul * 1000ul * 60ul * 60ul)) + (timeToks[1].toInt() * (1000ul * 1000ul * 60ul))
+ (timeToks[2].toDouble() * (1000.0 * 1000.0));
thisFrame.timestamp = timeStamp;
thisFrame.ID = unQuote(tokens[1]).toInt(NULL, 16);
if (unQuote(tokens[2]).toUpper().at(0) == 'S') thisFrame.extended = false;
else thisFrame.extended = true;
thisFrame.isReceived = true;
thisFrame.bus = 0;
QStringList dataToks = unQuote(tokens[4]).simplified().split(' ');
thisFrame.len = dataToks.length();
for (int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = dataToks[d].toInt(NULL, 16);
}
frames->append(thisFrame);
}
inFile->close();
delete inFile;
return true;
}
bool FrameFileIO::saveIXXATFile(QString filename, const QVector<CANFrame>* frames)
{
QFile *outFile = new QFile(filename);
QDateTime timestamp, tempStamp;
int lineCounter = 0;
timestamp = QDateTime::currentDateTime();
if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text))
{
delete outFile;
return false;
}
outFile->write("ASCII Trace IXXAT SavvyCAN V" + QString::number(VERSION).toUtf8() + "\n");
outFile->write("Date: " + timestamp.toString("d:M:yyyy").toUtf8() + "\n");
outFile->write("Start time: " + timestamp.toString("h:m:s").toUtf8() + "\n");
timestamp.addMSecs((frames->last().timestamp - frames->first().timestamp) / 1000);
outFile->write("Stop time: " + timestamp.toString("h:m:s").toUtf8() + "\n");
outFile->write("Overruns: 0\n");
outFile->write("Baudrate: 500 kbit/s\n"); //could be a lie... this code has no way to know the baud rate (at the moment)
outFile->write("\"Time\",\"Identifier (hex)\",\"Format\",\"Flags\",\"Data (hex)\"\n");
for (int c = 0; c < frames->count(); c++)
{
lineCounter++;
if (lineCounter > 100)
{
qApp->processEvents();
lineCounter = 0;
}
timestamp.setTime(QTime());
tempStamp = timestamp.addMSecs(frames->at(c).timestamp / 1000);
outFile->write("\"" + tempStamp.toString("h:m:s.").toUtf8() + tempStamp.toString("z").rightJustified(3, '0').toUtf8() + "\"");
outFile->write(",\"" + QString::number(frames->at(c).ID, 16).toUpper().rightJustified(8, '0').toUtf8() + "\"");
if (frames->at(c).extended) outFile->write(",\"Ext\"");
else outFile->write(",\"Std\"");
outFile->write(",\"\",\"");
for (int temp = 0; temp < frames->at(c).len; temp++)
{
outFile->write(QString::number(frames->at(c).data[temp], 16).toUpper().rightJustified(2, '0').toUtf8());
outFile->putChar(' ');
}
outFile->write("\"\n");
}
outFile->close();
delete outFile;
return true;
}
bool FrameFileIO::loadCANDOFile(QString filename, QVector<CANFrame>* frames)
{
QFile *inFile = new QFile(filename);
CANFrame thisFrame;
int lineCounter = 0;
QByteArray data;
int timeOffset = 0;
uint64_t lastTimeStamp = 0;
if (!inFile->open(QIODevice::ReadOnly))
{
delete inFile;
return false;
}
//this file format is in static 12 byte blocks.
//Bytes 0 - 1 are a time stamp
//Bytes 2 - 3 are the data length (top 4 bits) then ID (bottom 11 bits)
//Bytes 4 - 11 are the data bytes (padded with FF for bytes not used)
while (!inFile->atEnd())
{
lineCounter++;
if (lineCounter > 100)
{
qApp->processEvents();
lineCounter = 0;
}
data = inFile->read(12);
thisFrame.bus = 0;
thisFrame.isReceived = true;
thisFrame.extended = false; //format is incapable of extended frames
thisFrame.timestamp = 1000000ul * ((unsigned char)data[0] >> 2);
thisFrame.timestamp += (((data[0] & 3) << 8) + (unsigned char)data[1]) * 1000;
thisFrame.timestamp += timeOffset;
if (thisFrame.timestamp < lastTimeStamp)
{
timeOffset += 60000000ul;
}
lastTimeStamp = thisFrame.timestamp;
thisFrame.ID = ((unsigned char)data[3] * 256 + (unsigned char)data[2]) & 0x7FF;
thisFrame.len = (unsigned char)data[3] >> 4;
if (thisFrame.len <= 8 && thisFrame.ID <= 0x7FF)
{
for (int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = (unsigned char)data[4 + d];
frames->append(thisFrame);
}
}
inFile->close();
delete inFile;
return true;
}
bool FrameFileIO::saveCANDOFile(QString filename, const QVector<CANFrame>* frames)
{
QFile *outFile = new QFile(filename);
int lineCounter = 0;
QByteArray data;
CANFrame thisFrame;
int ms, id;
if (!outFile->open(QIODevice::WriteOnly))
{
delete outFile;
return false;
}
data.reserve(13);
//The initial frame in official files sets the global time but I don't care so it is set all zeros here.
thisFrame = frames->at(0);
ms = (thisFrame.timestamp / 1000);
data[0] = (((ms / 1000) % 60) << 2) + ((ms % 1000) >> 8);
data[1] = (char)(ms & 0xFF);
data[2] = 0xFF;
data[3] = 0xFF;
for (int l = 0; l < 8; l++) data[4 + l] = 0;
outFile->write(data);
for (int c = 0; c < frames->count(); c++)
{
lineCounter++;
if (lineCounter > 100)
{
qApp->processEvents();
lineCounter = 0;
}
for (int j = 0; j < 8; j++) data[4 + j] = 0xFF;
thisFrame = frames->at(c);
if (!thisFrame.extended)
{
ms = (thisFrame.timestamp / 1000);
id = thisFrame.ID & 0x7FF;
data[0] = (((ms / 1000) % 60) << 2) + ((ms % 1000) >> 8);
data[1] = (char)(ms & 0xFF);
data[2] = (char)(id & 0xFF);
data[3] = (char)((id >> 8) + (thisFrame.len << 4));
for (int d = 0; d < thisFrame.len; d++) data[4 + d] = (char)thisFrame.data[d];
outFile->write(data);
}
}
outFile->close();
delete outFile;
return true;
}
//log file from microchip tool
/*
tokens:
@@ -547,7 +971,7 @@ bool FrameFileIO::loadMicrochipFile(QString filename, QVector<CANFrame>* frames)
CANFrame thisFrame;
QByteArray line;
bool inComment = false;
long long timeStamp = Utility::GetTimeMS();
long long timeStamp;
int lineCounter = 0;
if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text))
@@ -578,9 +1002,10 @@ bool FrameFileIO::loadMicrochipFile(QString filename, QVector<CANFrame>* frames)
if (!inComment)
{
QList<QByteArray> tokens = line.split(';');
timeStamp += 5;
timeStamp = tokens[0].toInt() * 1000;
thisFrame.timestamp = timeStamp;
if (tokens[1].at(0) == 'R') thisFrame.isReceived = true;
else thisFrame.isReceived = false;
thisFrame.ID = Utility::ParseStringToNum(tokens[2]);
if (thisFrame.ID <= 0x7FF) thisFrame.extended = false;
else thisFrame.extended = true;
@@ -641,7 +1066,8 @@ bool FrameFileIO::saveMicrochipFile(QString filename, const QVector<CANFrame>* f
}
outFile->write(QString::number((int)(frames->at(c).timestamp / 1000)).toUtf8());
outFile->write(";RX;");
if (frames->at(c).isReceived) outFile->write(";RX;");
else outFile->write(";TX;");
outFile->write("0x" + QString::number(frames->at(c).ID, 16).toUpper().rightJustified(8, '0').toUtf8() + ";");
outFile->write(QString::number(frames->at(c).len).toUtf8() + ";");
@@ -658,3 +1084,187 @@ bool FrameFileIO::saveMicrochipFile(QString filename, const QVector<CANFrame>* f
delete outFile;
return true;
}
/*
; CAN Logger trace file
; Device Serial Number :
; Start Time : Tue, Nov 17, 2015 :: 11:19:59
;
; Column description :
; ~~~~~~~~~~~~~~~~~~~~~
;
; + Message Number
; |
; | + Time Stamp (ms)
; | |
; | | + Message ID (hex)
; | | |
; | | | + Data Length Code
; | | | |
; | | | | + Data Bytes (hex)
; | | | | |
;---+----- -----+------ ----+--- + -+ -- -- -- -- -- -- --
0 00:00:00:0008 00000268 8 55 00 84 03 A8 0D 38 00
1 00:00:00:0021 0000000E 8 1F D3 3F FF 08 FF E0 CB
It appears that all lines that start in a semicolon are comments
The rest of the lines need the whitespace stripped from beginning and end
and then it appears to be tab delimited. The first field is a sequence number
and irrelevant. The second field is a funky timestamp, then message id, etc as
shown in the file comments. The bytes seem to be space delimited and in hex
*/
bool FrameFileIO::loadTraceFile(QString filename, QVector<CANFrame>* frames)
{
QFile *inFile = new QFile(filename);
CANFrame thisFrame;
QByteArray line;
long long timeStamp = 0;
int lineCounter = 0;
if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text))
{
delete inFile;
return false;
}
while (!inFile->atEnd()) {
lineCounter++;
if (lineCounter > 100)
{
qApp->processEvents();
lineCounter = 0;
}
line = inFile->readLine();
line = line.trimmed();
if (line.length() > 2)
{
if (line.startsWith(";"))
{
// a comment. Ignore it.
}
else
{
QList<QByteArray> tokens = line.split('\t');
QList<QByteArray> timestampToks = tokens[1].split(':');
timeStamp = timestampToks[0].toInt() * 1000000ul * 60 * 60;
timeStamp += timestampToks[1].toInt() * 1000000ul * 60;
timeStamp += timestampToks[2].toInt() * 1000000ul;
timeStamp += timestampToks[3].toInt() * 100;
thisFrame.timestamp = timeStamp;
thisFrame.ID = tokens[2].toLong(NULL, 16);
if (thisFrame.ID <= 0x7FF) thisFrame.extended = false;
else thisFrame.extended = true;
thisFrame.bus = 0;
thisFrame.len = tokens[3].toInt();
QList<QByteArray> dataToks = tokens[4].split(' ');
for (int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = (unsigned char)dataToks[d].toInt(NULL, 16);
frames->append(thisFrame);
}
}
}
inFile->close();
delete inFile;
return true;
}
bool FrameFileIO::saveTraceFile(QString filename, const QVector<CANFrame> * frames)
{
QFile *outFile = new QFile(filename);
QDateTime timestamp;
int lineCounter = 0;
uint64_t tempTime;
int tempTimePiece;
timestamp = QDateTime::currentDateTime();
if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text))
{
delete outFile;
return false;
}
outFile->write("; SavvyCAN CAN Logger trace file\n");
outFile->write("; Device Serial Number : 0000 \n");
outFile->write("; Start Time : ");
outFile->write(timestamp.toString("ddd, MMM dd, yyyy :: h:m:s\n").toUtf8());
outFile->write(";\n");
outFile->write("; Column description :\n");
outFile->write("; ~~~~~~~~~~~~~~~~~~~~~\n");
outFile->write(";\n");
outFile->write("; + Message Number\n");
outFile->write("; |\n");
outFile->write("; | + Time Stamp (ms)\n");
outFile->write("; | |\n");
outFile->write("; | | + Message ID (hex)\n");
outFile->write("; | | |\n");
outFile->write("; | | | + Data Length Code\n");
outFile->write("; | | | |\n");
outFile->write("; | | | | + Data Bytes (hex)\n");
outFile->write("; | | | | |\n");
outFile->write(";---+----- -----+------ ----+--- + -+ -- -- -- -- -- -- --\n");
for (int c = 0; c < frames->count(); c++)
{
lineCounter++;
if ((lineCounter % 100) == 0)
{
qApp->processEvents();
//lineCounter = 0;
}
//1F D3 3F FF 08 FF E0 CB
outFile->write(QString::number(lineCounter).rightJustified(10, ' ').toUtf8());
outFile->write("\t");
tempTime = frames->at(c).timestamp;
tempTimePiece = tempTime / 1000000ul / 60 / 60;
tempTime -= tempTimePiece * 1000000ul * 60 * 60;
outFile->write(QString::number(tempTimePiece).rightJustified(2, '0').toUtf8());
outFile->write(":");
tempTimePiece = tempTime / 1000000ul / 60;
tempTime -= tempTimePiece * 1000000ul * 60;
outFile->write(QString::number(tempTimePiece).rightJustified(2, '0').toUtf8());
outFile->write(":");
tempTimePiece = tempTime / 1000000ul;
tempTime -= tempTimePiece * 1000000ul;
outFile->write(QString::number(tempTimePiece).rightJustified(2, '0').toUtf8());
outFile->write(":");
tempTimePiece = tempTime / 100;
outFile->write(QString::number(tempTimePiece).rightJustified(4, '0').toUtf8());
outFile->write("\t");
outFile->write(QString::number(frames->at(c).ID, 16).toUpper().rightJustified(8, '0').toUtf8() + "\t");
outFile->write(QString::number(frames->at(c).len).toUtf8() + "\t");
for (int temp = 0; temp < frames->at(c).len; temp++)
{
outFile->write(QString::number(frames->at(c).data[temp], 16).toUpper().rightJustified(2, '0').toUtf8());
outFile->putChar(' ');
}
outFile->write("\n");
}
outFile->close();
delete outFile;
return true;
}
QString FrameFileIO::unQuote(QString inStr)
{
return inStr.split('\"')[1];
}
+14 -2
View File
@@ -27,12 +27,24 @@ public:
static bool loadGenericCSVFile(QString, QVector<CANFrame>*);
static bool loadLogFile(QString, QVector<CANFrame>*);
static bool loadMicrochipFile(QString, QVector<CANFrame>*);
static bool saveCRTDFile(QString, const QVector<CANFrame>*);
static bool loadTraceFile(QString, QVector<CANFrame>*);
static bool loadIXXATFile(QString, QVector<CANFrame>*);
static bool loadCANDOFile(QString, QVector<CANFrame>*);
static bool loadVehicleSpyFile(QString, QVector<CANFrame>*);
static bool saveCRTDFile(QString, const QVector<CANFrame>*);
static bool saveNativeCSVFile(QString, const QVector<CANFrame>*);
static bool saveGenericCSVFile(QString, const QVector<CANFrame>*);
static bool saveLogFile(QString, const QVector<CANFrame>*);
static bool saveMicrochipFile(QString, const QVector<CANFrame>*);
static QString loadFrameFile(QVector<CANFrame>*);
static bool saveTraceFile(QString, const QVector<CANFrame>*);
static bool saveIXXATFile(QString, const QVector<CANFrame>*);
static bool saveCANDOFile(QString, const QVector<CANFrame>*);
static bool saveVehicleSpyFile(QString, const QVector<CANFrame>*);
static bool loadFrameFile(QString &, QVector<CANFrame>*);
static bool saveFrameFile(QString &, const QVector<CANFrame>*);
private:
static QString unQuote(QString);
};
#endif // FRAMEFILEIO_H
+81 -5
View File
@@ -13,8 +13,10 @@ FrameInfoWindow::FrameInfoWindow(const QVector<CANFrame> *frames, QWidget *paren
modelFrames = frames;
connect(ui->listFrameID, SIGNAL(currentTextChanged(QString)), this, SLOT(updateDetailsWindow(QString)));
connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int)));
connect(ui->listFrameID, &QListWidget::currentTextChanged, this, &FrameInfoWindow::updateDetailsWindow);
connect(MainWindow::getReference(), &MainWindow::framesUpdated, this, &FrameInfoWindow::updatedFrames);
connect(ui->btnSave, &QAbstractButton::clicked, this, &FrameInfoWindow::saveDetails);
}
void FrameInfoWindow::showEvent(QShowEvent* event)
@@ -81,7 +83,10 @@ void FrameInfoWindow::updatedFrames(int numFrames)
}
else //just got some new frames. See if they are relevant.
{
int currID = ui->listFrameID->currentItem()->text().toInt(NULL, 16);
if (numFrames > modelFrames->count()) return;
int currID = 0;
if (ui->listFrameID->currentItem())
currID = ui->listFrameID->currentItem()->text().toInt(NULL, 16);
bool foundID = false;
for (int x = modelFrames->count() - numFrames; x < modelFrames->count(); x++)
{
@@ -113,6 +118,8 @@ void FrameInfoWindow::updateDetailsWindow(QString newID)
int maxData[8];
int dataHistogram[256][8];
int bitfieldHistogram[64];
uint8_t changedBits[8];
uint8_t referenceBits[8];
QTreeWidgetItem *baseNode, *dataBase, *histBase, *tempItem;
targettedID = Utility::ParseStringToNum(newID);
@@ -196,6 +203,13 @@ void FrameInfoWindow::updateDetailsWindow(QString newID)
}
for (int j = 0; j < 64; j++) bitfieldHistogram[j] = 0;
for (int c = 0; c < 8; c++)
{
changedBits[c] = 0;
referenceBits[c] = frameCache.at(0).data[c];
qDebug() << referenceBits[c];
}
//then find all data points
for (int j = 0; j < frameCache.count(); j++)
{
@@ -217,10 +231,13 @@ void FrameInfoWindow::updateDetailsWindow(QString newID)
bitfieldHistogram[c * 8 + l]++;
}
}
changedBits[c] |= referenceBits[c] ^ dat;
}
}
avgInterval = avgInterval / (frameCache.count() - 1);
if (frameCache.count() > 1)
avgInterval = avgInterval / (frameCache.count() - 1);
else avgInterval = 0;
tempItem = new QTreeWidgetItem();
@@ -242,6 +259,13 @@ void FrameInfoWindow::updateDetailsWindow(QString newID)
dataBase->setText(0, tr("Data Byte ") + QString::number(c));
baseNode->addChild(dataBase);
tempItem = new QTreeWidgetItem();
QString builder;
builder = tr("Changed bits: 0x") + QString::number(changedBits[c], 16) + " (" + Utility::formatByteAsBinary(changedBits[c]) + ")";
tempItem->setText(0, builder);
dataBase->addChild(tempItem);
tempItem = new QTreeWidgetItem();
tempItem->setText(0, tr("Range: ") + Utility::formatNumber(minData[c]) + tr(" to ") + Utility::formatNumber(maxData[c]));
dataBase->addChild(tempItem);
@@ -253,7 +277,7 @@ void FrameInfoWindow::updateDetailsWindow(QString newID)
if (dataHistogram[d][c] > 0)
{
tempItem = new QTreeWidgetItem();
tempItem->setText(0, QString::number(d) + "/0x" + QString::number(d, 16) +": " + QString::number(dataHistogram[d][c]));
tempItem->setText(0, QString::number(d) + "/0x" + QString::number(d, 16) +" (" + Utility::formatByteAsBinary(d) +") -> " + QString::number(dataHistogram[d][c]));
histBase->addChild(tempItem);
}
}
@@ -300,3 +324,55 @@ void FrameInfoWindow::refreshIDList()
ui->listFrameID->sortItems();
ui->lblFrameID->setText(tr("Frame IDs: (") + QString::number(ui->listFrameID->count()) + tr(" unique ids)"));
}
void FrameInfoWindow::saveDetails()
{
QString filename;
QFileDialog dialog(this);
QStringList filters;
filters.append(QString(tr("Text File (*.txt)")));
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
dialog.setAcceptMode(QFileDialog::AcceptSave);
if (dialog.exec() == QDialog::Accepted)
{
filename = dialog.selectedFiles()[0];
if (!filename.contains('.')) filename += ".txt";
if (dialog.selectedNameFilter() == filters[0])
{
QFile *outFile = new QFile(filename);
if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text))
{
delete outFile;
return;
}
//go through all IDs, recalculate the data, and then save it to file
for (int i = 0; i < ui->listFrameID->count(); i++)
{
updateDetailsWindow(ui->listFrameID->item(i)->text());
dumpNode(ui->treeDetails->invisibleRootItem(), outFile, 0);
outFile->write("\n\n");
}
outFile->close();
delete outFile;
}
}
}
void FrameInfoWindow::dumpNode(QTreeWidgetItem* item, QFile *file, int indent)
{
for (int i = 0; i < indent; i++) file->write("\t");
file->write(item->text(0).toUtf8());
file->write("\n");
for( int i = 0; i < item->childCount(); ++i )
dumpNode( item->child(i), file, indent + 1 );
}
+4
View File
@@ -2,7 +2,9 @@
#define FRAMEINFOWINDOW_H
#include <QDialog>
#include <QFile>
#include <QListWidget>
#include <QTreeWidget>
#include "can_structs.h"
namespace Ui {
@@ -21,6 +23,7 @@ public:
private slots:
void updateDetailsWindow(QString);
void updatedFrames(int);
void saveDetails();
private:
Ui::FrameInfoWindow *ui;
@@ -33,6 +36,7 @@ private:
void closeEvent(QCloseEvent *event);
void readSettings();
void writeSettings();
void dumpNode(QTreeWidgetItem* item, QFile *file, int indent);
};
#endif // FRAMEINFOWINDOW_H
+50 -39
View File
@@ -13,53 +13,64 @@
<property name="windowTitle">
<string>Detailed Frame Information</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="lblFrameID">
<property name="text">
<string>Frame IDs:</string>
</property>
</widget>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="lblFrameID">
<property name="text">
<string>Frame IDs:</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listFrameID">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QListWidget" name="listFrameID">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
</widget>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Details:</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="treeDetails">
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Details:</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="treeDetails">
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
</layout>
<widget class="QPushButton" name="btnSave">
<property name="text">
<string>Save details to file</string>
</property>
</widget>
</item>
</layout>
</widget>
+22 -44
View File
@@ -342,56 +342,33 @@ void FramePlaybackWindow::btnDeleteCurrSeq()
void FramePlaybackWindow::btnLoadFile()
{
QString filename;
QFileDialog dialog(this);
bool result = false;
SequenceItem item;
QStringList filters;
filters.append(QString(tr("CRTD Logs (*.txt)")));
filters.append(QString(tr("GVRET Logs (*.csv)")));
filters.append(QString(tr("Generic ID/Data CSV (*.csv)")));
filters.append(QString(tr("BusMaster Log (*.log)")));
filters.append(QString(tr("Microchip Log (*.log)")));
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
if (dialog.exec() == QDialog::Accepted)
if (FrameFileIO::loadFrameFile(filename, &item.data))
{
filename = dialog.selectedFiles()[0];
if (dialog.selectedNameFilter() == filters[0]) result = FrameFileIO::loadCRTDFile(filename, &item.data);
if (dialog.selectedNameFilter() == filters[1]) result = FrameFileIO::loadNativeCSVFile(filename, &item.data);
if (dialog.selectedNameFilter() == filters[2]) result = FrameFileIO::loadGenericCSVFile(filename, &item.data);
if (dialog.selectedNameFilter() == filters[3]) result = FrameFileIO::loadLogFile(filename, &item.data);
if (dialog.selectedNameFilter() == filters[4]) result = FrameFileIO::loadMicrochipFile(filename, &item.data);
if (result)
QStringList fileList = filename.split('/');
item.filename = fileList[fileList.length() - 1];
item.currentLoopCount = 0;
item.maxLoops = 1;
fillIDHash(item);
if (ui->tblSequence->currentRow() == -1)
{
QStringList fileList = filename.split('/');
item.filename = fileList[fileList.length() - 1];
item.currentLoopCount = 0;
item.maxLoops = 1;
fillIDHash(item);
if (ui->tblSequence->currentRow() == -1)
{
ui->tblSequence->setCurrentCell(0,0);
}
seqItems.append(item);
int row = ui->tblSequence->rowCount();
ui->tblSequence->insertRow(row);
ui->tblSequence->setItem(row, 0, new QTableWidgetItem(item.filename));
ui->tblSequence->setItem(row, 1, new QTableWidgetItem(QString::number(item.maxLoops)));
qDebug() << currentSeqNum;
if (currentSeqNum == -1)
{
currentSeqNum = 0;
currentSeqItem = &seqItems[0];
}
updateFrameLabel();
ui->tblSequence->setCurrentCell(0,0);
}
seqItems.append(item);
int row = ui->tblSequence->rowCount();
ui->tblSequence->insertRow(row);
ui->tblSequence->setItem(row, 0, new QTableWidgetItem(item.filename));
ui->tblSequence->setItem(row, 1, new QTableWidgetItem(QString::number(item.maxLoops)));
qDebug() << currentSeqNum;
if (currentSeqNum == -1)
{
currentSeqNum = 0;
currentSeqItem = &seqItems[0];
}
refreshIDList();
updateFrameLabel();
}
}
void FramePlaybackWindow::btnLoadLive()
@@ -416,6 +393,7 @@ void FramePlaybackWindow::btnLoadLive()
currentSeqNum = 0;
currentSeqItem = &seqItems[0];
}
refreshIDList();
updateFrameLabel();
btnStopClick();
}
+42 -46
View File
@@ -109,7 +109,8 @@ void FrameSenderWindow::updatedFrames(int numFrames)
buildFrameCache();
}
else //just got some new frames. See if they are relevant.
{
{
if (numFrames > modelFrames->count()) return;
qDebug() << "New frames in sender window";
//run through the supposedly new frames in order
for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
@@ -429,7 +430,7 @@ int FrameSenderWindow::fetchOperand(int idx, ModifierOperand op)
if (op.notOper) return ~op.databyte;
else return op.databyte;
}
else if (op.ID == -2)
else if (op.ID == -2) //fetch data from a data byte within the output frame
{
if (op.notOper) return ~sendingData.at(idx).data[op.databyte];
else return sendingData.at(idx).data[op.databyte];
@@ -472,6 +473,8 @@ void FrameSenderWindow::processModifierText(int line)
QString modString;
bool firstOp = true;
bool abort = false;
QString token;
ModifierOp thisOp;
//Example line:
//d0 = D0 + 1,d1 = id:0x200:d3 + id:0x200:d4 AND 0xF0 - Original version
@@ -492,6 +495,7 @@ void FrameSenderWindow::processModifierText(int line)
{
Modifier thisMod;
thisMod.destByte = 0;
firstOp = true;
QString leftSide = Utility::grabAlphaNumeric(mods[i]);
if (leftSide.startsWith("D") && leftSide.length() == 2)
@@ -510,58 +514,50 @@ void FrameSenderWindow::processModifierText(int line)
continue;
}
abort = false;
token = Utility::grabAlphaNumeric(mods[i]);
if (token[0] == '~')
{
thisOp.first.notOper = true;
token = token.remove(0, 1); //remove the ~ character
}
else thisOp.first.notOper = false;
parseOperandString(token.split(":"), thisOp.first);
if (mods[i].length() < 2) {
abort = true;
thisOp.operation = ADDITION;
thisOp.second.ID = 0;
thisOp.second.databyte = 0;
thisOp.second.notOper = false;
thisMod.operations.append(thisOp);
}
while (!abort)
{
QString token = Utility::grabAlphaNumeric(mods[i]);
ModifierOp thisOp;
if (firstOp)
QString operation = Utility::grabOperation(mods[i]);
if (operation == "")
{
if (token[0] == '~')
{
thisOp.first.notOper = true;
token = token.remove(0, 1); //remove the ~ character
}
else thisOp.first.notOper = false;
parseOperandString(token.split(":"), thisOp.first);
firstOp = false;
abort = true;
}
else
{
thisOp.first.ID = -1; //shadow register
}
if (mods[i].length() == 0) //if this thing had no actual operation or second operand then fake it
{
abort = true;
thisOp.operation = ADDITION;
thisOp.second.ID = 0;
thisOp.second.databyte = 0;
thisOp.second.notOper = false;
thisOp.operation = parseOperation(operation);
QString secondOp = Utility::grabAlphaNumeric(mods[i]);
if (mods[i][0] == '~')
{
thisOp.second.notOper = true;
mods[i] = mods[i].remove(0, 1); //remove the ~ character
}
else thisOp.second.notOper = false;
thisOp.second.bus = sendingData[line].bus;
thisOp.second.ID = sendingData[line].ID;
parseOperandString(secondOp.split(":"), thisOp.second);
thisMod.operations.append(thisOp);
}
else //otherwise try to grab them
{
QString operation = Utility::grabOperation(mods[i]);
if (operation == "")
{
abort = true;
}
else
{
thisOp.operation = parseOperation(operation);
QString secondOp = Utility::grabAlphaNumeric(mods[i]);
if (mods[i][0] == '~')
{
thisOp.second.notOper = true;
mods[i] = mods[i].remove(0, 1); //remove the ~ character
}
else thisOp.second.notOper = false;
thisOp.second.bus = sendingData[line].bus;
thisOp.second.ID = sendingData[line].ID;
parseOperandString(secondOp.split(":"), thisOp.second);
thisMod.operations.append(thisOp);
}
}
thisOp.first.ID = -1; //shadow register
if (mods[i].length() < 2) abort = true;
}
sendingData[line].modifiers.append(thisMod);
+440
View File
@@ -0,0 +1,440 @@
#include "fuzzingwindow.h"
#include "ui_fuzzingwindow.h"
#include "utility.h"
#include <QDebug>
#include "mainwindow.h"
FuzzingWindow::FuzzingWindow(const QVector<CANFrame> *frames, QWidget *parent) :
QDialog(parent),
ui(new Ui::FuzzingWindow)
{
ui->setupUi(this);
modelFrames = frames;
fuzzTimer = new QTimer();
connect(ui->btnStartStop, &QPushButton::clicked, this, &FuzzingWindow::toggleFuzzing);
connect(ui->btnAllFilters, &QPushButton::clicked, this, &FuzzingWindow::setAllFilters);
connect(ui->btnNoFilters, &QPushButton::clicked, this, &FuzzingWindow::clearAllFilters);
connect(fuzzTimer, &QTimer::timeout, this, &FuzzingWindow::timerTriggered);
connect(ui->spinTiming, SIGNAL(valueChanged(int)), this, SLOT(changePlaybackSpeed(int)));
connect(ui->listID, &QListWidget::itemChanged, this, &FuzzingWindow::idListChanged);
connect(ui->spinBytes, SIGNAL(valueChanged(int)), this, SLOT(changedNumDataBytes(int)));
connect(ui->bitfield, SIGNAL(gridClicked(int,int)), this, SLOT(bitfieldClicked(int,int)));
connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int)));
refreshIDList();
currentlyFuzzing = false;
for (int j = 0; j < 64; j++) bitGrid[j] = 1;
numBits = 64;
bitAccum = 0;
redrawGrid();
fuzzTimer->setInterval(ui->spinTiming->value());
ui->cbBuses->addItem(tr("0"));
ui->cbBuses->addItem(tr("1"));
ui->cbBuses->addItem(tr("Both"));
}
FuzzingWindow::~FuzzingWindow()
{
delete ui;
}
void FuzzingWindow::updatedFrames(int numFrames)
{
CANFrame thisFrame;
int id;
if (numFrames == -1) //all frames deleted. Kill the display
{
ui->listID->clear();
foundIDs.clear();
refreshIDList();
}
else if (numFrames == -2) //all new set of frames. Reset
{
ui->listID->clear();
foundIDs.clear();
refreshIDList();
}
else //just got some new frames. See if they are relevant.
{
if (numFrames > modelFrames->count()) return;
for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
{
id = modelFrames->at(i).ID;
if (!foundIDs.contains(id))
{
foundIDs.append(id);
selectedIDs.append(id);
QListWidgetItem *thisItem = new QListWidgetItem();
thisItem->setText(Utility::formatNumber(id));
thisItem->setFlags(thisItem->flags() | Qt::ItemIsUserCheckable);
thisItem->setCheckState(Qt::Checked);
ui->listID->addItem(thisItem);
}
}
}
}
void FuzzingWindow::changePlaybackSpeed(int newSpeed)
{
fuzzTimer->setInterval(newSpeed);
}
void FuzzingWindow::changedNumDataBytes(int newVal)
{
qDebug() << "new num bytes: " << newVal;
int byt;
for (int i = 0; i < 64; i++)
{
byt = i / 8;
if (byt >= newVal)
{
bitGrid[i] = 3;
}
else
{
if (bitGrid[i] == 3) bitGrid[i] = 1;
}
}
redrawGrid();
}
void FuzzingWindow::timerTriggered()
{
CANFrame thisFrame;
sendingBuffer.clear();
int buses = ui->cbBuses->currentIndex() + 1;
if (buses == 0) buses = 1;
for (int count = 0; count < ui->spinBurst->value(); count++)
{
thisFrame.ID = currentID;
for (int i = 0; i < 8; i++) thisFrame.data[i] = currentBytes[i];
if (currentID > 0x7FF) thisFrame.extended = true;
else thisFrame.extended = false;
thisFrame.bus = 0; //hard coded for now. TODO: do not hard code
thisFrame.len = ui->spinBytes->value();
if (buses & 1)
{
thisFrame.bus = 0;
sendingBuffer.append(thisFrame);
}
if (buses & 2)
{
thisFrame.bus = 1;
sendingBuffer.append(thisFrame);
}
calcNextID();
calcNextBitPattern();
numSentFrames++;
}
emit sendFrameBatch(&sendingBuffer);
ui->lblNumFrames->setText("# of sent frames: " + QString::number(numSentFrames));
}
void FuzzingWindow::clearAllFilters()
{
for (int i = 0; i < ui->listID->count(); i++)
{
ui->listID->item(i)->setCheckState(Qt::Unchecked);
}
}
void FuzzingWindow::setAllFilters()
{
for (int i = 0; i < ui->listID->count(); i++)
{
ui->listID->item(i)->setCheckState(Qt::Checked);
}
}
void FuzzingWindow::calcNextID()
{
if (seqIDScan)
{
if (rangeIDSelect)
{
currentID++;
if (currentID > endID) currentID = startID;
}
else //IDs by filter. So, select the first filter
{
currentIdx++;
if (currentIdx >= selectedIDs.length()) currentIdx = 0;
currentID = selectedIDs[currentIdx];
qDebug() << "idx id: " << currentID;
}
}
else //random IDs
{
if (rangeIDSelect)
{
int range = endID - startID;
if (range != 0) currentID = startID + qrand() % range;
else currentID = startID;
}
else //IDs by filter so pick a random selected ID from the filter list
{
currentIdx = qrand() % selectedIDs.length();
currentID = selectedIDs[currentIdx];
}
}
}
void FuzzingWindow::calcNextBitPattern()
{
uint64_t accum;
switch (bitSequenceType)
{
case BitSequenceType::Random:
int thisBit;
for (int byt = 0; byt < ui->spinBytes->value(); byt++)
{
currentBytes[byt] = 0;
for (int bit = 0; bit < 8; bit++)
{
thisBit = bitGrid[byt * 8 + bit];
if (thisBit == 1)
{
if ((qrand() % 2) == 1) currentBytes[byt] |= (1 << bit);
}
if (thisBit == 2) currentBytes[byt] |= (1 << bit);
}
}
break;
case BitSequenceType::Sequential:
bitAccum++;
bitAccum &= ((1 << numBits) - 1);
accum = bitAccum;
for (int byt = 0; byt < ui->spinBytes->value(); byt++)
{
currentBytes[byt] = 0;
for (int bit = 0; bit < 8; bit++)
{
thisBit = bitGrid[byt * 8 + bit];
if (thisBit == 1)
{
if (accum & 1) currentBytes[byt] |= (1 << bit);
accum >>= 1;
}
if (thisBit == 2) currentBytes[byt] |= (1 << bit);
}
}
break;
case BitSequenceType::Sweeping:
qDebug() << "Start " << bitAccum;
accum = bitAccum;
int offset;
for (int i = 1; i < 64; i++)
{
offset = (i + bitAccum) % 64;
if (bitGrid[offset] == 1)
{
bitAccum = offset;
qDebug() << "End " << bitAccum;
break;
}
}
for (int byt = 0; byt < ui->spinBytes->value(); byt++)
{
currentBytes[byt] = 0;
for (int bit = 0; bit < 8; bit++)
{
thisBit = bitGrid[byt * 8 + bit];
if (thisBit == 1 && (byt * 8 + bit) == bitAccum)
{
currentBytes[byt] |= (1 << bit);
}
if (thisBit == 2) currentBytes[byt] |= (1 << bit);
}
}
break;
}
}
void FuzzingWindow::toggleFuzzing()
{
if (currentlyFuzzing) //stop it then
{
ui->btnStartStop->setText("Start Fuzzing");
currentlyFuzzing = false;
fuzzTimer->stop();
}
else //start it then
{
ui->btnStartStop->setText("Stop Fuzzing");
currentlyFuzzing = true;
startID = Utility::ParseStringToNum(ui->txtStartID->text());
endID = Utility::ParseStringToNum(ui->txtEndID->text());
seqIDScan = ui->rbSequentialID->isChecked();
rangeIDSelect = ui->rbRangeIDSel->isChecked();
if (ui->rbSequentialBits->isChecked()) bitSequenceType = BitSequenceType::Sequential;
if (ui->rbRandomBits->isChecked())
{
bitSequenceType = BitSequenceType::Random;
bitAccum = 0;
}
if (ui->rbSweep->isChecked())
{
bitSequenceType = BitSequenceType::Sweeping;
for (int i = 0; i < 64; i++)
{
if (bitGrid[i] == 1)
{
bitAccum = i;
break;
}
}
}
numSentFrames = 0;
if (seqIDScan)
{
if (rangeIDSelect)
{
currentID = startID;
}
else //IDs by filter. So, select the first filter
{
currentIdx = 0;
currentID = selectedIDs[currentIdx];
}
}
else //random IDs
{
if (rangeIDSelect)
{
int range = endID - startID;
if (range != 0) currentID = startID + qrand() % range;
else currentID = startID;
}
else //IDs by filter so pick a random selected ID from the filter list
{
currentIdx = qrand() % selectedIDs.length();
currentID = selectedIDs[currentIdx];
}
}
calcNextBitPattern();
fuzzTimer->start();
}
}
void FuzzingWindow::refreshIDList()
{
ui->listID->clear();
foundIDs.clear();
int id;
for (int i = 0; i < modelFrames->count(); i++)
{
id = modelFrames->at(i).ID;
if (!foundIDs.contains(id))
{
foundIDs.append(id);
selectedIDs.append(id);
QListWidgetItem *thisItem = new QListWidgetItem();
thisItem->setText(Utility::formatNumber(id));
thisItem->setFlags(thisItem->flags() | Qt::ItemIsUserCheckable);
thisItem->setCheckState(Qt::Checked);
ui->listID->addItem(thisItem);
}
}
//default is to sort in ascending order
ui->listID->sortItems();
}
void FuzzingWindow::idListChanged(QListWidgetItem *item)
{
int id = Utility::ParseStringToNum(item->text());
if (item->checkState() == Qt::Checked)
{
if (!selectedIDs.contains(id))
{
qDebug() << "adding " << id << " to list of selected IDs";
selectedIDs.append(id);
}
}
else
{
qDebug() << "removing " << id << " from the list of selected ids";
selectedIDs.removeOne(id);
}
}
/*
bitGrid stores the state of all 64 bits.
The grid is capable of showing the following colors:
White = not used (left as 0)
Gray = past the end of the valid bits (because of # of data bytes requested)
Green = fuzz it
black = always keep it set to 1
*/
void FuzzingWindow::bitfieldClicked(int x, int y)
{
qDebug() << "X: " << x << " Y: " << y;
int bit = (7 - x) + (y * 8);
if (bitGrid[bit] == 3) return; //naughty!
bitGrid[bit]++;
if (bitGrid[bit] > 2) bitGrid[bit] = 0;
redrawGrid();
}
void FuzzingWindow::redrawGrid()
{
//now update the bits in the bitfield control
uint8_t refBytes[8];
uint8_t dataBytes[8];
uint8_t usedBytes[8];
for (int j = 0; j < 8; j++)
{
refBytes[j] = 0;
dataBytes[j] = 0;
usedBytes[j] = 0;
}
numBits = 0;
for (int i = 0; i < 64; i++)
{
int byt = i / 8;
int bit = i % 8;
switch (bitGrid[i])
{
case 0: //white, keep this bit off always
break;
case 1: //Green, fuzz this bit
dataBytes[byt] |= (1 << bit);
numBits++;
break;
case 2: //black, bit always set
dataBytes[byt] |= (1 << bit);
refBytes[byt] |= (1 << bit);
break;
case 3: //gray, this bit doesn't exist
usedBytes[byt] |= (1 << bit);
break;
}
}
ui->bitfield->setUsed(usedBytes, false);
ui->bitfield->setReference(refBytes, false);
ui->bitfield->updateData(dataBytes, true);
}
+69
View File
@@ -0,0 +1,69 @@
#ifndef FUZZINGWINDOW_H
#define FUZZINGWINDOW_H
#include <QDialog>
#include <QListWidget>
#include <QTimer>
#include "can_structs.h"
namespace Ui {
class FuzzingWindow;
}
namespace BitSequenceType
{
enum
{
Sequential,
Sweeping,
Random
};
}
class FuzzingWindow : public QDialog
{
Q_OBJECT
public:
explicit FuzzingWindow(const QVector<CANFrame> *frames, QWidget *parent = 0);
~FuzzingWindow();
signals:
void sendCANFrame(const CANFrame *, int);
void sendFrameBatch(const QList<CANFrame> *);
private slots:
void changePlaybackSpeed(int newSpeed);
void timerTriggered();
void clearAllFilters();
void setAllFilters();
void toggleFuzzing();
void idListChanged(QListWidgetItem *item);
void bitfieldClicked(int, int);
void changedNumDataBytes(int newVal);
void updatedFrames(int numFrames);
private:
Ui::FuzzingWindow *ui;
const QVector<CANFrame> *modelFrames;
QTimer *fuzzTimer;
QList<int> foundIDs;
QList<int> selectedIDs;
QList<CANFrame> sendingBuffer;
int startID, endID, currentID, currentIdx;
bool seqIDScan, rangeIDSelect;
int bitSequenceType;
bool currentlyFuzzing;
uint8_t currentBytes[8];
uint8_t bitGrid[64];
uint8_t numBits;
uint64_t bitAccum;
int numSentFrames;
void refreshIDList();
void calcNextID();
void calcNextBitPattern();
void redrawGrid();
};
#endif // FUZZINGWINDOW_H
+306
View File
@@ -0,0 +1,306 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FuzzingWindow</class>
<widget class="QDialog" name="FuzzingWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>760</width>
<height>671</height>
</rect>
</property>
<property name="windowTitle">
<string>Fuzzing Window</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,1,3,6,0,0,0">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Interframe Timing (ms)</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinTiming">
<property name="maximum">
<number>10000</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Number of data bytes</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBytes">
<property name="maximum">
<number>8</number>
</property>
<property name="value">
<number>8</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Burst Rate</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBurst">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>10</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Bus or Buses to Send On</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbBuses"/>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>ID Scanning</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QRadioButton" name="rbSequentialID">
<property name="text">
<string>Seqential</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbRandomID">
<property name="text">
<string>Random</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>ID Selection</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QRadioButton" name="rbRangeIDSel">
<property name="text">
<string>Range of IDs</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbFilterIDSel">
<property name="text">
<string>Filter List</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Bit Scanning</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QRadioButton" name="rbSequentialBits">
<property name="text">
<string>Sequential</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbSweep">
<property name="text">
<string>Sweep</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbRandomBits">
<property name="text">
<string>Random</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>IDs</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Start ID:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txtStartID"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>End ID:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txtEndID"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QListWidget" name="listID"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QPushButton" name="btnAllFilters">
<property name="text">
<string>All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnNoFilters">
<property name="text">
<string>None</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="CANDataGrid" name="bitfield" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>200</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>White = Bit always 0, Green = Fuzz Bit, Black = Bit always set, Gray = Past end of data bytes</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnStartStop">
<property name="text">
<string>Start Fuzzing</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblNumFrames">
<property name="font">
<font>
<pointsize>11</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string># of sent frames:</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>CANDataGrid</class>
<extends>QWidget</extends>
<header>candatagrid.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
+329 -215
View File
@@ -142,6 +142,7 @@ void GraphingWindow::updatedFrames(int numFrames)
}
else //just got some new frames. See if they are relevant.
{
if (numFrames > modelFrames->count()) return;
for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
{
thisFrame = modelFrames->at(i);
@@ -686,21 +687,31 @@ void GraphingWindow::saveDefinitions()
{
outFile->write(QString::number(iter->ID, 16).toUtf8());
outFile->putChar(',');
outFile->write(QString::number(iter->mask, 16).toUtf8());
outFile->putChar(',');
outFile->write(QString::number(iter->startByte).toUtf8());
outFile->putChar(',');
outFile->write(QString::number(iter->endByte).toUtf8());
outFile->putChar(',');
if (iter->isSigned) outFile->putChar('Y');
else outFile->putChar('N');
outFile->putChar(',');
outFile->write(QString::number(iter->bias).toUtf8());
outFile->putChar(',');
outFile->write(QString::number(iter->scale).toUtf8());
outFile->putChar(',');
outFile->write(QString::number(iter->stride).toUtf8());
outFile->putChar(',');
if (iter->isDBCSignal)
{
outFile->putChar('S');
outFile->putChar(',');
outFile->write(iter->signal.toUtf8());
outFile->putChar(',');
}
else
{
outFile->write(QString::number(iter->mask, 16).toUtf8());
outFile->putChar(',');
outFile->write(QString::number(iter->startByte).toUtf8());
outFile->putChar(',');
outFile->write(QString::number(iter->endByte).toUtf8());
outFile->putChar(',');
if (iter->isSigned) outFile->putChar('Y');
else outFile->putChar('N');
outFile->putChar(',');
outFile->write(QString::number(iter->bias).toUtf8());
outFile->putChar(',');
outFile->write(QString::number(iter->scale).toUtf8());
outFile->putChar(',');
outFile->write(QString::number(iter->stride).toUtf8());
outFile->putChar(',');
}
outFile->write(QString::number(iter->color.red()).toUtf8());
outFile->putChar(',');
outFile->write(QString::number(iter->color.green()).toUtf8());
@@ -718,10 +729,14 @@ void GraphingWindow::loadDefinitions()
{
QString filename;
QFileDialog dialog;
bool dbcMissing = false;
QStringList filters;
filters.append(QString(tr("Graph definition (*.gdf)")));
if (dbcHandler == NULL) return;
if (dbcHandler->getFileCount() == 0) dbcHandler->createBlankFile();
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
@@ -741,28 +756,65 @@ void GraphingWindow::loadDefinitions()
{
GraphParams gp;
QList<QByteArray> tokens = line.split(',');
gp.isDBCSignal = false;
gp.ID = tokens[0].toInt(NULL, 16);
gp.mask = tokens[1].toULongLong(NULL, 16);
qDebug() << gp.mask;
gp.startByte = tokens[2].toInt();
gp.endByte = tokens[3].toInt();
if (tokens[4] == "Y") gp.isSigned = true;
else gp.isSigned = false;
gp.bias = tokens[5].toFloat();
gp.scale = tokens[6].toFloat();
gp.stride = tokens[7].toInt();
gp.color.setRed(tokens[8].toInt());
gp.color.setGreen(tokens[9].toInt());
gp.color.setBlue(tokens[10].toInt());
if (tokens.length() > 11)
gp.graphName = tokens[11];
if (tokens[1] == "S")
{
gp.isDBCSignal = true;
//tokens[2] is the signal name. Need to use the message ID and this name to look it up
DBC_MESSAGE *msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByID(gp.ID);
if (msg != NULL)
{
DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(tokens[2]);
if (sig == NULL) dbcMissing = true;
gp.signal = tokens[2];
}
else
{
gp.signal = "";
dbcMissing = true;
}
}
else
gp.graphName = QString();
{
gp.mask = tokens[1].toULongLong(NULL, 16);
qDebug() << gp.mask;
gp.startByte = tokens[2].toInt();
gp.endByte = tokens[3].toInt();
if (tokens[4] == "Y") gp.isSigned = true;
else gp.isSigned = false;
gp.bias = tokens[5].toFloat();
gp.scale = tokens[6].toFloat();
gp.stride = tokens[7].toInt();
}
if (!gp.isDBCSignal)
{
gp.color.setRed(tokens[8].toInt());
gp.color.setGreen(tokens[9].toInt());
gp.color.setBlue(tokens[10].toInt());
if (tokens.length() > 11)
gp.graphName = tokens[11];
else
gp.graphName = QString();
}
else
{
gp.color.setRed(tokens[3].toInt());
gp.color.setGreen(tokens[4].toInt());
gp.color.setBlue(tokens[5].toInt());
gp.graphName = tokens[6];
}
createGraph(gp, true);
}
}
inFile->close();
if (dbcMissing)
{
QMessageBox msg;
msg.setText("One or more graphs could not be loaded\r\nbecause the signal could not be found.\r\nPerhaps you forgot to load\r\nthe DBC file?");
msg.exec();
}
}
}
@@ -799,178 +851,61 @@ void GraphingWindow::addNewGraph()
void GraphingWindow::appendToGraph(GraphParams &params, CANFrame &frame)
{
int64_t tempVal; //64 bit temp value.
if (params.endByte == -1 || params.startByte == params.endByte)
if (params.isDBCSignal)
{
tempVal = (frame.data[params.startByte] & params.mask);
if (params.isSigned && tempVal > 127)
double tempValue;
DBC_MESSAGE *msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByID(params.ID);
DBC_SIGNAL *sig = NULL;
if (msg) sig = msg->sigHandler->findSignalByName(params.signal);
if (sig == NULL) return;
//if the given signal was found and successfully processed in this frame then add it to the graph
if (sig->processAsDouble(frame, tempValue))
{
tempVal = tempVal - 256;
}
if (secondsMode)
{
params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias);
}
else
{
params.x.append(frame.timestamp - params.xbias);
}
params.y.append((tempVal * params.scale) + params.bias);
//qDebug() << "tempValue: " << tempValue;
if (secondsMode)
{
params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias);
}
else
{
params.x.append(frame.timestamp - params.xbias);
}
params.y.append(tempValue);
}
}
else if (params.endByte > params.startByte) //big endian
else
{
float tempValue;
int64_t tempValInt;
int numBytes = (params.endByte - params.startByte) + 1;
int64_t shiftRef = 1 << (numBytes * 8);
uint64_t maskShifter;
uint8_t tempByte;
tempValInt = 0;
int64_t expon = 1;
maskShifter = params.mask;
for (int c = 0; c < numBytes; c++)
if (params.endByte == -1 || params.startByte == params.endByte)
{
tempByte = frame.data[params.endByte - c];
tempByte &= maskShifter;
tempValInt += (tempByte * expon);
expon *= 256;
maskShifter = maskShifter >> 8;
}
tempValInt &= params.mask;
int64_t twocompPoint = params.mask;
if (shiftRef < twocompPoint || twocompPoint == -1) twocompPoint = shiftRef;
//qDebug() << "two comp point: " << twocompPoint;
if (params.isSigned && tempValInt > ((twocompPoint / 2)))
{
tempValInt = tempValInt - twocompPoint;
}
tempValue = (float)tempValInt;
if (secondsMode)
{
params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias);
}
else
{
params.x.append(frame.timestamp - params.xbias);
}
params.y.append((tempValue * params.scale) + params.bias);
}
else //little endian
{
float tempValue;
int64_t tempValInt;
int numBytes = (params.startByte - params.endByte) + 1;
int64_t shiftRef = 1 << (numBytes * 8);
uint64_t maskShifter;
uint8_t tempByte;
tempValInt = 0;
int64_t expon = 1;
maskShifter = params.mask;
for (int c = 0; c < numBytes; c++)
{
tempByte = frame.data[params.endByte + c];
tempByte &= maskShifter;
tempValInt += tempByte * expon;
expon *= 256;
maskShifter = maskShifter >> 8;
}
tempValInt &= params.mask;
int64_t twocompPoint = params.mask;
if (shiftRef < twocompPoint || twocompPoint == -1) twocompPoint = shiftRef;
//qDebug() << "two comp point: " << twocompPoint;
if (params.isSigned && tempValInt > ((twocompPoint / 2)))
{
tempValInt = tempValInt - twocompPoint;
}
tempValue = (float)tempValInt;
if (secondsMode)
{
params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias);
}
else
{
params.x.append(frame.timestamp - params.xbias);
}
params.y.append((tempValue * params.scale) + params.bias);
}
params.ref->setData(params.x,params.y);
}
void GraphingWindow::createGraph(GraphParams &params, bool createGraphParam)
{
int64_t tempVal; //64 bit temp value.
float yminval=10000000.0, ymaxval = -1000000.0;
float xminval=10000000000.0, xmaxval = -10000000000.0;
GraphParams *refParam = &params;
qDebug() << "New Graph ID: " << params.ID;
qDebug() << "Start byte: " << params.startByte;
qDebug() << "End Byte: " << params.endByte;
qDebug() << "Mask: " << params.mask;
frameCache.clear();
for (int i = 0; i < modelFrames->count(); i++)
{
CANFrame thisFrame = modelFrames->at(i);
if (thisFrame.ID == params.ID) frameCache.append(thisFrame);
}
int numEntries = frameCache.count() / params.stride;
params.x.reserve(numEntries);
params.y.reserve(numEntries);
params.x.fill(0, numEntries);
params.y.fill(0, numEntries);
if (params.endByte == -1 || params.startByte == params.endByte)
{
for (int j = 0; j < numEntries; j++)
{
tempVal = (frameCache[j * params.stride].data[params.startByte] & params.mask);
tempVal = (frame.data[params.startByte] & params.mask);
if (params.isSigned && tempVal > 127)
{
tempVal = tempVal - 256;
}
if (secondsMode)
{
params.x[j] = (double)(frameCache[j].timestamp) / 1000000.0;
params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias);
}
else
{
params.x[j] = frameCache[j].timestamp;
params.x.append(frame.timestamp - params.xbias);
}
params.y[j] = (tempVal * params.scale) + params.bias;
if (params.y[j] < yminval) yminval = params.y[j];
if (params.y[j] > ymaxval) ymaxval = params.y[j];
if (params.x[j] < xminval) xminval = params.x[j];
if (params.x[j] > xmaxval) xmaxval = params.x[j];
params.y.append((tempVal * params.scale) + params.bias);
}
}
else if (params.endByte > params.startByte) //big endian
{
float tempValue;
int64_t tempValInt;
int numBytes = (params.endByte - params.startByte) + 1;
int64_t shiftRef = (uint64_t)1 << (numBytes * 8);
uint64_t maskShifter;
uint8_t tempByte;
for (int j = 0; j < numEntries; j++)
else if (params.endByte > params.startByte) //big endian
{
float tempValue;
int64_t tempValInt;
int numBytes = (params.endByte - params.startByte) + 1;
int64_t shiftRef = 1 << (numBytes * 8);
uint64_t maskShifter;
uint8_t tempByte;
tempValInt = 0;
int64_t expon = 1;
maskShifter = params.mask;
for (int c = 0; c < numBytes; c++)
{
tempByte = frameCache[j * params.stride].data[params.endByte - c];
tempByte = frame.data[params.endByte - c];
tempByte &= maskShifter;
tempValInt += (tempByte * expon);
expon *= 256;
@@ -991,36 +926,29 @@ void GraphingWindow::createGraph(GraphParams &params, bool createGraphParam)
if (secondsMode)
{
params.x[j] = (double)(frameCache[j].timestamp) / 1000000.0;
params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias);
}
else
{
params.x[j] = frameCache[j].timestamp;
params.x.append(frame.timestamp - params.xbias);
}
params.y[j] = (tempValue * params.scale) + params.bias;
if (params.y[j] < yminval) yminval = params.y[j];
if (params.y[j] > ymaxval) ymaxval = params.y[j];
if (params.x[j] < xminval) xminval = params.x[j];
if (params.x[j] > xmaxval) xmaxval = params.x[j];
params.y.append((tempValue * params.scale) + params.bias);
}
}
else //little endian
{
float tempValue;
int64_t tempValInt;
int numBytes = (params.startByte - params.endByte) + 1;
int64_t shiftRef = (uint64_t)1 << (numBytes * 8);
uint64_t maskShifter;
uint8_t tempByte;
for (int j = 0; j < numEntries; j++)
else //little endian
{
float tempValue;
int64_t tempValInt;
int numBytes = (params.startByte - params.endByte) + 1;
int64_t shiftRef = 1 << (numBytes * 8);
uint64_t maskShifter;
uint8_t tempByte;
tempValInt = 0;
int64_t expon = 1;
maskShifter = params.mask;
for (int c = 0; c < numBytes; c++)
{
tempByte = frameCache[j * params.stride].data[params.endByte + c];
tempByte = frame.data[params.endByte + c];
tempByte &= maskShifter;
tempValInt += tempByte * expon;
expon *= 256;
@@ -1040,33 +968,219 @@ void GraphingWindow::createGraph(GraphParams &params, bool createGraphParam)
if (secondsMode)
{
params.x[j] = (double)(frameCache[j].timestamp) / 1000000.0;
params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias);
}
else
{
params.x[j] = frameCache[j].timestamp;
params.x.append(frame.timestamp - params.xbias);
}
params.y[j] = (tempValue * params.scale) + params.bias;
if (params.y[j] < yminval) yminval = params.y[j];
if (params.y[j] > ymaxval) ymaxval = params.y[j];
if (params.x[j] < xminval) xminval = params.x[j];
if (params.x[j] > xmaxval) xmaxval = params.x[j];
params.y.append((tempValue * params.scale) + params.bias);
}
}
params.ref->setData(params.x,params.y);
}
void GraphingWindow::createGraph(GraphParams &params, bool createGraphParam)
{
int64_t tempVal; //64 bit temp value.
float yminval=10000000.0, ymaxval = -1000000.0;
float xminval=10000000000.0, xmaxval = -10000000000.0;
GraphParams *refParam = &params;
DBC_MESSAGE *msg = NULL;
DBC_SIGNAL *sig = NULL;
if (params.isDBCSignal)
{
msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByID(params.ID);
if (msg) sig = msg->sigHandler->findSignalByName(params.signal);
if (sig == NULL) return;
qDebug() << "New signal graph: " << params.signal <<" in ID:" << params.ID;
}
else
{
qDebug() << "New Graph ID: " << params.ID;
qDebug() << "Start byte: " << params.startByte;
qDebug() << "End Byte: " << params.endByte;
qDebug() << "Mask: " << params.mask;
}
frameCache.clear();
for (int i = 0; i < modelFrames->count(); i++)
{
CANFrame thisFrame = modelFrames->at(i);
if (thisFrame.ID == params.ID) frameCache.append(thisFrame);
}
int numEntries = frameCache.count() / params.stride;
params.x.reserve(numEntries);
params.y.reserve(numEntries);
params.x.fill(0, numEntries);
params.y.fill(0, numEntries);
if (params.isDBCSignal)
{
double tempValue;
int l = 0;
for (int j = 0; j < numEntries; j++)
{
//if the given signal was found and successfully processed in this frame then add it to the graph
if (sig->processAsDouble(frameCache[j], tempValue))
{
//qDebug() << "tempValue: " << tempValue;
if (secondsMode)
{
params.x[l] = (double)(frameCache[j].timestamp) / 1000000.0;
}
else
{
params.x[l] = frameCache[j].timestamp;
}
params.y[l] = tempValue;
if (params.y[l] < yminval) yminval = params.y[l];
if (params.y[l] > ymaxval) ymaxval = params.y[l];
if (params.x[l] < xminval) xminval = params.x[l];
if (params.x[l] > xmaxval) xmaxval = params.x[l];
l++;
}
}
params.x.resize(l);
params.y.resize(l);
params.x.squeeze();
params.y.squeeze();
}
else
{
if (params.endByte == -1 || params.startByte == params.endByte)
{
for (int j = 0; j < numEntries; j++)
{
tempVal = (frameCache[j * params.stride].data[params.startByte] & params.mask);
if (params.isSigned && tempVal > 127)
{
tempVal = tempVal - 256;
}
if (secondsMode)
{
params.x[j] = (double)(frameCache[j].timestamp) / 1000000.0;
}
else
{
params.x[j] = frameCache[j].timestamp;
}
params.y[j] = (tempVal * params.scale) + params.bias;
if (params.y[j] < yminval) yminval = params.y[j];
if (params.y[j] > ymaxval) ymaxval = params.y[j];
if (params.x[j] < xminval) xminval = params.x[j];
if (params.x[j] > xmaxval) xmaxval = params.x[j];
}
}
else if (params.endByte > params.startByte) //big endian
{
float tempValue;
int64_t tempValInt;
int numBytes = (params.endByte - params.startByte) + 1;
int64_t shiftRef = (uint64_t)1 << (numBytes * 8);
uint64_t maskShifter;
uint8_t tempByte;
for (int j = 0; j < numEntries; j++)
{
tempValInt = 0;
int64_t expon = 1;
maskShifter = params.mask;
for (int c = 0; c < numBytes; c++)
{
tempByte = frameCache[j * params.stride].data[params.endByte - c];
tempByte &= maskShifter;
tempValInt += (tempByte * expon);
expon *= 256;
maskShifter = maskShifter >> 8;
}
tempValInt &= params.mask;
int64_t twocompPoint = params.mask;
if (shiftRef < twocompPoint || twocompPoint == -1) twocompPoint = shiftRef;
//qDebug() << "two comp point: " << twocompPoint;
if (params.isSigned && tempValInt > ((twocompPoint / 2)))
{
tempValInt = tempValInt - twocompPoint;
}
tempValue = (float)tempValInt;
if (secondsMode)
{
params.x[j] = (double)(frameCache[j].timestamp) / 1000000.0;
}
else
{
params.x[j] = frameCache[j].timestamp;
}
params.y[j] = (tempValue * params.scale) + params.bias;
if (params.y[j] < yminval) yminval = params.y[j];
if (params.y[j] > ymaxval) ymaxval = params.y[j];
if (params.x[j] < xminval) xminval = params.x[j];
if (params.x[j] > xmaxval) xmaxval = params.x[j];
}
}
else //little endian
{
float tempValue;
int64_t tempValInt;
int numBytes = (params.startByte - params.endByte) + 1;
int64_t shiftRef = (uint64_t)1 << (numBytes * 8);
uint64_t maskShifter;
uint8_t tempByte;
for (int j = 0; j < numEntries; j++)
{
tempValInt = 0;
int64_t expon = 1;
maskShifter = params.mask;
for (int c = 0; c < numBytes; c++)
{
tempByte = frameCache[j * params.stride].data[params.endByte + c];
tempByte &= maskShifter;
tempValInt += tempByte * expon;
expon *= 256;
maskShifter = maskShifter >> 8;
}
tempValInt &= params.mask;
int64_t twocompPoint = params.mask;
if (shiftRef < twocompPoint || twocompPoint == -1) twocompPoint = shiftRef;
//qDebug() << "two comp point: " << twocompPoint;
if (params.isSigned && tempValInt > ((twocompPoint / 2)))
{
tempValInt = tempValInt - twocompPoint;
}
tempValue = (float)tempValInt;
if (secondsMode)
{
params.x[j] = (double)(frameCache[j].timestamp) / 1000000.0;
}
else
{
params.x[j] = frameCache[j].timestamp;
}
params.y[j] = (tempValue * params.scale) + params.bias;
if (params.y[j] < yminval) yminval = params.y[j];
if (params.y[j] > ymaxval) ymaxval = params.y[j];
if (params.x[j] < xminval) xminval = params.x[j];
if (params.x[j] > xmaxval) xmaxval = params.x[j];
}
}
}
params.xbias = 0;
/*
for (int ct = 0; ct < params.x.count(); ct++)
{
params.x[ct] -= xminval;
}
params.xbias = xminval;
xmaxval -= xminval;
xminval = 0;
*/
ui->graphingView->addGraph();
params.ref = ui->graphingView->graph();
if (createGraphParam)
+3 -1
View File
@@ -24,7 +24,9 @@ public:
QColor color;
QCPGraph *ref;
QString graphName;
//the below stuff is used for internal purposes only
bool isDBCSignal; //is this graph actually a DBC signal? If so the below pointer should be filled out
QString signal;
//the below stuff is used for internal purposes only - code should be refactored so these can be private
QVector<double> x, y;
double xbias;
};
+191
View File
@@ -0,0 +1,191 @@
#include "isotp_decoder.h"
//in order descriptions based on the list above. But, just in order one after the other
QString UDS_SERVICE_DESCRIPT[] =
{
"Diagnostic session control",
"Reset ECU",
"Clear diagnostic trouble codes",
"Read diagnostic trouble codes",
"Read data by ID",
"Read data by address",
"Read scaling data by ID",
"Request security access",
"Communication control",
"Read data by ID periodically",
"Create dynamic data ID",
"Write data by ID",
"Input/Output control (force)",
"Call a service routine",
"Request data download (from client to server)",
"Request data upload (from server to client)",
"Transfer data",
"Request that data transfer cease",
"Request file transfer",
"Write data by address",
"Tester is present",
"Read or write comm timing parameters",
"Secured data transmission",
"Control DTC settings",
"Request start/stop transmission on event",
"Control comm link"
};
ISOTP_DECODER::ISOTP_DECODER(const QVector<CANFrame> *frames, QObject *parent)
: QObject(parent)
{
modelFrames = frames;
useExtendedAddressing = false;
}
void ISOTP_DECODER::setExtendedAddressing(bool mode)
{
useExtendedAddressing = mode;
}
//remember, negative numbers are special -1 = all frames deleted, -2 = totally new set of frames.
void ISOTP_DECODER::updatedFrames(int numFrames)
{
if (numFrames == -1) //all frames deleted. Kill the display
{
}
else if (numFrames == -2) //all new set of frames. Reset
{
for (int i = 0; i < modelFrames->length(); i++) processFrame(modelFrames->at(i));
}
else //just got some new frames. See if they are relevant.
{
for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
{
processFrame(modelFrames->at(i));
}
}
}
void ISOTP_DECODER::processFrame(const CANFrame &frame)
{
uint64_t ID = frame.ID;
int frameType;
int frameLen;
int ln;
int offset;
ISOTP_MESSAGE msg;
ISOTP_MESSAGE *pMsg;
frameType = 0;
frameLen = 0;
if (useExtendedAddressing)
{
ID = ID << 8;
ID += frame.data[0];
frameType = frame.data[1] >> 4;
frameLen = frame.data[1] & 0xF;
}
else
{
frameType = frame.data[0] >> 4;
frameLen = frame.data[0] & 0xF;
}
switch(frameType)
{
case 0: //single frame message
checkNeedFlush(ID);
if (frameLen == 0) return; //length of zero isn't valid.
if (frameLen > 6 && useExtendedAddressing) return; //impossible
if (frameLen > 7) return;
msg.bus = frame.bus;
msg.extended = frame.extended;
msg.ID = ID;
msg.isReceived = frame.isReceived;
msg.len = frameLen;
msg.data.reserve(frameLen);
msg.timestamp = frame.timestamp;
if (useExtendedAddressing) for (int j = 0; j < frameLen; j++) msg.data.append(frame.data[j+2]);
else for (int j = 0; j < frameLen; j++) msg.data.append(frame.data[j+1]);
qDebug() << "Emitting single frame ISOTP message";
emit newISOMessage(msg);
break;
case 1: //first frame of a multi-frame message
checkNeedFlush(ID);
msg.bus = frame.bus;
msg.extended = frame.extended;
msg.ID = ID;
msg.timestamp = frame.timestamp;
msg.isReceived = frame.isReceived;
frameLen = frameLen << 8;
if (useExtendedAddressing)
{
frameLen += frame.data[2];
frameLen = frameLen & 0xFFF;
msg.len = frameLen;
msg.data.reserve(frameLen);
for (int j = 0; j < 5; j++) msg.data.append(frame.data[3 + j]);
}
else
{
frameLen += frame.data[1];
frameLen = frameLen & 0xFFF;
msg.len = frameLen;
msg.data.reserve(frameLen);
for (int j = 0; j < 6; j++) msg.data.append(frame.data[2 + j]);
}
messageBuffer.append(msg);
break;
case 2: //subsequent frames for multi-frame messages
pMsg = NULL;
for (int i = 0; i < messageBuffer.length(); i++)
{
if (messageBuffer[i].ID == ID)
{
pMsg = &messageBuffer[i];
break;
}
}
if (!pMsg) return;
ln = pMsg->len - pMsg->data.count();
offset = pMsg->data.count();
if (useExtendedAddressing)
{
if (ln > 6) ln = 6;
for (int j = 0; j < ln; j++) pMsg->data.append(frame.data[j+2]);
}
else
{
if (ln > 7) ln = 7;
for (int j = 0; j < ln; j++) pMsg->data.append(frame.data[j+1]);
}
if (pMsg->len <= pMsg->data.count())
{
qDebug() << "Emitting multiframe ISOTP message";
emit newISOMessage(*pMsg);
}
break;
case 3: //flow control messages -ignored for now
break;
}
}
void ISOTP_DECODER::checkNeedFlush(uint64_t ID)
{
for (int i = 0; i < messageBuffer.length(); i++)
{
if (messageBuffer[i].ID == ID)
{
//warning... this code will work for direct signals as emit turns into a function call
//and thus the other side will have time to do its processing before control returns
//and we delete the message on our side. But, if this code were used cross thread the
//emit would be a queued message instead and control would immediately return
//here and then the message would be deleted before being delivered.
//To fix that the message would have to be passed by value instead which I'd like to avoid.
//Thus, don't use this across threads unless you like to debug strange issues.
qDebug() << "Flushing a partial frame";
emit newISOMessage(messageBuffer[i]);
messageBuffer.removeAt(i);
return;
}
}
}
+75
View File
@@ -0,0 +1,75 @@
#ifndef ISOTP_DECODER_H
#define ISOTP_DECODER_H
#include <QObject>
#include <QDebug>
#include "can_structs.h"
enum OBDII_FUNCTS
{
UDS_OBDII_SHOW_CURRENT = 1,
UDS_OBDII_SHOW_FREEZE = 2,
UDS_OBDII_SHOW_STORED_DTC = 3,
UDS_OBDII_CLEAR_DTC = 4,
UDS_OBDII_TEST_O2 = 5,
UDS_OBDII_TEST_RESULTS = 6,
UDS_OBDII_SHOW_PENDING_DTC = 7,
UDS_OBDII_CONTROL_DEVICES = 8,
UDS_OBDII_VEH_INFO = 9,
UDS_OBDII_PERM_DTC = 0xA
};
enum UDS_FUNCTS
{
UDS_DIAG_CONTROL=0x10,
UDS_ECU_RESET=0x11,
UDS_CLEAR_DIAG=0x14,
UDS_READ_DTC=0x19,
UDS_READ_BY_ID=0x22,
UDS_READ_BY_ADDR=0x23,
UDS_READ_SCALING_ID=0x24,
UDS_SECURITY_ACCESS=0x27,
UDS_COMM_CTRL=0x28,
UDS_READ_DATA_ID_PERIODIC=0x2A,
UDS_DYNAMIC_DATA_DEFINE=0x2C,
UDS_WRITE_BY_ID=0x2E,
UDS_IO_CTRL=0x2F,
UDS_ROUTINE_CTRL=0x31,
UDS_REQUEST_DOWNLOAD=0x34,
UDS_REQUEST_UPLOAD=0x35,
UDS_TRANSFER_DATA=0x36,
UDS_REQ_TRANS_EXIT=0x37,
UDS_REQ_FILE_TRANS=0x38,
UDS_WRITE_BY_ADDR=0x3D,
UDS_TESTER_PRESENT=0x3E,
UDS_ACCESS_TIMING=0x83,
UDS_SECURED_DATA_TRANS=0x84,
UDS_CTRL_DTC_SETTINGS=0x85,
UDS_RESPONSE_ON_EVENT=0x86,
UDS_RESPONSE_LINK_CTRL=0x87
};
class ISOTP_DECODER : public QObject
{
Q_OBJECT
public:
explicit ISOTP_DECODER(const QVector<CANFrame> *frames, QObject *parent = 0);
void setExtendedAddressing(bool mode);
public slots:
void updatedFrames(int);
signals:
void newISOMessage(ISOTP_MESSAGE &msg);
private:
QList<ISOTP_MESSAGE> messageBuffer;
const QVector<CANFrame> *modelFrames;
bool useExtendedAddressing;
void processFrame(const CANFrame &frame);
void checkNeedFlush(uint64_t ID);
};
#endif // ISOTP_DECODER_H
+154
View File
@@ -0,0 +1,154 @@
#include "isotp_interpreterwindow.h"
#include "ui_isotp_interpreterwindow.h"
#include "mainwindow.h"
ISOTP_InterpreterWindow::ISOTP_InterpreterWindow(const QVector<CANFrame> *frames, QWidget *parent) :
QDialog(parent),
ui(new Ui::ISOTP_InterpreterWindow)
{
ui->setupUi(this);
modelFrames = frames;
decoder = new ISOTP_DECODER(modelFrames);
connect(MainWindow::getReference(), &MainWindow::framesUpdated, this, &ISOTP_InterpreterWindow::updatedFrames);
connect(MainWindow::getReference(), &MainWindow::framesUpdated, decoder, &ISOTP_DECODER::updatedFrames);
connect(decoder, &ISOTP_DECODER::newISOMessage, this, &ISOTP_InterpreterWindow::newISOMessage);
connect(ui->tableIsoFrames, &QTableWidget::itemSelectionChanged, this, &ISOTP_InterpreterWindow::showDetailView);
QStringList headers;
headers << "Timestamp" << "ID" << "Bus" << "Dir" << "Length" << "Data";
ui->tableIsoFrames->setColumnCount(6);
ui->tableIsoFrames->setColumnWidth(0, 100);
ui->tableIsoFrames->setColumnWidth(1, 50);
ui->tableIsoFrames->setColumnWidth(2, 50);
ui->tableIsoFrames->setColumnWidth(3, 50);
ui->tableIsoFrames->setColumnWidth(4, 75);
ui->tableIsoFrames->setColumnWidth(5, 200);
ui->tableIsoFrames->setHorizontalHeaderLabels(headers);
QHeaderView *HorzHdr = ui->tableIsoFrames->horizontalHeader();
HorzHdr->setStretchLastSection(true);
}
ISOTP_InterpreterWindow::~ISOTP_InterpreterWindow()
{
delete decoder;
delete ui;
}
void ISOTP_InterpreterWindow::showEvent(QShowEvent* event)
{
QDialog::showEvent(event);
readSettings();
decoder->updatedFrames(-2);
}
void ISOTP_InterpreterWindow::closeEvent(QCloseEvent *event)
{
Q_UNUSED(event);
writeSettings();
}
void ISOTP_InterpreterWindow::readSettings()
{
QSettings settings;
if (settings.value("Main/SaveRestorePositions", false).toBool())
{
resize(settings.value("ISODecodeWindow/WindowSize", this->size()).toSize());
move(settings.value("ISODecodeWindow/WindowPos", QPoint(50, 50)).toPoint());
}
}
void ISOTP_InterpreterWindow::writeSettings()
{
QSettings settings;
if (settings.value("Main/SaveRestorePositions", false).toBool())
{
settings.setValue("ISODecodeWindow/WindowSize", size());
settings.setValue("ISODecodeWindow/WindowPos", pos());
}
}
void ISOTP_InterpreterWindow::updatedFrames(int numFrames)
{
if (numFrames == -1) //all frames deleted. Kill the display
{
messages.clear();
ui->tableIsoFrames->clear();
}
else if (numFrames == -2) //all new set of frames. Reset
{
messages.clear();
ui->tableIsoFrames->clear();
}
else //just got some new frames. See if they are relevant.
{
}
}
void ISOTP_InterpreterWindow::showDetailView()
{
QString buildString;
ISOTP_MESSAGE *msg;
int rowNum = ui->tableIsoFrames->currentRow();
ui->txtFrameDetails->clear();
if (rowNum == -1) return;
msg = &messages[rowNum];
if (msg->len != msg->data.length())
{
buildString.append("Message didn't have the correct number of bytes.\rExpected "
+ QString::number(msg->len) + " got "
+ QString::number(msg->data.length()) + "\r\r");
}
buildString.append(tr("Raw Payload: "));
for (int i = 0; i < messages[rowNum].data.count(); i++)
{
buildString.append(Utility::formatNumber(messages[rowNum].data[i]));
buildString.append(" ");
}
buildString.append("\r\r");
//if (ui->cb->isChecked())
//{
//}
ui->txtFrameDetails->setText(buildString);
}
void ISOTP_InterpreterWindow::newISOMessage(ISOTP_MESSAGE &msg)
{
int rowNum;
QString tempString;
if ((msg.len != msg.data.count()) && !ui->cbShowIncomplete->isChecked()) return;
messages.append(msg);
rowNum = ui->tableIsoFrames->rowCount();
ui->tableIsoFrames->insertRow(rowNum);
ui->tableIsoFrames->setItem(rowNum, 0, new QTableWidgetItem(QString::number(msg.timestamp)));
ui->tableIsoFrames->setItem(rowNum, 1, new QTableWidgetItem(QString::number(msg.ID, 16)));
ui->tableIsoFrames->setItem(rowNum, 2, new QTableWidgetItem(QString::number(msg.bus)));
if (msg.isReceived) ui->tableIsoFrames->setItem(rowNum, 3, new QTableWidgetItem("Rx"));
else ui->tableIsoFrames->setItem(rowNum, 3, new QTableWidgetItem("Tx"));
ui->tableIsoFrames->setItem(rowNum, 4, new QTableWidgetItem(QString::number(msg.len)));
for (int i = 0; i < msg.data.count(); i++)
{
tempString.append(Utility::formatNumber(msg.data[i]));
tempString.append(" ");
}
ui->tableIsoFrames->setItem(rowNum, 5, new QTableWidgetItem(tempString));
}
+38
View File
@@ -0,0 +1,38 @@
#ifndef ISOTP_INTERPRETERWINDOW_H
#define ISOTP_INTERPRETERWINDOW_H
#include <QDialog>
#include "can_structs.h"
#include "isotp_decoder.h"
namespace Ui {
class ISOTP_InterpreterWindow;
}
class ISOTP_InterpreterWindow : public QDialog
{
Q_OBJECT
public:
explicit ISOTP_InterpreterWindow(const QVector<CANFrame> *frames, QWidget *parent = 0);
~ISOTP_InterpreterWindow();
void showEvent(QShowEvent*);
private slots:
void newISOMessage(ISOTP_MESSAGE &msg);
void showDetailView();
void updatedFrames(int);
private:
Ui::ISOTP_InterpreterWindow *ui;
ISOTP_DECODER *decoder;
const QVector<CANFrame> *modelFrames;
QVector<ISOTP_MESSAGE> messages;
void closeEvent(QCloseEvent *event);
void readSettings();
void writeSettings();
};
#endif // ISOTP_INTERPRETERWINDOW_H
+123
View File
@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ISOTP_InterpreterWindow</class>
<widget class="QDialog" name="ISOTP_InterpreterWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>792</width>
<height>725</height>
</rect>
</property>
<property name="windowTitle">
<string>ISO-TP Decoder</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label">
<property name="text">
<string>Interpreted Messages Overview</string>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="tableIsoFrames"/>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QCheckBox" name="cbShowIncomplete">
<property name="text">
<string>Show incomplete and/or corrupted messages</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Data Details of Selected Message</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Interpret As:</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbRaw">
<property name="text">
<string>Raw</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbUDS">
<property name="text">
<string>UDS</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbOBDII">
<property name="text">
<string>OBDII</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTextBrowser" name="txtFrameDetails"/>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="lblFilter">
<property name="text">
<string>IDs to Decode</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listFilter"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="btnAll">
<property name="text">
<string>All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnNone">
<property name="text">
<string>None</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
-3
View File
@@ -29,7 +29,6 @@ MainSettingsDialog::MainSettingsDialog(QWidget *parent) :
ui->cbTimeSeconds->setChecked(settings->value("Main/TimeSeconds", false).toBool());
ui->comboSendingBus->setCurrentIndex(settings->value("Playback/SendingBus", 4).toInt());
ui->cbUseFiltered->setChecked(settings->value("Main/UseFiltered", false).toBool());
ui->cbSingleWire->setChecked(settings->value("Main/SingleWireMode", false).toBool());
//just for simplicity they all call the same function and that function updates all settings at once
connect(ui->cbDisplayHex, SIGNAL(toggled(bool)), this, SLOT(updateSettings()));
@@ -44,7 +43,6 @@ MainSettingsDialog::MainSettingsDialog(QWidget *parent) :
connect(ui->cbTimeSeconds, SIGNAL(toggled(bool)), this, SLOT(updateSettings()));
connect(ui->comboSendingBus, SIGNAL(currentIndexChanged(int)), this, SLOT(updateSettings()));
connect(ui->cbUseFiltered, SIGNAL(toggled(bool)), this, SLOT(updateSettings()));
connect(ui->cbSingleWire, SIGNAL(toggled(bool)), this, SLOT(updateSettings()));
}
MainSettingsDialog::~MainSettingsDialog()
@@ -73,7 +71,6 @@ void MainSettingsDialog::updateSettings()
settings->setValue("Main/TimeSeconds", ui->cbTimeSeconds->isChecked());
settings->setValue("Playback/SendingBus", ui->comboSendingBus->currentIndex());
settings->setValue("Main/UseFiltered", ui->cbUseFiltered->isChecked());
settings->setValue("Main/SingleWireMode", ui->cbSingleWire->isChecked());
settings->sync();
}
-7
View File
@@ -55,13 +55,6 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbSingleWire">
<property name="text">
<string>Put second canbus in single wire mode</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
+238 -291
View File
@@ -9,14 +9,17 @@
#include "serialworker.h"
/*
Things that were planned for the GVRET-PC project but never completed.
Compile for all platforms and create release (remember to include QScintilla libs) and make Win32 binary.
Some notes on things I'd like to put into the program but haven't put on github (yet)
Single / Multi state - The goal is to find bits that change based on toggles or discrete state items (shifters, etc)
Range state - Find things that range like accelerator pedal inputs, road speed, tach, etc
fuzzy scope - Try to find potential places where a given value might be stored - offer guesses and the program tries to find candidates for you
or, try to find things that appear to be multi-byte integers
Allow scripts to read/write signals from DBC files
allow scripts to load DBC files in support of the script - maybe the graphing system too.
*/
QString MainWindow::loadedFileName = "";
@@ -49,15 +52,13 @@ MainWindow::MainWindow(QWidget *parent) :
readSettings();
QStringList headers;
headers << "Timestamp" << "ID" << "Ext" << "Bus" << "Len" << "Data";
//model->setHorizontalHeaderLabels(headers);
ui->canFramesView->setColumnWidth(0, 110);
ui->canFramesView->setColumnWidth(1, 70);
ui->canFramesView->setColumnWidth(2, 40);
ui->canFramesView->setColumnWidth(3, 40);
ui->canFramesView->setColumnWidth(4, 40);
ui->canFramesView->setColumnWidth(5, 275);
ui->canFramesView->setColumnWidth(5, 40);
ui->canFramesView->setColumnWidth(6, 275);
QHeaderView *HorzHdr = ui->canFramesView->horizontalHeader();
HorzHdr->setStretchLastSection(true); //causes the data column to automatically fill the tableview
//enabling the below line kills performance in every way imaginable. Left here as a warning. Do not do this.
@@ -66,8 +67,9 @@ MainWindow::MainWindow(QWidget *parent) :
worker = new SerialWorker(model);
worker->moveToThread(&serialWorkerThread);
connect(&serialWorkerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(&serialWorkerThread, &QThread::started, worker, &SerialWorker::run); //setup timers within the proper thread
connect(this, &MainWindow::sendSerialPort, worker, &SerialWorker::setSerialPort, Qt::QueuedConnection);
connect(worker, &SerialWorker::frameUpdateTick, this, &MainWindow::gotFrames, Qt::QueuedConnection);
connect(worker, &SerialWorker::frameUpdateRapid, this, &MainWindow::gotFrames, Qt::QueuedConnection);
connect(this, &MainWindow::updateBaudRates, worker, &SerialWorker::updateBaudRates, Qt::QueuedConnection);
connect(this, &MainWindow::sendCANFrame, worker, &SerialWorker::sendFrame, Qt::QueuedConnection);
connect(worker, &SerialWorker::connectionSuccess, this, &MainWindow::connectionSucceeded, Qt::QueuedConnection);
@@ -78,7 +80,7 @@ MainWindow::MainWindow(QWidget *parent) :
connect(this, &MainWindow::stopFrameCapturing, worker, &SerialWorker::stopFrameCapture);
connect(this, &MainWindow::settingsUpdated, worker, &SerialWorker::readSettings);
serialWorkerThread.start();
serialWorkerThread.setPriority(QThread::HighPriority);
serialWorkerThread.setPriority(QThread::HighPriority);
graphingWindow = NULL;
frameInfoWindow = NULL;
@@ -91,46 +93,55 @@ MainWindow::MainWindow(QWidget *parent) :
firmwareUploaderWindow = NULL;
discreteStateWindow = NULL;
connectionWindow = NULL;
scriptingWindow = NULL;
rangeWindow = NULL;
dbcFileWindow = NULL;
fuzzingWindow = NULL;
udsScanWindow = NULL;
isoWindow = NULL;
dbcHandler = new DBCHandler;
bDirty = false;
inhibitFilterUpdate = false;
rxFrames = 0;
model->setDBCHandler(dbcHandler);
connect(ui->actionOpen_Log_File, SIGNAL(triggered(bool)), this, SLOT(handleLoadFile()));
connect(ui->actionGraph_Dta, SIGNAL(triggered(bool)), this, SLOT(showGraphingWindow()));
connect(ui->actionFrame_Data_Analysis, SIGNAL(triggered(bool)), this, SLOT(showFrameDataAnalysis()));
connect(ui->btnClearFrames, SIGNAL(clicked(bool)), this, SLOT(clearFrames()));
connect(ui->actionSave_Log_File, SIGNAL(triggered(bool)), this, SLOT(handleSaveFile()));
connect(ui->actionSave_Filtered_Log_File, SIGNAL(triggered(bool)), this, SLOT(handleSaveFilteredFile()));
connect(ui->actionLoad_Filter_Definition, SIGNAL(triggered(bool)), this, SLOT(handleLoadFilters()));
connect(ui->actionSave_Filter_Definition, SIGNAL(triggered(bool)), this, SLOT(handleSaveFilters()));
connect(ui->action_Playback, SIGNAL(triggered(bool)), this, SLOT(showPlaybackWindow()));
connect(ui->actionFlow_View, SIGNAL(triggered(bool)), this, SLOT(showFlowViewWindow()));
connect(ui->action_Custom, SIGNAL(triggered(bool)), this, SLOT(showFrameSenderWindow()));
connect(ui->actionLoad_DBC_File, SIGNAL(triggered(bool)), this, SLOT(handleLoadDBC()));
connect(ui->actionSave_DBC_File, SIGNAL(triggered(bool)), this, SLOT(handleSaveDBC()));
connect(ui->canFramesView, SIGNAL(clicked(QModelIndex)), this, SLOT(gridClicked(QModelIndex)));
connect(ui->canFramesView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(gridDoubleClicked(QModelIndex)));
connect(ui->cbInterpret, SIGNAL(toggled(bool)), this, SLOT(interpretToggled(bool)));
connect(ui->cbOverwrite, SIGNAL(toggled(bool)), this, SLOT(overwriteToggled(bool)));
connect(ui->actionEdit_Messages_Signals, SIGNAL(triggered(bool)), this, SLOT(showDBCEditor()));
connect(ui->btnCaptureToggle, SIGNAL(clicked(bool)), this, SLOT(toggleCapture()));
connect(ui->actionExit_Application, SIGNAL(triggered(bool)), this, SLOT(exitApp()));
connect(ui->actionFuzzy_Scope, SIGNAL(triggered(bool)), this, SLOT(showFuzzyScopeWindow()));
connect(ui->actionRange_State, SIGNAL(triggered(bool)), this, SLOT(showRangeWindow()));
connect(ui->actionSave_Decoded_Frames, SIGNAL(triggered(bool)), this, SLOT(handleSaveDecoded()));
connect(ui->actionSingle_Multi_State, SIGNAL(triggered(bool)), this, SLOT(showSingleMultiWindow()));
connect(ui->actionFile_Comparison, SIGNAL(triggered(bool)), this, SLOT(showComparisonWindow()));
connect(ui->btnNormalize, SIGNAL(clicked(bool)), this, SLOT(normalizeTiming()));
connect(ui->actionPreferences, SIGNAL(triggered(bool)), this, SLOT(showSettingsDialog()));
connect(model, SIGNAL(updatedFiltersList()), this, SLOT(updateFilterList()));
connect(ui->listFilters, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(filterListItemChanged(QListWidgetItem*)));
connect(ui->btnFilterAll, SIGNAL(clicked(bool)), this, SLOT(filterSetAll()));
connect(ui->btnFilterNone, SIGNAL(clicked(bool)), this, SLOT(filterClearAll()));
connect(ui->actionFirmware_Uploader, SIGNAL(triggered(bool)), this, SLOT(showFirmwareUploaderWindow()));
connect(ui->actionSetup, SIGNAL(triggered(bool)), SLOT(showConnectionSettingsWindow()));
connect(ui->actionConnect, SIGNAL(triggered(bool)), this, SLOT(connButtonPress()));
connect(ui->actionOpen_Log_File, &QAction::triggered, this, &MainWindow::handleLoadFile);
connect(ui->actionGraph_Dta, &QAction::triggered, this, &MainWindow::showGraphingWindow);
connect(ui->actionFrame_Data_Analysis, &QAction::triggered, this, &MainWindow::showFrameDataAnalysis);
connect(ui->btnClearFrames, &QAbstractButton::clicked, this, &MainWindow::clearFrames);
connect(ui->actionSave_Log_File, &QAction::triggered, this, &MainWindow::handleSaveFile);
connect(ui->actionSave_Filtered_Log_File, &QAction::triggered, this, &MainWindow::handleSaveFilteredFile);
connect(ui->actionLoad_Filter_Definition, &QAction::triggered, this, &MainWindow::handleLoadFilters);
connect(ui->actionSave_Filter_Definition, &QAction::triggered, this, &MainWindow::handleSaveFilters);
connect(ui->action_Playback, &QAction::triggered, this, &MainWindow::showPlaybackWindow);
connect(ui->actionFlow_View, &QAction::triggered, this, &MainWindow::showFlowViewWindow);
connect(ui->action_Custom, &QAction::triggered, this, &MainWindow::showFrameSenderWindow);
connect(ui->canFramesView, &QAbstractItemView::clicked, this, &MainWindow::gridClicked);
connect(ui->canFramesView, &QAbstractItemView::doubleClicked, this, &MainWindow::gridDoubleClicked);
connect(ui->cbInterpret, &QAbstractButton::toggled, this, &MainWindow::interpretToggled);
connect(ui->cbOverwrite, &QAbstractButton::toggled, this, &MainWindow::overwriteToggled);
connect(ui->btnCaptureToggle, &QAbstractButton::clicked, this, &MainWindow::toggleCapture);
connect(ui->actionExit_Application, &QAction::triggered, this, &MainWindow::exitApp);
connect(ui->actionFuzzy_Scope, &QAction::triggered, this, &MainWindow::showFuzzyScopeWindow);
connect(ui->actionRange_State_2, &QAction::triggered, this, &MainWindow::showRangeWindow);
connect(ui->actionSave_Decoded_Frames, &QAction::triggered, this, &MainWindow::handleSaveDecoded);
connect(ui->actionSingle_Multi_State_2, &QAction::triggered, this, &MainWindow::showSingleMultiWindow);
connect(ui->actionFile_Comparison, &QAction::triggered, this, &MainWindow::showComparisonWindow);
connect(ui->actionScripting_INterface, &QAction::triggered, this, &MainWindow::showScriptingWindow);
connect(ui->btnNormalize, &QAbstractButton::clicked, this, &MainWindow::normalizeTiming);
connect(ui->actionPreferences, &QAction::triggered, this, &MainWindow::showSettingsDialog);
connect(model, &CANFrameModel::updatedFiltersList, this, &MainWindow::updateFilterList);
connect(ui->listFilters, &QListWidget::itemChanged, this, &MainWindow::filterListItemChanged);
connect(ui->btnFilterAll, &QAbstractButton::clicked, this, &MainWindow::filterSetAll);
connect(ui->btnFilterNone, &QAbstractButton::clicked, this, &MainWindow::filterClearAll);
connect(ui->actionFirmware_Uploader, &QAction::triggered, this, &MainWindow::showFirmwareUploaderWindow);
connect(ui->actionDBC_File_Manager, &QAction::triggered, this, &MainWindow::showDBCFileWindow);
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);
lbStatusConnected.setText(tr("Not connected"));
updateFileStatus();
@@ -142,6 +153,13 @@ MainWindow::MainWindow(QWidget *parent) :
ui->lbFPS->setText("0");
ui->lbNumFrames->setText("0");
connect(&updateTimer, &QTimer::timeout, this, &MainWindow::tickGUIUpdate);
updateTimer.setInterval(250);
updateTimer.start();
elapsedTime = new QTime;
elapsedTime->start();
isConnected = false;
allowCapture = true;
@@ -156,6 +174,10 @@ MainWindow::MainWindow(QWidget *parent) :
normalRowHeight = ui->canFramesView->rowHeight(0);
qDebug() << "normal row height = " << normalRowHeight;
model->clearFrames();
//Automatically create the connection window so it can be updated even if we never opened it.
connectionWindow = new ConnectionWindow();
connect(connectionWindow, SIGNAL(updateConnectionSettings(QString,QString,int,int)), this, SLOT(updateConnectionSettings(QString,QString,int,int)));
}
MainWindow::~MainWindow()
@@ -223,6 +245,44 @@ MainWindow::~MainWindow()
connectionWindow->close();
delete connectionWindow;
}
if (scriptingWindow)
{
scriptingWindow->close();
delete scriptingWindow;
}
if (rangeWindow)
{
rangeWindow->close();
delete rangeWindow;
}
if (dbcFileWindow)
{
dbcFileWindow->close();
delete dbcFileWindow;
}
if (fuzzingWindow)
{
fuzzingWindow->close();
delete fuzzingWindow;
}
if (udsScanWindow)
{
udsScanWindow->close();
delete udsScanWindow;
}
if (isoWindow)
{
isoWindow->close();
delete isoWindow;
}
delete elapsedTime;
delete ui;
delete dbcHandler;
@@ -230,6 +290,27 @@ MainWindow::~MainWindow()
delete model;
}
void MainWindow::exitApp()
{
if (graphingWindow) graphingWindow->close();
if (frameInfoWindow) frameInfoWindow->close();
if (playbackWindow) playbackWindow->close();
if (flowViewWindow) flowViewWindow->close();
if (frameSenderWindow) frameSenderWindow->close();
if (dbcMainEditor) dbcMainEditor->close();
if (comparatorWindow) comparatorWindow->close();
if (connectionWindow) connectionWindow->close();
if (settingsDialog) settingsDialog->close();
if (discreteStateWindow) discreteStateWindow->close();
if (scriptingWindow) scriptingWindow->close();
if (rangeWindow) rangeWindow->close();
if (dbcFileWindow) dbcFileWindow->close();
if (fuzzingWindow) fuzzingWindow->close();
if (udsScanWindow) udsScanWindow->close();
if (isoWindow) isoWindow->close();
this->close();
}
void MainWindow::closeEvent(QCloseEvent *event)
{
Q_UNUSED(event);
@@ -255,6 +336,20 @@ void MainWindow::readSettings()
{
ui->cbAutoScroll->setChecked(true);
}
int cType = settings.value("Main/DefaultConnectionType", 0).toInt();
if (cType == 0) connType = "GVRET";
if (cType == 1) connType = "KVASER";
if (cType == 2) connType = "SOCKETCAN";
portName = settings.value("Main/DefaultConnectionPort", "").toString();
canSpeed0 = -1;
canSpeed1 = -1;
qDebug() << connType;
qDebug() << portName;
//"Main/SingleWireMode"
readUpdateableSettings();
}
@@ -307,6 +402,7 @@ void MainWindow::connButtonPress()
if (ports[i].portName() == portName)
{
portInfo = ports[i];
//need to create some way to send single wire mode to serial port code
emit sendSerialPort(&portInfo);
lbStatusConnected.setText(tr("Attempting to connect to port ") + portName);
}
@@ -416,20 +512,30 @@ void MainWindow::filterClearAll()
model->setAllFilters(false);
}
//most of the work is handled elsewhere. Need only to update the # of frames
//and maybe auto scroll
void MainWindow::gotFrames(int FPS, int framesSinceLastUpdate)
void MainWindow::tickGUIUpdate()
{
framesPerSec += rxFrames * 1000 / elapsedTime->elapsed() - (framesPerSec / 4);
elapsedTime->restart();
model->sendBulkRefresh(rxFrames);
ui->lbNumFrames->setText(QString::number(model->rowCount()));
if (ui->cbAutoScroll->isChecked()) ui->canFramesView->scrollToBottom();
ui->lbFPS->setText(QString::number(FPS));
if (framesSinceLastUpdate > 0)
ui->lbFPS->setText(QString::number(framesPerSec / 4));
if (rxFrames > 0)
{
bDirty = true;
emit framesUpdated(framesSinceLastUpdate); //anyone care that frames were updated?
emit framesUpdated(rxFrames); //anyone care that frames were updated?
}
if (model->needsFilterRefresh()) updateFilterList();
rxFrames = 0;
}
void MainWindow::gotFrames(int framesSinceLastUpdate)
{
rxFrames += framesSinceLastUpdate;
emit frameUpdateRapid(framesSinceLastUpdate);
}
void MainWindow::addFrameToDisplay(CANFrame &frame, bool autoRefresh = false)
@@ -483,200 +589,42 @@ void MainWindow::changeBaudRates()
void MainWindow::handleLoadFile()
{
QString filename;
QFileDialog dialog(this);
bool result = false;
QVector<CANFrame> tempFrames;
QStringList filters;
filters.append(QString(tr("CRTD Logs (*.txt)")));
filters.append(QString(tr("GVRET Logs (*.csv)")));
filters.append(QString(tr("Generic ID/Data CSV (*.csv)")));
filters.append(QString(tr("BusMaster Log (*.log)")));
filters.append(QString(tr("Microchip Log (*.can)")));
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
if (dialog.exec() == QDialog::Accepted)
if (FrameFileIO::loadFrameFile(filename, &tempFrames))
{
filename = dialog.selectedFiles()[0];
ui->canFramesView->scrollToTop();
model->clearFrames();
model->insertFrames(tempFrames);
loadedFileName = filename;
model->recalcOverwrite();
ui->lbNumFrames->setText(QString::number(model->rowCount()));
if (ui->cbAutoScroll->isChecked()) ui->canFramesView->scrollToBottom();
QVector<CANFrame> tempFrames;
QProgressDialog progress(this);
progress.setWindowModality(Qt::WindowModal);
progress.setLabelText("Loading file...");
progress.setCancelButton(0);
progress.setRange(0,0);
progress.setMinimumDuration(0);
progress.show();
qApp->processEvents();
if (dialog.selectedNameFilter() == filters[0]) result = FrameFileIO::loadCRTDFile(filename, &tempFrames);
if (dialog.selectedNameFilter() == filters[1]) result = FrameFileIO::loadNativeCSVFile(filename, &tempFrames);
if (dialog.selectedNameFilter() == filters[2]) result = FrameFileIO::loadGenericCSVFile(filename, &tempFrames);
if (dialog.selectedNameFilter() == filters[3]) result = FrameFileIO::loadLogFile(filename, &tempFrames);
if (dialog.selectedNameFilter() == filters[4]) result = FrameFileIO::loadMicrochipFile(filename, &tempFrames);
progress.cancel();
if (result)
{
model->insertFrames(tempFrames);
QStringList fileList = filename.split('/');
loadedFileName = fileList[fileList.length() - 1];
model->recalcOverwrite();
ui->lbNumFrames->setText(QString::number(model->rowCount()));
if (ui->cbAutoScroll->isChecked()) ui->canFramesView->scrollToBottom();
updateFileStatus();
emit framesUpdated(-2);
}
updateFileStatus();
emit framesUpdated(-2);
}
}
void MainWindow::handleSaveFile()
{
QString filename;
QFileDialog dialog(this);
bool result = false;
QStringList filters;
filters.append(QString(tr("CRTD Logs (*.txt)")));
filters.append(QString(tr("GVRET Logs (*.csv)")));
filters.append(QString(tr("Generic ID/Data CSV (*.csv)")));
filters.append(QString(tr("BusMaster Log (*.log)")));
filters.append(QString(tr("Microchip Log (*.can)")));
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
dialog.setAcceptMode(QFileDialog::AcceptSave);
if (dialog.exec() == QDialog::Accepted)
if (FrameFileIO::saveFrameFile(filename, model->getListReference()))
{
const QVector<CANFrame> *frames = model->getListReference();
filename = dialog.selectedFiles()[0];
QProgressDialog progress(this);
progress.setWindowModality(Qt::WindowModal);
progress.setLabelText("Saving file...");
progress.setCancelButton(0);
progress.setRange(0,0);
progress.setMinimumDuration(0);
progress.show();
qApp->processEvents();
if (dialog.selectedNameFilter() == filters[0])
{
if (!filename.contains('.')) filename += ".txt";
result = FrameFileIO::saveCRTDFile(filename, frames);
}
if (dialog.selectedNameFilter() == filters[1])
{
if (!filename.contains('.')) filename += ".csv";
result = FrameFileIO::saveNativeCSVFile(filename, frames);
}
if (dialog.selectedNameFilter() == filters[2])
{
if (!filename.contains('.')) filename += ".csv";
result = FrameFileIO::saveGenericCSVFile(filename, frames);
}
if (dialog.selectedNameFilter() == filters[3])
{
if (!filename.contains('.')) filename += ".log";
result = FrameFileIO::saveLogFile(filename, frames);
}
if (dialog.selectedNameFilter() == filters[4])
{
if (!filename.contains('.')) filename += ".log";
result = FrameFileIO::saveMicrochipFile(filename, frames);
}
progress.cancel();
if (result)
{
QStringList fileList = filename.split('/');
loadedFileName = fileList[fileList.length() - 1];
updateFileStatus();
}
loadedFileName = filename;
updateFileStatus();
}
}
void MainWindow::handleSaveFilteredFile()
{
QString filename;
QFileDialog dialog(this);
bool result = false;
QStringList filters;
filters.append(QString(tr("CRTD Logs (*.txt)")));
filters.append(QString(tr("GVRET Logs (*.csv)")));
filters.append(QString(tr("Generic ID/Data CSV (*.csv)")));
filters.append(QString(tr("BusMaster Log (*.log)")));
filters.append(QString(tr("Microchip Log (*.log)")));
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
dialog.setAcceptMode(QFileDialog::AcceptSave);
if (dialog.exec() == QDialog::Accepted)
if (FrameFileIO::saveFrameFile(filename, model->getFilteredListReference()))
{
const QVector<CANFrame> *frames = model->getFilteredListReference();
filename = dialog.selectedFiles()[0];
QProgressDialog progress(this);
progress.setWindowModality(Qt::WindowModal);
progress.setLabelText("Saving filtered file...");
progress.setCancelButton(0);
progress.setRange(0,0);
progress.setMinimumDuration(0);
progress.show();
qApp->processEvents();
if (dialog.selectedNameFilter() == filters[0])
{
if (!filename.contains('.')) filename += ".txt";
result = FrameFileIO::saveCRTDFile(filename, frames);
}
if (dialog.selectedNameFilter() == filters[1])
{
if (!filename.contains('.')) filename += ".csv";
result = FrameFileIO::saveNativeCSVFile(filename, frames);
}
if (dialog.selectedNameFilter() == filters[2])
{
if (!filename.contains('.')) filename += ".csv";
result = FrameFileIO::saveGenericCSVFile(filename, frames);
}
if (dialog.selectedNameFilter() == filters[3])
{
if (!filename.contains('.')) filename += ".log";
result = FrameFileIO::saveLogFile(filename, frames);
}
if (dialog.selectedNameFilter() == filters[4])
{
if (!filename.contains('.')) filename += ".log";
result = FrameFileIO::saveMicrochipFile(filename, frames);
}
progress.cancel();
if (result)
{
QStringList fileList = filename.split('/');
loadedFileName = fileList[fileList.length() - 1];
updateFileStatus();
}
loadedFileName = filename;
updateFileStatus();
}
}
@@ -721,52 +669,7 @@ void MainWindow::handleLoadFilters()
}
}
void MainWindow::handleLoadDBC()
{
QString filename;
QFileDialog dialog(this);
QStringList filters;
filters.append(QString(tr("DBC File (*.dbc)")));
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
if (dialog.exec() == QDialog::Accepted)
{
filename = dialog.selectedFiles()[0];
//right now there is only one file type that can be loaded here so just do it.
dbcHandler->loadDBCFile(filename);
//dbcHandler->listDebugging();
QStringList fileList = filename.split('/');
lbStatusDatabase.setText(fileList[fileList.length() - 1] + tr(" loaded."));
}
}
void MainWindow::handleSaveDBC()
{
QString filename;
QFileDialog dialog(this);
QStringList filters;
filters.append(QString(tr("DBC File (*.dbc)")));
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
dialog.setAcceptMode(QFileDialog::AcceptSave);
if (dialog.exec() == QDialog::Accepted)
{
filename = dialog.selectedFiles()[0];
if (!filename.contains('.')) filename += ".dbc";
dbcHandler->saveDBCFile(filename);
QStringList fileList = filename.split('/');
lbStatusDatabase.setText(fileList[fileList.length() - 1] + tr(" loaded."));
}
}
//lbStatusDatabase.setText(fileList[fileList.length() - 1] + tr(" loaded."));
void MainWindow::handleSaveDecoded()
{
@@ -792,7 +695,7 @@ void MainWindow::handleSaveDecoded()
void MainWindow::saveDecodedTextFile(QString filename)
{
QFile *outFile = new QFile(filename);
const QVector<CANFrame> *frames = model->getListReference();
const QVector<CANFrame> *frames = model->getFilteredListReference();
if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text))
return;
@@ -824,14 +727,18 @@ Data Bytes: 88 10 00 13 BB 00 06 00
builderString = "";
if (dbcHandler != NULL)
{
DBC_MESSAGE *msg = dbcHandler->findMsgByID(thisFrame.ID);
DBC_MESSAGE *msg = dbcHandler->findMessage(thisFrame);
if (msg != NULL)
{
for (int j = 0; j < msg->msgSignals.length(); j++)
for (int j = 0; j < msg->sigHandler->getCount(); j++)
{
builderString.append("\t" + dbcHandler->processSignal(thisFrame, msg->msgSignals.at(j)));
builderString.append("\n");
QString temp;
if (msg->sigHandler->findSignalByIdx(j)->processAsText(thisFrame, temp))
{
builderString.append("\t" + temp);
builderString.append("\n");
}
}
}
builderString.append("\n");
@@ -878,6 +785,7 @@ void MainWindow::connectionSucceeded(int baud0, int baud1)
ui->cbSpeed2->setCurrentIndex(ui->cbSpeed2->count() - 1);
}
*/
if (connectionWindow) connectionWindow->setSpeeds(baud0, baud1);
//ui->btnConnect->setEnabled(false);
ui->actionConnect->setText(tr("Disconnect"));
isConnected = true;
@@ -932,6 +840,12 @@ void MainWindow::gotDeviceInfo(int build, int swCAN)
+ "\n\nPlease upgrade your firmware version.\nOtherwise, you may experience difficulties.");
msgBox.exec();
}
if (connectionWindow)
{
if (swCAN == 1) connectionWindow->setCAN1SWMode(true);
else connectionWindow->setCAN1SWMode(false);
}
}
void MainWindow::setTargettedID(int id)
@@ -979,6 +893,18 @@ void MainWindow::showFrameDataAnalysis()
frameInfoWindow->show();
}
void MainWindow::showISOInterpreterWindow()
{
if (!isoWindow)
{
if (!useFiltered)
isoWindow = new ISOTP_InterpreterWindow(model->getListReference());
else
isoWindow = new ISOTP_InterpreterWindow(model->getFilteredListReference());
}
isoWindow->show();
}
void MainWindow::showFrameSenderWindow()
{
if (!frameSenderWindow)
@@ -988,7 +914,7 @@ void MainWindow::showFrameSenderWindow()
else
frameSenderWindow = new FrameSenderWindow(model->getFilteredListReference());
connect(frameSenderWindow, SIGNAL(sendCANFrame(const CANFrame*,int)), worker, SLOT(sendFrame(const CANFrame*,int)));
connect(frameSenderWindow, &FrameSenderWindow::sendCANFrame, worker, &SerialWorker::sendFrame);
}
frameSenderWindow->show();
}
@@ -1034,9 +960,44 @@ void MainWindow::showSingleMultiWindow()
discreteStateWindow->show();
}
void MainWindow::showFuzzingWindow()
{
if (!fuzzingWindow)
{
fuzzingWindow = new FuzzingWindow(model->getListReference());
connect(fuzzingWindow, SIGNAL(sendCANFrame(const CANFrame*,int)), worker, SLOT(sendFrame(const CANFrame*,int)));
connect(fuzzingWindow, SIGNAL(sendFrameBatch(const QList<CANFrame>*)), worker, SLOT(sendFrameBatch(const QList<CANFrame>*)));
}
fuzzingWindow->show();
}
void MainWindow::showUDSScanWindow()
{
if (!udsScanWindow)
{
udsScanWindow = new UDSScanWindow(model->getListReference());
connect(udsScanWindow, SIGNAL(sendCANFrame(const CANFrame*,int)), worker, SLOT(sendFrame(const CANFrame*,int)));
}
udsScanWindow->show();
}
void MainWindow::showScriptingWindow()
{
if (!scriptingWindow)
{
scriptingWindow = new ScriptingWindow(model->getListReference());
connect(scriptingWindow, &ScriptingWindow::sendCANFrame, worker, &SerialWorker::sendFrame);
}
scriptingWindow->show();
}
void MainWindow::showRangeWindow()
{
//not done yet
if (!rangeWindow)
{
rangeWindow = new RangeStateWindow(model->getListReference());
}
rangeWindow->show();
}
void MainWindow::showFuzzyScopeWindow()
@@ -1044,19 +1005,6 @@ void MainWindow::showFuzzyScopeWindow()
//not done yet
}
void MainWindow::exitApp()
{
if (graphingWindow) graphingWindow->close();
if (frameInfoWindow) frameInfoWindow->close();
if (playbackWindow) playbackWindow->close();
if (flowViewWindow) flowViewWindow->close();
if (frameSenderWindow) frameSenderWindow->close();
if (dbcMainEditor) dbcMainEditor->close();
if (comparatorWindow) comparatorWindow->close();
if (connectionWindow) connectionWindow->close();
this->close();
}
void MainWindow::showFlowViewWindow()
{
if (!flowViewWindow)
@@ -1078,14 +1026,13 @@ void MainWindow::showFlowViewWindow()
flowViewWindow->show();
}
//this one always gets the unfiltered list intentionally.
void MainWindow::showDBCEditor()
void MainWindow::showDBCFileWindow()
{
if (!dbcMainEditor)
if (!dbcFileWindow)
{
dbcMainEditor = new DBCMainEditor(dbcHandler, model->getListReference());
dbcFileWindow = new DBCLoadSaveWindow(dbcHandler, model->getListReference());
}
dbcMainEditor->show();
dbcFileWindow->show();
}
void MainWindow::showConnectionSettingsWindow()
+24 -4
View File
@@ -20,6 +20,12 @@
#include "firmwareuploaderwindow.h"
#include "discretestatewindow.h"
#include "connectionwindow.h"
#include "scriptingwindow.h"
#include "rangestatewindow.h"
#include "dbcloadsavewindow.h"
#include "fuzzingwindow.h"
#include "udsscanwindow.h"
#include "isotp_interpreterwindow.h"
namespace Ui {
class MainWindow;
@@ -40,8 +46,6 @@ private slots:
void handleLoadFile();
void handleSaveFile();
void handleSaveFilteredFile();
void handleLoadDBC();
void handleSaveDBC();
void handleSaveFilters();
void handleLoadFilters();
void connButtonPress();
@@ -58,6 +62,11 @@ private slots:
void showSettingsDialog();
void showFirmwareUploaderWindow();
void showConnectionSettingsWindow();
void showScriptingWindow();
void showDBCFileWindow();
void showFuzzingWindow();
void showUDSScanWindow();
void showISOInterpreterWindow();
void exitApp();
void handleSaveDecoded();
void changeBaudRates();
@@ -68,7 +77,7 @@ private slots:
void gridDoubleClicked(QModelIndex);
void interpretToggled(bool);
void overwriteToggled(bool);
void showDBCEditor();
void tickGUIUpdate();
void toggleCapture();
void normalizeTiming();
void updateFilterList();
@@ -77,7 +86,7 @@ private slots:
void filterClearAll();
public slots:
void gotFrames(int, int);
void gotFrames(int);
void updateSettings();
void gotCenterTimeID(int32_t ID, double timestamp);
void updateConnectionSettings(QString connectionType, QString port, int speed0, int speed1);
@@ -92,6 +101,7 @@ signals:
//-1 = frames cleared, -2 = a new file has been loaded (so all frames are different), otherwise # of new frames
void framesUpdated(int numFrames); //something has updated the frame list
void frameUpdateRapid(int numFrames);
void settingsUpdated();
void sendCenterTimeID(int32_t ID, double timestamp);
@@ -105,6 +115,10 @@ private:
QThread serialWorkerThread;
SerialWorker *worker;
QByteArray inputBuffer;
QTimer updateTimer;
QTime *elapsedTime;
int framesPerSec;
int rxFrames;
bool inhibitFilterUpdate;
bool useHex;
bool allowCapture;
@@ -124,6 +138,12 @@ private:
DiscreteStateWindow *discreteStateWindow;
FirmwareUploaderWindow *firmwareUploaderWindow;
ConnectionWindow *connectionWindow;
ScriptingWindow *scriptingWindow;
RangeStateWindow *rangeWindow;
DBCLoadSaveWindow *dbcFileWindow;
FuzzingWindow *fuzzingWindow;
UDSScanWindow *udsScanWindow;
ISOTP_InterpreterWindow *isoWindow;
//various private storage
QLabel lbStatusConnected;
+43 -18
View File
@@ -204,13 +204,13 @@
<property name="title">
<string>RE Tools</string>
</property>
<addaction name="actionSingle_Multi_State"/>
<addaction name="actionRange_State"/>
<addaction name="actionFlow_View"/>
<addaction name="actionFuzzy_Scope"/>
<addaction name="actionGraph_Dta"/>
<addaction name="actionFrame_Data_Analysis"/>
<addaction name="actionFile_Comparison"/>
<addaction name="actionRange_State_2"/>
<addaction name="actionSingle_Multi_State_2"/>
<addaction name="actionISO_TP_Decoder"/>
</widget>
<widget class="QMenu" name="menuSend_Frames">
<property name="title">
@@ -218,7 +218,9 @@
</property>
<addaction name="action_Playback"/>
<addaction name="action_Custom"/>
<addaction name="actionFirmware_Uploader"/>
<addaction name="actionScripting_INterface"/>
<addaction name="actionFuzzing"/>
<addaction name="actionUDS_Scanner"/>
</widget>
<widget class="QMenu" name="menuFile">
<property name="title">
@@ -231,9 +233,7 @@
<addaction name="actionLoad_Filter_Definition"/>
<addaction name="actionSave_Filter_Definition"/>
<addaction name="separator"/>
<addaction name="actionLoad_DBC_File"/>
<addaction name="actionEdit_Messages_Signals"/>
<addaction name="actionSave_DBC_File"/>
<addaction name="actionDBC_File_Manager"/>
<addaction name="actionSave_Decoded_Frames"/>
<addaction name="separator"/>
<addaction name="actionPreferences"/>
@@ -263,16 +263,6 @@
<string>Custom</string>
</property>
</action>
<action name="actionSingle_Multi_State">
<property name="text">
<string>Single / Multi State</string>
</property>
</action>
<action name="actionRange_State">
<property name="text">
<string>Range State</string>
</property>
</action>
<action name="actionFlow_View">
<property name="text">
<string>Flow View</string>
@@ -360,7 +350,7 @@
</action>
<action name="actionSetup">
<property name="text">
<string>Setup</string>
<string>Set Port</string>
</property>
</action>
<action name="actionConnect">
@@ -368,6 +358,41 @@
<string>Connect</string>
</property>
</action>
<action name="actionScripting_INterface">
<property name="text">
<string>Scripting Interface</string>
</property>
</action>
<action name="actionRange_State_2">
<property name="text">
<string>Range State</string>
</property>
</action>
<action name="actionSingle_Multi_State_2">
<property name="text">
<string>Single/Multi State</string>
</property>
</action>
<action name="actionDBC_File_Manager">
<property name="text">
<string>DBC File Manager</string>
</property>
</action>
<action name="actionFuzzing">
<property name="text">
<string>Fuzzing</string>
</property>
</action>
<action name="actionUDS_Scanner">
<property name="text">
<string>UDS Scanner</string>
</property>
</action>
<action name="actionISO_TP_Decoder">
<property name="text">
<string>ISO-TP Decoder</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
+114 -48
View File
@@ -25,6 +25,8 @@ NewGraphDialog::NewGraphDialog(DBCHandler *handler, QWidget *parent) :
connect(ui->cbMessages, SIGNAL(currentIndexChanged(int)), this, SLOT(loadSignals(int)));
connect(ui->cbSignals, SIGNAL(currentIndexChanged(int)), this, SLOT(fillFormFromSignal(int)));
connect(ui->rbSignalGraph, SIGNAL(toggled(bool)), this, SLOT(setSignalActive(bool)));
connect(ui->rbStandardGraph, SIGNAL(toggled(bool)), this, SLOT(setStandardActive(bool)));
loadMessages();
}
@@ -58,28 +60,41 @@ void NewGraphDialog::clearParams()
ui->txtStride->clear();
ui->txtName->clear();
ui->txtData->clear();
ui->rbStandardGraph->setChecked(true);
setStandardActive(true);
}
void NewGraphDialog::setParams(GraphParams &params)
{
ui->txtID->setText(Utility::formatNumber(params.ID));
ui->txtBias->setText(QString::number(params.bias));
ui->txtMask->setText(Utility::formatNumber(params.mask));
ui->txtScale->setText(QString::number(params.scale));
ui->txtStride->setText(QString::number(params.stride));
ui->cbSigned->setChecked(params.isSigned);
ui->txtName->setText(params.graphName);
if (params.endByte > -1)
if (params.isDBCSignal)
{
ui->txtData->setText(QString::number(params.startByte) + "-" + QString::number(params.endByte));
clearParams();
setSignalActive(true);
//loadMessages();
}
else
{
ui->txtData->setText(QString::number(params.startByte));
setStandardActive(true);
ui->txtBias->setText(QString::number(params.bias));
ui->txtMask->setText(Utility::formatNumber(params.mask));
ui->txtScale->setText(QString::number(params.scale));
ui->txtStride->setText(QString::number(params.stride));
ui->cbSigned->setChecked(params.isSigned);
if (params.endByte > -1)
{
ui->txtData->setText(QString::number(params.startByte) + "-" + QString::number(params.endByte));
}
else
{
ui->txtData->setText(QString::number(params.startByte));
}
}
ui->txtID->setText(Utility::formatNumber(params.ID));
ui->txtName->setText(params.graphName);
QPalette p = ui->colorSwatch->palette();
p.setColor(QPalette::Button, params.color);
ui->colorSwatch->setPalette(p);
@@ -87,40 +102,58 @@ void NewGraphDialog::setParams(GraphParams &params)
void NewGraphDialog::getParams(GraphParams &params)
{
params.ID = Utility::ParseStringToNum(ui->txtID->text());
params.bias = ui->txtBias->text().toFloat();
params.isDBCSignal = ui->rbSignalGraph->isChecked();
params.color = ui->colorSwatch->palette().button().color();
params.isSigned = ui->cbSigned->isChecked();
params.mask = Utility::ParseStringToNum(ui->txtMask->text());
params.scale = ui->txtScale->text().toFloat();
params.stride = Utility::ParseStringToNum(ui->txtStride->text());
QStringList values = ui->txtData->text().split('-');
params.startByte = -1;
params.endByte = -1;
if (values.count() > 0)
{
params.startByte = values[0].toInt();
if (values.count() > 1)
{
params.endByte = values[1].toInt();
}
}
//now catch stupidity and bring it to defaults
if (params.mask == 0) params.mask = 0xFFFFFFFF;
if (fabs(params.scale) < 0.00000001) params.scale = 1.0f;
if (params.stride < 1) params.stride = 1;
params.graphName = ui->txtName->text();
if (params.isDBCSignal)
{
params.signal = ui->cbSignals->currentText();
params.ID = Utility::ParseStringToNum(ui->txtID->text());
params.bias = 0;
params.isSigned = false;
params.mask = 0;
params.scale = 1;
params.bias = 0;
params.stride = 1;
}
else {
params.ID = Utility::ParseStringToNum(ui->txtID->text());
params.bias = ui->txtBias->text().toFloat();
params.isSigned = ui->cbSigned->isChecked();
params.mask = Utility::ParseStringToNum(ui->txtMask->text());
params.scale = ui->txtScale->text().toFloat();
params.stride = Utility::ParseStringToNum(ui->txtStride->text());
params.signal = "";
QStringList values = ui->txtData->text().split('-');
params.startByte = -1;
params.endByte = -1;
if (values.count() > 0)
{
params.startByte = values[0].toInt();
if (values.count() > 1)
{
params.endByte = values[1].toInt();
}
}
//now catch stupidity and bring it to defaults
if (params.mask == 0) params.mask = 0xFFFFFFFF;
if (fabs(params.scale) < 0.00000001) params.scale = 1.0f;
if (params.stride < 1) params.stride = 1;
}
}
void NewGraphDialog::loadMessages()
{
ui->cbMessages->clear();
if (dbcHandler == NULL) return;
for (int x = 0; x < dbcHandler->dbc_messages.count(); x++)
if (dbcHandler->getFileCount() == 0) dbcHandler->createBlankFile();
for (int x = 0; x < dbcHandler->getFileByIdx(0)->messageHandler->getCount(); x++)
{
ui->cbMessages->addItem(dbcHandler->dbc_messages[x].name);
ui->cbMessages->addItem(dbcHandler->getFileByIdx(0)->messageHandler->findMsgByIdx(x)->name);
}
}
@@ -131,13 +164,13 @@ void NewGraphDialog::loadSignals(int idx)
//in the data structure so it should have been possible to just
//look it up based on index but by name is probably safer and this operation
//is not time critical at all.
DBC_MESSAGE *msg = dbcHandler->findMsgByName(ui->cbMessages->currentText());
DBC_MESSAGE *msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByName(ui->cbMessages->currentText());
if (msg == NULL) return;
ui->cbSignals->clear();
for (int x = 0; x < msg->msgSignals.count(); x++)
for (int x = 0; x < msg->sigHandler->getCount(); x++)
{
ui->cbSignals->addItem(msg->msgSignals[x].name);
ui->cbSignals->addItem(msg->sigHandler->findSignalByIdx(x)->name);
}
}
@@ -145,23 +178,24 @@ void NewGraphDialog::fillFormFromSignal(int idx)
{
Q_UNUSED(idx);
GraphParams params;
DBC_MESSAGE *msg = dbcHandler->findMsgByName(ui->cbMessages->currentText());
DBC_MESSAGE *msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByName(ui->cbMessages->currentText());
if (msg == NULL) return;
DBC_SIGNAL *sig = dbcHandler->findSignalByName(msg, ui->cbSignals->currentText());
DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(ui->cbSignals->currentText());
if (sig == NULL) return;
params.graphName = sig->name;
params.ID = msg->ID;
params.bias = sig->bias;
params.scale = sig->factor;
params.stride = 1;
params.mask = (1 << (sig->signalSize)) - 1;
if (sig->valType == SIGNED_INT) params.isSigned = true;
else params.isSigned = false;
//params.bias = sig->bias;
//params.scale = sig->factor;
//params.stride = 1;
//params.mask = (1 << (sig->signalSize)) - 1;
//if (sig->valType == SIGNED_INT) params.isSigned = true;
//else params.isSigned = false;
params.color = ui->colorSwatch->palette().color(QPalette::Button);
/*
if (sig->intelByteOrder)
{
//for this ordering the byte order is reserved and starting byte
@@ -175,6 +209,38 @@ void NewGraphDialog::fillFormFromSignal(int idx)
params.startByte = sig->startBit / 8;
params.endByte = (sig->startBit + sig->signalSize - 1) / 8;
}
*/
setParams(params);
}
void NewGraphDialog::setSignalActive(bool state)
{
if (!state) return;
ui->rbSignalGraph->setChecked(true);
ui->rbStandardGraph->setChecked(false);
ui->cbMessages->setEnabled(true);
ui->cbSignals->setEnabled(true);
ui->cbSigned->setEnabled(false);
ui->txtBias->setEnabled(false);
ui->txtData->setEnabled(false);
ui->txtID->setEnabled(false);
ui->txtMask->setEnabled(false);
ui->txtScale->setEnabled(false);
ui->txtStride->setEnabled(false);
}
void NewGraphDialog::setStandardActive(bool state)
{
if (!state) return;
ui->rbStandardGraph->setChecked(true);
ui->rbSignalGraph->setChecked(false);
ui->cbMessages->setEnabled(false);
ui->cbSignals->setEnabled(false);
ui->cbSigned->setEnabled(true);
ui->txtBias->setEnabled(true);
ui->txtData->setEnabled(true);
ui->txtID->setEnabled(true);
ui->txtMask->setEnabled(true);
ui->txtScale->setEnabled(true);
ui->txtStride->setEnabled(true);
}
+2
View File
@@ -26,6 +26,8 @@ private slots:
void loadMessages();
void loadSignals(int idx);
void fillFormFromSignal(int idx);
void setSignalActive(bool);
void setStandardActive(bool);
private:
Ui::NewGraphDialog *ui;
+25 -1
View File
@@ -7,13 +7,37 @@
<x>0</x>
<y>0</y>
<width>492</width>
<height>309</height>
<height>340</height>
</rect>
</property>
<property name="windowTitle">
<string>Graph Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QRadioButton" name="rbStandardGraph">
<property name="text">
<string>Standard Graph</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbSignalGraph">
<property name="text">
<string>DBC Signal Graph</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1">
<item>
+430
View File
@@ -0,0 +1,430 @@
#include "rangestatewindow.h"
#include "ui_rangestatewindow.h"
#include "mainwindow.h"
#include "utility.h"
RangeStateWindow::RangeStateWindow(const QVector<CANFrame> *frames, QWidget *parent) :
QDialog(parent),
ui(new Ui::RangeStateWindow)
{
ui->setupUi(this);
modelFrames = frames;
ui->graphSignal->xAxis->setRange(0, 8);
ui->graphSignal->yAxis->setRange(-10, 265); //run range a bit outside possible number so they aren't plotted in a hard to see place
ui->graphSignal->xAxis->setVisible(false);
ui->graphSignal->yAxis->setVisible(false);
//ui->graphSignal->axisRect()->setupFullAxesBox();
ui->cbSignalMode->addItem(tr("Big Endian"));
ui->cbSignalMode->addItem(tr("Little Endian"));
ui->cbSignalMode->addItem(tr("Try Both"));
ui->cbSignedMode->addItem(tr("Signed Value"));
ui->cbSignedMode->addItem(tr("Unsigned Value"));
ui->cbSignedMode->addItem(tr("Try Both"));
//lambda expressions used here because these are tiny functions that don't have any reason to be full named functions
//unfortunately the fact that valueChanged is overloaded makes the syntax here HORRIBLE. That sucks.
connect(ui->spinMaxSigSize, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
[=](int newVal)
{
if (newVal < ui->spinMinSigSize->value()) ui->spinMinSigSize->setValue(newVal);
});
connect(ui->spinMinSigSize, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
[=](int newVal)
{
if (newVal > ui->spinMaxSigSize->value()) ui->spinMaxSigSize->setValue(newVal);
});
connect(ui->btnAllFilter, &QAbstractButton::clicked,
[=]()
{
for (int i = 0; i < ui->listFilter->count(); i++)
{
QListWidgetItem *item = ui->listFilter->item(i);
item->setCheckState(Qt::Checked);
idFilters[Utility::ParseStringToNum(item->text())] = true;
}
});
connect(ui->btnNoneFilter, &QAbstractButton::clicked,
[=]()
{
for (int i = 0; i < ui->listFilter->count(); i++)
{
QListWidgetItem *item = ui->listFilter->item(i);
item->setCheckState(Qt::Unchecked);
idFilters[Utility::ParseStringToNum(item->text())] = false;
}
});
connect(ui->listFilter, &QListWidget::itemChanged,
[=](QListWidgetItem *item)
{
bool isChecked = false;
int id = Utility::ParseStringToNum(item->text());
if (item->checkState() == Qt::Checked) isChecked = true;
idFilters[id] = isChecked;
});
connect(ui->btnRecalc, &QAbstractButton::clicked, this, &RangeStateWindow::recalcButton);
connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int)));
connect(ui->listCandidates, &QListWidget::currentRowChanged, this, &RangeStateWindow::clickedSignalList);
}
RangeStateWindow::~RangeStateWindow()
{
delete ui;
}
void RangeStateWindow::showEvent(QShowEvent* event)
{
QDialog::showEvent(event);
readSettings();
refreshFilterList();
}
void RangeStateWindow::closeEvent(QCloseEvent *event)
{
Q_UNUSED(event);
writeSettings();
}
void RangeStateWindow::readSettings()
{
QSettings settings;
if (settings.value("Main/SaveRestorePositions", false).toBool())
{
resize(settings.value("RangeStateView/WindowSize", QSize(765, 615)).toSize());
move(settings.value("RangeStateView/WindowPos", QPoint(50, 50)).toPoint());
}
}
void RangeStateWindow::writeSettings()
{
QSettings settings;
if (settings.value("Main/SaveRestorePositions", false).toBool())
{
settings.setValue("RangeStateView/WindowSize", size());
settings.setValue("RangeStateView/WindowPos", pos());
}
}
void RangeStateWindow::updatedFrames(int numFrames)
{
CANFrame thisFrame;
if (numFrames == -1) //all frames deleted. We don't need to do a thing on this window but erase everything in the filters section
{
ui->listFilter->clear();
idFilters.clear();
}
else if (numFrames == -2) //all new set of frames. Reset
{
refreshFilterList();
}
else //just got some new frames. See if we need to update the filters list. Otherwise nothing to do - no recalc happens until the button is pressed
{
if (numFrames > modelFrames->count()) return;
for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
{
thisFrame = modelFrames->at(i);
if (!idFilters.contains(thisFrame.ID))
{
idFilters.insert(thisFrame.ID, true);
QListWidgetItem* listItem = new QListWidgetItem(Utility::formatNumber(thisFrame.ID), ui->listFilter);
listItem->setFlags(listItem->flags() | Qt::ItemIsUserCheckable); // set checkable flag
listItem->setCheckState(Qt::Checked); //default all filters to be set active
}
}
}
}
void RangeStateWindow::refreshFilterList()
{
int id;
idFilters.clear();
ui->listFilter->clear();
for (int i = 0; i < modelFrames->length(); i++)
{
id = modelFrames->at(i).ID;
if (!idFilters.contains(id))
{
idFilters.insert(id, true);
QListWidgetItem* listItem = new QListWidgetItem(Utility::formatNumber(id), ui->listFilter);
listItem->setFlags(listItem->flags() | Qt::ItemIsUserCheckable); // set checkable flag
listItem->setCheckState(Qt::Checked); //default all filters to be set active
}
}
ui->listFilter->sortItems();
}
void RangeStateWindow::recalcButton()
{
QHash<int, bool>::iterator iter;
int id;
ui->listCandidates->clear();
foundSignals.clear();
for (iter = idFilters.begin(); iter != idFilters.end(); ++iter)
{
if (iter.value() == true)
{
qDebug() << "Processing for ID: " << iter.key();
//so, we're supposed to process this frame ID. We'll need to create a frame cache for it
frameCache.clear();
frameCache.reserve(modelFrames->count()); //block allocate more than enough space
id = iter.key();
for (int j = 0; j < modelFrames->count(); j++)
{
if (modelFrames->at(j).ID == id) frameCache.append(modelFrames->at(j));
}
//now we've got a list with all the same ID. Time to send it off for processing
signalsFactory();
}
}
qDebug() << "Found " << foundSignals.count() << " signals total.";
}
/*
* Uses the settings exposed to the user to generate a set of candidate signals that should be checked.
* The user could specify signal sizes, granularity, endian type and we generate all the permutations from there
* Should process from max to min and stop when a valid signal is found (at least as an option) to declutter a bit.
* Mostly what we're interested in is the largest signal that matches
*/
void RangeStateWindow::signalsFactory()
{
int minSig = ui->spinMinSigSize->value();
int maxSig = ui->spinMaxSigSize->value();
int granularity = ui->spinGranularity->value();
int sigType = ui->cbSignalMode->currentIndex() + 1;
int signedType = ui->cbSignedMode->currentIndex() + 1;
int maxBits = frameCache.at(0).len * 8;
int sens = ui->slideSensitivity->value();
for (int sigSize = maxSig; sigSize >= minSig; sigSize--)
{
for (int startBit = 0; startBit < maxBits; startBit += granularity)
{
if (sigType & 1)
{
if (signedType & 1) processSignal(startBit, sigSize, sens, true, true);
if (signedType & 2) processSignal(startBit, sigSize, sens, true, false);
}
if (sigType & 2)
{
if (signedType & 1) processSignal(startBit, sigSize, sens, false, true);
if (signedType & 2) processSignal(startBit, sigSize, sens, false, false);
}
//have to try both types even with 8 bit and smaller signals
//because they could cross byte boundaries. Could check whether they
//do and not try both types if it is impossible.
}
}
}
/*
* Given the signal we generate the relevant data and figure out whether this signal seems to be a smooth range signal
*/
bool RangeStateWindow::processSignal(int startBit, int bitLength, int sensitivity, bool bigEndian, bool isSigned)
{
qDebug() << "";
qDebug() << "S:" << startBit << " B:" << bitLength << " Sens:" << sensitivity << " Big E:" << bigEndian << " Signed: " << isSigned;
QVector<int> scaledVals;
QVector<int> diff1;
QVector<int> diff2;
int64_t valu;
int64_t highestValue = -1000000000000LL;
int64_t lowestValue = 1000000000000LL;
double multiplier;
int numFrames = frameCache.count();
scaledVals.reserve(frameCache.count());
diff1.reserve(frameCache.count() - 1);
diff2.reserve(frameCache.count() - 2);
int i;
for (i = 0; i < numFrames; i++)
{
valu = Utility::processIntegerSignal(frameCache.at(i).data, startBit, bitLength, !bigEndian, isSigned);
if (valu < lowestValue) lowestValue = valu;
if (valu > highestValue) highestValue = valu;
}
qDebug() << "Min: " << lowestValue << " Max: " << highestValue;
if (lowestValue == highestValue) return false; //a signal that never changes is worthless and not a range signal
//multiplier is calculated such that the range of the signal is scaled down to the value of sensitivity.
//so, if we have a sensitivity of 50 then the range is scaled so that it is 50
//this has the effect of smoothing the data a bit by completely removing the fine detail. It's irrelevant to
//an algorithm like this anyway and just gets in the way.
//Also, we're going to offset by lowest value so that everything is based at a value of 0 as the low end.
//All of this drastically modifies the actual signal but that's OK, we're looking for patterns here not
//being faithful to the signal (what a dirty cheater)
int range = highestValue - lowestValue;
multiplier = (double)sensitivity / (double)range;
for (i = 0; i < numFrames; i++) scaledVals.append((int)((Utility::processIntegerSignal(frameCache.at(i).data, startBit, bitLength, !bigEndian, isSigned) - lowestValue) * multiplier));
for (i = 1; i < numFrames; i++)
{
valu = scaledVals[i-1] - scaledVals[i];
diff1.append(valu);
}
for (i = 1; i < diff1.count(); i++)
{
valu = diff1[i-1] - diff1[i];
diff2.append(valu);
}
//now the differences are all stored so let's go through and see if they seem to suggest a ramping sort of signal or not.
//for a first test lets let through any signal where the acceleration values are all much smaller than the signal range
bool isGood = true;
int comparisonValue = Utility::Lerp(sensitivity / 5, sensitivity / 40, (sensitivity - 10) / 240.0);
qDebug() << "range: " << sensitivity << " comparisonvalue: " << comparisonValue;
int overValues = 0;
for (i = 0; i < diff2.count(); i++)
{
if (abs(diff1[i]) > comparisonValue) overValues++;
}
int maxOvers = Utility::Lerp(4, numFrames / 50.0, 1.0 - ((sensitivity -10) / 240.0));
qDebug() << "Max Overs: " << maxOvers;
if (overValues > maxOvers) isGood = false;
qDebug() << "Is this signal good: " << isGood << " Num overs: " << overValues;
if (isGood)
{
//createGraph(scaledVals);
QString temp;
temp = "ID: " + QString::number(frameCache.at(0).ID, 16) + " startBit: " + QString::number(startBit) + " len: " + QString::number(bitLength);
int64_t foundSig;
foundSig = frameCache.at(0).ID;
foundSig += (int64_t)startBit << 32;
foundSig += (int64_t)bitLength << 40;
if (isSigned)
{
temp += " Signed";
foundSig += (int64_t)1 << 48;
}
else
{
temp += " Unsigned";
}
if (bigEndian)
{
temp += " BigEndian";
foundSig += (int64_t)1 << 49;
}
else
{
temp += " LittleEndian";
}
ui->listCandidates->addItem(temp);
foundSignals.append(foundSig);
}
return isGood;
}
//graphs the vector such that the X axis is just the index into the vector and Y is perfectly graphed within the window
void RangeStateWindow::createGraph(QVector<int> values)
{
int tempVal;
float minval=1000000, maxval = -100000;
int numEntries = values.count();
ui->graphSignal->clearGraphs();
QVector<double> x(numEntries), y(numEntries);
for (int j = 0; j < numEntries; j++)
{
tempVal = values[j];
x[j] = j;
y[j] = tempVal;
if (y[j] < minval) minval = y[j];
if (y[j] > maxval) maxval = y[j];
}
qDebug() << "YMin: " << minval << " YMax: " << maxval;
double ymin, ymax;
ymin = minval;
if (ymin < 0) ymin *= 1.2;
else ymin *= 0.8;
ymax = maxval;
if (ymax < 0) ymax * 0.8;
else ymax *= 1.2;
if (fabs(ymin) < 0.01) ymin -= (ymax / 60.0);
if (fabs(ymax) < 0.01) ymax -= (ymin / 60.0);
qDebug() << "YFm: " << ymin << " YFM: " << ymax;
ui->graphSignal->addGraph();
ui->graphSignal->graph()->setName("Graph");
ui->graphSignal->graph()->setData(x,y);
ui->graphSignal->graph()->setLineStyle(QCPGraph::lsLine); //connect points with lines
QPen graphPen;
graphPen.setColor(Qt::black);
graphPen.setWidth(1);
ui->graphSignal->graph()->setPen(graphPen);
ui->graphSignal->xAxis->setRange(0, numEntries);
ui->graphSignal->yAxis->setRange(ymin, ymax);
ui->graphSignal->replot();
}
void RangeStateWindow::clickedSignalList(int idx)
{
if (idx == -1) return; //just in case...
int id, startBit, bitLength;
bool isSigned = false, isBigEndian = false;
int64_t valu;
valu = foundSignals.at(idx);
id = valu & 0xFFFFFFFFULL;
startBit = (valu >> 32) & 0xFF;
bitLength = (valu >> 40) & 0xFF;
if (valu & (1LL << 48)) isSigned = true;
if (valu & (1LL << 49)) isBigEndian = true;
qDebug() << "I:" << id << " sb:" << startBit << " len:" << bitLength << " signed:" << isSigned << " big:" << isBigEndian;
frameCache.clear();
frameCache.reserve(modelFrames->count()); //block allocate more than enough space
for (int j = 0; j < modelFrames->count(); j++)
{
if (modelFrames->at(j).ID == id) frameCache.append(modelFrames->at(j));
}
int numFrames = frameCache.count();
QVector<int> values;
values.reserve(numFrames);
for (int i = 0; i < numFrames; i++) values.append((int)((Utility::processIntegerSignal(frameCache.at(i).data, startBit, bitLength, !isBigEndian, isSigned))));
createGraph(values);
}
+41
View File
@@ -0,0 +1,41 @@
#ifndef RANGESTATEWINDOW_H
#define RANGESTATEWINDOW_H
#include <QDialog>
#include "can_structs.h"
namespace Ui {
class RangeStateWindow;
}
class RangeStateWindow : public QDialog
{
Q_OBJECT
public:
explicit RangeStateWindow(const QVector<CANFrame> *frames, QWidget *parent = 0);
~RangeStateWindow();
void showEvent(QShowEvent*);
private slots:
void updatedFrames(int);
void recalcButton();
void clickedSignalList(int idx);
private:
Ui::RangeStateWindow *ui;
const QVector<CANFrame> *modelFrames;
QVector<CANFrame> frameCache;
QList<int64_t> foundSignals;
QHash<int, bool> idFilters;
void refreshFilterList();
void closeEvent(QCloseEvent *event);
void readSettings();
void writeSettings();
void signalsFactory();
bool processSignal(int startBit, int bitLength, int sensitivity, bool bigEndian, bool isSigned);
void createGraph(QVector<int> values);
};
#endif // RANGESTATEWINDOW_H
+207
View File
@@ -0,0 +1,207 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RangeStateWindow</class>
<widget class="QDialog" name="RangeStateWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>763</width>
<height>938</height>
</rect>
</property>
<property name="windowTitle">
<string>Range State Window</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_4" stretch="0,2,0,2">
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Candidate Signals:</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listCandidates"/>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Graphical Representation of Selected Signal:</string>
</property>
</widget>
</item>
<item>
<widget class="QCustomPlot" name="graphSignal" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Sensitivity</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="slideSensitivity">
<property name="minimum">
<number>10</number>
</property>
<property name="maximum">
<number>250</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Min Signal Size:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinMinSigSize">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>32</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Max Signal Size:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinMaxSigSize">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>32</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Granularity:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinGranularity">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>32</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Signal Mode:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbSignalMode"/>
</item>
<item>
<widget class="QLabel" name="label_9">
<property name="text">
<string>Signed Mode:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbSignedMode"/>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>ID Filter:</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listFilter"/>
</item>
<item>
<widget class="QPushButton" name="btnAllFilter">
<property name="text">
<string>All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnNoneFilter">
<property name="text">
<string>None</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="btnRecalc">
<property name="text">
<string>Recalculate Candidate Signals</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QCustomPlot</class>
<extends>QWidget</extends>
<header>qcustomplot.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
+148
View File
@@ -0,0 +1,148 @@
#include "scriptcontainer.h"
#include <QJSValueIterator>
#include <QDebug>
ScriptContainer::ScriptContainer()
{
fileName = QString();
filePath = QString();
scriptText = QString();
connect(&timer, SIGNAL(timeout()), this, SLOT(tick()));
}
void ScriptContainer::compileScript()
{
QJSValue result = scriptEngine.evaluate(scriptText, fileName);
errorWidget->clear();
if (result.isError() && errorWidget)
{
errorWidget->addItem("SCRIPT EXCEPTION!");
errorWidget->addItem("Line: " + result.property("lineNumber").toString());
errorWidget->addItem(result.property("message").toString());
errorWidget->addItem("Stack:");
errorWidget->addItem(result.property("stack").toString());
}
else
{
compiledScript = result;
QJSValue hostObj = scriptEngine.newQObject(this);
scriptEngine.globalObject().setProperty("host", hostObj);
setupFunction = scriptEngine.globalObject().property("setup");
gotFrameFunction = scriptEngine.globalObject().property("gotFrame");
tickFunction = scriptEngine.globalObject().property("tick");
if (setupFunction.isCallable())
{
qDebug() << "setup exists";
QJSValue res = setupFunction.call();
if (res.isError())
{
errorWidget->addItem("Error in setup function on line " + res.property("lineNumber").toString());
errorWidget->addItem(res.property("message").toString());
}
}
if (gotFrameFunction.isCallable()) qDebug() << "gotFrame exists";
if (tickFunction.isCallable()) qDebug() << "tick exists";
}
}
void ScriptContainer::setErrorWidget(QListWidget *list)
{
errorWidget = list;
}
void ScriptContainer::setFilter(QJSValue id, QJSValue mask, QJSValue bus)
{
uint32_t idVal = id.toUInt();
uint32_t maskVal = mask.toUInt();
int busVal = bus.toInt();
qDebug() << "Called set filter";
qDebug() << idVal << "*" << maskVal << "*" << busVal;
CANFilter filter;
filter.setFilter(idVal, maskVal, busVal);
filters.append(filter);
}
void ScriptContainer::setTickInterval(QJSValue interval)
{
int intervalValue = interval.toInt();
qDebug() << "called set tick interval with value " << intervalValue;
if (intervalValue > 0)
{
timer.setInterval(intervalValue);
timer.start();
}
else timer.stop();
}
void ScriptContainer::clearFilters()
{
qDebug() << "Called clear filters";
filters.clear();
}
void ScriptContainer::sendFrame(QJSValue bus, QJSValue id, QJSValue length, QJSValue data)
{
CANFrame frame;
frame.extended = false;
frame.ID = id.toInt();
frame.len = length.toInt();
if (frame.len < 0) frame.len = 0;
if (frame.len > 8) frame.len = 8;
if (!data.isArray()) qDebug() << "data isn't an array";
for (int i = 0; i < frame.len; i++)
{
frame.data[i] = (uint8_t)data.property(i).toInt();
}
frame.bus = bus.toInt();
if (frame.bus < 0) frame.bus = 0;
if (frame.bus > 1) frame.bus = 1;
if (frame.ID > 0x7FF) frame.extended = true;
//qDebug() << "Sending frame from script";
emit sendCANFrame(&frame, frame.bus);
}
void ScriptContainer::gotFrame(const CANFrame &frame)
{
if (!gotFrameFunction.isCallable()) return; //nothing to do if we can't even call the function
//qDebug() << "Got frame in script interface";
for (int i = 0; i < filters.length(); i++)
{
if (filters[i].checkFilter(frame.ID, frame.bus))
{
QJSValueList args;
args << frame.bus << frame.ID << frame.len;
QJSValue dataBytes = scriptEngine.newArray(frame.len);
for (int j = 0; j < frame.len; j++) dataBytes.setProperty(j, QJSValue(frame.data[j]));
args.append(dataBytes);
gotFrameFunction.call(args);
return; //as soon as one filter matches we jump out
}
}
}
void ScriptContainer::tick()
{
if (tickFunction.isCallable())
{
//qDebug() << "Calling tick function";
QJSValue res = tickFunction.call();
if (res.isError())
{
errorWidget->addItem("Error in tick function on line " + res.property("lineNumber").toString());
errorWidget->addItem(res.property("message").toString());
}
}
}
+48
View File
@@ -0,0 +1,48 @@
#ifndef SCRIPTCONTAINER_H
#define SCRIPTCONTAINER_H
#include "can_structs.h"
#include "canfilter.h"
#include <QJSEngine>
#include <QTimer>
#include <qlistwidget.h>
class ScriptContainer : public QObject
{
Q_OBJECT
public:
ScriptContainer();
void gotFrame(const CANFrame &frame);
QString fileName;
QString filePath;
QString scriptText;
public slots:
void compileScript();
void setErrorWidget(QListWidget *list);
void setFilter(QJSValue id, QJSValue mask, QJSValue bus);
void setTickInterval(QJSValue interval);
void clearFilters();
void sendFrame(QJSValue bus, QJSValue id, QJSValue length, QJSValue data);
private slots:
void tick();
signals:
void sendCANFrame(const CANFrame *, int);
private:
QJSEngine scriptEngine;
QJSValue compiledScript;
QJSValue setupFunction;
QJSValue gotFrameFunction;
QJSValue tickFunction;
QTimer timer;
QListWidget *errorWidget;
QList<CANFilter> filters;
};
#endif // SCRIPTCONTAINER_H
+204
View File
@@ -0,0 +1,204 @@
#include "scriptingwindow.h"
#include "ui_scriptingwindow.h"
#include <QFile>
#include <QFileDialog>
#include <Qsci/qscilexerjavascript.h>
#include "mainwindow.h"
ScriptingWindow::ScriptingWindow(const QVector<CANFrame> *frames, QWidget *parent) :
QDialog(parent),
ui(new Ui::ScriptingWindow)
{
ui->setupUi(this);
readSettings();
modelFrames = frames;
connect(ui->btnLoadScript, &QAbstractButton::pressed, this, &ScriptingWindow::loadNewScript);
connect(ui->btnNewScript, &QAbstractButton::pressed, this, &ScriptingWindow::createNewScript);
connect(ui->btnRecompile, &QAbstractButton::pressed, this, &ScriptingWindow::recompileScript);
connect(ui->btnRemoveScript, &QAbstractButton::pressed, this, &ScriptingWindow::deleteCurrentScript);
connect(ui->btnRevertScript, &QAbstractButton::pressed, this, &ScriptingWindow::revertScript);
connect(ui->btnSaveScript, &QAbstractButton::pressed, this, &ScriptingWindow::saveScript);
connect(MainWindow::getReference(), &MainWindow::framesUpdated, this, &ScriptingWindow::updatedFrames);
ui->txtScriptSource->setLexer(new QsciLexerJavaScript(ui->txtScriptSource));
}
ScriptingWindow::~ScriptingWindow()
{
delete ui;
}
void ScriptingWindow::updatedFrames(int numFrames)
{
CANFrame thisFrame;
//-1 means all frames deleted and -2 means a full refresh, neither of which we care about here.
if (numFrames > 0)
{
if (numFrames > modelFrames->count()) return;
qDebug() << "Got frames into script window: " << numFrames;
//for every new frame pass it on to each script container. The container will determine if it needs to actually
//notify the script and do that if applicable.
for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
{
thisFrame = modelFrames->at(i);
for (int j = 0; j < scripts.length(); j++)
{
scripts[j]->gotFrame(thisFrame);
}
}
}
}
void ScriptingWindow::closeEvent(QCloseEvent *event)
{
Q_UNUSED(event);
writeSettings();
}
void ScriptingWindow::readSettings()
{
QSettings settings;
if (settings.value("Main/SaveRestorePositions", false).toBool())
{
resize(settings.value("ScriptingWindow/WindowSize", QSize(860, 650)).toSize());
move(settings.value("ScriptingWindow/WindowPos", QPoint(100, 100)).toPoint());
}
}
void ScriptingWindow::writeSettings()
{
QSettings settings;
if (settings.value("Main/SaveRestorePositions", false).toBool())
{
settings.setValue("ScriptingWindow/WindowSize", size());
settings.setValue("ScriptingWindow/WindowPos", pos());
}
}
void ScriptingWindow::showEvent(QShowEvent* event)
{
QDialog::showEvent(event);
}
void ScriptingWindow::loadNewScript()
{
QString filename;
QFileDialog dialog;
bool result = false;
ScriptContainer *container;
QStringList filters;
filters.append(QString(tr("Javascript File (*.js)")));
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
if (dialog.exec() == QDialog::Accepted)
{
filename = dialog.selectedFiles()[0];
if (dialog.selectedNameFilter() == filters[0])
{
QFile scriptFile(filename);
if (scriptFile.open(QIODevice::ReadOnly))
{
QString contents = scriptFile.readAll();
scriptFile.close();
QStringList fileList = filename.split('/');
QString justFileName = fileList[fileList.length() - 1];
ui->listLoadedScripts->addItem(justFileName);
container = new ScriptContainer();
container->fileName = justFileName;
container->filePath = filename;
container->scriptText = contents;
container->setErrorWidget(ui->listErrors);
container->compileScript();
connect(container, &ScriptContainer::sendCANFrame, this, &ScriptingWindow::sendCANFrame);
scripts.append(container);
currentScript = container;
ui->txtScriptSource->setText(container->scriptText);
ui->txtScriptSource->setEnabled(true);
}
}
}
}
void ScriptingWindow::createNewScript()
{
ScriptContainer *container;
container = new ScriptContainer();
container->fileName = "UNNAMED_" + QString::number((qrand() % 10000)) + ".js";
container->filePath = QString();
container->scriptText = QString();
container->setErrorWidget(ui->listErrors);
connect(container, &ScriptContainer::sendCANFrame, this, &ScriptingWindow::sendCANFrame);
scripts.append(container);
ui->listLoadedScripts->addItem(container->fileName);
currentScript = container;
ui->txtScriptSource->setText(container->scriptText);
ui->txtScriptSource->setEnabled(true);
}
void ScriptingWindow::deleteCurrentScript()
{
}
void ScriptingWindow::refreshSourceWindow()
{
ui->txtScriptSource->setText(currentScript->scriptText);
}
void ScriptingWindow::saveScript()
{
QString filename;
QFileDialog dialog(this);
QStringList filters;
filters.append(QString(tr("Javascript File (*.js)")));
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
dialog.setAcceptMode(QFileDialog::AcceptSave);
if (dialog.exec() == QDialog::Accepted)
{
filename = dialog.selectedFiles()[0];
if (!filename.contains('.')) filename += ".js";
if (dialog.selectedNameFilter() == filters[0])
{
QFile *outFile = new QFile(filename);
if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text))
{
delete outFile;
return;
}
outFile->write(ui->txtScriptSource->text().toUtf8());
outFile->close();
delete outFile;
}
}
}
void ScriptingWindow::revertScript()
{
}
void ScriptingWindow::recompileScript()
{
currentScript->scriptText = ui->txtScriptSource->text();
currentScript->compileScript();
}
+51
View File
@@ -0,0 +1,51 @@
#ifndef SCRIPTINGWINDOW_H
#define SCRIPTINGWINDOW_H
#include "can_structs.h"
#include "scriptcontainer.h"
#include <QDialog>
#include <QJSEngine>
namespace Ui {
class ScriptingWindow;
}
class ScriptingWindow : public QDialog
{
Q_OBJECT
public:
explicit ScriptingWindow(const QVector<CANFrame> *frames, QWidget *parent = 0);
void showEvent(QShowEvent*);
~ScriptingWindow();
private slots:
void loadNewScript();
void createNewScript();
void deleteCurrentScript();
void refreshSourceWindow();
void saveScript();
void revertScript();
void recompileScript();
void updatedFrames(int);
public slots:
signals:
void sendCANFrame(const CANFrame *, int);
private:
void closeEvent(QCloseEvent *event);
void readSettings();
void writeSettings();
Ui::ScriptingWindow *ui;
QList<ScriptContainer *> scripts;
ScriptContainer *currentScript;
const QVector<CANFrame> *modelFrames;
};
#endif // SCRIPTINGWINDOW_H
+128
View File
@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ScriptingWindow</class>
<widget class="QDialog" name="ScriptingWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>862</width>
<height>651</height>
</rect>
</property>
<property name="windowTitle">
<string>Scripting Interface</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,4">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Loaded Scripts</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listLoadedScripts"/>
</item>
<item>
<widget class="QPushButton" name="btnLoadScript">
<property name="text">
<string>Load New Script</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnNewScript">
<property name="text">
<string>Create New Script</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnRemoveScript">
<property name="text">
<string>Remove Current Script</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,10,0,0,4">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Script Editing Window</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QsciScintilla" name="txtScriptSource">
<property name="toolTip">
<string/>
</property>
<property name="whatsThis">
<string/>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="btnSaveScript">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnRevertScript">
<property name="text">
<string>Revert</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnRecompile">
<property name="text">
<string>Recompile</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Current Scripting Errors</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listErrors"/>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QsciScintilla</class>
<extends>QFrame</extends>
<header>Qsci/qsciscintilla.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
+83 -38
View File
@@ -12,15 +12,15 @@ SerialWorker::SerialWorker(CANFrameModel *model, QObject *parent) : QObject(pare
rx_step = 0;
buildFrame = new CANFrame;
canModel = model;
gotFrames = 0;
ticker = NULL;
elapsedTime = NULL;
framesPerSec = 0;
framesRapid = 0;
capturing = true;
gotValidated = true;
isAutoRestart = false;
targetID = -1;
txTimestampBasis = QDateTime::currentMSecsSinceEpoch();
readSettings();
}
@@ -40,6 +40,16 @@ SerialWorker::~SerialWorker()
if (ticker != NULL) ticker->stop();
}
void SerialWorker::run()
{
ticker = new QTimer;
connect(ticker, SIGNAL(timeout()), this, SLOT(handleTick()));
ticker->setInterval(250); //tick four times per second
ticker->setSingleShot(false); //keep ticking
ticker->start();
}
void SerialWorker::readSettings()
{
QSettings settings;
@@ -105,24 +115,16 @@ void SerialWorker::setSerialPort(QSerialPortInfo *port)
output.append((char)0xF1); //yet another command
output.append((char)0x09); //comm validation command
output.append((char)0xF1); //and another command
output.append((char)0x01); //Time Sync - Not implemented until 333 but we can try
continuousTimeSync = true;
serial->write(output);
if (doValidation) connected = false;
else connected = true;
connect(serial, SIGNAL(readyRead()), this, SLOT(readSerialData()));
if (doValidation) QTimer::singleShot(1000, this, SLOT(connectionTimeout()));
if (ticker == NULL)
{
ticker = new QTimer;
connect(ticker, SIGNAL(timeout()), this, SLOT(handleTick()));
}
if (elapsedTime == NULL)
{
elapsedTime = new QTime;
elapsedTime->start();
}
ticker->setInterval(250); //tick four times per second
ticker->setSingleShot(false); //keep ticking
ticker->start();
}
void SerialWorker::connectionTimeout()
@@ -132,7 +134,7 @@ void SerialWorker::connectionTimeout()
{
//then emit the the failure signal and see if anyone cares
qDebug() << "Failed to connect to GVRET at that com port";
ticker->stop();
//ticker->stop();
closeSerialPort(); //make sure it's properly closed anyway
emit connectionFailure();
}
@@ -148,6 +150,11 @@ void SerialWorker::readSerialData()
c = data.at(i);
procRXChar(c);
}
if (framesRapid > 0)
{
emit frameUpdateRapid(framesRapid);
framesRapid = 0;
}
}
void SerialWorker::sendFrame(const CANFrame *frame, int bus = 0)
@@ -155,9 +162,18 @@ void SerialWorker::sendFrame(const CANFrame *frame, int bus = 0)
QByteArray buffer;
int c;
int ID;
CANFrame tempFrame = *frame;
tempFrame.isReceived = false;
tempFrame.timestamp = ((QDateTime::currentMSecsSinceEpoch() - txTimestampBasis) * 1000);
//qDebug() << "Sending out frame with id " << frame->ID;
//show our sent frames in the list too. This happens even if we're not connected.
canModel->addFrame(tempFrame, false);
framesRapid++;
if (serial == NULL) return;
if (!serial->isOpen()) return;
if (!connected) return;
ID = frame->ID;
@@ -177,8 +193,6 @@ void SerialWorker::sendFrame(const CANFrame *frame, int bus = 0)
}
buffer[8 + frame->len] = 0;
if (serial == NULL) return;
if (!serial->isOpen()) return;
//qDebug() << "writing " << buffer.length() << " bytes to serial port";
serial->write(buffer);
}
@@ -188,7 +202,9 @@ void SerialWorker::sendFrame(const CANFrame *frame, int bus = 0)
//buffers and besides, the other end will get buried in traffic.
void SerialWorker::sendFrameBatch(const QList<CANFrame> *frames)
{
sendBulkMutex.lock();
for (int i = 0; i < frames->length(); i++) sendFrame(&frames->at(i), frames->at(i).bus);
sendBulkMutex.unlock();
}
void SerialWorker::updateBaudRates(int Speed1, int Speed2)
@@ -225,8 +241,9 @@ void SerialWorker::procRXChar(unsigned char c)
rx_state = BUILD_CAN_FRAME;
rx_step = 0;
break;
case 1: //we don't accept time sync commands from the firmware
rx_state = IDLE;
case 1: //time sync
rx_state = TIME_SYNC;
rx_step = 0;
break;
case 2: //process a return reply for digital input states.
rx_state = GET_DIG_INPUTS;
@@ -306,8 +323,11 @@ void SerialWorker::procRXChar(unsigned char c)
//qDebug() << "emit from serial handler to main form id: " << buildFrame->ID;
if (capturing)
{
buildFrame->isReceived = true;
canModel->addFrame(*buildFrame, false);
gotFrames++;
//take the time the frame came in and try to resync the time base.
if (continuousTimeSync) txTimestampBasis = QDateTime::currentMSecsSinceEpoch() - (buildFrame->timestamp / 1000);
framesRapid++;
if (buildFrame->ID == targetID) emit gotTargettedFrame(canModel->rowCount() - 1);
}
}
@@ -315,6 +335,29 @@ void SerialWorker::procRXChar(unsigned char c)
}
rx_step++;
break;
case TIME_SYNC: //gives a pretty good base guess for the proper timestamp. Can be refined when traffic starts to flow (if wanted)
switch (rx_step)
{
case 0:
buildTimeBasis = c;
break;
case 1:
buildTimeBasis += ((uint32_t)c << 8);
break;
case 2:
buildTimeBasis += ((uint32_t)c << 16);
break;
case 3:
buildTimeBasis += ((uint32_t)c << 24);
qDebug() << "GVRET firmware reports timestamp of " << buildTimeBasis;
txTimestampBasis = QDateTime::currentMSecsSinceEpoch() - ((uint64_t)buildTimeBasis / (uint64_t)1000ull);
continuousTimeSync = false;
rx_state = IDLE;
break;
}
rx_step++;
break;
case GET_ANALOG_INPUTS: //get 9 bytes - 2 per analog input plus checksum
switch (rx_step)
{
@@ -402,9 +445,6 @@ void SerialWorker::procRXChar(unsigned char c)
}
rx_step++;
break;
case TIME_SYNC:
rx_state = IDLE;
break;
case SET_DIG_OUTPUTS:
rx_state = IDLE;
break;
@@ -422,23 +462,23 @@ void SerialWorker::handleTick()
{
//qDebug() << "Tick!";
if (!gotValidated)
if (connected)
{
if (serial->isOpen()) //if it's still false we have a problem...
if (!gotValidated && doValidation)
{
qDebug() << "Comm validation failed. ";
closeSerialPort(); //start by stopping everything.
QTimer::singleShot(500, this, SLOT(handleReconnect()));
return;
if (serial == NULL) return;
if (serial->isOpen()) //if it's still false we have a problem...
{
qDebug() << "Comm validation failed. ";
closeSerialPort(); //start by stopping everything.
//Then wait 500ms and restart the connection automatically
QTimer::singleShot(500, this, SLOT(handleReconnect()));
return;
}
}
}
framesPerSec += gotFrames * 1000 / elapsedTime->elapsed() - (framesPerSec / 4);
elapsedTime->restart();
emit frameUpdateTick(framesPerSec / 4, gotFrames); //sends stats to interested parties
canModel->sendBulkRefresh(gotFrames);
gotFrames = 0;
if (doValidation) sendCommValidation();
if (doValidation && serial && serial->isOpen()) sendCommValidation();
}
void SerialWorker::handleReconnect()
@@ -454,6 +494,10 @@ void SerialWorker::sendCommValidation()
gotValidated = false;
output.append((char)0xF1); //another command to the GVRET
output.append((char)0x09); //request a reply to get validation
//send it twice for good measure.
output.append((char)0xF1); //another command to the GVRET
output.append((char)0x09); //request a reply to get validation
serial->write(output);
}
@@ -467,7 +511,8 @@ void SerialWorker::closeSerialPort()
serial->close();
}
serial->disconnect();
ticker->stop();
//do not stop the ticker here. It always stays running now.
//ticker->stop();
delete serial;
serial = NULL;
}
+9 -6
View File
@@ -40,8 +40,8 @@ public:
void targetFrameID(int);
signals: //we emit signals
void error(const QString &);
void frameUpdateTick(int, int); //update interested parties about the # of frames that have come in
void error(const QString &);
void frameUpdateRapid(int); //sent *much* more rapidly than the above signal - one param for # of frames
void connectionSuccess(int, int);
void connectionFailure();
void deviceInfo(int, int);
@@ -54,6 +54,7 @@ private slots: //we receive things in slots
void handleReconnect();
public slots:
void run();
void setSerialPort(QSerialPortInfo*);
void closeSerialPort();
void sendFrame(const CANFrame *, int);
@@ -70,13 +71,13 @@ private:
bool doValidation;
bool gotValidated;
bool isAutoRestart;
bool continuousTimeSync;
QSerialPort *serial;
QSerialPortInfo *currentPort;
CANFrameModel *canModel;
QTimer *ticker;
QTime *elapsedTime;
int framesPerSec;
int gotFrames;
QTimer *ticker;
QMutex sendBulkMutex;
int framesRapid;
int targetID;
STATE rx_state;
int rx_step;
@@ -85,6 +86,8 @@ private:
bool can0Enabled, can1Enabled;
int deviceBuildNum;
int deviceSingleWireMode;
uint64_t txTimestampBasis;
uint32_t buildTimeBasis;
void procRXChar(unsigned char);
void sendCommValidation();
+222
View File
@@ -0,0 +1,222 @@
#include "udsscanwindow.h"
#include "ui_udsscanwindow.h"
#include "mainwindow.h"
UDSScanWindow::UDSScanWindow(const QVector<CANFrame> *frames, QWidget *parent) :
QDialog(parent),
ui(new Ui::UDSScanWindow)
{
ui->setupUi(this);
modelFrames = frames;
currentlyRunning = false;
waitTimer = new QTimer;
waitTimer->setInterval(100);
connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int)));
connect(MainWindow::getReference(), SIGNAL(frameUpdateRapid(int)), this, SLOT(rapidFrames(int)));
connect(ui->btnScan, &QPushButton::clicked, this, &UDSScanWindow::scanUDS);
connect(waitTimer, &QTimer::timeout, this, &UDSScanWindow::timeOut);
ui->cbBuses->addItem("0");
ui->cbBuses->addItem("1");
ui->cbBuses->addItem("Both");
}
UDSScanWindow::~UDSScanWindow()
{
delete ui;
waitTimer->stop();
delete waitTimer;
}
void UDSScanWindow::scanUDS()
{
if (currentlyRunning)
{
waitTimer->stop();
sendingFrames.clear();
currentlyRunning = false;
ui->btnScan->setText("Start Scan");
}
ui->listResults->clear();
sendingFrames.clear();
CANFrame frame;
int typ, id;
int startID, endID;
startID = Utility::ParseStringToNum(ui->txtStartID->text());
endID = Utility::ParseStringToNum(ui->txtEndID->text());
int buses = ui->cbBuses->currentIndex();
buses++;
if (buses < 1) buses = 1;
//start out by sending tester present to every address to see if anyone replies
for (id = startID; id <= endID; id++)
{
frame.ID = id;
frame.len = 8;
frame.extended = false;
frame.data[0] = 2;
frame.data[1] = 0x3E; //tester present
frame.data[2] = 0;
frame.data[3] = 0;frame.data[4] = 0;frame.data[5] = 0;
frame.data[6] = 0;frame.data[7] = 0;
if (buses & 1)
{
frame.bus = 0;
sendingFrames.append(frame);
}
if (buses & 2)
{
frame.bus = 1;
sendingFrames.append(frame);
}
}
//then try asking for the various diagnostic session types
for (typ = 1; typ < 5; typ++)
{
for (id = startID; id <= endID; id++)
{
frame.ID = id;
frame.len = 8;
frame.extended = false;
frame.data[0] = 2;
frame.data[1] = 0x10;
frame.data[2] = typ;
frame.data[3] = 0;frame.data[4] = 0;frame.data[5] = 0;
frame.data[6] = 0;frame.data[7] = 0;
if (buses & 1)
{
frame.bus = 0;
sendingFrames.append(frame);
}
if (buses & 2)
{
frame.bus = 1;
sendingFrames.append(frame);
}
}
}
waitTimer->start();
currIdx = -1;
currentlyRunning = true;
ui->btnScan->setText("Abort Scan");
sendNextMsg();
}
//Updates here are sent about every 1/4 second. That's fine for most windows but not this one.
void UDSScanWindow::updatedFrames(int numFrames)
{
if (numFrames == -1) //all frames deleted. We don't care
{
}
else if (numFrames == -2) //all new set of frames. Don't care.
{
}
else //just got some new frames. See if they are relevant.
{
}
}
//Updates here are nearly once per millisecond if there is heavy traffic. That's more like it!
void UDSScanWindow::rapidFrames(int numFrames)
{
CANFrame thisFrame;
QString result;
int id;
int offset = ui->spinReplyOffset->value();
CANFrame sentFrame;
bool gotReply = false;
if (numFrames > modelFrames->count()) return;
int numSending = sendingFrames.length();
if (numSending == 0) return;
if (currIdx >= numSending) return;
sentFrame = sendingFrames[currIdx];
for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
{
if (currIdx >= numSending) return;
thisFrame = modelFrames->at(i);
id = thisFrame.ID;
if ((id == (sentFrame.ID + offset)) || ui->cbAllowAdaptiveOffset->isChecked())
{
int temp = thisFrame.data[0] >> 4;
if (temp == 0) //single frame reply (maybe)
{
if (thisFrame.data[1] == 0x40 + sendingFrames[currIdx].data[1])
{
result = "Request on bus " + QString::number(sentFrame.bus) + " ID: " + QString::number(sentFrame.ID, 16) + " got response to mode "
+ QString::number(sentFrame.data[1], 16)
+ " " + QString::number(sentFrame.data[2], 16) + " with affirmation from ID " + QString::number(id, 16)
+ " on bus " + QString::number(thisFrame.bus) + ".";
gotReply = true;
}
else if ( thisFrame.data[1] == 0x7F)
{
result = "Request on bus " + QString::number(sentFrame.bus) + " ID: " + QString::number(sentFrame.ID, 16) + " got response to mode "
+ QString::number(sentFrame.data[1], 16)
+ " " + QString::number(sentFrame.data[2], 16) + " with an error from ID " + QString::number(id, 16)
+ " on bus " + QString::number(thisFrame.bus) + ".";
gotReply = true;
}
}
if (temp == 1) //start of a multiframe reply
{
if (thisFrame.data[2] == 0x40 + sendingFrames[currIdx].data[1])
{
result = "Request on bus " + QString::number(sentFrame.bus) + " ID: " + QString::number(sentFrame.ID, 16) + " got response to mode "
+ QString::number(sentFrame.data[1], 16)
+ " " + QString::number(sentFrame.data[2], 16) + " with affirmation from ID " + QString::number(id, 16)
+ " on bus " + QString::number(thisFrame.bus) + ".";
gotReply = true;
}
//error replies are never multiframe so the check doesn't have to be done here.
}
}
}
if (gotReply)
{
ui->listResults->addItem(result);
sendNextMsg();
}
}
void UDSScanWindow::timeOut()
{
QString result;
result = "Request on bus " + QString::number(sendingFrames[currIdx].bus) + " ID: " + QString::number(sendingFrames[currIdx].ID, 16) + " got no response to mode "
+ QString::number(sendingFrames[currIdx].data[1], 16) + " " + QString::number(sendingFrames[currIdx].data[2], 16);
ui->listResults->addItem(result);
sendNextMsg();
}
void UDSScanWindow::sendNextMsg()
{
currIdx++;
if (currIdx < sendingFrames.count())
{
emit sendCANFrame(&sendingFrames[currIdx], sendingFrames[currIdx].bus);
waitTimer->start();
}
else
{
waitTimer->stop();
ui->btnScan->setText("Start Scan");
currentlyRunning = false;
}
}
+40
View File
@@ -0,0 +1,40 @@
#ifndef UDSSCANWINDOW_H
#define UDSSCANWINDOW_H
#include "can_structs.h"
#include <QDialog>
namespace Ui {
class UDSScanWindow;
}
class UDSScanWindow : public QDialog
{
Q_OBJECT
public:
explicit UDSScanWindow(const QVector<CANFrame> *frames, QWidget *parent = 0);
~UDSScanWindow();
signals:
void sendCANFrame(const CANFrame *, int);
private slots:
void updatedFrames(int numFrames);
void rapidFrames(int numFrames);
void scanUDS();
void timeOut();
private:
Ui::UDSScanWindow *ui;
const QVector<CANFrame> *modelFrames;
QTimer *waitTimer;
QList<CANFrame> sendingFrames;
int currIdx = 0;
bool currentlyRunning;
void sendNextMsg();
};
#endif // UDSSCANWINDOW_H
+115
View File
@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UDSScanWindow</class>
<widget class="QDialog" name="UDSScanWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>742</width>
<height>527</height>
</rect>
</property>
<property name="windowTitle">
<string>UDS Scanner</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Results</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listResults"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Starting ID</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txtStartID">
<property name="text">
<string>0x7E0</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Ending ID</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txtEndID">
<property name="text">
<string>0x7E7</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Reply Offset:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinReplyOffset">
<property name="minimum">
<number>-128</number>
</property>
<property name="maximum">
<number>128</number>
</property>
<property name="value">
<number>8</number>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbAllowAdaptiveOffset">
<property name="text">
<string>Allow adaptive reply offset</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Buses to scan</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbBuses"/>
</item>
<item>
<widget class="QPushButton" name="btnScan">
<property name="text">
<string>Start Scan</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+72
View File
@@ -73,6 +73,17 @@ public:
else return formatHexNum(value);
}
static QString formatByteAsBinary(uint8_t value)
{
QString output;
for (int b = 7; b >= 0; b--)
{
if (value & (1 << b)) output += "1";
else output += "0";
}
return output;
}
//parses the input string to grab as much of it as possible while staying alpha numeric
static QString grabAlphaNumeric(QString &input)
{
@@ -113,6 +124,67 @@ public:
{
return (value1 * (1.0 - samplePoint)) + (value2 * samplePoint);
}
static int64_t processIntegerSignal(const uint8_t *data, int startBit, int sigSize, bool littleEndian, bool isSigned)
{
int64_t result = 0;
int bit;
if (littleEndian)
{
bit = startBit;
for (int bitpos = 0; bitpos < sigSize; bitpos++)
{
if (data[bit / 8] & (1 << (bit % 8)))
result += (1ULL << bitpos);
bit++;
}
}
else //motorola / big endian mode
{
bit = startBit;
for (int bitpos = 0; bitpos < sigSize; bitpos++)
{
if (data[bit / 8] & (1 << (bit % 8)))
result += (1ULL << (sigSize - bitpos - 1));
if ((bit % 8) == 0)
bit += 15;
else bit--;
}
}
if (isSigned)
{
int64_t mask = (1ULL << (sigSize - 1));
if ((result & mask) == mask) //is the highest bit possible for this signal size set?
{
/*
* if so we need to also set every bit higher in the result int too.
* This leads to the below two lines that are nasty. Here's the theory behind that...
* If the value is signed and the highest bit is set then it is negative. To create
* a negative value out of this even though the variable result is 64 bit we have to
* run 1's all of the way up to bit 63 in result. -1 is all ones for whatever size integer
* you have. So, it's 64 1's in this case.
* signedMask is done this way:
* first you take the signal size and shift 1 up that far. Then subtract one. Lets
* see that for a 16 bit signal:
* (1 << 16) - 1 = the first 16 bits set as 1's. So far so good. We then negate the whole
* thing which flips all bits. Thus signedMask ends up with 1's everwhere that the signal
* doesn't take up in the 64 bit signed integer result. Then, result has an OR operation on
* it with the old value and -1 masked so that the the 1 bits from -1 don't overwrite bits from the
* actual signal. This extends the sign bits out so that the integer result reads as the proper negative
* value. We dont need to do any of this if the sign bit wasn't set.
*/
int64_t signedMask = ~((1ULL << sigSize) - 1);
result = (-1LL & signedMask) | result;
}
}
return result;
}
};
#endif // UTILITY_H