Bladeren bron

first commit

shenlongtailang 1 jaar geleden
commit
f55934b789
58 gewijzigde bestanden met toevoegingen van 5313 en 0 verwijderingen
  1. 12 0
      .gitignore
  2. 655 0
      RedisAgent/Redis.cpp
  3. 52 0
      RedisAgent/Redis.h
  4. 81 0
      RedisAgent/RedisAgent.cpp
  5. 44 0
      RedisAgent/RedisAgent.h
  6. 28 0
      RedisAgent/RedisAgent.pro
  7. 130 0
      RedisAgent/include/adapters/ae.h
  8. 156 0
      RedisAgent/include/adapters/glib.h
  9. 84 0
      RedisAgent/include/adapters/ivykis.h
  10. 188 0
      RedisAgent/include/adapters/libev.h
  11. 175 0
      RedisAgent/include/adapters/libevent.h
  12. 123 0
      RedisAgent/include/adapters/libhv.h
  13. 171 0
      RedisAgent/include/adapters/libuv.h
  14. 115 0
      RedisAgent/include/adapters/macosx.h
  15. 197 0
      RedisAgent/include/adapters/poll.h
  16. 135 0
      RedisAgent/include/adapters/qt.h
  17. 96 0
      RedisAgent/include/alloc.h
  18. 152 0
      RedisAgent/include/async.h
  19. 366 0
      RedisAgent/include/hiredis.h
  20. 129 0
      RedisAgent/include/read.h
  21. 280 0
      RedisAgent/include/sds.h
  22. 95 0
      RedisAgent/include/sockcompat.h
  23. BIN
      RedisAgent/lib/libhiredis.a
  24. BIN
      RedisAgent/lib/libhiredis.dll.a
  25. 9 0
      RedisAgent/libaray_symbols.h
  26. 68 0
      TDAgent/TDAgent.cpp
  27. 42 0
      TDAgent/TDAgent.h
  28. 27 0
      TDAgent/TDAgent.pro
  29. 285 0
      TDAgent/TDengine.cpp
  30. 41 0
      TDAgent/TDengine.h
  31. 362 0
      TDAgent/include/taos.h
  32. BIN
      TDAgent/lib/taos.lib
  33. 9 0
      TDAgent/libaray_symbols.h
  34. 148 0
      alarmlight/alarmlight.cpp
  35. 42 0
      alarmlight/alarmlight.h
  36. 23 0
      alarmlight/alarmlight.pro
  37. 12 0
      alarmlight/alarmlight_global.h
  38. 25 0
      include/BaseModule.h
  39. 82 0
      include/Define.h
  40. 10 0
      include/JobModule.h
  41. 100 0
      include/LibraryLoader.h
  42. 93 0
      include/LockFreeQueue.h
  43. 18 0
      include/MWareInterface.h
  44. 10 0
      include/RunnableModule.h
  45. BIN
      lib/RedisAgent.dll
  46. BIN
      lib/TDAgent.dll
  47. BIN
      lib/libRedisAgent.a
  48. BIN
      lib/libTDAgent.a
  49. 19 0
      lpintelligentcontrol.pro
  50. 3 0
      maincontrol/EngineDemo_zh_CN.ts
  51. 14 0
      maincontrol/main.cpp
  52. 135 0
      maincontrol/maincontrol.cpp
  53. 29 0
      maincontrol/maincontrol.h
  54. 30 0
      maincontrol/maincontrol.pro
  55. 65 0
      maincontrol/oneitem.cpp
  56. 19 0
      maincontrol/oneitem.h
  57. 110 0
      maincontrol/readmodulefromredis.cpp
  58. 19 0
      maincontrol/readmodulefromredis.h

+ 12 - 0
.gitignore

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

+ 655 - 0
RedisAgent/Redis.cpp

