Merge PR #620: Refactor NymeaCore class
This commit is contained in:
commit
9355694c24
@ -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, bool initialSetup);
|
||||
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()));
|
||||
|
||||
@ -529,7 +529,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=4
|
||||
|
||||
@ -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