implement a logging framework

This commit is contained in:
Michael Zanetti 2014-12-21 16:50:00 +01:00 committed by Michael Zanetti
parent e605a41c37
commit 240c5e36d6
39 changed files with 1254 additions and 89 deletions

View File

@ -903,7 +903,7 @@ void DeviceManager::slotDeviceStateValueChanged(const QUuid &stateTypeId, const
emit deviceStateChanged(device, stateTypeId, value);
Param valueParam("value", value);
Event event(EventTypeId(stateTypeId.toString()), device->id(), ParamList() << valueParam);
Event event(EventTypeId(stateTypeId.toString()), device->id(), ParamList() << valueParam, true);
emit eventTriggered(event);
}

View File

@ -41,6 +41,14 @@ Action::Action(const ActionTypeId &actionTypeId, const DeviceId &deviceId) :
{
}
Action::Action(const Action &other):
m_id(other.id()),
m_actionTypeId(other.actionTypeId()),
m_deviceId(other.deviceId()),
m_params(other.params())
{
}
/*! Returns the actionId for this Action. */
ActionId Action::id() const
{
@ -88,3 +96,10 @@ Param Action::param(const QString &paramName) const
}
return Param(QString());
}
void Action::operator =(const Action &other)
{
m_id = other.id();
m_actionTypeId = other.actionTypeId();
m_params = other.params();
}

View File

