Fixed up ability to send frames while not connected and to show frames

that were sent within the same window as received frames. Added a new
scripting window that can be used to create scripts that send and
receive messages at runtime - not done but sort of working.
This commit is contained in:
Collin Kidder
2015-11-30 20:35:08 -05:00
parent 3fb702e22e
commit 0ff076de3a
17 changed files with 577 additions and 48 deletions
+11 -5
View File
@@ -6,8 +6,7 @@
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets serialport printsupport
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets serialport printsupport qml
CONFIG(release, debug|release):DEFINES += QT_NO_DEBUG_OUTPUT
CONFIG += c++11
@@ -36,7 +35,10 @@ SOURCES += main.cpp\
filecomparatorwindow.cpp \
mainsettingsdialog.cpp \
firmwareuploaderwindow.cpp \
discretestatewindow.cpp
discretestatewindow.cpp \
scriptingwindow.cpp \
scriptcontainer.cpp \
canfilter.cpp
HEADERS += mainwindow.h \
can_structs.h \
@@ -61,7 +63,10 @@ HEADERS += mainwindow.h \
filecomparatorwindow.h \
mainsettingsdialog.h \
firmwareuploaderwindow.h \
discretestatewindow.h
discretestatewindow.h \
scriptingwindow.h \
scriptcontainer.h \
canfilter.h
FORMS += mainwindow.ui \
graphingwindow.ui \
@@ -76,7 +81,8 @@ FORMS += mainwindow.ui \
filecomparatorwindow.ui \
mainsettingsdialog.ui \
firmwareuploaderwindow.ui \
discretestatewindow.ui
discretestatewindow.ui \
scriptingwindow.ui
DISTFILES +=
+27
View File
@@ -0,0 +1,27 @@
#include "canfilter.h"
CANFilter::CANFilter()
{
ID = 0;
mask = 0;
bus = -1;
}
void CANFilter::setFilter(uint32_t id, uint32_t mask, int bus)
{
this->ID = id;
this->mask = mask;
this->bus = bus;
}
bool CANFilter::checkFilter(uint32_t id, int bus)
{
if (bus == -1 || bus == this->bus)
{
uint32_t result = id & this->mask;
if (result == this->ID) return true;
}
return false;
}
+19
View File
@@ -0,0 +1,19 @@
#ifndef CANFILTER_H
#define CANFILTER_H
#include <stdint.h>
class CANFilter
{
public:
CANFilter();
void setFilter(uint32_t id, uint32_t mask, int bus);
bool checkFilter(uint32_t id, int bus);
private:
uint32_t ID;
uint32_t mask;
int bus;
};
#endif // CANFILTER_H
+16 -14
View File
@@ -251,25 +251,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 +280,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 +301,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();
}
}
+1 -1
View File
@@ -23,7 +23,7 @@ public:
int columnCount(const QModelIndex &) const;
int totalFrameCount();
void addFrame(CANFrame &, bool);
void addFrame(const CANFrame &, bool);
void sendRefresh();
void sendRefresh(int);
void sendBulkRefresh(int);
+1 -1
View File
@@ -1,7 +1,7 @@
#ifndef CONFIG
#define CONFIG
#define VERSION 141
#define VERSION 142
//try to keep this in sync.
//SavvyCAN will complain if you connect a GVRET board with a revision
+1 -1
View File
@@ -799,7 +799,7 @@
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Trigger Values</string>
<string>Data Seek Values</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
+18
View File
@@ -86,6 +86,7 @@ MainWindow::MainWindow(QWidget *parent) :
worker = new SerialWorker(model);
worker->moveToThread(&serialWorkerThread);
connect(&serialWorkerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(&serialWorkerThread, SIGNAL(started()), worker, SLOT(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);
@@ -110,6 +111,7 @@ MainWindow::MainWindow(QWidget *parent) :
settingsDialog = NULL;
firmwareUploaderWindow = NULL;
discreteStateWindow = NULL;
scriptingWindow = NULL;
dbcHandler = new DBCHandler;
bDirty = false;
inhibitFilterUpdate = false;
@@ -143,6 +145,7 @@ MainWindow::MainWindow(QWidget *parent) :
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->actionScripting_INterface, SIGNAL(triggered(bool)), this, SLOT(showScriptingWindow()));
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()));
@@ -237,6 +240,12 @@ MainWindow::~MainWindow()
delete discreteStateWindow;
}
if (scriptingWindow)
{
scriptingWindow->close();
delete scriptingWindow;
}
delete ui;
delete dbcHandler;
model->clearFrames();
@@ -1035,6 +1044,15 @@ void MainWindow::showSingleMultiWindow()
discreteStateWindow->show();
}
void MainWindow::showScriptingWindow()
{
if (!scriptingWindow)
{
scriptingWindow = new ScriptingWindow(model->getListReference());
}
scriptingWindow->show();
}
void MainWindow::showRangeWindow()
{
//not done yet
+3
View File
@@ -19,6 +19,7 @@
#include "mainsettingsdialog.h"
#include "firmwareuploaderwindow.h"
#include "discretestatewindow.h"
#include "scriptingwindow.h"
namespace Ui {
class MainWindow;
@@ -56,6 +57,7 @@ private slots:
void showComparisonWindow();
void showSettingsDialog();
void showFirmwareUploaderWindow();
void showScriptingWindow();
void exitApp();
void handleSaveDecoded();
void changeBaudRates();
@@ -121,6 +123,7 @@ private:
MainSettingsDialog *settingsDialog;
DiscreteStateWindow *discreteStateWindow;
FirmwareUploaderWindow *firmwareUploaderWindow;
ScriptingWindow *scriptingWindow;
//various private storage
QLabel lbStatusConnected;
+7 -1
View File
@@ -293,7 +293,7 @@
<x>0</x>
<y>0</y>
<width>800</width>
<height>21</height>
<height>27</height>
</rect>
</property>
<widget class="QMenu" name="menu_RE_Tools">
@@ -311,6 +311,7 @@
</property>
<addaction name="action_Playback"/>
<addaction name="action_Custom"/>
<addaction name="actionScripting_INterface"/>
</widget>
<widget class="QMenu" name="menuFile">
<property name="title">
@@ -442,6 +443,11 @@
<string>Firmware Uploader</string>
</property>
</action>
<action name="actionScripting_INterface">
<property name="text">
<string>Scripting Interface</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
+99
View File
@@ -0,0 +1,99 @@
#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);
if (result.isError() && errorWidget)
{
errorWidget->clear();
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)
{
qDebug() << "Called set filter";
qDebug() << id.toInt() << "*" << mask.toInt() << "*" << bus.toInt();
}
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";
}
void ScriptContainer::sendFrame(QJSValue id, QJSValue length, QJSValue data)
{
qDebug() << "called send frame";
}
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());
}
}
}
+39
View File
@@ -0,0 +1,39 @@
#ifndef SCRIPTCONTAINER_H
#define SCRIPTCONTAINER_H
#include <QJSEngine>
#include <QTimer>
#include <qlistwidget.h>
class ScriptContainer : public QObject
{
Q_OBJECT
public:
ScriptContainer();
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 id, QJSValue length, QJSValue data);
private slots:
void tick();
private:
QJSEngine scriptEngine;
QJSValue compiledScript;
QJSValue setupFunction;
QJSValue gotFrameFunction;
QJSValue tickFunction;
QTimer timer;
QListWidget *errorWidget;
};
#endif // SCRIPTCONTAINER_H
+144
View File
@@ -0,0 +1,144 @@
#include "scriptingwindow.h"
#include "ui_scriptingwindow.h"
#include <QFile>
#include <QFileDialog>
ScriptingWindow::ScriptingWindow(const QVector<CANFrame> *frames, QWidget *parent) :
QDialog(parent),
ui(new Ui::ScriptingWindow)
{
ui->setupUi(this);
modelFrames = frames;
connect(ui->btnLoadScript, SIGNAL(pressed()), this, SLOT(loadNewScript()));
connect(ui->btnNewScript, SIGNAL(pressed()), this, SLOT(createNewScript()));
connect(ui->btnRecompile, SIGNAL(pressed()), this, SLOT(recompileScript()));
connect(ui->btnRemoveScript, SIGNAL(pressed()), this, SLOT(deleteCurrentScript()));
connect(ui->btnRevertScript, SIGNAL(pressed()), this, SLOT(revertScript()));
connect(ui->btnSaveScript, SIGNAL(pressed()), this, SLOT(saveScript()));
currentScript = new ScriptContainer();
currentScript->setErrorWidget(ui->listErrors);
}
ScriptingWindow::~ScriptingWindow()
{
delete ui;
}
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();
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);
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->toPlainText().toUtf8());
outFile->close();
delete outFile;
}
}
}
void ScriptingWindow::revertScript()
{
}
void ScriptingWindow::recompileScript()
{
currentScript->scriptText = ui->txtScriptSource->toPlainText();
currentScript->compileScript();
}
+44
View File
@@ -0,0 +1,44 @@
#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);
~ScriptingWindow();
private slots:
void loadNewScript();
void createNewScript();
void deleteCurrentScript();
void refreshSourceWindow();
void saveScript();
void revertScript();
void recompileScript();
public slots:
private:
Ui::ScriptingWindow *ui;
QList<ScriptContainer *> scripts;
ScriptContainer *currentScript;
const QVector<CANFrame> *modelFrames;
};
#endif // SCRIPTINGWINDOW_H
+118
View File
@@ -0,0 +1,118 @@
<?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>994</width>
<height>858</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,5,0,0,1">
<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="QTextEdit" name="txtScriptSource">
<property name="enabled">
<bool>false</bool>
</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>
<resources/>
<connections/>
</ui>
+21 -18
View File
@@ -40,6 +40,19 @@ SerialWorker::~SerialWorker()
if (ticker != NULL) ticker->stop();
}
void SerialWorker::run()
{
ticker = new QTimer;
connect(ticker, SIGNAL(timeout()), this, SLOT(handleTick()));
elapsedTime = new QTime;
elapsedTime->start();
ticker->setInterval(250); //tick four times per second
ticker->setSingleShot(false); //keep ticking
ticker->start();
}
void SerialWorker::readSettings()
{
QSettings settings;
@@ -110,19 +123,6 @@ void SerialWorker::setSerialPort(QSerialPortInfo *port)
else connected = true;
connect(serial, SIGNAL(readyRead()), this, SLOT(readSerialData()));
if (doValidation) QTimer::singleShot(1000, this, SLOT(connectionTimeout()));
if (ticker == NULL)
{
ticker = new QTimer;
connect(ticker, SIGNAL(timeout()), this, SLOT(handleTick()));
}
if (elapsedTime == NULL)
{
elapsedTime = new QTime;
elapsedTime->start();
}
ticker->setInterval(250); //tick four times per second
ticker->setSingleShot(false); //keep ticking
ticker->start();
}
void SerialWorker::connectionTimeout()
@@ -158,6 +158,10 @@ void SerialWorker::sendFrame(const CANFrame *frame, int bus = 0)
//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(*frame, false);
gotFrames++;
if (!connected) return;
ID = frame->ID;
@@ -181,10 +185,6 @@ void SerialWorker::sendFrame(const CANFrame *frame, int bus = 0)
if (!serial->isOpen()) return;
//qDebug() << "writing " << buffer.length() << " bytes to serial port";
serial->write(buffer);
//show our sent frames in the list too.
canModel->addFrame(frame, false);
gotFrames++;
}
//a simple way for another thread to pass us a bunch of frames to send.
@@ -426,6 +426,8 @@ void SerialWorker::handleTick()
{
//qDebug() << "Tick!";
if (connected)
{
if (!gotValidated)
{
if (serial->isOpen()) //if it's still false we have a problem...
@@ -436,13 +438,14 @@ void SerialWorker::handleTick()
return;
}
}
}
framesPerSec += gotFrames * 1000 / elapsedTime->elapsed() - (framesPerSec / 4);
elapsedTime->restart();
emit frameUpdateTick(framesPerSec / 4, gotFrames); //sends stats to interested parties
canModel->sendBulkRefresh(gotFrames);
gotFrames = 0;
if (doValidation) sendCommValidation();
if (doValidation && connected) sendCommValidation();
}
void SerialWorker::handleReconnect()
+1
View File
@@ -54,6 +54,7 @@ private slots: //we receive things in slots
void handleReconnect();
public slots:
void run();
void setSerialPort(QSerialPortInfo*);
void closeSerialPort();
void sendFrame(const CANFrame *, int);