From abd2305bc005a96450b8787ff64bce6c566a17fe Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Wed, 20 Jun 2018 19:42:43 +0200 Subject: [PATCH] add support for paramTypes by name --- libnymea-core/jsonrpc/jsontypes.cpp | 21 +++- libnymea-core/jsonrpc/ruleshandler.cpp | 4 + libnymea-core/rule.cpp | 16 +++ libnymea-core/rule.h | 2 + libnymea-core/ruleengine.cpp | 152 +++++++++++++++++++------ libnymea-core/stateevaluator.cpp | 15 ++- libnymea-core/stateevaluator.h | 2 + libnymea-core/time/calendaritem.cpp | 8 ++ libnymea-core/time/calendaritem.h | 1 + libnymea-core/time/repeatingoption.cpp | 6 + libnymea-core/time/repeatingoption.h | 1 + libnymea-core/time/timedescriptor.cpp | 14 +++ libnymea-core/time/timedescriptor.h | 2 + libnymea-core/time/timeeventitem.cpp | 8 ++ libnymea-core/time/timeeventitem.h | 2 + libnymea/types/eventdescriptor.cpp | 13 ++- libnymea/types/paramdescriptor.cpp | 24 ++++ libnymea/types/paramdescriptor.h | 5 + libnymea/types/ruleaction.cpp | 18 +++ libnymea/types/ruleaction.h | 3 + libnymea/types/ruleactionparam.cpp | 4 +- libnymea/types/statedescriptor.cpp | 8 ++ libnymea/types/statedescriptor.h | 3 +- plugins/mock/httpdaemon.cpp | 9 +- tests/auto/rules/testrules.cpp | 117 ++++++++++++++++++- tests/scripts/getruledetails.sh | 1 + 26 files changed, 404 insertions(+), 55 deletions(-) diff --git a/libnymea-core/jsonrpc/jsontypes.cpp b/libnymea-core/jsonrpc/jsontypes.cpp index 0cead778..5c641508 100644 --- a/libnymea-core/jsonrpc/jsontypes.cpp +++ b/libnymea-core/jsonrpc/jsontypes.cpp @@ -182,7 +182,8 @@ void JsonTypes::init() s_ruleActionParam.insert("o:eventParamTypeId", basicTypeToString(Uuid)); // ParamDescriptor - s_paramDescriptor.insert("paramTypeId", basicTypeToString(Uuid)); + s_paramDescriptor.insert("o:paramTypeId", basicTypeToString(Uuid)); + s_paramDescriptor.insert("o:paramName", basicTypeToString(Uuid)); s_paramDescriptor.insert("value", basicTypeRef()); s_paramDescriptor.insert("operator", valueOperatorRef()); @@ -683,7 +684,11 @@ QVariantMap JsonTypes::packParam(const Param ¶m) QVariantMap JsonTypes::packParamDescriptor(const ParamDescriptor ¶mDescriptor) { QVariantMap variantMap; - variantMap.insert("paramTypeId", paramDescriptor.paramTypeId().toString()); + if (!paramDescriptor.paramTypeId().isNull()) { + variantMap.insert("paramTypeId", paramDescriptor.paramTypeId().toString()); + } else { + variantMap.insert("paramName", paramDescriptor.paramName()); + } variantMap.insert("value", paramDescriptor.value()); variantMap.insert("operator", s_valueOperator.at(paramDescriptor.operatorType())); return variantMap; @@ -1389,13 +1394,19 @@ RuleActionParamList JsonTypes::unpackRuleActionParams(const QVariantList &ruleAc /*! Returns a \l{ParamDescriptor} created from the given \a paramMap. */ ParamDescriptor JsonTypes::unpackParamDescriptor(const QVariantMap ¶mMap) { - ParamDescriptor param(ParamTypeId(paramMap.value("paramTypeId").toString()), paramMap.value("value")); QString operatorString = paramMap.value("operator").toString(); - QMetaObject metaObject = Types::staticMetaObject; int enumIndex = metaObject.indexOfEnumerator("ValueOperator"); QMetaEnum metaEnum = metaObject.enumerator(enumIndex); - param.setOperatorType((Types::ValueOperator)metaEnum.keyToValue(operatorString.toLatin1().data())); + Types::ValueOperator valueOperator = (Types::ValueOperator)metaEnum.keyToValue(operatorString.toLatin1().data()); + + if (paramMap.contains("paramTypeId")) { + ParamDescriptor param = ParamDescriptor(ParamTypeId(paramMap.value("paramTypeId").toString()), paramMap.value("value")); + param.setOperatorType(valueOperator); + return param; + } + ParamDescriptor param = ParamDescriptor(paramMap.value("paramName").toString(), paramMap.value("value")); + param.setOperatorType(valueOperator); return param; } diff --git a/libnymea-core/jsonrpc/ruleshandler.cpp b/libnymea-core/jsonrpc/ruleshandler.cpp index 41643f45..b20187a9 100644 --- a/libnymea-core/jsonrpc/ruleshandler.cpp +++ b/libnymea-core/jsonrpc/ruleshandler.cpp @@ -58,6 +58,7 @@ #include "loggingcategories.h" #include +#include namespace nymeaserver { @@ -215,6 +216,9 @@ JsonReply *RulesHandler::GetRuleDetails(const QVariantMap ¶ms) return createReply(statusToReply(RuleEngine::RuleErrorRuleNotFound)); } QVariantMap returns = statusToReply(RuleEngine::RuleErrorNoError); + qWarning() << "Have rule" << rule; + QJsonDocument jsonDoc = QJsonDocument::fromVariant(JsonTypes::packRule(rule)); + qWarning() << "packed:" << jsonDoc.toJson(); returns.insert("rule", JsonTypes::packRule(rule)); return createReply(returns); } diff --git a/libnymea-core/rule.cpp b/libnymea-core/rule.cpp index ae927540..ab54e4a0 100644 --- a/libnymea-core/rule.cpp +++ b/libnymea-core/rule.cpp @@ -230,4 +230,20 @@ void Rule::setActive(const bool &active) m_active = active; } +QDebug operator<<(QDebug dbg, const Rule &rule) +{ + dbg.nospace() << endl << "=== Rule begin ===" << endl; + dbg.nospace() << "ID:" << rule.id().toString() << endl; + dbg.nospace() << "Name:" << rule.name() << endl; + dbg.nospace() << "Enabled:" << rule.enabled() << endl; + dbg.nospace() << "Active:" << rule.active() << endl; + dbg.nospace() << rule.eventDescriptors(); + dbg.nospace() << rule.timeDescriptor(); + dbg.nospace() << rule.stateEvaluator(); + dbg.nospace() << "Actions:" << rule.actions(); + dbg.nospace() << "ExitActions:" << rule.exitActions(); + dbg.nospace() << "=== Rule end ==="; + return dbg.space(); +} + } diff --git a/libnymea-core/rule.h b/libnymea-core/rule.h index 82dd0746..6a688e6e 100644 --- a/libnymea-core/rule.h +++ b/libnymea-core/rule.h @@ -94,6 +94,8 @@ private: bool m_executable; }; +QDebug operator<<(QDebug dbg, const Rule &rule); + } #endif // RULE_H diff --git a/libnymea-core/ruleengine.cpp b/libnymea-core/ruleengine.cpp index 599901b7..7612f7ec 100644 --- a/libnymea-core/ruleengine.cpp +++ b/libnymea-core/ruleengine.cpp @@ -227,20 +227,34 @@ RuleEngine::RuleEngine(QObject *parent) : settings.beginGroup(eventGroupName); EventTypeId eventTypeId(settings.value("eventTypeId").toString()); DeviceId deviceId(settings.value("deviceId").toString()); + QString interface = settings.value("interface").toString(); + QString interfaceEvent = settings.value("interfaceEvent").toString(); QList params; foreach (QString groupName, settings.childGroups()) { if (groupName.startsWith("ParamDescriptor-")) { settings.beginGroup(groupName); - ParamDescriptor paramDescriptor(ParamTypeId(groupName.remove(QRegExp("^ParamDescriptor-"))), settings.value("value")); - paramDescriptor.setOperatorType((Types::ValueOperator)settings.value("operator").toInt()); - params.append(paramDescriptor); + QString strippedGroupName = groupName.remove(QRegExp("^ParamDescriptor-")); + if (!ParamTypeId(strippedGroupName).isNull()) { + ParamDescriptor paramDescriptor(ParamTypeId(strippedGroupName), settings.value("value")); + paramDescriptor.setOperatorType((Types::ValueOperator)settings.value("operator").toInt()); + params.append(paramDescriptor); + } else { + ParamDescriptor paramDescriptor(strippedGroupName, settings.value("value")); + paramDescriptor.setOperatorType((Types::ValueOperator)settings.value("operator").toInt()); + params.append(paramDescriptor); + } settings.endGroup(); } } - EventDescriptor eventDescriptor(eventTypeId, deviceId, params); - eventDescriptorList.append(eventDescriptor); + if (!eventTypeId.isNull()) { + EventDescriptor eventDescriptor(eventTypeId, deviceId, params); + eventDescriptorList.append(eventDescriptor); + } else { + EventDescriptor eventDescriptor(interface, interfaceEvent, params); + eventDescriptorList.append(eventDescriptor); + } settings.endGroup(); } } @@ -256,24 +270,40 @@ RuleEngine::RuleEngine(QObject *parent) : 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 paramTypeIdString, settings.childGroups()) { if (paramTypeIdString.startsWith("RuleActionParam-")) { settings.beginGroup(paramTypeIdString); - RuleActionParam param(ParamTypeId(paramTypeIdString.remove(QRegExp("^RuleActionParam-"))), - settings.value("value",QVariant()), - EventTypeId(settings.value("eventTypeId", EventTypeId()).toString()), - settings.value("eventParamTypeId", ParamTypeId()).toString()); - params.append(param); + QString strippedParamTypeIdString = paramTypeIdString.remove(QRegExp("^RuleActionParam-")); + EventTypeId eventTypeId = EventTypeId(settings.value("eventTypeId", EventTypeId()).toString()); + ParamTypeId eventParamTypeId = ParamTypeId(settings.value("eventParamTypeId", ParamTypeId()).toString()); + QVariant value = settings.value("value"); + if (!ParamTypeId(strippedParamTypeIdString).isNull()) { + RuleActionParam param(ParamTypeId(strippedParamTypeIdString), + value, + eventTypeId, + eventParamTypeId); + params.append(param); + } else { + RuleActionParam param(strippedParamTypeIdString, + value, + eventTypeId, + eventParamTypeId); + params.append(param); + } settings.endGroup(); } } - action.setRuleActionParams(params); - actions.append(action); + if (settings.contains("actionTypeId") && settings.contains("deviceId")) { + RuleAction action = RuleAction(ActionTypeId(settings.value("actionTypeId").toString()), DeviceId(settings.value("deviceId").toString())); + action.setRuleActionParams(params); + actions.append(action); + } else if (settings.contains("interface") && settings.contains("interfaceAction")){ + RuleAction action = RuleAction(settings.value("interface").toString(), settings.value("interfaceAction").toString()); + action.setRuleActionParams(params); + actions.append(action); + } settings.endGroup(); } @@ -285,21 +315,33 @@ RuleEngine::RuleEngine(QObject *parent) : 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 paramTypeIdString, settings.childGroups()) { if (paramTypeIdString.startsWith("RuleActionParam-")) { settings.beginGroup(paramTypeIdString); - RuleActionParam param(ParamTypeId(paramTypeIdString.remove(QRegExp("^RuleActionParam-"))), - settings.value("value")); - params.append(param); + QString strippedParamTypeIdString = paramTypeIdString.remove(QRegExp("^RuleActionParam-")); + QVariant value = settings.value("value"); + if (!ParamTypeId(strippedParamTypeIdString).isNull()) { + RuleActionParam param(ParamTypeId(strippedParamTypeIdString), value); + params.append(param); + } else { + RuleActionParam param(strippedParamTypeIdString, value); + params.append(param); + } settings.endGroup(); } } - action.setRuleActionParams(params); - exitActions.append(action); + + if (settings.contains("actionTypeId") && settings.contains("deviceId")) { + RuleAction action = RuleAction(ActionTypeId(settings.value("actionTypeId").toString()), DeviceId(settings.value("deviceId").toString())); + action.setRuleActionParams(params); + exitActions.append(action); + } else if (settings.contains("interface") && settings.contains("interfaceAction")) { + RuleAction action = RuleAction(settings.value("interface").toString(),settings.value("interfaceAction").toString()); + action.setRuleActionParams(params); + exitActions.append(action); + } + settings.endGroup(); } settings.endGroup(); @@ -730,7 +772,6 @@ RuleEngine::RuleError RuleEngine::addRule(const Rule &rule, bool fromEdit) emit ruleAdded(rule); qCDebug(dcRuleEngine()) << "Rule" << rule.name() << rule.id().toString() << "added successfully."; - return RuleErrorNoError; } @@ -1004,12 +1045,12 @@ QList RuleEngine::devicesInRules() const } } foreach (const RuleAction &action, rule.actions()) { - if (!tmp.contains(action.deviceId())) { + if (!tmp.contains(action.deviceId()) && !action.deviceId().isNull()) { tmp.append(action.deviceId()); } } foreach (const RuleAction &exitAction, rule.exitActions()) { - if (!tmp.contains(exitAction.deviceId())) { + if (!tmp.contains(exitAction.deviceId()) && !exitAction.deviceId().isNull()) { tmp.append(exitAction.deviceId()); } } @@ -1111,14 +1152,29 @@ bool RuleEngine::containsEvent(const Rule &rule, const Event &event, const Devic // Ok, either device/eventTypeId or interface/interfaceEvent are matching. Compare the paramdescriptor bool allOK = true; foreach (const ParamDescriptor ¶mDescriptor, eventDescriptor.paramDescriptors()) { + QVariant paramValue; + if (!paramDescriptor.paramTypeId().isNull()) { + paramValue = event.param(paramDescriptor.paramTypeId()).value(); + } else { + if (paramDescriptor.paramName().isEmpty()) { + qWarning(dcRuleEngine()) << "ParamDescriptor invalid. Either paramTypeId or paramName are required"; + allOK = false; + continue; + } + DeviceClass dc = NymeaCore::instance()->deviceManager()->findDeviceClass(deviceClassId); + EventType et = dc.eventTypes().findById(event.eventTypeId()); + ParamType pt = et.paramTypes().findByName(paramDescriptor.paramName()); + paramValue = event.param(pt.id()).value(); + } + switch (paramDescriptor.operatorType()) { case Types::ValueOperatorEquals: - if (event.param(paramDescriptor.paramTypeId()).value() != paramDescriptor.value()) { + if (paramValue != paramDescriptor.value()) { allOK = false; } break; case Types::ValueOperatorNotEquals: - if (event.param(paramDescriptor.paramTypeId()).value() == paramDescriptor.value()) { + if (paramValue == paramDescriptor.value()) { allOK = false; } break; @@ -1227,6 +1283,7 @@ void RuleEngine::appendRule(const Rule &rule) { Rule newRule = rule; newRule.setStatesActive(newRule.stateEvaluator().evaluate()); + qCDebug(dcRuleEngineDebug()) << "Appending new Rule:" << newRule; m_rules.insert(rule.id(), newRule); m_ruleIds.append(rule.id()); } @@ -1318,9 +1375,15 @@ void RuleEngine::saveRule(const Rule &rule) settings.beginGroup("EventDescriptor-" + QString::number(i)); settings.setValue("deviceId", eventDescriptor.deviceId().toString()); settings.setValue("eventTypeId", eventDescriptor.eventTypeId().toString()); + settings.setValue("interface", eventDescriptor.interface()); + settings.setValue("interfaceEvent", eventDescriptor.interfaceEvent()); foreach (const ParamDescriptor ¶mDescriptor, eventDescriptor.paramDescriptors()) { - settings.beginGroup("ParamDescriptor-" + paramDescriptor.paramTypeId().toString()); + if (!paramDescriptor.paramTypeId().isNull()) { + settings.beginGroup("ParamDescriptor-" + paramDescriptor.paramTypeId().toString()); + } else { + settings.beginGroup("ParamDescriptor-" + paramDescriptor.paramName()); + } settings.setValue("value", paramDescriptor.value()); settings.setValue("operator", paramDescriptor.operatorType()); settings.endGroup(); @@ -1337,10 +1400,19 @@ void RuleEngine::saveRule(const Rule &rule) 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()); + if (!action.deviceId().isNull() && !action.actionTypeId().isNull()) { + settings.setValue("deviceId", action.deviceId().toString()); + settings.setValue("actionTypeId", action.actionTypeId().toString()); + } else { + settings.setValue("interface", action.interface()); + settings.setValue("interfaceAction", action.interfaceAction()); + } foreach (const RuleActionParam ¶m, action.ruleActionParams()) { - settings.beginGroup("RuleActionParam-" + param.paramTypeId().toString()); + if (!param.paramTypeId().isNull()) { + settings.beginGroup("RuleActionParam-" + param.paramTypeId().toString()); + } else { + settings.beginGroup("RuleActionParam-" + param.paramName()); + } settings.setValue("value", param.value()); if (param.eventTypeId() != EventTypeId()) { settings.setValue("eventTypeId", param.eventTypeId().toString()); @@ -1358,10 +1430,19 @@ void RuleEngine::saveRule(const Rule &rule) 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()); + if (!action.deviceId().isNull() && !action.actionTypeId().isNull()) { + settings.setValue("deviceId", action.deviceId().toString()); + settings.setValue("actionTypeId", action.actionTypeId().toString()); + } else { + settings.setValue("interface", action.interface()); + settings.setValue("interfaceAction", action.interfaceAction()); + } foreach (const RuleActionParam ¶m, action.ruleActionParams()) { - settings.beginGroup("RuleActionParam-" + param.paramTypeId().toString()); + if (!param.paramTypeId().isNull()) { + settings.beginGroup("RuleActionParam-" + param.paramTypeId().toString()); + } else { + settings.beginGroup("RuleActionParam-" + param.paramName()); + } settings.setValue("value", param.value()); settings.endGroup(); } @@ -1369,6 +1450,7 @@ void RuleEngine::saveRule(const Rule &rule) settings.endGroup(); } settings.endGroup(); + qWarning() << "#### Saved rule to config:" << rule; } } diff --git a/libnymea-core/stateevaluator.cpp b/libnymea-core/stateevaluator.cpp index de9e1129..ee86602b 100644 --- a/libnymea-core/stateevaluator.cpp +++ b/libnymea-core/stateevaluator.cpp @@ -197,7 +197,9 @@ void StateEvaluator::removeDevice(const DeviceId &deviceId) QList StateEvaluator::containedDevices() const { QList ret; - ret.append(m_stateDescriptor.deviceId()); + if (!m_stateDescriptor.deviceId().isNull()) { + ret.append(m_stateDescriptor.deviceId()); + } foreach (const StateEvaluator &childEvaluator, m_childEvaluators) { ret.append(childEvaluator.containedDevices()); } @@ -208,6 +210,7 @@ QList StateEvaluator::containedDevices() const The \a groupName will normally be the corresponding \l Rule. */ void StateEvaluator::dumpToSettings(NymeaSettings &settings, const QString &groupName) const { + qWarning() << "Dumping to settings:" << groupName; settings.beginGroup(groupName); settings.beginGroup("stateDescriptor"); @@ -260,6 +263,7 @@ StateEvaluator StateEvaluator::loadFromSettings(NymeaSettings &settings, const Q } settings.endGroup(); settings.endGroup(); + qWarning() << "*** loading from settings" << groupName << ret; return ret; } @@ -344,4 +348,13 @@ bool StateEvaluator::isValid() const return true; } +QDebug operator<<(QDebug dbg, const StateEvaluator &stateEvaluator) +{ + dbg.nospace() << "StateEvaluator: Operator:" << stateEvaluator.operatorType() << endl << " " << stateEvaluator.stateDescriptor() << endl; + for (int i = 0; i < stateEvaluator.childEvaluators().count(); i++) { + dbg.nospace() << " " << i << ": " << stateEvaluator.childEvaluators().at(i); + } + return dbg; +} + } diff --git a/libnymea-core/stateevaluator.h b/libnymea-core/stateevaluator.h index 3f1e54eb..69a05fcb 100644 --- a/libnymea-core/stateevaluator.h +++ b/libnymea-core/stateevaluator.h @@ -64,6 +64,8 @@ private: Types::StateOperator m_operatorType; }; +QDebug operator<<(QDebug dbg, const StateEvaluator &stateEvaluator); + } #endif // STATEEVALUATOR_H diff --git a/libnymea-core/time/calendaritem.cpp b/libnymea-core/time/calendaritem.cpp index bfbef064..5ab94499 100644 --- a/libnymea-core/time/calendaritem.cpp +++ b/libnymea-core/time/calendaritem.cpp @@ -31,6 +31,8 @@ #include "calendaritem.h" #include "loggingcategories.h" +#include + namespace nymeaserver { /*! Construct a invalid \l{CalendarItem}. */ @@ -266,5 +268,11 @@ bool CalendarItem::evaluateYearly(const QDateTime &dateTime) const return false; } +QDebug operator<<(QDebug dbg, const CalendarItem &calendarItem) +{ + dbg.nospace() << "CalendarItem (StartTime:" << calendarItem.startTime() << ", DateTime:" << calendarItem.dateTime().toString() << ", " << calendarItem.repeatingOption() << ", Duration:" << calendarItem.duration() << ")"; + return dbg; +} + } diff --git a/libnymea-core/time/calendaritem.h b/libnymea-core/time/calendaritem.h index ea7c74c5..c99798b4 100644 --- a/libnymea-core/time/calendaritem.h +++ b/libnymea-core/time/calendaritem.h @@ -63,6 +63,7 @@ private: }; +QDebug operator<<(QDebug dbg, const CalendarItem &calendarItem); } #endif // CALENDARITEM_H diff --git a/libnymea-core/time/repeatingoption.cpp b/libnymea-core/time/repeatingoption.cpp index d3fb2d32..78e201fd 100644 --- a/libnymea-core/time/repeatingoption.cpp +++ b/libnymea-core/time/repeatingoption.cpp @@ -191,4 +191,10 @@ bool RepeatingOption::evaluateMonthDay(const QDateTime &dateTime) const return false; } +QDebug operator<<(QDebug dbg, const RepeatingOption &repeatingOption) +{ + dbg.nospace() << "RepeatingOption(Mode:" << repeatingOption.mode() << ", Monthdays:" << repeatingOption.monthDays() << "Weekdays:" << repeatingOption.weekDays() << ")"; + return dbg; +} + } diff --git a/libnymea-core/time/repeatingoption.h b/libnymea-core/time/repeatingoption.h index 360fc7c6..74cfdfc2 100644 --- a/libnymea-core/time/repeatingoption.h +++ b/libnymea-core/time/repeatingoption.h @@ -65,6 +65,7 @@ private: }; +QDebug operator<<(QDebug dbg, const RepeatingOption &RepeatingOption); } #endif // REPEATINGOPTION_H diff --git a/libnymea-core/time/timedescriptor.cpp b/libnymea-core/time/timedescriptor.cpp index 16cf8a1b..854b75a7 100644 --- a/libnymea-core/time/timedescriptor.cpp +++ b/libnymea-core/time/timedescriptor.cpp @@ -34,6 +34,8 @@ #include "timedescriptor.h" +#include + namespace nymeaserver { /*! Constructs an invalid \l{TimeDescriptor}.*/ @@ -105,4 +107,16 @@ bool TimeDescriptor::evaluate(const QDateTime &lastEvaluationTime, const QDateTi return false; } +QDebug operator<<(QDebug dbg, const TimeDescriptor &timeDescriptor) +{ + dbg.nospace() << "TimeDescriptor (TimeEventItems:" << timeDescriptor.timeEventItems().count() << ", CalendarItems:" << timeDescriptor.calendarItems().count() << ")" << endl; + for (int i = 0; i < timeDescriptor.timeEventItems().count(); i++) { + dbg.nospace() << " " << i << ": " << timeDescriptor.timeEventItems().at(i); + } + for (int i = 0; i < timeDescriptor.calendarItems().count(); i++) { + dbg.nospace() << " " << i << ": " << timeDescriptor.calendarItems().at(i); + } + return dbg; +} + } diff --git a/libnymea-core/time/timedescriptor.h b/libnymea-core/time/timedescriptor.h index 48a8996b..94efcc7c 100644 --- a/libnymea-core/time/timedescriptor.h +++ b/libnymea-core/time/timedescriptor.h @@ -52,6 +52,8 @@ private: }; +QDebug operator<<(QDebug dbg, const TimeDescriptor &timeDescriptor); + } #endif // TIMEDESCRIPTOR_H diff --git a/libnymea-core/time/timeeventitem.cpp b/libnymea-core/time/timeeventitem.cpp index 877afc81..d3ffe7e4 100644 --- a/libnymea-core/time/timeeventitem.cpp +++ b/libnymea-core/time/timeeventitem.cpp @@ -31,6 +31,8 @@ #include "timeeventitem.h" +#include + namespace nymeaserver { /*! Constructs an invalid \l{TimeEventItem}. */ @@ -132,4 +134,10 @@ bool TimeEventItem::evaluate(const QDateTime &lastEvaluationTime, const QDateTim return lastEvaluationTime < m_dateTime && m_dateTime <= dateTime; } +QDebug operator<<(QDebug dbg, const TimeEventItem &timeEventItem) +{ + dbg.nospace() << "TimeEventItem (Time:" << timeEventItem.time() << ", DateTime:" << timeEventItem.dateTime().toString() << ", " << timeEventItem.repeatingOption() << ")" << endl; + return dbg; +} + } diff --git a/libnymea-core/time/timeeventitem.h b/libnymea-core/time/timeeventitem.h index 03501923..c91f0be2 100644 --- a/libnymea-core/time/timeeventitem.h +++ b/libnymea-core/time/timeeventitem.h @@ -54,6 +54,8 @@ private: RepeatingOption m_repeatingOption; }; +QDebug operator<<(QDebug dbg, const TimeEventItem &timeEventItem); + } #endif // TIMEEVENTITEM_H diff --git a/libnymea/types/eventdescriptor.cpp b/libnymea/types/eventdescriptor.cpp index 64f78768..31fa4347 100644 --- a/libnymea/types/eventdescriptor.cpp +++ b/libnymea/types/eventdescriptor.cpp @@ -146,18 +146,21 @@ bool EventDescriptor::operator ==(const EventDescriptor &other) const /*! Writes the eventTypeId and the deviceId of the given \a eventDescriptor to \a dbg. */ QDebug operator<<(QDebug dbg, const EventDescriptor &eventDescriptor) { - dbg.nospace() << "EventDescriptor(EventTypeId: " << eventDescriptor.eventTypeId().toString() << ", DeviceId" << eventDescriptor.deviceId() << ")"; + dbg.nospace() << "EventDescriptor(EventTypeId: " << eventDescriptor.eventTypeId().toString() << ", DeviceId:" << eventDescriptor.deviceId().toString() << ", Interface:" << eventDescriptor.interface() << ", InterfaceEvent:" << eventDescriptor.interfaceEvent() << ")" << endl; + for (int i = 0; i < eventDescriptor.paramDescriptors().count(); i++) { + dbg.nospace() << " " << i << ": " << eventDescriptor.paramDescriptors().at(i); + } - return dbg.space(); + return dbg; } /*! Writes each \a eventDescriptors to \a dbg. */ QDebug operator<<(QDebug dbg, const QList &eventDescriptors) { - dbg.nospace() << "EventDescriptorList (count:" << eventDescriptors.count() << ")"; + dbg.nospace() << "EventDescriptorList (count:" << eventDescriptors.count() << "):" << endl; for (int i = 0; i < eventDescriptors.count(); i++ ) { - dbg.nospace() << " " << i << ": " << eventDescriptors.at(i); + dbg.nospace() << " " << i << ": " << eventDescriptors.at(i); } - return dbg.space(); + return dbg; } diff --git a/libnymea/types/paramdescriptor.cpp b/libnymea/types/paramdescriptor.cpp index 2ddfa510..0855cc40 100644 --- a/libnymea/types/paramdescriptor.cpp +++ b/libnymea/types/paramdescriptor.cpp @@ -36,6 +36,8 @@ #include "paramdescriptor.h" +#include + /*! Constructs an ParamDescriptor describing an \l{Param} with the given \a paramTypeId and \a value. * The ValueOperator is by default ValueOperatorEquals. */ ParamDescriptor::ParamDescriptor(const ParamTypeId ¶mTypeId, const QVariant &value): @@ -44,6 +46,22 @@ ParamDescriptor::ParamDescriptor(const ParamTypeId ¶mTypeId, const QVariant { } +/*! Constructs an ParamDescriptor describing an \l{Param} with the given \a paramTypeId and \a value. + * The ValueOperator is by default ValueOperatorEquals. */ +ParamDescriptor::ParamDescriptor(const QString ¶mName, const QVariant &value): + Param(ParamTypeId(), value), + m_paramName(paramName), + m_operatorType(Types::ValueOperatorEquals) +{ + +} + +/*! Returns the paramName of this ParamDescriptor. */ +QString ParamDescriptor::paramName() const +{ + return m_paramName; +} + /*! Returns the ValueOperator of this ParamDescriptor. */ Types::ValueOperator ParamDescriptor::operatorType() const { @@ -56,3 +74,9 @@ void ParamDescriptor::setOperatorType(Types::ValueOperator operatorType) m_operatorType = operatorType; } + +QDebug operator<<(QDebug dbg, const ParamDescriptor ¶mDescriptor) +{ + dbg.nospace() << "ParamDescriptor(ParamTypeId: " << paramDescriptor.paramTypeId().toString() << ", Name:" << paramDescriptor.paramName() << ", Value:" << paramDescriptor.value() << ")" << endl; + return dbg; +} diff --git a/libnymea/types/paramdescriptor.h b/libnymea/types/paramdescriptor.h index 24fcb5e7..0c038069 100644 --- a/libnymea/types/paramdescriptor.h +++ b/libnymea/types/paramdescriptor.h @@ -33,12 +33,17 @@ class LIBNYMEA_EXPORT ParamDescriptor : public Param { public: ParamDescriptor(const ParamTypeId ¶mTypeId, const QVariant &value = QVariant()); + ParamDescriptor(const QString ¶mName, const QVariant &value = QVariant()); + QString paramName() const; Types::ValueOperator operatorType() const; void setOperatorType(Types::ValueOperator operatorType); private: + QString m_paramName; Types::ValueOperator m_operatorType; }; +QDebug operator<<(QDebug dbg, const ParamDescriptor ¶mDescriptor); + #endif // PARAMDESCRIPTOR_H diff --git a/libnymea/types/ruleaction.cpp b/libnymea/types/ruleaction.cpp index 560f3deb..d5aba1a8 100644 --- a/libnymea/types/ruleaction.cpp +++ b/libnymea/types/ruleaction.cpp @@ -190,3 +190,21 @@ void RuleAction::operator=(const RuleAction &other) m_actionTypeId = other.actionTypeId(); m_ruleActionParams = other.ruleActionParams(); } + +QDebug operator<<(QDebug dbg, const RuleAction &ruleAction) +{ + dbg.nospace() << "RuleAction(ActionTypeId:" << ruleAction.actionTypeId().toString() << ", DeviceId:" << ruleAction.deviceId().toString() << ", Interface:" << ruleAction.interface() << ", InterfaceAction:" << ruleAction.interfaceAction() << ")" << endl; + for (int i = 0; i < ruleAction.ruleActionParams().count(); i++) { + dbg.nospace() << " " << i << ": " << ruleAction.ruleActionParams().at(i) << endl; + } + return dbg; +} + +QDebug operator<<(QDebug dbg, const QList &ruleActionList) +{ + dbg.nospace() << "RuleActionList (count:" << ruleActionList.count() << "):" << endl; + for (int i = 0; i < ruleActionList.count(); i++ ) { + dbg.nospace() << " " << i << ": " << ruleActionList.at(i); + } + return dbg; +} diff --git a/libnymea/types/ruleaction.h b/libnymea/types/ruleaction.h index 3d31e791..75b6d72d 100644 --- a/libnymea/types/ruleaction.h +++ b/libnymea/types/ruleaction.h @@ -70,4 +70,7 @@ private: RuleActionParamList m_ruleActionParams; }; +QDebug operator<<(QDebug dbg, const RuleAction &ruleAction); +QDebug operator<<(QDebug dbg, const QList &ruleActionList); + #endif // RULEACTION_H diff --git a/libnymea/types/ruleactionparam.cpp b/libnymea/types/ruleactionparam.cpp index 878d19fa..a60943de 100644 --- a/libnymea/types/ruleactionparam.cpp +++ b/libnymea/types/ruleactionparam.cpp @@ -127,13 +127,13 @@ void RuleActionParam::setEventTypeId(const EventTypeId &eventTypeId) /*! Writes the paramTypeId, value, eventId and eventParamTypeId of the given \a ruleActionParam to \a dbg. */ QDebug operator<<(QDebug dbg, const RuleActionParam &ruleActionParam) { - dbg.nospace() << "RuleActionParam(ParamTypeId: " << ruleActionParam.paramTypeId() << ", Value:" << ruleActionParam.value(); + dbg.nospace() << "RuleActionParam(ParamTypeId: " << ruleActionParam.paramTypeId().toString() << ", Name:" << ruleActionParam.paramName() << ", Value:" << ruleActionParam.value(); if (ruleActionParam.eventTypeId() != EventTypeId()) { dbg.nospace() << ", EventTypeId:" << ruleActionParam.eventTypeId().toString() << ", EventParamTypeId:" << ruleActionParam.eventParamTypeId().toString() << ")"; } else { dbg.nospace() << ")"; } - return dbg.space(); + return dbg; } // ActionTypeParamList diff --git a/libnymea/types/statedescriptor.cpp b/libnymea/types/statedescriptor.cpp index f0df240c..563a4329 100644 --- a/libnymea/types/statedescriptor.cpp +++ b/libnymea/types/statedescriptor.cpp @@ -161,3 +161,11 @@ bool StateDescriptor::isValid() const { return ((!m_deviceId.isNull() && !m_stateTypeId.isNull()) || (!m_interface.isNull() && !m_interfaceState.isNull())) && m_stateValue.isValid(); } + +QDebug operator<<(QDebug dbg, const StateDescriptor &stateDescriptor) +{ + dbg.nospace() << "StateDescriptor(DeviceId:" << stateDescriptor.deviceId().toString() << ", StateTypeId:" + << stateDescriptor.stateTypeId().toString() << ", Interface:" << stateDescriptor.interface() + << ", InterfaceState:" << stateDescriptor.interfaceState() << ", Operator:" << stateDescriptor.operatorType() << ", Value:" << stateDescriptor.stateValue(); + return dbg; +} diff --git a/libnymea/types/statedescriptor.h b/libnymea/types/statedescriptor.h index ec275eb9..e53034dd 100644 --- a/libnymea/types/statedescriptor.h +++ b/libnymea/types/statedescriptor.h @@ -73,7 +73,6 @@ private: Types::ValueOperator m_operatorType; }; -QDebug operator<<(QDebug dbg, const StateDescriptor &eventDescriptor); -QDebug operator<<(QDebug dbg, const QList &eventDescriptors); +QDebug operator<<(QDebug dbg, const StateDescriptor &stateDescriptor); #endif // STATEDESCRIPTOR_H diff --git a/plugins/mock/httpdaemon.cpp b/plugins/mock/httpdaemon.cpp index c3155fe4..6bada84c 100644 --- a/plugins/mock/httpdaemon.cpp +++ b/plugins/mock/httpdaemon.cpp @@ -83,8 +83,13 @@ void HttpDaemon::readClient() QUrl url("http://foo.bar" + tokens[1]); QUrlQuery query(url); if (url.path() == "/setstate") { - qCDebug(dcMockDevice) << "Set state value" << query.queryItems().first().second; - emit setState(StateTypeId(query.queryItems().first().first), QVariant(query.queryItems().first().second)); + StateTypeId stateTypeId = StateTypeId(query.queryItems().first().first); + QVariant stateValue = query.queryItems().first().second; + if (stateTypeId == mockBoolValueStateTypeId || stateTypeId == mockBatteryCriticalStateTypeId) { + stateValue.convert(QVariant::Bool); + } + qCDebug(dcMockDevice) << "Set state value" << stateValue; + emit setState(stateTypeId, stateValue); } else if (url.path() == "/generateevent") { emit triggerEvent(EventTypeId(query.queryItemValue("eventtypeid"))); } else if (url.path() == "/actionhistory") { diff --git a/tests/auto/rules/testrules.cpp b/tests/auto/rules/testrules.cpp index 37e612ee..a6b904d5 100644 --- a/tests/auto/rules/testrules.cpp +++ b/tests/auto/rules/testrules.cpp @@ -1147,6 +1147,44 @@ void TestRules::loadStoreConfig() validEventDescriptor3.insert("paramDescriptors", QVariantList()); validEventDescriptors3.append(validEventDescriptor3); + // Interface based event descriptor + QVariantMap eventDescriptorInterfaces; + eventDescriptorInterfaces.insert("interface", "battery"); + eventDescriptorInterfaces.insert("interfaceEvent", "batteryCritical"); + QVariantMap eventDescriptorInterfacesParam; + eventDescriptorInterfacesParam.insert("paramName", "batteryCritical"); + eventDescriptorInterfacesParam.insert("value", true); + eventDescriptorInterfacesParam.insert("operator", "ValueOperatorEquals"); + QVariantList eventDescriptorInterfacesParams; + eventDescriptorInterfacesParams.append(eventDescriptorInterfacesParam); + eventDescriptorInterfaces.insert("paramDescriptors", eventDescriptorInterfacesParams); + QVariantList eventDescriptorsInterfaces; + eventDescriptorsInterfaces.append(eventDescriptorInterfaces); + + // Interface based state evaluator + QVariantMap stateDescriptorInterfaces; + stateDescriptorInterfaces.insert("interface", "battery"); + stateDescriptorInterfaces.insert("interfaceState", "batteryCritical"); + stateDescriptorInterfaces.insert("operator", "ValueOperatorEquals"); + stateDescriptorInterfaces.insert("value", true); + QVariantMap stateEvaluatorInterfaces; + stateEvaluatorInterfaces.insert("stateDescriptor", stateDescriptorInterfaces); + stateEvaluatorInterfaces.insert("operator", "StateOperatorAnd"); + + // Interface based actions + QVariantMap ruleActionParamInterfaces; + ruleActionParamInterfaces.insert("paramName", "power"); + ruleActionParamInterfaces.insert("value", true); + QVariantList ruleActionParamsInterfaces; + ruleActionParamsInterfaces.append(ruleActionParamInterfaces); + QVariantMap actionInterfaces; + actionInterfaces.insert("interface", "light"); + actionInterfaces.insert("interfaceAction", "power"); + actionInterfaces.insert("ruleActionParams", ruleActionParamsInterfaces); + + QVariantList actionsInterfaces; + actionsInterfaces.append(actionInterfaces); + // rule 1 QVariantMap params; QVariantList actions; @@ -1188,6 +1226,17 @@ void TestRules::loadStoreConfig() RuleId newRuleId3 = RuleId(response3.toMap().value("params").toMap().value("ruleId").toString()); verifyRuleError(response3); + // rule 4, interface based + QVariantMap params4; + params4.insert("name", "TestRule4 - Interface based"); + params4.insert("eventDescriptors", eventDescriptorsInterfaces); + params4.insert("stateEvaluator", stateEvaluatorInterfaces); + params4.insert("actions", actionsInterfaces); + QVariant response4 = injectAndWait("Rules.AddRule", params4); + + RuleId newRuleId4 = RuleId(response4.toMap().value("params").toMap().value("ruleId").toString()); + verifyRuleError(response4); + response = injectAndWait("Rules.GetRules"); QVariantList rules = response.toMap().value("params").toMap().value("ruleDescriptions").toList(); qDebug() << "GetRules before server shutdown:" << response; @@ -1197,7 +1246,7 @@ void TestRules::loadStoreConfig() response = injectAndWait("Rules.GetRules"); rules = response.toMap().value("params").toMap().value("ruleDescriptions").toList(); - QVERIFY2(rules.count() == 3, "There should be exactly three rule."); + QVERIFY2(rules.count() == 4, "There should be exactly four rule."); QStringList idList; foreach (const QVariant &ruleDescription, rules) { @@ -1207,6 +1256,7 @@ void TestRules::loadStoreConfig() QVERIFY2(idList.contains(newRuleId.toString()), "Rule 1 should be in ruleIds list."); QVERIFY2(idList.contains(newRuleId2.toString()), "Rule 2 should be in ruleIds list."); QVERIFY2(idList.contains(newRuleId3.toString()), "Rule 3 should be in ruleIds list."); + QVERIFY2(idList.contains(newRuleId4.toString()), "Rule 4 should be in ruleIds list."); // Rule 1 params.clear(); @@ -1379,6 +1429,48 @@ void TestRules::loadStoreConfig() QVERIFY2(found, "Action not found after loading from config."); } + // Rule 4 + params.clear(); + params.insert("ruleId", newRuleId4); + response.clear(); + response = injectAndWait("Rules.GetRuleDetails", params); + + QVariantMap rule4 = response.toMap().value("params").toMap().value("rule").toMap(); + + qDebug() << rule4; + + QVariantList eventDescriptors4 = rule4.value("eventDescriptors").toList(); + QVERIFY2(eventDescriptors4.count() == 1, "There should be exactly 1 eventDescriptor"); + eventDescriptor = eventDescriptors4.first().toMap(); + QVERIFY2(eventDescriptor.value("interface").toString() == "battery", "Loaded the wrong interface name in rule 4"); + QVERIFY2(eventDescriptor.value("interfaceEvent").toString() == "batteryCritical", "Loaded the wrong interfaceEvent from eventDescriptor in rule 4"); + QCOMPARE(eventDescriptor.value("paramDescriptors").toList().count(), 1); + QVERIFY2(eventDescriptor.value("paramDescriptors").toList().first().toMap().value("paramName").toString() == "batteryCritical", "Loaded wrong ParamDescriptor in rule 4"); + QVERIFY2(eventDescriptor.value("paramDescriptors").toList().first().toMap().value("value").toBool() == true, "Loaded wrong ParamDescriptor in rule 3"); + + QVariantList replyActions4 = rule4.value("actions").toList(); + QVERIFY2(replyActions4.count() == 1, "Rule 4 should have exactly 1 action"); + foreach (const QVariant &actionVariant, actionsInterfaces) { + bool found = false; + foreach (const QVariant &replyActionVariant, replyActions4) { + if (actionVariant.toMap().value("interface") == replyActionVariant.toMap().value("interface") && + actionVariant.toMap().value("interfaceAction") == replyActionVariant.toMap().value("interfaceAction")) { + found = true; + // Check rule action params + QVariantList actionParams = actionVariant.toMap().value("ruleActionParams").toList(); + QVariantList replyActionParams = replyActionVariant.toMap().value("ruleActionParams").toList(); + QVERIFY2(actionParams.count() == replyActionParams.count(), "Not the same list size of action params"); + foreach (const QVariant &ruleParam, actionParams) { + QVERIFY(replyActionParams.contains(ruleParam)); + } + } + } + QVERIFY2(found, "Action not found after loading from config."); + } + QVariantList replyExitActions4 = rule4.value("exitActions").toList(); + QVERIFY2(replyExitActions4.isEmpty(), "Rule 4 should not have any exitAction"); + + // Remove Rule1 params.clear(); params.insert("ruleId", newRuleId); @@ -2288,6 +2380,11 @@ void TestRules::testInterfaceBasedEventRule() QVariantMap lowBatteryEvent; lowBatteryEvent.insert("interface", "battery"); lowBatteryEvent.insert("interfaceEvent", "batteryCritical"); + QVariantMap eventParams; + eventParams.insert("paramName", "batteryCritical"); + eventParams.insert("value", true); + eventParams.insert("operator", "ValueOperatorEquals"); + lowBatteryEvent.insert("paramDescriptors", QVariantList() << eventParams); QVariantMap addRuleParams; addRuleParams.insert("name", "TestInterfaceBasedRule"); @@ -2314,15 +2411,29 @@ void TestRules::testInterfaceBasedEventRule() QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("ruleActionParams").toList().first().toMap().value("value").toString(), QString("true")); - // Change the state + // Change the state to true, action should trigger spy.clear(); + request = QNetworkRequest(QUrl(QString("http://localhost:%1/clearactionhistory").arg(m_mockDevice1Port))); + reply = nam.get(request); + spy.wait(); spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBatteryCriticalStateId.toString()).arg(true))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); - verifyRuleExecuted(mockActionIdPower); + + // Change the state to false, action should not trigger + spy.clear(); + request = QNetworkRequest(QUrl(QString("http://localhost:%1/clearactionhistory").arg(m_mockDevice1Port))); + reply = nam.get(request); + spy.wait(); spy.clear(); + request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBatteryCriticalStateId.toString()).arg(false))); + reply = nam.get(request); + spy.wait(); + QCOMPARE(spy.count(), 1); + reply->deleteLater(); + verifyRuleNotExecuted(); } void TestRules::testInterfaceBasedStateRule() diff --git a/tests/scripts/getruledetails.sh b/tests/scripts/getruledetails.sh index f23490d1..ee7c101a 100755 --- a/tests/scripts/getruledetails.sh +++ b/tests/scripts/getruledetails.sh @@ -3,5 +3,6 @@ if [ -z $2 ]; then echo "usage: $0 host ruleId" else + echo '{"id":1, "method":"Rules.GetRuleDetails", "params": {"ruleId": "'$2'"}}' (echo '{"id":1, "method":"Rules.GetRuleDetails", "params": {"ruleId": "'$2'"}}'; sleep 1) | nc $1 2222 fi