@ -27,7 +27,8 @@
class Action
{
public:
explicit Action(const ActionTypeId &actionTypeId, const DeviceId &deviceId);
explicit Action(const ActionTypeId &actionTypeId = ActionTypeId(), const DeviceId &deviceId = DeviceId());
Action(const Action &other);
ActionId id() const;
@ -40,6 +41,7 @@ public:
void setParams(const ParamList &params);
Param param(const QString &paramName) const;
void operator=(const Action &other);
private:
ActionId m_id;
ActionTypeId m_actionTypeId;

View File

@ -42,11 +42,12 @@ Event::Event():
/*! Constructs an Event reflecting the \l{Event} given by \a eventTypeId, associated with
* the \l{Device} given by \a deviceId and the parameters given by \a params. The parameters must
* match the description in the reflecting \l{Event}. */
Event::Event(const EventTypeId &eventTypeId, const DeviceId &deviceId, const ParamList &params):
Event::Event(const EventTypeId &eventTypeId, const DeviceId &deviceId, const ParamList &params, bool isStateChangeEvent):
m_id(EventId::createEventId()),
m_eventTypeId(eventTypeId),
m_deviceId(deviceId),
m_params(params)
m_params(params),
m_isStateChangeEvent(isStateChangeEvent)
{
}
@ -104,6 +105,12 @@ Param Event::param(const QString &paramName) const
return Param(QString());
}
/*! Returns true if this event is autogenerated by a state change. */
bool Event::isStateChangeEvent() const
{
return m_isStateChangeEvent;
}
/*! 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

View File

@ -30,7 +30,7 @@ class Event
{
public:
Event();
Event(const EventTypeId &eventTypeId, const DeviceId &deviceId, const ParamList &params = ParamList());
Event(const EventTypeId &eventTypeId, const DeviceId &deviceId, const ParamList &params = ParamList(), bool isStateChangeEvent = false);
EventId eventId() const;
@ -46,11 +46,15 @@ public:
bool operator ==(const Event &other) const;
bool isStateChangeEvent() const;
private:
EventId m_id;
EventTypeId m_eventTypeId;
DeviceId m_deviceId;
ParamList m_params;
bool m_isStateChangeEvent;
};
Q_DECLARE_METATYPE(Event)
QDebug operator<<(QDebug dbg, const Event &event);

View File

@ -277,7 +277,6 @@ void DevicePluginElro::radioData(const QList<int> &rawData)
DeviceClass deviceClass = supportedDevices().first();
foreach (const EventType &eventType, deviceClass.eventTypes()) {
if (eventType.name() == button) {
qDebug() << "got event: " << pluginName() << group << "power = " << power;
Event event = Event(eventType.id(), device->id(), params);
emit emitEvent(event);
return;

View File

@ -60,6 +60,7 @@
#include "guhcore.h"
#include "jsonrpcserver.h"
#include "ruleengine.h"
#include "logging/logengine.h"
#include "devicemanager.h"
#include "plugin/device.h"
@ -80,6 +81,7 @@ GuhCore *GuhCore::instance()
/*! Destructor of the \l{GuhCore}. */
GuhCore::~GuhCore()
{
m_logger->logSystemEvent(false);
qDebug() << "Shutting down. Bye.";
}
@ -182,7 +184,15 @@ DeviceManager::DeviceError GuhCore::confirmPairing(const PairingTransactionId &p
* \sa DeviceManager::executeAction(), */
DeviceManager::DeviceError GuhCore::executeAction(const Action &action)
{
return m_deviceManager->executeAction(action);
DeviceManager::DeviceError ret = m_deviceManager->executeAction(action);
if (ret == DeviceManager::DeviceErrorNoError) {
m_logger->logAction(action);
} else if (ret == DeviceManager::DeviceErrorAsync) {
m_pendingActions.insert(action.id(), action);
} else {
m_logger->logAction(action, Logging::LoggingLevelAlert, ret);
}
return ret;
}
/*! Calls the metheod DeviceManager::findDeviceClass(\a deviceClassId).
@ -307,6 +317,8 @@ GuhCore::GuhCore(QObject *parent) :
qDebug() << "* GUH version:" << GUH_VERSION_STRING << "starting up. *";
qDebug() << "*****************************************";
m_logger = new LogEngine(this);
qDebug() << "*****************************************";
qDebug() << "* Creating Device Manager *";
qDebug() << "*****************************************";
@ -324,22 +336,39 @@ GuhCore::GuhCore(QObject *parent) :
connect(m_deviceManager, &DeviceManager::eventTriggered, this, &GuhCore::gotEvent);
connect(m_deviceManager, &DeviceManager::deviceStateChanged, this, &GuhCore::deviceStateChanged);
connect(m_deviceManager, &DeviceManager::actionExecutionFinished, this, &GuhCore::actionExecuted);
connect(m_deviceManager, &DeviceManager::actionExecutionFinished, this, &GuhCore::actionExecutionFinished);
connect(m_deviceManager, &DeviceManager::devicesDiscovered, this, &GuhCore::devicesDiscovered);
connect(m_deviceManager, &DeviceManager::deviceSetupFinished, this, &GuhCore::deviceSetupFinished);
connect(m_deviceManager, &DeviceManager::pairingFinished, this, &GuhCore::pairingFinished);
m_logger->logSystemEvent(true);
}
/*! Connected to the DeviceManager's emitEvent signal. Events received in
here will be evaluated by the \l{RuleEngine} and the according \l{Action}{Actions} are executed.*/
void GuhCore::gotEvent(const Event &event)
{
// first inform other things about it.
m_logger->logEvent(event);
emit eventTriggered(event);
// Now execute all the associated rules
foreach (const Action &action, m_ruleEngine->evaluateEvent(event)) {
QList<Action> actions;
foreach (const Rule &rule, m_ruleEngine->evaluateEvent(event)) {
if (rule.eventDescriptors().count() > 0) {
m_logger->logRuleTriggered(rule);
actions.append(rule.actions());
} else {
m_logger->logRuleActiveChanged(rule);
if (rule.active()) {
actions.append(rule.actions());
} else {
// TODO: execute state exit actions
}
}
}
// Now execute all the associated actions
foreach (const Action &action, actions) {
qDebug() << "executing action" << action.actionTypeId();
DeviceManager::DeviceError status = m_deviceManager->executeAction(action);
switch(status) {
@ -356,3 +385,15 @@ void GuhCore::gotEvent(const Event &event)
}
}
}
LogEngine* GuhCore::logEngine() const
{
return m_logger;
}
void GuhCore::actionExecutionFinished(const ActionId &id, DeviceManager::DeviceError status)
{
emit actionExecuted(id, status);
Action action = m_pendingActions.take(id);
m_logger->logAction(action, status == DeviceManager::DeviceErrorNoError ? Logging::LoggingLevelInfo : Logging::LoggingLevelAlert, status);
}

View File

@ -31,6 +31,7 @@
class JsonRPCServer;
class Device;
class LogEngine;
class GuhCore : public QObject
{
@ -72,6 +73,8 @@ public:
RuleEngine::RuleError enableRule(const RuleId &ruleId);
RuleEngine::RuleError disableRule(const RuleId &ruleId);
LogEngine* logEngine() const;
signals:
void eventTriggered(const Event &event);
void deviceStateChanged(Device *device, const QUuid &stateTypeId, const QVariant &value);
@ -91,8 +94,12 @@ private:
DeviceManager *m_deviceManager;
RuleEngine *m_ruleEngine;
LogEngine *m_logger;
QHash<ActionId, Action> m_pendingActions;
private slots:
void gotEvent(const Event &event);
void actionExecutionFinished(const ActionId &id, DeviceManager::DeviceError status);
friend class GuhTestBase;
};

View File

@ -74,6 +74,7 @@ JsonReply* ActionHandler::ExecuteAction(const QVariantMap &params)
JsonReply *ActionHandler::GetActionType(const QVariantMap &params) const
{
qDebug() << "asked for action type" << params;
ActionTypeId actionTypeId(params.value("actionTypeId").toString());
foreach (const DeviceClass &deviceClass, GuhCore::instance()->supportedDevices()) {
foreach (const ActionType &actionType, deviceClass.actionTypes()) {

View File

@ -146,6 +146,12 @@ QVariantMap JsonHandler::statusToReply(RuleEngine::RuleError status) const
return returns;
}
QVariantMap JsonHandler::statusToReply(Logging::LoggingError status) const
{
QVariantMap returns;
returns.insert("loggingError", JsonTypes::loggingErrorToString(status));
return returns;
}
JsonReply::JsonReply(Type type, JsonHandler *handler, const QString &method, const QVariantMap &data):
m_type(type),

View File

@ -105,6 +105,7 @@ protected:
JsonReply *createAsyncReply(const QString &method) const;
QVariantMap statusToReply(DeviceManager::DeviceError status) const;
QVariantMap statusToReply(RuleEngine::RuleError status) const;
QVariantMap statusToReply(Logging::LoggingError status) const;
private:
QHash<QString, QString> m_descriptions;

View File

@ -38,11 +38,13 @@
#include "actionhandler.h"
#include "ruleshandler.h"
#include "eventhandler.h"
#include "logginghandler.h"
#include "statehandler.h"
#include <QJsonDocument>
#include <QStringList>
#define JSON_PROTOCOL_VERSION 10
#define JSON_PROTOCOL_VERSION 11
JsonRPCServer::JsonRPCServer(QObject *parent):
JsonHandler(parent),
@ -140,6 +142,8 @@ void JsonRPCServer::setup()
registerHandler(new ActionHandler(this));
registerHandler(new RulesHandler(this));
registerHandler(new EventHandler(this));
registerHandler(new LoggingHandler(this));
registerHandler(new StateHandler(this));
}
void JsonRPCServer::processData(const QUuid &clientId, const QByteArray &jsonData)

View File

@ -38,6 +38,10 @@ QVariantList JsonTypes::s_setupMethod;
QVariantList JsonTypes::s_removePolicy;
QVariantList JsonTypes::s_deviceError;
QVariantList JsonTypes::s_ruleError;
QVariantList JsonTypes::s_loggingError;
QVariantList JsonTypes::s_loggingSource;
QVariantList JsonTypes::s_loggingLevel;
QVariantList JsonTypes::s_loggingEventType;
QVariantMap JsonTypes::s_paramType;
QVariantMap JsonTypes::s_param;
@ -57,6 +61,7 @@ QVariantMap JsonTypes::s_deviceClass;
QVariantMap JsonTypes::s_device;
QVariantMap JsonTypes::s_deviceDescriptor;
QVariantMap JsonTypes::s_rule;
QVariantMap JsonTypes::s_logEntry;
void JsonTypes::init()
{
@ -69,6 +74,10 @@ void JsonTypes::init()
s_removePolicy = enumToStrings(RuleEngine::staticMetaObject, "RemovePolicy");
s_deviceError = enumToStrings(DeviceManager::staticMetaObject, "DeviceError");
s_ruleError = enumToStrings(RuleEngine::staticMetaObject, "RuleError");
s_loggingError = enumToStrings(Logging::staticMetaObject, "LoggingError");
s_loggingSource = enumToStrings(Logging::staticMetaObject, "LoggingSource");
s_loggingLevel = enumToStrings(Logging::staticMetaObject, "LoggingLevel");
s_loggingEventType = enumToStrings(Logging::staticMetaObject, "LoggingEventType");
// ParamType
s_paramType.insert("name", basicTypeToString(String));
@ -174,6 +183,17 @@ void JsonTypes::init()
s_rule.insert("actions", QVariantList() << actionRef());
s_rule.insert("stateEvaluator", stateEvaluatorRef());
// LogEntry
s_logEntry.insert("timestamp", basicTypeToString(Int));
s_logEntry.insert("loggingLevel", loggingLevelRef());
s_logEntry.insert("source", loggingSourceRef());
s_logEntry.insert("o:typeId", basicTypeToString(Uuid));
s_logEntry.insert("o:deviceId", basicTypeToString(Uuid));
s_logEntry.insert("o:value", basicTypeToString(String));
s_logEntry.insert("o:active", basicTypeToString(Bool));
s_logEntry.insert("o:eventType", loggingEventTypeRef());
s_logEntry.insert("o:errorCode", basicTypeToString(String));
s_initialized = true;
}
@ -207,6 +227,11 @@ QVariantMap JsonTypes::allTypes()
allTypes.insert("RemovePolicy", removePolicy());
allTypes.insert("DeviceError", deviceError());
allTypes.insert("RuleError", ruleError());
allTypes.insert("LoggingError", loggingError());
allTypes.insert("LoggingLevel", loggingLevel());
allTypes.insert("LoggingSource", loggingSource());
allTypes.insert("LoggingEventType", loggingEventType());
allTypes.insert("StateType", stateTypeDescription());
allTypes.insert("StateDescriptor", stateDescriptorDescription());
allTypes.insert("StateEvaluator", stateEvaluatorDescription());
@ -224,6 +249,7 @@ QVariantMap JsonTypes::allTypes()
allTypes.insert("DeviceDescriptor", deviceDescriptorDescription());
allTypes.insert("Action", actionDescription());
allTypes.insert("Rule", ruleDescription());
allTypes.insert("LogEntry", logEntryDescription());
return allTypes;
}
@ -461,12 +487,58 @@ QVariantMap JsonTypes::packRule(const Rule &rule)
actionList.append(JsonTypes::packAction(action));
}
ruleMap.insert("actions", actionList);
qDebug() << "packing state evaluator";
ruleMap.insert("stateEvaluator", JsonTypes::packStateEvaluator(rule.stateEvaluator()));
qDebug() << "done p se";
return ruleMap;
}
QVariantMap JsonTypes::packLogEntry(const LogEntry &logEntry)
{
QVariantMap logEntryMap;
logEntryMap.insert("timestamp", logEntry.timestamp().toMSecsSinceEpoch());
logEntryMap.insert("loggingLevel", s_loggingLevel.at(logEntry.level()));
logEntryMap.insert("source", s_loggingSource.at(logEntry.source()));
logEntryMap.insert("eventType", s_loggingEventType.at(logEntry.eventType()));
if (logEntry.eventType() == Logging::LoggingEventTypeActiveChange) {
logEntryMap.insert("active", logEntry.active());
}
if (logEntry.level() == Logging::LoggingLevelAlert) {
switch (logEntry.source()) {
case Logging::LoggingSourceRules:
logEntryMap.insert("errorCode", s_ruleError.at(logEntry.errorCode()));
break;
case Logging::LoggingSourceActions:
case Logging::LoggingSourceEvents:
case Logging::LoggingSourceStates:
logEntryMap.insert("errorCode", s_deviceError.at(logEntry.errorCode()));
break;
case Logging::LoggingSourceSystem:
// FIXME: Update this once we support error codes for the general system
// logEntryMap.insert("errorCode", "");
break;
}
}
switch (logEntry.source()) {
case Logging::LoggingSourceActions:
case Logging::LoggingSourceEvents:
case Logging::LoggingSourceStates:
logEntryMap.insert("typeId", logEntry.typeId().toString());
logEntryMap.insert("deviceId", logEntry.deviceId().toString());
logEntryMap.insert("value", logEntry.value());
break;
case Logging::LoggingSourceSystem:
logEntryMap.insert("active", logEntry.active());
break;
case Logging::LoggingSourceRules:
logEntryMap.insert("typeId", logEntry.typeId().toString());
break;
}
return logEntryMap;
}
QVariantList JsonTypes::packCreateMethods(DeviceClass::CreateMethods createMethods)
{
QVariantList ret;
@ -748,6 +820,12 @@ QPair<bool, QString> JsonTypes::validateVariant(const QVariant &templateVariant,
qDebug() << "evendescriptor not matching";
return result;
}
} else if (refName == logEntryRef()) {
QPair<bool, QString> result = validateMap(logEntryDescription(), variant.toMap());
if (!result.first) {
qDebug() << "logEntry not matching";
return result;
}
} else if (refName == basicTypeRef()) {
QPair<bool, QString> result = validateBasicType(variant);
if (!result.first) {
@ -755,41 +833,65 @@ QPair<bool, QString> JsonTypes::validateVariant(const QVariant &templateVariant,
return result;
}
} else if (refName == stateOperatorRef()) {
QPair<bool, QString> result = validateStateOperator(variant);
QPair<bool, QString> result = validateEnum(s_stateOperator, variant);
if (!result.first) {
qDebug() << "value not allowed in" << stateOperatorRef();
return result;
}
} else if (refName == createMethodRef()) {
QPair<bool, QString> result = validateCreateMethod(variant);
QPair<bool, QString> result = validateEnum(s_createMethod, variant);
if (!result.first) {
qDebug() << "value not allowed in" << createMethodRef() << variant;
return result;
}
} else if (refName == setupMethodRef()) {
QPair<bool, QString> result = validateSetupMethod(variant);
QPair<bool, QString> result = validateEnum(s_setupMethod, variant);
if (!result.first) {
qDebug() << "value not allowed in" << createMethodRef();
return result;
}
} else if (refName == valueOperatorRef()) {
QPair<bool, QString> result = validateValueOperator(variant);
QPair<bool, QString> result = validateEnum(s_valueOperator, variant);
if (!result.first) {
qDebug() << QString("value %1 not allowed in %2").arg(variant.toString()).arg(valueOperatorRef());
return result;
}
} else if (refName == deviceErrorRef()) {
QPair<bool, QString> result = validateDeviceError(variant);
QPair<bool, QString> result = validateEnum(s_deviceError, variant);
if (!result.first) {
qDebug() << QString("value %1 not allowed in %2").arg(variant.toString()).arg(deviceErrorRef());
return result;
}
} else if (refName == ruleErrorRef()) {
QPair<bool, QString> result = validateRuleError(variant);
QPair<bool, QString> result = validateEnum(s_ruleError, variant);
if (!result.first) {
qDebug() << QString("value %1 not allowed in %2").arg(variant.toString()).arg(ruleErrorRef());
return result;
}
} else if (refName == loggingErrorRef()) {
QPair<bool, QString> result = validateEnum(s_loggingError, variant);
if (!result.first) {
qDebug() << QString("value %1 not allowed in %2").arg(variant.toString()).arg(loggingErrorRef());
return result;
}
} else if (refName == loggingSourceRef()) {
QPair<bool, QString> result = validateEnum(s_loggingSource, variant);
if (!result.first) {
qDebug() << QString("value %1 not allowed in %2").arg(variant.toString()).arg(loggingSourceRef());
return result;
}
} else if (refName == loggingLevelRef()) {
QPair<bool, QString> result = validateEnum(s_loggingLevel, variant);
if (!result.first) {
qDebug() << QString("value %1 not allowed in %2").arg(variant.toString()).arg(loggingLevelRef());
return result;
}
} else if (refName == loggingEventTypeRef()) {
QPair<bool, QString> result = validateEnum(s_loggingEventType, variant);
if (!result.first) {
qDebug() << QString("value %1 not allowed in %2").arg(variant.toString()).arg(loggingEventTypeRef());
return result;
}
} else {
Q_ASSERT_X(false, "JsonTypes", QString("Unhandled ref: %1").arg(refName).toLatin1().data());
return report(false, QString("Unhandled ref %1. Server implementation incomplete.").arg(refName));
@ -844,32 +946,12 @@ QPair<bool, QString> JsonTypes::validateBasicType(const QVariant &variant)
return report(false, QString("Error validating basic type %1.").arg(variant.toString()));
}
QPair<bool, QString> JsonTypes::validateStateOperator(const QVariant &variant)
QPair<bool, QString> JsonTypes::validateEnum(const QVariantList &enumDescription, const QVariant &value)
{
return report(s_stateOperator.contains(variant.toString()), QString("Unknown state operator %1").arg(variant.toString()));
}
QStringList enumStrings;
foreach (const QVariant &variant, enumDescription) {
enumStrings.append(variant.toString());
}
QPair<bool, QString> JsonTypes::validateCreateMethod(const QVariant &variant)
{
return report(s_createMethod.contains(variant.toString()), QString("Unknwon createMethod type %1").arg(variant.toString()));
}
QPair<bool, QString> JsonTypes::validateSetupMethod(const QVariant &variant)
{
return report(s_setupMethod.contains(variant.toString()), QString("Unknwon SetupMethod: %1").arg(variant.toString()));
}
QPair<bool, QString> JsonTypes::validateValueOperator(const QVariant &variant)
{
return report(s_valueOperator.contains(variant.toString()), QString("Unknown ValueOperator: %1").arg(variant.toString()));
}
QPair<bool, QString> JsonTypes::validateDeviceError(const QVariant &variant)
{
return report(s_deviceError.contains(variant.toString()), QString("Unknown DeviceError: %1").arg(variant.toString()));
}
QPair<bool, QString> JsonTypes::validateRuleError(const QVariant &variant)
{
return report(s_ruleError.contains(variant.toString()), QString("Unknown RuleError: %1").arg(variant.toString()));
return report(enumDescription.contains(value.toString()), QString("Value %1 not allowed in %2").arg(value.toString()).arg(enumStrings.join(", ")));
}

View File

@ -31,6 +31,9 @@
#include "types/paramtype.h"
#include "types/paramdescriptor.h"
#include "logging/logging.h"
#include "logging/logentry.h"
#include <QObject>
#include <QVariantMap>
@ -94,6 +97,10 @@ public:
DECLARE_TYPE(deviceError, "DeviceError", DeviceManager, DeviceError)
DECLARE_TYPE(removePolicy, "RemovePolicy", RuleEngine, RemovePolicy)
DECLARE_TYPE(ruleError, "RuleError", RuleEngine, RuleError)
DECLARE_TYPE(loggingError, "LoggingError", Logging, LoggingError)
DECLARE_TYPE(loggingSource, "LoggingSource", Logging, LoggingSource)
DECLARE_TYPE(loggingLevel, "LoggingLevel", Logging, LoggingLevel)
DECLARE_TYPE(loggingEventType, "LoggingEventType", Logging, LoggingEventType)
DECLARE_OBJECT(paramType, "ParamType")
DECLARE_OBJECT(param, "Param")
DECLARE_OBJECT(paramDescriptor, "ParamDescriptor")
@ -112,6 +119,7 @@ public:
DECLARE_OBJECT(device, "Device")
DECLARE_OBJECT(deviceDescriptor, "DeviceDescriptor")
DECLARE_OBJECT(rule, "Rule")
DECLARE_OBJECT(logEntry, "LogEntry")
static QVariantMap packEventType(const EventType &eventType);
static QVariantMap packEvent(const Event &event);
@ -130,6 +138,7 @@ public:
static QVariantMap packDevice(Device *device);
static QVariantMap packDeviceDescriptor(const DeviceDescriptor &descriptor);
static QVariantMap packRule(const Rule &rule);
static QVariantMap packLogEntry(const LogEntry &logEntry);
static QVariantList packCreateMethods(DeviceClass::CreateMethods createMethods);
static Param unpackParam(const QVariantMap &paramMap);
@ -144,13 +153,8 @@ public:
static QPair<bool, QString> validateProperty(const QVariant &templateValue, const QVariant &value);
static QPair<bool, QString> validateList(const QVariantList &templateList, const QVariantList &list);
static QPair<bool, QString> validateVariant(const QVariant &templateVariant, const QVariant &variant);
static QPair<bool, QString> validateEnum(const QVariantList &enumList, const QVariant &value);
static QPair<bool, QString> validateBasicType(const QVariant &variant);
static QPair<bool, QString> validateStateOperator(const QVariant &variant);
static QPair<bool, QString> validateCreateMethod(const QVariant &variant);
static QPair<bool, QString> validateSetupMethod(const QVariant &variant);
static QPair<bool, QString> validateValueOperator(const QVariant &variant);
static QPair<bool, QString> validateDeviceError(const QVariant &variant);
static QPair<bool, QString> validateRuleError(const QVariant &variant);
private:
static bool s_initialized;

View File

@ -0,0 +1,69 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "logginghandler.h"
#include "logging/logengine.h"
#include "guhcore.h"
LoggingHandler::LoggingHandler(QObject *parent) :
JsonHandler(parent)
{
QVariantMap params;
QVariantMap returns;
// Notifications
params.clear(); returns.clear();
setDescription("LogEntryAdded", "Emitted whenever an entry is appended to the logging system.");
params.insert("logEntry", JsonTypes::logEntryRef());
setParams("LogEntryAdded", params);
params.clear(); returns.clear();
setDescription("GetLogEntries", "Get the LogEntries matching the given filter.");
// params.insert("eventTypeId", JsonTypes::basicTypeToString(JsonTypes::Uuid));
setParams("GetLogEntries", params);
returns.insert("loggingError", JsonTypes::loggingErrorRef());
returns.insert("o:logEntries", QVariantList() << JsonTypes::logEntryRef());
setReturns("GetLogEntries", returns);
connect(GuhCore::instance()->logEngine(), &LogEngine::logEntryAdded, this, &LoggingHandler::logEntryAdded);
}
QString LoggingHandler::name() const
{
return "Logging";
}
void LoggingHandler::logEntryAdded(const LogEntry &logEntry)
{
QVariantMap params;
params.insert("logEntry", JsonTypes::packLogEntry(logEntry));
emit LogEntryAdded(params);
}
JsonReply* LoggingHandler::GetLogEntries(const QVariantMap &params) const
{
Q_UNUSED(params)
QVariantList entries;
foreach (const LogEntry &entry, GuhCore::instance()->logEngine()->logEntries()) {
entries.append(JsonTypes::packLogEntry(entry));
}
QVariantMap returns = statusToReply(Logging::LoggingErrorNoError);
returns.insert("logEntries", entries);
return createReply(returns);
}

View File

@ -0,0 +1,40 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef LOGGINGHANDLER_H
#define LOGGINGHANDLER_H
#include "jsonhandler.h"
#include "logging/logentry.h"
class LoggingHandler : public JsonHandler
{
Q_OBJECT
public:
explicit LoggingHandler(QObject *parent = 0);
QString name() const override;
Q_INVOKABLE JsonReply *GetLogEntries(const QVariantMap &params) const;
signals:
void LogEntryAdded(const QVariantMap &params);
private slots:
void logEntryAdded(const LogEntry &entry);
};
#endif // LOGGINGHANDLER_H

View File

@ -39,7 +39,8 @@ RulesHandler::RulesHandler(QObject *parent) :
setDescription("GetRuleDetails", "Get details for the rule identified by ruleId");
params.insert("ruleId", JsonTypes::basicTypeToString(JsonTypes::Uuid));
setParams("GetRuleDetails", params);
returns.insert("rule", JsonTypes::ruleRef());
returns.insert("o:rule", JsonTypes::ruleRef());
returns.insert("ruleError", JsonTypes::ruleErrorRef());
setReturns("GetRuleDetails", returns);
params.clear(); returns.clear();
@ -110,13 +111,12 @@ JsonReply *RulesHandler::GetRuleDetails(const QVariantMap &params)
{
RuleId ruleId = RuleId(params.value("ruleId").toString());
Rule rule = GuhCore::instance()->findRule(ruleId);
QVariantMap ruleData;
if (!rule.id().isNull()) {
qDebug() << "packing rule";
ruleData.insert("rule", JsonTypes::packRule(rule));
qDebug() << "done packing";
if (rule.id().isNull()) {
return createReply(statusToReply(RuleEngine::RuleErrorRuleNotFound));
}
return createReply(ruleData);
QVariantMap returns = statusToReply(RuleEngine::RuleErrorNoError);
returns.insert("rule", JsonTypes::packRule(rule));
return createReply(returns);
}
JsonReply* RulesHandler::AddRule(const QVariantMap &params)
@ -147,7 +147,9 @@ JsonReply* RulesHandler::AddRule(const QVariantMap &params)
foreach (const QVariant &actionVariant, actionList) {
QVariantMap actionMap = actionVariant.toMap();
Action action(ActionTypeId(actionMap.value("actionTypeId").toString()), DeviceId(actionMap.value("deviceId").toString()));
qDebug() << "params from json" << actionMap.value("params");
action.setParams(JsonTypes::unpackParams(actionMap.value("params").toList()));
qDebug() << "params in action" << action.params();
actions.append(action);
}

View File

@ -0,0 +1,55 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "statehandler.h"
#include "guhcore.h"
StateHandler::StateHandler(QObject *parent) :
JsonHandler(parent)
{
QVariantMap params;
QVariantMap returns;
params.clear(); returns.clear();
setDescription("GetStateType", "Get the StateType for the given stateTypeId.");
params.insert("stateTypeId", JsonTypes::basicTypeToString(JsonTypes::Uuid));
setParams("GetStateType", params);
returns.insert("deviceError", JsonTypes::deviceErrorRef());
returns.insert("o:stateType", JsonTypes::stateTypeRef());
setReturns("GetStateType", returns);
}
QString StateHandler::name() const
{
return "States";
}
JsonReply* StateHandler::GetStateType(const QVariantMap &params) const
{
StateTypeId stateTypeId(params.value("stateTypeId").toString());
foreach (const DeviceClass &deviceClass, GuhCore::instance()->supportedDevices()) {
foreach (const StateType &stateType, deviceClass.stateTypes()) {
if (stateType.id() == stateTypeId) {
QVariantMap data = statusToReply(DeviceManager::DeviceErrorNoError);
data.insert("stateType", JsonTypes::packStateType(stateType));
return createReply(data);
}
}
}
return createReply(statusToReply(DeviceManager::DeviceErrorStateTypeNotFound));
}

View File

@ -0,0 +1,34 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef STATEHANDLER_H
#define STATEHANDLER_H
#include "jsonhandler.h"
class StateHandler : public JsonHandler
{
Q_OBJECT
public:
explicit StateHandler(QObject *parent = 0);
QString name() const override;
Q_INVOKABLE JsonReply *GetStateType(const QVariantMap &params) const;
};
#endif // EVENTHANDLER_H

View File

@ -0,0 +1,209 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "logengine.h"
#include "logging.h"
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QMetaEnum>
#include <QDateTime>
#define DB_SCHEMA_VERSION 1
LogEngine::LogEngine(QObject *parent):
QObject(parent)
{
m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setDatabaseName("/tmp/guhd-logs.sqlite");
if (!m_db.open()) {
qWarning() << "Error opening log database:" << m_db.lastError().driverText() << m_db.lastError().databaseText();
return;
}
initDB();
}
QList<LogEntry> LogEngine::logEntries() const
{
QList<LogEntry> results;
QSqlQuery query;
query.exec("SELECT * FROM entries;");
while (query.next()) {
LogEntry entry(
QDateTime::fromMSecsSinceEpoch(query.value("timestamp").toLongLong()),
(Logging::LoggingLevel)query.value("loggingLevel").toInt(),
(Logging::LoggingSource)query.value("sourceType").toInt(),
query.value("errorCode").toInt());
entry.setTypeId(query.value("typeId").toUuid());
entry.setDeviceId(DeviceId(query.value("deviceId").toString()));
entry.setValue(query.value("value").toString());
if ((Logging::LoggingEventType)query.value("loggingEventType").toInt() == Logging::LoggingEventTypeActiveChange) {
entry.setActive(query.value("active").toBool());
}
results.append(entry);
}
return results;
}
void LogEngine::logSystemEvent(bool active, Logging::LoggingLevel level)
{
LogEntry entry(level, Logging::LoggingSourceSystem);
entry.setActive(active);
appendLogEntry(entry);
emit logEntryAdded(entry);
}
void LogEngine::logEvent(const Event &event)
{
QStringList valueList;
Logging::LoggingSource sourceType;
if (event.isStateChangeEvent()) {
sourceType = Logging::LoggingSourceStates;
valueList << event.param("value").value().toString();
} else {
sourceType = Logging::LoggingSourceEvents;
foreach (const Param &param, event.params()) {
valueList << param.value().toString();
}
}
LogEntry entry(sourceType);
entry.setTypeId(event.eventTypeId());
entry.setDeviceId(event.deviceId());
entry.setValue(valueList.join(", "));
appendLogEntry(entry);
emit logEntryAdded(entry);
}
void LogEngine::logAction(const Action &action, Logging::LoggingLevel level, int errorCode)
{
QStringList valueList;
foreach (const Param &param, action.params()) {
valueList << param.value().toString();
}
LogEntry entry(level, Logging::LoggingSourceActions, errorCode);
entry.setTypeId(action.actionTypeId());
entry.setDeviceId(action.deviceId());
entry.setValue(valueList.join(", "));
appendLogEntry(entry);
emit logEntryAdded(entry);
}
void LogEngine::logRuleTriggered(const Rule &rule)
{
LogEntry entry(Logging::LoggingSourceRules);
entry.setTypeId(rule.id());
appendLogEntry(entry);
emit logEntryAdded(entry);
}
void LogEngine::logRuleActiveChanged(const Rule &rule)
{
LogEntry entry(Logging::LoggingSourceRules);
entry.setTypeId(rule.id());
entry.setActive(rule.active());
appendLogEntry(entry);
emit logEntryAdded(entry);
}
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');")
.arg(entry.timestamp().toMSecsSinceEpoch())
.arg(entry.eventType())
.arg(entry.level())
.arg(entry.source())
.arg(entry.typeId().toString())
.arg(entry.deviceId().toString())
.arg(entry.value())
.arg(entry.active())
.arg(entry.errorCode());
QSqlQuery query;
query.exec(queryString);
if (query.lastError().isValid()) {
qWarning() << "Error writing log entry. Driver error:" << query.lastError().driverText() << "Database error:" << query.lastError().databaseText();
}
}
void LogEngine::initDB()
{
m_db.close();
m_db.open();
QSqlQuery query;
if (!m_db.tables().contains("metadata")) {
query.exec("CREATE TABLE metadata (key varchar(10), data varchar(40));");
query.exec(QString("INSERT INTO metadata (key, data) VALUES('version', '%1');").arg(DB_SCHEMA_VERSION));
}
query.exec("SELECT data FROM metadata WHERE key = 'version';");
if (query.next()) {
int version = query.value("data").toInt();
if (version != DB_SCHEMA_VERSION) {
qWarning() << "Log schema version not matching! Schema upgrade not implemented yet. Logging might fail.";
} else {
qDebug() << "version matches";
}
} else {
qWarning() << "Broken log database. Version not found in metadata table.";
}
if (!m_db.tables().contains("sourceTypes")) {
query.exec("CREATE TABLE sourceTypes (id int, name varchar(20), PRIMARY KEY(id));");
qDebug() << query.lastError().databaseText();
QMetaEnum logTypes = Logging::staticMetaObject.enumerator(Logging::staticMetaObject.indexOfEnumerator("LoggingSource"));
Q_ASSERT_X(logTypes.isValid(), "LogEngine", "Logging has no enum LoggingSource");
for (int i = 0; i < logTypes.keyCount(); i++) {
query.exec(QString("INSERT INTO sourceTypes (id, name) VALUES(%1, '%2');").arg(i).arg(logTypes.key(i)));
}
}
if (!m_db.tables().contains("loggingEventTypes")) {
query.exec("CREATE TABLE loggingEventTypes (id int, name varchar(20), PRIMARY KEY(id));");
qDebug() << query.lastError().databaseText();
QMetaEnum logTypes = Logging::staticMetaObject.enumerator(Logging::staticMetaObject.indexOfEnumerator("LoggingEventType"));
Q_ASSERT_X(logTypes.isValid(), "LogEngine", "Logging has no enum LoggingEventType");
for (int i = 0; i < logTypes.keyCount(); i++) {
query.exec(QString("INSERT INTO loggingEventTypes (id, name) VALUES(%1, '%2');").arg(i).arg(logTypes.key(i)));
}
}
if (!m_db.tables().contains("entries")) {
query.exec("CREATE TABLE entries "
"("
"timestamp int,"
"loggingLevel int,"
"sourceType int,"
"typeId varchar(38),"
"deviceId varchar(38),"
"value varchar(100),"
"loggingEventType int,"
"active bool,"
"errorCode int,"
"FOREIGN KEY(sourceType) REFERENCES sourceTypes(id),"
"FOREIGN KEY(loggingEventType) REFERENCES loggingEventTypes(id)"
");");
if (query.lastError().isValid()) {
qWarning() << "Error creating log table in database. Driver error:" << query.lastError().driverText() << "Database error:" << query.lastError().databaseText();
}
}
}

View File

@ -0,0 +1,57 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef LOGENGINE_H
#define LOGENGINE_H
#include "logentry.h"
#include "types/event.h"
#include "types/action.h"
#include "rule.h"
#include <QObject>
#include <QSqlDatabase>
class LogEngine: public QObject
{
Q_OBJECT
public:
LogEngine(QObject *parent = 0);
QList<LogEntry> logEntries() const;
signals:
void logEntryAdded(const LogEntry &logEntry);
private:
// Only GuhCore is allowed to log events.
friend class GuhCore;
void logSystemEvent(bool active, Logging::LoggingLevel level = Logging::LoggingLevelInfo);
void logEvent(const Event &event);
void logAction(const Action &action, Logging::LoggingLevel level = Logging::LoggingLevelInfo, int errorCode = 0);
void logRuleTriggered(const Rule &rule);
void logRuleActiveChanged(const Rule &rule);
private:
void initDB();
void appendLogEntry(const LogEntry &entry);
QSqlDatabase m_db;
};
#endif

109
server/logging/logentry.cpp Normal file
View File

@ -0,0 +1,109 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "logentry.h"
#include <QDebug>
LogEntry::LogEntry(QDateTime timestamp, Logging::LoggingLevel level, Logging::LoggingSource source, int errorCode):
m_timestamp(timestamp),
m_level(level),
m_source(source),
m_eventType(Logging::LoggingEventTypeTrigger),
m_active(false),
m_errorCode(errorCode)
{
}
LogEntry::LogEntry(Logging::LoggingLevel level, Logging::LoggingSource source, int errorCode):
LogEntry(QDateTime::currentDateTime(), level, source, errorCode)
{
}
LogEntry::LogEntry(Logging::LoggingSource source):
LogEntry(Logging::LoggingLevelInfo, source)
{
}
QDateTime LogEntry::timestamp() const
{
return m_timestamp;
}
Logging::LoggingLevel LogEntry::level() const
{
return m_level;
}
Logging::LoggingSource LogEntry::source() const
{
return m_source;
}
QUuid LogEntry::typeId() const
{
return m_typeId;
}
void LogEntry::setTypeId(const QUuid &typeId) {
m_typeId = typeId;
}
DeviceId LogEntry::deviceId() const
{
return m_deviceId;
}
void LogEntry::setDeviceId(const DeviceId &deviceId)
{
m_deviceId = deviceId;
}
QString LogEntry::value() const
{
return m_value;
}
void LogEntry::setValue(const QString &value)
{
m_value = value;
}
Logging::LoggingEventType LogEntry::eventType() const
{
return m_eventType;
}
bool LogEntry::active() const
{
return m_active;
}
void LogEntry::setActive(bool active)
{
m_eventType = Logging::LoggingEventTypeActiveChange;
m_active = active;
}
int LogEntry::errorCode() const
{
return m_errorCode;
}

77
server/logging/logentry.h Normal file
View File

@ -0,0 +1,77 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef LOGENTRY_H
#define LOGENTRY_H
#include "logging.h"
#include "typeutils.h"
#include <QObject>
#include <QDateTime>
class LogEntry
{
Q_GADGET
public:
LogEntry(QDateTime timestamp, Logging::LoggingLevel level, Logging::LoggingSource source, int errorCode = 0);
LogEntry(Logging::LoggingLevel level, Logging::LoggingSource source, int errorCode = 0);
LogEntry(Logging::LoggingSource source);
// Valid for all LoggingSources
QDateTime timestamp() const;
Logging::LoggingLevel level() const;
Logging::LoggingSource source() const;
Logging::LoggingEventType eventType() const;
// Valid for LoggingSourceStates, LoggingSourceEvents, LoggingSourceActions, LoggingSourceRules
QUuid typeId() const;
void setTypeId(const QUuid &typeId);
// Valid for LoggingSourceStates, LoggingSourceEvents, LoggingSourceActions
DeviceId deviceId() const;
void setDeviceId(const DeviceId &deviceId);
// Valid for LoggingSourceStates
QString value() const;
void setValue(const QString &value);
// Valid for LoggingEventTypeActiveChanged
bool active() const;
void setActive(bool active);
// Valid for LoggingLevelAlert
int errorCode() const;
private:
QDateTime m_timestamp;
Logging::LoggingLevel m_level;
Logging::LoggingSource m_source;
// RuleSource specifiv properties.
// FIXME: If it turns out we need many more of those, we should subclass LogEntry with specific ones.
QUuid m_typeId;
DeviceId m_deviceId;
QString m_value;
Logging::LoggingEventType m_eventType;
bool m_active;
int m_errorCode;
};
#endif

View File

View File

@ -0,0 +1,31 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef LOGFILTER_H
#define LOGFILTER_H
#include <QDateTime>
class LogFilter
{
QDateTime m_startDate;
QDateTime m_endDate;
};
#endif

58
server/logging/logging.h Normal file
View File

@ -0,0 +1,58 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef LOGGING_H
#define LOGGING_H
#include <QObject>
class Logging
{
Q_GADGET
Q_ENUMS(LoggingError)
Q_ENUMS(LoggingSource)
Q_FLAGS(LoggingSources)
Q_ENUMS(LoggingLevel)
Q_ENUMS(LoggingEventType)
public:
enum LoggingError {
LoggingErrorNoError,
LoggingErrorLogEntryNotFound
};
enum LoggingSource {
LoggingSourceSystem,
LoggingSourceEvents,
LoggingSourceActions,
LoggingSourceStates,
LoggingSourceRules
};
Q_DECLARE_FLAGS(LoggingSources, LoggingSource)
enum LoggingLevel {
LoggingLevelInfo,
LoggingLevelAlert
};
enum LoggingEventType {
LoggingEventTypeTrigger,
LoggingEventTypeActiveChange
};
};
#endif

View File

View File

View File

@ -51,9 +51,9 @@
#include <QDebug>
/*! Constructs an empty, invalid rule. */
Rule::Rule()
Rule::Rule():
Rule(RuleId(), QList<EventDescriptor>(), StateEvaluator(), QList<Action>())
{
}
/*! Constructs a Rule with the given \a id, \a eventDescriptorList, \a stateEvaluator and \a actions.*/
@ -61,7 +61,9 @@ Rule::Rule(const RuleId &id, const QList<EventDescriptor> &eventDescriptorList,
m_id(id),
m_eventDescriptors(eventDescriptorList),
m_stateEvaluator(stateEvaluator),
m_actions(actions)
m_actions(actions),
m_enabled(false),
m_active(false)
{
}
@ -100,3 +102,13 @@ void Rule::setEnabled(bool enabled)
{
m_enabled = enabled;
}
bool Rule::active() const
{
return m_active;
}
void Rule::setActive(bool active)
{
m_active = active;
}

View File

@ -40,6 +40,12 @@ public:
bool enabled() const;
void setEnabled(bool enabled);
bool active() const;
private:
friend class RuleEngine;
void setActive(bool active);
private:
RuleId m_id;
QList<EventDescriptor> m_eventDescriptors;
@ -47,6 +53,7 @@ private:
QList<Action> m_actions;
bool m_enabled;
bool m_active;
};
#endif // RULE_H

View File

@ -160,14 +160,15 @@ RuleEngine::RuleEngine(QObject *parent) :
/*! Ask the Engine to evaluate all the rules for the given \a event.
This will search all the \l{Rule}{Rules} triggered by the given \a event
and evaluate their states in the system. It will return a
list of all \l{Action}{Actions} that should be executed. */
QList<Action> RuleEngine::evaluateEvent(const Event &event)
list of all \l{Rule}{Rules} that are triggered or change its active state
because of this \a event. */
QList<Rule> RuleEngine::evaluateEvent(const Event &event)
{
Device *device = GuhCore::instance()->findConfiguredDevice(event.deviceId());
qDebug() << "got event:" << event << device->name() << event.eventTypeId();
QList<Action> actions;
QList<Rule> rules;
foreach (const RuleId &id, m_ruleIds) {
Rule rule = m_rules.value(id);
if (!rule.enabled()) {
@ -182,11 +183,14 @@ QList<Action> RuleEngine::evaluateEvent(const Event &event)
qDebug() << "Rule" << rule.id() << "still in active state.";
} else {
qDebug() << "Rule" << rule.id() << "entered active state.";
rule.setActive(true);
m_rules[rule.id()] = rule;
m_activeRules.append(rule.id());
actions.append(rule.actions());
rules.append(rule);
}
} else {
qDebug() << "Rule" << rule.id() << "left active state.";
m_rules[rule.id()].setActive(false);
m_activeRules.removeAll(rule.id());
}
}
@ -194,12 +198,12 @@ QList<Action> RuleEngine::evaluateEvent(const Event &event)
if (containsEvent(rule, event)) {
if (rule.stateEvaluator().evaluate()) {
qDebug() << "Rule" << rule.id() << "contains event" << event.eventId() << "and all states match.";
actions.append(rule.actions());
rules.append(rule);
}
}
}
}
return actions;
return rules;
}
/*! Add a new \l{Rule} with the given \a ruleId , \a eventDescriptorList and \a actions to the engine.
@ -258,7 +262,9 @@ RuleEngine::RuleError RuleEngine::addRule(const RuleId &ruleId, const QList<Even
return RuleErrorActionTypeNotFound;
}
}
if (actions.count() > 0) {
qDebug() << "***** actions" << actions.last().actionTypeId() << actions.last().params();
}
Rule rule = Rule(ruleId, eventDescriptorList, stateEvaluator, actions);
rule.setEnabled(enabled);
appendRule(rule);

View File

@ -51,7 +51,7 @@ public:
explicit RuleEngine(QObject *parent = 0);
QList<Action> evaluateEvent(const Event &event);
QList<Rule> evaluateEvent(const Event &event);
RuleError addRule(const RuleId &ruleId, const QList<EventDescriptor> &eventDescriptorList, const QList<Action> &actions, bool enabled = true);
RuleError addRule(const RuleId &ruleId, const QList<EventDescriptor> &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList<Action> &actions, bool enabled = true);

View File

@ -9,7 +9,12 @@ SOURCES += $$top_srcdir/server/guhcore.cpp \
$$top_srcdir/server/jsonrpc/ruleshandler.cpp \
$$top_srcdir/server/jsonrpc/actionhandler.cpp \
$$top_srcdir/server/jsonrpc/eventhandler.cpp \
$$top_srcdir/server/jsonrpc/statehandler.cpp \
$$top_srcdir/server/jsonrpc/logginghandler.cpp \
$$top_srcdir/server/stateevaluator.cpp \
$$top_srcdir/server/logging/logengine.cpp \
$$top_srcdir/server/logging/logfilter.cpp \
$$top_srcdir/server/logging/logentry.cpp \
HEADERS += $$top_srcdir/server/guhcore.h \
$$top_srcdir/server/tcpserver.h \
@ -22,5 +27,11 @@ HEADERS += $$top_srcdir/server/guhcore.h \
$$top_srcdir/server/jsonrpc/ruleshandler.h \
$$top_srcdir/server/jsonrpc/actionhandler.h \
$$top_srcdir/server/jsonrpc/eventhandler.h \
$$top_srcdir/server/jsonrpc/statehandler.h \
$$top_srcdir/server/jsonrpc/logginghandler.h \
$$top_srcdir/server/stateevaluator.h \
$$top_srcdir/server/jsontypes.h
$$top_srcdir/server/jsontypes.h \
$$top_srcdir/server/logging/logging.h \
$$top_srcdir/server/logging/logengine.h \
$$top_srcdir/server/logging/logfilter.h \
$$top_srcdir/server/logging/logentry.h \

View File

@ -10,7 +10,7 @@ INCLUDEPATH += ../libguh jsonrpc
target.path = /usr/bin
INSTALLS += target
QT += network
QT += network sql
LIBS += -L$$top_builddir/libguh/ -lguh

View File

@ -1,4 +1,4 @@
10
11
{
"methods": {
"Actions.ExecuteAction": {
@ -262,6 +262,17 @@
"version": "String"
}
},
"Logging.GetLogEntries": {
"description": "Get the LogEntries matching the given filter.",
"params": {
},
"returns": {
"loggingError": "$ref:LoggingError",
"o:logEntries": [
"$ref:LogEntry"
]
}
},
"Rules.AddRule": {
"description": "Add a rule. You can describe rules by one or many EventDesciptors and a StateEvaluator. Note that onlyone of either eventDescriptor or eventDescriptorList may be passed at a time. A rule can be created but left disabled,meaning it won't actually be executed until set to enabled. If not given, enabled defaults to true.",
"params": {
@ -336,6 +347,16 @@
"returns": {
"ruleError": "$ref:RuleError"
}
},
"States.GetStateType": {
"description": "Get the StateType for the given stateTypeId.",
"params": {
"stateTypeId": "Uuid"
},
"returns": {
"deviceError": "$ref:DeviceError",
"o:stateType": "$ref:StateType"
}
}
},
"notifications": {
@ -352,6 +373,12 @@
"params": {
"event": "$ref:Event"
}
},
"Logging.LogEntryAdded": {
"description": "Emitted whenever an entry is appended to the logging system.",
"params": {
"logEntry": "$ref:LogEntry"
}
}
},
"types": {
@ -463,6 +490,36 @@
"$ref:ParamType"
]
},
"LogEntry": {
"loggingLevel": "$ref:LoggingLevel",
"o:active": "Bool",
"o:deviceId": "Uuid",
"o:errorCode": "String",
"o:eventType": "$ref:LoggingEventType",
"o:typeId": "Uuid",
"o:value": "String",
"source": "$ref:LoggingSource",
"timestamp": "Int"
},
"LoggingError": [
"LoggingErrorNoError",
"LoggingErrorLogEntryNotFound"
],
"LoggingEventType": [
"LoggingEventTypeTrigger",
"LoggingEventTypeActiveChange"
],
"LoggingLevel": [
"LoggingLevelInfo",
"LoggingLevelAlert"
],
"LoggingSource": [
"LoggingSourceSystem",
"LoggingSourceEvents",
"LoggingSourceActions",
"LoggingSourceStates",
"LoggingSourceRules"
],
"Param": {
"name": "String",
"value": "$ref:BasicType"

View File

@ -1,4 +1,4 @@
QT += testlib network
QT += testlib network sql
CONFIG += testcase
include($$top_srcdir/server/server.pri)

View File

@ -206,7 +206,7 @@ void TestJSONRPC::stateChangeEmitsNotifications()
// Lets wait for the notification
clientSpy.wait();
QCOMPARE(clientSpy.count(), 2);
QCOMPARE(clientSpy.count(), 3); // statechangeevent, state change, log entry added
// Make sure the notification contains all the stuff we expect
QJsonDocument jsonDoc = QJsonDocument::fromJson(clientSpy.at(0).at(1).toByteArray());
@ -216,6 +216,11 @@ void TestJSONRPC::stateChangeEmitsNotifications()
// Make sure the notification contains all the stuff we expect
jsonDoc = QJsonDocument::fromJson(clientSpy.at(1).at(1).toByteArray());
QCOMPARE(jsonDoc.toVariant().toMap().value("notification").toString(), QString("Logging.LogEntryAdded"));
QCOMPARE(jsonDoc.toVariant().toMap().value("params").toMap().value("logEntry").toMap().value("typeId").toUuid(), stateTypeId);
// Make sure the notification contains all the stuff we expect
jsonDoc = QJsonDocument::fromJson(clientSpy.at(2).at(1).toByteArray());
QCOMPARE(jsonDoc.toVariant().toMap().value("notification").toString(), QString("Events.EventTriggered"));
QCOMPARE(jsonDoc.toVariant().toMap().value("params").toMap().value("event").toMap().value("eventTypeId").toUuid(), stateTypeId);
QCOMPARE(jsonDoc.toVariant().toMap().value("params").toMap().value("event").toMap().value("params").toList().first().toMap().value("value").toInt(), newVal);

View File

@ -368,7 +368,11 @@ void TestRules::loadStoreConfig()
if (actionVariant.toMap().value("actionTypeId") == replyActionVariant.toMap().value("actionTypeId") &&
actionVariant.toMap().value("deviceId") == replyActionVariant.toMap().value("deviceId")) {
found = true;
QVERIFY2(actionVariant == replyActionVariant, "Action doesn't match after loading from config.");
QJsonDocument bDoc = QJsonDocument::fromVariant(actionVariant);
QString bString = bDoc.toJson();
QJsonDocument aDoc = QJsonDocument::fromVariant(replyActionVariant);
QString aString = aDoc.toJson();
QVERIFY2(actionVariant == replyActionVariant, QString("Action doesn't match after loading from config.\nBefore storing: %1\nAfter storing:%2").arg(bString).arg(aString).toUtf8().data());
}
}
QVERIFY2(found, "Action not found after loading from config.");

View File

@ -2,6 +2,7 @@
import telnetlib
import json
import datetime
HOST='localhost'
PORT=1234
@ -21,7 +22,9 @@ methods = {'1': 'add_device',
'12': 'list_deviceClasses',
'13': 'list_deviceClasses_by_vendor',
'14': 'list_rules',
'15': 'list_rules_containig_deviceId'}
'15': 'list_rules_containig_deviceId',
'16': 'list_logEntries'
}
def get_menu_selection():
@ -50,6 +53,7 @@ def get_menu_selection():
print " 13 -> List supported devices by vendor"
print " 14 -> List configured rules"
print " 15 -> List rules containing a certain device"
print " 16 -> Print log"
print "----------------------------------------"
print ""
selection = raw_input("Enter selection: ")
@ -321,7 +325,9 @@ def get_actionType(actionTypeId):
params = {}
params['actionTypeId'] = actionTypeId
response = send_command("Actions.GetActionType", params)
return response['params']['actionType']
if "actionType" in response['params']:
return response['params']['actionType']
return None
def get_eventType(eventTypeId):
@ -668,20 +674,25 @@ def list_rules():
print response['params']['ruleIds'][i], "(", get_rule_status(ruleId), ")"
def get_rule_detail(ruleId):
params = {}
params['ruleId'] = ruleId
response = send_command("Rules.GetRuleDetails", params)
if 'rule' in response['params']:
return response['params']['rule']
return None
def list_rule_detail():
ruleId = select_rule()
if ruleId == "":
print "\n No rules found"
return None
params = {}
params['ruleId'] = ruleId
response = send_command("Rules.GetRuleDetails", params)
print response
rule = get_rule_detail(ruleId)
print "\nDetails for rule", ruleId, "which currently is", get_rule_status(ruleId)
print "\nEvents ->", get_stateEvaluator_text(response['params']['rule']['stateEvaluator']['operator']), ":"
for i in range(len(response['params']['rule']['eventDescriptors'])):
eventDescriptor = response['params']['rule']['eventDescriptors'][i]
print "\nEvents ->", get_stateEvaluator_text(rule['stateEvaluator']['operator']), ":"
for i in range(len(rule['eventDescriptors'])):
eventDescriptor = rule['eventDescriptors'][i]
device = get_device(eventDescriptor['deviceId'])
eventType = get_eventType(eventDescriptor['eventTypeId'])
paramDescriptors = eventDescriptor['paramDescriptors']
@ -690,11 +701,11 @@ def list_rule_detail():
print "%58s %s %s" %(paramDescriptors[i]['name'], get_valueOperator_symbol(paramDescriptors[i]['operator']), paramDescriptors[i]['value'])
print "\nActions:"
for i in range(len(response['params']['rule']['actions'])):
action = response['params']['rule']['actions'][i]
for i in range(len(rule['actions'])):
action = rule['actions'][i]
device = get_device(action['deviceId'])
actionType = get_actionType(response['params']['rule']['actions'][i]['actionTypeId'])
actionParams = response['params']['rule']['actions'][i]['params']
actionType = get_actionType(rule['actions'][i]['actionTypeId'])
actionParams = rule['actions'][i]['params']
print "%5s. -> %40s -> action: %s" %(i, device['name'], actionType['name'])
for i in range(len(actionParams)):
print "%61s: %s" %(actionParams[i]['name'], actionParams[i]['value'])
@ -761,7 +772,145 @@ def remove_rule():
response = send_command("Rules.RemoveRule", params)
print "removeRule response", response
def get_stateType(stateTypeId):
params = {}
params['stateTypeId'] = stateTypeId
response = send_command("States.GetStateType", params);
if "stateType" in response['params']:
return response['params']['stateType']
return None
def list_logEntries():
params = {}
response = send_command("Logging.GetLogEntries", params)
stateTypeIdCache = {}
actionTypeIdCache = {}
eventTypeIdCache = {}
deviceIdCache = {}
ruleIdCache = {}
for i in range(len(response['params']['logEntries'])):
entry = response['params']['logEntries'][i]
if entry['loggingLevel'] == "LoggingLevelInfo":
levelString = "(I)"
error = ""
else:
levelString = "(A)"
error = entry['errorCode']
if entry['source'] == "LoggingSourceSystem":
deviceName = "Guh Server"
sourceType = "System Event"
symbolString = "->"
sourceName = "Active changed"
if entry['active'] == True:
value = "active"
else:
value = "inactive"
if entry['source'] == "LoggingSourceStates":
typeId = entry['typeId']
sourceType = "State Changed"
symbolString = "->"
if typeId in stateTypeIdCache:
sourceName = stateTypeIdCache[typeId]
else:
stateType = get_stateType(typeId)
if stateType is not None:
sourceName = stateType["name"]
stateTypeIdCache[typeId] = sourceName
else:
sourceName = typeId
value = entry['value']
if entry['deviceId'] in deviceIdCache:
deviceName = deviceIdCache[entry['deviceId']]
else:
device = get_device(entry['deviceId'])
if device is not None:
deviceName = device['name']
deviceIdCache[entry['deviceId']] = deviceName
else:
deviceName = typeId
if entry['source'] == "LoggingSourceActions":
typeId = entry['typeId']
sourceType = "Action executed"
symbolString = "()"
if typeId in actionTypeIdCache:
sourceName = actionTypeIdCache[typeId]
else:
actionType = get_actionType(typeId)
if actionType is not None:
sourceName = actionType['name']
else:
sourceName = typeId
actionTypeIdCache[typeId] = sourceName
value = entry['value']
if entry['deviceId'] in deviceIdCache:
deviceName = deviceIdCache[entry['deviceId']]
else:
device = get_device(entry['deviceId'])
if device is not None:
deviceName = device['name']
else:
deviceName = entry['deviceId']
deviceIdCache[entry['deviceId']] = deviceName
if entry['source'] == "LoggingSourceEvents":
typeId = entry['typeId']
sourceType = "Event triggered"
symbolString = "()"
if typeId in eventTypeIdCache:
sourceName = eventTypeIdCache[typeId]
else:
eventType = get_eventType(typeId)
sourceName = eventType['name']
eventTypeIdCache[typeId] = sourceName
value = entry['value']
if entry['deviceId'] in deviceIdCache:
deviceName = deviceIdCache[entry['deviceId']]
else:
device = get_device(entry['deviceId'])
if device is not None:
deviceName = device['name']
else:
devieName = entry['deviceId']
deviceIdCache[entry['deviceId']] = deviceName
if entry['source'] == "LoggingSourceRules":
typeId = entry['typeId']
if entry['eventType'] == "LoggingEventTypeTrigger":
sourceType = "Rule triggered"
sourceName = "triggered"
symbolString = "()"
value = ""
else:
sourceType = "Rule active changed"
symbolString = "()"
sourceName = "active"
if entry['active']:
value = "active"
else:
value = "inactive"
if typeId in ruleIdCache:
deviceName = ruleIdCache[typeId]
else:
rule = get_rule_detail(typeId)
if rule is not None and 'name' in rule:
deviceName = rule['name']
else:
deviceName = typeId
ruleIdCache[typeId] = deviceName
timestamp = datetime.datetime.fromtimestamp(entry['timestamp']/1000)
sourceType = sourceType.ljust(20)
deviceName = deviceName.ljust(38)
sourceName = sourceName.ljust(38)
value = value.ljust(30)
print levelString, timestamp, ":", sourceType, ":", deviceName, ":", sourceName, symbolString, value, ":", error
############################################################################################
import sys