Files
giesCANne/re/flowviewwindow.cpp
T
mjuhanne 428b19c5c4 - Save DBC file configuration when changed, and restore it when restarting the application (no need for manual entry everytime app is launched)
- Add GMLAN 29bit filtering feature. In addition to J1939 matching, it is now possible to match DBC messages using only the Arbitration Id (bits 14-26 of CAN Address), since  Priority id (higher bits) and Sender Id (lowest 13 bits) are not relevant when interpreting the CAN message content. This allows to use the excellent GM Global A DBC files on https://github.com/commaai/opendbc. Just remember to select the "GMLAN" matching criteria on DBC File manager.
- Add "Filter labeling" feature. Using loaded DBC files, it matches the filter Ids with DBC messages and shows them in filter windows. Labeling works in the main window (no need to set "Frame interpret" on, since this is a separate feature), as well as in Flow view, Frame data analysis, Playback and Fuzzzing windows. This makes it so much easier to concentrate on those CAN messages that matter. To use this feature, select global "Label filters using messages from DBC files" on Main Configuration page, and then enable the feature on individual DBC files (on DBC file manager) as needed. This is needed if there's overlap in CAN addresses between DBC files, and because the filter labeler cannot tell apart anymore which Filter ID is associated to which bus, it might label them incorrectly. But usually it's ok to just enable the labeling with all the DBC files.
- In Frame data analysis window, in addition to J1939 decoding show also GMLAN (Priority bits, Arbitration Id, Sender Id) decoding.
- FilterUtility static class used for centralized ListWidgetItem creation and for other helper functions
- DBC handler: To allow setting bus numbers even before connection is configured, do not enforce "valid" bus numbers
2019-08-11 23:16:02 +03:00

819 lines
26 KiB
C++

