Merge branch 'WIP2' into WIP

Conflicts:
	SavvyCAN.pro
	config.h
	mainwindow.cpp
	mainwindow.h
	mainwindow.ui

Resolved conflicts between branches
This commit is contained in:
Collin Kidder
2015-12-20 21:29:23 -05:00
27 changed files with 1818 additions and 186 deletions
+10
View File
@@ -27,3 +27,13 @@ of this program. It can load and save in several formats:
5. Generic CSV file (ID,D0 D1 D2 D3 D4 D5 D6 D7)
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.
+15 -5
View File
@@ -6,11 +6,11 @@
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets serialport printsupport network
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,11 @@ SOURCES += main.cpp\
mainsettingsdialog.cpp \
firmwareuploaderwindow.cpp \
discretestatewindow.cpp \
connectionwindow.cpp
connectionwindow.cpp \
scriptingwindow.cpp \
scriptcontainer.cpp \
canfilter.cpp \
rangestatewindow.cpp
HEADERS += mainwindow.h \
can_structs.h \
@@ -63,7 +67,11 @@ HEADERS += mainwindow.h \
mainsettingsdialog.h \
firmwareuploaderwindow.h \
discretestatewindow.h \
connectionwindow.h
connectionwindow.h \
scriptingwindow.h \
scriptcontainer.h \
canfilter.h \
rangestatewindow.h
FORMS += mainwindow.ui \
graphingwindow.ui \
@@ -79,7 +87,9 @@ FORMS += mainwindow.ui \
mainsettingsdialog.ui \
firmwareuploaderwindow.ui \
discretestatewindow.ui \
connectionwindow.ui
connectionwindow.ui \
scriptingwindow.ui \
rangestatewindow.ui
DISTFILES +=
+1
View File
@@ -9,6 +9,7 @@ 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;
+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
+40 -25
View File
@@ -17,7 +17,7 @@ int CANFrameModel::totalFrameCount()
int CANFrameModel::columnCount(const QModelIndex &index) const
{
Q_UNUSED(index);
return 6;
return 7;
}
CANFrameModel::CANFrameModel(QObject *parent)
@@ -159,7 +159,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 +168,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 +176,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]));
@@ -234,12 +238,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 +258,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 +287,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 +308,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();
}
}
@@ -383,6 +392,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 +412,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 145
//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
+4 -56
View File
@@ -3,6 +3,7 @@
#include <QFile>
#include <QRegularExpression>
#include <QDebug>
#include "utility.h"
DBCHandler::DBCHandler(QObject *parent) : QObject(parent)
{
@@ -538,8 +539,7 @@ QString DBCHandler::processSignal(const CANFrame &frame, const DBC_SIGNAL &sig)
{
int64_t result = 0;
int bit;
int sigSize;
bool isSigned = false;
if (sig.valType == STRING)
{
QString buildString;
@@ -549,60 +549,8 @@ QString DBCHandler::processSignal(const CANFrame &frame, const DBC_SIGNAL &sig)
return buildString;
}
sigSize = sig.signalSize;
if (sig.intelByteOrder)
{
bit = sig.startBit;
for (int bitpos = 0; bitpos < sigSize; bitpos++)
{
if (frame.data[bit / 8] & (1 << (bit % 8)))
result += (1ULL << bitpos);
bit++;
}
}
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;
}
}
if (sig.valType == SIGNED_INT) isSigned = true;
result = Utility::processIntegerSignal(frame.data, sig.startBit, sig.signalSize, sig.intelByteOrder, isSigned);
double endResult = ((double)result * sig.factor) + sig.bias;
result = (int) endResult;
+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>
+227 -13
View File
@@ -19,6 +19,7 @@ QString FrameFileIO::loadFrameFile(QVector<CANFrame>* frameCache)
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("Vector trace files (*.trace)")));
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilters(filters);
@@ -43,6 +44,7 @@ QString FrameFileIO::loadFrameFile(QVector<CANFrame>* 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);
progress.cancel();
@@ -92,7 +94,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 +121,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 +174,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 +206,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 +216,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 +245,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;
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 +285,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 +306,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 +477,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 +486,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 +543,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 ");
@@ -547,7 +580,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 +611,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 +675,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 +693,182 @@ 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;
}
+2
View File
@@ -27,11 +27,13 @@ public:
static bool loadGenericCSVFile(QString, QVector<CANFrame>*);
static bool loadLogFile(QString, QVector<CANFrame>*);
static bool loadMicrochipFile(QString, QVector<CANFrame>*);
static bool loadTraceFile(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 bool saveTraceFile(QString, const QVector<CANFrame>*);
static QString loadFrameFile(QVector<CANFrame>*);
};
+2
View File
@@ -388,6 +388,7 @@ void FramePlaybackWindow::btnLoadFile()
currentSeqNum = 0;
currentSeqItem = &seqItems[0];
}
refreshIDList();
updateFrameLabel();
}
}
@@ -416,6 +417,7 @@ void FramePlaybackWindow::btnLoadLive()
currentSeqNum = 0;
currentSeqItem = &seqItems[0];
}
refreshIDList();
updateFrameLabel();
btnStopClick();
}
+83 -39
View File
@@ -49,15 +49,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,6 +64,7 @@ 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(this, &MainWindow::updateBaudRates, worker, &SerialWorker::updateBaudRates, Qt::QueuedConnection);
@@ -91,46 +90,50 @@ MainWindow::MainWindow(QWidget *parent) :
firmwareUploaderWindow = NULL;
discreteStateWindow = NULL;
connectionWindow = NULL;
scriptingWindow = NULL;
rangeWindow = NULL;
dbcHandler = new DBCHandler;
bDirty = false;
inhibitFilterUpdate = false;
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->btnBaudSet, &QAbstractButton::clicked, this, &MainWindow::changeBaudRates);
connect(ui->actionFlow_View, &QAction::triggered, this, &MainWindow::showFlowViewWindow);
connect(ui->action_Custom, &QAction::triggered, this, &MainWindow::showFrameSenderWindow);
connect(ui->actionLoad_DBC_File, &QAction::triggered, this, &MainWindow::handleLoadDBC);
connect(ui->actionSave_DBC_File, &QAction::triggered, this, &MainWindow::handleSaveDBC);
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->actionEdit_Messages_Signals, &QAction::triggered, this, &MainWindow::showDBCEditor);
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);
lbStatusConnected.setText(tr("Not connected"));
updateFileStatus();
@@ -224,6 +227,18 @@ MainWindow::~MainWindow()
delete connectionWindow;
}
if (scriptingWindow)
{
scriptingWindow->close();
delete scriptingWindow;
}
if (rangeWindow)
{
rangeWindow->close();
delete rangeWindow;
}
delete ui;
delete dbcHandler;
model->clearFrames();
@@ -492,6 +507,7 @@ void MainWindow::handleLoadFile()
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("Vector Trace Files (*.trace)")));
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilters(filters);
@@ -520,13 +536,13 @@ void MainWindow::handleLoadFile()
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);
if (dialog.selectedNameFilter() == filters[5]) result = FrameFileIO::loadTraceFile(filename, &tempFrames);
progress.cancel();
if (result)
{
model->insertFrames(tempFrames);
QStringList fileList = filename.split('/');
loadedFileName = fileList[fileList.length() - 1];
@@ -552,6 +568,7 @@ void MainWindow::handleSaveFile()
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("Vector Trace Files (*.trace)")));
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setNameFilters(filters);
@@ -599,6 +616,12 @@ void MainWindow::handleSaveFile()
result = FrameFileIO::saveMicrochipFile(filename, frames);
}
if (dialog.selectedNameFilter() == filters[5])
{
if (!filename.contains('.')) filename += ".trace";
result = FrameFileIO::saveTraceFile(filename, frames);
}
progress.cancel();
if (result)
@@ -622,6 +645,7 @@ void MainWindow::handleSaveFilteredFile()
filters.append(QString(tr("Generic ID/Data CSV (*.csv)")));
filters.append(QString(tr("BusMaster Log (*.log)")));
filters.append(QString(tr("Microchip Log (*.log)")));
filters.append(QString(tr("Vector Trace Files (*.trace)")));
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setNameFilters(filters);
@@ -669,6 +693,12 @@ void MainWindow::handleSaveFilteredFile()
result = FrameFileIO::saveMicrochipFile(filename, frames);
}
if (dialog.selectedNameFilter() == filters[5])
{
if (!filename.contains('.')) filename += ".trace";
result = FrameFileIO::saveTraceFile(filename, frames);
}
progress.cancel();
if (result)
@@ -988,7 +1018,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 +1064,23 @@ void MainWindow::showSingleMultiWindow()
discreteStateWindow->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()
+11
View File
@@ -20,6 +20,8 @@
#include "firmwareuploaderwindow.h"
#include "discretestatewindow.h"
#include "connectionwindow.h"
#include "scriptingwindow.h"
#include "rangestatewindow.h"
namespace Ui {
class MainWindow;
@@ -57,7 +59,11 @@ private slots:
void showComparisonWindow();
void showSettingsDialog();
void showFirmwareUploaderWindow();
<<<<<<< HEAD
void showConnectionSettingsWindow();
=======
void showScriptingWindow();
>>>>>>> WIP2
void exitApp();
void handleSaveDecoded();
void changeBaudRates();
@@ -123,7 +129,12 @@ private:
MainSettingsDialog *settingsDialog;
DiscreteStateWindow *discreteStateWindow;
FirmwareUploaderWindow *firmwareUploaderWindow;
<<<<<<< HEAD
ConnectionWindow *connectionWindow;
=======
ScriptingWindow *scriptingWindow;
RangeStateWindow *rangeWindow;
>>>>>>> WIP2
//various private storage
QLabel lbStatusConnected;
+16 -14
View File
@@ -204,13 +204,12 @@
<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"/>
</widget>
<widget class="QMenu" name="menuSend_Frames">
<property name="title">
@@ -218,7 +217,7 @@
</property>
<addaction name="action_Playback"/>
<addaction name="action_Custom"/>
<addaction name="actionFirmware_Uploader"/>
<addaction name="actionScripting_INterface"/>
</widget>
<widget class="QMenu" name="menuFile">
<property name="title">
@@ -263,16 +262,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>
@@ -366,6 +355,19 @@
<action name="actionConnect">
<property name="text">
<string>Connect</string>
<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>
</widget>
+429
View File
@@ -0,0 +1,429 @@
#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
{
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
+203
View File
@@ -0,0 +1,203 @@
#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)
{
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>
+26 -16
View File
@@ -40,6 +40,19 @@ SerialWorker::~SerialWorker()
if (ticker != NULL) ticker->stop();
}
void SerialWorker::run()
{
ticker = new QTimer;
connect(ticker, SIGNAL(timeout()), this, SLOT(handleTick()));
elapsedTime = new QTime;
elapsedTime->start();
ticker->setInterval(250); //tick four times per second
ticker->setSingleShot(false); //keep ticking
ticker->start();
}
void SerialWorker::readSettings()
{
QSettings settings;
@@ -110,19 +123,6 @@ void SerialWorker::setSerialPort(QSerialPortInfo *port)
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()
@@ -155,9 +155,17 @@ void SerialWorker::sendFrame(const CANFrame *frame, int bus = 0)
QByteArray buffer;
int c;
int ID;
CANFrame tempFrame = *frame;
tempFrame.isReceived = false;
//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);
gotFrames++;
if (serial == NULL) return;
if (!serial->isOpen()) return;
if (!connected) return;
ID = frame->ID;
@@ -177,8 +185,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);
}
@@ -306,6 +312,7 @@ 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++;
if (buildFrame->ID == targetID) emit gotTargettedFrame(canModel->rowCount() - 1);
@@ -422,6 +429,8 @@ void SerialWorker::handleTick()
{
//qDebug() << "Tick!";
if (connected)
{
if (!gotValidated)
{
if (serial->isOpen()) //if it's still false we have a problem...
@@ -432,13 +441,14 @@ void SerialWorker::handleTick()
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) sendCommValidation();
}
void SerialWorker::handleReconnect()
+1
View File
@@ -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);
+61
View File
@@ -113,6 +113,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