From 85ba7af29ca2ac6b4aebc27d5400a9e974de7db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 29 Jun 2015 12:58:35 +0200 Subject: [PATCH] add Rules.EditRule method and Rules.CRuleConfigurationChanged notification --- guh.pri | 2 +- server/guhcore.cpp | 6 + server/guhcore.h | 2 + server/jsonrpc/ruleshandler.cpp | 341 ++++++++++++++++++++++---------- server/jsonrpc/ruleshandler.h | 8 + server/ruleengine.cpp | 189 ++++++++++-------- server/ruleengine.h | 10 +- 7 files changed, 369 insertions(+), 189 deletions(-) diff --git a/guh.pri b/guh.pri index 6e39a50e..9ca85cfb 100644 --- a/guh.pri +++ b/guh.pri @@ -2,7 +2,7 @@ GUH_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"') # define JSON protocol version -JSON_PROTOCOL_VERSION=26 +JSON_PROTOCOL_VERSION=27 DEFINES += GUH_VERSION_STRING=\\\"$${GUH_VERSION_STRING}\\\" JSON_PROTOCOL_VERSION=\\\"$${JSON_PROTOCOL_VERSION}\\\" diff --git a/server/guhcore.cpp b/server/guhcore.cpp index db68ac91..33c57de8 100644 --- a/server/guhcore.cpp +++ b/server/guhcore.cpp @@ -339,6 +339,11 @@ RuleEngine::RuleError GuhCore::addRule(const RuleId &id, const QString &name, co return m_ruleEngine->addRule(id, name, eventDescriptorList, stateEvaluator, actionList, exitActionList, enabled); } +RuleEngine::RuleError GuhCore::editRule(const RuleId &id, const QString &name, const QList &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList &actionList, const QList &exitActionList, bool enabled) +{ + return m_ruleEngine->editRule(id, name, eventDescriptorList, stateEvaluator, actionList, exitActionList, enabled); +} + /*! Calls the metheod RuleEngine::removeRule(\a id). * \sa RuleEngine, */ RuleEngine::RuleError GuhCore::removeRule(const RuleId &id) @@ -406,6 +411,7 @@ GuhCore::GuhCore(QObject *parent) : connect(m_ruleEngine, &RuleEngine::ruleAdded, this, &GuhCore::ruleAdded); connect(m_ruleEngine, &RuleEngine::ruleRemoved, this, &GuhCore::ruleRemoved); + connect(m_ruleEngine, &RuleEngine::ruleConfigurationChanged, this, &GuhCore::ruleConfigurationChanged); m_logger->logSystemEvent(true); } diff --git a/server/guhcore.h b/server/guhcore.h index a71810c8..12d04dcb 100644 --- a/server/guhcore.h +++ b/server/guhcore.h @@ -82,6 +82,7 @@ public: 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, const QList &exitActionList, bool enabled = true); + RuleEngine::RuleError editRule(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); @@ -105,6 +106,7 @@ signals: void ruleRemoved(const RuleId &ruleId); void ruleAdded(const Rule &rule); void ruleActiveChanged(const Rule &rule); + void ruleConfigurationChanged(const Rule &rule); private: diff --git a/server/jsonrpc/ruleshandler.cpp b/server/jsonrpc/ruleshandler.cpp index fc77879e..6d3381a3 100644 --- a/server/jsonrpc/ruleshandler.cpp +++ b/server/jsonrpc/ruleshandler.cpp @@ -66,6 +66,25 @@ RulesHandler::RulesHandler(QObject *parent) : returns.insert("o:ruleId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); setReturns("AddRule", returns); + params.clear(); returns.clear(); actions.clear(); + setDescription("EditRule", "Edit the parameters of a rule. The configuration of the rule with the given ruleId " + "will be replaced with the new given configuration. In ordert to enable or disable a Rule, please use the " + "methods \"Rules.EnableRule\" and \"Rules.DisableRule\". If successfull, the notification \"Rule.RuleConfigurationChanged\" " + "will be emitted."); + params.insert("ruleId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); + params.insert("name", JsonTypes::basicTypeToString(JsonTypes::String)); + 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::ruleActionRef()); + params.insert("o:enabled", JsonTypes::basicTypeToString(JsonTypes::Bool)); + actions.append(JsonTypes::ruleActionRef()); + params.insert("actions", actions); + setParams("EditRule", params); + returns.insert("ruleError", JsonTypes::ruleErrorRef()); + returns.insert("o:rule", JsonTypes::ruleRef()); + setReturns("EditRule", returns); + params.clear(); returns.clear(); setDescription("RemoveRule", "Remove a rule"); params.insert("ruleId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); @@ -81,14 +100,16 @@ RulesHandler::RulesHandler(QObject *parent) : setReturns("FindRules", returns); params.clear(); returns.clear(); - setDescription("EnableRule", "Enabled a rule that has previously been disabled."); + setDescription("EnableRule", "Enabled a rule that has previously been disabled." + "If successfull, the notification \"Rule.RuleConfigurationChanged\" will be emitted."); params.insert("ruleId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); setParams("EnableRule", params); returns.insert("ruleError", JsonTypes::ruleErrorRef()); setReturns("EnableRule", returns); params.clear(); returns.clear(); - setDescription("DisableRule", "Disable a rule. The rule won't be triggered by it's events or state changes while it is disabled."); + setDescription("DisableRule", "Disable a rule. The rule won't be triggered by it's events or state changes while it is disabled. " + "If successfull, the notification \"Rule.RuleConfigurationChanged\" will be emitted."); params.insert("ruleId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); setParams("DisableRule", params); returns.insert("ruleError", JsonTypes::ruleErrorRef()); @@ -111,9 +132,15 @@ RulesHandler::RulesHandler(QObject *parent) : params.insert("active", JsonTypes::basicTypeToString(JsonTypes::Bool)); setParams("RuleActiveChanged", params); + params.clear(); returns.clear(); + setDescription("RuleConfigurationChanged", "Emitted whenever the configuration of a Rule changed."); + params.insert("rule", JsonTypes::ruleRef()); + setParams("RuleConfigurationChanged", params); + connect(GuhCore::instance(), &GuhCore::ruleAdded, this, &RulesHandler::ruleAddedNotification); connect(GuhCore::instance(), &GuhCore::ruleRemoved, this, &RulesHandler::ruleRemovedNotification); connect(GuhCore::instance(), &GuhCore::ruleActiveChanged, this, &RulesHandler::ruleActiveChangedNotification); + connect(GuhCore::instance(), &GuhCore::ruleConfigurationChanged, this, &RulesHandler::ruleConfigurationChangedNotification); } QString RulesHandler::name() const @@ -149,35 +176,76 @@ JsonReply *RulesHandler::GetRuleDetails(const QVariantMap ¶ms) JsonReply* RulesHandler::AddRule(const QVariantMap ¶ms) { - if (params.contains("eventDescriptor") && params.contains("eventDescriptorList")) { + // check rule consistency + RuleEngine::RuleError ruleConsistencyError = verifyRuleConsistency(params); + if (ruleConsistencyError != RuleEngine::RuleErrorNoError) { QVariantMap returns; - qCWarning(dcJsonRpc) << "Only one of eventDesciptor or eventDescriptorList may be used."; - returns.insert("ruleError", JsonTypes::ruleErrorToString(RuleEngine::RuleErrorInvalidParameter)); + returns.insert("ruleError", JsonTypes::ruleErrorToString(ruleConsistencyError)); return createReply(returns); } - if (params.contains("eventDescriptor") || params.contains("eventDescriptorList")) { - if (params.contains("exitActions")) { - QVariantMap returns; - qCWarning(dcJsonRpc) << "The exitActions will never be executed if the rule contains an eventDescriptor."; - returns.insert("ruleError", JsonTypes::ruleErrorToString(RuleEngine::RuleErrorInvalidRuleFormat)); - return createReply(returns); - } + // Check and upack eventDescriptorList + QPair, RuleEngine::RuleError> eventDescriptorVerification = verifyEventDescriptors(params); + QList eventDescriptorList = eventDescriptorVerification.first; + if (eventDescriptorVerification.second != RuleEngine::RuleErrorNoError) { + QVariantMap returns; + returns.insert("ruleError", JsonTypes::ruleErrorToString(eventDescriptorVerification.second)); + return createReply(returns); } - // Check and unpack eventDescriptors - QList eventDescriptorList; - if (params.contains("eventDescriptor")) { - QVariantMap eventMap = params.value("eventDescriptor").toMap(); - qCDebug(dcJsonRpc) << "unpacking eventDescriptor" << eventMap; - eventDescriptorList.append(JsonTypes::unpackEventDescriptor(eventMap)); - } else if (params.contains("eventDescriptorList")) { - QVariantList eventDescriptors = params.value("eventDescriptorList").toList(); - qCDebug(dcJsonRpc) << "unpacking eventDescriptorList:" << eventDescriptors; - foreach (const QVariant &eventVariant, eventDescriptors) { - QVariantMap eventMap = eventVariant.toMap(); - eventDescriptorList.append(JsonTypes::unpackEventDescriptor(eventMap)); - } + + // Check and unpack stateEvaluator + qCDebug(dcJsonRpc) << "unpacking stateEvaluator:" << params.value("stateEvaluator").toMap(); + StateEvaluator stateEvaluator = JsonTypes::unpackStateEvaluator(params.value("stateEvaluator").toMap()); + + // Check and unpack actions + QPair, RuleEngine::RuleError> actionsVerification = verifyActions(params, eventDescriptorList); + QList actions = actionsVerification.first; + if (actionsVerification.second != RuleEngine::RuleErrorNoError) { + QVariantMap returns; + returns.insert("ruleError", JsonTypes::ruleErrorToString(actionsVerification.second)); + return createReply(returns); + } + + // Check and unpack exitActions + QPair, RuleEngine::RuleError> exitActionsVerification = verifyExitActions(params); + QList exitActions = exitActionsVerification.first; + if (exitActionsVerification.second != RuleEngine::RuleErrorNoError) { + QVariantMap returns; + returns.insert("ruleError", JsonTypes::ruleErrorToString(exitActionsVerification.second)); + return createReply(returns); + } + + 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, exitActions, enabled); + QVariantMap returns; + if (status == RuleEngine::RuleErrorNoError) { + returns.insert("ruleId", newRuleId.toString()); + } + returns.insert("ruleError", JsonTypes::ruleErrorToString(status)); + return createReply(returns); +} + +JsonReply *RulesHandler::EditRule(const QVariantMap ¶ms) +{ + // check rule consistency + RuleEngine::RuleError ruleConsistencyError = verifyRuleConsistency(params); + if (ruleConsistencyError != RuleEngine::RuleErrorNoError) { + QVariantMap returns; + returns.insert("ruleError", JsonTypes::ruleErrorToString(ruleConsistencyError)); + return createReply(returns); + } + + // Check and upack eventDescriptorList + QPair, RuleEngine::RuleError> eventDescriptorVerification = verifyEventDescriptors(params); + QList eventDescriptorList = eventDescriptorVerification.first; + if (eventDescriptorVerification.second != RuleEngine::RuleErrorNoError) { + QVariantMap returns; + returns.insert("ruleError", JsonTypes::ruleErrorToString(eventDescriptorVerification.second)); + return createReply(returns); } // Check and unpack stateEvaluator @@ -185,97 +253,31 @@ JsonReply* RulesHandler::AddRule(const QVariantMap ¶ms) StateEvaluator stateEvaluator = JsonTypes::unpackStateEvaluator(params.value("stateEvaluator").toMap()); // Check and unpack actions - QList actions; - QVariantList actionList = params.value("actions").toList(); - qCDebug(dcJsonRpc) << "unpacking actions:" << actionList; - foreach (const QVariant &actionVariant, actionList) { - QVariantMap actionMap = actionVariant.toMap(); - RuleAction action(ActionTypeId(actionMap.value("actionTypeId").toString()), DeviceId(actionMap.value("deviceId").toString())); - RuleActionParamList actionParamList = JsonTypes::unpackRuleActionParams(actionMap.value("ruleActionParams").toList()); - foreach (const RuleActionParam &ruleActionParam, actionParamList) { - if (!ruleActionParam.isValid()) { - qCWarning(dcJsonRpc) << "got an actionParam with value AND eventTypeId!"; - QVariantMap returns; - returns.insert("ruleError", JsonTypes::ruleErrorToString(RuleEngine::RuleErrorInvalidRuleActionParameter)); - return createReply(returns); - } - } - - action.setRuleActionParams(actionParamList); - actions.append(action); - } - - // check possible eventTypeIds in params - foreach (const RuleAction &ruleAction, actions) { - if (ruleAction.isEventBased()) { - foreach (const RuleActionParam &ruleActionParam, ruleAction.ruleActionParams()) { - if (ruleActionParam.eventTypeId() != EventTypeId()) { - // We have an eventTypeId - if (eventDescriptorList.isEmpty()) { - QVariantMap returns; - qCWarning(dcJsonRpc) << "RuleAction" << ruleAction.actionTypeId() << "contains an eventTypeId, but there are no eventDescriptors."; - returns.insert("ruleError", JsonTypes::ruleErrorToString(RuleEngine::RuleErrorInvalidRuleActionParameter)); - return createReply(returns); - } - // now check if this eventType is in the eventDescriptorList of this rule - if (!checkEventDescriptors(eventDescriptorList, ruleActionParam.eventTypeId())) { - QVariantMap returns; - qCWarning(dcJsonRpc) << "eventTypeId from RuleAction" << ruleAction.actionTypeId() << "missing in eventDescriptors."; - returns.insert("ruleError", JsonTypes::ruleErrorToString(RuleEngine::RuleErrorInvalidRuleActionParameter)); - return createReply(returns); - } - - // check if the param type of the event and the action match - QVariant::Type eventParamType = getEventParamType(ruleActionParam.eventTypeId(), ruleActionParam.eventParamName()); - QVariant::Type actionParamType = getActionParamType(ruleAction.actionTypeId(), ruleActionParam.name()); - if (eventParamType != actionParamType) { - QVariantMap returns; - qCWarning(dcJsonRpc) << "RuleActionParam" << ruleActionParam.name() << " and given event param " << ruleActionParam.eventParamName() << "have not the same type:"; - qCWarning(dcJsonRpc) << " -> actionParamType:" << actionParamType; - qCWarning(dcJsonRpc) << " -> eventParamType:" << eventParamType; - returns.insert("ruleError", JsonTypes::ruleErrorToString(RuleEngine::RuleErrorTypesNotMatching)); - return createReply(returns); - } - } - } - } - } - - - QVariantMap returns; - if (actions.count() == 0) { - returns.insert("ruleError", JsonTypes::ruleErrorToString(RuleEngine::RuleErrorMissingParameter)); + QPair, RuleEngine::RuleError> actionsVerification = verifyActions(params, eventDescriptorList); + QList actions = actionsVerification.first; + if (actionsVerification.second != RuleEngine::RuleErrorNoError) { + QVariantMap returns; + returns.insert("ruleError", JsonTypes::ruleErrorToString(actionsVerification.second)); return createReply(returns); } // Check and unpack exitActions - QList exitActions; - if (params.contains("exitActions")) { - QVariantList exitActionList = params.value("exitActions").toList(); - qCDebug(dcJsonRpc) << "unpacking exitActions:" << exitActionList; - foreach (const QVariant &actionVariant, exitActionList) { - QVariantMap actionMap = actionVariant.toMap(); - RuleAction action(ActionTypeId(actionMap.value("actionTypeId").toString()), DeviceId(actionMap.value("deviceId").toString())); - if (action.isEventBased()) { - qCWarning(dcJsonRpc) << "got exitAction with a param value containing an eventTypeId!"; - QVariantMap returns; - returns.insert("ruleError", JsonTypes::ruleErrorToString(RuleEngine::RuleErrorInvalidRuleActionParameter)); - return createReply(returns); - } - action.setRuleActionParams(JsonTypes::unpackRuleActionParams(actionMap.value("ruleActionParams").toList())); - qCDebug(dcJsonRpc) << "params in exitAction" << action.ruleActionParams(); - exitActions.append(action); - } + QPair, RuleEngine::RuleError> exitActionsVerification = verifyExitActions(params); + QList exitActions = exitActionsVerification.first; + if (exitActionsVerification.second != RuleEngine::RuleErrorNoError) { + QVariantMap returns; + returns.insert("ruleError", JsonTypes::ruleErrorToString(exitActionsVerification.second)); + return createReply(returns); } - 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, exitActions, enabled); + RuleId ruleId = RuleId(params.value("ruleId").toString()); + RuleEngine::RuleError status = GuhCore::instance()->editRule(ruleId, name, eventDescriptorList, stateEvaluator, actions, exitActions, enabled); + QVariantMap returns; if (status == RuleEngine::RuleErrorNoError) { - returns.insert("ruleId", newRuleId.toString()); + returns.insert("ruleId", ruleId.toString()); } returns.insert("ruleError", JsonTypes::ruleErrorToString(status)); return createReply(returns); @@ -358,6 +360,125 @@ bool RulesHandler::checkEventDescriptors(const QList eventDescr return false; } +RuleEngine::RuleError RulesHandler::verifyRuleConsistency(const QVariantMap ¶ms) +{ + // check if there are an eventDescriptor and an eventDescriptorList + if (params.contains("eventDescriptor") && params.contains("eventDescriptorList")) { + qCWarning(dcJsonRpc) << "Only one of eventDesciptor or eventDescriptorList may be used."; + return RuleEngine::RuleErrorInvalidParameter; + } + + // check if this rules is based on any event and contains exit actions + if (params.contains("eventDescriptor") || params.contains("eventDescriptorList")) { + if (params.contains("exitActions")) { + qCWarning(dcJsonRpc) << "The exitActions will never be executed if the rule contains an eventDescriptor."; + return RuleEngine::RuleErrorInvalidRuleFormat; + } + } + + // check if there are any actions + if (params.value("actions").toList().isEmpty()) { + qCWarning(dcJsonRpc) << "Rule actions missing. A rule without actions has no effect."; + return RuleEngine::RuleErrorMissingParameter; + } + + // TODO: check if events and stateEvaluators are missing + + return RuleEngine::RuleErrorNoError; +} + +QPair, RuleEngine::RuleError> RulesHandler::verifyEventDescriptors(const QVariantMap ¶ms) +{ + // Check and unpack eventDescriptors + QList eventDescriptorList = QList(); + if (params.contains("eventDescriptor")) { + QVariantMap eventMap = params.value("eventDescriptor").toMap(); + qCDebug(dcJsonRpc) << "unpacking eventDescriptor" << eventMap; + eventDescriptorList.append(JsonTypes::unpackEventDescriptor(eventMap)); + } else if (params.contains("eventDescriptorList")) { + QVariantList eventDescriptors = params.value("eventDescriptorList").toList(); + qCDebug(dcJsonRpc) << "unpacking eventDescriptorList:" << eventDescriptors; + foreach (const QVariant &eventVariant, eventDescriptors) { + QVariantMap eventMap = eventVariant.toMap(); + eventDescriptorList.append(JsonTypes::unpackEventDescriptor(eventMap)); + } + } + return QPair, RuleEngine::RuleError>(eventDescriptorList, RuleEngine::RuleErrorNoError); +} + +QPair, RuleEngine::RuleError> RulesHandler::verifyActions(const QVariantMap ¶ms, const QList &eventDescriptorList) +{ + QList actions; + QVariantList actionList = params.value("actions").toList(); + qCDebug(dcJsonRpc) << "unpacking actions:" << actionList; + foreach (const QVariant &actionVariant, actionList) { + QVariantMap actionMap = actionVariant.toMap(); + RuleAction action(ActionTypeId(actionMap.value("actionTypeId").toString()), DeviceId(actionMap.value("deviceId").toString())); + RuleActionParamList actionParamList = JsonTypes::unpackRuleActionParams(actionMap.value("ruleActionParams").toList()); + foreach (const RuleActionParam &ruleActionParam, actionParamList) { + if (!ruleActionParam.isValid()) { + qCWarning(dcJsonRpc) << "got an actionParam with value AND eventTypeId!"; + return QPair, RuleEngine::RuleError>(actions, RuleEngine::RuleErrorInvalidRuleActionParameter); + } + } + qCDebug(dcJsonRpc) << "params in exitAction" << action.ruleActionParams(); + action.setRuleActionParams(actionParamList); + actions.append(action); + } + + // check possible eventTypeIds in params + foreach (const RuleAction &ruleAction, actions) { + if (ruleAction.isEventBased()) { + foreach (const RuleActionParam &ruleActionParam, ruleAction.ruleActionParams()) { + if (ruleActionParam.eventTypeId() != EventTypeId()) { + // We have an eventTypeId + if (eventDescriptorList.isEmpty()) { + qCWarning(dcJsonRpc) << "RuleAction" << ruleAction.actionTypeId() << "contains an eventTypeId, but there are no eventDescriptors."; + return QPair, RuleEngine::RuleError>(actions, RuleEngine::RuleErrorInvalidRuleActionParameter); + } + // now check if this eventType is in the eventDescriptorList of this rule + if (!checkEventDescriptors(eventDescriptorList, ruleActionParam.eventTypeId())) { + qCWarning(dcJsonRpc) << "eventTypeId from RuleAction" << ruleAction.actionTypeId() << "missing in eventDescriptors."; + return QPair, RuleEngine::RuleError>(actions, RuleEngine::RuleErrorInvalidRuleActionParameter); + } + + // check if the param type of the event and the action match + QVariant::Type eventParamType = getEventParamType(ruleActionParam.eventTypeId(), ruleActionParam.eventParamName()); + QVariant::Type actionParamType = getActionParamType(ruleAction.actionTypeId(), ruleActionParam.name()); + if (eventParamType != actionParamType) { + qCWarning(dcJsonRpc) << "RuleActionParam" << ruleActionParam.name() << " and given event param " << ruleActionParam.eventParamName() << "have not the same type:"; + qCWarning(dcJsonRpc) << " -> actionParamType:" << actionParamType; + qCWarning(dcJsonRpc) << " -> eventParamType:" << eventParamType; + return QPair, RuleEngine::RuleError>(actions, RuleEngine::RuleErrorTypesNotMatching); + } + } + } + } + } + return QPair, RuleEngine::RuleError>(actions, RuleEngine::RuleErrorNoError); +} + +QPair, RuleEngine::RuleError> RulesHandler::verifyExitActions(const QVariantMap ¶ms) +{ + QList exitActions; + if (params.contains("exitActions")) { + QVariantList exitActionList = params.value("exitActions").toList(); + qCDebug(dcJsonRpc) << "unpacking exitActions:" << exitActionList; + foreach (const QVariant &actionVariant, exitActionList) { + QVariantMap actionMap = actionVariant.toMap(); + RuleAction action(ActionTypeId(actionMap.value("actionTypeId").toString()), DeviceId(actionMap.value("deviceId").toString())); + if (action.isEventBased()) { + qCWarning(dcJsonRpc) << "got exitAction with a param value containing an eventTypeId!"; + return QPair, RuleEngine::RuleError>(exitActions, RuleEngine::RuleErrorInvalidRuleActionParameter); + } + qCDebug(dcJsonRpc) << "params in exitAction" << action.ruleActionParams(); + action.setRuleActionParams(JsonTypes::unpackRuleActionParams(actionMap.value("ruleActionParams").toList())); + exitActions.append(action); + } + } + return QPair, RuleEngine::RuleError>(exitActions, RuleEngine::RuleErrorNoError); +} + void RulesHandler::ruleRemovedNotification(const RuleId &ruleId) { QVariantMap params; @@ -382,3 +503,11 @@ void RulesHandler::ruleActiveChangedNotification(const Rule &rule) emit RuleActiveChanged(params); } + +void RulesHandler::ruleConfigurationChangedNotification(const Rule &rule) +{ + QVariantMap params; + params.insert("rule", JsonTypes::packRule(rule)); + + emit RuleConfigurationChanged(params); +} diff --git a/server/jsonrpc/ruleshandler.h b/server/jsonrpc/ruleshandler.h index ac864d57..6b627a2c 100644 --- a/server/jsonrpc/ruleshandler.h +++ b/server/jsonrpc/ruleshandler.h @@ -36,6 +36,7 @@ public: Q_INVOKABLE JsonReply* GetRuleDetails(const QVariantMap ¶ms); Q_INVOKABLE JsonReply* AddRule(const QVariantMap ¶ms); + Q_INVOKABLE JsonReply* EditRule(const QVariantMap ¶ms); Q_INVOKABLE JsonReply* RemoveRule(const QVariantMap ¶ms); Q_INVOKABLE JsonReply* FindRules(const QVariantMap ¶ms); @@ -46,6 +47,7 @@ signals: void RuleRemoved(const QVariantMap ¶ms); void RuleAdded(const QVariantMap ¶ms); void RuleActiveChanged(const QVariantMap ¶ms); + void RuleConfigurationChanged(const QVariantMap ¶ms); private: QVariant::Type getActionParamType(const ActionTypeId &actionTypeId, const QString ¶mName); @@ -53,10 +55,16 @@ private: bool checkEventDescriptors(const QList eventDescriptors, const EventTypeId &eventTypeId); + RuleEngine::RuleError verifyRuleConsistency(const QVariantMap ¶ms); + QPair, RuleEngine::RuleError> verifyEventDescriptors(const QVariantMap ¶ms); + QPair, RuleEngine::RuleError> verifyActions(const QVariantMap ¶ms, const QList &eventDescriptorList); + QPair, RuleEngine::RuleError> verifyExitActions(const QVariantMap ¶ms); + private slots: void ruleRemovedNotification(const RuleId &ruleId); void ruleAddedNotification(const Rule &rule); void ruleActiveChangedNotification(const Rule &rule); + void ruleConfigurationChangedNotification(const Rule &rule); }; diff --git a/server/ruleengine.cpp b/server/ruleengine.cpp index b6aa48fe..98e5b452 100644 --- a/server/ruleengine.cpp +++ b/server/ruleengine.cpp @@ -42,6 +42,10 @@ \a ruleId holds the id of the removed rule. You should remove any references or copies you hold for this rule.*/ +/*! \fn void RuleEngine::ruleConfigurationChanged(const RuleId &ruleId) + Will be emitted whenever a \l{Rule} changed his enable/disable status. + \a ruleId holds the id of the changed rule.*/ + /*! \enum RuleEngine::RuleError \value RuleErrorNoError No error happened. Everything is fine. @@ -255,8 +259,10 @@ RuleEngine::RuleError RuleEngine::addRule(const RuleId &ruleId, const QString &n 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 the list of \a exitActions 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, const QList &exitActions, bool enabled) +/*! Add a new \l{Rule} with the given \a ruleId, \a name, \a eventDescriptorList, \a stateEvaluator, the list of \a actions the list of \a exitActions and the \a enabled value to the engine. + If \a fromEdit is true, the notification Rules.RuleAdded will not be emitted. +*/ +RuleEngine::RuleError RuleEngine::addRule(const RuleId &ruleId, const QString &name, const QList &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList &actions, const QList &exitActions, bool enabled, bool fromEdit) { if (ruleId.isNull()) { return RuleErrorInvalidRuleId; @@ -266,8 +272,6 @@ RuleEngine::RuleError RuleEngine::addRule(const RuleId &ruleId, const QString &n return RuleErrorInvalidRuleId; } - // TODO: check rule name for duplicated rulesnames - foreach (const EventDescriptor &eventDescriptor, eventDescriptorList) { Device *device = GuhCore::instance()->findConfiguredDevice(eventDescriptor.deviceId()); if (!device) { @@ -337,70 +341,37 @@ RuleEngine::RuleError RuleEngine::addRule(const RuleId &ruleId, const QString &n Rule rule = Rule(ruleId, name, eventDescriptorList, stateEvaluator, actions, exitActions); rule.setEnabled(enabled); appendRule(rule); - emit ruleAdded(rule); + saveRule(rule); + if (!fromEdit) + emit ruleAdded(rule); - // Save Events / EventDescriptors - QSettings settings(m_settingsFile); - settings.beginGroup(rule.id().toString()); - settings.setValue("name", name); - settings.setValue("enabled", enabled); - settings.beginGroup("events"); - for (int i = 0; i < eventDescriptorList.count(); i++) { - const EventDescriptor &eventDescriptor = eventDescriptorList.at(i); - settings.beginGroup("EventDescriptor-" + QString::number(i)); - settings.setValue("deviceId", eventDescriptor.deviceId().toString()); - settings.setValue("eventTypeId", eventDescriptor.eventTypeId().toString()); + return RuleErrorNoError; +} - foreach (const ParamDescriptor ¶mDescriptor, eventDescriptor.paramDescriptors()) { - settings.beginGroup("ParamDescriptor-" + paramDescriptor.name()); - settings.setValue("value", paramDescriptor.value()); - settings.setValue("operator", paramDescriptor.operatorType()); - settings.endGroup(); - } - settings.endGroup(); +RuleEngine::RuleError RuleEngine::editRule(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; } - settings.endGroup(); - // Save StateEvaluator - stateEvaluator.dumpToSettings(settings, "stateEvaluator"); - - // Save ruleActions - int i = 0; - settings.beginGroup("ruleActions"); - foreach (const RuleAction &action, rule.actions()) { - settings.beginGroup(QString::number(i)); - settings.setValue("deviceId", action.deviceId().toString()); - settings.setValue("actionTypeId", action.actionTypeId().toString()); - foreach (const RuleActionParam ¶m, action.ruleActionParams()) { - settings.beginGroup("RuleActionParam-" + param.name()); - settings.setValue("value", param.value()); - if (param.eventTypeId() != EventTypeId()) { - settings.setValue("eventTypeId", param.eventTypeId().toString()); - settings.setValue("eventParamName", param.eventParamName()); - } - settings.endGroup(); - } - i++; - settings.endGroup(); + if (findRule(ruleId).id().isNull()) { + qCWarning(dcRuleEngine) << "Cannot edit rule. There is no rule with id:" << ruleId.toString(); + return RuleErrorRuleNotFound; } - settings.endGroup(); - // Save ruleExitActions - settings.beginGroup("ruleExitActions"); - i = 0; - foreach (const RuleAction &action, rule.exitActions()) { - settings.beginGroup(QString::number(i)); - settings.setValue("deviceId", action.deviceId().toString()); - settings.setValue("actionTypeId", action.actionTypeId().toString()); - foreach (const RuleActionParam ¶m, action.ruleActionParams()) { - settings.beginGroup("RuleActionParam-" + param.name()); - settings.setValue("value", param.value()); - settings.endGroup(); - } - i++; - settings.endGroup(); + // first remove old rule with this id + RuleError removeResult = removeRule(ruleId, true); + if (removeResult != RuleErrorNoError) { + return removeResult; } - settings.endGroup(); + + // the rule is removed, now add it with the same id and new vonfiguration + RuleError addResult = addRule(ruleId, name, eventDescriptorList, stateEvaluator, actions, exitActions, enabled, true); + if (addResult != RuleErrorNoError) { + return addResult; + } + + emit ruleConfigurationChanged(m_rules.value(ruleId)); return RuleErrorNoError; } @@ -422,8 +393,10 @@ QList RuleEngine::ruleIds() const /*! Removes the \l{Rule} with the given \a ruleId from the Engine. Returns \l{RuleError} which describes whether the operation - was successful or not. */ -RuleEngine::RuleError RuleEngine::removeRule(const RuleId &ruleId) + was successful or not. If \a fromEdit is true, the notification Rules.RuleRemoved + will not be emitted. +*/ +RuleEngine::RuleError RuleEngine::removeRule(const RuleId &ruleId, bool fromEdit) { int index = m_ruleIds.indexOf(ruleId); @@ -439,7 +412,9 @@ RuleEngine::RuleError RuleEngine::removeRule(const RuleId &ruleId) settings.remove(""); settings.endGroup(); - emit ruleRemoved(ruleId); + if (!fromEdit) + emit ruleRemoved(ruleId); + return RuleErrorNoError; } @@ -454,13 +429,9 @@ RuleEngine::RuleError RuleEngine::enableRule(const RuleId &ruleId) Rule rule = m_rules.value(ruleId); rule.setEnabled(true); m_rules[ruleId] = rule; - QSettings settings(m_settingsFile); - settings.beginGroup(ruleId.toString()); - if (!settings.value("enabled", true).toBool()) { - settings.setValue("enabled", true); - emit ruleChanged(ruleId); - } - settings.endGroup(); + + saveRule(rule); + emit ruleConfigurationChanged(rule); return RuleErrorNoError; } @@ -476,14 +447,8 @@ RuleEngine::RuleError RuleEngine::disableRule(const RuleId &ruleId) Rule rule = m_rules.value(ruleId); rule.setEnabled(false); m_rules[ruleId] = rule; - QSettings settings(m_settingsFile); - settings.beginGroup(ruleId.toString()); - if (settings.value("enabled", true).toBool()) { - settings.setValue("enabled", false); - emit ruleChanged(ruleId); - } - settings.endGroup(); - + saveRule(rule); + emit ruleConfigurationChanged(rule); return RuleErrorNoError; } @@ -591,3 +556,69 @@ void RuleEngine::appendRule(const Rule &rule) m_rules.insert(rule.id(), rule); m_ruleIds.append(rule.id()); } + +void RuleEngine::saveRule(const Rule &rule) +{ + // Save Events / EventDescriptors + QSettings settings(m_settingsFile); + settings.beginGroup(rule.id().toString()); + settings.setValue("name", rule.name()); + settings.setValue("enabled", rule.enabled()); + settings.beginGroup("events"); + for (int i = 0; i < rule.eventDescriptors().count(); i++) { + const EventDescriptor &eventDescriptor = rule.eventDescriptors().at(i); + settings.beginGroup("EventDescriptor-" + QString::number(i)); + settings.setValue("deviceId", eventDescriptor.deviceId().toString()); + settings.setValue("eventTypeId", eventDescriptor.eventTypeId().toString()); + + foreach (const ParamDescriptor ¶mDescriptor, eventDescriptor.paramDescriptors()) { + settings.beginGroup("ParamDescriptor-" + paramDescriptor.name()); + settings.setValue("value", paramDescriptor.value()); + settings.setValue("operator", paramDescriptor.operatorType()); + settings.endGroup(); + } + settings.endGroup(); + } + settings.endGroup(); + + // Save StateEvaluator + rule.stateEvaluator().dumpToSettings(settings, "stateEvaluator"); + + // Save ruleActions + int i = 0; + settings.beginGroup("ruleActions"); + foreach (const RuleAction &action, rule.actions()) { + settings.beginGroup(QString::number(i)); + settings.setValue("deviceId", action.deviceId().toString()); + settings.setValue("actionTypeId", action.actionTypeId().toString()); + foreach (const RuleActionParam ¶m, action.ruleActionParams()) { + settings.beginGroup("RuleActionParam-" + param.name()); + settings.setValue("value", param.value()); + if (param.eventTypeId() != EventTypeId()) { + settings.setValue("eventTypeId", param.eventTypeId().toString()); + settings.setValue("eventParamName", param.eventParamName()); + } + settings.endGroup(); + } + i++; + settings.endGroup(); + } + settings.endGroup(); + + // Save ruleExitActions + settings.beginGroup("ruleExitActions"); + i = 0; + foreach (const RuleAction &action, rule.exitActions()) { + settings.beginGroup(QString::number(i)); + settings.setValue("deviceId", action.deviceId().toString()); + settings.setValue("actionTypeId", action.actionTypeId().toString()); + foreach (const RuleActionParam ¶m, action.ruleActionParams()) { + settings.beginGroup("RuleActionParam-" + param.name()); + settings.setValue("value", param.value()); + settings.endGroup(); + } + i++; + settings.endGroup(); + } + settings.endGroup(); +} diff --git a/server/ruleengine.h b/server/ruleengine.h index 4923ead2..a0952fc7 100644 --- a/server/ruleengine.h +++ b/server/ruleengine.h @@ -60,11 +60,14 @@ 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, const QList &exitActions, 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, bool fromEdit = false); + RuleError editRule(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; - RuleError removeRule(const RuleId &ruleId); + RuleError removeRule(const RuleId &ruleId, bool fromEdit = false); RuleError enableRule(const RuleId &ruleId); RuleError disableRule(const RuleId &ruleId); @@ -77,13 +80,14 @@ public: signals: void ruleAdded(const Rule &rule); void ruleRemoved(const RuleId &ruleId); - void ruleChanged(const RuleId &ruleId); + void ruleConfigurationChanged(const Rule &rule); private: bool containsEvent(const Rule &rule, const Event &event); bool containsState(const StateEvaluator &stateEvaluator, const Event &stateChangeEvent); void appendRule(const Rule &rule); + void saveRule(const Rule &rule); private: QString m_settingsFile;