瀏覽代碼

trans2mqtt

ZenZ 1 年之前
當前提交
a532d4fc78
共有 100 個文件被更改,包括 7536 次插入0 次删除
  1. 11 0
      .gitignore
  2. 43 0
      DataConsumer/Alarm/Alarm.cpp
  3. 24 0
      DataConsumer/Alarm/Alarm.h
  4. 43 0
      DataConsumer/Alarm/Alarm.pro
  5. 12 0
      DataConsumer/Alarm/Alarm_global.h
  6. 43 0
      DataConsumer/TDengine/TDengine.cpp
  7. 24 0
      DataConsumer/TDengine/TDengine.h
  8. 37 0
      DataConsumer/TDengine/TDengine.pro
  9. 12 0
      DataConsumer/TDengine/TDengine_global.h
  10. 109 0
      DataConsumer/Trans2Mqtt/Trans2Mqtt.cpp
  11. 30 0
      DataConsumer/Trans2Mqtt/Trans2Mqtt.h
  12. 48 0
      DataConsumer/Trans2Mqtt/Trans2Mqtt.pro
  13. 12 0
      DataConsumer/Trans2Mqtt/Trans2mqtt_global.h
  14. 41 0
      DataManagerMain/Config.cpp
  15. 20 0
      DataManagerMain/Config.h
  16. 75 0
      DataManagerMain/DataConsumer.cpp
  17. 39 0
      DataManagerMain/DataConsumer.h
  18. 57 0
      DataManagerMain/DataManager.cpp
  19. 24 0
      DataManagerMain/DataManager.h
  20. 60 0
      DataManagerMain/DataManagerMain.pro
  21. 138 0
      DataManagerMain/DataManagerProxy.cpp
  22. 16 0
      DataManagerMain/DataManagerProxy.h
  23. 3 0
      DataManagerMain/DataManager_zh_CN.ts
  24. 79 0
      DataManagerMain/DataSubscribe.cpp
  25. 37 0
      DataManagerMain/DataSubscribe.h
  26. 33 0
      DataManagerMain/main.cpp
  27. 25 0
      DataManater.pro
  28. 109 0
      DataSubscribe/RedisSubscriber/RedisSubscriber.cpp
  29. 36 0
      DataSubscribe/RedisSubscriber/RedisSubscriber.h
  30. 43 0
      DataSubscribe/RedisSubscriber/RedisSubscriber.pro
  31. 12 0
      DataSubscribe/RedisSubscriber/RedisSubscriber_global.h
  32. 87 0
      DataSubscribe/TDengineSubscriber/TDengineSubscriber.cpp
  33. 37 0
      DataSubscribe/TDengineSubscriber/TDengineSubscriber.h
  34. 40 0
      DataSubscribe/TDengineSubscriber/TDengineSubscriber.pro
  35. 12 0
      DataSubscribe/TDengineSubscriber/TDengineSubscriber_global.h
  36. 16 0
      doc/config/config.json
  37. 42 0
      doc/config/configs.json
  38. 42 0
      doc/config/configs2.json
  39. 98 0
      doc/config/module_config.json
  40. 64 0
      doc/config/结构说明.json
  41. 22 0
      include/BaseModule.h
  42. 17 0
      include/Client.h
  43. 88 0
      include/Define.h
  44. 9 0
      include/JobModule.h
  45. 91 0
      include/LibraryLoader.h
  46. 21 0
      include/Publisher.h
  47. 13 0
      include/RunnableModule.h
  48. 87 0
      modules/MQTTClient/MQTTClient.cpp
  49. 46 0
      modules/MQTTClient/MQTTClient.h
  50. 37 0
      modules/MQTTClient/MQTTClient.pro
  51. 9 0
      modules/MQTTClient/MQTTClient_global.h
  52. 623 0
      modules/RedisClient/RedisClient.cpp
  53. 55 0
      modules/RedisClient/RedisClient.h
  54. 38 0
      modules/RedisClient/RedisClient.pro
  55. 9 0
      modules/RedisClient/RedisClient_global.h
  56. 302 0
      modules/TDengineClient/TDengineClient.cpp
  57. 57 0
      modules/TDengineClient/TDengineClient.h
  58. 31 0
      modules/TDengineClient/TDengineClient.pro
  59. 9 0
      modules/TDengineClient/libaray_symbols.h
  60. 130 0
      thirdparty/hiredis/include/hiredis/adapters/ae.h
  61. 156 0
      thirdparty/hiredis/include/hiredis/adapters/glib.h
  62. 84 0
      thirdparty/hiredis/include/hiredis/adapters/ivykis.h
  63. 188 0
      thirdparty/hiredis/include/hiredis/adapters/libev.h
  64. 175 0
      thirdparty/hiredis/include/hiredis/adapters/libevent.h
  65. 123 0
      thirdparty/hiredis/include/hiredis/adapters/libhv.h
  66. 171 0
      thirdparty/hiredis/include/hiredis/adapters/libuv.h
  67. 115 0
      thirdparty/hiredis/include/hiredis/adapters/macosx.h
  68. 197 0
      thirdparty/hiredis/include/hiredis/adapters/poll.h
  69. 135 0
      thirdparty/hiredis/include/hiredis/adapters/qt.h
  70. 96 0
      thirdparty/hiredis/include/hiredis/alloc.h
  71. 152 0
      thirdparty/hiredis/include/hiredis/async.h
  72. 366 0
      thirdparty/hiredis/include/hiredis/hiredis.h
  73. 129 0
      thirdparty/hiredis/include/hiredis/read.h
  74. 280 0
      thirdparty/hiredis/include/hiredis/sds.h
  75. 95 0
      thirdparty/hiredis/include/hiredis/sockcompat.h
  76. 二進制
      thirdparty/hiredis/lib/libhiredis.a
  77. 二進制
      thirdparty/hiredis/lib/libhiredis.dll.a
  78. 12 0
      thirdparty/hiredis/lib/pkgconfig/hiredis.pc
  79. 37 0
      thirdparty/hiredis/share/hiredis/hiredis-config.cmake
  80. 29 0
      thirdparty/hiredis/share/hiredis/hiredis-targets-release.cmake
  81. 114 0
      thirdparty/hiredis/share/hiredis/hiredis-targets.cmake
  82. 38 0
      thirdparty/qmqtt/include/qmqtt.h
  83. 286 0
      thirdparty/qmqtt/include/qmqtt_client.h
  84. 137 0
      thirdparty/qmqtt/include/qmqtt_frame.h
  85. 48 0
      thirdparty/qmqtt/include/qmqtt_global.h
  86. 96 0
      thirdparty/qmqtt/include/qmqtt_message.h
  87. 92 0
      thirdparty/qmqtt/include/qmqtt_networkinterface.h
  88. 71 0
      thirdparty/qmqtt/include/qmqtt_routedmessage.h
  89. 60 0
      thirdparty/qmqtt/include/qmqtt_router.h
  90. 79 0
      thirdparty/qmqtt/include/qmqtt_routesubscription.h
  91. 84 0
      thirdparty/qmqtt/include/qmqtt_socketinterface.h
  92. 62 0
      thirdparty/qmqtt/include/qmqtt_timerinterface.h
  93. 二進制
      thirdparty/qmqtt/lib/Qt5Qmqtt.dll
  94. 二進制
      thirdparty/qmqtt/lib/Qt5Qmqtt.dll.debug
  95. 5 0
      thirdparty/qmqtt/lib/Qt5Qmqtt.prl
  96. 7 0
      thirdparty/qmqtt/lib/cmake/Qt5Qmqtt/ExtraSourceIncludes.cmake
  97. 253 0
      thirdparty/qmqtt/lib/cmake/Qt5Qmqtt/Qt5QmqttConfig.cmake
  98. 11 0
      thirdparty/qmqtt/lib/cmake/Qt5Qmqtt/Qt5QmqttConfigVersion.cmake
  99. 19 0
      thirdparty/qmqtt/lib/cmake/qmqtt/qmqtt-debug.cmake
  100. 107 0
      thirdparty/qmqtt/lib/cmake/qmqtt/qmqtt.cmake

+ 11 - 0
.gitignore

@@ -0,0 +1,11 @@
+build-*
+*.pro.user*
+*.TMP
+*.log
+*.ilk
+*.exp
+*.dmp
+**/log.txt
+tmp/
+build/
+bin/

+ 43 - 0
DataConsumer/Alarm/Alarm.cpp

@@ -0,0 +1,43 @@
+#include "Alarm.h"
+#include <QDebug>
+Alarm::Alarm() {
+    //AlarmClient* tdclient = new AlarmClient();
+    //tdclient->start();
+}
+Alarm::~Alarm(){
+    qDebug() << __FILE__ << __FUNCTION__<< __LINE__;
+}
+void Alarm::Run(const ConsumerInfo& ci)
+{
+    //start();
+}
+
+void Alarm::OnData(const QString& user, const QString& key, const QVariant& val)
+{
+    qDebug() << __FILE__ << __FUNCTION__<< __LINE__ << user << key << val;
+}
+
+void Alarm::setLoader(QLibrary *)
+{
+
+}
+
+void Alarm::run()
+{
+    /*while(!isInterruptionRequested()){
+        qDebug() << __FILE__ << __FUNCTION__;
+        QThread::msleep(1000);
+    }*/
+}
+Client* instance()
+{
+    return new Alarm();
+}
+
+void destroy(Client* pInstance)
+{
+    if( pInstance )
+    {
+        delete pInstance;
+    }
+}

+ 24 - 0
DataConsumer/Alarm/Alarm.h

@@ -0,0 +1,24 @@
+#ifndef Alarm_H
+#define Alarm_H
+
+#include "Alarm_global.h"
+#include "Client.h"
+
+class ALARM_EXPORT Alarm:public Client
+{
+public:
+    Alarm();
+    ~Alarm();
+    virtual void Run(const ConsumerInfo& ci);
+    virtual void OnData(const QString& user, const QString& key, const QVariant& val);
+    virtual void setLoader(QLibrary*);
+private:
+    virtual void run();
+};
+
+extern "C" {//一定要添加上
+ALARM_EXPORT Client* instance();
+ALARM_EXPORT void destroy(Client*);
+}
+
+#endif // Alarm_H

+ 43 - 0
DataConsumer/Alarm/Alarm.pro

@@ -0,0 +1,43 @@
+QT -= gui
+
+TEMPLATE = lib
+DEFINES += ALARM_LIBRARY
+
+CONFIG += c++17
+
+# You can make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+INCLUDEPATH += ../../include
+
+HEADERS += ../../include/Client.h
+
+SOURCES += \
+    Alarm.cpp
+
+HEADERS += \
+    Alarm_global.h \
+    Alarm.h
+
+# TDENGINE_DIR =$$PWD/../../thirdparty/tdengine
+# LIBS += -L$${TDENGINE_DIR}/lib -ltaos
+# INCLUDEPATH += $${TDENGINE_DIR}/include
+# INCLUDEPATH += $$PWD/../../modules/TDengineClient
+# LIBS +=-L$$PWD/../../bin/plugins -lTDengineClient
+
+HIREDIS_DIR =$$PWD/../../thirdparty/hiredis
+HEADERS += $${HIREDIS_DIR}/include/hiredis/adapters/qt.h
+LIBS += -L$${HIREDIS_DIR}/lib -lhiredis
+#LIBS += ../thirdparty/hiredis/lib/libhiredis.a
+INCLUDEPATH += $${HIREDIS_DIR}/include
+
+
+# Default rules for deployment.
+unix {
+    target.path = /usr/lib
+}else{
+    DESTDIR = $$PWD/../../bin/plugins
+}
+
+!isEmpty(target.path): INSTALLS += target

+ 12 - 0
DataConsumer/Alarm/Alarm_global.h

@@ -0,0 +1,12 @@
+#ifndef ALARM_GLOBAL_H
+#define ALARM_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(ALARM_LIBRARY)
+#define ALARM_EXPORT Q_DECL_EXPORT
+#else
+#define ALARM_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // TDENGINE_GLOBAL_H

+ 43 - 0
DataConsumer/TDengine/TDengine.cpp

@@ -0,0 +1,43 @@
+#include "TDengine.h"
+#include <QDebug>
+TDengine::TDengine() {
+    //TDengineClient* tdclient = new TDengineClient();
+    //tdclient->start();
+}
+TDengine::~TDengine(){
+    qDebug() << __FILE__ << __FUNCTION__<< __LINE__;
+}
+void TDengine::Run(const ConsumerInfo& ci)
+{
+    //start();
+}
+
+void TDengine::OnData(const QString& user, const QString& key, const QVariant& val)
+{
+    qDebug() << __FILE__ << __FUNCTION__<< __LINE__ << user << key << val;
+}
+
+void TDengine::setLoader(QLibrary *)
+{
+
+}
+
+void TDengine::run()
+{
+    /*while(!isInterruptionRequested()){
+        qDebug() << __FILE__ << __FUNCTION__;
+        QThread::msleep(1000);
+    }*/
+}
+Client* instance()
+{
+    return new TDengine();
+}
+
+void destroy(Client* pInstance)
+{
+    if( pInstance )
+    {
+        delete pInstance;
+    }
+}

+ 24 - 0
DataConsumer/TDengine/TDengine.h

@@ -0,0 +1,24 @@
+#ifndef TDENGINE_H
+#define TDENGINE_H
+
+#include "TDengine_global.h"
+#include "Client.h"
+
+class TDENGINE_EXPORT TDengine:public Client
+{
+public:
+    TDengine();
+    ~TDengine();
+    virtual void Run(const ConsumerInfo& ci);
+    virtual void OnData(const QString& user, const QString& key, const QVariant& val);
+    virtual void setLoader(QLibrary*);
+private:
+    virtual void run();
+};
+
+extern "C" {//一定要添加上
+TDENGINE_EXPORT Client* instance();
+TDENGINE_EXPORT void destroy(Client*);
+}
+
+#endif // TDENGINE_H

+ 37 - 0
DataConsumer/TDengine/TDengine.pro

@@ -0,0 +1,37 @@
+QT -= gui
+
+TEMPLATE = lib
+DEFINES += TDENGINE_LIBRARY
+
+CONFIG += c++17
+
+# You can make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+INCLUDEPATH += ../../include
+
+HEADERS += ../../include/Client.h
+
+SOURCES += \
+    TDengine.cpp
+
+HEADERS += \
+    TDengine_global.h \
+    TDengine.h
+
+TDENGINE_DIR =$$PWD/../../thirdparty/tdengine
+LIBS += -L$${TDENGINE_DIR}/lib -ltaos
+INCLUDEPATH += $${TDENGINE_DIR}/include
+INCLUDEPATH += $$PWD/../../modules/TDengineClient
+LIBS +=-L$$PWD/../../bin/plugins -lTDengineClient
+
+
+# Default rules for deployment.
+unix {
+    target.path = /usr/lib
+}else{
+    DESTDIR = $$PWD/../../bin/plugins
+}
+
+!isEmpty(target.path): INSTALLS += target

+ 12 - 0
DataConsumer/TDengine/TDengine_global.h

@@ -0,0 +1,12 @@
+#ifndef TDENGINE_GLOBAL_H
+#define TDENGINE_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(TDENGINE_LIBRARY)
+#define TDENGINE_EXPORT Q_DECL_EXPORT
+#else
+#define TDENGINE_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // TDENGINE_GLOBAL_H

+ 109 - 0
DataConsumer/Trans2Mqtt/Trans2Mqtt.cpp

@@ -0,0 +1,109 @@
+#include "Trans2Mqtt.h"
+#include <QDebug>
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <QJsonObject>
+Trans2Mqtt::Trans2Mqtt()	 {
+    mqtt = new MQTTClient();
+    //tdclient->start();
+}
+Trans2Mqtt::~Trans2Mqtt(){
+    delete mqtt;
+    qDebug() << __FILE__ << __FUNCTION__<< __LINE__;
+}
+void Trans2Mqtt::Run(const ConsumerInfo& ci)
+{
+    QString host = "192.168.9.6";
+    quint16 port = 1883;
+    QString usr = "admin";
+    QString passwd = "N6pNXbZjspDRqNGnxMmc";
+    QString clientID = "Trans2mqtt";
+
+    QJsonParseError jsonParseError;
+    QJsonDocument jsonDocument(QJsonDocument::fromJson(QString::fromStdString(ci.Settings.c_str()).toUtf8(), &jsonParseError));
+    if(QJsonParseError::NoError != jsonParseError.error)
+    {
+        //LOGERROR("parse json file {} error", fullpath.toStdString().c_str());
+        qDebug() << "ERROR:" << __FILE__ << __FUNCTION__<< __LINE__;
+    }
+    QJsonArray ja = jsonDocument.array();
+    foreach (auto var, ja) {
+        host = var["host"].toString();
+        usr = var["usr"].toString();
+        port = var["port"].toVariant().toInt();
+        passwd = var["passwd"].toString();
+    }
+    clientID = QString("%1@%2").arg(usr, passwd);
+    dataItems = ci.dataItems;
+    topicsMap = ci.topicsMap;
+    foreach (auto var, ci.dataItems) {
+        dataMap.insert({var, {}});
+    }
+    mqtt->connect2Host(host, port, usr, passwd, clientID);
+    //start();
+}
+
+void Trans2Mqtt::OnData(const QString& user, const QString& key, const QVariant& val)
+{
+    qDebug() << __FILE__ << __FUNCTION__<< __LINE__ << user << key << val;
+    QString topic = key;
+    // QByteArray payload = "data";
+    // QByteArray byteArray(val.c_str(), val.length());
+    QJsonParseError err;
+    QJsonDocument jsonDoc(QJsonDocument::fromJson(val.toString().toLatin1(),&err));
+    QJsonObject jsonObject = jsonDoc.object();
+    // QStringList keys = jsonObject.keys();
+
+    // auto itTopic = topicsMap.find(topic.toStdString());
+    auto range = topicsMap.equal_range(key.toStdString());
+    for (auto it = range.first; it != range.second; ++it) {
+        // std::cout << it->first << ' ' << it->second << '\n';
+        QJsonObject val = jsonObject[QString::fromStdString(it->second)].toObject();
+        QString dataName = QString("%1.%2").arg(key, it->second.c_str());
+        dataMap[dataName.toStdString()] = val;
+        int type = val["type"].toInt();
+        switch (type) {
+            case QMetaType::Type::ULongLong:
+            break;
+            case QMetaType::Type::QString:
+            break;
+            case QMetaType::Type::Double:
+            break;
+            case QMetaType::Type::Float:
+            break;
+            case QMetaType::Type::Int:
+            break;
+            case QMetaType::Type::LongLong:
+            break;
+            default:
+            break;
+        }
+    }
+
+    mqtt->publish(topic, val.toString().toLatin1());
+}
+
+void Trans2Mqtt::setLoader(QLibrary *)
+{
+
+}
+
+void Trans2Mqtt::run()
+{
+    /*while(!isInterruptionRequested()){
+        qDebug() << __FILE__ << __FUNCTION__;
+        QThread::msleep(1000);
+    }*/
+}
+Client* instance()
+{
+    return new Trans2Mqtt();
+}
+
+void destroy(Client* pInstance)
+{
+    if( pInstance )
+    {
+        delete pInstance;
+    }
+}

+ 30 - 0
DataConsumer/Trans2Mqtt/Trans2Mqtt.h

@@ -0,0 +1,30 @@
+#ifndef Trans2mqtt_H
+#define Trans2mqtt_H
+
+#include "Trans2mqtt_global.h"
+#include "Client.h"
+#include "MQTTClient.h"
+#include <QJsonObject>
+class TRANS2MQTT_EXPORT Trans2Mqtt:public Client
+{
+public:
+    Trans2Mqtt();
+    ~Trans2Mqtt();
+    virtual void Run(const ConsumerInfo& ci);
+    virtual void OnData(const QString& user, const QString& key, const QVariant& val);
+    virtual void setLoader(QLibrary*);
+private:
+    virtual void run();
+    MQTTClient* mqtt = nullptr;
+    std::list<std::string>dataItems;
+    std::unordered_multimap<std::string, std::string>topicsMap;
+    std::unordered_map<std::string, QJsonObject>dataMap;
+
+};
+
+extern "C" {//一定要添加上
+TRANS2MQTT_EXPORT Client* instance();
+TRANS2MQTT_EXPORT void destroy(Client*);
+}
+
+#endif // Trans2mqtt_H

+ 48 - 0
DataConsumer/Trans2Mqtt/Trans2Mqtt.pro

@@ -0,0 +1,48 @@
+QT -= gui
+QT += network
+TEMPLATE = lib
+DEFINES += TRANS2MQTT_LIBRARY
+
+CONFIG += c++17
+
+# You can make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+INCLUDEPATH += ../../include
+
+HEADERS += ../../include/Client.h
+
+SOURCES += \
+    Trans2Mqtt.cpp
+
+HEADERS += \
+    Trans2Mqtt.h \
+    Trans2mqtt_global.h
+
+# TDENGINE_DIR =$$PWD/../../thirdparty/tdengine
+# LIBS += -L$${TDENGINE_DIR}/lib -ltaos
+# INCLUDEPATH += $${TDENGINE_DIR}/include
+# INCLUDEPATH += $$PWD/../../modules/TDengineClient
+# LIBS +=-L$$PWD/../../bin/plugins -lTDengineClient
+
+# HIREDIS_DIR =$$PWD/../../thirdparty/hiredis
+# HEADERS += $${HIREDIS_DIR}/include/hiredis/adapters/qt.h
+# LIBS += -L$${HIREDIS_DIR}/lib -lhiredis
+# #LIBS += ../thirdparty/hiredis/lib/libhiredis.a
+# INCLUDEPATH += $${HIREDIS_DIR}/include
+
+QMQTT_DIR =$$PWD/../../thirdparty/qmqtt
+LIBS += -L$${QMQTT_DIR}/lib -lQt5Qmqtt
+INCLUDEPATH += $${QMQTT_DIR}/include
+INCLUDEPATH += $$PWD/../../modules/MQTTClient
+LIBS +=-L$$PWD/../../bin/plugins -lMQTTClient
+
+# Default rules for deployment.
+unix {
+    target.path = /usr/lib
+}else{
+    DESTDIR = $$PWD/../../bin/plugins
+}
+
+!isEmpty(target.path): INSTALLS += target

+ 12 - 0
DataConsumer/Trans2Mqtt/Trans2mqtt_global.h

@@ -0,0 +1,12 @@
+#ifndef TRANS2MQTT_GLOBAL_H
+#define TRANS2MQTT_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(TRANS2MQTT_LIBRARY)
+#define TRANS2MQTT_EXPORT Q_DECL_EXPORT
+#else
+#define TRANS2MQTT_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif

+ 41 - 0
DataManagerMain/Config.cpp

@@ -0,0 +1,41 @@
+#include "Config.h"
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QFile>
+
+
+Config::Config(const QString &filename)
+{
+    // QMap<QString, QString> configMap;
+
+    //QString fullpath = QCoreApplication::applicationDirPath() + "/config.json";
+    QFile file(filename);
+    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+        //LOGERROR("open {} error", filename.toStdString().c_str());
+        return;
+    }
+    QByteArray array = file.readAll();
+    file.close();
+    QJsonParseError jsonParseError;
+    QJsonDocument jsonDocument(QJsonDocument::fromJson(array, &jsonParseError));
+    if(QJsonParseError::NoError != jsonParseError.error)
+    {
+        //LOGERROR("parse json file {} error", fullpath.toStdString().c_str());
+        return;
+    }
+    if(!jsonDocument.isObject()){
+        return;
+    }
+    QJsonObject obj = jsonDocument.object();
+    QString appName = obj["AppName"].toString();
+    QString appId = obj["AppId"].toString();
+    QString service = obj["Service"].toString();
+    QJsonObject redis = obj["redis"].toObject();
+    AppName = appName;
+    AppId = appId;
+    Service = service;
+    redisCfg.host = redis["host"].toString();
+    redisCfg.port = redis["port"].toInt();
+    redisCfg.au = redis["au"].toString();
+}

+ 20 - 0
DataManagerMain/Config.h

@@ -0,0 +1,20 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+#include <QString>
+class Config
+{
+public:
+    Config() = delete;
+    Config(const QString& filename);
+    struct RedisConfig{
+        QString host;
+        uint16_t port;
+        QString au;
+    };
+    RedisConfig redisCfg;
+    QString AppName;
+    QString AppId;
+    QString Service;
+};
+
+#endif // CONFIG_H

+ 75 - 0
DataManagerMain/DataConsumer.cpp

@@ -0,0 +1,75 @@
+#include "DataConsumer.h"
+#include <QThread>
+#include <QDateTime>
+#include <QVariant>
+#include "LibraryLoader.h"
+#include "Client.h"
+DataConsumer::DataConsumer()
+{
+
+}
+
+DataConsumer::~DataConsumer()
+{
+
+}
+
+
+
+// void DataConsumer::run()
+// {
+
+//     while(!isInterruptionRequested()){
+//         qDebug() << __FILE__ << __FUNCTION__;
+//         QThread::msleep(1000);
+//     }
+
+// }
+
+void DataConsumer::Setup(ConsumerInfo &ci)
+{
+    std::string assemblyName = ci.AssemblyName;
+    // dataName = ci.Name;
+    topicsMap = ci.topicsMap;
+    Client* pModule = nullptr;
+    // = (BaseModule)Assembly.LoadFile(Path.GetFullPath(@".\" + assemblyName)).CreateInstance(className);
+    pModule = LibraryLoader::load<Client>(assemblyName);
+    if( pModule == nullptr )
+    {
+        qCritical() << " load " << assemblyName.c_str() << " failed.";
+        return;
+    }
+    if( pModule == nullptr )
+    {
+        qCritical() << __FILE__ << __LINE__ << " " << assemblyName.c_str() << " load failed.";
+        return;
+    }
+
+    {
+        //Client*
+        runable = dynamic_cast<Client*>(pModule);
+        runable->Run(ci);
+    }
+}
+
+std::string DataConsumer::getTypeList()
+{
+    return "DataStorage::RunnableModule";
+}
+
+// void DataConsumer::setLoader(QLibrary *)
+// {
+
+// }
+
+
+void DataConsumer::Run()
+{
+    //start();
+}
+
+void DataConsumer::OnData(const QString&usr ,const QString& key,const QVariant&val)
+{
+    qDebug() << __FILE__ <<__FUNCTION__<< usr << key << val ;
+    runable->OnData(usr, key, val);
+}

+ 39 - 0
DataManagerMain/DataConsumer.h

@@ -0,0 +1,39 @@
+#ifndef DATACONSUMER_H
+#define DATACONSUMER_H
+
+#include <string>
+#include <QVariant>
+#include "Define.h"
+#include <QThread>
+#include <QLibrary>
+class Client;
+class DataConsumer:public QObject//, public BaseModule
+{
+    Q_OBJECT
+public:
+    DataConsumer();
+    ~DataConsumer();
+    // std::string dataName;
+    std::unordered_multimap<std::string, std::string>topicsMap;
+    virtual void Setup(ConsumerInfo& ci);
+
+    virtual std::string getTypeList();
+
+    //virtual void setLoader(QLibrary*);
+
+    virtual void Run() ;
+// public slots:
+    // virtual
+    void OnData(const QString& ,const QString& ,const QVariant&);
+//private:
+    //virtual void run();
+private:
+    QLibrary* library = nullptr;
+    DataConsumer* dataConsumer = nullptr;
+    //std::string dataName;
+    ConsumerInfo ci;
+    Client* runable = nullptr;
+
+};
+
+#endif // DATACONSUMER_H

+ 57 - 0
DataManagerMain/DataManager.cpp

@@ -0,0 +1,57 @@
+#include "DataManager.h"
+#include "Define.h"
+#include "DataManagerProxy.h"
+#include "DataSubscribe.h"
+#include "DataConsumer.h"
+#include <QDebug>
+
+DataManager::DataManager() {
+
+}
+
+DataManager::~DataManager()
+{
+    /*for(auto it = consumerMap.begin(); it != consumerMap.end(); ++it){
+        delete it.value();
+    }*/
+}
+
+// void DataManager::OnData(std::string key, QVariant val)
+// {
+//     //consumerMap[key]->OnData(key, val);
+// }
+
+void DataManager::Startup(Config& config)
+{
+    //Config config;
+    DataManagerProxy dmp;
+    auto lstModules = dmp.loadModuleInfos(config);
+    for(auto itModules = lstModules.begin(); itModules != lstModules.end(); itModules++){
+        for(auto itr = itModules->consumers.begin(); itr != itModules->consumers.end(); ++itr){
+            //ScopedPointer<DataConsumer> consumer {new DataConsumer()};
+            DataConsumer * consumer = new DataConsumer();
+            ConsumerInfo ci = *itr;
+            consumer->Setup(ci);
+            consumerMap.insert(QString(itr->SubscribeName.c_str()),consumer);
+        }
+
+        for(auto itr = itModules->modules.begin();itr!= itModules->modules.end();++itr)
+        {
+            ModuleInfo mi = *itr;
+            if(consumerMap.contains(itr->Name.c_str())){
+                //QScopedPointer<DataSubscribe> subscriber(new DataSubscribe());
+                DataSubscribe* subscriber = new DataSubscribe();
+
+                auto mapIt = consumerMap.find(itr->Name.c_str());
+                while(mapIt != consumerMap.end() && mapIt.key() == itr->Name.c_str()){
+                    subscriber->regConsumer(mapIt.value());
+                    mapIt++;
+                }
+                subscriber->Setup(mi);
+                //subscriber->Run();
+                subscribers.append(subscriber);
+            }
+        }
+    }
+
+}

+ 24 - 0
DataManagerMain/DataManager.h

@@ -0,0 +1,24 @@
+#ifndef DATAMANAGER_H
+#define DATAMANAGER_H
+#include <QVariant>
+#include <QMap>
+#include <QList>
+#include "DataConsumer.h"
+#include "DataSubscribe.h"
+#include <QSharedDataPointer>
+#include "Config.h"
+class DataManager
+{
+public:
+    DataManager();
+    ~DataManager();
+public:
+    //virtual void OnData(std::string key, QVariant val);
+    void Startup(Config& config);
+
+private:
+    QMultiMap<QString, DataConsumer*> consumerMap;
+    QList<DataSubscribe*> subscribers;
+};
+
+#endif // DATAMANAGER_H

+ 60 - 0
DataManagerMain/DataManagerMain.pro

