Merge PR #620: Refactor NymeaCore class

This commit is contained in:
jenkins 2023-02-21 17:43:34 +01:00
commit 9355694c24
23 changed files with 493 additions and 980 deletions

View File

@ -59,6 +59,7 @@
//#include "unistd.h" //#include "unistd.h"
#include "plugintimer.h" #include "plugintimer.h"
#include "logging/logengine.h"
#include <QPluginLoader> #include <QPluginLoader>
#include <QStaticPlugin> #include <QStaticPlugin>
@ -70,9 +71,10 @@
#include <QDir> #include <QDir>
#include <QJsonDocument> #include <QJsonDocument>
ThingManagerImplementation::ThingManagerImplementation(HardwareManager *hardwareManager, const QLocale &locale, QObject *parent) : ThingManagerImplementation::ThingManagerImplementation(HardwareManager *hardwareManager, LogEngine *logEngine, const QLocale &locale, QObject *parent) :
ThingManager(parent), ThingManager(parent),
m_hardwareManager(hardwareManager), m_hardwareManager(hardwareManager),
m_logEngine(logEngine),
m_locale(locale), m_locale(locale),
m_translator(new Translator(this)) m_translator(new Translator(this))
{ {
@ -853,47 +855,66 @@ ThingSetupInfo* ThingManagerImplementation::addConfiguredThingInternal(const Thi
} }
Thing::ThingError ThingManagerImplementation::removeConfiguredThing(const ThingId &thingId) Thing::ThingError ThingManagerImplementation::removeConfiguredThing(const ThingId &thingId)
{
Thing *thing = m_configuredThings.value(thingId);
if (!thing) {
return Thing::ThingErrorThingNotFound;
}
if (!thing->parentId().isNull() && thing->autoCreated()) {
qCWarning(dcThingManager) << "Thing is an autocreated child of" << thing->parentId().toString() << ". Remove the parent instead.";
return Thing::ThingErrorThingIsChild;
}
removeConfiguredThingInternal(thing);
return Thing::ThingErrorNoError;
}
void ThingManagerImplementation::removeConfiguredThingInternal(Thing *thing)
{ {
// We're checking thingSetupStatus and abort any pending setup here. As setup finished() // We're checking thingSetupStatus and abort any pending setup here. As setup finished()
// comes in as a QueuedConnection, make sure to process all events before going on so we // comes in as a QueuedConnection, make sure to process all events before going on so we
// don't end up aborting an already finished setup instead of calling thingRemoved() on it. // don't end up aborting an already finished setup instead of calling thingRemoved() on it.
qApp->processEvents(); qApp->processEvents();
Thing *thing = m_configuredThings.take(thingId); Things toBeRemoved = findChilds(thing->id());
if (!thing) { toBeRemoved.append(thing);
return Thing::ThingErrorThingNotFound; while (!toBeRemoved.isEmpty()) {
} Thing *t = m_configuredThings.take(toBeRemoved.takeFirst()->id());
IntegrationPlugin *plugin = m_integrationPlugins.value(thing->pluginId());
if (!plugin) {
qCWarning(dcThingManager()).nospace() << "Plugin not loaded for thing " << thing << ". Not calling thingRemoved on plugin.";
} else if (thing->setupStatus() == Thing::ThingSetupStatusInProgress) {
qCWarning(dcThingManager()).nospace() << "Thing " << thing << " is still being set up. Aborting setup.";
ThingSetupInfo *setupInfo = m_pendingSetups.value(thingId);
emit setupInfo->aborted();
} else if (thing->setupStatus() == Thing::ThingSetupStatusComplete) {
plugin->thingRemoved(thing);
}
thing->deleteLater(); IntegrationPlugin *plugin = m_integrationPlugins.value(t->pluginId());
if (!plugin) {
NymeaSettings settings(NymeaSettings::SettingsRoleThings); qCWarning(dcThingManager()).nospace() << "Plugin not loaded for thing " << t << ". Not calling thingRemoved on plugin.";
settings.beginGroup("ThingConfig"); } else if (thing->setupStatus() == Thing::ThingSetupStatusInProgress) {
settings.beginGroup(thingId.toString()); qCWarning(dcThingManager()).nospace() << "Thing " << thing << " is still being set up. Aborting setup.";
settings.remove(""); ThingSetupInfo *setupInfo = m_pendingSetups.value(t->id());
settings.endGroup(); emit setupInfo->aborted();
} else if (thing->setupStatus() == Thing::ThingSetupStatusComplete) {
QFile::remove(statesCacheFile(thingId)); plugin->thingRemoved(t);
foreach (const IOConnectionId &ioConnectionId, m_ioConnections.keys()) {
IOConnection ioConnection = m_ioConnections.value(ioConnectionId);
if (ioConnection.inputThingId() == thing->id() || ioConnection.outputThingId() == thing->id()) {
disconnectIO(ioConnectionId);
} }
t->deleteLater();
NymeaSettings settings(NymeaSettings::SettingsRoleThings);
settings.beginGroup("ThingConfig");
settings.beginGroup(t->id().toString());
settings.remove("");
settings.endGroup();
QFile::remove(statesCacheFile(t->id()));
foreach (const IOConnectionId &ioConnectionId, m_ioConnections.keys()) {
IOConnection ioConnection = m_ioConnections.value(ioConnectionId);
if (ioConnection.inputThingId() == t->id() || ioConnection.outputThingId() == t->id()) {
disconnectIO(ioConnectionId);
}
}
m_logEngine->removeThingLogs(thing->id());
emit thingRemoved(t->id());
} }
emit thingRemoved(thingId);
return Thing::ThingErrorNoError;
} }
BrowseResult *ThingManagerImplementation::browseThing(const ThingId &thingId, const QString &itemId, const QLocale &locale) BrowseResult *ThingManagerImplementation::browseThing(const ThingId &thingId, const QString &itemId, const QLocale &locale)
@ -977,6 +998,9 @@ BrowserActionInfo* ThingManagerImplementation::executeBrowserItem(const BrowserA
Thing *thing = m_configuredThings.value(browserAction.thingId()); Thing *thing = m_configuredThings.value(browserAction.thingId());
BrowserActionInfo *info = new BrowserActionInfo(thing, this, browserAction, this, 30000); BrowserActionInfo *info = new BrowserActionInfo(thing, this, browserAction, this, 30000);
connect(info, &BrowserActionInfo::finished, info->thing(), [this, info](){
m_logEngine->logBrowserAction(info->browserAction(), info->status() == Thing::ThingErrorNoError ? Logging::LoggingLevelInfo : Logging::LoggingLevelAlert, info->status());
});
if (!thing) { if (!thing) {
info->finish(Thing::ThingErrorThingNotFound); info->finish(Thing::ThingErrorThingNotFound);
@ -1009,6 +1033,9 @@ BrowserItemActionInfo* ThingManagerImplementation::executeBrowserItemAction(cons
Thing *thing = m_configuredThings.value(browserItemAction.thingId()); Thing *thing = m_configuredThings.value(browserItemAction.thingId());
BrowserItemActionInfo *info = new BrowserItemActionInfo(thing, this, browserItemAction, this, 30000); BrowserItemActionInfo *info = new BrowserItemActionInfo(thing, this, browserItemAction, this, 30000);
connect(info, &BrowserItemActionInfo::finished, info->thing(), [this, info](){
m_logEngine->logBrowserItemAction(info->browserItemAction(), info->status() == Thing::ThingErrorNoError ? Logging::LoggingLevelInfo : Logging::LoggingLevelAlert, info->status());
});
if (!thing) { if (!thing) {
info->finish(Thing::ThingErrorThingNotFound); info->finish(Thing::ThingErrorThingNotFound);
@ -1359,6 +1386,7 @@ ThingActionInfo *ThingManagerImplementation::executeAction(const Action &action)
ThingActionInfo *info = new ThingActionInfo(thing, finalAction, this, 15000); ThingActionInfo *info = new ThingActionInfo(thing, finalAction, this, 15000);
connect(info, &ThingActionInfo::finished, this, [=](){ connect(info, &ThingActionInfo::finished, this, [=](){
m_logEngine->logAction(finalAction, info->status());
emit actionExecuted(action, info->status()); emit actionExecuted(action, info->status());
}); });
@ -1810,7 +1838,7 @@ void ThingManagerImplementation::onAutoThingDisappeared(const ThingId &thingId)
return; return;
} }
emit thingDisappeared(thingId); removeConfiguredThingInternal(thing);
} }
void ThingManagerImplementation::onLoaded() void ThingManagerImplementation::onLoaded()
@ -1849,7 +1877,7 @@ void ThingManagerImplementation::onEventTriggered(Event event)
} }
// configure logging // configure logging
if (thing->loggedEventTypeIds().contains(event.eventTypeId())) { if (thing->loggedEventTypeIds().contains(event.eventTypeId())) {
event.setLogged(true); m_logEngine->logEvent(event);
} }
// Forward the event // Forward the event
@ -1867,6 +1895,10 @@ void ThingManagerImplementation::slotThingStateValueChanged(const StateTypeId &s
storeThingState(thing, stateTypeId); storeThingState(thing, stateTypeId);
} }
if (thing->loggedStateTypeIds().contains(stateTypeId)) {
m_logEngine->logStateChange(thing, stateTypeId, value);
}
emit thingStateChanged(thing, stateTypeId, value, minValue, maxValue); emit thingStateChanged(thing, stateTypeId, value, minValue, maxValue);
syncIOConnection(thing, stateTypeId); syncIOConnection(thing, stateTypeId);

View File

