mirror of https://github.com/nymea/nymea.git
778 lines
30 KiB
C++
778 lines
30 KiB
C++
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* *
|
|
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
|
|
* Copyright (C) 2014 Michael Zanetti <michael_zanetti@gmx.net> *
|
|
* *
|
|
* This file is part of guh. *
|
|
* *
|
|
* Guh is free software: you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation, version 2 of the License. *
|
|
* *
|
|
* Guh 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. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
|
|
* *
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
/*!
|
|
\class guhserver::RuleEngine
|
|
\brief The Engine that evaluates \l{Rule}{Rules} and finds \l{Action}{Actions} to be executed.
|
|
|
|
\ingroup rules
|
|
\inmodule core
|
|
|
|
You can add, remove and update rules and query the engine for actions to be executed
|
|
for a given \l{Event} described by an \l{EventDescriptor}.
|
|
|
|
\sa Event, EventDescriptor, Rule, RuleAction
|
|
*/
|
|
|
|
/*! \fn void guhserver::RuleEngine::ruleAdded(const Rule &rule)
|
|
Will be emitted whenever a new \l{Rule} is added to this Engine.
|
|
The \a rule parameter holds the entire new rule.*/
|
|
|
|
/*! \fn void guhserver::RuleEngine::ruleRemoved(const RuleId &ruleId)
|
|
Will be emitted whenever a \l{Rule} is removed from this Engine.
|
|
\a ruleId holds the id of the removed rule. You should remove any references
|
|
or copies you hold for this rule.*/
|
|
|
|
/*! \fn void guhserver::RuleEngine::ruleConfigurationChanged(const Rule &rule)
|
|
Will be emitted whenever a \l{Rule} changed his enable/disable status.
|
|
The parameter \a rule holds the changed rule.*/
|
|
|
|
/*! \enum guhserver::RuleEngine::RuleError
|
|
\value RuleErrorNoError
|
|
No error happened. Everything is fine.
|
|
\value RuleErrorInvalidRuleId
|
|
The given RuleId is not valid.
|
|
\value RuleErrorRuleNotFound
|
|
Couldn't find a \l{Rule} with the given id.
|
|
\value RuleErrorDeviceNotFound
|
|
Couldn't find a \l{Device} with the given id.
|
|
\value RuleErrorEventTypeNotFound
|
|
Couldn't find a \l{EventType} with the given id.
|
|
\value RuleErrorStateTypeNotFound
|
|
Couldn't find a \l{StateType} with the given id.
|
|
\value RuleErrorActionTypeNotFound
|
|
Couldn't find a \l{ActionType} with the given id.
|
|
\value RuleErrorInvalidParameter
|
|
The given \l{Param} is not valid.
|
|
\value RuleErrorInvalidRuleFormat
|
|
The format of the rule is not valid. (i.e. add \l{Rule} with exitActions and eventDescriptors)
|
|
\value RuleErrorMissingParameter
|
|
One of the given \l{Param}{Params} is missing.
|
|
\value RuleErrorInvalidRuleActionParameter
|
|
One of the given \l{RuleActionParam}{RuleActionParams} is not valid.
|
|
\value RuleErrorInvalidStateEvaluatorValue
|
|
One of the given \l{StateEvaluator}{StateEvaluators} has an invalid \l{State} value.
|
|
\value RuleErrorTypesNotMatching
|
|
The types of the \l{RuleActionParam} and the corresponding \l{Event} \l{Param} do not match.
|
|
\value RuleErrorNotExecutable
|
|
This rule is not executable.
|
|
\value RuleErrorContainsEventBasesAction
|
|
This rule contains an \l{Action} which depends on an \l{Event} value. This \l{Rule} cannot execute
|
|
the \l{Action}{Actions} without the \l{Event} value.
|
|
\value RuleErrorNoExitActions
|
|
This rule does not have any ExitActions which means they cannot be executed.
|
|
*/
|
|
|
|
/*! \enum guhserver::RuleEngine::RemovePolicy
|
|
|
|
\value RemovePolicyCascade
|
|
Remove the whole \l{Rule}.
|
|
\value RemovePolicyUpdate
|
|
Remove a \l{Device} from a rule.
|
|
*/
|
|
|
|
|
|
#include "ruleengine.h"
|
|
#include "loggingcategories.h"
|
|
#include "types/paramdescriptor.h"
|
|
#include "types/eventdescriptor.h"
|
|
#include "guhsettings.h"
|
|
|
|
#include "guhcore.h"
|
|
|
|
#include "devicemanager.h"
|
|
#include "plugin/device.h"
|
|
|
|
#include <QDebug>
|
|
#include <QStringList>
|
|
#include <QStandardPaths>
|
|
#include <QCoreApplication>
|
|
|
|
namespace guhserver {
|
|
|
|
/*! Constructs the RuleEngine with the given \a parent. Although it wouldn't harm to have multiple RuleEngines, there is one
|
|
instance available from \l{GuhCore}. This one should be used instead of creating multiple ones.
|
|
*/
|
|
RuleEngine::RuleEngine(QObject *parent) :
|
|
QObject(parent)
|
|
{
|
|
GuhSettings settings(GuhSettings::SettingsRoleRules);
|
|
qCDebug(dcRuleEngine) << "loading rules from" << settings.fileName();
|
|
foreach (const QString &idString, settings.childGroups()) {
|
|
settings.beginGroup(idString);
|
|
|
|
QString name = settings.value("name", idString).toString();
|
|
bool enabled = settings.value("enabled", true).toBool();
|
|
bool executable = settings.value("executable", true).toBool();
|
|
|
|
qCDebug(dcRuleEngine) << "load rule" << name << idString;
|
|
|
|
QList<EventDescriptor> eventDescriptorList;
|
|
settings.beginGroup("events");
|
|
|
|
foreach (QString eventGroupName, settings.childGroups()) {
|
|
if (eventGroupName.startsWith("EventDescriptor-")) {
|
|
settings.beginGroup(eventGroupName);
|
|
EventTypeId eventTypeId(settings.value("eventTypeId").toString());
|
|
DeviceId deviceId(settings.value("deviceId").toString());
|
|
|
|
QList<ParamDescriptor> params;
|
|
foreach (QString groupName, settings.childGroups()) {
|
|
if (groupName.startsWith("ParamDescriptor-")) {
|
|
settings.beginGroup(groupName);
|
|
ParamDescriptor paramDescriptor(groupName.remove(QRegExp("^ParamDescriptor-")), settings.value("value"));
|
|
paramDescriptor.setOperatorType((Types::ValueOperator)settings.value("operator").toInt());
|
|
params.append(paramDescriptor);
|
|
settings.endGroup();
|
|
}
|
|
}
|
|
|
|
EventDescriptor eventDescriptor(eventTypeId, deviceId, params);
|
|
eventDescriptorList.append(eventDescriptor);
|
|
settings.endGroup();
|
|
}
|
|
}
|
|
settings.endGroup();
|
|
|
|
StateEvaluator stateEvaluator = StateEvaluator::loadFromSettings(settings, "stateEvaluator");
|
|
|
|
QList<RuleAction> actions;
|
|
settings.beginGroup("ruleActions");
|
|
foreach (const QString &actionNumber, settings.childGroups()) {
|
|
settings.beginGroup(actionNumber);
|
|
|
|
RuleAction action = RuleAction(ActionTypeId(settings.value("actionTypeId").toString()),
|
|
DeviceId(settings.value("deviceId").toString()));
|
|
|
|
RuleActionParamList params;
|
|
foreach (QString paramNameString, settings.childGroups()) {
|
|
if (paramNameString.startsWith("RuleActionParam-")) {
|
|
settings.beginGroup(paramNameString);
|
|
RuleActionParam param(paramNameString.remove(QRegExp("^RuleActionParam-")),
|
|
settings.value("value",QVariant()),
|
|
EventTypeId(settings.value("eventTypeId", EventTypeId()).toString()),
|
|
settings.value("eventParamName", QString()).toString());
|
|
params.append(param);
|
|
settings.endGroup();
|
|
}
|
|
}
|
|
|
|
action.setRuleActionParams(params);
|
|
actions.append(action);
|
|
|
|
settings.endGroup();
|
|
}
|
|
settings.endGroup();
|
|
|
|
QList<RuleAction> exitActions;
|
|
settings.beginGroup("ruleExitActions");
|
|
foreach (const QString &actionNumber, settings.childGroups()) {
|
|
settings.beginGroup(actionNumber);
|
|
|
|
RuleAction action = RuleAction(ActionTypeId(settings.value("actionTypeId").toString()),
|
|
DeviceId(settings.value("deviceId").toString()));
|
|
|
|
RuleActionParamList params;
|
|
foreach (QString paramNameString, settings.childGroups()) {
|
|
if (paramNameString.startsWith("RuleActionParam-")) {
|
|
settings.beginGroup(paramNameString);
|
|
RuleActionParam param(paramNameString.remove(QRegExp("^RuleActionParam-")), settings.value("value"));
|
|
params.append(param);
|
|
settings.endGroup();
|
|
}
|
|
}
|
|
action.setRuleActionParams(params);
|
|
exitActions.append(action);
|
|
settings.endGroup();
|
|
}
|
|
settings.endGroup();
|
|
|
|
Rule rule = Rule(RuleId(idString), name, eventDescriptorList, stateEvaluator, actions, exitActions);
|
|
rule.setEnabled(enabled);
|
|
rule.setExecutable(executable);
|
|
appendRule(rule);
|
|
settings.endGroup();
|
|
}
|
|
}
|
|
|
|
RuleEngine::~RuleEngine()
|
|
{
|
|
qCDebug(dcApplication) << "Shutting down rule engine";
|
|
}
|
|
|
|
/*! Ask the Engine to evaluate all the rules for the given \a event.
|
|
This will search all the \l{Rule}{Rules} triggered by the given \a event
|
|
and evaluate their states in the system. It will return a
|
|
list of all \l{Rule}{Rules} that are triggered or change its active state
|
|
because of this \a event.
|
|
*/
|
|
QList<Rule> RuleEngine::evaluateEvent(const Event &event)
|
|
{
|
|
Device *device = GuhCore::instance()->findConfiguredDevice(event.deviceId());
|
|
|
|
qCDebug(dcRuleEngine) << "got event:" << event << device->name() << event.eventTypeId();
|
|
|
|
QList<Rule> rules;
|
|
foreach (const RuleId &id, m_ruleIds) {
|
|
Rule rule = m_rules.value(id);
|
|
if (!rule.enabled())
|
|
continue;
|
|
|
|
if (rule.eventDescriptors().isEmpty()) {
|
|
// This rule seems to have only states, check on state changed
|
|
if (containsState(rule.stateEvaluator(), event)) {
|
|
if (rule.stateEvaluator().evaluate()) {
|
|
if (m_activeRules.contains(rule.id())) {
|
|
qCDebug(dcRuleEngine) << "Rule" << rule.id() << "still in active state.";
|
|
} else {
|
|
qCDebug(dcRuleEngine) << "Rule" << rule.id() << "entered active state.";
|
|
rule.setActive(true);
|
|
m_rules[rule.id()] = rule;
|
|
m_activeRules.append(rule.id());
|
|
rules.append(rule);
|
|
}
|
|
} else {
|
|
if (m_activeRules.contains(rule.id())) {
|
|
qCDebug(dcRuleEngine) << "Rule" << rule.id() << "left active state.";
|
|
rule.setActive(false);
|
|
m_rules[rule.id()] = rule;
|
|
m_activeRules.removeAll(rule.id());
|
|
rules.append(rule);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (containsEvent(rule, event)) {
|
|
if (rule.stateEvaluator().evaluate()) {
|
|
qCDebug(dcRuleEngine) << "Rule" << rule.id() << "contains event" << event.eventId() << "and all states match.";
|
|
rules.append(rule);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rules;
|
|
}
|
|
|
|
/*! Add a new \l{Rule} with the given \a ruleId , \a name, \a eventDescriptorList, \a actions and \a enabled value to the engine.
|
|
For convenience, this creates a Rule without any \l{State} comparison.
|
|
*/
|
|
RuleEngine::RuleError RuleEngine::addRule(const RuleId &ruleId, const QString &name, const QList<EventDescriptor> &eventDescriptorList, const QList<RuleAction> &actions, bool enabled)
|
|
{
|
|
return addRule(ruleId, name, eventDescriptorList, StateEvaluator(), actions, QList<RuleAction>(), 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, the \a enabled and the \a executable 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<EventDescriptor> &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList<RuleAction> &actions, const QList<RuleAction> &exitActions, bool enabled, bool executable, bool fromEdit)
|
|
{
|
|
if (ruleId.isNull())
|
|
return RuleErrorInvalidRuleId;
|
|
|
|
if (!findRule(ruleId).id().isNull()) {
|
|
qCWarning(dcRuleEngine) << "Already have a rule with this id!";
|
|
return RuleErrorInvalidRuleId;
|
|
}
|
|
|
|
foreach (const EventDescriptor &eventDescriptor, eventDescriptorList) {
|
|
Device *device = GuhCore::instance()->findConfiguredDevice(eventDescriptor.deviceId());
|
|
if (!device) {
|
|
qCWarning(dcRuleEngine) << "Cannot create rule. No configured device for eventTypeId" << eventDescriptor.eventTypeId();
|
|
return RuleErrorDeviceNotFound;
|
|
}
|
|
DeviceClass deviceClass = GuhCore::instance()->findDeviceClass(device->deviceClassId());
|
|
|
|
bool eventTypeFound = false;
|
|
foreach (const EventType &eventType, deviceClass.eventTypes()) {
|
|
if (eventType.id() == eventDescriptor.eventTypeId()) {
|
|
eventTypeFound = true;
|
|
}
|
|
}
|
|
if (!eventTypeFound) {
|
|
qCWarning(dcRuleEngine) << "Cannot create rule. Device " + device->name() + " has no event type:" << eventDescriptor.eventTypeId();
|
|
return RuleErrorEventTypeNotFound;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
foreach (const RuleAction &action, actions) {
|
|
Device *device = GuhCore::instance()->findConfiguredDevice(action.deviceId());
|
|
if (!device) {
|
|
qCWarning(dcRuleEngine) << "Cannot create rule. No configured device for actionTypeId" << action.actionTypeId();
|
|
return RuleErrorDeviceNotFound;
|
|
}
|
|
DeviceClass deviceClass = GuhCore::instance()->findDeviceClass(device->deviceClassId());
|
|
if (!deviceClass.hasActionType(action.actionTypeId())) {
|
|
qCWarning(dcRuleEngine) << "Cannot create rule. Device " + device->name() + " has no action type:" << action.actionTypeId();
|
|
return RuleErrorActionTypeNotFound;
|
|
}
|
|
|
|
// if the action is eventbased, it is already checked
|
|
if (!action.isEventBased()) {
|
|
// verify action params
|
|
foreach (const ActionType &actionType, deviceClass.actionTypes()) {
|
|
if (actionType.id() == action.actionTypeId()) {
|
|
ParamList finalParams = action.toAction().params();
|
|
DeviceManager::DeviceError paramCheck = GuhCore::instance()->deviceManager()->verifyParams(actionType.paramTypes(), finalParams);
|
|
if (paramCheck != DeviceManager::DeviceErrorNoError)
|
|
return RuleErrorInvalidRuleActionParameter;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (actions.count() > 0)
|
|
qCDebug(dcRuleEngine) << "actions" << actions.last().actionTypeId() << actions.last().ruleActionParams();
|
|
|
|
foreach (const RuleAction &action, exitActions) {
|
|
Device *device = GuhCore::instance()->findConfiguredDevice(action.deviceId());
|
|
if (!device) {
|
|
qCWarning(dcRuleEngine) << "Cannot create rule. No configured device for actionTypeId" << action.actionTypeId();
|
|
return RuleErrorDeviceNotFound;
|
|
}
|
|
DeviceClass deviceClass = GuhCore::instance()->findDeviceClass(device->deviceClassId());
|
|
|
|
if (!deviceClass.hasActionType(action.actionTypeId())) {
|
|
qCWarning(dcRuleEngine) << "Cannot create rule. Device " + device->name() + " has no action type:" << action.actionTypeId();
|
|
return RuleErrorActionTypeNotFound;
|
|
}
|
|
|
|
// verify action params
|
|
foreach (const ActionType &actionType, deviceClass.actionTypes()) {
|
|
if (actionType.id() == action.actionTypeId()) {
|
|
ParamList finalParams = action.toAction().params();
|
|
DeviceManager::DeviceError paramCheck = GuhCore::instance()->deviceManager()->verifyParams(actionType.paramTypes(), finalParams);
|
|
if (paramCheck != DeviceManager::DeviceErrorNoError)
|
|
return RuleErrorInvalidRuleActionParameter;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (exitActions.count() > 0)
|
|
qCDebug(dcRuleEngine) << "exit actions" << exitActions.last().actionTypeId() << exitActions.last().ruleActionParams();
|
|
|
|
Rule rule = Rule(ruleId, name, eventDescriptorList, stateEvaluator, actions, exitActions);
|
|
rule.setEnabled(enabled);
|
|
rule.setExecutable(executable);
|
|
appendRule(rule);
|
|
saveRule(rule);
|
|
if (!fromEdit)
|
|
emit ruleAdded(rule);
|
|
|
|
return RuleErrorNoError;
|
|
}
|
|
|
|
/*! Edit a \l{Rule} with the given \a ruleId, \a name, \a eventDescriptorList, \a stateEvaluator,
|
|
the list of \a actions, the list of \a exitActions, the \a enabled and the \a executable in the engine.
|
|
*/
|
|
RuleEngine::RuleError RuleEngine::editRule(const RuleId &ruleId, const QString &name, const QList<EventDescriptor> &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList<RuleAction> &actions, const QList<RuleAction> &exitActions, bool enabled, bool executable)
|
|
{
|
|
if (ruleId.isNull())
|
|
return RuleErrorInvalidRuleId;
|
|
|
|
|
|
// Store rule in case the add new rule fails
|
|
Rule rule = findRule(ruleId);
|
|
|
|
if (rule.id().isNull()) {
|
|
qCWarning(dcRuleEngine) << "Cannot edit rule. There is no rule with id:" << ruleId.toString();
|
|
return RuleErrorRuleNotFound;
|
|
}
|
|
|
|
// First remove old rule with this id
|
|
RuleError removeResult = removeRule(ruleId, true);
|
|
if (removeResult != RuleErrorNoError) {
|
|
// no need to restore, rule is still in system
|
|
return removeResult;
|
|
}
|
|
|
|
// The rule is removed, now add it with the same id and new vonfiguration
|
|
RuleError addResult = addRule(ruleId, name, eventDescriptorList, stateEvaluator, actions, exitActions, enabled, executable, true);
|
|
if (addResult != RuleErrorNoError) {
|
|
// restore rule
|
|
appendRule(rule);
|
|
return addResult;
|
|
}
|
|
|
|
emit ruleConfigurationChanged(m_rules.value(ruleId));
|
|
|
|
return RuleErrorNoError;
|
|
}
|
|
|
|
/*! Returns a list of all \l{Rule}{Rules} loaded in this Engine.
|
|
Be aware that this does not necessarily reflect the order of the rules in the engine.
|
|
Use ruleIds() if you need the correct order.
|
|
*/
|
|
QList<Rule> RuleEngine::rules() const
|
|
{
|
|
return m_rules.values();
|
|
}
|
|
|
|
/*! Returns a list of all ruleIds loaded in this Engine. */
|
|
QList<RuleId> RuleEngine::ruleIds() const
|
|
{
|
|
return m_ruleIds;
|
|
}
|
|
|
|
/*! Removes the \l{Rule} with the given \a ruleId from the Engine.
|
|
Returns \l{RuleError} which describes whether the operation
|
|
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);
|
|
|
|
if (index < 0) {
|
|
return RuleErrorRuleNotFound;
|
|
}
|
|
|
|
m_ruleIds.takeAt(index);
|
|
m_rules.remove(ruleId);
|
|
|
|
GuhSettings settings(GuhSettings::SettingsRoleRules);
|
|
settings.beginGroup(ruleId.toString());
|
|
settings.remove("");
|
|
settings.endGroup();
|
|
|
|
if (!fromEdit)
|
|
emit ruleRemoved(ruleId);
|
|
|
|
return RuleErrorNoError;
|
|
}
|
|
|
|
/*! Enables the rule with the given \a ruleId that has been previously disabled.
|
|
|
|
\sa disableRule()
|
|
*/
|
|
RuleEngine::RuleError RuleEngine::enableRule(const RuleId &ruleId)
|
|
{
|
|
if (!m_rules.contains(ruleId)) {
|
|
qCWarning(dcRuleEngine) << "Rule not found. Can't enable it";
|
|
return RuleErrorRuleNotFound;
|
|
}
|
|
Rule rule = m_rules.value(ruleId);
|
|
rule.setEnabled(true);
|
|
m_rules[ruleId] = rule;
|
|
|
|
saveRule(rule);
|
|
emit ruleConfigurationChanged(rule);
|
|
|
|
return RuleErrorNoError;
|
|
}
|
|
|
|
/*! Disables the rule with the given \a ruleId. Disabled rules won't be triggered.
|
|
|
|
\sa enableRule()
|
|
*/
|
|
RuleEngine::RuleError RuleEngine::disableRule(const RuleId &ruleId)
|
|
{
|
|
if (!m_rules.contains(ruleId)) {
|
|
qCWarning(dcRuleEngine) << "Rule not found. Can't disable it";
|
|
return RuleErrorRuleNotFound;
|
|
}
|
|
|
|
Rule rule = m_rules.value(ruleId);
|
|
rule.setEnabled(false);
|
|
m_rules[ruleId] = rule;
|
|
saveRule(rule);
|
|
emit ruleConfigurationChanged(rule);
|
|
return RuleErrorNoError;
|
|
}
|
|
|
|
/*! Executes the list of \l{Action}{Actions} of the rule with the given \a ruleId.
|
|
Returns the corresponding RuleEngine::RuleError to inform about the result.
|
|
|
|
\sa executeExitActions()
|
|
*/
|
|
RuleEngine::RuleError RuleEngine::executeActions(const RuleId &ruleId)
|
|
{
|
|
// check if rule exits
|
|
if (!m_rules.contains(ruleId)) {
|
|
qCWarning(dcRuleEngine) << "Not executing rule actions: rule not found.";
|
|
return RuleErrorRuleNotFound;
|
|
}
|
|
|
|
Rule rule = m_rules.value(ruleId);
|
|
|
|
// check if rule is executable
|
|
if (!rule.executable()) {
|
|
qCWarning(dcRuleEngine) << "Not executing rule actions: rule is not executable.";
|
|
return RuleErrorNotExecutable;
|
|
}
|
|
|
|
// check if an Action is eventBased
|
|
foreach (const RuleAction &ruleAction, rule.actions()) {
|
|
if (ruleAction.isEventBased()) {
|
|
qCWarning(dcRuleEngine) << "Not executing rule actions: rule action depends on an event:" << ruleAction.actionTypeId() << ruleAction.ruleActionParams();
|
|
return RuleErrorContainsEventBasesAction;
|
|
}
|
|
}
|
|
|
|
qCDebug(dcRuleEngine) << "Executing rule actions of rule" << rule.name() << rule.id();
|
|
GuhCore::instance()->executeRuleActions(rule.actions());
|
|
return RuleErrorNoError;
|
|
}
|
|
|
|
/*! Executes the list of \l{Action}{ExitActions} of the rule with the given \a ruleId.
|
|
Returns the corresponding RuleEngine::RuleError to inform about the result.
|
|
|
|
\sa executeActions()
|
|
*/
|
|
RuleEngine::RuleError RuleEngine::executeExitActions(const RuleId &ruleId)
|
|
{
|
|
// check if rule exits
|
|
if (!m_rules.contains(ruleId)) {
|
|
qCWarning(dcRuleEngine) << "Not executing rule exit actions: rule not found.";
|
|
return RuleErrorRuleNotFound;
|
|
}
|
|
|
|
Rule rule = m_rules.value(ruleId);
|
|
|
|
// check if rule is executable
|
|
if (!rule.executable()) {
|
|
qCWarning(dcRuleEngine) << "Not executing rule exit actions: rule is not executable.";
|
|
return RuleErrorNotExecutable;
|
|
}
|
|
|
|
if (rule.exitActions().isEmpty()) {
|
|
qCWarning(dcRuleEngine) << "Not executing rule exit actions: rule has no exit actions.";
|
|
return RuleErrorNoExitActions;
|
|
}
|
|
|
|
qCDebug(dcRuleEngine) << "Executing rule exit actions of rule" << rule.name() << rule.id();
|
|
GuhCore::instance()->executeRuleActions(rule.exitActions());
|
|
return RuleErrorNoError;
|
|
}
|
|
|
|
/*! Returns the \l{Rule} with the given \a ruleId. If the \l{Rule} does not exist, it will return \l{Rule::Rule()} */
|
|
Rule RuleEngine::findRule(const RuleId &ruleId)
|
|
{
|
|
foreach (const Rule &rule, m_rules) {
|
|
if (rule.id() == ruleId) {
|
|
return rule;
|
|
}
|
|
}
|
|
return Rule();
|
|
}
|
|
|
|
/*! Returns a list of all \l{Rule}{Rules} loaded in this Engine, which contains a \l{Device} with the given \a deviceId. */
|
|
QList<RuleId> RuleEngine::findRules(const DeviceId &deviceId)
|
|
{
|
|
// Find all offending rules
|
|
QList<RuleId> offendingRules;
|
|
foreach (const Rule &rule, m_rules) {
|
|
bool offending = false;
|
|
foreach (const EventDescriptor &eventDescriptor, rule.eventDescriptors()) {
|
|
if (eventDescriptor.deviceId() == deviceId) {
|
|
offending = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!offending && rule.stateEvaluator().containsDevice(deviceId))
|
|
offending = true;
|
|
|
|
if (!offending) {
|
|
foreach (const RuleAction &action, rule.actions()) {
|
|
if (action.deviceId() == deviceId) {
|
|
offending = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!offending) {
|
|
foreach (const RuleAction &action, rule.exitActions()) {
|
|
if (action.deviceId() == deviceId) {
|
|
offending = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (offending)
|
|
offendingRules.append(rule.id());
|
|
|
|
}
|
|
return offendingRules;
|
|
}
|
|
|
|
/*! Removes a \l{Device} from a \l{Rule} with the given \a id and \a deviceId. */
|
|
void RuleEngine::removeDeviceFromRule(const RuleId &id, const DeviceId &deviceId)
|
|
{
|
|
if (!m_rules.contains(id))
|
|
return;
|
|
|
|
Rule rule = m_rules.value(id);
|
|
|
|
// remove device from eventDescriptors
|
|
QList<EventDescriptor> eventDescriptors = rule.eventDescriptors();
|
|
QList<int> removeIndexes;
|
|
for (int i = 0; i < eventDescriptors.count(); i++) {
|
|
if (eventDescriptors.at(i).deviceId() == deviceId) {
|
|
removeIndexes.append(i);
|
|
}
|
|
}
|
|
while (removeIndexes.count() > 0) {
|
|
eventDescriptors.takeAt(removeIndexes.takeLast());
|
|
}
|
|
|
|
// remove device from state evaluators
|
|
StateEvaluator stateEvalatuator = rule.stateEvaluator();
|
|
stateEvalatuator.removeDevice(deviceId);
|
|
|
|
// remove device from actions
|
|
QList<RuleAction> actions = rule.actions();
|
|
for (int i = 0; i < actions.count(); i++) {
|
|
if (actions.at(i).deviceId() == deviceId) {
|
|
removeIndexes.append(i);
|
|
}
|
|
}
|
|
while (removeIndexes.count() > 0) {
|
|
actions.takeAt(removeIndexes.takeLast());
|
|
}
|
|
|
|
// remove device from exit actions
|
|
QList<RuleAction> exitActions = rule.exitActions();
|
|
for (int i = 0; i < exitActions.count(); i++) {
|
|
if (exitActions.at(i).deviceId() == deviceId) {
|
|
removeIndexes.append(i);
|
|
}
|
|
}
|
|
while (removeIndexes.count() > 0) {
|
|
exitActions.takeAt(removeIndexes.takeLast());
|
|
}
|
|
|
|
// remove the rule from settings
|
|
GuhSettings settings(GuhSettings::SettingsRoleRules);
|
|
settings.beginGroup(id.toString());
|
|
settings.remove("");
|
|
settings.endGroup();
|
|
|
|
Rule newRule(id, rule.name(), eventDescriptors, stateEvalatuator, actions);
|
|
m_rules[id] = newRule;
|
|
|
|
// save it
|
|
saveRule(newRule);
|
|
emit ruleConfigurationChanged(newRule);
|
|
}
|
|
|
|
bool RuleEngine::containsEvent(const Rule &rule, const Event &event)
|
|
{
|
|
foreach (const EventDescriptor &eventDescriptor, rule.eventDescriptors()) {
|
|
if (eventDescriptor == event) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool RuleEngine::containsState(const StateEvaluator &stateEvaluator, const Event &stateChangeEvent)
|
|
{
|
|
if (stateEvaluator.stateDescriptor().isValid() && stateEvaluator.stateDescriptor().stateTypeId().toString() == stateChangeEvent.eventTypeId().toString()) {
|
|
return true;
|
|
}
|
|
foreach (const StateEvaluator &childEvaluator, stateEvaluator.childEvaluators()) {
|
|
if (containsState(childEvaluator, stateChangeEvent)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
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
|
|
GuhSettings settings(GuhSettings::SettingsRoleRules);
|
|
settings.beginGroup(rule.id().toString());
|
|
settings.setValue("name", rule.name());
|
|
settings.setValue("enabled", rule.enabled());
|
|
settings.setValue("executable", rule.executable());
|
|
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();
|
|
}
|
|
|
|
}
|