@@ -0,0 +1,60 @@
+QT = core
+
+CONFIG += c++17 cmdline
+
+# You can make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+INCLUDEPATH += ../include/
+
+HEADERS += \
+    ../include/BaseModule.h \
+    ../include/Define.h \
+    ../include/JobModule.h \
+    ../include/LibraryLoader.h \
+    ../include/RunnableModule.h \
+    ../include/Publisher.h \
+    Config.h
+
+
+HEADERS += \
+    DataConsumer.h \
+    DataManager.h \
+    DataManagerProxy.h \
+    DataSubscribe.h
+
+SOURCES += \
+        Config.cpp \
+        DataConsumer.cpp \
+        DataManager.cpp \
+        DataManagerProxy.cpp \
+        DataSubscribe.cpp \
+        main.cpp
+
+HIREDIS_DIR =$$PWD/../thirdparty/hiredis
+HEADERS += $${HIREDIS_DIR}/include/hiredis/adapters/qt.h
+
+LIBS += -L$${HIREDIS_DIR}/lib -lhiredis
+#LIBS += ../thirdparty/hiredis/lib/libhiredis.a
+INCLUDEPATH += $${HIREDIS_DIR}/include
+
+INCLUDEPATH += $$PWD/../modules/RedisClient
+LIBS +=-L$$PWD/../bin/plugins -lRedisClient
+
+TRANSLATIONS += \
+    DataManager_zh_CN.ts
+CONFIG += lrelease
+CONFIG += embed_translations
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
+
+unix{
+}
+else{
+    DESTDIR = $$PWD/../bin/
+}
+
+

+ 138 - 0
DataManagerMain/DataManagerProxy.cpp

@@ -0,0 +1,138 @@
+#include "DataManagerProxy.h"
+#include "RedisClient.h"
+#include <QJsonArray>
+#include <QJsonObject>
+DataManagerProxy::DataManagerProxy() {
+
+}
+
+QList<DataManagerInfo> DataManagerProxy::loadModuleInfos(const Config& config)
+{
+    RedisClient redis;
+    QList<DataManagerInfo > listDataManageInfo;
+    // QString key = QString("%1:%2").arg(config.AppName, config.AppId);
+    QString key = config.Service;
+    DeviceInfo di;
+
+    redis.Setup(config.redisCfg.host, config.redisCfg.port, config.redisCfg.au);
+    // QHash<QString, QString> h = redis->hgetall(key);
+    // for(auto it = h.begin(); it != h.end(); it++)
+    // {
+    //     qDebug()<< "key" << it.key();
+    //     //qDebug()<< "val" << it.value();
+    //     QJsonArray ja = QJsonDocument::fromJson(it.value().toUtf8()).array();
+    //     // QJsonObject jo = QJsonDocument::fromJson(it.value().toUtf8()).object();
+    // }
+
+    QStringList lst = redis.hvals(key);
+    foreach (QString str, lst)
+    {
+        if( str.isEmpty() )
+        {
+            continue;
+        }
+        QJsonParseError jsonParseError;
+        QJsonDocument jsonDocument(QJsonDocument::fromJson(str.toUtf8(), &jsonParseError));
+        if(QJsonParseError::NoError != jsonParseError.error)
+        {
+            //LOGERROR("parse json file {} error", fullpath.toStdString().c_str());
+            continue;
+        }
+        if(!jsonDocument.isObject()){
+            continue;
+        }
+        // QJsonArray ja = jsonDocument.array();
+        QJsonObject jo = jsonDocument.object();
+        if(!jo.contains("config")){
+            continue;
+        }
+        QJsonObject jConfig = jo["config"].toObject();
+        ModuleInfo mi;
+        // mi.Name = "TDengineSubscriber";
+        mi.AssemblyName = "plugins/TDengineSubscriber.dll";
+        // mi.Name = jConfig["name"].toString().toStdString();
+        mi.Name = jConfig["code"].toString().toStdString();
+        // mi.AssemblyName = jConfig["model"].toString().toStdString();
+        QJsonObject jSettings = jConfig["settings"].toObject();
+        QJsonArray jInputs = jConfig["inputs"].toArray();
+
+        auto parse = [](const QJsonArray& jInputs)
+            ->std::tuple<std::list<std::string>, std::set<std::string>, std::unordered_multimap<std::string, std::string>>{
+            std::list<std::string>inputLists;
+            std::set<std::string>tables;
+            std::unordered_multimap<std::string,std::string>inputsMap;
+            foreach (auto var, jInputs) {
+                QString item = var.toString();
+                inputLists.push_back(item.toLocal8Bit().toStdString());
+                QStringList strlist = item.split(QLatin1Char('.'), Qt::SkipEmptyParts);
+                tables.insert(strlist.at(0).toStdString());
+                inputsMap.insert({strlist.at(0).toStdString(),strlist.at(1).toStdString()});
+            }
+            return {inputLists, tables, inputsMap};
+        };
+        auto [inputLists, tables, inputsMap] = parse(jInputs);
+        mi.Topics = tables;
+
+        ConsumerInfo ci;
+        ci.Name = jConfig["code"].toString().toStdString();
+        ci.AssemblyName = jConfig["model"].toString().toStdString();
+        ci.SubscribeName = mi.Name;
+        ci.dataItems = inputLists;
+        ci.topicsMap = inputsMap;
+        QJsonDocument doc(jSettings);
+        QString js(doc.toJson(QJsonDocument::Compact));
+        ci.Settings = js.toStdString();
+
+        DataManagerInfo dataManageInfo;
+        dataManageInfo.consumers.push_back(ci);
+        dataManageInfo.modules.push_back(mi);
+        listDataManageInfo.push_back(dataManageInfo);
+
+
+        // foreach (auto var, ja) {
+        //     QJsonObject item = var.toObject();
+        //     QString topic = item["topic"].toString();
+        //     QJsonArray clients = item["Client"].toArray();
+        //     DataManagerInfo dataManageInfo;
+        //     ModuleInfo mi;
+        //     mi.Name = "TDengineSubscriber";
+        //     mi.AssemblyName = "plugins/TDengineSubscriber.dll";
+        //     // mi.ClassName = "TDengineSubscriber";
+        //     mi.Code = topic.toStdString();
+        //     mi.Topic = topic.toStdString();
+        //     foreach (auto client, clients) {
+        //         ConsumerInfo ci;
+        //         ci.Name = client["Name"].toString().toStdString();
+        //         ci.AssemblyName = client["AssemblyName"].toString().toStdString();
+        //         //ci.ClassName = "Alarm";
+        //         ci.SubscribeName = topic.toStdString();
+        //         ci.Settings = client["Settings"].toVariant().toString().toStdString();
+        //         dataManageInfo.consumers.push_back(ci);
+        //     }
+        //     dataManageInfo.modules.push_back(mi);
+        //     listDataManageInfo.push_back(dataManageInfo);
+        // }
+    }
+
+#if 0
+
+            ModuleInfo mi;
+            mi.Name = "TDengineSubscriber";
+            mi.AssemblyName = "plugins/TDengineSubscriber.dll";
+            mi.ClassName = "TDengineSubscriber";
+            mi.Code = "topic:xxx";
+
+            ConsumerInfo ci;
+            ci.Name = "Alarm";
+            ci.AssemblyName = "plugins/Alarm.dll";
+            ci.ClassName = "Alarm";
+            ci.SubscribeName = "TDengineSubscriber";
+            DataManagerInfo dataManageInfo;
+            dataManageInfo.consumers.push_back(ci);
+            dataManageInfo.modules.push_back(mi);
+            listDataManageInfo.push_back(dataManageInfo);
+#endif
+    //return di;
+
+    return listDataManageInfo;
+}

+ 16 - 0
DataManagerMain/DataManagerProxy.h

@@ -0,0 +1,16 @@
+#ifndef DATAMANAGERPROXY_H
+#define DATAMANAGERPROXY_H
+#include "Define.h"
+#include <QMap>
+#include <QList>
+#include "Config.h"
+class DataManagerProxy
+{
+public:
+    DataManagerProxy();
+
+public:
+    QList<DataManagerInfo> loadModuleInfos(const Config& config);
+};
+
+#endif // DATAMANAGERPROXY_H

+ 3 - 0
DataManagerMain/DataManager_zh_CN.ts

@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="zh_CN"></TS>

+ 79 - 0
DataManagerMain/DataSubscribe.cpp

@@ -0,0 +1,79 @@
+#include "DataSubscribe.h"
+#include <QVariant>
+#include "LibraryLoader.h"
+#include "Publisher.h"
+#include <QDebug>
+DataSubscribe::DataSubscribe()
+{
+    //shares = new SharedData();
+}
+
+DataSubscribe::~DataSubscribe()
+{
+    //delete shares;
+}
+
+
+void DataSubscribe::Setup(ModuleInfo &mi)
+{
+    std::string assemblyName = mi.AssemblyName;
+    dataName = mi.Name;
+    Publisher* pModule = nullptr;// = (BaseModule)Assembly.LoadFile(Path.GetFullPath(@".\" + assemblyName)).CreateInstance(className);
+    pModule = LibraryLoader::load<Publisher>(assemblyName);
+    if( pModule == nullptr )
+    {
+        //        qCritical() << LOG_HEADER << " load " << szPlugin.c_str() << " failed.";
+        return;
+    }
+    if( pModule == nullptr )
+    {
+
+        qCritical() << __FILE__ << __LINE__ << " " << assemblyName.c_str() << " load failed.";
+        return;
+    }
+
+    {
+        Publisher* runable = dynamic_cast<Publisher*>(pModule);
+        //runable->shares(shares);
+
+        connect(runable, SIGNAL(pubData(const QString& ,const QString& ,const QVariant& )), this, SLOT(OnData(const QString& ,const QString& ,const QVariant&)));
+        // ------------- 由于tdengine订阅模块是单例运行,消息发送是广播发送 -------------
+        // for(auto it =  dataConsumerList.begin(); it != dataConsumerList.end(); it++){
+        //     connect(runable, SIGNAL(pubData(const QString& ,const QString& ,const QVariant& )), *it, SLOT(OnData(const QString& ,const QString& ,const QVariant&)));
+        // }
+        // ______________
+        runable->Run(mi);
+    }
+}
+
+
+
+std::string DataSubscribe::getTypeList()
+{
+    return "DataStorage::RunnableModule";
+}
+
+// void DataSubscribe::setLoader(QLibrary *)
+// {
+
+// }
+
+void DataSubscribe::regConsumer(DataConsumer *dc)
+{
+    dataConsumerList.append(dc);
+}
+
+void DataSubscribe::OnData(const QString &user, const QString &key, const QVariant &val)
+{
+    for(auto it =  dataConsumerList.begin(); it != dataConsumerList.end(); it++){
+        if((*it)->topicsMap.count(key.toStdString().c_str()) ){
+            (*it)->OnData(user, key, val);
+        }
+    }
+}
+
+
+
+
+
+

+ 37 - 0
DataManagerMain/DataSubscribe.h

@@ -0,0 +1,37 @@
+#ifndef DATASUBSCRIBE_H
+#define DATASUBSCRIBE_H
+
+//#include "BaseModule.h"
+#include <QtCore/QThread>
+#include "DataConsumer.h"
+#include "Define.h"
+#include <QDateTime>
+
+class SharedData;
+
+class DataSubscribe : public QObject//, public BaseModule
+{
+    Q_OBJECT
+public:
+    DataSubscribe();
+    ~DataSubscribe();
+    virtual void Setup(ModuleInfo& mi);
+
+    virtual std::string getTypeList();
+
+    virtual void regConsumer(DataConsumer* dc) ;
+public slots:
+    virtual  void OnData(const QString& user, const QString& key, const QVariant& val);
+    //void OnData(QString, QString, QVariant);
+// private:
+//     virtual void setLoader(QLibrary*);
+private:
+    QLibrary* library = nullptr;
+    QList<DataConsumer*> dataConsumerList;
+    std::string dataName;
+    ModuleInfo mi;
+    SharedData* shares;
+
+};
+
+#endif // DATASUBSCRIBE_H

+ 33 - 0
DataManagerMain/main.cpp

@@ -0,0 +1,33 @@
+#include <QCoreApplication>
+#include <QLocale>
+#include <QTranslator>
+#include "DataManager.h"
+#include <QCommandLineParser>
+#include <QCommandLineOption>
+#include "Config.h"
+int main(int argc, char *argv[])
+{
+    QCoreApplication a(argc, argv);
+
+    QTranslator translator;
+    const QStringList uiLanguages = QLocale::system().uiLanguages();
+    for (const QString &locale : uiLanguages) {
+        const QString baseName = "DataManager_" + QLocale(locale).name();
+        if (translator.load(":/i18n/" + baseName)) {
+            a.installTranslator(&translator);
+            break;
+        }
+    }
+    QCommandLineOption op1("config","config file path","config[filepath]", "config/config.json");
+    QCommandLineParser parser;
+    parser.addOption(op1);
+    parser.addHelpOption();
+
+    parser.process(a);
+    Config config(parser.value("config"));
+
+    DataManager dataMgr;
+    dataMgr.Startup(config);
+
+    return a.exec();
+}

+ 25 - 0
DataManater.pro

@@ -0,0 +1,25 @@
+TEMPLATE = subdirs
+CONFIG += ordered
+
+SUBDIRS += \
+    #DataConsumer \
+    #DataSubscribe \
+    modules/RedisClient \
+    modules/TDengineClient \
+    modules/MQTTClient \
+    DataConsumer/TDengine \
+    DataConsumer/Trans2Mqtt \
+    DataManagerMain/DataManagerMain.pro \
+    DataSubscribe/RedisSubscriber   \
+    DataSubscribe/TDengineSubscriber
+    
+
+
+# HEADERS += \
+#     include/BaseModule.h \
+#     include/Define.h \
+#     include/JobModule.h \
+#     include/LibraryLoader.h \
+#     include/RunnableModule.h \
+#     include/Publisher.h \
+#     include/Client.h

+ 109 - 0
DataSubscribe/RedisSubscriber/RedisSubscriber.cpp

@@ -0,0 +1,109 @@
+#include "RedisSubscriber.h"
+#include <QDebug>
+RedisSubscriber::RedisSubscriber() {
+
+}
+
+
+#define REDISTOPIC "test"
+
+static void fnRedisCallback(redisAsyncContext* ctx, void* r, void* data){
+    redisReply *reply = static_cast<redisReply*>(r);
+    RedisSubscriber* subscriber = static_cast<RedisSubscriber*>(data);
+    if(reply == nullptr){
+        qDebug() << "The reply is nullptr";
+        return;
+    }
+
+    qDebug() << reply->element[0]->str << " "
+             << reply->element[1]->str << " "
+             << reply->element[2]->str << " ";
+
+    if (QString(reply->element[0]->str) == "message"){ //收到订阅消息
+
+        QString ch = QString(reply->element[1]->str);
+        //        QString cmd = QString(reply->element[2]->str);
+        QString js = QString(reply->element[2]->str);
+
+        //qDebug() << js;
+        QJsonDocument doc = QJsonDocument::fromJson(js.toLatin1());
+
+        if(ch == REDISTOPIC){
+            QJsonObject jsonObj = doc.object(); //转成本地编码(utf-8), 中文正常
+            if(jsonObj["engine"].toString().toLower() == "on"){
+                subscriber->enqueue(ch, js);
+            }else if(jsonObj["engine"].toString().toLower() == "off"){
+                subscriber->enqueue(ch, js);
+            }else{
+                subscriber->enqueue(ch, js);
+            }
+        }else{
+            subscriber->enqueue(ch, js);
+        }
+    }else{
+        qDebug() << "NOT A MESSAGE";
+    }
+}
+
+void RedisSubscriber::enqueue(const QString &key, const QString &val)
+{
+    emit pubData("redis", key, val);
+
+    /*mutex.lock();
+    msgQueue.push_back({key, val});
+    cond.wakeAll();
+    mutex.unlock();*/
+
+}
+
+void RedisSubscriber::Run(const ModuleInfo& mi)
+{
+    //subscribe redis topic
+    redis = new RedisClient();
+    redis->start();
+    redis->subscribe(mi.Topics.begin()->c_str(), fnRedisCallback, this);
+    //start();
+}
+
+void RedisSubscriber::setLoader(QLibrary *)
+{
+
+}
+
+void RedisSubscriber::run(){
+    QThread::msleep(1000);
+    //redis message emit to onData signal.
+    /*while(!isInterruptionRequested()){
+        qDebug() << __FILE__ << __FUNCTION__;
+        QString usr = "Redis";
+        QString key = "key";
+        QVariant val = "val";
+        Msg msg;
+        mutex.lock();
+        if(cond.wait(&mutex), 500)
+        {
+            msg = msgQueue.dequeue();
+            emit pubData(usr, msg.key, msg.val);
+            qDebug() << __FILE__ << __FUNCTION__;
+        };
+
+        mutex.unlock();
+
+        //QThread::msleep(1000);
+    }*/
+}
+
+
+Publisher* instance()
+{
+    return new RedisSubscriber();
+}
+
+void destroy(Publisher* pInstance)
+{
+    if( pInstance )
+    {
+        delete pInstance;
+    }
+}
+

+ 36 - 0
DataSubscribe/RedisSubscriber/RedisSubscriber.h

@@ -0,0 +1,36 @@
+#pragma once
+#include "RedisSubscriber_global.h"
+#include "Publisher.h"
+#include "RedisClient.h"
+#include <QQueue>
+#include <QMutex>
+#include <QWaitCondition>
+struct Msg{
+    QString key;
+    QString val;
+};
+class REDISSUBSCRIBER_EXPORT RedisSubscriber : public Publisher
+{
+    Q_OBJECT
+public:
+    RedisSubscriber();
+    // virtual void shares(SharedData * share);
+    virtual void Run(const ModuleInfo& mi);
+    virtual void setLoader(QLibrary*);
+    void enqueue(const QString& key,const QString& val);
+protected:
+    virtual void run();
+signals:
+    void pubData(const QString& ,const QString& ,const QVariant&);
+private:
+    RedisClient* redis;
+    QMutex mutex;
+    QWaitCondition cond;
+    QQueue<Msg> msgQueue;
+};
+
+
+extern "C" {//一定要添加上
+REDISSUBSCRIBER_EXPORT Publisher* instance();
+REDISSUBSCRIBER_EXPORT void destroy(Publisher*);
+}

+ 43 - 0
DataSubscribe/RedisSubscriber/RedisSubscriber.pro

@@ -0,0 +1,43 @@
+QT -= gui
+
+TEMPLATE = lib
+DEFINES += REDISSUBSCRIBER_LIBRARY
+
+CONFIG += c++17
+
+# You can make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+INCLUDEPATH += ../../include
+
+HEADERS += \
+    ../../include/LibraryLoader.h \
+    ../../include/Publisher.h
+    #Redis.h
+
+HIREDIS_DIR =$$PWD/../../thirdparty/hiredis
+HEADERS += $${HIREDIS_DIR}/include/hiredis/adapters/qt.h
+
+LIBS += -L$${HIREDIS_DIR}/lib -lhiredis
+#LIBS += ../thirdparty/hiredis/lib/libhiredis.a
+INCLUDEPATH += $${HIREDIS_DIR}/include
+
+SOURCES += \
+    #Redis.cpp \
+    RedisSubscriber.cpp
+
+HEADERS += \
+    RedisSubscriber_global.h \
+    RedisSubscriber.h
+
+INCLUDEPATH += $$PWD/../../modules/RedisClient
+LIBS +=-L$$PWD/../../bin/plugins -lRedisClient
+
+# Default rules for deployment.
+unix {
+    target.path = /usr/lib
+}else{
+    DESTDIR = $$PWD/../../bin/plugins
+}
+!isEmpty(target.path): INSTALLS += target

+ 12 - 0
DataSubscribe/RedisSubscriber/RedisSubscriber_global.h

@@ -0,0 +1,12 @@
+#ifndef REDISSUBSCRIBER_GLOBAL_H
+#define REDISSUBSCRIBER_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(REDISSUBSCRIBER_LIBRARY)
+#define REDISSUBSCRIBER_EXPORT Q_DECL_EXPORT
+#else
+#define REDISSUBSCRIBER_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // REDISSUBSCRIBER_GLOBAL_H

+ 87 - 0
DataSubscribe/TDengineSubscriber/TDengineSubscriber.cpp

@@ -0,0 +1,87 @@
+#include "TDengineSubscriber.h"
+#include <QDebug>
+TDengineSubscriber::TDengineSubscriber() {
+
+}
+
+TDengineSubscriber::~TDengineSubscriber()
+{
+    if(tdengine){
+        delete tdengine;
+        tdengine = nullptr;
+    }
+}
+
+
+#define REDISTOPIC "test"
+
+static void fnRedisCallback(const char* topic, const char* data, void* usr){
+
+    TDengineSubscriber* subscriber = static_cast<TDengineSubscriber*>(usr);
+    subscriber->enqueue(topic, data);
+}
+
+void TDengineSubscriber::enqueue(const QString &key, const QString &val)
+{
+    emit pubData("tdengine", key, val);
+    /*mutex.lock();
+    msgQueue.push_back({key, val});
+    cond.wakeAll();
+    mutex.unlock();*/
+}
+
+void TDengineSubscriber::Run(const ModuleInfo& mi)
+{
+    //subscribe redis topic
+    tdengine = new TDengineClient();
+    tdengine->subscribe(mi.Topics, fnRedisCallback, this);
+    tdengine->start();
+    //start();
+}
+
+void TDengineSubscriber::setLoader(QLibrary *)
+{
+
+}
+
+void TDengineSubscriber::run(){
+    QThread::msleep(1000);
+
+
+    //redis message emit to onData signal.
+    /*while(!isInterruptionRequested()){
+        QString usr = "Redis";
+        QString key = "key";
+        QVariant val = "val";
+        Msg msg;
+        mutex.lock();
+        if(cond.wait(&mutex), 500)
+        {
+            msg = msgQueue.dequeue();
+            emit pubData(usr, msg.key, msg.val);
+            qDebug() << __FILE__ << __FUNCTION__;
+        }
+
+        mutex.unlock();
+
+
+
+        //QThread::msleep(1000);
+    }*/
+
+}
+
+
+Publisher* instance()
+{
+    return new TDengineSubscriber();
+}
+
+void destroy(Publisher* pInstance)
+{
+    if( pInstance )
+    {
+        delete pInstance;
+    }
+}
+

+ 37 - 0
DataSubscribe/TDengineSubscriber/TDengineSubscriber.h

@@ -0,0 +1,37 @@
+#pragma once
+#include "TDengineSubscriber_global.h"
+#include "Publisher.h"
+#include "TDengineClient.h"
+#include <QQueue>
+#include <QMutex>
+#include <QWaitCondition>
+struct Msg{
+    QString key;
+    QString val;
+};
+class TDENGINESUBSCRIBER_EXPORT TDengineSubscriber : public Publisher
+{
+    Q_OBJECT
+public:
+    TDengineSubscriber();
+    ~TDengineSubscriber();
+    // virtual void shares(SharedData * share);
+    virtual void Run(const ModuleInfo& mi);
+    virtual void setLoader(QLibrary*);
+    void enqueue(const QString& key,const QString& val);
+protected:
+    virtual void run();
+signals:
+    void pubData(const QString& ,const QString& ,const QVariant&);
+private:
+    TDengineClient* tdengine = nullptr;
+    QMutex mutex;
+    QWaitCondition cond;
+    QQueue<Msg> msgQueue;
+};
+
+
+extern "C" {//一定要添加上
+TDENGINESUBSCRIBER_EXPORT Publisher* instance();
+TDENGINESUBSCRIBER_EXPORT void destroy(Publisher*);
+}

+ 40 - 0
DataSubscribe/TDengineSubscriber/TDengineSubscriber.pro

@@ -0,0 +1,40 @@
+QT -= gui
+
+TEMPLATE = lib
+DEFINES += TDENGINESUBSCRIBER_LIBRARY
+
+CONFIG += c++17
+
+# You can make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+INCLUDEPATH += ../../include
+
+HEADERS += \
+    ../../include/LibraryLoader.h \
+    ../../include/Publisher.h
+    #Redis.h
+
+SOURCES += \
+    TDengineSubscriber.cpp
+    #Redis.cpp \
+
+HEADERS += \
+    TDengineSubscriber.h \
+    TDengineSubscriber_global.h
+
+TDENGINE_DIR =$$PWD/../../thirdparty/tdengine
+LIBS += -L$${TDENGINE_DIR}/lib -ltaos
+INCLUDEPATH += $${TDENGINE_DIR}/include
+INCLUDEPATH += $$PWD/../../modules/TDengineClient
+LIBS +=-L$$PWD/../../bin/plugins -lTDengineClient
+
+
+# Default rules for deployment.
+unix {
+    target.path = /usr/lib
+}else{
+    DESTDIR = $$PWD/../../bin/plugins
+}
+!isEmpty(target.path): INSTALLS += target

+ 12 - 0
DataSubscribe/TDengineSubscriber/TDengineSubscriber_global.h

@@ -0,0 +1,12 @@
+#ifndef TDENGINESUBSCRIBER_GLOBAL_H
+#define TDENGINESUBSCRIBER_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(TDENGINESUBSCRIBER_LIBRARY)
+#define TDENGINESUBSCRIBER_EXPORT Q_DECL_EXPORT
+#else
+#define TDENGINESUBSCRIBER_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // TDENGINESUBSCRIBER_GLOBAL_H

+ 16 - 0
doc/config/config.json

@@ -0,0 +1,16 @@
+{
+	"AppName":"DataMgr",
+	"AppId":"R001",
+	"redis":{
+		"host":"192.168.9.6",
+		"port":6379,
+		"au":"FRERzN2z9*$D=7WH@@be"
+	},
+	"tdengine":{
+		"host":"192.168.9.6",
+		"port":6030,
+		"usr":"root",
+		"au":"x=gheLw7QMAD4zjQh3d9"
+	}
+	
+}

+ 42 - 0
doc/config/configs.json

@@ -0,0 +1,42 @@
+{
+  "id": 25,
+  "name": "传输25",
+  "moduleInfoId": 1000016,
+  "code": "tr0025",
+  "description": "传输25",
+  "transferConfig": {
+    "name": "mqtt转发",
+    "code": "Trans2Mqtt",
+    "model": "plugins/Trans2Mqtt.dll",
+    "type": "ModuleType::Transfer",
+    "settings": {
+      "host": "127.0.0.1",
+      "port": "3366",
+      "usr": "4456",
+      "passwd": "4566"
+    },
+    "inputs": [
+      "1.1",
+      "2.2"
+    ],
+    "args": [
+      {
+        "name": "x",
+        "value": 100
+      },
+      {
+        "name": "y",
+        "value": 200
+      }
+    ],
+    "outputs": [
+      
+    ]
+  },
+  "delFlag": "0",
+  "createTime": 1708424991000,
+  "updateTime": 1708429399000,
+  "params": {
+    
+  }
+}

+ 42 - 0
doc/config/configs2.json

@@ -0,0 +1,42 @@
+{
+  "id": 25,
+  "name": "传输25",
+  "moduleInfoId": 1000016,
+  "code": "tr0025",
+  "description": "传输25",
+  "delFlag": "0",
+  "createTime": 1708424991000,
+  "updateTime": 1708483312000,
+  "params": {
+    
+  },
+  "config": {
+    "name": "mqtt转发",
+    "code": "Trans2Mqtt",
+    "model": "plugins/Trans2Mqtt.dll",
+    "type": "ModuleType::Transfer",
+    "settings": {
+      "host": "127.0.0.1",
+      "port": "3366",
+      "usr": "4456",
+      "passwd": "4566"
+    },
+    "inputs": [
+      "device1.dim1",
+      "device2.dim2"
+    ],
+    "args": [
+      {
+        "name": "x",
+        "value": 100
+      },
+      {
+        "name": "y",
+        "value": 200
+      }
+    ],
+    "outputs": [
+      
+    ]
+  }
+}

+ 98 - 0
doc/config/module_config.json

@@ -0,0 +1,98 @@
+{
+    "name":"mqtt转发",
+    "code":"Trans2Mqtt",
+    "model":"plugins/Trans2Mqtt.dll",
+    "type":"ModuleType::Transfer",
+    "settings":[
+        {
+            "name":"host",
+            "text":"主机",
+            "dataType":"string",
+            "readonly":false,
+            "inputType":"text"
+        },
+        {
+            "name":"port",
+            "text":"端口",
+            "dataType":"int",
+            "readonly":false,
+            "inputType":"text"
+        },
+        {
+            "name":"usr",
+            "text":"用户名",
+            "dataType":"string",
+            "readonly":false,
+            "inputType":"text"
+        },
+        {
+            "name":"passwd",
+            "text":"密码",
+            "dataType":"string",
+            "readonly":false,
+            "inputType":"text"
+        }
+		
+    ],
+    "inputs": [
+    {
+      "name": "inputsitems",
+      "text": "输入数据项",
+      "inputType": "table",
+      "fields": [
+        {
+          "name": "id",
+          "text": "序号",
+          "dataType": "string",
+          "inputType": "text"
+        },
+        {
+          "name": "step",
+          "text": "源步骤",
+          "dataType": "string",
+          "inputType": "text"
+        },
+        {
+          "name": "dataitem",
+          "text": "数据项",
+          "dataType": "string",
+          "inputType": "text"
+        },
+        {
+          "name": "code",
+          "text": "数据标识",
+          "dataType": "string",
+          "inputType": "text"
+        },
+        {
+          "name": "unit",
+          "text": "数据单位",
+          "dataType": "string",
+          "inputType": "text"
+        },
+        {
+          "name": "discription",
+          "text": "变量名",
+          "dataType": "string",
+          "inputType": "text"
+        }
+      ],
+      "props": {
+        "allowAdd": "true"
+      }
+    }
+  ],
+    "args":[
+        {
+            "name":"x",
+            "value":100
+        },
+        {
+            "name":"y",
+            "value":200
+        }
+    ],
+    "outputs":[
+        
+    ]
+}

+ 64 - 0
doc/config/结构说明.json

