A bit more work on MQTT support. It can actually connect to an MQTT broker now.
This commit is contained in:
@@ -17,6 +17,7 @@ TARGET = SavvyCAN
|
|||||||
TEMPLATE = app
|
TEMPLATE = app
|
||||||
|
|
||||||
SOURCES += main.cpp\
|
SOURCES += main.cpp\
|
||||||
|
connections/mqtt_bus.cpp \
|
||||||
mqtt/qmqtt_client.cpp \
|
mqtt/qmqtt_client.cpp \
|
||||||
mqtt/qmqtt_client_p.cpp \
|
mqtt/qmqtt_client_p.cpp \
|
||||||
mqtt/qmqtt_frame.cpp \
|
mqtt/qmqtt_frame.cpp \
|
||||||
@@ -91,6 +92,7 @@ SOURCES += main.cpp\
|
|||||||
HEADERS += mainwindow.h \
|
HEADERS += mainwindow.h \
|
||||||
can_structs.h \
|
can_structs.h \
|
||||||
canframemodel.h \ \
|
canframemodel.h \ \
|
||||||
|
connections/mqtt_bus.h \
|
||||||
mqtt/qmqtt.h \
|
mqtt/qmqtt.h \
|
||||||
mqtt/qmqtt_client.h \
|
mqtt/qmqtt_client.h \
|
||||||
mqtt/qmqtt_client_p.h \
|
mqtt/qmqtt_client_p.h \
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "canconfactory.h"
|
#include "canconfactory.h"
|
||||||
#include "serialbusconnection.h"
|
#include "serialbusconnection.h"
|
||||||
#include "gvretserial.h"
|
#include "gvretserial.h"
|
||||||
|
#include "mqtt_bus.h"
|
||||||
|
|
||||||
using namespace CANCon;
|
using namespace CANCon;
|
||||||
|
|
||||||
@@ -14,6 +15,8 @@ CANConnection* CanConFactory::create(type pType, QString pPortName, QString pDri
|
|||||||
return new GVRetSerial(pPortName, false);
|
return new GVRetSerial(pPortName, false);
|
||||||
case REMOTE:
|
case REMOTE:
|
||||||
return new GVRetSerial(pPortName, true); //it's a special case of GVRET connected over TCP/IP so it uses the same class
|
return new GVRetSerial(pPortName, true); //it's a special case of GVRET connected over TCP/IP so it uses the same class
|
||||||
|
case MQTT:
|
||||||
|
return new MQTT_BUS(pPortName);
|
||||||
default: {}
|
default: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,205 @@
|
|||||||
|
#include <QObject>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QCanBusFrame>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QStringBuilder>
|
||||||
|
#include <QtNetwork>
|
||||||
|
|
||||||
|
#include "mqtt_bus.h"
|
||||||
|
|
||||||
|
MQTT_BUS::MQTT_BUS(QString topicName) :
|
||||||
|
CANConnection(topicName, "mqtt_client", CANCon::MQTT, 1, 4000, true),
|
||||||
|
mTimer(this) /*NB: set this as parent of timer to manage it from working thread */
|
||||||
|
{
|
||||||
|
sendDebug("MQTT_BUS()");
|
||||||
|
|
||||||
|
crypto = new SimpleCrypt(Q_UINT64_C(0xdeadbeefface6285));
|
||||||
|
|
||||||
|
isAutoRestart = false;
|
||||||
|
this->topicName = topicName;
|
||||||
|
|
||||||
|
timeBasis = 0;
|
||||||
|
lastSystemTimeBasis = 0;
|
||||||
|
|
||||||
|
readSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MQTT_BUS::~MQTT_BUS()
|
||||||
|
{
|
||||||
|
delete crypto;
|
||||||
|
stop();
|
||||||
|
sendDebug("~MQTT_BUS");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MQTT_BUS::sendDebug(const QString debugText)
|
||||||
|
{
|
||||||
|
qDebug() << debugText;
|
||||||
|
debugOutput(debugText);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MQTT_BUS::piStarted()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
QString userName = settings.value("Remote/User", "Anonymous").toString();
|
||||||
|
QString host = settings.value("Remote/Host", "api.savvycan.com").toString();
|
||||||
|
int port = settings.value("Remote/Port", 8333).toInt();
|
||||||
|
QByteArray encPass = settings.value("Remote/Pass", "").toByteArray();
|
||||||
|
QByteArray password = crypto->decryptToByteArray(encPass);
|
||||||
|
|
||||||
|
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
|
||||||
|
mqttClient = new QMQTT::Client(host, port, sslConfig);
|
||||||
|
|
||||||
|
connect(mqttClient, &QMQTT::Client::connected, this, &MQTT_BUS::clientConnected);
|
||||||
|
connect(mqttClient, &QMQTT::Client::error, this, &MQTT_BUS::clientErrored);
|
||||||
|
|
||||||
|
//qDebug() << "User: " << userName << " Pass: " << password;
|
||||||
|
mqttClient->setClientId(genRandomClientID());
|
||||||
|
mqttClient->setUsername(userName);
|
||||||
|
mqttClient->setPassword(password);
|
||||||
|
mqttClient->connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
//MQTT required a client ID and they cannot be the same for any two clients. But, they really aren't super exciting
|
||||||
|
//or important to be named explicitly. Perhaps it might be nice to be able to see it for debugging though.
|
||||||
|
QString MQTT_BUS::genRandomClientID()
|
||||||
|
{
|
||||||
|
QString output;
|
||||||
|
output.reserve(12);
|
||||||
|
QRandomGenerator gen;
|
||||||
|
for (int i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
int val = gen.bounded(0, 62);
|
||||||
|
if (val < 26) output.append(QChar('A'+val));
|
||||||
|
else if (val < 52) output.append(QChar('a'+val-26));
|
||||||
|
else output.append(QChar('0'+val-52));
|
||||||
|
}
|
||||||
|
qDebug() << "Client ID: " << output;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MQTT_BUS::clientErrored(const QMQTT::ClientError error)
|
||||||
|
{
|
||||||
|
qDebug() << QString::number(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MQTT_BUS::piSuspend(bool pSuspend)
|
||||||
|
{
|
||||||
|
/* update capSuspended */
|
||||||
|
setCapSuspended(pSuspend);
|
||||||
|
|
||||||
|
/* flush queue if we are suspended */
|
||||||
|
if(isCapSuspended())
|
||||||
|
getQueue().flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MQTT_BUS::piStop()
|
||||||
|
{
|
||||||
|
mTimer.stop();
|
||||||
|
disconnectDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool MQTT_BUS::piGetBusSettings(int pBusIdx, CANBus& pBus)
|
||||||
|
{
|
||||||
|
return getBusConfig(pBusIdx, pBus);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MQTT_BUS::piSetBusSettings(int pBusIdx, CANBus bus)
|
||||||
|
{
|
||||||
|
/* sanity checks */
|
||||||
|
if( (pBusIdx < 0) || pBusIdx >= getNumBuses())
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* copy bus config */
|
||||||
|
setBusConfig(pBusIdx, bus);
|
||||||
|
//we don't really update anything. We're just here to listen and perhaps send frames.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool MQTT_BUS::piSendFrame(const CANFrame& frame)
|
||||||
|
{
|
||||||
|
QByteArray buffer;
|
||||||
|
int c;
|
||||||
|
quint32 ID;
|
||||||
|
|
||||||
|
//qDebug() << "Sending out GVRET frame with id " << frame.ID << " on bus " << frame.bus;
|
||||||
|
|
||||||
|
framesRapid++;
|
||||||
|
|
||||||
|
// Doesn't make sense to send an error frame
|
||||||
|
// to an adapter
|
||||||
|
if (frame.frameId() & 0x20000000) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ID = frame.frameId();
|
||||||
|
if (frame.hasExtendedFrameFormat()) ID |= 1u << 31;
|
||||||
|
|
||||||
|
buffer[0] = (char)0xF1; //start of a command over serial
|
||||||
|
buffer[1] = 0; //command ID for sending a CANBUS frame
|
||||||
|
buffer[2] = (char)(ID & 0xFF); //four bytes of ID LSB first
|
||||||
|
buffer[3] = (char)(ID >> 8);
|
||||||
|
buffer[4] = (char)(ID >> 16);
|
||||||
|
buffer[5] = (char)(ID >> 24);
|
||||||
|
buffer[6] = (char)((frame.bus) & 3);
|
||||||
|
buffer[7] = (char)frame.payload().length();
|
||||||
|
for (c = 0; c < frame.payload().length(); c++)
|
||||||
|
{
|
||||||
|
buffer[8 + c] = frame.payload()[c];
|
||||||
|
}
|
||||||
|
buffer[8 + frame.payload().length()] = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************/
|
||||||
|
|
||||||
|
void MQTT_BUS::readSettings()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MQTT_BUS::clientConnected()
|
||||||
|
{
|
||||||
|
sendDebug("Connecting to MQTT Broker!");
|
||||||
|
|
||||||
|
setStatus(CANCon::CONNECTED);
|
||||||
|
CANConStatus stats;
|
||||||
|
stats.conStatus = getStatus();
|
||||||
|
stats.numHardwareBuses = 1;//mNumBuses;
|
||||||
|
emit status(stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MQTT_BUS::disconnectDevice() {
|
||||||
|
|
||||||
|
setStatus(CANCon::NOT_CONNECTED);
|
||||||
|
CANConStatus stats;
|
||||||
|
stats.conStatus = getStatus();
|
||||||
|
stats.numHardwareBuses = mNumBuses;
|
||||||
|
emit status(stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MQTT_BUS::rebuildLocalTimeBasis()
|
||||||
|
{
|
||||||
|
|
||||||
|
//qDebug() << "Rebuilding GVRET time base. GVRET local base = " << buildTimeBasis;
|
||||||
|
|
||||||
|
/*
|
||||||
|
our time basis is the value we have to modulate the main system basis by in order
|
||||||
|
to sync the GVRET timestamps to the rest of the system.
|
||||||
|
The rest of the system uses CANConManager::getInstance()->getTimeBasis as the basis.
|
||||||
|
GVRET returns to us the current time since boot up in microseconds.
|
||||||
|
timeAtGVRETSync stores the "system" timestamp when the GVRET timestamp was retrieved.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
lastSystemTimeBasis = CANConManager::getInstance()->getTimeBasis();
|
||||||
|
int64_t systemDelta = timeAtGVRETSync - lastSystemTimeBasis;
|
||||||
|
int32_t localDelta = buildTimeBasis - systemDelta;
|
||||||
|
timeBasis = -localDelta;
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
#ifndef MQTTBUS_H
|
||||||
|
#define MQTTBUS_H
|
||||||
|
|
||||||
|
#include <QCanBusDevice>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QTimer>
|
||||||
|
#include "mqtt/qmqtt.h"
|
||||||
|
|
||||||
|
/*************/
|
||||||
|
#include <QDateTime>
|
||||||
|
/*************/
|
||||||
|
|
||||||
|
#include "canframemodel.h"
|
||||||
|
#include "canconnection.h"
|
||||||
|
#include "canconmanager.h"
|
||||||
|
#include "simplecrypt.h"
|
||||||
|
|
||||||
|
class MQTT_BUS : public CANConnection
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MQTT_BUS(QString topicName);
|
||||||
|
virtual ~MQTT_BUS();
|
||||||
|
|
||||||
|
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 bool piSendFrame(const CANFrame&) ;
|
||||||
|
|
||||||
|
void disconnectDevice();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void clientConnected();
|
||||||
|
void clientErrored(const QMQTT::ClientError error);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void readSettings();
|
||||||
|
void rebuildLocalTimeBasis();
|
||||||
|
void sendDebug(const QString debugText);
|
||||||
|
QString genRandomClientID();
|
||||||
|
SimpleCrypt *crypto;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QTimer mTimer;
|
||||||
|
QThread mThread;
|
||||||
|
|
||||||
|
QMQTT::Client *mqttClient;
|
||||||
|
QString topicName;
|
||||||
|
|
||||||
|
bool isAutoRestart;
|
||||||
|
int framesRapid;
|
||||||
|
CANFrame buildFrame;
|
||||||
|
qint64 buildTimestamp;
|
||||||
|
quint32 buildId;
|
||||||
|
QByteArray buildData;
|
||||||
|
uint32_t buildTimeBasis;
|
||||||
|
int32_t timeBasis;
|
||||||
|
uint64_t lastSystemTimeBasis;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MQTT_BUS_H
|
||||||
@@ -52,7 +52,7 @@ void NewConnectionDialog::handleConnTypeChanged()
|
|||||||
if (ui->rbGVRET->isChecked()) selectSerial();
|
if (ui->rbGVRET->isChecked()) selectSerial();
|
||||||
if (ui->rbSocketCAN->isChecked()) selectSocketCan();
|
if (ui->rbSocketCAN->isChecked()) selectSocketCan();
|
||||||
if (ui->rbRemote->isChecked()) selectRemote();
|
if (ui->rbRemote->isChecked()) selectRemote();
|
||||||
|
if (ui->rbMQTT->isChecked()) selectMQTT();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NewConnectionDialog::handleDeviceTypeChanged()
|
void NewConnectionDialog::handleDeviceTypeChanged()
|
||||||
@@ -123,7 +123,12 @@ void NewConnectionDialog::setPortName(CANCon::type pType, QString pPortName, QSt
|
|||||||
break;
|
break;
|
||||||
case CANCon::SERIALBUS:
|
case CANCon::SERIALBUS:
|
||||||
ui->rbSocketCAN->setChecked(true);
|
ui->rbSocketCAN->setChecked(true);
|
||||||
//you can't configure any of the below three with socketcan so dim them out
|
break;
|
||||||
|
case CANCon::REMOTE:
|
||||||
|
ui->rbRemote->setChecked(true);
|
||||||
|
break;
|
||||||
|
case CANCon::MQTT:
|
||||||
|
ui->rbMQTT->setChecked(true);
|
||||||
break;
|
break;
|
||||||
default: {}
|
default: {}
|
||||||
}
|
}
|
||||||
@@ -157,6 +162,9 @@ void NewConnectionDialog::setPortName(CANCon::type pType, QString pPortName, QSt
|
|||||||
else ui->cbPort->addItem(pPortName);
|
else ui->cbPort->addItem(pPortName);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CANCon::MQTT:
|
||||||
|
ui->cbPort->setCurrentText(pPortName);
|
||||||
|
break;
|
||||||
default: {}
|
default: {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -32,7 +32,7 @@
|
|||||||
#ifndef QMQTT_H
|
#ifndef QMQTT_H
|
||||||
#define QMQTT_H
|
#define QMQTT_H
|
||||||
|
|
||||||
#include <qmqtt_message.h>
|
#include "qmqtt_message.h"
|
||||||
#include <qmqtt_client.h>
|
#include "qmqtt_client.h"
|
||||||
|
|
||||||
#endif // QMQTT_H
|
#endif // QMQTT_H
|
||||||
|
|||||||
Reference in New Issue
Block a user