#include "flowviewwindow.h"
#include "ui_flowviewwindow.h"
#include "mainwindow.h"
#include "helpwindow.h"
#include "filterutility.h"
const QColor FlowViewWindow::graphColors[8] = {Qt::blue, Qt::green, Qt::black, Qt::red, //0 1 2 3
Qt::gray, Qt::yellow, Qt::cyan, Qt::darkMagenta}; //4 5 6 7
FlowViewWindow::FlowViewWindow(const QVector<CANFrame> *frames, QWidget *parent) :
QDialog(parent),
ui(new Ui::FlowViewWindow)
{
ui->setupUi(this);
setWindowFlags(Qt::Window);
readSettings();
modelFrames = frames;
playbackTimer = new QTimer();
currentPosition = 0;
playbackActive = false;
playbackForward = true;
memset(refBytes, 0, 8);
memset(currBytes, 0, 8);
memset(triggerValues, -1, sizeof(int) * 8);
triggerBits = 0;
//ui->graphView->setInteractions();
ui->graphView->xAxis->setRange(0, 8);
ui->graphView->yAxis->setRange(-10, 265); //run range a bit outside possible number so they aren't plotted in a hard to see place
ui->graphView->axisRect()->setupFullAxesBox();
QCPItemText *textLabel = new QCPItemText(ui->graphView);
//ui->graphView->addItem(textLabel);
textLabel->setPositionAlignment(Qt::AlignTop|Qt::AlignHCenter);
textLabel->position->setType(QCPItemPosition::ptAxisRectRatio);
textLabel->position->setCoords(0.5, .5);
textLabel->setText("+");
textLabel->setFont(QFont(font().family(), 16)); // make font a bit larger
textLabel->setPen(QPen(Qt::black)); // show black border around text
ui->graphView->xAxis->setLabel("Time Axis");
ui->graphView->yAxis->setLabel("Value Axis");
QFont legendFont = font();
legendFont.setPointSize(10);
QFont legendSelectedFont = font();
legendSelectedFont.setPointSize(12);
legendSelectedFont.setBold(true);
ui->graphView->legend->setFont(legendFont);
ui->graphView->legend->setSelectedFont(legendSelectedFont);
ui->graphView->legend->setSelectableParts(QCPLegend::spItems); // legend box shall not be selectable, only legend items
//ui->graphView->xAxis->setAutoSubTicks(false);
//ui->graphView->xAxis->setAutoTicks(false);
QCPAxisTicker *xTicker = new QCPAxisTicker();
xTicker->setTickCount(5);
xTicker->setTickStepStrategy(QCPAxisTicker::tssReadability);
ui->graphView->xAxis->setTicker(QSharedPointer<QCPAxisTicker>(xTicker));
//ui->graphView->xAxis->setAutoTickStep(false);
//ui->graphView->xAxis->setAutoSubTicks(false);
//ui->graphView->xAxis->setNumberFormat("gb");
//ui->graphView->xAxis->setTickStep(5.0);
//ui->graphView->xAxis->setSubTickCount(0);
if (openGLMode)
{
ui->graphView->setAntialiasedElements(QCP::aeAll);
ui->graphView->setOpenGl(true);
}
else
{
ui->graphView->setOpenGl(false);
ui->graphView->setAntialiasedElements(QCP::aeNone);
}
connect(ui->btnBackOne, SIGNAL(clicked(bool)), this, SLOT(btnBackOneClick()));
connect(ui->btnPause, SIGNAL(clicked(bool)), this, SLOT(btnPauseClick()));
connect(ui->btnReverse, SIGNAL(clicked(bool)), this, SLOT(btnReverseClick()));
connect(ui->btnStop, SIGNAL(clicked(bool)), this, SLOT(btnStopClick()));
connect(ui->btnPlay, SIGNAL(clicked(bool)), this, SLOT(btnPlayClick()));
connect(ui->btnForwardOne, SIGNAL(clicked(bool)), this, SLOT(btnFwdOneClick()));
connect(ui->spinPlayback, SIGNAL(valueChanged(int)), this, SLOT(changePlaybackSpeed(int)));
connect(ui->cbLoopPlayback, SIGNAL(clicked(bool)), this, SLOT(changeLooping(bool)));
connect(playbackTimer, SIGNAL(timeout()), this, SLOT(timerTriggered()));
connect(ui->graphView, SIGNAL(plottableDoubleClick(QCPAbstractPlottable*,QMouseEvent*)), this, SLOT(plottableDoubleClick(QCPAbstractPlottable*,QMouseEvent*)));
connect(ui->txtTrigger0, SIGNAL(textEdited(QString)), this, SLOT(updateTriggerValues()));
connect(ui->txtTrigger1, SIGNAL(textEdited(QString)), this, SLOT(updateTriggerValues()));
connect(ui->txtTrigger2, SIGNAL(textEdited(QString)), this, SLOT(updateTriggerValues()));
connect(ui->txtTrigger3, SIGNAL(textEdited(QString)), this, SLOT(updateTriggerValues()));
connect(ui->txtTrigger4, SIGNAL(textEdited(QString)), this, SLOT(updateTriggerValues()));
connect(ui->txtTrigger5, SIGNAL(textEdited(QString)), this, SLOT(updateTriggerValues()));
connect(ui->txtTrigger6, SIGNAL(textEdited(QString)), this, SLOT(updateTriggerValues()));
connect(ui->txtTrigger7, SIGNAL(textEdited(QString)), this, SLOT(updateTriggerValues()));
// Using lambda expression to strip away the possible filter label before passing the ID to updateDetailsWindow
connect(ui->listFrameID, &QListWidget::currentTextChanged,
[this](QString itemText)
{
changeID(FilterUtility::getId(itemText));
} );
connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int)));
ui->graphView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->graphView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequestGraph(QPoint)));
ui->flowView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->flowView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequestFlow(QPoint)));
connect(ui->flowView, SIGNAL(gridClicked(int,int)), this, SLOT(gotCellClick(int, int)));
// Prevent annoying accidental horizontal scrolling when filter list is populated with long interpreted message names
ui->listFrameID->horizontalScrollBar()->setEnabled(false);
playbackTimer->setInterval(ui->spinPlayback->value()); //set the timer to the default value of the control
}
void FlowViewWindow::showEvent(QShowEvent* event)
{
QDialog::showEvent(event);
installEventFilter(this);
readSettings();
refreshIDList();
if (ui->listFrameID->count() > 0)
{
changeID(FilterUtility::getId(ui->listFrameID->item(0)));
ui->listFrameID->setCurrentRow(0);
}
updateFrameLabel();
qDebug() << "FlowView show event was processed";
}
FlowViewWindow::~FlowViewWindow()
{
removeEventFilter(this);
delete ui;
playbackTimer->stop();
delete playbackTimer;
}
void FlowViewWindow::closeEvent(QCloseEvent *event)
{
Q_UNUSED(event);
writeSettings();
}
void FlowViewWindow::readSettings()
{
QSettings settings;
if (settings.value("Main/SaveRestorePositions", false).toBool())
{
resize(settings.value("FlowView/WindowSize", QSize(1078, 621)).toSize());
move(settings.value("FlowView/WindowPos", QPoint(50, 50)).toPoint());
}
if (settings.value("FlowView/AutoRef", false).toBool())
{
ui->cbAutoRef->setChecked(true);
}
if (settings.value("FlowView/UseTimestamp", false).toBool())
{
ui->cbTimeGraph->setChecked(true);
}
if (settings.value("Main/FilterLabeling", false).toBool())
ui->listFrameID->setMaximumWidth(250);
else
ui->listFrameID->setMaximumWidth(120);
secondsMode = settings.value("Main/TimeSeconds", false).toBool();
openGLMode = settings.value("Main/UseOpenGL", false).toBool();
}
void FlowViewWindow::writeSettings()
{
QSettings settings;
if (settings.value("Main/SaveRestorePositions", false).toBool())
{
settings.setValue("FlowView/WindowSize", size());
settings.setValue("FlowView/WindowPos", pos());
}
}
/*
* Keyboard shortcuts to allow for quick work without needing to move around a mouse.
* R = resume or pause playback
* T = go back one frame
* Y = go forward one frame
*/
bool FlowViewWindow::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyRelease) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
switch (keyEvent->key())
{
case Qt::Key_R:
if (playbackActive) btnPauseClick();
else btnPlayClick();
break;
case Qt::Key_T:
btnBackOneClick();
break;
case Qt::Key_Y:
btnFwdOneClick();
break;
case Qt::Key_F1:
HelpWindow::getRef()->showHelp("flowview.html");
break;
}
return true;
} else {
// standard event processing
return QObject::eventFilter(obj, event);
}
}
void FlowViewWindow::gotCellClick(int x, int y)
{
int bitnum = (7-x) + (8 * y);
triggerBits = triggerBits ^ (1ull << bitnum);
if (triggerBits & (1ull << bitnum)) ui->flowView->setCellTextState(x, y, GridTextState::BOLD_BLUE);
else ui->flowView->setCellTextState(x, y, GridTextState::NORMAL);
qDebug() << "Bit Num: " << bitnum << " Hex of trigger bits: " << QString::number(triggerBits, 16);
}
void FlowViewWindow::updateTriggerValues()
{
triggerValues[0] = Utility::ParseStringToNum(ui->txtTrigger0->text());
triggerValues[1] = Utility::ParseStringToNum(ui->txtTrigger1->text());
triggerValues[2] = Utility::ParseStringToNum(ui->txtTrigger2->text());
triggerValues[3] = Utility::ParseStringToNum(ui->txtTrigger3->text());
triggerValues[4] = Utility::ParseStringToNum(ui->txtTrigger4->text());
triggerValues[5] = Utility::ParseStringToNum(ui->txtTrigger5->text());
triggerValues[6] = Utility::ParseStringToNum(ui->txtTrigger6->text());
triggerValues[7] = Utility::ParseStringToNum(ui->txtTrigger7->text());
}
void FlowViewWindow::plottableDoubleClick(QCPAbstractPlottable* plottable, QMouseEvent* event)
{
int id = 0;
//apply transforms to get the X axis value where we double clicked
double coord = plottable->keyAxis()->pixelToCoord(event->localPos().x());
if (frameCache.count() > 0) id = frameCache[0].ID;
if (secondsMode) emit sendCenterTimeID(id, coord);
else emit sendCenterTimeID(id, coord / 1000000.0);
}
void FlowViewWindow::gotCenterTimeID(int32_t ID, double timestamp)
{
uint64_t t_stamp;
t_stamp = timestamp * 1000000l;
qDebug() << "timestamp: " << t_stamp;
changeID(QString::number(ID)); //to be sure we're focused on the proper ID
for (int j = 0; j < ui->listFrameID->count(); j++)
{
int thisNum = FilterUtility::getIdAsInt(ui->listFrameID->item(j));
if (thisNum == ID)
{
ui->listFrameID->setCurrentRow(j);
break;
}
}
int bestIdx = -1;
for (int i = 0; i < frameCache.count(); i++)
{
if (frameCache[i].timestamp > t_stamp)
{
bestIdx = i - 1;
break;
}
}
qDebug() << "Best index " << bestIdx;
if (bestIdx > -1)
{
currentPosition = bestIdx;
if (ui->cbAutoRef->isChecked())
{
memcpy(refBytes, currBytes, 8);
}
memcpy(currBytes, frameCache.at(currentPosition).data, 8);
updateDataView();
}
}
void FlowViewWindow::contextMenuRequestFlow(QPoint pos)
{
QMenu *menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose);
menu->addAction(tr("Save image to file"), this, SLOT(saveFileFlow()));
menu->popup(ui->flowView->mapToGlobal(pos));
}
void FlowViewWindow::contextMenuRequestGraph(QPoint pos)
{
QMenu *menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose);
menu->addAction(tr("Save image to file"), this, SLOT(saveFileGraph()));
menu->popup(ui->graphView->mapToGlobal(pos));
}
void FlowViewWindow::saveFileGraph()
{
QString filename;
QFileDialog dialog(this);
QSettings settings;
QStringList filters;
filters.append(QString(tr("PDF Files (*.pdf)")));
filters.append(QString(tr("PNG Files (*.png)")));
filters.append(QString(tr("JPEG Files (*.jpg)")));
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
dialog.setAcceptMode(QFileDialog::AcceptSave);
dialog.setDirectory(settings.value("FlowView/LoadSaveDirectory", dialog.directory().path()).toString());
if (dialog.exec() == QDialog::Accepted)
{
filename = dialog.selectedFiles()[0];
if (dialog.selectedNameFilter() == filters[0])
{
if (!filename.contains('.')) filename += ".pdf";
ui->graphView->savePdf(filename, 0, 0);
}
if (dialog.selectedNameFilter() == filters[1])
{
if (!filename.contains('.')) filename += ".png";
ui->graphView->savePng(filename, 1024, 768);
}
if (dialog.selectedNameFilter() == filters[2])
{
if (!filename.contains('.')) filename += ".jpg";
ui->graphView->saveJpg(filename, 1024, 768);
}
settings.setValue("FlowView/LoadSaveDirectory", dialog.directory().path());
}
}
void FlowViewWindow::saveFileFlow()
{
QString filename;
QFileDialog dialog(this);
QSettings settings;
QStringList filters;
filters.append(QString(tr("PNG Files (*.png)")));
filters.append(QString(tr("JPEG Files (*.jpg)")));
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
dialog.setAcceptMode(QFileDialog::AcceptSave);
dialog.setDirectory(settings.value("FlowView/LoadSaveDirectory", dialog.directory().path()).toString());
if (dialog.exec() == QDialog::Accepted)
{
filename = dialog.selectedFiles()[0];
if (dialog.selectedNameFilter() == filters[0])
{
if (!filename.contains('.')) filename += ".png";
ui->flowView->saveImage(filename, 1024, 768);
}
if (dialog.selectedNameFilter() == filters[1])
{
if (!filename.contains('.')) filename += ".jpg";
ui->flowView->saveImage(filename, 1024, 768);
}
settings.setValue("FlowView/LoadSaveDirectory", dialog.directory().path());
}
}
void FlowViewWindow::updatedFrames(int numFrames)
{
QVector<double>newX[8];
QVector<double>newY[8];
CANFrame thisFrame;
if (numFrames == -1) //all frames deleted. Kill the display
{
ui->listFrameID->clear();
foundID.clear();
currentPosition = 0;
refreshIDList();
updateFrameLabel();
removeAllGraphs();
memset(refBytes, 0, 8);
memset(currBytes, 0, 8);
updateDataView();
}
else if (numFrames == -2) //all new set of frames. Reset
{
ui->listFrameID->clear();
foundID.clear();
currentPosition = 0;
refreshIDList();
//if (ui->listFrameID->count() > 0)
//{
// changeID(ui->listFrameID->item(0)->text());
// ui->listFrameID->setCurrentRow(0);
//}
//updateFrameLabel();
}
else //just got some new frames. See if they are relevant.
{
if (numFrames > modelFrames->count()) return;
unsigned int refID;
if (frameCache.count() > 0) refID = frameCache[0].ID;
else refID = 0;
bool needRefresh = false;
for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
{
thisFrame = modelFrames->at(i);
if (!foundID.contains(thisFrame.ID))
{
foundID.append(thisFrame.ID);
FilterUtility::createFilterItem(thisFrame.ID, ui->listFrameID);
}
if (thisFrame.ID == refID)
{
frameCache.append(thisFrame);
for (int k = 0; k < 8; k++)
{
if (ui->cbTimeGraph->isChecked())
{
if (secondsMode){
newX[k].append((double)(thisFrame.timestamp) / 1000000.0);
}
else
{
newX[k].append(thisFrame.timestamp);
}
}
else
{
newX[k].append(x[k].count());
}
newY[k].append(thisFrame.data[k]);
needRefresh = true;
}
}
}
if (ui->cbLiveMode->checkState() == Qt::Checked)
{
currentPosition = frameCache.count() - 1;
memcpy(currBytes, frameCache.at(currentPosition).data, 8);
memcpy(refBytes, currBytes, 8);
}
if (needRefresh)
{
for (int k = 0; k < 8; k++)
{
if (graphRef[k] && graphRef[k]->data())
graphRef[k]->addData(newX[k], newY[k]);
}
ui->graphView->replot();
updateDataView();
if (ui->cbSync->checkState() == Qt::Checked) emit sendCenterTimeID(frameCache[currentPosition].ID, frameCache[currentPosition].timestamp / 1000000.0);
}
}
updateFrameLabel();
}
void FlowViewWindow::removeAllGraphs()
{
ui->graphView->clearGraphs();
ui->graphView->replot();
}
void FlowViewWindow::createGraph(int byteNum)
{
int tempVal;
float minval=1000000, maxval = -100000;
qDebug() << "Create Graph " << byteNum;
bool graphByTime = ui->cbTimeGraph->isChecked();
int numEntries = frameCache.count();
x[byteNum].clear();
y[byteNum].clear();
x[byteNum].resize(numEntries);
y[byteNum].resize(numEntries);
for (int j = 0; j < numEntries; j++)
{
tempVal = frameCache[j].data[byteNum];
if (graphByTime)
{
if (secondsMode){
x[byteNum][j] = (double)(frameCache[j].timestamp) / 1000000.0;
}
else
{
x[byteNum][j] = frameCache[j].timestamp;
}
}
else
{
x[byteNum][j] = j;
}
y[byteNum][j] = tempVal;
if (y[byteNum][j] < minval) minval = y[byteNum][j];
if (y[byteNum][j] > maxval) maxval = y[byteNum][j];
}
graphRef[byteNum] = ui->graphView->addGraph();
ui->graphView->graph()->setName(QString("Graph %1").arg(ui->graphView->graphCount()-1));
ui->graphView->graph()->setData(x[byteNum],y[byteNum]);
ui->graphView->graph()->setLineStyle(QCPGraph::lsLine); //connect points with lines
QPen graphPen;
graphPen.setColor(graphColors[byteNum]);
graphPen.setWidth(1);
ui->graphView->graph()->setPen(graphPen);
ui->graphView->axisRect()->setupFullAxesBox();
}
void FlowViewWindow::refreshIDList()
{
int id;
for (int i = 0; i < modelFrames->count(); i++)
{
CANFrame thisFrame = modelFrames->at(i);
id = thisFrame.ID;
if (!foundID.contains(id))
{
foundID.append(id);
FilterUtility::createFilterItem(id, ui->listFrameID);
}
}
//default is to sort in ascending order
ui->listFrameID->sortItems();
}
void FlowViewWindow::updateFrameLabel()
{
ui->lblNumFrames->setText(QString::number(currentPosition) + tr(" of ") + QString::number(frameCache.count()));
}
void FlowViewWindow::changeID(QString newID)
{
qDebug() << "change id " << newID;
//parse the ID and then load up the frame cache with just messages with that ID.
uint32_t id = (uint32_t)Utility::ParseStringToNum(newID);
frameCache.clear();
if (modelFrames->count() == 0) return;
playbackTimer->stop();
playbackActive = false;
for (int x = 0; x < modelFrames->count(); x++)
{
CANFrame thisFrame = modelFrames->at(x);
if (thisFrame.ID == id)
{
for (int j = thisFrame.len; j < 8; j++) thisFrame.data[j] = 0;
frameCache.append(thisFrame);
}
}
currentPosition = 0;
if (frameCache.count() == 0) return;
removeAllGraphs();
//for (uint32_t c = 0; c < frameCache.at(0).len; c++)
for (uint32_t c = 0; c < 8; c++)
{
createGraph(c);
}
updateGraphLocation();
memcpy(currBytes, frameCache.at(currentPosition).data, 8);
memcpy(refBytes, currBytes, 8);
updateDataView();
}
void FlowViewWindow::btnBackOneClick()
{
playbackTimer->stop(); //pushing this button halts automatic playback
playbackActive = false;
updatePosition(false);
updateDataView();
}
void FlowViewWindow::btnPauseClick()
{
playbackActive = false;
playbackTimer->stop();
updateDataView();
}
void FlowViewWindow::btnReverseClick()
{
playbackActive = true;
playbackForward = false;
playbackTimer->start();
}
void FlowViewWindow::btnStopClick()
{
playbackTimer->stop(); //pushing this button halts automatic playback
playbackActive = false;
currentPosition = 0;
memcpy(currBytes, frameCache.at(currentPosition).data, 8);
memcpy(refBytes, currBytes, 8);
updateFrameLabel();
updateDataView();
}
void FlowViewWindow::btnPlayClick()
{
playbackActive = true;
playbackForward = true;
playbackTimer->start();
}
void FlowViewWindow::btnFwdOneClick()
{
playbackTimer->stop();
playbackActive = false;
updatePosition(true);
updateDataView();
}
void FlowViewWindow::changePlaybackSpeed(int newSpeed)
{
playbackTimer->setInterval(newSpeed);
}
void FlowViewWindow::changeLooping(bool check)
{
Q_UNUSED(check);
}
void FlowViewWindow::timerTriggered()
{
if (!playbackActive || (ui->cbLiveMode->checkState() == Qt::Checked))
{
playbackTimer->stop();
return;
}
if (playbackForward)
{
updatePosition(true);
}
else
{
updatePosition(false);
}
updateDataView();
if (!ui->cbLoopPlayback->isChecked())
{
if (currentPosition == 0) playbackActive = false;
if (currentPosition == (frameCache.count() - 1)) playbackActive = false;
}
}
void FlowViewWindow::updateDataView()
{
ui->txtCurr1->setText(Utility::formatNumber(currBytes[0]));
ui->txtCurr2->setText(Utility::formatNumber(currBytes[1]));
ui->txtCurr3->setText(Utility::formatNumber(currBytes[2]));
ui->txtCurr4->setText(Utility::formatNumber(currBytes[3]));
ui->txtCurr5->setText(Utility::formatNumber(currBytes[4]));
ui->txtCurr6->setText(Utility::formatNumber(currBytes[5]));
ui->txtCurr7->setText(Utility::formatNumber(currBytes[6]));
ui->txtCurr8->setText(Utility::formatNumber(currBytes[7]));
ui->txtRef1->setText(Utility::formatNumber(refBytes[0]));
ui->txtRef2->setText(Utility::formatNumber(refBytes[1]));
ui->txtRef3->setText(Utility::formatNumber(refBytes[2]));
ui->txtRef4->setText(Utility::formatNumber(refBytes[3]));
ui->txtRef5->setText(Utility::formatNumber(refBytes[4]));
ui->txtRef6->setText(Utility::formatNumber(refBytes[5]));
ui->txtRef7->setText(Utility::formatNumber(refBytes[6]));
ui->txtRef8->setText(Utility::formatNumber(refBytes[7]));
ui->flowView->setReference(refBytes, false);
ui->flowView->updateData(currBytes, true);
for (int i = 0; i < 8; i++)
{
if (currBytes[i] == triggerValues[i])
{
playbackActive = false;
playbackTimer->stop();
break; //once any trigger is hit we can stop looking
}
}
updateGraphLocation();
updateFrameLabel();
}
void FlowViewWindow::updatePosition(bool forward)
{
if (forward)
{
if (currentPosition < (frameCache.count() - 1)) currentPosition++;
else if (ui->cbLoopPlayback->isChecked()) currentPosition = 0;
}
else
{
if (currentPosition > 0) currentPosition--;
else if (ui->cbLoopPlayback->isChecked()) currentPosition = frameCache.count() - 1;
}
if (ui->cbAutoRef->isChecked())
{
memcpy(refBytes, currBytes, 8);
}
//figure out which bits changed since the previous frame and then AND that with the trigger bits. If any bits
//get through that then they're changed and a trigger so we stop playback at this frame.
uint64_t changedBits = 0;
uint8_t cngByte;
for (int i = 0; i < 8; i++)
{
cngByte = currBytes[i] ^ frameCache.at(currentPosition).data[i];
changedBits |= (uint64_t)cngByte << (8ull * i);
}
qDebug() << "ChangedBits: " << QString::number(changedBits, 16);
qDebug() << "TriggerBits: " << QString::number(triggerBits, 16);
changedBits &= triggerBits;
qDebug() << "Final ChangedBits: " << QString::number(changedBits, 16);
if (changedBits)
{
playbackActive = false;
playbackTimer->stop();
}
memcpy(currBytes, frameCache.at(currentPosition).data, 8);
if (ui->cbSync->checkState() == Qt::Checked) emit sendCenterTimeID(frameCache[currentPosition].ID, frameCache[currentPosition].timestamp / 1000000.0);
}
void FlowViewWindow::updateGraphLocation()
{
if (frameCache.count() == 0) return;
int start = currentPosition - 5;
if (start < 0) start = 0;
int end = currentPosition + 5;
if (end >= frameCache.count()) end = frameCache.count() - 1;
if (ui->cbTimeGraph->isChecked())
{
if (secondsMode)
{
ui->graphView->xAxis->setRange(frameCache[start].timestamp / 1000000.0, frameCache[end].timestamp / 1000000.0);
/*
ui->graphView->xAxis->setTickStep((frameCache[end].timestamp - frameCache[start].timestamp)/ 3000000.0);
ui->graphView->xAxis->setSubTickCount(0);
ui->graphView->xAxis->setNumberFormat("f");
ui->graphView->xAxis->setNumberPrecision(6);
*/
}
else
{
ui->graphView->xAxis->setRange(frameCache[start].timestamp, frameCache[end].timestamp);
/*
ui->graphView->xAxis->setTickStep((frameCache[end].timestamp - frameCache[start].timestamp)/ 3.0);
ui->graphView->xAxis->setSubTickCount(0);
ui->graphView->xAxis->setNumberFormat("f");
ui->graphView->xAxis->setNumberPrecision(0); */
}
}
else
{
ui->graphView->xAxis->setRange(start, end);
//ui->graphView->xAxis->setTickStep(5.0);
//ui->graphView->xAxis->setSubTickCount(0);
ui->graphView->xAxis->setNumberFormat("gb");
}
ui->graphView->replot();
}