From e30a5333916814615938f32d6e374c453a1bca01 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Thu, 2 Jan 2014 00:54:47 +0100 Subject: [PATCH] add simple rule engine --- libhive/action.cpp | 5 +++ libhive/action.h | 3 +- libhive/devicemanager.cpp | 24 +++++++++++++ libhive/devicemanager.h | 4 +++ libhive/trigger.cpp | 5 +++ libhive/trigger.h | 3 +- server/hivecore.cpp | 21 ++++++++++-- server/hivecore.h | 5 +++ server/jsonrpcserver.cpp | 41 ++++++++++++++++++++-- server/jsonrpcserver.h | 2 ++ server/rule.cpp | 23 +++++++++++++ server/rule.h | 21 ++++++++++++ server/ruleengine.cpp | 64 +++++++++++++++++++++++++++++++++++ server/ruleengine.h | 32 ++++++++++++++++++ server/server.pro | 4 +++ tests/addrule.sh | 7 ++++ tests/getconfigureddevices.sh | 6 +++- tests/getrules.sh | 7 ++++ 18 files changed, 269 insertions(+), 8 deletions(-) create mode 100644 server/rule.cpp create mode 100644 server/rule.h create mode 100644 server/ruleengine.cpp create mode 100644 server/ruleengine.h create mode 100755 tests/addrule.sh create mode 100755 tests/getrules.sh diff --git a/libhive/action.cpp b/libhive/action.cpp index 838bb113..ca1d7eb6 100644 --- a/libhive/action.cpp +++ b/libhive/action.cpp @@ -5,6 +5,11 @@ Action::Action(const QUuid &id) : { } +bool Action::isValid() const +{ + return !m_id.isNull(); +} + QUuid Action::id() const { return m_id; diff --git a/libhive/action.h b/libhive/action.h index 99dd34e0..87d17495 100644 --- a/libhive/action.h +++ b/libhive/action.h @@ -7,7 +7,8 @@ class Action { public: - Action(const QUuid &id); + Action(const QUuid &id = QUuid()); + bool isValid() const; QUuid id() const; diff --git a/libhive/devicemanager.cpp b/libhive/devicemanager.cpp index c6570048..b48e7b90 100644 --- a/libhive/devicemanager.cpp +++ b/libhive/devicemanager.cpp @@ -95,6 +95,30 @@ DeviceClass DeviceManager::findDeviceClass(const QUuid &deviceClassId) return DeviceClass(QUuid(), QUuid()); } +Trigger DeviceManager::findTrigger(const QUuid &triggerId) +{ + foreach (Device *device, m_configuredDevices) { + foreach (const Trigger &trigger, device->triggers()) { + if (trigger.id() == triggerId) { + return trigger; + } + } + } + return Trigger(); +} + +Action DeviceManager::findAction(const QUuid &actionId) +{ + foreach (Device *device, m_configuredDevices) { + foreach (const Action &action, device->actions()) { + if (action.id() == actionId) { + return action; + } + } + } + return Action(); +} + Radio433 *DeviceManager::radio433() const { return m_radio433; diff --git a/libhive/devicemanager.h b/libhive/devicemanager.h index 33661005..5fcf6898 100644 --- a/libhive/devicemanager.h +++ b/libhive/devicemanager.h @@ -3,6 +3,7 @@ #include "deviceclass.h" #include "trigger.h" +#include "action.h" #include @@ -32,6 +33,9 @@ public: QList findConfiguredDevices(const QUuid &deviceClassId); DeviceClass findDeviceClass(const QUuid &deviceClassId); + Trigger findTrigger(const QUuid &triggerId); + Action findAction(const QUuid &actionId); + Radio433 *radio433() const; signals: diff --git a/libhive/trigger.cpp b/libhive/trigger.cpp index 7b324ea6..b65c6f69 100644 --- a/libhive/trigger.cpp +++ b/libhive/trigger.cpp @@ -5,6 +5,11 @@ Trigger::Trigger(const QUuid &id): { } +bool Trigger::isValid() const +{ + return !m_id.isNull(); +} + QUuid Trigger::id() const { return m_id; diff --git a/libhive/trigger.h b/libhive/trigger.h index 202ed46a..d7e89833 100644 --- a/libhive/trigger.h +++ b/libhive/trigger.h @@ -8,7 +8,8 @@ class Trigger { public: - Trigger(const QUuid &id); + Trigger(const QUuid &id = QUuid()); + bool isValid() const; QUuid id() const; diff --git a/server/hivecore.cpp b/server/hivecore.cpp index 51bb620f..0159de4c 100644 --- a/server/hivecore.cpp +++ b/server/hivecore.cpp @@ -1,6 +1,7 @@ #include "hivecore.h" #include "jsonrpcserver.h" #include "devicemanager.h" +#include "ruleengine.h" #include @@ -19,15 +20,28 @@ DeviceManager *HiveCore::deviceManager() const return m_deviceManager; } +RuleEngine *HiveCore::ruleEngine() const +{ + return m_ruleEngine; +} + HiveCore::HiveCore(QObject *parent) : QObject(parent) { - qDebug() << "creating devmanager"; + qDebug() << "*****************************************"; + qDebug() << "* Creating Device Manager *"; + qDebug() << "*****************************************"; m_deviceManager = new DeviceManager(this); + qDebug() << "*****************************************"; + qDebug() << "* Creating Rule Engine *"; + qDebug() << "*****************************************"; + m_ruleEngine = new RuleEngine(this); - // start the server + qDebug() << "*****************************************"; + qDebug() << "* Starting JSON RPC Server *"; + qDebug() << "*****************************************"; m_jsonServer = new JsonRPCServer(this); connect(m_deviceManager,SIGNAL(emitTrigger(QUuid,QVariantMap)),this,SLOT(gotSignal(QUuid,QVariantMap))); @@ -40,4 +54,7 @@ void HiveCore::gotSignal(const QUuid &triggerId, const QVariantMap ¶ms) qDebug() << "id: " << triggerId; qDebug() << params; + foreach (const QUuid &actionId, m_ruleEngine->evaluateTrigger(triggerId)) { + m_deviceManager->executeAction(actionId, params); + } } diff --git a/server/hivecore.h b/server/hivecore.h index 86eb2d50..0edcd13b 100644 --- a/server/hivecore.h +++ b/server/hivecore.h @@ -1,10 +1,13 @@ #ifndef HIVECORE_H #define HIVECORE_H +#include "rule.h" + #include class JsonRPCServer; class DeviceManager; +class RuleEngine; class HiveCore : public QObject { @@ -13,6 +16,7 @@ public: static HiveCore* instance(); DeviceManager* deviceManager() const; + RuleEngine *ruleEngine() const; private: explicit HiveCore(QObject *parent = 0); @@ -20,6 +24,7 @@ private: JsonRPCServer *m_jsonServer; DeviceManager *m_deviceManager; + RuleEngine *m_ruleEngine; private slots: void gotSignal(const QUuid &triggerId, const QVariantMap ¶ms); diff --git a/server/jsonrpcserver.cpp b/server/jsonrpcserver.cpp index 255583e1..c747423e 100644 --- a/server/jsonrpcserver.cpp +++ b/server/jsonrpcserver.cpp @@ -6,6 +6,8 @@ #include "devicemanager.h" #include "deviceclass.h" #include "device.h" +#include "rule.h" +#include "ruleengine.h" #include #include @@ -75,21 +77,54 @@ void JsonRPCServer::processData(int clientId, const QByteArray &jsonData) break; } } else if (method == "GetConfiguredDevices") { - QVariantMap params; + QVariantMap rspParams; QVariantList configuredDeviceList; foreach (Device *device, HiveCore::instance()->deviceManager()->configuredDevices()) { configuredDeviceList.append(packDevice(device)); } - params.insert("devices", configuredDeviceList); - sendResponse(clientId, commandId, params); + rspParams.insert("devices", configuredDeviceList); + sendResponse(clientId, commandId, rspParams); } else { sendErrorResponse(clientId, commandId, "No such method"); } + } else if (targetNamspace == "Rules") { + handleRulesMessage(clientId, commandId, method, params); } else { qDebug() << "got unknown namespace" << targetNamspace; } } +void JsonRPCServer::handleRulesMessage(int clientId, int commandId, const QString &method, const QVariantMap ¶ms) +{ + if (method == "GetRules") { + QVariantList rulesList; + foreach (const Rule &rule, HiveCore::instance()->ruleEngine()->rules()) { + QVariantMap ruleMap; + ruleMap.insert("id", rule.id()); + ruleMap.insert("triggerId", rule.triggerId()); + ruleMap.insert("actionId", rule.actionId()); + rulesList.append(ruleMap); + } + QVariantMap rspParams; + rspParams.insert("rules", rulesList); + sendResponse(clientId, commandId, rspParams); + } else if (method == "AddRule") { + QUuid triggerId = params.value("triggerId").toUuid(); + QUuid actionId = params.value("actionId").toUuid(); + switch(HiveCore::instance()->ruleEngine()->addRule(triggerId, actionId)) { + case RuleEngine::RuleErrorNoError: + sendResponse(clientId, commandId); + break; + case RuleEngine::RuleErrorNoSuchTrigger: + sendErrorResponse(clientId, commandId, "No such trigger"); + break; + case RuleEngine::RuleErrorNoSuchAction: + sendErrorResponse(clientId, commandId, "No such action"); + break; + } + } +} + QVariantMap JsonRPCServer::packDeviceClass(const DeviceClass &deviceClass) { QVariantMap variant; diff --git a/server/jsonrpcserver.h b/server/jsonrpcserver.h index 0dc19ef1..5ccd6225 100644 --- a/server/jsonrpcserver.h +++ b/server/jsonrpcserver.h @@ -23,6 +23,8 @@ private slots: void processData(int clientId, const QByteArray &jsonData); private: + void handleRulesMessage(int clientId, int commandId, const QString &method, const QVariantMap ¶ms); + QVariantMap packDeviceClass(const DeviceClass &deviceClass); QVariantMap packDevice(Device *device); diff --git a/server/rule.cpp b/server/rule.cpp new file mode 100644 index 00000000..e687b470 --- /dev/null +++ b/server/rule.cpp @@ -0,0 +1,23 @@ +#include "rule.h" + +Rule::Rule(const QUuid &id, const QUuid &triggerId, const QUuid &actionId): + m_id(id), + m_triggerId(triggerId), + m_actionId(actionId) +{ +} + +QUuid Rule::id() const +{ + return m_id; +} + +QUuid Rule::triggerId() const +{ + return m_triggerId; +} + +QUuid Rule::actionId() const +{ + return m_actionId; +} diff --git a/server/rule.h b/server/rule.h new file mode 100644 index 00000000..841db966 --- /dev/null +++ b/server/rule.h @@ -0,0 +1,21 @@ +#ifndef RULE_H +#define RULE_H + +#include + +class Rule +{ +public: + Rule(const QUuid &id, const QUuid &triggerId, const QUuid &actionId); + + QUuid id() const; + QUuid triggerId() const; + QUuid actionId() const; + +private: + QUuid m_id; + QUuid m_triggerId; + QUuid m_actionId; +}; + +#endif // RULE_H diff --git a/server/ruleengine.cpp b/server/ruleengine.cpp new file mode 100644 index 00000000..58ff365c --- /dev/null +++ b/server/ruleengine.cpp @@ -0,0 +1,64 @@ +#include "ruleengine.h" + +#include "hivecore.h" +#include "devicemanager.h" + +#include +#include +#include +#include + +QString rulesFileName = "hiveyourhome/rules"; + +RuleEngine::RuleEngine(QObject *parent) : + QObject(parent) +{ + QSettings settings(rulesFileName); + qDebug() << "loading rules from" << rulesFileName; + foreach (const QString &idString, settings.childGroups()) { + qDebug() << "found rule" << idString; + settings.beginGroup(idString); + Rule rule = Rule(QUuid(idString), settings.value("triggerId").toUuid(), settings.value("actionId").toUuid()); + settings.endGroup(); + m_rules.append(rule); + } + +} + +QList RuleEngine::evaluateTrigger(const QUuid &triggerId) +{ + QList actions; + for (int i = 0; i < m_rules.count(); ++i) { + if (m_rules.at(i).triggerId() == triggerId) { + actions << m_rules.at(i).actionId(); + } + } + return actions; +} + +RuleEngine::RuleError RuleEngine::addRule(const QUuid &triggerId, const QUuid &actionId) +{ + if (!HiveCore::instance()->deviceManager()->findTrigger(triggerId).isValid()) { + qWarning() << "Cannot create rule. No such trigger."; + return RuleErrorNoSuchTrigger; + } + if (!HiveCore::instance()->deviceManager()->findAction(actionId).isValid()) { + qWarning() << "Cannot create rule. No such action."; + return RuleErrorNoSuchAction; + } + + Rule rule = Rule(QUuid::createUuid(), triggerId, actionId); + m_rules.append(rule); + + QSettings settings(rulesFileName); + settings.beginGroup(rule.id().toString()); + settings.setValue("triggerId", rule.triggerId()); + settings.setValue("actionId", rule.actionId()); + + return RuleErrorNoError; +} + +QList RuleEngine::rules() const +{ + return m_rules; +} diff --git a/server/ruleengine.h b/server/ruleengine.h new file mode 100644 index 00000000..fde2a403 --- /dev/null +++ b/server/ruleengine.h @@ -0,0 +1,32 @@ +#ifndef RULEENGINE_H +#define RULEENGINE_H + +#include "rule.h" + +#include +#include +#include + +class RuleEngine : public QObject +{ + Q_OBJECT +public: + enum RuleError { + RuleErrorNoError, + RuleErrorNoSuchTrigger, + RuleErrorNoSuchAction + }; + + explicit RuleEngine(QObject *parent = 0); + + QList evaluateTrigger(const QUuid &triggerId); + + RuleError addRule(const QUuid &triggerId, const QUuid &actionId); + QList rules() const; + +private: + QList m_rules; + +}; + +#endif // RULEENGINE_H diff --git a/server/server.pro b/server/server.pro index ced8679c..938da9b8 100644 --- a/server/server.pro +++ b/server/server.pro @@ -14,10 +14,14 @@ SOURCES += main.cpp \ hivecore.cpp \ jsonrpcserver.cpp \ tcpserver.cpp \ + ruleengine.cpp \ + rule.cpp HEADERS += hivecore.h \ jsonrpcserver.h \ tcpserver.h \ + ruleengine.h \ + rule.h # FIXME: Drop this and link them dynamically LIBS += -L../plugins/deviceplugins/rfremotemumbi/ -lhive_rfremotemumbi diff --git a/tests/addrule.sh b/tests/addrule.sh new file mode 100755 index 00000000..6c57de7a --- /dev/null +++ b/tests/addrule.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +if test -z $3; then + echo "usage: $1 host triggerId actionId" +else + (echo '{"id":1, "method":"Rules.AddRule", "params":{"triggerId": "$2", "actionId":"$3" }}'; sleep 1) | nc $1 1234 +fi diff --git a/tests/getconfigureddevices.sh b/tests/getconfigureddevices.sh index 4ca259f3..86058709 100755 --- a/tests/getconfigureddevices.sh +++ b/tests/getconfigureddevices.sh @@ -1,3 +1,7 @@ #!/bin/bash -(echo '{"id":1, "method":"Devices.GetConfiguredDevices"}'; sleep 1) | nc 10.10.10.114 1234 +if [ -z $1 ]; then + echo "usage: $0 host" +else + (echo '{"id":1, "method":"Devices.GetConfiguredDevices"}'; sleep 1) | nc $1 1234 +fi diff --git a/tests/getrules.sh b/tests/getrules.sh new file mode 100755 index 00000000..f365f6e8 --- /dev/null +++ b/tests/getrules.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +if [ -z $1 ]; then + echo "usage: $0 host" +else + (echo '{"id":1, "method":"Rules.GetRules"}'; sleep 1) | nc $1 1234 +fi