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
|
||||
|
||||
SOURCES += main.cpp\
|
||||
connections/mqtt_bus.cpp \
|
||||
mqtt/qmqtt_client.cpp \
|
||||
mqtt/qmqtt_client_p.cpp \
|
||||
mqtt/qmqtt_frame.cpp \
|
||||
@@ -91,6 +92,7 @@ SOURCES += main.cpp\
|
||||
HEADERS += mainwindow.h \
|
||||
can_structs.h \
|
||||
canframemodel.h \ \
|
||||
connections/mqtt_bus.h \
|
||||
mqtt/qmqtt.h \
|
||||
mqtt/qmqtt_client.h \
|
||||
mqtt/qmqtt_client_p.h \
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "canconfactory.h"
|
||||
#include "serialbusconnection.h"
|
||||
#include "gvretserial.h"
|
||||
#include "mqtt_bus.h"
|
||||
|
||||
using namespace CANCon;
|
||||
|
||||
@@ -14,6 +15,8 @@ CANConnection* CanConFactory::create(type pType, QString pPortName, QString pDri
|
||||
return new GVRetSerial(pPortName, false);
|
||||
case REMOTE:
|
||||
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: {}
|
||||
}
|
||||
|
||||
|
||||
@@ -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->rbSocketCAN->isChecked()) selectSocketCan();
|
||||
if (ui->rbRemote->isChecked()) selectRemote();
|
||||
|
||||
if (ui->rbMQTT->isChecked()) selectMQTT();
|
||||
}
|
||||
|
||||
void NewConnectionDialog::handleDeviceTypeChanged()
|
||||
@@ -123,7 +123,12 @@ void NewConnectionDialog::setPortName(CANCon::type pType, QString pPortName, QSt
|
||||
break;
|
||||
case CANCon::SERIALBUS:
|
||||
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;
|
||||
default: {}
|
||||
}
|
||||
@@ -157,6 +162,9 @@ void NewConnectionDialog::setPortName(CANCon::type pType, QString pPortName, QSt
|
||||
else ui->cbPort->addItem(pPortName);
|
||||
break;
|
||||
}
|
||||
case CANCon::MQTT:
|
||||
ui->cbPort->setCurrentText(pPortName);
|
||||
break;
|
||||
default: {}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -32,7 +32,7 @@
|
||||
#ifndef QMQTT_H
|
||||
#define QMQTT_H
|
||||
|
||||
#include <qmqtt_message.h>
|
||||
#include <qmqtt_client.h>
|
||||
#include "qmqtt_message.h"
|
||||
#include "qmqtt_client.h"
|
||||
|
||||
#endif // QMQTT_H
|
||||
|
||||
Reference in New Issue
Block a user