diff --git a/SavvyCAN.pro b/SavvyCAN.pro index 7f1d15e..39f0178 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -64,7 +64,8 @@ SOURCES += main.cpp\ bus_protocols/isotp_handler.cpp \ bus_protocols/j1939_handler.cpp \ bus_protocols/uds_handler.cpp \ - jsedit.cpp + jsedit.cpp \ + frameplaybackobject.cpp HEADERS += mainwindow.h \ can_structs.h \ @@ -117,7 +118,8 @@ HEADERS += mainwindow.h \ bus_protocols/j1939_handler.h \ bus_protocols/uds_handler.h \ bus_protocols/isotp_message.h \ - jsedit.h + jsedit.h \ + frameplaybackobject.h FORMS += ui/candatagrid.ui \ ui/connectionwindow.ui \ diff --git a/bus_protocols/uds_handler.cpp b/bus_protocols/uds_handler.cpp index 86f7551..e9e0ed0 100644 --- a/bus_protocols/uds_handler.cpp +++ b/bus_protocols/uds_handler.cpp @@ -23,6 +23,8 @@ QVector UDS_COMM_CTRL_SUB = { {0,"COMM_NORMAL", "Enable both Rx and Tx of normal messages"}, {1,"COMM_DIS_TX", "Enable reception of normal messages but don't Tx them"}, {3,"COMM_DIS_ALL", "Disable both Rx and Tx of non-diagnostics messages"}, + {4,"COMM_DIS_TX_ENH", "Addressed bus master should turn off TX on related sub-bus"}, + {5,"COMM_ENHANC", "Addressed bus master should set related sub-bus to app scheduling mode"}, }; QVector UDS_ROUTINE_SUB = { @@ -319,6 +321,8 @@ QString UDS_HANDLER::getDetailedMessageAnalysis(const UDS_MESSAGE &msg) { QString buildString; bool isResponse = true; + int dataSize; + int addrSize; if (msg.isErrorReply) { @@ -361,9 +365,9 @@ QString UDS_HANDLER::getDetailedMessageAnalysis(const UDS_MESSAGE &msg) else { int p2 = msg.data[1] * 256 + msg.data[2]; - buildString.append("\nP2MAX: " + QString::number(p2) + "ms"); + buildString.append("\nP2MAX (Max Wait / Resp Time): " + QString::number(p2) + "ms"); p2 = (msg.data[3] * 256 + msg.data[4]) * 10; - buildString.append("\nP2*MAX: " + QString::number(p2) + "ms"); + buildString.append("\nP2 Ext MAX: " + QString::number(p2) + "ms"); } break; case UDS_SERVICES::ECU_RESET: @@ -384,8 +388,15 @@ QString UDS_HANDLER::getDetailedMessageAnalysis(const UDS_MESSAGE &msg) else buildString.append("\nNo powerdown time returned"); break; case UDS_SERVICES::COMM_CTRL: - //Comm control has two parameters, sub funct and the next byte should be 1. But it's always one so ignore for now - buildString.append("Comm type: " + getLongDesc(UDS_COMM_CTRL_SUB, msg.subFunc)); + //Comm control has potentially a lot of parameters. control type, comm type, nodeID + buildString.append("Control type: " + getLongDesc(UDS_COMM_CTRL_SUB, msg.subFunc)); + if (msg.data.length() > 1) + buildString.append("\nComm Type: " + QString::number(msg.data[1])); //TODO: no attempt to interpret yet + if (msg.data.length() > 3) + { + int nodeID = (msg.data[2] * 256 + msg.data[3]); + buildString.append("\nNode ID: " + Utility::formatHexNum(nodeID)); + } break; case UDS_SERVICES::SECURITY_ACCESS: if ((msg.subFunc % 2) == 1) @@ -423,6 +434,96 @@ QString UDS_HANDLER::getDetailedMessageAnalysis(const UDS_MESSAGE &msg) buildString.append("\nECU is now unlocked"); } break; + case UDS_SERVICES::READ_BY_ID: + //parameter is groups of two bytes, each of which specify an ID to read + if (msg.data.length() > 2) + { + uint32_t id; + for (int i = 1; i < msg.data.length(); i = i + 2) + { + id = (msg.data[i] * 256) + msg.data[i+1]; + buildString.append("\nID to read: " + Utility::formatHexNum(id)); + } + } + break; + case UDS_SERVICES::READ_BY_ID + 0x40: //reply + buildString.append("Reply is non-standard and so no decoding is done. The format is (ID) followed by how ever much data that ID returns, followed by more ID/data pairs if applicable.\nPayload: "); + for (int i = 1; i < msg.data.length(); i++) + { + buildString.append(Utility::formatHexNum(msg.data[i]) + " "); + } + break; + case UDS_SERVICES::READ_BY_ADDR: + //subfunc byte specifies address and length format, then address, then size + dataSize = msg.subFunc >> 4; + addrSize = msg.subFunc & 0xF; + if (msg.data.length() > (dataSize + addrSize)) + { + buildString.append("Address: 0x"); + for (int i = 0; i < addrSize; i++) buildString.append(QString::number(msg.data[1+i], 16).toUpper().rightJustified(2,'0')); + buildString.append("\nSize: 0x"); + for (int i = 0; i < dataSize; i++) buildString.append(QString::number(msg.data[1+i+addrSize], 16).toUpper().rightJustified(2,'0')); + } + else + { + buildString.append("Message has insufficient bytes to properly decode address and size!"); + } + break; + case UDS_SERVICES::READ_BY_ADDR + 0x40: + buildString.append("Reply is a raw packet of data of the size requested.\nPayload: "); + for (int i = 1; i < msg.data.length(); i++) + { + buildString.append(Utility::formatHexNum(msg.data[i]) + " "); + } + break; + case UDS_SERVICES::WRITE_BY_ID: + break; + case UDS_SERVICES::ROUTINE_CTRL: + buildString.append("Routine Control: " + getLongDesc(UDS_ROUTINE_SUB, msg.subFunc)); + if (msg.data.length() > 2) + { + int routineID; + routineID = (msg.data[1] * 256 + msg.data[2]); + buildString.append("\nRoutine ID: " + Utility::formatHexNum(routineID)); + } + if (msg.data.length() > 3) + { + buildString.append("\nParameter bytes to routine: "); + for (int i = 4; i < msg.data.length(); i++) + { + buildString.append(Utility::formatHexNum(msg.data[i]) + " "); + } + } + break; + case UDS_SERVICES::ROUTINE_CTRL + 0x40: + buildString.append("Routine Control: " + getLongDesc(UDS_ROUTINE_SUB, msg.subFunc)); + if (msg.data.length() > 2) + { + int routineID; + routineID = (msg.data[1] * 256 + msg.data[2]); + buildString.append("\nRoutine ID: " + Utility::formatHexNum(routineID)); + } + if (msg.data.length() > 3) + { + buildString.append("\nBytes returned by routine: "); + for (int i = 4; i < msg.data.length(); i++) + { + buildString.append(Utility::formatHexNum(msg.data[i]) + " "); + } + } + break; + case UDS_SERVICES::REQUEST_DOWNLOAD: + break; + case UDS_SERVICES::REQUEST_UPLOAD: + break; + case UDS_SERVICES::TRANSFER_DATA: + break; + case UDS_SERVICES::REQ_TRANS_EXIT: + break; + case UDS_SERVICES::REQ_FILE_TRANS: + break; + case UDS_SERVICES::WRITE_BY_ADDR: + break; } } diff --git a/can_structs.h b/can_structs.h index 42c5c2e..b19e901 100644 --- a/can_structs.h +++ b/can_structs.h @@ -15,6 +15,11 @@ public: uint32_t len; unsigned char data[8]; uint64_t timestamp; + + friend bool operator<(const CANFrame& l, const CANFrame& r) + { + return l.timestamp < r.timestamp; + } }; class CANFltObserver diff --git a/config.h b/config.h index 94ffff0..eba6add 100644 --- a/config.h +++ b/config.h @@ -1,7 +1,7 @@ #ifndef CONFIG #define CONFIG -#define VERSION 178 +#define VERSION 179 //try to keep this in sync. //SavvyCAN will complain if you connect a GVRET board with a revision diff --git a/connections/canbus.cpp b/connections/canbus.cpp index d0f4646..13aeb11 100644 --- a/connections/canbus.cpp +++ b/connections/canbus.cpp @@ -1,4 +1,5 @@ #include +#include #include "canbus.h" CANBus::CANBus() @@ -25,18 +26,22 @@ bool CANBus::operator==(const CANBus& bus) const{ } void CANBus::setSpeed(int newSpeed){ + //qDebug() << "CANBUS SetSpeed = " << newSpeed; speed = newSpeed; } void CANBus::setListenOnly(bool mode){ + //qDebug() << "CANBUS SetListenOnly = " << mode; listenOnly = mode; } void CANBus::setSingleWire(bool mode){ + //qDebug() << "CANBUS SetSingleWire = " << mode; singleWire = mode; } void CANBus::setEnabled(bool mode){ + //qDebug() << "CANBUS SetEnabled = " << mode; active = mode; } diff --git a/connections/canbus.h b/connections/canbus.h index 9b39358..73f945f 100644 --- a/connections/canbus.h +++ b/connections/canbus.h @@ -1,6 +1,7 @@ #ifndef CANBus_H #define CANBus_H #include +#include "can_structs.h" class CANBus { @@ -31,4 +32,10 @@ QDataStream & operator>>(QDataStream & pStream, CANBus& pCanBus); Q_DECLARE_METATYPE(CANBus); +struct BusData { + CANBus mBus; + bool mConfigured; + QVector mTargettedFrames; +}; + #endif // CANBus_H diff --git a/connections/canconconst.h b/connections/canconconst.h index 707355d..386407a 100644 --- a/connections/canconconst.h +++ b/connections/canconconst.h @@ -21,4 +21,11 @@ namespace CANCon { }; } +class CANConStatus +{ +public: + CANCon::status conStatus; + int numHardwareBuses; +}; + #endif // CANCONCONST_H diff --git a/connections/canconmanager.cpp b/connections/canconmanager.cpp index e9c3736..b3428a3 100644 --- a/connections/canconmanager.cpp +++ b/connections/canconmanager.cpp @@ -36,6 +36,7 @@ CANConManager::CANConManager(QObject *parent): QObject(parent) void CANConManager::resetTimeBasis() { mTimestampBasis = QDateTime::currentMSecsSinceEpoch() * 1000; + mElapsedTimer.restart(); } CANConManager::~CANConManager() @@ -182,8 +183,15 @@ bool CANConManager::sendFrame(const CANFrame& pFrame) { workingFrame.bus -= busBase; workingFrame.isReceived = false; - workingFrame.timestamp = (QDateTime::currentMSecsSinceEpoch() * 1000); - if (!useSystemTime) workingFrame.timestamp -= mTimestampBasis; + if (useSystemTime) + { + workingFrame.timestamp = (QDateTime::currentMSecsSinceEpoch() * 1000); + } + else + { + workingFrame.timestamp = mElapsedTimer.nsecsElapsed() / 1000; + //workingFrame.timestamp -= mTimestampBasis; + } txFrame = conn->getQueue().get(); *txFrame = workingFrame; conn->getQueue().queue(); diff --git a/connections/canconmanager.h b/connections/canconmanager.h index 7d82bb2..26c6c76 100644 --- a/connections/canconmanager.h +++ b/connections/canconmanager.h @@ -3,6 +3,7 @@ #include #include +#include #include "canconnection.h" @@ -73,6 +74,7 @@ private: static CANConManager* mInstance; QList mConns; QTimer mTimer; + QElapsedTimer mElapsedTimer; uint64_t mTimestampBasis; uint32_t mNumActiveBuses; bool useSystemTime; diff --git a/connections/canconnection.cpp b/connections/canconnection.cpp index 0294795..8617ac9 100644 --- a/connections/canconnection.cpp +++ b/connections/canconnection.cpp @@ -2,14 +2,6 @@ #include #include "canconnection.h" - -struct BusData { - CANBus mBus; - bool mConfigured; - QVector mTargettedFrames; -}; - - CANConnection::CANConnection(QString pPort, CANCon::type pType, int pNumBuses, @@ -27,7 +19,7 @@ CANConnection::CANConnection(QString pPort, /* register types */ qRegisterMetaType("CANBus"); qRegisterMetaType("CANFrame"); - qRegisterMetaType("CANCon::status"); + qRegisterMetaType("CANConStatus"); qRegisterMetaType("CANFlt"); /* set queue size */ @@ -35,9 +27,9 @@ CANConnection::CANConnection(QString pPort, /* allocate buses */ /* TODO: change those tables for a vector */ - mBusData_p = new BusData[mNumBuses]; + mBusData.resize(mNumBuses); for(int i=0 ; i= getNumBuses()) return false; - return mBusData_p[pBusId].mConfigured; + return mBusData[pBusId].mConfigured; } void CANConnection::setConfigured(int pBusId, bool pConfigured) { if( pBusId < 0 || pBusId >= getNumBuses()) return; - mBusData_p[pBusId].mConfigured = pConfigured; + mBusData[pBusId].mConfigured = pConfigured; } @@ -220,7 +209,7 @@ bool CANConnection::getBusConfig(int pBusId, CANBus& pBus) { if( pBusId < 0 || pBusId >= getNumBuses() || !isConfigured(pBusId)) return false; - pBus = mBusData_p[pBusId].mBus; + pBus = mBusData[pBusId].mBus; return true; } @@ -229,8 +218,8 @@ void CANConnection::setBusConfig(int pBusId, CANBus& pBus) { if( pBusId < 0 || pBusId >= getNumBuses()) return; - mBusData_p[pBusId].mConfigured = true; - mBusData_p[pBusId].mBus = pBus; + mBusData[pBusId].mConfigured = true; + mBusData[pBusId].mBus = pBus; } @@ -293,7 +282,7 @@ bool CANConnection::addTargettedFrame(int pBusId, uint32_t ID, uint32_t mask, QO target.id = ID; target.mask = mask; target.observer = receiver; - mBusData_p[pBusId].mTargettedFrames.append(target); + mBusData[pBusId].mTargettedFrames.append(target); return true; } @@ -321,7 +310,7 @@ bool CANConnection::removeTargettedFrame(int pBusId, uint32_t ID, uint32_t mask, target.id = ID; target.mask = mask; target.observer = receiver; - mBusData_p[pBusId].mTargettedFrames.removeAll(target); + mBusData[pBusId].mTargettedFrames.removeAll(target); return true; } @@ -329,9 +318,9 @@ bool CANConnection::removeTargettedFrame(int pBusId, uint32_t ID, uint32_t mask, bool CANConnection::removeAllTargettedFrames(QObject *receiver) { for (int i = 0; i < getNumBuses(); i++) { - foreach (const CANFltObserver filt, mBusData_p[i].mTargettedFrames) + foreach (const CANFltObserver filt, mBusData[i].mTargettedFrames) { - if (filt.observer == receiver) mBusData_p[i].mTargettedFrames.removeOne(filt); + if (filt.observer == receiver) mBusData[i].mTargettedFrames.removeOne(filt); } } @@ -342,10 +331,10 @@ void CANConnection::checkTargettedFrame(CANFrame &frame) { unsigned int maskedID; //qDebug() << "Got frame with ID " << frame.ID << " on bus " << frame.bus; - if (mBusData_p == 0) return; + if (mBusData.count() == 0) return; - if (mBusData_p[frame.bus].mTargettedFrames.length() == 0) return; - foreach (const CANFltObserver filt, mBusData_p[frame.bus].mTargettedFrames) + if (mBusData[frame.bus].mTargettedFrames.length() == 0) return; + foreach (const CANFltObserver filt, mBusData[frame.bus].mTargettedFrames) { //qDebug() << "Checking filter with id " << filt.id << " mask " << filt.mask; maskedID = frame.ID & filt.mask; diff --git a/connections/canconnection.h b/connections/canconnection.h index c659cab..0966ea2 100644 --- a/connections/canconnection.h +++ b/connections/canconnection.h @@ -89,7 +89,7 @@ signals: * @brief event emitted when the CANCon::status of the connection changes (connected->not_connected or the other way round) * @param pStatus: the new status of the device */ - void status(CANCon::status pStatus); + void status(CANConStatus pStatus); /** * @brief Event sent when device has done something worthy of debugging output. @@ -183,7 +183,8 @@ public slots: void debugInput(QByteArray bytes); protected: - int mNumBuses; //protected to allow connected device to figure out how many buses are available + int mNumBuses; //protected to allow connected device to figure out how many buses are available + QVector mBusData; //determine if the passed frame is part of a filter or not. void checkTargettedFrame(CANFrame &frame); @@ -297,7 +298,6 @@ private: bool mIsCapSuspended; QAtomicInt mStatus; bool mStarted; - BusData* mBusData_p; QThread* mThread_p; }; diff --git a/connections/canconnectionmodel.cpp b/connections/canconnectionmodel.cpp index 726c334..a63c508 100644 --- a/connections/canconnectionmodel.cpp +++ b/connections/canconnectionmodel.cpp @@ -40,10 +40,10 @@ QVariant CANConnectionModel::headerData(int section, Qt::Orientation orientation return QString(tr("Single Wire")); break; case 6: - return QString(tr("Status")); + return QString(tr("Active")); break; case 7: - return QString(tr("Active")); + return QString(tr("Status")); break; } } @@ -54,11 +54,10 @@ QVariant CANConnectionModel::headerData(int section, Qt::Orientation orientation return QVariant(); } - int CANConnectionModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); - return 7; + return 8; } @@ -66,32 +65,99 @@ int CANConnectionModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); - int rows=0; + int rows = 0; QList& conns = CANConManager::getInstance()->getConnections(); foreach(const CANConnection* conn_p, conns) - rows+=conn_p->getNumBuses(); + rows += conn_p->getNumBuses(); + + //qDebug() << "Num Rows: " << rows; return rows; } +Qt::ItemFlags CANConnectionModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemFlag::NoItemFlags; + + int busId; + CANConnection *conn_p = getAtIdx(index.row(), busId); + if (!conn_p) return Qt::ItemFlag::NoItemFlags; + + //you can't set speed, single wire, or listen only on socketcan devices so + //detect if we're using GVRET where you can and turn that functionality on + bool editParams = false; + if (conn_p->getType() == CANCon::GVRET_SERIAL) editParams = true; + + switch (index.column()) + { + case 3: //speed + if (editParams) return Qt::ItemFlag::ItemIsEditable | Qt::ItemFlag::ItemIsEnabled; + else return Qt::ItemFlag::NoItemFlags; + break; + case 4: //listen only + case 5: //single wire + if (editParams) return Qt::ItemFlag::ItemIsEditable | Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsUserCheckable; + else return Qt::ItemFlag::NoItemFlags; + break; + case 6: //enabled + return Qt::ItemFlag::ItemIsEditable | Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsUserCheckable; + break; + default: + return Qt::ItemFlag::ItemIsEnabled; + break; + } +} + +bool CANConnectionModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + qDebug() << "setData: " << index.row() << ":" << index.column() << " role: " << role << " Val: " << value; + + int busId; + CANConnection *conn_p = getAtIdx(index.row(), busId); + if (!conn_p) return false; + CANBus bus; + bool ret; + ret = conn_p->getBusSettings(busId, bus); + if (!ret) return false; + + switch (index.column()) + { + case 3: //speed + bus.speed = value.toInt(); + break; + case 4: //listen only + bus.listenOnly = value.toBool(); + break; + case 5: //single wire + bus.singleWire = value.toBool(); + break; + case 6: //active + bus.active = value.toBool(); + break; + } + conn_p->setBusSettings(busId, bus); + return true; +} QVariant CANConnectionModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); + //qDebug() << "Row: " << index.row(); + int busId; + CANConnection *conn_p = getAtIdx(index.row(), busId); + CANBus bus; + bool ret; + if (!conn_p) return QVariant(); + ret = conn_p->getBusSettings(busId, bus); + bool isSocketCAN = (conn_p->getType() == CANCon::SOCKETCAN) ? true: false; - if (role == Qt::DisplayRole) { - int busId; - CANConnection *conn_p = getAtIdx(index.row(), busId); - if(!conn_p) - return QVariant(); + //qDebug() << "ConnP: " << conn_p << " ret " << ret; - CANBus bus; - bool ret; - ret = conn_p->getBusSettings(busId, bus); - if(!ret) return QVariant(); + if (role == Qt::DisplayRole) { switch (index.column()) { @@ -114,18 +180,35 @@ QVariant CANConnectionModel::data(const QModelIndex &index, int role) const else qDebug() << "Tried to show connection port but connection was NULL"; break; case 3: //speed - return QString::number(bus.speed); + if(!ret) return QVariant(); + if (!isSocketCAN) return QString::number(bus.speed); + else return QString("N/A"); case 4: //Listen Only - return (bus.listenOnly) ? "True" : "False"; + return QVariant(); case 5: //Single Wire - return (bus.singleWire) ? "True" : "False"; + return QVariant(); case 6: //Status - return (conn_p->getStatus()==CANCon::CONNECTED) ? "Connected" : "Not Connected"; + return QVariant(); case 7: //Active - return (bus.active) ? "True" : "False"; + return (conn_p->getStatus()==CANCon::CONNECTED) ? "Connected" : "Not Connected"; default: {} } } + if (role == Qt::CheckStateRole) + { + switch (index.column()) + { + case 4: + return (bus.listenOnly) ? Qt::Checked : Qt::Unchecked; + break; + case 5: + return (bus.singleWire) ? Qt::Checked : Qt::Unchecked; + break; + case 6: + return (bus.active) ? Qt::Checked : Qt::Unchecked; + break; + } + } return QVariant(); } @@ -172,9 +255,11 @@ CANConnection* CANConnectionModel::getAtIdx(int pIdx, int& pBusId) const return NULL; } - void CANConnectionModel::refresh(int pIndex) { + beginResetModel(); + endResetModel(); + /* QModelIndex begin; QModelIndex end; @@ -186,5 +271,5 @@ void CANConnectionModel::refresh(int pIndex) begin = createIndex(0, 0); end = createIndex(rowCount()-1, columnCount()-1); } - dataChanged(begin, end, QVector(Qt::DisplayRole)); + dataChanged(begin, end, QVector(Qt::DisplayRole)); */ } diff --git a/connections/canconnectionmodel.h b/connections/canconnectionmodel.h index 68a8d45..190dd27 100644 --- a/connections/canconnectionmodel.h +++ b/connections/canconnectionmodel.h @@ -21,6 +21,10 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + Qt::ItemFlags flags(const QModelIndex &index) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; void add(CANConnection* pConn_p); diff --git a/connections/connectionwindow.cpp b/connections/connectionwindow.cpp index ccaeaab..99d084b 100644 --- a/connections/connectionwindow.cpp +++ b/connections/connectionwindow.cpp @@ -24,25 +24,17 @@ ConnectionWindow::ConnectionWindow(QWidget *parent) : connModel = new CANConnectionModel(this); ui->tableConnections->setModel(connModel); - ui->tableConnections->setColumnWidth(0, 50); - ui->tableConnections->setColumnWidth(1, 110); - ui->tableConnections->setColumnWidth(2, 110); - ui->tableConnections->setColumnWidth(3, 110); - ui->tableConnections->setColumnWidth(4, 75); - ui->tableConnections->setColumnWidth(5, 75); - ui->tableConnections->setColumnWidth(6, 75); - ui->tableConnections->setColumnWidth(7, 75); + ui->tableConnections->setColumnWidth(0, 40); + ui->tableConnections->setColumnWidth(1, 70); + ui->tableConnections->setColumnWidth(2, 70); + ui->tableConnections->setColumnWidth(3, 70); + ui->tableConnections->setColumnWidth(4, 70); + ui->tableConnections->setColumnWidth(5, 70); + ui->tableConnections->setColumnWidth(6, 70); + ui->tableConnections->setColumnWidth(7, 90); QHeaderView *HorzHdr = ui->tableConnections->horizontalHeader(); HorzHdr->setStretchLastSection(true); //causes the data column to automatically fill the tableview - ui->ckSingleWire->setChecked(settings.value("Main/SingleWireMode", false).toBool()); - - ui->cbSpeed->addItem(tr("125000")); - ui->cbSpeed->addItem(tr("250000")); - ui->cbSpeed->addItem(tr("500000")); - ui->cbSpeed->addItem(tr("1000000")); - ui->cbSpeed->addItem(tr("33333")); - ui->textConsole->setEnabled(false); ui->btnClearDebug->setEnabled(false); ui->btnSendHex->setEnabled(false); @@ -61,9 +53,7 @@ ConnectionWindow::ConnectionWindow(QWidget *parent) : connect(ui->rbGVRET, &QAbstractButton::clicked, this, &ConnectionWindow::handleConnTypeChanged); connect(ui->rbKvaser, &QAbstractButton::clicked, this, &ConnectionWindow::handleConnTypeChanged); connect(ui->rbSocketCAN, &QAbstractButton::clicked, this, &ConnectionWindow::handleConnTypeChanged); - connect(ui->btnRevert, &QPushButton::clicked, this, &ConnectionWindow::handleRevert); connect(ui->tableConnections->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &ConnectionWindow::currentRowChanged); - connect(ui->btnNewConn, &QPushButton::clicked, this, &ConnectionWindow::handleNewConn); connect(ui->btnActivateAll, &QPushButton::clicked, this, &ConnectionWindow::handleEnableAll); connect(ui->btnDeactivateAll, &QPushButton::clicked, this, &ConnectionWindow::handleDisableAll); connect(ui->btnRemoveBus, &QPushButton::clicked, this, &ConnectionWindow::handleRemoveConn); @@ -100,6 +90,7 @@ void ConnectionWindow::showEvent(QShowEvent* event) readSettings(); ui->tableConnections->selectRow(0); currentRowChanged(ui->tableConnections->currentIndex(), ui->tableConnections->currentIndex()); + handleConnTypeChanged(); } void ConnectionWindow::closeEvent(QCloseEvent *event) @@ -207,7 +198,7 @@ void ConnectionWindow::handleConnTypeChanged() /* status */ -void ConnectionWindow::connectionStatus(CANCon::status pStatus) +void ConnectionWindow::connectionStatus(CANConStatus pStatus) { Q_UNUSED(pStatus); @@ -218,53 +209,14 @@ void ConnectionWindow::connectionStatus(CANCon::status pStatus) void ConnectionWindow::handleOKButton() { - int whichRow = ui->tableConnections->selectionModel()->currentIndex().row(); - CANConnection* conn_p = NULL; - if (whichRow > -1) - { - /* set information for selected connection */ - int busId; - CANBus bus; - bool ret; - - conn_p = connModel->getAtIdx(whichRow, busId); - if(!conn_p) return; - - ret = conn_p->getBusSettings(busId, bus); - if(!ret) return; - - - bus.setListenOnly(ui->ckListenOnly->isChecked()); - bus.setSingleWire(ui->ckSingleWire->isChecked()); - bus.setEnabled(ui->ckEnabled->isChecked()); - bus.setSpeed(ui->cbSpeed->currentText().toInt()); - /* update bus settings */ - conn_p->setBusSettings(busId, bus); - - connModel->refresh(whichRow); - } - else if( ! CANConManager::getInstance()->getByName(getPortName()) ) + if( ! CANConManager::getInstance()->getByName(getPortName()) ) { /* create connection */ conn_p = create(getConnectionType(), getPortName()); if(!conn_p) return; - - for (int i=0 ; igetNumBuses() ; i++) { - /* set bus configuration */ - CANBus bus; - bus.active = ui->ckEnabled->isChecked(); - bus.listenOnly = ui->ckListenOnly->isChecked(); - bus.singleWire = ui->ckSingleWire->isChecked(); - - bus.speed = ui->cbSpeed->currentText().toInt(); - - /* update bus settings */ - conn_p->setBusSettings(i, bus); - } - /* add connection to model */ connModel->add(conn_p); } @@ -280,6 +232,7 @@ void ConnectionWindow::currentRowChanged(const QModelIndex ¤t, const QMode disconnect(connModel->getAtIdx(previous.row(), busId), SIGNAL(debugOutput(QString)), 0, 0); disconnect(this, SIGNAL(sendDebugData(QByteArray)), connModel->getAtIdx(previous.row(), busId), SLOT(debugInput(QByteArray))); +return; /* enable / diable connection type */ ui->stPort->setEnabled(selIdx==-1); @@ -291,9 +244,6 @@ void ConnectionWindow::currentRowChanged(const QModelIndex ¤t, const QMode { ui->btnOK->setText(tr("Create New Connection")); ui->rbGVRET->setChecked(true); - ui->ckListenOnly->setChecked(false); - ui->ckSingleWire->setChecked(false); - ui->ckEnabled->setChecked(false); setSpeed(0); setPortName(CANCon::GVRET_SERIAL, ""); } @@ -313,9 +263,6 @@ void ConnectionWindow::currentRowChanged(const QModelIndex ¤t, const QMode if(!ret) return; ui->btnOK->setText(tr("Update Connection Settings")); - ui->ckListenOnly->setChecked(bus.isListenOnly()); - ui->ckSingleWire->setChecked(bus.isSingleWire()); - ui->ckEnabled->setChecked(bus.isActive()); setSpeed(bus.getSpeed()); setPortName(conn_p->getType(), conn_p->getPort()); } @@ -349,7 +296,6 @@ void ConnectionWindow::selectSerial() { /* set combobox page visible */ ui->stPort->setCurrentWidget(ui->cbPage); - ui->cbSpeed->setEnabled(true); ui->cbPort->clear(); ports = QSerialPortInfo::availablePorts(); @@ -362,7 +308,6 @@ void ConnectionWindow::selectKvaser() { /* set combobox page visible */ ui->stPort->setCurrentWidget(ui->cbPage); - ui->cbSpeed->setEnabled(false); } void ConnectionWindow::selectSocketCan() @@ -373,18 +318,6 @@ void ConnectionWindow::selectSocketCan() void ConnectionWindow::setSpeed(int speed0) { - for (int i = 0; i < ui->cbSpeed->count(); i++) - { - if (ui->cbSpeed->itemText(i).toInt() == speed0) - { - ui->cbSpeed->setCurrentIndex(i); - return; - } - } - - /* add custom speed */ - ui->cbSpeed->addItem(QString::number(speed0)); - ui->cbSpeed->setCurrentIndex(ui->cbSpeed->count() - 1); } void ConnectionWindow::setPortName(CANCon::type pType, QString pPortName) @@ -393,9 +326,6 @@ void ConnectionWindow::setPortName(CANCon::type pType, QString pPortName) { case CANCon::GVRET_SERIAL: ui->rbGVRET->setChecked(true); - ui->cbSpeed->setEnabled(true); - ui->ckListenOnly->setEnabled(true); - ui->ckSingleWire->setEnabled(true); break; case CANCon::KVASER: ui->rbKvaser->setChecked(true); @@ -403,9 +333,6 @@ void ConnectionWindow::setPortName(CANCon::type pType, QString pPortName) case CANCon::SOCKETCAN: ui->rbSocketCAN->setChecked(true); //you can't configure any of the below three with socketcan so dim them out - ui->cbSpeed->setEnabled(false); - ui->ckListenOnly->setEnabled(false); - ui->ckSingleWire->setEnabled(false); break; default: {} } @@ -435,16 +362,6 @@ void ConnectionWindow::setPortName(CANCon::type pType, QString pPortName) //-1 means leave it at whatever it booted up to. 0 means disable. Otherwise the actual rate we want. int ConnectionWindow::getSpeed() { - switch (ui->cbSpeed->currentIndex()) - { - case -1: - case 0: - return -1; - case 1: - return 0; - default: - return (ui->cbSpeed->currentText().toInt()); - } } QString ConnectionWindow::getPortName() @@ -475,12 +392,10 @@ CANCon::type ConnectionWindow::getConnectionType() void ConnectionWindow::setSWMode(bool mode) { - ui->ckSingleWire->setChecked(mode); } bool ConnectionWindow::getSWMode() { - if (ui->ckSingleWire->checkState() == Qt::Checked) return true; return false; } @@ -530,8 +445,8 @@ CANConnection* ConnectionWindow::create(CANCon::type pTye, QString pPortName) if(conn_p) { /* connect signal */ - connect(conn_p, SIGNAL(status(CANCon::status)), - this, SLOT(connectionStatus(CANCon::status))); + connect(conn_p, SIGNAL(status(CANConStatus)), + this, SLOT(connectionStatus(CANConStatus))); /*TODO add return value and checks */ conn_p->start(); @@ -552,17 +467,10 @@ void ConnectionWindow::loadConnections() /* fill connection list */ QVector portNames = settings.value("connections/portNames").value>(); QVector devTypes = settings.value("connections/types").value>(); - QList busses = settings.value("connections/busses").value>(); - for(int i=0 ; igetNumBuses() ; j++) - conn_p->setBusSettings(j, busses.takeFirst()); - } /* add connection to model */ connModel->add(conn_p); } @@ -579,23 +487,14 @@ void ConnectionWindow::saveConnections() QSettings settings; QVector portNames; QVector devTypes; - QList busses; /* save connections */ foreach(CANConnection* conn_p, conns) { portNames.append(conn_p->getPort()); devTypes.append(conn_p->getType()); - - for(int i=0 ; igetNumBuses() ; i++) - { - CANBus bus; - conn_p->getBusSettings(i, bus); - busses.append(bus); - } } settings.setValue("connections/portNames", QVariant::fromValue(portNames)); settings.setValue("connections/types", QVariant::fromValue(devTypes)); - settings.setValue("connections/busses", QVariant::fromValue(busses)); } diff --git a/connections/connectionwindow.h b/connections/connectionwindow.h index 58e99b2..77a0d88 100644 --- a/connections/connectionwindow.h +++ b/connections/connectionwindow.h @@ -57,7 +57,7 @@ private slots: void handleClearDebugText(); void handleSendHex(); void handleSendText(); - void connectionStatus(CANCon::status); + void connectionStatus(CANConStatus); private: Ui::ConnectionWindow *ui; diff --git a/connections/gvretserial.cpp b/connections/gvretserial.cpp index b8f24c6..0e38871 100644 --- a/connections/gvretserial.cpp +++ b/connections/gvretserial.cpp @@ -125,25 +125,71 @@ void GVRetSerial::piSetBusSettings(int pBusIdx, CANBus bus) } else deviceSingleWireMode = 0; } + else if (pBusIdx == 2) + { + swcanBaud = bus.getSpeed(); + swcanBaud |= 0x80000000; + if (bus.isActive()) + { + swcanBaud |= 0x40000000; + swcanEnabled = true; + } + else swcanEnabled = false; - /* update baud rates */ - QByteArray buffer; - qDebug() << "Got signal to update bauds. 1: " << can0Baud <<" 2: " << can1Baud; - debugOutput("Got signal to update bauds. 1: " + QString::number(can0Baud) + " 2: " + QString::number(can1Baud)); - buffer[0] = (char)0xF1; //start of a command over serial - buffer[1] = 5; //setup canbus - buffer[2] = (unsigned char)(can0Baud & 0xFF); //four bytes of ID LSB first - buffer[3] = (unsigned char)(can0Baud >> 8); - buffer[4] = (unsigned char)(can0Baud >> 16); - buffer[5] = (unsigned char)(can0Baud >> 24); - buffer[6] = (unsigned char)(can1Baud & 0xFF); //four bytes of ID LSB first - buffer[7] = (unsigned char)(can1Baud >> 8); - buffer[8] = (unsigned char)(can1Baud >> 16); - buffer[9] = (unsigned char)(can1Baud >> 24); - buffer[10] = 0; - if (serial == NULL) return; - if (!serial->isOpen()) return; - serial->write(buffer); + if (bus.isListenOnly()) + { + swcanBaud |= 0x20000000; + swcanListenOnly = true; + } + else swcanListenOnly = false; + + } + + if (pBusIdx < 2) { + /* update baud rates */ + QByteArray buffer; + qDebug() << "Got signal to update bauds. 1: " << (can0Baud & 0xFFFFFFF) <<" 2: " << (can1Baud & 0xFFFFFFF); + debugOutput("Got signal to update bauds. 1: " + QString::number((can0Baud & 0xFFFFFFF)) + " 2: " + QString::number((can1Baud & 0xFFFFFFF))); + buffer[0] = (char)0xF1; //start of a command over serial + buffer[1] = 5; //setup canbus + buffer[2] = (unsigned char)(can0Baud & 0xFF); //four bytes of ID LSB first + buffer[3] = (unsigned char)(can0Baud >> 8); + buffer[4] = (unsigned char)(can0Baud >> 16); + buffer[5] = (unsigned char)(can0Baud >> 24); + buffer[6] = (unsigned char)(can1Baud & 0xFF); //four bytes of ID LSB first + buffer[7] = (unsigned char)(can1Baud >> 8); + buffer[8] = (unsigned char)(can1Baud >> 16); + buffer[9] = (unsigned char)(can1Baud >> 24); + buffer[10] = 0; + if (serial == NULL) return; + if (!serial->isOpen()) return; + serial->write(buffer); + } + else + { + /* update baud rates */ + QByteArray buffer; + qDebug() << "Got signal to update extended bus speeds SWCAN: " << swcanBaud <<" LIN1: " << lin1Baud << " LIN2: " << lin2Baud; + debugOutput("Got signal to update extended bus speeds SWCAN: " + QString::number(swcanBaud) + " LIN1: " + QString::number(lin1Baud) + " LIN2: " + QString::number(lin2Baud)); + buffer[0] = (char)0xF1; //start of a command over serial + buffer[1] = 14; //setup extended buses + buffer[2] = (unsigned char)(swcanBaud & 0xFF); //four bytes of ID LSB first + buffer[3] = (unsigned char)(swcanBaud >> 8); + buffer[4] = (unsigned char)(swcanBaud >> 16); + buffer[5] = (unsigned char)(swcanBaud >> 24); + buffer[6] = (unsigned char)(lin1Baud & 0xFF); //four bytes of ID LSB first + buffer[7] = (unsigned char)(lin1Baud >> 8); + buffer[8] = (unsigned char)(lin1Baud >> 16); + buffer[9] = (unsigned char)(lin1Baud >> 24); + buffer[10] = (unsigned char)(lin2Baud & 0xFF); //four bytes of ID LSB first + buffer[11] = (unsigned char)(lin2Baud >> 8); + buffer[12] = (unsigned char)(lin2Baud >> 16); + buffer[13] = (unsigned char)(lin2Baud >> 24); + buffer[14] = 0; + if (serial == NULL) return; + if (!serial->isOpen()) return; + serial->write(buffer); + } } @@ -232,6 +278,10 @@ void GVRetSerial::connectDevice() output.append((char)0xE7); //this puts the device into binary comm mode output.append((char)0xE7); + output.append((char)0xF1); + output.append((char)0x0C); //get number of actually implemented buses. Not implemented except on M2RET + mNumBuses = 2; //the proper number if C/12 is not implemented + output.append((char)0xF1); //signal we want to issue a command output.append((char)0x06); //request canbus stats from the board @@ -249,15 +299,11 @@ void GVRetSerial::connectDevice() output.append((char)0xFF); //signal we don't want single wire mode }*/ - output.append((char)0xF1); //yet another command - output.append((char)0x09); //comm validation command - output.append((char)0xF1); //and another command output.append((char)0x01); //Time Sync - Not implemented until 333 but we can try - output.append((char)0xF1); - output.append((char)0x12); //get number of actually implemented buses. Not implemented except on M2RET - mNumBuses = 2; //the proper number if 0x12 is not implemented + output.append((char)0xF1); //yet another command + output.append((char)0x09); //comm validation command continuousTimeSync = true; @@ -274,7 +320,10 @@ void GVRetSerial::connectDevice() } else { setStatus(CANCon::CONNECTED); - emit status(getStatus()); + CANConStatus stats; + stats.conStatus = getStatus(); + stats.numHardwareBuses = mNumBuses; + emit status(stats); } /* connect reading event */ @@ -336,6 +385,10 @@ void GVRetSerial::debugInput(QByteArray bytes) { void GVRetSerial::procRXChar(unsigned char c) { + CANConStatus stats; + int oldBuses; + QByteArray output; + switch (rx_state) { case IDLE: @@ -380,6 +433,12 @@ void GVRetSerial::procRXChar(unsigned char c) break; case 12: rx_state = GET_NUM_BUSES; + qDebug() << "Got num buses reply"; + rx_step = 0; + break; + case 13: + rx_state = GET_EXT_BUSES; + qDebug() << "Got extended buses info reply"; rx_step = 0; break; } @@ -545,6 +604,12 @@ void GVRetSerial::procRXChar(unsigned char c) rx_state = IDLE; qDebug() << "Baud 0 = " << can0Baud; qDebug() << "Baud 1 = " << can1Baud; + mBusData[0].mBus.setSpeed(can0Baud); + mBusData[1].mBus.setSpeed(can1Baud); + mBusData[0].mBus.setEnabled(can0Enabled); + mBusData[1].mBus.setEnabled(can1Enabled); + mBusData[0].mConfigured = true; + mBusData[1].mConfigured = true; can0Baud |= 0x80000000; if (can0Enabled) can0Baud |= 0x40000000; @@ -556,7 +621,9 @@ void GVRetSerial::procRXChar(unsigned char c) if (deviceSingleWireMode > 0) can1Baud |= 0x10000000; setStatus(CANCon::CONNECTED); - emit status(getStatus()); + stats.conStatus = getStatus(); + stats.numHardwareBuses = mNumBuses; + emit status(stats); int can0Status = 0x78; //updating everything we can update int can1Status = 0x78; @@ -606,8 +673,89 @@ void GVRetSerial::procRXChar(unsigned char c) rx_state = IDLE; break; case GET_NUM_BUSES: + oldBuses = mNumBuses; mNumBuses = c; rx_state = IDLE; + qDebug() << "Get number of buses = " << mNumBuses; + stats.conStatus = getStatus(); + stats.numHardwareBuses = mNumBuses; + mBusData.resize(mNumBuses); + if (mNumBuses > oldBuses) + { + for (int i = oldBuses; i < mNumBuses; i++) + { + mBusData[i].mConfigured = true; + mBusData[i].mBus = mBusData[0].mBus; + } + } + + output.append((char)0xF1); //start a new command + output.append((char)13); //get extended buses + serial->write(output); + + emit status(stats); + break; + case GET_EXT_BUSES: + switch (rx_step) + { + case 0: + swcanEnabled = (c & 0xF); + swcanListenOnly = (c >> 4); + break; + case 1: + swcanBaud = c; + break; + case 2: + swcanBaud |= c << 8; + break; + case 3: + swcanBaud |= c << 16; + break; + case 4: + swcanBaud |= c << 24; + break; + case 5: + lin1Enabled = (c & 0xF); + break; + case 6: + lin1Baud = c; + break; + case 7: + lin1Baud |= c << 8; + break; + case 8: + lin1Baud |= c << 16; + break; + case 9: + lin1Baud |= c << 24; + case 10: + lin2Enabled = (c & 0xF); + break; + case 11: + lin2Baud = c; + break; + case 12: + lin2Baud |= c << 8; + break; + case 13: + lin2Baud |= c << 16; + break; + case 14: + lin2Baud |= c << 24; + rx_state = IDLE; + qDebug() << "SWCAN Baud = " << swcanBaud; + qDebug() << "LIN1 Baud = " << lin1Baud; + qDebug() << "LIN2 Baud = " << lin2Baud; + mBusData[2].mBus.setSpeed(swcanBaud); + mBusData[2].mBus.setEnabled(swcanEnabled); + + setStatus(CANCon::CONNECTED); + stats.conStatus = getStatus(); + stats.numHardwareBuses = mNumBuses; + emit status(stats); + break; + } + rx_step++; break; } } diff --git a/connections/gvretserial.h b/connections/gvretserial.h index 6b8e5ac..a4a3963 100644 --- a/connections/gvretserial.h +++ b/connections/gvretserial.h @@ -16,7 +16,7 @@ namespace SERIALSTATE { -enum STATE //keep this enum synchronized with the Arduino firmware project +enum STATE { IDLE, GET_COMMAND, @@ -29,12 +29,12 @@ enum STATE //keep this enum synchronized with the Arduino firmware project GET_CANBUS_PARAMS, GET_DEVICE_INFO, SET_SINGLEWIRE_MODE, - GET_NUM_BUSES + GET_NUM_BUSES, + GET_EXT_BUSES }; } - using namespace SERIALSTATE; class GVRetSerial : public CANConnection { @@ -83,9 +83,9 @@ protected: STATE rx_state; uint32_t rx_step; CANFrame buildFrame; - int can0Baud, can1Baud; - bool can0Enabled, can1Enabled; - bool can0ListenOnly, can1ListenOnly; + int can0Baud, can1Baud, swcanBaud, lin1Baud, lin2Baud; + bool can0Enabled, can1Enabled, swcanEnabled, lin1Enabled, lin2Enabled; + bool can0ListenOnly, can1ListenOnly, swcanListenOnly; int deviceBuildNum; int deviceSingleWireMode; uint32_t buildTimeBasis; diff --git a/connections/socketcan.cpp b/connections/socketcan.cpp index 7eefe2f..d370784 100644 --- a/connections/socketcan.cpp +++ b/connections/socketcan.cpp @@ -31,6 +31,8 @@ void SocketCan::piStarted() mTimer.setInterval(1000); mTimer.setSingleShot(false); //keep ticking mTimer.start(); + mBusData[0].mBus.setEnabled(true); + mBusData[0].mConfigured = true; } @@ -59,6 +61,7 @@ bool SocketCan::piGetBusSettings(int pBusIdx, CANBus& pBus) void SocketCan::piSetBusSettings(int pBusIdx, CANBus bus) { + CANConStatus stats; /* sanity checks */ if(0 != pBusIdx) return; @@ -109,7 +112,6 @@ bool SocketCan::piSendFrame(const CANFrame& pFrame) /* sanity checks */ if(0 != pFrame.bus || pFrame.len>8) return false; - if (!mDev_p) return false; /* fill frame */ @@ -211,6 +213,7 @@ void SocketCan::framesReceived() void SocketCan::testConnection() { QCanBusDevice* dev_p = QCanBus::instance()->createDevice("socketcan", getPort()); + CANConStatus stats; switch(getStatus()) { @@ -220,7 +223,9 @@ void SocketCan::testConnection() { disconnectDevice(); setStatus(CANCon::NOT_CONNECTED); - emit status(getStatus()); + stats.conStatus = getStatus(); + stats.numHardwareBuses = mNumBuses; + emit status(stats); } break; case CANCon::NOT_CONNECTED: @@ -229,13 +234,18 @@ void SocketCan::testConnection() { /* try to reconnect */ CANBus bus; if(getBusConfig(0, bus)) + { + bus.setEnabled(true); setBusSettings(0, bus); + } } /* disconnect test instance */ dev_p->disconnectDevice(); setStatus(CANCon::CONNECTED); - emit status(getStatus()); + stats.conStatus = getStatus(); + stats.numHardwareBuses = mNumBuses; + emit status(stats); } break; default: {} diff --git a/frameplaybackobject.cpp b/frameplaybackobject.cpp new file mode 100644 index 0000000..5d41774 --- /dev/null +++ b/frameplaybackobject.cpp @@ -0,0 +1,399 @@ +#include "frameplaybackobject.h" + +FramePlaybackObject::FramePlaybackObject() +{ + mThread_p = new QThread(); + + currentPosition = 0; + playbackInterval = 1; + playbackBurst = 1; + statusCounter = 0; + numBuses = 0; + playbackActive = false; + playbackForward = true; + useOrigTiming = false; + whichBusSend = -1; +} + +FramePlaybackObject::~FramePlaybackObject() +{ + mThread_p->quit(); + mThread_p->wait(); + delete mThread_p; +} + +quint64 FramePlaybackObject::updatePosition(bool forward) +{ + //qDebug() << "updatePosition"; + if (forward) + { + if (currentPosition < (currentSeqItem->data.count() - 1)) currentPosition++; //still in same file so keep going + else //hit the end of the current file + { + qDebug() << "hit end of current sequence"; + currentSeqItem->currentLoopCount++; + currentPosition = 0; + if (currentSeqItem->currentLoopCount == currentSeqItem->maxLoops) //have we looped enough times? + { + playbackActive = false; + playbackTimer->stop(); + emit EndOfFrameCache(); + } + } + } + else + { + if (currentPosition > 0) currentPosition--; + else //hit the beginning of the current sequence + { + qDebug() << "hit start of current sequence"; + currentSeqItem->currentLoopCount++; + currentPosition = currentSeqItem->data.count() - 1; + if (currentSeqItem->currentLoopCount == currentSeqItem->maxLoops) //have we looped enough times? + { + playbackActive = false; + playbackTimer->stop(); + emit EndOfFrameCache(); + } + } + } + + //only send frame out if its ID is checked in the list. Otherwise discard it. + CANFrame *thisFrame = ¤tSeqItem->data[currentPosition]; + uint32_t originalBus = thisFrame->bus; + if (currentSeqItem->idFilters.find(thisFrame->ID).value()) + { + if (whichBusSend > -1) + { + thisFrame->bus = whichBusSend; + sendingBuffer.append(*thisFrame); + } + else if (whichBusSend == -1) + { + for (int c = 0; c < numBuses; c++) + { + thisFrame->bus = c; + sendingBuffer.append(*thisFrame); + } + } + else //from file so retain original bus and send as-is + { + sendingBuffer.append(*thisFrame); + } + + thisFrame->bus = originalBus; + } + return thisFrame->timestamp; +} + +quint64 FramePlaybackObject::peekPosition(bool forward) +{ + int peekCurrentPosition = currentPosition; + if (forward) + { + if (peekCurrentPosition < (currentSeqItem->data.count() - 1)) peekCurrentPosition++; //still in same file so keep going + else //hit the end of the current file + { + return 0xFFFFFFFFFFFFFFFFull; + } + } + else + { + if (peekCurrentPosition > 0) peekCurrentPosition--; + else //hit the beginning of the current sequence + { + return 0xFFFFFFFFFFFFFFFFull; + } + } + CANFrame *thisFrame = ¤tSeqItem->data[peekCurrentPosition]; + return thisFrame->timestamp; +} + +void FramePlaybackObject::piStart() +{ + playbackTimer = new QTimer(); + playbackTimer->setTimerType(Qt::PreciseTimer); + playbackTimer->setInterval(1); + + currentPosition = 0; + playbackActive = false; + playbackForward = true; + whichBusSend = 0; + + connect(playbackTimer, &QTimer::timeout, this, &FramePlaybackObject::timerTriggered); +} + +void FramePlaybackObject::piStop() +{ + playbackTimer->stop(); + delete playbackTimer; +} + +void FramePlaybackObject::initialize() +{ + if( mThread_p && (mThread_p != QThread::currentThread()) ) + { + /* move ourself to the thread */ + moveToThread(mThread_p); /*TODO handle errors */ + /* connect started() */ + connect(mThread_p, SIGNAL(started()), this, SLOT(initialize())); + /* start the thread */ + mThread_p->start(QThread::HighPriority); + return; + } + + /* set started flag */ + //mStarted = true; + + /* in multithread case, this will be called before entering thread event loop */ + return piStart(); + +} + +void FramePlaybackObject::finalize() +{ + /* 1) execute in mThread_p context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) + { + /* if thread is finished, it means we call this function for the second time so we can leave */ + if( !mThread_p->isFinished() ) + { + /* we need to call piStop() */ + QMetaObject::invokeMethod(this, "finalize", + Qt::BlockingQueuedConnection); + /* 3) stop thread */ + mThread_p->quit(); + if(!mThread_p->wait()) { + qDebug() << "can't stop thread"; + } + } + return; + } + + /* 2) call piStop in mThread context */ + return piStop(); +} + +void FramePlaybackObject::startPlaybackForward() +{ + /* make sure we execute in mThread context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) { + QMetaObject::invokeMethod(this, "startPlaybackForward", + Qt::BlockingQueuedConnection); + return; + } + + playbackActive = true; + playbackForward = true; + + if (useOrigTiming) + { + playbackTimer->setInterval(1); + playbackElapsed.start(); + if (currentSeqItem->data[currentPosition].timestamp > 2000) + playbackLastTimeStamp = currentSeqItem->data[currentPosition].timestamp - 2000; + else playbackLastTimeStamp = 0; + } + playbackTimer->start(); +} + +void FramePlaybackObject::startPlaybackBackward() +{ + /* make sure we execute in mThread context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) { + QMetaObject::invokeMethod(this, "startPlaybackBackward", + Qt::BlockingQueuedConnection); + return; + } + + playbackActive = true; + playbackForward = false; + if (useOrigTiming) + { + playbackElapsed.start(); + playbackTimer->setInterval(1); + playbackLastTimeStamp = currentSeqItem->data[currentPosition].timestamp + 2000; + } + playbackTimer->start(); +} + +void FramePlaybackObject::stepPlaybackForward() +{ + /* make sure we execute in mThread context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) { + QMetaObject::invokeMethod(this, "stepPlaybackForward", + Qt::BlockingQueuedConnection); + return; + } + + sendingBuffer.clear(); + playbackTimer->stop(); + playbackActive = false; + updatePosition(true); + CANConManager::getInstance()->sendFrames(sendingBuffer); +} + +void FramePlaybackObject::stepPlaybackBackward() +{ + /* make sure we execute in mThread context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) { + QMetaObject::invokeMethod(this, "stepPlaybackBackward", + Qt::BlockingQueuedConnection); + return; + } + + sendingBuffer.clear(); + playbackTimer->stop(); //pushing this button halts automatic playback + playbackActive = false; + + updatePosition(false); + CANConManager::getInstance()->sendFrames(sendingBuffer); +} + +void FramePlaybackObject::stopPlayback() +{ + /* make sure we execute in mThread context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) { + QMetaObject::invokeMethod(this, "stopPlayback", + Qt::BlockingQueuedConnection); + return; + } + + playbackTimer->stop(); //pushing this button halts automatic playback + playbackActive = false; + currentPosition = 0; +} + +void FramePlaybackObject::pausePlayback() +{ + /* make sure we execute in mThread context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) { + QMetaObject::invokeMethod(this, "pausePlayback", + Qt::BlockingQueuedConnection); + return; + } + + playbackActive = false; + playbackTimer->stop(); +} + +void FramePlaybackObject::setSequenceObject(SequenceItem *item) +{ + currentSeqItem = item; +} + +void FramePlaybackObject::setUseOriginalTiming(bool state) +{ + useOrigTiming = state; +} + +void FramePlaybackObject::setSendingBus(int bus) +{ + whichBusSend = bus; +} + +void FramePlaybackObject::setPlaybackBurst(int burst) +{ + playbackBurst = burst; +} + +void FramePlaybackObject::setNumBuses(int buses) +{ + numBuses = buses; +} + +//any time you touch objects that live in a potentially different thread you must switch into that thread. +//So this function is a bit different than the rest of the simple setters for that reason. +void FramePlaybackObject::setPlaybackInterval(int interval) +{ + /* make sure we execute in mThread context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) { + QMetaObject::invokeMethod(this, "setPlaybackInterval", + Qt::BlockingQueuedConnection, + Q_ARG(int , interval) ); + return; + } + playbackInterval = interval; + playbackTimer->setInterval(interval); +} + +void FramePlaybackObject::timerTriggered() +{ + sendingBuffer.clear(); + + if (useOrigTiming) + { + //get elapsed microseconds since last tick (in case timer skips or is otherwise inaccurate, though there are no guarantees about elapsed timer either) + quint64 elapsed = playbackElapsed.nsecsElapsed() / 1000; + playbackElapsed.start(); + if (playbackForward) + { + playbackLastTimeStamp += elapsed; + //qDebug() << playbackLastTimeStamp; + //qDebug() << "El: " << elapsed; + while (peekPosition(true) <= playbackLastTimeStamp) + { + updatePosition(true); + } + if (peekPosition(true) == 0xFFFFFFFFFFFFFFFFull) + { + updatePosition(true); //this'll go to the next log (if there is one) + if (currentSeqItem->data[currentPosition].timestamp > 1000) + playbackLastTimeStamp = currentSeqItem->data[currentPosition].timestamp - 1000; + else playbackLastTimeStamp = 0; + } + } + else + { + if (playbackLastTimeStamp > elapsed) + playbackLastTimeStamp -= elapsed; + else playbackLastTimeStamp = 0; + while (peekPosition(false) >= playbackLastTimeStamp && peekPosition(false) != 0xFFFFFFFFFFFFFFFFull) + { + updatePosition(false); + } + if (peekPosition(false) == 0xFFFFFFFFFFFFFFFFull) + { + updatePosition(false); //this'll go to the next log (if there is one) + playbackLastTimeStamp = currentSeqItem->data[currentPosition].timestamp + 1000; + } + } + statusCounter++; + } + else + { + for (int count = 0; count < playbackBurst; count++) + { + if (!playbackActive) + { + playbackTimer->stop(); + return; + } + if (playbackForward) + { + updatePosition(true); + } + else + { + updatePosition(false); + } + } + statusCounter += playbackInterval; + } + + if (statusCounter > 249) + { + statusCounter = 0; + emit statusUpdate(currentPosition); + } + + //qDebug() << "sb: " << sendingBuffer.count(); + if (sendingBuffer.count() > 0) CANConManager::getInstance()->sendFrames(sendingBuffer); +} + + + + + + + diff --git a/frameplaybackobject.h b/frameplaybackobject.h new file mode 100644 index 0000000..0935ffc --- /dev/null +++ b/frameplaybackobject.h @@ -0,0 +1,102 @@ +#ifndef FRAMEPLAYBACKOBJECT_H +#define FRAMEPLAYBACKOBJECT_H + +#include +#include +#include +#include +#include +#include "can_structs.h" +#include "connections/canconmanager.h" + +//one entry in the sequence of data to use +struct SequenceItem +{ + QString filename; + QVector data; + QHash idFilters; + int maxLoops; + int currentLoopCount; +}; + +/* + broken out functionality that used to be in frameplaybackwindow. That mixed code for Model/View/Controller into one giant + class. I wasn't too concerned about that but the problem is that all code within a GUI object runs in GUI context and that + is most certainly not a feature, especially if you have a multi-core CPU. This object lives in a separate thread from the GUI + and thus is better scheduled and doesn't block the GUI thread. Really all functionality in this program should be broken into + a separate thread from GUI if it is prone to running a long time and/or taking up a lot of CPU time (unless it really does + have to interface with the GUI in some way. All gui touching code must run on its thread). +*/ +class FramePlaybackObject : public QObject +{ + Q_OBJECT + +public: + FramePlaybackObject(); + ~FramePlaybackObject(); + +public slots: + /** + * @brief start the device. This calls piStarted + * @note starts the working thread if required (piStart is called in the working thread context) + */ + void initialize(); + + /** + * @brief stop the device. This calls piStop + * @note if a working thread is used, piStop is called before exiting the working thread + */ + void finalize(); + + void startPlaybackForward(); + void startPlaybackBackward(); + void stepPlaybackForward(); + void stepPlaybackBackward(); + void stopPlayback(); + void pausePlayback(); + + void setSequenceObject(SequenceItem *item); + void setUseOriginalTiming(bool state); + void setSendingBus(int bus); + void setPlaybackInterval(int interval); + void setPlaybackBurst(int burst); + void setNumBuses(int buses); + +signals: + void EndOfFrameCache(); //we hit the end/beginning of the frame cache (depending on direction of playback) + void statusUpdate(int frameNum); + +private slots: + void timerTriggered(); + +private: + QList sendingBuffer; + SequenceItem *currentSeqItem; + int currentPosition; + QTimer *playbackTimer; + QElapsedTimer playbackElapsed; + quint64 playbackLastTimeStamp; + int playbackInterval; + int playbackBurst; + int numBuses; + int statusCounter; + bool playbackActive; + bool playbackForward; + bool useOrigTiming; + int whichBusSend; + QThread* mThread_p; + + quint64 updatePosition(bool forward); + quint64 peekPosition(bool forward); + /** + * @brief starts the device + */ + void piStart(); + + /** + * @brief stops the device + */ + void piStop(); +}; + +#endif // FRAMEPLAYBACKOBJECT_H diff --git a/frameplaybackwindow.cpp b/frameplaybackwindow.cpp index 70f929d..d8460cb 100644 --- a/frameplaybackwindow.cpp +++ b/frameplaybackwindow.cpp @@ -32,47 +32,50 @@ FramePlaybackWindow::FramePlaybackWindow(const QVector *frames, QWidge ui->comboCANBus->addItem(tr("From File")); ui->comboCANBus->setCurrentIndex(0); + playbackObject.initialize(); + playbackObject.setNumBuses(numBuses); + readSettings(); modelFrames = frames; - playbackTimer = new QTimer(); - playbackTimer->setTimerType(Qt::PreciseTimer); - - currentPosition = 0; - playbackActive = false; - playbackForward = true; - whichBusSend = 0; //0 = no bus, 1 = bus 0, 2 = bus 1, 4 = from file - Bitfield so you can 'or' them. currentSeqItem = NULL; currentSeqNum = -1; + currentPosition = 0; + forward = true; + isPlaying = false; updateFrameLabel(); - connect(ui->btnStepBack, SIGNAL(clicked(bool)), this, SLOT(btnBackOneClick())); - connect(ui->btnPause, SIGNAL(clicked(bool)), this, SLOT(btnPauseClick())); - connect(ui->btnPlayReverse, 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->btnStepForward, SIGNAL(clicked(bool)), this, SLOT(btnFwdOneClick())); - connect(ui->btnSelectAll, SIGNAL(clicked(bool)), this, SLOT(btnSelectAllClick())); - connect(ui->btnSelectNone, SIGNAL(clicked(bool)), this, SLOT(btnSelectNoneClick())); - connect(ui->btnDelete, SIGNAL(clicked(bool)), this, SLOT(btnDeleteCurrSeq())); + connect(ui->btnStepBack, &QAbstractButton::clicked, this, &FramePlaybackWindow::btnBackOneClick); + connect(ui->btnPause, &QAbstractButton::clicked, this, &FramePlaybackWindow::btnPauseClick); + connect(ui->btnPlayReverse, &QAbstractButton::clicked, this, &FramePlaybackWindow::btnReverseClick); + connect(ui->btnStop, &QAbstractButton::clicked, this, &FramePlaybackWindow::btnStopClick); + connect(ui->btnPlay, &QAbstractButton::clicked, this, &FramePlaybackWindow::btnPlayClick); + connect(ui->btnStepForward, &QAbstractButton::clicked, this, &FramePlaybackWindow::btnFwdOneClick); + connect(ui->btnSelectAll, &QAbstractButton::clicked, this, &FramePlaybackWindow::btnSelectAllClick); + connect(ui->btnSelectNone, &QAbstractButton::clicked, this, &FramePlaybackWindow::btnSelectNoneClick); + connect(ui->btnDelete, &QAbstractButton::clicked, this, &FramePlaybackWindow::btnDeleteCurrSeq); connect(ui->spinPlaySpeed, SIGNAL(valueChanged(int)), this, SLOT(changePlaybackSpeed(int))); + connect(ui->spinBurstSpeed, SIGNAL(valueChanged(int)), this, SLOT(changeBurstRate(int))); //connect(ui->cbLoop, SIGNAL(clicked(bool)), this, SLOT(changeLooping(bool))); connect(ui->comboCANBus, SIGNAL(currentIndexChanged(int)), this, SLOT(changeSendingBus(int))); - connect(ui->listID, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(changeIDFiltering(QListWidgetItem*))); - connect(playbackTimer, SIGNAL(timeout()), this, SLOT(timerTriggered())); - connect(ui->btnLoadFile, SIGNAL(clicked(bool)), this, SLOT(btnLoadFile())); - connect(ui->btnLoadLive, SIGNAL(clicked(bool)), this, SLOT(btnLoadLive())); - connect(ui->tblSequence, SIGNAL(cellPressed(int,int)), this, SLOT(seqTableCellClicked(int,int))); - connect(ui->tblSequence, SIGNAL(cellChanged(int,int)), this, SLOT(seqTableCellChanged(int,int))); - connect(ui->btnLoadFilters, SIGNAL(clicked(bool)), this, SLOT(loadFilters())); - connect(ui->btnSaveFilters, SIGNAL(clicked(bool)), this, SLOT(saveFilters())); + connect(ui->listID, &QListWidget::itemClicked, this, &FramePlaybackWindow::changeIDFiltering); + connect(ui->btnLoadFile, &QAbstractButton::clicked, this, &FramePlaybackWindow::btnLoadFile); + connect(ui->btnLoadLive, &QAbstractButton::clicked, this, &FramePlaybackWindow::btnLoadLive); + connect(ui->tblSequence, &QTableWidget::cellPressed, this, &FramePlaybackWindow::seqTableCellClicked); + connect(ui->tblSequence, &QTableWidget::cellChanged, this, &FramePlaybackWindow::seqTableCellChanged); + connect(ui->btnLoadFilters, &QAbstractButton::clicked, this, &FramePlaybackWindow::loadFilters); + connect(ui->btnSaveFilters, &QAbstractButton::clicked, this, &FramePlaybackWindow::saveFilters); + connect(ui->cbOriginalTiming, &QCheckBox::toggled, this, &FramePlaybackWindow::useOrigTimingClicked); + + connect(&playbackObject, &FramePlaybackObject::EndOfFrameCache, this, &FramePlaybackWindow::EndOfFrameCache); + connect(&playbackObject, &FramePlaybackObject::statusUpdate, this, &FramePlaybackWindow::getStatusUpdate); ui->listID->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->listID, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuFilters(QPoint))); - playbackTimer->setInterval(ui->spinPlaySpeed->value()); //set the timer to the default value of the control + playbackObject.setPlaybackInterval(ui->spinPlaySpeed->value()); QStringList headers; headers << "Source" << "Loops"; @@ -85,9 +88,6 @@ FramePlaybackWindow::FramePlaybackWindow(const QVector *frames, QWidge FramePlaybackWindow::~FramePlaybackWindow() { delete ui; - - playbackTimer->stop(); - delete playbackTimer; } void FramePlaybackWindow::showEvent(QShowEvent *) @@ -115,7 +115,8 @@ void FramePlaybackWindow::readSettings() } ui->spinPlaySpeed->setValue(settings.value("Playback/DefSpeed", 5).toInt()); ui->comboCANBus->setCurrentIndex(settings.value("Playback/SendingBus", 0).toInt()); - whichBusSend = ui->comboCANBus->currentIndex(); + + calculateWhichBus(); } void FramePlaybackWindow::writeSettings() @@ -256,6 +257,73 @@ void FramePlaybackWindow::refreshIDList() ui->listID->sortItems(); } +void FramePlaybackWindow::calculateWhichBus() +{ + int idx = ui->comboCANBus->currentIndex(); + int maxIdx = ui->comboCANBus->count() - 1; + int out = idx; + if (idx == (maxIdx - 1) ) out = -1; + if (idx == maxIdx) out = -2; + + playbackObject.setSendingBus(out); +} + +void FramePlaybackWindow::EndOfFrameCache() +{ + if (forward) + { + currentSeqNum++; //go forward in the sequence + if (currentSeqNum == seqItems.count()) //are we at the end of the sequence? + { + //reset the loop figures for each sequence entry + for (int i = 0; i < seqItems.count(); i++) seqItems[i].currentLoopCount = 0; + currentSeqNum = 0; + if (ui->cbLoop->isChecked()) //go back to beginning if we're looping the sequence + { + + } + else //not looping so stop playback entirely + { + isPlaying = false; + playbackObject.stopPlayback(); + } + } + currentSeqItem = &seqItems[currentSeqNum]; + playbackObject.setSequenceObject(currentSeqItem); + if (isPlaying) playbackObject.startPlaybackForward(); + ui->tblSequence->setCurrentCell(currentSeqNum, 0); + } + else + { + currentSeqNum--; //go backward in the sequence + if (currentSeqNum == -1) //are we trying to go past the beginning? + { + //reset the loop figures for each sequence entry + for (int i = 0; i < seqItems.count(); i++) seqItems[i].currentLoopCount = 0; + currentSeqNum = seqItems.count() - 1; + if (ui->cbLoop->isChecked()) //go back to the last sequence entry if we're looping + { + + } + else //not looping so stop playback entirely + { + isPlaying = false; + playbackObject.stopPlayback(); + } + } + currentSeqItem = &seqItems[currentSeqNum]; + playbackObject.setSequenceObject(currentSeqItem); + if (isPlaying) playbackObject.startPlaybackBackward(); + ui->tblSequence->setCurrentCell(currentSeqNum, 0); + } +} + +void FramePlaybackWindow::getStatusUpdate(int frameNum) +{ + currentPosition = frameNum; + updateFrameLabel(); +} + void FramePlaybackWindow::updateFrameLabel() { int row = currentSeqNum; @@ -313,15 +381,27 @@ void FramePlaybackWindow::fillIDHash(SequenceItem &item) } } +void FramePlaybackWindow::useOrigTimingClicked() +{ + if (ui->cbOriginalTiming->isChecked()) + { + ui->spinBurstSpeed->setEnabled(false); + ui->spinPlaySpeed->setEnabled(false); + playbackObject.setUseOriginalTiming(true); + } + else + { + ui->spinBurstSpeed->setEnabled(true); + ui->spinPlaySpeed->setEnabled(true); + playbackObject.setUseOriginalTiming(false); + } +} + void FramePlaybackWindow::btnDeleteCurrSeq() { if (currentSeqNum == -1) return; - if (playbackActive) - { - playbackActive = false; - playbackTimer->stop(); - } + playbackObject.stopPlayback(); seqItems.removeAt(currentSeqNum); ui->tblSequence->removeRow(currentSeqNum); @@ -345,6 +425,7 @@ void FramePlaybackWindow::btnLoadFile() if (FrameFileIO::loadFrameFile(filename, &item.data)) { + qSort(item.data); //sort by timestamp to be sure it's in order QStringList fileList = filename.split('/'); item.filename = fileList[fileList.length() - 1]; item.currentLoopCount = 0; @@ -364,6 +445,7 @@ void FramePlaybackWindow::btnLoadFile() { currentSeqNum = 0; currentSeqItem = &seqItems[0]; + playbackObject.setSequenceObject(currentSeqItem); } refreshIDList(); updateFrameLabel(); @@ -377,6 +459,7 @@ void FramePlaybackWindow::btnLoadLive() item.currentLoopCount = 0; item.maxLoops = 1; item.data = QVector(*modelFrames); //create a copy of the current frames from the main view + qSort(item.data); //be sure it's all in time based order fillIDHash(item); if (ui->tblSequence->currentRow() == -1) { @@ -391,6 +474,7 @@ void FramePlaybackWindow::btnLoadLive() { currentSeqNum = 0; currentSeqItem = &seqItems[0]; + playbackObject.setSequenceObject(currentSeqItem); } refreshIDList(); updateFrameLabel(); @@ -399,32 +483,28 @@ void FramePlaybackWindow::btnLoadLive() void FramePlaybackWindow::btnBackOneClick() { - sendingBuffer.clear(); - playbackTimer->stop(); //pushing this button halts automatic playback - playbackActive = false; - - updatePosition(false); - CANConManager::getInstance()->sendFrames(sendingBuffer); + forward = false; + isPlaying = false; + playbackObject.stepPlaybackBackward(); } void FramePlaybackWindow::btnPauseClick() { - playbackActive = false; - playbackTimer->stop(); + isPlaying = false; + playbackObject.pausePlayback(); } void FramePlaybackWindow::btnReverseClick() { - playbackActive = true; - playbackForward = false; - playbackTimer->start(); + forward = false; + isPlaying = true; + playbackObject.startPlaybackBackward(); } void FramePlaybackWindow::btnStopClick() { - playbackTimer->stop(); //pushing this button halts automatic playback - playbackActive = false; - currentPosition = 0; + isPlaying = false; + playbackObject.stopPlayback(); if (seqItems.count() > 0) { currentSeqNum = 0; @@ -438,29 +518,32 @@ void FramePlaybackWindow::btnStopClick() { ui->tblSequence->setCurrentCell(0, 0); refreshIDList(); - } + } updateFrameLabel(); } void FramePlaybackWindow::btnPlayClick() { - playbackActive = true; - playbackForward = true; - playbackTimer->start(); + forward = true; + isPlaying = true; + playbackObject.startPlaybackForward(); } void FramePlaybackWindow::btnFwdOneClick() { - sendingBuffer.clear(); - playbackTimer->stop(); - playbackActive = false; - updatePosition(true); - CANConManager::getInstance()->sendFrames(sendingBuffer); + forward = true; + isPlaying = false; + playbackObject.stepPlaybackForward(); } void FramePlaybackWindow::changePlaybackSpeed(int newSpeed) { - playbackTimer->setInterval(newSpeed); + playbackObject.setPlaybackInterval(newSpeed); +} + +void FramePlaybackWindow::changeBurstRate(int burst) +{ + playbackObject.setPlaybackBurst(burst); } void FramePlaybackWindow::changeLooping(bool check) @@ -470,7 +553,7 @@ void FramePlaybackWindow::changeLooping(bool check) void FramePlaybackWindow::changeSendingBus(int newIdx) { - whichBusSend = newIdx; + calculateWhichBus(); } void FramePlaybackWindow::changeIDFiltering(QListWidgetItem *item) @@ -499,118 +582,3 @@ void FramePlaybackWindow::btnSelectNoneClick() currentSeqItem->idFilters[Utility::ParseStringToNum(item->text())] = false; } } - -void FramePlaybackWindow::timerTriggered() -{ - sendingBuffer.clear(); - for (int count = 0; count < ui->spinBurstSpeed->value(); count++) - { - if (!playbackActive) - { - playbackTimer->stop(); - return; - } - if (playbackForward) - { - updatePosition(true); - } - else - { - updatePosition(false); - } - } - CANConManager::getInstance()->sendFrames(sendingBuffer); -} - -void FramePlaybackWindow::updatePosition(bool forward) -{ - //qDebug() << "updatePosition"; - if (forward) - { - if (currentPosition < (currentSeqItem->data.count() - 1)) currentPosition++; //still in same file so keep going - else //hit the end of the current file - { - qDebug() << "hit end of current sequence"; - currentSeqItem->currentLoopCount++; - currentPosition = 0; - if (currentSeqItem->currentLoopCount == currentSeqItem->maxLoops) //have we looped enough times? - { - currentSeqNum++; //go forward in the sequence - if (currentSeqNum == seqItems.count()) //are we at the end of the sequence? - { - //reset the loop figures for each sequence entry - for (int i = 0; i < seqItems.count(); i++) seqItems[i].currentLoopCount = 0; - currentSeqNum = 0; - if (ui->cbLoop->isChecked()) //go back to beginning if we're looping the sequence - { - - } - else //not looping so stop playback entirely - { - playbackActive = false; - playbackTimer->stop(); - } - } - currentSeqItem = &seqItems[currentSeqNum]; - ui->tblSequence->setCurrentCell(currentSeqNum, 0); - } - } - } - else - { - if (currentPosition > 0) currentPosition--; - else //hit the beginning of the current sequence - { - qDebug() << "hit start of current sequence"; - currentSeqItem->currentLoopCount++; - currentPosition = currentSeqItem->data.count() - 1; - if (currentSeqItem->currentLoopCount == currentSeqItem->maxLoops) //have we looped enough times? - { - currentSeqNum--; //go backward in the sequence - if (currentSeqNum == -1) //are we trying to go past the beginning? - { - //reset the loop figures for each sequence entry - for (int i = 0; i < seqItems.count(); i++) seqItems[i].currentLoopCount = 0; - currentSeqNum = seqItems.count() - 1; - if (ui->cbLoop->isChecked()) //go back to the last sequence entry if we're looping - { - - } - else //not looping so stop playback entirely - { - playbackActive = false; - playbackTimer->stop(); - } - } - currentSeqItem = &seqItems[currentSeqNum]; - ui->tblSequence->setCurrentCell(currentSeqNum, 0); - } - } - } - updateFrameLabel(); - //only send frame out if its ID is checked in the list. Otherwise discard it. - CANFrame *thisFrame = ¤tSeqItem->data[currentPosition]; - uint32_t originalBus = thisFrame->bus; - if (currentSeqItem->idFilters.find(thisFrame->ID).value()) - { - if (whichBusSend < ui->comboCANBus->count() - 2) - { - thisFrame->bus = whichBusSend; - sendingBuffer.append(*thisFrame); - } - else if (whichBusSend == (ui->comboCANBus->count() - 2)) //all - { - for (int c = 0; c < ui->comboCANBus->count() - 2; c++) - { - thisFrame->bus = c; - sendingBuffer.append(*thisFrame); - } - } - else //from file so retain original bus and send as-is - { - sendingBuffer.append(*thisFrame); - } - - thisFrame->bus = originalBus; - } -} diff --git a/frameplaybackwindow.h b/frameplaybackwindow.h index a0a2026..996ac55 100644 --- a/frameplaybackwindow.h +++ b/frameplaybackwindow.h @@ -3,24 +3,14 @@ #include #include -#include #include "can_structs.h" #include "framefileio.h" +#include "frameplaybackobject.h" namespace Ui { class FramePlaybackWindow; } -//one entry in the sequence of data to use -struct SequenceItem -{ - QString filename; - QVector data; - QHash idFilters; - int maxLoops; - int currentLoopCount; -}; - class FramePlaybackWindow : public QDialog { Q_OBJECT @@ -38,12 +28,12 @@ private slots: void btnFwdOneClick(); void btnDeleteCurrSeq(); void changePlaybackSpeed(int newSpeed); + void changeBurstRate(int burst); void changeLooping(bool check); void changeSendingBus(int newIdx); void changeIDFiltering(QListWidgetItem *item); void btnSelectAllClick(); void btnSelectNoneClick(); - void timerTriggered(); void btnLoadFile(); void btnLoadLive(); void seqTableCellClicked(int row, int col); @@ -51,30 +41,31 @@ private slots: void contextMenuFilters(QPoint); void saveFilters(); void loadFilters(); + void useOrigTimingClicked(); + void getStatusUpdate(int frameNum); + void EndOfFrameCache(); private: Ui::FramePlaybackWindow *ui; QList foundID; QList frameCache; - QList sendingBuffer; const QVector *modelFrames; - int currentPosition; - QTimer *playbackTimer; - bool playbackActive; - bool playbackForward; - int whichBusSend; QList seqItems; SequenceItem *currentSeqItem; int currentSeqNum; + FramePlaybackObject playbackObject; + bool forward; + bool isPlaying; + int currentPosition; void refreshIDList(); void updateFrameLabel(); - void updatePosition(bool forward); - void fillIDHash(SequenceItem &item); + void fillIDHash(SequenceItem &item); void showEvent(QShowEvent *); void closeEvent(QCloseEvent *event); void readSettings(); void writeSettings(); + void calculateWhichBus(); }; #endif // FRAMEPLAYBACKWINDOW_H diff --git a/ui/connectionwindow.ui b/ui/connectionwindow.ui index 76cc8fe..c24610e 100644 --- a/ui/connectionwindow.ui +++ b/ui/connectionwindow.ui @@ -25,6 +25,9 @@ + + true + QAbstractItemView::SingleSelection @@ -35,13 +38,6 @@ - - - - New Connection - - - @@ -49,10 +45,6 @@ - - - - @@ -193,45 +185,6 @@ - - - - Single Wire Mode - - - - - - - Listen Only Mode - - - - - - - Enabled - - - - - - - - - CAN Speed - - - - - - - true - - - - - @@ -241,15 +194,21 @@ - - - - Revert - - - + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -319,19 +278,6 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - diff --git a/ui/frameplaybackwindow.ui b/ui/frameplaybackwindow.ui index 557cb94..b49c350 100644 --- a/ui/frameplaybackwindow.ui +++ b/ui/frameplaybackwindow.ui @@ -100,6 +100,13 @@ + + + + Use original frame timing from captured frames + + +