diff --git a/libguh/devicemanager.cpp b/libguh/devicemanager.cpp index 4a3cbb45..14a3815a 100644 --- a/libguh/devicemanager.cpp +++ b/libguh/devicemanager.cpp @@ -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); } diff --git a/libguh/types/action.cpp b/libguh/types/action.cpp index b6494ced..8df0d25b 100644 --- a/libguh/types/action.cpp +++ b/libguh/types/action.cpp @@ -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 ¶mName) const } return Param(QString()); } + +void Action::operator =(const Action &other) +{ + m_id = other.id(); + m_actionTypeId = other.actionTypeId(); + m_params = other.params(); +} diff --git a/libguh/types/action.h b/libguh/types/action.h index 8c4a297c..0adcc410 100644 --- a/libguh/types/action.h +++ b/libguh/types/action.h @@ -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 ¶ms); Param param(const QString ¶mName) const; + void operator=(const Action &other); private: ActionId m_id; ActionTypeId m_actionTypeId; diff --git a/libguh/types/event.cpp b/libguh/types/event.cpp index f7251fc4..df216f52 100644 --- a/libguh/types/event.cpp +++ b/libguh/types/event.cpp @@ -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 ¶ms): +Event::Event(const EventTypeId &eventTypeId, const DeviceId &deviceId, const ParamList ¶ms, 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 ¶mName) 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 diff --git a/libguh/types/event.h b/libguh/types/event.h index d0ca8f36..7dda2ed0 100644 --- a/libguh/types/event.h +++ b/libguh/types/event.h @@ -30,7 +30,7 @@ class Event { public: Event(); - Event(const EventTypeId &eventTypeId, const DeviceId &deviceId, const ParamList ¶ms = ParamList()); + Event(const EventTypeId &eventTypeId, const DeviceId &deviceId, const ParamList ¶ms = 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); diff --git a/plugins/deviceplugins/elro/devicepluginelro.cpp b/plugins/deviceplugins/elro/devicepluginelro.cpp index bcd0816f..f4e58b7f 100644 --- a/plugins/deviceplugins/elro/devicepluginelro.cpp +++ b/plugins/deviceplugins/elro/devicepluginelro.cpp @@ -277,7 +277,6 @@ void DevicePluginElro::radioData(const QList &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; diff --git a/server/guhcore.cpp b/server/guhcore.cpp index 25026471..5846a9f5 100644 --- a/server/guhcore.cpp +++ b/server/guhcore.cpp @@ -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 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); +} diff --git a/server/guhcore.h b/server/guhcore.h index 49ada964..233c7cdc 100644 --- a/server/guhcore.h +++ b/server/guhcore.h @@ -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 m_pendingActions; private slots: void gotEvent(const Event &event); + void actionExecutionFinished(const ActionId &id, DeviceManager::DeviceError status); friend class GuhTestBase; }; diff --git a/server/jsonrpc/actionhandler.cpp b/server/jsonrpc/actionhandler.cpp index 628248fd..6038a995 100644 --- a/server/jsonrpc/actionhandler.cpp +++ b/server/jsonrpc/actionhandler.cpp @@ -74,6 +74,7 @@ JsonReply* ActionHandler::ExecuteAction(const QVariantMap ¶ms) JsonReply *ActionHandler::GetActionType(const QVariantMap ¶ms) 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()) { diff --git a/server/jsonrpc/jsonhandler.cpp b/server/jsonrpc/jsonhandler.cpp index f2cfa313..5f54174a 100644 --- a/server/jsonrpc/jsonhandler.cpp +++ b/server/jsonrpc/jsonhandler.cpp @@ -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), diff --git a/server/jsonrpc/jsonhandler.h b/server/jsonrpc/jsonhandler.h index d91104e4..75383f2c 100644 --- a/server/jsonrpc/jsonhandler.h +++ b/server/jsonrpc/jsonhandler.h @@ -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 m_descriptions; diff --git a/server/jsonrpc/jsonrpcserver.cpp b/server/jsonrpc/jsonrpcserver.cpp index dca17caf..1c24f809 100644 --- a/server/jsonrpc/jsonrpcserver.cpp +++ b/server/jsonrpc/jsonrpcserver.cpp @@ -38,11 +38,13 @@ #include "actionhandler.h" #include "ruleshandler.h" #include "eventhandler.h" +#include "logginghandler.h" +#include "statehandler.h" #include #include -#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) diff --git a/server/jsonrpc/jsontypes.cpp b/server/jsonrpc/jsontypes.cpp index 7042a757..7066872b 100644 --- a/server/jsonrpc/jsontypes.cpp +++ b/server/jsonrpc/jsontypes.cpp @@ -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 JsonTypes::validateVariant(const QVariant &templateVariant, qDebug() << "evendescriptor not matching"; return result; } + } else if (refName == logEntryRef()) { + QPair result = validateMap(logEntryDescription(), variant.toMap()); + if (!result.first) { + qDebug() << "logEntry not matching"; + return result; + } } else if (refName == basicTypeRef()) { QPair result = validateBasicType(variant); if (!result.first) { @@ -755,41 +833,65 @@ QPair JsonTypes::validateVariant(const QVariant &templateVariant, return result; } } else if (refName == stateOperatorRef()) { - QPair result = validateStateOperator(variant); + QPair result = validateEnum(s_stateOperator, variant); if (!result.first) { qDebug() << "value not allowed in" << stateOperatorRef(); return result; } } else if (refName == createMethodRef()) { - QPair result = validateCreateMethod(variant); + QPair result = validateEnum(s_createMethod, variant); if (!result.first) { qDebug() << "value not allowed in" << createMethodRef() << variant; return result; } } else if (refName == setupMethodRef()) { - QPair result = validateSetupMethod(variant); + QPair result = validateEnum(s_setupMethod, variant); if (!result.first) { qDebug() << "value not allowed in" << createMethodRef(); return result; } } else if (refName == valueOperatorRef()) { - QPair result = validateValueOperator(variant); + QPair 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 result = validateDeviceError(variant); + QPair 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 result = validateRuleError(variant); + QPair 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 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 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 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 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 JsonTypes::validateBasicType(const QVariant &variant) return report(false, QString("Error validating basic type %1.").arg(variant.toString())); } -QPair JsonTypes::validateStateOperator(const QVariant &variant) +QPair 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 JsonTypes::validateCreateMethod(const QVariant &variant) -{ - return report(s_createMethod.contains(variant.toString()), QString("Unknwon createMethod type %1").arg(variant.toString())); -} - -QPair JsonTypes::validateSetupMethod(const QVariant &variant) -{ - return report(s_setupMethod.contains(variant.toString()), QString("Unknwon SetupMethod: %1").arg(variant.toString())); -} - -QPair JsonTypes::validateValueOperator(const QVariant &variant) -{ - return report(s_valueOperator.contains(variant.toString()), QString("Unknown ValueOperator: %1").arg(variant.toString())); -} - -QPair JsonTypes::validateDeviceError(const QVariant &variant) -{ - return report(s_deviceError.contains(variant.toString()), QString("Unknown DeviceError: %1").arg(variant.toString())); -} - -QPair 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(", "))); } diff --git a/server/jsonrpc/jsontypes.h b/server/jsonrpc/jsontypes.h index 86a6306b..78bbce5d 100644 --- a/server/jsonrpc/jsontypes.h +++ b/server/jsonrpc/jsontypes.h @@ -31,6 +31,9 @@ #include "types/paramtype.h" #include "types/paramdescriptor.h" +#include "logging/logging.h" +#include "logging/logentry.h" + #include #include @@ -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 ¶mMap); @@ -144,13 +153,8 @@ public: static QPair validateProperty(const QVariant &templateValue, const QVariant &value); static QPair validateList(const QVariantList &templateList, const QVariantList &list); static QPair validateVariant(const QVariant &templateVariant, const QVariant &variant); + static QPair validateEnum(const QVariantList &enumList, const QVariant &value); static QPair validateBasicType(const QVariant &variant); - static QPair validateStateOperator(const QVariant &variant); - static QPair validateCreateMethod(const QVariant &variant); - static QPair validateSetupMethod(const QVariant &variant); - static QPair validateValueOperator(const QVariant &variant); - static QPair validateDeviceError(const QVariant &variant); - static QPair validateRuleError(const QVariant &variant); private: static bool s_initialized; diff --git a/server/jsonrpc/logginghandler.cpp b/server/jsonrpc/logginghandler.cpp new file mode 100644 index 00000000..6bb42355 --- /dev/null +++ b/server/jsonrpc/logginghandler.cpp @@ -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 . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#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 ¶ms) 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); +} diff --git a/server/jsonrpc/logginghandler.h b/server/jsonrpc/logginghandler.h new file mode 100644 index 00000000..0a9d2663 --- /dev/null +++ b/server/jsonrpc/logginghandler.h @@ -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 . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#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 ¶ms) const; +signals: + void LogEntryAdded(const QVariantMap ¶ms); + +private slots: + void logEntryAdded(const LogEntry &entry); +}; + +#endif // LOGGINGHANDLER_H diff --git a/server/jsonrpc/ruleshandler.cpp b/server/jsonrpc/ruleshandler.cpp index 83be6a74..c6597bca 100644 --- a/server/jsonrpc/ruleshandler.cpp +++ b/server/jsonrpc/ruleshandler.cpp @@ -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 ¶ms) { 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 ¶ms) @@ -147,7 +147,9 @@ JsonReply* RulesHandler::AddRule(const QVariantMap ¶ms) 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); } diff --git a/server/jsonrpc/statehandler.cpp b/server/jsonrpc/statehandler.cpp new file mode 100644 index 00000000..5242fa91 --- /dev/null +++ b/server/jsonrpc/statehandler.cpp @@ -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 . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#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 ¶ms) 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)); +} diff --git a/server/jsonrpc/statehandler.h b/server/jsonrpc/statehandler.h new file mode 100644 index 00000000..1185f0a8 --- /dev/null +++ b/server/jsonrpc/statehandler.h @@ -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 . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#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 ¶ms) const; +}; + +#endif // EVENTHANDLER_H diff --git a/server/logging/logengine.cpp b/server/logging/logengine.cpp new file mode 100644 index 00000000..c3b7727a --- /dev/null +++ b/server/logging/logengine.cpp @@ -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 . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "logengine.h" +#include "logging.h" + +#include +#include +#include +#include +#include + +#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 LogEngine::logEntries() const +{ + QList 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 ¶m, 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 ¶m, 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(); + } + } +} diff --git a/server/logging/logengine.h b/server/logging/logengine.h new file mode 100644 index 00000000..2b23cbfc --- /dev/null +++ b/server/logging/logengine.h @@ -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 . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef LOGENGINE_H +#define LOGENGINE_H + +#include "logentry.h" +#include "types/event.h" +#include "types/action.h" +#include "rule.h" + +#include +#include + +class LogEngine: public QObject +{ + Q_OBJECT +public: + LogEngine(QObject *parent = 0); + + QList 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 diff --git a/server/logging/logentry.cpp b/server/logging/logentry.cpp new file mode 100644 index 00000000..cd878c7e --- /dev/null +++ b/server/logging/logentry.cpp @@ -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 . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "logentry.h" + +#include + +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; +} diff --git a/server/logging/logentry.h b/server/logging/logentry.h new file mode 100644 index 00000000..35b469ce --- /dev/null +++ b/server/logging/logentry.h @@ -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 . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef LOGENTRY_H +#define LOGENTRY_H + +#include "logging.h" +#include "typeutils.h" + +#include +#include + +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 diff --git a/server/logging/logfilter.cpp b/server/logging/logfilter.cpp new file mode 100644 index 00000000..e69de29b diff --git a/server/logging/logfilter.h b/server/logging/logfilter.h new file mode 100644 index 00000000..2a3566dd --- /dev/null +++ b/server/logging/logfilter.h @@ -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 . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef LOGFILTER_H +#define LOGFILTER_H + +#include + +class LogFilter +{ + QDateTime m_startDate; + QDateTime m_endDate; + +}; + +#endif diff --git a/server/logging/logging.h b/server/logging/logging.h new file mode 100644 index 00000000..9827257d --- /dev/null +++ b/server/logging/logging.h @@ -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 . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef LOGGING_H +#define LOGGING_H + +#include + +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 diff --git a/server/logging/logsource.cpp b/server/logging/logsource.cpp new file mode 100644 index 00000000..e69de29b diff --git a/server/logging/logsource.h b/server/logging/logsource.h new file mode 100644 index 00000000..e69de29b diff --git a/server/rule.cpp b/server/rule.cpp index 25f837af..f5fc1d7d 100644 --- a/server/rule.cpp +++ b/server/rule.cpp @@ -51,9 +51,9 @@ #include /*! Constructs an empty, invalid rule. */ -Rule::Rule() +Rule::Rule(): + Rule(RuleId(), QList(), StateEvaluator(), QList()) { - } /*! 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 &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; +} diff --git a/server/rule.h b/server/rule.h index e3c7ca3d..3d31baf4 100644 --- a/server/rule.h +++ b/server/rule.h @@ -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 m_eventDescriptors; @@ -47,6 +53,7 @@ private: QList m_actions; bool m_enabled; + bool m_active; }; #endif // RULE_H diff --git a/server/ruleengine.cpp b/server/ruleengine.cpp index 5d33518c..664b0061 100644 --- a/server/ruleengine.cpp +++ b/server/ruleengine.cpp @@ -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 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 RuleEngine::evaluateEvent(const Event &event) { Device *device = GuhCore::instance()->findConfiguredDevice(event.deviceId()); qDebug() << "got event:" << event << device->name() << event.eventTypeId(); - QList actions; + QList rules; foreach (const RuleId &id, m_ruleIds) { Rule rule = m_rules.value(id); if (!rule.enabled()) { @@ -182,11 +183,14 @@ QList 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 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 0) { + qDebug() << "***** actions" << actions.last().actionTypeId() << actions.last().params(); + } Rule rule = Rule(ruleId, eventDescriptorList, stateEvaluator, actions); rule.setEnabled(enabled); appendRule(rule); diff --git a/server/ruleengine.h b/server/ruleengine.h index 7ff2d18c..da86b25a 100644 --- a/server/ruleengine.h +++ b/server/ruleengine.h @@ -51,7 +51,7 @@ public: explicit RuleEngine(QObject *parent = 0); - QList evaluateEvent(const Event &event); + QList evaluateEvent(const Event &event); RuleError addRule(const RuleId &ruleId, const QList &eventDescriptorList, const QList &actions, bool enabled = true); RuleError addRule(const RuleId &ruleId, const QList &eventDescriptorList, const StateEvaluator &stateEvaluator, const QList &actions, bool enabled = true); diff --git a/server/server.pri b/server/server.pri index daed4e18..404e113c 100644 --- a/server/server.pri +++ b/server/server.pri @@ -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 \ diff --git a/server/server.pro b/server/server.pro index 87f7df11..1f89be28 100644 --- a/server/server.pro +++ b/server/server.pro @@ -10,7 +10,7 @@ INCLUDEPATH += ../libguh jsonrpc target.path = /usr/bin INSTALLS += target -QT += network +QT += network sql LIBS += -L$$top_builddir/libguh/ -lguh diff --git a/tests/auto/api.json b/tests/auto/api.json index 4c741509..97bd1ebf 100644 --- a/tests/auto/api.json +++ b/tests/auto/api.json @@ -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" diff --git a/tests/auto/autotests.pri b/tests/auto/autotests.pri index d8f05f36..c9dc941b 100644 --- a/tests/auto/autotests.pri +++ b/tests/auto/autotests.pri @@ -1,4 +1,4 @@ -QT += testlib network +QT += testlib network sql CONFIG += testcase include($$top_srcdir/server/server.pri) diff --git a/tests/auto/jsonrpc/testjsonrpc.cpp b/tests/auto/jsonrpc/testjsonrpc.cpp index 6a05c95a..d68c0a99 100644 --- a/tests/auto/jsonrpc/testjsonrpc.cpp +++ b/tests/auto/jsonrpc/testjsonrpc.cpp @@ -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); diff --git a/tests/auto/rules/testrules.cpp b/tests/auto/rules/testrules.cpp index 8a7b9a2e..12625893 100644 --- a/tests/auto/rules/testrules.cpp +++ b/tests/auto/rules/testrules.cpp @@ -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."); diff --git a/tests/scripts/guh-cli.py b/tests/scripts/guh-cli.py index 5cea76ea..8986cc9a 100755 --- a/tests/scripts/guh-cli.py +++ b/tests/scripts/guh-cli.py @@ -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