@@ -0,0 +1,64 @@
+{
+    "name":"一条报警灯控制方案",
+	"code":"l3a",
+	"model":"",
+	"type":"控制组件",
+	"moduleassembly":"l3a.dll",
+	"moduleclass":"l3a",
+	"props":[],
+	"settings":
+	[
+		{
+		    "object": "d1",
+		    "operator": "beyound",
+		    "operator1": 15.5,
+		    "operator2": 0.0,
+		    "target": "device1",
+			"action": "openredandsound"
+	    },
+		{
+		    "object": "d1",
+		    "operator": "below",
+		    "operator1": 14.5,
+		    "operator2": 0.0,
+		    "target": "device1",
+			"action": "openyellowandsound"
+	    },
+		{
+		    "object": "d2",
+		    "operator": "beyound",
+		    "operator1": 25.5,
+		    "operator2": 0.0,
+		    "target": "device2",
+			"action": "openredandsound"
+	    },
+		{
+		    "object": "d2",
+		    "operator": "below",
+		    "operator1": 24.5,
+		    "operator2": 0.0,
+		    "target": "device2",
+			"action": "openyellowandsound"
+	    }
+	],
+	"inputs":
+	[
+		{
+		    "nodename":"净化节点1",
+			"nodetype":"净化模块",
+			"nodecode":"purify1",
+		    "name":"直径1",
+			"code":"diameter1",
+			"parameter":"d1"
+		},
+		{
+		    "nodename":"计算节点1",
+			"nodetype":"计算模块",
+			"nodecode":"calculator1",
+		    "name":"直径1",
+			"code":"diameter1",
+			"parameter":"d2"
+		}
+	],
+	"outputs":[]
+}

+ 22 - 0
include/BaseModule.h

@@ -0,0 +1,22 @@
+#pragma once
+#include "Define.h"
+#include <QtCore/QLibrary>
+#include <string>
+#include <QString>
+#include <QVariant>
+
+class Receiver
+{
+public:
+    virtual void OnData(const QString& user, const QString& key, const QVariant& val) = 0;
+};
+
+
+class BaseModule
+{
+public:
+    BaseModule(){}
+    virtual ~BaseModule(){};
+    virtual void setLoader(QLibrary*) = 0;
+    virtual std::string getTypeList() = 0;
+};

+ 17 - 0
include/Client.h

@@ -0,0 +1,17 @@
+#pragma once
+#include <QtCore/QThread>
+#include <QString>
+#include <QtCore/QLibrary>
+#include "Define.h"
+class Client{
+
+public:
+    Client(){}
+    virtual ~Client(){}
+    virtual void setLoader(QLibrary*) = 0;
+    virtual void OnData(const QString& user, const QString& key, const QVariant& val) = 0;
+    virtual void Run(const ConsumerInfo& ci) = 0;
+};
+
+
+

+ 88 - 0
include/Define.h

@@ -0,0 +1,88 @@
+#pragma once
+
+#include <string>
+#include <list>
+#include <set>
+#include <unordered_map>
+
+enum ModuleType
+{
+    Sensor=1,
+    Actor=2,
+    Purifier=3,
+    Calculator=4,
+    Storage=5,
+    Transfer=6,
+};
+
+struct DataItem
+{
+    int Id;
+    std::string DataName;
+    std::string Code;
+    std::string DataType;
+    bool Displayed;
+    std::string Description;
+    std::string ToString()
+    {
+        return Code + DataName;
+    }
+};
+
+struct ModuleInfo
+{
+    // int Id;
+    // ModuleType Type;                // 类型
+    std::string Name;               // 名称
+    // std::string Code;               // 标识码
+    // std::string Model;              // 型号
+    std::string AssemblyName;       // 组件程序(dll、jar)名字,含路径
+    // std::string ClassName;          // 组件的 类名
+    // std::string Description;        // 描述
+    // std::string Version;            // 版本
+    std::set<std::string> Topics;
+};
+
+struct DeviceInfo
+{
+    int Id;
+    std::string Name;                // 名称
+    std::string Type;                // 类型
+    std::string Code;                // 标识码
+    std::string Model;               // 型号,通信组件的Code属性
+    int ServerId;                    //
+    ModuleInfo ModuleInfo;           //
+    std::string Description;         // 描述
+    std::list<DataItem> Properties;
+};
+
+struct ConsumerInfo{
+    std::string Name;
+    std::string AssemblyName;       // 组件程序(dll、jar)名字,含路径
+    // std::string ClassName;          // 组件的 类名
+    std::string SubscribeName;
+    std::string Settings;
+    std::list<std::string>dataItems;
+    std::unordered_multimap<std::string, std::string>topicsMap;
+};
+
+struct DataManagerInfo{
+    int Id;
+    std::string Name;                // 名称
+    std::string Type;                // 类型
+    std::string Code;                // 标识码
+    std::string Model;               // 型号,通信组件的Code属性
+    int ServerId;                    //
+    std::string Description;         // 描述
+    std::list<ModuleInfo> modules;
+    std::list<ConsumerInfo>consumers;
+};
+
+
+/*struct Config
+{
+    int serverId;
+    std::string appName;
+};*/
+
+

+ 9 - 0
include/JobModule.h

@@ -0,0 +1,9 @@
+#pragma once
+#include "BaseModule.h"
+#include <QtCore/QObject>
+
+class JobModule : public BaseModule
+{
+public:
+    virtual QObject Process(QObject data) = 0;
+};

+ 91 - 0
include/LibraryLoader.h

@@ -0,0 +1,91 @@
+#pragma once
+
+#include <QtCore/QLibrary>
+#include <QtCore/QFile>
+#include <QtCore/QDebug>
+
+class LibraryLoader
+{  
+private:
+   LibraryLoader()
+   {
+   }  
+  
+   ~LibraryLoader()
+   {
+   }  
+
+public:
+    template <typename T,typename D>
+    static D* to(T* t)
+    {
+        return dynamic_cast<D*>(t);
+    }
+
+
+    template <typename T>
+    static T* load(std::string szPlugin)
+    {
+       QString szAddin = szPlugin.c_str();
+       return load<T>(szAddin);
+    }
+
+    template <typename T>
+    static T* load(QString szPlugin)
+    {
+#ifndef _Andriod
+       QFile file;
+       if( file.exists(szPlugin.toStdString().c_str()) == false )
+       //if( FileManager::ifFileExist(szPlugin.toStdString()) == false )
+       {
+//         qCritical() << LOG_HEADER << szPlugin << " not exist.";
+          return NULL;
+       }
+#endif
+        T* pPlugin = NULL;
+
+        QLibrary* pLoader = new QLibrary(szPlugin);
+        if( pLoader->load() )
+        {
+           typedef T* (*funInterface)();
+           funInterface fun = (funInterface)pLoader->resolve("instance");
+           if( fun != nullptr )
+           {
+               pPlugin = fun();
+           }
+        }
+
+        if( pPlugin != nullptr)
+        {
+            pPlugin->setLoader(pLoader);
+        }
+        else
+        {
+			QString szError = pLoader->errorString();
+//			qCritical() << LOG_HEADER << " load " << szPlugin.toStdString().c_str() << " : " << szError.toStdString().c_str();
+            pLoader->unload();
+            pLoader->deleteLater();
+        }
+
+        return pPlugin;
+    }
+
+    // 释放对象
+    // 卸载插件
+    template <typename T>
+    static bool unload(T* pPlugin )
+    {
+        if( pPlugin == nullptr )
+        {
+            return false;
+        }
+
+        QLibrary* pLoader = pPlugin->getLoader();
+        if( pLoader != nullptr )
+        {
+            pLoader->unload();
+        }
+
+        return true;
+    }
+};  

+ 21 - 0
include/Publisher.h

@@ -0,0 +1,21 @@
+#pragma once
+
+#include <QtCore/QLibrary>
+#include <QObject>
+#include <QThread>
+#include "Define.h"
+class Publisher : public QObject{
+    Q_OBJECT
+public:
+    Publisher(){}
+    virtual ~Publisher(){}
+    // virtual void shares(SharedData * share) = 0;
+    virtual void Run(const ModuleInfo& mi) = 0;
+    virtual void setLoader(QLibrary*) = 0;
+signals:
+    void pubData(const QString& ,const QString& ,const QVariant&);
+
+};
+
+
+

+ 13 - 0
include/RunnableModule.h

@@ -0,0 +1,13 @@
+#pragma once
+#include "BaseModule.h"
+#include <QtCore/QObject>
+#include <QtCore/QThread>
+class RunnableModule: public BaseModule,public QThread
+{
+
+public:
+ //   virtual void onData(QObject data) = 0;
+    virtual void Run() = 0;
+};
+
+

+ 87 - 0
modules/MQTTClient/MQTTClient.cpp

@@ -0,0 +1,87 @@
+#include "MQTTClient.h"
+#include "qmqtt.h"
+MQTTClient::MQTTClient(QObject *parent) : QObject(parent)
+{
+
+}
+
+void MQTTClient::connect2Host(const QString &host, const uint16_t port, const QString &usr, const QString &passwd, const QString &clientID)
+{
+    mqtt = new QMQTT::Client();
+
+    connect(mqtt, &QMQTT::Client::connected, []{
+            qDebug()<< __FILE__ << __LINE__ << "connected";
+    });
+    connect(mqtt, &QMQTT::Client::disconnected, []{
+            qDebug()<< __FILE__ << __LINE__ << "disconnected";
+        }
+    );
+    connect(mqtt, &QMQTT::Client::received, this, &MQTTClient::onReceived);
+
+    //mqtt->setHostName("y.kjxry.cn");
+    mqtt->setHostName(host);
+    mqtt->setPort(port);
+    mqtt->setKeepAlive(60);
+    mqtt->setClientId(clientID); //唯一id, 相同id不能同时连接
+    mqtt->setUsername(usr);
+    mqtt->setPassword(passwd.toUtf8());
+
+    mqtt->setAutoReconnect(true); //开启自动重连
+    mqtt->setCleanSession(true); //非持久化连接,上线时,将不再关心之前所有的订阅关系以及离线消息
+
+    mqtt->setVersion(QMQTT::V3_1_1);
+    qDebug()<< "ver" << mqtt->version();
+
+    mqtt->connectToHost();
+}
+
+MQTTClient::~MQTTClient()
+{
+    mqtt->disconnected();
+    mqtt->deleteLater();
+}
+
+//接收到的消息
+void MQTTClient::onReceived(const QMQTT::Message& message)
+{
+    //qDebug() << "recv" << message.topic() << message.payload();
+    MQTTCallbackFn cbFunc = hCbFuncs.value(message.topic());
+    if (cbFunc)
+    {
+        cbFunc(message.topic(), message.payload());
+    }
+    else if (shareCbFunc)
+    {
+        shareCbFunc(message.topic(), message.payload());
+    }
+}
+
+void MQTTClient::subscribe(const QString& topic, MQTTCallbackFn& func)
+{
+    //qDebug()<< "sub" << topic;
+    if (topic.contains("+") || topic.contains("#") || topic.contains("*"))
+    {
+        shareCbFunc = func; //共享回调
+    }
+    else
+    {
+        hCbFuncs.insert(topic, func); //独立回调
+    }
+    mqtt->subscribe(topic, 1);
+
+    //不同的订阅方式
+    //mqtt->subscribe("alarm/led", 1);
+    //mqtt->subscribe("+/led", 1);
+    //mqtt->subscribe("alarm/#", 1);
+}
+
+void MQTTClient::publish(const QString &topic, const QByteArray &payload)
+{
+    //QMQTT::Message message(i, EXAMPLE_TOPIC, QString("Number %1").arg(i).toUtf8());
+    QMQTT::Message message;
+    message.setTopic(topic);
+    message.setPayload(payload);
+    message.setRetain(true); //保留最后一条数据
+    mqtt->publish(message);
+}
+

+ 46 - 0
modules/MQTTClient/MQTTClient.h

@@ -0,0 +1,46 @@
+#ifndef Mqtt_H
+#define Mqtt_H
+#pragma once
+// #include "qmqtt.h"
+#include <qmqtt_client.h>
+#include <QObject>
+#include <functional>
+//回调函数,注意:为了防止复制(深拷贝),多处使用了右值引用&&
+// typedef void (mqttCallbackFn)(QString &&topic, QByteArray &&payload);
+using MQTTCallbackFn = std::function<void(const QString& topic, const QByteArray&payload)>;
+//调用例子
+//void subCbFunc(QString &&topic, QByteArray &&payload)
+//{
+//    qDebug()<< "sub cb func" << topic << payload;
+//}
+//mqtt->subscribe(topic, subCbFunc);
+
+class MQTTClient : public QObject
+{
+    Q_OBJECT
+
+public:
+    explicit MQTTClient(QObject *parent = 0);
+    ~MQTTClient();
+
+    //void regCallback(mqttCallbackFn *func);                   //通配符订阅公共回调
+    void subscribe(const QString& topic, MQTTCallbackFn&func);        //单一主题分开回调,通配符为公共回调
+    void publish(const QString &topic, const QByteArray &payload);         //发布主题
+    void connect2Host(const QString& host, const uint16_t port, const QString& usr, const QString& passwd, const QString& clientID);
+
+public slots:
+    void onReceived(const QMQTT::Message& message);         //收到数据后发送到回调
+
+private:
+    QMQTT::Client *mqtt;
+    // QString host = "192.168.9.6";
+    // quint16 port = 1883;
+    // QString userName = "admin";
+    // QString password = "N6pNXbZjspDRqNGnxMmc";
+
+    MQTTCallbackFn shareCbFunc;                  //共享回调
+    QHash<QString, MQTTCallbackFn> hCbFuncs;               //独立回调集
+
+};
+
+#endif // Mqtt_H

+ 37 - 0
modules/MQTTClient/MQTTClient.pro

@@ -0,0 +1,37 @@
+QT -= gui
+QT += core network
+
+TEMPLATE = lib
+DEFINES += MQTTCLIENT_LIBRARY
+
+CONFIG += c++17
+
+# You can make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+INCLUDEPATH += ../../include
+
+HEADERS += \
+    MQTTClient.h
+
+QMQTT_DIR =$$PWD/../../thirdparty/qmqtt
+
+LIBS += -L$${QMQTT_DIR}/lib -lQt5Qmqtt
+INCLUDEPATH += $${QMQTT_DIR}/include
+
+SOURCES += \
+    MQTTClient.cpp
+
+
+HEADERS += \
+    MQTTClient_global.h
+
+
+# Default rules for deployment.
+unix {
+    target.path = /usr/lib
+}else{
+    DESTDIR = $$PWD/../../bin/plugins
+}
+!isEmpty(target.path): INSTALLS += target

+ 9 - 0
modules/MQTTClient/MQTTClient_global.h

@@ -0,0 +1,9 @@
+#pragma once
+#include <QtCore/qglobal.h>
+
+#if defined(MQTTCLIENT_LIBRARY)
+#define MQTTCLIENT_EXPORT Q_DECL_EXPORT
+#else
+#define MQTTCLIENT_EXPORT Q_DECL_IMPORT
+#endif
+

+ 623 - 0
modules/RedisClient/RedisClient.cpp

@@ -0,0 +1,623 @@
+#include "RedisClient.h"
+#include <QThread>
+#include <QTimer>
+
+//void Test()
+//{
+//    struct st
+//    {
+//        int a;
+//        int b;
+//        char c;
+//        int d;
+//    };
+
+//    st t;
+//    t.a = 1;
+//    t.b = 2;
+//    t.c = 'a';
+//    t.d = 4;
+//    const char *v[3];
+//    size_t vlen[3];
+//    v[0] = "set";
+//    vlen[0] = strlen("set");
+
+//    v[1] = "st";
+//    vlen[1] = strlen("st");
+
+//    v[2] = (const char *)&t;
+//    vlen[2] = sizeof(struct st);
+
+//    redisReply *r = (redisReply *)redisCommandArgv(rc, sizeof(v) / sizeof(v[0]), v, vlen);
+
+//    r = (redisReply *)redisCommand(rc, "get st");
+//    if (!r) {
+//        printf("empty reply\n");
+//        exit(1);
+//    }
+//    st *t2 = (st*)(r->str);
+//    qDebug()<< "st2" << t2->a << t2->b << t2->c << t2->d;
+//}
+
+void getCallback(redisAsyncContext *ctx, void *r, void *privdata){
+
+    qDebug() << "getCallback called";
+    redisReply *reply = static_cast<redisReply*>(r);
+    if(reply == nullptr){
+
+        qDebug() << "The reply is nullptr";
+        return;
+    }
+    for(int i = 0; i < reply->elements; i++){
+        qDebug() << "key: " << reply->element[i]->str;
+    }
+    if (QString(reply->element[0]->str) == "message") //收到订阅消息
+    {
+        QString ch = QString(reply->element[1]->str);
+        if (ch == "DevCh")
+        {
+            qDebug()<<"devch...";
+//            qDebug()<<"img:" << ++cnt;
+//            QString js = QString(reply->element[2]->str);
+//            QJsonObject jsonObj = QJsonDocument::fromJson(js.toLocal8Bit()).object(); //转成本地编码(utf-8), 中文正常
+
+//            //quint32 type = jsonObj.value("type").toInt(); //toInt() 会丢数据
+//            //qDebug()<<"type:" << type;
+
+//            //QString base64Str = QString(reply->element[2]->str);
+//            QString base64Str = jsonObj["img"].toString();
+//            //qDebug()<<"str size:" << base64Str.size();
+//            QPixmap baseimage;
+//            baseimage.loadFromData(QByteArray::fromBase64(base64Str.toLatin1()));
+//            QPixmap scaledPixmap = baseimage.scaled(500, 500, Qt::KeepAspectRatio);  //设置图片大小并设置为按比例缩放
+//            lab->setPixmap(scaledPixmap); //label放置图片并显示
+        }
+        else if (ch == "Alarm")
+        {
+            qDebug()<<"alarm...";
+        }
+        else if (ch == "Led")
+        {
+            qDebug()<<"led...";
+        }
+    }
+
+    redisAsyncCommand(ctx, getCallback, nullptr, "blpop lTest 0");
+}
+
+void connectCallback(const redisAsyncContext *c, int status) {
+    if (status != REDIS_OK) {
+        qDebug("Error1: %s\n", c->errstr);
+        //redisOk = false;
+        return;
+    }
+    qDebug("Connected...\n");
+}
+
+void disconnectCallback(const redisAsyncContext *c, int status) {
+    if (status != REDIS_OK) {
+        qDebug("Error2: %s\n", c->errstr);
+        //redisOk = false;
+        return;
+    }
+    qDebug("Disconnected...\n");
+}
+
+RedisClient::RedisClient(QObject *parent) : QObject(parent)
+{
+    redisOk = false;
+//    conn();
+}
+
+RedisClient::~RedisClient()
+{
+    delete rc;
+    delete rac;
+}
+
+void RedisClient::conn()
+{
+    if (!redisOk) //未连接
+    {
+        //同步连接
+        rc = redisConnect(ip.toUtf8(), port);
+        if(rc == nullptr || rc->err)
+        {
+            qDebug()<< "redis err:" << rc->errstr;
+        }
+        else
+        {
+            QString authCmd = "auth " + auth; //认证
+            redisReply * reply = (redisReply*)redisCommand(rc, authCmd.toUtf8());
+            //qDebug()<< "auth:" << reply->str;
+            if (QString::compare(reply->str, "OK"))
+            {
+                qDebug() << "redis connect fail.";
+            }
+            else
+            {
+                redisOk = true;
+                qDebug() << "redis connect successfully.";
+            }
+        }
+    }
+    else
+    {
+        //qDebug()<< "~ ";
+    }
+}
+
+void RedisClient::start()
+{
+    conn();
+    //开启一个定时器, 每秒检测一次是否断开, 断开后自动连接redis服务器
+    QTimer *redisTimer = new QTimer();
+    connect(redisTimer, SIGNAL(timeout()), this, SLOT(conn()));
+    redisTimer->start(1000);
+}
+
+bool RedisClient::hset(const QString& m, const QString& k, const QString& v)
+{
+    //如果 field 是哈希表中的一个新建域,并且值设置成功,reply->integer为1
+    //如果哈希表中域 field 已经存在且旧值已被新值覆盖,reply->integer为0
+    //无法区别是否成功
+    bool ret = false;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("hset " + m + " " + k + " " + v).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        //qDebug() <<"type:" << reply->type << reply->integer; //type = 3
+        ret = true;
+    }
+    return ret;
+}
+
+QString RedisClient::hget(const QString& m, const QString& k)
+{
+    QString ret;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("hget " + m + " " + k).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        //type:4表示不存在,type:1表示返回的为字符串
+        //qDebug() <<"type:" << reply->type << reply->str << reply->len; //type:1
+        if (reply->len)
+        {
+            ret = QString(reply->str);
+        }
+    }
+    return ret;
+}
+
+QStringList RedisClient::hkeys(const QString& k)
+{
+    QStringList ret;
+    redisReply *reply = (redisReply *)redisCommand(rc, QString("hkeys " + k).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else if (reply->type == REDIS_REPLY_ARRAY)
+    {
+        // type:4表示不存在,type:1表示返回的为字符串
+        // qDebug() <<"type:" << reply->type << reply->str << reply->len; //type:1
+        for (size_t i = 0; i < reply->elements; i++)
+        {
+            // qDebug() << "key: " << i << reply->element[i]->str;
+            ret.append(QString(reply->element[i]->str));
+        }
+    }
+    return ret;
+}
+
+QStringList RedisClient::hvals(const QString& k)
+{
+    QStringList ret;
+    redisReply *reply = (redisReply *)redisCommand(rc, QString("hvals " + k).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else if (reply->type == REDIS_REPLY_ARRAY)
+    {
+        // type:4表示不存在,type:1表示返回的为字符串
+        // qDebug() <<"type:" << reply->type << reply->str << reply->len; //type:1
+        for (size_t i = 0; i < reply->elements; i++)
+        {
+            // qDebug() << "key: " << i << reply->element[i]->str;
+            ret.append(QString(reply->element[i]->str));
+        }
+    }
+    return ret;
+}
+
+QHash<QString, QString> RedisClient::hgetall(const QString& k)
+{
+    QHash<QString, QString> ret;
+    redisReply *reply = (redisReply *)redisCommand(rc, QString("hgetall " + k).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else if (reply->type == REDIS_REPLY_ARRAY)
+    {
+        // type:4表示不存在,type:1表示返回的为字符串
+        // qDebug() <<"type:" << reply->type << reply->str << reply->len; //type:1
+        for (size_t i = 0; i < reply->elements; i += 2)
+        {
+            // qDebug() << "key: " << i << reply->element[i]->str;
+            ret.insert(QString(reply->element[i]->str), QString(reply->element[i + 1]->str));
+        }
+    }
+    return ret;
+}
+
+//向队列尾(右)部加入字符串数据
+bool RedisClient::rpush(const QString& lData, const QString& js)
+{
+    bool ret = false;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("rpush " + lData + " " + js).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        //qDebug() <<"type:" << reply->type << reply->integer; //type = 3
+        //reply->integer为队列的个数,理论应该大于0
+        if (reply->integer)
+        {
+            ret = true;
+        }
+    }
+    return ret;
+}
+
+//向队列尾(右)部加入二进制数据
+bool RedisClient::rpushb(const QString& lData, const QByteArray& ba)
+{
+    bool ret = false;
+    const char *arg[3]; //3个参数(cmd, k, v)
+    size_t vlen[3]; //3个参数长度
+    arg[0] = "rpush";
+    vlen[0] = strlen("rpush");
+
+    arg[1] = lData.toStdString().c_str();
+    vlen[1] = lData.size();
+
+    arg[2] = (const char *)ba.data();
+    vlen[2] = ba.size();
+
+    redisReply *reply = (redisReply *)redisCommandArgv(rc, sizeof(arg) / sizeof(arg[0]), arg, vlen);
+    //redisReply * reply = (redisReply*)redisCommand(rc, QString("rpush " + lData + " " + js).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        //qDebug() <<"type:" << reply->type << reply->integer; //type = 3
+        //reply->integer为队列的个数,理论应该大于0
+        if (reply->integer)
+        {
+            ret = true;
+        }
+    }
+    return ret;
+}
+
+QString RedisClient::lpop(const QString& lData)
+{
+    QString ret;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("lpop " + lData).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else if (reply->len)
+    {
+        ret = QString(reply->str);
+    }
+    return ret;
+}
+
+//阻塞从队列头部获取最早数据
+bool RedisClient::blpop(const QString& lData, redisCallbackFn *fn)
+{
+    if (rac == nullptr)
+    {
+        //异步连接
+        rac = redisAsyncConnect(ip.toUtf8(), port);
+        if(rac->err)
+        {
+            qDebug()<< "error: " << rac->errstr;
+            redisAsyncFree(rac);
+        }
+        else
+        {
+            adapter.setContext(rac);
+            redisAsyncSetConnectCallback(rac, connectCallback);
+            redisAsyncSetDisconnectCallback(rac, disconnectCallback);
+            QString authCmd = "auth " + auth; //认证
+            redisAsyncCommand(rac, nullptr, nullptr, authCmd.toUtf8());
+            qDebug()<< "redis ok.";
+            redisOk = true;
+        }
+    }
+    if (rac && redisOk)
+    {
+        redisAsyncCommand(rac, fn, nullptr, ("blpop " + lData + " 0").toStdString().c_str());
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+//同步阻塞一定时间返回数据
+QString RedisClient::blpop(const QString& lData, quint32 timeout)
+{
+    //最小为1,防止0时永久阻塞
+    if (timeout < 1)
+    {
+        timeout = 1;
+    }
+    QString ret;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("blpop " + lData + " " + QString::number(timeout)).toStdString().c_str());
+//    for(int i = 0; i < reply->elements; i++){
+//        qDebug() << "key: " << reply->element[i]->str;
+//    }
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else if (reply->elements > 1)
+    {
+        ret = QString(reply->element[1]->str);
+    }
+    return ret;
+}
+
+bool RedisClient::set(const QString& k, const QString& v)
+{
+    bool ret = false;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("set " + k + " " + v).toStdString().c_str()); //支持空格
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        //qDebug() <<"type:" << reply->type << reply->str << reply->len; //type:5 str:OK len:2
+        if (reply->type == 6)
+        {
+            qDebug()<<"err:" << v;
+        }
+        //返回OK为成功,长度2为str长度
+        ret = true;
+    }
+    return ret;
+}
+
+bool RedisClient::setb(const QString& k, const QByteArray &v)
+{
+    bool ret = false;
+    const char *arg[3]; //3个参数(cmd, k, v)
+    size_t vlen[3]; //3个参数长度
+    arg[0] = "set";
+    vlen[0] = strlen("set");
+
+    arg[1] = k.toStdString().c_str();
+    vlen[1] = k.size();
+
+    arg[2] = (const char *)v.data();
+    vlen[2] = v.size();
+
+    redisReply *reply = (redisReply *)redisCommandArgv(rc, sizeof(arg) / sizeof(arg[0]), arg, vlen);
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        //qDebug() <<"type:" << reply->type << reply->str << reply->len; //type:5 str:OK len:2
+        if (reply->type == 6)
+        {
+            qDebug()<<"err:" << v;
+        }
+        //返回OK为成功,长度2为str长度
+        ret = true;
+    }
+    return ret;
+}
+
+QString RedisClient::get(const QString& k)
+{
+    QString ret;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("get " + k).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else if (reply->len)
+    {
+        ret = QString(reply->str);
+    }
+    return ret;
+}
+
+QByteArray RedisClient::getb(const QString& k)
+{
+    QByteArray ret;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("get " + k).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else if (reply->len)
+    {
+        ret = QByteArray(reply->str, reply->len); //加上长度,保证二进制安全
+        //qDebug()<< reply->len << ret.size();
+    }
+    return ret;
+}
+
+bool RedisClient::expire(const QString& k,int sec)
+{
+    bool ret = false;
+    redisReply * reply = (redisReply*)redisCommand(rc, QString("expire " + k + " " + QString::number(sec, 10)).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        //qDebug() <<"type:" << reply->type << reply->integer; //type = 3
+        //K不存在为0,存在为1
+        if (reply->integer)
+        {
+            ret = true;
+        }
+    }
+    return ret;
+}
+
+bool RedisClient::publish(const QString& ch, const QString& js)
+{
+    bool ret = false;
+    QString pub = QString("publish " + ch + " " + js);
+    redisReply * reply = (redisReply*)redisCommand(rc, pub.toStdString().c_str());
+
+    qDebug() << __FUNCTION__ << pub;
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        //qDebug() <<"type:" << reply->type << reply->integer; //type:3, integer:2
+        //integer为2,不懂什么意思,这个用于实时,无需判断是否成功,连接正常即可
+        ret = true;
+    }
+    return ret;
+}
+
+bool RedisClient::publishb(const QString& ch,const QByteArray& ba)
+{
+    bool ret = false;
+    const char *arg[3]; //3个参数(cmd, k, v)
+    size_t vlen[3]; //3个参数长度
+    arg[0] = "publish";
+    vlen[0] = strlen("publish");
+
+    arg[1] = ch.toStdString().c_str();
+    vlen[1] = ch.size();
+
+    arg[2] = (const char *)ba.data();
+    vlen[2] = ba.size();
+
+    redisReply *reply = (redisReply *)redisCommandArgv(rc, sizeof(arg) / sizeof(arg[0]), arg, vlen);
+    //redisReply * reply = (redisReply*)redisCommand(rc, QString("publish " + ch + " " + js).toStdString().c_str());
+    if (rc->err)
+    {
+        redisOk = false;
+        qDebug() << rc->errstr;
+    }
+    else
+    {
+        qDebug() <<"type:" << reply->type << reply->integer; //type:3, integer:2
+        //integer为2,不懂什么意思,这个用于实时,无需判断是否成功,连接正常即可
+        ret = true;
+    }
+    return ret;
+}
+
+void RedisClient::subscribe(const QString& ch, redisCallbackFn *fn, void* data)
+{
+    if (rac == nullptr)
+    {
+        //异步连接
+        rac = redisAsyncConnect(ip.toUtf8(), port);
+        if(rac->err)
+        {
+            qDebug()<< "error: " << rac->errstr;
+            redisAsyncFree(rac);
+        }
+        else
+        {
+            adapter.setContext(rac);
+
+            redisAsyncSetConnectCallback(rac, connectCallback);
+            redisAsyncSetDisconnectCallback(rac, disconnectCallback);
+            QString authCmd = "auth " + auth; //认证
+            redisAsyncCommand(rac, nullptr, data, authCmd.toUtf8());
+            qDebug()<< "redis ok.";
+            redisOk = true;
+        }
+    }
+    if (rac && redisOk)
+    {
+        redisAsyncCommand(rac, fn, data, QString("subscribe " + ch).toStdString().c_str());
+    }
+}
+
+void RedisClient::psubscribe(const QString& ch, redisCallbackFn *fn, void* data)
+{
+    if (rac == nullptr)
+    {
+        //异步连接
+        rac = redisAsyncConnect(ip.toUtf8(), port);
+        if(rac->err)
+        {
+            qDebug()<< "error: " << rac->errstr;
+            redisAsyncFree(rac);
+        }
+        else
+        {
+            adapter.setContext(rac);
+
+            redisAsyncSetConnectCallback(rac, connectCallback);
+            redisAsyncSetDisconnectCallback(rac, disconnectCallback);
+            QString authCmd = "auth " + auth; //认证
+            redisAsyncCommand(rac, nullptr, data, authCmd.toUtf8());
+
+            qDebug()<< "redis ok.";
+            redisOk = true;
+        }
+    }
+    if (rac && redisOk)
+    {
+        redisAsyncCommand(rac, fn, data, QString("psubscribe " + ch).toStdString().c_str());
+    }
+}
+
+void RedisClient::Setup(const QString& addr, uint _port,const QString&password)
+{
+    ip = addr;
+    port = _port;
+    auth = password;
+    redisOk = false;
+    conn();
+}

