Partial implementation of a more generic and multi-threaded version of

the frame sender. Now a simplified interface is attached to the main
window as well. Doesn't necessarily work just yet...
This commit is contained in:
Collin Kidder
2024-02-12 21:20:54 -05:00
parent b5619e0de8
commit 0c84b6998f
9 changed files with 1104 additions and 324 deletions
+2
View File
@@ -26,6 +26,7 @@ SOURCES += main.cpp\
connections/lawicel_serial.cpp \
connections/mqtt_bus.cpp \
dbc/dbcnodeduplicateeditor.cpp \
framesenderobject.cpp \
mqtt/qmqtt_client.cpp \
mqtt/qmqtt_client_p.cpp \
mqtt/qmqtt_frame.cpp \
@@ -113,6 +114,7 @@ HEADERS += mainwindow.h \
connections/mqtt_bus.h \
dbc/dbcnodeduplicateeditor.h \
dbc/dbcnoderebaseeditor.h \
framesenderobject.h \
mqtt/qmqtt.h \
mqtt/qmqtt_client.h \
mqtt/qmqtt_client_p.h \
+5 -1
View File
@@ -4,6 +4,7 @@
#include "can_structs.h"
#include <QList>
#include <QUuid>
enum TriggerMask
{
@@ -38,6 +39,7 @@ public:
//If ID is -1 then this is the temporary storage register. This is a shadow
//register used to accumulate the results of a multi operation modifier.
//if ID is -2 then this is a look up of our own data bytes stored in the class data.
//Of course, if the ID is positive then we grab bytes or signals from newest message with that ID
class ModifierOperand
{
public:
@@ -45,6 +47,7 @@ public:
int bus;
int databyte;
bool notOper; //should a bitwise NOT be applied to this prior to doing the actual calculation?
QString signalName; //if ID is positive and there is text in here then we'll look up the signal and use its value
};
//list of operations that can be done between the two operands
@@ -73,7 +76,8 @@ public:
class Modifier
{
public:
int destByte;
int destByte; //if -1 then target one of this ID's signals instead
QString signalName;
QList<ModifierOp> operations;
};
+453
View File
@@ -0,0 +1,453 @@
#include "framesenderobject.h"
#include "mainwindow.h"
FrameSenderObject::FrameSenderObject(const QVector<CANFrame> *frames)
{
mThread_p = new QThread();
statusCounter = 0;
modelFrames = frames;
dbcHandler = DBCHandler::getReference();
}
FrameSenderObject::~FrameSenderObject()
{
mThread_p->quit();
mThread_p->wait();
delete mThread_p;
}
void FrameSenderObject::piStart()
{
sendingTimer = new QTimer();
sendingTimer->setTimerType(Qt::PreciseTimer);
sendingTimer->setInterval(1);
connect(sendingTimer, &QTimer::timeout, this, &FrameSenderObject::timerTriggered);
}
void FrameSenderObject::piStop()
{
sendingTimer->stop();
delete sendingTimer;
}
void FrameSenderObject::initialize()
{
if( mThread_p && (mThread_p != QThread::currentThread()) )
{
/* move ourself to the thread */
moveToThread(mThread_p); /*TODO handle errors */
/* connect started() */
connect(mThread_p, SIGNAL(started()), this, SLOT(initialize()));
/* start the thread */
mThread_p->start(QThread::HighPriority);
return;
}
/* set started flag */
//mStarted = true;
connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int)));
/* in multithread case, this will be called before entering thread event loop */
return piStart();
}
void FrameSenderObject::finalize()
{
/* 1) execute in mThread_p context */
if( mThread_p && (mThread_p != QThread::currentThread()) )
{
/* if thread is finished, it means we call this function for the second time so we can leave */
if( !mThread_p->isFinished() )
{
/* we need to call piStop() */
QMetaObject::invokeMethod(this, "finalize",
Qt::BlockingQueuedConnection);
/* 3) stop thread */
mThread_p->quit();
if(!mThread_p->wait()) {
qDebug() << "can't stop thread";
}
}
return;
}
/* 2) call piStop in mThread context */
return piStop();
}
void FrameSenderObject::startSending()
{
/* make sure we execute in mThread context */
if( mThread_p && (mThread_p != QThread::currentThread()) ) {
QMetaObject::invokeMethod(this, "startSending",
Qt::BlockingQueuedConnection);
return;
}
sendingElapsed.start();
sendingTimer->start();
}
void FrameSenderObject::stopSending()
{
/* make sure we execute in mThread context */
if( mThread_p && (mThread_p != QThread::currentThread()) ) {
QMetaObject::invokeMethod(this, "stopPlayback",
Qt::BlockingQueuedConnection);
return;
}
sendingTimer->stop(); //pushing this button halts automatic playback
//emit statusUpdate(currentPosition);
}
void FrameSenderObject::addSendRecord(FrameSendData record)
{
/* make sure we execute in mThread context */
if( mThread_p && (mThread_p != QThread::currentThread()) ) {
QMetaObject::invokeMethod(this, "addSendRecord",
Qt::BlockingQueuedConnection,
Q_ARG(FrameSendData, record));
return;
}
sendingData.append(record);
}
void FrameSenderObject::removeSendRecord(int idx)
{
/* make sure we execute in mThread context */
if( mThread_p && (mThread_p != QThread::currentThread()) ) {
QMetaObject::invokeMethod(this, "removeSendRecord",
Qt::BlockingQueuedConnection,
Q_ARG(int, idx));
return;
}
sendingData.removeAt(idx);
}
/*
* Note: Getting a reference to an item in a QList is a dangerous thing. If anyone
* or anything happens to reallocate the list you're screwed. So, call this, do the
* work, and drop the reference as soon as possible.
*/
FrameSendData *FrameSenderObject::getSendRecordRef(int idx)
{
if (idx < 0 || idx >= sendingData.count()) return nullptr;
return &sendingData[idx];
}
/// <summary>
/// Called every millisecond to set the system update figures and send frames if necessary.
/// </summary>
/// <param name="sender"></param>
/// <param name="timerEventArgs"></param>
///
void FrameSenderObject::timerTriggered()
{
FrameSendData *sendData;
Trigger *trigger;
sendingList.clear();
if(mutex.tryLock())
{
//get elapsed microseconds since last tick (in case timer skips or is otherwise inaccurate, though there are no guarantees about elapsed timer either)
quint64 elapsed = sendingElapsed.nsecsElapsed() / 1000;
if (elapsed == 0) elapsed = 1;
sendingElapsed.start();
sendingLastTimeStamp += elapsed;
//qDebug() << playbackLastTimeStamp;
//qDebug() << "El: " << elapsed;
statusCounter++;
for (int i = 0; i < sendingData.count(); i++)
{
sendData = &sendingData[i];
if (!sendData->enabled)
{
if (sendData->triggers.count() > 0)
{
for (int j = 0; j < sendData->triggers.count(); j++) //resetting currCount when line is disabled
{
sendData->triggers[j].currCount = 0;
}
}
continue; //abort any processing on this if it is not enabled.
}
if (sendData->triggers.count() == 0)
{
qDebug() << "No triggers to process";
break;
}
for (int j = 0; j < sendData->triggers.count(); j++)
{
trigger = &sendData->triggers[j];
if (trigger->currCount >= trigger->maxCount) continue; //don't process if we've sent max frames we were supposed to
if (!trigger->readyCount) continue; //don't tick if not ready to tick
//is it time to fire?
trigger->msCounter += elapsed; //gives proper tracking even if timer doesn't fire as fast as it should
if (trigger->msCounter >= trigger->milliseconds)
{
trigger->msCounter -= trigger->milliseconds;
sendData->count++;
trigger->currCount++;
doModifiers(i);
//updateGridRow(i);
//qDebug() << "About to try to send a frame";
//CANConManager::getInstance()->sendFrame(sendingData[i]);
sendingList.append(sendingData[i]); //queue it instead of immediate sending
if (trigger->ID > 0) trigger->readyCount = false; //reset flag if this is a timed ID trigger
}
}
}
//if we have any frames to send after the above then send as a batch
if (sendingList.count() > 0) CANConManager::getInstance()->sendFrames(sendingList);
mutex.unlock();
}
else
{
qDebug() << "framesenderobject::handleTick() couldn't get mutex, elapsed is: " << sendingElapsed.elapsed();
}
}
void FrameSenderObject::buildFrameCache()
{
CANFrame thisFrame;
frameCache.clear();
for (int i = 0; i < modelFrames->count(); i++)
{
thisFrame = modelFrames->at(i);
if (!frameCache.contains(thisFrame.frameId()))
{
frameCache.insert(thisFrame.frameId(), thisFrame);
}
else
{
frameCache[thisFrame.frameId()] = thisFrame;
}
}
}
//remember, negative numbers are special -1 = all frames deleted, -2 = totally new set of frames.
void FrameSenderObject::updatedFrames(int numFrames)
{
CANFrame thisFrame;
if (numFrames == -1) //all frames deleted.
{
}
else if (numFrames == -2) //all new set of frames.
{
buildFrameCache();
}
else //just got some new frames. See if they are relevant.
{
if (numFrames > modelFrames->count()) return;
qDebug() << "New frames in sender window";
//run through the supposedly new frames in order
for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
{
thisFrame = modelFrames->at(i);
if (!frameCache.contains(thisFrame.frameId()))
{
frameCache.insert(thisFrame.frameId(), thisFrame);
}
else
{
frameCache[thisFrame.frameId()] = thisFrame;
}
processIncomingFrame(&thisFrame);
}
}
}
void FrameSenderObject::processIncomingFrame(CANFrame *frame)
{
for (int sd = 0; sd < sendingData.count(); sd++)
{
if (sendingData[sd].triggers.count() == 0) continue;
bool passedChecks = true;
for (int trig = 0; trig < sendingData[sd].triggers.count(); trig++)
{
Trigger *thisTrigger = &sendingData[sd].triggers[trig];
//need to ensure that this trigger is actually related to frames incoming.
//Otherwise ignore it here. Only frames that have BUS and/or ID set as trigger will work here.
if (!(thisTrigger->triggerMask & (TriggerMask::TRG_BUS | TriggerMask::TRG_ID) ) ) continue;
passedChecks = true;
//qDebug() << "Trigger ID: " << thisTrigger->ID;
//qDebug() << "Frame ID: " << frame->frameId();
//Check to see if we have a bus trigger condition and if so does it match
if (thisTrigger->bus != frame->bus && (thisTrigger->triggerMask & TriggerMask::TRG_BUS) )
passedChecks = false;
//check to see if we have an ID trigger condition and if so does it match
if ((thisTrigger->triggerMask & TriggerMask::TRG_ID) && (uint32_t)thisTrigger->ID != frame->frameId() )
passedChecks = false;
//check to see if we're limiting the trigger by max count and have we reached that count?
if ( (thisTrigger->triggerMask & TriggerMask::TRG_COUNT) && (thisTrigger->currCount >= thisTrigger->maxCount) )
passedChecks = false;
//if the above passed then are we triggering not only on ID but also signal?
if (passedChecks && (thisTrigger->triggerMask & (TriggerMask::TRG_SIGNAL | TriggerMask::TRG_ID) ) )
{
bool sigCheckPassed = false;
DBC_MESSAGE *msg = dbcHandler->findMessage(thisTrigger->ID);
if (msg)
{
DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(thisTrigger->sigName);
if (sig)
{
//first of all, is this signal really in this message we got?
if (sig->isSignalInMessage(*frame)) sigCheckPassed = true;
//if it was and we're also filtering on value then try that next
if (sigCheckPassed && (thisTrigger->triggerMask & TriggerMask::TRG_SIGVAL))
{
double sigval = 0.0;
if (sig->processAsDouble(*frame, sigval))
{
if (abs(sigval - thisTrigger->sigValueDbl) > 0.001)
{
sigCheckPassed = false;
}
}
else sigCheckPassed = false;
}
}
else sigCheckPassed = false;
}
else sigCheckPassed = false;
passedChecks &= sigCheckPassed; //passedChecks can only be true if both are
}
//if all the above says it's OK then we'll go ahead and send that message.
//If a message that comes through here has a MS value then we use it as a delay
//after the check passes. This allows for delaying the sending of the frame if that
//is required. Otherwise, just send it immediately.
if (passedChecks)
{
if (thisTrigger->milliseconds == 0) //immediate reply
{
thisTrigger->currCount++;
sendingData[sd].count++;
doModifiers(sd);
//updateGridRow(sd);
CANConManager::getInstance()->sendFrame(sendingData[sd]);
}
else //delayed sending frame
{
thisTrigger->readyCount = true;
}
}
}
}
}
/// <summary>
/// given an index into the sendingData list we run the modifiers that it has set up
/// </summary>
/// <param name="idx">The index into the sendingData list</param>
void FrameSenderObject::doModifiers(int idx)
{
int shadowReg = 0; //shadow register we use to accumulate results
int first=0, second=0;
FrameSendData *sendData = &sendingData[idx];
Modifier *mod;
ModifierOp op;
if (sendData->modifiers.count() == 0) return; //if no modifiers just leave right now
//qDebug() << "Executing mods";
for (int i = 0; i < sendData->modifiers.count(); i++)
{
mod = &sendData->modifiers[i];
for (int j = 0; j < mod->operations.count(); j++)
{
op = mod->operations.at(j);
if (op.first.ID == -1)
{
first = shadowReg;
}
else first = fetchOperand(idx, op.first);
second = fetchOperand(idx, op.second);
switch (op.operation)
{
case ADDITION:
shadowReg = first + second;
break;
case AND:
shadowReg = first & second;
break;
case DIVISION:
shadowReg = first / second;
break;
case MULTIPLICATION:
shadowReg = first * second;
break;
case OR:
shadowReg = first | second;
break;
case SUBTRACTION:
shadowReg = first - second;
break;
case XOR:
shadowReg = first ^ second;
break;
case MOD:
shadowReg = first % second;
}
}
//Finally, drop the result into the proper data byte
QByteArray newArr(sendData->payload());
newArr[mod->destByte] = (char) shadowReg;
sendData->setPayload(newArr);
}
}
int FrameSenderObject::fetchOperand(int idx, ModifierOperand op)
{
CANFrame *tempFrame = nullptr;
if (op.ID == 0) //numeric constant
{
if (op.notOper) return ~op.databyte;
else return op.databyte;
}
else if (op.ID == -2) //fetch data from a data byte within the output frame
{
if (op.notOper) return ~((unsigned char)sendingData.at(idx).payload()[op.databyte]);
else return (unsigned char)sendingData.at(idx).payload()[op.databyte];
}
else //look up external data byte
{
tempFrame = lookupFrame(op.ID, op.bus);
if (tempFrame != nullptr)
{
if (op.notOper) return ~((unsigned char)tempFrame->payload()[op.databyte]);
else return (unsigned char)tempFrame->payload()[op.databyte];
}
else return 0;
}
}
/// <summary>
/// Try to find the most recent frame given the input criteria
/// </summary>
/// <param name="ID">The ID to find</param>
/// <param name="bus">Which bus to look on (-1 if you don't care)</param>
/// <returns></returns>
CANFrame* FrameSenderObject::lookupFrame(int ID, int bus)
{
if (!frameCache.contains(ID)) return nullptr;
if (bus == -1 || frameCache[ID].bus == bus) return &frameCache[ID];
return nullptr;
}
+81
View File
@@ -0,0 +1,81 @@
#ifndef FRAMESENDEROBJECT_H
#define FRAMESENDEROBJECT_H
#include <QElapsedTimer>
#include <QTimer>
#include <QHash>
#include <QThread>
#include <QDebug>
#include <QMutex>
#include "can_structs.h"
#include "connections/canconmanager.h"
#include "can_trigger_structs.h"
#include "dbc/dbchandler.h"
class FrameSenderObject : public QObject
{
Q_OBJECT
public:
FrameSenderObject(const QVector<CANFrame> *frames);
~FrameSenderObject();
public slots:
/**
* @brief start the device. This calls piStarted
* @note starts the working thread if required (piStart is called in the working thread context)
*/
void initialize();
/**
* @brief stop the device. This calls piStop
* @note if a working thread is used, piStop is called before exiting the working thread
*/
void finalize();
void startSending();
void stopSending();
void addSendRecord(FrameSendData record);
void removeSendRecord(int idx);
FrameSendData *getSendRecordRef(int idx);
signals:
private slots:
void timerTriggered();
void updatedFrames(int);
private:
QList<CANFrame> sendingList;
int currentPosition;
QTimer *sendingTimer;
QElapsedTimer sendingElapsed;
quint64 sendingLastTimeStamp;
int statusCounter;
QList<FrameSendData> sendingData;
QThread* mThread_p;
QHash<int, CANFrame> frameCache; //hash with frame ID as the key and the most recent frame as the value
const QVector<CANFrame> *modelFrames;
bool inhibitChanged = false;
QMutex mutex;
DBCHandler *dbcHandler;
void doModifiers(int);
int fetchOperand(int, ModifierOperand);
CANFrame* lookupFrame(int, int);
void buildFrameCache();
void processIncomingFrame(CANFrame *frame);
/**
* @brief starts the device
*/
void piStart();
/**
* @brief stops the device
*/
void piStop();
};
#endif // FRAMESENDEROBJECT_H
+29 -12
View File
@@ -63,8 +63,8 @@ void FrameSenderWindow::setupGrid()
ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_EXT, 50);
ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_REM, 50);
ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_DATA, 220);
ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_TRIGGER, 220);
ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_MODS, 220);
ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_TRIGGER, 270);
ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_MODS, 270);
ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_COUNT, 80);
ui->tableSender->setHorizontalHeaderLabels(headers);
}
@@ -693,6 +693,12 @@ void FrameSenderWindow::processModifierText(int line)
//Example line:
//d0 = D0 + 1,d1 = id:0x200:d3 + id:0x200:d4 AND 0xF0 - Original version
//D0=D0+1,D1=ID:0x200:D3+ID:0x200:D4&0xF0
//[BMS_TargetVoltage]=45 would set the value of signal BMS_TargetVoltage to 45
//[BMS_TargetVoltage]=[0x234:BMS_CurrentVoltage] + 4 would instead grab the value
//of BMS_CurrentVoltage from ID 0x234, add 4 to it, and set BMS_TargetVoltage to that value.
//This is certainly much harder to parse than the trigger definitions.
//the left side of the = has to be D0 to D7. After that there is a string of
//data. Spaces used to be required but no longer are. This makes parsing harder but data entry easier
@@ -710,22 +716,33 @@ void FrameSenderWindow::processModifierText(int line)
Modifier thisMod;
thisMod.destByte = 0;
QString leftSide = Utility::grabAlphaNumeric(mods[i]);
if (leftSide.startsWith("D") && leftSide.length() == 2)
QRegularExpression regex;
QRegularExpressionMatch match;
regex.setPattern("^\\[(\\w+)]=(\\w+)");
match = regex.match(mods[i]);
if (match.hasMatch())
{
thisMod.destByte = leftSide.right(1).toInt();
thisMod.destByte = -1;
thisMod.signalName = match.captured(1);
mods[i] = match.captured(2);
thisMod.operations.clear();
}
else
{
qDebug() << "Something wrong with lefthand val";
continue;
}
if (!(Utility::grabOperation(mods[i]) == "="))
{
qDebug() << "Err: No = after lefthand val";
continue;
QString leftSide = Utility::grabAlphaNumeric(mods[i]);
if (leftSide.startsWith("D") && leftSide.length() == 2)
{
thisMod.destByte = leftSide.right(1).toInt();
thisMod.operations.clear();
if (!(Utility::grabOperation(mods[i]) == "="))
{
qDebug() << "Err: No = after lefthand val";
continue;
}
}
}
abort = false;
token = Utility::grabAlphaNumeric(mods[i]);
+198
View File
@@ -164,6 +164,8 @@ MainWindow::MainWindow(QWidget *parent) :
connect(ui->btnExpandAll, &QAbstractButton::clicked, this, &MainWindow::expandAllRows);
connect(ui->btnCollapseAll, &QAbstractButton::clicked, this, &MainWindow::collapseAllRows);
connect(ui->tableSimpleSender, SIGNAL(cellChanged(int,int)), this, SLOT(onSenderCellChanged(int,int)));
lbStatusConnected.setText(tr("Connected to 0 buses"));
lbHelp.setText(tr("Press F1 on any screen for help"));
lbHelp.setAlignment(Qt::AlignCenter);
@@ -227,12 +229,35 @@ MainWindow::MainWindow(QWidget *parent) :
ui->actionMotorControlConfig->setVisible(false);
ui->actionSingle_Multi_State_2->setVisible(false);
QStringList headers;
headers << "En" << "Bus" << "ID" << "Len" << "Ext" << "Rem" << "Data"
<< "Interval" << "Count";
ui->tableSimpleSender->setColumnCount(9);
ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_EN, 70);
ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_BUS, 70);
ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_ID, 70);
ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_LEN, 70);
ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_EXT, 70);
ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_REM, 70);
ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_DATA, 300);
ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_INTERVAL, 100);
ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_COUNT, 100);
ui->tableSimpleSender->setHorizontalHeaderLabels(headers);
createSenderRow();
frameSender = new FrameSenderObject(model->getListReference());
frameSender->initialize(); //creates the thread and sets things up
frameSender->startSending(); //start the timer in the object so enabled things can send
installEventFilter(this);
}
MainWindow::~MainWindow()
{
updateTimer.stop();
frameSender->stopSending();
killEmAll(); //Ride the lightning
delete ui;
delete model;
@@ -438,6 +463,174 @@ void MainWindow::writeSettings()
}
}
void MainWindow::onSenderCellChanged(int row, int col)
{
if (inhibitSenderChanged) return;
qDebug() << "onCellChanged";
if (row == ui->tableSimpleSender->rowCount() - 1)
{
createSenderRow();
}
processSenderCellChange(row, col);
}
void MainWindow::processSenderCellChange(int line, int col)
{
qDebug() << "processSenderCellChange";
FrameSendData *tempData;
QStringList tokens;
int tempVal;
int numBuses = CANConManager::getInstance()->getNumBuses();
QByteArray arr;
tempData = frameSender->getSendRecordRef(line);
if (!tempData)
{
FrameSendData dat;
frameSender->addSendRecord(dat);
tempData = frameSender->getSendRecordRef(line);
}
if (!tempData)
{
qDebug() << "No data to modify in processSenderCellChange. This is a bug!";
return;
}
switch (col)
{
case SIMP_COL::SC_COL_EN: //Enable check box
if (ui->tableSimpleSender->item(line, 0)->checkState() == Qt::Checked)
{
tempData->enabled = true;
}
else tempData->enabled = false;
qDebug() << "Setting enabled to " << tempData->enabled;
break;
case SIMP_COL::SC_COL_BUS: //Bus designation
tempVal = Utility::ParseStringToNum(ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_BUS)->text());
if (tempVal < -1) tempVal = -1;
if (tempVal >= numBuses) tempVal = numBuses - 1;
tempData->bus = tempVal;
qDebug() << "Setting bus to " << tempVal;
break;
case SIMP_COL::SC_COL_ID: //ID field
tempVal = Utility::ParseStringToNum(ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_ID)->text());
if (tempVal < 0) tempVal = 0;
if (tempVal > 0x7FFFFFFF) tempVal = 0x7FFFFFFF;
tempData->setFrameId(tempVal);
if (tempData->frameId() > 0x7FF) {
tempData->setExtendedFrameFormat(true);
ui->tableSimpleSender->blockSignals(true);
ui->tableSimpleSender->item(line, ST_COLS::SENDTAB_COL_EXT)->setCheckState(Qt::Checked);
ui->tableSimpleSender->blockSignals(false);
}
qDebug() << "setting ID to " << tempVal;
break;
case SIMP_COL::SC_COL_LEN:
tempVal = Utility::ParseStringToNum(ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_LEN)->text());
if (tempVal < 0) tempVal = 0;
if (tempVal > 8) tempVal = 8;
arr.resize(tempVal);
tempData->setPayload(arr);
break;
case SIMP_COL::SC_COL_EXT:
if (ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_EXT)->checkState() == Qt::Checked) {
tempData->setExtendedFrameFormat(true);
} else {
tempData->setExtendedFrameFormat(false);
}
break;
case SIMP_COL::SC_COL_REM:
if (ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_REM)->checkState() == Qt::Checked) {
tempData->setFrameType(QCanBusFrame::RemoteRequestFrame);
} else {
tempData->setFrameType(QCanBusFrame::DataFrame);
}
break;
case SIMP_COL::SC_COL_DATA: //Data bytes
for (int i = 0; i < 8; i++) tempData->payload().data()[i] = 0;
#if QT_VERSION >= QT_VERSION_CHECK( 5, 14, 0 )
tokens = ui->tableSimpleSender->item(line, ST_COLS::SENDTAB_COL_DATA)->text().split(" ", Qt::SkipEmptyParts);
#else
tokens = ui->tableSender->item(line, ST_COLS::SENDTAB_COL_DATA)->text().split(" ", QString::SkipEmptyParts);
#endif
arr.clear();
arr.reserve(tokens.count());
for (int j = 0; j < tokens.count(); j++)
{
arr.append((uint8_t)Utility::ParseStringToNum(tokens[j]));
}
tempData->setPayload(arr);
break;
case SIMP_COL::SC_COL_INTERVAL: //interval in ms
QString trigger = ui->tableSimpleSender->item(line, ST_COLS::SENDTAB_COL_TRIGGER)->text().toUpper();
Trigger thisTrigger;
thisTrigger.bus = -1; //-1 means we don't care which
thisTrigger.ID = -1; //the rest of these being -1 means nothing has changed it
thisTrigger.maxCount = -1;
thisTrigger.milliseconds = -1;
thisTrigger.currCount = 0;
thisTrigger.msCounter = 0;
thisTrigger.triggerMask = 0;
thisTrigger.readyCount = true;
tempData->triggers.clear();
tempData->triggers.reserve(1);
if (trigger != "")
{
thisTrigger.milliseconds = Utility::ParseStringToNum(trigger);
thisTrigger.triggerMask |= TriggerMask::TRG_MS;
}
if (thisTrigger.milliseconds < 1) thisTrigger.milliseconds = 1;
tempData->triggers.append(thisTrigger);
break;
}
}
void MainWindow::createSenderRow()
{
int row = ui->tableSimpleSender->rowCount();
ui->tableSimpleSender->insertRow(row);
QTableWidgetItem *item = new QTableWidgetItem();
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Unchecked);
inhibitSenderChanged = true;
ui->tableSimpleSender->setItem(row, SIMP_COL::SC_COL_EN, item);
item = new QTableWidgetItem();
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Unchecked);
ui->tableSimpleSender->setItem(row, SIMP_COL::SC_COL_EXT, item);
item = new QTableWidgetItem();
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Unchecked);
ui->tableSimpleSender->setItem(row, SIMP_COL::SC_COL_REM, item);
for (int i = 1; i <= SIMP_COL::SC_COL_COUNT; i++)
{
if (i != SIMP_COL::SC_COL_EXT && i != SIMP_COL::SC_COL_REM) {
item = new QTableWidgetItem("");
ui->tableSimpleSender->setItem(row, i, item);
}
}
inhibitSenderChanged = false;
}
void MainWindow::updateConnectionSettings(QString connectionType, QString port, int speed0, int speed1)
{
Q_UNUSED(connectionType);
@@ -1371,6 +1564,11 @@ CANFrameModel* MainWindow::getCANFrameModel()
return model;
}
/*
* All functions past this point set up the various other windows that can be opened
*/
void MainWindow::showSettingsDialog()
{
if (!settingsDialog)
+19 -1
View File
@@ -10,7 +10,7 @@
#include "framefileio.h"
#include "dbc/dbchandler.h"
#include "bus_protocols/isotp_handler.h"
#include "framesenderobject.h"
#include "re/graphingwindow.h"
#include "re/frameinfowindow.h"
#include "frameplaybackwindow.h"
@@ -40,6 +40,19 @@ class ConnectionWindow;
class ISOTP_InterpreterWindow;
class ScriptingWindow;
enum SIMP_COL
{
SC_COL_EN = 0,
SC_COL_BUS = 1,
SC_COL_ID = 2,
SC_COL_LEN = 3,
SC_COL_EXT = 4,
SC_COL_REM = 5,
SC_COL_DATA = 6,
SC_COL_INTERVAL = 7,
SC_COL_COUNT = 8,
};
namespace Ui {
class MainWindow;
}
@@ -114,6 +127,7 @@ private slots:
void filterClearAll();
void headerClicked (int logicalIndex);
void DBCSettingsUpdated();
void onSenderCellChanged(int, int);
public slots:
void gotFrames(int);
@@ -142,6 +156,7 @@ private:
QByteArray inputBuffer;
QTimer updateTimer;
QElapsedTimer *elapsedTime;
FrameSenderObject *frameSender;
int framesPerSec;
int rxFrames;
bool inhibitFilterUpdate;
@@ -151,6 +166,7 @@ private:
bool CSVAbsTime;
bool bDirty; //have frames been added or subtracted since the last save/load?
bool useFiltered; //should sub-windows use the unfiltered or filtered frames list?
bool inhibitSenderChanged;
bool continuousLogging;
int continuousLogFlushCounter;
@@ -211,6 +227,8 @@ private:
bool eventFilter(QObject *obj, QEvent *event);
void manageRowExpansion();
void disableAutoRowExpansion();
void createSenderRow();
void processSenderCellChange(int line, int col);
};
#endif // MAINWINDOW_H
+1 -1
View File
@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>1008</width>
<width>1350</width>
<height>611</height>
</rect>
</property>
+316 -309
View File
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>682</height>
<width>1177</width>
<height>717</height>
</rect>
</property>
<property name="sizePolicy">
@@ -26,340 +26,347 @@
<string>SavvyCAN</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="5,1">
<item>
<widget class="QTableView" name="canFramesView">
<property name="font">
<font>
<pointsize>11</pointsize>
<bold>false</bold>
</font>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="4,1">
<item>
<widget class="QLabel" name="lblRemoteConn">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Remote Connection Active:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineRemoteKey">
<property name="enabled">
<bool>true</bool>
</property>
<property name="maximumSize">
<size>
<width>1000</width>
<height>50</height>
</size>
</property>
<property name="baseSize">
<size>
<width>150</width>
<height>50</height>
</size>
</property>
<property name="font">
<font>
<bold>false</bold>
</font>
</property>
<property name="maxLength">
<number>8</number>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblContMsg">
<property name="palette">
<palette>
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>120</red>
<green>120</green>
<blue>120</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<widget class="QTableView" name="canFramesView">
<property name="font">
<font>
<pointsize>11</pointsize>
<italic>true</italic>
<bold>true</bold>
<bold>false</bold>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Total Frames Captured:</string>
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="lbNumFrames">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Frames Per Second:</string>
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="lbFPS">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnCaptureToggle">
<property name="text">
<string>Suspend Capturing</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnNormalize">
<property name="text">
<string>Normalize Frame Timing</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnClearFrames">
<property name="text">
<string>Clear Frames</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbPersistentFilters">
<property name="text">
<string>Keep Filters When Clearing</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbAutoScroll">
<property name="text">
<string>Auto Scroll Window</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbOverwrite">
<property name="enabled">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="text">
<string>Overwrite Mode</string>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbInterpret">
<property name="text">
<string>Interpret Frames</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnExpandAll">
<property name="text">
<string>Expand All Rows</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnCollapseAll">
<property name="text">
<string>Collapse All Rows</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Bus Filtering:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listBusFilters">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>175</width>
<height>40</height>
</size>
</property>
<property name="flow">
<enum>QListView::LeftToRight</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>Frame Filtering:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listFilters">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>160</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>175</width>
<height>16777215</height>
</size>
</property>
<property name="alternatingRowColors">
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="btnFilterAll">
<widget class="QLabel" name="lblRemoteConn">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>All</string>
<string>Remote Connection Active:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnFilterNone">
<property name="text">
<string>None</string>
<widget class="QLineEdit" name="lineRemoteKey">
<property name="enabled">
<bool>true</bool>
</property>
<property name="maximumSize">
<size>
<width>1000</width>
<height>50</height>
</size>
</property>
<property name="baseSize">
<size>
<width>150</width>
<height>50</height>
</size>
</property>
<property name="font">
<font>
<bold>false</bold>
</font>
</property>
<property name="maxLength">
<number>8</number>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblContMsg">
<property name="palette">
<palette>
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>120</red>
<green>120</green>
<blue>120</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
<italic>true</italic>
<bold>true</bold>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Total Frames Captured:</string>
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="lbNumFrames">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Frames Per Second:</string>
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="lbFPS">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnCaptureToggle">
<property name="text">
<string>Suspend Capturing</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnNormalize">
<property name="text">
<string>Normalize Frame Timing</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnClearFrames">
<property name="text">
<string>Clear Frames</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbPersistentFilters">
<property name="text">
<string>Keep Filters When Clearing</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbAutoScroll">
<property name="text">
<string>Auto Scroll Window</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbOverwrite">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Overwrite Mode</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbInterpret">
<property name="text">
<string>Interpret Frames</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnExpandAll">
<property name="text">
<string>Expand All Rows</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnCollapseAll">
<property name="text">
<string>Collapse All Rows</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Bus Filtering:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listBusFilters">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>175</width>
<height>40</height>
</size>
</property>
<property name="flow">
<enum>QListView::LeftToRight</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>Frame Filtering:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listFilters">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>160</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>175</width>
<height>16777215</height>
</size>
</property>
<property name="alternatingRowColors">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="btnFilterAll">
<property name="text">
<string>All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnFilterNone">
<property name="text">
<string>None</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QTableWidget" name="tableSimpleSender"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
@@ -367,7 +374,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<width>1177</width>
<height>26</height>
</rect>
</property>