diff --git a/SavvyCAN.pro b/SavvyCAN.pro index ef81cb2..34879f4 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -4,7 +4,7 @@ # #------------------------------------------------- -QT += core gui +QT += core gui serialbus greaterThan(QT_MAJOR_VERSION, 4): QT += widgets serialport printsupport qml @@ -50,8 +50,11 @@ SOURCES += main.cpp\ isotp_interpreterwindow.cpp \ isotp_handler.cpp \ canconnectionmodel.cpp \ - canconnection.cpp \ - canconnectioncontainer.cpp + canbus.cpp \ + connections/canconnection.cpp \ + connections/socketcan.cpp \ + connections/canconfactory.cpp \ + connections/gvretserial.cpp HEADERS += mainwindow.h \ can_structs.h \ @@ -88,8 +91,13 @@ HEADERS += mainwindow.h \ isotp_interpreterwindow.h \ isotp_handler.h \ canconnectionmodel.h \ - canconnection.h \ - canconnectioncontainer.h + canbus.h \ + utils/lfqueue.h \ + connections/canconnection.h \ + connections/socketcan.h \ + connections/canconconst.h \ + connections/canconfactory.h \ + connections/gvretserial.h FORMS += mainwindow.ui \ graphingwindow.ui \ diff --git a/can_structs.h b/can_structs.h index 0326947..d118dc8 100644 --- a/can_structs.h +++ b/can_structs.h @@ -4,7 +4,7 @@ #include #include -class CANFrame +struct CANFrame { public: int ID; diff --git a/canbus.cpp b/canbus.cpp new file mode 100644 index 0000000..c01f274 --- /dev/null +++ b/canbus.cpp @@ -0,0 +1,67 @@ +#include +#include "canbus.h" + +CANBus::CANBus() +{ + speed = 250000; + listenOnly = false; + singleWire = false; + active = false; +} + + +CANBus::CANBus(const CANBus& pBus) : + speed(pBus.speed), + listenOnly(pBus.listenOnly), + singleWire(pBus.singleWire), + active(pBus.active) {} + + +bool CANBus::operator==(CANBus& bus) { + return speed == bus.speed && + listenOnly == bus.listenOnly && + singleWire == bus.singleWire && + active == bus.active; +} + + +void CANBus::setSpeed(int newSpeed) +{ + speed = newSpeed; +} + +void CANBus::setListenOnly(bool mode) +{ + listenOnly = mode; +} + +void CANBus::setSingleWire(bool mode) +{ + singleWire = mode; +} + +void CANBus::setEnabled(bool mode) +{ + active = mode; +} + +int CANBus::getSpeed() +{ + return speed; +} + +bool CANBus::isListenOnly() +{ + return listenOnly; +} + +bool CANBus::isSingleWire() +{ + return singleWire; +} + +bool CANBus::isActive() +{ + return active; +} + diff --git a/canbus.h b/canbus.h new file mode 100644 index 0000000..c8b4e88 --- /dev/null +++ b/canbus.h @@ -0,0 +1,28 @@ +#ifndef CANBus_H +#define CANBus_H + + +class CANBus +{ +public: + CANBus(); + CANBus(const CANBus&); + bool operator==(CANBus&); + virtual ~CANBus(){}; /*TODO: remove connection from CANBus and add CANBus as an element of CANConnection */ + int speed; + bool listenOnly; + bool singleWire; + bool active; //is this bus turned on? + + + void setSpeed(int); // new speed + void setListenOnly(bool); //bool for whether to only listen + void setSingleWire(bool); //bool for whether to use single wire mode + void setEnabled(bool); //whether this bus should be enabled or not. + int getSpeed(); + bool isListenOnly(); + bool isSingleWire(); + bool isActive(); +}; + +#endif // CANBus_H diff --git a/canconnection.cpp b/canconnection.cpp deleted file mode 100644 index fb9ac11..0000000 --- a/canconnection.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include "canconnection.h" - -CAN_Bus::CAN_Bus() -{ - speed = 250000; - listenOnly = false; - singleWire = false; - active = false; - connection = NULL; - busNum = 0; -} - -void CAN_Bus::setSpeed(int newSpeed) -{ - speed = newSpeed; -} - -void CAN_Bus::setListenOnly(bool mode) -{ - listenOnly = mode; -} - -void CAN_Bus::setSingleWire(bool mode) -{ - singleWire = mode; -} - -void CAN_Bus::setEnabled(bool mode) -{ - active = mode; -} - -void CAN_Bus::setConnection(CANConnection *conn) -{ - connection = conn; -} - -void CAN_Bus::setBusNum(int num) -{ - busNum = num; -} - -int CAN_Bus::getSpeed() -{ - return speed; -} - -int CAN_Bus::getBusNum() -{ - return busNum; -} - -bool CAN_Bus::isListenOnly() -{ - return listenOnly; -} - -bool CAN_Bus::isSingleWire() -{ - return singleWire; -} - -bool CAN_Bus::isActive() -{ - return active; -} - -CANConnection* CAN_Bus::getConnection() -{ - return connection; -} - -CANConnection::CANConnection(CANFrameModel *pModel, int base) -{ - model = pModel; - numBuses = getNumBuses(); - busBase = base; -} - -int CANConnection::getNumBuses() -{ - return 1; -} - -int CANConnection::getBusBase() -{ - return busBase; -} - -QString CANConnection::getConnTypeName() -{ - return QString("Generic"); -} - -QString CANConnection::getConnPortName() -{ - return portName; -} - -void CANConnection::run() -{ - -} - -void CANConnection::sendFrame(const CANFrame * frame) -{ - -} - -void CANConnection::sendFrameBatch(const QList *frames) -{ - -} - -void CANConnection::updatePortName(QString portName) -{ - this->portName = portName; -} - -void CANConnection::stopFrameCapture(int bus) -{ - -} - -void CANConnection::startFrameCapture(int bus) -{ - -} - -void CANConnection::updateBusSettings(CAN_Bus *bus) -{ - -} diff --git a/canconnection.h b/canconnection.h deleted file mode 100644 index 2a824cb..0000000 --- a/canconnection.h +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef CANCONNECTION_H -#define CANCONNECTION_H - -#include -#include -#include "can_structs.h" -#include "canframemodel.h" - -class CANConnection; - -class CAN_Bus -{ -public: - CAN_Bus(); - int busNum; - int speed; - bool listenOnly; - bool singleWire; - bool active; //is this bus turned on? - CANConnection *connection; - - void setSpeed(int); // new speed - void setListenOnly(bool); //bool for whether to only listen - void setSingleWire(bool); //bool for whether to use single wire mode - void setEnabled(bool); //whether this bus should be enabled or not. - void setConnection(CANConnection *); - void setBusNum(int); - int getSpeed(); - int getBusNum(); - bool isListenOnly(); - bool isSingleWire(); - bool isActive(); - CANConnection *getConnection(); -}; - - -//Gentle reminder, CANConnection objects run in their own thread (each one, different thread) -//so, for the love of God, do not try to access them directly. -//Use the signal/slots system to do indirect calls. -//Please and thank you. -class CANConnection : public QObject -{ - Q_OBJECT - -public: - CANConnection(CANFrameModel *, int); - virtual int getNumBuses(); - int getBusBase(); - virtual QString getConnTypeName(); - QString getConnPortName(); - -signals: - void error(const QString &); - void frameUpdateRapid(int); - void frameUpdate(int); - void connectionSuccess(CANConnection *); - void connectionFailure(CANConnection *); - void deviceInfo(int, int); //First param = driver version (or version of whatever you want), second param a status byte - - //bus number, bus speed, status (bit 0 = enabled, 1 = single wire, 2 = listen only) - //3 = Use value stored for enabled, 4 = use value passed for single wire, 5 = use value passed for listen only - //6 = use value passed for speed. This allows bus status to be updated but set that some things aren't really - //being passed. Just set for things that really are being updated. - void busStatus(int, int, int); - -public slots: - virtual void run(); - virtual void sendFrame(const CANFrame *); - virtual void sendFrameBatch(const QList *); - virtual void updatePortName(QString); //string version of the port to connect to. This base doesnt know a thing about this value - virtual void stopFrameCapture(int); //pass bus number - virtual void startFrameCapture(int); //pass bus number. Only if stopped. Defaults to started anyway - virtual void updateBusSettings(CAN_Bus *bus); //reference to the bus that changed. - -protected: - bool quit; - int numBuses; - int busBase; //first bus this class is supposed to handle - QString portName; //for easy access later on - QString connType; //what kind of connection this is (socketcan, kvaser, etc) - bool isConnected; //is the whole device connected? (Is the code connected to the device itself) - CANFrameModel *model; -}; - -#endif // CANCONNECTION_H diff --git a/canconnectioncontainer.cpp b/canconnectioncontainer.cpp deleted file mode 100644 index 63a5069..0000000 --- a/canconnectioncontainer.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "canconnection.h" -#include "canconnectioncontainer.h" - -CANConnectionContainer::CANConnectionContainer(CANConnection *conn) -{ - thread = new QThread(); - connection = conn; - connection->moveToThread(thread); - - connect(thread, &QThread::finished, conn, &QObject::deleteLater); - connect(thread, &QThread::started, conn, &CANConnection::run); //setup timers within the proper thread - connect(conn, &CANConnection::frameUpdateRapid, MainWindow::getReference(), &MainWindow::gotFrames, Qt::QueuedConnection); - connect(MainWindow::getReference(), &MainWindow::sendCANFrame, conn, &CANConnection::sendFrame, Qt::QueuedConnection); - thread->start(); - thread->setPriority(QThread::HighPriority); -} - -CANConnectionContainer::~CANConnectionContainer() -{ - //have to stop the actual execution first before deleting - delete thread; - delete connection; -} - -CANConnection* CANConnectionContainer::getRef() -{ - return connection; -} diff --git a/canconnectioncontainer.h b/canconnectioncontainer.h deleted file mode 100644 index 9ff2b22..0000000 --- a/canconnectioncontainer.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef CANCONNECTIONCONTAINER_H -#define CANCONNECTIONCONTAINER_H - -#include "canconnection.h" -#include -#include -#include "mainwindow.h" - -class CANConnectionContainer : public QObject -{ - Q_OBJECT -public: - CANConnectionContainer(CANConnection *conn); - ~CANConnectionContainer(); - - CANConnection* getRef(); - -private: - CANConnection *connection; - QThread *thread; -}; - -#endif // CANCONNECTIONCONTAINER_H diff --git a/canconnectionmodel.cpp b/canconnectionmodel.cpp index 8510f8d..3f96f94 100644 --- a/canconnectionmodel.cpp +++ b/canconnectionmodel.cpp @@ -1,5 +1,4 @@ -#include "canconnection.h" -#include "canconnectioncontainer.h" +#include "connections/canconnection.h" #include "canconnectionmodel.h" CANConnectionModel::CANConnectionModel(QObject *parent) @@ -7,11 +6,6 @@ CANConnectionModel::CANConnectionModel(QObject *parent) { } -void CANConnectionModel::refreshView() -{ - beginResetModel(); - endResetModel(); -} QVariant CANConnectionModel::headerData(int section, Qt::Orientation orientation, int role) const { @@ -41,6 +35,9 @@ QVariant CANConnectionModel::headerData(int section, Qt::Orientation orientation return QString(tr("Single Wire")); break; case 6: + return QString(tr("Status")); + break; + case 7: return QString(tr("Active")); break; } @@ -52,100 +49,124 @@ QVariant CANConnectionModel::headerData(int section, Qt::Orientation orientation return QVariant(); } -int CANConnectionModel::rowCount(const QModelIndex &parent) const -{ - return buses.count(); -} int CANConnectionModel::columnCount(const QModelIndex &parent) const { return 7; } + +int CANConnectionModel::rowCount(const QModelIndex &parent) const { + int rows=0; + QList::const_iterator iter; + + for (iter = mConns.begin() ; iter != mConns.end() ; ++iter) { + rows+=(*iter)->getNumBuses(); + } + + return rows; +} + + QVariant CANConnectionModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); - if (index.row() >= (buses.count())) - return QVariant(); if (role == Qt::DisplayRole) { - CAN_Bus bus = buses[index.row()]; - CANConnection *conn = bus.connection; + int busId; + CANConnection *conn_p = getAtIdx(index.row(), busId); + if(!conn_p) + return QVariant(); + + CANBus bus; + bool ret; + ret = conn_p->getBusSettings(busId, bus); + if(!ret) return QVariant(); + switch (index.column()) { - case 0: //bus - return QString::number(bus.busNum); - break; - case 1: //type - if (conn) return conn->getConnTypeName(); - else qDebug() << "Tried to show connection type but connection was NULL"; - break; - case 2: //port - if (conn) return conn->getConnPortName(); - else qDebug() << "Tried to show connection port but connection was NULL"; - break; - case 3: //speed - return QString::number(bus.speed); - break; - case 4: //Listen Only - if (bus.listenOnly) return QString("True"); - else return QString("False"); - break; - case 5: //Single Wire - if (bus.singleWire) return QString("True"); - else return QString("False"); - break; - case 6: //Active - if (bus.active) return QString("True"); - else return QString("False"); - break; - default: - return QVariant(); + case 0: //bus + return QString::number(busId); + break; + case 1: //type + if (conn_p) + switch (conn_p->getType()) { + case CANCon::KVASER: return "KVASER"; + case CANCon::SOCKETCAN: return "SocketCAN"; + case CANCon::GVRET_SERIAL: return "GVRET"; + default: {} + } + else qDebug() << "Tried to show connection type but connection was NULL"; + break; + case 2: //port + if (conn_p) return conn_p->getPort(); + else qDebug() << "Tried to show connection port but connection was NULL"; + break; + case 3: //speed + return QString::number(bus.speed); + case 4: //Listen Only + return (bus.listenOnly) ? "True" : "False"; + case 5: //Single Wire + return (bus.singleWire) ? "True" : "False"; + case 6: //Status + return (conn_p->getStatus()==CANCon::CONNECTED) ? "Connected" : "Not Connected"; + case 7: //Active + return (bus.active) ? "True" : "False"; + default: {} } } - else - return QVariant(); + + return QVariant(); } -void CANConnectionModel::addConnection(CANConnection *conn) -{ - CAN_Bus bus; - CANConnectionContainer *cont = new CANConnectionContainer(conn); - connections.append(cont); -} -void CANConnectionModel::addBus(CAN_Bus &bus) +void CANConnectionModel::add(CANConnection* pConn_p) { beginResetModel(); - buses.append(bus); + mConns.append(pConn_p); endResetModel(); } -CAN_Bus* CANConnectionModel::getBus(int bus) + +void CANConnectionModel::remove(CANConnection* pConn_p) { - if (bus < 0) return NULL; - if (bus >= buses.count()) return NULL; - return &buses[bus]; + beginResetModel(); + mConns.removeOne(pConn_p); + endResetModel(); } -CANConnection* CANConnectionModel::getConnection(int conn) + +QList& CANConnectionModel::getConnections() { - if (conn < 0) return NULL; - if (conn >= connections.count()) return NULL; - return connections[conn]->getRef(); + return mConns; } -CAN_Bus* CANConnectionModel::findBusByNum(int bus) + +CANConnection* CANConnectionModel::getAtIdx(int pIdx, int& pBusId) const { - for (int i = 0; i < buses.count(); i++) - { - if (buses[i].busNum == bus) - { - return &buses[i]; + if (pIdx < 0) + return NULL; + + int i=0; + QList::const_iterator iter = mConns.begin(); + + for (iter = mConns.begin() ; iter != mConns.end() ; ++iter) { + if( i <= pIdx && pIdx < i+(*iter)->getNumBuses() ) { + pBusId = pIdx - i; + return (*iter); } + + i+= (*iter)->getNumBuses(); } + return NULL; } + +void CANConnectionModel::refreshView() +{ + beginResetModel(); + endResetModel(); +} diff --git a/canconnectionmodel.h b/canconnectionmodel.h index 1e0410e..c91c9e2 100644 --- a/canconnectionmodel.h +++ b/canconnectionmodel.h @@ -1,12 +1,13 @@ #ifndef CANCONNECTIONMODEL_H #define CANCONNECTIONMODEL_H -#include "canconnection.h" -#include "canconnectioncontainer.h" #include -class CANConnectionContainer; +#include "canbus.h" +#include "connections/canconnection.h" +#include "connectionwindow.h" + class CANConnectionModel : public QAbstractTableModel { @@ -18,22 +19,21 @@ public: // Header: QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - void addConnection(CANConnection *conn); - void addBus(CAN_Bus &bus); - CAN_Bus* getBus(int bus); - CANConnection* getConnection(int conn); + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + void add(CANConnection* pConn_p); + void remove(CANConnection* pConn_p); + + QList& getConnections(); + CANConnection* getAtIdx(int, int&) const; void refreshView(); private: - QList connections; - QList buses; - - CAN_Bus *findBusByNum(int bus); + QList mConns; }; #endif // CANCONNECTIONMODEL_H diff --git a/canframemodel.cpp b/canframemodel.cpp index fe54877..02fc0cf 100644 --- a/canframemodel.cpp +++ b/canframemodel.cpp @@ -281,8 +281,10 @@ QVariant CANFrameModel::headerData(int section, Qt::Orientation orientation, return QVariant(); } -void CANFrameModel::addFrame(const CANFrame &frame, bool autoRefresh = false) + +void CANFrameModel::addFrame(CANFrame& frame, bool autoRefresh = false) { + /*TODO: remove mutex */ mutex.lock(); CANFrame tempFrame; tempFrame = frame; diff --git a/canframemodel.h b/canframemodel.h index 7e93007..ddea3ce 100644 --- a/canframemodel.h +++ b/canframemodel.h @@ -23,7 +23,6 @@ public: int columnCount(const QModelIndex &) const; int totalFrameCount(); - void addFrame(const CANFrame &, bool); void sendRefresh(); void sendRefresh(int); void sendBulkRefresh(int); @@ -46,6 +45,9 @@ public: const QVector *getFilteredListReference() const; //Thus saith the Lord, NO. const QMap *getFiltersReference() const; //this neither +public slots: + void addFrame(CANFrame&, bool); + signals: void updatedFiltersList(); diff --git a/connections/canconconst.h b/connections/canconconst.h new file mode 100644 index 0000000..707355d --- /dev/null +++ b/connections/canconconst.h @@ -0,0 +1,24 @@ +#ifndef CANCONCONST_H +#define CANCONCONST_H + +namespace CANCon { + + /** + * @brief The status enum + */ + enum status + { + NOT_CONNECTED, /*!< device is not connected */ + CONNECTED /*!< device is connected */ + }; + + enum type + { + GVRET_SERIAL, + KVASER, + SOCKETCAN, + NONE + }; +} + +#endif // CANCONCONST_H diff --git a/connections/canconfactory.cpp b/connections/canconfactory.cpp new file mode 100644 index 0000000..7926001 --- /dev/null +++ b/connections/canconfactory.cpp @@ -0,0 +1,19 @@ +#include +#include "canconfactory.h" +#include "socketcan.h" +#include "gvretserial.h" + +using namespace CANCon; + +CANConnection* CanConFactory::create(type pType, QString pPortName) +{ + switch(pType) { + case SOCKETCAN: + return new SocketCan(pPortName); + case GVRET_SERIAL: + return new GVRetSerial(pPortName); + default: {} + } + + return NULL; +} diff --git a/connections/canconfactory.h b/connections/canconfactory.h new file mode 100644 index 0000000..2b482f5 --- /dev/null +++ b/connections/canconfactory.h @@ -0,0 +1,13 @@ +#ifndef CANCONFACTORY_H +#define CANCONFACTORY_H + +#include "canconconst.h" +#include "canconnection.h" + +class CanConFactory +{ +public: + static CANConnection* create(CANCon::type, QString pPortName); +}; + +#endif // CANCONFACTORY_H diff --git a/connections/canconnection.cpp b/connections/canconnection.cpp new file mode 100644 index 0000000..526d88e --- /dev/null +++ b/connections/canconnection.cpp @@ -0,0 +1,245 @@ +#include +#include "canconnection.h" + +CANConnection::CANConnection(QString pPort, + CANCon::type pType, + int pNumBuses, + int pQueueLen, + bool pUseThread) : + mQueue(), + mNumBuses(pNumBuses), + mPort(pPort), + mType(pType), + mIsCapSuspended(false), + mStatus(CANCon::NOT_CONNECTED), + mThread_p(NULL) +{ + qDebug() << "CANConnection()"; + + /* register types */ + qRegisterMetaType("CANBus"); + qRegisterMetaType("CANFrame"); + qRegisterMetaType("CANCon::status"); + + /* set queue size */ + mQueue.setSize(pQueueLen); /*TODO add check on returned value */ + + /* allocate buses */ + mBus = new CANBus[mNumBuses]; + mConfigured = new bool[mNumBuses]; + + for(int i=0 ; iquit(); + mThread_p->wait(); + delete mThread_p; + mThread_p = NULL; + } + + /* delete bus table */ + delete[] mBus; + mBus = NULL; + /* configured table */ + delete[] mConfigured; + mConfigured = NULL; +} + + +void CANConnection::start() +{ + 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(start())); + /* start the thread */ + mThread_p->start(QThread::HighPriority); + return; + } + + /* in multithread case, this will be called before entering thread event loop */ + return piStarted(); +} + + +void CANConnection::suspend(bool pSuspend) +{ + /* execute in mThread_p context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) { + QMetaObject::invokeMethod(this, "suspend", + Qt::BlockingQueuedConnection, + Q_ARG(bool, pSuspend)); + return; + } + + return piSuspend(pSuspend); +} + + +void CANConnection::stop() +{ + /* 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, "stop", + 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(); +} + + +bool CANConnection::getBusSettings(int pBusIdx, CANBus& pBus) +{ + /* make sure we execute in mThread context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) { + bool ret; + QMetaObject::invokeMethod(this, "getBusSettings", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, ret), + Q_ARG(int , pBusIdx), + Q_ARG(CANBus& , pBus)); + return ret; + } + + return piGetBusSettings(pBusIdx, pBus); +} + + +void CANConnection::setBusSettings(int pBusIdx, CANBus pBus) +{ + /* make sure we execute in mThread context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) { + QMetaObject::invokeMethod(this, "setBusSettings", + Qt::BlockingQueuedConnection, + Q_ARG(int, pBusIdx), + Q_ARG(CANBus, pBus)); + return; + } + + return piSetBusSettings(pBusIdx, pBus); +} + + +void CANConnection::sendFrame(const CANFrame& pFrame) +{ + /* make sure we execute in mThread context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) { + QMetaObject::invokeMethod(this, "sendFrame", + Qt::BlockingQueuedConnection, + Q_ARG(const CANFrame&, pFrame)); + return; + } + + return piSendFrame(pFrame); +} + + +void CANConnection::sendFrameBatch(const QList& pFrames) +{ + /* make sure we execute in mThread context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) { + QMetaObject::invokeMethod(this, "sendFrameBatch", + Qt::BlockingQueuedConnection, + Q_ARG(const QList&, pFrames)); + return; + } + + return piSendFrameBatch(pFrames); +} + + +int CANConnection::getNumBuses() { + return mNumBuses; +} + + +bool CANConnection::isConfigured(int pBusId) { + if( pBusId < 0 || pBusId >= mNumBuses) + return false; + return mConfigured[pBusId]; +} + +void CANConnection::setConfigured(int pBusId, bool pConfigured) { + if( pBusId < 0 || pBusId >= mNumBuses) + return; + mConfigured[pBusId] = pConfigured; +} + + +bool CANConnection::getBusConfig(int pBusId, CANBus& pBus) { + if( pBusId < 0 || pBusId >= mNumBuses || !isConfigured(pBusId)) + return false; + + pBus = mBus[pBusId]; + return true; +} + + +void CANConnection::setBusConfig(int pBusId, CANBus& pBus) { + if( pBusId < 0 || pBusId >= mNumBuses) + return; + + mConfigured[pBusId] = true; + mBus[pBusId] = pBus; +} + + +QString CANConnection::getPort() { + return mPort; +} + + +LFQueue& CANConnection::getQueue() { + return mQueue; +} + + +CANCon::type CANConnection::getType() { + return mType; +} + + +CANCon::status CANConnection::getStatus() { + return (CANCon::status) mStatus.load(); +} + + +void CANConnection::setStatus(CANCon::status pStatus) { + mStatus.store(pStatus); +} + +bool CANConnection::isCapSuspended() { + return mIsCapSuspended; +} + +void CANConnection::setCapSuspended(bool pIsSuspended) { + mIsCapSuspended = pIsSuspended; +} + diff --git a/connections/canconnection.h b/connections/canconnection.h new file mode 100644 index 0000000..9199c7b --- /dev/null +++ b/connections/canconnection.h @@ -0,0 +1,252 @@ +#ifndef CANCONNECTION_H +#define CANCONNECTION_H + +#include +#include +#include "utils/lfqueue.h" +#include "can_structs.h" +#include "canbus.h" +#include "canconconst.h" + + +class CANConnection : public QObject +{ + Q_OBJECT + +public: + + /** + * @brief CANConnection constructor + * @param pPort: string containing port name + * @param pType: the type of connection @ref CANCon::type + * @param pNumBuses: the number of buses the device has + * @param pQueueLen: the length of the lock free queue to use + * @param pUseThread: if set to true, object will be execute in a dedicated thread + */ + CANConnection(QString pPort, + CANCon::type pType, + int pNumBuses, + int pQueueLen, + bool pUseThread); + /** + * @brief CANConnection destructor + */ + + virtual ~CANConnection(); + + /** + * @brief getNumBuses + * @return returns the number of buses of the device + */ + int getNumBuses(); + + /** + * @brief getPort + * @return returns the port name of the device + */ + QString getPort(); + + /** + * @brief getQueue + * @return the lock free queue of the device + */ + LFQueue& getQueue(); + + /** + * @brief getType + * @return the @ref CANCon::type of the device + */ + CANCon::type getType(); + + /** + * @brief getStatus + * @return the @ref CANCon::status of the device (either connected or not) + */ + CANCon::status getStatus(); + + +signals: + /*not implemented yet */ + void error(const QString &); + void deviceInfo(int, int); //First param = driver version (or version of whatever you want), second param a status byte + + //bus number, bus speed, status (bit 0 = enabled, 1 = single wire, 2 = listen only) + //3 = Use value stored for enabled, 4 = use value passed for single wire, 5 = use value passed for listen only + //6 = use value passed for speed. This allows bus status to be updated but set that some things aren't really + //being passed. Just set for things that really are being updated. + void busStatus(int, int, int); + + /** + * @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); + + +public slots: + + /** + * @brief start the device. This calls piStarted + * @note starts the working thread if required (piStarted is called in the working thread context) + */ + void start(); + + /** + * @brief stop the device. This calls piStop + * @note if a working thread is used, piStop is called before exiting the working thread + */ + void stop(); + + /** + * @brief setBusSettings + * @param pBusIdx: the index of the bus for which settings have to be set + * @param pBus: the settings to set + * @note this calls piSetBusSettings in the working thread context (if one has been started) + */ + void setBusSettings(int pBusIdx, CANBus pBus); + + /** + * @brief getBusSettings + * @param pBusIdx: the index of the bus for which settings have to be retrieved + * @param pBus: the CANBus struct to fill with information + * @return true if operation succeeds, false if pBusIdx is invalid or bus has not been configured yet + * @note this calls piGetBusSettings in the working thread context (if one has been started) + */ + bool getBusSettings(int pBusIdx, CANBus& pBus); + + /** + * @brief suspends/restarts data capture + * @param pSuspend: suspends capture if true else restarts it + * @note this calls piSuspend (in the working thread context if one has been started) + * @note the caller shall not access the queue when capture is suspended, it is then safe for callee to flush the queue + */ + void suspend(bool pSuspend); + + /** + * @brief provides device with the frame to send + * @param pFrame: the frame to send + * @note this calls piSendFrame (in the working thread context if one has been started) + */ + void sendFrame(const CANFrame& pFrame); + + /** + * @brief provides device with a list of frames to send + * @param pFrame: the list of frames to send + * @note this calls piSendFrameBatch (in the working thread context if one has been started) + */ + void sendFrameBatch(const QList& pFrames); + +protected: + + /** + * @brief setStatus + * @param pStatus: the status to set + */ + void setStatus(CANCon::status pStatus); + + /** + * @brief isConfigured + * @param pBusId + * @return true if bus is configured + */ + bool isConfigured(int pBusId); + + /** + * @brief setConfigured + * @param pBusId + * @param pConfigured + * @note it is not necessary to call this function to set pBusId configured, it is enough to call @ref setBusConfig + */ + void setConfigured(int pBusId, bool pConfigured); + + /** + * @brief getBusConfig + * @param pBusId + * @param pBus + * @return true if operation succeeds, false if pBusIdx is invalid or bus has not been configured yet + */ + bool getBusConfig(int pBusId, CANBus& pBus); + + /** + * @brief setBusConfig + * @param pBusId: the index of the bus for which settings have to be set + * @param pBus: the settings to set + */ + void setBusConfig(int pBusId, CANBus& pBus); + + /** + * @brief isCapSuspended + * @return true if the capture is suspended + */ + bool isCapSuspended(); + + /** + * @brief setCapSuspended + * @param pIsSuspended + */ + void setCapSuspended(bool pIsSuspended); + +protected: + + /**************************************************************/ + /*********** protected interface to implement *******/ + /**************************************************************/ + + /** + * @brief starts the device + */ + virtual void piStarted() = 0; + + /** + * @brief stops the device + */ + virtual void piStop() = 0; + + /** + * @brief piSetBusSettings + * @param pBusIdx: the index of the bus for which settings have to be set + * @param pBus: the settings to set + */ + virtual void piSetBusSettings(int pBusIdx, CANBus pBus) = 0; + + /** + * @brief piGetBusSettings + * @param pBusIdx: the index of the bus for which settings have to be retrieved + * @param pBus: the CANBus struct to fill with information + * @return true if operation succeeds, false if pBusIdx is invalid or bus has not been configured yet + */ + virtual bool piGetBusSettings(int pBusIdx, CANBus& pBus) = 0; + + /** + * @brief suspends/restarts data capture + * @param pSuspend: suspends capture if true else restarts it + * @note the caller will not access the queue when capture is suspended, so it is safe for callee to flush the queue + */ + virtual void piSuspend(bool pSuspend) = 0; + + /** + * @brief provides device with the frame to send + * @param pFrame: the frame to send + */ + virtual void piSendFrame(const CANFrame&) = 0; + + /** + * @brief provides device with a list of frames to send + * @param pFrame: the list of frames to send + */ + virtual void piSendFrameBatch(const QList&) = 0; + + +private: + CANBus* mBus; + bool* mConfigured; + LFQueue mQueue; + const int mNumBuses; + const QString mPort; + const CANCon::type mType; + bool mIsCapSuspended; + QAtomicInt mStatus; + QThread* mThread_p; +}; + +#endif // CANCONNECTION_H diff --git a/connections/gvretserial.cpp b/connections/gvretserial.cpp new file mode 100644 index 0000000..1d34f38 --- /dev/null +++ b/connections/gvretserial.cpp @@ -0,0 +1,578 @@ +#include +#include +#include +#include +#include + +#include "gvretserial.h" + +GVRetSerial::GVRetSerial(QString portName) : + CANConnection(portName, CANCon::GVRET_SERIAL, 2, 4000, true), + mTimer(this) /*NB: set this as parent of timer to manage it from working thread */ +{ + qDebug() << "GVRetSerial()"; + + serial = NULL; + rx_state = IDLE; + rx_step = 0; + gotValidated = true; + isAutoRestart = false; + + txTimestampBasis = QDateTime::currentMSecsSinceEpoch(); + + readSettings(); +} + + +GVRetSerial::~GVRetSerial() +{ + stop(); + qDebug() << "~GVRetSerial()"; +} + + +void GVRetSerial::piStarted() +{ + connectDevice(); + + /* start timer */ + connect(&mTimer, SIGNAL(timeout()), this, SLOT(handleTick())); + mTimer.setInterval(250); //tick four times per second + mTimer.setSingleShot(false); //keep ticking + mTimer.start(); +} + + +void GVRetSerial::piSuspend(bool pSuspend) +{ + /* update capSuspended */ + setCapSuspended(pSuspend); + + /* flush queue if we are suspended */ + if(isCapSuspended()) + getQueue().flush(); +} + + +void GVRetSerial::piStop() +{ + mTimer.stop(); + disconnectDevice(); +} + + +bool GVRetSerial::piGetBusSettings(int pBusIdx, CANBus& pBus) +{ + return getBusConfig(pBusIdx, pBus); +} + + +void GVRetSerial::piSetBusSettings(int pBusIdx, CANBus bus) +{ + /* sanity checks */ + if( (pBusIdx < 0) || pBusIdx >= getNumBuses()) + return; + + /* copy bus config */ + setBusConfig(pBusIdx, bus); + + qDebug() << "About to update bus " << pBusIdx << " on GVRET"; + if (pBusIdx == 0) + { + can0Baud = bus.getSpeed(); + can0Baud |= 0x80000000; + if (bus.isActive()) + { + can0Baud |= 0x40000000; + can0Enabled = true; + } + else can0Enabled = false; + + if (bus.isListenOnly()) + { + can0Baud |= 0x20000000; + can0ListenOnly = true; + } + else can0ListenOnly = false; + } + else if (pBusIdx == 1) + { + can1Baud = bus.getSpeed(); + can1Baud |= 0x80000000; + if (bus.isActive()) + { + can1Baud |= 0x40000000; + can1Enabled = true; + } + else can1Enabled = false; + + if (bus.isListenOnly()) + { + can1Baud |= 0x20000000; + can1ListenOnly = true; + } + else can1ListenOnly = false; + + if (bus.isSingleWire()) + { + can1Baud |= 0x10000000; + deviceSingleWireMode = 1; + } + else deviceSingleWireMode = 0; + } + + /* update baud rates */ + QByteArray buffer; + qDebug() << "Got signal to update bauds. 1: " << can0Baud <<" 2: " << 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); +} + + +void GVRetSerial::piSendFrame(const CANFrame&) {} +void GVRetSerial::piSendFrameBatch(const QList&){} + + +/****************************************************************/ + +void GVRetSerial::readSettings() +{ + QSettings settings; + + if (settings.value("Main/ValidateComm", true).toBool()) + { + doValidation = true; + } + else doValidation = false; +} + + +void GVRetSerial::connectDevice() +{ + QSettings settings; + + /* disconnect device */ + if(serial) + disconnectDevice(); + + /* open new device */ + serial = new QSerialPort(QSerialPortInfo(getPort())); + if(!serial) { + qDebug() << "can't open serial port " << getPort(); + return; + } + + /* configure */ + serial->setDataBits(serial->Data8); + serial->setFlowControl(serial->HardwareControl); //this is important though + if (!serial->open(QIODevice::ReadWrite)) + { + qDebug() << serial->errorString(); + } + serial->setDataTerminalReady(true); //you do need to set these or the fan gets dirty + serial->setRequestToSend(true); + + + QByteArray output; + output.append((char)0xE7); //this puts the device into binary comm mode + output.append((char)0xE7); + + output.append((char)0xF1); //signal we want to issue a command + output.append((char)0x06); //request canbus stats from the board + + output.append((char)0xF1); //another command to the GVRET + output.append((char)0x07); //request device information + + output.append((char)0xF1); + output.append((char)0x08); //setting singlewire mode + if (settings.value("Main/SingleWireMode", false).toBool()) + { + output.append((char)0x10); //signal that we do want single wire mode + } + else + { + 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 + + continuousTimeSync = true; + + serial->write(output); + + if(doValidation) { + QTimer::singleShot(1000, this, SLOT(connectionTimeout())); + } + else { + setStatus(CANCon::CONNECTED); + emit status(getStatus()); + } + + /* connect reading event */ + connect(serial, SIGNAL(readyRead()), this, SLOT(readSerialData())); +} + + +void GVRetSerial::disconnectDevice() { + if (serial != NULL) + { + if (serial->isOpen()) + { + serial->clear(); + serial->close(); + + } + serial->disconnect(); //disconnect all signals + delete serial; + serial = NULL; + } +} + + +void GVRetSerial::connectionTimeout() +{ + //one second after trying to connect are we actually connected? + if (CANCon::NOT_CONNECTED==getStatus()) //no? + { + //then emit the the failure signal and see if anyone cares + qDebug() << "Failed to connect to GVRET at that com port"; + + disconnectDevice(); + } +} + + +void GVRetSerial::readSerialData() +{ + QByteArray data = serial->readAll(); + unsigned char c; + //qDebug() << (tr("Got data from serial. Len = %0").arg(data.length())); + for (int i = 0; i < data.length(); i++) + { + c = data.at(i); + procRXChar(c); + } +} + + +void GVRetSerial::procRXChar(unsigned char c) +{ + switch (rx_state) + { + case IDLE: + if (c == 0xF1) rx_state = GET_COMMAND; + break; + case GET_COMMAND: + switch (c) + { + case 0: //receiving a can frame + rx_state = BUILD_CAN_FRAME; + rx_step = 0; + break; + case 1: //time sync + rx_state = TIME_SYNC; + rx_step = 0; + break; + case 2: //process a return reply for digital input states. + rx_state = GET_DIG_INPUTS; + rx_step = 0; + break; + case 3: //process a return reply for analog inputs + rx_state = GET_ANALOG_INPUTS; + break; + case 4: //we set digital outputs we don't accept replies so nothing here. + rx_state = IDLE; + break; + case 5: //we set canbus specs we don't accept replies. + rx_state = IDLE; + break; + case 6: //get canbus parameters from GVRET + rx_state = GET_CANBUS_PARAMS; + rx_step = 0; + break; + case 7: //get device info + rx_state = GET_DEVICE_INFO; + rx_step = 0; + break; + case 9: + gotValidated = true; + //qDebug() << "Got validated"; + rx_state = IDLE; + break; + } + break; + case BUILD_CAN_FRAME: + switch (rx_step) + { + case 0: + buildFrame.timestamp = c; + break; + case 1: + buildFrame.timestamp |= (uint)(c << 8); + break; + case 2: + buildFrame.timestamp |= (uint)c << 16; + break; + case 3: + buildFrame.timestamp |= (uint)c << 24; + break; + case 4: + buildFrame.ID = c; + break; + case 5: + buildFrame.ID |= c << 8; + break; + case 6: + buildFrame.ID |= c << 16; + break; + case 7: + buildFrame.ID |= c << 24; + if ((buildFrame.ID & 1 << 31) == 1 << 31) + { + buildFrame.ID &= 0x7FFFFFFF; + buildFrame.extended = true; + } + else buildFrame.extended = false; + break; + case 8: + buildFrame.len = c & 0xF; + if (buildFrame.len > 8) buildFrame.len = 8; + buildFrame.bus = (c & 0xF0) >> 4; + break; + default: + if (rx_step < buildFrame.len + 9) + { + buildFrame.data[rx_step - 9] = c; + } + else + { + rx_state = IDLE; + rx_step = 0; + buildFrame.isReceived = true; + + if (!isCapSuspended()) + { + /* get frame from queue */ + CANFrame* frame_p = getQueue().get(); + if(frame_p) { + /* copy frame */ + *frame_p = buildFrame; + /* enqueue frame */ + getQueue().queue(); + } + else + qDebug() << "can't get a frame, ERROR"; + + //take the time the frame came in and try to resync the time base. + if (continuousTimeSync) txTimestampBasis = QDateTime::currentMSecsSinceEpoch() - (buildFrame.timestamp / 1000); + } + } + break; + } + rx_step++; + break; + case TIME_SYNC: //gives a pretty good base guess for the proper timestamp. Can be refined when traffic starts to flow (if wanted) + switch (rx_step) + { + case 0: + buildTimeBasis = c; + break; + case 1: + buildTimeBasis += ((uint32_t)c << 8); + break; + case 2: + buildTimeBasis += ((uint32_t)c << 16); + break; + case 3: + buildTimeBasis += ((uint32_t)c << 24); + qDebug() << "GVRET firmware reports timestamp of " << buildTimeBasis; + txTimestampBasis = QDateTime::currentMSecsSinceEpoch() - ((uint64_t)buildTimeBasis / (uint64_t)1000ull); + continuousTimeSync = false; + rx_state = IDLE; + break; + } + rx_step++; + break; + + case GET_ANALOG_INPUTS: //get 9 bytes - 2 per analog input plus checksum + switch (rx_step) + { + case 0: + break; + } + rx_step++; + break; + case GET_DIG_INPUTS: //get two bytes. One for digital in status and one for checksum. + switch (rx_step) + { + case 0: + break; + case 1: + rx_state = IDLE; + break; + } + rx_step++; + break; + case GET_CANBUS_PARAMS: + switch (rx_step) + { + case 0: + can0Enabled = (c & 0xF); + can0ListenOnly = (c >> 4); + break; + case 1: + can0Baud = c; + break; + case 2: + can0Baud |= c << 8; + break; + case 3: + can0Baud |= c << 16; + break; + case 4: + can0Baud |= c << 24; + break; + case 5: + can1Enabled = (c & 0xF); + can1ListenOnly = (c >> 4); + deviceSingleWireMode = (c >> 6); + break; + case 6: + can1Baud = c; + break; + case 7: + can1Baud |= c << 8; + break; + case 8: + can1Baud |= c << 16; + break; + case 9: + can1Baud |= c << 24; + rx_state = IDLE; + qDebug() << "Baud 0 = " << can0Baud; + qDebug() << "Baud 1 = " << can1Baud; + + can0Baud |= 0x80000000; + if (can0Enabled) can0Baud |= 0x40000000; + if (can0ListenOnly) can0Baud |= 0x20000000; + + can1Baud |= 0x80000000; + if (can1Enabled) can1Baud |= 0x40000000; + if (can1ListenOnly) can1Baud |= 0x20000000; + if (deviceSingleWireMode > 0) can1Baud |= 0x10000000; + + setStatus(CANCon::CONNECTED); + emit status(getStatus()); + + int can0Status = 0x78; //updating everything we can update + int can1Status = 0x78; + if (can0Enabled) can0Status +=1; + if (can0ListenOnly) can0Status += 4; + if (can1Enabled) can1Status += 1; + if (deviceSingleWireMode > 0) can1Status += 2; + if (can1ListenOnly) can1Status += 4; + //emit busStatus(busBase, can0Baud & 0xFFFFF, can0Status); + //emit busStatus(busBase + 1, can1Baud & 0xFFFFF, can1Status); + break; + } + rx_step++; + break; + case GET_DEVICE_INFO: + switch (rx_step) + { + case 0: + deviceBuildNum = c; + break; + case 1: + deviceBuildNum |= c << 8; + break; + case 2: + break; //don't care about eeprom version + case 3: + break; //don't care about file type + case 4: + break; //don't care about whether it auto logs or not + case 5: + deviceSingleWireMode = c; + rx_state = IDLE; + qDebug() << "build num: " << deviceBuildNum; + qDebug() << "single wire can: " << deviceSingleWireMode; + emit deviceInfo(deviceBuildNum, deviceSingleWireMode); + break; + } + rx_step++; + break; + case SET_DIG_OUTPUTS: + rx_state = IDLE; + break; + case SETUP_CANBUS: + rx_state = IDLE; + break; + case SET_SINGLEWIRE_MODE: + rx_state = IDLE; + break; + + } +} + + +void GVRetSerial::handleTick() +{ + //qDebug() << "Tick!"; + + if( CANCon::CONNECTED == getStatus() ) + { + if (!gotValidated && doValidation) + { + if (serial == NULL) return; + if (serial->isOpen()) //if it's still false we have a problem... + { + qDebug() << "Comm validation failed. "; + + setStatus(CANCon::NOT_CONNECTED); + emit status(getStatus()); + + disconnectDevice(); //start by stopping everything. + //Then wait 500ms and restart the connection automatically + QTimer::singleShot(500, this, SLOT(connectDevice())); + return; + } + } + } + + if (doValidation && serial && serial->isOpen()) sendCommValidation(); +} + + +void GVRetSerial::sendCommValidation() +{ + QByteArray output; + + gotValidated = false; + output.append((char)0xF1); //another command to the GVRET + output.append((char)0x09); //request a reply to get validation + //send it twice for good measure. + output.append((char)0xF1); //another command to the GVRET + output.append((char)0x09); //request a reply to get validation + + serial->write(output); +} + + + diff --git a/connections/gvretserial.h b/connections/gvretserial.h new file mode 100644 index 0000000..350277f --- /dev/null +++ b/connections/gvretserial.h @@ -0,0 +1,105 @@ +#ifndef GVRETSERIAL_H +#define GVRETSERIAL_H + +#include +#include +#include +#include + +/*************/ +#include +/*************/ + + +#include "canframemodel.h" +#include "canconnection.h" + + +namespace SERIALSTATE { + +enum STATE //keep this enum synchronized with the Arduino firmware project +{ + IDLE, + GET_COMMAND, + BUILD_CAN_FRAME, + TIME_SYNC, + GET_DIG_INPUTS, + GET_ANALOG_INPUTS, + SET_DIG_OUTPUTS, + SETUP_CANBUS, + GET_CANBUS_PARAMS, + GET_DEVICE_INFO, + SET_SINGLEWIRE_MODE +}; + +} + + +using namespace SERIALSTATE; +class GVRetSerial : public CANConnection +{ + Q_OBJECT + +public: + GVRetSerial(QString portName); + virtual ~GVRetSerial(); + +signals: + void error(const QString &); + + void status(CANCon::status); + void deviceInfo(int, int); //First param = driver version (or version of whatever you want), second param a status byte + + //bus number, bus speed, status (bit 0 = enabled, 1 = single wire, 2 = listen only) + //3 = Use value stored for enabled, 4 = use value passed for single wire, 5 = use value passed for listen only + //6 = use value passed for speed. This allows bus status to be updated but set that some things aren't really + //being passed. Just set for things that really are being updated. + void busStatus(int, int, int); + +protected: + + virtual void piStarted(); + virtual void piStop(); + virtual void piSetBusSettings(int pBusIdx, CANBus pBus); + virtual bool piGetBusSettings(int pBusIdx, CANBus& pBus); + virtual void piSuspend(bool pSuspend); + virtual void piSendFrame(const CANFrame&) ; + virtual void piSendFrameBatch(const QList&); + + void disconnectDevice(); + +private slots: + void connectDevice(); + void connectionTimeout(); + void readSerialData(); + void handleTick(); + +private: + void readSettings(); + void procRXChar(unsigned char); + void sendCommValidation(); + +protected: + QTimer mTimer; + QThread mThread; + + + bool doValidation; + bool gotValidated; + bool isAutoRestart; + bool continuousTimeSync; + QSerialPort *serial; + int framesRapid; + STATE rx_state; + int rx_step; + CANFrame buildFrame; + int can0Baud, can1Baud; + bool can0Enabled, can1Enabled; + bool can0ListenOnly, can1ListenOnly; + int deviceBuildNum; + int deviceSingleWireMode; + uint64_t txTimestampBasis; + uint32_t buildTimeBasis; +}; + +#endif // GVRETSERIAL_H diff --git a/connections/socketcan.cpp b/connections/socketcan.cpp new file mode 100644 index 0000000..16fff23 --- /dev/null +++ b/connections/socketcan.cpp @@ -0,0 +1,223 @@ +#include +#include +#include + +#include "socketcan.h" + + + +/***********************************/ +/**** class definition ****/ +/***********************************/ + +SocketCan::SocketCan(QString portName) : + CANConnection(portName, CANCon::SOCKETCAN, 1, 4000, true), + mDev_p(NULL), + mTimer(this) /*NB: set connection as parent of timer to manage it from working thread */ +{ + qDebug() << "SocketCan()"; +} + + +SocketCan::~SocketCan() +{ + stop(); + qDebug() << "~SocketCan()"; +} + + +void SocketCan::piStarted() +{ + connect(&mTimer, SIGNAL(timeout()), this, SLOT(testConnection())); + mTimer.setInterval(1000); + mTimer.setSingleShot(false); //keep ticking + mTimer.start(); +} + + +void SocketCan::piSuspend(bool pSuspend) +{ + /* update capSuspended */ + setCapSuspended(pSuspend); + + /* flush queue if we are suspended */ + if(isCapSuspended()) + getQueue().flush(); +} + + +void SocketCan::piStop() { + mTimer.stop(); + disconnectDevice(); +} + + +bool SocketCan::piGetBusSettings(int pBusIdx, CANBus& pBus) +{ + return getBusConfig(pBusIdx, pBus); +} + + +void SocketCan::piSetBusSettings(int pBusIdx, CANBus bus) +{ + /* sanity checks */ + if(0 != pBusIdx) + return; + + /* disconnect device if we have one connected */ + if(mDev_p) + disconnectDevice(); + + /* copy bus config */ + setBusConfig(0, bus); + + /* if bus is not active we are done */ + if(!bus.active) + return; + + /* create device */ + mDev_p = QCanBus::instance()->createDevice("socketcan", getPort()); + if (!mDev_p) { + disconnectDevice(); + qDebug() << "can't create device"; + return; + } + + /* connect slots */ + connect(mDev_p, &QCanBusDevice::errorOccurred, this, &SocketCan::errorReceived); + connect(mDev_p, &QCanBusDevice::framesWritten, this, &SocketCan::framesWritten); + connect(mDev_p, &QCanBusDevice::framesReceived, this, &SocketCan::framesReceived); + + /* set configuration */ + /*if (p.useConfigurationEnabled) { + foreach (const SettingsDialog::ConfigurationItem &item, p.configurations) + mDev->setConfigurationParameter(item.first, item.second); + }*/ + + /* connect device */ + if (!mDev_p->connectDevice()) { + disconnectDevice(); + qDebug() << "can't connect device"; + } +} + + +void SocketCan::piSendFrame(const CANFrame&) {} +void SocketCan::piSendFrameBatch(const QList&){} + + +/***********************************/ +/**** private methods ****/ +/***********************************/ + + +/* disconnect device */ +void SocketCan::disconnectDevice() { + if(mDev_p) { + mDev_p->disconnectDevice(); + delete mDev_p; + mDev_p = Q_NULLPTR; + } +} + + +void SocketCan::errorReceived(QCanBusDevice::CanBusError error) const +{ + switch (error) { + case QCanBusDevice::ReadError: + case QCanBusDevice::WriteError: + case QCanBusDevice::ConnectionError: + case QCanBusDevice::ConfigurationError: + case QCanBusDevice::UnknownError: + qWarning() << mDev_p->errorString(); + default: + break; + } +} + +void SocketCan::framesWritten(qint64 count) +{ + qDebug() << "Number of frames written:" << count; +} + +void SocketCan::framesReceived() +{ + /* sanity checks */ + if(!mDev_p) + return; + + /* read frame */ + while(true) + { + const QCanBusFrame recFrame = mDev_p->readFrame(); + + /* exit case */ + if(!recFrame.isValid()) + return; + + /* drop frame if capture is suspended */ + if(isCapSuspended()) + continue; + + /* check frame */ + if(!recFrame.payload().isEmpty() && + recFrame.payload().length()<=8) + { + CANFrame* frame_p = getQueue().get(); + if(frame_p) { + frame_p->len = recFrame.payload().length(); + frame_p->bus = 0; + memcpy(frame_p->data, recFrame.payload().data(), frame_p->len); + frame_p->extended = false; + frame_p->ID = recFrame.frameId(); + frame_p->isReceived = true; + frame_p->timestamp = recFrame.timeStamp().seconds()*1000000 + recFrame.timeStamp().microSeconds(); + + /* enqueue frame */ + getQueue().queue(); + } + else + qDebug() << "can't get a frame, ERROR"; + } + else { + qDebug() << "invalid frame"; + } + } +} + + +void SocketCan::testConnection() { + QCanBusDevice* dev_p = QCanBus::instance()->createDevice("socketcan", getPort()); + + switch(getStatus()) + { + case CANCon::CONNECTED: + if (!dev_p || !dev_p->connectDevice()) { + /* we have lost connectivity */ + disconnectDevice(); + + setStatus(CANCon::NOT_CONNECTED); + emit status(getStatus()); + } + break; + case CANCon::NOT_CONNECTED: + if (dev_p && dev_p->connectDevice()) { + if(!mDev_p) { + /* try to reconnect */ + CANBus bus; + if(getBusConfig(0, bus)) + setBusSettings(0, bus); + } + /* disconnect test instance */ + dev_p->disconnectDevice(); + + setStatus(CANCon::CONNECTED); + emit status(getStatus()); + } + break; + default: {} + } + + if(dev_p) + delete dev_p; +} diff --git a/connections/socketcan.h b/connections/socketcan.h new file mode 100644 index 0000000..dd1877b --- /dev/null +++ b/connections/socketcan.h @@ -0,0 +1,57 @@ +#ifndef SocketCan_H +#define SocketCan_H + +#include +#include +#include +#include + +#include "canframemodel.h" +#include "canconnection.h" + + +class SocketCan : public CANConnection +{ + Q_OBJECT + +public: + SocketCan(QString portName); + virtual ~SocketCan(); + +signals: + void error(const QString &); + + void status(CANCon::status); + void deviceInfo(int, int); //First param = driver version (or version of whatever you want), second param a status byte + + //bus number, bus speed, status (bit 0 = enabled, 1 = single wire, 2 = listen only) + //3 = Use value stored for enabled, 4 = use value passed for single wire, 5 = use value passed for listen only + //6 = use value passed for speed. This allows bus status to be updated but set that some things aren't really + //being passed. Just set for things that really are being updated. + void busStatus(int, int, int); + +protected: + + virtual void piStarted(); + virtual void piStop(); + virtual void piSetBusSettings(int pBusIdx, CANBus pBus); + virtual bool piGetBusSettings(int pBusIdx, CANBus& pBus); + virtual void piSuspend(bool pSuspend); + virtual void piSendFrame(const CANFrame&) ; + virtual void piSendFrameBatch(const QList&); + + void disconnectDevice(); + +private slots: + void errorReceived(QCanBusDevice::CanBusError) const; + void framesWritten(qint64 count); + void framesReceived(); + void testConnection(); + +protected: + QCanBusDevice* mDev_p; + QTimer mTimer; +}; + + +#endif // SocketCan_H diff --git a/connectionwindow.cpp b/connectionwindow.cpp index 1ee5c74..d9237e6 100644 --- a/connectionwindow.cpp +++ b/connectionwindow.cpp @@ -1,6 +1,9 @@ +#include + #include "connectionwindow.h" #include "ui_connectionwindow.h" -#include +#include "connections/canconfactory.h" + ConnectionWindow::ConnectionWindow(CANFrameModel *cModel, QWidget *parent) : QDialog(parent), @@ -10,7 +13,7 @@ ConnectionWindow::ConnectionWindow(CANFrameModel *cModel, QWidget *parent) : settings = new QSettings(); - qRegisterMetaType("CAN_Bus *"); + qRegisterMetaType("CANBus"); qRegisterMetaType("const CANFrame *"); qRegisterMetaType *>("const QList *"); @@ -22,10 +25,11 @@ ConnectionWindow::ConnectionWindow(CANFrameModel *cModel, QWidget *parent) : ui->tableConnections->setColumnWidth(0, 50); ui->tableConnections->setColumnWidth(1, 110); ui->tableConnections->setColumnWidth(2, 110); - ui->tableConnections->setColumnWidth(3, 150); + 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); int temp = settings->value("Main/DefaultConnectionType", 0).toInt(); @@ -36,7 +40,6 @@ ConnectionWindow::ConnectionWindow(CANFrameModel *cModel, QWidget *parent) : ui->ckSingleWire->setChecked(settings->value("Main/SingleWireMode", false).toBool()); ui->cbSpeed->addItem(tr("")); - ui->cbSpeed->addItem(tr("Disabled")); ui->cbSpeed->addItem(tr("125000")); ui->cbSpeed->addItem(tr("250000")); ui->cbSpeed->addItem(tr("500000")); @@ -44,7 +47,7 @@ ConnectionWindow::ConnectionWindow(CANFrameModel *cModel, QWidget *parent) : ui->cbSpeed->addItem(tr("33333")); #ifdef Q_OS_LINUX - ui->rbSocketCAN->setEnabled(true); + ui->rbSocketCAN->setEnabled(isSocketCanAvailable()); #endif #ifdef Q_OS_WIN @@ -57,14 +60,38 @@ ConnectionWindow::ConnectionWindow(CANFrameModel *cModel, QWidget *parent) : connect(ui->rbSocketCAN, &QAbstractButton::toggled, this, &ConnectionWindow::handleConnTypeChanged); connect(ui->btnRevert, &QPushButton::clicked, this, &ConnectionWindow::handleRevert); connect(ui->tableConnections->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ConnectionWindow::handleConnSelectionChanged); + connect(connModel, &QAbstractItemModel::modelReset, this, &ConnectionWindow::handleConnSelectionChanged); 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); + + + qDebug() << "Serial worker thread starting"; + + connect(&mTicker, SIGNAL(timeout()), this, SLOT(refreshCanList())); + /* tick frequency has a huge impact on performances */ + /* TODO: make this configurable and part of the connection constructor to let connection configure the length of the queue */ + mTicker.setInterval(500); /*tick twice a second */ + mTicker.setSingleShot(false); + mTicker.start(); } ConnectionWindow::~ConnectionWindow() { + QList& conns = connModel->getConnections(); + CANConnection* conn_p; + + /* delete connections */ + while(!conns.isEmpty()) { + conn_p = conns.takeFirst(); + conn_p->stop(); + delete conn_p; + } + + delete connModel; + + mTicker.stop(); delete settings; delete ui; } @@ -76,6 +103,20 @@ void ConnectionWindow::showEvent(QShowEvent* event) handleConnTypeChanged(); } +void ConnectionWindow::refreshCanList() { + QList::iterator conn_p; + QList& conns = connModel->getConnections(); + + CANFrame* frame_p = NULL; + + for (conn_p = conns.begin(); conn_p != conns.end(); ++conn_p) { + while( (frame_p = (*conn_p)->getQueue().peek() ) ) { + canModel->addFrame(*frame_p, true); + (*conn_p)->getQueue().dequeue(); + } + } +} + void ConnectionWindow::handleNewConn() { ui->tableConnections->selectionModel()->clearSelection(); @@ -83,100 +124,113 @@ void ConnectionWindow::handleNewConn() handleConnSelectionChanged(); } +void ConnectionWindow::setSuspendAll(bool pSuspend) +{ + qDebug() << "setSuspendAll"; + + QList::iterator iter; + QList& conns = connModel->getConnections(); + + for (iter = conns.begin(); iter != conns.end(); ++iter) + (*iter)->suspend(pSuspend); +} + +void ConnectionWindow::setActiveAll(bool pActive) +{ + QList::iterator iter; + QList& conns = connModel->getConnections(); + CANBus bus; + + for (iter = conns.begin(); iter != conns.end(); ++iter) { + for(int i=0 ; i<(*iter)->getNumBuses() ; i++) { + if( (*iter)->getBusSettings(i, bus) ) { + bus.active = pActive; + (*iter)->setBusSettings(i, bus); + } + } + } +} + void ConnectionWindow::handleEnableAll() { - + setActiveAll(true); } void ConnectionWindow::handleDisableAll() { - + setActiveAll(false); } void ConnectionWindow::handleConnTypeChanged() { - if (ui->rbGVRET->isChecked()) getSerialPorts(); - if (ui->rbKvaser->isChecked()) getKvaserPorts(); - if (ui->rbSocketCAN->isChecked()) getSocketcanPorts(); + if (ui->rbGVRET->isChecked()) selectSerial(); + if (ui->rbKvaser->isChecked()) selectKvaser(); + if (ui->rbSocketCAN->isChecked()) selectSocketCan(); } -void ConnectionWindow::connectionSuccess(CANConnection *conn) + +/* status */ +void ConnectionWindow::connectionStatus(CANCon::status pStatus) { - CAN_Bus bus; - bus.active = true; - bus.busNum = conn->getBusBase(); - bus.connection = conn; - bus.listenOnly = ui->ckListenOnly->isChecked(); - bus.singleWire = ui->ckSingleWire->isChecked(); - - if (ui->cbSpeed->currentIndex() < 1) bus.speed = 0; //default speed - else if (ui->cbSpeed->currentIndex() == 1) - { - bus.speed = 0; - bus.active = false; - } - else bus.speed = ui->cbSpeed->currentText().toInt(); - connModel->addBus(bus); - - int numBuses = conn->getNumBuses(); - for (int i = 1; i < numBuses; i++) - { - bus.active = false; - bus.listenOnly = false; - bus.singleWire = false; - bus.speed = 250000; - bus.busNum = conn->getBusBase() + i; - bus.connection = conn; - connModel->addBus(bus); - qDebug() << "Added bus " << bus.busNum; - } + qDebug() << "Connectionstatus changed"; + connModel->refreshView(); } + void ConnectionWindow::handleOKButton() { - QString conn; - int connType = 0; int whichRow = ui->tableConnections->selectionModel()->currentIndex().row(); - qDebug() << "OK pressed. Row " << whichRow; + + CANConnection* conn_p = NULL; if (whichRow > -1) { - //load settings from GUI into appropriate CAN_Bus entry and then send it off to the appropriate - //CANConnection object - CAN_Bus *bus = connModel->getBus(whichRow); - bus->setListenOnly(ui->ckListenOnly->isChecked()); - bus->setSingleWire(ui->ckSingleWire->isChecked()); - bus->setEnabled(ui->ckEnabled->isChecked()); - if (ui->cbSpeed->currentIndex() == 1) + 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()); + if (ui->cbSpeed->currentIndex() == 0) { - bus->speed = 0; - bus->setEnabled(false); + bus.speed = 0; + bus.setEnabled(false); } - else if (ui->cbSpeed->currentIndex() > 1) + else if (ui->cbSpeed->currentIndex() >= 1) { - bus->setSpeed(ui->cbSpeed->currentText().toInt()); + bus.setSpeed(ui->cbSpeed->currentText().toInt()); } - //call through signal/slot interface without using connect - QMetaObject::invokeMethod(bus->connection, "updateBusSettings", - Qt::QueuedConnection, - Q_ARG(CAN_Bus *, bus)); + /* update bus settings */ + conn_p->setBusSettings(busId, bus); + connModel->refreshView(); } else //new connection { +#if 0 if (ui->rbGVRET->isChecked()) - { + { + SerialWorker *serial = new SerialWorker(canModel, connModel->rowCount()); connect(serial, SIGNAL(busStatus(int,int,int)), this, SLOT(receiveBusStatus(int,int,int))); connect(serial, SIGNAL(connectionSuccess(CANConnection*)), this, SLOT(connectionSuccess(CANConnection*))); - connModel->addConnection(serial); + CANConnectionContainer* container = new CANConnectionContainer(serial); + qDebug() << "Setup initial connection object"; - CAN_Bus bus; + CANBus bus; bus.active = ui->ckEnabled->isChecked(); bus.busNum = serial->getBusBase(); - bus.connection = serial; + bus.container = container; bus.listenOnly = ui->ckListenOnly->isChecked(); bus.singleWire = ui->ckSingleWire->isChecked(); @@ -187,6 +241,7 @@ void ConnectionWindow::handleOKButton() bus.active = false; } else bus.speed = ui->cbSpeed->currentText().toInt(); + connModel->addBus(bus); int numBuses = serial->getNumBuses(); @@ -197,7 +252,7 @@ void ConnectionWindow::handleOKButton() bus.singleWire = false; bus.speed = 250000; bus.busNum = serial->getBusBase() + i; - bus.connection = serial; + bus.container = container; connModel->addBus(bus); qDebug() << "Added bus " << bus.busNum; } @@ -206,22 +261,45 @@ void ConnectionWindow::handleOKButton() QMetaObject::invokeMethod(serial, "updatePortName", Qt::QueuedConnection, Q_ARG(QString, ui->cbPort->currentText())); - } - else if (ui->rbKvaser->isChecked()) - { +#endif - } - else if (ui->rbSocketCAN->isChecked()) - { + /* create connection */ + conn_p = CanConFactory::create(getConnectionType(), getPortName()); + if(!conn_p) + return; + /* connect signal */ + connect(conn_p, SIGNAL(status(CANCon::status)), + this, SLOT(connectionStatus(CANCon::status))); + + /*TODO add return value and checks */ + conn_p->start(); + + 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(); + + if (ui->cbSpeed->currentIndex() < 1) bus.speed = 0; + else bus.speed = ui->cbSpeed->currentText().toInt(); + + /* update bus settings */ + conn_p->setBusSettings(i, bus); } + + /* add connection to model */ + connModel->add(conn_p); } } + void ConnectionWindow::receiveBusStatus(int bus, int speed, int status) { +#if 0 qDebug() << "bus " << bus << " speed " << speed << " status " << status; - CAN_Bus *busRef = connModel->getBus(bus); + CANBus *busRef = connModel->getBus(bus); if (status & 40) busRef->setSpeed(speed); if (status & 8) //update enabled status { @@ -236,19 +314,22 @@ void ConnectionWindow::receiveBusStatus(int bus, int speed, int status) busRef->setListenOnly((status & 4)?true:false); } connModel->refreshView(); +#endif } void ConnectionWindow::handleConnSelectionChanged() { int selIdx = ui->tableConnections->selectionModel()->currentIndex().row(); - qDebug() << "Selection: " << selIdx; if (selIdx == -1) { ui->btnOK->setText(tr("Create New Connection")); + ui->cbPort->setEnabled(true); ui->rbGVRET->setEnabled(true); ui->rbKvaser->setEnabled(true); ui->rbSocketCAN->setEnabled(true); + ui->cbSpeed->setEnabled(false); + ui->cbPort->setCurrentIndex(0); ui->ckListenOnly->setChecked(false); ui->ckSingleWire->setChecked(false); @@ -256,25 +337,43 @@ void ConnectionWindow::handleConnSelectionChanged() } else { + int busId; + bool ret; + CANBus bus; + CANConnection* conn_p = connModel->getAtIdx(selIdx, busId); + if(!conn_p) return; + ret = conn_p->getBusSettings(busId, bus); + if(!ret) return; + ui->btnOK->setText(tr("Update Connection Settings")); + ui->cbPort->setEnabled(false); ui->rbGVRET->setEnabled(false); ui->rbKvaser->setEnabled(false); - ui->rbSocketCAN->setEnabled(false); - CAN_Bus *bus = connModel->getBus(selIdx); - if (bus->connection->getConnTypeName() == "GVRET") ui->rbGVRET->setChecked(true); - if (bus->connection->getConnTypeName() == "KVASER") ui->rbKvaser->setChecked(true); - if (bus->connection->getConnTypeName() == "SOCKETCAN") ui->rbSocketCAN->setChecked(true); - ui->ckListenOnly->setChecked(bus->isListenOnly()); - ui->ckSingleWire->setChecked(bus->isSingleWire()); - ui->ckEnabled->setChecked(bus->isActive()); - int speed = bus->getSpeed(); + ui->rbSocketCAN->setEnabled(false); + ui->cbSpeed->setEnabled(true); + + switch(conn_p->getType()) { + case CANCon::GVRET_SERIAL: ui->rbGVRET->setChecked(true); break; + case CANCon::KVASER: ui->rbKvaser->setChecked(true); break; + case CANCon::SOCKETCAN: ui->rbSocketCAN->setChecked(true); break; + default: {} + } + + ui->ckListenOnly->setChecked(bus.isListenOnly()); + ui->ckSingleWire->setChecked(bus.isSingleWire()); + ui->ckEnabled->setChecked(bus.isActive()); + int speed = bus.getSpeed(); setSpeed(speed); } } -void ConnectionWindow::getSerialPorts() + +void ConnectionWindow::selectSerial() { + /* set combobox page visible */ + ui->stPort->setCurrentWidget(ui->cbPage); + ui->cbPort->clear(); ports = QSerialPortInfo::availablePorts(); @@ -285,28 +384,19 @@ void ConnectionWindow::getSerialPorts() } } -void ConnectionWindow::getKvaserPorts() +void ConnectionWindow::selectKvaser() { - +#ifdef Q_OS_WIN + /* set combobox page visible */ + ui->stPort->setCurrentWidget(ui->cbPage); +#endif } -void ConnectionWindow::getSocketcanPorts() +void ConnectionWindow::selectSocketCan() { #ifdef Q_OS_LINUX - QList interfaces = QNetworkInterface::allInterfaces(); - QString interfaceName; - - ui->cbPort->clear(); - - foreach (QNetworkInterface interface, interfaces) - { - interfaceName = interface.name().toLower(); - qDebug() << "Interface: " << interface.name(); - if (interfaceName.contains("can")) - { - ui->cbPort->addItem(interfaceName); - } - } + /* set edit text page visible */ + ui->stPort->setCurrentWidget(ui->etPage); #endif } @@ -355,16 +445,30 @@ int ConnectionWindow::getSpeed() QString ConnectionWindow::getPortName() { - return (ui->cbPort->currentText()); + switch( getConnectionType() ) { + case CANCon::GVRET_SERIAL: + case CANCon::KVASER: + return ui->cbPort->currentText(); + case CANCon::SOCKETCAN: + return ui->lePort->text(); + default: + qDebug() << "getPortName: can't get port"; + } + + return ""; } -ConnectionType::ConnectionType ConnectionWindow::getConnectionType() +CANCon::type ConnectionWindow::getConnectionType() { - if (ui->rbGVRET->isChecked()) return ConnectionType::GVRET_SERIAL; - if (ui->rbKvaser->isChecked()) return ConnectionType::KVASER; - if (ui->rbSocketCAN->isChecked()) return ConnectionType::SOCKETCAN; + if (ui->rbGVRET->isChecked()) return CANCon::GVRET_SERIAL; + if (ui->rbKvaser->isChecked()) return CANCon::KVASER; + if (ui->rbSocketCAN->isChecked()) return CANCon::SOCKETCAN; + + qDebug() << "getConnectionType: error"; + return CANCon::NONE; } + void ConnectionWindow::setSWMode(bool mode) { ui->ckSingleWire->setChecked(mode); @@ -378,7 +482,21 @@ bool ConnectionWindow::getSWMode() void ConnectionWindow::handleRemoveConn() { + int selIdx = ui->tableConnections->selectionModel()->currentIndex().row(); + if (selIdx <0) return; + qDebug() << "remove connection at index: " << selIdx; + + int busId; + CANConnection* conn_p = connModel->getAtIdx(selIdx, busId); + if(!conn_p) return; + + /* remove connection from model */ + connModel->remove(conn_p); + + /* stop and delete connection */ + conn_p->stop(); + delete conn_p; } void ConnectionWindow::handleRevert() @@ -386,31 +504,37 @@ void ConnectionWindow::handleRevert() } -void ConnectionWindow::handleEnableAll() -{ - -} - -void ConnectionWindow::handleDisableAll() -{ - -} - void ConnectionWindow::sendFrame(const CANFrame *frame) { - CAN_Bus *bus = connModel->getBus(frame->bus); +#if 0 + CANBus *bus = connModel->getBus(frame->bus); if (bus == NULL) return; - QMetaObject::invokeMethod(bus->connection, "sendFrame", + QMetaObject::invokeMethod(bus->getContainer()->getRef(), "sendFrame", Qt::QueuedConnection, Q_ARG(const CANFrame *, frame)); +#endif } void ConnectionWindow::sendFrameBatch(const QList *frames) { +#if 0 if (frames->count() == 0) return; - CAN_Bus *bus = connModel->getBus(frames->at(0).bus); + CANBus *bus = connModel->getBus(frames->at(0).bus); if (bus == NULL) return; - QMetaObject::invokeMethod(bus->connection, "sendFrameBatch", + QMetaObject::invokeMethod(bus->getContainer()->getRef(), "sendFrameBatch", Qt::QueuedConnection, Q_ARG(const QList *, frames)); +#endif +} + +bool ConnectionWindow::isSocketCanAvailable() +{ +#ifdef Q_OS_LINUX + foreach (const QByteArray &backend, QCanBus::instance()->plugins()) { + if (backend == "socketcan") { + return true; + } + } +#endif + return false; } diff --git a/connectionwindow.h b/connectionwindow.h index 6d91216..066cad0 100644 --- a/connectionwindow.h +++ b/connectionwindow.h @@ -1,14 +1,18 @@ #ifndef CONNECTIONWINDOW_H #define CONNECTIONWINDOW_H -#include "canconnectionmodel.h" + #include #include #include #include -#include "canconnection.h" -#include "serialworker.h" +#include +//#include "canconnection_old.h" +//#include "serialworker.h" +#include "canconnectionmodel.h" +#include "canframemodel.h" + class CANConnectionModel; @@ -16,15 +20,6 @@ namespace Ui { class ConnectionWindow; } -namespace ConnectionType -{ - enum ConnectionType - { - GVRET_SERIAL, - KVASER, - SOCKETCAN - }; -} class ConnectionWindow : public QDialog { @@ -36,11 +31,11 @@ public: void showEvent(QShowEvent *); int getSpeed(); QString getPortName(); //name of port to connect to - ConnectionType::ConnectionType getConnectionType(); + CANCon::type getConnectionType(); bool getSWMode(); signals: - void updateBusSettings(CAN_Bus *bus); + void updateBusSettings(CANBus *bus); void updatePortName(QString port); public slots: @@ -48,6 +43,8 @@ public slots: void setSWMode(bool mode); void sendFrame(const CANFrame *); void sendFrameBatch(const QList *); + void setSuspendAll(bool); + private slots: void handleOKButton(); @@ -58,10 +55,10 @@ private slots: void handleDisableAll(); void handleRevert(); void handleNewConn(); - void handleEnableAll(); - void handleDisableAll(); void receiveBusStatus(int bus, int speed, int status); - void connectionSuccess(CANConnection *conn); + void connectionStatus(CANCon::status); + + void refreshCanList(); private: Ui::ConnectionWindow *ui; @@ -69,11 +66,14 @@ private: QSettings *settings; CANConnectionModel *connModel; CANFrameModel *canModel; + QTimer mTicker; - void getSerialPorts(); - void getKvaserPorts(); - void getSocketcanPorts(); + void selectSerial(); + void selectKvaser(); + void selectSocketCan(); + bool isSocketCanAvailable(); + void setActiveAll(bool pActive); }; #endif // CONNECTIONWINDOW_H diff --git a/connectionwindow.ui b/connectionwindow.ui index f157991..f3a5b39 100644 --- a/connectionwindow.ui +++ b/connectionwindow.ui @@ -6,8 +6,8 @@ 0 0 - 1009 - 433 + 956 + 527 @@ -129,7 +129,69 @@ - + + + + 0 + 0 + + + + + 0 + 22 + + + + Qt::LeftToRight + + + 0 + + + + + 0 + 0 + + + + Qt::LeftToRight + + + + + + + 0 + 0 + + + + + 341 + 16777215 + + + + + + + + + + + + + 0 + 0 + + + + + + + diff --git a/mainwindow.cpp b/mainwindow.cpp index 07aa433..b456467 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -47,7 +47,8 @@ MainWindow::MainWindow(QWidget *parent) : this->setWindowTitle("Savvy CAN V" + QString::number(VERSION)); - model = new CANFrameModel(); + model = new CANFrameModel(this); // set parent to mainwindow to prevent canframemodel to change thread (might be done by setModel but just in case) + ui->canFramesView->setModel(model); readSettings(); @@ -180,6 +181,7 @@ MainWindow::MainWindow(QWidget *parent) : //Automatically create the connection window so it can be updated even if we never opened it. connectionWindow = new ConnectionWindow(model); connect(connectionWindow, SIGNAL(updateConnectionSettings(QString,QString,int,int)), this, SLOT(updateConnectionSettings(QString,QString,int,int))); + connect(this, SIGNAL(suspendCapturing(bool)), connectionWindow, SLOT(setSuspendAll(bool))); } MainWindow::~MainWindow() @@ -510,7 +512,7 @@ void MainWindow::gotFrames(int framesSinceLastUpdate) void MainWindow::addFrameToDisplay(CANFrame &frame, bool autoRefresh = false) { - model->addFrame(frame, autoRefresh); + model->addFrame(frame, autoRefresh); if (autoRefresh) { if (ui->cbAutoScroll->isChecked()) ui->canFramesView->scrollToBottom(); @@ -712,15 +714,11 @@ void MainWindow::toggleCapture() { allowCapture = !allowCapture; if (allowCapture) - { ui->btnCaptureToggle->setText("Suspend Capturing"); - emit startFrameCapturing(); - } else - { ui->btnCaptureToggle->setText("Restart Capturing"); - emit stopFrameCapturing(); - } + + emit suspendCapturing(!allowCapture); } void MainWindow::connectionSucceeded(int baud0, int baud1) diff --git a/mainwindow.h b/mainwindow.h index cbe0c0d..191008a 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -92,8 +92,7 @@ public slots: signals: void sendCANFrame(const CANFrame *, int); - void stopFrameCapturing(); - void startFrameCapturing(); + void suspendCapturing(bool); //-1 = frames cleared, -2 = a new file has been loaded (so all frames are different), otherwise # of new frames void framesUpdated(int numFrames); //something has updated the frame list diff --git a/serialworker.cpp b/serialworker.cpp index 36c26e7..f3b35d1 100644 --- a/serialworker.cpp +++ b/serialworker.cpp @@ -5,13 +5,14 @@ #include #include +#if 0 SerialWorker::SerialWorker(CANFrameModel *model, int base) : CANConnection(model, base) { qDebug() << "Serial Worker constructor"; serial = NULL; rx_state = IDLE; rx_step = 0; - buildFrame = new CANFrame; + buildFrame; ticker = NULL; framesRapid = 0; gotValidated = true; @@ -166,8 +167,13 @@ void SerialWorker::sendFrame(const CANFrame *frame) //qDebug() << "Sending out frame with id " << frame->ID; - //show our sent frames in the list too. This happens even if we're not connected. - model->addFrame(tempFrame, false); + //show our sent frames in the list too. This happens even if we're not connected. + /* model lives in UI thread, we need to call invokeMethod */ + QMetaObject::invokeMethod(model, "addFrame", + Qt::QueuedConnection, + Q_ARG(CANFrame, tempFrame), + Q_ARG(bool, false)); + framesRapid++; if (serial == NULL) return; @@ -275,56 +281,61 @@ void SerialWorker::procRXChar(unsigned char c) switch (rx_step) { case 0: - buildFrame->timestamp = c; + buildFrame.timestamp = c; break; case 1: - buildFrame->timestamp |= (uint)(c << 8); + buildFrame.timestamp |= (uint)(c << 8); break; case 2: - buildFrame->timestamp |= (uint)c << 16; + buildFrame.timestamp |= (uint)c << 16; break; case 3: - buildFrame->timestamp |= (uint)c << 24; + buildFrame.timestamp |= (uint)c << 24; break; case 4: - buildFrame->ID = c; + buildFrame.ID = c; break; case 5: - buildFrame->ID |= c << 8; + buildFrame.ID |= c << 8; break; case 6: - buildFrame->ID |= c << 16; + buildFrame.ID |= c << 16; break; case 7: - buildFrame->ID |= c << 24; - if ((buildFrame->ID & 1 << 31) == 1 << 31) + buildFrame.ID |= c << 24; + if ((buildFrame.ID & 1 << 31) == 1 << 31) { - buildFrame->ID &= 0x7FFFFFFF; - buildFrame->extended = true; + buildFrame.ID &= 0x7FFFFFFF; + buildFrame.extended = true; } - else buildFrame->extended = false; + else buildFrame.extended = false; break; case 8: - buildFrame->len = c & 0xF; - if (buildFrame->len > 8) buildFrame->len = 8; - buildFrame->bus = (c & 0xF0) >> 4; + buildFrame.len = c & 0xF; + if (buildFrame.len > 8) buildFrame.len = 8; + buildFrame.bus = (c & 0xF0) >> 4; break; default: - if (rx_step < buildFrame->len + 9) + if (rx_step < buildFrame.len + 9) { - buildFrame->data[rx_step - 9] = c; + buildFrame.data[rx_step - 9] = c; } else { rx_state = IDLE; rx_step = 0; - //qDebug() << "emit from serial handler to main form id: " << buildFrame->ID; + //qDebug() << "emit from serial handler to main form id: " << buildFrame.ID; //if (capturing) //{ - buildFrame->isReceived = true; - model->addFrame(*buildFrame, false); + buildFrame.isReceived = true; + /* model lives in UI thread, we need to call invokeMethod */ + QMetaObject::invokeMethod(model, "addFrame", + Qt::QueuedConnection, + Q_ARG(CANFrame, buildFrame), + Q_ARG(bool, false)); + //take the time the frame came in and try to resync the time base. - if (continuousTimeSync) txTimestampBasis = QDateTime::currentMSecsSinceEpoch() - (buildFrame->timestamp / 1000); + if (continuousTimeSync) txTimestampBasis = QDateTime::currentMSecsSinceEpoch() - (buildFrame.timestamp / 1000); framesRapid++; //} } @@ -566,6 +577,64 @@ void SerialWorker::updatePortName(QString portName) } } + + +void SerialWorker::updateBusSettings(CANBus bus) +{ + int busNum = bus.busNum - busBase; + if (busNum < 0) return; + if (busNum >= numBuses) return; + qDebug() << "About to update bus " << busNum << " on GVRET"; + if (busNum == 0) + { + can0Baud = bus.getSpeed(); + can0Baud |= 0x80000000; + if (bus.isActive()) + { + can0Baud |= 0x40000000; + can0Enabled = true; + } + else can0Enabled = false; + + if (bus.isListenOnly()) + { + can0Baud |= 0x20000000; + can0ListenOnly = true; + } + else can0ListenOnly = false; + } + if (busNum == 1) + { + can1Baud = bus.getSpeed(); + can1Baud |= 0x80000000; + if (bus.isActive()) + { + can1Baud |= 0x40000000; + can1Enabled = true; + } + else can1Enabled = false; + + if (bus.isListenOnly()) + { + can1Baud |= 0x20000000; + can1ListenOnly = true; + } + else can1ListenOnly = false; + + if (bus.isSingleWire()) + { + can1Baud |= 0x10000000; + deviceSingleWireMode = 1; + } + else deviceSingleWireMode = 0; + } + + updateBaudRates(can0Baud,can1Baud); +} + + +/************************************/ + int SerialWorker::getNumBuses() { return 2; @@ -576,55 +645,4 @@ QString SerialWorker::getConnTypeName() return QString("GVRET"); } -void SerialWorker::updateBusSettings(CAN_Bus *bus) -{ - int busNum = bus->busNum - busBase; - if (busNum < 0) return; - if (busNum >= numBuses) return; - qDebug() << "About to update bus " << busNum << " on GVRET"; - if (busNum == 0) - { - can0Baud = bus->getSpeed(); - can0Baud |= 0x80000000; - if (bus->isActive()) - { - can0Baud |= 0x40000000; - can0Enabled = true; - } - else can0Enabled = false; - - if (bus->isListenOnly()) - { - can0Baud |= 0x20000000; - can0ListenOnly = true; - } - else can0ListenOnly = false; - } - if (busNum == 1) - { - can1Baud = bus->getSpeed(); - can1Baud |= 0x80000000; - if (bus->isActive()) - { - can1Baud |= 0x40000000; - can1Enabled = true; - } - else can1Enabled = false; - - if (bus->isListenOnly()) - { - can1Baud |= 0x20000000; - can1ListenOnly = true; - } - else can1ListenOnly = false; - - if (bus->isSingleWire()) - { - can1Baud |= 0x10000000; - deviceSingleWireMode = 1; - } - else deviceSingleWireMode = 0; - } - - updateBaudRates(can0Baud,can1Baud); -} +#endif diff --git a/serialworker.h b/serialworker.h index 9505281..dc1a9a9 100644 --- a/serialworker.h +++ b/serialworker.h @@ -8,7 +8,8 @@ #include #include "can_structs.h" #include "canframemodel.h" -#include "canconnection.h" +//#include "canconnection_old.h" + namespace SERIALSTATE { @@ -29,6 +30,7 @@ enum STATE //keep this enum synchronized with the Arduino firmware project } +#if 0 using namespace SERIALSTATE; class SerialWorker : public CANConnection { @@ -57,7 +59,7 @@ public slots: //void stopFrameCapture(int) override; //void startFrameCapture(int) override; void updatePortName(QString) override; //string version of the port to connect to. This base doesnt know a thing about this value - void updateBusSettings(CAN_Bus *bus) override; + void updateBusSettings(CANBus) override; private: bool doValidation; @@ -71,7 +73,7 @@ private: int framesRapid; STATE rx_state; int rx_step; - CANFrame *buildFrame; + CANFrame buildFrame; int can0Baud, can1Baud; bool can0Enabled, can1Enabled; bool can0ListenOnly, can1ListenOnly; @@ -83,5 +85,5 @@ private: void procRXChar(unsigned char); void sendCommValidation(); }; - +#endif #endif // SERIALTHREAD_H diff --git a/test/test.pro b/test/test.pro new file mode 100644 index 0000000..56c22c3 --- /dev/null +++ b/test/test.pro @@ -0,0 +1,16 @@ +QT += widgets testlib +QT -= gui + +CONFIG += c++11 + +INCLUDEPATH += ../utils + +SOURCES += \ + tst_lfqueue.cpp + + +#HEADERS += \ +# ../utils/lfqueue.h + +target.path= . +INSTALLS += target diff --git a/test/tst_lfqueue.cpp b/test/tst_lfqueue.cpp new file mode 100644 index 0000000..1120de5 --- /dev/null +++ b/test/tst_lfqueue.cpp @@ -0,0 +1,98 @@ +#include +#include + +#include "lfqueue.h" + +class TestLFQueue: public QObject +{ + Q_OBJECT +private: + +private slots: + void setSize_data(); + void setSize(); + void exchange_data(); + void exchange(); +}; + + +void TestLFQueue::setSize_data() +{ + QTest::addColumn("size"); + QTest::addColumn("result"); + + QTest::newRow("-1") << -1 << false; + QTest::newRow("0") << 0 << true; + QTest::newRow("10") << 10 << true; + QTest::newRow("2000") << 20000 << true; + +} + + +void TestLFQueue::setSize() +{ + QFETCH(int, size); + QFETCH(bool, result); + + LFQueue queue; + QCOMPARE(queue.setSize(size), result); +} + + +void readerThread(LFQueue* pQueue_p, int pSize, bool pSleep) { + int* val_p; + + for(int i=0; ipeek()) ); + QVERIFY(val_p); + + QCOMPARE(*val_p, i); + pQueue_p->dequeue(); + + if(pSleep) + QThread::msleep(1); + } +} + + +void TestLFQueue::exchange_data() +{ + QTest::addColumn("size"); + QTest::addColumn("writerSleep"); + QTest::addColumn("readerSleep"); + + QTest::newRow("nosleep") << 1000 << false << false; + QTest::newRow("readersleep") << 1000 << false << true; + QTest::newRow("writersleep") << 1000 << true << false; +} + + +void TestLFQueue::exchange() +{ + LFQueue queue; + QFETCH(int, size); + QFETCH(bool, writerSleep); + QFETCH(bool, readerSleep); + + int* val_p; + QCOMPARE(queue.setSize(2), true); + + QFuture thread = QtConcurrent::run(readerThread, &queue, size, readerSleep); + + for(int i=0; i +#include + + +/* macros */ +#define IS_EMPTY() ( mWIdx.load() == mRIdx.load() ) +#define IS_FULL() ( (mWIdx.load()+1)%mSize == mRIdx.load() ) + + +template +class LFQueue +{ +public: + LFQueue() : mSize(0), mArray(NULL){} + + ~LFQueue() {setSize(0);} + + bool setSize(int size) { + if(size<0) + return false; + + if(mArray) { + delete[] mArray; + mArray = NULL; + } + + if(size>0) { + mArray = new T[size]; + if(mArray) + mSize = size; + return ( mArray!=NULL ); + } + + return true; + } + + void flush() { + mRIdx.store(0); + mWIdx.store(0); + } + + T* get() { + if(IS_FULL()) + return NULL; + + return &(mArray[mWIdx.loadAcquire()]); /* prevent memory reordering (belt and braces) */ + } + + + void queue() { + #ifdef QT_DEBUG + if(IS_FULL()) + qCritical() << "BUG: queueing in full queue"; + #endif + + int wIdx = mWIdx.load(); + mWIdx.storeRelease((wIdx+1)%mSize); + } + + + T* peek() { + if(IS_EMPTY()) + return NULL; + + return &(mArray[mRIdx.loadAcquire()]); /* prevent memory reordering (belt and braces) */ + } + + + void dequeue() { + #ifdef QT_DEBUG + if(IS_EMPTY()) + qCritical() << "BUG: dequeueing an empty queue"; + #endif + + int rIdx = mRIdx.load(); + mRIdx.storeRelease((rIdx+1)%mSize); + } + + +private: + int mSize; + T* mArray; + + QAtomicInt mRIdx; + QAtomicInt mWIdx; +}; + +#endif // LFQUEUE_H