+ 55 - 0
modules/RedisClient/RedisClient.h

@@ -0,0 +1,55 @@
+#pragma once
+
+#include "hiredis/adapters/qt.h"
+#include "hiredis/async.h"
+#include "hiredis/hiredis.h"
+#include <QObject>
+#include <QDebug>
+#include <QJsonObject>
+#include <QJsonDocument>
+#include "RedisClient_global.h"
+
+class REDISCLIENT_EXPORT RedisClient : public QObject
+{
+    Q_OBJECT
+public:
+    explicit RedisClient(QObject *parent = 0);
+    ~RedisClient();
+
+public slots:
+    void start();
+    void conn();
+    bool hset(const QString& m, const QString& k, const QString& v);
+    QString hget(const QString& m, const QString& k);
+    bool set(const QString& k, const QString& v);
+    bool setb(const QString& k,const QByteArray &v);
+    QString get(const QString& k);
+    QByteArray getb(const QString& k);
+    QStringList hkeys(const QString& k);
+    QStringList hvals(const QString& k);
+    QHash<QString, QString> hgetall(const QString& k);
+    bool rpush(const QString& lData, const QString& js);                  //向队列尾部加入字符串数据
+    bool rpushb(const QString& lData, const QByteArray& ba);              //向队列尾部加入二进制数据
+    QString lpop(const QString& lData);
+    QString blpop(const QString& lData, quint32 timeout);          //同步阻塞一定时间返回数据
+    bool blpop(const QString& lData, redisCallbackFn *fn);         //阻塞从队列头部获取最早数据
+    bool publish(const QString& ch, const QString& js);
+    bool publishb(const QString& ch, const QByteArray& ba);
+    bool expire(const QString& k,int sec);
+    void subscribe(const QString& ch, redisCallbackFn *fn, void* data = nullptr);        // 订阅
+    void psubscribe(const QString& ch, redisCallbackFn *fn, void* priData = nullptr);       // 订阅:模式匹配
+
+private:
+    //QString ip = "192.168.9.56";
+    QString ip = "127.0.0.1";
+    quint16 port = 16379;
+    QString auth = "<LanPeng&&CeKong>";
+
+    bool redisOk = false;               //redis全局状态,操作出错时置为false, redis线程内会自动重连
+    redisContext *rc;                   //同步连接
+    redisAsyncContext *rac = nullptr;   //异步连接
+    RedisQtAdapter adapter;             //异步事件驱动适配器
+
+public:
+    void Setup(const QString& addr, uint port, const QString& password);
+};

+ 38 - 0
modules/RedisClient/RedisClient.pro

@@ -0,0 +1,38 @@
+QT -= gui
+
+TEMPLATE = lib
+DEFINES += REDISCLIENT_LIBRARY
+
+CONFIG += c++17
+
+# You can make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+INCLUDEPATH += ../../include
+
+HEADERS += \
+    RedisClient.h
+
+HIREDIS_DIR =$$PWD/../../thirdparty/hiredis
+HEADERS += $${HIREDIS_DIR}/include/hiredis/adapters/qt.h
+
+LIBS += -L$${HIREDIS_DIR}/lib -lhiredis
+#LIBS += ../thirdparty/hiredis/lib/libhiredis.a
+INCLUDEPATH += $${HIREDIS_DIR}/include
+
+SOURCES += \
+    RedisClient.cpp
+
+
+HEADERS += \
+    RedisClient_global.h
+
+
+# Default rules for deployment.
+unix {
+    target.path = /usr/lib
+}else{
+    DESTDIR = $$PWD/../../bin/plugins
+}
+!isEmpty(target.path): INSTALLS += target

+ 9 - 0
modules/RedisClient/RedisClient_global.h

@@ -0,0 +1,9 @@
+#pragma once
+#include <QtCore/qglobal.h>
+
+#if defined(REDISCLIENT_LIBRARY)
+#define REDISCLIENT_EXPORT Q_DECL_EXPORT
+#else
+#define REDISCLIENT_EXPORT Q_DECL_IMPORT
+#endif
+

+ 302 - 0
modules/TDengineClient/TDengineClient.cpp

@@ -0,0 +1,302 @@
+#include "TDengineClient.h"
+#include "taos.h"
+#include <QtCore/QDebug>
+#include <QtConcurrent/QtConcurrent>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "taos.h"
+#include <QVariant>
+
+
+
+int32_t TDengineClient::msgProcess(TAOS_RES* msg)
+{
+    char    buf[1024];
+    int32_t rows = 0;
+
+    const char* topicName = tmq_get_topic_name(msg);
+    const char* dbName = tmq_get_db_name(msg);
+    int32_t     vgroupId = tmq_get_vgroup_id(msg);
+
+    //qDebug() << __FILE__ << __LINE__ << "db: " << dbName;
+    //qDebug() << __FILE__ << __LINE__ << "topic: " << topicName;
+    //qDebug() << __FILE__ << __LINE__ << "vgroup id:" << vgroupId;
+
+    while (1)
+    {
+        TAOS_ROW row = taos_fetch_row(msg);
+        if (row == NULL)
+            break;
+
+        TAOS_FIELD* fields = taos_fetch_fields(msg);
+        int32_t     numOfFields = taos_field_count(msg);
+        rows++;
+        taos_print_row(buf, row, fields, numOfFields);
+
+        QJsonObject jsonObjects;
+        for(int k = 0;k < numOfFields;k++)
+        {
+            QJsonObject jsonVal;
+            QString fieldName = fields[k].name;
+            if(fields[k].type == TSDB_DATA_TYPE_TIMESTAMP)
+            {
+                qint64 value = *((int64_t *)row[k]);
+                jsonVal.insert("value",value);
+                jsonVal.insert("type", QMetaType::Type::ULongLong);
+            }
+            else if(fields[k].type == TSDB_DATA_TYPE_VARCHAR)
+            {
+                QString value((char *)row[k]);
+                jsonVal.insert("value",value);
+                jsonVal.insert("type", QMetaType::Type::QString);
+            }
+            else if(fields[k].type == TSDB_DATA_TYPE_DOUBLE)
+            {
+                double value = *(double *)(row[k]);
+                jsonVal.insert("value",value);
+                jsonVal.insert("type", QMetaType::Type::Double);
+            }
+            else if(fields[k].type == TSDB_DATA_TYPE_FLOAT)
+            {
+                float value = *(float *)(row[k]);
+                jsonVal.insert("value",value);
+                jsonVal.insert("type", QMetaType::Type::Float);
+            }
+            else if(fields[k].type == TSDB_DATA_TYPE_INT)
+            {
+                int value = *(int *)(row[k]);
+                jsonVal.insert("value",value);
+                jsonVal.insert("type", QMetaType::Type::Int);
+            }
+            else if(fields[k].type == TSDB_DATA_TYPE_BIGINT)
+            {
+                qint64 value = *(int64_t *)(row[k]);
+                jsonVal.insert("value",value);
+                jsonVal.insert("type", QMetaType::Type::LongLong);
+            }
+            jsonObjects.insert(fieldName, jsonVal);
+        }
+
+        std::string content = QJsonDocument(jsonObjects).toJson(QJsonDocument::Compact).toStdString();
+
+        //if( g_pSubCB != nullptr )
+        {
+            std::string topic = topicName;
+            //std::string content = buf;
+            callback((char*)topic.c_str(),(char*)content.c_str(), usrData);
+        }
+        //qDebug() << __FILE__ << __LINE__ << "row content: " << buf;
+    }
+
+    return rows;
+}
+
+//构建消费者
+tmq_t* TDengineClient::buildConsumer()
+{
+    tmq_conf_res_t code;
+    tmq_conf_t*    conf = tmq_conf_new();
+
+    code = tmq_conf_set(conf, "td.connect.ip", host.toStdString().c_str());
+    if (TMQ_CONF_OK != code) {
+        tmq_conf_destroy(conf);
+        return NULL;
+    }
+
+    //code = tmq_conf_set(conf, "enable.auto.commit", "true");
+    code = tmq_conf_set(conf, "enable.auto.commit", "false"); //禁用提交回调
+    if (TMQ_CONF_OK != code) {
+        tmq_conf_destroy(conf);
+        return NULL;
+    }
+
+    //    code = tmq_conf_set(conf, "auto.commit.interval.ms", "1000");
+    //    if (TMQ_CONF_OK != code) {
+    //        tmq_conf_destroy(conf);
+    //        return NULL;
+    //    }
+
+    code = tmq_conf_set(conf, "group.id", "cgrpName");
+    if (TMQ_CONF_OK != code) {
+        tmq_conf_destroy(conf);
+        return NULL;
+    }
+
+    code = tmq_conf_set(conf, "client.id", "user defined name");
+    if (TMQ_CONF_OK != code) {
+        tmq_conf_destroy(conf);
+        return NULL;
+    }
+
+    code = tmq_conf_set(conf, "td.connect.user", user.toStdString().c_str());
+    if (TMQ_CONF_OK != code) {
+        tmq_conf_destroy(conf);
+        return NULL;
+    }
+
+    code = tmq_conf_set(conf, "td.connect.pass", password.toStdString().c_str());
+    if (TMQ_CONF_OK != code) {
+        tmq_conf_destroy(conf);
+        return NULL;
+    }
+
+    //v3.2以后默认为最新的latest
+//    code = tmq_conf_set(conf, "auto.offset.reset", "latest");
+//    if (TMQ_CONF_OK != code) {
+//        tmq_conf_destroy(conf);
+//        return NULL;
+//    }
+
+    //tmq_conf_set_auto_commit_cb(conf, tmq_commit_cb_print, NULL); //禁用提交回调
+
+    tmq_t* tmq = tmq_consumer_new(conf, NULL, 0);
+    //qDebug()<< __FILE__ << __LINE__ << "tmq" << tmq;
+    tmq_conf_destroy(conf);
+    return tmq;
+}
+
+//构建主题
+tmq_list_t* TDengineClient::buildTopicList()
+{
+    tmq_list_t* tmqList = tmq_list_new();
+
+    std::set<std::string>::iterator itr;
+    for( itr = topicList.begin(); itr != topicList.end(); ++itr )
+    {
+        std::string szTopic = *itr;
+        int32_t code = tmq_list_append(tmqList, szTopic.c_str());
+        if (code) {
+            return NULL;
+        }
+    }
+    return tmqList;
+}
+
+//轮询主题
+void TDengineClient::topicLoop()
+{
+    int32_t code;
+    tmq_t* tmq = buildConsumer();
+    if (NULL == tmq) {
+        qDebug()<< __FILE__ << __LINE__ << "err" << stderr;
+        return;
+    }
+
+    tmq_list_t* topic_list = buildTopicList();
+    code = tmq_subscribe(tmq, topic_list);
+    if ( code ) {
+        qDebug() << __FILE__ << __LINE__ << "Failed to tmq_subscribe(): " << tmq_err2str(code);
+    }
+
+    tmq_list_destroy(topic_list);
+
+    int32_t totalRows = 0;
+    int32_t msgCnt = 0;
+    int32_t timeout = 100; //多个主题共用1个线程轮询,超时时间太长会影响其它主题实时性
+    while (true) {
+        TAOS_RES* tmqmsg = tmq_consumer_poll(tmq, timeout);
+        if (tmqmsg) {
+            msgCnt++;
+            totalRows += msgProcess(tmqmsg);
+            taos_free_result(tmqmsg);
+        } else {
+            //超时
+            //break;
+        }
+    }
+
+    code = tmq_consumer_close(tmq);
+    if (code) {
+        fprintf(stderr, "%% Failed to close consumer: %s\n", tmq_err2str(code));
+    } else {
+        fprintf(stderr, "%% Consumer closed\n");
+    }
+    qDebug()<< __FILE__ << __LINE__ << "stderr" << stderr;
+}
+
+TDengineClient::TDengineClient(QObject *parent)
+    : QObject(parent)
+{
+    pConn = nullptr;
+    //g_pSubCB = nullptr;
+    topicList.clear();
+}
+
+TDengineClient::~TDengineClient()
+{
+    taos_close(pConn);
+    if(future.isRunning()){
+        future.waitForFinished();
+    }
+}
+
+void TDengineClient::exec(QString sql)
+{
+    if (pConn)
+    {
+        TAOS_RES* pRes = taos_query(pConn, sql.toStdString().c_str());
+        if (taos_errno(pRes) != 0) {
+            qDebug() << __FILE__ << __LINE__ << "failed to insert into tab, reason:" << taos_errstr(pRes);
+            qDebug()<< __FILE__ << __LINE__ << "err sql" << sql;
+        }
+        else
+        {
+            //qDebug()<< "exec ok";
+        }
+        taos_free_result(pRes);
+    }
+}
+
+void TDengineClient::subscribe(QString ch,const std::function<void(const char* topic, const char* data, void*usr)>& fn , void* usrdata)
+{
+    callback = fn;
+    usrData = usrdata;
+    topicList.insert(ch.toLocal8Bit().toStdString());
+}
+
+void TDengineClient::subscribe(std::set<std::string>topics,const std::function<void(const char* topic, const char* data, void*usr)>& fn , void* usrdata)
+{
+    callback = fn;
+    usrData = usrdata;
+    topicList.merge(topics);
+}
+void TDengineClient::psubscribe(QString ch,const std::function<void(const char* topic, const char* data, void*usr)>& fn, void*usrdata)
+{
+    callback = fn;
+    usrData = usrdata;
+    topicList.insert(ch.toLocal8Bit().toStdString());
+}
+
+void TDengineClient::Setup(const char* _host, const char* _user, const char* _passwd, uint16_t _port)
+{
+    // host = ts.addr.c_str();
+    // user = ts.user.c_str();
+    // dbName = ts.db.c_str();
+    // password = ts.password.c_str();
+    // port = ts.port;
+    host = _host;
+    user = _user;
+    password = _passwd;
+    port = _port;
+}
+
+void TDengineClient::start()
+{
+    pConn = taos_connect(host.toStdString().c_str(),
+                         user.toStdString().c_str(),
+                         password.toStdString().c_str(),
+                         dbName.toStdString().c_str(),
+                         port);
+    if (pConn == NULL) {
+        qDebug()<< __FILE__ << __LINE__ << "td conn err.";
+        return;
+    }
+
+    //开启一个线程轮询订阅的主题
+    future = QtConcurrent::run(this, &TDengineClient::topicLoop);
+
+}

+ 57 - 0
modules/TDengineClient/TDengineClient.h

@@ -0,0 +1,57 @@
+#pragma once
+
+//#include "MWareInterface.h"
+#include "taos.h"
+#include <QtCore/QObject>
+#include <QtCore/QDebug>
+#include "libaray_symbols.h"
+#include <functional>
+#include <QFuture>
+#include <set>
+class TDENGINECLIENT_EXPORT TDengineClient : public QObject
+{
+    Q_OBJECT
+
+public:
+    explicit TDengineClient(QObject *parent = 0);
+    TDengineClient(QString password,
+             std::function<void(const char *, const char *, void*)>& callback)
+        : password(std::move(password)), callback(callback) {}
+    ~TDengineClient();
+
+    void exec(QString sql);
+
+public:
+    int32_t msgProcess(TAOS_RES* msg);  //消息处理
+    tmq_t* buildConsumer();             //构建消费者
+    tmq_list_t* buildTopicList();       //构建主题
+    void topicLoop();                   //轮询主题
+
+signals:
+public slots:
+
+private:
+    QString host = "192.168.9.6";
+    uint16_t    port = 6030;
+    QString user = "root";
+    QString dbName = "lanpengdb";
+    QString password = "x=gheLw7QMAD4zjQh3d9";
+
+    TAOS* pConn = NULL;
+    std::function<void(const char* topic, const char* data, void*usr)> callback;
+
+    //TAOS* pConn = NULL;
+    //EventSubInterface*  g_pSubCB;       // 订阅回调
+    std::set<std::string>  topicList;
+
+    void * usrData;
+    QFuture<void> future;
+
+public:
+    //void Setup(tagSetup ts);
+    void Setup(const char* host, const char* user, const char* passwd, uint16_t port);
+    void subscribe(QString ch,const std::function<void(const char* topic, const char* data, void*usr)>& fn, void* usrdata);      // 订阅
+    void subscribe(std::set<std::string>topics,const std::function<void(const char* topic, const char* data, void*usr)>& fn , void* usrdata);
+    void psubscribe(QString ch, const std::function<void(const char* topic, const char* data, void*usr)>& fn, void*usrdata);     // 订阅:模式匹配
+    void start();
+};

+ 31 - 0
modules/TDengineClient/TDengineClient.pro

@@ -0,0 +1,31 @@
+QT = core network
+QT -= gui
+
+CONFIG += c++17 cmdline
+
+TARGET = TDengineClient
+TEMPLATE = lib
+
+unix{
+}
+else{
+     DESTDIR = $$PWD/../../bin/plugins
+}
+
+DEFINES += TDENGINECLIENT_LIBRARY
+
+INCLUDEPATH += ../include
+
+TDENGINE_DIR =$$PWD/../../thirdparty/tdengine
+
+LIBS += -L$${TDENGINE_DIR}/lib -ltaos
+#LIBS += ../thirdparty/hiredis/lib/libhiredis.a
+INCLUDEPATH += $${TDENGINE_DIR}/include
+
+
+HEADERS += \
+    TDengineClient.h \
+    libaray_symbols.h
+
+SOURCES += \
+    TDengineClient.cpp

+ 9 - 0
modules/TDengineClient/libaray_symbols.h

@@ -0,0 +1,9 @@
+#pragma once
+#include <QtCore/QGlobal.h>
+
+#if defined(TDENGINECLIENT_LIBRARY)
+#  define TDENGINECLIENT_EXPORT Q_DECL_EXPORT
+#else
+#  define TDENGINECLIENT_EXPORT Q_DECL_IMPORT
+#endif
+

+ 130 - 0
thirdparty/hiredis/include/hiredis/adapters/ae.h

@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_AE_H__
+#define __HIREDIS_AE_H__
+#include <sys/types.h>
+#include <ae.h>
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct redisAeEvents {
+    redisAsyncContext *context;
+    aeEventLoop *loop;
+    int fd;
+    int reading, writing;
+} redisAeEvents;
+
+static void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
+    ((void)el); ((void)fd); ((void)mask);
+
+    redisAeEvents *e = (redisAeEvents*)privdata;
+    redisAsyncHandleRead(e->context);
+}
+
+static void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
+    ((void)el); ((void)fd); ((void)mask);
+
+    redisAeEvents *e = (redisAeEvents*)privdata;
+    redisAsyncHandleWrite(e->context);
+}
+
+static void redisAeAddRead(void *privdata) {
+    redisAeEvents *e = (redisAeEvents*)privdata;
+    aeEventLoop *loop = e->loop;
+    if (!e->reading) {
+        e->reading = 1;
+        aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e);
+    }
+}
+
+static void redisAeDelRead(void *privdata) {
+    redisAeEvents *e = (redisAeEvents*)privdata;
+    aeEventLoop *loop = e->loop;
+    if (e->reading) {
+        e->reading = 0;
+        aeDeleteFileEvent(loop,e->fd,AE_READABLE);
+    }
+}
+
+static void redisAeAddWrite(void *privdata) {
+    redisAeEvents *e = (redisAeEvents*)privdata;
+    aeEventLoop *loop = e->loop;
+    if (!e->writing) {
+        e->writing = 1;
+        aeCreateFileEvent(loop,e->fd,AE_WRITABLE,redisAeWriteEvent,e);
+    }
+}
+
+static void redisAeDelWrite(void *privdata) {
+    redisAeEvents *e = (redisAeEvents*)privdata;
+    aeEventLoop *loop = e->loop;
+    if (e->writing) {
+        e->writing = 0;
+        aeDeleteFileEvent(loop,e->fd,AE_WRITABLE);
+    }
+}
+
+static void redisAeCleanup(void *privdata) {
+    redisAeEvents *e = (redisAeEvents*)privdata;
+    redisAeDelRead(privdata);
+    redisAeDelWrite(privdata);
+    hi_free(e);
+}
+
+static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
+    redisContext *c = &(ac->c);
+    redisAeEvents *e;
+
+    /* Nothing should be attached when something is already attached */
+    if (ac->ev.data != NULL)
+        return REDIS_ERR;
+
+    /* Create container for context and r/w events */
+    e = (redisAeEvents*)hi_malloc(sizeof(*e));
+    if (e == NULL)
+        return REDIS_ERR;
+
+    e->context = ac;
+    e->loop = loop;
+    e->fd = c->fd;
+    e->reading = e->writing = 0;
+
+    /* Register functions to start/stop listening for events */
+    ac->ev.addRead = redisAeAddRead;
+    ac->ev.delRead = redisAeDelRead;
+    ac->ev.addWrite = redisAeAddWrite;
+    ac->ev.delWrite = redisAeDelWrite;
+    ac->ev.cleanup = redisAeCleanup;
+    ac->ev.data = e;
+
+    return REDIS_OK;
+}
+#endif

+ 156 - 0
thirdparty/hiredis/include/hiredis/adapters/glib.h

@@ -0,0 +1,156 @@
+#ifndef __HIREDIS_GLIB_H__
+#define __HIREDIS_GLIB_H__
+
+#include <glib.h>
+
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct
+{
+    GSource source;
+    redisAsyncContext *ac;
+    GPollFD poll_fd;
+} RedisSource;
+
+static void
+redis_source_add_read (gpointer data)
+{
+    RedisSource *source = (RedisSource *)data;
+    g_return_if_fail(source);
+    source->poll_fd.events |= G_IO_IN;
+    g_main_context_wakeup(g_source_get_context((GSource *)data));
+}
+
+static void
+redis_source_del_read (gpointer data)
+{
+    RedisSource *source = (RedisSource *)data;
+    g_return_if_fail(source);
+    source->poll_fd.events &= ~G_IO_IN;
+    g_main_context_wakeup(g_source_get_context((GSource *)data));
+}
+
+static void
+redis_source_add_write (gpointer data)
+{
+    RedisSource *source = (RedisSource *)data;
+    g_return_if_fail(source);
+    source->poll_fd.events |= G_IO_OUT;
+    g_main_context_wakeup(g_source_get_context((GSource *)data));
+}
+
+static void
+redis_source_del_write (gpointer data)
+{
+    RedisSource *source = (RedisSource *)data;
+    g_return_if_fail(source);
+    source->poll_fd.events &= ~G_IO_OUT;
+    g_main_context_wakeup(g_source_get_context((GSource *)data));
+}
+
+static void
+redis_source_cleanup (gpointer data)
+{
+    RedisSource *source = (RedisSource *)data;
+
+    g_return_if_fail(source);
+
+    redis_source_del_read(source);
+    redis_source_del_write(source);
+    /*
+     * It is not our responsibility to remove ourself from the
+     * current main loop. However, we will remove the GPollFD.
+     */
+    if (source->poll_fd.fd >= 0) {
+        g_source_remove_poll((GSource *)data, &source->poll_fd);
+        source->poll_fd.fd = -1;
+    }
+}
+
+static gboolean
+redis_source_prepare (GSource *source,
+                      gint    *timeout_)
+{
+    RedisSource *redis = (RedisSource *)source;
+    *timeout_ = -1;
+    return !!(redis->poll_fd.events & redis->poll_fd.revents);
+}
+
+static gboolean
+redis_source_check (GSource *source)
+{
+    RedisSource *redis = (RedisSource *)source;
+    return !!(redis->poll_fd.events & redis->poll_fd.revents);
+}
+
+static gboolean
+redis_source_dispatch (GSource      *source,
+                       GSourceFunc   callback,
+                       gpointer      user_data)
+{
+    RedisSource *redis = (RedisSource *)source;
+
+    if ((redis->poll_fd.revents & G_IO_OUT)) {
+        redisAsyncHandleWrite(redis->ac);
+        redis->poll_fd.revents &= ~G_IO_OUT;
+    }
+
+    if ((redis->poll_fd.revents & G_IO_IN)) {
+        redisAsyncHandleRead(redis->ac);
+        redis->poll_fd.revents &= ~G_IO_IN;
+    }
+
+    if (callback) {
+        return callback(user_data);
+    }
+
+    return TRUE;
+}
+
+static void
+redis_source_finalize (GSource *source)
+{
+    RedisSource *redis = (RedisSource *)source;
+
+    if (redis->poll_fd.fd >= 0) {
+        g_source_remove_poll(source, &redis->poll_fd);
+        redis->poll_fd.fd = -1;
+    }
+}
+
+static GSource *
+redis_source_new (redisAsyncContext *ac)
+{
+    static GSourceFuncs source_funcs = {
+        .prepare  = redis_source_prepare,
+        .check     = redis_source_check,
+        .dispatch = redis_source_dispatch,
+        .finalize = redis_source_finalize,
+    };
+    redisContext *c = &ac->c;
+    RedisSource *source;
+
+    g_return_val_if_fail(ac != NULL, NULL);
+
+    source = (RedisSource *)g_source_new(&source_funcs, sizeof *source);
+    if (source == NULL)
+        return NULL;
+
+    source->ac = ac;
+    source->poll_fd.fd = c->fd;
+    source->poll_fd.events = 0;
+    source->poll_fd.revents = 0;
+    g_source_add_poll((GSource *)source, &source->poll_fd);
+
+    ac->ev.addRead = redis_source_add_read;
+    ac->ev.delRead = redis_source_del_read;
+    ac->ev.addWrite = redis_source_add_write;
+    ac->ev.delWrite = redis_source_del_write;
+    ac->ev.cleanup = redis_source_cleanup;
+    ac->ev.data = source;
+
+    return (GSource *)source;
+}
+
+#endif /* __HIREDIS_GLIB_H__ */

+ 84 - 0
thirdparty/hiredis/include/hiredis/adapters/ivykis.h

@@ -0,0 +1,84 @@
+#ifndef __HIREDIS_IVYKIS_H__
+#define __HIREDIS_IVYKIS_H__
+#include <iv.h>
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct redisIvykisEvents {
+    redisAsyncContext *context;
+    struct iv_fd fd;
+} redisIvykisEvents;
+
+static void redisIvykisReadEvent(void *arg) {
+    redisAsyncContext *context = (redisAsyncContext *)arg;
+    redisAsyncHandleRead(context);
+}
+
+static void redisIvykisWriteEvent(void *arg) {
+    redisAsyncContext *context = (redisAsyncContext *)arg;
+    redisAsyncHandleWrite(context);
+}
+
+static void redisIvykisAddRead(void *privdata) {
+    redisIvykisEvents *e = (redisIvykisEvents*)privdata;
+    iv_fd_set_handler_in(&e->fd, redisIvykisReadEvent);
+}
+
+static void redisIvykisDelRead(void *privdata) {
+    redisIvykisEvents *e = (redisIvykisEvents*)privdata;
+    iv_fd_set_handler_in(&e->fd, NULL);
+}
+
+static void redisIvykisAddWrite(void *privdata) {
+    redisIvykisEvents *e = (redisIvykisEvents*)privdata;
+    iv_fd_set_handler_out(&e->fd, redisIvykisWriteEvent);
+}
+
+static void redisIvykisDelWrite(void *privdata) {
+    redisIvykisEvents *e = (redisIvykisEvents*)privdata;
+    iv_fd_set_handler_out(&e->fd, NULL);
+}
+
+static void redisIvykisCleanup(void *privdata) {
+    redisIvykisEvents *e = (redisIvykisEvents*)privdata;
+
+    iv_fd_unregister(&e->fd);
+    hi_free(e);
+}
+
+static int redisIvykisAttach(redisAsyncContext *ac) {
+    redisContext *c = &(ac->c);
+    redisIvykisEvents *e;
+
+    /* Nothing should be attached when something is already attached */
+    if (ac->ev.data != NULL)
+        return REDIS_ERR;
+
+    /* Create container for context and r/w events */
+    e = (redisIvykisEvents*)hi_malloc(sizeof(*e));
+    if (e == NULL)
+        return REDIS_ERR;
+
+    e->context = ac;
+
+    /* Register functions to start/stop listening for events */
+    ac->ev.addRead = redisIvykisAddRead;
+    ac->ev.delRead = redisIvykisDelRead;
+    ac->ev.addWrite = redisIvykisAddWrite;
+    ac->ev.delWrite = redisIvykisDelWrite;
+    ac->ev.cleanup = redisIvykisCleanup;
+    ac->ev.data = e;
+
+    /* Initialize and install read/write events */
+    IV_FD_INIT(&e->fd);
+    e->fd.fd = c->fd;
+    e->fd.handler_in = redisIvykisReadEvent;
+    e->fd.handler_out = redisIvykisWriteEvent;
+    e->fd.handler_err = NULL;
+    e->fd.cookie = e->context;
+
+    iv_fd_register(&e->fd);
+
+    return REDIS_OK;
+}
+#endif

+ 188 - 0
thirdparty/hiredis/include/hiredis/adapters/libev.h

