Add more customization support to CANDataGrid. Used that new functionality in Flow View

to allow for bitfield triggers. Click a cell to trigger on it.
Now playback will stop if that cell changes state while playing back.
This commit is contained in:
Collin Kidder
2019-08-01 21:57:03 -04:00
parent fb1016c933
commit 1fdd67777c
4 changed files with 101 additions and 0 deletions
+44
View File
@@ -14,6 +14,9 @@ CANDataGrid::CANDataGrid(QWidget *parent) :
memset(data, 0, 8); memset(data, 0, 8);
memset(refData, 0, 8); memset(refData, 0, 8);
memset(usedData, 0, 8); memset(usedData, 0, 8);
for (int x = 0; x < 8; x++)
for (int y = 0; y < 8; y++)
textStates[x][y] = GridTextState::NORMAL;
} }
CANDataGrid::~CANDataGrid() CANDataGrid::~CANDataGrid()
@@ -40,6 +43,25 @@ void CANDataGrid::mousePressEvent(QMouseEvent *event)
} }
} }
void CANDataGrid::setCellTextState(int x, int y, GridTextState state)
{
if (x < 0) return;
if (x > 7) return;
if (y < 0) return;
if (y > 7) return;
textStates[x][y] = state;
this->update();
}
GridTextState CANDataGrid::geCellTextState(int x, int y)
{
if (x < 0) return GridTextState::NORMAL;
if (x > 7) return GridTextState::NORMAL;
if (y < 0) return GridTextState::NORMAL;
if (y > 7) return GridTextState::NORMAL;
return textStates[x][y];
}
void CANDataGrid::paintEvent(QPaintEvent *event) void CANDataGrid::paintEvent(QPaintEvent *event)
{ {
Q_UNUSED(event); Q_UNUSED(event);
@@ -50,6 +72,7 @@ void CANDataGrid::paintEvent(QPaintEvent *event)
QBrush blackBrush, whiteBrush, redBrush, greenBrush, grayBrush; QBrush blackBrush, whiteBrush, redBrush, greenBrush, grayBrush;
QPainter painter(this); QPainter painter(this);
QRect viewport = painter.viewport(); QRect viewport = painter.viewport();
int xSpan = viewport.right() - viewport.left(); int xSpan = viewport.right() - viewport.left();
@@ -79,6 +102,9 @@ void CANDataGrid::paintEvent(QPaintEvent *event)
painter.setFont(mainFont); painter.setFont(mainFont);
QFont smallFont; QFont smallFont;
smallFont.setPixelSize(qMin(xSector, ySector) - 15); smallFont.setPixelSize(qMin(xSector, ySector) - 15);
QFont boldFont;
boldFont.setPixelSize(qMin(xSector, ySector) - 10);
boldFont.setBold(true);
painter.drawText(QRect(viewport.left(), viewport.top(), xSpan, ySector), Qt::AlignCenter, tr("BITS")); painter.drawText(QRect(viewport.left(), viewport.top(), xSpan, ySector), Qt::AlignCenter, tr("BITS"));
@@ -142,7 +168,25 @@ void CANDataGrid::paintEvent(QPaintEvent *event)
//painter.fillRect(viewport.left() + (x+2) * xSector, viewport.top() + (y+2) * ySector, xSector, ySector, redBrush); //painter.fillRect(viewport.left() + (x+2) * xSector, viewport.top() + (y+2) * ySector, xSector, ySector, redBrush);
painter.drawRect(viewport.left() + (x+2) * xSector, viewport.top() + (y+2) * ySector, xSector, ySector); painter.drawRect(viewport.left() + (x+2) * xSector, viewport.top() + (y+2) * ySector, xSector, ySector);
switch (textStates[x][y])
{
case GridTextState::NORMAL:
painter.setPen(QPen(Qt::gray));
painter.setFont(mainFont);
break;
case GridTextState::BOLD_BLUE:
painter.setPen(QPen(Qt::blue));
painter.setFont(boldFont);
break;
case GridTextState::INVERT:
painter.setFont(mainFont);
QColor brushColor = painter.brush().color();
painter.setPen(QColor(255-brushColor.red(), 255-brushColor.green(), 255-brushColor.blue()));
break;
}
painter.drawText(viewport.left() + (x+2) * xSector + (xSector / 8), viewport.top() + (y + 3) * ySector - (ySector / 3), QString::number(y * 8 + (7-x))); painter.drawText(viewport.left() + (x+2) * xSector + (xSector / 8), viewport.top() + (y + 3) * ySector - (ySector / 3), QString::number(y * 8 + (7-x)));
painter.setPen(QPen(Qt::gray));
} }
} }
upperLeft.setX(viewport.left() + 2 * xSector); upperLeft.setX(viewport.left() + 2 * xSector);
+24
View File
@@ -7,6 +7,27 @@ namespace Ui {
class CANDataGrid; class CANDataGrid;
} }
/*
* CANDataGrid is a custom QT widget that proves that it's possible to shoehorn 12x too much crap into one widget. The purported
* goal of the widget was to show an 8x8 grid of bits that show whether each bit in a given message is set or not. It allows one to quickly
* visualize the bits within a CAN frame's data bytes. From there it evolved to also show whether a given bit was freshly set recently, freshly
* unset recently, or has stayed set or unset for a while. It can also gray out bytes that don't exist in the current CAN frame.
* This is handled by the calling code.
*
* After that functionality was added so it could also emit a signal when it is clicked. The signal will tell you which "bit" cell was clicked.
*
* Then, support was added for modifying the text color of each cell. This can be used for whatever the calling code wants. An example is
* FlowView that uses all of the above functionality plus this functionality in order to show which bits are set as triggers for stopping
* the flowview playback.
*/
enum GridTextState
{
NORMAL,
BOLD_BLUE,
INVERT
};
class CANDataGrid : public QWidget class CANDataGrid : public QWidget
{ {
Q_OBJECT Q_OBJECT
@@ -19,6 +40,8 @@ public:
void updateData(unsigned char *, bool); void updateData(unsigned char *, bool);
void setUsed(unsigned char *, bool); void setUsed(unsigned char *, bool);
void saveImage(QString filename, int width, int height); void saveImage(QString filename, int width, int height);
void setCellTextState(int x, int y, GridTextState state);
GridTextState geCellTextState(int x, int y);
protected: protected:
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
@@ -31,6 +54,7 @@ private:
unsigned char refData[8]; unsigned char refData[8];
unsigned char data[8]; unsigned char data[8];
unsigned char usedData[8]; unsigned char usedData[8];
GridTextState textStates[8][8];
QPoint upperLeft, gridSize; QPoint upperLeft, gridSize;
}; };
+31
View File
@@ -27,6 +27,7 @@ FlowViewWindow::FlowViewWindow(const QVector<CANFrame> *frames, QWidget *parent)
memset(refBytes, 0, 8); memset(refBytes, 0, 8);
memset(currBytes, 0, 8); memset(currBytes, 0, 8);
memset(triggerValues, -1, sizeof(int) * 8); memset(triggerValues, -1, sizeof(int) * 8);
triggerBits = 0;
//ui->graphView->setInteractions(); //ui->graphView->setInteractions();
@@ -102,6 +103,7 @@ FlowViewWindow::FlowViewWindow(const QVector<CANFrame> *frames, QWidget *parent)
connect(ui->graphView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequestGraph(QPoint))); connect(ui->graphView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequestGraph(QPoint)));
ui->flowView->setContextMenuPolicy(Qt::CustomContextMenu); ui->flowView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->flowView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequestFlow(QPoint))); connect(ui->flowView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequestFlow(QPoint)));
connect(ui->flowView, SIGNAL(gridClicked(int,int)), this, SLOT(gotCellClick(int, int)));
playbackTimer->setInterval(ui->spinPlayback->value()); //set the timer to the default value of the control playbackTimer->setInterval(ui->spinPlayback->value()); //set the timer to the default value of the control
} }
@@ -207,6 +209,15 @@ bool FlowViewWindow::eventFilter(QObject *obj, QEvent *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() void FlowViewWindow::updateTriggerValues()
{ {
triggerValues[0] = Utility::ParseStringToNum(ui->txtTrigger0->text()); triggerValues[0] = Utility::ParseStringToNum(ui->txtTrigger0->text());
@@ -725,6 +736,26 @@ void FlowViewWindow::updatePosition(bool forward)
memcpy(refBytes, currBytes, 8); 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); memcpy(currBytes, frameCache.at(currentPosition).data, 8);
if (ui->cbSync->checkState() == Qt::Checked) emit sendCenterTimeID(frameCache[currentPosition].ID, frameCache[currentPosition].timestamp / 1000000.0); if (ui->cbSync->checkState() == Qt::Checked) emit sendCenterTimeID(frameCache[currentPosition].ID, frameCache[currentPosition].timestamp / 1000000.0);
+2
View File
@@ -37,6 +37,7 @@ private slots:
void plottableDoubleClick(QCPAbstractPlottable* plottable, QMouseEvent* event); void plottableDoubleClick(QCPAbstractPlottable* plottable, QMouseEvent* event);
void gotCenterTimeID(int32_t ID, double timestamp); void gotCenterTimeID(int32_t ID, double timestamp);
void updateTriggerValues(); void updateTriggerValues();
void gotCellClick(int x, int y);
signals: signals:
void sendCenterTimeID(int32_t ID, double timestamp); void sendCenterTimeID(int32_t ID, double timestamp);
@@ -49,6 +50,7 @@ private:
unsigned char refBytes[8]; unsigned char refBytes[8];
unsigned char currBytes[8]; unsigned char currBytes[8];
int triggerValues[8]; int triggerValues[8];
uint64_t triggerBits;
int currentPosition; int currentPosition;
QTimer *playbackTimer; QTimer *playbackTimer;
bool playbackActive; bool playbackActive;