From 0fb42681af1623601a0382b717e90e4ba7abe2e1 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Wed, 17 Jul 2019 20:48:16 +0200 Subject: [PATCH] A little more work on browser item support for rules --- libnymea-core/jsonrpc/jsontypes.cpp | 11 +- libnymea-core/libnymea-core.pro | 8 +- libnymea-core/nymeacore.cpp | 32 +++++ .../ruleengine}/ruleaction.cpp | 2 +- .../ruleengine}/ruleaction.h | 4 +- .../ruleengine}/ruleactionparam.cpp | 0 .../ruleengine}/ruleactionparam.h | 2 +- libnymea-core/ruleengine/ruleengine.cpp | 132 +++++++++--------- libnymea-core/ruleengine/ruleengine.h | 2 + .../stateevaluator.cpp | 0 .../{devices => ruleengine}/stateevaluator.h | 0 libnymea/libnymea.pro | 4 - 12 files changed, 120 insertions(+), 77 deletions(-) rename {libnymea/types => libnymea-core/ruleengine}/ruleaction.cpp (99%) rename {libnymea/types => libnymea-core/ruleengine}/ruleaction.h (98%) rename {libnymea/types => libnymea-core/ruleengine}/ruleactionparam.cpp (100%) rename {libnymea/types => libnymea-core/ruleengine}/ruleactionparam.h (99%) rename libnymea-core/{devices => ruleengine}/stateevaluator.cpp (100%) rename libnymea-core/{devices => ruleengine}/stateevaluator.h (100%) diff --git a/libnymea-core/jsonrpc/jsontypes.cpp b/libnymea-core/jsonrpc/jsontypes.cpp index b682567f..88326016 100644 --- a/libnymea-core/jsonrpc/jsontypes.cpp +++ b/libnymea-core/jsonrpc/jsontypes.cpp @@ -184,6 +184,7 @@ void JsonTypes::init() s_ruleAction.insert("o:actionTypeId", basicTypeToString(Uuid)); s_ruleAction.insert("o:interface", basicTypeToString(String)); s_ruleAction.insert("o:interfaceAction", basicTypeToString(String)); + s_ruleAction.insert("o:browserItemId", basicTypeToString(String)); s_ruleAction.insert("o:ruleActionParams", QVariantList() << ruleActionParamRef()); // RuleActionParam @@ -617,8 +618,11 @@ QVariantMap JsonTypes::packRuleAction(const RuleAction &ruleAction) { QVariantMap variant; if (ruleAction.type() == RuleAction::TypeDevice) { - variant.insert("actionTypeId", ruleAction.actionTypeId().toString()); variant.insert("deviceId", ruleAction.deviceId().toString()); + variant.insert("actionTypeId", ruleAction.actionTypeId().toString()); + } else if (ruleAction.type() == RuleAction::TypeBrowser) { + variant.insert("deviceId", ruleAction.deviceId().toString()); + variant.insert("browserItemId", ruleAction.browserItemId()); } else { variant.insert("interface", ruleAction.interface()); variant.insert("interfaceAction", ruleAction.interfaceAction()); @@ -1500,10 +1504,13 @@ RuleAction JsonTypes::unpackRuleAction(const QVariantMap &ruleActionMap) DeviceId actionDeviceId(ruleActionMap.value("deviceId").toString()); QString interface = ruleActionMap.value("interface").toString(); QString interfaceAction = ruleActionMap.value("interfaceAction").toString(); + QString browserItemId = ruleActionMap.value("browserItemId").toString(); RuleActionParamList actionParamList = JsonTypes::unpackRuleActionParams(ruleActionMap.value("ruleActionParams").toList()); - if (!actionTypeId.isNull() && !actionDeviceId.isNull()) { + if (!actionDeviceId.isNull() && !actionTypeId.isNull()) { return RuleAction(actionTypeId, actionDeviceId, actionParamList); + } else if (!actionDeviceId.isNull() && !browserItemId.isNull()) { + return RuleAction(actionDeviceId, browserItemId); } return RuleAction(interface, interfaceAction, actionParamList); } diff --git a/libnymea-core/libnymea-core.pro b/libnymea-core/libnymea-core.pro index d539ac5b..c8964329 100644 --- a/libnymea-core/libnymea-core.pro +++ b/libnymea-core/libnymea-core.pro @@ -18,9 +18,11 @@ RESOURCES += $$top_srcdir/icons.qrc \ HEADERS += nymeacore.h \ devices/devicemanagerimplementation.h \ devices/translator.h \ - devices/stateevaluator.h \ ruleengine/ruleengine.h \ ruleengine/rule.h \ + ruleengine/stateevaluator.h \ + ruleengine/ruleaction.h \ + ruleengine/ruleactionparam.h \ transportinterface.h \ nymeaconfiguration.h \ servermanager.h \ @@ -102,9 +104,11 @@ HEADERS += nymeacore.h \ SOURCES += nymeacore.cpp \ devices/devicemanagerimplementation.cpp \ devices/translator.cpp \ - devices/stateevaluator.cpp \ ruleengine/ruleengine.cpp \ ruleengine/rule.cpp \ + ruleengine/stateevaluator.cpp \ + ruleengine/ruleaction.cpp \ + ruleengine/ruleactionparam.cpp \ transportinterface.cpp \ nymeaconfiguration.cpp \ servermanager.cpp \ diff --git a/libnymea-core/nymeacore.cpp b/libnymea-core/nymeacore.cpp index 9cfa886f..39489898 100644 --- a/libnymea-core/nymeacore.cpp +++ b/libnymea-core/nymeacore.cpp @@ -478,9 +478,14 @@ Device::DeviceError NymeaCore::executeBrowserItemAction(const BrowserItemAction void NymeaCore::executeRuleActions(const QList ruleActions) { QList actions; + QList browserActions; foreach (const RuleAction &ruleAction, ruleActions) { if (ruleAction.type() == RuleAction::TypeDevice) { Device *device = m_deviceManager->findConfiguredDevice(ruleAction.deviceId()); + if (!device) { + qCWarning(dcRuleEngine()) << "Unable to find device" << ruleAction.deviceId() << "for rule action" << ruleAction; + continue; + } ActionTypeId actionTypeId = ruleAction.actionTypeId(); ParamList params; bool ok = true; @@ -510,6 +515,14 @@ void NymeaCore::executeRuleActions(const QList ruleActions) Action action(actionTypeId, device->id()); action.setParams(params); actions.append(action); + } else if (ruleAction.type() == RuleAction::TypeBrowser) { + Device *device = m_deviceManager->findConfiguredDevice(ruleAction.deviceId()); + if (!device) { + qCWarning(dcRuleEngine()) << "Unable to find device" << ruleAction.deviceId() << "for rule action" << ruleAction; + continue; + } + BrowserAction browserAction(ruleAction.deviceId(), ruleAction.browserItemId()); + browserActions.append(browserAction); } else { QList devices = m_deviceManager->findConfiguredDevices(ruleAction.interface()); foreach (Device* device, devices) { @@ -581,6 +594,25 @@ void NymeaCore::executeRuleActions(const QList ruleActions) // if (status != Device::DeviceErrorAsync) // m_logger->logAction(action, status == Device::DeviceErrorNoError ? Logging::LoggingLevelInfo : Logging::LoggingLevelAlert, status); } + + foreach (const BrowserAction &browserAction, browserActions) { + Device::DeviceError status = executeBrowserItem(browserAction); + switch(status) { + case Device::DeviceErrorNoError: + break; + case Device::DeviceErrorSetupFailed: + qCWarning(dcRuleEngine) << "Error executing action. Device setup failed."; + break; + case Device::DeviceErrorAsync: + qCDebug(dcRuleEngine) << "Executing asynchronous action."; + break; + case Device::DeviceErrorInvalidParameter: + qCWarning(dcRuleEngine) << "Error executing action. Invalid action parameter."; + break; + default: + qCWarning(dcRuleEngine) << "Error executing action:" << status; + } + } } /*! Calls the metheod RuleEngine::removeRule(\a id). diff --git a/libnymea/types/ruleaction.cpp b/libnymea-core/ruleengine/ruleaction.cpp similarity index 99% rename from libnymea/types/ruleaction.cpp rename to libnymea-core/ruleengine/ruleaction.cpp index 1e7580e2..10b31d11 100644 --- a/libnymea/types/ruleaction.cpp +++ b/libnymea-core/ruleengine/ruleaction.cpp @@ -112,7 +112,7 @@ RuleAction::Type RuleAction::type() const if (!m_deviceId.isNull() && !m_actionTypeId.isNull()) { return TypeDevice; } - if (!m_deviceId.isNull() && m_browserItemId.isEmpty()) { + if (!m_deviceId.isNull() && !m_browserItemId.isEmpty()) { return TypeBrowser; } if (!m_interface.isEmpty() && !m_interfaceAction.isEmpty()) { diff --git a/libnymea/types/ruleaction.h b/libnymea-core/ruleengine/ruleaction.h similarity index 98% rename from libnymea/types/ruleaction.h rename to libnymea-core/ruleengine/ruleaction.h index 3dc50ab3..d71b318a 100644 --- a/libnymea/types/ruleaction.h +++ b/libnymea-core/ruleengine/ruleaction.h @@ -25,9 +25,9 @@ #define RULEACTION_H #include "libnymea.h" -#include "action.h" +#include "types/action.h" +#include "types/browseritemaction.h" #include "ruleactionparam.h" -#include "browseritemaction.h" class LIBNYMEA_EXPORT RuleAction { diff --git a/libnymea/types/ruleactionparam.cpp b/libnymea-core/ruleengine/ruleactionparam.cpp similarity index 100% rename from libnymea/types/ruleactionparam.cpp rename to libnymea-core/ruleengine/ruleactionparam.cpp diff --git a/libnymea/types/ruleactionparam.h b/libnymea-core/ruleengine/ruleactionparam.h similarity index 99% rename from libnymea/types/ruleactionparam.h rename to libnymea-core/ruleengine/ruleactionparam.h index 20257546..5608844c 100644 --- a/libnymea/types/ruleactionparam.h +++ b/libnymea-core/ruleengine/ruleactionparam.h @@ -28,7 +28,7 @@ #include #include -#include "param.h" +#include "types/param.h" #include "libnymea.h" #include "typeutils.h" diff --git a/libnymea-core/ruleengine/ruleengine.cpp b/libnymea-core/ruleengine/ruleengine.cpp index 76f4e0b2..35f0e93d 100644 --- a/libnymea-core/ruleengine/ruleengine.cpp +++ b/libnymea-core/ruleengine/ruleengine.cpp @@ -912,7 +912,7 @@ bool RuleEngine::containsState(const StateEvaluator &stateEvaluator, const Event RuleEngine::RuleError RuleEngine::checkRuleAction(const RuleAction &ruleAction, const Rule &rule) { if (!ruleAction.isValid()) { - qWarning(dcRuleEngine()) << "Action is incomplete. It must have either actionTypeId and deviceId, or interface and interfaceAction"; + qWarning(dcRuleEngine()) << "Action is incomplete. It must have either deviceId and actionTypeId/browserItemId, or interface and interfaceAction:" << ruleAction; return RuleErrorActionTypeNotFound; } @@ -920,7 +920,7 @@ RuleEngine::RuleError RuleEngine::checkRuleAction(const RuleAction &ruleAction, if (ruleAction.type() == RuleAction::TypeDevice) { Device *device = NymeaCore::instance()->deviceManager()->findConfiguredDevice(ruleAction.deviceId()); if (!device) { - qCWarning(dcRuleEngine) << "Cannot create rule. No configured device for action with actionTypeId" << ruleAction.actionTypeId(); + qCWarning(dcRuleEngine) << "Cannot create rule. No configured device with ID" << ruleAction.deviceId(); return RuleErrorDeviceNotFound; } @@ -942,30 +942,44 @@ RuleEngine::RuleError RuleEngine::checkRuleAction(const RuleAction &ruleAction, qCWarning(dcRuleEngine()) << "Cannot create rule. Interface" << iface.name() << "does not implement action" << ruleAction.interfaceAction(); return RuleError::RuleErrorActionTypeNotFound; } + } else if (ruleAction.type() == RuleAction::TypeBrowser) { + Device *device = NymeaCore::instance()->deviceManager()->findConfiguredDevice(ruleAction.deviceId()); + if (!device) { + qCWarning(dcRuleEngine) << "Cannot create rule. No configured device with ID" << ruleAction.deviceId(); + return RuleErrorDeviceNotFound; + } + if (ruleAction.browserItemId().isEmpty()) { + qCWarning(dcRuleEngine()) << "Cannot create rule with empty browserItemId"; + return RuleErrorInvalidRuleActionParameter; + } + } else { return RuleErrorActionTypeNotFound; } - // Verify given params - foreach (const RuleActionParam &ruleActionParam, ruleAction.ruleActionParams()) { - RuleError ruleActionParamError = checkRuleActionParam(ruleActionParam, actionType, rule); - if (ruleActionParamError != RuleErrorNoError) { - return ruleActionParamError; - } - } - - // Verify all required params are given - foreach (const ParamType ¶mType, actionType.paramTypes()) { - bool found = false; + // Not all rule actions might have an actiontype (e.g. browser item executions) + if (!actionType.id().isNull()) { + // Verify given params foreach (const RuleActionParam &ruleActionParam, ruleAction.ruleActionParams()) { - if (ruleActionParam.paramTypeId() == paramType.id() - || ruleActionParam.paramName() == paramType.name()) { - found = true; - break; + RuleError ruleActionParamError = checkRuleActionParam(ruleActionParam, actionType, rule); + if (ruleActionParamError != RuleErrorNoError) { + return ruleActionParamError; } } - if (!found) { - return RuleErrorMissingParameter; + + // Verify all required params are given + foreach (const ParamType ¶mType, actionType.paramTypes()) { + bool found = false; + foreach (const RuleActionParam &ruleActionParam, ruleAction.ruleActionParams()) { + if (ruleActionParam.paramTypeId() == paramType.id() + || ruleActionParam.paramName() == paramType.name()) { + found = true; + break; + } + } + if (!found) { + return RuleErrorMissingParameter; + } } } @@ -1190,66 +1204,54 @@ void RuleEngine::saveRule(const Rule &rule) rule.stateEvaluator().dumpToSettings(settings, "stateEvaluator"); // Save ruleActions - int i = 0; settings.beginGroup("ruleActions"); - foreach (const RuleAction &action, rule.actions()) { - settings.beginGroup(QString::number(i)); - 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()) { - if (!param.paramTypeId().isNull()) { - settings.beginGroup("RuleActionParam-" + param.paramTypeId().toString()); - } else { - settings.beginGroup("RuleActionParam-" + param.paramName()); - } - settings.setValue("valueType", static_cast(param.value().type())); - settings.setValue("value", param.value()); - if (param.isEventBased()) { - settings.setValue("eventTypeId", param.eventTypeId().toString()); - settings.setValue("eventParamTypeId", param.eventParamTypeId()); - } else if (param.isStateBased()) { - settings.setValue("stateDeviceId", param.stateDeviceId().toString()); - settings.setValue("stateTypeId", param.stateTypeId()); - } - settings.endGroup(); - } - i++; - settings.endGroup(); - } + saveRuleActions(&settings, rule.actions()); settings.endGroup(); // Save ruleExitActions settings.beginGroup("ruleExitActions"); - i = 0; - foreach (const RuleAction &action, rule.exitActions()) { - settings.beginGroup(QString::number(i)); - if (!action.deviceId().isNull() && !action.actionTypeId().isNull()) { - settings.setValue("deviceId", action.deviceId().toString()); - settings.setValue("actionTypeId", action.actionTypeId().toString()); + saveRuleActions(&settings, rule.exitActions()); + settings.endGroup(); + qCDebug(dcRuleEngineDebug()) << "Saved rule to config:" << rule; +} + +void RuleEngine::saveRuleActions(NymeaSettings *settings, const QList &ruleActions) +{ + int i = 0; + foreach (const RuleAction &action, ruleActions) { + settings->beginGroup(QString::number(i)); + if (action.type() == RuleAction::TypeDevice) { + settings->setValue("deviceId", action.deviceId().toString()); + settings->setValue("actionTypeId", action.actionTypeId().toString()); + } else if (action.type() == RuleAction::TypeBrowser) { + settings->setValue("deviceId", action.deviceId().toString()); + settings->setValue("browserItemId", action.browserItemId()); + } else if (action.type() == RuleAction::TypeInterface){ + settings->setValue("interface", action.interface()); + settings->setValue("interfaceAction", action.interfaceAction()); } else { - settings.setValue("interface", action.interface()); - settings.setValue("interfaceAction", action.interfaceAction()); + Q_ASSERT_X(false, "RuleEngine::saveRule", "Unhandled rule action type."); } foreach (const RuleActionParam ¶m, action.ruleActionParams()) { if (!param.paramTypeId().isNull()) { - settings.beginGroup("RuleActionParam-" + param.paramTypeId().toString()); + settings->beginGroup("RuleActionParam-" + param.paramTypeId().toString()); } else { - settings.beginGroup("RuleActionParam-" + param.paramName()); + settings->beginGroup("RuleActionParam-" + param.paramName()); } - settings.setValue("valueType", static_cast(param.value().type())); - settings.setValue("value", param.value()); - settings.endGroup(); + settings->setValue("valueType", static_cast(param.value().type())); + settings->setValue("value", param.value()); + if (param.isEventBased()) { + settings->setValue("eventTypeId", param.eventTypeId().toString()); + settings->setValue("eventParamTypeId", param.eventParamTypeId()); + } else if (param.isStateBased()) { + settings->setValue("stateDeviceId", param.stateDeviceId().toString()); + settings->setValue("stateTypeId", param.stateTypeId()); + } + settings->endGroup(); } i++; - settings.endGroup(); + settings->endGroup(); } - settings.endGroup(); - qCDebug(dcRuleEngineDebug()) << "Saved rule to config:" << rule; } void RuleEngine::init() diff --git a/libnymea-core/ruleengine/ruleengine.h b/libnymea-core/ruleengine/ruleengine.h index 00a67dd2..e2039fae 100644 --- a/libnymea-core/ruleengine/ruleengine.h +++ b/libnymea-core/ruleengine/ruleengine.h @@ -30,6 +30,7 @@ #include #include #include +#include namespace nymeaserver { @@ -112,6 +113,7 @@ private: void appendRule(const Rule &rule); void saveRule(const Rule &rule); + void saveRuleActions(NymeaSettings *settings, const QList &ruleActions); private: QList m_ruleIds; // Keeping a list of RuleIds to keep sorting order... diff --git a/libnymea-core/devices/stateevaluator.cpp b/libnymea-core/ruleengine/stateevaluator.cpp similarity index 100% rename from libnymea-core/devices/stateevaluator.cpp rename to libnymea-core/ruleengine/stateevaluator.cpp diff --git a/libnymea-core/devices/stateevaluator.h b/libnymea-core/ruleengine/stateevaluator.h similarity index 100% rename from libnymea-core/devices/stateevaluator.h rename to libnymea-core/ruleengine/stateevaluator.h diff --git a/libnymea/libnymea.pro b/libnymea/libnymea.pro index bee2ccdc..a0d0fc0f 100644 --- a/libnymea/libnymea.pro +++ b/libnymea/libnymea.pro @@ -63,8 +63,6 @@ HEADERS += \ types/paramtype.h \ types/param.h \ types/paramdescriptor.h \ - types/ruleaction.h \ - types/ruleactionparam.h \ types/statedescriptor.h \ types/interface.h \ hardwareresource.h \ @@ -130,8 +128,6 @@ SOURCES += \ types/paramtype.cpp \ types/param.cpp \ types/paramdescriptor.cpp \ - types/ruleaction.cpp \ - types/ruleactionparam.cpp \ types/statedescriptor.cpp \ types/interface.cpp \ hardwareresource.cpp \