@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_LIBEV_H__
+#define __HIREDIS_LIBEV_H__
+#include <stdlib.h>
+#include <sys/types.h>
+#include <ev.h>
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct redisLibevEvents {
+    redisAsyncContext *context;
+    struct ev_loop *loop;
+    int reading, writing;
+    ev_io rev, wev;
+    ev_timer timer;
+} redisLibevEvents;
+
+static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
+#if EV_MULTIPLICITY
+    ((void)EV_A);
+#endif
+    ((void)revents);
+
+    redisLibevEvents *e = (redisLibevEvents*)watcher->data;
+    redisAsyncHandleRead(e->context);
+}
+
+static void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {
+#if EV_MULTIPLICITY
+    ((void)EV_A);
+#endif
+    ((void)revents);
+
+    redisLibevEvents *e = (redisLibevEvents*)watcher->data;
+    redisAsyncHandleWrite(e->context);
+}
+
+static void redisLibevAddRead(void *privdata) {
+    redisLibevEvents *e = (redisLibevEvents*)privdata;
+#if EV_MULTIPLICITY
+    struct ev_loop *loop = e->loop;
+#endif
+    if (!e->reading) {
+        e->reading = 1;
+        ev_io_start(EV_A_ &e->rev);
+    }
+}
+
+static void redisLibevDelRead(void *privdata) {
+    redisLibevEvents *e = (redisLibevEvents*)privdata;
+#if EV_MULTIPLICITY
+    struct ev_loop *loop = e->loop;
+#endif
+    if (e->reading) {
+        e->reading = 0;
+        ev_io_stop(EV_A_ &e->rev);
+    }
+}
+
+static void redisLibevAddWrite(void *privdata) {
+    redisLibevEvents *e = (redisLibevEvents*)privdata;
+#if EV_MULTIPLICITY
+    struct ev_loop *loop = e->loop;
+#endif
+    if (!e->writing) {
+        e->writing = 1;
+        ev_io_start(EV_A_ &e->wev);
+    }
+}
+
+static void redisLibevDelWrite(void *privdata) {
+    redisLibevEvents *e = (redisLibevEvents*)privdata;
+#if EV_MULTIPLICITY
+    struct ev_loop *loop = e->loop;
+#endif
+    if (e->writing) {
+        e->writing = 0;
+        ev_io_stop(EV_A_ &e->wev);
+    }
+}
+
+static void redisLibevStopTimer(void *privdata) {
+    redisLibevEvents *e = (redisLibevEvents*)privdata;
+#if EV_MULTIPLICITY
+    struct ev_loop *loop = e->loop;
+#endif
+    ev_timer_stop(EV_A_ &e->timer);
+}
+
+static void redisLibevCleanup(void *privdata) {
+    redisLibevEvents *e = (redisLibevEvents*)privdata;
+    redisLibevDelRead(privdata);
+    redisLibevDelWrite(privdata);
+    redisLibevStopTimer(privdata);
+    hi_free(e);
+}
+
+static void redisLibevTimeout(EV_P_ ev_timer *timer, int revents) {
+#if EV_MULTIPLICITY
+    ((void)EV_A);
+#endif
+    ((void)revents);
+    redisLibevEvents *e = (redisLibevEvents*)timer->data;
+    redisAsyncHandleTimeout(e->context);
+}
+
+static void redisLibevSetTimeout(void *privdata, struct timeval tv) {
+    redisLibevEvents *e = (redisLibevEvents*)privdata;
+#if EV_MULTIPLICITY
+    struct ev_loop *loop = e->loop;
+#endif
+
+    if (!ev_is_active(&e->timer)) {
+        ev_init(&e->timer, redisLibevTimeout);
+        e->timer.data = e;
+    }
+
+    e->timer.repeat = tv.tv_sec + tv.tv_usec / 1000000.00;
+    ev_timer_again(EV_A_ &e->timer);
+}
+
+static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
+    redisContext *c = &(ac->c);
+    redisLibevEvents *e;
+
+    /* Nothing should be attached when something is already attached */
+    if (ac->ev.data != NULL)
+        return REDIS_ERR;
+
+    /* Create container for context and r/w events */
+    e = (redisLibevEvents*)hi_calloc(1, sizeof(*e));
+    if (e == NULL)
+        return REDIS_ERR;
+
+    e->context = ac;
+#if EV_MULTIPLICITY
+    e->loop = EV_A;
+#else
+    e->loop = NULL;
+#endif
+    e->rev.data = e;
+    e->wev.data = e;
+
+    /* Register functions to start/stop listening for events */
+    ac->ev.addRead = redisLibevAddRead;
+    ac->ev.delRead = redisLibevDelRead;
+    ac->ev.addWrite = redisLibevAddWrite;
+    ac->ev.delWrite = redisLibevDelWrite;
+    ac->ev.cleanup = redisLibevCleanup;
+    ac->ev.scheduleTimer = redisLibevSetTimeout;
+    ac->ev.data = e;
+
+    /* Initialize read/write events */
+    ev_io_init(&e->rev,redisLibevReadEvent,c->fd,EV_READ);
+    ev_io_init(&e->wev,redisLibevWriteEvent,c->fd,EV_WRITE);
+    return REDIS_OK;
+}
+
+#endif

+ 175 - 0
thirdparty/hiredis/include/hiredis/adapters/libevent.h

@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_LIBEVENT_H__
+#define __HIREDIS_LIBEVENT_H__
+#include <event2/event.h>
+#include "../hiredis.h"
+#include "../async.h"
+
+#define REDIS_LIBEVENT_DELETED 0x01
+#define REDIS_LIBEVENT_ENTERED 0x02
+
+typedef struct redisLibeventEvents {
+    redisAsyncContext *context;
+    struct event *ev;
+    struct event_base *base;
+    struct timeval tv;
+    short flags;
+    short state;
+} redisLibeventEvents;
+
+static void redisLibeventDestroy(redisLibeventEvents *e) {
+    hi_free(e);
+}
+
+static void redisLibeventHandler(evutil_socket_t fd, short event, void *arg) {
+    ((void)fd);
+    redisLibeventEvents *e = (redisLibeventEvents*)arg;
+    e->state |= REDIS_LIBEVENT_ENTERED;
+
+    #define CHECK_DELETED() if (e->state & REDIS_LIBEVENT_DELETED) {\
+        redisLibeventDestroy(e);\
+        return; \
+    }
+
+    if ((event & EV_TIMEOUT) && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
+        redisAsyncHandleTimeout(e->context);
+        CHECK_DELETED();
+    }
+
+    if ((event & EV_READ) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
+        redisAsyncHandleRead(e->context);
+        CHECK_DELETED();
+    }
+
+    if ((event & EV_WRITE) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
+        redisAsyncHandleWrite(e->context);
+        CHECK_DELETED();
+    }
+
+    e->state &= ~REDIS_LIBEVENT_ENTERED;
+    #undef CHECK_DELETED
+}
+
+static void redisLibeventUpdate(void *privdata, short flag, int isRemove) {
+    redisLibeventEvents *e = (redisLibeventEvents *)privdata;
+    const struct timeval *tv = e->tv.tv_sec || e->tv.tv_usec ? &e->tv : NULL;
+
+    if (isRemove) {
+        if ((e->flags & flag) == 0) {
+            return;
+        } else {
+            e->flags &= ~flag;
+        }
+    } else {
+        if (e->flags & flag) {
+            return;
+        } else {
+            e->flags |= flag;
+        }
+    }
+
+    event_del(e->ev);
+    event_assign(e->ev, e->base, e->context->c.fd, e->flags | EV_PERSIST,
+                 redisLibeventHandler, privdata);
+    event_add(e->ev, tv);
+}
+
+static void redisLibeventAddRead(void *privdata) {
+    redisLibeventUpdate(privdata, EV_READ, 0);
+}
+
+static void redisLibeventDelRead(void *privdata) {
+    redisLibeventUpdate(privdata, EV_READ, 1);
+}
+
+static void redisLibeventAddWrite(void *privdata) {
+    redisLibeventUpdate(privdata, EV_WRITE, 0);
+}
+
+static void redisLibeventDelWrite(void *privdata) {
+    redisLibeventUpdate(privdata, EV_WRITE, 1);
+}
+
+static void redisLibeventCleanup(void *privdata) {
+    redisLibeventEvents *e = (redisLibeventEvents*)privdata;
+    if (!e) {
+        return;
+    }
+    event_del(e->ev);
+    event_free(e->ev);
+    e->ev = NULL;
+
+    if (e->state & REDIS_LIBEVENT_ENTERED) {
+        e->state |= REDIS_LIBEVENT_DELETED;
+    } else {
+        redisLibeventDestroy(e);
+    }
+}
+
+static void redisLibeventSetTimeout(void *privdata, struct timeval tv) {
+    redisLibeventEvents *e = (redisLibeventEvents *)privdata;
+    short flags = e->flags;
+    e->flags = 0;
+    e->tv = tv;
+    redisLibeventUpdate(e, flags, 0);
+}
+
+static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
+    redisContext *c = &(ac->c);
+    redisLibeventEvents *e;
+
+    /* Nothing should be attached when something is already attached */
+    if (ac->ev.data != NULL)
+        return REDIS_ERR;
+
+    /* Create container for context and r/w events */
+    e = (redisLibeventEvents*)hi_calloc(1, sizeof(*e));
+    if (e == NULL)
+        return REDIS_ERR;
+
+    e->context = ac;
+
+    /* Register functions to start/stop listening for events */
+    ac->ev.addRead = redisLibeventAddRead;
+    ac->ev.delRead = redisLibeventDelRead;
+    ac->ev.addWrite = redisLibeventAddWrite;
+    ac->ev.delWrite = redisLibeventDelWrite;
+    ac->ev.cleanup = redisLibeventCleanup;
+    ac->ev.scheduleTimer = redisLibeventSetTimeout;
+    ac->ev.data = e;
+
+    /* Initialize and install read/write events */
+    e->ev = event_new(base, c->fd, EV_READ | EV_WRITE, redisLibeventHandler, e);
+    e->base = base;
+    return REDIS_OK;
+}
+#endif

+ 123 - 0
thirdparty/hiredis/include/hiredis/adapters/libhv.h

@@ -0,0 +1,123 @@
+#ifndef __HIREDIS_LIBHV_H__
+#define __HIREDIS_LIBHV_H__
+
+#include <hv/hloop.h>
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct redisLibhvEvents {
+    hio_t *io;
+    htimer_t *timer;
+} redisLibhvEvents;
+
+static void redisLibhvHandleEvents(hio_t* io) {
+    redisAsyncContext* context = (redisAsyncContext*)hevent_userdata(io);
+    int events = hio_events(io);
+    int revents = hio_revents(io);
+    if (context && (events & HV_READ) && (revents & HV_READ)) {
+        redisAsyncHandleRead(context);
+    }
+    if (context && (events & HV_WRITE) && (revents & HV_WRITE)) {
+        redisAsyncHandleWrite(context);
+    }
+}
+
+static void redisLibhvAddRead(void *privdata) {
+    redisLibhvEvents* events = (redisLibhvEvents*)privdata;
+    hio_add(events->io, redisLibhvHandleEvents, HV_READ);
+}
+
+static void redisLibhvDelRead(void *privdata) {
+    redisLibhvEvents* events = (redisLibhvEvents*)privdata;
+    hio_del(events->io, HV_READ);
+}
+
+static void redisLibhvAddWrite(void *privdata) {
+    redisLibhvEvents* events = (redisLibhvEvents*)privdata;
+    hio_add(events->io, redisLibhvHandleEvents, HV_WRITE);
+}
+
+static void redisLibhvDelWrite(void *privdata) {
+    redisLibhvEvents* events = (redisLibhvEvents*)privdata;
+    hio_del(events->io, HV_WRITE);
+}
+
+static void redisLibhvCleanup(void *privdata) {
+    redisLibhvEvents* events = (redisLibhvEvents*)privdata;
+
+    if (events->timer)
+        htimer_del(events->timer);
+
+    hio_close(events->io);
+    hevent_set_userdata(events->io, NULL);
+
+    hi_free(events);
+}
+
+static void redisLibhvTimeout(htimer_t* timer) {
+    hio_t* io = (hio_t*)hevent_userdata(timer);
+    redisAsyncHandleTimeout((redisAsyncContext*)hevent_userdata(io));
+}
+
+static void redisLibhvSetTimeout(void *privdata, struct timeval tv) {
+    redisLibhvEvents* events;
+    uint32_t millis;
+    hloop_t* loop;
+
+    events = (redisLibhvEvents*)privdata;
+    millis = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+
+    if (millis == 0) {
+        /* Libhv disallows zero'd timers so treat this as a delete or NO OP */
+        if (events->timer) {
+            htimer_del(events->timer);
+            events->timer = NULL;
+        }
+    } else if (events->timer == NULL) {
+        /* Add new timer */
+        loop = hevent_loop(events->io);
+        events->timer = htimer_add(loop, redisLibhvTimeout, millis, 1);
+        hevent_set_userdata(events->timer, events->io);
+    } else {
+        /* Update existing timer */
+        htimer_reset(events->timer, millis);
+    }
+}
+
+static int redisLibhvAttach(redisAsyncContext* ac, hloop_t* loop) {
+    redisContext *c = &(ac->c);
+    redisLibhvEvents *events;
+    hio_t* io = NULL;
+
+    if (ac->ev.data != NULL) {
+        return REDIS_ERR;
+    }
+
+    /* Create container struct to keep track of our io and any timer */
+    events = (redisLibhvEvents*)hi_malloc(sizeof(*events));
+    if (events == NULL) {
+        return REDIS_ERR;
+    }
+
+    io = hio_get(loop, c->fd);
+    if (io == NULL) {
+        hi_free(events);
+        return REDIS_ERR;
+    }
+
+    hevent_set_userdata(io, ac);
+
+    events->io = io;
+    events->timer = NULL;
+
+    ac->ev.addRead  = redisLibhvAddRead;
+    ac->ev.delRead  = redisLibhvDelRead;
+    ac->ev.addWrite = redisLibhvAddWrite;
+    ac->ev.delWrite = redisLibhvDelWrite;
+    ac->ev.cleanup  = redisLibhvCleanup;
+    ac->ev.scheduleTimer = redisLibhvSetTimeout;
+    ac->ev.data = events;
+
+    return REDIS_OK;
+}
+#endif

+ 171 - 0
thirdparty/hiredis/include/hiredis/adapters/libuv.h

@@ -0,0 +1,171 @@
+#ifndef __HIREDIS_LIBUV_H__
+#define __HIREDIS_LIBUV_H__
+#include <stdlib.h>
+#include <uv.h>
+#include "../hiredis.h"
+#include "../async.h"
+#include <string.h>
+
+typedef struct redisLibuvEvents {
+    redisAsyncContext* context;
+    uv_poll_t          handle;
+    uv_timer_t         timer;
+    int                events;
+} redisLibuvEvents;
+
+
+static void redisLibuvPoll(uv_poll_t* handle, int status, int events) {
+    redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
+    int ev = (status ? p->events : events);
+
+    if (p->context != NULL && (ev & UV_READABLE)) {
+        redisAsyncHandleRead(p->context);
+    }
+    if (p->context != NULL && (ev & UV_WRITABLE)) {
+        redisAsyncHandleWrite(p->context);
+    }
+}
+
+
+static void redisLibuvAddRead(void *privdata) {
+    redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+    if (p->events & UV_READABLE) {
+        return;
+    }
+
+    p->events |= UV_READABLE;
+
+    uv_poll_start(&p->handle, p->events, redisLibuvPoll);
+}
+
+
+static void redisLibuvDelRead(void *privdata) {
+    redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+    p->events &= ~UV_READABLE;
+
+    if (p->events) {
+        uv_poll_start(&p->handle, p->events, redisLibuvPoll);
+    } else {
+        uv_poll_stop(&p->handle);
+    }
+}
+
+
+static void redisLibuvAddWrite(void *privdata) {
+    redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+    if (p->events & UV_WRITABLE) {
+        return;
+    }
+
+    p->events |= UV_WRITABLE;
+
+    uv_poll_start(&p->handle, p->events, redisLibuvPoll);
+}
+
+
+static void redisLibuvDelWrite(void *privdata) {
+    redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+    p->events &= ~UV_WRITABLE;
+
+    if (p->events) {
+        uv_poll_start(&p->handle, p->events, redisLibuvPoll);
+    } else {
+        uv_poll_stop(&p->handle);
+    }
+}
+
+static void on_timer_close(uv_handle_t *handle) {
+    redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
+    p->timer.data = NULL;
+    if (!p->handle.data) {
+        // both timer and handle are closed
+        hi_free(p);
+    }
+    // else, wait for `on_handle_close`
+}
+
+static void on_handle_close(uv_handle_t *handle) {
+    redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
+    p->handle.data = NULL;
+    if (!p->timer.data) {
+        // timer never started, or timer already destroyed
+        hi_free(p);
+    }
+    // else, wait for `on_timer_close`
+}
+
+// libuv removed `status` parameter since v0.11.23
+// see: https://github.com/libuv/libuv/blob/v0.11.23/include/uv.h
+#if (UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR < 11) || \
+    (UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR == 11 && UV_VERSION_PATCH < 23)
+static void redisLibuvTimeout(uv_timer_t *timer, int status) {
+    (void)status; // unused
+#else
+static void redisLibuvTimeout(uv_timer_t *timer) {
+#endif
+    redisLibuvEvents *e = (redisLibuvEvents*)timer->data;
+    redisAsyncHandleTimeout(e->context);
+}
+
+static void redisLibuvSetTimeout(void *privdata, struct timeval tv) {
+    redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+    uint64_t millsec = tv.tv_sec * 1000 + tv.tv_usec / 1000.0;
+    if (!p->timer.data) {
+        // timer is uninitialized
+        if (uv_timer_init(p->handle.loop, &p->timer) != 0) {
+            return;
+        }
+        p->timer.data = p;
+    }
+    // updates the timeout if the timer has already started
+    // or start the timer
+    uv_timer_start(&p->timer, redisLibuvTimeout, millsec, 0);
+}
+
+static void redisLibuvCleanup(void *privdata) {
+    redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+    p->context = NULL; // indicate that context might no longer exist
+    if (p->timer.data) {
+        uv_close((uv_handle_t*)&p->timer, on_timer_close);
+    }
+    uv_close((uv_handle_t*)&p->handle, on_handle_close);
+}
+
+
+static int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) {
+    redisContext *c = &(ac->c);
+
+    if (ac->ev.data != NULL) {
+        return REDIS_ERR;
+    }
+
+    ac->ev.addRead        = redisLibuvAddRead;
+    ac->ev.delRead        = redisLibuvDelRead;
+    ac->ev.addWrite       = redisLibuvAddWrite;
+    ac->ev.delWrite       = redisLibuvDelWrite;
+    ac->ev.cleanup        = redisLibuvCleanup;
+    ac->ev.scheduleTimer  = redisLibuvSetTimeout;
+
+    redisLibuvEvents* p = (redisLibuvEvents*)hi_malloc(sizeof(*p));
+    if (p == NULL)
+        return REDIS_ERR;
+
+    memset(p, 0, sizeof(*p));
+
+    if (uv_poll_init_socket(loop, &p->handle, c->fd) != 0) {
+        return REDIS_ERR;
+    }
+
+    ac->ev.data    = p;
+    p->handle.data = p;
+    p->context     = ac;
+
+    return REDIS_OK;
+}
+#endif

+ 115 - 0
thirdparty/hiredis/include/hiredis/adapters/macosx.h

@@ -0,0 +1,115 @@
+//
+//  Created by Дмитрий Бахвалов on 13.07.15.
+//  Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved.
+//
+
+#ifndef __HIREDIS_MACOSX_H__
+#define __HIREDIS_MACOSX_H__
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct {
+    redisAsyncContext *context;
+    CFSocketRef socketRef;
+    CFRunLoopSourceRef sourceRef;
+} RedisRunLoop;
+
+static int freeRedisRunLoop(RedisRunLoop* redisRunLoop) {
+    if( redisRunLoop != NULL ) {
+        if( redisRunLoop->sourceRef != NULL ) {
+            CFRunLoopSourceInvalidate(redisRunLoop->sourceRef);
+            CFRelease(redisRunLoop->sourceRef);
+        }
+        if( redisRunLoop->socketRef != NULL ) {
+            CFSocketInvalidate(redisRunLoop->socketRef);
+            CFRelease(redisRunLoop->socketRef);
+        }
+        hi_free(redisRunLoop);
+    }
+    return REDIS_ERR;
+}
+
+static void redisMacOSAddRead(void *privdata) {
+    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
+    CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);
+}
+
+static void redisMacOSDelRead(void *privdata) {
+    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
+    CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);
+}
+
+static void redisMacOSAddWrite(void *privdata) {
+    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
+    CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);
+}
+
+static void redisMacOSDelWrite(void *privdata) {
+    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
+    CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);
+}
+
+static void redisMacOSCleanup(void *privdata) {
+    RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
+    freeRedisRunLoop(redisRunLoop);
+}
+
+static void redisMacOSAsyncCallback(CFSocketRef __unused s, CFSocketCallBackType callbackType, CFDataRef __unused address, const void __unused *data, void *info) {
+    redisAsyncContext* context = (redisAsyncContext*) info;
+
+    switch (callbackType) {
+        case kCFSocketReadCallBack:
+            redisAsyncHandleRead(context);
+            break;
+
+        case kCFSocketWriteCallBack:
+            redisAsyncHandleWrite(context);
+            break;
+
+        default:
+            break;
+    }
+}
+
+static int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLoop) {
+    redisContext *redisCtx = &(redisAsyncCtx->c);
+
+    /* Nothing should be attached when something is already attached */
+    if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR;
+
+    RedisRunLoop* redisRunLoop = (RedisRunLoop*) hi_calloc(1, sizeof(RedisRunLoop));
+    if (redisRunLoop == NULL)
+        return REDIS_ERR;
+
+    /* Setup redis stuff */
+    redisRunLoop->context = redisAsyncCtx;
+
+    redisAsyncCtx->ev.addRead  = redisMacOSAddRead;
+    redisAsyncCtx->ev.delRead  = redisMacOSDelRead;
+    redisAsyncCtx->ev.addWrite = redisMacOSAddWrite;
+    redisAsyncCtx->ev.delWrite = redisMacOSDelWrite;
+    redisAsyncCtx->ev.cleanup  = redisMacOSCleanup;
+    redisAsyncCtx->ev.data     = redisRunLoop;
+
+    /* Initialize and install read/write events */
+    CFSocketContext socketCtx = { 0, redisAsyncCtx, NULL, NULL, NULL };
+
+    redisRunLoop->socketRef = CFSocketCreateWithNative(NULL, redisCtx->fd,
+                                                       kCFSocketReadCallBack | kCFSocketWriteCallBack,
+                                                       redisMacOSAsyncCallback,
+                                                       &socketCtx);
+    if( !redisRunLoop->socketRef ) return freeRedisRunLoop(redisRunLoop);
+
+    redisRunLoop->sourceRef = CFSocketCreateRunLoopSource(NULL, redisRunLoop->socketRef, 0);
+    if( !redisRunLoop->sourceRef ) return freeRedisRunLoop(redisRunLoop);
+
+    CFRunLoopAddSource(runLoop, redisRunLoop->sourceRef, kCFRunLoopDefaultMode);
+
+    return REDIS_OK;
+}
+
+#endif
+

+ 197 - 0
thirdparty/hiredis/include/hiredis/adapters/poll.h

@@ -0,0 +1,197 @@
+
+#ifndef HIREDIS_POLL_H
+#define HIREDIS_POLL_H
+
+#include "../async.h"
+#include "../sockcompat.h"
+#include <string.h> // for memset
+#include <errno.h>
+
+/* Values to return from redisPollTick */
+#define REDIS_POLL_HANDLED_READ    1
+#define REDIS_POLL_HANDLED_WRITE   2
+#define REDIS_POLL_HANDLED_TIMEOUT 4
+
+/* An adapter to allow manual polling of the async context by checking the state
+ * of the underlying file descriptor.  Useful in cases where there is no formal
+ * IO event loop but regular ticking can be used, such as in game engines. */
+
+typedef struct redisPollEvents {
+    redisAsyncContext *context;
+    redisFD fd;
+    char reading, writing;
+    char in_tick;
+    char deleted;
+    double deadline;
+} redisPollEvents;
+
+static double redisPollTimevalToDouble(struct timeval *tv) {
+    if (tv == NULL)
+        return 0.0;
+    return tv->tv_sec + tv->tv_usec / 1000000.00;
+}
+
+static double redisPollGetNow(void) {
+#ifndef _MSC_VER
+    struct timeval tv;
+    gettimeofday(&tv,NULL);
+    return redisPollTimevalToDouble(&tv);
+#else
+    FILETIME ft;
+    ULARGE_INTEGER li;
+    GetSystemTimeAsFileTime(&ft);
+    li.HighPart = ft.dwHighDateTime;
+    li.LowPart = ft.dwLowDateTime;
+    return (double)li.QuadPart * 1e-7;
+#endif
+}
+
+/* Poll for io, handling any pending callbacks.  The timeout argument can be
+ * positive to wait for a maximum given time for IO, zero to poll, or negative
+ * to wait forever */
+static int redisPollTick(redisAsyncContext *ac, double timeout) {
+    int reading, writing;
+    struct pollfd pfd;
+    int handled;
+    int ns;
+    int itimeout;
+
+    redisPollEvents *e = (redisPollEvents*)ac->ev.data;
+    if (!e)
+        return 0;
+
+    /* local flags, won't get changed during callbacks */
+    reading = e->reading;
+    writing = e->writing;
+    if (!reading && !writing)
+        return 0;
+
+    pfd.fd = e->fd;
+    pfd.events = 0;
+    if (reading)
+        pfd.events = POLLIN;   
+    if (writing)
+        pfd.events |= POLLOUT;
+
+    if (timeout >= 0.0) {
+        itimeout = (int)(timeout * 1000.0);
+    } else {
+        itimeout = -1;
+    }
+
+    ns = poll(&pfd, 1, itimeout);
+    if (ns < 0) {
+        /* ignore the EINTR error */
+        if (errno != EINTR)
+            return ns;
+        ns = 0;
+    }
+    
+    handled = 0;
+    e->in_tick = 1;
+    if (ns) {
+        if (reading && (pfd.revents & POLLIN)) {
+            redisAsyncHandleRead(ac);
+            handled |= REDIS_POLL_HANDLED_READ;
+        }
+        /* on Windows, connection failure is indicated with the Exception fdset.
+         * handle it the same as writable. */
+        if (writing && (pfd.revents & (POLLOUT | POLLERR))) {
+            /* context Read callback may have caused context to be deleted, e.g.
+               by doing an redisAsyncDisconnect() */
+            if (!e->deleted) {
+                redisAsyncHandleWrite(ac);
+                handled |= REDIS_POLL_HANDLED_WRITE;
+            }
+        }
+    }
+
+    /* perform timeouts */
+    if (!e->deleted && e->deadline != 0.0) {
+        double now = redisPollGetNow();
+        if (now >= e->deadline) {
+            /* deadline has passed.  disable timeout and perform callback */
+            e->deadline = 0.0;
+            redisAsyncHandleTimeout(ac);
+            handled |= REDIS_POLL_HANDLED_TIMEOUT;
+        }
+    }
+
+    /* do a delayed cleanup if required */
+    if (e->deleted)
+        hi_free(e);
+    else
+        e->in_tick = 0;
+
+    return handled;
+}
+
+static void redisPollAddRead(void *data) {
+    redisPollEvents *e = (redisPollEvents*)data;
+    e->reading = 1;
+}
+
+static void redisPollDelRead(void *data) {
+    redisPollEvents *e = (redisPollEvents*)data;
+    e->reading = 0;
+}
+
+static void redisPollAddWrite(void *data) {
+    redisPollEvents *e = (redisPollEvents*)data;
+    e->writing = 1;
+}
+
+static void redisPollDelWrite(void *data) {
+    redisPollEvents *e = (redisPollEvents*)data;
+    e->writing = 0;
+}
+
+static void redisPollCleanup(void *data) {
+    redisPollEvents *e = (redisPollEvents*)data;
+
+    /* if we are currently processing a tick, postpone deletion */
+    if (e->in_tick)
+        e->deleted = 1;
+    else
+        hi_free(e);
+}
+
+static void redisPollScheduleTimer(void *data, struct timeval tv)
+{
+    redisPollEvents *e = (redisPollEvents*)data;
+    double now = redisPollGetNow();
+    e->deadline = now + redisPollTimevalToDouble(&tv);
+}
+
+static int redisPollAttach(redisAsyncContext *ac) {
+    redisContext *c = &(ac->c);
+    redisPollEvents *e;
+
+    /* Nothing should be attached when something is already attached */
+    if (ac->ev.data != NULL)
+        return REDIS_ERR;
+
+    /* Create container for context and r/w events */
+    e = (redisPollEvents*)hi_malloc(sizeof(*e));
+    if (e == NULL)
+        return REDIS_ERR;
+    memset(e, 0, sizeof(*e));
+
+    e->context = ac;
+    e->fd = c->fd;
+    e->reading = e->writing = 0;
+    e->in_tick = e->deleted = 0;
+    e->deadline = 0.0;
+
+    /* Register functions to start/stop listening for events */
+    ac->ev.addRead = redisPollAddRead;
+    ac->ev.delRead = redisPollDelRead;
+    ac->ev.addWrite = redisPollAddWrite;
+    ac->ev.delWrite = redisPollDelWrite;
+    ac->ev.scheduleTimer = redisPollScheduleTimer;
+    ac->ev.cleanup = redisPollCleanup;
+    ac->ev.data = e;
+
+    return REDIS_OK;
+}
+#endif /* HIREDIS_POLL_H */

+ 135 - 0
thirdparty/hiredis/include/hiredis/adapters/qt.h

@@ -0,0 +1,135 @@
+/*-
+ * Copyright (C) 2014 Pietro Cerutti <gahr@gahr.ch>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_QT_H__
+#define __HIREDIS_QT_H__
+#include <QSocketNotifier>
+#include "../async.h"
+
+static void RedisQtAddRead(void *);
+static void RedisQtDelRead(void *);
+static void RedisQtAddWrite(void *);
+static void RedisQtDelWrite(void *);
+static void RedisQtCleanup(void *);
+
+class RedisQtAdapter : public QObject {
+
+    Q_OBJECT
+
+    friend
+    void RedisQtAddRead(void * adapter) {
+        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
+        a->addRead();
+    }
+
+    friend
+    void RedisQtDelRead(void * adapter) {
+        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
+        a->delRead();
+    }
+
+    friend
+    void RedisQtAddWrite(void * adapter) {
+        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
+        a->addWrite();
+    }
+
+    friend
+    void RedisQtDelWrite(void * adapter) {
+        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
+        a->delWrite();
+    }
+
+    friend
+    void RedisQtCleanup(void * adapter) {
+        RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
+        a->cleanup();
+    }
+
+    public:
+        RedisQtAdapter(QObject * parent = 0)
+            : QObject(parent), m_ctx(0), m_read(0), m_write(0) { }
+
+        ~RedisQtAdapter() {
+            if (m_ctx != 0) {
+                m_ctx->ev.data = NULL;
+            }
+        }
+
+        int setContext(redisAsyncContext * ac) {
+            if (ac->ev.data != NULL) {
+                return REDIS_ERR;
+            }
+            m_ctx = ac;
+            m_ctx->ev.data = this;
+            m_ctx->ev.addRead = RedisQtAddRead;
+            m_ctx->ev.delRead = RedisQtDelRead;
+            m_ctx->ev.addWrite = RedisQtAddWrite;
+            m_ctx->ev.delWrite = RedisQtDelWrite;
+            m_ctx->ev.cleanup = RedisQtCleanup;
+            return REDIS_OK;
+        }
+
+    private:
+        void addRead() {
+            if (m_read) return;
+            m_read = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Read, 0);
+            connect(m_read, SIGNAL(activated(int)), this, SLOT(read()));
+        }
+
+        void delRead() {
+            if (!m_read) return;
+            delete m_read;
+            m_read = 0;
+        }
+
+        void addWrite() {
+            if (m_write) return;
+            m_write = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Write, 0);
+            connect(m_write, SIGNAL(activated(int)), this, SLOT(write()));
+        }
+
+        void delWrite() {
+            if (!m_write) return;
+            delete m_write;
+            m_write = 0;
+        }
+
+        void cleanup() {
+            delRead();
+            delWrite();
+        }
+
+    private slots:
+        void read() { redisAsyncHandleRead(m_ctx); }
+        void write() { redisAsyncHandleWrite(m_ctx); }
+
+    private:
+        redisAsyncContext * m_ctx;
+        QSocketNotifier * m_read;
+        QSocketNotifier * m_write;
+};
+
+#endif /* !__HIREDIS_QT_H__ */

