/*! \class RuleEngine \brief The Engine that evaluates \l{Rule}{Rules} and finds \l{Action}{Actions} to be executed. \ingroup rules \inmodule server You can add, remove and update rules and query the engine for actions to be executed for a given \l{Event}. \sa Event, Rule, Action */ /*! \fn void RuleEngine::ruleAdded(const QUuid &ruleId) Will be emitted whenever a new \l{Rule} is added to this Engine. \a ruleId holds the id of the new rule.*/ /*! \fn void RuleEngine::ruleRemoved(const QUuid &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.*/ /*! \enum RuleEngine::RuleError \value RuleErrorNoError No error happened. Everything is fine. \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. */ #include "ruleengine.h" #include "hivecore.h" #include "devicemanager.h" #include "device.h" #include #include #include #include #include /*! Constructs the RuleEngine with the given \a parent. Although it wouldn't harm to have multiple RuleEngines, there is one instance available from \l{HiveCore}. This one should be used instead of creating multiple ones. */ RuleEngine::RuleEngine(QObject *parent) : QObject(parent) { m_settingsFile = QCoreApplication::instance()->organizationName() + "/rules"; qDebug() << "laoding rules from" << m_settingsFile; QSettings settings(m_settingsFile); foreach (const QString &idString, settings.childGroups()) { qDebug() << "found rule" << idString; settings.beginGroup(idString); settings.beginGroup("event"); Event event(settings.value("eventTypeId").toUuid(), settings.value("deviceId").toUuid(), settings.value("params").toMap()); settings.endGroup(); settings.beginGroup("states"); QList states; foreach (const QString &stateTypeIdString, settings.childGroups()) { settings.beginGroup(stateTypeIdString); State state(stateTypeIdString, settings.value("deviceId").toUuid()); state.setValue(settings.value("value")); settings.endGroup(); states.append(state); } settings.endGroup(); settings.beginGroup("actions"); QList actions; foreach (const QString &actionIdString, settings.childGroups()) { settings.beginGroup(actionIdString); Action action = Action(settings.value("deviceId").toUuid(), settings.value("actionTypeId").toUuid()); action.setParams(settings.value("params").toMap()); settings.endGroup(); actions.append(action); } settings.endGroup(); settings.endGroup(); Rule rule = Rule(QUuid(idString), event, states, actions); m_rules.append(rule); } } /*! Ask the Engine to evaluate all the rules for the given \a event. This will search all the \l{Rule}{Rules} evented by this \l{Event} and evaluate it's states according to its type. It will return a list of all \l{Action}{Actions} that should be executed. */ QList RuleEngine::evaluateEvent(const Event &event) { QList actions; for (int i = 0; i < m_rules.count(); ++i) { if (m_rules.at(i).event() == event) { bool statesMatching = true; qDebug() << "checking states"; foreach (const State &state, m_rules.at(i).states()) { Device *device = HiveCore::instance()->deviceManager()->findConfiguredDevice(state.deviceId()); if (!device) { qWarning() << "Device referenced in rule cannot be found"; break; } if (state.value() != device->stateValue(state.stateTypeId())) { statesMatching = false; break; } } qDebug() << "states matching" << statesMatching; if (statesMatching) { actions.append(m_rules.at(i).actions()); } } } qDebug() << "found" << actions.count() << "actions"; return actions; } /*! Add a new \l{Rule} with the given \a event and \a actions to the engine. For convenience, this creates a Rule without any \l{State} comparison. */ RuleEngine::RuleError RuleEngine::addRule(const Event &event, const QList &actions) { return addRule(event, QList(), actions); } /*! Add a new \l{Rule} with the given \a event, \a states and \a actions to the engine. */ RuleEngine::RuleError RuleEngine::addRule(const Event &event, const QList &states, const QList &actions) { qDebug() << "adding rule: Event:" << event.eventTypeId() << "with" << actions.count() << "actions"; DeviceClass eventDeviceClass = HiveCore::instance()->deviceManager()->findDeviceClassforEvent(event.eventTypeId()); Device *device = HiveCore::instance()->deviceManager()->findConfiguredDevice(event.deviceId()); if (!device) { qWarning() << "Cannot create rule. No configured device for eventTypeId" << event.eventTypeId(); return RuleErrorDeviceNotFound; } DeviceClass deviceClass = HiveCore::instance()->deviceManager()->findDeviceClass(device->deviceClassId()); qDebug() << "found deviceClass" << deviceClass.name(); bool eventTypeFound = false; foreach (const EventType &eventType, deviceClass.events()) { if (eventType.id() == event.eventTypeId()) { eventTypeFound = true; } } if (!eventTypeFound) { qWarning() << "Cannot create rule. Device " + device->name() + " has no event type:" << event.eventTypeId(); return RuleErrorEventTypeNotFound; } Rule rule = Rule(QUuid::createUuid(), event, states, actions); m_rules.append(rule); emit ruleAdded(rule.id()); QSettings settings(m_settingsFile); settings.beginGroup(rule.id().toString()); settings.beginGroup("event"); settings.setValue("eventTypeId", event.eventTypeId()); settings.setValue("deviceId", event.deviceId()); settings.setValue("params", event.params()); settings.endGroup(); settings.beginGroup("states"); foreach (const State &state, states) { settings.beginGroup(state.stateTypeId().toString()); settings.setValue("deviceId", state.deviceId()); settings.setValue("value", state.value()); settings.endGroup(); } settings.endGroup(); settings.beginGroup("actions"); foreach (const Action &action, rule.actions()) { settings.beginGroup(action.actionTypeId().toString()); settings.setValue("deviceId", action.deviceId()); settings.setValue("actionTypeId", action.actionTypeId()); settings.setValue("params", action.params()); settings.endGroup(); } settings.endGroup(); return RuleErrorNoError; } /*! Returns a list of all \l{Rule}{Rules} loaded in this Engine.*/ QList RuleEngine::rules() const { return m_rules; } /*! Removes the \l{Rule} with the given \a ruleId from the Engine. Returns \l{RuleEngine::RuleError} which describes whether the operation was successful or not. */ RuleEngine::RuleError RuleEngine::removeRule(const QUuid &ruleId) { for (int i = 0; i < m_rules.count(); ++i) { Rule rule = m_rules.at(i); if (rule.id() == ruleId) { m_rules.takeAt(i); QSettings settings(m_settingsFile); settings.beginGroup(rule.id().toString()); settings.remove(""); settings.endGroup(); emit ruleRemoved(rule.id()); return RuleErrorNoError; } } return RuleErrorRuleNotFound; }