@ -61,6 +61,11 @@ class HardwareManager;
class Translator; class Translator;
class ApiKeysProvidersLoader; class ApiKeysProvidersLoader;
namespace nymeaserver {
class LogEngine;
}
using namespace nymeaserver;
class ThingManagerImplementation: public ThingManager class ThingManagerImplementation: public ThingManager
{ {
Q_OBJECT Q_OBJECT
@ -68,7 +73,7 @@ class ThingManagerImplementation: public ThingManager
friend class IntegrationPlugin; friend class IntegrationPlugin;
public: public:
explicit ThingManagerImplementation(HardwareManager *hardwareManager, const QLocale &locale, QObject *parent = nullptr); explicit ThingManagerImplementation(HardwareManager *hardwareManager, LogEngine *logEngine, const QLocale &locale, QObject *parent = nullptr);
~ThingManagerImplementation() override; ~ThingManagerImplementation() override;
static QStringList pluginSearchDirs(); static QStringList pluginSearchDirs();
@ -131,9 +136,6 @@ public:
ThingClass translateThingClass(const ThingClass &thingClass, const QLocale &locale) override; ThingClass translateThingClass(const ThingClass &thingClass, const QLocale &locale) override;
Vendor translateVendor(const Vendor &vendor, const QLocale &locale) override; Vendor translateVendor(const Vendor &vendor, const QLocale &locale) override;
signals:
void loaded();
private slots: private slots:
void loadPlugins(); void loadPlugins();
void loadPlugin(IntegrationPlugin *pluginIface); void loadPlugin(IntegrationPlugin *pluginIface);
@ -157,6 +159,7 @@ private:
ParamList buildParams(const ParamTypes &types, const ParamList &first, const ParamList &second = ParamList()); ParamList buildParams(const ParamTypes &types, const ParamList &first, const ParamList &second = ParamList());
void pairThingInternal(ThingPairingInfo *info); void pairThingInternal(ThingPairingInfo *info);
ThingSetupInfo *addConfiguredThingInternal(const ThingClassId &thingClassId, const QString &name, const ParamList &params, const ThingId &parentId = ThingId()); ThingSetupInfo *addConfiguredThingInternal(const ThingClassId &thingClassId, const QString &name, const ParamList &params, const ThingId &parentId = ThingId());
void removeConfiguredThingInternal(Thing *thing);
ThingSetupInfo *reconfigureThingInternal(Thing *thing, const ParamList &params, const QString &name = QString()); ThingSetupInfo *reconfigureThingInternal(Thing *thing, const ParamList &params, const QString &name = QString());
ThingSetupInfo *setupThing(Thing *thing, bool initialSetup); ThingSetupInfo *setupThing(Thing *thing, bool initialSetup);
void initThing(Thing *thing); void initThing(Thing *thing);
@ -176,6 +179,7 @@ private:
private: private:
HardwareManager *m_hardwareManager; HardwareManager *m_hardwareManager;
nymeaserver::LogEngine *m_logEngine;
QLocale m_locale; QLocale m_locale;
Translator *m_translator = nullptr; Translator *m_translator = nullptr;

View File

@ -29,7 +29,6 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "integrationshandler.h" #include "integrationshandler.h"
#include "nymeacore.h"
#include "integrations/thingmanager.h" #include "integrations/thingmanager.h"
#include "integrations/thing.h" #include "integrations/thing.h"
#include "integrations/integrationplugin.h" #include "integrations/integrationplugin.h"
@ -43,9 +42,11 @@
#include "integrations/thingsetupinfo.h" #include "integrations/thingsetupinfo.h"
#include "integrations/browseresult.h" #include "integrations/browseresult.h"
#include "integrations/browseritemresult.h" #include "integrations/browseritemresult.h"
#include "ruleengine/ruleengine.h"
#include <QDebug> #include <QDebug>
#include <QJsonDocument> #include <QJsonDocument>
#include <QCryptographicHash>
namespace nymeaserver { namespace nymeaserver {
@ -63,7 +64,6 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
registerEnum<Types::InputType>(); registerEnum<Types::InputType>();
registerEnum<Types::IOType>(); registerEnum<Types::IOType>();
registerEnum<Types::StateValueFilter>(); registerEnum<Types::StateValueFilter>();
registerEnum<RuleEngine::RemovePolicy>();
registerEnum<BrowserItem::BrowserIcon>(); registerEnum<BrowserItem::BrowserIcon>();
registerEnum<MediaBrowserItem::MediaBrowserIcon>(); registerEnum<MediaBrowserItem::MediaBrowserIcon>();
@ -268,17 +268,10 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
registerMethod("SetStateFilter", description, params, returns, Types::PermissionScopeConfigureThings); registerMethod("SetStateFilter", description, params, returns, Types::PermissionScopeConfigureThings);
params.clear(); returns.clear(); params.clear(); returns.clear();
description = "Remove a thing from the system."; description = "Remove a thing and all its childs from the system. RemovePolicy is deprecated and has no effect any more.";
params.insert("thingId", enumValueName(Uuid)); params.insert("thingId", enumValueName(Uuid));
params.insert("o:removePolicy", enumRef<RuleEngine::RemovePolicy>()); params.insert("d:o:removePolicy", enumValueName(String));
QVariantMap policy;
policy.insert("ruleId", enumValueName(Uuid));
policy.insert("policy", enumRef<RuleEngine::RemovePolicy>());
QVariantList removePolicyList;
removePolicyList.append(policy);
params.insert("o:removePolicyList", removePolicyList);
returns.insert("thingError", enumRef<Thing::ThingError>()); returns.insert("thingError", enumRef<Thing::ThingError>());
returns.insert("o:ruleIds", QVariantList() << enumValueName(Uuid));
registerMethod("RemoveThing", description, params, returns, Types::PermissionScopeConfigureThings); registerMethod("RemoveThing", description, params, returns, Types::PermissionScopeConfigureThings);
params.clear(); returns.clear(); params.clear(); returns.clear();
@ -443,7 +436,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
description = "Emitted whenever an Event is triggered."; description = "Emitted whenever an Event is triggered.";
params.insert("event", objectRef<Event>()); params.insert("event", objectRef<Event>());
registerNotification("EventTriggered", description, params); registerNotification("EventTriggered", description, params);
connect(NymeaCore::instance(), &NymeaCore::eventTriggered, this, [this](const Event &event){ connect(m_thingManager, &ThingManager::eventTriggered, this, [this](const Event &event){
QVariantMap params; QVariantMap params;
params.insert("event", pack(event)); params.insert("event", pack(event));
emit EventTriggered(params); emit EventTriggered(params);
@ -469,14 +462,14 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
emit IOConnectionRemoved(params); emit IOConnectionRemoved(params);
}); });
connect(NymeaCore::instance(), &NymeaCore::pluginConfigChanged, this, &IntegrationsHandler::pluginConfigChanged); connect(m_thingManager, &ThingManager::pluginConfigChanged, this, &IntegrationsHandler::pluginConfigChanged);
connect(NymeaCore::instance(), &NymeaCore::thingStateChanged, this, &IntegrationsHandler::thingStateChanged); connect(m_thingManager, &ThingManager::thingStateChanged, this, &IntegrationsHandler::thingStateChanged);
connect(NymeaCore::instance(), &NymeaCore::thingRemoved, this, &IntegrationsHandler::thingRemovedNotification); connect(m_thingManager, &ThingManager::thingRemoved, this, &IntegrationsHandler::thingRemovedNotification);
connect(NymeaCore::instance(), &NymeaCore::thingAdded, this, &IntegrationsHandler::thingAddedNotification); connect(m_thingManager, &ThingManager::thingAdded, this, &IntegrationsHandler::thingAddedNotification);
connect(NymeaCore::instance(), &NymeaCore::thingChanged, this, &IntegrationsHandler::thingChangedNotification); connect(m_thingManager, &ThingManager::thingChanged, this, &IntegrationsHandler::thingChangedNotification);
connect(NymeaCore::instance(), &NymeaCore::thingSettingChanged, this, &IntegrationsHandler::thingSettingChangedNotification); connect(m_thingManager, &ThingManager::thingSettingChanged, this, &IntegrationsHandler::thingSettingChangedNotification);
connect(NymeaCore::instance(), &NymeaCore::initialized, this, [=](){ connect(m_thingManager, &ThingManager::loaded, this, [=](){
// Generating cache hashes. // Generating cache hashes.
// NOTE: We need to sort the lists to get a stable result // NOTE: We need to sort the lists to get a stable result
QHash<ThingClassId, ThingClass> thingClassesMap; QHash<ThingClassId, ThingClass> thingClassesMap;
@ -535,8 +528,8 @@ JsonReply* IntegrationsHandler::GetVendors(const QVariantMap &params, const Json
{ {
Q_UNUSED(params) Q_UNUSED(params)
QVariantList vendors; QVariantList vendors;
foreach (const Vendor &vendor, NymeaCore::instance()->thingManager()->supportedVendors()) { foreach (const Vendor &vendor, m_thingManager->supportedVendors()) {
Vendor translatedVendor = NymeaCore::instance()->thingManager()->translateVendor(vendor, context.locale()); Vendor translatedVendor = m_thingManager->translateVendor(vendor, context.locale());
vendors.append(pack(translatedVendor)); vendors.append(pack(translatedVendor));
} }
@ -550,7 +543,7 @@ JsonReply* IntegrationsHandler::GetThingClasses(const QVariantMap &params, const
QVariantMap returns; QVariantMap returns;
QVariantList thingClasses; QVariantList thingClasses;
foreach (const ThingClass &thingClass, NymeaCore::instance()->thingManager()->supportedThings()) { foreach (const ThingClass &thingClass, m_thingManager->supportedThings()) {
if (params.contains("vendorId") && thingClass.vendorId() != VendorId(params.value("vendorId").toUuid())) { if (params.contains("vendorId") && thingClass.vendorId() != VendorId(params.value("vendorId").toUuid())) {
continue; continue;
} }
@ -567,7 +560,7 @@ JsonReply* IntegrationsHandler::GetThingClasses(const QVariantMap &params, const
} }
} }
ThingClass translatedThingClass = NymeaCore::instance()->thingManager()->translateThingClass(thingClass, context.locale()); ThingClass translatedThingClass = m_thingManager->translateThingClass(thingClass, context.locale());
thingClasses.append(pack(translatedThingClass)); thingClasses.append(pack(translatedThingClass));
} }
@ -586,7 +579,7 @@ JsonReply *IntegrationsHandler::DiscoverThings(const QVariantMap &params, const
ParamList discoveryParams = unpack<ParamList>(params.value("discoveryParams")); ParamList discoveryParams = unpack<ParamList>(params.value("discoveryParams"));
JsonReply *reply = createAsyncReply("DiscoverThings"); JsonReply *reply = createAsyncReply("DiscoverThings");
ThingDiscoveryInfo *info = NymeaCore::instance()->thingManager()->discoverThings(thingClassId, discoveryParams); ThingDiscoveryInfo *info = m_thingManager->discoverThings(thingClassId, discoveryParams);
connect(info, &ThingDiscoveryInfo::finished, reply, [this, reply, info, locale](){ connect(info, &ThingDiscoveryInfo::finished, reply, [this, reply, info, locale](){
QVariantMap returns; QVariantMap returns;
returns.insert("thingError", enumValueName<Thing::ThingError>(info->status())); returns.insert("thingError", enumValueName<Thing::ThingError>(info->status()));
@ -614,9 +607,9 @@ JsonReply* IntegrationsHandler::GetPlugins(const QVariantMap &params, const Json
{ {
Q_UNUSED(params) Q_UNUSED(params)
QVariantList plugins; QVariantList plugins;
foreach (IntegrationPlugin* plugin, NymeaCore::instance()->thingManager()->plugins()) { foreach (IntegrationPlugin* plugin, m_thingManager->plugins()) {
QVariantMap packedPlugin = pack(*plugin).toMap(); QVariantMap packedPlugin = pack(*plugin).toMap();
packedPlugin["displayName"] = NymeaCore::instance()->thingManager()->translate(plugin->pluginId(), plugin->pluginDisplayName(), context.locale()); packedPlugin["displayName"] = m_thingManager->translate(plugin->pluginId(), plugin->pluginDisplayName(), context.locale());
plugins.append(packedPlugin); plugins.append(packedPlugin);
} }
@ -629,7 +622,7 @@ JsonReply *IntegrationsHandler::GetPluginConfiguration(const QVariantMap &params
{ {
QVariantMap returns; QVariantMap returns;
IntegrationPlugin *plugin = NymeaCore::instance()->thingManager()->plugins().findById(PluginId(params.value("pluginId").toString())); IntegrationPlugin *plugin = m_thingManager->plugins().findById(PluginId(params.value("pluginId").toString()));
if (!plugin) { if (!plugin) {
returns.insert("thingError", enumValueName<Thing::ThingError>(Thing::ThingErrorPluginNotFound)); returns.insert("thingError", enumValueName<Thing::ThingError>(Thing::ThingErrorPluginNotFound));
return createReply(returns); return createReply(returns);
@ -649,7 +642,7 @@ JsonReply* IntegrationsHandler::SetPluginConfiguration(const QVariantMap &params
QVariantMap returns; QVariantMap returns;
PluginId pluginId = PluginId(params.value("pluginId").toString()); PluginId pluginId = PluginId(params.value("pluginId").toString());
ParamList pluginParams = unpack<ParamList>(params.value("configuration")); ParamList pluginParams = unpack<ParamList>(params.value("configuration"));
Thing::ThingError result = NymeaCore::instance()->thingManager()->setPluginConfig(pluginId, pluginParams); Thing::ThingError result = m_thingManager->setPluginConfig(pluginId, pluginParams);
returns.insert("thingError",enumValueName<Thing::ThingError>(result)); returns.insert("thingError",enumValueName<Thing::ThingError>(result));
return createReply(returns); return createReply(returns);
} }
@ -674,10 +667,10 @@ JsonReply* IntegrationsHandler::AddThing(const QVariantMap &params, const JsonCo
jsonReply->finished(); jsonReply->finished();
return jsonReply; return jsonReply;
} }
info = NymeaCore::instance()->thingManager()->addConfiguredThing(thingClassId, thingParams, thingName); info = m_thingManager->addConfiguredThing(thingClassId, thingParams, thingName);
} else { } else {
info = NymeaCore::instance()->thingManager()->addConfiguredThing(thingDescriptorId, thingParams, thingName); info = m_thingManager->addConfiguredThing(thingDescriptorId, thingParams, thingName);
} }
connect(info, &ThingSetupInfo::finished, jsonReply, [info, jsonReply, locale](){ connect(info, &ThingSetupInfo::finished, jsonReply, [info, jsonReply, locale](){
QVariantMap returns; QVariantMap returns;
@ -706,24 +699,24 @@ JsonReply *IntegrationsHandler::PairThing(const QVariantMap &params, const JsonC
ThingPairingInfo *info; ThingPairingInfo *info;
if (params.contains("thingDescriptorId")) { if (params.contains("thingDescriptorId")) {
ThingDescriptorId thingDescriptorId = ThingDescriptorId(params.value("thingDescriptorId").toString()); ThingDescriptorId thingDescriptorId = ThingDescriptorId(params.value("thingDescriptorId").toString());
info = NymeaCore::instance()->thingManager()->pairThing(thingDescriptorId, thingParams, thingName); info = m_thingManager->pairThing(thingDescriptorId, thingParams, thingName);
} else if (params.contains("thingId")) { } else if (params.contains("thingId")) {
ThingId thingId = ThingId(params.value("thingId").toString()); ThingId thingId = ThingId(params.value("thingId").toString());
info = NymeaCore::instance()->thingManager()->pairThing(thingId, thingParams, thingName); info = m_thingManager->pairThing(thingId, thingParams, thingName);
} else { } else {
ThingClassId thingClassId(params.value("thingClassId").toString()); ThingClassId thingClassId(params.value("thingClassId").toString());
info = NymeaCore::instance()->thingManager()->pairThing(thingClassId, thingParams, thingName); info = m_thingManager->pairThing(thingClassId, thingParams, thingName);
} }
JsonReply *jsonReply = createAsyncReply("PairThing"); JsonReply *jsonReply = createAsyncReply("PairThing");
connect(info, &ThingPairingInfo::finished, jsonReply, [jsonReply, info, locale](){ connect(info, &ThingPairingInfo::finished, jsonReply, [jsonReply, info, locale, this](){
QVariantMap returns; QVariantMap returns;
returns.insert("thingError", enumValueName<Thing::ThingError>(info->status())); returns.insert("thingError", enumValueName<Thing::ThingError>(info->status()));
returns.insert("pairingTransactionId", info->transactionId().toString()); returns.insert("pairingTransactionId", info->transactionId().toString());
if (info->status() == Thing::ThingErrorNoError) { if (info->status() == Thing::ThingErrorNoError) {
ThingClass thingClass = NymeaCore::instance()->thingManager()->findThingClass(info->thingClassId()); ThingClass thingClass = m_thingManager->findThingClass(info->thingClassId());
returns.insert("setupMethod", enumValueName<ThingClass::SetupMethod>(thingClass.setupMethod())); returns.insert("setupMethod", enumValueName<ThingClass::SetupMethod>(thingClass.setupMethod()));
} }
@ -751,7 +744,7 @@ JsonReply *IntegrationsHandler::ConfirmPairing(const QVariantMap &params)
JsonReply *jsonReply = createAsyncReply("ConfirmPairing"); JsonReply *jsonReply = createAsyncReply("ConfirmPairing");
ThingPairingInfo *info = NymeaCore::instance()->thingManager()->confirmPairing(pairingTransactionId, username, secret); ThingPairingInfo *info = m_thingManager->confirmPairing(pairingTransactionId, username, secret);
connect(info, &ThingPairingInfo::finished, jsonReply, [info, jsonReply, locale](){ connect(info, &ThingPairingInfo::finished, jsonReply, [info, jsonReply, locale](){
QVariantMap returns; QVariantMap returns;
@ -774,22 +767,22 @@ JsonReply* IntegrationsHandler::GetThings(const QVariantMap &params, const JsonC
QVariantMap returns; QVariantMap returns;
QVariantList things; QVariantList things;
if (params.contains("thingId")) { if (params.contains("thingId")) {
Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(ThingId(params.value("thingId").toString())); Thing *thing = m_thingManager->findConfiguredThing(ThingId(params.value("thingId").toString()));
if (!thing) { if (!thing) {
returns.insert("thingError", enumValueName<Thing::ThingError>(Thing::ThingErrorThingNotFound)); returns.insert("thingError", enumValueName<Thing::ThingError>(Thing::ThingErrorThingNotFound));
return createReply(returns); return createReply(returns);
} else { } else {
QVariantMap packedThing = pack(thing).toMap(); QVariantMap packedThing = pack(thing).toMap();
QString translatedSetupStatus = NymeaCore::instance()->thingManager()->translate(thing->pluginId(), thing->setupDisplayMessage(), context.locale()); QString translatedSetupStatus = m_thingManager->translate(thing->pluginId(), thing->setupDisplayMessage(), context.locale());
if (!translatedSetupStatus.isEmpty()) { if (!translatedSetupStatus.isEmpty()) {
packedThing["setupDisplayMessage"] = translatedSetupStatus; packedThing["setupDisplayMessage"] = translatedSetupStatus;
} }
things.append(packedThing); things.append(packedThing);
} }
} else { } else {
foreach (Thing *thing, NymeaCore::instance()->thingManager()->configuredThings()) { foreach (Thing *thing, m_thingManager->configuredThings()) {
QVariantMap packedThing = pack(thing).toMap(); QVariantMap packedThing = pack(thing).toMap();
QString translatedSetupStatus = NymeaCore::instance()->thingManager()->translate(thing->pluginId(), thing->setupDisplayMessage(), context.locale()); QString translatedSetupStatus = m_thingManager->translate(thing->pluginId(), thing->setupDisplayMessage(), context.locale());
if (!translatedSetupStatus.isEmpty()) { if (!translatedSetupStatus.isEmpty()) {
packedThing["setupDisplayMessage"] = translatedSetupStatus; packedThing["setupDisplayMessage"] = translatedSetupStatus;
} }
@ -812,9 +805,9 @@ JsonReply *IntegrationsHandler::ReconfigureThing(const QVariantMap &params, cons
ThingSetupInfo *info; ThingSetupInfo *info;
if (!thingDescriptorId.isNull()) { if (!thingDescriptorId.isNull()) {
info = NymeaCore::instance()->thingManager()->reconfigureThing(thingDescriptorId, thingParams); info = m_thingManager->reconfigureThing(thingDescriptorId, thingParams);
} else if (!thingId.isNull()){ } else if (!thingId.isNull()){
info = NymeaCore::instance()->thingManager()->reconfigureThing(thingId, thingParams); info = m_thingManager->reconfigureThing(thingId, thingParams);
} else { } else {
qCWarning(dcJsonRpc()) << "Either thingId or thingDescriptorId are required"; qCWarning(dcJsonRpc()) << "Either thingId or thingDescriptorId are required";
QVariantMap ret; QVariantMap ret;
@ -842,7 +835,7 @@ JsonReply *IntegrationsHandler::EditThing(const QVariantMap &params)
qCDebug(dcJsonRpc()) << "Edit thing" << thingId << name; qCDebug(dcJsonRpc()) << "Edit thing" << thingId << name;
Thing::ThingError status = NymeaCore::instance()->thingManager()->editThing(thingId, name); Thing::ThingError status = m_thingManager->editThing(thingId, name);
return createReply(statusToReply(status)); return createReply(statusToReply(status));
} }
@ -851,33 +844,8 @@ JsonReply* IntegrationsHandler::RemoveThing(const QVariantMap &params)
{ {
QVariantMap returns; QVariantMap returns;
ThingId thingId = ThingId(params.value("thingId").toString()); ThingId thingId = ThingId(params.value("thingId").toString());
Thing::ThingError status = m_thingManager->removeConfiguredThing(thingId);
// global removePolicy has priority returns.insert("thingError", enumValueName<Thing::ThingError>(status));
if (params.contains("removePolicy")) {
RuleEngine::RemovePolicy removePolicy = params.value("removePolicy").toString() == "RemovePolicyCascade" ? RuleEngine::RemovePolicyCascade : RuleEngine::RemovePolicyUpdate;
Thing::ThingError status = NymeaCore::instance()->removeConfiguredThing(thingId, removePolicy);
returns.insert("thingError", enumValueName<Thing::ThingError>(status));
return createReply(returns);
}
QHash<RuleId, RuleEngine::RemovePolicy> removePolicyList;
foreach (const QVariant &variant, params.value("removePolicyList").toList()) {
RuleId ruleId = RuleId(variant.toMap().value("ruleId").toString());
RuleEngine::RemovePolicy policy = variant.toMap().value("policy").toString() == "RemovePolicyCascade" ? RuleEngine::RemovePolicyCascade : RuleEngine::RemovePolicyUpdate;
removePolicyList.insert(ruleId, policy);
}
QPair<Thing::ThingError, QList<RuleId> > status = NymeaCore::instance()->removeConfiguredThing(thingId, removePolicyList);
returns.insert("thingError", enumValueName<Thing::ThingError>(status.first));
if (!status.second.isEmpty()) {
QVariantList ruleIdList;
foreach (const RuleId &ruleId, status.second) {
ruleIdList.append(ruleId.toString());
}
returns.insert("ruleIds", ruleIdList);
}
return createReply(returns); return createReply(returns);
} }
@ -885,7 +853,7 @@ JsonReply *IntegrationsHandler::SetThingSettings(const QVariantMap &params)
{ {
ThingId thingId = ThingId(params.value("thingId").toString()); ThingId thingId = ThingId(params.value("thingId").toString());
ParamList settings = unpack<ParamList>(params.value("settings")); ParamList settings = unpack<ParamList>(params.value("settings"));
Thing::ThingError status = NymeaCore::instance()->thingManager()->setThingSettings(thingId, settings); Thing::ThingError status = m_thingManager->setThingSettings(thingId, settings);
return createReply(statusToReply(status)); return createReply(statusToReply(status));
} }
@ -894,7 +862,7 @@ JsonReply *IntegrationsHandler::SetStateLogging(const QVariantMap &params)
ThingId thingId = ThingId(params.value("thingId").toString()); ThingId thingId = ThingId(params.value("thingId").toString());
StateTypeId stateTypeId = StateTypeId(params.value("stateTypeId").toUuid()); StateTypeId stateTypeId = StateTypeId(params.value("stateTypeId").toUuid());
bool enabled = params.value("enabled").toBool(); bool enabled = params.value("enabled").toBool();
Thing::ThingError status = NymeaCore::instance()->thingManager()->setStateLogging(thingId, stateTypeId, enabled); Thing::ThingError status = m_thingManager->setStateLogging(thingId, stateTypeId, enabled);
return createReply(statusToReply(status)); return createReply(statusToReply(status));
} }
@ -903,7 +871,7 @@ JsonReply *IntegrationsHandler::SetEventLogging(const QVariantMap &params)
ThingId thingId = ThingId(params.value("thingId").toString()); ThingId thingId = ThingId(params.value("thingId").toString());
EventTypeId eventTypeId = EventTypeId(params.value("eventTypeId").toUuid()); EventTypeId eventTypeId = EventTypeId(params.value("eventTypeId").toUuid());
bool enabled = params.value("enabled").toBool(); bool enabled = params.value("enabled").toBool();
Thing::ThingError status = NymeaCore::instance()->thingManager()->setEventLogging(thingId, eventTypeId, enabled); Thing::ThingError status = m_thingManager->setEventLogging(thingId, eventTypeId, enabled);
return createReply(statusToReply(status)); return createReply(statusToReply(status));
} }
@ -914,14 +882,14 @@ JsonReply *IntegrationsHandler::SetStateFilter(const QVariantMap &params)
QString filterString = params.value("filter").toString(); QString filterString = params.value("filter").toString();
QMetaEnum metaEnum = QMetaEnum::fromType<Types::StateValueFilter>(); QMetaEnum metaEnum = QMetaEnum::fromType<Types::StateValueFilter>();
Types::StateValueFilter filter = static_cast<Types::StateValueFilter>(metaEnum.keyToValue(filterString.toUtf8())); Types::StateValueFilter filter = static_cast<Types::StateValueFilter>(metaEnum.keyToValue(filterString.toUtf8()));
Thing::ThingError status = NymeaCore::instance()->thingManager()->setStateFilter(thingId, stateTypeId, filter); Thing::ThingError status = m_thingManager->setStateFilter(thingId, stateTypeId, filter);
return createReply(statusToReply(status)); return createReply(statusToReply(status));
} }
JsonReply* IntegrationsHandler::GetEventTypes(const QVariantMap &params, const JsonContext &context) const JsonReply* IntegrationsHandler::GetEventTypes(const QVariantMap &params, const JsonContext &context) const
{ {
ThingClass thingClass = NymeaCore::instance()->thingManager()->findThingClass(ThingClassId(params.value("thingClassId").toString())); ThingClass thingClass = m_thingManager->findThingClass(ThingClassId(params.value("thingClassId").toString()));
ThingClass translatedThingClass = NymeaCore::instance()->thingManager()->translateThingClass(thingClass, context.locale()); ThingClass translatedThingClass = m_thingManager->translateThingClass(thingClass, context.locale());
QVariantMap returns; QVariantMap returns;
returns.insert("eventTypes", pack(translatedThingClass.eventTypes())); returns.insert("eventTypes", pack(translatedThingClass.eventTypes()));
@ -930,8 +898,8 @@ JsonReply* IntegrationsHandler::GetEventTypes(const QVariantMap &params, const J
JsonReply* IntegrationsHandler::GetActionTypes(const QVariantMap &params, const JsonContext &context) const JsonReply* IntegrationsHandler::GetActionTypes(const QVariantMap &params, const JsonContext &context) const
{ {
ThingClass thingClass = NymeaCore::instance()->thingManager()->findThingClass(ThingClassId(params.value("thingClassId").toString())); ThingClass thingClass = m_thingManager->findThingClass(ThingClassId(params.value("thingClassId").toString()));
ThingClass translatedThingClass = NymeaCore::instance()->thingManager()->translateThingClass(thingClass, context.locale()); ThingClass translatedThingClass = m_thingManager->translateThingClass(thingClass, context.locale());
QVariantMap returns; QVariantMap returns;
returns.insert("actionTypes", pack(translatedThingClass.actionTypes())); returns.insert("actionTypes", pack(translatedThingClass.actionTypes()));
@ -940,8 +908,8 @@ JsonReply* IntegrationsHandler::GetActionTypes(const QVariantMap &params, const
JsonReply* IntegrationsHandler::GetStateTypes(const QVariantMap &params, const JsonContext &context) const JsonReply* IntegrationsHandler::GetStateTypes(const QVariantMap &params, const JsonContext &context) const
{ {
ThingClass thingClass = NymeaCore::instance()->thingManager()->findThingClass(ThingClassId(params.value("thingClassId").toString())); ThingClass thingClass = m_thingManager->findThingClass(ThingClassId(params.value("thingClassId").toString()));
ThingClass translatedThingClass = NymeaCore::instance()->thingManager()->translateThingClass(thingClass, context.locale()); ThingClass translatedThingClass = m_thingManager->translateThingClass(thingClass, context.locale());
QVariantMap returns; QVariantMap returns;
returns.insert("stateTypes", pack(translatedThingClass.stateTypes())); returns.insert("stateTypes", pack(translatedThingClass.stateTypes()));
@ -950,7 +918,7 @@ JsonReply* IntegrationsHandler::GetStateTypes(const QVariantMap &params, const J
JsonReply* IntegrationsHandler::GetStateValue(const QVariantMap &params) const JsonReply* IntegrationsHandler::GetStateValue(const QVariantMap &params) const
{ {
Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(ThingId(params.value("thingId").toString())); Thing *thing = m_thingManager->findConfiguredThing(ThingId(params.value("thingId").toString()));
if (!thing) { if (!thing) {
return createReply(statusToReply(Thing::ThingErrorThingNotFound)); return createReply(statusToReply(Thing::ThingErrorThingNotFound));
} }
@ -966,7 +934,7 @@ JsonReply* IntegrationsHandler::GetStateValue(const QVariantMap &params) const
JsonReply *IntegrationsHandler::GetStateValues(const QVariantMap &params) const JsonReply *IntegrationsHandler::GetStateValues(const QVariantMap &params) const
{ {
Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(ThingId(params.value("thingId").toString())); Thing *thing = m_thingManager->findConfiguredThing(ThingId(params.value("thingId").toString()));
if (!thing) { if (!thing) {
return createReply(statusToReply(Thing::ThingErrorThingNotFound)); return createReply(statusToReply(Thing::ThingErrorThingNotFound));
} }
@ -983,7 +951,7 @@ JsonReply *IntegrationsHandler::BrowseThing(const QVariantMap &params, const Jso
JsonReply *jsonReply = createAsyncReply("BrowseThing"); JsonReply *jsonReply = createAsyncReply("BrowseThing");
BrowseResult *result = NymeaCore::instance()->thingManager()->browseThing(thingId, itemId, context.locale()); BrowseResult *result = m_thingManager->browseThing(thingId, itemId, context.locale());
connect(result, &BrowseResult::finished, jsonReply, [this, jsonReply, result, context](){ connect(result, &BrowseResult::finished, jsonReply, [this, jsonReply, result, context](){
QVariantMap returns = statusToReply(result->status()); QVariantMap returns = statusToReply(result->status());
@ -1010,7 +978,7 @@ JsonReply *IntegrationsHandler::GetBrowserItem(const QVariantMap &params, const
JsonReply *jsonReply = createAsyncReply("GetBrowserItem"); JsonReply *jsonReply = createAsyncReply("GetBrowserItem");
BrowserItemResult *result = NymeaCore::instance()->thingManager()->browserItemDetails(thingId, itemId, context.locale()); BrowserItemResult *result = m_thingManager->browserItemDetails(thingId, itemId, context.locale());
connect(result, &BrowserItemResult::finished, jsonReply, [this, jsonReply, result, context](){ connect(result, &BrowserItemResult::finished, jsonReply, [this, jsonReply, result, context](){
QVariantMap params = statusToReply(result->status()); QVariantMap params = statusToReply(result->status());
if (result->status() == Thing::ThingErrorNoError) { if (result->status() == Thing::ThingErrorNoError) {
@ -1038,7 +1006,7 @@ JsonReply *IntegrationsHandler::ExecuteAction(const QVariantMap &params, const J
JsonReply *jsonReply = createAsyncReply("ExecuteAction"); JsonReply *jsonReply = createAsyncReply("ExecuteAction");
ThingActionInfo *info = NymeaCore::instance()->thingManager()->executeAction(action); ThingActionInfo *info = m_thingManager->executeAction(action);
connect(info, &ThingActionInfo::finished, jsonReply, [info, jsonReply, locale](){ connect(info, &ThingActionInfo::finished, jsonReply, [info, jsonReply, locale](){
QVariantMap data; QVariantMap data;
data.insert("thingError", enumValueName(info->status())); data.insert("thingError", enumValueName(info->status()));
@ -1060,7 +1028,7 @@ JsonReply *IntegrationsHandler::ExecuteBrowserItem(const QVariantMap &params, co
JsonReply *jsonReply = createAsyncReply("ExecuteBrowserItem"); JsonReply *jsonReply = createAsyncReply("ExecuteBrowserItem");
BrowserActionInfo *info = NymeaCore::instance()->executeBrowserItem(action); BrowserActionInfo *info = m_thingManager->executeBrowserItem(action);
connect(info, &BrowserActionInfo::finished, jsonReply, [info, jsonReply, context](){ connect(info, &BrowserActionInfo::finished, jsonReply, [info, jsonReply, context](){
QVariantMap data; QVariantMap data;
data.insert("thingError", enumValueName<Thing::ThingError>(info->status())); data.insert("thingError", enumValueName<Thing::ThingError>(info->status()));
@ -1084,7 +1052,7 @@ JsonReply *IntegrationsHandler::ExecuteBrowserItemAction(const QVariantMap &para
JsonReply *jsonReply = createAsyncReply("ExecuteBrowserItemAction"); JsonReply *jsonReply = createAsyncReply("ExecuteBrowserItemAction");
BrowserItemActionInfo *info = NymeaCore::instance()->executeBrowserItemAction(browserItemAction); BrowserItemActionInfo *info = m_thingManager->executeBrowserItemAction(browserItemAction);
connect(info, &BrowserItemActionInfo::finished, jsonReply, [info, jsonReply, context](){ connect(info, &BrowserItemActionInfo::finished, jsonReply, [info, jsonReply, context](){
QVariantMap data; QVariantMap data;
data.insert("thingError", enumValueName<Thing::ThingError>(info->status())); data.insert("thingError", enumValueName<Thing::ThingError>(info->status()));

View File

@ -529,7 +529,7 @@ void JsonRPCServerImplementation::setup()
{ {
registerHandler(this); registerHandler(this);
registerHandler(new IntegrationsHandler(NymeaCore::instance()->thingManager(), this)); registerHandler(new IntegrationsHandler(NymeaCore::instance()->thingManager(), this));
registerHandler(new RulesHandler(this)); registerHandler(new RulesHandler(NymeaCore::instance()->ruleEngine(), this));
registerHandler(new LoggingHandler(this)); registerHandler(new LoggingHandler(this));
registerHandler(new ConfigurationHandler(this)); registerHandler(new ConfigurationHandler(this));
registerHandler(new NetworkManagerHandler(NymeaCore::instance()->networkManager(), this)); registerHandler(new NetworkManagerHandler(NymeaCore::instance()->networkManager(), this));

View File

@ -62,7 +62,6 @@
*/ */
#include "ruleshandler.h" #include "ruleshandler.h"
#include "nymeacore.h"
#include "ruleengine/ruleengine.h" #include "ruleengine/ruleengine.h"
#include "loggingcategories.h" #include "loggingcategories.h"
@ -72,8 +71,9 @@
namespace nymeaserver { namespace nymeaserver {
/*! Constructs a new \l{RulesHandler} with the given \a parent. */ /*! Constructs a new \l{RulesHandler} with the given \a parent. */
RulesHandler::RulesHandler(QObject *parent) : RulesHandler::RulesHandler(RuleEngine *ruleEngine, QObject *parent) :
JsonHandler(parent) JsonHandler(parent),
m_ruleEngine(ruleEngine)
{ {
// Enums // Enums
registerEnum<RuleEngine::RuleError>(); registerEnum<RuleEngine::RuleError>();
@ -218,10 +218,10 @@ RulesHandler::RulesHandler(QObject *parent) :
params.insert("rule", objectRef("Rule")); params.insert("rule", objectRef("Rule"));
registerNotification("RuleConfigurationChanged", description, params); registerNotification("RuleConfigurationChanged", description, params);
connect(NymeaCore::instance(), &NymeaCore::ruleAdded, this, &RulesHandler::ruleAddedNotification); connect(m_ruleEngine, &RuleEngine::ruleAdded, this, &RulesHandler::ruleAddedNotification);
connect(NymeaCore::instance(), &NymeaCore::ruleRemoved, this, &RulesHandler::ruleRemovedNotification); connect(m_ruleEngine, &RuleEngine::ruleRemoved, this, &RulesHandler::ruleRemovedNotification);
connect(NymeaCore::instance(), &NymeaCore::ruleActiveChanged, this, &RulesHandler::ruleActiveChangedNotification); connect(m_ruleEngine, &RuleEngine::ruleActiveChanged, this, &RulesHandler::ruleActiveChangedNotification);
connect(NymeaCore::instance(), &NymeaCore::ruleConfigurationChanged, this, &RulesHandler::ruleConfigurationChangedNotification); connect(m_ruleEngine, &RuleEngine::ruleConfigurationChanged, this, &RulesHandler::ruleConfigurationChangedNotification);
} }
/*! Returns the name of the \l{RulesHandler}. In this case \b Rules.*/ /*! Returns the name of the \l{RulesHandler}. In this case \b Rules.*/
@ -235,7 +235,7 @@ JsonReply* RulesHandler::GetRules(const QVariantMap &params)
Q_UNUSED(params) Q_UNUSED(params)
QVariantList rulesList; QVariantList rulesList;
foreach (const Rule &rule, NymeaCore::instance()->ruleEngine()->rules()) { foreach (const Rule &rule, m_ruleEngine->rules()) {
rulesList.append(packRuleDescription(rule)); rulesList.append(packRuleDescription(rule));
} }
@ -247,7 +247,7 @@ JsonReply* RulesHandler::GetRules(const QVariantMap &params)
JsonReply *RulesHandler::GetRuleDetails(const QVariantMap &params) JsonReply *RulesHandler::GetRuleDetails(const QVariantMap &params)
{ {
RuleId ruleId = RuleId(params.value("ruleId").toString()); RuleId ruleId = RuleId(params.value("ruleId").toString());
Rule rule = NymeaCore::instance()->ruleEngine()->findRule(ruleId); Rule rule = m_ruleEngine->findRule(ruleId);
if (rule.id().isNull()) { if (rule.id().isNull()) {
QVariantMap data; QVariantMap data;
data.insert("ruleError", enumValueName<RuleEngine::RuleError>(RuleEngine::RuleErrorRuleNotFound)); data.insert("ruleError", enumValueName<RuleEngine::RuleError>(RuleEngine::RuleErrorRuleNotFound));
@ -264,7 +264,7 @@ JsonReply* RulesHandler::AddRule(const QVariantMap &params)
Rule rule = unpack<Rule>(params); Rule rule = unpack<Rule>(params);
rule.setId(RuleId::createRuleId()); rule.setId(RuleId::createRuleId());
RuleEngine::RuleError status = NymeaCore::instance()->ruleEngine()->addRule(rule); RuleEngine::RuleError status = m_ruleEngine->addRule(rule);
QVariantMap returns; QVariantMap returns;
if (status == RuleEngine::RuleErrorNoError) { if (status == RuleEngine::RuleErrorNoError) {
returns.insert("ruleId", rule.id().toString()); returns.insert("ruleId", rule.id().toString());
@ -280,10 +280,10 @@ JsonReply *RulesHandler::EditRule(const QVariantMap &params)
// FIXME: Edit rule API currently has "ruleId" while the Rule type has "id". Auto unpacking will fail for this property // FIXME: Edit rule API currently has "ruleId" while the Rule type has "id". Auto unpacking will fail for this property
rule.setId(params.value("ruleId").toUuid()); rule.setId(params.value("ruleId").toUuid());
RuleEngine::RuleError status = NymeaCore::instance()->ruleEngine()->editRule(rule); RuleEngine::RuleError status = m_ruleEngine->editRule(rule);
QVariantMap returns; QVariantMap returns;
if (status == RuleEngine::RuleErrorNoError) { if (status == RuleEngine::RuleErrorNoError) {
returns.insert("rule", pack(NymeaCore::instance()->ruleEngine()->findRule(rule.id()))); returns.insert("rule", pack(m_ruleEngine->findRule(rule.id())));
} }
returns.insert("ruleError", enumValueName<RuleEngine::RuleError>(status)); returns.insert("ruleError", enumValueName<RuleEngine::RuleError>(status));
return createReply(returns); return createReply(returns);
@ -293,7 +293,7 @@ JsonReply* RulesHandler::RemoveRule(const QVariantMap &params)
{ {
QVariantMap returns; QVariantMap returns;
RuleId ruleId(params.value("ruleId").toString()); RuleId ruleId(params.value("ruleId").toString());
RuleEngine::RuleError status = NymeaCore::instance()->removeRule(ruleId); RuleEngine::RuleError status = m_ruleEngine->removeRule(ruleId);
returns.insert("ruleError", enumValueName<RuleEngine::RuleError>(status)); returns.insert("ruleError", enumValueName<RuleEngine::RuleError>(status));
return createReply(returns); return createReply(returns);
} }
@ -301,7 +301,7 @@ JsonReply* RulesHandler::RemoveRule(const QVariantMap &params)
JsonReply *RulesHandler::FindRules(const QVariantMap &params) JsonReply *RulesHandler::FindRules(const QVariantMap &params)
{ {
ThingId thingId = ThingId(params.value("thingId").toString()); ThingId thingId = ThingId(params.value("thingId").toString());
QList<RuleId> rules = NymeaCore::instance()->ruleEngine()->findRules(thingId); QList<RuleId> rules = m_ruleEngine->findRules(thingId);
QVariantList rulesList; QVariantList rulesList;
foreach (const RuleId &ruleId, rules) { foreach (const RuleId &ruleId, rules) {
@ -315,7 +315,7 @@ JsonReply *RulesHandler::FindRules(const QVariantMap &params)
JsonReply *RulesHandler::EnableRule(const QVariantMap &params) JsonReply *RulesHandler::EnableRule(const QVariantMap &params)
{ {
RuleEngine::RuleError status = NymeaCore::instance()->ruleEngine()->enableRule(RuleId(params.value("ruleId").toString())); RuleEngine::RuleError status = m_ruleEngine->enableRule(RuleId(params.value("ruleId").toString()));
QVariantMap ret; QVariantMap ret;
ret.insert("ruleError", enumValueName<RuleEngine::RuleError>(status)); ret.insert("ruleError", enumValueName<RuleEngine::RuleError>(status));
return createReply(ret); return createReply(ret);
@ -323,7 +323,7 @@ JsonReply *RulesHandler::EnableRule(const QVariantMap &params)
JsonReply *RulesHandler::DisableRule(const QVariantMap &params) JsonReply *RulesHandler::DisableRule(const QVariantMap &params)
{ {
RuleEngine::RuleError status = NymeaCore::instance()->ruleEngine()->disableRule(RuleId(params.value("ruleId").toString())); RuleEngine::RuleError status = m_ruleEngine->disableRule(RuleId(params.value("ruleId").toString()));
QVariantMap ret; QVariantMap ret;
ret.insert("ruleError", enumValueName<RuleEngine::RuleError>(status)); ret.insert("ruleError", enumValueName<RuleEngine::RuleError>(status));
return createReply(ret); return createReply(ret);
@ -333,7 +333,7 @@ JsonReply *RulesHandler::ExecuteActions(const QVariantMap &params)
{ {
QVariantMap returns; QVariantMap returns;
RuleId ruleId(params.value("ruleId").toString()); RuleId ruleId(params.value("ruleId").toString());
RuleEngine::RuleError status = NymeaCore::instance()->ruleEngine()->executeActions(ruleId); RuleEngine::RuleError status = m_ruleEngine->executeActions(ruleId);
returns.insert("ruleError", enumValueName<RuleEngine::RuleError>(status)); returns.insert("ruleError", enumValueName<RuleEngine::RuleError>(status));
return createReply(returns); return createReply(returns);
} }
@ -342,7 +342,7 @@ JsonReply *RulesHandler::ExecuteExitActions(const QVariantMap &params)
{ {
QVariantMap returns; QVariantMap returns;
RuleId ruleId(params.value("ruleId").toString()); RuleId ruleId(params.value("ruleId").toString());
RuleEngine::RuleError status = NymeaCore::instance()->ruleEngine()->executeExitActions(ruleId); RuleEngine::RuleError status = m_ruleEngine->executeExitActions(ruleId);
returns.insert("ruleError", enumValueName<RuleEngine::RuleError>(status)); returns.insert("ruleError", enumValueName<RuleEngine::RuleError>(status));
return createReply(returns); return createReply(returns);
} }

View File

@ -37,11 +37,13 @@
namespace nymeaserver { namespace nymeaserver {
class RuleEngine;
class RulesHandler : public JsonHandler class RulesHandler : public JsonHandler
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit RulesHandler(QObject *parent = nullptr); explicit RulesHandler(RuleEngine *ruleEngine, QObject *parent = nullptr);
QString name() const override; QString name() const override;
@ -73,6 +75,9 @@ private slots:
private: private:
QVariantMap packRuleDescription(const Rule &rule); QVariantMap packRuleDescription(const Rule &rule);
private:
RuleEngine *m_ruleEngine = nullptr;
}; };
} }

View File

@ -106,14 +106,6 @@ LogEngine::~LogEngine()
m_db.close(); m_db.close();
} }
void LogEngine::setThingManager(ThingManager *thingManager)
{
m_thingManager = thingManager;
connect(thingManager, &ThingManager::eventTriggered, this, &LogEngine::logEvent);
connect(thingManager, &ThingManager::thingStateChanged, this, &LogEngine::logStateChange);
connect(thingManager, &ThingManager::actionExecuted, this, &LogEngine::logAction);
}
LogEntriesFetchJob *LogEngine::fetchLogEntries(const LogFilter &filter) LogEntriesFetchJob *LogEngine::fetchLogEntries(const LogFilter &filter)
{ {
QList<LogEntry> results; QList<LogEntry> results;
@ -233,10 +225,6 @@ void LogEngine::logSystemEvent(const QDateTime &dateTime, bool active, Logging::
void LogEngine::logEvent(const Event &event) void LogEngine::logEvent(const Event &event)
{ {
if (!event.logged()) {
return;
}
QVariantList valueList; QVariantList valueList;
foreach (const Param &param, event.params()) { foreach (const Param &param, event.params()) {
valueList << param.value(); valueList << param.value();
@ -255,10 +243,6 @@ void LogEngine::logEvent(const Event &event)
void LogEngine::logStateChange(Thing *thing, const StateTypeId &stateTypeId, const QVariant &value) void LogEngine::logStateChange(Thing *thing, const StateTypeId &stateTypeId, const QVariant &value)
{ {
if (!thing->loggedStateTypeIds().contains(stateTypeId)) {
return;
}
LogEntry entry(Logging::LoggingSourceStates); LogEntry entry(Logging::LoggingSourceStates);
entry.setTypeId(stateTypeId); entry.setTypeId(stateTypeId);
entry.setThingId(thing->id()); entry.setThingId(thing->id());

View File

@ -61,8 +61,6 @@ public:
LogEngine(const QString &driver, const QString &dbName, const QString &hostname = QString("127.0.0.1"), const QString &username = QString(), const QString &password = QString(), int maxDBSize = 50000, QObject *parent = nullptr); LogEngine(const QString &driver, const QString &dbName, const QString &hostname = QString("127.0.0.1"), const QString &username = QString(), const QString &password = QString(), int maxDBSize = 50000, QObject *parent = nullptr);
~LogEngine(); ~LogEngine();
void setThingManager(ThingManager *thingManager);
LogEntriesFetchJob *fetchLogEntries(const LogFilter &filter = LogFilter()); LogEntriesFetchJob *fetchLogEntries(const LogFilter &filter = LogFilter());
ThingsFetchJob *fetchThings(); ThingsFetchJob *fetchThings();
@ -76,6 +74,9 @@ public:
public slots: public slots:
void logSystemEvent(const QDateTime &dateTime, bool active, Logging::LoggingLevel level = Logging::LoggingLevelInfo); void logSystemEvent(const QDateTime &dateTime, bool active, Logging::LoggingLevel level = Logging::LoggingLevelInfo);
void logEvent(const Event &event);
void logStateChange(Thing *thing, const StateTypeId &stateTypeId, const QVariant &value);
void logAction(const Action &action, Thing::ThingError status);
void logBrowserAction(const BrowserAction &browserAction, Logging::LoggingLevel level = Logging::LoggingLevelInfo, int errorCode = 0); void logBrowserAction(const BrowserAction &browserAction, Logging::LoggingLevel level = Logging::LoggingLevelInfo, int errorCode = 0);
void logBrowserItemAction(const BrowserItemAction &browserItemAction, Logging::LoggingLevel level = Logging::LoggingLevelInfo, int errorCode = 0); void logBrowserItemAction(const BrowserItemAction &browserItemAction, Logging::LoggingLevel level = Logging::LoggingLevelInfo, int errorCode = 0);
void logRuleTriggered(const Rule &rule); void logRuleTriggered(const Rule &rule);
@ -84,11 +85,6 @@ public slots:
void logRuleActionsExecuted(const Rule &rule); void logRuleActionsExecuted(const Rule &rule);
void logRuleExitActionsExecuted(const Rule &rule); void logRuleExitActionsExecuted(const Rule &rule);
private slots:
void logEvent(const Event &event);
void logStateChange(Thing *thing, const StateTypeId &stateTypeId, const QVariant &value);
void logAction(const Action &action, Thing::ThingError status);
signals: signals:
void logEntryAdded(const LogEntry &logEntry); void logEntryAdded(const LogEntry &logEntry);
void logDatabaseUpdated(); void logDatabaseUpdated();
@ -122,8 +118,6 @@ private:
bool m_initialized = false; bool m_initialized = false;
bool m_dbMalformed = false; bool m_dbMalformed = false;
ThingManager *m_thingManager = nullptr;
// When maxQueueLength is exceeded, jobs will be flagged and discarded if this source logs more events // When maxQueueLength is exceeded, jobs will be flagged and discarded if this source logs more events
int m_maxQueueLength; int m_maxQueueLength;
QHash<QString, QList<DatabaseJob*>> m_flaggedJobs; QHash<QString, QList<DatabaseJob*>> m_flaggedJobs;

View File

@ -121,15 +121,14 @@ void NymeaCore::init(const QStringList &additionalInterfaces) {
qCDebug(dcCore) << "Creating Hardware Manager"; qCDebug(dcCore) << "Creating Hardware Manager";
m_hardwareManager = new HardwareManagerImplementation(m_platform, m_serverManager->mqttBroker(), m_zigbeeManager, m_zwaveManager, m_modbusRtuManager, this); m_hardwareManager = new HardwareManagerImplementation(m_platform, m_serverManager->mqttBroker(), m_zigbeeManager, m_zwaveManager, m_modbusRtuManager, this);
qCDebug(dcCore) << "Creating Thing Manager (locale:" << m_configuration->locale() << ")";
m_thingManager = new ThingManagerImplementation(m_hardwareManager, m_configuration->locale(), this);
qCDebug(dcCore) << "Creating Rule Engine";
m_ruleEngine = new RuleEngine(this);
qCDebug(dcCore) << "Creating Log Engine"; qCDebug(dcCore) << "Creating Log Engine";
m_logger = new LogEngine(m_configuration->logDBDriver(), m_configuration->logDBName(), m_configuration->logDBHost(), m_configuration->logDBUser(), m_configuration->logDBPassword(), m_configuration->logDBMaxEntries(), this); m_logger = new LogEngine(m_configuration->logDBDriver(), m_configuration->logDBName(), m_configuration->logDBHost(), m_configuration->logDBUser(), m_configuration->logDBPassword(), m_configuration->logDBMaxEntries(), this);
m_logger->setThingManager(m_thingManager);
qCDebug(dcCore) << "Creating Thing Manager (locale:" << m_configuration->locale() << ")";
m_thingManager = new ThingManagerImplementation(m_hardwareManager, m_logger, m_configuration->locale(), this);
qCDebug(dcCore) << "Creating Rule Engine";
m_ruleEngine = new RuleEngine(m_thingManager, m_timeManager, m_logger, this);
qCDebug(dcCore()) << "Creating Script Engine"; qCDebug(dcCore()) << "Creating Script Engine";
m_scriptEngine = new scriptengine::ScriptEngine(m_thingManager, this); m_scriptEngine = new scriptengine::ScriptEngine(m_thingManager, this);
@ -151,22 +150,8 @@ void NymeaCore::init(const QStringList &additionalInterfaces) {
connect(m_configuration, &NymeaConfiguration::serverNameChanged, m_serverManager, &ServerManager::setServerName); connect(m_configuration, &NymeaConfiguration::serverNameChanged, m_serverManager, &ServerManager::setServerName);
connect(m_thingManager, &ThingManagerImplementation::pluginConfigChanged, this, &NymeaCore::pluginConfigChanged);
connect(m_thingManager, &ThingManagerImplementation::eventTriggered, this, &NymeaCore::onEventTriggered);
connect(m_thingManager, &ThingManagerImplementation::thingStateChanged, this, &NymeaCore::onThingStateChanged);
connect(m_thingManager, &ThingManagerImplementation::thingAdded, this, &NymeaCore::thingAdded);
connect(m_thingManager, &ThingManagerImplementation::thingChanged, this, &NymeaCore::thingChanged);
connect(m_thingManager, &ThingManagerImplementation::thingSettingChanged, this, &NymeaCore::thingSettingChanged);
connect(m_thingManager, &ThingManagerImplementation::thingRemoved, this, &NymeaCore::thingRemoved);
connect(m_thingManager, &ThingManagerImplementation::thingDisappeared, this, &NymeaCore::onThingDisappeared);
connect(m_thingManager, &ThingManagerImplementation::loaded, this, &NymeaCore::thingManagerLoaded); connect(m_thingManager, &ThingManagerImplementation::loaded, this, &NymeaCore::thingManagerLoaded);
connect(m_ruleEngine, &RuleEngine::ruleAdded, this, &NymeaCore::ruleAdded);
connect(m_ruleEngine, &RuleEngine::ruleRemoved, this, &NymeaCore::ruleRemoved);
connect(m_ruleEngine, &RuleEngine::ruleConfigurationChanged, this, &NymeaCore::ruleConfigurationChanged);
connect(m_timeManager, &TimeManager::dateTimeChanged, this, &NymeaCore::onDateTimeChanged);
m_logger->logSystemEvent(m_timeManager->currentDateTime(), true); m_logger->logSystemEvent(m_timeManager->currentDateTime(), true);
} }
@ -215,322 +200,6 @@ void NymeaCore::destroy()
s_instance = nullptr; s_instance = nullptr;
} }
QPair<Thing::ThingError, QList<RuleId> > NymeaCore::removeConfiguredThing(const ThingId &thingId, const QHash<RuleId, RuleEngine::RemovePolicy> &removePolicyList)
{
Thing *thing = m_thingManager->findConfiguredThing(thingId);
if (!thing) {
return QPair<Thing::ThingError, QList<RuleId> > (Thing::ThingErrorThingNotFound, QList<RuleId>());
}
// Check if this is a child
if (!thing->parentId().isNull() && thing->autoCreated()) {
qCWarning(dcThingManager) << "Thing is an autocreated child of" << thing->parentId().toString() << ". Please remove the parent.";
return QPair<Thing::ThingError, QList<RuleId> > (Thing::ThingErrorThingIsChild, QList<RuleId>());
}
// FIXME: Let's remove this for now. It will come back with more fine grained control, presumably introducing a RemoveMethod flag in the DeviceClass
// if (thing->autoCreated()) {
// qCWarning(dcThingManager) << "This thing has been auto-created and cannot be deleted manually.";
// return QPair<Thing::ThingError, QList<RuleId> >(Thing::ThingErrorCreationMethodNotSupported, {});
// }
// Check if this thing has childs
QList<Thing *> thingsToRemove;
thingsToRemove.append(thing);
QList<Thing *> childs = m_thingManager->findChilds(thingId);
if (!childs.isEmpty()) {
foreach (Thing *child, childs) {
thingsToRemove.append(child);
}
}
// check things
QList<RuleId> offendingRules;
qCDebug(dcThingManager) << "Things to remove:";
foreach (Thing *d, thingsToRemove) {
qCDebug(dcThingManager) << " -> " << d->name() << d->id().toString();
// Check if thing is in a rule
foreach (const RuleId &ruleId, m_ruleEngine->findRules(d->id())) {
qCDebug(dcThingManager) << " -> in rule:" << ruleId.toString();
if (!offendingRules.contains(ruleId)) {
offendingRules.append(ruleId);
}
}
}
// check each offending rule if there is a corresponding remove policy
QHash<RuleId, RuleEngine::RemovePolicy> toBeChanged;
QList<RuleId> unhandledRules;
foreach (const RuleId &ruleId, offendingRules) {
bool found = false;
foreach (const RuleId &policyRuleId, removePolicyList.keys()) {
if (ruleId == policyRuleId) {
found = true;
toBeChanged.insert(ruleId, removePolicyList.value(ruleId));
break;
}
}
if (!found)
unhandledRules.append(ruleId);
}
if (!unhandledRules.isEmpty()) {
qCWarning(dcThingManager) << "There are unhandled rules which depend on this thing:\n" << unhandledRules;
return QPair<Thing::ThingError, QList<RuleId> > (Thing::ThingErrorThingInRule, unhandledRules);
}
// Update the rules...
foreach (const RuleId &ruleId, toBeChanged.keys()) {
if (toBeChanged.value(ruleId) == RuleEngine::RemovePolicyCascade) {
m_ruleEngine->removeRule(ruleId);
} else if (toBeChanged.value(ruleId) == RuleEngine::RemovePolicyUpdate){
foreach (Thing *thing, thingsToRemove) {
m_ruleEngine->removeThingFromRule(ruleId, thing->id());
}
}
}
// remove the childs
foreach (Thing *d, childs) {
Thing::ThingError removeError = m_thingManager->removeConfiguredThing(d->id());
if (removeError == Thing::ThingErrorNoError) {
m_logger->removeThingLogs(d->id());
}
}
// delete the things
Thing::ThingError removeError = m_thingManager->removeConfiguredThing(thingId);
if (removeError == Thing::ThingErrorNoError) {
m_logger->removeThingLogs(thingId);
}
return QPair<Thing::ThingError, QList<RuleId> > (Thing::ThingErrorNoError, QList<RuleId>());
}
Thing::ThingError NymeaCore::removeConfiguredThing(const ThingId &thingId, const RuleEngine::RemovePolicy &removePolicy)
{
Thing *thing = m_thingManager->findConfiguredThing(thingId);
if (!thing) {
return Thing::ThingErrorThingNotFound;
}
// Check if this is a child
if (!thing->parentId().isNull() && thing->autoCreated()) {
qCWarning(dcThingManager) << "Thing is an autocreated child of" << thing->parentId().toString() << ". Please remove the parent.";
return Thing::ThingErrorThingIsChild;
}
// FIXME: Let's remove this for now. It will come back with more fine grained control, presumably introducing a RemoveMethod flag in the DeviceClass
// if (thing->autoCreated()) {
// qCWarning(dcThingManager) << "This thing has been auto-created and cannot be deleted manually.";
// return Thing::ThingErrorCreationMethodNotSupported;
// }
// Check if this thing has childs
QList<Thing *> thingsToRemove;
thingsToRemove.append(thing);
QList<Thing *> childs = m_thingManager->findChilds(thingId);
if (!childs.isEmpty()) {
foreach (Thing *child, childs) {
thingsToRemove.append(child);
}
}
// check things
QList<RuleId> offendingRules;
qCDebug(dcThingManager) << "Things to remove:";
foreach (Thing *d, thingsToRemove) {
qCDebug(dcThingManager) << " -> " << d->name() << d->id().toString();
// Check if thing is in a rule
foreach (const RuleId &ruleId, m_ruleEngine->findRules(d->id())) {
qCDebug(dcThingManager) << " -> in rule:" << ruleId.toString();
if (!offendingRules.contains(ruleId)) {
offendingRules.append(ruleId);
}
}
}
// apply removepolicy for foreach rule
foreach (const RuleId &ruleId, offendingRules) {
if (removePolicy == RuleEngine::RemovePolicyCascade) {
m_ruleEngine->removeRule(ruleId);
} else if (removePolicy == RuleEngine::RemovePolicyUpdate){
foreach (Thing *thing, thingsToRemove) {
m_ruleEngine->removeThingFromRule(ruleId, thing->id());
}
}
}
// remove the childs
foreach (Thing *d, childs) {
Thing::ThingError removeError = m_thingManager->removeConfiguredThing(d->id());
if (removeError == Thing::ThingErrorNoError) {
m_logger->removeThingLogs(d->id());
}
}
// delete the things
Thing::ThingError removeError = m_thingManager->removeConfiguredThing(thingId);
if (removeError == Thing::ThingErrorNoError) {
m_logger->removeThingLogs(thingId);
}
return removeError;
}
BrowserActionInfo* NymeaCore::executeBrowserItem(const BrowserAction &browserAction)
{
BrowserActionInfo *info = m_thingManager->executeBrowserItem(browserAction);
connect(info, &BrowserActionInfo::finished, info->thing(), [this, info](){
m_logger->logBrowserAction(info->browserAction(), info->status() == Thing::ThingErrorNoError ? Logging::LoggingLevelInfo : Logging::LoggingLevelAlert, info->status());
});
return info;
}
BrowserItemActionInfo *NymeaCore::executeBrowserItemAction(const BrowserItemAction &browserItemAction)
{
BrowserItemActionInfo *info = m_thingManager->executeBrowserItemAction(browserItemAction);
connect(info, &BrowserItemActionInfo::finished, info->thing(), [this, info](){
m_logger->logBrowserItemAction(info->browserItemAction(), info->status() == Thing::ThingErrorNoError ? Logging::LoggingLevelInfo : Logging::LoggingLevelAlert, info->status());
});
return info;
}
/*! Execute the given \a ruleActions. */
void NymeaCore::executeRuleActions(const QList<RuleAction> ruleActions)
{
QList<Action> actions;
QList<BrowserAction> browserActions;
foreach (const RuleAction &ruleAction, ruleActions) {
if (ruleAction.type() == RuleAction::TypeThing) {
Thing *thing = m_thingManager->findConfiguredThing(ruleAction.thingId());
if (!thing) {
qCWarning(dcRuleEngine()) << "Unable to find thing" << ruleAction.thingId() << "for rule action" << ruleAction;
continue;
}
ActionTypeId actionTypeId = ruleAction.actionTypeId();
ParamList params;
bool ok = true;
foreach (const RuleActionParam &ruleActionParam, ruleAction.ruleActionParams()) {
if (ruleActionParam.isValueBased()) {
params.append(Param(ruleActionParam.paramTypeId(), ruleActionParam.value()));
} else if (ruleActionParam.isStateBased()) {
Thing *stateThing = m_thingManager->findConfiguredThing(ruleActionParam.stateThingId());
if (!stateThing) {
qCWarning(dcRuleEngine()) << "Cannot find thing" << ruleActionParam.stateThingId() << "required by rule action";
ok = false;
break;
}
ThingClass stateThingClass = m_thingManager->findThingClass(stateThing->thingClassId());
if (!stateThingClass.hasStateType(ruleActionParam.stateTypeId())) {
qCWarning(dcRuleEngine()) << "Device" << thing->name() << thing->id() << "does not have a state type" << ruleActionParam.stateTypeId();
ok = false;
break;
}
params.append(Param(ruleActionParam.paramTypeId(), stateThing->stateValue(ruleActionParam.stateTypeId())));
}
}
if (!ok) {
qCWarning(dcRuleEngine()) << "Not executing rule action";
continue;
}
Action action(actionTypeId, thing->id(), Action::TriggeredByRule);
action.setParams(params);
actions.append(action);
} else if (ruleAction.type() == RuleAction::TypeBrowser) {
Thing *thing = m_thingManager->findConfiguredThing(ruleAction.thingId());
if (!thing) {
qCWarning(dcRuleEngine()) << "Unable to find thing" << ruleAction.thingId() << "for rule action" << ruleAction;
continue;
}
BrowserAction browserAction(ruleAction.thingId(), ruleAction.browserItemId());
browserActions.append(browserAction);
} else {
Things things = m_thingManager->findConfiguredThings(ruleAction.interface());
foreach (Thing* thing, things) {
ThingClass thingClass = m_thingManager->findThingClass(thing->thingClassId());
ActionType actionType = thingClass.actionTypes().findByName(ruleAction.interfaceAction());
if (actionType.id().isNull()) {
qCWarning(dcRuleEngine()) << "Error creating Action. The given ThingClass does not implement action:" << ruleAction.interfaceAction();
continue;
}
ParamList params;
bool ok = true;
foreach (const RuleActionParam &ruleActionParam, ruleAction.ruleActionParams()) {
ParamType paramType = actionType.paramTypes().findByName(ruleActionParam.paramName());
if (paramType.id().isNull()) {
qCWarning(dcRuleEngine()) << "Error creating Action. The given ActionType does not have a parameter:" << ruleActionParam.paramName();
ok = false;
continue;
}
if (ruleActionParam.isValueBased()) {
params.append(Param(paramType.id(), ruleActionParam.value()));
} else if (ruleActionParam.isStateBased()) {
Thing *stateThing = m_thingManager->findConfiguredThing(ruleActionParam.stateThingId());
if (!stateThing) {
qCWarning(dcRuleEngine()) << "Cannot find thing" << ruleActionParam.stateThingId() << "required by rule action";
ok = false;
break;
}
ThingClass stateThingClass = m_thingManager->findThingClass(stateThing->thingClassId());
if (!stateThingClass.hasStateType(ruleActionParam.stateTypeId())) {
qCWarning(dcRuleEngine()) << "Thing" << thing->name() << thing->id() << "does not have a state type" << ruleActionParam.stateTypeId();
ok = false;
break;
}
params.append(Param(paramType.id(), stateThing->stateValue(ruleActionParam.stateTypeId())));
}
}
if (!ok) {
qCWarning(dcRuleEngine()) << "Not executing rule action";
continue;
}
Action action = Action(actionType.id(), thing->id(), Action::TriggeredByRule);
action.setParams(params);
actions.append(action);
}
}
}
foreach (const Action &action, actions) {
qCDebug(dcRuleEngine) << "Executing action" << action.actionTypeId() << action.params();
ThingActionInfo *info = m_thingManager->executeAction(action);
connect(info, &ThingActionInfo::finished, this, [info](){
if (info->status() != Thing::ThingErrorNoError) {
qCWarning(dcRuleEngine) << "Error executing action:" << info->status() << info->displayMessage();
}
});
}
foreach (const BrowserAction &browserAction, browserActions) {
BrowserActionInfo *info = executeBrowserItem(browserAction);
connect(info, &BrowserActionInfo::finished, this, [info](){
if (info->status() != Thing::ThingErrorNoError) {
qCWarning(dcRuleEngine) << "Error executing browser action:" << info->status();
}
});
}
}
/*! Calls the metheod RuleEngine::removeRule(\a id).
* \sa RuleEngine, */
RuleEngine::RuleError NymeaCore::removeRule(const RuleId &id)
{
RuleEngine::RuleError removeError = m_ruleEngine->removeRule(id);
if (removeError == RuleEngine::RuleErrorNoError)
m_logger->removeRuleLogs(id);
return removeError;
}
NymeaConfiguration *NymeaCore::configuration() const NymeaConfiguration *NymeaCore::configuration() const
{ {
return m_configuration; return m_configuration;
@ -663,117 +332,6 @@ ExperienceManager *NymeaCore::experienceManager() const
return m_experienceManager; return m_experienceManager;
} }
void NymeaCore::onEventTriggered(const Event &event)
{
emit eventTriggered(event);
evaluateRules(event);
}
void NymeaCore::onThingStateChanged(Thing *thing, const StateTypeId &stateTypeId, const QVariant &value, const QVariant &minValue, const QVariant &maxValue)
{
emit thingStateChanged(thing, stateTypeId, value, minValue, maxValue);
// The rule engine can have event based rules that would trigger when a state changes
// without "binding" to the state (as a stateEvaluator would do). So generate a fake event
// for every state change.
// TODO: This whole rule engine related code in this file should probably move into the RuleEngine itself.
Param valueParam(ParamTypeId(stateTypeId.toString()), value);
Event event(EventTypeId(stateTypeId.toString()), thing->id(), ParamList() << valueParam);
evaluateRules(event);
}
void NymeaCore::evaluateRules(const Event &event)
{
QList<RuleAction> actions;
QList<RuleAction> eventBasedActions;
foreach (const Rule &rule, m_ruleEngine->evaluateEvent(event)) {
if (m_executingRules.contains(rule.id())) {
qCWarning(dcRuleEngine()) << "WARNING: Loop detected in rule execution for rule" << rule.id().toString() << rule.name();
break;
}
m_executingRules.append(rule.id());
// Event based
if (!rule.eventDescriptors().isEmpty()) {
m_logger->logRuleTriggered(rule);
QList<RuleAction> tmp;
if (rule.statesActive() && rule.timeActive()) {
qCDebug(dcRuleEngineDebug()) << "Executing actions";
tmp = rule.actions();
} else {
qCDebug(dcRuleEngineDebug()) << "Executing exitActions";
tmp = rule.exitActions();
}
// check if we have an event based action or a normal action
foreach (const RuleAction &action, tmp) {
if (action.isEventBased()) {
eventBasedActions.append(action);
} else {
actions.append(action);
}
}
} else {
// State based rule
m_logger->logRuleActiveChanged(rule);
emit ruleActiveChanged(rule);
if (rule.active()) {
actions.append(rule.actions());
} else {
actions.append(rule.exitActions());
}
}
}
// Set action params, depending on the event value
foreach (RuleAction ruleAction, eventBasedActions) {
RuleActionParams newParams;
foreach (RuleActionParam ruleActionParam, ruleAction.ruleActionParams()) {
// if this event param should be taken over in this action
if (event.eventTypeId() == ruleActionParam.eventTypeId()) {
QVariant eventValue = event.params().paramValue(ruleActionParam.eventParamTypeId());
// TODO: limits / scale calculation -> actionValue = eventValue * x
// something like a EventParamDescriptor
ruleActionParam.setValue(eventValue);
qCDebug(dcRuleEngine) << "Using param value from event:" << ruleActionParam.value();
}
newParams.append(ruleActionParam);
}
ruleAction.setRuleActionParams(newParams);
actions.append(ruleAction);
}
executeRuleActions(actions);
m_executingRules.clear();
}
void NymeaCore::onDateTimeChanged(const QDateTime &dateTime)
{
QList<RuleAction> actions;
foreach (const Rule &rule, m_ruleEngine->evaluateTime(dateTime)) {
// TimeEvent based
if (!rule.timeDescriptor().timeEventItems().isEmpty()) {
m_logger->logRuleTriggered(rule);
if (rule.statesActive() && rule.timeActive()) {
actions.append(rule.actions());
} else {
actions.append(rule.exitActions());
}
} else {
// Calendar based rule
m_logger->logRuleActiveChanged(rule);
emit ruleActiveChanged(rule);
if (rule.active()) {
actions.append(rule.actions());
} else {
actions.append(rule.exitActions());
}
}
}
executeRuleActions(actions);
}
LogEngine* NymeaCore::logEngine() const LogEngine* NymeaCore::logEngine() const
{ {
return m_logger; return m_logger;
@ -784,65 +342,8 @@ JsonRPCServerImplementation *NymeaCore::jsonRPCServer() const
return m_serverManager->jsonServer(); return m_serverManager->jsonServer();
} }
void NymeaCore::onThingDisappeared(const ThingId &thingId)
{
Thing *thing = m_thingManager->findConfiguredThing(thingId);
if (!thing) {
return;
}
// Check if this thing has childs
Things thingsToRemove;
thingsToRemove.append(thing);
QList<Thing *> childs = m_thingManager->findChilds(thingId);
if (!childs.isEmpty()) {
foreach (Thing *child, childs) {
thingsToRemove.append(child);
}
}
// check things
QList<RuleId> offendingRules;
qCDebug(dcThingManager) << "Thing to remove:";
foreach (Thing *d, thingsToRemove) {
qCDebug(dcThingManager) << " -> " << d->name() << d->id().toString();
// Check if thing is in a rule
foreach (const RuleId &ruleId, m_ruleEngine->findRules(d->id())) {
qCDebug(dcThingManager) << " -> in rule:" << ruleId.toString();
if (!offendingRules.contains(ruleId)) {
offendingRules.append(ruleId);
}
}
}
// update involved rules
foreach (const RuleId &ruleId, offendingRules) {
foreach (Thing *thing, thingsToRemove) {
m_ruleEngine->removeThingFromRule(ruleId, thing->id());
}
}
// remove the child devices
foreach (Thing *d, childs) {
Thing::ThingError removeError = m_thingManager->removeConfiguredThing(d->id());
if (removeError == Thing::ThingErrorNoError) {
m_logger->removeThingLogs(d->id());
}
}
// delete the thing
Thing::ThingError removeError = m_thingManager->removeConfiguredThing(thingId);
if (removeError == Thing::ThingErrorNoError) {
m_logger->removeThingLogs(thingId);
}
}
void NymeaCore::thingManagerLoaded() void NymeaCore::thingManagerLoaded()
{ {
m_ruleEngine->init();
// Evaluate rules on current time
onDateTimeChanged(m_timeManager->currentDateTime());
// Tell hardare resources we're done with loading stuff... // Tell hardare resources we're done with loading stuff...
m_hardwareManager->thingsLoaded(); m_hardwareManager->thingsLoaded();
@ -881,7 +382,8 @@ void NymeaCore::thingManagerLoaded()
qCDebug(dcCore()) << "Cleaning up stale thing tag" << tag.tagId(); qCDebug(dcCore()) << "Cleaning up stale thing tag" << tag.tagId();
m_tagsStorage->removeTag(tag); m_tagsStorage->removeTag(tag);
} }
} }
} }
} }

View File

@ -87,15 +87,6 @@ public:
void init(const QStringList &additionalInterfaces = QStringList()); void init(const QStringList &additionalInterfaces = QStringList());
void destroy(); void destroy();
// Thing handling
QPair<Thing::ThingError, QList<RuleId> >removeConfiguredThing(const ThingId &thingId, const QHash<RuleId, RuleEngine::RemovePolicy> &removePolicyList);
Thing::ThingError removeConfiguredThing(const ThingId &thingId, const RuleEngine::RemovePolicy &removePolicy);
BrowserActionInfo* executeBrowserItem(const BrowserAction &browserAction);
BrowserItemActionInfo* executeBrowserItemAction(const BrowserItemAction &browserItemAction);
void executeRuleActions(const QList<RuleAction> ruleActions);
RuleEngine::RuleError removeRule(const RuleId &id); RuleEngine::RuleError removeRule(const RuleId &id);
NymeaConfiguration *configuration() const; NymeaConfiguration *configuration() const;
@ -125,19 +116,6 @@ public:
signals: signals:
void initialized(); void initialized();
void pluginConfigChanged(const PluginId &id, const ParamList &config);
void eventTriggered(const Event &event);
void thingStateChanged(Thing *thing, const StateTypeId &stateTypeId, const QVariant &value, const QVariant &minValue, const QVariant &maxValue);
void thingRemoved(const ThingId &thingId);
void thingAdded(Thing *thing);
void thingChanged(Thing *thing);
void thingSettingChanged(const ThingId &thingId, const ParamTypeId &settingParamTypeId, const QVariant &value);
void ruleRemoved(const RuleId &ruleId);
void ruleAdded(const Rule &rule);
void ruleActiveChanged(const Rule &rule);
void ruleConfigurationChanged(const Rule &rule);
private: private:
explicit NymeaCore(QObject *parent = nullptr); explicit NymeaCore(QObject *parent = nullptr);
static NymeaCore *s_instance; static NymeaCore *s_instance;
@ -165,14 +143,8 @@ private:
SerialPortMonitor *m_serialPortMonitor; SerialPortMonitor *m_serialPortMonitor;
ModbusRtuManager *m_modbusRtuManager; ModbusRtuManager *m_modbusRtuManager;
QList<RuleId> m_executingRules;
private slots: private slots:
void onEventTriggered(const Event &event);
void onThingStateChanged(Thing *thing, const StateTypeId &stateTypeId, const QVariant &value, const QVariant &minValue, const QVariant &maxValue);
void evaluateRules(const Event &event);
void onDateTimeChanged(const QDateTime &dateTime);
void onThingDisappeared(const ThingId &thingId);
void thingManagerLoaded(); void thingManagerLoaded();
}; };

View File

@ -207,19 +207,16 @@ bool Rule::isConsistent() const
{ {
// check if this rules is based on any event and contains exit actions // check if this rules is based on any event and contains exit actions
if (!eventDescriptors().isEmpty() && stateEvaluator().isEmpty() && timeDescriptor().calendarItems().isEmpty() && !exitActions().isEmpty()) { if (!eventDescriptors().isEmpty() && stateEvaluator().isEmpty() && timeDescriptor().calendarItems().isEmpty() && !exitActions().isEmpty()) {
qCWarning(dcRuleEngine) << "Rule not consistent. The exitActions will never be executed if the rule contains an eventDescriptor but no stateEvaluator or calendarItem.";
return false; return false;
} }
// check if this rules is based on any time events and contains exit actions // check if this rules is based on any time events and contains exit actions
if (!timeDescriptor().timeEventItems().isEmpty() && stateEvaluator().isEmpty() && timeDescriptor().calendarItems().isEmpty() && !exitActions().isEmpty()) { if (!timeDescriptor().timeEventItems().isEmpty() && stateEvaluator().isEmpty() && timeDescriptor().calendarItems().isEmpty() && !exitActions().isEmpty()) {
qCWarning(dcRuleEngine) << "Rule not consistent. The exitActions will never be executed if the rule contains a timeEvent but no stateEvaluator or calendarItem.";
return false; return false;
} }
// check if there are any actions // check if there are any actions
if (actions().isEmpty()) { if (actions().isEmpty()) {
qCWarning(dcRuleEngine) << "Rule not consistent. A rule without actions has no effect.";
return false; return false;
} }

View File

@ -110,31 +110,58 @@
#include "ruleengine.h" #include "ruleengine.h"
#include "nymeacore.h"
#include "loggingcategories.h" #include "loggingcategories.h"
#include "time/calendaritem.h" #include "time/calendaritem.h"
#include "time/repeatingoption.h" #include "time/repeatingoption.h"
#include "time/timeeventitem.h" #include "time/timeeventitem.h"
#include "time/timemanager.h"
#include "types/eventdescriptor.h" #include "types/eventdescriptor.h"
#include "types/paramdescriptor.h" #include "types/paramdescriptor.h"
#include "nymeasettings.h" #include "nymeasettings.h"
#include "integrations/thingmanager.h" #include "integrations/thingmanager.h"
#include "integrations/thing.h" #include "integrations/thing.h"
#include "logging/logengine.h"
#include <QDebug> #include <QDebug>
#include <QStringList> #include <QStringList>
#include <QStandardPaths> #include <QStandardPaths>
#include <QCoreApplication> #include <QCoreApplication>
NYMEA_LOGGING_CATEGORY(dcRuleEngine, "RuleEngine")
NYMEA_LOGGING_CATEGORY(dcRuleEngineDebug, "RuleEngineDebug")
namespace nymeaserver { namespace nymeaserver {
/*! Constructs the RuleEngine with the given \a parent. Although it wouldn't harm to have multiple RuleEngines, there is one /*! Constructs the RuleEngine with the given \a parent. Although it wouldn't harm to have multiple RuleEngines, there is one
instance available from \l{NymeaCore}. This one should be used instead of creating multiple ones. instance available from \l{NymeaCore}. This one should be used instead of creating multiple ones.
*/ */
RuleEngine::RuleEngine(QObject *parent) : RuleEngine::RuleEngine(ThingManager *thingManager, TimeManager *timeManager, LogEngine *logEngine, QObject *parent) :
QObject(parent) QObject(parent),
m_thingManager(thingManager),
m_timeManager(timeManager),
m_logEngine(logEngine)
{ {
connect(m_thingManager, &ThingManager::eventTriggered, this, &RuleEngine::onEventTriggered);
connect(m_thingManager, &ThingManager::thingStateChanged, this, [this](Thing *thing, const StateTypeId &stateTypeId, const QVariant &value, const QVariant &/*minValue*/, const QVariant &/*maxValue*/){
// There can be event based rules that would trigger when a state changes
// without "binding" to the state (as a stateEvaluator would do). So generate a fake event
// for every state change.
Param valueParam(ParamTypeId(stateTypeId.toString()), value);
Event event(EventTypeId(stateTypeId.toString()), thing->id(), ParamList() << valueParam);
onEventTriggered(event);
});
connect(m_thingManager, &ThingManager::thingRemoved, this, &RuleEngine::onThingRemoved);
connect(m_timeManager, &TimeManager::dateTimeChanged, this, &RuleEngine::onDateTimeChanged);
connect(m_thingManager, &ThingManager::loaded, this, [=](){
init();
onDateTimeChanged(m_timeManager->currentDateTime());
});
} }
/*! Destructor of the \l{RuleEngine}. */ /*! Destructor of the \l{RuleEngine}. */
@ -150,7 +177,7 @@ RuleEngine::~RuleEngine()
*/ */
QList<Rule> RuleEngine::evaluateEvent(const Event &event) QList<Rule> RuleEngine::evaluateEvent(const Event &event)
{ {
Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(event.thingId()); Thing *thing = m_thingManager->findConfiguredThing(event.thingId());
if (!thing) { if (!thing) {
qCWarning(dcRuleEngine()) << "Invalid event. ThingID does not reference a valid thing"; qCWarning(dcRuleEngine()) << "Invalid event. ThingID does not reference a valid thing";
return QList<Rule>(); return QList<Rule>();
@ -307,7 +334,7 @@ RuleEngine::RuleError RuleEngine::addRule(const Rule &rule, bool fromEdit)
} }
if (!rule.isConsistent()) { if (!rule.isConsistent()) {
qCWarning(dcRuleEngine) << "Rule inconsistent."; qCWarning(dcRuleEngine) << "Invalid rule format. (Either missing actions, or exitActions without condition given.)";
return RuleErrorInvalidRuleFormat; return RuleErrorInvalidRuleFormat;
} }
@ -319,14 +346,14 @@ RuleEngine::RuleError RuleEngine::addRule(const Rule &rule, bool fromEdit)
} }
if (eventDescriptor.type() == EventDescriptor::TypeThing) { if (eventDescriptor.type() == EventDescriptor::TypeThing) {
// check thingId // check thingId
Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(eventDescriptor.thingId()); Thing *thing = m_thingManager->findConfiguredThing(eventDescriptor.thingId());
if (!thing) { if (!thing) {
qCWarning(dcRuleEngine) << "Cannot create rule. No configured thing for eventTypeId" << eventDescriptor.eventTypeId(); qCWarning(dcRuleEngine) << "Cannot create rule. No configured thing for eventTypeId" << eventDescriptor.eventTypeId();
return RuleErrorThingNotFound; return RuleErrorThingNotFound;
} }
// Check eventTypeId for this deivce // Check eventTypeId for this deivce
ThingClass thingClass = NymeaCore::instance()->thingManager()->findThingClass(thing->thingClassId()); ThingClass thingClass = m_thingManager->findThingClass(thing->thingClassId());
bool eventTypeFound = false; bool eventTypeFound = false;
foreach (const EventType &eventType, thingClass.eventTypes()) { foreach (const EventType &eventType, thingClass.eventTypes()) {
if (eventType.id() == eventDescriptor.eventTypeId()) { if (eventType.id() == eventDescriptor.eventTypeId()) {
@ -345,7 +372,7 @@ RuleEngine::RuleError RuleEngine::addRule(const Rule &rule, bool fromEdit)
} }
} else { } else {
// Interface based event // Interface based event
Interface iface = NymeaCore::instance()->thingManager()->supportedInterfaces().findByName(eventDescriptor.interface()); Interface iface = m_thingManager->supportedInterfaces().findByName(eventDescriptor.interface());
if (!iface.isValid()) { if (!iface.isValid()) {
qWarning(dcRuleEngine()) << "No such interface:" << eventDescriptor.interface(); qWarning(dcRuleEngine()) << "No such interface:" << eventDescriptor.interface();
return RuleErrorInterfaceNotFound; return RuleErrorInterfaceNotFound;
@ -507,6 +534,8 @@ RuleEngine::RuleError RuleEngine::removeRule(const RuleId &ruleId, bool fromEdit
settings.remove(""); settings.remove("");
settings.endGroup(); settings.endGroup();
m_logEngine->removeRuleLogs(ruleId);
if (!fromEdit) if (!fromEdit)
emit ruleRemoved(ruleId); emit ruleRemoved(ruleId);
@ -536,7 +565,7 @@ RuleEngine::RuleError RuleEngine::enableRule(const RuleId &ruleId)
saveRule(rule); saveRule(rule);
emit ruleConfigurationChanged(rule); emit ruleConfigurationChanged(rule);
NymeaCore::instance()->logEngine()->logRuleEnabledChanged(rule, true); m_logEngine->logRuleEnabledChanged(rule, true);
qCDebug(dcRuleEngine()) << "Rule" << rule.name() << rule.id().toString() << "enabled."; qCDebug(dcRuleEngine()) << "Rule" << rule.name() << rule.id().toString() << "enabled.";
return RuleErrorNoError; return RuleErrorNoError;
@ -562,7 +591,7 @@ RuleEngine::RuleError RuleEngine::disableRule(const RuleId &ruleId)
saveRule(rule); saveRule(rule);
emit ruleConfigurationChanged(rule); emit ruleConfigurationChanged(rule);
NymeaCore::instance()->logEngine()->logRuleEnabledChanged(rule, false); m_logEngine->logRuleEnabledChanged(rule, false);
qCDebug(dcRuleEngine()) << "Rule" << rule.name() << rule.id().toString() << "disabled."; qCDebug(dcRuleEngine()) << "Rule" << rule.name() << rule.id().toString() << "disabled.";
return RuleErrorNoError; return RuleErrorNoError;
} }
@ -597,8 +626,8 @@ RuleEngine::RuleError RuleEngine::executeActions(const RuleId &ruleId)
} }
qCDebug(dcRuleEngine) << "Executing rule actions of rule" << rule.name() << rule.id().toString(); qCDebug(dcRuleEngine) << "Executing rule actions of rule" << rule.name() << rule.id().toString();
NymeaCore::instance()->logEngine()->logRuleActionsExecuted(rule); m_logEngine->logRuleActionsExecuted(rule);
NymeaCore::instance()->executeRuleActions(rule.actions()); executeRuleActions(rule.actions());
return RuleErrorNoError; return RuleErrorNoError;
} }
@ -629,8 +658,8 @@ RuleEngine::RuleError RuleEngine::executeExitActions(const RuleId &ruleId)
} }
qCDebug(dcRuleEngine) << "Executing rule exit actions of rule" << rule.name() << rule.id().toString(); qCDebug(dcRuleEngine) << "Executing rule exit actions of rule" << rule.name() << rule.id().toString();
NymeaCore::instance()->logEngine()->logRuleExitActionsExecuted(rule); m_logEngine->logRuleExitActionsExecuted(rule);
NymeaCore::instance()->executeRuleActions(rule.exitActions()); executeRuleActions(rule.exitActions());
return RuleErrorNoError; return RuleErrorNoError;
} }
@ -830,7 +859,7 @@ bool RuleEngine::containsEvent(const Rule &rule, const Event &event, const Thing
// If this is a interface based rule, the thing must implement the interface // If this is a interface based rule, the thing must implement the interface
if (eventDescriptor.type() == EventDescriptor::TypeInterface) { if (eventDescriptor.type() == EventDescriptor::TypeInterface) {
ThingClass dc = NymeaCore::instance()->thingManager()->findThingClass(thingClassId); ThingClass dc = m_thingManager->findThingClass(thingClassId);
if (!dc.interfaces().contains(eventDescriptor.interface())) { if (!dc.interfaces().contains(eventDescriptor.interface())) {
// ThingClass for this event doesn't implement the interface for this eventDescriptor // ThingClass for this event doesn't implement the interface for this eventDescriptor
continue; continue;
@ -863,7 +892,7 @@ bool RuleEngine::containsEvent(const Rule &rule, const Event &event, const Thing
allOK = false; allOK = false;
continue; continue;
} }
ThingClass dc = NymeaCore::instance()->thingManager()->findThingClass(thingClassId); ThingClass dc = m_thingManager->findThingClass(thingClassId);
EventType et = dc.eventTypes().findById(event.eventTypeId()); EventType et = dc.eventTypes().findById(event.eventTypeId());
StateType st = dc.stateTypes().findById(event.eventTypeId()); StateType st = dc.stateTypes().findById(event.eventTypeId());
if (et.isValid()) { if (et.isValid()) {
@ -928,8 +957,8 @@ bool RuleEngine::containsState(const StateEvaluator &stateEvaluator, const Event
return true; return true;
} }
} else { } else {
Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(stateChangeEvent.thingId()); Thing *thing = m_thingManager->findConfiguredThing(stateChangeEvent.thingId());
ThingClass thingClass = NymeaCore::instance()->thingManager()->findThingClass(thing->thingClassId()); ThingClass thingClass = m_thingManager->findThingClass(thing->thingClassId());
if (thingClass.interfaces().contains(stateEvaluator.stateDescriptor().interface())) { if (thingClass.interfaces().contains(stateEvaluator.stateDescriptor().interface())) {
return true; return true;
} }
@ -954,13 +983,13 @@ RuleEngine::RuleError RuleEngine::checkRuleAction(const RuleAction &ruleAction,
ActionType actionType; ActionType actionType;
if (ruleAction.type() == RuleAction::TypeThing) { if (ruleAction.type() == RuleAction::TypeThing) {
Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(ruleAction.thingId()); Thing *thing = m_thingManager->findConfiguredThing(ruleAction.thingId());
if (!thing) { if (!thing) {
qCWarning(dcRuleEngine) << "Cannot create rule. No configured thing with ID" << ruleAction.thingId(); qCWarning(dcRuleEngine) << "Cannot create rule. No configured thing with ID" << ruleAction.thingId();
return RuleErrorThingNotFound; return RuleErrorThingNotFound;
} }
ThingClass thingClass = NymeaCore::instance()->thingManager()->findThingClass(thing->thingClassId()); ThingClass thingClass = m_thingManager->findThingClass(thing->thingClassId());
if (!thingClass.hasActionType(ruleAction.actionTypeId())) { if (!thingClass.hasActionType(ruleAction.actionTypeId())) {
qCWarning(dcRuleEngine) << "Cannot create rule. Thing " + thing->name() + " has no action type:" << ruleAction.actionTypeId(); qCWarning(dcRuleEngine) << "Cannot create rule. Thing " + thing->name() + " has no action type:" << ruleAction.actionTypeId();
return RuleErrorActionTypeNotFound; return RuleErrorActionTypeNotFound;
@ -968,7 +997,7 @@ RuleEngine::RuleError RuleEngine::checkRuleAction(const RuleAction &ruleAction,
actionType = thingClass.actionTypes().findById(ruleAction.actionTypeId()); actionType = thingClass.actionTypes().findById(ruleAction.actionTypeId());
} else if (ruleAction.type() == RuleAction::TypeInterface) { } else if (ruleAction.type() == RuleAction::TypeInterface) {
Interface iface = NymeaCore::instance()->thingManager()->supportedInterfaces().findByName(ruleAction.interface()); Interface iface = m_thingManager->supportedInterfaces().findByName(ruleAction.interface());
if (!iface.isValid()) { if (!iface.isValid()) {
qCWarning(dcRuleEngine()) << "Cannot create rule. No such interface:" << ruleAction.interface(); qCWarning(dcRuleEngine()) << "Cannot create rule. No such interface:" << ruleAction.interface();
return RuleError::RuleErrorInterfaceNotFound; return RuleError::RuleErrorInterfaceNotFound;
@ -979,7 +1008,7 @@ RuleEngine::RuleError RuleEngine::checkRuleAction(const RuleAction &ruleAction,
return RuleError::RuleErrorActionTypeNotFound; return RuleError::RuleErrorActionTypeNotFound;
} }
} else if (ruleAction.type() == RuleAction::TypeBrowser) { } else if (ruleAction.type() == RuleAction::TypeBrowser) {
Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(ruleAction.thingId()); Thing *thing = m_thingManager->findConfiguredThing(ruleAction.thingId());
if (!thing) { if (!thing) {
qCWarning(dcRuleEngine) << "Cannot create rule. No configured thing with ID" << ruleAction.thingId(); qCWarning(dcRuleEngine) << "Cannot create rule. No configured thing with ID" << ruleAction.thingId();
return RuleErrorThingNotFound; return RuleErrorThingNotFound;
@ -1057,12 +1086,12 @@ RuleEngine::RuleError RuleEngine::checkRuleActionParam(const RuleActionParam &ru
return RuleErrorTypesNotMatching; return RuleErrorTypesNotMatching;
} }
} else if (ruleActionParam.isStateBased()) { } else if (ruleActionParam.isStateBased()) {
Thing *d = NymeaCore::instance()->thingManager()->findConfiguredThing(ruleActionParam.stateThingId()); Thing *d = m_thingManager->findConfiguredThing(ruleActionParam.stateThingId());
if (!d) { if (!d) {
qCWarning(dcRuleEngine()) << "Cannot create Rule. ThingId from RuleActionParam" << ruleActionParam.paramTypeId() << "not found in system."; qCWarning(dcRuleEngine()) << "Cannot create Rule. ThingId from RuleActionParam" << ruleActionParam.paramTypeId() << "not found in system.";
return RuleErrorThingNotFound; return RuleErrorThingNotFound;
} }
ThingClass stateThingClass = NymeaCore::instance()->thingManager()->findThingClass(d->thingClassId()); ThingClass stateThingClass = m_thingManager->findThingClass(d->thingClassId());
StateType stateType = stateThingClass.stateTypes().findById(ruleActionParam.stateTypeId()); StateType stateType = stateThingClass.stateTypes().findById(ruleActionParam.stateTypeId());
QVariant::Type actionParamType = getActionParamType(actionType.id(), ruleActionParam.paramTypeId()); QVariant::Type actionParamType = getActionParamType(actionType.id(), ruleActionParam.paramTypeId());
QVariant v(stateType.type()); QVariant v(stateType.type());
@ -1090,7 +1119,7 @@ RuleEngine::RuleError RuleEngine::checkRuleActionParam(const RuleActionParam &ru
QVariant::Type RuleEngine::getActionParamType(const ActionTypeId &actionTypeId, const ParamTypeId &paramTypeId) QVariant::Type RuleEngine::getActionParamType(const ActionTypeId &actionTypeId, const ParamTypeId &paramTypeId)
{ {
foreach (const ThingClass &thingClass, NymeaCore::instance()->thingManager()->supportedThings()) { foreach (const ThingClass &thingClass, m_thingManager->supportedThings()) {
foreach (const ActionType &actionType, thingClass.actionTypes()) { foreach (const ActionType &actionType, thingClass.actionTypes()) {
if (actionType.id() == actionTypeId) { if (actionType.id() == actionTypeId) {
foreach (const ParamType &paramType, actionType.paramTypes()) { foreach (const ParamType &paramType, actionType.paramTypes()) {
@ -1107,7 +1136,7 @@ QVariant::Type RuleEngine::getActionParamType(const ActionTypeId &actionTypeId,
QVariant::Type RuleEngine::getEventParamType(const EventTypeId &eventTypeId, const ParamTypeId &paramTypeId) QVariant::Type RuleEngine::getEventParamType(const EventTypeId &eventTypeId, const ParamTypeId &paramTypeId)
{ {
foreach (const ThingClass &thingClass, NymeaCore::instance()->thingManager()->supportedThings()) { foreach (const ThingClass &thingClass, m_thingManager->supportedThings()) {
foreach (const EventType &eventType, thingClass.eventTypes()) { foreach (const EventType &eventType, thingClass.eventTypes()) {
if (eventType.id() == eventTypeId) { if (eventType.id() == eventTypeId) {
foreach (const ParamType &paramType, eventType.paramTypes()) { foreach (const ParamType &paramType, eventType.paramTypes()) {
@ -1371,6 +1400,231 @@ QList<RuleAction> RuleEngine::loadRuleActions(NymeaSettings *settings)
return actions; return actions;
} }
void RuleEngine::executeRuleActions(const QList<RuleAction> ruleActions)
{
QList<Action> actions;
QList<BrowserAction> browserActions;
foreach (const RuleAction &ruleAction, ruleActions) {
if (ruleAction.type() == RuleAction::TypeThing) {
Thing *thing = m_thingManager->findConfiguredThing(ruleAction.thingId());
if (!thing) {
qCWarning(dcRuleEngine()) << "Unable to find thing" << ruleAction.thingId() << "for rule action" << ruleAction;
continue;
}
ActionTypeId actionTypeId = ruleAction.actionTypeId();
ParamList params;
bool ok = true;
foreach (const RuleActionParam &ruleActionParam, ruleAction.ruleActionParams()) {
if (ruleActionParam.isValueBased()) {
params.append(Param(ruleActionParam.paramTypeId(), ruleActionParam.value()));
} else if (ruleActionParam.isStateBased()) {
Thing *stateThing = m_thingManager->findConfiguredThing(ruleActionParam.stateThingId());
if (!stateThing) {
qCWarning(dcRuleEngine()) << "Cannot find thing" << ruleActionParam.stateThingId() << "required by rule action";
ok = false;
break;
}
ThingClass stateThingClass = m_thingManager->findThingClass(stateThing->thingClassId());
if (!stateThingClass.hasStateType(ruleActionParam.stateTypeId())) {
qCWarning(dcRuleEngine()) << "Device" << thing->name() << thing->id() << "does not have a state type" << ruleActionParam.stateTypeId();
ok = false;
break;
}
params.append(Param(ruleActionParam.paramTypeId(), stateThing->stateValue(ruleActionParam.stateTypeId())));
}
}
if (!ok) {
qCWarning(dcRuleEngine()) << "Not executing rule action";
continue;
}
Action action(actionTypeId, thing->id(), Action::TriggeredByRule);
action.setParams(params);
actions.append(action);
} else if (ruleAction.type() == RuleAction::TypeBrowser) {
Thing *thing = m_thingManager->findConfiguredThing(ruleAction.thingId());
if (!thing) {
qCWarning(dcRuleEngine()) << "Unable to find thing" << ruleAction.thingId() << "for rule action" << ruleAction;
continue;
}
BrowserAction browserAction(ruleAction.thingId(), ruleAction.browserItemId());
browserActions.append(browserAction);
} else {
Things things = m_thingManager->findConfiguredThings(ruleAction.interface());
foreach (Thing* thing, things) {
ThingClass thingClass = m_thingManager->findThingClass(thing->thingClassId());
ActionType actionType = thingClass.actionTypes().findByName(ruleAction.interfaceAction());
if (actionType.id().isNull()) {
qCWarning(dcRuleEngine()) << "Error creating Action. The given ThingClass does not implement action:" << ruleAction.interfaceAction();
continue;
}
ParamList params;
bool ok = true;
foreach (const RuleActionParam &ruleActionParam, ruleAction.ruleActionParams()) {
ParamType paramType = actionType.paramTypes().findByName(ruleActionParam.paramName());
if (paramType.id().isNull()) {
qCWarning(dcRuleEngine()) << "Error creating Action. The given ActionType does not have a parameter:" << ruleActionParam.paramName();
ok = false;
continue;
}
if (ruleActionParam.isValueBased()) {
params.append(Param(paramType.id(), ruleActionParam.value()));
} else if (ruleActionParam.isStateBased()) {
Thing *stateThing = m_thingManager->findConfiguredThing(ruleActionParam.stateThingId());
if (!stateThing) {
qCWarning(dcRuleEngine()) << "Cannot find thing" << ruleActionParam.stateThingId() << "required by rule action";
ok = false;
break;
}
ThingClass stateThingClass = m_thingManager->findThingClass(stateThing->thingClassId());
if (!stateThingClass.hasStateType(ruleActionParam.stateTypeId())) {
qCWarning(dcRuleEngine()) << "Thing" << thing->name() << thing->id() << "does not have a state type" << ruleActionParam.stateTypeId();
ok = false;
break;
}
params.append(Param(paramType.id(), stateThing->stateValue(ruleActionParam.stateTypeId())));
}
}
if (!ok) {
qCWarning(dcRuleEngine()) << "Not executing rule action";
continue;
}
Action action = Action(actionType.id(), thing->id(), Action::TriggeredByRule);
action.setParams(params);
actions.append(action);
}
}
}
foreach (const Action &action, actions) {
qCDebug(dcRuleEngine) << "Executing action" << action.actionTypeId() << action.params();
ThingActionInfo *info = m_thingManager->executeAction(action);
connect(info, &ThingActionInfo::finished, this, [info](){
if (info->status() != Thing::ThingErrorNoError) {
qCWarning(dcRuleEngine) << "Error executing action:" << info->status() << info->displayMessage();
}
});
}
foreach (const BrowserAction &browserAction, browserActions) {
BrowserActionInfo *info = m_thingManager->executeBrowserItem(browserAction);
connect(info, &BrowserActionInfo::finished, this, [info, this](){
m_logEngine->logBrowserAction(info->browserAction(), info->status() == Thing::ThingErrorNoError ? Logging::LoggingLevelInfo : Logging::LoggingLevelAlert, info->status());
if (info->status() != Thing::ThingErrorNoError) {
qCWarning(dcRuleEngine) << "Error executing browser action:" << info->status();
}
});
}
}
void RuleEngine::onEventTriggered(const Event &event)
{
QList<RuleAction> actions;
QList<RuleAction> eventBasedActions;
foreach (const Rule &rule, evaluateEvent(event)) {
if (m_executingRules.contains(rule.id())) {
qCWarning(dcRuleEngine()) << "WARNING: Loop detected in rule execution for rule" << rule.id().toString() << rule.name();
break;
}
m_executingRules.append(rule.id());
// Event based
if (!rule.eventDescriptors().isEmpty()) {
m_logEngine->logRuleTriggered(rule);
QList<RuleAction> tmp;
if (rule.statesActive() && rule.timeActive()) {
qCDebug(dcRuleEngineDebug()) << "Executing actions";
tmp = rule.actions();
} else {
qCDebug(dcRuleEngineDebug()) << "Executing exitActions";
tmp = rule.exitActions();
}
// check if we have an event based action or a normal action
foreach (const RuleAction &action, tmp) {
if (action.isEventBased()) {
eventBasedActions.append(action);
} else {
actions.append(action);
}
}
} else {
// State based rule
m_logEngine->logRuleActiveChanged(rule);
emit ruleActiveChanged(rule);
if (rule.active()) {
actions.append(rule.actions());
} else {
actions.append(rule.exitActions());
}
}
}
// Set action params, depending on the event value
foreach (RuleAction ruleAction, eventBasedActions) {
RuleActionParams newParams;
foreach (RuleActionParam ruleActionParam, ruleAction.ruleActionParams()) {
// if this event param should be taken over in this action
if (event.eventTypeId() == ruleActionParam.eventTypeId()) {
QVariant eventValue = event.params().paramValue(ruleActionParam.eventParamTypeId());
// TODO: limits / scale calculation -> actionValue = eventValue * x
// something like a EventParamDescriptor
ruleActionParam.setValue(eventValue);
qCDebug(dcRuleEngine) << "Using param value from event:" << ruleActionParam.value();
}
newParams.append(ruleActionParam);
}
ruleAction.setRuleActionParams(newParams);
actions.append(ruleAction);
}
executeRuleActions(actions);
m_executingRules.clear();
}
void RuleEngine::onDateTimeChanged(const QDateTime &dateTime)
{
QList<RuleAction> actions;
foreach (const Rule &rule, evaluateTime(dateTime)) {
// TimeEvent based
if (!rule.timeDescriptor().timeEventItems().isEmpty()) {
m_logEngine->logRuleTriggered(rule);
if (rule.statesActive() && rule.timeActive()) {
actions.append(rule.actions());
} else {
actions.append(rule.exitActions());
}
} else {
// Calendar based rule
m_logEngine->logRuleActiveChanged(rule);
emit ruleActiveChanged(rule);
if (rule.active()) {
actions.append(rule.actions());
} else {
actions.append(rule.exitActions());
}
}
}
executeRuleActions(actions);
}
void RuleEngine::onThingRemoved(const ThingId &thingId)
{
QList<RuleId> affectedRules;
foreach (const RuleId &ruleId, findRules(thingId)) {
if (!affectedRules.contains(ruleId)) {
affectedRules.append(ruleId);
}
}
while (!affectedRules.isEmpty()) {
removeRule(affectedRules.takeFirst());
}
}
void RuleEngine::init() void RuleEngine::init()
{ {
NymeaSettings settings(NymeaSettings::SettingsRoleRules); NymeaSettings settings(NymeaSettings::SettingsRoleRules);

View File

@ -34,15 +34,24 @@
#include "rule.h" #include "rule.h"
#include "stateevaluator.h" #include "stateevaluator.h"
#include "types/event.h" #include "types/event.h"
#include "types/thingclass.h"
#include "integrations/thingmanager.h"
#include <QObject> #include <QObject>
#include <QList> #include <QList>
#include <QUuid> #include <QUuid>
#include <QSettings> #include <QSettings>
Q_DECLARE_LOGGING_CATEGORY(dcRuleEngine)
Q_DECLARE_LOGGING_CATEGORY(dcRuleEngineDebug)
class ThingManager;
namespace nymeaserver { namespace nymeaserver {
class LogEngine;
class TimeManager;
class RuleEngine : public QObject class RuleEngine : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -72,18 +81,8 @@ public:
}; };
Q_ENUM(RuleError) Q_ENUM(RuleError)
enum RemovePolicy { explicit RuleEngine(ThingManager *thingManager, TimeManager *timeManager, LogEngine *logEngine, QObject *parent = nullptr);
RemovePolicyCascade,
RemovePolicyUpdate
};
Q_ENUM(RemovePolicy)
explicit RuleEngine(QObject *parent = nullptr);
~RuleEngine(); ~RuleEngine();
void init();
QList<Rule> evaluateEvent(const Event &event);
QList<Rule> evaluateTime(const QDateTime &dateTime);
RuleError addRule(const Rule &rule, bool fromEdit = false); RuleError addRule(const Rule &rule, bool fromEdit = false);
RuleError editRule(const Rule &rule); RuleError editRule(const Rule &rule);
@ -109,8 +108,18 @@ signals:
void ruleAdded(const Rule &rule); void ruleAdded(const Rule &rule);
void ruleRemoved(const RuleId &ruleId); void ruleRemoved(const RuleId &ruleId);
void ruleConfigurationChanged(const Rule &rule); void ruleConfigurationChanged(const Rule &rule);
void ruleActiveChanged(const Rule &rule);
private slots:
void init();
void onEventTriggered(const Event &event);
void onDateTimeChanged(const QDateTime &dateTime);
void onThingRemoved(const ThingId &thingId);
private:
QList<Rule> evaluateEvent(const Event &event);
QList<Rule> evaluateTime(const QDateTime &dateTime);
private:
bool containsEvent(const Rule &rule, const Event &event, const ThingClassId &thingClassId); bool containsEvent(const Rule &rule, const Event &event, const ThingClassId &thingClassId);
bool containsState(const StateEvaluator &stateEvaluator, const Event &stateChangeEvent); bool containsState(const StateEvaluator &stateEvaluator, const Event &stateChangeEvent);
@ -125,12 +134,22 @@ private:
void saveRuleActions(NymeaSettings *settings, const QList<RuleAction> &ruleActions); void saveRuleActions(NymeaSettings *settings, const QList<RuleAction> &ruleActions);
QList<RuleAction> loadRuleActions(NymeaSettings *settings); QList<RuleAction> loadRuleActions(NymeaSettings *settings);
void executeRuleActions(const QList<RuleAction> ruleActions);
private: private:
ThingManager *m_thingManager = nullptr;
TimeManager *m_timeManager = nullptr;
LogEngine *m_logEngine = nullptr;
QList<RuleId> m_ruleIds; // Keeping a list of RuleIds to keep sorting order... QList<RuleId> m_ruleIds; // Keeping a list of RuleIds to keep sorting order...
QHash<RuleId, Rule> m_rules; // ...but use a Hash for faster finding QHash<RuleId, Rule> m_rules; // ...but use a Hash for faster finding
QList<RuleId> m_activeRules; QList<RuleId> m_activeRules;
QDateTime m_lastEvaluationTime; QDateTime m_lastEvaluationTime;
QList<RuleId> m_executingRules;
}; };
} }

View File

@ -110,11 +110,11 @@ protected:
virtual IOConnectionResult connectIO(const IOConnection &connection) = 0; virtual IOConnectionResult connectIO(const IOConnection &connection) = 0;
signals: signals:
void loaded();
void pluginConfigChanged(const PluginId &id, const ParamList &config); void pluginConfigChanged(const PluginId &id, const ParamList &config);
void eventTriggered(const Event &event); void eventTriggered(const Event &event);
void thingStateChanged(Thing *thing, const StateTypeId &stateTypeId, const QVariant &value, const QVariant &minValue, const QVariant &maxValue); void thingStateChanged(Thing *thing, const StateTypeId &stateTypeId, const QVariant &value, const QVariant &minValue, const QVariant &maxValue);
void thingRemoved(const ThingId &thingId); void thingRemoved(const ThingId &thingId);
void thingDisappeared(const ThingId &thingId);
void thingAdded(Thing *thing); void thingAdded(Thing *thing);
void thingChanged(Thing *thing); void thingChanged(Thing *thing);
void thingSettingChanged(const ThingId &thingId, const ParamTypeId &settingParamTypeId, const QVariant &value); void thingSettingChanged(const ThingId &thingId, const ParamTypeId &settingParamTypeId, const QVariant &value);

View File

@ -48,8 +48,6 @@ NYMEA_LOGGING_CATEGORY(dcPlatformUpdate, "PlatformUpdate")
NYMEA_LOGGING_CATEGORY(dcPlatformZeroConf, "PlatformZeroConf") NYMEA_LOGGING_CATEGORY(dcPlatformZeroConf, "PlatformZeroConf")
NYMEA_LOGGING_CATEGORY(dcExperiences, "Experiences") NYMEA_LOGGING_CATEGORY(dcExperiences, "Experiences")
NYMEA_LOGGING_CATEGORY(dcTimeManager, "TimeManager") NYMEA_LOGGING_CATEGORY(dcTimeManager, "TimeManager")
NYMEA_LOGGING_CATEGORY(dcRuleEngine, "RuleEngine")
NYMEA_LOGGING_CATEGORY(dcRuleEngineDebug, "RuleEngineDebug")
NYMEA_LOGGING_CATEGORY(dcHardware, "Hardware") NYMEA_LOGGING_CATEGORY(dcHardware, "Hardware")
NYMEA_LOGGING_CATEGORY(dcLogEngine, "LogEngine") NYMEA_LOGGING_CATEGORY(dcLogEngine, "LogEngine")
NYMEA_LOGGING_CATEGORY(dcServerManager, "ServerManager") NYMEA_LOGGING_CATEGORY(dcServerManager, "ServerManager")

View File

@ -56,8 +56,6 @@ Q_DECLARE_LOGGING_CATEGORY(dcPlatformUpdate)
Q_DECLARE_LOGGING_CATEGORY(dcPlatformZeroConf) Q_DECLARE_LOGGING_CATEGORY(dcPlatformZeroConf)
Q_DECLARE_LOGGING_CATEGORY(dcExperiences) Q_DECLARE_LOGGING_CATEGORY(dcExperiences)
Q_DECLARE_LOGGING_CATEGORY(dcTimeManager) Q_DECLARE_LOGGING_CATEGORY(dcTimeManager)
Q_DECLARE_LOGGING_CATEGORY(dcRuleEngine)
Q_DECLARE_LOGGING_CATEGORY(dcRuleEngineDebug)
Q_DECLARE_LOGGING_CATEGORY(dcHardware) Q_DECLARE_LOGGING_CATEGORY(dcHardware)
Q_DECLARE_LOGGING_CATEGORY(dcLogEngine) Q_DECLARE_LOGGING_CATEGORY(dcLogEngine)
Q_DECLARE_LOGGING_CATEGORY(dcServerManager) Q_DECLARE_LOGGING_CATEGORY(dcServerManager)

View File

@ -154,7 +154,6 @@ bool RepeatingOption::isValid() const
// Validate weekdays range // Validate weekdays range
foreach (const uint &weekDay, m_weekDays) { foreach (const uint &weekDay, m_weekDays) {
if (weekDay <= 0 || weekDay > 7) { if (weekDay <= 0 || weekDay > 7) {
qCWarning(dcRuleEngine()) << "Invalid week day value:" << weekDay << ". Value out of range [1,7].";
return false; return false;
} }
} }
@ -162,7 +161,6 @@ bool RepeatingOption::isValid() const
// Validate monthdays range // Validate monthdays range
foreach (const uint &monthDay, m_monthDays) { foreach (const uint &monthDay, m_monthDays) {
if (monthDay <= 0 || monthDay > 31) { if (monthDay <= 0 || monthDay > 31) {
qCWarning(dcRuleEngine()) << "Invalid month day value:" << monthDay << ". Value out of range [1,31].";
return false; return false;
} }
} }

View File

@ -119,16 +119,6 @@ QVariant Event::paramValue(const ParamTypeId &paramTypeId) const
return QVariant(); return QVariant();
} }
bool Event::logged() const
{
return m_logged;
}
void Event::setLogged(bool logged)
{
m_logged = logged;
}
/*! Compare this Event to the Event given by \a other. /*! Compare this Event to the Event given by \a other.
* Events are equal (returns true) if eventTypeId, deviceId and params match. */ * Events are equal (returns true) if eventTypeId, deviceId and params match. */
bool Event::operator ==(const Event &other) const bool Event::operator ==(const Event &other) const

View File

@ -62,15 +62,10 @@ public:
bool operator ==(const Event &other) const; bool operator ==(const Event &other) const;
bool logged() const;
void setLogged(bool logged);
private: private:
EventTypeId m_eventTypeId; EventTypeId m_eventTypeId;
ThingId m_thingId; ThingId m_thingId;
ParamList m_params; ParamList m_params;
bool m_logged = false;
}; };
Q_DECLARE_METATYPE(Event) Q_DECLARE_METATYPE(Event)
QDebug operator<<(QDebug dbg, const Event &event); QDebug operator<<(QDebug dbg, const Event &event);

View File

@ -5,7 +5,7 @@ NYMEA_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"
# define protocol versions # define protocol versions
JSON_PROTOCOL_VERSION_MAJOR=7 JSON_PROTOCOL_VERSION_MAJOR=7
JSON_PROTOCOL_VERSION_MINOR=0 JSON_PROTOCOL_VERSION_MINOR=1
JSON_PROTOCOL_VERSION="$${JSON_PROTOCOL_VERSION_MAJOR}.$${JSON_PROTOCOL_VERSION_MINOR}" JSON_PROTOCOL_VERSION="$${JSON_PROTOCOL_VERSION_MAJOR}.$${JSON_PROTOCOL_VERSION_MINOR}"
LIBNYMEA_API_VERSION_MAJOR=7 LIBNYMEA_API_VERSION_MAJOR=7
LIBNYMEA_API_VERSION_MINOR=4 LIBNYMEA_API_VERSION_MINOR=4

View File

@ -1,4 +1,4 @@
7.0 7.1
{ {
"enums": { "enums": {
"BasicType": [ "BasicType": [
@ -169,10 +169,6 @@
"PermissionScopeConfigureRules", "PermissionScopeConfigureRules",
"PermissionScopeAdmin" "PermissionScopeAdmin"
], ],
"RemovePolicy": [
"RemovePolicyCascade",
"RemovePolicyUpdate"
],
"RepeatingMode": [ "RepeatingMode": [
"RepeatingModeNone", "RepeatingModeNone",
"RepeatingModeHourly", "RepeatingModeHourly",
@ -1121,22 +1117,13 @@
} }
}, },
"Integrations.RemoveThing": { "Integrations.RemoveThing": {
"description": "Remove a thing from the system.", "description": "Remove a thing and all its childs from the system. RemovePolicy is deprecated and has no effect any more.",
"params": { "params": {
"o:removePolicy": "$ref:RemovePolicy", "d:o:removePolicy": "String",
"o:removePolicyList": [
{
"policy": "$ref:RemovePolicy",
"ruleId": "Uuid"
}
],
"thingId": "Uuid" "thingId": "Uuid"
}, },
"permissionScope": "PermissionScopeConfigureThings", "permissionScope": "PermissionScopeConfigureThings",
"returns": { "returns": {
"o:ruleIds": [
"Uuid"
],
"thingError": "$ref:ThingError" "thingError": "$ref:ThingError"
} }
}, },

View File

@ -1910,9 +1910,9 @@ void TestIntegrations::discoverThingsParenting()
QVERIFY(childThing->thingClassId() == childMockThingClassId); QVERIFY(childThing->thingClassId() == childMockThingClassId);
// Now delete the parent and make sure the child will be deleted too // Now delete the parent and make sure the child will be deleted too
QSignalSpy removeSpy(NymeaCore::instance(), &NymeaCore::thingRemoved); QSignalSpy removeSpy(NymeaCore::instance()->thingManager(), &ThingManager::thingRemoved);
QPair<Thing::ThingError, QList<RuleId> > ret = NymeaCore::instance()->removeConfiguredThing(parentThing->id(), QHash<RuleId, RuleEngine::RemovePolicy>()); Thing::ThingError ret = NymeaCore::instance()->thingManager()->removeConfiguredThing(parentThing->id());
QCOMPARE(ret.first, Thing::ThingErrorNoError); QCOMPARE(ret, Thing::ThingErrorNoError);
QCOMPARE(removeSpy.count(), 3); // The parent, the auto-mock and the discovered mock QCOMPARE(removeSpy.count(), 3); // The parent, the auto-mock and the discovered mock
} }
@ -2107,8 +2107,8 @@ void TestIntegrations::triggerEvent()
QVERIFY2(things.count() > 0, "There needs to be at least one configured Mock Device for this test"); QVERIFY2(things.count() > 0, "There needs to be at least one configured Mock Device for this test");
Thing *thing = things.first(); Thing *thing = things.first();
QSignalSpy spy(NymeaCore::instance(), SIGNAL(eventTriggered(const Event&))); QSignalSpy spy(NymeaCore::instance()->thingManager(), &ThingManager::eventTriggered);
QSignalSpy notificationSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);
// Setup connection to mock client // Setup connection to mock client
QNetworkAccessManager nam; QNetworkAccessManager nam;
@ -2148,8 +2148,8 @@ void TestIntegrations::triggerStateChangeSignal()
QVERIFY2(things.count() > 0, "There needs to be at least one configured Mock for this test"); QVERIFY2(things.count() > 0, "There needs to be at least one configured Mock for this test");
Thing *thing = things.first(); Thing *thing = things.first();
QSignalSpy spy(NymeaCore::instance(), SIGNAL(thingStateChanged(Thing *, const StateTypeId &, const QVariant &, const QVariant &, const QVariant &))); QSignalSpy spy(NymeaCore::instance()->thingManager(), &ThingManager::thingStateChanged);
QSignalSpy notificationSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);
// Setup connection to mock client // Setup connection to mock client
QNetworkAccessManager nam; QNetworkAccessManager nam;

View File

@ -115,9 +115,7 @@ private slots:
void testStateBasedAction(); void testStateBasedAction();
void removePolicyUpdate(); void removeThingCleansRule();
void removePolicyCascade();
void removePolicyUpdateRendersUselessRule();
void testRuleActionParams_data(); void testRuleActionParams_data();
void testRuleActionParams(); void testRuleActionParams();
@ -2385,7 +2383,7 @@ void TestRules::testStateBasedAction()
qCDebug(dcTests()) << "Log entries:" << entries; qCDebug(dcTests()) << "Log entries:" << entries;
} }
void TestRules::removePolicyUpdate() void TestRules::removeThingCleansRule()
{ {
// ADD parent // ADD parent
QVariantMap params; QVariantMap params;
@ -2441,16 +2439,10 @@ void TestRules::removePolicyUpdate()
response = injectAndWait("Integrations.RemoveThing", params); response = injectAndWait("Integrations.RemoveThing", params);
verifyThingError(response, Thing::ThingErrorThingIsChild); verifyThingError(response, Thing::ThingErrorThingIsChild);
// Try to remove child
params.clear(); response.clear();
params.insert("thingId", parentId);
response = injectAndWait("Integrations.RemoveThing", params);
verifyThingError(response, Thing::ThingErrorThingInRule);
// Remove policy // Remove policy
params.clear(); response.clear(); params.clear(); response.clear();
params.insert("thingId", parentId); params.insert("thingId", parentId);
params.insert("removePolicy", "RemovePolicyUpdate"); params.insert("removePolicy", "RemovePolicyCascade"); // This is deprecated and doesn't do anything any more, keeping it as clients may pass it too still
response = injectAndWait("Integrations.RemoveThing", params); response = injectAndWait("Integrations.RemoveThing", params);
verifyThingError(response); verifyThingError(response);
@ -2458,182 +2450,6 @@ void TestRules::removePolicyUpdate()
params.clear(); params.clear();
params.insert("ruleId", ruleId); params.insert("ruleId", ruleId);
response = injectAndWait("Rules.GetRuleDetails", params); response = injectAndWait("Rules.GetRuleDetails", params);
verifyRuleError(response);
QVariantMap rule = response.toMap().value("params").toMap().value("rule").toMap();
qDebug() << "Updated rule:" << QJsonDocument::fromVariant(rule).toJson();
QVERIFY(rule.value("eventDescriptors").toList().count() == 1);
// REMOVE rule
QVariantMap removeParams;
removeParams.insert("ruleId", ruleId);
response = injectAndWait("Rules.RemoveRule", removeParams);
verifyRuleError(response);
}
void TestRules::removePolicyCascade()
{
// ADD parent
QVariantMap params;
params.insert("thingClassId", parentMockThingClassId);
params.insert("name", "Parent");
QSignalSpy addedSpy(NymeaCore::instance()->thingManager(), &ThingManager::thingAdded);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
ThingId parentId = ThingId(response.toMap().value("params").toMap().value("thingId").toString());
QVERIFY(!parentId.isNull());
addedSpy.wait();
// find child
response = injectAndWait("Integrations.GetThings");
QVariantList things = response.toMap().value("params").toMap().value("things").toList();
ThingId childId;
foreach (const QVariant thingVariant, things) {
QVariantMap thingMap = thingVariant.toMap();
if (thingMap.value("thingClassId").toUuid() == childMockThingClassId) {
if (thingMap.value("parentId").toUuid() == parentId) {
childId = ThingId(thingMap.value("id").toString());
}
}
}
QVERIFY2(!childId.isNull(), "Could not find child");
// Add rule with child
QVariantList eventDescriptors;
eventDescriptors.append(createEventDescriptor(childId, childMockEvent1EventTypeId));
eventDescriptors.append(createEventDescriptor(parentId, parentMockEvent1EventTypeId));
eventDescriptors.append(createEventDescriptor(m_mockThingId, mockEvent1EventTypeId));
params.clear(); response.clear();
params.insert("name", "RemovePolicy");
params.insert("eventDescriptors", eventDescriptors);
params.insert("actions", QVariantList() << createActionWithParams(m_mockThingId));
response = injectAndWait("Rules.AddRule", params);
verifyRuleError(response);
RuleId ruleId = RuleId(response.toMap().value("params").toMap().value("ruleId").toString());
QVERIFY2(!ruleId.isNull(), "Could not get ruleId");
// Try to remove child
params.clear(); response.clear();
params.insert("thingId", childId);
response = injectAndWait("Integrations.RemoveThing", params);
verifyThingError(response, Thing::ThingErrorThingIsChild);
// Try to remove child by removing parent
params.clear(); response.clear();
params.insert("thingId", parentId);
response = injectAndWait("Integrations.RemoveThing", params);
verifyThingError(response, Thing::ThingErrorThingInRule);
// Remove policy
params.clear(); response.clear();
params.insert("thingId", parentId);
params.insert("removePolicy", "RemovePolicyCascade");
response = injectAndWait("Integrations.RemoveThing", params);
verifyThingError(response);
// get updated rule
params.clear();
params.insert("ruleId", ruleId);
response = injectAndWait("Rules.GetRuleDetails", params);
verifyRuleError(response, RuleEngine::RuleErrorRuleNotFound);
}
void TestRules::removePolicyUpdateRendersUselessRule()
{
// ADD parent
QVariantMap params;
params.insert("thingClassId", parentMockThingClassId);
params.insert("name", "Parent");
QSignalSpy addedSpy(NymeaCore::instance()->thingManager(), &ThingManager::thingAdded);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
ThingId parentId = ThingId(response.toMap().value("params").toMap().value("thingId").toString());
QVERIFY(!parentId.isNull());
addedSpy.wait();
// find child
qCDebug(dcTests()) << "Get things";
response = injectAndWait("Integrations.GetThings");
QVariantList things = response.toMap().value("params").toMap().value("things").toList();
ThingId childId;
foreach (const QVariant thingVariant, things) {
QVariantMap thingMap = thingVariant.toMap();
if (thingMap.value("thingClassId").toUuid() == childMockThingClassId) {
if (thingMap.value("parentId").toUuid() == parentId) {
childId = ThingId(thingMap.value("id").toString());
}
}
}
QVERIFY2(!childId.isNull(), "Could not find child");
// Add rule with child
QVariantList eventDescriptors;
eventDescriptors.append(createEventDescriptor(childId, childMockEvent1EventTypeId));
eventDescriptors.append(createEventDescriptor(parentId, parentMockEvent1EventTypeId));
eventDescriptors.append(createEventDescriptor(m_mockThingId, mockEvent1EventTypeId));
params.clear(); response.clear();
params.insert("name", "RemovePolicy");
params.insert("eventDescriptors", eventDescriptors);
QVariantMap action;
action.insert("thingId", childId);
action.insert("actionTypeId", childMockBoolValueActionTypeId);
QVariantMap ruleActionParam;
ruleActionParam.insert("paramTypeId", childMockBoolValueActionBoolValueParamTypeId);
ruleActionParam.insert("value", true);
action.insert("ruleActionParams", QVariantList() << ruleActionParam);
params.insert("actions", QVariantList() << action);
qCDebug(dcTests()) << "Adding Rule";
response = injectAndWait("Rules.AddRule", params);
verifyRuleError(response);
RuleId ruleId = RuleId(response.toMap().value("params").toMap().value("ruleId").toString());
QVERIFY2(!ruleId.isNull(), "Could not get ruleId");
// Try to remove child
qCDebug(dcTests()) << "Removing thing (expecing failure - thing is child)";
params.clear(); response.clear();
params.insert("thingId", childId);
response = injectAndWait("Integrations.RemoveThing", params);
verifyThingError(response, Thing::ThingErrorThingIsChild);
// Try to remove child by removing parent
qCDebug(dcTests()) << "Removing thing (expeciting failure - thing in use)";
params.clear(); response.clear();
params.insert("thingId", parentId);
response = injectAndWait("Integrations.RemoveThing", params);
verifyThingError(response, Thing::ThingErrorThingInRule);
// Remove policy
qCDebug(dcTests()) << "Removing thing with update policy";
params.clear(); response.clear();
params.insert("thingId", parentId);
params.insert("removePolicy", "RemovePolicyUpdate");
response = injectAndWait("Integrations.RemoveThing", params);
verifyThingError(response);
// get updated rule. It should've been deleted given it ended up with no actions
qCDebug(dcTests()) << "Getting details";
params.clear();
params.insert("ruleId", ruleId);
response = injectAndWait("Rules.GetRuleDetails", params);
verifyRuleError(response, RuleEngine::RuleErrorRuleNotFound); verifyRuleError(response, RuleEngine::RuleErrorRuleNotFound);
} }