+ 96 - 0
thirdparty/hiredis/include/hiredis/alloc.h

@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef HIREDIS_ALLOC_H
+#define HIREDIS_ALLOC_H
+
+#include <stddef.h> /* for size_t */
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Structure pointing to our actually configured allocators */
+typedef struct hiredisAllocFuncs {
+    void *(*mallocFn)(size_t);
+    void *(*callocFn)(size_t,size_t);
+    void *(*reallocFn)(void*,size_t);
+    char *(*strdupFn)(const char*);
+    void (*freeFn)(void*);
+} hiredisAllocFuncs;
+
+hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *ha);
+void hiredisResetAllocators(void);
+
+#ifndef _WIN32
+
+/* Hiredis' configured allocator function pointer struct */
+extern hiredisAllocFuncs hiredisAllocFns;
+
+static inline void *hi_malloc(size_t size) {
+    return hiredisAllocFns.mallocFn(size);
+}
+
+static inline void *hi_calloc(size_t nmemb, size_t size) {
+    /* Overflow check as the user can specify any arbitrary allocator */
+    if (SIZE_MAX / size < nmemb)
+        return NULL;
+
+    return hiredisAllocFns.callocFn(nmemb, size);
+}
+
+static inline void *hi_realloc(void *ptr, size_t size) {
+    return hiredisAllocFns.reallocFn(ptr, size);
+}
+
+static inline char *hi_strdup(const char *str) {
+    return hiredisAllocFns.strdupFn(str);
+}
+
+static inline void hi_free(void *ptr) {
+    hiredisAllocFns.freeFn(ptr);
+}
+
+#else
+
+void *hi_malloc(size_t size);
+void *hi_calloc(size_t nmemb, size_t size);
+void *hi_realloc(void *ptr, size_t size);
+char *hi_strdup(const char *str);
+void hi_free(void *ptr);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HIREDIS_ALLOC_H */

+ 152 - 0
thirdparty/hiredis/include/hiredis/async.h

@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_ASYNC_H
+#define __HIREDIS_ASYNC_H
+#include "hiredis.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct redisAsyncContext; /* need forward declaration of redisAsyncContext */
+struct dict; /* dictionary header is included in async.c */
+
+/* Reply callback prototype and container */
+typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*);
+typedef struct redisCallback {
+    struct redisCallback *next; /* simple singly linked list */
+    redisCallbackFn *fn;
+    int pending_subs;
+    int unsubscribe_sent;
+    void *privdata;
+} redisCallback;
+
+/* List of callbacks for either regular replies or pub/sub */
+typedef struct redisCallbackList {
+    redisCallback *head, *tail;
+} redisCallbackList;
+
+/* Connection callback prototypes */
+typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status);
+typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status);
+typedef void (redisConnectCallbackNC)(struct redisAsyncContext *, int status);
+typedef void(redisTimerCallback)(void *timer, void *privdata);
+
+/* Context for an async connection to Redis */
+typedef struct redisAsyncContext {
+    /* Hold the regular context, so it can be realloc'ed. */
+    redisContext c;
+
+    /* Setup error flags so they can be used directly. */
+    int err;
+    char *errstr;
+
+    /* Not used by hiredis */
+    void *data;
+    void (*dataCleanup)(void *privdata);
+
+    /* Event library data and hooks */
+    struct {
+        void *data;
+
+        /* Hooks that are called when the library expects to start
+         * reading/writing. These functions should be idempotent. */
+        void (*addRead)(void *privdata);
+        void (*delRead)(void *privdata);
+        void (*addWrite)(void *privdata);
+        void (*delWrite)(void *privdata);
+        void (*cleanup)(void *privdata);
+        void (*scheduleTimer)(void *privdata, struct timeval tv);
+    } ev;
+
+    /* Called when either the connection is terminated due to an error or per
+     * user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */
+    redisDisconnectCallback *onDisconnect;
+
+    /* Called when the first write event was received. */
+    redisConnectCallback *onConnect;
+    redisConnectCallbackNC *onConnectNC;
+
+    /* Regular command callbacks */
+    redisCallbackList replies;
+
+    /* Address used for connect() */
+    struct sockaddr *saddr;
+    size_t addrlen;
+
+    /* Subscription callbacks */
+    struct {
+        redisCallbackList replies;
+        struct dict *channels;
+        struct dict *patterns;
+        int pending_unsubs;
+    } sub;
+
+    /* Any configured RESP3 PUSH handler */
+    redisAsyncPushFn *push_cb;
+} redisAsyncContext;
+
+/* Functions that proxy to hiredis */
+redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options);
+redisAsyncContext *redisAsyncConnect(const char *ip, int port);
+redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr);
+redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
+                                                  const char *source_addr);
+redisAsyncContext *redisAsyncConnectUnix(const char *path);
+int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
+int redisAsyncSetConnectCallbackNC(redisAsyncContext *ac, redisConnectCallbackNC *fn);
+int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
+
+redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn);
+int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv);
+void redisAsyncDisconnect(redisAsyncContext *ac);
+void redisAsyncFree(redisAsyncContext *ac);
+
+/* Handle read/write events */
+void redisAsyncHandleRead(redisAsyncContext *ac);
+void redisAsyncHandleWrite(redisAsyncContext *ac);
+void redisAsyncHandleTimeout(redisAsyncContext *ac);
+void redisAsyncRead(redisAsyncContext *ac);
+void redisAsyncWrite(redisAsyncContext *ac);
+
+/* Command functions for an async context. Write the command to the
+ * output buffer and register the provided callback. */
+int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap);
+int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...);
+int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen);
+int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 366 - 0
thirdparty/hiredis/include/hiredis/hiredis.h

@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
+ *                     Jan-Erik Rediger <janerik at fnordig dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_H
+#define __HIREDIS_H
+#include "read.h"
+#include <stdarg.h> /* for va_list */
+#ifndef _MSC_VER
+#include <sys/time.h> /* for struct timeval */
+#else
+struct timeval; /* forward declaration */
+typedef long long ssize_t;
+#endif
+#include <stdint.h> /* uintXX_t, etc */
+#include "sds.h" /* for sds */
+#include "alloc.h" /* for allocation wrappers */
+
+#define HIREDIS_MAJOR 1
+#define HIREDIS_MINOR 1
+#define HIREDIS_PATCH 0
+#define HIREDIS_SONAME 1.1.0
+
+/* Connection type can be blocking or non-blocking and is set in the
+ * least significant bit of the flags field in redisContext. */
+#define REDIS_BLOCK 0x1
+
+/* Connection may be disconnected before being free'd. The second bit
+ * in the flags field is set when the context is connected. */
+#define REDIS_CONNECTED 0x2
+
+/* The async API might try to disconnect cleanly and flush the output
+ * buffer and read all subsequent replies before disconnecting.
+ * This flag means no new commands can come in and the connection
+ * should be terminated once all replies have been read. */
+#define REDIS_DISCONNECTING 0x4
+
+/* Flag specific to the async API which means that the context should be clean
+ * up as soon as possible. */
+#define REDIS_FREEING 0x8
+
+/* Flag that is set when an async callback is executed. */
+#define REDIS_IN_CALLBACK 0x10
+
+/* Flag that is set when the async context has one or more subscriptions. */
+#define REDIS_SUBSCRIBED 0x20
+
+/* Flag that is set when monitor mode is active */
+#define REDIS_MONITORING 0x40
+
+/* Flag that is set when we should set SO_REUSEADDR before calling bind() */
+#define REDIS_REUSEADDR 0x80
+
+/* Flag that is set when the async connection supports push replies. */
+#define REDIS_SUPPORTS_PUSH 0x100
+
+/**
+ * Flag that indicates the user does not want the context to
+ * be automatically freed upon error
+ */
+#define REDIS_NO_AUTO_FREE 0x200
+
+/* Flag that indicates the user does not want replies to be automatically freed */
+#define REDIS_NO_AUTO_FREE_REPLIES 0x400
+
+/* Flags to prefer IPv6 or IPv4 when doing DNS lookup. (If both are set,
+ * AF_UNSPEC is used.) */
+#define REDIS_PREFER_IPV4 0x800
+#define REDIS_PREFER_IPV6 0x1000
+
+#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */
+
+/* number of times we retry to connect in the case of EADDRNOTAVAIL and
+ * SO_REUSEADDR is being used. */
+#define REDIS_CONNECT_RETRIES  10
+
+/* Forward declarations for structs defined elsewhere */
+struct redisAsyncContext;
+struct redisContext;
+
+/* RESP3 push helpers and callback prototypes */
+#define redisIsPushReply(r) (((redisReply*)(r))->type == REDIS_REPLY_PUSH)
+typedef void (redisPushFn)(void *, void *);
+typedef void (redisAsyncPushFn)(struct redisAsyncContext *, void *);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This is the reply object returned by redisCommand() */
+typedef struct redisReply {
+    int type; /* REDIS_REPLY_* */
+    long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
+    double dval; /* The double when type is REDIS_REPLY_DOUBLE */
+    size_t len; /* Length of string */
+    char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING
+                  REDIS_REPLY_VERB, REDIS_REPLY_DOUBLE (in additional to dval),
+                  and REDIS_REPLY_BIGNUM. */
+    char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null
+                      terminated 3 character content type, such as "txt". */
+    size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
+    struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
+} redisReply;
+
+redisReader *redisReaderCreate(void);
+
+/* Function to free the reply objects hiredis returns by default. */
+void freeReplyObject(void *reply);
+
+/* Functions to format a command according to the protocol. */
+int redisvFormatCommand(char **target, const char *format, va_list ap);
+int redisFormatCommand(char **target, const char *format, ...);
+long long redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
+long long redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);
+void redisFreeCommand(char *cmd);
+void redisFreeSdsCommand(sds cmd);
+
+enum redisConnectionType {
+    REDIS_CONN_TCP,
+    REDIS_CONN_UNIX,
+    REDIS_CONN_USERFD
+};
+
+struct redisSsl;
+
+#define REDIS_OPT_NONBLOCK 0x01
+#define REDIS_OPT_REUSEADDR 0x02
+#define REDIS_OPT_PREFER_IPV4 0x04
+#define REDIS_OPT_PREFER_IPV6 0x08
+#define REDIS_OPT_PREFER_IP_UNSPEC (REDIS_OPT_PREFER_IPV4 | REDIS_OPT_PREFER_IPV6)
+
+/**
+ * Don't automatically free the async object on a connection failure,
+ * or other implicit conditions. Only free on an explicit call to disconnect() or free()
+ */
+#define REDIS_OPT_NOAUTOFREE 0x04
+
+/* Don't automatically intercept and free RESP3 PUSH replies. */
+#define REDIS_OPT_NO_PUSH_AUTOFREE 0x08
+
+/**
+ * Don't automatically free replies
+ */
+#define REDIS_OPT_NOAUTOFREEREPLIES 0x10
+
+/* In Unix systems a file descriptor is a regular signed int, with -1
+ * representing an invalid descriptor. In Windows it is a SOCKET
+ * (32- or 64-bit unsigned integer depending on the architecture), where
+ * all bits set (~0) is INVALID_SOCKET.  */
+#ifndef _WIN32
+typedef int redisFD;
+#define REDIS_INVALID_FD -1
+#else
+#ifdef _WIN64
+typedef unsigned long long redisFD; /* SOCKET = 64-bit UINT_PTR */
+#else
+typedef unsigned long redisFD;      /* SOCKET = 32-bit UINT_PTR */
+#endif
+#define REDIS_INVALID_FD ((redisFD)(~0)) /* INVALID_SOCKET */
+#endif
+
+typedef struct {
+    /*
+     * the type of connection to use. This also indicates which
+     * `endpoint` member field to use
+     */
+    int type;
+    /* bit field of REDIS_OPT_xxx */
+    int options;
+    /* timeout value for connect operation. If NULL, no timeout is used */
+    const struct timeval *connect_timeout;
+    /* timeout value for commands. If NULL, no timeout is used.  This can be
+     * updated at runtime with redisSetTimeout/redisAsyncSetTimeout. */
+    const struct timeval *command_timeout;
+    union {
+        /** use this field for tcp/ip connections */
+        struct {
+            const char *source_addr;
+            const char *ip;
+            int port;
+        } tcp;
+        /** use this field for unix domain sockets */
+        const char *unix_socket;
+        /**
+         * use this field to have hiredis operate an already-open
+         * file descriptor */
+        redisFD fd;
+    } endpoint;
+
+    /* Optional user defined data/destructor */
+    void *privdata;
+    void (*free_privdata)(void *);
+
+    /* A user defined PUSH message callback */
+    redisPushFn *push_cb;
+    redisAsyncPushFn *async_push_cb;
+} redisOptions;
+
+/**
+ * Helper macros to initialize options to their specified fields.
+ */
+#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) do { \
+        (opts)->type = REDIS_CONN_TCP;               \
+        (opts)->endpoint.tcp.ip = ip_;               \
+        (opts)->endpoint.tcp.port = port_;           \
+    } while(0)
+
+#define REDIS_OPTIONS_SET_UNIX(opts, path) do { \
+        (opts)->type = REDIS_CONN_UNIX;         \
+        (opts)->endpoint.unix_socket = path;    \
+    } while(0)
+
+#define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) do {  \
+        (opts)->privdata = data;                           \
+        (opts)->free_privdata = dtor;                      \
+    } while(0)
+
+typedef struct redisContextFuncs {
+    void (*close)(struct redisContext *);
+    void (*free_privctx)(void *);
+    void (*async_read)(struct redisAsyncContext *);
+    void (*async_write)(struct redisAsyncContext *);
+
+    /* Read/Write data to the underlying communication stream, returning the
+     * number of bytes read/written.  In the event of an unrecoverable error
+     * these functions shall return a value < 0.  In the event of a
+     * recoverable error, they should return 0. */
+    ssize_t (*read)(struct redisContext *, char *, size_t);
+    ssize_t (*write)(struct redisContext *);
+} redisContextFuncs;
+
+
+/* Context for a connection to Redis */
+typedef struct redisContext {
+    const redisContextFuncs *funcs;   /* Function table */
+
+    int err; /* Error flags, 0 when there is no error */
+    char errstr[128]; /* String representation of error when applicable */
+    redisFD fd;
+    int flags;
+    char *obuf; /* Write buffer */
+    redisReader *reader; /* Protocol reader */
+
+    enum redisConnectionType connection_type;
+    struct timeval *connect_timeout;
+    struct timeval *command_timeout;
+
+    struct {
+        char *host;
+        char *source_addr;
+        int port;
+    } tcp;
+
+    struct {
+        char *path;
+    } unix_sock;
+
+    /* For non-blocking connect */
+    struct sockaddr *saddr;
+    size_t addrlen;
+
+    /* Optional data and corresponding destructor users can use to provide
+     * context to a given redisContext.  Not used by hiredis. */
+    void *privdata;
+    void (*free_privdata)(void *);
+
+    /* Internal context pointer presently used by hiredis to manage
+     * SSL connections. */
+    void *privctx;
+
+    /* An optional RESP3 PUSH handler */
+    redisPushFn *push_cb;
+} redisContext;
+
+redisContext *redisConnectWithOptions(const redisOptions *options);
+redisContext *redisConnect(const char *ip, int port);
+redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
+redisContext *redisConnectNonBlock(const char *ip, int port);
+redisContext *redisConnectBindNonBlock(const char *ip, int port,
+                                       const char *source_addr);
+redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
+                                                const char *source_addr);
+redisContext *redisConnectUnix(const char *path);
+redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
+redisContext *redisConnectUnixNonBlock(const char *path);
+redisContext *redisConnectFd(redisFD fd);
+
+/**
+ * Reconnect the given context using the saved information.
+ *
+ * This re-uses the exact same connect options as in the initial connection.
+ * host, ip (or path), timeout and bind address are reused,
+ * flags are used unmodified from the existing context.
+ *
+ * Returns REDIS_OK on successful connect or REDIS_ERR otherwise.
+ */
+int redisReconnect(redisContext *c);
+
+redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn);
+int redisSetTimeout(redisContext *c, const struct timeval tv);
+int redisEnableKeepAlive(redisContext *c);
+void redisFree(redisContext *c);
+redisFD redisFreeKeepFd(redisContext *c);
+int redisBufferRead(redisContext *c);
+int redisBufferWrite(redisContext *c, int *done);
+
+/* In a blocking context, this function first checks if there are unconsumed
+ * replies to return and returns one if so. Otherwise, it flushes the output
+ * buffer to the socket and reads until it has a reply. In a non-blocking
+ * context, it will return unconsumed replies until there are no more. */
+int redisGetReply(redisContext *c, void **reply);
+int redisGetReplyFromReader(redisContext *c, void **reply);
+
+/* Write a formatted command to the output buffer. Use these functions in blocking mode
+ * to get a pipeline of commands. */
+int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len);
+
+/* Write a command to the output buffer. Use these functions in blocking mode
+ * to get a pipeline of commands. */
+int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
+int redisAppendCommand(redisContext *c, const char *format, ...);
+int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
+
+/* Issue a command to Redis. In a blocking context, it is identical to calling
+ * redisAppendCommand, followed by redisGetReply. The function will return
+ * NULL if there was an error in performing the request, otherwise it will
+ * return the reply. In a non-blocking context, it is identical to calling
+ * only redisAppendCommand and will always return NULL. */
+void *redisvCommand(redisContext *c, const char *format, va_list ap);
+void *redisCommand(redisContext *c, const char *format, ...);
+void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 129 - 0
thirdparty/hiredis/include/hiredis/read.h

@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#ifndef __HIREDIS_READ_H
+#define __HIREDIS_READ_H
+#include <stdio.h> /* for size_t */
+
+#define REDIS_ERR -1
+#define REDIS_OK 0
+
+/* When an error occurs, the err flag in a context is set to hold the type of
+ * error that occurred. REDIS_ERR_IO means there was an I/O error and you
+ * should use the "errno" variable to find out what is wrong.
+ * For other values, the "errstr" field will hold a description. */
+#define REDIS_ERR_IO 1 /* Error in read or write */
+#define REDIS_ERR_EOF 3 /* End of file */
+#define REDIS_ERR_PROTOCOL 4 /* Protocol error */
+#define REDIS_ERR_OOM 5 /* Out of memory */
+#define REDIS_ERR_TIMEOUT 6 /* Timed out */
+#define REDIS_ERR_OTHER 2 /* Everything else... */
+
+#define REDIS_REPLY_STRING 1
+#define REDIS_REPLY_ARRAY 2
+#define REDIS_REPLY_INTEGER 3
+#define REDIS_REPLY_NIL 4
+#define REDIS_REPLY_STATUS 5
+#define REDIS_REPLY_ERROR 6
+#define REDIS_REPLY_DOUBLE 7
+#define REDIS_REPLY_BOOL 8
+#define REDIS_REPLY_MAP 9
+#define REDIS_REPLY_SET 10
+#define REDIS_REPLY_ATTR 11
+#define REDIS_REPLY_PUSH 12
+#define REDIS_REPLY_BIGNUM 13
+#define REDIS_REPLY_VERB 14
+
+/* Default max unused reader buffer. */
+#define REDIS_READER_MAX_BUF (1024*16)
+
+/* Default multi-bulk element limit */
+#define REDIS_READER_MAX_ARRAY_ELEMENTS ((1LL<<32) - 1)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct redisReadTask {
+    int type;
+    long long elements; /* number of elements in multibulk container */
+    int idx; /* index in parent (array) object */
+    void *obj; /* holds user-generated value for a read task */
+    struct redisReadTask *parent; /* parent task */
+    void *privdata; /* user-settable arbitrary field */
+} redisReadTask;
+
+typedef struct redisReplyObjectFunctions {
+    void *(*createString)(const redisReadTask*, char*, size_t);
+    void *(*createArray)(const redisReadTask*, size_t);
+    void *(*createInteger)(const redisReadTask*, long long);
+    void *(*createDouble)(const redisReadTask*, double, char*, size_t);
+    void *(*createNil)(const redisReadTask*);
+    void *(*createBool)(const redisReadTask*, int);
+    void (*freeObject)(void*);
+} redisReplyObjectFunctions;
+
+typedef struct redisReader {
+    int err; /* Error flags, 0 when there is no error */
+    char errstr[128]; /* String representation of error when applicable */
+
+    char *buf; /* Read buffer */
+    size_t pos; /* Buffer cursor */
+    size_t len; /* Buffer length */
+    size_t maxbuf; /* Max length of unused buffer */
+    long long maxelements; /* Max multi-bulk elements */
+
+    redisReadTask **task;
+    int tasks;
+
+    int ridx; /* Index of current read task */
+    void *reply; /* Temporary reply pointer */
+
+    redisReplyObjectFunctions *fn;
+    void *privdata;
+} redisReader;
+
+/* Public API for the protocol parser. */
+redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn);
+void redisReaderFree(redisReader *r);
+int redisReaderFeed(redisReader *r, const char *buf, size_t len);
+int redisReaderGetReply(redisReader *r, void **reply);
+
+#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
+#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply)
+#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 280 - 0
thirdparty/hiredis/include/hiredis/sds.h

@@ -0,0 +1,280 @@
+/* SDSLib 2.0 -- A C dynamic strings library
+ *
+ * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2015, Oran Agra
+ * Copyright (c) 2015, Redis Labs, Inc
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __SDS_H
+#define __SDS_H
+
+#define SDS_MAX_PREALLOC (1024*1024)
+#ifdef _MSC_VER
+typedef long long ssize_t;
+#define SSIZE_MAX (LLONG_MAX >> 1)
+#ifndef __clang__
+#define __attribute__(x)
+#endif
+#endif
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+typedef char *sds;
+
+/* Note: sdshdr5 is never used, we just access the flags byte directly.
+ * However is here to document the layout of type 5 SDS strings. */
+struct __attribute__ ((__packed__)) sdshdr5 {
+    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
+    char buf[];
+};
+struct __attribute__ ((__packed__)) sdshdr8 {
+    uint8_t len; /* used */
+    uint8_t alloc; /* excluding the header and null terminator */
+    unsigned char flags; /* 3 lsb of type, 5 unused bits */
+    char buf[];
+};
+struct __attribute__ ((__packed__)) sdshdr16 {
+    uint16_t len; /* used */
+    uint16_t alloc; /* excluding the header and null terminator */
+    unsigned char flags; /* 3 lsb of type, 5 unused bits */
+    char buf[];
+};
+struct __attribute__ ((__packed__)) sdshdr32 {
+    uint32_t len; /* used */
+    uint32_t alloc; /* excluding the header and null terminator */
+    unsigned char flags; /* 3 lsb of type, 5 unused bits */
+    char buf[];
+};
+struct __attribute__ ((__packed__)) sdshdr64 {
+    uint64_t len; /* used */
+    uint64_t alloc; /* excluding the header and null terminator */
+    unsigned char flags; /* 3 lsb of type, 5 unused bits */
+    char buf[];
+};
+
+#define SDS_TYPE_5  0
+#define SDS_TYPE_8  1
+#define SDS_TYPE_16 2
+#define SDS_TYPE_32 3
+#define SDS_TYPE_64 4
+#define SDS_TYPE_MASK 7
+#define SDS_TYPE_BITS 3
+#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)));
+#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
+#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
+
+static inline size_t sdslen(const sds s) {
+    unsigned char flags = s[-1];
+    switch(flags&SDS_TYPE_MASK) {
+        case SDS_TYPE_5:
+            return SDS_TYPE_5_LEN(flags);
+        case SDS_TYPE_8:
+            return SDS_HDR(8,s)->len;
+        case SDS_TYPE_16:
+            return SDS_HDR(16,s)->len;
+        case SDS_TYPE_32:
+            return SDS_HDR(32,s)->len;
+        case SDS_TYPE_64:
+            return SDS_HDR(64,s)->len;
+    }
+    return 0;
+}
+
+static inline size_t sdsavail(const sds s) {
+    unsigned char flags = s[-1];
+    switch(flags&SDS_TYPE_MASK) {
+        case SDS_TYPE_5: {
+            return 0;
+        }
+        case SDS_TYPE_8: {
+            SDS_HDR_VAR(8,s);
+            return sh->alloc - sh->len;
+        }
+        case SDS_TYPE_16: {
+            SDS_HDR_VAR(16,s);
+            return sh->alloc - sh->len;
+        }
+        case SDS_TYPE_32: {
+            SDS_HDR_VAR(32,s);
+            return sh->alloc - sh->len;
+        }
+        case SDS_TYPE_64: {
+            SDS_HDR_VAR(64,s);
+            return sh->alloc - sh->len;
+        }
+    }
+    return 0;
+}
+
+static inline void sdssetlen(sds s, size_t newlen) {
+    unsigned char flags = s[-1];
+    switch(flags&SDS_TYPE_MASK) {
+        case SDS_TYPE_5:
+            {
+                unsigned char *fp = ((unsigned char*)s)-1;
+                *fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS));
+            }
+            break;
+        case SDS_TYPE_8:
+            SDS_HDR(8,s)->len = (uint8_t)newlen;
+            break;
+        case SDS_TYPE_16:
+            SDS_HDR(16,s)->len = (uint16_t)newlen;
+            break;
+        case SDS_TYPE_32:
+            SDS_HDR(32,s)->len = (uint32_t)newlen;
+            break;
+        case SDS_TYPE_64:
+            SDS_HDR(64,s)->len = (uint64_t)newlen;
+            break;
+    }
+}
+
+static inline void sdsinclen(sds s, size_t inc) {
+    unsigned char flags = s[-1];
+    switch(flags&SDS_TYPE_MASK) {
+        case SDS_TYPE_5:
+            {
+                unsigned char *fp = ((unsigned char*)s)-1;
+                unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc;
+                *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
+            }
+            break;
+        case SDS_TYPE_8:
+            SDS_HDR(8,s)->len += (uint8_t)inc;
+            break;
+        case SDS_TYPE_16:
+            SDS_HDR(16,s)->len += (uint16_t)inc;
+            break;
+        case SDS_TYPE_32:
+            SDS_HDR(32,s)->len += (uint32_t)inc;
+            break;
+        case SDS_TYPE_64:
+            SDS_HDR(64,s)->len += (uint64_t)inc;
+            break;
+    }
+}
+
+/* sdsalloc() = sdsavail() + sdslen() */
+static inline size_t sdsalloc(const sds s) {
+    unsigned char flags = s[-1];
+    switch(flags&SDS_TYPE_MASK) {
+        case SDS_TYPE_5:
+            return SDS_TYPE_5_LEN(flags);
+        case SDS_TYPE_8:
+            return SDS_HDR(8,s)->alloc;
+        case SDS_TYPE_16:
+            return SDS_HDR(16,s)->alloc;
+        case SDS_TYPE_32:
+            return SDS_HDR(32,s)->alloc;
+        case SDS_TYPE_64:
+            return SDS_HDR(64,s)->alloc;
+    }
+    return 0;
+}
+
+static inline void sdssetalloc(sds s, size_t newlen) {
+    unsigned char flags = s[-1];
+    switch(flags&SDS_TYPE_MASK) {
+        case SDS_TYPE_5:
+            /* Nothing to do, this type has no total allocation info. */
+            break;
+        case SDS_TYPE_8:
+            SDS_HDR(8,s)->alloc = (uint8_t)newlen;
+            break;
+        case SDS_TYPE_16:
+            SDS_HDR(16,s)->alloc = (uint16_t)newlen;
+            break;
+        case SDS_TYPE_32:
+            SDS_HDR(32,s)->alloc = (uint32_t)newlen;
+            break;
+        case SDS_TYPE_64:
+            SDS_HDR(64,s)->alloc = (uint64_t)newlen;
+            break;
+    }
+}
+
+sds sdsnewlen(const void *init, size_t initlen);
+sds sdsnew(const char *init);
+sds sdsempty(void);
+sds sdsdup(const sds s);
+void sdsfree(sds s);
+sds sdsgrowzero(sds s, size_t len);
+sds sdscatlen(sds s, const void *t, size_t len);
+sds sdscat(sds s, const char *t);
+sds sdscatsds(sds s, const sds t);
+sds sdscpylen(sds s, const char *t, size_t len);
+sds sdscpy(sds s, const char *t);
+
+sds sdscatvprintf(sds s, const char *fmt, va_list ap);
+#ifdef __GNUC__
+sds sdscatprintf(sds s, const char *fmt, ...)
+    __attribute__((format(printf, 2, 3)));
+#else
+sds sdscatprintf(sds s, const char *fmt, ...);
+#endif
+
+sds sdscatfmt(sds s, char const *fmt, ...);
+sds sdstrim(sds s, const char *cset);
+int sdsrange(sds s, ssize_t start, ssize_t end);
+void sdsupdatelen(sds s);
+void sdsclear(sds s);
+int sdscmp(const sds s1, const sds s2);
+sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);
+void sdsfreesplitres(sds *tokens, int count);
+void sdstolower(sds s);
+void sdstoupper(sds s);
+sds sdsfromlonglong(long long value);
+sds sdscatrepr(sds s, const char *p, size_t len);
+sds *sdssplitargs(const char *line, int *argc);
+sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
+sds sdsjoin(char **argv, int argc, char *sep);
+sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);
+
+/* Low level functions exposed to the user API */
+sds sdsMakeRoomFor(sds s, size_t addlen);
+void sdsIncrLen(sds s, int incr);
+sds sdsRemoveFreeSpace(sds s);
+size_t sdsAllocSize(sds s);
+void *sdsAllocPtr(sds s);
+
+/* Export the allocator used by SDS to the program using SDS.
+ * Sometimes the program SDS is linked to, may use a different set of
+ * allocators, but may want to allocate or free things that SDS will
+ * respectively free or allocate. */
+void *sds_malloc(size_t size);
+void *sds_realloc(void *ptr, size_t size);
+void sds_free(void *ptr);
+
+#ifdef REDIS_TEST
+int sdsTest(int argc, char *argv[]);
+#endif
+
+#endif

