A bit more work on MQTT support. It can actually connect to an MQTT broker now.

This commit is contained in:
Collin Kidder
2020-08-05 21:53:21 -04:00
parent ee287eac4b
commit 2da2855bfa
6 changed files with 288 additions and 4 deletions
+2
View File
@@ -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 \
+3
View File
@@ -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: {}
} }
+205
View File
@@ -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;
*/
}
+66
View File
@@ -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
+10 -2
View File
@@ -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
View File
@@ -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