Used in signal editor to help make it easier to know what bits are used. Added single and double precision float code where appropriate. They weren't supported at all before. But, that code isn't really tested yet. Added support for multiplexed signals. Most all the code for the UI is done and some of the backend but it isn't hooked into the actual signal processing code just yet. Updated program version number.
574 lines
18 KiB
C++
574 lines
18 KiB
C++
#include "dbcsignaleditor.h"
|
|
#include "ui_dbcsignaleditor.h"
|
|
#include <QDateTime>
|
|
#include <QDebug>
|
|
#include <QMenu>
|
|
#include <QSettings>
|
|
|
|
DBCSignalEditor::DBCSignalEditor(DBCHandler *handler, QWidget *parent) :
|
|
QDialog(parent),
|
|
ui(new Ui::DBCSignalEditor)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
readSettings();
|
|
|
|
qsrand(QDateTime::currentMSecsSinceEpoch());
|
|
|
|
dbcHandler = handler;
|
|
dbcMessage = NULL;
|
|
currentSignal = NULL;
|
|
|
|
QStringList headers2;
|
|
headers2 << "Value" << "Text";
|
|
ui->valuesTable->setColumnCount(2);
|
|
ui->valuesTable->setColumnWidth(0, 200);
|
|
ui->valuesTable->setColumnWidth(1, 440);
|
|
ui->valuesTable->setHorizontalHeaderLabels(headers2);
|
|
ui->valuesTable->horizontalHeader()->setStretchLastSection(true);
|
|
|
|
ui->comboType->addItem("UNSIGNED INTEGER");
|
|
ui->comboType->addItem("SIGNED INTEGER");
|
|
ui->comboType->addItem("SINGLE PRECISION");
|
|
ui->comboType->addItem("DOUBLE PRECISION");
|
|
ui->comboType->addItem("STRING");
|
|
|
|
for (int x = 0; x < dbcHandler->dbc_nodes.count(); x++)
|
|
{
|
|
ui->comboReceiver->addItem(dbcHandler->dbc_nodes[x].name);
|
|
}
|
|
|
|
connect(ui->signalsList, SIGNAL(currentRowChanged(int)), this, SLOT(clickSignalList(int)));
|
|
connect(ui->bitfield, SIGNAL(gridClicked(int,int)), this, SLOT(bitfieldClicked(int,int)));
|
|
connect(ui->signalsList, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomMenuSignals(QPoint)));
|
|
ui->signalsList->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(ui->valuesTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomMenuValues(QPoint)));
|
|
ui->valuesTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(ui->valuesTable, SIGNAL(cellChanged(int,int)), this, SLOT(onValuesCellChanged(int,int)));
|
|
|
|
//now with 100% more lambda expressions just to make it interesting (and shorter, and easier...)
|
|
connect(ui->cbIntelFormat, &QCheckBox::toggled,
|
|
[=]()
|
|
{
|
|
if (currentSignal == NULL) return;
|
|
currentSignal->intelByteOrder = ui->cbIntelFormat->isChecked();
|
|
if (currentSignal->valType == SP_FLOAT || currentSignal->valType == DP_FLOAT)
|
|
currentSignal->intelByteOrder = false;
|
|
fillSignalForm(currentSignal);
|
|
});
|
|
|
|
connect(ui->comboReceiver, &QComboBox::currentTextChanged,
|
|
[=]()
|
|
{
|
|
if (currentSignal == NULL) return;
|
|
currentSignal->receiver = dbcHandler->findNodeByName(ui->comboReceiver->currentText());
|
|
});
|
|
connect(ui->comboType, &QComboBox::currentTextChanged,
|
|
[=]()
|
|
{
|
|
if (currentSignal == NULL) return;
|
|
switch (ui->comboType->currentIndex())
|
|
{
|
|
case 0:
|
|
currentSignal->valType = UNSIGNED_INT;
|
|
break;
|
|
case 1:
|
|
currentSignal->valType = SIGNED_INT;
|
|
break;
|
|
case 2:
|
|
currentSignal->valType = SP_FLOAT;
|
|
currentSignal->intelByteOrder = false;
|
|
if (currentSignal->startBit > 39) currentSignal->startBit = 39;
|
|
currentSignal->signalSize = 32;
|
|
break;
|
|
case 3:
|
|
currentSignal->valType = DP_FLOAT;
|
|
currentSignal->intelByteOrder = false;
|
|
currentSignal->startBit = 7; //has to be!
|
|
currentSignal->signalSize = 64;
|
|
break;
|
|
case 4:
|
|
currentSignal->valType = STRING;
|
|
break;
|
|
}
|
|
fillSignalForm(currentSignal);
|
|
});
|
|
connect(ui->txtBias, &QLineEdit::editingFinished,
|
|
[=]()
|
|
{
|
|
if (currentSignal == NULL) return;
|
|
double temp;
|
|
bool result;
|
|
temp = ui->txtBias->text().toDouble(&result);
|
|
if (result) currentSignal->bias = temp;
|
|
});
|
|
|
|
connect(ui->txtMaxVal, &QLineEdit::editingFinished,
|
|
[=]()
|
|
{
|
|
if (currentSignal == NULL) return;
|
|
double temp;
|
|
bool result;
|
|
temp = ui->txtMaxVal->text().toDouble(&result);
|
|
if (result) currentSignal->max = temp;
|
|
});
|
|
|
|
connect(ui->txtMinVal, &QLineEdit::editingFinished,
|
|
[=]()
|
|
{
|
|
if (currentSignal == NULL) return;
|
|
double temp;
|
|
bool result;
|
|
temp = ui->txtMinVal->text().toDouble(&result);
|
|
if (result) currentSignal->min = temp;
|
|
});
|
|
connect(ui->txtScale, &QLineEdit::editingFinished,
|
|
[=]()
|
|
{
|
|
if (currentSignal == NULL) return;
|
|
double temp;
|
|
bool result;
|
|
temp = ui->txtScale->text().toDouble(&result);
|
|
if (result) currentSignal->factor = temp;
|
|
});
|
|
connect(ui->txtComment, &QLineEdit::editingFinished,
|
|
[=]()
|
|
{
|
|
if (currentSignal == NULL) return;
|
|
currentSignal->comment = ui->txtComment->text();
|
|
});
|
|
|
|
connect(ui->txtUnitName, &QLineEdit::editingFinished,
|
|
[=]()
|
|
{
|
|
if (currentSignal == NULL) return;
|
|
currentSignal->unitName = ui->txtUnitName->text();
|
|
});
|
|
connect(ui->txtBitLength, &QLineEdit::editingFinished,
|
|
[=]()
|
|
{
|
|
if (currentSignal == NULL) return;
|
|
int temp;
|
|
temp = Utility::ParseStringToNum(ui->txtBitLength->text());
|
|
if (temp < 0) return;
|
|
if (temp > 63) return;
|
|
if (currentSignal->valType != SP_FLOAT && currentSignal->valType != DP_FLOAT)
|
|
currentSignal->signalSize = temp;
|
|
fillSignalForm(currentSignal);
|
|
});
|
|
connect(ui->txtName, &QLineEdit::editingFinished,
|
|
[=]()
|
|
{
|
|
if (currentSignal == NULL) return;
|
|
currentSignal->name = ui->txtName->text();
|
|
//need to update the list too.
|
|
ui->signalsList->currentItem()->setText(currentSignal->name);
|
|
});
|
|
|
|
connect(ui->txtMultiplexValue, &QLineEdit::editingFinished,
|
|
[=]()
|
|
{
|
|
if (currentSignal == NULL) return;
|
|
int temp;
|
|
temp = Utility::ParseStringToNum(ui->txtMultiplexValue->text());
|
|
//TODO: could look up the multiplexor and ensure that the value is within a range that the multiplexor could return
|
|
currentSignal->multiplexValue = temp;
|
|
});
|
|
connect(ui->rbMultiplexed, &QRadioButton::toggled,
|
|
[=](bool state)
|
|
{
|
|
if (state) //signal is now set as a multiplexed signal
|
|
{
|
|
currentSignal->isMultiplexed = true;
|
|
currentSignal->isMultiplexor = false;
|
|
}
|
|
});
|
|
|
|
connect(ui->rbMultiplexor, &QRadioButton::toggled,
|
|
[=](bool state)
|
|
{
|
|
if (state) //signal is now set as a multiplexed signal
|
|
{
|
|
currentSignal->isMultiplexed = false;
|
|
currentSignal->isMultiplexor = true;
|
|
}
|
|
});
|
|
|
|
connect(ui->rbNotMulti, &QRadioButton::toggled,
|
|
[=](bool state)
|
|
{
|
|
if (state) //signal is now set as a multiplexed signal
|
|
{
|
|
currentSignal->isMultiplexed = false;
|
|
currentSignal->isMultiplexor = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
DBCSignalEditor::~DBCSignalEditor()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
void DBCSignalEditor::closeEvent(QCloseEvent *event)
|
|
{
|
|
Q_UNUSED(event);
|
|
writeSettings();
|
|
}
|
|
|
|
void DBCSignalEditor::readSettings()
|
|
{
|
|
QSettings settings;
|
|
if (settings.value("Main/SaveRestorePositions", false).toBool())
|
|
{
|
|
resize(settings.value("DBCSignalEditor/WindowSize", QSize(800, 572)).toSize());
|
|
move(settings.value("DBCSignalEditor/WindowPos", QPoint(100, 100)).toPoint());
|
|
}
|
|
}
|
|
|
|
void DBCSignalEditor::writeSettings()
|
|
{
|
|
QSettings settings;
|
|
|
|
if (settings.value("Main/SaveRestorePositions", false).toBool())
|
|
{
|
|
settings.setValue("DBCSignalEditor/WindowSize", size());
|
|
settings.setValue("DBCSignalEditor/WindowPos", pos());
|
|
}
|
|
}
|
|
|
|
|
|
void DBCSignalEditor::setMessageRef(DBC_MESSAGE *msg)
|
|
{
|
|
dbcMessage = msg;
|
|
}
|
|
|
|
void DBCSignalEditor::showEvent(QShowEvent* event)
|
|
{
|
|
QDialog::showEvent(event);
|
|
|
|
refreshSignalsList();
|
|
currentSignal = NULL;
|
|
if (dbcMessage->msgSignals.count() > 0)
|
|
{
|
|
currentSignal = &dbcMessage->msgSignals[0];
|
|
fillSignalForm(currentSignal);
|
|
fillValueTable(currentSignal);
|
|
}
|
|
}
|
|
|
|
void DBCSignalEditor::onValuesCellChanged(int row,int col)
|
|
{
|
|
if (inhibitCellChanged) return;
|
|
if (row == ui->valuesTable->rowCount() - 1)
|
|
{
|
|
DBC_VAL newVal;
|
|
newVal.value = 0;
|
|
newVal.descript = "No Description";
|
|
currentSignal->valList.append(newVal);
|
|
qDebug() << "Created new entry in value list";
|
|
|
|
//QTableWidgetItem *widgetVal = new QTableWidgetItem(QString::number(newVal.value));
|
|
//ui->valuesTable->setItem(row, 0, widgetVal);
|
|
//QTableWidgetItem *widgetDesc = new QTableWidgetItem(newVal.descript);
|
|
//ui->valuesTable->setItem(row, 1, widgetDesc);
|
|
|
|
//add the blank at the end again
|
|
ui->valuesTable->insertRow(ui->valuesTable->rowCount());
|
|
}
|
|
|
|
if (col == 0)
|
|
{
|
|
currentSignal->valList[row].value = Utility::ParseStringToNum(ui->valuesTable->item(row, col)->text());
|
|
}
|
|
else if (col == 1)
|
|
{
|
|
currentSignal->valList[row].descript = ui->valuesTable->item(row, col)->text();
|
|
}
|
|
}
|
|
|
|
void DBCSignalEditor::onCustomMenuSignals(QPoint point)
|
|
{
|
|
QMenu *menu = new QMenu(this);
|
|
menu->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
menu->addAction(tr("Add a new signal"), this, SLOT(addNewSignal()));
|
|
menu->addAction(tr("Delete currently selected signal"), this, SLOT(deleteCurrentSignal()));
|
|
|
|
menu->popup(ui->signalsList->mapToGlobal(point));
|
|
}
|
|
|
|
void DBCSignalEditor::onCustomMenuValues(QPoint point)
|
|
{
|
|
QMenu *menu = new QMenu(this);
|
|
menu->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
menu->addAction(tr("Delete currently selected value"), this, SLOT(deleteCurrentValue()));
|
|
|
|
menu->popup(ui->valuesTable->mapToGlobal(point));
|
|
}
|
|
|
|
void DBCSignalEditor::addNewSignal()
|
|
{
|
|
int num = qrand() % 70000;
|
|
QString newName = "SIGNAL" + QString::number(num);
|
|
DBC_SIGNAL newSig;
|
|
newSig.name = newName;
|
|
newSig.bias = 0.0;
|
|
newSig.factor = 1.0;
|
|
newSig.intelByteOrder = true;
|
|
newSig.max = 0.0;
|
|
newSig.min = 0.0;
|
|
newSig.receiver = &dbcHandler->dbc_nodes[0];
|
|
newSig.signalSize = 1;
|
|
newSig.startBit = 0;
|
|
newSig.valType = UNSIGNED_INT;
|
|
newSig.isMultiplexed = false;
|
|
newSig.isMultiplexor = false;
|
|
newSig.multiplexValue = 0;
|
|
ui->signalsList->addItem(newName);
|
|
dbcMessage->msgSignals.append(newSig);
|
|
if (dbcMessage->msgSignals.count() == 1) clickSignalList(0);
|
|
}
|
|
|
|
void DBCSignalEditor::deleteCurrentSignal()
|
|
{
|
|
int currIdx = ui->signalsList->currentRow();
|
|
if (currIdx > -1)
|
|
{
|
|
delete(ui->signalsList->item(currIdx));
|
|
dbcMessage->msgSignals.removeAt(currIdx);
|
|
currentSignal = NULL;
|
|
currIdx = ui->signalsList->currentRow();
|
|
if (currIdx > -1) currentSignal = &dbcMessage->msgSignals[currIdx];
|
|
fillSignalForm(currentSignal);
|
|
fillValueTable(currentSignal);
|
|
}
|
|
}
|
|
|
|
void DBCSignalEditor::deleteCurrentValue()
|
|
{
|
|
int currIdx = ui->valuesTable->currentRow();
|
|
if (currIdx > -1)
|
|
{
|
|
ui->valuesTable->removeRow(currIdx);
|
|
currentSignal->valList.removeAt(currIdx);
|
|
}
|
|
}
|
|
|
|
void DBCSignalEditor::refreshSignalsList()
|
|
{
|
|
ui->signalsList->clear();
|
|
for (int x = 0; x < dbcMessage->msgSignals.count(); x++)
|
|
{
|
|
DBC_SIGNAL sig = dbcMessage->msgSignals.at(x);
|
|
ui->signalsList->addItem(sig.name);
|
|
}
|
|
}
|
|
|
|
void DBCSignalEditor::fillSignalForm(DBC_SIGNAL *sig)
|
|
{
|
|
unsigned char bitpattern[8];
|
|
|
|
generateUsedBits();
|
|
|
|
if (sig == NULL)
|
|
{
|
|
ui->txtName->setText("");
|
|
ui->txtBias->setText("");
|
|
ui->txtBitLength->setText("");
|
|
ui->txtComment->setText("");
|
|
ui->txtMaxVal->setText("");
|
|
ui->txtMinVal->setText("");
|
|
ui->txtScale->setText("");
|
|
ui->txtUnitName->setText("");
|
|
ui->txtMultiplexValue->setText("");
|
|
ui->rbMultiplexed->setChecked(false);
|
|
ui->rbMultiplexor->setChecked(false);
|
|
ui->rbNotMulti->setChecked(true);
|
|
memset(bitpattern, 0, 8); //clear it out
|
|
ui->bitfield->setReference(bitpattern, false);
|
|
ui->bitfield->updateData(bitpattern, true);
|
|
ui->comboReceiver->setCurrentIndex(0);
|
|
ui->comboType->setCurrentIndex(0);
|
|
return;
|
|
}
|
|
|
|
ui->txtName->setText(sig->name);
|
|
ui->txtBias->setText(QString::number(sig->bias));
|
|
ui->txtBitLength->setText(QString::number(sig->signalSize));
|
|
ui->txtMultiplexValue->setText(QString::number(sig->multiplexValue));
|
|
ui->txtComment->setText(sig->comment);
|
|
ui->txtMaxVal->setText(QString::number(sig->max));
|
|
ui->txtMinVal->setText(QString::number(sig->min));
|
|
ui->txtScale->setText(QString::number(sig->factor));
|
|
ui->txtUnitName->setText(sig->unitName);
|
|
ui->rbMultiplexed->setChecked(sig->isMultiplexed);
|
|
ui->rbMultiplexor->setChecked(sig->isMultiplexor);
|
|
ui->rbNotMulti->setChecked( !(sig->isMultiplexor | sig->isMultiplexed) );
|
|
|
|
memset(bitpattern, 0, 8); //clear it out first.
|
|
|
|
int startBit, endBit;
|
|
|
|
startBit = sig->startBit;
|
|
|
|
bitpattern[startBit / 8] |= 1 << (startBit % 8); //make the start bit a different color to set it apart
|
|
ui->bitfield->setReference(bitpattern, false);
|
|
|
|
if (sig->intelByteOrder)
|
|
{
|
|
endBit = startBit + sig->signalSize - 1;
|
|
if (startBit < 0) startBit = 0;
|
|
if (endBit > 63) endBit = 63;
|
|
for (int y = startBit; y <= endBit; y++)
|
|
{
|
|
int byt = y / 8;
|
|
bitpattern[byt] |= 1 << (y % 8);
|
|
}
|
|
}
|
|
else //big endian / motorola format
|
|
{
|
|
//much more irritating than the intel version...
|
|
int size = sig->signalSize;
|
|
while (size > 0)
|
|
{
|
|
int byt = startBit / 8;
|
|
bitpattern[byt] |= 1 << (startBit % 8);
|
|
size--;
|
|
if ((startBit % 8) == 0) startBit += 15;
|
|
else startBit--;
|
|
if (startBit > 63) startBit = 63;
|
|
}
|
|
}
|
|
|
|
ui->bitfield->updateData(bitpattern, true);
|
|
ui->cbIntelFormat->setChecked(sig->intelByteOrder);
|
|
|
|
switch (sig->valType)
|
|
{
|
|
case UNSIGNED_INT:
|
|
ui->comboType->setCurrentIndex(0);
|
|
break;
|
|
case SIGNED_INT:
|
|
ui->comboType->setCurrentIndex(1);
|
|
break;
|
|
case SP_FLOAT:
|
|
ui->comboType->setCurrentIndex(2);
|
|
break;
|
|
case DP_FLOAT:
|
|
ui->comboType->setCurrentIndex(3);
|
|
break;
|
|
case STRING:
|
|
ui->comboType->setCurrentIndex(4);
|
|
break;
|
|
}
|
|
|
|
for (int i = 0; i < ui->comboReceiver->count(); i++)
|
|
{
|
|
if (ui->comboReceiver->itemText(i) == sig->receiver->name)
|
|
{
|
|
ui->comboReceiver->setCurrentIndex(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DBCSignalEditor::fillValueTable(DBC_SIGNAL *sig)
|
|
{
|
|
int rowIdx;
|
|
|
|
inhibitCellChanged = true;
|
|
|
|
ui->valuesTable->clearContents();
|
|
ui->valuesTable->setRowCount(0);
|
|
|
|
if (sig == NULL) {
|
|
inhibitCellChanged = false;
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < sig->valList.count(); i++)
|
|
{
|
|
QTableWidgetItem *val = new QTableWidgetItem(Utility::formatNumber(sig->valList[i].value));
|
|
QTableWidgetItem *desc = new QTableWidgetItem(sig->valList[i].descript);
|
|
rowIdx = ui->valuesTable->rowCount();
|
|
ui->valuesTable->insertRow(rowIdx);
|
|
ui->valuesTable->setItem(rowIdx, 0, val);
|
|
ui->valuesTable->setItem(rowIdx, 1, desc);
|
|
}
|
|
|
|
//Add a blank row at the end to allow for adding records easily
|
|
ui->valuesTable->insertRow(ui->valuesTable->rowCount());
|
|
|
|
inhibitCellChanged = false;
|
|
}
|
|
|
|
void DBCSignalEditor::clickSignalList(int row)
|
|
{
|
|
if (row < 0) return;
|
|
//qDebug() << ui->signalsList->item(row)->text();
|
|
|
|
DBC_SIGNAL *thisSig = dbcHandler->findSignalByName(dbcMessage, ui->signalsList->item(row)->text());
|
|
if (thisSig == NULL) return;
|
|
currentSignal = thisSig;
|
|
fillSignalForm(thisSig);
|
|
fillValueTable(thisSig);
|
|
|
|
}
|
|
|
|
void DBCSignalEditor::bitfieldClicked(int x, int y)
|
|
{
|
|
int bit = (7 - x) + (y * 8);
|
|
if (currentSignal == NULL) return;
|
|
currentSignal->startBit = bit;
|
|
if (currentSignal->valType == SP_FLOAT)
|
|
{
|
|
if (currentSignal->startBit > 31) currentSignal->startBit = 39;
|
|
}
|
|
|
|
if (currentSignal->valType == DP_FLOAT)
|
|
currentSignal->startBit = 7;
|
|
fillSignalForm(currentSignal);
|
|
}
|
|
|
|
void DBCSignalEditor::generateUsedBits()
|
|
{
|
|
uint8_t usedBits[8] = {0,0,0,0,0,0,0,0};
|
|
int startBit, endBit;
|
|
|
|
for (int x = 0; x < dbcMessage->msgSignals.count(); x++)
|
|
{
|
|
DBC_SIGNAL sig = dbcMessage->msgSignals.at(x);
|
|
|
|
startBit = sig.startBit;
|
|
|
|
if (sig.intelByteOrder)
|
|
{
|
|
endBit = startBit + sig.signalSize - 1;
|
|
if (startBit < 0) startBit = 0;
|
|
if (endBit > 63) endBit = 63;
|
|
for (int y = startBit; y <= endBit; y++)
|
|
{
|
|
int byt = y / 8;
|
|
usedBits[byt] |= 1 << (y % 8);
|
|
}
|
|
}
|
|
else //big endian / motorola format
|
|
{
|
|
//much more irritating than the intel version...
|
|
int size = sig.signalSize;
|
|
while (size > 0)
|
|
{
|
|
int byt = startBit / 8;
|
|
usedBits[byt] |= 1 << (startBit % 8);
|
|
size--;
|
|
if ((startBit % 8) == 0) startBit += 15;
|
|
else startBit--;
|
|
if (startBit > 63) startBit = 63;
|
|
}
|
|
}
|
|
}
|
|
ui->bitfield->setUsed(usedBits, false);
|
|
}
|