From c6d9bdd75c6cbc552ebadc4e13af2fb65adf7907 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Tue, 8 Jan 2019 13:33:28 +0100 Subject: [PATCH] Make loading and tearing down of the core more predictable --- libnymea-core/nymeacore.cpp | 170 +++++------ libnymea-core/nymeacore.h | 2 +- libnymea-core/ruleengine.cpp | 553 +++++++++++++++++------------------ libnymea-core/ruleengine.h | 2 +- libnymea/devicemanager.cpp | 1 + server/main.cpp | 2 +- tests/auto/nymeatestbase.cpp | 12 +- 7 files changed, 370 insertions(+), 372 deletions(-) diff --git a/libnymea-core/nymeacore.cpp b/libnymea-core/nymeacore.cpp index ebfce796..76a43ca5 100644 --- a/libnymea-core/nymeacore.cpp +++ b/libnymea-core/nymeacore.cpp @@ -132,6 +132,85 @@ NymeaCore *NymeaCore::instance() return s_instance; } +/*! Constructs NymeaCore with the given \a parent. This is private. + Use \l{NymeaCore::instance()} to access the single instance.*/ +NymeaCore::NymeaCore(QObject *parent) : + QObject(parent) +{ +} + +void NymeaCore::init() { + qCDebug(dcApplication()) << "Initializing NymeaCore"; + + qCDebug(dcApplication()) << "Loading nymea configurations" << NymeaSettings(NymeaSettings::SettingsRoleGlobal).fileName(); + m_configuration = new NymeaConfiguration(this); + + qCDebug(dcApplication()) << "Creating Time Manager"; + m_timeManager = new TimeManager(m_configuration->timeZone(), this); + + qCDebug(dcApplication) << "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); + + qCDebug(dcApplication()) << "Creating User Manager"; + m_userManager = new UserManager(NymeaSettings::settingsPath() + "/user-db.sqlite", this); + + qCDebug(dcApplication) << "Creating Server Manager"; + m_serverManager = new ServerManager(m_configuration, this); + + qCDebug(dcApplication) << "Creating Hardware Manager"; + m_hardwareManager = new HardwareManagerImplementation(m_serverManager->mqttBroker(), this); + + qCDebug(dcApplication) << "Creating Device Manager (locale:" << m_configuration->locale() << ")"; + m_deviceManager = new DeviceManager(m_hardwareManager, m_configuration->locale(), this); + + qCDebug(dcApplication) << "Creating Rule Engine"; + m_ruleEngine = new RuleEngine(this); + + qCDebug(dcApplication()) << "Creating Tags Storage"; + m_tagsStorage = new TagsStorage(m_deviceManager, m_ruleEngine, this); + + qCDebug(dcApplication) << "Creating Network Manager"; + m_networkManager = new NetworkManager(this); + + qCDebug(dcApplication) << "Creating Debug Server Handler"; + m_debugServerHandler = new DebugServerHandler(this); + + qCDebug(dcApplication) << "Creating Cloud Manager"; + m_cloudManager = new CloudManager(m_configuration, m_networkManager, this); + + CloudNotifications *cloudNotifications = m_cloudManager->createNotificationsPlugin(); + m_deviceManager->registerStaticPlugin(cloudNotifications, cloudNotifications->metaData()); + + CloudTransport *cloudTransport = m_cloudManager->createTransportInterface(); + m_serverManager->jsonServer()->registerTransportInterface(cloudTransport, false); + + connect(m_configuration, &NymeaConfiguration::localeChanged, this, &NymeaCore::onLocaleChanged); + connect(m_configuration, &NymeaConfiguration::serverNameChanged, m_serverManager, &ServerManager::setServerName); + + connect(m_deviceManager, &DeviceManager::pluginConfigChanged, this, &NymeaCore::pluginConfigChanged); + connect(m_deviceManager, &DeviceManager::eventTriggered, this, &NymeaCore::gotEvent); + connect(m_deviceManager, &DeviceManager::deviceStateChanged, this, &NymeaCore::deviceStateChanged); + connect(m_deviceManager, &DeviceManager::deviceAdded, this, &NymeaCore::deviceAdded); + connect(m_deviceManager, &DeviceManager::deviceChanged, this, &NymeaCore::deviceChanged); + connect(m_deviceManager, &DeviceManager::deviceRemoved, this, &NymeaCore::deviceRemoved); + connect(m_deviceManager, &DeviceManager::deviceDisappeared, this, &NymeaCore::onDeviceDisappeared); + connect(m_deviceManager, &DeviceManager::actionExecutionFinished, this, &NymeaCore::actionExecutionFinished); + connect(m_deviceManager, &DeviceManager::devicesDiscovered, this, &NymeaCore::devicesDiscovered); + connect(m_deviceManager, &DeviceManager::deviceSetupFinished, this, &NymeaCore::deviceSetupFinished); + connect(m_deviceManager, &DeviceManager::deviceReconfigurationFinished, this, &NymeaCore::deviceReconfigurationFinished); + connect(m_deviceManager, &DeviceManager::pairingFinished, this, &NymeaCore::pairingFinished); + connect(m_deviceManager, &DeviceManager::loaded, this, &NymeaCore::deviceManagerLoaded); + + 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); + connect(m_timeManager, &TimeManager::tick, m_deviceManager, &DeviceManager::timeTick); + + m_logger->logSystemEvent(m_timeManager->currentDateTime(), true); +} + /*! Destructor of the \l{NymeaCore}. */ NymeaCore::~NymeaCore() { @@ -155,6 +234,8 @@ NymeaCore::~NymeaCore() qCDebug(dcApplication) << "Shutting down \"CloudManager\""; delete m_cloudManager; + + qCDebug(dcApplication) << "Done shutting down NymeaCore"; } /*! Destroyes the \l{NymeaCore} instance. */ @@ -506,88 +587,6 @@ TagsStorage *NymeaCore::tagsStorage() const } -/*! Constructs NymeaCore with the given \a parent. This is private. - Use \l{NymeaCore::instance()} to access the single instance.*/ -NymeaCore::NymeaCore(QObject *parent) : - QObject(parent) -{ - staticMetaObject.invokeMethod(this, "init", Qt::QueuedConnection); -} - -void NymeaCore::init() { - qCDebug(dcApplication()) << "Loading nymea configurations" << NymeaSettings(NymeaSettings::SettingsRoleGlobal).fileName(); - m_configuration = new NymeaConfiguration(this); - - qCDebug(dcApplication()) << "Creating Time Manager"; - m_timeManager = new TimeManager(QTimeZone::systemTimeZoneId(), this); - - qCDebug(dcApplication) << "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); - - qCDebug(dcApplication()) << "Creating User Manager"; - m_userManager = new UserManager(NymeaSettings::settingsPath() + "/user-db.sqlite", this); - - qCDebug(dcApplication) << "Creating Server Manager"; - m_serverManager = new ServerManager(m_configuration, this); - - qCDebug(dcApplication) << "Creating Hardware Manager"; - m_hardwareManager = new HardwareManagerImplementation(m_serverManager->mqttBroker(), this); - - qCDebug(dcApplication) << "Creating Device Manager (locale:" << m_configuration->locale() << ")"; - m_deviceManager = new DeviceManager(m_hardwareManager, m_configuration->locale(), this); - - qCDebug(dcApplication) << "Creating Rule Engine"; - m_ruleEngine = new RuleEngine(this); - - qCDebug(dcApplication()) << "Creating Tags Storage"; - m_tagsStorage = new TagsStorage(m_deviceManager, m_ruleEngine, this); - - qCDebug(dcApplication) << "Creating Network Manager"; - m_networkManager = new NetworkManager(this); - - qCDebug(dcApplication) << "Creating Debug Server Handler"; - m_debugServerHandler = new DebugServerHandler(this); - - qCDebug(dcApplication) << "Creating Cloud Manager"; - m_cloudManager = new CloudManager(m_configuration, m_networkManager, this); - - CloudNotifications *cloudNotifications = m_cloudManager->createNotificationsPlugin(); - m_deviceManager->registerStaticPlugin(cloudNotifications, cloudNotifications->metaData()); - - CloudTransport *cloudTransport = m_cloudManager->createTransportInterface(); - m_serverManager->jsonServer()->registerTransportInterface(cloudTransport, false); - - connect(m_configuration, &NymeaConfiguration::localeChanged, this, &NymeaCore::onLocaleChanged); - connect(m_configuration, &NymeaConfiguration::serverNameChanged, m_serverManager, &ServerManager::setServerName); - - connect(m_deviceManager, &DeviceManager::pluginConfigChanged, this, &NymeaCore::pluginConfigChanged); - connect(m_deviceManager, &DeviceManager::eventTriggered, this, &NymeaCore::gotEvent); - connect(m_deviceManager, &DeviceManager::deviceStateChanged, this, &NymeaCore::deviceStateChanged); - connect(m_deviceManager, &DeviceManager::deviceAdded, this, &NymeaCore::deviceAdded); - connect(m_deviceManager, &DeviceManager::deviceChanged, this, &NymeaCore::deviceChanged); - connect(m_deviceManager, &DeviceManager::deviceRemoved, this, &NymeaCore::deviceRemoved); - connect(m_deviceManager, &DeviceManager::deviceDisappeared, this, &NymeaCore::onDeviceDisappeared); - connect(m_deviceManager, &DeviceManager::actionExecutionFinished, this, &NymeaCore::actionExecutionFinished); - connect(m_deviceManager, &DeviceManager::devicesDiscovered, this, &NymeaCore::devicesDiscovered); - connect(m_deviceManager, &DeviceManager::deviceSetupFinished, this, &NymeaCore::deviceSetupFinished); - connect(m_deviceManager, &DeviceManager::deviceReconfigurationFinished, this, &NymeaCore::deviceReconfigurationFinished); - connect(m_deviceManager, &DeviceManager::pairingFinished, this, &NymeaCore::pairingFinished); - connect(m_deviceManager, &DeviceManager::loaded, this, &NymeaCore::deviceManagerLoaded); - - 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); - connect(m_timeManager, &TimeManager::tick, m_deviceManager, &DeviceManager::timeTick); - - m_logger->logSystemEvent(m_timeManager->currentDateTime(), true); - - emit initialized(); - - // Evaluate rules on current time - onDateTimeChanged(m_timeManager->currentDateTime()); -} /*! Connected to the DeviceManager's emitEvent signal. Events received in here will be evaluated by the \l{RuleEngine} and the according \l{RuleAction}{RuleActions} are executed.*/ @@ -767,7 +766,11 @@ void NymeaCore::onDeviceDisappeared(const DeviceId &deviceId) void NymeaCore::deviceManagerLoaded() { - m_ruleEngine->initRuleStates(); + m_ruleEngine->init(); + // Evaluate rules on current time + onDateTimeChanged(m_timeManager->currentDateTime()); + + emit initialized(); // Do some houskeeping... qCDebug(dcApplication()) << "Starting housekeeping..."; @@ -789,6 +792,7 @@ void NymeaCore::deviceManagerLoaded() } qCDebug(dcApplication()) << "Housekeeping done in" << startTime.msecsTo(QDateTime::currentDateTime()) << "ms."; + } } diff --git a/libnymea-core/nymeacore.h b/libnymea-core/nymeacore.h index 5b0caef9..c89b861d 100644 --- a/libnymea-core/nymeacore.h +++ b/libnymea-core/nymeacore.h @@ -61,6 +61,7 @@ public: static NymeaCore* instance(); ~NymeaCore(); + void init(); void destroy(); // Device handling @@ -132,7 +133,6 @@ private: QHash m_pendingActions; private slots: - void init(); void gotEvent(const Event &event); void onDateTimeChanged(const QDateTime &dateTime); void onLocaleChanged(); diff --git a/libnymea-core/ruleengine.cpp b/libnymea-core/ruleengine.cpp index 41a25cb1..a02268de 100644 --- a/libnymea-core/ruleengine.cpp +++ b/libnymea-core/ruleengine.cpp @@ -126,279 +126,6 @@ namespace nymeaserver { RuleEngine::RuleEngine(QObject *parent) : QObject(parent) { - NymeaSettings settings(NymeaSettings::SettingsRoleRules); - qCDebug(dcRuleEngine) << "Loading rules from" << settings.fileName(); - foreach (const QString &idString, settings.childGroups()) { - settings.beginGroup(idString); - - QString name = settings.value("name", idString).toString(); - bool enabled = settings.value("enabled", true).toBool(); - bool executable = settings.value("executable", true).toBool(); - - qCDebug(dcRuleEngine) << "Loading rule" << name << idString; - - // Load timeDescriptor - TimeDescriptor timeDescriptor; - QList calendarItems; - QList timeEventItems; - - settings.beginGroup("timeDescriptor"); - - settings.beginGroup("calendarItems"); - foreach (const QString &childGroup, settings.childGroups()) { - settings.beginGroup(childGroup); - - CalendarItem calendarItem; - calendarItem.setDateTime(QDateTime::fromTime_t(settings.value("dateTime", 0).toUInt())); - calendarItem.setStartTime(QTime::fromString(settings.value("startTime").toString())); - calendarItem.setDuration(settings.value("duration", 0).toUInt()); - - QList weekDays; - QList monthDays; - RepeatingOption::RepeatingMode mode = (RepeatingOption::RepeatingMode)settings.value("mode", 0).toInt(); - - // Load weekDays - int weekDaysCount = settings.beginReadArray("weekDays"); - for (int i = 0; i < weekDaysCount; ++i) { - settings.setArrayIndex(i); - weekDays.append(settings.value("weekDay", 0).toInt()); - } - settings.endArray(); - - // Load weekDays - int monthDaysCount = settings.beginReadArray("monthDays"); - for (int i = 0; i < monthDaysCount; ++i) { - settings.setArrayIndex(i); - monthDays.append(settings.value("monthDay", 0).toInt()); - } - settings.endArray(); - - settings.endGroup(); - - calendarItem.setRepeatingOption(RepeatingOption(mode, weekDays, monthDays)); - calendarItems.append(calendarItem); - } - settings.endGroup(); - - timeDescriptor.setCalendarItems(calendarItems); - - settings.beginGroup("timeEventItems"); - foreach (const QString &childGroup, settings.childGroups()) { - settings.beginGroup(childGroup); - - TimeEventItem timeEventItem; - timeEventItem.setDateTime(settings.value("dateTime", 0).toUInt()); - timeEventItem.setTime(QTime::fromString(settings.value("time").toString())); - - QList weekDays; - QList monthDays; - RepeatingOption::RepeatingMode mode = (RepeatingOption::RepeatingMode)settings.value("mode", 0).toInt(); - - // Load weekDays - int weekDaysCount = settings.beginReadArray("weekDays"); - for (int i = 0; i < weekDaysCount; ++i) { - settings.setArrayIndex(i); - weekDays.append(settings.value("weekDay", 0).toInt()); - } - settings.endArray(); - - // Load weekDays - int monthDaysCount = settings.beginReadArray("monthDays"); - for (int i = 0; i < monthDaysCount; ++i) { - settings.setArrayIndex(i); - monthDays.append(settings.value("monthDay", 0).toInt()); - } - settings.endArray(); - - settings.endGroup(); - - timeEventItem.setRepeatingOption(RepeatingOption(mode, weekDays, monthDays)); - timeEventItems.append(timeEventItem); - } - settings.endGroup(); - - settings.endGroup(); - - timeDescriptor.setTimeEventItems(timeEventItems); - - // Load events - QList eventDescriptorList; - settings.beginGroup("events"); - foreach (QString eventGroupName, settings.childGroups()) { - if (eventGroupName.startsWith("EventDescriptor-")) { - settings.beginGroup(eventGroupName); - EventTypeId eventTypeId(settings.value("eventTypeId").toString()); - DeviceId deviceId(settings.value("deviceId").toString()); - QString interface = settings.value("interface").toString(); - QString interfaceEvent = settings.value("interfaceEvent").toString(); - - QList params; - foreach (QString groupName, settings.childGroups()) { - if (groupName.startsWith("ParamDescriptor-")) { - settings.beginGroup(groupName); - QString strippedGroupName = groupName.remove(QRegExp("^ParamDescriptor-")); - - QVariant value = settings.value("value"); - if (settings.contains("valueType")) { - QVariant::Type valueType = (QVariant::Type)settings.value("valueType").toInt(); - // Note: only warn, and continue with the QVariant guessed type - if (valueType == QVariant::Invalid) { - qCWarning(dcRuleEngine()) << name << idString << "Could not load the value type of the param descriptor" << strippedGroupName << ". The value type will be guessed by QVariant."; - } else if (!value.canConvert(valueType)) { - qCWarning(dcRuleEngine()) << "Error loading rule" << name << idString << ". Could not convert the param descriptor value" << value << "to the stored type" << valueType; - } else { - value.convert(valueType); - } - } - - if (!ParamTypeId(strippedGroupName).isNull()) { - ParamDescriptor paramDescriptor(ParamTypeId(strippedGroupName), value); - paramDescriptor.setOperatorType((Types::ValueOperator)settings.value("operator").toInt()); - params.append(paramDescriptor); - } else { - ParamDescriptor paramDescriptor(strippedGroupName, value); - paramDescriptor.setOperatorType((Types::ValueOperator)settings.value("operator").toInt()); - params.append(paramDescriptor); - } - settings.endGroup(); - } - } - - if (!eventTypeId.isNull()) { - EventDescriptor eventDescriptor(eventTypeId, deviceId, params); - eventDescriptorList.append(eventDescriptor); - } else { - EventDescriptor eventDescriptor(interface, interfaceEvent, params); - eventDescriptorList.append(eventDescriptor); - } - settings.endGroup(); - } - } - settings.endGroup(); - - - // Load stateEvaluator - StateEvaluator stateEvaluator = StateEvaluator::loadFromSettings(settings, "stateEvaluator"); - - // Load actions - QList actions; - settings.beginGroup("ruleActions"); - foreach (const QString &actionNumber, settings.childGroups()) { - settings.beginGroup(actionNumber); - - RuleActionParamList params; - foreach (QString paramTypeIdString, settings.childGroups()) { - if (paramTypeIdString.startsWith("RuleActionParam-")) { - settings.beginGroup(paramTypeIdString); - QString strippedParamTypeIdString = paramTypeIdString.remove(QRegExp("^RuleActionParam-")); - EventTypeId eventTypeId = EventTypeId(settings.value("eventTypeId", EventTypeId()).toString()); - ParamTypeId eventParamTypeId = ParamTypeId(settings.value("eventParamTypeId", ParamTypeId()).toString()); - QVariant value = settings.value("value"); - if (settings.contains("valueType")) { - QVariant::Type valueType = (QVariant::Type)settings.value("valueType").toInt(); - // Note: only warn, and continue with the QVariant guessed type - if (valueType == QVariant::Invalid) { - qCWarning(dcRuleEngine()) << name << idString << "Could not load the value type of the rule action param " << strippedParamTypeIdString << ". The value type will be guessed by QVariant."; - } else if (!value.canConvert(valueType)) { - qCWarning(dcRuleEngine()) << "Error loading rule" << name << idString << ". Could not convert the rule action param value" << value << "to the stored type" << valueType; - } else { - value.convert(valueType); - } - } - - if (!ParamTypeId(strippedParamTypeIdString).isNull()) { - RuleActionParam param(ParamTypeId(strippedParamTypeIdString), - value, - eventTypeId, - eventParamTypeId); - params.append(param); - } else { - RuleActionParam param(strippedParamTypeIdString, - value, - eventTypeId, - eventParamTypeId); - params.append(param); - } - settings.endGroup(); - } - } - - if (settings.contains("actionTypeId") && settings.contains("deviceId")) { - RuleAction action = RuleAction(ActionTypeId(settings.value("actionTypeId").toString()), DeviceId(settings.value("deviceId").toString())); - action.setRuleActionParams(params); - actions.append(action); - } else if (settings.contains("interface") && settings.contains("interfaceAction")){ - RuleAction action = RuleAction(settings.value("interface").toString(), settings.value("interfaceAction").toString()); - action.setRuleActionParams(params); - actions.append(action); - } - - settings.endGroup(); - } - settings.endGroup(); - - // Load exit actions - QList exitActions; - settings.beginGroup("ruleExitActions"); - foreach (const QString &actionNumber, settings.childGroups()) { - settings.beginGroup(actionNumber); - - RuleActionParamList params; - foreach (QString paramTypeIdString, settings.childGroups()) { - if (paramTypeIdString.startsWith("RuleActionParam-")) { - settings.beginGroup(paramTypeIdString); - QString strippedParamTypeIdString = paramTypeIdString.remove(QRegExp("^RuleActionParam-")); - QVariant value = settings.value("value"); - if (settings.contains("valueType")) { - QVariant::Type valueType = (QVariant::Type)settings.value("valueType").toInt(); - // Note: only warn, and continue with the QVariant guessed type - if (valueType == QVariant::Invalid) { - qCWarning(dcRuleEngine()) << name << idString << "Could not load the value type of the rule action param " << strippedParamTypeIdString << ". The value type will be guessed by QVariant."; - } else if (!value.canConvert(valueType)) { - qCWarning(dcRuleEngine()) << "Error loading rule" << name << idString << ". Could not convert the rule action param value" << value << "to the stored type" << valueType; - } else { - value.convert(valueType); - } - } - - if (!ParamTypeId(strippedParamTypeIdString).isNull()) { - RuleActionParam param(ParamTypeId(strippedParamTypeIdString), value); - params.append(param); - } else { - RuleActionParam param(strippedParamTypeIdString, value); - params.append(param); - } - settings.endGroup(); - } - } - - if (settings.contains("actionTypeId") && settings.contains("deviceId")) { - RuleAction action = RuleAction(ActionTypeId(settings.value("actionTypeId").toString()), DeviceId(settings.value("deviceId").toString())); - action.setRuleActionParams(params); - exitActions.append(action); - } else if (settings.contains("interface") && settings.contains("interfaceAction")) { - RuleAction action = RuleAction(settings.value("interface").toString(),settings.value("interfaceAction").toString()); - action.setRuleActionParams(params); - exitActions.append(action); - } - - settings.endGroup(); - } - settings.endGroup(); - - Rule rule; - rule.setId(RuleId(idString)); - rule.setName(name); - rule.setTimeDescriptor(timeDescriptor); - rule.setEventDescriptors(eventDescriptorList); - rule.setStateEvaluator(stateEvaluator); - rule.setActions(actions); - rule.setExitActions(exitActions); - rule.setEnabled(enabled); - rule.setExecutable(executable); - appendRule(rule); - settings.endGroup(); - } } /*! Destructor of the \l{RuleEngine}. */ @@ -466,7 +193,7 @@ QList RuleEngine::evaluateEvent(const Event &event) qCDebug(dcRuleEngine).nospace().noquote() << "Rule " << rule.name() << " (" + rule.id().toString() << ") contains event" << event.eventId() << "and all states match."; rules.append(rule); } else { - qCDebug(dcRuleEngine).nospace().noquote() << "Rule " << rule.name() << " (" + rule.id().toString() << ") contains event" << event.eventId() << "and states are not matching."; + qCDebug(dcRuleEngine).nospace().noquote() << "Rule " << rule.name() << " (" + rule.id().toString() << ") contains event" << event.eventId() << "but state are not matching."; rules.append(rule); } } @@ -1514,11 +1241,283 @@ void RuleEngine::saveRule(const Rule &rule) qCDebug(dcRuleEngineDebug()) << "Saved rule to config:" << rule; } -void RuleEngine::initRuleStates() +void RuleEngine::init() { - foreach (const RuleId &ruleId, m_rules.keys()) { - m_rules[ruleId].setStatesActive(m_rules[ruleId].stateEvaluator().evaluate()); + NymeaSettings settings(NymeaSettings::SettingsRoleRules); + qCDebug(dcRuleEngine) << "Loading rules from" << settings.fileName(); + foreach (const QString &idString, settings.childGroups()) { + settings.beginGroup(idString); + + QString name = settings.value("name", idString).toString(); + bool enabled = settings.value("enabled", true).toBool(); + bool executable = settings.value("executable", true).toBool(); + + qCDebug(dcRuleEngine) << "Loading rule" << name << idString; + + // Load timeDescriptor + TimeDescriptor timeDescriptor; + QList calendarItems; + QList timeEventItems; + + settings.beginGroup("timeDescriptor"); + + settings.beginGroup("calendarItems"); + foreach (const QString &childGroup, settings.childGroups()) { + settings.beginGroup(childGroup); + + CalendarItem calendarItem; + calendarItem.setDateTime(QDateTime::fromTime_t(settings.value("dateTime", 0).toUInt())); + calendarItem.setStartTime(QTime::fromString(settings.value("startTime").toString())); + calendarItem.setDuration(settings.value("duration", 0).toUInt()); + + QList weekDays; + QList monthDays; + RepeatingOption::RepeatingMode mode = (RepeatingOption::RepeatingMode)settings.value("mode", 0).toInt(); + + // Load weekDays + int weekDaysCount = settings.beginReadArray("weekDays"); + for (int i = 0; i < weekDaysCount; ++i) { + settings.setArrayIndex(i); + weekDays.append(settings.value("weekDay", 0).toInt()); + } + settings.endArray(); + + // Load weekDays + int monthDaysCount = settings.beginReadArray("monthDays"); + for (int i = 0; i < monthDaysCount; ++i) { + settings.setArrayIndex(i); + monthDays.append(settings.value("monthDay", 0).toInt()); + } + settings.endArray(); + + settings.endGroup(); + + calendarItem.setRepeatingOption(RepeatingOption(mode, weekDays, monthDays)); + calendarItems.append(calendarItem); + } + settings.endGroup(); + + timeDescriptor.setCalendarItems(calendarItems); + + settings.beginGroup("timeEventItems"); + foreach (const QString &childGroup, settings.childGroups()) { + settings.beginGroup(childGroup); + + TimeEventItem timeEventItem; + timeEventItem.setDateTime(settings.value("dateTime", 0).toUInt()); + timeEventItem.setTime(QTime::fromString(settings.value("time").toString())); + + QList weekDays; + QList monthDays; + RepeatingOption::RepeatingMode mode = (RepeatingOption::RepeatingMode)settings.value("mode", 0).toInt(); + + // Load weekDays + int weekDaysCount = settings.beginReadArray("weekDays"); + for (int i = 0; i < weekDaysCount; ++i) { + settings.setArrayIndex(i); + weekDays.append(settings.value("weekDay", 0).toInt()); + } + settings.endArray(); + + // Load weekDays + int monthDaysCount = settings.beginReadArray("monthDays"); + for (int i = 0; i < monthDaysCount; ++i) { + settings.setArrayIndex(i); + monthDays.append(settings.value("monthDay", 0).toInt()); + } + settings.endArray(); + + settings.endGroup(); + + timeEventItem.setRepeatingOption(RepeatingOption(mode, weekDays, monthDays)); + timeEventItems.append(timeEventItem); + } + settings.endGroup(); + + settings.endGroup(); + + timeDescriptor.setTimeEventItems(timeEventItems); + + // Load events + QList eventDescriptorList; + settings.beginGroup("events"); + foreach (QString eventGroupName, settings.childGroups()) { + if (eventGroupName.startsWith("EventDescriptor-")) { + settings.beginGroup(eventGroupName); + EventTypeId eventTypeId(settings.value("eventTypeId").toString()); + DeviceId deviceId(settings.value("deviceId").toString()); + QString interface = settings.value("interface").toString(); + QString interfaceEvent = settings.value("interfaceEvent").toString(); + + QList params; + foreach (QString groupName, settings.childGroups()) { + if (groupName.startsWith("ParamDescriptor-")) { + settings.beginGroup(groupName); + QString strippedGroupName = groupName.remove(QRegExp("^ParamDescriptor-")); + + QVariant value = settings.value("value"); + if (settings.contains("valueType")) { + QVariant::Type valueType = (QVariant::Type)settings.value("valueType").toInt(); + // Note: only warn, and continue with the QVariant guessed type + if (valueType == QVariant::Invalid) { + qCWarning(dcRuleEngine()) << name << idString << "Could not load the value type of the param descriptor" << strippedGroupName << ". The value type will be guessed by QVariant."; + } else if (!value.canConvert(valueType)) { + qCWarning(dcRuleEngine()) << "Error loading rule" << name << idString << ". Could not convert the param descriptor value" << value << "to the stored type" << valueType; + } else { + value.convert(valueType); + } + } + + if (!ParamTypeId(strippedGroupName).isNull()) { + ParamDescriptor paramDescriptor(ParamTypeId(strippedGroupName), value); + paramDescriptor.setOperatorType((Types::ValueOperator)settings.value("operator").toInt()); + params.append(paramDescriptor); + } else { + ParamDescriptor paramDescriptor(strippedGroupName, value); + paramDescriptor.setOperatorType((Types::ValueOperator)settings.value("operator").toInt()); + params.append(paramDescriptor); + } + settings.endGroup(); + } + } + + if (!eventTypeId.isNull()) { + EventDescriptor eventDescriptor(eventTypeId, deviceId, params); + eventDescriptorList.append(eventDescriptor); + } else { + EventDescriptor eventDescriptor(interface, interfaceEvent, params); + eventDescriptorList.append(eventDescriptor); + } + settings.endGroup(); + } + } + settings.endGroup(); + + + // Load stateEvaluator + StateEvaluator stateEvaluator = StateEvaluator::loadFromSettings(settings, "stateEvaluator"); + + // Load actions + QList actions; + settings.beginGroup("ruleActions"); + foreach (const QString &actionNumber, settings.childGroups()) { + settings.beginGroup(actionNumber); + + RuleActionParamList params; + foreach (QString paramTypeIdString, settings.childGroups()) { + if (paramTypeIdString.startsWith("RuleActionParam-")) { + settings.beginGroup(paramTypeIdString); + QString strippedParamTypeIdString = paramTypeIdString.remove(QRegExp("^RuleActionParam-")); + EventTypeId eventTypeId = EventTypeId(settings.value("eventTypeId", EventTypeId()).toString()); + ParamTypeId eventParamTypeId = ParamTypeId(settings.value("eventParamTypeId", ParamTypeId()).toString()); + QVariant value = settings.value("value"); + if (settings.contains("valueType")) { + QVariant::Type valueType = (QVariant::Type)settings.value("valueType").toInt(); + // Note: only warn, and continue with the QVariant guessed type + if (valueType == QVariant::Invalid) { + qCWarning(dcRuleEngine()) << name << idString << "Could not load the value type of the rule action param " << strippedParamTypeIdString << ". The value type will be guessed by QVariant."; + } else if (!value.canConvert(valueType)) { + qCWarning(dcRuleEngine()) << "Error loading rule" << name << idString << ". Could not convert the rule action param value" << value << "to the stored type" << valueType; + } else { + value.convert(valueType); + } + } + + if (!ParamTypeId(strippedParamTypeIdString).isNull()) { + RuleActionParam param(ParamTypeId(strippedParamTypeIdString), + value, + eventTypeId, + eventParamTypeId); + params.append(param); + } else { + RuleActionParam param(strippedParamTypeIdString, + value, + eventTypeId, + eventParamTypeId); + params.append(param); + } + settings.endGroup(); + } + } + + if (settings.contains("actionTypeId") && settings.contains("deviceId")) { + RuleAction action = RuleAction(ActionTypeId(settings.value("actionTypeId").toString()), DeviceId(settings.value("deviceId").toString())); + action.setRuleActionParams(params); + actions.append(action); + } else if (settings.contains("interface") && settings.contains("interfaceAction")){ + RuleAction action = RuleAction(settings.value("interface").toString(), settings.value("interfaceAction").toString()); + action.setRuleActionParams(params); + actions.append(action); + } + + settings.endGroup(); + } + settings.endGroup(); + + // Load exit actions + QList exitActions; + settings.beginGroup("ruleExitActions"); + foreach (const QString &actionNumber, settings.childGroups()) { + settings.beginGroup(actionNumber); + + RuleActionParamList params; + foreach (QString paramTypeIdString, settings.childGroups()) { + if (paramTypeIdString.startsWith("RuleActionParam-")) { + settings.beginGroup(paramTypeIdString); + QString strippedParamTypeIdString = paramTypeIdString.remove(QRegExp("^RuleActionParam-")); + QVariant value = settings.value("value"); + if (settings.contains("valueType")) { + QVariant::Type valueType = (QVariant::Type)settings.value("valueType").toInt(); + // Note: only warn, and continue with the QVariant guessed type + if (valueType == QVariant::Invalid) { + qCWarning(dcRuleEngine()) << name << idString << "Could not load the value type of the rule action param " << strippedParamTypeIdString << ". The value type will be guessed by QVariant."; + } else if (!value.canConvert(valueType)) { + qCWarning(dcRuleEngine()) << "Error loading rule" << name << idString << ". Could not convert the rule action param value" << value << "to the stored type" << valueType; + } else { + value.convert(valueType); + } + } + + if (!ParamTypeId(strippedParamTypeIdString).isNull()) { + RuleActionParam param(ParamTypeId(strippedParamTypeIdString), value); + params.append(param); + } else { + RuleActionParam param(strippedParamTypeIdString, value); + params.append(param); + } + settings.endGroup(); + } + } + + if (settings.contains("actionTypeId") && settings.contains("deviceId")) { + RuleAction action = RuleAction(ActionTypeId(settings.value("actionTypeId").toString()), DeviceId(settings.value("deviceId").toString())); + action.setRuleActionParams(params); + exitActions.append(action); + } else if (settings.contains("interface") && settings.contains("interfaceAction")) { + RuleAction action = RuleAction(settings.value("interface").toString(),settings.value("interfaceAction").toString()); + action.setRuleActionParams(params); + exitActions.append(action); + } + + settings.endGroup(); + } + settings.endGroup(); + + Rule rule; + rule.setId(RuleId(idString)); + rule.setName(name); + rule.setTimeDescriptor(timeDescriptor); + rule.setEventDescriptors(eventDescriptorList); + rule.setStateEvaluator(stateEvaluator); + rule.setActions(actions); + rule.setExitActions(exitActions); + rule.setEnabled(enabled); + rule.setExecutable(executable); + rule.setStatesActive(rule.stateEvaluator().evaluate()); + appendRule(rule); + settings.endGroup(); } + } } diff --git a/libnymea-core/ruleengine.h b/libnymea-core/ruleengine.h index d414d4ae..a5aa8219 100644 --- a/libnymea-core/ruleengine.h +++ b/libnymea-core/ruleengine.h @@ -70,7 +70,7 @@ public: explicit RuleEngine(QObject *parent = nullptr); ~RuleEngine(); - void initRuleStates(); + void init(); QList evaluateEvent(const Event &event); QList evaluateTime(const QDateTime &dateTime); diff --git a/libnymea/devicemanager.cpp b/libnymea/devicemanager.cpp index 65576005..77396492 100644 --- a/libnymea/devicemanager.cpp +++ b/libnymea/devicemanager.cpp @@ -1455,6 +1455,7 @@ void DeviceManager::onAutoDeviceDisappeared(const DeviceId &deviceId) void DeviceManager::onLoaded() { + qCWarning(dcDeviceManager()) << "Done loading plugins and devices."; emit loaded(); // schedule some housekeeping... diff --git a/server/main.cpp b/server/main.cpp index 6cf2d25e..97a50aee 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -270,7 +270,7 @@ int main(int argc, char *argv[]) } // create core instance - NymeaCore::instance(); + NymeaCore::instance()->init(); int ret = application.exec(); if (s_logFile.isOpen()) { s_logFile.close(); diff --git a/tests/auto/nymeatestbase.cpp b/tests/auto/nymeatestbase.cpp index f864a487..8064b733 100644 --- a/tests/auto/nymeatestbase.cpp +++ b/tests/auto/nymeatestbase.cpp @@ -122,16 +122,11 @@ void NymeaTestBase::initTestCase() QLoggingCategory::setFilterRules("*.debug=false\nTests.debug=true"); // Start the server - NymeaCore::instance(); + NymeaCore::instance()->init(); // Wait unitl the server is initialized QSignalSpy coreInitializedSpy(NymeaCore::instance(), SIGNAL(initialized())); - coreInitializedSpy.wait(); - - // Wait for the DeviceManager to signal that it has loaded plugins and everything - QSignalSpy deviceManagerSpy(NymeaCore::instance()->deviceManager(), SIGNAL(loaded())); - QVERIFY(deviceManagerSpy.isValid()); - QVERIFY(deviceManagerSpy.wait()); + QVERIFY(coreInitializedSpy.wait()); // Yes, we're intentionally mixing upper/lower case email here... username should not be case sensitive NymeaCore::instance()->userManager()->removeUser("dummy@guh.io"); @@ -442,10 +437,9 @@ void NymeaTestBase::restartServer() { // Destroy and recreate the core instance... NymeaCore::instance()->destroy(); + NymeaCore::instance()->init(); QSignalSpy coreSpy(NymeaCore::instance(), SIGNAL(initialized())); coreSpy.wait(); - QSignalSpy spy(NymeaCore::instance()->deviceManager(), SIGNAL(loaded())); - spy.wait(); m_mockTcpServer = MockTcpServer::servers().first(); m_mockTcpServer->clientConnected(m_clientId); }