1148 lines
42 KiB
C++
1148 lines
42 KiB
C++
#include "framesenderwindow.h"
|
|
#include "ui_framesenderwindow.h"
|
|
#include "utility.h"
|
|
|
|
#include <QFileDialog>
|
|
#include <QDebug>
|
|
#include "mainwindow.h"
|
|
#include "helpwindow.h"
|
|
#include "connections/canconmanager.h"
|
|
#include "triggerdialog.h"
|
|
|
|
/*
|
|
* notes: need to ensure that you grab pointers when modifying data structures and dont
|
|
* make copies. Also, check # of ms elapsed since last tick since they don't tend to come in at 1ms
|
|
* intervals like we asked.
|
|
* Also, rows default to enabled which is odd because the button state does not reflect that.
|
|
*/
|
|
|
|
FrameSenderWindow::FrameSenderWindow(const QVector<CANFrame> *frames, QWidget *parent) :
|
|
QDialog(parent),
|
|
ui(new Ui::FrameSenderWindow)
|
|
{
|
|
ui->setupUi(this);
|
|
setWindowFlags(Qt::Window);
|
|
|
|
modelFrames = frames;
|
|
|
|
dbcHandler = DBCHandler::getReference();
|
|
|
|
intervalTimer = new QTimer();
|
|
intervalTimer->setTimerType(Qt::PreciseTimer);
|
|
intervalTimer->setInterval(1);
|
|
|
|
setupGrid();
|
|
createBlankRow();
|
|
|
|
connect(ui->tableSender, SIGNAL(cellChanged(int,int)), this, SLOT(onCellChanged(int,int)));
|
|
connect(ui->tableSender, SIGNAL(cellDoubleClicked(int, int)), SLOT(onCellDoubleTap(int, int)));
|
|
connect(intervalTimer, SIGNAL(timeout()), this, SLOT(handleTick()));
|
|
connect(ui->btnClearGrid, SIGNAL(clicked(bool)), this, SLOT(clearGrid()));
|
|
connect(ui->btnDisableAll, SIGNAL(clicked(bool)), this, SLOT(disableAll()));
|
|
connect(ui->btnEnableAll, SIGNAL(clicked(bool)), this, SLOT(enableAll()));
|
|
connect(ui->btnLoadGrid, SIGNAL(clicked(bool)), this, SLOT(loadGrid()));
|
|
connect(ui->btnSaveGrid, SIGNAL(clicked(bool)), this, SLOT(saveGrid()));
|
|
bool connected = connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int)));
|
|
fprintf(stderr, "DEBUG: FrameSenderWindow signal connection: %s\n", connected ? "SUCCESS" : "FAILED");
|
|
fflush(stderr);
|
|
|
|
buildFrameCache(); // Initialize frame cache with existing frames
|
|
|
|
intervalTimer->start();
|
|
elapsedTimer.start();
|
|
installEventFilter(this);
|
|
}
|
|
|
|
void FrameSenderWindow::setupGrid()
|
|
{
|
|
QStringList headers;
|
|
headers << "En" << "Bus" << "ID" << "MsgName" << "Len" << "Ext" << "Rem" << "Data"
|
|
<< "Trigger" << "Modifications" << "Count";
|
|
ui->tableSender->setColumnCount(11);
|
|
ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_EN, 50);
|
|
ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_BUS, 50);
|
|
ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_ID, 50);
|
|
ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_MSGNAME, 150);
|
|
ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_LEN, 50);
|
|
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, 270);
|
|
ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_MODS, 270);
|
|
ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_COUNT, 80);
|
|
ui->tableSender->setHorizontalHeaderLabels(headers);
|
|
}
|
|
|
|
FrameSenderWindow::~FrameSenderWindow()
|
|
{
|
|
removeEventFilter(this);
|
|
delete ui;
|
|
|
|
intervalTimer->stop();
|
|
delete intervalTimer;
|
|
}
|
|
|
|
bool FrameSenderWindow::eventFilter(QObject *obj, QEvent *event)
|
|
{
|
|
if (event->type() == QEvent::KeyRelease) {
|
|
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
|
switch (keyEvent->key())
|
|
{
|
|
case Qt::Key_F1:
|
|
HelpWindow::getRef()->showHelp("customsender.md");
|
|
break;
|
|
}
|
|
return true;
|
|
} else {
|
|
// standard event processing
|
|
return QObject::eventFilter(obj, event);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void FrameSenderWindow::createBlankRow()
|
|
{
|
|
int row = ui->tableSender->rowCount();
|
|
ui->tableSender->insertRow(row);
|
|
|
|
QTableWidgetItem *item = new QTableWidgetItem();
|
|
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
|
item->setCheckState(Qt::Unchecked);
|
|
inhibitChanged = true;
|
|
ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_EN, item);
|
|
|
|
item = new QTableWidgetItem();
|
|
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
|
item->setCheckState(Qt::Unchecked);
|
|
ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_EXT, item);
|
|
|
|
item = new QTableWidgetItem();
|
|
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
|
item->setCheckState(Qt::Unchecked);
|
|
ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_REM, item);
|
|
|
|
for (int i = 1; i <= ST_COLS::SENDTAB_COL_COUNT; i++)
|
|
{
|
|
if (i != ST_COLS::SENDTAB_COL_EXT && i != ST_COLS::SENDTAB_COL_REM) {
|
|
item = new QTableWidgetItem("");
|
|
//msgname is looked up via DBC interface and not able to be edited.
|
|
//Though, it might be perhaps interesting to allow renaming it to
|
|
//a shorter name in some cases? Jury is still out on this one...
|
|
if (i == ST_COLS::SENDTAB_COL_MSGNAME) item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
|
ui->tableSender->setItem(row, i, item);
|
|
}
|
|
}
|
|
|
|
inhibitChanged = false;
|
|
}
|
|
|
|
void FrameSenderWindow::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 FrameSenderWindow::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;
|
|
//run through the supposedly new frames in order
|
|
for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
|
|
{
|
|
thisFrame = modelFrames->at(i);
|
|
// No debug here to reduce noise
|
|
if (!frameCache.contains(thisFrame.frameId()))
|
|
{
|
|
frameCache.insert(thisFrame.frameId(), thisFrame);
|
|
}
|
|
else
|
|
{
|
|
frameCache[thisFrame.frameId()] = thisFrame;
|
|
}
|
|
processIncomingFrame(&thisFrame);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FrameSenderWindow::processIncomingFrame(CANFrame *frame)
|
|
{
|
|
for (int sd = 0; sd < sendingData.count(); sd++)
|
|
{
|
|
if (sendingData[sd].triggers.count() == 0) continue;
|
|
if (!sendingData[sd].enabled) continue; // Skip if not enabled
|
|
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;
|
|
|
|
//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)
|
|
{
|
|
if ((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) )
|
|
{
|
|
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
|
|
{
|
|
fprintf(stderr, "DEBUG: DELAYED SEND - Setting readyCount = true\n");
|
|
thisTrigger->readyCount = true;
|
|
}
|
|
fflush(stderr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FrameSenderWindow::enableAll()
|
|
{
|
|
for (int i = 0; i < ui->tableSender->rowCount() - 1; i++)
|
|
{
|
|
ui->tableSender->item(i, ST_COLS::SENDTAB_COL_EN)->setCheckState(Qt::Checked);
|
|
sendingData[i].enabled = true;
|
|
}
|
|
}
|
|
|
|
void FrameSenderWindow::disableAll()
|
|
{
|
|
for (int i = 0; i < ui->tableSender->rowCount() - 1; i++)
|
|
{
|
|
ui->tableSender->item(i, ST_COLS::SENDTAB_COL_EN)->setCheckState(Qt::Unchecked);
|
|
sendingData[i].enabled = false;
|
|
}
|
|
}
|
|
|
|
void FrameSenderWindow::clearGrid()
|
|
{
|
|
if (ui->tableSender->rowCount() == 1) return;
|
|
for (int i = ui->tableSender->rowCount() - 2; i >= 0; i--)
|
|
{
|
|
sendingData[i].enabled = false;
|
|
sendingData.removeAt(i);
|
|
ui->tableSender->removeRow(i);
|
|
}
|
|
}
|
|
|
|
void FrameSenderWindow::saveGrid()
|
|
{
|
|
QString filename;
|
|
QFileDialog dialog(this);
|
|
QSettings settings;
|
|
|
|
QStringList filters;
|
|
filters.append(QString(tr("Frame Sender Definition (*.fsd)")));
|
|
|
|
dialog.setDirectory(settings.value("FrameSender/LoadSaveDirectory", dialog.directory().path()).toString());
|
|
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 (dialog.selectedNameFilter() == filters[0])
|
|
{
|
|
if (!filename.contains('.')) filename += ".fsd";
|
|
saveSenderFile(filename);
|
|
settings.setValue("FrameSender/LoadSaveDirectory", dialog.directory().path());
|
|
}
|
|
}
|
|
}
|
|
|
|
void FrameSenderWindow::loadGrid()
|
|
{
|
|
QString filename;
|
|
QFileDialog dialog(this);
|
|
QSettings settings;
|
|
|
|
QStringList filters;
|
|
filters.append(QString(tr("Frame Sender Definition (*.fsd)")));
|
|
|
|
dialog.setDirectory(settings.value("FrameSender/LoadSaveDirectory", dialog.directory().path()).toString());
|
|
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])
|
|
{
|
|
loadSenderFile(filename);
|
|
settings.setValue("FrameSender/LoadSaveDirectory", dialog.directory().path());
|
|
}
|
|
}
|
|
|
|
createBlankRow();
|
|
|
|
setupGrid();
|
|
}
|
|
|
|
void FrameSenderWindow::saveSenderFile(QString filename)
|
|
{
|
|
QFile *outFile = new QFile(filename);
|
|
QString outString;
|
|
|
|
if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text))
|
|
{
|
|
delete outFile;
|
|
return;
|
|
}
|
|
|
|
for (int c = 0; c < sendingData.count(); c++)
|
|
{
|
|
outString.clear();
|
|
if (ui->tableSender->item(c, ST_COLS::SENDTAB_COL_EN)->checkState() == Qt::Checked)
|
|
{
|
|
outString = "T#";
|
|
}
|
|
else outString = "F#";
|
|
for (int i = 1; i < ST_COLS::SENDTAB_COL_COUNT; i++)
|
|
{
|
|
if (i == ST_COLS::SENDTAB_COL_EXT || i == ST_COLS::SENDTAB_COL_REM) {
|
|
if (ui->tableSender->item(c, i)->checkState() == Qt::Checked) {
|
|
outString.append("T");
|
|
} else {
|
|
outString.append("F");
|
|
}
|
|
} else {
|
|
outString.append(ui->tableSender->item(c, i)->text());
|
|
}
|
|
outString.append("#");
|
|
}
|
|
outString.append("\n");
|
|
outFile->write(outString.toUtf8());
|
|
}
|
|
|
|
outFile->close();
|
|
delete outFile;
|
|
}
|
|
|
|
void FrameSenderWindow::loadSenderFile(QString filename)
|
|
{
|
|
QFile *inFile = new QFile(filename);
|
|
QByteArray line;
|
|
|
|
if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text))
|
|
{
|
|
delete inFile;
|
|
return;
|
|
}
|
|
|
|
ui->tableSender->clear();
|
|
while (ui->tableSender->rowCount() > 0) ui->tableSender->removeRow(0);
|
|
sendingData.clear();
|
|
|
|
while (!inFile->atEnd()) {
|
|
inhibitChanged = true;
|
|
line = inFile->readLine().simplified();
|
|
if (line.length() > 2)
|
|
{
|
|
QList<QByteArray> tokens = line.split('#');
|
|
int row = ui->tableSender->rowCount();
|
|
ui->tableSender->insertRow(row);
|
|
QTableWidgetItem *item = new QTableWidgetItem();
|
|
ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_EN, item);
|
|
ui->tableSender->item(row, ST_COLS::SENDTAB_COL_EN)->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
|
|
|
item = new QTableWidgetItem();
|
|
ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_EXT, item);
|
|
ui->tableSender->item(row, ST_COLS::SENDTAB_COL_EN)->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
|
item->setCheckState(Qt::Unchecked);
|
|
|
|
item = new QTableWidgetItem();
|
|
ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_REM, item);
|
|
ui->tableSender->item(row, ST_COLS::SENDTAB_COL_REM)->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
|
item->setCheckState(Qt::Unchecked);
|
|
|
|
if (tokens[0] == "T")
|
|
{
|
|
ui->tableSender->item(row, ST_COLS::SENDTAB_COL_EN)->setCheckState(Qt::Checked);
|
|
}
|
|
else ui->tableSender->item(row, ST_COLS::SENDTAB_COL_EN)->setCheckState(Qt::Unchecked);
|
|
if (tokens.length() >= 9) {
|
|
for (int i = 1; i < ST_COLS::SENDTAB_COL_COUNT; i++)
|
|
{
|
|
if (i != ST_COLS::SENDTAB_COL_EXT && i != ST_COLS::SENDTAB_COL_REM) {
|
|
ui->tableSender->setItem(row, i, new QTableWidgetItem(QString(tokens[i])));
|
|
} else {
|
|
if (tokens[i] == "T") {
|
|
ui->tableSender->item(row, i)->setCheckState(Qt::Checked);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_BUS, new QTableWidgetItem(QString(tokens[1])));
|
|
ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_ID, new QTableWidgetItem(QString(tokens[2])));
|
|
ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_LEN, new QTableWidgetItem(QString(tokens[3])));
|
|
ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_DATA, new QTableWidgetItem(QString(tokens[4])));
|
|
ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_TRIGGER, new QTableWidgetItem(QString(tokens[5])));
|
|
ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_MODS, new QTableWidgetItem(QString(tokens[6])));
|
|
}
|
|
inhibitChanged = false;
|
|
for (int k = 0; k < ST_COLS::SENDTAB_COL_COUNT; k++) processCellChange(row, k);
|
|
|
|
}
|
|
}
|
|
inFile->close();
|
|
delete inFile;
|
|
}
|
|
|
|
void FrameSenderWindow::onCellDoubleTap(int row, int column)
|
|
{
|
|
if (column == ST_COLS::SENDTAB_COL_TRIGGER)
|
|
{
|
|
if (row == ui->tableSender->rowCount() - 1)
|
|
{
|
|
createBlankRow();
|
|
}
|
|
if (row >= sendingData.count())
|
|
{
|
|
FrameSendData tempData;
|
|
tempData.enabled = false;
|
|
tempData.setFrameType(QCanBusFrame::DataFrame);
|
|
tempData.setExtendedFrameFormat(false);
|
|
sendingData.append(tempData);
|
|
}
|
|
sendData = nullptr;
|
|
sendData = &sendingData[row];
|
|
td = new TriggerDialog(sendData->triggers);
|
|
if (td->exec() == QDialog::Accepted)
|
|
{
|
|
sendData->triggers.clear();
|
|
sendData->triggers.append(td->getUpdatedTriggers());
|
|
//now have to generate the actual trigger text
|
|
QString output;
|
|
foreach (Trigger trig, sendData->triggers)
|
|
{
|
|
output += td->buildEntry(trig) + ",";
|
|
}
|
|
output.chop(1); //don't want the trailing ,
|
|
inhibitChanged = true;
|
|
ui->tableSender->item(row, ST_COLS::SENDTAB_COL_TRIGGER)->setText(output);
|
|
inhibitChanged = false;
|
|
}
|
|
delete td;
|
|
td = nullptr;
|
|
}
|
|
}
|
|
|
|
void FrameSenderWindow::onCellChanged(int row, int col)
|
|
{
|
|
if (inhibitChanged) return;
|
|
qDebug() << "onCellChanged";
|
|
if (row == ui->tableSender->rowCount() - 1)
|
|
{
|
|
createBlankRow();
|
|
}
|
|
|
|
processCellChange(row, col);
|
|
}
|
|
|
|
/// <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 FrameSenderWindow::handleTick()
|
|
{
|
|
FrameSendData *sendData;
|
|
Trigger *trigger;
|
|
QList<CANFrame> sendingList;
|
|
|
|
if(mutex.tryLock())
|
|
{
|
|
int elapsed = elapsedTimer.restart();
|
|
if (elapsed == 0) elapsed = 1;
|
|
//Modifier modifier;
|
|
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) continue;
|
|
for (int j = 0; j < sendData->triggers.count(); j++)
|
|
{
|
|
trigger = &sendData->triggers[j];
|
|
if (trigger->maxCount > 0 && 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 = 0;
|
|
sendData->count++;
|
|
trigger->currCount++;
|
|
doModifiers(i);
|
|
updateGridRow(i);
|
|
sendingList.append(sendingData[i]);
|
|
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() << "framesenderwindow::handleTick() couldn't get mutex, elapsed is: " << elapsedTimer.elapsed();
|
|
}
|
|
|
|
}
|
|
|
|
/// <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 FrameSenderWindow::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 FrameSenderWindow::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* FrameSenderWindow::lookupFrame(int ID, int bus)
|
|
{
|
|
if (!frameCache.contains(ID)) return nullptr;
|
|
|
|
if (bus == -1 || frameCache[ID].bus == bus) return &frameCache[ID];
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Process a single line from the dataGrid. Right now it seems to not trigger at all after the first adding of the code but that seems to maybe
|
|
/// be because whichever field you where just in will show up as nothing to the code.
|
|
/// </summary>
|
|
/// <param name="line"></param>
|
|
void FrameSenderWindow::processModifierText(int line)
|
|
{
|
|
qDebug() << "processModifierText";
|
|
QString modString;
|
|
//bool firstOp = true;
|
|
bool abort = false;
|
|
QString token;
|
|
ModifierOp thisOp;
|
|
|
|
//Example line:
|
|
//d0 = D0 + 1,d1 = id:0x200:d3 + id:0x200:d4 AND 0xF0 - Original version
|
|
//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
|
|
|
|
//yeah, lots of operations on this one line but it's for a good cause. Removes the convenience English versions of the
|
|
//logical operators and replaces them with the math equivs. Also uppercases and removes all superfluous whitespace
|
|
modString = ui->tableSender->item(line, 9)->text().toUpper().trimmed().replace("AND", "&").replace("XOR", "^").replace("OR", "|").replace(" ", "");
|
|
if (modString != "")
|
|
{
|
|
QStringList mods = modString.split(',');
|
|
sendingData[line].modifiers.clear();
|
|
sendingData[line].modifiers.reserve(mods.length());
|
|
for (int i = 0; i < mods.length(); i++)
|
|
{
|
|
Modifier thisMod;
|
|
thisMod.destByte = 0;
|
|
|
|
QRegularExpression regex;
|
|
QRegularExpressionMatch match;
|
|
|
|
regex.setPattern("^\\[(\\w+)]=(\\w+)");
|
|
match = regex.match(mods[i]);
|
|
if (match.hasMatch())
|
|
{
|
|
thisMod.destByte = -1;
|
|
thisMod.signalName = match.captured(1);
|
|
mods[i] = match.captured(2);
|
|
thisMod.operations.clear();
|
|
}
|
|
else
|
|
{
|
|
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]);
|
|
if (token[0] == '~')
|
|
{
|
|
thisOp.first.notOper = true;
|
|
token = token.remove(0, 1); //remove the ~ character
|
|
}
|
|
else thisOp.first.notOper = false;
|
|
parseOperandString(token.split(":"), thisOp.first);
|
|
|
|
if (mods[i].length() < 2) {
|
|
abort = true;
|
|
thisOp.operation = ADDITION;
|
|
thisOp.second.ID = 0;
|
|
thisOp.second.databyte = 0;
|
|
thisOp.second.notOper = false;
|
|
thisMod.operations.append(thisOp);
|
|
}
|
|
|
|
while (!abort)
|
|
{
|
|
QString operation = Utility::grabOperation(mods[i]);
|
|
if (operation == "")
|
|
{
|
|
abort = true;
|
|
}
|
|
else
|
|
{
|
|
thisOp.operation = parseOperation(operation);
|
|
QString secondOp = Utility::grabAlphaNumeric(mods[i]);
|
|
if (secondOp.length() > 0 && secondOp[0] == '~')
|
|
{
|
|
thisOp.second.notOper = true;
|
|
secondOp = secondOp.remove(0, 1); //remove the ~ character
|
|
}
|
|
else thisOp.second.notOper = false;
|
|
thisOp.second.bus = sendingData[line].bus;
|
|
thisOp.second.ID = sendingData[line].frameId();
|
|
parseOperandString(secondOp.split(":"), thisOp.second);
|
|
thisMod.operations.append(thisOp);
|
|
}
|
|
|
|
thisOp.first.ID = -1; //shadow register
|
|
if (mods[i].length() < 2) abort = true;
|
|
}
|
|
|
|
sendingData[line].modifiers.append(thisMod);
|
|
}
|
|
}
|
|
//there is no else for the modifiers. We'll accept there not being any
|
|
}
|
|
|
|
void FrameSenderWindow::processTriggerText(int line)
|
|
{
|
|
qDebug() << "processTriggerText";
|
|
QString trigger;
|
|
|
|
//Example line:
|
|
//id0x200 5ms 10x bus0,1000ms,ID0x202 SIG[BMS_maxChargeCurrent;300]
|
|
//trigger has two levels of syntactic parsing. First you split by comma to get each
|
|
//actual trigger. Then you split by spaces to get the tokens within each trigger
|
|
trigger = ui->tableSender->item(line, ST_COLS::SENDTAB_COL_TRIGGER)->text().toUpper();
|
|
if (trigger != "")
|
|
{
|
|
QStringList triggers = trigger.split(',');
|
|
sendingData[line].triggers.clear();
|
|
sendingData[line].triggers.reserve(triggers.length());
|
|
for (int k = 0; k < triggers.length(); k++)
|
|
{
|
|
Trigger thisTrigger;
|
|
//start out by setting defaults - should be moved to constructor for class Trigger.
|
|
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;
|
|
|
|
QStringList trigToks = triggers[k].split(' ');
|
|
for (int x = 0; x < trigToks.length(); x++)
|
|
{
|
|
QString tok = trigToks.at(x);
|
|
if (tok.left(2) == "ID")
|
|
{
|
|
thisTrigger.ID = Utility::ParseStringToNum(tok.right(tok.length() - 2));
|
|
//if (thisTrigger.maxCount == -1) thisTrigger.maxCount = 0;
|
|
|
|
if (thisTrigger.milliseconds == -1) thisTrigger.milliseconds = 0; //by default don't count, just send it upon trigger
|
|
thisTrigger.readyCount = false; //won't try counting until trigger hits
|
|
thisTrigger.triggerMask |= TriggerMask::TRG_ID;
|
|
}
|
|
else if (tok.endsWith("MS"))
|
|
{
|
|
thisTrigger.milliseconds = Utility::ParseStringToNum(tok.left(tok.length()-2));
|
|
thisTrigger.triggerMask |= TriggerMask::TRG_MS;
|
|
//if (thisTrigger.maxCount == -1) thisTrigger.maxCount = 0;
|
|
//if (thisTrigger.ID == -1) thisTrigger.ID = 0;
|
|
}
|
|
else if (tok.endsWith("X"))
|
|
{
|
|
thisTrigger.maxCount = Utility::ParseStringToNum(tok.left(tok.length() - 1));
|
|
thisTrigger.triggerMask |= TriggerMask::TRG_COUNT;
|
|
//if (thisTrigger.ID == -1) thisTrigger.ID = 0;
|
|
if (thisTrigger.milliseconds == -1)
|
|
{
|
|
thisTrigger.milliseconds = 10;
|
|
thisTrigger.triggerMask |= TriggerMask::TRG_ID;
|
|
}
|
|
}
|
|
else if (tok.startsWith("BUS"))
|
|
{
|
|
thisTrigger.bus = Utility::ParseStringToNum(tok.right(tok.length() - 3));
|
|
thisTrigger.triggerMask |= TriggerMask::TRG_BUS;
|
|
}
|
|
else if (tok.startsWith("SIG["))
|
|
{
|
|
//remove the SIG[ header
|
|
tok = tok.right(tok.size() - 4);
|
|
int semi = tok.indexOf(';');
|
|
if (semi == -1) semi = tok.indexOf(']');
|
|
QString signame = tok.left(semi);
|
|
thisTrigger.sigName = signame;
|
|
thisTrigger.triggerMask |= TriggerMask::TRG_SIGNAL;
|
|
semi = tok.indexOf(';');
|
|
if (semi > -1) //have a signal value to match too
|
|
{
|
|
thisTrigger.triggerMask |= TriggerMask::TRG_SIGVAL;
|
|
int end = tok.indexOf(']');
|
|
thisTrigger.sigValueDbl =
|
|
tok.mid(semi + 1, end - semi - 1)
|
|
.toDouble();
|
|
}
|
|
}
|
|
}
|
|
//now, find anything that wasn't set and set it to defaults
|
|
//if (thisTrigger.maxCount == -1) thisTrigger.maxCount = 0;
|
|
//if (thisTrigger.milliseconds == -1) thisTrigger.milliseconds = 100;
|
|
//if (thisTrigger.ID == -1) thisTrigger.ID = 0;
|
|
sendingData[line].triggers.append(thisTrigger);
|
|
}
|
|
}
|
|
else //setup a default single shot trigger
|
|
{
|
|
Trigger thisTrigger;
|
|
thisTrigger.bus = -1;
|
|
thisTrigger.ID = 0;
|
|
thisTrigger.maxCount = 1;
|
|
thisTrigger.milliseconds = 10;
|
|
thisTrigger.triggerMask = TriggerMask::TRG_MS;
|
|
sendingData[line].triggers.append(thisTrigger);
|
|
}
|
|
}
|
|
|
|
//Turn a set of tokens into an operand
|
|
void FrameSenderWindow::parseOperandString(QStringList tokens, ModifierOperand &operand)
|
|
{
|
|
qDebug() << "parseOperandString";
|
|
//example string -> bus:0:id:200:d3
|
|
|
|
operand.bus = -1;
|
|
operand.ID = -2;
|
|
operand.databyte = 0;
|
|
|
|
for (int i = 0; i < tokens.length(); i++)
|
|
{
|
|
if (tokens[i] == "BUS")
|
|
{
|
|
operand.bus = Utility::ParseStringToNum(tokens[++i]);
|
|
}
|
|
else if (tokens[i] == "ID")
|
|
{
|
|
operand.ID = Utility::ParseStringToNum(tokens[++i]);
|
|
}
|
|
else if (tokens[i].length() == 2 && tokens[i].startsWith("D"))
|
|
{
|
|
operand.databyte = Utility::ParseStringToNum(tokens[i].right(tokens[i].length() - 1));
|
|
}
|
|
else
|
|
{
|
|
operand.databyte = Utility::ParseStringToNum(tokens[i]);
|
|
operand.ID = 0; //special ID to show this is a number not a look up.
|
|
}
|
|
}
|
|
}
|
|
|
|
ModifierOperationType FrameSenderWindow::parseOperation(QString op)
|
|
{
|
|
qDebug() << "parseOperation";
|
|
if (op == "+") return ADDITION;
|
|
if (op == "-") return SUBTRACTION;
|
|
if (op == "*") return MULTIPLICATION;
|
|
if (op == "/") return DIVISION;
|
|
if (op == "&") return AND;
|
|
if (op == "|") return OR;
|
|
if (op == "^") return XOR;
|
|
if (op == "%") return MOD;
|
|
return ADDITION;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update the DataGridView with the newest info from sendingData
|
|
/// </summary>
|
|
/// <param name="idx"></param>
|
|
void FrameSenderWindow::updateGridRow(int idx)
|
|
{
|
|
//qDebug() << "updateGridRow";
|
|
|
|
inhibitChanged = true;
|
|
FrameSendData *temp = &sendingData[idx];
|
|
int gridLine = idx;
|
|
QString dataString;
|
|
QTableWidgetItem *item = ui->tableSender->item(gridLine, ST_COLS::SENDTAB_COL_COUNT);
|
|
const unsigned char *data = reinterpret_cast<const unsigned char *>(temp->payload().constData());
|
|
int dataLen = temp->payload().length();
|
|
|
|
if (item == nullptr)
|
|
{
|
|
item = new QTableWidgetItem();
|
|
item->setText(QString::number(temp->count));
|
|
ui->tableSender->setItem(gridLine, ST_COLS::SENDTAB_COL_COUNT, item);
|
|
}
|
|
else
|
|
{
|
|
item->setText(QString::number(temp->count));
|
|
}
|
|
|
|
if (temp->frameType() != QCanBusFrame::RemoteRequestFrame)
|
|
{
|
|
for (int i = 0; i < dataLen; i++)
|
|
{
|
|
dataString.append(Utility::formatNumber(data[i]));
|
|
dataString.append(" ");
|
|
}
|
|
ui->tableSender->item(gridLine, ST_COLS::SENDTAB_COL_DATA)->setText(dataString);
|
|
}
|
|
else
|
|
{
|
|
ui->tableSender->item(gridLine, ST_COLS::SENDTAB_COL_DATA)->setText("");
|
|
}
|
|
inhibitChanged = false;
|
|
}
|
|
|
|
void FrameSenderWindow::processCellChange(int line, int col)
|
|
{
|
|
qDebug() << "processCellChange";
|
|
FrameSendData tempData;
|
|
QStringList tokens;
|
|
int tempVal;
|
|
DBC_MESSAGE *msg;
|
|
|
|
//if this is a new line then create the base object for the line
|
|
if (line >= sendingData.count())
|
|
{
|
|
FrameSendData tempData;
|
|
tempData.enabled = false;
|
|
tempData.bus = 0; // Initialize bus to 0
|
|
tempData.setFrameType(QCanBusFrame::DataFrame);
|
|
tempData.setExtendedFrameFormat(false);
|
|
tempData.setFrameId(0); // Initialize ID
|
|
sendingData.append(tempData);
|
|
}
|
|
|
|
sendingData[line].count = 0;
|
|
|
|
int numBuses = CANConManager::getInstance()->getNumBuses();
|
|
QByteArray arr;
|
|
|
|
switch (col)
|
|
{
|
|
case ST_COLS::SENDTAB_COL_EN: //Enable check box
|
|
if (ui->tableSender->item(line, 0)->checkState() == Qt::Checked)
|
|
{
|
|
sendingData[line].enabled = true;
|
|
// Process all fields to ensure frame is properly configured
|
|
// Do this in a specific order: Bus, ID, Ext, Len, Data, Triggers
|
|
inhibitChanged = true;
|
|
|
|
// Only process fields that have values
|
|
if (ui->tableSender->item(line, ST_COLS::SENDTAB_COL_BUS) &&
|
|
!ui->tableSender->item(line, ST_COLS::SENDTAB_COL_BUS)->text().isEmpty())
|
|
processCellChange(line, ST_COLS::SENDTAB_COL_BUS);
|
|
|
|
if (ui->tableSender->item(line, ST_COLS::SENDTAB_COL_ID) &&
|
|
!ui->tableSender->item(line, ST_COLS::SENDTAB_COL_ID)->text().isEmpty())
|
|
processCellChange(line, ST_COLS::SENDTAB_COL_ID);
|
|
|
|
if (ui->tableSender->item(line, ST_COLS::SENDTAB_COL_EXT))
|
|
processCellChange(line, ST_COLS::SENDTAB_COL_EXT);
|
|
|
|
if (ui->tableSender->item(line, ST_COLS::SENDTAB_COL_LEN) &&
|
|
!ui->tableSender->item(line, ST_COLS::SENDTAB_COL_LEN)->text().isEmpty())
|
|
processCellChange(line, ST_COLS::SENDTAB_COL_LEN);
|
|
|
|
if (ui->tableSender->item(line, ST_COLS::SENDTAB_COL_DATA) &&
|
|
!ui->tableSender->item(line, ST_COLS::SENDTAB_COL_DATA)->text().isEmpty())
|
|
processCellChange(line, ST_COLS::SENDTAB_COL_DATA);
|
|
|
|
if (ui->tableSender->item(line, ST_COLS::SENDTAB_COL_TRIGGER))
|
|
processCellChange(line, ST_COLS::SENDTAB_COL_TRIGGER);
|
|
|
|
inhibitChanged = false;
|
|
}
|
|
else sendingData[line].enabled = false;
|
|
break;
|
|
case ST_COLS::SENDTAB_COL_BUS: //Bus designation
|
|
tempVal = Utility::ParseStringToNum(ui->tableSender->item(line, ST_COLS::SENDTAB_COL_BUS)->text());
|
|
if (tempVal < -1) tempVal = -1;
|
|
if (tempVal >= numBuses) tempVal = numBuses - 1;
|
|
sendingData[line].bus = tempVal;
|
|
qDebug() << "Setting bus to " << tempVal;
|
|
break;
|
|
case ST_COLS::SENDTAB_COL_ID: //ID field
|
|
tempVal = Utility::ParseStringToNum(ui->tableSender->item(line, ST_COLS::SENDTAB_COL_ID)->text());
|
|
if (tempVal < 0) tempVal = 0;
|
|
if (tempVal > 0x7FFFFFFF) tempVal = 0x7FFFFFFF;
|
|
sendingData[line].setFrameId(tempVal);
|
|
if (sendingData[line].frameId() > 0x7FF) {
|
|
sendingData[line].setExtendedFrameFormat(true);
|
|
ui->tableSender->blockSignals(true);
|
|
ui->tableSender->item(line, ST_COLS::SENDTAB_COL_EXT)->setCheckState(Qt::Checked);
|
|
ui->tableSender->blockSignals(false);
|
|
}
|
|
msg = dbcHandler->findMessage(sendingData[line].frameId());
|
|
if (msg)
|
|
{
|
|
ui->tableSender->blockSignals(true);
|
|
ui->tableSender->item(line, ST_COLS::SENDTAB_COL_MSGNAME)->setText(msg->name);
|
|
ui->tableSender->item(line, ST_COLS::SENDTAB_COL_LEN)->setText(QString::number(msg->len));
|
|
ui->tableSender->blockSignals(false);
|
|
}
|
|
qDebug() << "setting ID to " << tempVal << "hex:" << QString::number(tempVal, 16);
|
|
break;
|
|
case ST_COLS::SENDTAB_COL_LEN:
|
|
tempVal = Utility::ParseStringToNum(ui->tableSender->item(line, ST_COLS::SENDTAB_COL_LEN)->text());
|
|
if (tempVal < 0) tempVal = 0;
|
|
if (tempVal > 8) tempVal = 8;
|
|
// Only resize if the current payload is different size
|
|
if (sendingData[line].payload().size() != tempVal)
|
|
{
|
|
arr = sendingData[line].payload();
|
|
arr.resize(tempVal);
|
|
sendingData[line].setPayload(arr);
|
|
}
|
|
break;
|
|
case ST_COLS::SENDTAB_COL_EXT:
|
|
if (ui->tableSender->item(line, ST_COLS::SENDTAB_COL_EXT)->checkState() == Qt::Checked) {
|
|
sendingData[line].setExtendedFrameFormat(true);
|
|
} else {
|
|
sendingData[line].setExtendedFrameFormat(false);
|
|
}
|
|
break;
|
|
case ST_COLS::SENDTAB_COL_REM:
|
|
if (ui->tableSender->item(line, ST_COLS::SENDTAB_COL_REM)->checkState() == Qt::Checked) {
|
|
sendingData[line].setFrameType(QCanBusFrame::RemoteRequestFrame);
|
|
} else {
|
|
sendingData[line].setFrameType(QCanBusFrame::DataFrame);
|
|
}
|
|
break;
|
|
case ST_COLS::SENDTAB_COL_DATA: //Data bytes
|
|
#if QT_VERSION >= QT_VERSION_CHECK( 5, 14, 0 )
|
|
tokens = ui->tableSender->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]));
|
|
}
|
|
sendingData[line].setPayload(arr);
|
|
break;
|
|
case ST_COLS::SENDTAB_COL_TRIGGER: //triggers
|
|
processTriggerText(line);
|
|
break;
|
|
case ST_COLS::SENDTAB_COL_MODS: //modifiers
|
|
processModifierText(line);
|
|
break;
|
|
}
|
|
}
|
|
|