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:
@@ -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
@@ -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 +=
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+5
-57
@@ -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;
|
||||
@@ -547,62 +547,10 @@ QString DBCHandler::processSignal(const CANFrame &frame, const DBC_SIGNAL &sig)
|
||||
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 (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
@@ -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>
|
||||
|
||||
+232
-18
@@ -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;
|
||||
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 +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;
|
||||
}
|
||||
|
||||
@@ -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>*);
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
+84
-40
@@ -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);
|
||||
@@ -78,7 +77,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 +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();
|
||||
@@ -223,6 +226,18 @@ MainWindow::~MainWindow()
|
||||
connectionWindow->close();
|
||||
delete connectionWindow;
|
||||
}
|
||||
|
||||
if (scriptingWindow)
|
||||
{
|
||||
scriptingWindow->close();
|
||||
delete scriptingWindow;
|
||||
}
|
||||
|
||||
if (rangeWindow)
|
||||
{
|
||||
rangeWindow->close();
|
||||
delete rangeWindow;
|
||||
}
|
||||
|
||||
delete ui;
|
||||
delete dbcHandler;
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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>
|
||||
+33
-23
@@ -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);
|
||||
}
|
||||
@@ -187,7 +193,7 @@ void SerialWorker::sendFrame(const CANFrame *frame, int bus = 0)
|
||||
//Don't get carried away here. The GVRET firmware only has finite
|
||||
//buffers and besides, the other end will get buried in traffic.
|
||||
void SerialWorker::sendFrameBatch(const QList<CANFrame> *frames)
|
||||
{
|
||||
{
|
||||
for (int i = 0; i < frames->length(); i++) sendFrame(&frames->at(i), frames->at(i).bus);
|
||||
}
|
||||
|
||||
@@ -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,14 +429,17 @@ void SerialWorker::handleTick()
|
||||
{
|
||||
//qDebug() << "Tick!";
|
||||
|
||||
if (!gotValidated)
|
||||
if (connected)
|
||||
{
|
||||
if (serial->isOpen()) //if it's still false we have a problem...
|
||||
if (!gotValidated)
|
||||
{
|
||||
qDebug() << "Comm validation failed. ";
|
||||
closeSerialPort(); //start by stopping everything.
|
||||
QTimer::singleShot(500, this, SLOT(handleReconnect()));
|
||||
return;
|
||||
if (serial->isOpen()) //if it's still false we have a problem...
|
||||
{
|
||||
qDebug() << "Comm validation failed. ";
|
||||
closeSerialPort(); //start by stopping everything.
|
||||
QTimer::singleShot(500, this, SLOT(handleReconnect()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,7 +448,7 @@ void SerialWorker::handleTick()
|
||||
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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user