@@ -0,0 +1,655 @@
+#include "redis.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");
+}
+
+EventSubInterface*  g_pSubCB;       // 订阅回调
+void RedisCB(redisAsyncContext *ctx, void *r, void *privdata)
+{
+    redisReply *reply = static_cast<redisReply*>(r);
+    if( reply->elements < 3)
+    {
+        return;
+    }
+
+    std::string type = reply->element[0]->str;
+    if(strcasecmp(type.c_str(), "message") != 0)
+    {
+        return;
+    }
+
+    std::string chanel = reply->element[1]->str;
+    std::string msg = reply->element[2]->str;
+    if( g_pSubCB != nullptr )
+    {
+        g_pSubCB->SubCB((char*)chanel.c_str(), (char*)msg.c_str());
+    }
+}
+
+Redis::Redis(QObject *parent) : QObject(parent)
+{
+    redisOk = false;
+    g_pSubCB = nullptr;
+//    conn();
+}
+
+Redis::~Redis()
+{
+    delete rc;
+    delete rac;
+}
+
+void Redis::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 Redis::start()
+{
+    conn();
+    //开启一个定时器, 每秒检测一次是否断开, 断开后自动连接redis服务器
+    QTimer *redisTimer = new QTimer();
+    connect(redisTimer, SIGNAL(timeout()), this, SLOT(conn()));
+    redisTimer->start(1000);
+}
+
+bool Redis::hset(QString k, QString f, QString v)
+{
+    // 如果 field 是哈希表中的一个新建域,并且值设置成功,reply->integer为1
+    // 如果哈希表中域 field 已经存在且旧值已被新值覆盖,reply->integer为0
+    // 无法区别是否成功
+    bool ret = false;
+    redisReply *reply = (redisReply *)redisCommand(rc, "hset %s %s %s", k.toStdString().c_str(), f.toStdString().c_str(), 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 Redis::hget(QString k, QString f)
+{
+    QString ret;
+    redisReply *reply = (redisReply *)redisCommand(rc, QString("hget " + k + " " + f).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 Redis::hkeys(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 Redis::hvals(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> Redis::hgetall(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 Redis::rpush(QString lData, QString s)
+{
+    bool ret = false;
+    // redisReply * reply = (redisReply*)redisCommand(rc, QString("rpush " + lData + " " + js).toStdString().c_str());
+    redisReply *reply = (redisReply *)redisCommand(rc, "rpush %s %s", lData.toStdString().c_str(), s.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 Redis::rpushb(QString lData, 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 Redis::lpop(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 Redis::blpop(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 Redis::blpop(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 Redis::set(QString k, QString v)
+{
+    bool ret = false;
+    // redisReply * reply = (redisReply*)redisCommand(rc, QString("set " + k + " " + v).toStdString().c_str()); //不支持空格
+    redisReply *reply = (redisReply *)redisCommand(rc, "set %s %s", k.toStdString().c_str(), 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 Redis::setb(QString k, 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 Redis::get(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 Redis::getb(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 Redis::expire(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 Redis::publish(QString ch, QString s)
+{
+    bool ret = false;
+    // redisReply * reply = (redisReply*)redisCommand(rc, QString("publish " + ch + " " + js).toStdString().c_str());
+    redisReply *reply = (redisReply *)redisCommand(rc, "publish %s %s", ch.toStdString().c_str(), s.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;
+}
+
+bool Redis::publishb(QString ch, 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 Redis::subscribe(QString ch, EventSubInterface *fn)
+{
+    g_pSubCB = 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, RedisCB, nullptr, QString("subscribe " + ch).toStdString().c_str());
+    }
+}
+
+void Redis::psubscribe(QString ch, EventSubInterface *fn)
+{
+    g_pSubCB = 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, RedisCB, nullptr, QString("psubscribe " + ch).toStdString().c_str());
+    }
+}
+
+void Redis::Setup(tagSetup ts)
+{
+    ip = ts.addr.c_str();
+    port = ts.port;
+    auth = ts.password.c_str();
+
+    redisOk = false;
+}

+ 52 - 0
RedisAgent/Redis.h

@@ -0,0 +1,52 @@
+#pragma once
+
+#include "include/adapters/qt.h"
+#include "include/async.h"
+#include "include/hiredis.h"
+#include "MWareInterface.h"
+#include <QtCore/QObject>
+#include <QtCore/QDebug>
+
+class Redis : public QObject
+{
+    Q_OBJECT
+public:
+    explicit Redis(QObject *parent = 0);
+    ~Redis();
+
+public slots:
+    void start();
+    void conn();
+    bool hset(QString m, QString k, QString v);
+    QString hget(QString m, QString k);
+    QHash<QString, QString> hgetall(QString k);
+    QStringList hkeys(QString k);
+    QStringList hvals(QString k);
+    bool set(QString k, QString v);
+    bool setb(QString k, QByteArray &v);
+    QString get(QString k);
+    QByteArray getb(QString k);
+    bool rpush(QString lData, QString js);                  //向队列尾部加入字符串数据
+    bool rpushb(QString lData, QByteArray ba);              //向队列尾部加入二进制数据
+    QString lpop(QString lData);
+    QString blpop(QString lData, quint32 timeout);          //同步阻塞一定时间返回数据
+    bool blpop(QString lData, redisCallbackFn *fn);         //阻塞从队列头部获取最早数据
+    bool publish(QString ch, QString js);
+    bool publishb(QString ch, QByteArray ba);
+    bool expire(QString k,int sec);
+    void subscribe(QString ch, EventSubInterface *fn);      // 订阅
+    void psubscribe(QString ch, EventSubInterface *fn);     // 订阅:模式匹配
+
+private:
+    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(tagSetup ts);
+};

+ 81 - 0
RedisAgent/RedisAgent.cpp

@@ -0,0 +1,81 @@
+#include "RedisAgent.h"
+
+RedisAgent::RedisAgent()
+	: QObject()
+{
+}
+
+RedisAgent::~RedisAgent()
+{
+}
+
+void RedisAgent::SubCB(const std::string topic, const std::string msg)
+{
+    emit sigEvent(topic, msg);
+}
+
+bool RedisAgent::Connect(tagSetup ts)
+{
+    objRedis.Setup(ts);
+    objRedis.start();
+
+    return true;
+}
+
+bool RedisAgent::Subscribe(std::list<std::string> topics)
+{
+    bool ret = true;
+    std::list<std::string>::iterator itr;
+    for (itr = topics.begin(); itr != topics.end(); ++itr)
+    {
+        objRedis.subscribe(itr->c_str(), this);
+    }
+
+	return ret;
+}
+
+void RedisAgent::Publish(std::string szKey,QVariant v)
+{
+    std::string szContent;
+    switch( v.type() )
+    {
+    case QMetaType::Bool:
+        szContent = v.toBool() ? "true":"false";
+        break;
+    case QMetaType::Int:
+        szContent = std::to_string(v.toInt());
+        break;
+    case QMetaType::UInt:
+        szContent = std::to_string(v.toUInt());
+        break;
+    case QMetaType::LongLong:
+        szContent = std::to_string(v.toLongLong());
+        break;
+    case QMetaType::ULongLong:
+        szContent = std::to_string(v.toULongLong());
+        break;
+    case QMetaType::Double:
+        szContent = std::to_string(v.toDouble());
+        break;
+    case QMetaType::QString:
+        szContent = v.toString().toLocal8Bit().toStdString();
+        break;
+    }
+
+    objRedis.publish(szKey.c_str(), szContent.c_str()); //把数据发送到对应主题
+}
+
+QStringList RedisAgent::hvals(QString k)
+{
+    return objRedis.hvals(k);
+}
+
+QHash<QString, QString> RedisAgent::hgetall(QString k)
+{
+    return objRedis.hgetall(k);
+}
+
+bool RedisAgent::hset(QString k, QString f, QString v)
+{
+    return objRedis.hset(k,f,v);
+}

+ 44 - 0
RedisAgent/RedisAgent.h

@@ -0,0 +1,44 @@
+#pragma once
+
+//#include "BaseModule.h"
+//#include "libaray_symbols.h"
+#include "Redis.h"
+#include <QtNetwork/QUdpSocket>
+#include <QtCore/QVariant>
+#include <QtCore/QObject>
+
+class RedisAgent : public QObject,public EventSubInterface//,public BaseModule
+{
+	Q_OBJECT
+public:
+    RedisAgent();
+    ~RedisAgent();
+
+private:
+//    virtual void Setup(ModuleInfo mi) {};
+//    virtual void regConsumer(DataConsumer* pDC) {};
+//    virtual bool isInheritedFrom(std::string tp) {return false;};
+//    virtual void OnSubData(std::string name,std::string val) {};
+
+private:
+    Redis objRedis;
+
+signals:
+    void sigEvent(const std::string topic, const std::string msg);
+
+private:
+    void SubCB(const std::string topic, const std::string msg);
+
+public:
+    bool Connect(tagSetup ts);
+    bool Subscribe(std::list<std::string> lstTopic);
+    void Publish(std::string,QVariant v);
+    QStringList hvals(QString k);
+    QHash<QString, QString> hgetall(QString k);
+    bool hset(QString k, QString f, QString v);
+};
+
+//extern "C" {//一定要添加上
+//REDISAGENT_EXPORT BaseModule* instance();
+//REDISAGENT_EXPORT void destroy(BaseModule*);
+//}

+ 28 - 0
RedisAgent/RedisAgent.pro

@@ -0,0 +1,28 @@
+QT = core network
+QT -= gui
+
+CONFIG += c++17 cmdline
+
+TARGET = RedisAgent
+TEMPLATE = lib
+
+unix{
+}
+else{
+#DESTDIR = $$PWD/../bin/plugins
+DESTDIR = $$PWD/../lib
+}
+
+#DEFINES += REDISAGENT_LIBRARY
+
+INCLUDEPATH += ../include
+LIBS += -L$$PWD/lib/ -lhiredis
+
+SOURCES += \
+    Redis.cpp \
+    RedisAgent.cpp
+
+HEADERS += \
+    Redis.h \
+    RedisAgent.h \
+    include/adapters/qt.h

+ 130 - 0
RedisAgent/include/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
RedisAgent/include/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
RedisAgent/include/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
RedisAgent/include/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
RedisAgent/include/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
RedisAgent/include/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
RedisAgent/include/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
RedisAgent/include/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
RedisAgent/include/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
RedisAgent/include/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
RedisAgent/include/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
RedisAgent/include/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
RedisAgent/include/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
RedisAgent/include/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
RedisAgent/include/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
RedisAgent/include/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 */

BIN
RedisAgent/lib/libhiredis.a


BIN
RedisAgent/lib/libhiredis.dll.a


+ 9 - 0
RedisAgent/libaray_symbols.h

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

+ 68 - 0
TDAgent/TDAgent.cpp

@@ -0,0 +1,68 @@
+#include "TDAgent.h"
+#include "TDengine.h"
+
+TDengine objTD;
+TDAgent::TDAgent()
+	: QObject()
+{
+}
+
+TDAgent::~TDAgent()
+{
+}
+
+void TDAgent::SubCB(const std::string topic, const std::string msg)
+{
+    emit sigEvent(topic, msg);
+}
+
+bool TDAgent::Connect(tagSetup ts)
+{
+    objTD.Setup(ts);
+
+    return true;
+}
+
+bool TDAgent::Subscribe(std::list<std::string> topics)
+{
+    bool ret = true;
+    std::list<std::string>::iterator itr;
+    for (itr = topics.begin(); itr != topics.end(); ++itr)
+    {
+        objTD.subscribe(itr->c_str(), this);
+    }
+
+    objTD.start();
+    return ret;
+}
+
+void TDAgent::Publish(std::string szKey,QVariant v)
+{
+    std::string szContent;
+    switch( v.type() )
+    {
+    case QMetaType::Bool:
+        szContent = v.toBool() ? "true":"false";
+        break;
+    case QMetaType::Int:
+        szContent = std::to_string(v.toInt());
+        break;
+    case QMetaType::UInt:
+        szContent = std::to_string(v.toUInt());
+        break;
+    case QMetaType::LongLong:
+        szContent = std::to_string(v.toLongLong());
+        break;
+    case QMetaType::ULongLong:
+        szContent = std::to_string(v.toULongLong());
+        break;
+    case QMetaType::Double:
+        szContent = std::to_string(v.toDouble());
+        break;
+    case QMetaType::QString:
+        szContent = v.toString().toLocal8Bit().toStdString();
+        break;
+    }
+
+//    objRedis.publish(szKey.c_str(), szContent.c_str()); //把数据发送到对应主题
+}

+ 42 - 0
TDAgent/TDAgent.h

@@ -0,0 +1,42 @@
+#pragma once
+
+//#include "BaseModule.h"
+//#include "libaray_symbols.h"
+#include "MWareInterface.h"
+#include <QtNetwork/QUdpSocket>
+#include <QtCore/QVariant>
+#include <QtCore/QObject>
+
+// insert into lpmcs.dev_12345 values(now,1,2,3)
+// topic:topic_dev_12346
+class TDAgent : public QObject,public EventSubInterface//,public BaseModule
+{
+	Q_OBJECT
+public:
+    TDAgent();
+    ~TDAgent();
+
+private:
+//    virtual void Setup(ModuleInfo mi) {};
+//    virtual void regConsumer(DataConsumer* pDC) {};
+//    virtual bool isInheritedFrom(std::string tp) {return false;};
+//    virtual void OnSubData(std::string name,std::string val) {};
+
+private:
+
+signals:
+    void sigEvent(const std::string topic, const std::string msg);
+
+private:
+    void SubCB(const std::string topic, const std::string msg);
+
+public:
+    bool Connect(tagSetup ts);
+    bool Subscribe(std::list<std::string> lstTopic);
+    void Publish(std::string,QVariant v);
+};
+
+//extern "C" {//一定要添加上
+//TDEGENT_EXPORT BaseModule* instance();
+//TDEGENT_EXPORT void destroy(BaseModule*);
+//}

+ 27 - 0
TDAgent/TDAgent.pro

@@ -0,0 +1,27 @@
+QT = core network
+QT -= gui
+
+CONFIG += c++17 cmdline
+
+TARGET = TDAgent
+TEMPLATE = lib
+
+unix{
+}
+else{
+    DESTDIR = $$PWD/../lib
+}
+
+DEFINES += TDEGENT_LIBRARY
+
+INCLUDEPATH += include
+INCLUDEPATH += ../include
+LIBS += -L$$PWD/lib/ -ltaos
+
+HEADERS += \
+    TDAgent.h \
+    TDengine.h
+
+SOURCES += \
+    TDAgent.cpp \
+    TDengine.cpp

+ 285 - 0
TDAgent/TDengine.cpp

@@ -0,0 +1,285 @@
+#include "TDengine.h"
+#include "taos.h"
+#include <QtCore/QDebug>
+#include <QtConcurrent/QtConcurrent>
+
+#include <QJsonArray>
+#include <QJsonObject>
+#include <QJsonDocument>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+
+TAOS* pConn = NULL;
+EventSubInterface*  g_pSubCB;       // 订阅回调
+std::list<std::string>  g_lstTopics;
+
+int32_t TDengine::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 jsonObject;
+        for(int k = 0;k < numOfFields;k++)
+        {
+            QString fieldName = fields[k].name;
+            if(fields[k].type == TSDB_DATA_TYPE_TIMESTAMP)
+            {
+                uint64_t value = *((int64_t *)row[k]);
+                int intvalue = (int)value;
+                jsonObject.insert(fieldName,intvalue);
+            }
+            else if(fields[k].type == TSDB_DATA_TYPE_VARCHAR)
+            {
+                QString value((char *)row[k]);
+                jsonObject.insert(fieldName,value);
+            }
+            else if(fields[k].type == TSDB_DATA_TYPE_DOUBLE)
+            {
+                double value = *(double *)(row[k]);
+                jsonObject.insert(fieldName,value);
+            }
+            else if(fields[k].type == TSDB_DATA_TYPE_FLOAT)
+            {
+                float value = *(float *)(row[k]);
+                jsonObject.insert(fieldName,value);
+            }
+            else if(fields[k].type == TSDB_DATA_TYPE_INT)
+            {
+                int value = *(int *)(row[k]);
+                jsonObject.insert(fieldName,value);
+            }
+            else if(fields[k].type == TSDB_DATA_TYPE_BIGINT)
+            {
+                int64_t value = *(int64_t *)(row[k]);
+                int intvalue = (int)value;
+                jsonObject.insert(fieldName,intvalue);
+            }
+        }
+
+        std::string content = QJsonDocument(jsonObject).toJson(QJsonDocument::Compact).toStdString();
+
+        if( g_pSubCB != nullptr )
+        {
+            std::string topic = topicName;
+            //std::string content = buf;
+            g_pSubCB->SubCB((char*)topic.c_str(),content);
+        }
+        //qDebug() << __FILE__ << __LINE__ << "row content: " << buf;
+    }
+
+    return rows;
+}
+
+//构建消费者
+tmq_t* TDengine::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* TDengine::buildTopicList()
+{
+    tmq_list_t* topicList = tmq_list_new();
+
+    std::list<std::string>::iterator itr;
+    for( itr = g_lstTopics.begin(); itr != g_lstTopics.end(); ++itr )
+    {
+        std::string szTopic = *itr;
+        int32_t code = tmq_list_append(topicList, szTopic.c_str());
+        if (code) {
+            return NULL;
+        }
+    }
+    return topicList;
+}
+
+//轮询主题
+void TDengine::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;
+}
+
+TDengine::TDengine(QObject *parent)
+    : QObject(parent)
+{
+    pConn = nullptr;
+    g_pSubCB = nullptr;
+    g_lstTopics.clear();
+}
+
+TDengine::~TDengine()
+{
+    taos_close(pConn);
+}
+
+void TDengine::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 TDengine::subscribe(QString ch, EventSubInterface *fn)
+{
+    g_pSubCB = fn;
+    g_lstTopics.push_back(ch.toLocal8Bit().toStdString());
+}
+
+void TDengine::psubscribe(QString ch, EventSubInterface *fn)
+{
+    g_pSubCB = fn;
+    g_lstTopics.push_back(ch.toLocal8Bit().toStdString());
+}
+
+void TDengine::Setup(tagSetup ts)
+{
+    host = ts.addr.c_str();
+    user = ts.user.c_str();
+    dbName = ts.db.c_str();
+    password = ts.password.c_str();
+    m_nPort = ts.port;
+}
+
+void TDengine::start()
+{
+    pConn = taos_connect(host.toStdString().c_str(),
+                         user.toStdString().c_str(),
+                         password.toStdString().c_str(),
+                         dbName.toStdString().c_str(),
+                         m_nPort);
+    if (pConn == NULL) {
+        qDebug()<< __FILE__ << __LINE__ << "td conn err.";
+        return;
+    }
+
+    //开启一个线程轮询订阅的主题
+    QtConcurrent::run(this, &TDengine::topicLoop);
+}

+ 41 - 0
TDAgent/TDengine.h

@@ -0,0 +1,41 @@
+#pragma once
+
+#include "MWareInterface.h"
+#include "taos.h"
+#include <QtCore/QObject>
+#include <QtCore/QDebug>
+
+class TDengine : public QObject
+{
+    Q_OBJECT
+
+public:
+    explicit TDengine(QObject *parent = 0);
+    ~TDengine();
+
+    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 = "localhost";
+    uint    m_nPort = 0;
+    QString user = "root";
+    QString dbName = "taosdata";
+    QString password;
+
+    TAOS* pConn = NULL;
+
+public:
+    void Setup(tagSetup ts);
+    void subscribe(QString ch, EventSubInterface *fn);      // 订阅
+    void psubscribe(QString ch, EventSubInterface *fn);     // 订阅:模式匹配
+    void start();
+};

+ 362 - 0
TDAgent/include/taos.h

@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
+ *
+ * This program is free software: you can use, redistribute, and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3
+ * or later ("AGPL"), as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TDENGINE_TAOS_H
+#define TDENGINE_TAOS_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void   TAOS;
+typedef void   TAOS_STMT;
+typedef void   TAOS_RES;
+typedef void **TAOS_ROW;
+typedef void   TAOS_SUB;
+
+// Data type definition
+#define TSDB_DATA_TYPE_NULL       0   // 1 bytes
+#define TSDB_DATA_TYPE_BOOL       1   // 1 bytes
+#define TSDB_DATA_TYPE_TINYINT    2   // 1 byte
+#define TSDB_DATA_TYPE_SMALLINT   3   // 2 bytes
+#define TSDB_DATA_TYPE_INT        4   // 4 bytes
+#define TSDB_DATA_TYPE_BIGINT     5   // 8 bytes
+#define TSDB_DATA_TYPE_FLOAT      6   // 4 bytes
+#define TSDB_DATA_TYPE_DOUBLE     7   // 8 bytes
+#define TSDB_DATA_TYPE_VARCHAR    8   // string, alias for varchar
+#define TSDB_DATA_TYPE_TIMESTAMP  9   // 8 bytes
+#define TSDB_DATA_TYPE_NCHAR      10  // unicode string
+#define TSDB_DATA_TYPE_UTINYINT   11  // 1 byte
+#define TSDB_DATA_TYPE_USMALLINT  12  // 2 bytes
+#define TSDB_DATA_TYPE_UINT       13  // 4 bytes
+#define TSDB_DATA_TYPE_UBIGINT    14  // 8 bytes
+#define TSDB_DATA_TYPE_JSON       15  // json string
+#define TSDB_DATA_TYPE_VARBINARY  16  // binary
+#define TSDB_DATA_TYPE_DECIMAL    17  // decimal
+#define TSDB_DATA_TYPE_BLOB       18  // binary
+#define TSDB_DATA_TYPE_MEDIUMBLOB 19
+#define TSDB_DATA_TYPE_BINARY     TSDB_DATA_TYPE_VARCHAR  // string
+#define TSDB_DATA_TYPE_GEOMETRY   20  // geometry
+#define TSDB_DATA_TYPE_MAX        21
+
+typedef enum {
+  TSDB_OPTION_LOCALE,
+  TSDB_OPTION_CHARSET,
+  TSDB_OPTION_TIMEZONE,
+  TSDB_OPTION_CONFIGDIR,
+  TSDB_OPTION_SHELL_ACTIVITY_TIMER,
+  TSDB_OPTION_USE_ADAPTER,
+  TSDB_MAX_OPTIONS
+} TSDB_OPTION;
+
+typedef enum {
+  TSDB_SML_UNKNOWN_PROTOCOL = 0,
+  TSDB_SML_LINE_PROTOCOL = 1,
+  TSDB_SML_TELNET_PROTOCOL = 2,
+  TSDB_SML_JSON_PROTOCOL = 3,
+} TSDB_SML_PROTOCOL_TYPE;
+
+typedef enum {
+  TSDB_SML_TIMESTAMP_NOT_CONFIGURED = 0,
+  TSDB_SML_TIMESTAMP_HOURS,
+  TSDB_SML_TIMESTAMP_MINUTES,
+  TSDB_SML_TIMESTAMP_SECONDS,
+  TSDB_SML_TIMESTAMP_MILLI_SECONDS,
+  TSDB_SML_TIMESTAMP_MICRO_SECONDS,
+  TSDB_SML_TIMESTAMP_NANO_SECONDS,
+} TSDB_SML_TIMESTAMP_TYPE;
+
+typedef struct taosField {
+  char    name[65];
+  int8_t  type;
+  int32_t bytes;
+} TAOS_FIELD;
+
+typedef struct TAOS_FIELD_E {
+  char    name[65];
+  int8_t  type;
+  uint8_t precision;
+  uint8_t scale;
+  int32_t bytes;
+} TAOS_FIELD_E;
+
+#ifdef WINDOWS
+#define DLL_EXPORT __declspec(dllexport)
+#else
+#define DLL_EXPORT
+#endif
+
+typedef void (*__taos_async_fn_t)(void *param, TAOS_RES *res, int code);
+typedef void (*__taos_notify_fn_t)(void *param, void *ext, int type);
+
+typedef struct TAOS_MULTI_BIND {
+  int       buffer_type;
+  void     *buffer;
+  uintptr_t buffer_length;
+  int32_t  *length;
+  char     *is_null;
+  int       num;
+} TAOS_MULTI_BIND;
+
+typedef enum {
+  SET_CONF_RET_SUCC = 0,
+  SET_CONF_RET_ERR_PART = -1,
+  SET_CONF_RET_ERR_INNER = -2,
+  SET_CONF_RET_ERR_JSON_INVALID = -3,
+  SET_CONF_RET_ERR_JSON_PARSE = -4,
+  SET_CONF_RET_ERR_ONLY_ONCE = -5,
+  SET_CONF_RET_ERR_TOO_LONG = -6
+} SET_CONF_RET_CODE;
+
+typedef enum {
+  TAOS_NOTIFY_PASSVER = 0,
+} TAOS_NOTIFY_TYPE;
+
+#define RET_MSG_LENGTH 1024
+typedef struct setConfRet {
+  SET_CONF_RET_CODE retCode;
+  char              retMsg[RET_MSG_LENGTH];
+} setConfRet;
+
+typedef struct TAOS_VGROUP_HASH_INFO {
+  int32_t  vgId;
+  uint32_t hashBegin;
+  uint32_t hashEnd;
+} TAOS_VGROUP_HASH_INFO;
+
+typedef struct TAOS_DB_ROUTE_INFO {
+  int32_t                routeVersion;
+  int16_t                hashPrefix;
+  int16_t                hashSuffix;
+  int8_t                 hashMethod;
+  int32_t                vgNum;
+  TAOS_VGROUP_HASH_INFO *vgHash;
+} TAOS_DB_ROUTE_INFO;
+
+DLL_EXPORT void       taos_cleanup(void);
+DLL_EXPORT int        taos_options(TSDB_OPTION option, const void *arg, ...);
+DLL_EXPORT setConfRet taos_set_config(const char *config);
+DLL_EXPORT int        taos_init(void);
+DLL_EXPORT TAOS      *taos_connect(const char *ip, const char *user, const char *pass, const char *db, uint16_t port);
+DLL_EXPORT TAOS *taos_connect_auth(const char *ip, const char *user, const char *auth, const char *db, uint16_t port);
+DLL_EXPORT void  taos_close(TAOS *taos);
+
+DLL_EXPORT const char *taos_data_type(int type);
+
+DLL_EXPORT TAOS_STMT *taos_stmt_init(TAOS *taos);
+DLL_EXPORT TAOS_STMT *taos_stmt_init_with_reqid(TAOS *taos, int64_t reqid);
+DLL_EXPORT int        taos_stmt_prepare(TAOS_STMT *stmt, const char *sql, unsigned long length);
+DLL_EXPORT int        taos_stmt_set_tbname_tags(TAOS_STMT *stmt, const char *name, TAOS_MULTI_BIND *tags);
+DLL_EXPORT int        taos_stmt_set_tbname(TAOS_STMT *stmt, const char *name);
+DLL_EXPORT int        taos_stmt_set_tags(TAOS_STMT *stmt, TAOS_MULTI_BIND *tags);
+DLL_EXPORT int        taos_stmt_set_sub_tbname(TAOS_STMT *stmt, const char *name);
+DLL_EXPORT int        taos_stmt_get_tag_fields(TAOS_STMT *stmt, int *fieldNum, TAOS_FIELD_E **fields);
+DLL_EXPORT int        taos_stmt_get_col_fields(TAOS_STMT *stmt, int *fieldNum, TAOS_FIELD_E **fields);
+// let stmt to reclaim TAOS_FIELD_E that was allocated by `taos_stmt_get_tag_fields`/`taos_stmt_get_col_fields`
+DLL_EXPORT void taos_stmt_reclaim_fields(TAOS_STMT *stmt, TAOS_FIELD_E *fields);
+
+DLL_EXPORT int       taos_stmt_is_insert(TAOS_STMT *stmt, int *insert);
+DLL_EXPORT int       taos_stmt_num_params(TAOS_STMT *stmt, int *nums);
+DLL_EXPORT int       taos_stmt_get_param(TAOS_STMT *stmt, int idx, int *type, int *bytes);
+DLL_EXPORT int       taos_stmt_bind_param(TAOS_STMT *stmt, TAOS_MULTI_BIND *bind);
+DLL_EXPORT int       taos_stmt_bind_param_batch(TAOS_STMT *stmt, TAOS_MULTI_BIND *bind);
+DLL_EXPORT int       taos_stmt_bind_single_param_batch(TAOS_STMT *stmt, TAOS_MULTI_BIND *bind, int colIdx);
+DLL_EXPORT int       taos_stmt_add_batch(TAOS_STMT *stmt);
+DLL_EXPORT int       taos_stmt_execute(TAOS_STMT *stmt);
+DLL_EXPORT TAOS_RES *taos_stmt_use_result(TAOS_STMT *stmt);
+DLL_EXPORT int       taos_stmt_close(TAOS_STMT *stmt);
+DLL_EXPORT char     *taos_stmt_errstr(TAOS_STMT *stmt);
+DLL_EXPORT int       taos_stmt_affected_rows(TAOS_STMT *stmt);
+DLL_EXPORT int       taos_stmt_affected_rows_once(TAOS_STMT *stmt);
+
+DLL_EXPORT TAOS_RES *taos_query(TAOS *taos, const char *sql);
+DLL_EXPORT TAOS_RES *taos_query_with_reqid(TAOS *taos, const char *sql, int64_t reqId);
+
+DLL_EXPORT TAOS_ROW taos_fetch_row(TAOS_RES *res);
+DLL_EXPORT int      taos_result_precision(TAOS_RES *res);  // get the time precision of result
+DLL_EXPORT void     taos_free_result(TAOS_RES *res);
+DLL_EXPORT void     taos_kill_query(TAOS *taos);
+DLL_EXPORT int      taos_field_count(TAOS_RES *res);
+DLL_EXPORT int      taos_num_fields(TAOS_RES *res);
+DLL_EXPORT int      taos_affected_rows(TAOS_RES *res);
+DLL_EXPORT int64_t  taos_affected_rows64(TAOS_RES *res);
+
+DLL_EXPORT TAOS_FIELD *taos_fetch_fields(TAOS_RES *res);
+DLL_EXPORT int         taos_select_db(TAOS *taos, const char *db);
+DLL_EXPORT int         taos_print_row(char *str, TAOS_ROW row, TAOS_FIELD *fields, int num_fields);
+DLL_EXPORT void        taos_stop_query(TAOS_RES *res);
+DLL_EXPORT bool        taos_is_null(TAOS_RES *res, int32_t row, int32_t col);
+DLL_EXPORT bool        taos_is_update_query(TAOS_RES *res);
+DLL_EXPORT int         taos_fetch_block(TAOS_RES *res, TAOS_ROW *rows);
+DLL_EXPORT int         taos_fetch_block_s(TAOS_RES *res, int *numOfRows, TAOS_ROW *rows);
+DLL_EXPORT int         taos_fetch_raw_block(TAOS_RES *res, int *numOfRows, void **pData);
+DLL_EXPORT int        *taos_get_column_data_offset(TAOS_RES *res, int columnIndex);
+DLL_EXPORT int         taos_validate_sql(TAOS *taos, const char *sql);
+DLL_EXPORT void        taos_reset_current_db(TAOS *taos);
+
+DLL_EXPORT int      *taos_fetch_lengths(TAOS_RES *res);
+DLL_EXPORT TAOS_ROW *taos_result_block(TAOS_RES *res);
+
+DLL_EXPORT const char *taos_get_server_info(TAOS *taos);
+DLL_EXPORT const char *taos_get_client_info();
+DLL_EXPORT int         taos_get_current_db(TAOS *taos, char *database, int len, int *required);
+
+DLL_EXPORT const char *taos_errstr(TAOS_RES *res);
+DLL_EXPORT int         taos_errno(TAOS_RES *res);
+
+DLL_EXPORT void taos_query_a(TAOS *taos, const char *sql, __taos_async_fn_t fp, void *param);
+DLL_EXPORT void taos_query_a_with_reqid(TAOS *taos, const char *sql, __taos_async_fn_t fp, void *param, int64_t reqid);
+DLL_EXPORT void taos_fetch_rows_a(TAOS_RES *res, __taos_async_fn_t fp, void *param);
+DLL_EXPORT void taos_fetch_raw_block_a(TAOS_RES *res, __taos_async_fn_t fp, void *param);
+DLL_EXPORT const void *taos_get_raw_block(TAOS_RES *res);
+
+DLL_EXPORT int taos_get_db_route_info(TAOS *taos, const char *db, TAOS_DB_ROUTE_INFO *dbInfo);
+DLL_EXPORT int taos_get_table_vgId(TAOS *taos, const char *db, const char *table, int *vgId);
+DLL_EXPORT int taos_get_tables_vgId(TAOS *taos, const char *db, const char *table[], int tableNum, int *vgId);
+
+DLL_EXPORT int taos_load_table_info(TAOS *taos, const char *tableNameList);
+
+// set heart beat thread quit mode , if quicByKill 1 then kill thread else quit from inner
+DLL_EXPORT void taos_set_hb_quit(int8_t quitByKill);
+
+DLL_EXPORT int taos_set_notify_cb(TAOS *taos, __taos_notify_fn_t fp, void *param, int type);
+
+/*  --------------------------schemaless INTERFACE------------------------------- */
+
+DLL_EXPORT TAOS_RES *taos_schemaless_insert(TAOS *taos, char *lines[], int numLines, int protocol, int precision);
+DLL_EXPORT TAOS_RES *taos_schemaless_insert_with_reqid(TAOS *taos, char *lines[], int numLines, int protocol,
+                                                       int precision, int64_t reqid);
+DLL_EXPORT TAOS_RES *taos_schemaless_insert_raw(TAOS *taos, char *lines, int len, int32_t *totalRows, int protocol,
+                                                int precision);
+DLL_EXPORT TAOS_RES *taos_schemaless_insert_raw_with_reqid(TAOS *taos, char *lines, int len, int32_t *totalRows,
+                                                           int protocol, int precision, int64_t reqid);
+DLL_EXPORT TAOS_RES *taos_schemaless_insert_ttl(TAOS *taos, char *lines[], int numLines, int protocol, int precision,
+                                                int32_t ttl);
+DLL_EXPORT TAOS_RES *taos_schemaless_insert_ttl_with_reqid(TAOS *taos, char *lines[], int numLines, int protocol,
+                                                           int precision, int32_t ttl, int64_t reqid);
+DLL_EXPORT TAOS_RES *taos_schemaless_insert_raw_ttl(TAOS *taos, char *lines, int len, int32_t *totalRows, int protocol,
+                                                    int precision, int32_t ttl);
+DLL_EXPORT TAOS_RES *taos_schemaless_insert_raw_ttl_with_reqid(TAOS *taos, char *lines, int len, int32_t *totalRows,
+                                                               int protocol, int precision, int32_t ttl, int64_t reqid);
+
+/* --------------------------TMQ INTERFACE------------------------------- */
+
+typedef struct tmq_t      tmq_t;
+typedef struct tmq_conf_t tmq_conf_t;
+typedef struct tmq_list_t tmq_list_t;
+
+typedef void(tmq_commit_cb(tmq_t *, int32_t code, void *param));
+
+DLL_EXPORT tmq_list_t *tmq_list_new();
+DLL_EXPORT int32_t     tmq_list_append(tmq_list_t *, const char *);
+DLL_EXPORT void        tmq_list_destroy(tmq_list_t *);
+DLL_EXPORT int32_t     tmq_list_get_size(const tmq_list_t *);
+DLL_EXPORT char      **tmq_list_to_c_array(const tmq_list_t *);
+
+DLL_EXPORT tmq_t *tmq_consumer_new(tmq_conf_t *conf, char *errstr, int32_t errstrLen);
+
+DLL_EXPORT const char *tmq_err2str(int32_t code);
+
+/* ------------------------TMQ CONSUMER INTERFACE------------------------ */
+typedef struct tmq_topic_assignment {
+  int32_t vgId;
+  int64_t currentOffset;
+  int64_t begin;
+  int64_t end;
+} tmq_topic_assignment;
+
+DLL_EXPORT int32_t   tmq_subscribe(tmq_t *tmq, const tmq_list_t *topic_list);
+DLL_EXPORT int32_t   tmq_unsubscribe(tmq_t *tmq);
+DLL_EXPORT int32_t   tmq_subscription(tmq_t *tmq, tmq_list_t **topics);
+DLL_EXPORT TAOS_RES *tmq_consumer_poll(tmq_t *tmq, int64_t timeout);
+DLL_EXPORT int32_t   tmq_consumer_close(tmq_t *tmq);
+DLL_EXPORT int32_t   tmq_commit_sync(tmq_t *tmq, const TAOS_RES *msg);
+DLL_EXPORT void      tmq_commit_async(tmq_t *tmq, const TAOS_RES *msg, tmq_commit_cb *cb, void *param);
+DLL_EXPORT int32_t   tmq_get_topic_assignment(tmq_t *tmq, const char *pTopicName, tmq_topic_assignment **assignment,
+                                              int32_t *numOfAssignment);
+DLL_EXPORT void      tmq_free_assignment(tmq_topic_assignment* pAssignment);
+DLL_EXPORT int32_t   tmq_offset_seek(tmq_t *tmq, const char *pTopicName, int32_t vgId, int64_t offset);
+
+/* ----------------------TMQ CONFIGURATION INTERFACE---------------------- */
+
+enum tmq_conf_res_t {
+  TMQ_CONF_UNKNOWN = -2,
+  TMQ_CONF_INVALID = -1,
+  TMQ_CONF_OK = 0,
+};
+
+typedef enum tmq_conf_res_t tmq_conf_res_t;
+
+DLL_EXPORT tmq_conf_t    *tmq_conf_new();
+DLL_EXPORT tmq_conf_res_t tmq_conf_set(tmq_conf_t *conf, const char *key, const char *value);
+DLL_EXPORT void           tmq_conf_destroy(tmq_conf_t *conf);
+DLL_EXPORT void           tmq_conf_set_auto_commit_cb(tmq_conf_t *conf, tmq_commit_cb *cb, void *param);
+
+/* -------------------------TMQ MSG HANDLE INTERFACE---------------------- */
+
+DLL_EXPORT const char *tmq_get_topic_name(TAOS_RES *res);
+DLL_EXPORT const char *tmq_get_db_name(TAOS_RES *res);
+DLL_EXPORT int32_t     tmq_get_vgroup_id(TAOS_RES *res);
+DLL_EXPORT int64_t     tmq_get_vgroup_offset(TAOS_RES* res);
+
+/* ------------------------------ TAOSX -----------------------------------*/
+// note: following apis are unstable
+enum tmq_res_t {
+  TMQ_RES_INVALID = -1,
+  TMQ_RES_DATA = 1,
+  TMQ_RES_TABLE_META = 2,
+  TMQ_RES_METADATA = 3,
+};
+
+typedef struct tmq_raw_data {
+  void    *raw;
+  uint32_t raw_len;
+  uint16_t raw_type;
+} tmq_raw_data;
+
+typedef enum tmq_res_t tmq_res_t;
+
+DLL_EXPORT const char *tmq_get_table_name(TAOS_RES *res);
+DLL_EXPORT tmq_res_t   tmq_get_res_type(TAOS_RES *res);
+DLL_EXPORT int32_t     tmq_get_raw(TAOS_RES *res, tmq_raw_data *raw);
+DLL_EXPORT int32_t     tmq_write_raw(TAOS *taos, tmq_raw_data raw);
+DLL_EXPORT int         taos_write_raw_block(TAOS *taos, int numOfRows, char *pData, const char *tbname);
+DLL_EXPORT int         taos_write_raw_block_with_fields(TAOS *taos, int rows, char *pData, const char *tbname,
+                                                        TAOS_FIELD *fields, int numFields);
+DLL_EXPORT void        tmq_free_raw(tmq_raw_data raw);
+// Returning null means error. Returned result need to be freed by tmq_free_json_meta
+DLL_EXPORT char *tmq_get_json_meta(TAOS_RES *res);
+DLL_EXPORT void  tmq_free_json_meta(char *jsonMeta);
+
+/* ---------------------------- TAOSX END -------------------------------- */
+
+typedef enum {
+  TSDB_SRV_STATUS_UNAVAILABLE = 0,
+  TSDB_SRV_STATUS_NETWORK_OK = 1,
+  TSDB_SRV_STATUS_SERVICE_OK = 2,
+  TSDB_SRV_STATUS_SERVICE_DEGRADED = 3,
+  TSDB_SRV_STATUS_EXTING = 4,
+} TSDB_SERVER_STATUS;
+
+DLL_EXPORT TSDB_SERVER_STATUS taos_check_server_status(const char *fqdn, int port, char *details, int maxlen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

BIN
TDAgent/lib/taos.lib


+ 9 - 0
TDAgent/libaray_symbols.h

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

+ 148 - 0
alarmlight/alarmlight.cpp

@@ -0,0 +1,148 @@
+#include "alarmlight.h"
+#include <QJsonArray>
+#include <QJsonObject>
+#include <QJsonDocument>
+#include <QtCore/QTextCodec>
+#include <fstream>
+
+Alarmlight::Alarmlight()
+{
+    m_mpDeviceLastCommand.clear();
+    m_mpRegulation.clear();
+    m_pDataConsumer = nullptr;
+}
+
+bool Alarmlight::isInheritedFrom(std::string tp)
+{
+    bool bEqual = (strcasecmp(tp.c_str(), "BaseModule") == 0);
+    return bEqual;
+}
+
+void Alarmlight::regConsumer(DataConsumer* pDC)
+{
+    m_pDataConsumer = pDC;
+}
+
+void Alarmlight::Setup(ModuleInfo moduleInfo)
+{
+    // 把模块里的关键信息读出来,例如怎么干活,什么条件让哪个设备干嘛?
+    std::map<QString,QVariant> vSettings;
+    for(std::vector<Setting>::iterator it = moduleInfo.vSettings.begin();it != moduleInfo.vSettings.end();it++)
+    {
+        vSettings[(*it).Name.c_str()] = (*it).qValue;
+    }
+    if(vSettings.find("num") != vSettings.end())
+    {
+        int number = vSettings["num"].toInt();
+        for(int i = 1;i <= number;i++)
+        {
+            oneregulation one;
+            if(vSettings.find(QString("object_%1").arg(i)) == vSettings.end())
+                continue;
+            one.sourceobject = vSettings[QString("object_%1").arg(i)].toString();
+
+            if(vSettings.find(QString("operator_%1").arg(i)) == vSettings.end())
+                continue;
+            one.useoperator = vSettings[QString("operator_%1").arg(i)].toString();
+
+            if(vSettings.find(QString("operator1_%1").arg(i)) == vSettings.end())
+                continue;
+            one.operator1 = vSettings[QString("operator1_%1").arg(i)].toDouble();
+
+            if(vSettings.find(QString("operator2_%1").arg(i)) == vSettings.end())
+                continue;
+            one.operator2 = vSettings[QString("operator2_%1").arg(i)].toDouble();
+
+            if(vSettings.find(QString("device_%1").arg(i)) == vSettings.end())
+                continue;
+            one.targetdevice = vSettings[QString("device_%1").arg(i)].toString();
+
+            if(vSettings.find(QString("devicecommand_%1").arg(i)) == vSettings.end())
+                continue;
+            one.targetdevicecommand = vSettings[QString("devicecommand_%1").arg(i)].toString();
+
+            this->m_mpRegulation.insert(std::pair<std::string,oneregulation>(one.sourceobject.toStdString(),one));
+        }
+    }
+}
+
+void Alarmlight::OnSubData(std::string name,std::string val)
+{
+    QByteArray byteArray(val.c_str(), val.length());
+    QJsonParseError err;
+    QJsonDocument jsonDoc(QJsonDocument::fromJson(byteArray,&err));
+    QJsonObject jsonObject = jsonDoc.object();
+    QStringList keys = jsonObject.keys();
+
+    for(int i = 0;i < keys.size();i++)
+    {
+        QString realkey = QString("%1.%2").arg(name.c_str()).arg(keys.at(i));
+        double realvalue = jsonObject[keys.at(i)].toDouble();
+
+        // 传入数据,判断数据,用m_pDataConsumer的ondata输出设
+        std::multimap<std::string,oneregulation>::iterator it = this->m_mpRegulation.find(realkey.toStdString());
+        if(it != this->m_mpRegulation.end())
+        {
+            for(; it != this->m_mpRegulation.end(); it++)
+            {
+                if((*it).first == realkey.toStdString())
+                {
+                    oneregulation one = (*it).second;
+                    //double value = std::stod(val);
+                    bool send = false;
+                    if(one.useoperator == "beyond")
+                    {
+                        if(realvalue > one.operator1)
+                        {
+                            send = true;
+                        }
+                    }
+                    else if(one.useoperator == "below")
+                    {
+                        if(realvalue < one.operator1)
+                        {
+                            send = true;
+                        }
+                    }
+                    else if(one.useoperator == "range")
+                    {
+                        if(realvalue >= one.operator1 && realvalue <= one.operator2)
+                        {
+                            send = true;
+                        }
+                    }
+                    if(send == true)
+                    {
+                        if(this->m_mpDeviceLastCommand.find(one.targetdevice) != this->m_mpDeviceLastCommand.end())
+                        {
+                            if(this->m_mpDeviceLastCommand[one.targetdevice] == one.targetdevicecommand)
+                                send = false;
+                        }
+                        if(send == true)
+                        {
+                            m_pDataConsumer->OnData(one.targetdevice.toStdString(),QVariant(one.targetdevicecommand));
+                            this->m_mpDeviceLastCommand[one.targetdevice] = one.targetdevicecommand;
+                        }
+                    }
+                }
+                else
+                {
+                    break;
+                }
+            }
+        }
+    }
+}
+
+BaseModule* instance()
+{
+    return new Alarmlight();
+}
+
+void destroy(BaseModule* pInstance)
+{
+    if( pInstance )
+    {
+        delete pInstance;
+    }
+}

+ 42 - 0
alarmlight/alarmlight.h

@@ -0,0 +1,42 @@
+#pragma once
+#include "BaseModule.h"
+#include <QtCore/QThread>
+#include <QtCore/QVariant>
+#include <QtCore/QDebug>
+#include <thread>
+#include <stdlib.h>
+#include <math.h>
+#include "alarmlight_global.h"
+
+typedef struct
+{
+public:
+    QString sourceobject;
+    QString useoperator;
+    double operator1;
+    double operator2;
+    QString targetdevice;
+    QString targetdevicecommand;
+}oneregulation;
+
+class ALARMLIGHT_EXPORT Alarmlight : public BaseModule
+{
+public:
+    Alarmlight();
+
+private:
+    std::map<QString,QString>                  m_mpDeviceLastCommand; // 记录设备的上一次操作,如果连续发后续就不发了
+    std::multimap<std::string,oneregulation>   m_mpRegulation;
+    DataConsumer*                              m_pDataConsumer;
+
+public:
+    virtual void Setup(ModuleInfo moduleInfo);
+    virtual void regConsumer(DataConsumer* pDC);
+    virtual bool isInheritedFrom(std::string tp);
+    virtual void OnSubData(std::string name,std::string val);
+};
+
+extern "C" {//一定要添加上
+ALARMLIGHT_EXPORT BaseModule* instance();
+ALARMLIGHT_EXPORT void destroy(BaseModule*);
+}

+ 23 - 0
alarmlight/alarmlight.pro

@@ -0,0 +1,23 @@
+QT = core
+QT -= gui
+
+CONFIG += c++17 cmdline
+
+TARGET = alarmlight
+TEMPLATE = lib
+
+unix{
+}
+else{
+    DESTDIR = $$PWD/../bin/plugins
+}
+
+DEFINES += ALARMLIGHT_LIBRARY
+INCLUDEPATH += ../include
+
+SOURCES += \
+    alarmlight.cpp
+
+HEADERS += \
+    alarmlight.h \
+    alarmlight_global.h

+ 12 - 0
alarmlight/alarmlight_global.h

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

+ 25 - 0
include/BaseModule.h

@@ -0,0 +1,25 @@
+#pragma once
+#include "../include/Define.h"
+#include <QtCore/QLibrary>
+#include <string>
+
+class DataConsumer
+{
+public:
+    virtual void OnData(std::string name,QVariant val) = 0;
+};
+
+class BaseModule
+{
+private:
+    QLibrary*       m_pLibrary;
+
+public:
+    virtual ~BaseModule(){}
+    virtual void setLoader(QLibrary* pLibrary){m_pLibrary = pLibrary;};
+
+    virtual void Setup(ModuleInfo mi) = 0;
+    virtual void regConsumer(DataConsumer* pDC) = 0;
+    virtual bool isInheritedFrom(std::string tp) = 0;
+    virtual void OnSubData(std::string name,std::string val) = 0;
+};

+ 82 - 0
include/Define.h

@@ -0,0 +1,82 @@
+#pragma once
+
+#include <QtCore/QVariant>
+#include <string>
+#include <list>
+
+enum ModuleType
+{
+    comm=1,
+    purify=2,
+    calculate=3,
+    transfer=4,
+    store=5,
+    control=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 Setting
+{
+    std::string Name;
+    QVariant    qValue;
+};
+
+struct ModuleInfo
+{
+    int Id;                         // 实例编号,不是moduleinfo里的编号,而是intelligentcontrol里的编号
+    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;            // 版本
+    int serverId;                   // 所属服务器编号
+
+    std::list<std::string> lstInputs;// 输入
+    std::vector<Setting> vSettings; // 自定义配置
+    std::list<DataItem> Properties;
+};
+
+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 AlarmRuler
+{
+    std::string NodeCode;            // 节点-标识码
+    std::string DataItemCode;        // 数据项-标识码
+    std::string Operator;            // 运算符
+    double dbOperator1;              // 操作数1
+    double dbOperator2;              // 操作数2
+    uint nKeepTimes;                 // 保持次数
+};
+
+struct Config
+{
+    std::string serverId;
+    std::string appName;
+};

+ 10 - 0
include/JobModule.h

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

+ 100 - 0
include/LibraryLoader.h

@@ -0,0 +1,100 @@
+#pragma once
+
+//#include "FileManager.h"
+//#include "StringConvert.h"
+#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 )
+        {
+            typedef void (*funDestroy)(T*);
+            funDestroy fun = (funDestroy)pLoader->resolve("destroy");
+            if( fun != nullptr )
+            {
+               fun(pPlugin);
+            }
+
+            pLoader->unload();
+        }
+
+        return true;
+    }
+};  

+ 93 - 0
include/LockFreeQueue.h

@@ -0,0 +1,93 @@
+#pragma once
+#include <iostream>
+#include <atomic>
+#include <vector>
+
+using namespace std;
+
+template<typename T>
+struct Node
+{
+    T data;	//数据
+    atomic_bool hasData; //是否有数据
+};
+
+template<typename T>
+class LockFreeQueue
+{
+public:
+    LockFreeQueue(int size = 1024)
+        :msg(size)
+    {
+        r_index = 0;
+        w_index = 0;
+    };
+    bool enqueue(T& value);  // 入队列
+    bool dequeue(T& value);  // 出队列
+
+private:
+    vector<Node<T>> msg;	// 消息队列
+    atomic<size_t> r_index; // 读索引(不考虑溢出情况)
+    atomic<size_t> w_index; // 写索引(不考虑溢出情况)
+};
+
+template<typename T>
+bool LockFreeQueue<T>::enqueue(T& value)
+{
+    size_t l_w_index = w_index.load(std::memory_order_relaxed);
+    Node<T>* node = NULL;
+
+    // CAS比较是否和预期一致,一致则对写索引递增1,不一致则重试
+    do
+    {
+        //队列是否已满
+        if (l_w_index >= r_index.load(std::memory_order_relaxed) + msg.size()) {
+            return false;
+        }
+
+        // 判断是否有数据
+        size_t index = l_w_index % msg.size();
+        node = &msg[index];
+        if (node->hasData.load(std::memory_order_relaxed))
+        {
+            return false;
+        }
+    } while (!w_index.compare_exchange_weak(l_w_index, l_w_index + 1, std::memory_order_relaxed));
+
+    //写数据
+    node->data = std::move(value);//左值转右值,避免拷贝带来的浪费
+    node->hasData.store(true);
+
+    return true;
+}
+
+template<typename T>
+bool LockFreeQueue<T>::dequeue(T& value)
+{
+    size_t l_r_index = r_index.load(std::memory_order_relaxed);;
+    Node<T>* node = NULL;
+
+    // CAS比较是否和预期一致,一致则对读索引递增1,不一致则重试
+    do
+    {
+        //队列是否为空
+        if (l_r_index > w_index.load(std::memory_order_relaxed))
+        {
+            return false;
+        }
+
+        // 判断是否有数据
+        size_t index = l_r_index % msg.size();
+        node = &msg[index];
+        if (!node->hasData.load(std::memory_order_relaxed))
+        {
+            return false;
+        }
+    } while (!r_index.compare_exchange_weak(l_r_index, l_r_index + 1, std::memory_order_relaxed));
+
+    //读数据
+    value = std::move(node->data);
+    node->hasData.store(false);
+
+    return true;
+}

+ 18 - 0
include/MWareInterface.h

@@ -0,0 +1,18 @@
+#pragma once
+#include <string>
+
+// 连接信息
+struct tagSetup
+{
+    unsigned int port;
+    std::string addr;
+    std::string user;
+    std::string password;
+    std::string db;
+};
+
+class EventSubInterface
+{
+public:
+    virtual void SubCB(const std::string topic, const std::string msg) = 0;
+};

+ 10 - 0
include/RunnableModule.h

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

BIN
lib/RedisAgent.dll


BIN
lib/TDAgent.dll


BIN
lib/libRedisAgent.a


BIN
lib/libTDAgent.a


+ 19 - 0
lpintelligentcontrol.pro

@@ -0,0 +1,19 @@
+
+TEMPLATE = subdirs
+CONFIG += ordered
+
+SUBDIRS += \
+    RedisAgent/RedisAgent.pro \
+    TDAgent/TDAgent.pro \
+    alarmlight \
+    maincontrol/maincontrol.pro \
+#    AlarmVerification/AlarmVerification.pro \
+#    AlarmRecognition/AlarmRecognition.pro \
+
+#    BitStreamUnpack/BitStreamUnpack.pro \
+#    DataValidate/DataValidate.pro \
+#    BitStreamExtrack/BitStreamExtrack.pro \
+#    Module001/Module001.pro \
+#    RedisProxy/RedisProxy.pro \
+#    AlarmServer/AlarmServer.pro \
+

+ 3 - 0
maincontrol/EngineDemo_zh_CN.ts

@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>

+<!DOCTYPE TS>

+<TS version="2.1" language="zh_CN"></TS>


+ 14 - 0
maincontrol/main.cpp

@@ -0,0 +1,14 @@
+#include "maincontrol.h"
+#include <QLocale>
+#include <QTranslator>
+#include <QCoreApplication>
+
+int main(int argc, char *argv[])
+{
+    QCoreApplication a(argc, argv);
+
+    maincontrol mc;
+    mc.Startup();
+
+    return a.exec();
+}

+ 135 - 0
maincontrol/maincontrol.cpp

@@ -0,0 +1,135 @@
+#include "maincontrol.h"
+#include "readmodulefromredis.h"
+#include <QJsonArray>
+#include <QJsonObject>
+#include <QJsonDocument>
+#include <fstream>
+#include <QtCore/QVariant>
+#include <QtCore/QDebug>
+#include <QtWidgets/QApplication>
+
+std::string loadContent(std::string szFilename)
+{
+    szFilename = QString::fromLocal8Bit(szFilename.c_str()).toLocal8Bit().toStdString();
+    std::ifstream inStream(szFilename.c_str(),std::ios::in | std::ios::binary);
+    if( inStream.is_open() != true )
+    {
+        return "";
+    }
+
+    return std::string((std::istreambuf_iterator<char>(inStream)),std::istreambuf_iterator<char>());
+}
+
+maincontrol::maincontrol() : QObject()
+{
+    m_pRedis = nullptr;
+    m_pTDengine = nullptr;
+    m_mpItems.clear();
+}
+
+void maincontrol::OnData(std::string name,QVariant val)
+{
+    // 传出来的设备命令需要发送给redis(module->oneitem->maincontrol)
+    m_pRedis->hset("devicecommand",name.c_str(),val.toString());
+}
+
+void maincontrol::sltSubData(const std::string topic, const std::string msg)
+{
+    // td过来的数据往模块里发, maincontrol->oneitem->module
+    std::multimap<std::string,oneitem*>::iterator it = this->m_mpItems.find(topic);
+    if(it != this->m_mpItems.end())
+    {
+        //找到了
+        //输出multimap 中的同一键的多个值
+        //方法一 通过比较key ,循环判断   推荐使用
+        for(; it != this->m_mpItems.end(); it++)
+        {
+            if((*it).first == topic)
+            {
+                (*it).second->OnSubData(topic,msg);
+            }
+            else
+            {
+                break;
+            }
+        }
+    }
+}
+
+void maincontrol::loadConfig(std::string szFile,std::map<QString,QString>& mps)
+{
+    std::string cfg = loadContent(szFile);
+    QJsonDocument jsonDoc(QJsonDocument::fromJson(cfg.c_str()));
+    QJsonObject json = jsonDoc.object();
+    QStringList keys = json.keys();
+    foreach (QString item, keys)
+    {
+        mps[item] = json[item].toString();
+    }
+}
+
+void maincontrol::Startup()
+{
+    // merge file name
+    QString szWorkDir = qApp->applicationDirPath();
+    if( szWorkDir.right(1).contains("/") == false && szWorkDir.right(1).contains("\\") == false)
+    {
+        szWorkDir += "/";
+    }
+    std::string szFile = szWorkDir.toLocal8Bit().toStdString() + "config/server.json";
+
+    // load server config
+    std::map<std::string,tagSetup> mpConfig;
+    std::string cfg = loadContent(szFile);
+    QJsonDocument jsonDoc(QJsonDocument::fromJson(cfg.c_str()));
+    QJsonArray ja = jsonDoc.array();
+    for(auto i : ja)
+    {
+        QJsonObject json = i.toObject();
+        QStringList items = json.keys();
+        if( items.contains("ignore",Qt::CaseInsensitive) && json["ignore"].type() == QJsonValue::Bool && json["ignore"].toBool())
+        {
+            continue;
+        }
+
+        tagSetup ts;
+        ts.addr = json["addr"].toString().toLocal8Bit().toStdString();
+        ts.user = json["user"].toString().toLocal8Bit().toStdString();
+        ts.password = json["password"].toString().toLocal8Bit().toStdString();
+        ts.db = json["database"].toString().toLocal8Bit().toStdString();
+        ts.port = json["port"].toInt();
+
+        std::string szName = json["name"].toString().toLocal8Bit().toStdString();
+        mpConfig[szName] = ts;
+    }
+
+    m_pRedis = new RedisAgent();
+    m_pRedis->Connect(mpConfig["redis"]);           // redis
+
+    m_pTDengine = new TDAgent();
+    m_pTDengine->Connect(mpConfig["tdengine"]);     // td
+
+    // server.id
+    std::map<QString,QString> mps;
+    this->loadConfig(szWorkDir.toLocal8Bit().toStdString() + "config/lpintelligentcontrol.json",mps);
+
+    // 从redis里找到所有的智能控制实例(当前服务器管理的)
+    readmodulefromredis instance(m_pRedis);
+    std::list<ModuleInfo> modules = instance.loadModules(mps["serverid"]);
+    std::list<ModuleInfo>::iterator itr;
+    for( itr = modules.begin();itr!= modules.end();++itr)
+    {
+        ModuleInfo moduleInfo = *itr;
+        oneitem *poneitem = new oneitem();
+        poneitem->regConsumer(this);
+        poneitem->createmodule(moduleInfo);
+        m_pTDengine->Subscribe(moduleInfo.lstInputs);
+        std::list<std::string>::iterator itrI;
+        for( itrI = moduleInfo.lstInputs.begin(); itrI != moduleInfo.lstInputs.end(); ++itrI )
+        {
+            this->m_mpItems.insert(std::pair<std::string,oneitem*>((*itrI), poneitem));
+        }
+    }
+    qRegisterMetaType<std::string>("std::string");
+    connect(m_pTDengine,SIGNAL(sigEvent(const std::string, const std::string)),this,SLOT(sltSubData(const std::string, const std::string)));
+}

+ 29 - 0
maincontrol/maincontrol.h

@@ -0,0 +1,29 @@
+#pragma once
+#include "../RedisAgent/RedisAgent.h"
+#include "../TDAgent/TDAgent.h"
+#include "BaseModule.h"
+#include "oneitem.h"
+#include <QtCore/QObject>
+#include <list>
+
+// 模块加载
+class maincontrol : public QObject,public DataConsumer
+{
+    Q_OBJECT
+public:
+    maincontrol();
+
+private:
+    RedisAgent*               m_pRedis;          // 读取配置
+    TDAgent*                  m_pTDengine;       // 订阅发布
+
+    std::multimap<std::string,oneitem*>  m_mpItems;
+    void loadConfig(std::string szFile,std::map<QString,QString>& mps);
+
+public slots:
+    void sltSubData(const std::string, const std::string);
+
+public:
+    virtual void OnData(std::string name,QVariant val);
+    void Startup();
+};

+ 30 - 0
maincontrol/maincontrol.pro

@@ -0,0 +1,30 @@
+QT = core
+QT -= gui
+
+CONFIG += c++17 cmdline
+
+TARGET = maincontrol
+TEMPLATE = app
+
+unix{
+}
+else{
+    DESTDIR = $$PWD/../bin
+}
+
+INCLUDEPATH += .
+INCLUDEPATH += ../include
+
+LIBS += -L$$PWD/../lib/ -lRedisAgent
+LIBS += -L$$PWD/../lib/ -lTDAgent
+
+SOURCES += \
+        main.cpp \
+        maincontrol.cpp \
+        oneitem.cpp \
+        readmodulefromredis.cpp
+
+HEADERS += \
+    maincontrol.h \
+    oneitem.h \
+    readmodulefromredis.h

+ 65 - 0
maincontrol/oneitem.cpp

@@ -0,0 +1,65 @@
+#include "oneitem.h"
+#include "JobModule.h"
+#include "RunnableModule.h"
+#include "LibraryLoader.h"
+#include <QtCore/QDebug>
+#include <typeinfo>
+#include <type_traits>
+
+oneitem::oneitem()
+{
+    m_pDataConsumer = nullptr;
+    m_pModule = nullptr;
+}
+
+void oneitem::OnData(std::string name,QVariant val)
+{
+    // 这是从里往外传的数据(设备控制命令)
+    //源头是模块,我倒一步手,传递给maincontrol里,通过maincontrol写redis
+    if( m_pDataConsumer != nullptr )
+    {
+        m_pDataConsumer->OnData(name,val);
+    }
+}
+
+void oneitem::OnSubData(std::string name,std::string val)
+{
+    // 从td读出来的测量数据,通过maincontrol给到这里
+    // 通过这里在给到模块里
+    if( m_pModule != nullptr )
+    {
+        m_pModule->OnSubData(name,val);
+    }
+}
+
+void oneitem::regConsumer(DataConsumer* pDC)
+{
+    m_pDataConsumer = pDC;
+}
+
+void oneitem::createmodule(const ModuleInfo& mi)
+{
+    std::string assemblyName = mi.AssemblyName;
+    std::string className = mi.ClassName;
+
+    BaseModule* pModule = LibraryLoader::load<BaseModule>(assemblyName);
+    if( pModule == nullptr )
+    {
+        qCritical() << __FILE__ << __LINE__ << " load " << assemblyName.c_str() << " failed.";
+        return;
+    }
+
+    m_pModule = pModule;
+    m_pModule->Setup(mi);
+    m_pModule->regConsumer(this);
+    if( m_pModule->isInheritedFrom("RunnableModule"))
+    {
+        RunnableModule* pRunable = dynamic_cast<RunnableModule*>(m_pModule);
+        pRunable->Run();
+    }
+    if( m_pModule->isInheritedFrom("JobModule"))
+    {
+        JobModule* pJob = dynamic_cast<JobModule*>(m_pModule);
+        pJob->Process(QObject());
+    }
+}

+ 19 - 0
maincontrol/oneitem.h

@@ -0,0 +1,19 @@
+#pragma once
+#include "Define.h"
+#include "BaseModule.h"
+
+class oneitem : public DataConsumer
+{
+public:
+    oneitem();
+
+private:
+    DataConsumer*   m_pDataConsumer;
+    BaseModule*     m_pModule;
+
+public:
+    virtual void OnData(std::string name,QVariant val);
+    virtual void OnSubData(std::string name,std::string val);
+    virtual void regConsumer(DataConsumer* pDC);
+    void createmodule(const ModuleInfo& di);
+};

+ 110 - 0
maincontrol/readmodulefromredis.cpp

@@ -0,0 +1,110 @@
+#include "readmodulefromredis.h"
+#include <QJsonArray>
+#include <QJsonObject>
+#include <QJsonDocument>
+#include <QtCore/QTextCodec>
+#include <fstream>
+
+readmodulefromredis::readmodulefromredis(RedisAgent* redis)
+{
+    m_pRedis = redis;
+}
+
+bool readmodulefromredis::parse(QString cfg,ModuleInfo& moduleInfo)
+{
+    QTextCodec *utf8 = QTextCodec::codecForName("UTF-8");
+    QTextCodec::setCodecForLocale(utf8);
+
+    QJsonParseError err;
+    QJsonDocument jsonDoc(QJsonDocument::fromJson(cfg.toLocal8Bit(),&err));
+    QJsonObject jsonObject = jsonDoc.object();
+    QStringList keys = jsonObject.keys();
+
+    foreach (QString key, keys)
+    {
+        if( key.compare("name",Qt::CaseInsensitive) == 0)
+        {
+            moduleInfo.Name = jsonObject["name"].toString().toLocal8Bit().toStdString();
+        }
+        else if( key.compare("code",Qt::CaseInsensitive) == 0)
+        {
+            moduleInfo.Code = jsonObject["code"].toString().toLocal8Bit().toStdString();
+        }
+        else if( key.compare("type",Qt::CaseInsensitive) == 0)
+        {
+            QString type = jsonObject["type"].toString();
+            moduleInfo.Type = control;
+        }
+        else if( key.compare("model",Qt::CaseInsensitive) == 0)
+        {
+            moduleInfo.AssemblyName = jsonObject["model"].toString().toLocal8Bit().toStdString();
+        }
+        else if( key.compare("serverId",Qt::CaseInsensitive) == 0)
+        {
+            moduleInfo.serverId = jsonObject["serverId"].toInt();
+        }
+        else if( key.compare("settings",Qt::CaseInsensitive) == 0)
+        {
+            QJsonObject jsonSettings = jsonObject["settings"].toObject();
+            QStringList lst = jsonSettings.keys();
+            foreach (QString item, lst)
+            {
+                Setting oSetting;
+                oSetting.Name = item.toLocal8Bit().toStdString();
+                QJsonValue::Type t = jsonSettings[item].type();
+                switch( t )
+                {
+                case QJsonValue::Bool:
+                    oSetting.qValue = jsonSettings[item].toBool();
+                    break;
+                case QJsonValue::Double:
+                    oSetting.qValue = jsonSettings[item].toDouble();
+                    break;
+                case QJsonValue::String:
+                    oSetting.qValue = jsonSettings[item].toString();
+                    break;
+                }
+                moduleInfo.vSettings.push_back(oSetting);
+            }
+        }
+        else if( key.compare("inputs",Qt::CaseInsensitive) == 0)
+        {
+            if( jsonObject["inputs"].isArray() )
+            {
+                QJsonArray arr = jsonObject["inputs"].toArray();
+                for(auto i : arr)
+                {
+                    if( i.type() == QJsonValue::String )
+                    {
+                        moduleInfo.lstInputs.push_back(i.toString().toLocal8Bit().toStdString());
+                    }
+                }
+            }
+        }
+    }
+    return true;
+}
+
+std::list<ModuleInfo> readmodulefromredis::loadModules(QString id)
+{
+    std::list<ModuleInfo> lstModules;
+
+    if( m_pRedis != nullptr)
+    {
+        QStringList lst = m_pRedis->hvals("intelligentcontrol");
+        foreach (QString str, lst) // 我就不取hkey了,那个就是用编号区分一下
+        {
+            if( str.isEmpty() )
+                continue;
+
+            ModuleInfo moduleInfo;
+            if( parse(str,moduleInfo) )
+            {
+                // 判断一下是否这个服务器管理的实例,如果是的话加入
+                if(moduleInfo.serverId == id.toInt())
+                    lstModules.push_back(moduleInfo);
+            }
+        }
+    }
+    return lstModules;
+}

+ 19 - 0
maincontrol/readmodulefromredis.h

@@ -0,0 +1,19 @@
+#pragma once
+#include "../include/Define.h"
+#include "../RedisAgent/RedisAgent.h"
+#include <string>
+#include <list>
+
+// 设备列表管理
+class readmodulefromredis
+{
+public:
+    readmodulefromredis(RedisAgent* redis);
+
+private:
+    RedisAgent*               m_pRedis;          // 读取配置
+    bool parse(QString cfg,ModuleInfo& moduleInfo);
+
+public:
+    std::list<ModuleInfo> loadModules(QString id);
+};