Refactor NymeaCore class
This moves all the things and rules logic away from NymeaCore into their respective modules where it belongs. One major change is the removal of the removePolicy functionality. This was somewhat broken as it was only working for rules but not for all the other modules like scripts, experiences etc. After an attempt to create something that works with all modules it really seemed that this does not make a lot of sence after all, given that updating rules would in most cases leave something very broken behind and removing them was the only sane thing to do. On the other hand, experience plugins may not work well with such a policy eithre as they may require to do their own special thing. So in the end the removePolicy was dropped altogether. Apps should instead figure out themselves what removal of a thing may imply and inform the user about that beforehand.
This commit is contained in:
parent
d43b9dc737
commit
92197cb97c
@ -59,6 +59,7 @@
|
||||
//#include "unistd.h"
|
||||
|
||||
#include "plugintimer.h"
|
||||
#include "logging/logengine.h"
|
||||
|
||||
#include <QPluginLoader>
|
||||
#include <QStaticPlugin>
|
||||
@ -70,9 +71,10 @@
|
||||
#include <QDir>
|
||||
#include <QJsonDocument>
|
||||
|
||||
ThingManagerImplementation::ThingManagerImplementation(HardwareManager *hardwareManager, const QLocale &locale, QObject *parent) :
|
||||
ThingManagerImplementation::ThingManagerImplementation(HardwareManager *hardwareManager, LogEngine *logEngine, const QLocale &locale, QObject *parent) :
|
||||
ThingManager(parent),
|
||||
m_hardwareManager(hardwareManager),
|
||||
m_logEngine(logEngine),
|
||||
m_locale(locale),
|
||||
m_translator(new Translator(this))
|
||||
{
|
||||
@ -853,47 +855,66 @@ ThingSetupInfo* ThingManagerImplementation::addConfiguredThingInternal(const Thi
|
||||
}
|
||||
|
||||
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()
|
||||
// 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.
|
||||
qApp->processEvents();
|
||||
|
||||
Thing *thing = m_configuredThings.take(thingId);
|
||||
if (!thing) {
|
||||
return Thing::ThingErrorThingNotFound;
|
||||
}
|
||||
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);
|
||||
}
|
||||
Things toBeRemoved = findChilds(thing->id());
|
||||
toBeRemoved.append(thing);
|
||||
while (!toBeRemoved.isEmpty()) {
|
||||
Thing *t = m_configuredThings.take(toBeRemoved.takeFirst()->id());
|
||||
|
||||
thing->deleteLater();
|
||||
|
||||
NymeaSettings settings(NymeaSettings::SettingsRoleThings);
|
||||
settings.beginGroup("ThingConfig");
|
||||
settings.beginGroup(thingId.toString());
|
||||
settings.remove("");
|
||||
settings.endGroup();
|
||||
|
||||
QFile::remove(statesCacheFile(thingId));
|
||||
|
||||
foreach (const IOConnectionId &ioConnectionId, m_ioConnections.keys()) {
|
||||
IOConnection ioConnection = m_ioConnections.value(ioConnectionId);
|
||||
if (ioConnection.inputThingId() == thing->id() || ioConnection.outputThingId() == thing->id()) {
|
||||
disconnectIO(ioConnectionId);
|
||||
IntegrationPlugin *plugin = m_integrationPlugins.value(t->pluginId());
|
||||
if (!plugin) {
|
||||
qCWarning(dcThingManager()).nospace() << "Plugin not loaded for thing " << t << ". 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(t->id());
|
||||
emit setupInfo->aborted();
|
||||
} else if (thing->setupStatus() == Thing::ThingSetupStatusComplete) {
|
||||
plugin->thingRemoved(t);
|
||||
}
|
||||
|
||||
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)
|
||||
@ -977,6 +998,9 @@ BrowserActionInfo* ThingManagerImplementation::executeBrowserItem(const BrowserA
|
||||
Thing *thing = m_configuredThings.value(browserAction.thingId());
|
||||
|
||||
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) {
|
||||
info->finish(Thing::ThingErrorThingNotFound);
|
||||
@ -1009,6 +1033,9 @@ BrowserItemActionInfo* ThingManagerImplementation::executeBrowserItemAction(cons
|
||||
Thing *thing = m_configuredThings.value(browserItemAction.thingId());
|
||||
|
||||
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) {
|
||||
info->finish(Thing::ThingErrorThingNotFound);
|
||||
@ -1359,6 +1386,7 @@ ThingActionInfo *ThingManagerImplementation::executeAction(const Action &action)
|
||||
|
||||
ThingActionInfo *info = new ThingActionInfo(thing, finalAction, this, 15000);
|
||||
connect(info, &ThingActionInfo::finished, this, [=](){
|
||||
m_logEngine->logAction(finalAction, info->status());
|
||||
emit actionExecuted(action, info->status());
|
||||
});
|
||||
|
||||
@ -1810,7 +1838,7 @@ void ThingManagerImplementation::onAutoThingDisappeared(const ThingId &thingId)
|
||||
return;
|
||||
}
|
||||
|
||||
emit thingDisappeared(thingId);
|
||||
removeConfiguredThingInternal(thing);
|
||||
}
|
||||
|
||||
void ThingManagerImplementation::onLoaded()
|
||||
@ -1849,7 +1877,7 @@ void ThingManagerImplementation::onEventTriggered(Event event)
|
||||
}
|
||||
// configure logging
|
||||
if (thing->loggedEventTypeIds().contains(event.eventTypeId())) {
|
||||
event.setLogged(true);
|
||||
m_logEngine->logEvent(event);
|
||||
}
|
||||
|
||||
// Forward the event
|
||||
@ -1867,6 +1895,10 @@ void ThingManagerImplementation::slotThingStateValueChanged(const StateTypeId &s
|
||||
storeThingState(thing, stateTypeId);
|
||||
}
|
||||
|
||||
if (thing->loggedStateTypeIds().contains(stateTypeId)) {
|
||||
m_logEngine->logStateChange(thing, stateTypeId, value);
|
||||
}
|
||||
|
||||
emit thingStateChanged(thing, stateTypeId, value, minValue, maxValue);
|
||||
|
||||
syncIOConnection(thing, stateTypeId);
|
||||
|
||||
@ -61,6 +61,11 @@ class HardwareManager;
|
||||
class Translator;
|
||||
class ApiKeysProvidersLoader;
|
||||
|
||||
namespace nymeaserver {
|
||||
class LogEngine;
|
||||
}
|
||||
using namespace nymeaserver;
|
||||
|
||||
class ThingManagerImplementation: public ThingManager
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -68,7 +73,7 @@ class ThingManagerImplementation: public ThingManager
|
||||
friend class IntegrationPlugin;
|
||||
|
||||
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;
|
||||
|
||||
static QStringList pluginSearchDirs();
|
||||
@ -131,9 +136,6 @@ public:
|
||||
ThingClass translateThingClass(const ThingClass &thingClass, const QLocale &locale) override;
|
||||
Vendor translateVendor(const Vendor &vendor, const QLocale &locale) override;
|
||||
|
||||
signals:
|
||||
void loaded();
|
||||
|
||||
private slots:
|
||||
void loadPlugins();
|
||||
void loadPlugin(IntegrationPlugin *pluginIface);
|
||||
@ -157,6 +159,7 @@ private:
|
||||
ParamList buildParams(const ParamTypes &types, const ParamList &first, const ParamList &second = ParamList());
|
||||
void pairThingInternal(ThingPairingInfo *info);
|
||||
ThingSetupInfo *addConfiguredThingInternal(const ThingClassId &thingClassId, const QString &name, const ParamList ¶ms, const ThingId &parentId = ThingId());
|
||||
void removeConfiguredThingInternal(Thing *thing);
|
||||
ThingSetupInfo *reconfigureThingInternal(Thing *thing, const ParamList ¶ms, const QString &name = QString());
|
||||
ThingSetupInfo *setupThing(Thing *thing);
|
||||
void initThing(Thing *thing);
|
||||
@ -176,6 +179,7 @@ private:
|
||||
|
||||
private:
|
||||
HardwareManager *m_hardwareManager;
|
||||
nymeaserver::LogEngine *m_logEngine;
|
||||
|
||||
QLocale m_locale;
|
||||
Translator *m_translator = nullptr;
|
||||
|
||||
@ -29,7 +29,6 @@
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "integrationshandler.h"
|
||||
#include "nymeacore.h"
|
||||
#include "integrations/thingmanager.h"
|
||||
#include "integrations/thing.h"
|
||||
#include "integrations/integrationplugin.h"
|
||||
@ -43,9 +42,11 @@
|
||||
#include "integrations/thingsetupinfo.h"
|
||||
#include "integrations/browseresult.h"
|
||||
#include "integrations/browseritemresult.h"
|
||||
#include "ruleengine/ruleengine.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QCryptographicHash>
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
@ -63,7 +64,6 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
||||
registerEnum<Types::InputType>();
|
||||
registerEnum<Types::IOType>();
|
||||
registerEnum<Types::StateValueFilter>();
|
||||
registerEnum<RuleEngine::RemovePolicy>();
|
||||
registerEnum<BrowserItem::BrowserIcon>();
|
||||
registerEnum<MediaBrowserItem::MediaBrowserIcon>();
|
||||
|
||||
@ -268,17 +268,10 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
||||
registerMethod("SetStateFilter", description, params, returns, Types::PermissionScopeConfigureThings);
|
||||
|
||||
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("o:removePolicy", enumRef<RuleEngine::RemovePolicy>());
|
||||
QVariantMap policy;
|
||||
policy.insert("ruleId", enumValueName(Uuid));
|
||||
policy.insert("policy", enumRef<RuleEngine::RemovePolicy>());
|
||||
QVariantList removePolicyList;
|
||||
removePolicyList.append(policy);
|
||||
params.insert("o:removePolicyList", removePolicyList);
|
||||
params.insert("d:o:removePolicy", enumValueName(String));
|
||||
returns.insert("thingError", enumRef<Thing::ThingError>());
|
||||
returns.insert("o:ruleIds", QVariantList() << enumValueName(Uuid));
|
||||
registerMethod("RemoveThing", description, params, returns, Types::PermissionScopeConfigureThings);
|
||||
|
||||
params.clear(); returns.clear();
|
||||
@ -443,7 +436,7 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
||||
description = "Emitted whenever an Event is triggered.";
|
||||
params.insert("event", objectRef<Event>());
|
||||
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;
|
||||
params.insert("event", pack(event));
|
||||
emit EventTriggered(params);
|
||||
@ -469,14 +462,14 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
|
||||
emit IOConnectionRemoved(params);
|
||||
});
|
||||
|
||||
connect(NymeaCore::instance(), &NymeaCore::pluginConfigChanged, this, &IntegrationsHandler::pluginConfigChanged);
|
||||
connect(NymeaCore::instance(), &NymeaCore::thingStateChanged, this, &IntegrationsHandler::thingStateChanged);
|
||||
connect(NymeaCore::instance(), &NymeaCore::thingRemoved, this, &IntegrationsHandler::thingRemovedNotification);
|
||||
connect(NymeaCore::instance(), &NymeaCore::thingAdded, this, &IntegrationsHandler::thingAddedNotification);
|
||||
connect(NymeaCore::instance(), &NymeaCore::thingChanged, this, &IntegrationsHandler::thingChangedNotification);
|
||||
connect(NymeaCore::instance(), &NymeaCore::thingSettingChanged, this, &IntegrationsHandler::thingSettingChangedNotification);
|
||||
connect(m_thingManager, &ThingManager::pluginConfigChanged, this, &IntegrationsHandler::pluginConfigChanged);
|
||||
connect(m_thingManager, &ThingManager::thingStateChanged, this, &IntegrationsHandler::thingStateChanged);
|
||||
connect(m_thingManager, &ThingManager::thingRemoved, this, &IntegrationsHandler::thingRemovedNotification);
|
||||
connect(m_thingManager, &ThingManager::thingAdded, this, &IntegrationsHandler::thingAddedNotification);
|
||||
connect(m_thingManager, &ThingManager::thingChanged, this, &IntegrationsHandler::thingChangedNotification);
|
||||
connect(m_thingManager, &ThingManager::thingSettingChanged, this, &IntegrationsHandler::thingSettingChangedNotification);
|
||||
|
||||
connect(NymeaCore::instance(), &NymeaCore::initialized, this, [=](){
|
||||
connect(m_thingManager, &ThingManager::loaded, this, [=](){
|
||||
// Generating cache hashes.
|
||||
// NOTE: We need to sort the lists to get a stable result
|
||||
QHash<ThingClassId, ThingClass> thingClassesMap;
|
||||
@ -535,8 +528,8 @@ JsonReply* IntegrationsHandler::GetVendors(const QVariantMap ¶ms, const Json
|
||||
{
|
||||
Q_UNUSED(params)
|
||||
QVariantList vendors;
|
||||
foreach (const Vendor &vendor, NymeaCore::instance()->thingManager()->supportedVendors()) {
|
||||
Vendor translatedVendor = NymeaCore::instance()->thingManager()->translateVendor(vendor, context.locale());
|
||||
foreach (const Vendor &vendor, m_thingManager->supportedVendors()) {
|
||||
Vendor translatedVendor = m_thingManager->translateVendor(vendor, context.locale());
|
||||
vendors.append(pack(translatedVendor));
|
||||
}
|
||||
|
||||
@ -550,7 +543,7 @@ JsonReply* IntegrationsHandler::GetThingClasses(const QVariantMap ¶ms, const
|
||||
QVariantMap returns;
|
||||
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())) {
|
||||
continue;
|
||||
}
|
||||
@ -567,7 +560,7 @@ JsonReply* IntegrationsHandler::GetThingClasses(const QVariantMap ¶ms, const
|
||||
}
|
||||
}
|
||||
|
||||
ThingClass translatedThingClass = NymeaCore::instance()->thingManager()->translateThingClass(thingClass, context.locale());
|
||||
ThingClass translatedThingClass = m_thingManager->translateThingClass(thingClass, context.locale());
|
||||
thingClasses.append(pack(translatedThingClass));
|
||||
}
|
||||
|
||||
@ -586,7 +579,7 @@ JsonReply *IntegrationsHandler::DiscoverThings(const QVariantMap ¶ms, const
|
||||
ParamList discoveryParams = unpack<ParamList>(params.value("discoveryParams"));
|
||||
|
||||
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](){
|
||||
QVariantMap returns;
|
||||
returns.insert("thingError", enumValueName<Thing::ThingError>(info->status()));
|
||||
@ -614,9 +607,9 @@ JsonReply* IntegrationsHandler::GetPlugins(const QVariantMap ¶ms, const Json
|
||||
{
|
||||
Q_UNUSED(params)
|
||||
QVariantList plugins;
|
||||
foreach (IntegrationPlugin* plugin, NymeaCore::instance()->thingManager()->plugins()) {
|
||||
foreach (IntegrationPlugin* plugin, m_thingManager->plugins()) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -629,7 +622,7 @@ JsonReply *IntegrationsHandler::GetPluginConfiguration(const QVariantMap ¶ms
|
||||
{
|
||||
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) {
|
||||
returns.insert("thingError", enumValueName<Thing::ThingError>(Thing::ThingErrorPluginNotFound));
|
||||
return createReply(returns);
|
||||
@ -649,7 +642,7 @@ JsonReply* IntegrationsHandler::SetPluginConfiguration(const QVariantMap ¶ms
|
||||
QVariantMap returns;
|
||||
PluginId pluginId = PluginId(params.value("pluginId").toString());
|
||||
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));
|
||||
return createReply(returns);
|
||||
}
|
||||
@ -674,10 +667,10 @@ JsonReply* IntegrationsHandler::AddThing(const QVariantMap ¶ms, const JsonCo
|
||||
jsonReply->finished();
|
||||
return jsonReply;
|
||||
}
|
||||
info = NymeaCore::instance()->thingManager()->addConfiguredThing(thingClassId, thingParams, thingName);
|
||||
info = m_thingManager->addConfiguredThing(thingClassId, thingParams, thingName);
|
||||
|
||||
} else {
|
||||
info = NymeaCore::instance()->thingManager()->addConfiguredThing(thingDescriptorId, thingParams, thingName);
|
||||
info = m_thingManager->addConfiguredThing(thingDescriptorId, thingParams, thingName);
|
||||
}
|
||||
connect(info, &ThingSetupInfo::finished, jsonReply, [info, jsonReply, locale](){
|
||||
QVariantMap returns;
|
||||
@ -706,24 +699,24 @@ JsonReply *IntegrationsHandler::PairThing(const QVariantMap ¶ms, const JsonC
|
||||
ThingPairingInfo *info;
|
||||
if (params.contains("thingDescriptorId")) {
|
||||
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")) {
|
||||
ThingId thingId = ThingId(params.value("thingId").toString());
|
||||
info = NymeaCore::instance()->thingManager()->pairThing(thingId, thingParams, thingName);
|
||||
info = m_thingManager->pairThing(thingId, thingParams, thingName);
|
||||
} else {
|
||||
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");
|
||||
|
||||
connect(info, &ThingPairingInfo::finished, jsonReply, [jsonReply, info, locale](){
|
||||
connect(info, &ThingPairingInfo::finished, jsonReply, [jsonReply, info, locale, this](){
|
||||
QVariantMap returns;
|
||||
returns.insert("thingError", enumValueName<Thing::ThingError>(info->status()));
|
||||
returns.insert("pairingTransactionId", info->transactionId().toString());
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
@ -751,7 +744,7 @@ JsonReply *IntegrationsHandler::ConfirmPairing(const QVariantMap ¶ms)
|
||||
|
||||
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](){
|
||||
|
||||
QVariantMap returns;
|
||||
@ -774,22 +767,22 @@ JsonReply* IntegrationsHandler::GetThings(const QVariantMap ¶ms, const JsonC
|
||||
QVariantMap returns;
|
||||
QVariantList things;
|
||||
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) {
|
||||
returns.insert("thingError", enumValueName<Thing::ThingError>(Thing::ThingErrorThingNotFound));
|
||||
return createReply(returns);
|
||||
} else {
|
||||
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()) {
|
||||
packedThing["setupDisplayMessage"] = translatedSetupStatus;
|
||||
}
|
||||
things.append(packedThing);
|
||||
}
|
||||
} else {
|
||||
foreach (Thing *thing, NymeaCore::instance()->thingManager()->configuredThings()) {
|
||||
foreach (Thing *thing, m_thingManager->configuredThings()) {
|
||||
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()) {
|
||||
packedThing["setupDisplayMessage"] = translatedSetupStatus;
|
||||
}
|
||||
@ -812,9 +805,9 @@ JsonReply *IntegrationsHandler::ReconfigureThing(const QVariantMap ¶ms, cons
|
||||
|
||||
ThingSetupInfo *info;
|
||||
if (!thingDescriptorId.isNull()) {
|
||||
info = NymeaCore::instance()->thingManager()->reconfigureThing(thingDescriptorId, thingParams);
|
||||
info = m_thingManager->reconfigureThing(thingDescriptorId, thingParams);
|
||||
} else if (!thingId.isNull()){
|
||||
info = NymeaCore::instance()->thingManager()->reconfigureThing(thingId, thingParams);
|
||||
info = m_thingManager->reconfigureThing(thingId, thingParams);
|
||||
} else {
|
||||
qCWarning(dcJsonRpc()) << "Either thingId or thingDescriptorId are required";
|
||||
QVariantMap ret;
|
||||
@ -842,7 +835,7 @@ JsonReply *IntegrationsHandler::EditThing(const QVariantMap ¶ms)
|
||||
|
||||
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));
|
||||
}
|
||||
@ -851,33 +844,8 @@ JsonReply* IntegrationsHandler::RemoveThing(const QVariantMap ¶ms)
|
||||
{
|
||||
QVariantMap returns;
|
||||
ThingId thingId = ThingId(params.value("thingId").toString());
|
||||
|
||||
// global removePolicy has priority
|
||||
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);
|
||||
}
|
||||
|
||||
Thing::ThingError status = m_thingManager->removeConfiguredThing(thingId);
|
||||
returns.insert("thingError", enumValueName<Thing::ThingError>(status));
|
||||
return createReply(returns);
|
||||
}
|
||||
|
||||
@ -885,7 +853,7 @@ JsonReply *IntegrationsHandler::SetThingSettings(const QVariantMap ¶ms)
|
||||
{
|
||||
ThingId thingId = ThingId(params.value("thingId").toString());
|
||||
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));
|
||||
}
|
||||
|
||||
@ -894,7 +862,7 @@ JsonReply *IntegrationsHandler::SetStateLogging(const QVariantMap ¶ms)
|
||||
ThingId thingId = ThingId(params.value("thingId").toString());
|
||||
StateTypeId stateTypeId = StateTypeId(params.value("stateTypeId").toUuid());
|
||||
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));
|
||||
}
|
||||
|
||||
@ -903,7 +871,7 @@ JsonReply *IntegrationsHandler::SetEventLogging(const QVariantMap ¶ms)
|
||||
ThingId thingId = ThingId(params.value("thingId").toString());
|
||||
EventTypeId eventTypeId = EventTypeId(params.value("eventTypeId").toUuid());
|
||||
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));
|
||||
}
|
||||
|
||||
@ -914,14 +882,14 @@ JsonReply *IntegrationsHandler::SetStateFilter(const QVariantMap ¶ms)
|
||||
QString filterString = params.value("filter").toString();
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<Types::StateValueFilter>();
|
||||
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));
|
||||
}
|
||||
|
||||
JsonReply* IntegrationsHandler::GetEventTypes(const QVariantMap ¶ms, const JsonContext &context) const
|
||||
{
|
||||
ThingClass thingClass = NymeaCore::instance()->thingManager()->findThingClass(ThingClassId(params.value("thingClassId").toString()));
|
||||
ThingClass translatedThingClass = NymeaCore::instance()->thingManager()->translateThingClass(thingClass, context.locale());
|
||||
ThingClass thingClass = m_thingManager->findThingClass(ThingClassId(params.value("thingClassId").toString()));
|
||||
ThingClass translatedThingClass = m_thingManager->translateThingClass(thingClass, context.locale());
|
||||
|
||||
QVariantMap returns;
|
||||
returns.insert("eventTypes", pack(translatedThingClass.eventTypes()));
|
||||
@ -930,8 +898,8 @@ JsonReply* IntegrationsHandler::GetEventTypes(const QVariantMap ¶ms, const J
|
||||
|
||||
JsonReply* IntegrationsHandler::GetActionTypes(const QVariantMap ¶ms, const JsonContext &context) const
|
||||
{
|
||||
ThingClass thingClass = NymeaCore::instance()->thingManager()->findThingClass(ThingClassId(params.value("thingClassId").toString()));
|
||||
ThingClass translatedThingClass = NymeaCore::instance()->thingManager()->translateThingClass(thingClass, context.locale());
|
||||
ThingClass thingClass = m_thingManager->findThingClass(ThingClassId(params.value("thingClassId").toString()));
|
||||
ThingClass translatedThingClass = m_thingManager->translateThingClass(thingClass, context.locale());
|
||||
|
||||
QVariantMap returns;
|
||||
returns.insert("actionTypes", pack(translatedThingClass.actionTypes()));
|
||||
@ -940,8 +908,8 @@ JsonReply* IntegrationsHandler::GetActionTypes(const QVariantMap ¶ms, const
|
||||
|
||||
JsonReply* IntegrationsHandler::GetStateTypes(const QVariantMap ¶ms, const JsonContext &context) const
|
||||
{
|
||||
ThingClass thingClass = NymeaCore::instance()->thingManager()->findThingClass(ThingClassId(params.value("thingClassId").toString()));
|
||||
ThingClass translatedThingClass = NymeaCore::instance()->thingManager()->translateThingClass(thingClass, context.locale());
|
||||
ThingClass thingClass = m_thingManager->findThingClass(ThingClassId(params.value("thingClassId").toString()));
|
||||
ThingClass translatedThingClass = m_thingManager->translateThingClass(thingClass, context.locale());
|
||||
|
||||
QVariantMap returns;
|
||||
returns.insert("stateTypes", pack(translatedThingClass.stateTypes()));
|
||||
@ -950,7 +918,7 @@ JsonReply* IntegrationsHandler::GetStateTypes(const QVariantMap ¶ms, const J
|
||||
|
||||
JsonReply* IntegrationsHandler::GetStateValue(const QVariantMap ¶ms) const
|
||||
{
|
||||
Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(ThingId(params.value("thingId").toString()));
|
||||
Thing *thing = m_thingManager->findConfiguredThing(ThingId(params.value("thingId").toString()));
|
||||
if (!thing) {
|
||||
return createReply(statusToReply(Thing::ThingErrorThingNotFound));
|
||||
}
|
||||
@ -966,7 +934,7 @@ JsonReply* IntegrationsHandler::GetStateValue(const QVariantMap ¶ms) const
|
||||
|
||||
JsonReply *IntegrationsHandler::GetStateValues(const QVariantMap ¶ms) const
|
||||
{
|
||||
Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(ThingId(params.value("thingId").toString()));
|
||||
Thing *thing = m_thingManager->findConfiguredThing(ThingId(params.value("thingId").toString()));
|
||||
if (!thing) {
|
||||
return createReply(statusToReply(Thing::ThingErrorThingNotFound));
|
||||
}
|
||||
@ -983,7 +951,7 @@ JsonReply *IntegrationsHandler::BrowseThing(const QVariantMap ¶ms, const Jso
|
||||
|
||||
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](){
|
||||
|
||||
QVariantMap returns = statusToReply(result->status());
|
||||
@ -1010,7 +978,7 @@ JsonReply *IntegrationsHandler::GetBrowserItem(const QVariantMap ¶ms, const
|
||||
|
||||
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](){
|
||||
QVariantMap params = statusToReply(result->status());
|
||||
if (result->status() == Thing::ThingErrorNoError) {
|
||||
@ -1038,7 +1006,7 @@ JsonReply *IntegrationsHandler::ExecuteAction(const QVariantMap ¶ms, const J
|
||||
|
||||
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](){
|
||||
QVariantMap data;
|
||||
data.insert("thingError", enumValueName(info->status()));
|
||||
@ -1060,7 +1028,7 @@ JsonReply *IntegrationsHandler::ExecuteBrowserItem(const QVariantMap ¶ms, co
|
||||
|
||||
JsonReply *jsonReply = createAsyncReply("ExecuteBrowserItem");
|
||||
|
||||
BrowserActionInfo *info = NymeaCore::instance()->executeBrowserItem(action);
|
||||
BrowserActionInfo *info = m_thingManager->executeBrowserItem(action);
|
||||
connect(info, &BrowserActionInfo::finished, jsonReply, [info, jsonReply, context](){
|
||||
QVariantMap data;
|
||||
data.insert("thingError", enumValueName<Thing::ThingError>(info->status()));
|
||||
@ -1084,7 +1052,7 @@ JsonReply *IntegrationsHandler::ExecuteBrowserItemAction(const QVariantMap ¶
|
||||
|
||||
JsonReply *jsonReply = createAsyncReply("ExecuteBrowserItemAction");
|
||||
|
||||
BrowserItemActionInfo *info = NymeaCore::instance()->executeBrowserItemAction(browserItemAction);
|
||||
BrowserItemActionInfo *info = m_thingManager->executeBrowserItemAction(browserItemAction);
|
||||
connect(info, &BrowserItemActionInfo::finished, jsonReply, [info, jsonReply, context](){
|
||||
QVariantMap data;
|
||||
data.insert("thingError", enumValueName<Thing::ThingError>(info->status()));
|
||||
|
||||
@ -525,7 +525,7 @@ void JsonRPCServerImplementation::setup()
|
||||
{
|
||||
registerHandler(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 ConfigurationHandler(this));
|
||||
registerHandler(new NetworkManagerHandler(NymeaCore::instance()->networkManager(), this));
|
||||
|
||||
@ -62,7 +62,6 @@
|
||||
*/
|
||||
|
||||
#include "ruleshandler.h"
|
||||
#include "nymeacore.h"
|
||||
#include "ruleengine/ruleengine.h"
|
||||
#include "loggingcategories.h"
|
||||
|
||||
@ -72,8 +71,9 @@
|
||||
namespace nymeaserver {
|
||||
|
||||
/*! Constructs a new \l{RulesHandler} with the given \a parent. */
|
||||
RulesHandler::RulesHandler(QObject *parent) :
|
||||
JsonHandler(parent)
|
||||
RulesHandler::RulesHandler(RuleEngine *ruleEngine, QObject *parent) :
|
||||
JsonHandler(parent),
|
||||
m_ruleEngine(ruleEngine)
|
||||
{
|
||||
// Enums
|
||||
registerEnum<RuleEngine::RuleError>();
|
||||
@ -218,10 +218,10 @@ RulesHandler::RulesHandler(QObject *parent) :
|
||||
params.insert("rule", objectRef("Rule"));
|
||||
registerNotification("RuleConfigurationChanged", description, params);
|
||||
|
||||
connect(NymeaCore::instance(), &NymeaCore::ruleAdded, this, &RulesHandler::ruleAddedNotification);
|
||||
connect(NymeaCore::instance(), &NymeaCore::ruleRemoved, this, &RulesHandler::ruleRemovedNotification);
|
||||
connect(NymeaCore::instance(), &NymeaCore::ruleActiveChanged, this, &RulesHandler::ruleActiveChangedNotification);
|
||||
connect(NymeaCore::instance(), &NymeaCore::ruleConfigurationChanged, this, &RulesHandler::ruleConfigurationChangedNotification);
|
||||
connect(m_ruleEngine, &RuleEngine::ruleAdded, this, &RulesHandler::ruleAddedNotification);
|
||||
connect(m_ruleEngine, &RuleEngine::ruleRemoved, this, &RulesHandler::ruleRemovedNotification);
|
||||
connect(m_ruleEngine, &RuleEngine::ruleActiveChanged, this, &RulesHandler::ruleActiveChangedNotification);
|
||||
connect(m_ruleEngine, &RuleEngine::ruleConfigurationChanged, this, &RulesHandler::ruleConfigurationChangedNotification);
|
||||
}
|
||||
|
||||
/*! Returns the name of the \l{RulesHandler}. In this case \b Rules.*/
|
||||
@ -235,7 +235,7 @@ JsonReply* RulesHandler::GetRules(const QVariantMap ¶ms)
|
||||
Q_UNUSED(params)
|
||||
|
||||
QVariantList rulesList;
|
||||
foreach (const Rule &rule, NymeaCore::instance()->ruleEngine()->rules()) {
|
||||
foreach (const Rule &rule, m_ruleEngine->rules()) {
|
||||
rulesList.append(packRuleDescription(rule));
|
||||
}
|
||||
|
||||
@ -247,7 +247,7 @@ JsonReply* RulesHandler::GetRules(const QVariantMap ¶ms)
|
||||
JsonReply *RulesHandler::GetRuleDetails(const QVariantMap ¶ms)
|
||||
{
|
||||
RuleId ruleId = RuleId(params.value("ruleId").toString());
|
||||
Rule rule = NymeaCore::instance()->ruleEngine()->findRule(ruleId);
|
||||
Rule rule = m_ruleEngine->findRule(ruleId);
|
||||
if (rule.id().isNull()) {
|
||||
QVariantMap data;
|
||||
data.insert("ruleError", enumValueName<RuleEngine::RuleError>(RuleEngine::RuleErrorRuleNotFound));
|
||||
@ -264,7 +264,7 @@ JsonReply* RulesHandler::AddRule(const QVariantMap ¶ms)
|
||||
Rule rule = unpack<Rule>(params);
|
||||
rule.setId(RuleId::createRuleId());
|
||||
|
||||
RuleEngine::RuleError status = NymeaCore::instance()->ruleEngine()->addRule(rule);
|
||||
RuleEngine::RuleError status = m_ruleEngine->addRule(rule);
|
||||
QVariantMap returns;
|
||||
if (status == RuleEngine::RuleErrorNoError) {
|
||||
returns.insert("ruleId", rule.id().toString());
|
||||
@ -280,10 +280,10 @@ JsonReply *RulesHandler::EditRule(const QVariantMap ¶ms)
|
||||
// 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());
|
||||
|
||||
RuleEngine::RuleError status = NymeaCore::instance()->ruleEngine()->editRule(rule);
|
||||
RuleEngine::RuleError status = m_ruleEngine->editRule(rule);
|
||||
QVariantMap returns;
|
||||
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));
|
||||
return createReply(returns);
|
||||
@ -293,7 +293,7 @@ JsonReply* RulesHandler::RemoveRule(const QVariantMap ¶ms)
|
||||
{
|
||||
QVariantMap returns;
|
||||
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));
|
||||
return createReply(returns);
|
||||
}
|
||||
@ -301,7 +301,7 @@ JsonReply* RulesHandler::RemoveRule(const QVariantMap ¶ms)
|
||||
JsonReply *RulesHandler::FindRules(const QVariantMap ¶ms)
|
||||
{
|
||||
ThingId thingId = ThingId(params.value("thingId").toString());
|
||||
QList<RuleId> rules = NymeaCore::instance()->ruleEngine()->findRules(thingId);
|
||||
QList<RuleId> rules = m_ruleEngine->findRules(thingId);
|
||||
|
||||
QVariantList rulesList;
|
||||
foreach (const RuleId &ruleId, rules) {
|
||||
@ -315,7 +315,7 @@ JsonReply *RulesHandler::FindRules(const QVariantMap ¶ms)
|
||||
|
||||
JsonReply *RulesHandler::EnableRule(const QVariantMap ¶ms)
|
||||
{
|
||||
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;
|
||||
ret.insert("ruleError", enumValueName<RuleEngine::RuleError>(status));
|
||||
return createReply(ret);
|
||||
@ -323,7 +323,7 @@ JsonReply *RulesHandler::EnableRule(const QVariantMap ¶ms)
|
||||
|
||||
JsonReply *RulesHandler::DisableRule(const QVariantMap ¶ms)
|
||||
{
|
||||
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;
|
||||
ret.insert("ruleError", enumValueName<RuleEngine::RuleError>(status));
|
||||
return createReply(ret);
|
||||
@ -333,7 +333,7 @@ JsonReply *RulesHandler::ExecuteActions(const QVariantMap ¶ms)
|
||||
{
|
||||
QVariantMap returns;
|
||||
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));
|
||||
return createReply(returns);
|
||||
}
|
||||
@ -342,7 +342,7 @@ JsonReply *RulesHandler::ExecuteExitActions(const QVariantMap ¶ms)
|
||||
{
|
||||
QVariantMap returns;
|
||||
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));
|
||||
return createReply(returns);
|
||||
}
|
||||
|
||||
@ -37,11 +37,13 @@
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
class RuleEngine;
|
||||
|
||||
class RulesHandler : public JsonHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit RulesHandler(QObject *parent = nullptr);
|
||||
explicit RulesHandler(RuleEngine *ruleEngine, QObject *parent = nullptr);
|
||||
|
||||
QString name() const override;
|
||||
|
||||
@ -73,6 +75,9 @@ private slots:
|
||||
|
||||
private:
|
||||
QVariantMap packRuleDescription(const Rule &rule);
|
||||
|
||||
private:
|
||||
RuleEngine *m_ruleEngine = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -106,14 +106,6 @@ LogEngine::~LogEngine()
|
||||
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)
|
||||
{
|
||||
QList<LogEntry> results;
|
||||
@ -233,10 +225,6 @@ void LogEngine::logSystemEvent(const QDateTime &dateTime, bool active, Logging::
|
||||
|
||||
void LogEngine::logEvent(const Event &event)
|
||||
{
|
||||
if (!event.logged()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantList valueList;
|
||||
foreach (const Param ¶m, event.params()) {
|
||||
valueList << param.value();
|
||||
@ -255,10 +243,6 @@ void LogEngine::logEvent(const Event &event)
|
||||
|
||||
void LogEngine::logStateChange(Thing *thing, const StateTypeId &stateTypeId, const QVariant &value)
|
||||
{
|
||||
if (!thing->loggedStateTypeIds().contains(stateTypeId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LogEntry entry(Logging::LoggingSourceStates);
|
||||
entry.setTypeId(stateTypeId);
|
||||
entry.setThingId(thing->id());
|
||||
|
||||
@ -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();
|
||||
|
||||
void setThingManager(ThingManager *thingManager);
|
||||
|
||||
LogEntriesFetchJob *fetchLogEntries(const LogFilter &filter = LogFilter());
|
||||
ThingsFetchJob *fetchThings();
|
||||
|
||||
@ -76,6 +74,9 @@ public:
|
||||
|
||||
public slots:
|
||||
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 logBrowserItemAction(const BrowserItemAction &browserItemAction, Logging::LoggingLevel level = Logging::LoggingLevelInfo, int errorCode = 0);
|
||||
void logRuleTriggered(const Rule &rule);
|
||||
@ -84,11 +85,6 @@ public slots:
|
||||
void logRuleActionsExecuted(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:
|
||||
void logEntryAdded(const LogEntry &logEntry);
|
||||
void logDatabaseUpdated();
|
||||
@ -122,8 +118,6 @@ private:
|
||||
bool m_initialized = 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
|
||||
int m_maxQueueLength;
|
||||
QHash<QString, QList<DatabaseJob*>> m_flaggedJobs;
|
||||
|
||||
@ -121,15 +121,14 @@ void NymeaCore::init(const QStringList &additionalInterfaces) {
|
||||
qCDebug(dcCore) << "Creating Hardware Manager";
|
||||
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";
|
||||
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";
|
||||
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_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_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);
|
||||
}
|
||||
|
||||
@ -215,322 +200,6 @@ void NymeaCore::destroy()
|
||||
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
|
||||
{
|
||||
return m_configuration;
|
||||
@ -663,117 +332,6 @@ ExperienceManager *NymeaCore::experienceManager() const
|
||||
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
|
||||
{
|
||||
return m_logger;
|
||||
@ -784,65 +342,8 @@ JsonRPCServerImplementation *NymeaCore::jsonRPCServer() const
|
||||
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()
|
||||
{
|
||||
m_ruleEngine->init();
|
||||
// Evaluate rules on current time
|
||||
onDateTimeChanged(m_timeManager->currentDateTime());
|
||||
|
||||
// Tell hardare resources we're done with loading stuff...
|
||||
m_hardwareManager->thingsLoaded();
|
||||
@ -881,7 +382,8 @@ void NymeaCore::thingManagerLoaded()
|
||||
qCDebug(dcCore()) << "Cleaning up stale thing tag" << tag.tagId();
|
||||
m_tagsStorage->removeTag(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -87,15 +87,6 @@ public:
|
||||
void init(const QStringList &additionalInterfaces = QStringList());
|
||||
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);
|
||||
|
||||
NymeaConfiguration *configuration() const;
|
||||
@ -125,19 +116,6 @@ public:
|
||||
signals:
|
||||
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:
|
||||
explicit NymeaCore(QObject *parent = nullptr);
|
||||
static NymeaCore *s_instance;
|
||||
@ -165,14 +143,8 @@ private:
|
||||
SerialPortMonitor *m_serialPortMonitor;
|
||||
ModbusRtuManager *m_modbusRtuManager;
|
||||
|
||||
QList<RuleId> m_executingRules;
|
||||
|
||||
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();
|
||||
|
||||
};
|
||||
|
||||
@ -207,19 +207,16 @@ bool Rule::isConsistent() const
|
||||
{
|
||||
// check if this rules is based on any event and contains exit actions
|
||||
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;
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
qCWarning(dcRuleEngine) << "Rule not consistent. The exitActions will never be executed if the rule contains a timeEvent but no stateEvaluator or calendarItem.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if there are any actions
|
||||
if (actions().isEmpty()) {
|
||||
qCWarning(dcRuleEngine) << "Rule not consistent. A rule without actions has no effect.";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -110,31 +110,58 @@
|
||||
|
||||
|
||||
#include "ruleengine.h"
|
||||
#include "nymeacore.h"
|
||||
#include "loggingcategories.h"
|
||||
#include "time/calendaritem.h"
|
||||
#include "time/repeatingoption.h"
|
||||
#include "time/timeeventitem.h"
|
||||
#include "time/timemanager.h"
|
||||
|
||||
#include "types/eventdescriptor.h"
|
||||
#include "types/paramdescriptor.h"
|
||||
#include "nymeasettings.h"
|
||||
#include "integrations/thingmanager.h"
|
||||
#include "integrations/thing.h"
|
||||
#include "logging/logengine.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QStringList>
|
||||
#include <QStandardPaths>
|
||||
#include <QCoreApplication>
|
||||
|
||||
NYMEA_LOGGING_CATEGORY(dcRuleEngine, "RuleEngine")
|
||||
NYMEA_LOGGING_CATEGORY(dcRuleEngineDebug, "RuleEngineDebug")
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
/*! 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.
|
||||
*/
|
||||
RuleEngine::RuleEngine(QObject *parent) :
|
||||
QObject(parent)
|
||||
RuleEngine::RuleEngine(ThingManager *thingManager, TimeManager *timeManager, LogEngine *logEngine, 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}. */
|
||||
@ -150,7 +177,7 @@ RuleEngine::~RuleEngine()
|
||||
*/
|
||||
QList<Rule> RuleEngine::evaluateEvent(const Event &event)
|
||||
{
|
||||
Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(event.thingId());
|
||||
Thing *thing = m_thingManager->findConfiguredThing(event.thingId());
|
||||
if (!thing) {
|
||||
qCWarning(dcRuleEngine()) << "Invalid event. ThingID does not reference a valid thing";
|
||||
return QList<Rule>();
|
||||
@ -307,7 +334,7 @@ RuleEngine::RuleError RuleEngine::addRule(const Rule &rule, bool fromEdit)
|
||||
}
|
||||
|
||||
if (!rule.isConsistent()) {
|
||||
qCWarning(dcRuleEngine) << "Rule inconsistent.";
|
||||
qCWarning(dcRuleEngine) << "Invalid rule format. (Either missing actions, or exitActions without condition given.)";
|
||||
return RuleErrorInvalidRuleFormat;
|
||||
}
|
||||
|
||||
@ -319,14 +346,14 @@ RuleEngine::RuleError RuleEngine::addRule(const Rule &rule, bool fromEdit)
|
||||
}
|
||||
if (eventDescriptor.type() == EventDescriptor::TypeThing) {
|
||||
// check thingId
|
||||
Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(eventDescriptor.thingId());
|
||||
Thing *thing = m_thingManager->findConfiguredThing(eventDescriptor.thingId());
|
||||
if (!thing) {
|
||||
qCWarning(dcRuleEngine) << "Cannot create rule. No configured thing for eventTypeId" << eventDescriptor.eventTypeId();
|
||||
return RuleErrorThingNotFound;
|
||||
}
|
||||
|
||||
// Check eventTypeId for this deivce
|
||||
ThingClass thingClass = NymeaCore::instance()->thingManager()->findThingClass(thing->thingClassId());
|
||||
ThingClass thingClass = m_thingManager->findThingClass(thing->thingClassId());
|
||||
bool eventTypeFound = false;
|
||||
foreach (const EventType &eventType, thingClass.eventTypes()) {
|
||||
if (eventType.id() == eventDescriptor.eventTypeId()) {
|
||||
@ -345,7 +372,7 @@ RuleEngine::RuleError RuleEngine::addRule(const Rule &rule, bool fromEdit)
|
||||
}
|
||||
} else {
|
||||
// Interface based event
|
||||
Interface iface = NymeaCore::instance()->thingManager()->supportedInterfaces().findByName(eventDescriptor.interface());
|
||||
Interface iface = m_thingManager->supportedInterfaces().findByName(eventDescriptor.interface());
|
||||
if (!iface.isValid()) {
|
||||
qWarning(dcRuleEngine()) << "No such interface:" << eventDescriptor.interface();
|
||||
return RuleErrorInterfaceNotFound;
|
||||
@ -507,6 +534,8 @@ RuleEngine::RuleError RuleEngine::removeRule(const RuleId &ruleId, bool fromEdit
|
||||
settings.remove("");
|
||||
settings.endGroup();
|
||||
|
||||
m_logEngine->removeRuleLogs(ruleId);
|
||||
|
||||
if (!fromEdit)
|
||||
emit ruleRemoved(ruleId);
|
||||
|
||||
@ -536,7 +565,7 @@ RuleEngine::RuleError RuleEngine::enableRule(const RuleId &ruleId)
|
||||
saveRule(rule);
|
||||
emit ruleConfigurationChanged(rule);
|
||||
|
||||
NymeaCore::instance()->logEngine()->logRuleEnabledChanged(rule, true);
|
||||
m_logEngine->logRuleEnabledChanged(rule, true);
|
||||
qCDebug(dcRuleEngine()) << "Rule" << rule.name() << rule.id().toString() << "enabled.";
|
||||
|
||||
return RuleErrorNoError;
|
||||
@ -562,7 +591,7 @@ RuleEngine::RuleError RuleEngine::disableRule(const RuleId &ruleId)
|
||||
saveRule(rule);
|
||||
emit ruleConfigurationChanged(rule);
|
||||
|
||||
NymeaCore::instance()->logEngine()->logRuleEnabledChanged(rule, false);
|
||||
m_logEngine->logRuleEnabledChanged(rule, false);
|
||||
qCDebug(dcRuleEngine()) << "Rule" << rule.name() << rule.id().toString() << "disabled.";
|
||||
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();
|
||||
NymeaCore::instance()->logEngine()->logRuleActionsExecuted(rule);
|
||||
NymeaCore::instance()->executeRuleActions(rule.actions());
|
||||
m_logEngine->logRuleActionsExecuted(rule);
|
||||
executeRuleActions(rule.actions());
|
||||
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();
|
||||
NymeaCore::instance()->logEngine()->logRuleExitActionsExecuted(rule);
|
||||
NymeaCore::instance()->executeRuleActions(rule.exitActions());
|
||||
m_logEngine->logRuleExitActionsExecuted(rule);
|
||||
executeRuleActions(rule.exitActions());
|
||||
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 (eventDescriptor.type() == EventDescriptor::TypeInterface) {
|
||||
ThingClass dc = NymeaCore::instance()->thingManager()->findThingClass(thingClassId);
|
||||
ThingClass dc = m_thingManager->findThingClass(thingClassId);
|
||||
if (!dc.interfaces().contains(eventDescriptor.interface())) {
|
||||
// ThingClass for this event doesn't implement the interface for this eventDescriptor
|
||||
continue;
|
||||
@ -863,7 +892,7 @@ bool RuleEngine::containsEvent(const Rule &rule, const Event &event, const Thing
|
||||
allOK = false;
|
||||
continue;
|
||||
}
|
||||
ThingClass dc = NymeaCore::instance()->thingManager()->findThingClass(thingClassId);
|
||||
ThingClass dc = m_thingManager->findThingClass(thingClassId);
|
||||
EventType et = dc.eventTypes().findById(event.eventTypeId());
|
||||
StateType st = dc.stateTypes().findById(event.eventTypeId());
|
||||
if (et.isValid()) {
|
||||
@ -928,8 +957,8 @@ bool RuleEngine::containsState(const StateEvaluator &stateEvaluator, const Event
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(stateChangeEvent.thingId());
|
||||
ThingClass thingClass = NymeaCore::instance()->thingManager()->findThingClass(thing->thingClassId());
|
||||
Thing *thing = m_thingManager->findConfiguredThing(stateChangeEvent.thingId());
|
||||
ThingClass thingClass = m_thingManager->findThingClass(thing->thingClassId());
|
||||
if (thingClass.interfaces().contains(stateEvaluator.stateDescriptor().interface())) {
|
||||
return true;
|
||||
}
|
||||
@ -954,13 +983,13 @@ RuleEngine::RuleError RuleEngine::checkRuleAction(const RuleAction &ruleAction,
|
||||
|
||||
ActionType actionType;
|
||||
if (ruleAction.type() == RuleAction::TypeThing) {
|
||||
Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(ruleAction.thingId());
|
||||
Thing *thing = m_thingManager->findConfiguredThing(ruleAction.thingId());
|
||||
if (!thing) {
|
||||
qCWarning(dcRuleEngine) << "Cannot create rule. No configured thing with ID" << ruleAction.thingId();
|
||||
return RuleErrorThingNotFound;
|
||||
}
|
||||
|
||||
ThingClass thingClass = NymeaCore::instance()->thingManager()->findThingClass(thing->thingClassId());
|
||||
ThingClass thingClass = m_thingManager->findThingClass(thing->thingClassId());
|
||||
if (!thingClass.hasActionType(ruleAction.actionTypeId())) {
|
||||
qCWarning(dcRuleEngine) << "Cannot create rule. Thing " + thing->name() + " has no action type:" << ruleAction.actionTypeId();
|
||||
return RuleErrorActionTypeNotFound;
|
||||
@ -968,7 +997,7 @@ RuleEngine::RuleError RuleEngine::checkRuleAction(const RuleAction &ruleAction,
|
||||
|
||||
actionType = thingClass.actionTypes().findById(ruleAction.actionTypeId());
|
||||
} 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()) {
|
||||
qCWarning(dcRuleEngine()) << "Cannot create rule. No such interface:" << ruleAction.interface();
|
||||
return RuleError::RuleErrorInterfaceNotFound;
|
||||
@ -979,7 +1008,7 @@ RuleEngine::RuleError RuleEngine::checkRuleAction(const RuleAction &ruleAction,
|
||||
return RuleError::RuleErrorActionTypeNotFound;
|
||||
}
|
||||
} else if (ruleAction.type() == RuleAction::TypeBrowser) {
|
||||
Thing *thing = NymeaCore::instance()->thingManager()->findConfiguredThing(ruleAction.thingId());
|
||||
Thing *thing = m_thingManager->findConfiguredThing(ruleAction.thingId());
|
||||
if (!thing) {
|
||||
qCWarning(dcRuleEngine) << "Cannot create rule. No configured thing with ID" << ruleAction.thingId();
|
||||
return RuleErrorThingNotFound;
|
||||
@ -1057,12 +1086,12 @@ RuleEngine::RuleError RuleEngine::checkRuleActionParam(const RuleActionParam &ru
|
||||
return RuleErrorTypesNotMatching;
|
||||
}
|
||||
} else if (ruleActionParam.isStateBased()) {
|
||||
Thing *d = NymeaCore::instance()->thingManager()->findConfiguredThing(ruleActionParam.stateThingId());
|
||||
Thing *d = m_thingManager->findConfiguredThing(ruleActionParam.stateThingId());
|
||||
if (!d) {
|
||||
qCWarning(dcRuleEngine()) << "Cannot create Rule. ThingId from RuleActionParam" << ruleActionParam.paramTypeId() << "not found in system.";
|
||||
return RuleErrorThingNotFound;
|
||||
}
|
||||
ThingClass stateThingClass = NymeaCore::instance()->thingManager()->findThingClass(d->thingClassId());
|
||||
ThingClass stateThingClass = m_thingManager->findThingClass(d->thingClassId());
|
||||
StateType stateType = stateThingClass.stateTypes().findById(ruleActionParam.stateTypeId());
|
||||
QVariant::Type actionParamType = getActionParamType(actionType.id(), ruleActionParam.paramTypeId());
|
||||
QVariant v(stateType.type());
|
||||
@ -1090,7 +1119,7 @@ RuleEngine::RuleError RuleEngine::checkRuleActionParam(const RuleActionParam &ru
|
||||
|
||||
QVariant::Type RuleEngine::getActionParamType(const ActionTypeId &actionTypeId, const ParamTypeId ¶mTypeId)
|
||||
{
|
||||
foreach (const ThingClass &thingClass, NymeaCore::instance()->thingManager()->supportedThings()) {
|
||||
foreach (const ThingClass &thingClass, m_thingManager->supportedThings()) {
|
||||
foreach (const ActionType &actionType, thingClass.actionTypes()) {
|
||||
if (actionType.id() == actionTypeId) {
|
||||
foreach (const ParamType ¶mType, actionType.paramTypes()) {
|
||||
@ -1107,7 +1136,7 @@ QVariant::Type RuleEngine::getActionParamType(const ActionTypeId &actionTypeId,
|
||||
|
||||
QVariant::Type RuleEngine::getEventParamType(const EventTypeId &eventTypeId, const ParamTypeId ¶mTypeId)
|
||||
{
|
||||
foreach (const ThingClass &thingClass, NymeaCore::instance()->thingManager()->supportedThings()) {
|
||||
foreach (const ThingClass &thingClass, m_thingManager->supportedThings()) {
|
||||
foreach (const EventType &eventType, thingClass.eventTypes()) {
|
||||
if (eventType.id() == eventTypeId) {
|
||||
foreach (const ParamType ¶mType, eventType.paramTypes()) {
|
||||
@ -1371,6 +1400,231 @@ QList<RuleAction> RuleEngine::loadRuleActions(NymeaSettings *settings)
|
||||
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()
|
||||
{
|
||||
NymeaSettings settings(NymeaSettings::SettingsRoleRules);
|
||||
|
||||
@ -34,15 +34,24 @@
|
||||
#include "rule.h"
|
||||
#include "stateevaluator.h"
|
||||
#include "types/event.h"
|
||||
#include "types/thingclass.h"
|
||||
|
||||
#include "integrations/thingmanager.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <QUuid>
|
||||
#include <QSettings>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcRuleEngine)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcRuleEngineDebug)
|
||||
|
||||
class ThingManager;
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
class LogEngine;
|
||||
class TimeManager;
|
||||
|
||||
class RuleEngine : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -72,18 +81,8 @@ public:
|
||||
};
|
||||
Q_ENUM(RuleError)
|
||||
|
||||
enum RemovePolicy {
|
||||
RemovePolicyCascade,
|
||||
RemovePolicyUpdate
|
||||
};
|
||||
Q_ENUM(RemovePolicy)
|
||||
|
||||
explicit RuleEngine(QObject *parent = nullptr);
|
||||
explicit RuleEngine(ThingManager *thingManager, TimeManager *timeManager, LogEngine *logEngine, QObject *parent = nullptr);
|
||||
~RuleEngine();
|
||||
void init();
|
||||
|
||||
QList<Rule> evaluateEvent(const Event &event);
|
||||
QList<Rule> evaluateTime(const QDateTime &dateTime);
|
||||
|
||||
RuleError addRule(const Rule &rule, bool fromEdit = false);
|
||||
RuleError editRule(const Rule &rule);
|
||||
@ -109,8 +108,18 @@ signals:
|
||||
void ruleAdded(const Rule &rule);
|
||||
void ruleRemoved(const RuleId &ruleId);
|
||||
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 containsState(const StateEvaluator &stateEvaluator, const Event &stateChangeEvent);
|
||||
|
||||
@ -125,12 +134,22 @@ private:
|
||||
void saveRuleActions(NymeaSettings *settings, const QList<RuleAction> &ruleActions);
|
||||
QList<RuleAction> loadRuleActions(NymeaSettings *settings);
|
||||
|
||||
void executeRuleActions(const QList<RuleAction> ruleActions);
|
||||
|
||||
|
||||
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...
|
||||
QHash<RuleId, Rule> m_rules; // ...but use a Hash for faster finding
|
||||
QList<RuleId> m_activeRules;
|
||||
|
||||
QDateTime m_lastEvaluationTime;
|
||||
|
||||
QList<RuleId> m_executingRules;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -110,11 +110,11 @@ protected:
|
||||
virtual IOConnectionResult connectIO(const IOConnection &connection) = 0;
|
||||
|
||||
signals:
|
||||
void loaded();
|
||||
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 thingDisappeared(const ThingId &thingId);
|
||||
void thingAdded(Thing *thing);
|
||||
void thingChanged(Thing *thing);
|
||||
void thingSettingChanged(const ThingId &thingId, const ParamTypeId &settingParamTypeId, const QVariant &value);
|
||||
|
||||
@ -48,8 +48,6 @@ NYMEA_LOGGING_CATEGORY(dcPlatformUpdate, "PlatformUpdate")
|
||||
NYMEA_LOGGING_CATEGORY(dcPlatformZeroConf, "PlatformZeroConf")
|
||||
NYMEA_LOGGING_CATEGORY(dcExperiences, "Experiences")
|
||||
NYMEA_LOGGING_CATEGORY(dcTimeManager, "TimeManager")
|
||||
NYMEA_LOGGING_CATEGORY(dcRuleEngine, "RuleEngine")
|
||||
NYMEA_LOGGING_CATEGORY(dcRuleEngineDebug, "RuleEngineDebug")
|
||||
NYMEA_LOGGING_CATEGORY(dcHardware, "Hardware")
|
||||
NYMEA_LOGGING_CATEGORY(dcLogEngine, "LogEngine")
|
||||
NYMEA_LOGGING_CATEGORY(dcServerManager, "ServerManager")
|
||||
|
||||
@ -56,8 +56,6 @@ Q_DECLARE_LOGGING_CATEGORY(dcPlatformUpdate)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcPlatformZeroConf)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcExperiences)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcTimeManager)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcRuleEngine)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcRuleEngineDebug)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcHardware)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcLogEngine)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcServerManager)
|
||||
|
||||
@ -154,7 +154,6 @@ bool RepeatingOption::isValid() const
|
||||
// Validate weekdays range
|
||||
foreach (const uint &weekDay, m_weekDays) {
|
||||
if (weekDay <= 0 || weekDay > 7) {
|
||||
qCWarning(dcRuleEngine()) << "Invalid week day value:" << weekDay << ". Value out of range [1,7].";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -162,7 +161,6 @@ bool RepeatingOption::isValid() const
|
||||
// Validate monthdays range
|
||||
foreach (const uint &monthDay, m_monthDays) {
|
||||
if (monthDay <= 0 || monthDay > 31) {
|
||||
qCWarning(dcRuleEngine()) << "Invalid month day value:" << monthDay << ". Value out of range [1,31].";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,16 +119,6 @@ QVariant Event::paramValue(const ParamTypeId ¶mTypeId) const
|
||||
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.
|
||||
* Events are equal (returns true) if eventTypeId, deviceId and params match. */
|
||||
bool Event::operator ==(const Event &other) const
|
||||
|
||||
@ -62,15 +62,10 @@ public:
|
||||
|
||||
bool operator ==(const Event &other) const;
|
||||
|
||||
bool logged() const;
|
||||
void setLogged(bool logged);
|
||||
|
||||
private:
|
||||
EventTypeId m_eventTypeId;
|
||||
ThingId m_thingId;
|
||||
ParamList m_params;
|
||||
|
||||
bool m_logged = false;
|
||||
};
|
||||
Q_DECLARE_METATYPE(Event)
|
||||
QDebug operator<<(QDebug dbg, const Event &event);
|
||||
|
||||
@ -5,7 +5,7 @@ NYMEA_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"
|
||||
|
||||
# define protocol versions
|
||||
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}"
|
||||
LIBNYMEA_API_VERSION_MAJOR=7
|
||||
LIBNYMEA_API_VERSION_MINOR=3
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
7.0
|
||||
7.1
|
||||
{
|
||||
"enums": {
|
||||
"BasicType": [
|
||||
@ -169,10 +169,6 @@
|
||||
"PermissionScopeConfigureRules",
|
||||
"PermissionScopeAdmin"
|
||||
],
|
||||
"RemovePolicy": [
|
||||
"RemovePolicyCascade",
|
||||
"RemovePolicyUpdate"
|
||||
],
|
||||
"RepeatingMode": [
|
||||
"RepeatingModeNone",
|
||||
"RepeatingModeHourly",
|
||||
@ -1121,22 +1117,13 @@
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"o:removePolicy": "$ref:RemovePolicy",
|
||||
"o:removePolicyList": [
|
||||
{
|
||||
"policy": "$ref:RemovePolicy",
|
||||
"ruleId": "Uuid"
|
||||
}
|
||||
],
|
||||
"d:o:removePolicy": "String",
|
||||
"thingId": "Uuid"
|
||||
},
|
||||
"permissionScope": "PermissionScopeConfigureThings",
|
||||
"returns": {
|
||||
"o:ruleIds": [
|
||||
"Uuid"
|
||||
],
|
||||
"thingError": "$ref:ThingError"
|
||||
}
|
||||
},
|
||||
|
||||
@ -1910,9 +1910,9 @@ void TestIntegrations::discoverThingsParenting()
|
||||
QVERIFY(childThing->thingClassId() == childMockThingClassId);
|
||||
|
||||
// Now delete the parent and make sure the child will be deleted too
|
||||
QSignalSpy removeSpy(NymeaCore::instance(), &NymeaCore::thingRemoved);
|
||||
QPair<Thing::ThingError, QList<RuleId> > ret = NymeaCore::instance()->removeConfiguredThing(parentThing->id(), QHash<RuleId, RuleEngine::RemovePolicy>());
|
||||
QCOMPARE(ret.first, Thing::ThingErrorNoError);
|
||||
QSignalSpy removeSpy(NymeaCore::instance()->thingManager(), &ThingManager::thingRemoved);
|
||||
Thing::ThingError ret = NymeaCore::instance()->thingManager()->removeConfiguredThing(parentThing->id());
|
||||
QCOMPARE(ret, Thing::ThingErrorNoError);
|
||||
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");
|
||||
Thing *thing = things.first();
|
||||
|
||||
QSignalSpy spy(NymeaCore::instance(), SIGNAL(eventTriggered(const Event&)));
|
||||
QSignalSpy notificationSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
||||
QSignalSpy spy(NymeaCore::instance()->thingManager(), &ThingManager::eventTriggered);
|
||||
QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);
|
||||
|
||||
// Setup connection to mock client
|
||||
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");
|
||||
Thing *thing = things.first();
|
||||
|
||||
QSignalSpy spy(NymeaCore::instance(), SIGNAL(thingStateChanged(Thing *, const StateTypeId &, const QVariant &, const QVariant &, const QVariant &)));
|
||||
QSignalSpy notificationSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
|
||||
QSignalSpy spy(NymeaCore::instance()->thingManager(), &ThingManager::thingStateChanged);
|
||||
QSignalSpy notificationSpy(m_mockTcpServer, &MockTcpServer::outgoingData);
|
||||
|
||||
// Setup connection to mock client
|
||||
QNetworkAccessManager nam;
|
||||
|
||||
@ -115,9 +115,7 @@ private slots:
|
||||
|
||||
void testStateBasedAction();
|
||||
|
||||
void removePolicyUpdate();
|
||||
void removePolicyCascade();
|
||||
void removePolicyUpdateRendersUselessRule();
|
||||
void removeThingCleansRule();
|
||||
|
||||
void testRuleActionParams_data();
|
||||
void testRuleActionParams();
|
||||
@ -2385,7 +2383,7 @@ void TestRules::testStateBasedAction()
|
||||
qCDebug(dcTests()) << "Log entries:" << entries;
|
||||
}
|
||||
|
||||
void TestRules::removePolicyUpdate()
|
||||
void TestRules::removeThingCleansRule()
|
||||
{
|
||||
// ADD parent
|
||||
QVariantMap params;
|
||||
@ -2441,16 +2439,10 @@ void TestRules::removePolicyUpdate()
|
||||
response = injectAndWait("Integrations.RemoveThing", params);
|
||||
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
|
||||
params.clear(); response.clear();
|
||||
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);
|
||||
verifyThingError(response);
|
||||
|
||||
@ -2458,182 +2450,6 @@ void TestRules::removePolicyUpdate()
|
||||
params.clear();
|
||||
params.insert("ruleId", ruleId);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user