From 06526462409ec0fee6eb50241b34dc962803872d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Tue, 10 Mar 2015 11:13:50 +0100 Subject: [PATCH] added exitActions for state based rules --- server/guhcore.cpp | 6 +-- server/guhcore.h | 2 +- server/jsonrpc/jsontypes.cpp | 9 +++++ server/jsonrpc/ruleshandler.cpp | 27 ++++++++++++- server/rule.cpp | 55 ++++++++++++++++--------- server/rule.h | 4 ++ server/ruleengine.cpp | 72 ++++++++++++++++++++++++++++++--- server/ruleengine.h | 3 +- 8 files changed, 147 insertions(+), 31 deletions(-) diff --git a/server/guhcore.cpp b/server/guhcore.cpp index 08ee25d9..c05ddd29 100644 --- a/server/guhcore.cpp +++ b/server/guhcore.cpp @@ -286,9 +286,9 @@ Rule GuhCore::findRule(const RuleId &ruleId) /*! Calls the metheod RuleEngine::addRule(\a id, \a name, \a eventDescriptorList, \a stateEvaluator \a actionList, \a enabled). * \sa RuleEngine, */ -RuleEngine::RuleError GuhCore::addRule(const RuleId &id, const QString &name, const QList &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList &actionList, bool enabled) +RuleEngine::RuleError GuhCore::addRule(const RuleId &id, const QString &name, const QList &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList &actionList, const QList &exitActionList, bool enabled) { - return m_ruleEngine->addRule(id, name, eventDescriptorList, stateEvaluator, actionList, enabled); + return m_ruleEngine->addRule(id, name, eventDescriptorList, stateEvaluator, actionList, exitActionList, enabled); } /*! Calls the metheod RuleEngine::removeRule(\a id). @@ -381,7 +381,7 @@ void GuhCore::gotEvent(const Event &event) if (rule.active()) { actions.append(rule.actions()); } else { - // TODO: execute state exit actions + actions.append(rule.exitActions()); } } } diff --git a/server/guhcore.h b/server/guhcore.h index dbd9f7e2..8aeac521 100644 --- a/server/guhcore.h +++ b/server/guhcore.h @@ -75,7 +75,7 @@ public: QList rules() const; QList ruleIds() const; Rule findRule(const RuleId &ruleId); - RuleEngine::RuleError addRule(const RuleId &id, const QString &name, const QList &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList &actionList, bool enabled = true); + RuleEngine::RuleError addRule(const RuleId &id, const QString &name, const QList &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList &actionList, const QList &exitActionList, bool enabled = true); RuleEngine::RuleError removeRule(const RuleId &id); QList findRules(const DeviceId &deviceId); RuleEngine::RuleError enableRule(const RuleId &ruleId); diff --git a/server/jsonrpc/jsontypes.cpp b/server/jsonrpc/jsontypes.cpp index b7a3434f..d5ab21c0 100644 --- a/server/jsonrpc/jsontypes.cpp +++ b/server/jsonrpc/jsontypes.cpp @@ -185,6 +185,7 @@ void JsonTypes::init() s_rule.insert("enabled", basicTypeToString(Bool)); s_rule.insert("eventDescriptors", QVariantList() << eventDescriptorRef()); s_rule.insert("actions", QVariantList() << actionRef()); + s_rule.insert("exitActions", QVariantList() << actionRef()); s_rule.insert("stateEvaluator", stateEvaluatorRef()); // LogEntry @@ -495,6 +496,14 @@ QVariantMap JsonTypes::packRule(const Rule &rule) foreach (const Action &action, rule.actions()) { actionList.append(JsonTypes::packAction(action)); } + if (!rule.exitActions().isEmpty()) { + QVariantList exitActionList; + foreach (const Action &action, rule.exitActions()) { + exitActionList.append(JsonTypes::packAction(action)); + } + ruleMap.insert("exitActions", exitActionList); + } + ruleMap.insert("actions", actionList); ruleMap.insert("stateEvaluator", JsonTypes::packStateEvaluator(rule.stateEvaluator())); return ruleMap; diff --git a/server/jsonrpc/ruleshandler.cpp b/server/jsonrpc/ruleshandler.cpp index bec5ef67..f4381510 100644 --- a/server/jsonrpc/ruleshandler.cpp +++ b/server/jsonrpc/ruleshandler.cpp @@ -50,6 +50,7 @@ RulesHandler::RulesHandler(QObject *parent) : params.insert("o:eventDescriptor", JsonTypes::eventDescriptorRef()); params.insert("o:eventDescriptorList", QVariantList() << JsonTypes::eventDescriptorRef()); params.insert("o:stateEvaluator", JsonTypes::stateEvaluatorRef()); + params.insert("o:exitActions", QVariantList() << JsonTypes::actionRef()); params.insert("o:enabled", JsonTypes::basicTypeToString(JsonTypes::Bool)); params.insert("name", JsonTypes::basicTypeToString(JsonTypes::String)); QVariantList actions; @@ -129,6 +130,16 @@ JsonReply* RulesHandler::AddRule(const QVariantMap ¶ms) return createReply(returns); } + if (params.contains("eventDescriptor") || params.contains("eventDescriptorList")) { + if (params.contains("exitActions")) { + QVariantMap returns; + qWarning() << "The exitActions will never executed if this rule contains any eventDescriptor."; + returns.insert("ruleError", JsonTypes::ruleErrorToString(RuleEngine::RuleErrorInvalidRuleFormat)); + return createReply(returns); + } + } + + QList eventDescriptorList; if (params.contains("eventDescriptor")) { QVariantMap eventMap = params.value("eventDescriptor").toMap(); @@ -160,11 +171,25 @@ JsonReply* RulesHandler::AddRule(const QVariantMap ¶ms) return createReply(returns); } + QList exitActions; + if (params.contains("exitActions")) { + QVariantList exitActionList = params.value("exitActions").toList(); + foreach (const QVariant &actionVariant, exitActionList) { + QVariantMap actionMap = actionVariant.toMap(); + Action action(ActionTypeId(actionMap.value("actionTypeId").toString()), DeviceId(actionMap.value("deviceId").toString())); + qDebug() << "params from json" << actionMap.value("params"); + action.setParams(JsonTypes::unpackParams(actionMap.value("params").toList())); + qDebug() << "params in action" << action.params(); + exitActions.append(action); + } + } + + QString name = params.value("name", QString()).toString(); bool enabled = params.value("enabled", true).toBool(); RuleId newRuleId = RuleId::createRuleId(); - RuleEngine::RuleError status = GuhCore::instance()->addRule(newRuleId, name, eventDescriptorList, stateEvaluator, actions, enabled); + RuleEngine::RuleError status = GuhCore::instance()->addRule(newRuleId, name, eventDescriptorList, stateEvaluator, actions, exitActions, enabled); if (status == RuleEngine::RuleErrorNoError) { returns.insert("ruleId", newRuleId.toString()); } diff --git a/server/rule.cpp b/server/rule.cpp index dcd35bac..00bdb067 100644 --- a/server/rule.cpp +++ b/server/rule.cpp @@ -29,35 +29,18 @@ \sa EventDescriptor, State, Action */ -// Additionally a Rule is either of type \l{Rule::RuleTypeAll} or \l{Rule::RuleTypeAny} -// which determines if all or any of the \l{State}{States} must be matching -// in order for the \l{Action}{Actions} to be executed. - - -//! \enum Rule::RuleType - -// Note: There is no RuleTypeNone. If you don't want to compare any -// states, construct a rule without states in which case it doesn't -// matter what the Rule's type is. - -// \value RuleTypeAll -// All States must match in order for the Rule to apply. -// \value RuleTypeAny -// Any State must match in order for the Rule to apply. - - #include "rule.h" #include /*! Constructs an empty, invalid rule. */ Rule::Rule(): - Rule(RuleId(), QString(), QList(), StateEvaluator(), QList()) + Rule(RuleId(), QString(), QList(), StateEvaluator(), QList(), QList()) { } /*! Constructs a Rule with the given \a id, \a name, \a eventDescriptorList, \a stateEvaluator and \a actions.*/ -Rule::Rule(const RuleId &id, const QString &name, const QList &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList &actions): +Rule::Rule(const RuleId &id, const QString &name, const QList &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList &actions) : m_id(id), m_name(name), m_eventDescriptors(eventDescriptorList), @@ -66,6 +49,34 @@ Rule::Rule(const RuleId &id, const QString &name, const QList & m_enabled(false), m_active(false) { + +} + +/*! Constructs a Rule with the given \a id, \a name, \a eventDescriptorList, \a stateEvaluator, \a actions and exitActions.*/ +Rule::Rule(const RuleId &id, const QString &name, const QList &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList &actions, const QList &exitActions): + m_id(id), + m_name(name), + m_eventDescriptors(eventDescriptorList), + m_stateEvaluator(stateEvaluator), + m_actions(actions), + m_exitActions(exitActions), + m_enabled(false), + m_active(false) +{ +} + +/*! Constructs a Rule with the given \a id, \a name, \a stateEvaluator, \a actions and \a exitActions. This type of rule + * works only state based and executes the \a actions once the rule enters the active state and executes the \a exitActions + * once the rule exits the active state.*/ +Rule::Rule(const RuleId &id, const QString &name, const StateEvaluator &stateEvaluator, const QList &actions, const QList &exitActions) : + m_id(id), + m_name(name), + m_stateEvaluator(stateEvaluator), + m_actions(actions), + m_exitActions(exitActions), + m_enabled(false), + m_active(false) +{ } /*! Returns the id or the Rule. */ @@ -92,6 +103,12 @@ QList Rule::actions() const return m_actions; } +/*! Returns the \l{Action}{Actions} to be executed when this Rule leaves the active state. */ +QList Rule::exitActions() const +{ + return m_exitActions; +} + /*! Returns the name of this rule. */ QString Rule::name() const { diff --git a/server/rule.h b/server/rule.h index f834710d..0867f77e 100644 --- a/server/rule.h +++ b/server/rule.h @@ -31,11 +31,14 @@ class Rule public: Rule(); Rule(const RuleId &id, const QString &name, const QList &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList &actions); + Rule(const RuleId &id, const QString &name, const QList &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList &actions, const QList &exitActions); + Rule(const RuleId &id, const QString &name, const StateEvaluator &stateEvaluator, const QList &actions, const QList &exitActions); RuleId id() const; QList eventDescriptors() const; StateEvaluator stateEvaluator() const; QList actions() const; + QList exitActions() const; QString name() const; bool enabled() const; @@ -54,6 +57,7 @@ private: QList m_eventDescriptors; StateEvaluator m_stateEvaluator; QList m_actions; + QList m_exitActions; bool m_enabled; bool m_active; diff --git a/server/ruleengine.cpp b/server/ruleengine.cpp index 03cff9b5..f8634577 100644 --- a/server/ruleengine.cpp +++ b/server/ruleengine.cpp @@ -127,7 +127,6 @@ RuleEngine::RuleEngine(QObject *parent) : settings.endGroup(); StateEvaluator stateEvaluator = StateEvaluator::loadFromSettings(settings, "stateEvaluator"); - settings.beginGroup("actions"); QList actions; @@ -150,13 +149,33 @@ RuleEngine::RuleEngine(QObject *parent) : } settings.endGroup(); + settings.beginGroup("exitActions"); + QList exitActions; + foreach (const QString &actionIdString, settings.childGroups()) { + settings.beginGroup(actionIdString); + Action action = Action(ActionTypeId(settings.value("actionTypeId").toString()), DeviceId(settings.value("deviceId").toString())); + ParamList params; + foreach (QString paramNameString, settings.childGroups()) { + if (paramNameString.startsWith("Param-")) { + settings.beginGroup(paramNameString); + Param param(paramNameString.remove(QRegExp("^Param-")), settings.value("value")); + params.append(param); + settings.endGroup(); + } + } + action.setParams(params); + + settings.endGroup(); + exitActions.append(action); + } settings.endGroup(); - Rule rule = Rule(RuleId(idString), name, eventDescriptorList, stateEvaluator, actions); + settings.endGroup(); + + Rule rule = Rule(RuleId(idString), name, eventDescriptorList, stateEvaluator, actions, exitActions); rule.setEnabled(enabled); appendRule(rule); } - } /*! Ask the Engine to evaluate all the rules for the given \a event. @@ -216,11 +235,11 @@ QList RuleEngine::evaluateEvent(const Event &event) For convenience, this creates a Rule without any \l{State} comparison. */ RuleEngine::RuleError RuleEngine::addRule(const RuleId &ruleId, const QString &name, const QList &eventDescriptorList, const QList &actions, bool enabled) { - return addRule(ruleId, name, eventDescriptorList, StateEvaluator(), actions, enabled); + return addRule(ruleId, name, eventDescriptorList, StateEvaluator(), actions, QList(), enabled); } /*! Add a new \l{Rule} with the given \a ruleId, \a name, \a eventDescriptorList, \a stateEvaluator, the list of \a actions and the \a enabled value to the engine.*/ -RuleEngine::RuleError RuleEngine::addRule(const RuleId &ruleId, const QString &name, const QList &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList &actions, bool enabled) +RuleEngine::RuleError RuleEngine::addRule(const RuleId &ruleId, const QString &name, const QList &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList &actions, const QList &exitActions, bool enabled) { if (ruleId.isNull()) { return RuleErrorInvalidRuleId; @@ -229,6 +248,9 @@ RuleEngine::RuleError RuleEngine::addRule(const RuleId &ruleId, const QString &n qWarning() << "Already have a rule with this id!"; return RuleErrorInvalidRuleId; } + + // TODO: check rule name for duplicated rulesnames + foreach (const EventDescriptor &eventDescriptor, eventDescriptorList) { Device *device = GuhCore::instance()->findConfiguredDevice(eventDescriptor.deviceId()); if (!device) { @@ -271,7 +293,31 @@ RuleEngine::RuleError RuleEngine::addRule(const RuleId &ruleId, const QString &n if (actions.count() > 0) { qDebug() << "***** actions" << actions.last().actionTypeId() << actions.last().params(); } - Rule rule = Rule(ruleId, name, eventDescriptorList, stateEvaluator, actions); + + foreach (const Action &action, exitActions) { + Device *device = GuhCore::instance()->findConfiguredDevice(action.deviceId()); + if (!device) { + qWarning() << "Cannot create rule. No configured device for actionTypeId" << action.actionTypeId(); + return RuleErrorDeviceNotFound; + } + DeviceClass deviceClass = GuhCore::instance()->findDeviceClass(device->deviceClassId()); + + bool actionTypeFound = false; + foreach (const ActionType &actionType, deviceClass.actionTypes()) { + if (actionType.id() == action.actionTypeId()) { + actionTypeFound = true; + } + } + if (!actionTypeFound) { + qWarning() << "Cannot create rule. Device " + device->name() + " has no action type:" << action.actionTypeId(); + return RuleErrorActionTypeNotFound; + } + } + if (exitActions.count() > 0) { + qDebug() << "***** exitActions" << actions.last().actionTypeId() << actions.last().params(); + } + + Rule rule = Rule(ruleId, name, eventDescriptorList, stateEvaluator, actions, exitActions); rule.setEnabled(enabled); appendRule(rule); emit ruleAdded(rule.id()); @@ -309,7 +355,21 @@ RuleEngine::RuleError RuleEngine::addRule(const RuleId &ruleId, const QString &n settings.setValue("value", param.value()); settings.endGroup(); } + settings.endGroup(); + } + settings.endGroup(); + + settings.beginGroup("exitActions"); + foreach (const Action &action, rule.exitActions()) { + settings.beginGroup(action.actionTypeId().toString()); + settings.setValue("deviceId", action.deviceId()); + settings.setValue("actionTypeId", action.actionTypeId()); + foreach (const Param ¶m, action.params()) { + settings.beginGroup("Param-" + param.name()); + settings.setValue("value", param.value()); + settings.endGroup(); + } settings.endGroup(); } diff --git a/server/ruleengine.h b/server/ruleengine.h index 15140af3..9905dbd8 100644 --- a/server/ruleengine.h +++ b/server/ruleengine.h @@ -41,6 +41,7 @@ public: RuleErrorEventTypeNotFound, RuleErrorActionTypeNotFound, RuleErrorInvalidParameter, + RuleErrorInvalidRuleFormat, RuleErrorMissingParameter }; @@ -54,7 +55,7 @@ public: QList evaluateEvent(const Event &event); RuleError addRule(const RuleId &ruleId, const QString &name, const QList &eventDescriptorList, const QList &actions, bool enabled = true); - RuleError addRule(const RuleId &ruleId, const QString &name, const QList &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList &actions, bool enabled = true); + RuleError addRule(const RuleId &ruleId, const QString &name, const QList &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList &actions, const QList &exitActions, bool enabled = true); QList rules() const; QList ruleIds() const;