+ 95 - 0
thirdparty/hiredis/include/hiredis/sockcompat.h

@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2019, Marcus Geelnard <m at bitsnbites dot eu>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __SOCKCOMPAT_H
+#define __SOCKCOMPAT_H
+
+#ifndef _WIN32
+/* For POSIX systems we use the standard BSD socket API. */
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <poll.h>
+#else
+/* For Windows we use winsock. */
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600 /* To get WSAPoll etc. */
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <stddef.h>
+#include <errno.h>
+#include <mstcpip.h>
+
+#ifdef _MSC_VER
+typedef long long ssize_t;
+#endif
+
+/* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */
+int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
+const char *win32_gai_strerror(int errcode);
+void win32_freeaddrinfo(struct addrinfo *res);
+SOCKET win32_socket(int domain, int type, int protocol);
+int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp);
+int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen);
+int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen);
+int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen);
+int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen);
+int win32_close(SOCKET fd);
+ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags);
+ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags);
+typedef ULONG nfds_t;
+int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout);
+
+int win32_redisKeepAlive(SOCKET sockfd, int interval_ms);
+
+#ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION
+#define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res)
+#undef gai_strerror
+#define gai_strerror(errcode) win32_gai_strerror(errcode)
+#define freeaddrinfo(res) win32_freeaddrinfo(res)
+#define socket(domain, type, protocol) win32_socket(domain, type, protocol)
+#define ioctl(fd, request, argp) win32_ioctl(fd, request, argp)
+#define bind(sockfd, addr, addrlen) win32_bind(sockfd, addr, addrlen)
+#define connect(sockfd, addr, addrlen) win32_connect(sockfd, addr, addrlen)
+#define getsockopt(sockfd, level, optname, optval, optlen) win32_getsockopt(sockfd, level, optname, optval, optlen)
+#define setsockopt(sockfd, level, optname, optval, optlen) win32_setsockopt(sockfd, level, optname, optval, optlen)
+#define close(fd) win32_close(fd)
+#define recv(sockfd, buf, len, flags) win32_recv(sockfd, buf, len, flags)
+#define send(sockfd, buf, len, flags) win32_send(sockfd, buf, len, flags)
+#define poll(fds, nfds, timeout) win32_poll(fds, nfds, timeout)
+#endif /* REDIS_SOCKCOMPAT_IMPLEMENTATION */
+#endif /* _WIN32 */
+
+#endif /* __SOCKCOMPAT_H */

二進制
thirdparty/hiredis/lib/libhiredis.a


二進制
thirdparty/hiredis/lib/libhiredis.dll.a


+ 12 - 0
thirdparty/hiredis/lib/pkgconfig/hiredis.pc

@@ -0,0 +1,12 @@
+prefix=D:/dev/hiredis
+install_libdir=lib
+exec_prefix=${prefix}
+libdir=${exec_prefix}/${install_libdir}
+includedir=${prefix}/include
+pkgincludedir=${includedir}/hiredis
+
+Name: hiredis
+Description: Minimalistic C client library for Redis.
+Version: 1.1.0
+Libs: -L${libdir} -lhiredis
+Cflags: -I${pkgincludedir} -I${includedir} -D_FILE_OFFSET_BITS=64

+ 37 - 0
thirdparty/hiredis/share/hiredis/hiredis-config.cmake

@@ -0,0 +1,37 @@
+
+####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() #######
+####### Any changes to this file will be overwritten by the next CMake run ####
+####### The input file was hiredis-config.cmake.in                            ########
+
+get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../" ABSOLUTE)
+
+macro(set_and_check _var _file)
+  set(${_var} "${_file}")
+  if(NOT EXISTS "${_file}")
+    message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !")
+  endif()
+endmacro()
+
+macro(check_required_components _NAME)
+  foreach(comp ${${_NAME}_FIND_COMPONENTS})
+    if(NOT ${_NAME}_${comp}_FOUND)
+      if(${_NAME}_FIND_REQUIRED_${comp})
+        set(${_NAME}_FOUND FALSE)
+      endif()
+    endif()
+  endforeach()
+endmacro()
+
+####################################################################################
+
+set_and_check(hiredis_INCLUDEDIR "${PACKAGE_PREFIX_DIR}/include")
+
+IF (NOT TARGET hiredis::hiredis)
+	INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis-targets.cmake)
+ENDIF()
+
+SET(hiredis_LIBRARIES hiredis::hiredis)
+SET(hiredis_INCLUDE_DIRS ${hiredis_INCLUDEDIR})
+
+check_required_components(hiredis)
+

+ 29 - 0
thirdparty/hiredis/share/hiredis/hiredis-targets-release.cmake

