diff --git a/README.md b/README.md index 7a2edea..ff088d2 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,36 @@ http://www.qcustomplot.com/ However, this source code is integrated into the source for SavvyCAN and one isn't required to download it separately. -The project now requires (yes, requires) at least QT 5.6.0 because of -a dependency on QtSerialBus which is still an optional feature. You -will need to download it when installing QT. \ No newline at end of file +This project will soon require 5.6.0 or higher because of an upcoming dependency on QSerialBus but currently +should compile with QT 5.5 + +Instructions for compiling with Ubuntu: + +sudo apt-get install qt5-default qtdeclarative5-dev libqt5serialport5-dev libudev-dev + +download and extract QScintilla (see above for link), assume it is extracted in QScintilla-gpl-2.9.1 + +cd ~/QScintilla-gpl-2.9.1/Qt4Qt5 + +qmake qscintilla.pro + +make + +sudo make install + +Optionally remove ~/QScintilla-gpl-2.9.1 + +cd ~ + +git clone https://github.com/collin80/SavvyCAN.git + +cd SavvyCAN + +qmake + +make + +Now run SavvyCAN + +./SavvyCAN + diff --git a/config.h b/config.h index 92a2526..5b093e6 100644 --- a/config.h +++ b/config.h @@ -1,7 +1,7 @@ #ifndef CONFIG #define CONFIG -#define VERSION 153 +#define VERSION 155 //try to keep this in sync. //SavvyCAN will complain if you connect a GVRET board with a revision diff --git a/dbc_classes.cpp b/dbc_classes.cpp index 8885b3f..03d4bc5 100644 --- a/dbc_classes.cpp +++ b/dbc_classes.cpp @@ -180,6 +180,7 @@ bool DBC_SIGNAL::processAsDouble(const CANFrame &frame, double &outValue) endResult = ((double)result * factor) + bias; result = (int64_t)endResult; } + /*TODO: It should be noted that the below floating point has not even been tested. For shame! Test it!*/ else if (valType == SP_FLOAT) { //The theory here is that we force the integer signal code to treat this as diff --git a/dbchandler.cpp b/dbchandler.cpp index 2851c60..057ecb1 100644 --- a/dbchandler.cpp +++ b/dbchandler.cpp @@ -764,8 +764,11 @@ DBCFile* DBCHandler::loadDBCFile(int idx) DBCFile newFile; newFile.loadFile(filename); loadedFiles.append(newFile); + + return &loadedFiles.last(); } - return &loadedFiles.last(); + + return NULL; } void DBCHandler::removeDBCFile(int idx) diff --git a/dbcloadsavewindow.cpp b/dbcloadsavewindow.cpp index e66a9b5..0ef704d 100644 --- a/dbcloadsavewindow.cpp +++ b/dbcloadsavewindow.cpp @@ -48,10 +48,12 @@ void DBCLoadSaveWindow::newFile() void DBCLoadSaveWindow::loadFile() { DBCFile *file = dbcHandler->loadDBCFile(-1); - int idx = ui->tableFiles->rowCount(); - ui->tableFiles->insertRow(ui->tableFiles->rowCount()); - ui->tableFiles->setItem(idx, 0, new QTableWidgetItem(file->getFullFilename())); - ui->tableFiles->setItem(idx, 1, new QTableWidgetItem("-1")); + if(file) { + int idx = ui->tableFiles->rowCount(); + ui->tableFiles->insertRow(ui->tableFiles->rowCount()); + ui->tableFiles->setItem(idx, 0, new QTableWidgetItem(file->getFullFilename())); + ui->tableFiles->setItem(idx, 1, new QTableWidgetItem("-1")); + } } void DBCLoadSaveWindow::saveFile() diff --git a/dbcmaineditor.cpp b/dbcmaineditor.cpp index 7a82e9b..596346e 100644 --- a/dbcmaineditor.cpp +++ b/dbcmaineditor.cpp @@ -382,16 +382,19 @@ void DBCMainEditor::onCellClickedMessage(int row, int col) { if (col == 3) //3 is the signals field. If clicked we go to the signals dialog { - QString idString = ui->MessagesTable->item(row, 0)->text(); - DBC_MESSAGE *message = dbcFile->messageHandler->findMsgByID(Utility::ParseStringToNum(idString)); - sigEditor->setMessageRef(message); - sigEditor->setFileIdx(fileIdx); - sigEditor->exec(); //blocks this window from being active until we're done - //now update the displayed # of signals - inhibitCellChanged = true; - QTableWidgetItem *replacement = new QTableWidgetItem(QString::number(message->sigHandler->getCount())); - ui->MessagesTable->setItem(row, col, replacement); - inhibitCellChanged = false; + QTableWidgetItem* msg = ui->MessagesTable->item(row, 0); + if(msg) { + QString idString = msg->text(); + DBC_MESSAGE *message = dbcFile->messageHandler->findMsgByID(Utility::ParseStringToNum(idString)); + sigEditor->setMessageRef(message); + sigEditor->setFileIdx(fileIdx); + sigEditor->exec(); //blocks this window from being active until we're done + //now update the displayed # of signals + inhibitCellChanged = true; + QTableWidgetItem *replacement = new QTableWidgetItem(QString::number(message->sigHandler->getCount())); + ui->MessagesTable->setItem(row, col, replacement); + inhibitCellChanged = false; + } } } diff --git a/dbcsignaleditor.cpp b/dbcsignaleditor.cpp index c7f73e0..9792439 100644 --- a/dbcsignaleditor.cpp +++ b/dbcsignaleditor.cpp @@ -172,7 +172,7 @@ DBCSignalEditor::DBCSignalEditor(DBCHandler *handler, QWidget *parent) : connect(ui->rbMultiplexed, &QRadioButton::toggled, [=](bool state) { - if (state) //signal is now set as a multiplexed signal + if (state && currentSignal) //signal is now set as a multiplexed signal { currentSignal->isMultiplexed = true; currentSignal->isMultiplexor = false; @@ -184,7 +184,7 @@ DBCSignalEditor::DBCSignalEditor(DBCHandler *handler, QWidget *parent) : connect(ui->rbMultiplexor, &QRadioButton::toggled, [=](bool state) { - if (state) //signal is now set as a multiplexed signal + if (state && currentSignal) //signal is now set as a multiplexed signal { //don't allow this signal to be a multiplexor if there is already one for this message. //if (dbcMessage->multiplexorSignal != currentSignal && dbcMessage->multiplexorSignal != NULL) return; //I spoke too soon above... @@ -198,7 +198,7 @@ DBCSignalEditor::DBCSignalEditor(DBCHandler *handler, QWidget *parent) : connect(ui->rbNotMulti, &QRadioButton::toggled, [=](bool state) { - if (state) //signal is now set as a multiplexed signal + if (state && currentSignal) //signal is now set as a multiplexed signal { currentSignal->isMultiplexed = false; currentSignal->isMultiplexor = false; @@ -260,19 +260,14 @@ void DBCSignalEditor::showEvent(QShowEvent* event) { QDialog::showEvent(event); - refreshSignalsList(); currentSignal = NULL; - if (dbcMessage->sigHandler->getCount() > 0) - { - currentSignal = dbcMessage->sigHandler->findSignalByIdx(0); - fillSignalForm(currentSignal); - fillValueTable(currentSignal); - } + refreshSignalsList(); } void DBCSignalEditor::onValuesCellChanged(int row,int col) { if (inhibitCellChanged) return; + if (row == ui->valuesTable->rowCount() - 1) { DBC_VAL newVal; @@ -340,9 +335,14 @@ void DBCSignalEditor::addNewSignal() newSig.isMultiplexor = false; newSig.multiplexValue = 0; newSig.parentMessage = dbcMessage; - ui->signalsList->addItem(newName); + dbcMessage->sigHandler->addSignal(newSig); - if (dbcMessage->sigHandler->getCount() == 1) clickSignalList(0); + + /* add item at the end of the list and select it */ + /* this will call clickSignalList */ + ui->signalsList->addItem(newName); + ui->signalsList->setCurrentRow(ui->signalsList->count()-1); + } void DBCSignalEditor::deleteCurrentSignal() @@ -353,10 +353,6 @@ void DBCSignalEditor::deleteCurrentSignal() delete(ui->signalsList->item(currIdx)); dbcMessage->sigHandler->removeSignal(currIdx); currentSignal = NULL; - currIdx = ui->signalsList->currentRow(); - if (currIdx > -1) currentSignal = dbcMessage->sigHandler->findSignalByIdx(currIdx); - fillSignalForm(currentSignal); - fillValueTable(currentSignal); } } @@ -373,21 +369,30 @@ void DBCSignalEditor::deleteCurrentValue() void DBCSignalEditor::refreshSignalsList() { ui->signalsList->clear(); + + for (int x = 0; x < dbcMessage->sigHandler->getCount(); x++) { DBC_SIGNAL *sig = dbcMessage->sigHandler->findSignalByIdx(x); ui->signalsList->addItem(sig->name); } + + if( ui->signalsList->count()>0 ) { + /* click first element */ + ui->signalsList->setCurrentRow(0); + } + else + clickSignalList(-1); } +/* fillSignalForm also handles group "enabled" state */ +/* WARNING: fillSignalForm can be called recursively since it is in the listener of cbIntelFormat */ void DBCSignalEditor::fillSignalForm(DBC_SIGNAL *sig) { unsigned char bitpattern[8]; - generateUsedBits(); - - if (sig == NULL) - { + if (sig == NULL) { + ui->groupBox->setEnabled(false); ui->txtName->setText(""); ui->txtBias->setText(""); ui->txtBitLength->setText(""); @@ -408,6 +413,10 @@ void DBCSignalEditor::fillSignalForm(DBC_SIGNAL *sig) return; } + /* we have a signal */ + ui->groupBox->setEnabled(true); + + generateUsedBits(); ui->txtName->setText(sig->name); ui->txtBias->setText(QString::number(sig->bias)); ui->txtBitLength->setText(QString::number(sig->signalSize)); @@ -489,6 +498,7 @@ void DBCSignalEditor::fillSignalForm(DBC_SIGNAL *sig) } } +/* fillValueTable also handles "enabled" state */ void DBCSignalEditor::fillValueTable(DBC_SIGNAL *sig) { int rowIdx; @@ -499,10 +509,13 @@ void DBCSignalEditor::fillValueTable(DBC_SIGNAL *sig) ui->valuesTable->setRowCount(0); if (sig == NULL) { + ui->valuesTable->setEnabled(false); inhibitCellChanged = false; return; } + ui->valuesTable->setEnabled(true); + for (int i = 0; i < sig->valList.count(); i++) { QTableWidgetItem *val = new QTableWidgetItem(Utility::formatNumber(sig->valList[i].value)); @@ -521,11 +534,9 @@ void DBCSignalEditor::fillValueTable(DBC_SIGNAL *sig) void DBCSignalEditor::clickSignalList(int row) { - if (row < 0) return; //qDebug() << ui->signalsList->item(row)->text(); - DBC_SIGNAL *thisSig = dbcMessage->sigHandler->findSignalByName(ui->signalsList->item(row)->text()); - if (thisSig == NULL) return; + DBC_SIGNAL *thisSig = (row<0) ? NULL : dbcMessage->sigHandler->findSignalByName(ui->signalsList->item(row)->text()); currentSignal = thisSig; fillSignalForm(thisSig); fillValueTable(thisSig); diff --git a/docs/make.bat b/docs/make.bat index da8b07a..22da6bc 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -1,263 +1,263 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source -set I18NSPHINXOPTS=%SPHINXOPTS% source -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% - set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. texinfo to make Texinfo files - echo. gettext to make PO message catalogs - echo. changes to make an overview over all changed/added/deprecated items - echo. xml to make Docutils-native XML files - echo. pseudoxml to make pseudoxml-XML files for display purposes - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - echo. coverage to run coverage check of the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - - -REM Check if sphinx-build is available and fallback to Python version if any -%SPHINXBUILD% 2> nul -if errorlevel 9009 goto sphinx_python -goto sphinx_ok - -:sphinx_python - -set SPHINXBUILD=python -m sphinx.__init__ -%SPHINXBUILD% 2> nul -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -:sphinx_ok - - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\SavvyCAN.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\SavvyCAN.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "latexpdf" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - cd %BUILDDIR%/latex - make all-pdf - cd %~dp0 - echo. - echo.Build finished; the PDF files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "latexpdfja" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - cd %BUILDDIR%/latex - make all-pdf-ja - cd %~dp0 - echo. - echo.Build finished; the PDF files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "texinfo" ( - %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. - goto end -) - -if "%1" == "gettext" ( - %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The message catalogs are in %BUILDDIR%/locale. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - if errorlevel 1 exit /b 1 - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - if errorlevel 1 exit /b 1 - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - if errorlevel 1 exit /b 1 - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -if "%1" == "coverage" ( - %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage - if errorlevel 1 exit /b 1 - echo. - echo.Testing of coverage in the sources finished, look at the ^ -results in %BUILDDIR%/coverage/python.txt. - goto end -) - -if "%1" == "xml" ( - %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The XML files are in %BUILDDIR%/xml. - goto end -) - -if "%1" == "pseudoxml" ( - %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. - goto end -) - -:end +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source +set I18NSPHINXOPTS=%SPHINXOPTS% source +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 2> nul +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\SavvyCAN.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\SavvyCAN.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/framefileio.cpp b/framefileio.cpp index 211b23e..2e96149 100644 --- a/framefileio.cpp +++ b/framefileio.cpp @@ -1,7 +1,10 @@ #include "framefileio.h" +#include #include +#include + FrameFileIO::FrameFileIO() { @@ -122,6 +125,7 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache filters.append(QString(tr("IXXAT MiniLog (*.csv *.CSV)"))); filters.append(QString(tr("CAN-DO Log (*.avc *.can *.evc *.qcc *.AVC *.CAN *.EVC *.QCC)"))); filters.append(QString(tr("Vehicle Spy (*.csv *.CSV)"))); + filters.append(QString(tr("Candump/Kayak (*.log *.LOG)"))); dialog.setFileMode(QFileDialog::ExistingFile); dialog.setNameFilters(filters); @@ -150,6 +154,7 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache if (dialog.selectedNameFilter() == filters[6]) result = loadIXXATFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[7]) result = loadCANDOFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[8]) result = loadVehicleSpyFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[9]) result = loadCanDumpFile(filename, frameCache); progress.cancel(); @@ -159,7 +164,13 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache fileName = fileList[fileList.length() - 1]; return true; } - else return false; + else + { + QMessageBox msgBox; + msgBox.setText("File load completed with errors.\r\nPerhaps you selected the wrong file type?"); + msgBox.exec(); + return false; + } } return false; } @@ -175,6 +186,7 @@ bool FrameFileIO::loadVehicleSpyFile(QString filename, QVector *frames QByteArray line; int lineCounter = 0; bool pastHeader = false; + bool foundErrors = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { @@ -189,6 +201,8 @@ bool FrameFileIO::loadVehicleSpyFile(QString filename, QVector *frames if (lineCounter == 2) pastHeader = true; } + if (inFile->atEnd()) foundErrors = true; + while (!inFile->atEnd()) { lineCounter++; if (lineCounter > 100) @@ -199,30 +213,33 @@ bool FrameFileIO::loadVehicleSpyFile(QString filename, QVector *frames line = inFile->readLine().simplified().toUpper(); QList tokens = line.split(','); - thisFrame.bus = 0; - thisFrame.timestamp = tokens[1].toDouble() * 1000000.0; - if (tokens[5].startsWith("T")) thisFrame.isReceived = false; - else thisFrame.isReceived = true; - thisFrame.ID = tokens[9].toInt(NULL, 16); - if (tokens[11].startsWith("T")) thisFrame.extended = true; - else thisFrame.extended = false; - - thisFrame.len = 0; - for (int i = 0; i < 8; i++) + if (tokens.length() > 20) { - if (tokens[12 + i].length() > 0) - { - thisFrame.data[i] = tokens[12 + i].toInt(NULL, 16); - thisFrame.len++; - } - else break; - } + thisFrame.bus = 0; + thisFrame.timestamp = tokens[1].toDouble() * 1000000.0; + if (tokens[5].startsWith("T")) thisFrame.isReceived = false; + else thisFrame.isReceived = true; + thisFrame.ID = tokens[9].toInt(NULL, 16); + if (tokens[11].startsWith("T")) thisFrame.extended = true; + else thisFrame.extended = false; - frames->append(thisFrame); + thisFrame.len = 0; + for (int i = 0; i < 8; i++) + { + if (tokens[12 + i].length() > 0) + { + thisFrame.data[i] = tokens[12 + i].toInt(NULL, 16); + thisFrame.len++; + } + else break; + } + frames->append(thisFrame); + } + else foundErrors = true; } inFile->close(); delete inFile; - return true; + return !foundErrors; } bool FrameFileIO::saveVehicleSpyFile(QString filename, const QVector *frames) @@ -258,6 +275,7 @@ bool FrameFileIO::loadCRTDFile(QString filename, QVector* frames) CANFrame thisFrame; QByteArray line; int lineCounter = 0; + bool foundErrors = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { @@ -279,44 +297,48 @@ bool FrameFileIO::loadCRTDFile(QString filename, QVector* frames) { QList tokens = line.split(' '); int multiplier; - int idxOfDecimal = tokens[0].indexOf('.'); - if (idxOfDecimal > -1) { - //int decimalPlaces = tokens[0].length() - tokens[0].indexOf('.') - 1; - //the result of the above is the # of digits after the decimal. - //This program deals in microsecond so turn the value into microseconds - multiplier = 1000000; //turn the decimal into full microseconds - } - else + if (tokens.length() > 3) { - multiplier = 1; //special case. Assume no decimal means microseconds - } - //qDebug() << "decimal places " << decimalPlaces; - thisFrame.timestamp = (int64_t)(tokens[0].toDouble() * multiplier); - char firstChar = tokens[1].left(1)[0]; - if (firstChar == 'R' || firstChar == 'T') - { - thisFrame.ID = tokens[2].toInt(NULL, 16); - if (tokens[1] == "R29" || tokens[1] == "T29") thisFrame.extended = true; - else thisFrame.extended = false; - if (firstChar == 'T') thisFrame.isReceived = false; - else thisFrame.isReceived = true; - thisFrame.bus = 0; - thisFrame.len = tokens.length() - 3; - for (int d = 0; d < thisFrame.len; d++) + int idxOfDecimal = tokens[0].indexOf('.'); + if (idxOfDecimal > -1) { + //int decimalPlaces = tokens[0].length() - tokens[0].indexOf('.') - 1; + //the result of the above is the # of digits after the decimal. + //This program deals in microsecond so turn the value into microseconds + multiplier = 1000000; //turn the decimal into full microseconds + } + else { - if (tokens[d + 3] != "") + multiplier = 1; //special case. Assume no decimal means microseconds + } + //qDebug() << "decimal places " << decimalPlaces; + thisFrame.timestamp = (int64_t)(tokens[0].toDouble() * multiplier); + char firstChar = tokens[1].left(1)[0]; + if (firstChar == 'R' || firstChar == 'T') + { + thisFrame.ID = tokens[2].toInt(NULL, 16); + if (tokens[1] == "R29" || tokens[1] == "T29") thisFrame.extended = true; + else thisFrame.extended = false; + if (firstChar == 'T') thisFrame.isReceived = false; + else thisFrame.isReceived = true; + thisFrame.bus = 0; + thisFrame.len = tokens.length() - 3; + for (int d = 0; d < thisFrame.len; d++) { - thisFrame.data[d] = tokens[d + 3].toInt(NULL, 16); + if (tokens[d + 3] != "") + { + thisFrame.data[d] = tokens[d + 3].toInt(NULL, 16); + } + else thisFrame.data[d] = 0; } - else thisFrame.data[d] = 0; + frames->append(thisFrame); } - frames->append(thisFrame); } + else foundErrors = true; } } inFile->close(); delete inFile; - return true; + return !foundErrors; } bool FrameFileIO::saveCRTDFile(QString filename, const QVector* frames) @@ -380,6 +402,7 @@ bool FrameFileIO::loadNativeCSVFile(QString filename, QVector* frames) int fileVersion = 1; long long timeStamp = Utility::GetTimeMS(); int lineCounter = 0; + bool foundErrors = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { @@ -402,47 +425,52 @@ bool FrameFileIO::loadNativeCSVFile(QString filename, QVector* frames) if (line.length() > 2) { QList tokens = line.split(','); - if (tokens[0].length() > 3) + if (tokens.length() >= 6) { - long long temp = tokens[0].right(10).toLongLong(); - thisFrame.timestamp = temp; - } - else - { - timeStamp += 5; - thisFrame.timestamp = timeStamp; - } + if (tokens[0].length() > 3) + { + long long temp = tokens[0].right(10).toLongLong(); + thisFrame.timestamp = temp; + } + else + { + timeStamp += 5; + thisFrame.timestamp = timeStamp; + } - thisFrame.ID = tokens[1].toInt(NULL, 16); - if (tokens[2].toUpper().contains("TRUE")) thisFrame.extended = 1; - else thisFrame.extended = 0; + thisFrame.ID = tokens[1].toInt(NULL, 16); + if (tokens[2].toUpper().contains("TRUE")) thisFrame.extended = 1; + else thisFrame.extended = 0; - if (fileVersion == 1) - { - thisFrame.isReceived = true; - thisFrame.bus = tokens[3].toInt(); - thisFrame.len = tokens[4].toInt(); - for (int c = 0; c < 8; c++) thisFrame.data[c] = 0; - for (int d = 0; d < thisFrame.len; d++) - thisFrame.data[d] = tokens[5 + d].toInt(NULL, 16); - } - else if (fileVersion == 2) - { - if (tokens[3].at(0) == 'R') thisFrame.isReceived = true; - else thisFrame.isReceived = false; - thisFrame.bus = tokens[4].toInt(); - thisFrame.len = tokens[5].toInt(); - for (int c = 0; c < 8; c++) thisFrame.data[c] = 0; - for (int d = 0; d < thisFrame.len; d++) - thisFrame.data[d] = tokens[6 + d].toInt(NULL, 16); - } + if (fileVersion == 1) + { + thisFrame.isReceived = true; + thisFrame.bus = tokens[3].toInt(); + thisFrame.len = tokens[4].toInt(); + for (int c = 0; c < 8; c++) thisFrame.data[c] = 0; + for (int d = 0; d < thisFrame.len; d++) + thisFrame.data[d] = tokens[5 + d].toInt(NULL, 16); + } + else if (fileVersion == 2) + { + if (tokens[3].at(0) == 'R') thisFrame.isReceived = true; + else thisFrame.isReceived = false; + thisFrame.bus = tokens[4].toInt(); + thisFrame.len = tokens[5].toInt(); + if (thisFrame.len + 6 > tokens.length()) thisFrame.len = tokens.length() - 6; + for (int c = 0; c < 8; c++) thisFrame.data[c] = 0; + for (int d = 0; d < thisFrame.len; d++) + thisFrame.data[d] = tokens[6 + d].toInt(NULL, 16); + } - frames->append(thisFrame); + frames->append(thisFrame); + } + else foundErrors = true; } } inFile->close(); delete inFile; - return true; + return !foundErrors; } bool FrameFileIO::saveNativeCSVFile(QString filename, const QVector* frames) @@ -510,6 +538,7 @@ bool FrameFileIO::loadGenericCSVFile(QString filename, QVector* frames QByteArray line; long long timeStamp = Utility::GetTimeMS(); int lineCounter = 0; + bool foundErrors = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { @@ -545,10 +574,11 @@ bool FrameFileIO::loadGenericCSVFile(QString filename, QVector* frames frames->append(thisFrame); } + else foundErrors = true; } inFile->close(); delete inFile; - return true; + return !foundErrors; } //4f5,ff 34 23 45 24 e4 @@ -631,6 +661,7 @@ bool FrameFileIO::loadLogFile(QString filename, QVector* frames) QByteArray line; uint64_t timeStamp = Utility::GetTimeMS(); int lineCounter = 0; + bool foundErrors = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { @@ -653,24 +684,30 @@ bool FrameFileIO::loadLogFile(QString filename, QVector* frames) if (line.length() > 1) { QList tokens = line.split(' '); - QList timeToks = tokens[0].split(':'); - timeStamp = (timeToks[0].toInt() * (1000ul * 1000ul * 60ul * 60ul)) + (timeToks[1].toInt() * (1000ul * 1000ul * 60ul)) + if (tokens.length() >= 6) + { + QList timeToks = tokens[0].split(':'); + timeStamp = (timeToks[0].toInt() * (1000ul * 1000ul * 60ul * 60ul)) + (timeToks[1].toInt() * (1000ul * 1000ul * 60ul)) + (timeToks[2].toInt() * (1000ul * 1000ul)) + (timeToks[3].toInt() * 100ul); - thisFrame.timestamp = timeStamp; - if (tokens[1].at(0) == 'R') thisFrame.isReceived = true; - else thisFrame.isReceived = false; - thisFrame.ID = tokens[3].right(tokens[3].length() - 2).toInt(NULL, 16); - if (tokens[4] == "s") thisFrame.extended = false; - else thisFrame.extended = true; - thisFrame.bus = tokens[2].toInt() - 1; - thisFrame.len = tokens[5].toInt(); - for (int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = tokens[d + 6].toInt(NULL, 16); - } - frames->append(thisFrame); + thisFrame.timestamp = timeStamp; + if (tokens[1].at(0) == 'R') thisFrame.isReceived = true; + else thisFrame.isReceived = false; + thisFrame.ID = tokens[3].right(tokens[3].length() - 2).toInt(NULL, 16); + if (tokens[4] == "s") thisFrame.extended = false; + else thisFrame.extended = true; + thisFrame.bus = tokens[2].toInt() - 1; + thisFrame.len = tokens[5].toInt(); + if (thisFrame.len > 8) thisFrame.len = 8; + if (thisFrame.len < 0) thisFrame.len = 0; + for (int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = tokens[d + 6].toInt(NULL, 16); + frames->append(thisFrame); + } + else foundErrors = true; + } } inFile->close(); delete inFile; - return true; + return !foundErrors; } bool FrameFileIO::saveLogFile(QString filename, const QVector* frames) @@ -744,6 +781,7 @@ bool FrameFileIO::loadIXXATFile(QString filename, QVector* frames) QByteArray line; uint64_t timeStamp = Utility::GetTimeMS(); int lineCounter = 0; + bool foundErrors = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { @@ -765,27 +803,48 @@ bool FrameFileIO::loadIXXATFile(QString filename, QVector* frames) if (line.length() > 1) { QList tokens = line.split(','); - QString timePortion = unQuote(tokens[0]); - QStringList timeToks = timePortion.split(':'); - timeStamp = (timeToks[0].toInt() * (1000ul * 1000ul * 60ul * 60ul)) + (timeToks[1].toInt() * (1000ul * 1000ul * 60ul)) + if (line.length() >= 5) + { + QString timePortion = unQuote(tokens[0]); + QStringList timeToks = timePortion.split(':'); + if (timeToks.length() >= 3) + { + timeStamp = (timeToks[0].toInt() * (1000ul * 1000ul * 60ul * 60ul)) + (timeToks[1].toInt() * (1000ul * 1000ul * 60ul)) + (timeToks[2].toDouble() * (1000.0 * 1000.0)); - thisFrame.timestamp = timeStamp; - thisFrame.ID = unQuote(tokens[1]).toInt(NULL, 16); - if (unQuote(tokens[2]).toUpper().at(0) == 'S') thisFrame.extended = false; - else thisFrame.extended = true; + } + else + { + timeStamp = 0; + foundErrors = true; + } + thisFrame.timestamp = timeStamp; + thisFrame.ID = unQuote(tokens[1]).toInt(NULL, 16); + QString tempStr = unQuote(tokens[2]).toUpper(); + if (tempStr.length() > 0) + { + if (tempStr.at(0) == 'S') thisFrame.extended = false; + else thisFrame.extended = true; + } + else + { + thisFrame.extended = false; + foundErrors = true; + } - thisFrame.isReceived = true; - thisFrame.bus = 0; + thisFrame.isReceived = true; + thisFrame.bus = 0; - QStringList dataToks = unQuote(tokens[4]).simplified().split(' '); - thisFrame.len = dataToks.length(); - for (int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = dataToks[d].toInt(NULL, 16); + QStringList dataToks = unQuote(tokens[4]).simplified().split(' '); + thisFrame.len = dataToks.length(); + for (int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = dataToks[d].toInt(NULL, 16); + frames->append(thisFrame); + } + else foundErrors = true; } - frames->append(thisFrame); } inFile->close(); delete inFile; - return true; + return !foundErrors; } bool FrameFileIO::saveIXXATFile(QString filename, const QVector* frames) @@ -852,6 +911,7 @@ bool FrameFileIO::loadCANDOFile(QString filename, QVector* frames) QByteArray data; int timeOffset = 0; uint64_t lastTimeStamp = 0; + bool foundErrors = false; if (!inFile->open(QIODevice::ReadOnly)) { @@ -887,7 +947,7 @@ bool FrameFileIO::loadCANDOFile(QString filename, QVector* frames) timeOffset += 60000000ul; } lastTimeStamp = thisFrame.timestamp; - thisFrame.ID = ((unsigned char)data[3] * 256 + (unsigned char)data[2]) & 0x7FF; + thisFrame.ID = (((unsigned char)data[3] & 0x0F) * 256 + (unsigned char)data[2]) & 0x7FF; thisFrame.len = (unsigned char)data[3] >> 4; if (thisFrame.len <= 8 && thisFrame.ID <= 0x7FF) @@ -895,11 +955,12 @@ bool FrameFileIO::loadCANDOFile(QString filename, QVector* frames) for (int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = (unsigned char)data[4 + d]; frames->append(thisFrame); } + else foundErrors = true; } inFile->close(); delete inFile; - return true; + return !foundErrors; } bool FrameFileIO::saveCANDOFile(QString filename, const QVector* frames) @@ -973,6 +1034,7 @@ bool FrameFileIO::loadMicrochipFile(QString filename, QVector* frames) bool inComment = false; long long timeStamp; int lineCounter = 0; + bool foundErrors = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { @@ -1002,24 +1064,31 @@ bool FrameFileIO::loadMicrochipFile(QString filename, QVector* frames) if (!inComment) { QList tokens = line.split(';'); - timeStamp = tokens[0].toInt() * 1000; - thisFrame.timestamp = timeStamp; - if (tokens[1].at(0) == 'R') thisFrame.isReceived = true; - else thisFrame.isReceived = false; - thisFrame.ID = Utility::ParseStringToNum(tokens[2]); - if (thisFrame.ID <= 0x7FF) thisFrame.extended = false; - else thisFrame.extended = true; - thisFrame.bus = 0; - thisFrame.len = tokens[3].toInt(); - for (int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = (unsigned char)Utility::ParseStringToNum(tokens[4 + d]); - frames->append(thisFrame); + if (tokens.length() >= 4) + { + timeStamp = tokens[0].toInt() * 1000; + thisFrame.timestamp = timeStamp; + if (tokens[1].at(0) == 'R') thisFrame.isReceived = true; + else thisFrame.isReceived = false; + thisFrame.ID = Utility::ParseStringToNum(tokens[2]); + if (thisFrame.ID <= 0x7FF) thisFrame.extended = false; + else thisFrame.extended = true; + thisFrame.bus = 0; + thisFrame.len = tokens[3].toInt(); + if (thisFrame.len > 8) thisFrame.len = 8; + if (thisFrame.len < 0) thisFrame.len = 0; + if (thisFrame.len + 4 > tokens.length()) thisFrame.len = tokens.length() - 4; + for (int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = (unsigned char)Utility::ParseStringToNum(tokens[4 + d]); + frames->append(thisFrame); + } + else foundErrors = true; } } } } inFile->close(); delete inFile; - return true; + return !foundErrors; } /* @@ -1122,6 +1191,7 @@ bool FrameFileIO::loadTraceFile(QString filename, QVector* frames) QByteArray line; long long timeStamp = 0; int lineCounter = 0; + bool foundErrors = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { @@ -1148,31 +1218,36 @@ bool FrameFileIO::loadTraceFile(QString filename, QVector* frames) else { QList tokens = line.split('\t'); + if (tokens.length() > 3) + { + QList timestampToks = tokens[1].split(':'); - QList timestampToks = tokens[1].split(':'); + timeStamp = timestampToks[0].toInt() * 1000000ul * 60 * 60; + timeStamp += timestampToks[1].toInt() * 1000000ul * 60; + timeStamp += timestampToks[2].toInt() * 1000000ul; + timeStamp += timestampToks[3].toInt() * 100; - timeStamp = timestampToks[0].toInt() * 1000000ul * 60 * 60; - timeStamp += timestampToks[1].toInt() * 1000000ul * 60; - timeStamp += timestampToks[2].toInt() * 1000000ul; - timeStamp += timestampToks[3].toInt() * 100; + thisFrame.timestamp = timeStamp; - thisFrame.timestamp = timeStamp; - - thisFrame.ID = tokens[2].toLong(NULL, 16); - if (thisFrame.ID <= 0x7FF) thisFrame.extended = false; - else thisFrame.extended = true; - thisFrame.bus = 0; - thisFrame.len = tokens[3].toInt(); - - QList dataToks = tokens[4].split(' '); - for (int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = (unsigned char)dataToks[d].toInt(NULL, 16); - frames->append(thisFrame); + thisFrame.ID = tokens[2].toLong(NULL, 16); + if (thisFrame.ID <= 0x7FF) thisFrame.extended = false; + else thisFrame.extended = true; + thisFrame.bus = 0; + thisFrame.len = tokens[3].toInt(); + if (thisFrame.len < 0) thisFrame.len = 0; + if (thisFrame.len > 8) thisFrame.len = 8; + QList dataToks = tokens[4].split(' '); + if (thisFrame.len > dataToks.length()) thisFrame.len = dataToks.length(); + for (int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = (unsigned char)dataToks[d].toInt(NULL, 16); + frames->append(thisFrame); + } + else foundErrors = true; } } } inFile->close(); delete inFile; - return true; + return !foundErrors; } bool FrameFileIO::saveTraceFile(QString filename, const QVector * frames) @@ -1264,7 +1339,89 @@ bool FrameFileIO::saveTraceFile(QString filename, const QVector * fram return true; } +/* (0.003800) vcan0 164#0000c01aa8000013 */ +bool FrameFileIO::loadCanDumpFile(QString filename, QVector* frames) +{ + QFile *inFile = new QFile(filename); + CANFrame thisFrame; + QByteArray line; + int lineCounter = 0; + int pos = 0; + bool ret; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + + while (!inFile->atEnd()) { + lineCounter++; + if (lineCounter > 100) + { + qApp->processEvents(); + lineCounter = 0; + } + + line = inFile->readLine().toUpper(); + if (line.length() > 1) + { + /* tokenize */ + QList tokens = line.split(' '); + if(tokens.count()<3) continue; + + /* timestamp */ + QRegExp timeExp("^\\((\\S+)\\)$"); + ret = timeExp.exactMatch(tokens[0]); + if(!ret) continue; + + thisFrame.timestamp = timeExp.cap(1).toDouble(&ret) * 1000000; + if(!ret) continue; + + /* ID & value */ + QRegExp IdValExp("^(\\S+)#(\\S+)\n$"); + qDebug() << tokens[2]; + ret = IdValExp.exactMatch(tokens[2]); + if(!ret) continue; + + /* ID */ + qDebug() << IdValExp.cap(1); + thisFrame.ID = IdValExp.cap(1).toInt(&ret, 16); + if(!ret) continue; + + QString val= IdValExp.cap(2); + QRegExp valExp("(\\S{2})"); + + /* val byte per byte */ + pos = 0; + thisFrame.len = 0; + while ((pos = valExp.indexIn(val, pos)) != -1) + { + thisFrame.data[thisFrame.len] = valExp.cap(1).toInt(&ret, 16); + if(!ret) continue; + + thisFrame.len++; + pos += valExp.matchedLength(); + } + + /*NB: should we make sure len <= 8? */ + thisFrame.extended = false; + thisFrame.isReceived = true; + thisFrame.bus = 0; + } + frames->append(thisFrame); + } + inFile->close(); + delete inFile; + return true; +} + + QString FrameFileIO::unQuote(QString inStr) { - return inStr.split('\"')[1]; + QStringList temp; + temp = inStr.split('\"'); + if (temp.length() >= 3) + return temp[1]; + return QString(""); } diff --git a/framefileio.h b/framefileio.h index d1a7169..44a648f 100644 --- a/framefileio.h +++ b/framefileio.h @@ -42,6 +42,7 @@ public: static bool saveVehicleSpyFile(QString, const QVector*); static bool loadFrameFile(QString &, QVector*); static bool saveFrameFile(QString &, const QVector*); + static bool loadCanDumpFile(QString, QVector*); private: static QString unQuote(QString); diff --git a/graphingwindow.cpp b/graphingwindow.cpp index afe9c4d..0deecd5 100644 --- a/graphingwindow.cpp +++ b/graphingwindow.cpp @@ -685,33 +685,24 @@ void GraphingWindow::saveDefinitions() QList::iterator iter; for (iter = graphParams.begin(); iter != graphParams.end(); ++iter) { + outFile->write("X,"); outFile->write(QString::number(iter->ID, 16).toUtf8()); outFile->putChar(','); - if (iter->isDBCSignal) - { - outFile->putChar('S'); - outFile->putChar(','); - outFile->write(iter->signal.toUtf8()); - outFile->putChar(','); - } - else - { - outFile->write(QString::number(iter->mask, 16).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->startByte).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->endByte).toUtf8()); - outFile->putChar(','); - if (iter->isSigned) outFile->putChar('Y'); - else outFile->putChar('N'); - outFile->putChar(','); - outFile->write(QString::number(iter->bias).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->scale).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->stride).toUtf8()); - outFile->putChar(','); - } + outFile->write(QString::number(iter->mask, 16).toUtf8()); + outFile->putChar(','); + outFile->write(QString::number(iter->startBit).toUtf8()); + outFile->putChar(','); + outFile->write(QString::number(iter->numBits).toUtf8()); + outFile->putChar(','); + if (iter->isSigned) outFile->putChar('Y'); + else outFile->putChar('N'); + outFile->putChar(','); + outFile->write(QString::number(iter->bias).toUtf8()); + outFile->putChar(','); + outFile->write(QString::number(iter->scale).toUtf8()); + outFile->putChar(','); + outFile->write(QString::number(iter->stride).toUtf8()); + outFile->putChar(','); outFile->write(QString::number(iter->color.red()).toUtf8()); outFile->putChar(','); outFile->write(QString::number(iter->color.green()).toUtf8()); @@ -729,7 +720,6 @@ void GraphingWindow::loadDefinitions() { QString filename; QFileDialog dialog; - bool dbcMissing = false; QStringList filters; filters.append(QString(tr("Graph definition (*.gdf)"))); @@ -755,66 +745,146 @@ void GraphingWindow::loadDefinitions() if (line.length() > 2) { GraphParams gp; - QList tokens = line.split(','); - gp.isDBCSignal = false; - gp.ID = tokens[0].toInt(NULL, 16); - if (tokens[1] == "S") + QList tokens = line.split(','); + + if (tokens[0] == "X") //newest format based around signals { - gp.isDBCSignal = true; - //tokens[2] is the signal name. Need to use the message ID and this name to look it up - DBC_MESSAGE *msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByID(gp.ID); - if (msg != NULL) - { - DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(tokens[2]); - if (sig == NULL) dbcMissing = true; - gp.signal = tokens[2]; - } - else - { - gp.signal = ""; - dbcMissing = true; - } - } - else - { - gp.mask = tokens[1].toULongLong(NULL, 16); - qDebug() << gp.mask; - gp.startByte = tokens[2].toInt(); - gp.endByte = tokens[3].toInt(); - if (tokens[4] == "Y") gp.isSigned = true; + gp.ID = tokens[1].toInt(NULL, 16); + gp.mask = tokens[2].toULongLong(NULL, 16); + gp.startBit = tokens[3].toInt(); + gp.numBits = tokens[4].toInt(); + if (tokens[5] == "Y") gp.isSigned = true; else gp.isSigned = false; - gp.bias = tokens[5].toFloat(); - gp.scale = tokens[6].toFloat(); - gp.stride = tokens[7].toInt(); - } - if (!gp.isDBCSignal) - { - gp.color.setRed(tokens[8].toInt()); - gp.color.setGreen(tokens[9].toInt()); - gp.color.setBlue(tokens[10].toInt()); - if (tokens.length() > 11) - gp.graphName = tokens[11]; + gp.bias = tokens[6].toFloat(); + gp.scale = tokens[7].toFloat(); + gp.stride = tokens[8].toInt(); + + gp.color.setRed(tokens[9].toInt()); + gp.color.setGreen(tokens[10].toInt()); + gp.color.setBlue(tokens[11].toInt()); + if (tokens.length() > 12) + gp.graphName = tokens[12]; else gp.graphName = QString(); + createGraph(gp, true); } - else + else //one of the two older formats then { - gp.color.setRed(tokens[3].toInt()); - gp.color.setGreen(tokens[4].toInt()); - gp.color.setBlue(tokens[5].toInt()); - gp.graphName = tokens[6]; + gp.ID = tokens[0].toInt(NULL, 16); + if (tokens[1] == "S") //old signal based graph definition + { + //tokens[2] is the signal name. Need to use the message ID and this name to look it up + DBC_MESSAGE *msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByID(gp.ID); + if (msg != NULL) + { + DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(tokens[2]); + if (sig) + { + gp.mask = 0xFFFFFFFF; + gp.bias = sig->bias; + gp.color.setRed(tokens[3].toInt()); + gp.color.setGreen(tokens[4].toInt()); + gp.color.setBlue(tokens[5].toInt()); + gp.graphName = sig->name; + gp.intelFormat = sig->intelByteOrder; + if (sig->valType == SIGNED_INT) gp.isSigned = true; + else gp.isSigned = false; + gp.numBits = sig->signalSize; + gp.scale = sig->factor; + gp.startBit = sig->startBit; + gp.stride = 1; + createGraph(gp, true); + } + } + } + else //old standard graph definition + { + //hard part - this all changed drastically + //the difference between intel and motorola format is whether + //start is larger than end byte or not. + uint64_t oldMask = tokens[1].toULongLong(NULL, 16); + int oldStart = tokens[2].toInt(); + int oldEnd = tokens[3].toInt(); + + if (oldEnd > oldStart) //motorola / big endian - hell... + { + gp.intelFormat = false; + //for now just naively use the entire bytes called for. + gp.startBit = 8 * oldStart + 7; + gp.numBits = (oldEnd - oldStart + 1) * 8; + } + else if (oldStart > oldEnd) //intel / little endian - easiest of multi-byte types + { + //have to find both ends. start bit is somewhere in oldEnd and last bit is somewhere in + //oldStart. + + gp.intelFormat = true; + + //start by setting a safe default if nothing else pans out. + gp.startBit = 8 * oldEnd; + + int numBytes = oldStart - oldEnd + 1; + gp.numBits = numBytes * 8; + + for (int b = 0; b < 8; b++) + { + if (oldMask & (1 << b)) + { + gp.startBit = (8 * oldEnd) + b; + break; + } + } + + for (int c = 7; c >= 0; c--) + { + if ( oldMask & (1<<(((numBytes - 1) * 8) + c)) ) + { + gp.numBits -= (7-c); + break; + } + } + } + else //within a single byte - easier than the above two by a bit - always use intel format for this + { + gp.intelFormat = true; + oldMask = oldMask & 0xFF; //only this part matters + //for intel format we give startbit as the lowest bit number in the signal + //we can find that by going backward from bit 0 to 7 and picking the first bit that is 1. + //that's our start bit (+ 8*oldStart) + //set default first in case the rest falls through + gp.startBit = 8 * oldStart; + gp.numBits = 8; + for (int b = 0; b < 8; b++) + { + if (oldMask & (1 << b)) + { + gp.startBit = 8 * oldStart + b; + gp.numBits = 8 - b; + break; + } + } + } + + //the rest is easy stuff + if (tokens[4] == "Y") gp.isSigned = true; + else gp.isSigned = false; + gp.bias = tokens[5].toFloat(); + gp.scale = tokens[6].toFloat(); + gp.stride = tokens[7].toInt(); + gp.color.setRed(tokens[8].toInt()); + gp.color.setGreen(tokens[9].toInt()); + gp.color.setBlue(tokens[10].toInt()); + if (tokens.length() > 11) + gp.graphName = tokens[11]; + else + gp.graphName = QString(); + createGraph(gp, true); + } } - createGraph(gp, true); } } inFile->close(); - if (dbcMissing) - { - QMessageBox msg; - msg.setText("One or more graphs could not be loaded\r\nbecause the signal could not be found.\r\nPerhaps you forgot to load\r\nthe DBC file?"); - msg.exec(); - } } } @@ -851,133 +921,16 @@ void GraphingWindow::addNewGraph() void GraphingWindow::appendToGraph(GraphParams ¶ms, CANFrame &frame) { int64_t tempVal; //64 bit temp value. - if (params.isDBCSignal) + tempVal = Utility::processIntegerSignal(frame.data, params.startBit, params.numBits, params.intelFormat, params.isSigned); //& params.mask; + if (secondsMode) { - double tempValue; - DBC_MESSAGE *msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByID(params.ID); - DBC_SIGNAL *sig = NULL; - if (msg) sig = msg->sigHandler->findSignalByName(params.signal); - if (sig == NULL) return; - //if the given signal was found and successfully processed in this frame then add it to the graph - if (sig->processAsDouble(frame, tempValue)) - { - //qDebug() << "tempValue: " << tempValue; - if (secondsMode) - { - params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias); - } - else - { - params.x.append(frame.timestamp - params.xbias); - } - params.y.append(tempValue); - } + params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias); } else { - if (params.endByte == -1 || params.startByte == params.endByte) - { - tempVal = (frame.data[params.startByte] & params.mask); - if (params.isSigned && tempVal > 127) - { - tempVal = tempVal - 256; - } - if (secondsMode) - { - params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias); - } - else - { - params.x.append(frame.timestamp - params.xbias); - } - params.y.append((tempVal * params.scale) + params.bias); - } - else if (params.endByte > params.startByte) //big endian - { - float tempValue; - int64_t tempValInt; - int numBytes = (params.endByte - params.startByte) + 1; - int64_t shiftRef = 1 << (numBytes * 8); - uint64_t maskShifter; - uint8_t tempByte; - tempValInt = 0; - int64_t expon = 1; - maskShifter = params.mask; - for (int c = 0; c < numBytes; c++) - { - tempByte = frame.data[params.endByte - c]; - tempByte &= maskShifter; - tempValInt += (tempByte * expon); - expon *= 256; - maskShifter = maskShifter >> 8; - } - - tempValInt &= params.mask; - - int64_t twocompPoint = params.mask; - if (shiftRef < twocompPoint || twocompPoint == -1) twocompPoint = shiftRef; - //qDebug() << "two comp point: " << twocompPoint; - if (params.isSigned && tempValInt > ((twocompPoint / 2))) - { - tempValInt = tempValInt - twocompPoint; - } - - tempValue = (float)tempValInt; - - if (secondsMode) - { - params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias); - } - else - { - params.x.append(frame.timestamp - params.xbias); - } - - params.y.append((tempValue * params.scale) + params.bias); - } - else //little endian - { - float tempValue; - int64_t tempValInt; - int numBytes = (params.startByte - params.endByte) + 1; - int64_t shiftRef = 1 << (numBytes * 8); - uint64_t maskShifter; - uint8_t tempByte; - tempValInt = 0; - int64_t expon = 1; - maskShifter = params.mask; - for (int c = 0; c < numBytes; c++) - { - tempByte = frame.data[params.endByte + c]; - tempByte &= maskShifter; - tempValInt += tempByte * expon; - expon *= 256; - maskShifter = maskShifter >> 8; - } - tempValInt &= params.mask; - - int64_t twocompPoint = params.mask; - if (shiftRef < twocompPoint || twocompPoint == -1) twocompPoint = shiftRef; - //qDebug() << "two comp point: " << twocompPoint; - if (params.isSigned && tempValInt > ((twocompPoint / 2))) - { - tempValInt = tempValInt - twocompPoint; - } - - tempValue = (float)tempValInt; - - if (secondsMode) - { - params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias); - } - else - { - params.x.append(frame.timestamp - params.xbias); - } - - params.y.append((tempValue * params.scale) + params.bias); - } + params.x.append(frame.timestamp - params.xbias); } + params.y.append((tempVal * params.scale) + params.bias); params.ref->setData(params.x,params.y); } @@ -988,23 +941,15 @@ void GraphingWindow::createGraph(GraphParams ¶ms, bool createGraphParam) float yminval=10000000.0, ymaxval = -1000000.0; float xminval=10000000000.0, xmaxval = -10000000000.0; GraphParams *refParam = ¶ms; - DBC_MESSAGE *msg = NULL; - DBC_SIGNAL *sig = NULL; + int sBit, bits; + bool intelFormat, isSigned; - if (params.isDBCSignal) - { - msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByID(params.ID); - if (msg) sig = msg->sigHandler->findSignalByName(params.signal); - if (sig == NULL) return; - qDebug() << "New signal graph: " << params.signal <<" in ID:" << params.ID; - } - else - { - qDebug() << "New Graph ID: " << params.ID; - qDebug() << "Start byte: " << params.startByte; - qDebug() << "End Byte: " << params.endByte; - qDebug() << "Mask: " << params.mask; - } + qDebug() << "New Graph ID: " << params.ID; + qDebug() << "Start bit: " << params.startBit; + qDebug() << "Data length: " << params.numBits; + qDebug() << "Intel Mode: " << params.intelFormat; + qDebug() << "Signed: " << params.isSigned; + qDebug() << "Mask: " << params.mask; frameCache.clear(); for (int i = 0; i < modelFrames->count(); i++) @@ -1020,163 +965,28 @@ void GraphingWindow::createGraph(GraphParams ¶ms, bool createGraphParam) params.x.fill(0, numEntries); params.y.fill(0, numEntries); - if (params.isDBCSignal) + sBit = params.startBit; + bits = params.numBits; + intelFormat = params.intelFormat; + isSigned = params.isSigned; + + for (int j = 0; j < numEntries; j++) { - double tempValue; - int l = 0; - for (int j = 0; j < numEntries; j++) + tempVal = Utility::processIntegerSignal(frameCache[j * params.stride].data, sBit, bits, intelFormat, isSigned); //& params.mask; + //qDebug() << tempVal; + if (secondsMode) { - //if the given signal was found and successfully processed in this frame then add it to the graph - if (sig->processAsDouble(frameCache[j], tempValue)) - { - //qDebug() << "tempValue: " << tempValue; - if (secondsMode) - { - params.x[l] = (double)(frameCache[j].timestamp) / 1000000.0; - } - else - { - params.x[l] = frameCache[j].timestamp; - } - params.y[l] = tempValue; - if (params.y[l] < yminval) yminval = params.y[l]; - if (params.y[l] > ymaxval) ymaxval = params.y[l]; - if (params.x[l] < xminval) xminval = params.x[l]; - if (params.x[l] > xmaxval) xmaxval = params.x[l]; - l++; - } + params.x[j] = (double)(frameCache[j].timestamp) / 1000000.0; } - params.x.resize(l); - params.y.resize(l); - params.x.squeeze(); - params.y.squeeze(); - } - else - { - - if (params.endByte == -1 || params.startByte == params.endByte) + else { - for (int j = 0; j < numEntries; j++) - { - tempVal = (frameCache[j * params.stride].data[params.startByte] & params.mask); - if (params.isSigned && tempVal > 127) - { - tempVal = tempVal - 256; - } - if (secondsMode) - { - params.x[j] = (double)(frameCache[j].timestamp) / 1000000.0; - } - else - { - params.x[j] = frameCache[j].timestamp; - } - params.y[j] = (tempVal * params.scale) + params.bias; - if (params.y[j] < yminval) yminval = params.y[j]; - if (params.y[j] > ymaxval) ymaxval = params.y[j]; - if (params.x[j] < xminval) xminval = params.x[j]; - if (params.x[j] > xmaxval) xmaxval = params.x[j]; - } - } - else if (params.endByte > params.startByte) //big endian - { - float tempValue; - int64_t tempValInt; - int numBytes = (params.endByte - params.startByte) + 1; - int64_t shiftRef = (uint64_t)1 << (numBytes * 8); - uint64_t maskShifter; - uint8_t tempByte; - for (int j = 0; j < numEntries; j++) - { - tempValInt = 0; - int64_t expon = 1; - maskShifter = params.mask; - for (int c = 0; c < numBytes; c++) - { - tempByte = frameCache[j * params.stride].data[params.endByte - c]; - tempByte &= maskShifter; - tempValInt += (tempByte * expon); - expon *= 256; - maskShifter = maskShifter >> 8; - } - - tempValInt &= params.mask; - - int64_t twocompPoint = params.mask; - if (shiftRef < twocompPoint || twocompPoint == -1) twocompPoint = shiftRef; - //qDebug() << "two comp point: " << twocompPoint; - if (params.isSigned && tempValInt > ((twocompPoint / 2))) - { - tempValInt = tempValInt - twocompPoint; - } - - tempValue = (float)tempValInt; - - if (secondsMode) - { - params.x[j] = (double)(frameCache[j].timestamp) / 1000000.0; - } - else - { - params.x[j] = frameCache[j].timestamp; - } - - params.y[j] = (tempValue * params.scale) + params.bias; - if (params.y[j] < yminval) yminval = params.y[j]; - if (params.y[j] > ymaxval) ymaxval = params.y[j]; - if (params.x[j] < xminval) xminval = params.x[j]; - if (params.x[j] > xmaxval) xmaxval = params.x[j]; - } - } - else //little endian - { - float tempValue; - int64_t tempValInt; - int numBytes = (params.startByte - params.endByte) + 1; - int64_t shiftRef = (uint64_t)1 << (numBytes * 8); - uint64_t maskShifter; - uint8_t tempByte; - for (int j = 0; j < numEntries; j++) - { - tempValInt = 0; - int64_t expon = 1; - maskShifter = params.mask; - for (int c = 0; c < numBytes; c++) - { - tempByte = frameCache[j * params.stride].data[params.endByte + c]; - tempByte &= maskShifter; - tempValInt += tempByte * expon; - expon *= 256; - maskShifter = maskShifter >> 8; - } - tempValInt &= params.mask; - - int64_t twocompPoint = params.mask; - if (shiftRef < twocompPoint || twocompPoint == -1) twocompPoint = shiftRef; - //qDebug() << "two comp point: " << twocompPoint; - if (params.isSigned && tempValInt > ((twocompPoint / 2))) - { - tempValInt = tempValInt - twocompPoint; - } - - tempValue = (float)tempValInt; - - if (secondsMode) - { - params.x[j] = (double)(frameCache[j].timestamp) / 1000000.0; - } - else - { - params.x[j] = frameCache[j].timestamp; - } - - params.y[j] = (tempValue * params.scale) + params.bias; - if (params.y[j] < yminval) yminval = params.y[j]; - if (params.y[j] > ymaxval) ymaxval = params.y[j]; - if (params.x[j] < xminval) xminval = params.x[j]; - if (params.x[j] > xmaxval) xmaxval = params.x[j]; - } + params.x[j] = frameCache[j].timestamp; } + params.y[j] = (tempVal * params.scale) + params.bias; + if (params.y[j] < yminval) yminval = params.y[j]; + if (params.y[j] > ymaxval) ymaxval = params.y[j]; + if (params.x[j] < xminval) xminval = params.x[j]; + if (params.x[j] > xmaxval) xmaxval = params.x[j]; } params.xbias = 0; @@ -1193,8 +1003,8 @@ void GraphingWindow::createGraph(GraphParams ¶ms, bool createGraphParam) if (params.graphName == NULL || params.graphName.length() == 0) { - params.graphName = QString("0x") + QString::number(params.ID, 16) + ":" + QString::number(params.startByte); - if ((params.endByte != -1) && (params.endByte != params.startByte)) params.graphName += "-" + QString::number(params.endByte); + params.graphName = QString("0x") + QString::number(params.ID, 16) + ":" + QString::number(params.startBit); + params.graphName += "-" + QString::number(params.numBits); } ui->graphingView->graph()->setName(params.graphName); ui->graphingView->graph()->setProperty("id", params.ID); diff --git a/graphingwindow.h b/graphingwindow.h index 80c8d4f..4887243 100644 --- a/graphingwindow.h +++ b/graphingwindow.h @@ -15,7 +15,8 @@ class GraphParams { public: uint32_t ID; - int startByte, endByte; + int startBit, numBits; + bool intelFormat; bool isSigned; uint64_t mask; float bias; @@ -24,8 +25,6 @@ public: QColor color; QCPGraph *ref; QString graphName; - bool isDBCSignal; //is this graph actually a DBC signal? If so the below pointer should be filled out - QString signal; //the below stuff is used for internal purposes only - code should be refactored so these can be private QVector x, y; double xbias; diff --git a/newgraphdialog.cpp b/newgraphdialog.cpp index 409e2f9..79da03e 100644 --- a/newgraphdialog.cpp +++ b/newgraphdialog.cpp @@ -24,9 +24,13 @@ NewGraphDialog::NewGraphDialog(DBCHandler *handler, QWidget *parent) : ui->colorSwatch->setPalette(p); connect(ui->cbMessages, SIGNAL(currentIndexChanged(int)), this, SLOT(loadSignals(int))); - connect(ui->cbSignals, SIGNAL(currentIndexChanged(int)), this, SLOT(fillFormFromSignal(int))); - connect(ui->rbSignalGraph, SIGNAL(toggled(bool)), this, SLOT(setSignalActive(bool))); - connect(ui->rbStandardGraph, SIGNAL(toggled(bool)), this, SLOT(setStandardActive(bool))); + connect(ui->gridData, SIGNAL(gridClicked(int,int)), this, SLOT(bitfieldClicked(int,int))); + connect(ui->txtDataLen, SIGNAL(textChanged(QString)), this, SLOT(handleDataLenUpdate())); + connect(ui->cbIntel, SIGNAL(toggled(bool)), this, SLOT(drawBitfield())); + connect(ui->btnCopySignal, SIGNAL(clicked(bool)), this, SLOT(copySignalToParamsUI())); + + startBit = 0; + dataLen = 1; loadMessages(); } @@ -36,6 +40,12 @@ NewGraphDialog::~NewGraphDialog() delete ui; } +void NewGraphDialog::showEvent(QShowEvent* event) +{ + QDialog::showEvent(event); + loadMessages(); +} + void NewGraphDialog::addButtonClicked() { accept(); @@ -59,91 +69,49 @@ void NewGraphDialog::clearParams() ui->txtScale->clear(); ui->txtStride->clear(); ui->txtName->clear(); - ui->txtData->clear(); - ui->rbStandardGraph->setChecked(true); - setStandardActive(true); - } void NewGraphDialog::setParams(GraphParams ¶ms) { - if (params.isDBCSignal) - { - clearParams(); - setSignalActive(true); - //loadMessages(); - - } - else - { - setStandardActive(true); - ui->txtBias->setText(QString::number(params.bias)); - ui->txtMask->setText(Utility::formatNumber(params.mask)); - ui->txtScale->setText(QString::number(params.scale)); - ui->txtStride->setText(QString::number(params.stride)); - ui->cbSigned->setChecked(params.isSigned); - - if (params.endByte > -1) - { - ui->txtData->setText(QString::number(params.startByte) + "-" + QString::number(params.endByte)); - } - else - { - ui->txtData->setText(QString::number(params.startByte)); - } - } + ui->txtBias->setText(QString::number(params.bias)); + ui->txtMask->setText(Utility::formatNumber(params.mask)); + ui->txtScale->setText(QString::number(params.scale)); + ui->txtStride->setText(QString::number(params.stride)); + ui->cbSigned->setChecked(params.isSigned); + ui->cbIntel->setChecked(params.intelFormat); + startBit = params.startBit; + dataLen = params.numBits; + ui->txtDataLen->setText(QString::number(dataLen)); ui->txtID->setText(Utility::formatNumber(params.ID)); ui->txtName->setText(params.graphName); QPalette p = ui->colorSwatch->palette(); p.setColor(QPalette::Button, params.color); ui->colorSwatch->setPalette(p); + + drawBitfield(); } void NewGraphDialog::getParams(GraphParams ¶ms) { - params.isDBCSignal = ui->rbSignalGraph->isChecked(); params.color = ui->colorSwatch->palette().button().color(); params.graphName = ui->txtName->text(); - if (params.isDBCSignal) - { - params.signal = ui->cbSignals->currentText(); - params.ID = Utility::ParseStringToNum(ui->txtID->text()); - params.bias = 0; - params.isSigned = false; - params.mask = 0; - params.scale = 1; - params.bias = 0; - params.stride = 1; + params.ID = Utility::ParseStringToNum(ui->txtID->text()); + params.bias = ui->txtBias->text().toFloat(); + params.isSigned = ui->cbSigned->isChecked(); + params.intelFormat = ui->cbIntel->isChecked(); + params.mask = Utility::ParseStringToNum(ui->txtMask->text()); + params.scale = ui->txtScale->text().toFloat(); + params.stride = Utility::ParseStringToNum(ui->txtStride->text()); - } - else { - params.ID = Utility::ParseStringToNum(ui->txtID->text()); - params.bias = ui->txtBias->text().toFloat(); - params.isSigned = ui->cbSigned->isChecked(); - params.mask = Utility::ParseStringToNum(ui->txtMask->text()); - params.scale = ui->txtScale->text().toFloat(); - params.stride = Utility::ParseStringToNum(ui->txtStride->text()); - params.signal = ""; + params.startBit = startBit; + params.numBits = dataLen; - QStringList values = ui->txtData->text().split('-'); - params.startByte = -1; - params.endByte = -1; - if (values.count() > 0) - { - params.startByte = values[0].toInt(); - if (values.count() > 1) - { - params.endByte = values[1].toInt(); - } - } - - //now catch stupidity and bring it to defaults - if (params.mask == 0) params.mask = 0xFFFFFFFF; - if (fabs(params.scale) < 0.00000001) params.scale = 1.0f; - if (params.stride < 1) params.stride = 1; - } + //now catch stupidity and bring it to defaults + if (params.mask == 0) params.mask = 0xFFFFFFFF; + if (fabs(params.scale) < 0.00000001) params.scale = 1.0f; + if (params.stride < 1) params.stride = 1; } void NewGraphDialog::loadMessages() @@ -174,73 +142,77 @@ void NewGraphDialog::loadSignals(int idx) } } -void NewGraphDialog::fillFormFromSignal(int idx) +void NewGraphDialog::bitfieldClicked(int x,int y) +{ + int bit = (y * 8 + (7-x)); + int res; + + qDebug() << "Clicked bit: " << bit; + startBit = bit; + drawBitfield(); +} + +void NewGraphDialog::drawBitfield() +{ + int64_t bitField = 0; + int endBit, sBit; + + bitField |= 1ull << (startBit); //make the start bit a different color to set it apart + ui->gridData->setReference((unsigned char *)&bitField, false); + + if (ui->cbIntel->isChecked()) + { + endBit = startBit + dataLen - 1; + if (startBit < 0) startBit = 0; + if (endBit > 63) endBit = 63; + for (int y = startBit; y <= endBit; y++) + { + bitField |= 1ull << y; + } + } + else //big endian / motorola format + { + //much more irritating than the intel version... + int size = dataLen; + sBit = startBit; + while (size > 0) + { + bitField |= 1ull << sBit; + size--; + if ((sBit % 8) == 0) sBit += 15; + else sBit--; + if (sBit > 63) sBit = 63; + } + } + + ui->gridData->updateData((unsigned char *)&bitField, true); +} + +void NewGraphDialog::handleDataLenUpdate() +{ + dataLen = ui->txtDataLen->text().toInt(); + if (dataLen < 1) dataLen = 1; + if (dataLen > 63) dataLen = 63; + drawBitfield(); +} + +void NewGraphDialog::copySignalToParamsUI() { - Q_UNUSED(idx); - GraphParams params; DBC_MESSAGE *msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByName(ui->cbMessages->currentText()); - - if (msg == NULL) return; - + if (!msg) return; DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(ui->cbSignals->currentText()); + if (!sig) return; - if (sig == NULL) return; - - params.graphName = sig->name; - params.ID = msg->ID; - //params.bias = sig->bias; - //params.scale = sig->factor; - //params.stride = 1; - //params.mask = (1 << (sig->signalSize)) - 1; - //if (sig->valType == SIGNED_INT) params.isSigned = true; - //else params.isSigned = false; - params.color = ui->colorSwatch->palette().color(QPalette::Button); - /* - if (sig->intelByteOrder) - { - //for this ordering the byte order is reserved and starting byte - //will be the higher value - params.endByte = sig->startBit / 8; - params.startByte = (sig->startBit + sig->signalSize - 1) / 8; - } - else - { - //for this ordering it goes in normal numerical order - params.startByte = sig->startBit / 8; - params.endByte = (sig->startBit + sig->signalSize - 1) / 8; - } - */ - setParams(params); -} - -void NewGraphDialog::setSignalActive(bool state) -{ - if (!state) return; - ui->rbSignalGraph->setChecked(true); - ui->rbStandardGraph->setChecked(false); - ui->cbMessages->setEnabled(true); - ui->cbSignals->setEnabled(true); - ui->cbSigned->setEnabled(false); - ui->txtBias->setEnabled(false); - ui->txtData->setEnabled(false); - ui->txtID->setEnabled(false); - ui->txtMask->setEnabled(false); - ui->txtScale->setEnabled(false); - ui->txtStride->setEnabled(false); -} - -void NewGraphDialog::setStandardActive(bool state) -{ - if (!state) return; - ui->rbStandardGraph->setChecked(true); - ui->rbSignalGraph->setChecked(false); - ui->cbMessages->setEnabled(false); - ui->cbSignals->setEnabled(false); - ui->cbSigned->setEnabled(true); - ui->txtBias->setEnabled(true); - ui->txtData->setEnabled(true); - ui->txtID->setEnabled(true); - ui->txtMask->setEnabled(true); - ui->txtScale->setEnabled(true); - ui->txtStride->setEnabled(true); + startBit = sig->startBit; + ui->txtBias->setText(QString::number(sig->bias)); + ui->txtDataLen->setText(QString::number(sig->signalSize)); + ui->txtID->setText(Utility::formatNumber(msg->ID)); + ui->txtMask->setText("0xFFFFFFFF"); + ui->txtName->setText(sig->name); + ui->txtScale->setText(QString::number(sig->factor)); + ui->txtStride->setText("1"); + ui->cbIntel->setChecked(sig->intelByteOrder); + if (sig->valType == SIGNED_INT) ui->cbSigned->setChecked(true); + else ui->cbSigned->setChecked(false); + drawBitfield(); } diff --git a/newgraphdialog.h b/newgraphdialog.h index 67fe5f0..907a03c 100644 --- a/newgraphdialog.h +++ b/newgraphdialog.h @@ -16,6 +16,7 @@ class NewGraphDialog : public QDialog public: explicit NewGraphDialog(DBCHandler *handler, QWidget *parent = 0); ~NewGraphDialog(); + void showEvent(QShowEvent*); void getParams(GraphParams &); void setParams(GraphParams &); void clearParams(); @@ -25,13 +26,15 @@ private slots: void colorSwatchClick(); void loadMessages(); void loadSignals(int idx); - void fillFormFromSignal(int idx); - void setSignalActive(bool); - void setStandardActive(bool); + void bitfieldClicked(int,int); + void handleDataLenUpdate(); + void drawBitfield(); + void copySignalToParamsUI(); private: Ui::NewGraphDialog *ui; DBCHandler *dbcHandler; + int startBit, dataLen; }; #endif // NEWGRAPHDIALOG_H diff --git a/newgraphdialog.ui b/newgraphdialog.ui index dcb7f12..0111816 100644 --- a/newgraphdialog.ui +++ b/newgraphdialog.ui @@ -6,8 +6,8 @@ 0 0 - 492 - 340 + 681 + 640 @@ -15,31 +15,7 @@ - - - - - Standard Graph - - - true - - - - - - - DBC Signal Graph - - - false - - - - - - - + @@ -55,28 +31,25 @@ - Data: + Start Bit: - - - - + Signed: - + - + QFrame::NoFrame @@ -86,47 +59,47 @@ - + - + Bias: - + - + Scale - + - + Stride - + - + Color - + true @@ -139,7 +112,7 @@ - + Add this graph @@ -156,6 +129,46 @@ + + + + + 0 + 290 + + + + + 150 + 150 + + + + + + + + Intel Format + + + + + + + + + + + + + + Data Len: + + + + + + @@ -214,12 +227,40 @@ + + + + Copy Signal Parameters + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + CANDataGrid + QWidget +
candatagrid.h
+ 1 +
+
diff --git a/serialworker.cpp b/serialworker.cpp index 36c26e7..ac8a5ec 100644 --- a/serialworker.cpp +++ b/serialworker.cpp @@ -495,6 +495,18 @@ void SerialWorker::handleTick() } } + int elapsed = elapsedTime->elapsed(); + if(elapsed) { + framesPerSec += gotFrames * 1000 / elapsed - (framesPerSec / 4); + elapsedTime->restart(); + } + else + framesPerSec = 0; + + emit frameUpdateTick(framesPerSec / 4, gotFrames); //sends stats to interested parties + canModel->sendBulkRefresh(gotFrames); + gotFrames = 0; + if (doValidation && serial && serial->isOpen()) sendCommValidation(); } diff --git a/utility.h b/utility.h index 0479172..d5614e8 100644 --- a/utility.h +++ b/utility.h @@ -178,7 +178,7 @@ public: * actual signal. This extends the sign bits out so that the integer result reads as the proper negative * value. We dont need to do any of this if the sign bit wasn't set. */ - int64_t signedMask = ~((1ULL << sigSize) - 1); + uint64_t signedMask = ~((1ULL << sigSize) - 1); result = (-1LL & signedMask) | result; } }