add some housekeeping
This commit is contained in:
parent
2781ae9288
commit
ac0c035566
@ -453,6 +453,7 @@ void GuhCore::init() {
|
||||
connect(m_deviceManager, &DeviceManager::deviceSetupFinished, this, &GuhCore::deviceSetupFinished);
|
||||
connect(m_deviceManager, &DeviceManager::deviceReconfigurationFinished, this, &GuhCore::deviceReconfigurationFinished);
|
||||
connect(m_deviceManager, &DeviceManager::pairingFinished, this, &GuhCore::pairingFinished);
|
||||
connect(m_deviceManager, &DeviceManager::loaded, this, &GuhCore::deviceManagerLoaded);
|
||||
|
||||
connect(m_ruleEngine, &RuleEngine::ruleAdded, this, &GuhCore::ruleAdded);
|
||||
connect(m_ruleEngine, &RuleEngine::ruleRemoved, this, &GuhCore::ruleRemoved);
|
||||
@ -631,4 +632,26 @@ void GuhCore::onDeviceDisappeared(const DeviceId &deviceId)
|
||||
}
|
||||
}
|
||||
|
||||
void GuhCore::deviceManagerLoaded()
|
||||
{
|
||||
// Do some houskeeping...
|
||||
qCDebug(dcApplication()) << "Starting housekeeping...";
|
||||
QDateTime startTime = QDateTime::currentDateTime();
|
||||
foreach (const DeviceId &deviceId, m_logger->devicesInLogs()) {
|
||||
if (!m_deviceManager->findConfiguredDevice(deviceId)) {
|
||||
qCDebug(dcApplication()) << "Cleaning stale device entries from log DB for device id" << deviceId;
|
||||
m_logger->removeDeviceLogs(deviceId);
|
||||
}
|
||||
}
|
||||
foreach (const DeviceId &deviceId, m_ruleEngine->devicesInRules()) {
|
||||
if (!m_deviceManager->findConfiguredDevice(deviceId)) {
|
||||
qCDebug(dcApplication()) << "Cleaning stale rule entries for device id" << deviceId;
|
||||
foreach (const RuleId &ruleId, m_ruleEngine->findRules(deviceId)) {
|
||||
m_ruleEngine->removeDeviceFromRule(ruleId, deviceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
qCDebug(dcApplication()) << "Housekeeping done in" << startTime.msecsTo(QDateTime::currentDateTime()) << "ms.";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -123,6 +123,7 @@ private slots:
|
||||
void onLocaleChanged();
|
||||
void actionExecutionFinished(const ActionId &id, DeviceManager::DeviceError status);
|
||||
void onDeviceDisappeared(const DeviceId &deviceId);
|
||||
void deviceManagerLoaded();
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -349,6 +349,24 @@ void LogEngine::removeRuleLogs(const RuleId &ruleId)
|
||||
}
|
||||
}
|
||||
|
||||
QList<DeviceId> LogEngine::devicesInLogs() const
|
||||
{
|
||||
QString queryString = QString("SELECT deviceId FROM entries WHERE deviceId != \"%1\" GROUP BY deviceId;").arg(QUuid().toString());
|
||||
QSqlQuery result = m_db.exec(queryString);
|
||||
QList<DeviceId> ret;
|
||||
if (result.lastError().type() != QSqlError::NoError) {
|
||||
qCWarning(dcLogEngine()) << "Error fetching device entries from log database:" << m_db.lastError().driverText() << m_db.lastError().databaseText();
|
||||
return ret;
|
||||
}
|
||||
if (!result.first()) {
|
||||
return ret;
|
||||
}
|
||||
do {
|
||||
ret.append(DeviceId::fromUuid(result.value("deviceId").toUuid()));
|
||||
} while (result.next());
|
||||
return ret;
|
||||
}
|
||||
|
||||
void LogEngine::appendLogEntry(const LogEntry &entry)
|
||||
{
|
||||
QString queryString = QString("INSERT INTO entries (timestamp, loggingEventType, loggingLevel, sourceType, typeId, deviceId, value, active, errorCode) values ('%1', '%2', '%3', '%4', '%5', '%6', '%7', '%8', '%9');")
|
||||
|
||||
@ -56,6 +56,7 @@ public:
|
||||
void logRuleExitActionsExecuted(const Rule &rule);
|
||||
void removeDeviceLogs(const DeviceId &deviceId);
|
||||
void removeRuleLogs(const RuleId &ruleId);
|
||||
QList<DeviceId> devicesInLogs() const;
|
||||
|
||||
signals:
|
||||
void logEntryAdded(const LogEntry &logEntry);
|
||||
|
||||
@ -843,7 +843,7 @@ Rule RuleEngine::findRule(const RuleId &ruleId)
|
||||
}
|
||||
|
||||
/*! Returns a list of all \l{Rule}{Rules} loaded in this Engine, which contains a \l{Device} with the given \a deviceId. */
|
||||
QList<RuleId> RuleEngine::findRules(const DeviceId &deviceId)
|
||||
QList<RuleId> RuleEngine::findRules(const DeviceId &deviceId) const
|
||||
{
|
||||
// Find all offending rules
|
||||
QList<RuleId> offendingRules;
|
||||
@ -884,6 +884,35 @@ QList<RuleId> RuleEngine::findRules(const DeviceId &deviceId)
|
||||
return offendingRules;
|
||||
}
|
||||
|
||||
/*! Returns all devices that are somehow contained in a rule */
|
||||
QList<DeviceId> RuleEngine::devicesInRules() const
|
||||
{
|
||||
QList<DeviceId> tmp;
|
||||
foreach (const Rule &rule, m_rules) {
|
||||
foreach (const EventDescriptor &descriptor, rule.eventDescriptors()) {
|
||||
if (!tmp.contains(descriptor.deviceId())) {
|
||||
tmp.append(descriptor.deviceId());
|
||||
}
|
||||
}
|
||||
foreach (const DeviceId &deviceId, rule.stateEvaluator().containedDevices()) {
|
||||
if (!tmp.contains(deviceId)) {
|
||||
tmp.append(deviceId);
|
||||
}
|
||||
}
|
||||
foreach (const RuleAction &action, rule.actions()) {
|
||||
if (!tmp.contains(action.deviceId())) {
|
||||
tmp.append(action.deviceId());
|
||||
}
|
||||
}
|
||||
foreach (const RuleAction &exitAction, rule.exitActions()) {
|
||||
if (!tmp.contains(exitAction.deviceId())) {
|
||||
tmp.append(exitAction.deviceId());
|
||||
}
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/*! Removes a \l{Device} from a \l{Rule} with the given \a id and \a deviceId. */
|
||||
void RuleEngine::removeDeviceFromRule(const RuleId &id, const DeviceId &deviceId)
|
||||
{
|
||||
@ -942,6 +971,7 @@ void RuleEngine::removeDeviceFromRule(const RuleId &id, const DeviceId &deviceId
|
||||
newRule.setEventDescriptors(eventDescriptors);
|
||||
newRule.setStateEvaluator(stateEvalatuator);
|
||||
newRule.setActions(actions);
|
||||
newRule.setExitActions(exitActions);
|
||||
m_rules[id] = newRule;
|
||||
|
||||
// save it
|
||||
|
||||
@ -88,7 +88,8 @@ public:
|
||||
RuleError executeExitActions(const RuleId &ruleId);
|
||||
|
||||
Rule findRule(const RuleId &ruleId);
|
||||
QList<RuleId> findRules(const DeviceId &deviceId);
|
||||
QList<RuleId> findRules(const DeviceId &deviceId) const;
|
||||
QList<DeviceId> devicesInRules() const;
|
||||
|
||||
void removeDeviceFromRule(const RuleId &id, const DeviceId &deviceId);
|
||||
|
||||
|
||||
@ -157,6 +157,16 @@ void StateEvaluator::removeDevice(const DeviceId &deviceId)
|
||||
}
|
||||
}
|
||||
|
||||
QList<DeviceId> StateEvaluator::containedDevices() const
|
||||
{
|
||||
QList<DeviceId> ret;
|
||||
ret.append(m_stateDescriptor.deviceId());
|
||||
foreach (const StateEvaluator &childEvaluator, m_childEvaluators) {
|
||||
ret.append(childEvaluator.containedDevices());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*! This method will be used to save this \l StateEvaluator to the given \a settings.
|
||||
The \a groupName will normally be the corresponding \l Rule. */
|
||||
void StateEvaluator::dumpToSettings(GuhSettings &settings, const QString &groupName) const
|
||||
|
||||
@ -50,6 +50,7 @@ public:
|
||||
bool containsDevice(const DeviceId &deviceId) const;
|
||||
|
||||
void removeDevice(const DeviceId &deviceId);
|
||||
QList<DeviceId> containedDevices() const;
|
||||
|
||||
void dumpToSettings(GuhSettings &settings, const QString &groupName) const;
|
||||
static StateEvaluator loadFromSettings(GuhSettings &settings, const QString &groupPrefix);
|
||||
|
||||
@ -241,7 +241,7 @@ DeviceManager::DeviceManager(const QLocale &locale, QObject *parent) :
|
||||
QMetaObject::invokeMethod(this, "loadConfiguredDevices", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(this, "startMonitoringAutoDevices", Qt::QueuedConnection);
|
||||
// Make sure this is always emitted after plugins and devices are loaded
|
||||
QMetaObject::invokeMethod(this, "loaded", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(this, "onLoaded", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
/*! Destructor of the DeviceManager. Each loaded \l{DevicePlugin} will be deleted. */
|
||||
@ -790,6 +790,9 @@ DeviceManager::DeviceError DeviceManager::removeConfiguredDevice(const DeviceId
|
||||
settings.remove("");
|
||||
settings.endGroup();
|
||||
|
||||
GuhSettings stateCache(GuhSettings::SettingsRoleDeviceStates);
|
||||
stateCache.remove(deviceId.toString());
|
||||
|
||||
emit deviceRemoved(deviceId);
|
||||
|
||||
return DeviceErrorNoError;
|
||||
@ -1412,6 +1415,26 @@ void DeviceManager::onAutoDeviceDisappeared(const DeviceId &deviceId)
|
||||
emit deviceDisappeared(deviceId);
|
||||
}
|
||||
|
||||
void DeviceManager::onLoaded()
|
||||
{
|
||||
emit loaded();
|
||||
|
||||
// schedule some housekeeping...
|
||||
QTimer::singleShot(0, this, &DeviceManager::cleanupDeviceStateCache);
|
||||
}
|
||||
|
||||
void DeviceManager::cleanupDeviceStateCache()
|
||||
{
|
||||
GuhSettings settings(GuhSettings::SettingsRoleDeviceStates);
|
||||
foreach (const QString &entry, settings.childGroups()) {
|
||||
DeviceId deviceId(entry);
|
||||
if (!m_configuredDevices.contains(deviceId)) {
|
||||
qCDebug(dcDeviceManager()) << "Device ID" << deviceId << "not found in configured devices. Cleaning up stale device state cache.";
|
||||
settings.remove(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceManager::slotDeviceStateValueChanged(const QUuid &stateTypeId, const QVariant &value)
|
||||
{
|
||||
Device *device = qobject_cast<Device*>(sender());
|
||||
|
||||
@ -175,6 +175,8 @@ private slots:
|
||||
void slotPairingFinished(const PairingTransactionId &pairingTransactionId, DeviceManager::DeviceSetupStatus status);
|
||||
void onAutoDevicesAppeared(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> &deviceDescriptors);
|
||||
void onAutoDeviceDisappeared(const DeviceId &deviceId);
|
||||
void onLoaded();
|
||||
void cleanupDeviceStateCache();
|
||||
|
||||
// Only connect this to Devices. It will query the sender()
|
||||
void slotDeviceStateValueChanged(const QUuid &stateTypeId, const QVariant &value);
|
||||
|
||||
@ -56,6 +56,8 @@ private slots:
|
||||
|
||||
void deviceLogs();
|
||||
|
||||
void testHouseKeeping();
|
||||
|
||||
// this has to be the last test
|
||||
void removeDevice();
|
||||
};
|
||||
@ -402,6 +404,46 @@ void TestLogging::deviceLogs()
|
||||
|
||||
}
|
||||
|
||||
void TestLogging::testHouseKeeping()
|
||||
{
|
||||
QVariantMap params;
|
||||
params.insert("deviceClassId", mockDeviceClassId);
|
||||
params.insert("name", "TestDeviceToBeRemoved");
|
||||
QVariantList deviceParams;
|
||||
QVariantMap httpParam;
|
||||
httpParam.insert("paramTypeId", httpportParamTypeId);
|
||||
httpParam.insert("value", 6667);
|
||||
deviceParams.append(httpParam);
|
||||
params.insert("deviceParams", deviceParams);
|
||||
QVariant response = injectAndWait("Devices.AddConfiguredDevice", params);
|
||||
DeviceId deviceId = DeviceId::fromUuid(response.toMap().value("params").toMap().value("deviceId").toUuid());
|
||||
QVERIFY2(!deviceId.isNull(), "Something went wrong creating the device for testing.");
|
||||
|
||||
// Trigger something that creates a logging entry
|
||||
QNetworkAccessManager nam;
|
||||
QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*)));
|
||||
QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(6667).arg(mockIntStateId.toString()).arg(4321)));
|
||||
QNetworkReply *reply = nam.get(request);
|
||||
connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater()));
|
||||
spy.wait();
|
||||
|
||||
params.clear();
|
||||
params.insert("deviceIds", QVariantList() << deviceId);
|
||||
response = injectAndWait("Logging.GetLogEntries", params);
|
||||
QVERIFY2(response.toMap().value("params").toMap().value("logEntries").toList().count() > 0, "Couldn't find state change event in log...");
|
||||
|
||||
// Manually delete this device from config
|
||||
GuhSettings settings(GuhSettings::SettingsRoleDevices);
|
||||
settings.beginGroup("DeviceConfig");
|
||||
settings.remove(deviceId.toString());
|
||||
settings.endGroup();
|
||||
|
||||
restartServer();
|
||||
|
||||
response = injectAndWait("Logging.GetLogEntries", params);
|
||||
QVERIFY2(response.toMap().value("params").toMap().value("logEntries").toList().count() == 0, "Device state change event still in log. Should've been cleaned by housekeeping.");
|
||||
}
|
||||
|
||||
void TestLogging::removeDevice()
|
||||
{
|
||||
// enable notifications
|
||||
|
||||
@ -90,6 +90,9 @@ private slots:
|
||||
|
||||
void testRuleActionParams_data();
|
||||
void testRuleActionParams();
|
||||
|
||||
void testHousekeeping_data();
|
||||
void testHousekeeping();
|
||||
};
|
||||
|
||||
void TestRules::cleanupMockHistory() {
|
||||
@ -1814,5 +1817,112 @@ void TestRules::testRuleActionParams()
|
||||
verifyRuleError(response, error);
|
||||
}
|
||||
|
||||
void TestRules::testHousekeeping_data()
|
||||
{
|
||||
QTest::addColumn<bool>("testAction");
|
||||
QTest::addColumn<bool>("testExitAction");
|
||||
QTest::addColumn<bool>("testStateEvaluator");
|
||||
QTest::addColumn<bool>("testEventDescriptor");
|
||||
|
||||
QTest::newRow("action") << true << false << false << false;
|
||||
QTest::newRow("exitAction") << false << true << false << false;
|
||||
QTest::newRow("stateDescriptor") << false << false << true << false;
|
||||
QTest::newRow("eventDescriptor")<< false << false << false << true;
|
||||
}
|
||||
|
||||
void TestRules::testHousekeeping()
|
||||
{
|
||||
QFETCH(bool, testAction);
|
||||
QFETCH(bool, testExitAction);
|
||||
QFETCH(bool, testStateEvaluator);
|
||||
QFETCH(bool, testEventDescriptor);
|
||||
|
||||
QVariantMap params;
|
||||
params.insert("deviceClassId", mockDeviceClassId);
|
||||
params.insert("name", "TestDeviceToBeRemoved");
|
||||
QVariantList deviceParams;
|
||||
QVariantMap httpParam;
|
||||
httpParam.insert("paramTypeId", httpportParamTypeId);
|
||||
httpParam.insert("value", 6667);
|
||||
deviceParams.append(httpParam);
|
||||
params.insert("deviceParams", deviceParams);
|
||||
QVariant response = injectAndWait("Devices.AddConfiguredDevice", params);
|
||||
DeviceId deviceId = DeviceId::fromUuid(response.toMap().value("params").toMap().value("deviceId").toUuid());
|
||||
QVERIFY2(!deviceId.isNull(), "Something went wrong creating the device for testing.");
|
||||
|
||||
// Create a rule with this device
|
||||
params.clear();
|
||||
params.insert("name", "testrule");
|
||||
if (testEventDescriptor) {
|
||||
QVariantList eventDescriptors;
|
||||
QVariantMap eventDescriptor;
|
||||
eventDescriptor.insert("eventTypeId", mockEvent1Id);
|
||||
eventDescriptor.insert("deviceId", testEventDescriptor ? deviceId : m_mockDeviceId);
|
||||
eventDescriptors.append(eventDescriptor);
|
||||
params.insert("eventDescriptors", eventDescriptors);
|
||||
}
|
||||
|
||||
QVariantMap stateEvaluator;
|
||||
QVariantMap stateDescriptor;
|
||||
stateDescriptor.insert("stateTypeId", mockIntStateId);
|
||||
stateDescriptor.insert("operator", "ValueOperatorGreater");
|
||||
stateDescriptor.insert("value", 555);
|
||||
stateDescriptor.insert("deviceId", testStateEvaluator ? deviceId : m_mockDeviceId);
|
||||
stateEvaluator.insert("stateDescriptor", stateDescriptor);
|
||||
params.insert("stateEvaluator", stateEvaluator);
|
||||
|
||||
QVariantList actions;
|
||||
QVariantMap action;
|
||||
action.insert("actionTypeId", mockActionIdNoParams);
|
||||
action.insert("deviceId", testAction ? deviceId : m_mockDeviceId);
|
||||
actions.append(action);
|
||||
params.insert("actions", actions);
|
||||
|
||||
if (!testEventDescriptor) {
|
||||
QVariantList exitActions;
|
||||
QVariantMap exitAction;
|
||||
exitAction.insert("actionTypeId", mockActionIdNoParams);
|
||||
exitAction.insert("deviceId", testExitAction ? deviceId : m_mockDeviceId);
|
||||
exitActions.append(exitAction);
|
||||
params.insert("exitActions", exitActions);
|
||||
}
|
||||
|
||||
response = injectAndWait("Rules.AddRule", params);
|
||||
RuleId ruleId = RuleId::fromUuid(response.toMap().value("params").toMap().value("ruleId").toUuid());
|
||||
|
||||
|
||||
// Verfy that the rule has been created successfully and our device is in there.
|
||||
params.clear();
|
||||
params.insert("ruleId", ruleId);
|
||||
response = injectAndWait("Rules.GetRuleDetails", params);
|
||||
if (testEventDescriptor) {
|
||||
QVERIFY2(response.toMap().value("params").toMap().value("rule").toMap().value("eventDescriptors").toList().first().toMap().value("deviceId").toUuid().toString() == (testEventDescriptor ? deviceId.toString() : m_mockDeviceId.toString()), "Couldn't find device in eventDescriptor of rule");
|
||||
}
|
||||
QVERIFY2(response.toMap().value("params").toMap().value("rule").toMap().value("stateEvaluator").toMap().value("stateDescriptor").toMap().value("deviceId").toUuid().toString() == (testStateEvaluator ? deviceId.toString() : m_mockDeviceId.toString()), "Couldn't find device in stateEvaluator of rule");
|
||||
QVERIFY2(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("deviceId").toUuid().toString() == (testAction ? deviceId.toString() : m_mockDeviceId.toString()), "Couldn't find device in actions of rule");
|
||||
if (!testEventDescriptor) {
|
||||
QVERIFY2(response.toMap().value("params").toMap().value("rule").toMap().value("exitActions").toList().first().toMap().value("deviceId").toUuid().toString() == (testExitAction ? deviceId.toString() : m_mockDeviceId.toString()), "Couldn't find device in exitActions of rule");
|
||||
}
|
||||
|
||||
// Manually delete this device from config
|
||||
GuhSettings settings(GuhSettings::SettingsRoleDevices);
|
||||
settings.beginGroup("DeviceConfig");
|
||||
settings.remove(deviceId.toString());
|
||||
settings.endGroup();
|
||||
|
||||
restartServer();
|
||||
|
||||
// Now make sure the appropriate entries with our device have disappeared
|
||||
response = injectAndWait("Rules.GetRuleDetails", params);
|
||||
if (testEventDescriptor) {
|
||||
QVERIFY2(response.toMap().value("params").toMap().value("rule").toMap().value("eventDescriptors").toList().count() == (testEventDescriptor ? 0: 1), "EventDescriptor still in rule... should've been removed by housekeeping.");
|
||||
}
|
||||
QVERIFY2(response.toMap().value("params").toMap().value("rule").toMap().value("stateEvaluator").toMap().value("stateDescriptor").toMap().isEmpty() == (testStateEvaluator ? true : false), "StateEvaluator still in rule... should've been removed by housekeeping.");
|
||||
QVERIFY2(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().count() == (testAction ? 0 : 1), "Action still in rule... should've been removed by housekeeping.");
|
||||
if (!testEventDescriptor) {
|
||||
QVERIFY2(response.toMap().value("params").toMap().value("rule").toMap().value("exitActions").toList().count() == (testExitAction ? 0: 1), "ExitAction still in rule... should've been removed by housekeeping.");
|
||||
}
|
||||
}
|
||||
|
||||
#include "testrules.moc"
|
||||
QTEST_MAIN(TestRules)
|
||||
|
||||
Reference in New Issue
Block a user