diff --git a/SavvyCAN.pro b/SavvyCAN.pro index 22465d3..b9863f2 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -6,7 +6,7 @@ QT += core gui -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets serialport +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets serialport printsupport TARGET = SavvyCAN TEMPLATE = app @@ -15,11 +15,19 @@ TEMPLATE = app SOURCES += main.cpp\ mainwindow.cpp \ canframemodel.cpp \ - utility.cpp + utility.cpp \ + qcustomplot.cpp \ + graphingwindow.cpp \ + frameinfowindow.cpp HEADERS += mainwindow.h \ can_structs.h \ canframemodel.h \ - utility.h + utility.h \ + qcustomplot.h \ + graphingwindow.h \ + frameinfowindow.h -FORMS += mainwindow.ui +FORMS += mainwindow.ui \ + graphingwindow.ui \ + frameinfowindow.ui diff --git a/canframemodel.cpp b/canframemodel.cpp index 5d3086f..684f2ca 100644 --- a/canframemodel.cpp +++ b/canframemodel.cpp @@ -122,3 +122,11 @@ void CANFrameModel::clearFrames() frames.clear(); endRemoveRows(); } + +//Is this safe? Maybe not but if we don't change it then that's OK +//Is it the best C++ practice? Probably not. This breaks the MVC paradigm +//but, it's for a good cause. +QList* CANFrameModel::getListReference() +{ + return &frames; +} diff --git a/canframemodel.h b/canframemodel.h index 66c372b..e1f841d 100644 --- a/canframemodel.h +++ b/canframemodel.h @@ -22,6 +22,7 @@ public: void sendRefresh(); void sendRefresh(int); void clearFrames(); + QList *getListReference(); private: diff --git a/frameinfowindow.cpp b/frameinfowindow.cpp new file mode 100644 index 0000000..4d56411 --- /dev/null +++ b/frameinfowindow.cpp @@ -0,0 +1,146 @@ +#include "frameinfowindow.h" +#include "ui_frameinfowindow.h" + +FrameInfoWindow::FrameInfoWindow(QList *frames, QWidget *parent) : + QDialog(parent), + ui(new Ui::FrameInfoWindow) +{ + ui->setupUi(this); + modelFrames = frames; + refreshIDList(); +} + +FrameInfoWindow::~FrameInfoWindow() +{ + delete ui; +} + +void FrameInfoWindow::updateDetailsWindow(int frameIdx) +{ + int idx, numFrames, targettedID; + int minLen, maxLen, thisLen; + int minData[8]; + int maxData[8]; + int dataHistogram[256][8]; + //TreeNode baseNode, dataBase, histBase, numBase; +/* + if (frameIdx > -1) + { + parseFrameCache(); + targettedID = Utility.ParseStringToNum(listFrameIDs.Items[listFrameIDs.SelectedIndex].ToString()); + idx = getIdxForID(targettedID); + numFrames = frames[idx].Count; + + treeDetails.Nodes.Clear(); + baseNode = treeDetails.Nodes.Add("ID: " + listFrameIDs.Items[listFrameIDs.SelectedIndex].ToString()); + if (frames[idx].ElementAt(0).extended) //if these frames seem to be extended then try for J1939 decoding + { + J1939ID jid = new J1939ID(); + jid.src = targettedID & 0xFF; + jid.priority = targettedID >> 26; + jid.pgn = (targettedID >> 8) & 0x3FFFF; //18 bits + jid.pf = (targettedID >> 16) & 0xFF; + jid.ps = (targettedID >> 8) & 0xFF; + + if (jid.pf > 0xEF) + { + jid.isBroadcast = true; + jid.dest = 0xFFFF; + baseNode.Nodes.Add("Broadcast frame"); + } + else + { + jid.dest = jid.ps; + baseNode.Nodes.Add("Destination ID: 0x" + jid.dest.ToString("X2")); + } + baseNode.Nodes.Add("SRC: 0x" + jid.src.ToString("X2")); + baseNode.Nodes.Add("PGN: " + jid.pgn.ToString()); + baseNode.Nodes.Add("PF: 0x" + jid.pf.ToString("X2")); + baseNode.Nodes.Add("PS: 0x" + jid.ps.ToString("X2")); + } + treeDetails.Nodes.Add("# Of Frames: " + numFrames.ToString()); + minLen = 8; + maxLen = 0; + for (int i = 0; i < 8; i++) + { + minData[i] = 256; + maxData[i] = -1; + for (int k = 0; k < 256; k++) dataHistogram[k, i] = 0; + } + for (int j = 0; j < numFrames; j++) + { + thisLen = frames[idx].ElementAt(j).len; + if (thisLen > maxLen) maxLen = thisLen; + if (thisLen < minLen) minLen = thisLen; + for (int c = 0; c < thisLen; c++) + { + byte dat = frames[idx].ElementAt(j).data[c]; + if (minData[c] > dat) minData[c] = dat; + if (maxData[c] < dat) maxData[c] = dat; + dataHistogram[dat, c]++; //add one to count for this + } + } + if (minLen < maxLen) + baseNode = treeDetails.Nodes.Add("Data Length: " + minLen.ToString() + " to " + maxLen.ToString()); + else + baseNode = treeDetails.Nodes.Add("Data Length: " + minLen.ToString()); + + for (int d = 0; d < numGraphs; d++) + { + Graphs[d].valueCache = null; + Graphs[d].ID = 0; + } + + for (int c = 0; c < maxLen; c++) + { + Graphs[6 + c].bias = 0; + Graphs[6 + c].scale = 1; + Graphs[6 + c].mask = 0xFF; + Graphs[6 + c].B1 = c; + Graphs[6 + c].B2 = c; + Graphs[6 + c].ID = targettedID; + Graphs[6 + c].color = theseColors[c]; + Graphs[6 + c].signed = false; + if (numFrames > 100) + { + Graphs[6 + c].stride = (int)((((float)numFrames) / 100.0f) + 0.5f); + } + else Graphs[6 + c].stride = 1; + + dataBase = baseNode.Nodes.Add("Data Byte " + c.ToString()); + dataBase.Nodes.Add("Range: " + minData[c] + " to " + maxData[c]); + histBase = dataBase.Nodes.Add("Histogram"); + for (int d = 0; d < 256; d++) + { + if (dataHistogram[d, c] > 0) + { + numBase = histBase.Nodes.Add(d.ToString() + "/0x" + d.ToString("X2") + ": " + dataHistogram[d, c]); + } + } + } + + parseFrameCache(); + for (int c = 0; c < maxLen; c++) setupGraph(6 + c); + setupGraphs(); + } + else + { + } +*/ +} + +void FrameInfoWindow::refreshIDList() +{ + int id; + for (int i = 0; i < modelFrames->count(); i++) + { + id = modelFrames->at(i).ID; + if (!foundID.contains(id)) + { + foundID.append(id); + ui->listFrameID->addItem(QString::number(id, 16).toUpper().rightJustified(4,'0')); + } + } + //default is to sort in ascending order + ui->listFrameID->sortItems(); +} diff --git a/frameinfowindow.h b/frameinfowindow.h new file mode 100644 index 0000000..bc94557 --- /dev/null +++ b/frameinfowindow.h @@ -0,0 +1,32 @@ +#ifndef FRAMEINFOWINDOW_H +#define FRAMEINFOWINDOW_H + +#include +#include "can_structs.h" + +namespace Ui { +class FrameInfoWindow; +} + +class FrameInfoWindow : public QDialog +{ + Q_OBJECT + +public: + explicit FrameInfoWindow(QList *frames, QWidget *parent = 0); + ~FrameInfoWindow(); + +private slots: + void updateDetailsWindow(int frameIdx); + +private: + Ui::FrameInfoWindow *ui; + + QList foundID; + QList frameCache; + QList *modelFrames; + + void refreshIDList(); +}; + +#endif // FRAMEINFOWINDOW_H diff --git a/frameinfowindow.ui b/frameinfowindow.ui new file mode 100644 index 0000000..9d8f066 --- /dev/null +++ b/frameinfowindow.ui @@ -0,0 +1,68 @@ + + + FrameInfoWindow + + + + 0 + 0 + 794 + 494 + + + + Detailed Frame Information + + + + + + + + Frame IDs: + + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + + + + + + + + Details: + + + + + + + + 1 + + + + + + + + + + + diff --git a/graphingwindow.cpp b/graphingwindow.cpp new file mode 100644 index 0000000..38ef927 --- /dev/null +++ b/graphingwindow.cpp @@ -0,0 +1,14 @@ +#include "graphingwindow.h" +#include "ui_graphingwindow.h" + +GraphingWindow::GraphingWindow(QWidget *parent) : + QDialog(parent), + ui(new Ui::GraphingWindow) +{ + ui->setupUi(this); +} + +GraphingWindow::~GraphingWindow() +{ + delete ui; +} diff --git a/graphingwindow.h b/graphingwindow.h new file mode 100644 index 0000000..8e021a9 --- /dev/null +++ b/graphingwindow.h @@ -0,0 +1,22 @@ +#ifndef GRAPHINGWINDOW_H +#define GRAPHINGWINDOW_H + +#include + +namespace Ui { +class GraphingWindow; +} + +class GraphingWindow : public QDialog +{ + Q_OBJECT + +public: + explicit GraphingWindow(QWidget *parent = 0); + ~GraphingWindow(); + +private: + Ui::GraphingWindow *ui; +}; + +#endif // GRAPHINGWINDOW_H diff --git a/graphingwindow.ui b/graphingwindow.ui new file mode 100644 index 0000000..1ef8f42 --- /dev/null +++ b/graphingwindow.ui @@ -0,0 +1,51 @@ + + + GraphingWindow + + + + 0 + 0 + 800 + 600 + + + + + 0 + 0 + + + + + 800 + 600 + + + + Dialog + + + true + + + + + + + + + + + + + + QCustomPlot + QWidget +
qcustomplot.h
+ 1 +
+
+ + +
diff --git a/mainwindow.cpp b/mainwindow.cpp index d59caa6..0edf988 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -36,11 +36,15 @@ MainWindow::MainWindow(QWidget *parent) : rx_state = IDLE; rx_step = 0; + + graphingWindow = NULL; + frameInfoWindow = NULL; } MainWindow::~MainWindow() { delete ui; + if (graphingWindow) delete graphingWindow; } void MainWindow::addFrameToDisplay(CANFrame &frame, bool autoRefresh = false) @@ -239,6 +243,19 @@ void MainWindow::connButtonPress() } } +void MainWindow::showGraphingWindow() +{ + if (!graphingWindow) graphingWindow = new GraphingWindow(); + graphingWindow->show(); +} + +void MainWindow::showFrameDataAnalysis() +{ + //only create an instance of the object if we dont have one. Otherwise just display the existing one. + if (!frameInfoWindow) frameInfoWindow = new FrameInfoWindow(model->getListReference()); + frameInfoWindow->show(); +} + void MainWindow::readSerialData() { QByteArray data = port->readAll(); diff --git a/mainwindow.h b/mainwindow.h index 092ee22..920ca51 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -5,6 +5,8 @@ #include #include "canframemodel.h" #include "can_structs.h" +#include "graphingwindow.h" +#include "frameinfowindow.h" namespace Ui { class MainWindow; @@ -34,6 +36,8 @@ private slots: void handleLoadFile(); void connButtonPress(); void readSerialData(); + void showGraphingWindow(); + void showFrameDataAnalysis(); private: Ui::MainWindow *ui; @@ -43,6 +47,8 @@ private: STATE rx_state; int rx_step; CANFrame buildFrame; + GraphingWindow *graphingWindow; + FrameInfoWindow *frameInfoWindow; void loadCRTDFile(QString); void loadNativeCSVFile(QString); void loadGenericCSVFile(QString); diff --git a/mainwindow.ui b/mainwindow.ui index c065db8..96a3d4e 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -104,6 +104,7 @@ + @@ -177,6 +178,11 @@ Save Log File + + + Frame Data Analysis + + @@ -213,9 +219,43 @@ + + actionGraph_Dta + triggered() + MainWindow + showGraphingWindow() + + + -1 + -1 + + + 463 + 249 + + + + + actionFrame_Data_Analysis + triggered() + MainWindow + showFrameDataAnalysis() + + + -1 + -1 + + + 463 + 249 + + + handleLoadFile() connButtonPress() + showGraphingWindow() + showFrameDataAnalysis()