@@ -0,0 +1,29 @@
+#----------------------------------------------------------------
+# Generated CMake target import file for configuration "Release".
+#----------------------------------------------------------------
+
+# Commands may need to know the format version.
+set(CMAKE_IMPORT_FILE_VERSION 1)
+
+# Import target "hiredis::hiredis" for configuration "Release"
+set_property(TARGET hiredis::hiredis APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
+set_target_properties(hiredis::hiredis PROPERTIES
+  IMPORTED_IMPLIB_RELEASE "${_IMPORT_PREFIX}/lib/libhiredis.dll.a"
+  IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/libhiredis.dll"
+  )
+
+list(APPEND _cmake_import_check_targets hiredis::hiredis )
+list(APPEND _cmake_import_check_files_for_hiredis::hiredis "${_IMPORT_PREFIX}/lib/libhiredis.dll.a" "${_IMPORT_PREFIX}/bin/libhiredis.dll" )
+
+# Import target "hiredis::hiredis_static" for configuration "Release"
+set_property(TARGET hiredis::hiredis_static APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
+set_target_properties(hiredis::hiredis_static PROPERTIES
+  IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C"
+  IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libhiredis.a"
+  )
+
+list(APPEND _cmake_import_check_targets hiredis::hiredis_static )
+list(APPEND _cmake_import_check_files_for_hiredis::hiredis_static "${_IMPORT_PREFIX}/lib/libhiredis.a" )
+
+# Commands beyond this point should not need to know the version.
+set(CMAKE_IMPORT_FILE_VERSION)

+ 114 - 0
thirdparty/hiredis/share/hiredis/hiredis-targets.cmake

@@ -0,0 +1,114 @@
+# Generated by CMake
+
+if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.8)
+   message(FATAL_ERROR "CMake >= 2.8.0 required")
+endif()
+if(CMAKE_VERSION VERSION_LESS "2.8.3")
+   message(FATAL_ERROR "CMake >= 2.8.3 required")
+endif()
+cmake_policy(PUSH)
+cmake_policy(VERSION 2.8.3...3.22)
+#----------------------------------------------------------------
+# Generated CMake target import file.
+#----------------------------------------------------------------
+
+# Commands may need to know the format version.
+set(CMAKE_IMPORT_FILE_VERSION 1)
+
+# Protect against multiple inclusion, which would fail when already imported targets are added once more.
+set(_cmake_targets_defined "")
+set(_cmake_targets_not_defined "")
+set(_cmake_expected_targets "")
+foreach(_cmake_expected_target IN ITEMS hiredis::hiredis hiredis::hiredis_static)
+  list(APPEND _cmake_expected_targets "${_cmake_expected_target}")
+  if(TARGET "${_cmake_expected_target}")
+    list(APPEND _cmake_targets_defined "${_cmake_expected_target}")
+  else()
+    list(APPEND _cmake_targets_not_defined "${_cmake_expected_target}")
+  endif()
+endforeach()
+unset(_cmake_expected_target)
+if(_cmake_targets_defined STREQUAL _cmake_expected_targets)
+  unset(_cmake_targets_defined)
+  unset(_cmake_targets_not_defined)
+  unset(_cmake_expected_targets)
+  unset(CMAKE_IMPORT_FILE_VERSION)
+  cmake_policy(POP)
+  return()
+endif()
+if(NOT _cmake_targets_defined STREQUAL "")
+  string(REPLACE ";" ", " _cmake_targets_defined_text "${_cmake_targets_defined}")
+  string(REPLACE ";" ", " _cmake_targets_not_defined_text "${_cmake_targets_not_defined}")
+  message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_cmake_targets_defined_text}\nTargets not yet defined: ${_cmake_targets_not_defined_text}\n")
+endif()
+unset(_cmake_targets_defined)
+unset(_cmake_targets_not_defined)
+unset(_cmake_expected_targets)
+
+
+# Compute the installation prefix relative to this file.
+get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
+get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
+get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
+if(_IMPORT_PREFIX STREQUAL "/")
+  set(_IMPORT_PREFIX "")
+endif()
+
+# Create imported target hiredis::hiredis
+add_library(hiredis::hiredis SHARED IMPORTED)
+
+set_target_properties(hiredis::hiredis PROPERTIES
+  INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
+  INTERFACE_LINK_LIBRARIES "ws2_32;crypt32"
+)
+
+# Create imported target hiredis::hiredis_static
+add_library(hiredis::hiredis_static STATIC IMPORTED)
+
+set_target_properties(hiredis::hiredis_static PROPERTIES
+  INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
+  INTERFACE_LINK_LIBRARIES "ws2_32;crypt32"
+)
+
+if(CMAKE_VERSION VERSION_LESS 2.8.12)
+  message(FATAL_ERROR "This file relies on consumers using CMake 2.8.12 or greater.")
+endif()
+
+# Load information for each installed configuration.
+file(GLOB _cmake_config_files "${CMAKE_CURRENT_LIST_DIR}/hiredis-targets-*.cmake")
+foreach(_cmake_config_file IN LISTS _cmake_config_files)
+  include("${_cmake_config_file}")
+endforeach()
+unset(_cmake_config_file)
+unset(_cmake_config_files)
+
+# Cleanup temporary variables.
+set(_IMPORT_PREFIX)
+
+# Loop over all imported files and verify that they actually exist
+foreach(_cmake_target IN LISTS _cmake_import_check_targets)
+  foreach(_cmake_file IN LISTS "_cmake_import_check_files_for_${_cmake_target}")
+    if(NOT EXISTS "${_cmake_file}")
+      message(FATAL_ERROR "The imported target \"${_cmake_target}\" references the file
+   \"${_cmake_file}\"
+but this file does not exist.  Possible reasons include:
+* The file was deleted, renamed, or moved to another location.
+* An install or uninstall procedure did not complete successfully.
+* The installation package was faulty and contained
+   \"${CMAKE_CURRENT_LIST_FILE}\"
+but not all the files it references.
+")
+    endif()
+  endforeach()
+  unset(_cmake_file)
+  unset("_cmake_import_check_files_for_${_cmake_target}")
+endforeach()
+unset(_cmake_target)
+unset(_cmake_import_check_targets)
+
+# This file does not depend on other imported targets which have
+# been exported from the same project but in a separate export set.
+
+# Commands beyond this point should not need to know the version.
+set(CMAKE_IMPORT_FILE_VERSION)
+cmake_policy(POP)

+ 38 - 0
thirdparty/qmqtt/include/qmqtt.h

@@ -0,0 +1,38 @@
+/*
+ * qmqtt.h - qmqtt library heaer
+ *
+ * Copyright (c) 2013  Ery Lee <ery.lee at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of mqttc nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef QMQTT_H
+#define QMQTT_H
+
+#include <qmqtt_message.h>
+#include <qmqtt_client.h>
+
+#endif // QMQTT_H

+ 286 - 0
thirdparty/qmqtt/include/qmqtt_client.h

@@ -0,0 +1,286 @@
+/*
+ * qmqtt_client.h - qmqtt client header
+ *
+ * Copyright (c) 2013  Ery Lee <ery.lee at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of mqttc nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef QMQTT_CLIENT_H
+#define QMQTT_CLIENT_H
+
+#include <qmqtt_global.h>
+
+#include <QObject>
+#include <QString>
+#include <QHostAddress>
+#include <QByteArray>
+#include <QAbstractSocket>
+#include <QScopedPointer>
+#include <QList>
+
+#ifdef QT_WEBSOCKETS_LIB
+#include <QWebSocket>
+#endif // QT_WEBSOCKETS_LIB
+
+#ifndef QT_NO_SSL
+#include <QSslConfiguration>
+QT_FORWARD_DECLARE_CLASS(QSslError)
+#endif // QT_NO_SSL
+
+#ifndef Q_ENUM_NS
+#define Q_ENUM_NS(x)
+#endif // Q_ENUM_NS
+
+namespace QMQTT {
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+Q_MQTT_EXPORT Q_NAMESPACE
+#endif
+
+static const quint8 LIBRARY_VERSION_MAJOR = 0;
+static const quint8 LIBRARY_VERSION_MINOR = 3;
+static const quint8 LIBRARY_VERSION_REVISION = 1;
+//static const char* LIBRARY_VERSION = "0.3.1";
+
+enum MQTTVersion
+{
+    V3_1_0 = 3,
+    V3_1_1 = 4
+};
+Q_ENUM_NS(MQTTVersion)
+
+enum ConnectionState
+{
+    STATE_INIT = 0,
+    STATE_CONNECTING,
+    STATE_CONNECTED,
+    STATE_DISCONNECTED
+};
+Q_ENUM_NS(ConnectionState)
+
+enum ClientError
+{
+    UnknownError = 0,
+    SocketConnectionRefusedError,
+    SocketRemoteHostClosedError,
+    SocketHostNotFoundError,
+    SocketAccessError,
+    SocketResourceError,
+    SocketTimeoutError,
+    SocketDatagramTooLargeError,
+    SocketNetworkError,
+    SocketAddressInUseError,
+    SocketAddressNotAvailableError,
+    SocketUnsupportedSocketOperationError,
+    SocketUnfinishedSocketOperationError,
+    SocketProxyAuthenticationRequiredError,
+    SocketSslHandshakeFailedError,
+    SocketProxyConnectionRefusedError,
+    SocketProxyConnectionClosedError,
+    SocketProxyConnectionTimeoutError,
+    SocketProxyNotFoundError,
+    SocketProxyProtocolError,
+    SocketOperationError,
+    SocketSslInternalError,
+    SocketSslInvalidUserDataError,
+    SocketTemporaryError,
+    MqttUnacceptableProtocolVersionError=1<<16,
+    MqttIdentifierRejectedError,
+    MqttServerUnavailableError,
+    MqttBadUserNameOrPasswordError,
+    MqttNotAuthorizedError,
+    MqttNoPingResponse
+};
+Q_ENUM_NS(ClientError)
+
+class ClientPrivate;
+class Message;
+class Frame;
+class NetworkInterface;
+
+class Q_MQTT_EXPORT Client : public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY(quint16 _port READ port WRITE setPort)
+    Q_PROPERTY(QHostAddress _host READ host WRITE setHost)
+    Q_PROPERTY(QString _hostName READ hostName WRITE setHostName)
+    Q_PROPERTY(QString _clientId READ clientId WRITE setClientId)
+    Q_PROPERTY(QString _username READ username WRITE setUsername)
+    Q_PROPERTY(QByteArray _password READ password WRITE setPassword)
+    Q_PROPERTY(quint16 _keepAlive READ keepAlive WRITE setKeepAlive)
+    Q_PROPERTY(MQTTVersion _version READ version WRITE setVersion)
+    Q_PROPERTY(bool _autoReconnect READ autoReconnect WRITE setAutoReconnect)
+    Q_PROPERTY(int _autoReconnectInterval READ autoReconnectInterval WRITE setAutoReconnectInterval)
+    Q_PROPERTY(bool _cleanSession READ cleanSession WRITE setCleanSession)
+    Q_PROPERTY(QString _willTopic READ willTopic WRITE setWillTopic)
+    Q_PROPERTY(quint8 _willQos READ willQos WRITE setWillQos)
+    Q_PROPERTY(bool _willRetain READ willRetain WRITE setWillRetain)
+    Q_PROPERTY(QByteArray _willMessage READ willMessage WRITE setWillMessage)
+    Q_PROPERTY(ConnectionState _connectionState READ connectionState)
+#ifndef QT_NO_SSL
+    Q_PROPERTY(QSslConfiguration _sslConfiguration READ sslConfiguration WRITE setSslConfiguration)
+#endif // QT_NO_SSL
+
+public:
+    Client(const QHostAddress& host = QHostAddress::LocalHost,
+           const quint16 port = 1883,
+           QObject* parent = nullptr);
+
+#ifndef QT_NO_SSL
+    Client(const QString& hostName,
+           const quint16 port,
+           const QSslConfiguration& config,
+           const bool ignoreSelfSigned=false,
+           QObject* parent = nullptr);
+#endif // QT_NO_SSL
+
+    // This function is provided for backward compatibility with older versions of QMQTT.
+    // If the ssl parameter is true, this function will load a private key ('cert.key') and a local
+    // certificate ('cert.crt') from the current working directory. It will also set PeerVerifyMode
+    // to None. This may not be the safest way to set up an SSL connection.
+    Client(const QString& hostName,
+           const quint16 port,
+           const bool ssl,
+           const bool ignoreSelfSigned,
+           QObject* parent = nullptr);
+
+#ifdef QT_WEBSOCKETS_LIB
+    // Create a connection over websockets
+    Client(const QString& url,
+           const QString& origin,
+           QWebSocketProtocol::Version version,
+           bool ignoreSelfSigned = false,
+           QObject* parent = nullptr);
+
+#ifndef QT_NO_SSL
+    Client(const QString& url,
+           const QString& origin,
+           QWebSocketProtocol::Version version,
+           const QSslConfiguration& config,
+           const bool ignoreSelfSigned = false,
+           QObject* parent = nullptr);
+#endif // QT_NO_SSL
+#endif // QT_WEBSOCKETS_LIB
+
+    // for testing purposes only
+    Client(NetworkInterface* network,
+           const QHostAddress& host = QHostAddress::LocalHost,
+           const quint16 port = 1883,
+           QObject* parent = nullptr);
+
+    virtual ~Client();
+
+    QHostAddress host() const;
+    QString hostName() const;
+    quint16 port() const;
+    QString clientId() const;
+    QString username() const;
+    QByteArray password() const;
+    QMQTT::MQTTVersion version() const;
+    quint16 keepAlive() const;
+    bool cleanSession() const;
+    bool autoReconnect() const;
+    int autoReconnectInterval() const;
+    ConnectionState connectionState() const;
+    QString willTopic() const;
+    quint8 willQos() const;
+    bool willRetain() const;
+    QByteArray willMessage() const;
+
+    bool isConnectedToHost() const;
+#ifndef QT_NO_SSL
+    QSslConfiguration sslConfiguration() const;
+    void setSslConfiguration(const QSslConfiguration& config);
+#endif // QT_NO_SSL
+
+public Q_SLOTS:
+    void setHost(const QHostAddress& host);
+    void setHostName(const QString& hostName);
+    void setPort(const quint16 port);
+    void setClientId(const QString& clientId);
+    void setUsername(const QString& username);
+    void setPassword(const QByteArray& password);
+    void setVersion(const MQTTVersion version);
+    void setKeepAlive(const quint16 keepAlive);
+    void setCleanSession(const bool cleanSession);
+    void setAutoReconnect(const bool value);
+    void setAutoReconnectInterval(const int autoReconnectInterval);
+    void setWillTopic(const QString& willTopic);
+    void setWillQos(const quint8 willQos);
+    void setWillRetain(const bool willRetain);
+    void setWillMessage(const QByteArray& willMessage);
+
+    void connectToHost();
+    void disconnectFromHost();
+
+    void subscribe(const QString& topic, const quint8 qos = 0);
+    void unsubscribe(const QString& topic);
+
+    quint16 publish(const QMQTT::Message& message);
+
+#ifndef QT_NO_SSL
+    void ignoreSslErrors();
+    void ignoreSslErrors(const QList<QSslError>& errors);
+#endif // QT_NO_SSL
+
+Q_SIGNALS:
+    void connected();
+    void disconnected();
+    void error(const QMQTT::ClientError error);
+
+    void subscribed(const QString& topic, const quint8 qos = 0);
+    void unsubscribed(const QString& topic);
+    void published(const QMQTT::Message& message, quint16 msgid = 0);
+    void received(const QMQTT::Message& message);
+    void pingresp();
+#ifndef QT_NO_SSL
+    void sslErrors(const QList<QSslError>& errors);
+#endif // QT_NO_SSL
+
+protected Q_SLOTS:
+    void onNetworkConnected();
+    void onNetworkDisconnected();
+    void onNetworkReceived(const QMQTT::Frame& frame);
+    void onTimerPingReq();
+    void onPingTimeout();
+    void onNetworkError(QAbstractSocket::SocketError error);
+#ifndef QT_NO_SSL
+    void onSslErrors(const QList<QSslError>& errors);
+#endif // QT_NO_SSL
+
+protected:
+    QScopedPointer<ClientPrivate> d_ptr;
+
+private:
+    Q_DISABLE_COPY(Client)
+    Q_DECLARE_PRIVATE(Client)
+};
+
+} // namespace QMQTT
+
+Q_DECLARE_METATYPE(QMQTT::ClientError)
+
+#endif // QMQTT_CLIENT_H

+ 137 - 0
thirdparty/qmqtt/include/qmqtt_frame.h

@@ -0,0 +1,137 @@
+/*
+ * qmqtt_frame.h - qmqtt frame heaer
+ *
+ * Copyright (c) 2013  Ery Lee <ery.lee at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of mqttc nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef QMQTT_FRAME_H
+#define QMQTT_FRAME_H
+
+#include <qmqtt_global.h>
+
+#include <QMetaType>
+#include <QByteArray>
+#include <QString>
+
+QT_FORWARD_DECLARE_CLASS(QDataStream)
+
+#define PROTOCOL_MAGIC_3_1_0 "MQIsdp"
+#define PROTOCOL_MAGIC_3_1_1 "MQTT"
+
+#define RANDOM_CLIENT_PREFIX "QMQTT-"
+
+#define CONNECT 0x10
+#define CONNACK 0x20
+#define PUBLISH 0x30
+#define PUBACK 0x40
+#define PUBREC 0x50
+#define PUBREL 0x60
+#define PUBCOMP 0x70
+#define SUBSCRIBE 0x80
+#define SUBACK 0x90
+#define UNSUBSCRIBE 0xA0
+#define UNSUBACK 0xB0
+#define PINGREQ 0xC0
+#define PINGRESP 0xD0
+#define DISCONNECT 0xE0
+
+#define LSB(A) quint8(A & 0x00FF)
+#define MSB(A) quint8((A & 0xFF00) >> 8)
+
+/*
+|--------------------------------------
+| 7 6 5 4 |     3    |  2 1  | 0      |
+|  Type   | DUP flag |  QoS  | RETAIN |
+|--------------------------------------
+*/
+#define GETTYPE(HDR)		(HDR & 0xF0)
+#define SETQOS(HDR, Q)		(HDR | ((Q) << 1))
+#define GETQOS(HDR)			((HDR & 0x06) >> 1)
+#define SETDUP(HDR, D)		(HDR | ((D) << 3))
+#define GETDUP(HDR)			((HDR & 0x08) >> 3)
+#define SETRETAIN(HDR, R)	(HDR | (R))
+#define GETRETAIN(HDR)		(HDR & 0x01)
+
+/*
+|----------------------------------------------------------------------------------
+|     7    |    6     |      5     |  4   3  |     2    |       1      |     0    |
+| username | password | willretain | willqos | willflag | cleansession | reserved |
+|----------------------------------------------------------------------------------
+*/
+#define FLAG_CLEANSESS(F, C)	(F | ((C) << 1))
+#define FLAG_WILL(F, W)			(F | ((W) << 2))
+#define FLAG_WILLQOS(F, Q)		(F | ((Q) << 3))
+#define FLAG_WILLRETAIN(F, R) 	(F | ((R) << 5))
+#define FLAG_PASSWD(F, P)		(F | ((P) << 6))
+#define FLAG_USERNAME(F, U)		(F | ((U) << 7))
+
+namespace QMQTT {
+
+class Q_MQTT_EXPORT Frame
+{
+public:
+    explicit Frame();
+    explicit Frame(const quint8 header);
+    explicit Frame(const quint8 header, const QByteArray &data);
+    virtual ~Frame();
+
+    Frame(const Frame& other);
+    Frame& operator=(const Frame& other);
+
+    bool operator==(const Frame& other) const;
+    inline bool operator!=(const Frame& other) const
+    { return !operator==(other); }
+
+    quint8 header() const;
+    QByteArray data() const;
+
+    quint16 readInt();
+    quint8 readChar();
+    QByteArray readByteArray();
+    QString readString();
+
+    void writeInt(const quint16 i);
+    void writeChar(const quint8 c);
+    void writeByteArray(const QByteArray &data);
+    void writeString(const QString &string);
+    void writeRawData(const QByteArray &data);
+
+    //TODO: FIXME LATER
+    void write(QDataStream &stream) const;
+    bool encodeLength(QByteArray &lenbuf, int length) const;
+
+private:
+    quint8 _header;
+    QByteArray _data;
+};
+
+} // namespace QMQTT
+
+Q_DECLARE_METATYPE(QMQTT::Frame)
+
+#endif // QMQTT_FRAME_H

+ 48 - 0
thirdparty/qmqtt/include/qmqtt_global.h

@@ -0,0 +1,48 @@
+/*
+ * qmqtt_global.h - qmqtt libray global
+ *
+ * Copyright (c) 2013  Ery Lee <ery.lee at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of mqttc nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef QMQTT_GLOBAL_H
+#define QMQTT_GLOBAL_H
+
+#include <QtGlobal>
+
+#if !defined(QT_STATIC) && !defined(MQTT_PROJECT_INCLUDE_SRC)
+#  if defined(QT_BUILD_QMQTT_LIB)
+#    define Q_MQTT_EXPORT Q_DECL_EXPORT
+#  else
+#    define Q_MQTT_EXPORT Q_DECL_IMPORT
+#  endif
+#else
+#  define Q_MQTT_EXPORT
+#endif
+
+#endif // QMQTT_GLOBAL_H
+

+ 96 - 0
thirdparty/qmqtt/include/qmqtt_message.h

@@ -0,0 +1,96 @@
+/*
+ * qmqtt_message.h - qmqtt message header
+ *
+ * Copyright (c) 2013  Ery Lee <ery.lee at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of mqttc nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef QMQTT_MESSAGE_H
+#define QMQTT_MESSAGE_H
+
+#include <qmqtt_global.h>
+
+#include <QMetaType>
+#include <QString>
+#include <QByteArray>
+#include <QSharedDataPointer>
+
+namespace QMQTT {
+
+class MessagePrivate;
+
+class Q_MQTT_EXPORT Message
+{
+public:
+    Message();
+    explicit Message(const quint16 id, const QString &topic, const QByteArray &payload,
+                     const quint8 qos = 0, const bool retain = false, const bool dup = false);
+    Message(const Message &other);
+    ~Message();
+
+    Message &operator=(const Message &other);
+#ifdef Q_COMPILER_RVALUE_REFS
+    inline Message &operator=(Message &&other) Q_DECL_NOTHROW
+    { swap(other); return *this; }
+#endif
+
+    bool operator==(const Message &other) const;
+    inline bool operator!=(const Message &other) const
+    { return !operator==(other); }
+
+    inline void swap(Message &other) Q_DECL_NOTHROW
+    { qSwap(d, other.d); }
+
+    quint16 id() const;
+    void setId(const quint16 id);
+
+    quint8 qos() const;
+    void setQos(const quint8 qos);
+
+    bool retain() const;
+    void setRetain(const bool retain);
+
+    bool dup() const;
+    void setDup(const bool dup);
+
+    QString topic() const;
+    void setTopic(const QString &topic);
+
+    QByteArray payload() const;
+    void setPayload(const QByteArray &payload);
+
+private:
+    QSharedDataPointer<MessagePrivate> d;
+};
+
+} // namespace QMQTT
+
+Q_DECLARE_SHARED(QMQTT::Message)
+
+Q_DECLARE_METATYPE(QMQTT::Message)
+
+#endif // QMQTT_MESSAGE_H

+ 92 - 0
thirdparty/qmqtt/include/qmqtt_networkinterface.h

@@ -0,0 +1,92 @@
+/*
+ * qmqtt_networkinterface.h - qmqtt network interface header
+ *
+ * Copyright (c) 2013  Ery Lee <ery.lee at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of mqttc nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef QMQTT_NETWORK_INTERFACE_H
+#define QMQTT_NETWORK_INTERFACE_H
+
+#include <qmqtt_global.h>
+
+#include <QObject>
+#include <QAbstractSocket>
+#include <QHostAddress>
+#include <QString>
+#include <QList>
+
+#ifndef QT_NO_SSL
+#include <QSslConfiguration>
+QT_FORWARD_DECLARE_CLASS(QSslError)
+#endif // QT_NO_SSL
+
+namespace QMQTT {
+
+class Frame;
+
+class Q_MQTT_EXPORT NetworkInterface : public QObject
+{
+    Q_OBJECT
+public:
+    explicit NetworkInterface(QObject* parent = nullptr) : QObject(parent) {}
+    virtual ~NetworkInterface() {}
+
+    virtual void sendFrame(const Frame& frame) = 0;
+    virtual bool isConnectedToHost() const = 0;
+    virtual bool autoReconnect() const = 0;
+    virtual void setAutoReconnect(const bool autoReconnect) = 0;
+    virtual int autoReconnectInterval() const = 0;
+    virtual void setAutoReconnectInterval(const int autoReconnectInterval) = 0;
+    virtual QAbstractSocket::SocketState state() const = 0;
+#ifndef QT_NO_SSL
+    virtual void ignoreSslErrors(const QList<QSslError>& errors) = 0;
+    virtual QSslConfiguration sslConfiguration() const = 0;
+    virtual void setSslConfiguration(const QSslConfiguration& config) = 0;
+#endif // QT_NO_SSL
+
+public Q_SLOTS:
+    virtual void connectToHost(const QHostAddress& host, const quint16 port) = 0;
+    virtual void connectToHost(const QString& hostName, const quint16 port) = 0;
+    virtual void disconnectFromHost() = 0;
+#ifndef QT_NO_SSL
+    virtual void ignoreSslErrors() = 0;
+#endif // QT_NO_SSL
+
+Q_SIGNALS:
+    void connected();
+    void disconnected();
+    void received(const QMQTT::Frame& frame);
+    void error(QAbstractSocket::SocketError error);
+#ifndef QT_NO_SSL
+    void sslErrors(const QList<QSslError>& errors);
+#endif // QT_NO_SSL
+};
+
+} // namespace QMQTT
+
+#endif // QMQTT_NETWORK_INTERFACE_H

+ 71 - 0
thirdparty/qmqtt/include/qmqtt_routedmessage.h

@@ -0,0 +1,71 @@
+/*
+ * qmqtt_router.h - qmqtt router
+ *
+ * Copyright (c) 2013  Ery Lee <ery.lee at gmail dot com>
+ * Router added by Niklas Wulf <nwulf at geenen-it-systeme dot de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of mqttc nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef QMQTT_ROUTEDMESSAGE_H
+#define QMQTT_ROUTEDMESSAGE_H
+
+#include <qmqtt_message.h>
+
+#include <QMetaType>
+#include <QHash>
+#include <QString>
+
+namespace QMQTT {
+
+class RouteSubscription;
+
+class Q_MQTT_EXPORT RoutedMessage
+{
+public:
+    inline RoutedMessage()
+    {}
+    inline RoutedMessage(const Message &message)
+        : _message(message)
+    {}
+
+    inline const Message &message() const
+    { return _message; }
+    inline QHash<QString, QString> parameters() const
+    { return _parameters; }
+
+private:
+    friend class RouteSubscription;
+
+    Message _message;
+    QHash<QString, QString> _parameters;
+};
+
+} // namespace QMQTT
+
+Q_DECLARE_METATYPE(QMQTT::RoutedMessage)
+
+#endif // QMQTT_ROUTEDMESSAGE_H

+ 60 - 0
thirdparty/qmqtt/include/qmqtt_router.h

@@ -0,0 +1,60 @@
+/*
+ * qmqtt_router.h - qmqtt router
+ *
+ * Copyright (c) 2013  Ery Lee <ery.lee at gmail dot com>
+ * Router added by Niklas Wulf <nwulf at geenen-it-systeme dot de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of mqttc nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef QMQTT_ROUTER_H
+#define QMQTT_ROUTER_H
+
+#include <qmqtt_global.h>
+
+#include <QObject>
+
+namespace QMQTT {
+
+class Client;
+class RouteSubscription;
+
+class Q_MQTT_EXPORT Router : public QObject
+{
+    Q_OBJECT
+public:
+    explicit Router(Client *parent = nullptr);
+
+    RouteSubscription *subscribe(const QString &route);
+    Client *client() const;
+
+private:
+    Client *_client;
+};
+
+} // namespace QMQTT
+
+#endif // QMQTT_ROUTER_H

+ 79 - 0
thirdparty/qmqtt/include/qmqtt_routesubscription.h

@@ -0,0 +1,79 @@
+/*
+ * qmqtt_router.h - qmqtt router
+ *
+ * Copyright (c) 2013  Ery Lee <ery.lee at gmail dot com>
+ * Router added by Niklas Wulf <nwulf at geenen-it-systeme dot de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of mqttc nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef QMQTT_ROUTESUBSCRIPTION_H
+#define QMQTT_ROUTESUBSCRIPTION_H
+
+#include <qmqtt_global.h>
+
+#include <QObject>
+#include <QPointer>
+#include <QString>
+#include <QRegularExpression>
+#include <QStringList>
+
+namespace QMQTT {
+
+class Client;
+class Message;
+class RoutedMessage;
+class Router;
+
+class Q_MQTT_EXPORT RouteSubscription : public QObject
+{
+    Q_OBJECT
+public:
+    ~RouteSubscription();
+
+    QString route() const;
+
+Q_SIGNALS:
+    void received(const RoutedMessage &message);
+
+private Q_SLOTS:
+    void routeMessage(const Message &message);
+
+private:
+    friend class Router;
+
+    explicit RouteSubscription(Router *parent = nullptr);
+    void setRoute(const QString &route);
+
+    QPointer<Client> _client;
+    QString _topic;
+    QRegularExpression _regularExpression;
+    QStringList _parameterNames;
+};
+
+} // namespace QMQTT
+
+#endif // QMQTT_ROUTESUBSCRIPTION_H

+ 84 - 0
thirdparty/qmqtt/include/qmqtt_socketinterface.h

@@ -0,0 +1,84 @@
+/*
+ * qmqtt_socketinterface.h - qmqtt socket interface header
+ *
+ * Copyright (c) 2013  Ery Lee <ery.lee at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of mqttc nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef QMQTT_SOCKET_INTERFACE_H
+#define QMQTT_SOCKET_INTERFACE_H
+
+#include <qmqtt_global.h>
+
+#include <QObject>
+#include <QHostAddress>
+#include <QString>
+#include <QAbstractSocket>
+#include <QList>
+
+#ifndef QT_NO_SSL
+#include <QSslConfiguration>
+QT_FORWARD_DECLARE_CLASS(QSslError)
+#endif // QT_NO_SSL
+
+QT_FORWARD_DECLARE_CLASS(QIODevice)
+
+namespace QMQTT
+{
+
+class Q_MQTT_EXPORT SocketInterface : public QObject
+{
+    Q_OBJECT
+public:
+    explicit SocketInterface(QObject* parent = nullptr) : QObject(parent) {}
+    virtual ~SocketInterface() {}
+
+    virtual QIODevice* ioDevice() = 0;
+    virtual void connectToHost(const QHostAddress& address, quint16 port) = 0;
+    virtual void connectToHost(const QString& hostName, quint16 port) = 0;
+    virtual void disconnectFromHost() = 0;
+    virtual QAbstractSocket::SocketState state() const = 0;
+    virtual QAbstractSocket::SocketError error() const = 0;
+#ifndef QT_NO_SSL
+    virtual void ignoreSslErrors(const QList<QSslError>& errors) { Q_UNUSED(errors); }
+    virtual void ignoreSslErrors() {}
+    virtual QSslConfiguration sslConfiguration() const { return QSslConfiguration(); }
+    virtual void setSslConfiguration(const QSslConfiguration& config) { Q_UNUSED(config); }
+#endif // QT_NO_SSL
+
+Q_SIGNALS:
+    void connected();
+    void disconnected();
+    void error(QAbstractSocket::SocketError socketError);
+#ifndef QT_NO_SSL
+    void sslErrors(const QList<QSslError>& errors);
+#endif // QT_NO_SSL
+};
+
+}
+
+#endif // QMQTT_SOCKET_INTERFACE_H

+ 62 - 0
thirdparty/qmqtt/include/qmqtt_timerinterface.h

@@ -0,0 +1,62 @@
+/*
+ * qmqtt_timerinterface.h - qmqtt timer interface header
+ *
+ * Copyright (c) 2013  Ery Lee <ery.lee at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of mqttc nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef QMQTT_TIMER_INTERFACE_H
+#define QMQTT_TIMER_INTERFACE_H
+
+#include <qmqtt_global.h>
+
+#include <QObject>
+
+namespace QMQTT {
+
+class Q_MQTT_EXPORT TimerInterface : public QObject
+{
+    Q_OBJECT
+public:
+    explicit TimerInterface(QObject* parent = nullptr) : QObject(parent) {}
+    virtual ~TimerInterface() {}
+
+    virtual bool isSingleShot() const = 0;
+    virtual void setSingleShot(bool singleShot) = 0;
+    virtual int interval() const = 0;
+    virtual void setInterval(int msec) = 0;
+    virtual void start() = 0;
+    virtual void stop() = 0;
+
+Q_SIGNALS:
+    void timeout();
+};
+
+}
+
+#endif // QMQTT_TIMER_INTERFACE_H
+

二進制
thirdparty/qmqtt/lib/Qt5Qmqtt.dll


二進制
thirdparty/qmqtt/lib/Qt5Qmqtt.dll.debug


+ 5 - 0
thirdparty/qmqtt/lib/Qt5Qmqtt.prl

@@ -0,0 +1,5 @@
+QMAKE_PRL_BUILD_DIR = F:/projects/caiji/lp-data-store/thirdparty/build-qmqtt-Desktop_Qt_5_15_2_MinGW_64_bit-Release/src/mqtt
+QMAKE_PRO_INPUT = qmqtt.pro
+QMAKE_PRL_TARGET = libQt5Qmqtt.a
+QMAKE_PRL_CONFIG = lex yacc debug depend_includepath testcase_targets import_plugins import_qpa_plugin windows prepare_docs qt_docs_targets qt_build_extra file_copies qmake_use qt warn_on link_prl debug_and_release precompile_header shared shared no_plugin_manifest win32 mingw gcc copy_dir_files sse2 aesni sse3 ssse3 sse4_1 sse4_2 compile_examples force_debug_info largefile precompile_header rdrnd shani x86SimdAlways prefix_build force_independent utf8_source create_prl link_prl no_private_qt_headers_warning QTDIR_build qt_example_installs exceptions_off testcase_exceptions warning_clean debug DebugBuild Debug build_pass qtquickcompiler debug DebugBuild Debug build_pass relative_qt_rpath target_qt c++11 strict_c++ c++14 c++1z c99 c11 separate_debug_info split_incpath qt_install_headers need_fwd_pri qt_install_module create_cmake skip_target_version_ext compiler_supports_fpmath create_pc debug DebugBuild Debug build_pass have_target dll no_plist exclusive_builds debug_info no_autoqmake thread moc resources
+QMAKE_PRL_VERSION = 1.0.3

+ 7 - 0
thirdparty/qmqtt/lib/cmake/Qt5Qmqtt/ExtraSourceIncludes.cmake

@@ -0,0 +1,7 @@
+
+list(APPEND _Qt5Qmqtt_OWN_INCLUDE_DIRS
+    "F:/projects/caiji/lp-data-store/thirdparty/qmqtt-1.0.3/include" "F:/projects/caiji/lp-data-store/thirdparty/qmqtt-1.0.3/include/QtQmqtt"
+)
+set(Qt5Qmqtt_PRIVATE_INCLUDE_DIRS
+    "F:/projects/caiji/lp-data-store/thirdparty/qmqtt-1.0.3/include/QtQmqtt/1.0.3" "F:/projects/caiji/lp-data-store/thirdparty/qmqtt-1.0.3/include/QtQmqtt/1.0.3/QtQmqtt"
+)

+ 253 - 0
thirdparty/qmqtt/lib/cmake/Qt5Qmqtt/Qt5QmqttConfig.cmake

@@ -0,0 +1,253 @@
+if (CMAKE_VERSION VERSION_LESS 3.1.0)
+    message(FATAL_ERROR "Qt 5 Qmqtt module requires at least CMake version 3.1.0")
+endif()
+
+get_filename_component(_qt5Qmqtt_install_prefix "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE)
+
+# For backwards compatibility only. Use Qt5Qmqtt_VERSION instead.
+set(Qt5Qmqtt_VERSION_STRING 1.0.3)
+
+set(Qt5Qmqtt_LIBRARIES Qt5::Qmqtt)
+
+macro(_qt5_Qmqtt_check_file_exists file)
+    if(NOT EXISTS "${file}" )
+        message(FATAL_ERROR "The imported target \"Qt5::Qmqtt\" references the file
+   \"${file}\"
+but this file does not exist.  Possible reasons include:
+* The file was deleted, renamed, or moved to another location.
+* An install or uninstall procedure did not complete successfully.
+* The installation package was faulty and contained
+   \"${CMAKE_CURRENT_LIST_FILE}\"
+but not all the files it references.
+")
+    endif()
+endmacro()
+
+
+macro(_populate_Qmqtt_target_properties Configuration LIB_LOCATION IMPLIB_LOCATION
+      IsDebugAndRelease)
+    set_property(TARGET Qt5::Qmqtt APPEND PROPERTY IMPORTED_CONFIGURATIONS ${Configuration})
+
+    set(imported_location "${_qt5Qmqtt_install_prefix}/bin/${LIB_LOCATION}")
+    _qt5_Qmqtt_check_file_exists(${imported_location})
+    set(_deps
+        ${_Qt5Qmqtt_LIB_DEPENDENCIES}
+    )
+    set(_static_deps
+    )
+
+    set_target_properties(Qt5::Qmqtt PROPERTIES
+        "IMPORTED_LOCATION_${Configuration}" ${imported_location}
+        # For backward compatibility with CMake < 2.8.12
+        "IMPORTED_LINK_INTERFACE_LIBRARIES_${Configuration}" "${_deps};${_static_deps}"
+    )
+    set_property(TARGET Qt5::Qmqtt APPEND PROPERTY INTERFACE_LINK_LIBRARIES
+                 "${_deps}"
+    )
+
+
+    set(imported_implib "${_qt5Qmqtt_install_prefix}/lib/${IMPLIB_LOCATION}")
+    _qt5_Qmqtt_check_file_exists(${imported_implib})
+    if(NOT "${IMPLIB_LOCATION}" STREQUAL "")
+        set_target_properties(Qt5::Qmqtt PROPERTIES
+        "IMPORTED_IMPLIB_${Configuration}" ${imported_implib}
+        )
+    endif()
+endmacro()
+
+if (NOT TARGET Qt5::Qmqtt)
+
+    set(_Qt5Qmqtt_OWN_INCLUDE_DIRS "${_qt5Qmqtt_install_prefix}/include/" "${_qt5Qmqtt_install_prefix}/include/QtQmqtt")
+    set(Qt5Qmqtt_PRIVATE_INCLUDE_DIRS "")
+    include("${CMAKE_CURRENT_LIST_DIR}/ExtraSourceIncludes.cmake" OPTIONAL)
+
+    foreach(_dir ${_Qt5Qmqtt_OWN_INCLUDE_DIRS})
+        _qt5_Qmqtt_check_file_exists(${_dir})
+    endforeach()
+
+    # Only check existence of private includes if the Private component is
+    # specified.
+    list(FIND Qt5Qmqtt_FIND_COMPONENTS Private _check_private)
+    if (NOT _check_private STREQUAL -1)
+        foreach(_dir ${Qt5Qmqtt_PRIVATE_INCLUDE_DIRS})
+            _qt5_Qmqtt_check_file_exists(${_dir})
+        endforeach()
+    endif()
+
+    set(Qt5Qmqtt_INCLUDE_DIRS ${_Qt5Qmqtt_OWN_INCLUDE_DIRS})
+
+    set(Qt5Qmqtt_DEFINITIONS -DQT_QMQTT_LIB)
+    set(Qt5Qmqtt_COMPILE_DEFINITIONS QT_QMQTT_LIB)
+    set(_Qt5Qmqtt_MODULE_DEPENDENCIES "Network;Core")
+
+
+    set(Qt5Qmqtt_OWN_PRIVATE_INCLUDE_DIRS ${Qt5Qmqtt_PRIVATE_INCLUDE_DIRS})
+
+    set(_Qt5Qmqtt_FIND_DEPENDENCIES_REQUIRED)
+    if (Qt5Qmqtt_FIND_REQUIRED)
+        set(_Qt5Qmqtt_FIND_DEPENDENCIES_REQUIRED REQUIRED)
+    endif()
+    set(_Qt5Qmqtt_FIND_DEPENDENCIES_QUIET)
+    if (Qt5Qmqtt_FIND_QUIETLY)
+        set(_Qt5Qmqtt_DEPENDENCIES_FIND_QUIET QUIET)
+    endif()
+    set(_Qt5Qmqtt_FIND_VERSION_EXACT)
+    if (Qt5Qmqtt_FIND_VERSION_EXACT)
+        set(_Qt5Qmqtt_FIND_VERSION_EXACT EXACT)
+    endif()
+
+    set(Qt5Qmqtt_EXECUTABLE_COMPILE_FLAGS "")
+
+    foreach(_module_dep ${_Qt5Qmqtt_MODULE_DEPENDENCIES})
+        if (NOT Qt5${_module_dep}_FOUND)
+            find_package(Qt5${_module_dep}
+                1.0.3 ${_Qt5Qmqtt_FIND_VERSION_EXACT}
+                ${_Qt5Qmqtt_DEPENDENCIES_FIND_QUIET}
+                ${_Qt5Qmqtt_FIND_DEPENDENCIES_REQUIRED}
+                PATHS "${CMAKE_CURRENT_LIST_DIR}/.." NO_DEFAULT_PATH
+            )
+        endif()
+
+        if (NOT Qt5${_module_dep}_FOUND)
+            set(Qt5Qmqtt_FOUND False)
+            return()
+        endif()
+
+        list(APPEND Qt5Qmqtt_INCLUDE_DIRS "${Qt5${_module_dep}_INCLUDE_DIRS}")
+        list(APPEND Qt5Qmqtt_PRIVATE_INCLUDE_DIRS "${Qt5${_module_dep}_PRIVATE_INCLUDE_DIRS}")
+        list(APPEND Qt5Qmqtt_DEFINITIONS ${Qt5${_module_dep}_DEFINITIONS})
+        list(APPEND Qt5Qmqtt_COMPILE_DEFINITIONS ${Qt5${_module_dep}_COMPILE_DEFINITIONS})
+        list(APPEND Qt5Qmqtt_EXECUTABLE_COMPILE_FLAGS ${Qt5${_module_dep}_EXECUTABLE_COMPILE_FLAGS})
+    endforeach()
+    list(REMOVE_DUPLICATES Qt5Qmqtt_INCLUDE_DIRS)
+    list(REMOVE_DUPLICATES Qt5Qmqtt_PRIVATE_INCLUDE_DIRS)
+    list(REMOVE_DUPLICATES Qt5Qmqtt_DEFINITIONS)
+    list(REMOVE_DUPLICATES Qt5Qmqtt_COMPILE_DEFINITIONS)
+    list(REMOVE_DUPLICATES Qt5Qmqtt_EXECUTABLE_COMPILE_FLAGS)
+
+    # It can happen that the same FooConfig.cmake file is included when calling find_package()
+    # on some Qt component. An example of that is when using a Qt static build with auto inclusion
+    # of plugins:
+    #
+    # Qt5WidgetsConfig.cmake -> Qt5GuiConfig.cmake -> Qt5Gui_QSvgIconPlugin.cmake ->
+    # Qt5SvgConfig.cmake -> Qt5WidgetsConfig.cmake ->
+    # finish processing of second Qt5WidgetsConfig.cmake ->
+    # return to first Qt5WidgetsConfig.cmake ->
+    # add_library cannot create imported target Qt5::Widgets.
+    #
+    # Make sure to return early in the original Config inclusion, because the target has already
+    # been defined as part of the second inclusion.
+    if(TARGET Qt5::Qmqtt)
+        return()
+    endif()
+
+    set(_Qt5Qmqtt_LIB_DEPENDENCIES "Qt5::Network;Qt5::Core")
+
+
+    add_library(Qt5::Qmqtt SHARED IMPORTED)
+
+
+    set_property(TARGET Qt5::Qmqtt PROPERTY
+      INTERFACE_INCLUDE_DIRECTORIES ${_Qt5Qmqtt_OWN_INCLUDE_DIRS})
+    set_property(TARGET Qt5::Qmqtt PROPERTY
+      INTERFACE_COMPILE_DEFINITIONS QT_QMQTT_LIB)
+
+    set_property(TARGET Qt5::Qmqtt PROPERTY INTERFACE_QT_ENABLED_FEATURES )
+    set_property(TARGET Qt5::Qmqtt PROPERTY INTERFACE_QT_DISABLED_FEATURES )
+
+    # Qt 6 forward compatible properties.
+    set_property(TARGET Qt5::Qmqtt
+                 PROPERTY QT_ENABLED_PUBLIC_FEATURES
+                 )
+    set_property(TARGET Qt5::Qmqtt
+                 PROPERTY QT_DISABLED_PUBLIC_FEATURES
+                 )
+    set_property(TARGET Qt5::Qmqtt
+                 PROPERTY QT_ENABLED_PRIVATE_FEATURES
+                 )
+    set_property(TARGET Qt5::Qmqtt
+                 PROPERTY QT_DISABLED_PRIVATE_FEATURES
+                 )
+
+    set_property(TARGET Qt5::Qmqtt PROPERTY INTERFACE_QT_PLUGIN_TYPES "")
+
+    set(_Qt5Qmqtt_PRIVATE_DIRS_EXIST TRUE)
+    foreach (_Qt5Qmqtt_PRIVATE_DIR ${Qt5Qmqtt_OWN_PRIVATE_INCLUDE_DIRS})
+        if (NOT EXISTS ${_Qt5Qmqtt_PRIVATE_DIR})
+            set(_Qt5Qmqtt_PRIVATE_DIRS_EXIST FALSE)
+        endif()
+    endforeach()
+
+    if (_Qt5Qmqtt_PRIVATE_DIRS_EXIST)
+        add_library(Qt5::QmqttPrivate INTERFACE IMPORTED)
+        set_property(TARGET Qt5::QmqttPrivate PROPERTY
+            INTERFACE_INCLUDE_DIRECTORIES ${Qt5Qmqtt_OWN_PRIVATE_INCLUDE_DIRS}
+        )
+        set(_Qt5Qmqtt_PRIVATEDEPS)
+        foreach(dep ${_Qt5Qmqtt_LIB_DEPENDENCIES})
+            if (TARGET ${dep}Private)
+                list(APPEND _Qt5Qmqtt_PRIVATEDEPS ${dep}Private)
+            endif()
+        endforeach()
+        set_property(TARGET Qt5::QmqttPrivate PROPERTY
+            INTERFACE_LINK_LIBRARIES Qt5::Qmqtt ${_Qt5Qmqtt_PRIVATEDEPS}
+        )
+
+        # Add a versionless target, for compatibility with Qt6.
+        if(NOT "${QT_NO_CREATE_VERSIONLESS_TARGETS}" AND NOT TARGET Qt::QmqttPrivate)
+            add_library(Qt::QmqttPrivate INTERFACE IMPORTED)
+            set_target_properties(Qt::QmqttPrivate PROPERTIES
+                INTERFACE_LINK_LIBRARIES "Qt5::QmqttPrivate"
+            )
+        endif()
+    endif()
+
+    _populate_Qmqtt_target_properties(RELEASE "Qt5Qmqtt.dll" "libQt5Qmqtt.a" FALSE)
+
+    if (EXISTS
+        "${_qt5Qmqtt_install_prefix}/bin/Qt5Qmqtt.dll"
+      AND EXISTS
+        "${_qt5Qmqtt_install_prefix}/lib/libQt5Qmqtt.a" )
+        _populate_Qmqtt_target_properties(DEBUG "Qt5Qmqtt.dll" "libQt5Qmqtt.a" FALSE)
+    endif()
+
+
+
+    # In Qt 5.15 the glob pattern was relaxed to also catch plugins not literally named Plugin.
+    # Define QT5_STRICT_PLUGIN_GLOB or ModuleName_STRICT_PLUGIN_GLOB to revert to old behavior.
+    if (QT5_STRICT_PLUGIN_GLOB OR Qt5Qmqtt_STRICT_PLUGIN_GLOB)
+        file(GLOB pluginTargets "${CMAKE_CURRENT_LIST_DIR}/Qt5Qmqtt_*Plugin.cmake")
+    else()
+        file(GLOB pluginTargets "${CMAKE_CURRENT_LIST_DIR}/Qt5Qmqtt_*.cmake")
+    endif()
+
+    macro(_populate_Qmqtt_plugin_properties Plugin Configuration PLUGIN_LOCATION
+          IsDebugAndRelease)
+        set_property(TARGET Qt5::${Plugin} APPEND PROPERTY IMPORTED_CONFIGURATIONS ${Configuration})
+
+        set(imported_location "${_qt5Qmqtt_install_prefix}/plugins/${PLUGIN_LOCATION}")
+        _qt5_Qmqtt_check_file_exists(${imported_location})
+        set_target_properties(Qt5::${Plugin} PROPERTIES
+            "IMPORTED_LOCATION_${Configuration}" ${imported_location}
+        )
+
+    endmacro()
+
+    if (pluginTargets)
+        foreach(pluginTarget ${pluginTargets})
+            include(${pluginTarget})
+        endforeach()
+    endif()
+
+
+
+    _qt5_Qmqtt_check_file_exists("${CMAKE_CURRENT_LIST_DIR}/Qt5QmqttConfigVersion.cmake")
+endif()
+
+# Add a versionless target, for compatibility with Qt6.
+if(NOT "${QT_NO_CREATE_VERSIONLESS_TARGETS}" AND TARGET Qt5::Qmqtt AND NOT TARGET Qt::Qmqtt)
+    add_library(Qt::Qmqtt INTERFACE IMPORTED)
+    set_target_properties(Qt::Qmqtt PROPERTIES
+        INTERFACE_LINK_LIBRARIES "Qt5::Qmqtt"
+    )
+endif()

+ 11 - 0
thirdparty/qmqtt/lib/cmake/Qt5Qmqtt/Qt5QmqttConfigVersion.cmake

@@ -0,0 +1,11 @@
+
+set(PACKAGE_VERSION 1.0.3)
+
+if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
+    set(PACKAGE_VERSION_COMPATIBLE FALSE)
+else()
+    set(PACKAGE_VERSION_COMPATIBLE TRUE)
+    if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
+        set(PACKAGE_VERSION_EXACT TRUE)
+    endif()
+endif()

+ 19 - 0
thirdparty/qmqtt/lib/cmake/qmqtt/qmqtt-debug.cmake

@@ -0,0 +1,19 @@
+#----------------------------------------------------------------
+# Generated CMake target import file for configuration "Debug".
+#----------------------------------------------------------------
+
+# Commands may need to know the format version.
+set(CMAKE_IMPORT_FILE_VERSION 1)
+
+# Import target "qmqtt" for configuration "Debug"
+set_property(TARGET qmqtt APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
+set_target_properties(qmqtt PROPERTIES
+  IMPORTED_IMPLIB_DEBUG "${_IMPORT_PREFIX}/lib/qmqtt.lib"
+  IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/bin/qmqtt.dll"
+  )
+
+list(APPEND _cmake_import_check_targets qmqtt )
+list(APPEND _cmake_import_check_files_for_qmqtt "${_IMPORT_PREFIX}/lib/qmqtt.lib" "${_IMPORT_PREFIX}/bin/qmqtt.dll" )
+
+# Commands beyond this point should not need to know the version.
+set(CMAKE_IMPORT_FILE_VERSION)

+ 107 - 0
thirdparty/qmqtt/lib/cmake/qmqtt/qmqtt.cmake

@@ -0,0 +1,107 @@
+# Generated by CMake
+
+if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.8)
+   message(FATAL_ERROR "CMake >= 2.8.0 required")
+endif()
+if(CMAKE_VERSION VERSION_LESS "2.8.3")
+   message(FATAL_ERROR "CMake >= 2.8.3 required")
+endif()
+cmake_policy(PUSH)
+cmake_policy(VERSION 2.8.3...3.22)
+#----------------------------------------------------------------
+# Generated CMake target import file.
+#----------------------------------------------------------------
+
+# Commands may need to know the format version.
+set(CMAKE_IMPORT_FILE_VERSION 1)
+
+# Protect against multiple inclusion, which would fail when already imported targets are added once more.
+set(_cmake_targets_defined "")
+set(_cmake_targets_not_defined "")
+set(_cmake_expected_targets "")
+foreach(_cmake_expected_target IN ITEMS qmqtt)
+  list(APPEND _cmake_expected_targets "${_cmake_expected_target}")
+  if(TARGET "${_cmake_expected_target}")
+    list(APPEND _cmake_targets_defined "${_cmake_expected_target}")
+  else()
+    list(APPEND _cmake_targets_not_defined "${_cmake_expected_target}")
+  endif()
+endforeach()
+unset(_cmake_expected_target)
+if(_cmake_targets_defined STREQUAL _cmake_expected_targets)
+  unset(_cmake_targets_defined)
+  unset(_cmake_targets_not_defined)
+  unset(_cmake_expected_targets)
+  unset(CMAKE_IMPORT_FILE_VERSION)
+  cmake_policy(POP)
+  return()
+endif()
+if(NOT _cmake_targets_defined STREQUAL "")
+  string(REPLACE ";" ", " _cmake_targets_defined_text "${_cmake_targets_defined}")
+  string(REPLACE ";" ", " _cmake_targets_not_defined_text "${_cmake_targets_not_defined}")
+  message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_cmake_targets_defined_text}\nTargets not yet defined: ${_cmake_targets_not_defined_text}\n")
+endif()
+unset(_cmake_targets_defined)
+unset(_cmake_targets_not_defined)
+unset(_cmake_expected_targets)
+
+
+# Compute the installation prefix relative to this file.
+get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
+get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
+get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
+get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
+if(_IMPORT_PREFIX STREQUAL "/")
+  set(_IMPORT_PREFIX "")
+endif()
+
+# Create imported target qmqtt
+add_library(qmqtt SHARED IMPORTED)
+
+set_target_properties(qmqtt PROPERTIES
+  INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
+  INTERFACE_LINK_LIBRARIES "Qt5::Core;Qt5::Network"
+)
+
+if(CMAKE_VERSION VERSION_LESS 2.8.12)
+  message(FATAL_ERROR "This file relies on consumers using CMake 2.8.12 or greater.")
+endif()
+
+# Load information for each installed configuration.
+file(GLOB _cmake_config_files "${CMAKE_CURRENT_LIST_DIR}/qmqtt-*.cmake")
+foreach(_cmake_config_file IN LISTS _cmake_config_files)
+  include("${_cmake_config_file}")
+endforeach()
+unset(_cmake_config_file)
+unset(_cmake_config_files)
+
+# Cleanup temporary variables.
+set(_IMPORT_PREFIX)
+
+# Loop over all imported files and verify that they actually exist
+foreach(_cmake_target IN LISTS _cmake_import_check_targets)
+  foreach(_cmake_file IN LISTS "_cmake_import_check_files_for_${_cmake_target}")
+    if(NOT EXISTS "${_cmake_file}")
+      message(FATAL_ERROR "The imported target \"${_cmake_target}\" references the file
+   \"${_cmake_file}\"
+but this file does not exist.  Possible reasons include:
+* The file was deleted, renamed, or moved to another location.
+* An install or uninstall procedure did not complete successfully.
+* The installation package was faulty and contained
+   \"${CMAKE_CURRENT_LIST_FILE}\"
+but not all the files it references.
+")
+    endif()
+  endforeach()
+  unset(_cmake_file)
+  unset("_cmake_import_check_files_for_${_cmake_target}")
+endforeach()
+unset(_cmake_target)
+unset(_cmake_import_check_targets)
+
+# This file does not depend on other imported targets which have
+# been exported from the same project but in a separate export set.
+
+# Commands beyond this point should not need to know the version.
+set(CMAKE_IMPORT_FILE_VERSION)
+cmake_policy(POP)

部分文件因文件數量過多而無法顯示