From 02c8e6e4b4aaf51d6da0f2dada00023c0d69ea5b Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 20 Jan 2014 01:13:43 +0100 Subject: [PATCH] reworked jsonrpc server. added introspection all calls and responses have now param validation --- libhive/action.cpp | 20 +- libhive/action.h | 10 +- libhive/devicemanager.cpp | 14 + libhive/devicemanager.h | 1 + plugins/deviceplugins/deviceplugins.pro | 2 +- server/jsonrpc/actionhandler.cpp | 62 ++++ server/jsonrpc/actionhandler.h | 17 + server/jsonrpc/devicehandler.cpp | 184 ++++++++++ server/jsonrpc/devicehandler.h | 30 ++ server/jsonrpc/jsonhandler.cpp | 81 +++++ server/jsonrpc/jsonhandler.h | 34 ++ server/jsonrpc/jsonrpcserver.cpp | 128 +++++++ server/{ => jsonrpc}/jsonrpcserver.h | 11 +- server/jsonrpc/jsontypes.cpp | 449 ++++++++++++++++++++++++ server/jsonrpc/jsontypes.h | 67 ++++ server/jsonrpc/ruleshandler.cpp | 99 ++++++ server/jsonrpc/ruleshandler.h | 20 ++ server/jsonrpcserver.cpp | 299 ---------------- server/ruleengine.cpp | 4 +- server/server.pro | 30 +- tests/addconfigureddevice.sh | 10 +- tests/addrule.sh | 8 +- tests/executeaction.sh | 6 +- tests/getactiontypes.sh | 7 + tests/gettriggertypes.sh | 7 + tests/introspect.sh | 7 + 26 files changed, 1252 insertions(+), 355 deletions(-) create mode 100644 server/jsonrpc/actionhandler.cpp create mode 100644 server/jsonrpc/actionhandler.h create mode 100644 server/jsonrpc/devicehandler.cpp create mode 100644 server/jsonrpc/devicehandler.h create mode 100644 server/jsonrpc/jsonhandler.cpp create mode 100644 server/jsonrpc/jsonhandler.h create mode 100644 server/jsonrpc/jsonrpcserver.cpp rename server/{ => jsonrpc}/jsonrpcserver.h (57%) create mode 100644 server/jsonrpc/jsontypes.cpp create mode 100644 server/jsonrpc/jsontypes.h create mode 100644 server/jsonrpc/ruleshandler.cpp create mode 100644 server/jsonrpc/ruleshandler.h delete mode 100644 server/jsonrpcserver.cpp create mode 100755 tests/getactiontypes.sh create mode 100755 tests/gettriggertypes.sh create mode 100755 tests/introspect.sh diff --git a/libhive/action.cpp b/libhive/action.cpp index f8f95c81..048ed9e2 100644 --- a/libhive/action.cpp +++ b/libhive/action.cpp @@ -1,19 +1,19 @@ #include "action.h" -Action::Action(const QUuid &deviceId, const QUuid &id) : - m_id(id), +Action::Action(const QUuid &deviceId, const QUuid &actionTypeId) : + m_actionTypeId(actionTypeId), m_deviceId(deviceId) { } bool Action::isValid() const { - return !m_id.isNull(); + return !m_actionTypeId.isNull() && !m_deviceId.isNull(); } -QUuid Action::id() const +QUuid Action::actionTypeId() const { - return m_id; + return m_actionTypeId; } QUuid Action::deviceId() const @@ -21,16 +21,6 @@ QUuid Action::deviceId() const return m_deviceId; } -QString Action::name() const -{ - return m_name; -} - -void Action::setName(const QString &name) -{ - m_name = name; -} - QVariantMap Action::params() const { return m_params; diff --git a/libhive/action.h b/libhive/action.h index 7edbc210..a6a9d9ef 100644 --- a/libhive/action.h +++ b/libhive/action.h @@ -7,23 +7,19 @@ class Action { public: - explicit Action(const QUuid &deviceId, const QUuid &id = QUuid::createUuid()); + explicit Action(const QUuid &deviceId, const QUuid &actionTypeId); bool isValid() const; - QUuid id() const; + QUuid actionTypeId() const; QUuid deviceId() const; - QString name() const; - void setName(const QString &name); - QVariantMap params() const; void setParams(const QVariantMap ¶ms); private: - QUuid m_id; + QUuid m_actionTypeId; QUuid m_deviceId; - QString m_name; QVariantMap m_params; }; diff --git a/libhive/devicemanager.cpp b/libhive/devicemanager.cpp index 5cfe6766..cd8af0e3 100644 --- a/libhive/devicemanager.cpp +++ b/libhive/devicemanager.cpp @@ -132,6 +132,20 @@ DeviceManager::DeviceError DeviceManager::executeAction(const Action &action) { foreach (Device *device, m_configuredDevices) { if (action.deviceId() == device->id()) { + // found device + + // Make sure this device has an action type with this id + DeviceClass deviceClass = findDeviceClass(device->deviceClassId()); + bool found = false; + foreach (const ActionType &actionType, deviceClass.actions()) { + if (actionType.id() == action.actionTypeId()) { + found = true; + } + } + if (!found) { + return DeviceErrorActionTypeNotFound; + } + m_devicePlugins.value(device->pluginId())->executeAction(device, action); return DeviceErrorNoError; } diff --git a/libhive/devicemanager.h b/libhive/devicemanager.h index 34484e30..b0d490cb 100644 --- a/libhive/devicemanager.h +++ b/libhive/devicemanager.h @@ -28,6 +28,7 @@ public: DeviceErrorNoError, DeviceErrorDeviceNotFound, DeviceErrorDeviceClassNotFound, + DeviceErrorActionTypeNotFound, DeviceErrorMissingParameter, DeviceErrorPluginNotFound, DeviceErrorSetupFailed diff --git a/plugins/deviceplugins/deviceplugins.pro b/plugins/deviceplugins/deviceplugins.pro index 489e1875..3eae814f 100644 --- a/plugins/deviceplugins/deviceplugins.pro +++ b/plugins/deviceplugins/deviceplugins.pro @@ -3,4 +3,4 @@ SUBDIRS += elro \ intertechno \ meisteranker \ wifidetector \ - +# boblight \ diff --git a/server/jsonrpc/actionhandler.cpp b/server/jsonrpc/actionhandler.cpp new file mode 100644 index 00000000..77532d15 --- /dev/null +++ b/server/jsonrpc/actionhandler.cpp @@ -0,0 +1,62 @@ +#include "actionhandler.h" + +#include "devicemanager.h" +#include "hivecore.h" +#include "action.h" + +#include + +ActionHandler::ActionHandler(QObject *parent) : + JsonHandler(parent) +{ + QVariantMap params; + QVariantMap returns; + + params.clear(); returns.clear(); + setDescription("ExecuteAction", "Execute a single action."); + params.insert("actionTypeId", "uuid"); + params.insert("deviceId", "uuid"); + params.insert("params", JsonTypes::paramTypeRef()); + setParams("ExecuteAction", JsonTypes::actionDescription()); + returns.insert("success", "bool"); + returns.insert("errorMessage", "string"); + setReturns("ExecuteAction", returns); +} + +QString ActionHandler::name() const +{ + return "Actions"; +} + +QVariantMap ActionHandler::ExecuteAction(const QVariantMap ¶ms) +{ + + QUuid deviceId = params.value("deviceId").toUuid(); + QUuid actionTypeId = params.value("actionTypeId").toUuid(); + QVariantMap actionParams = params.value("params").toMap(); + + Action action(deviceId, actionTypeId); + action.setParams(actionParams); + + qDebug() << "actions params in json" << action.params() << params; + + + QVariantMap returns; + DeviceManager::DeviceError error = HiveCore::instance()->deviceManager()->executeAction(action); + + switch (error) { + case DeviceManager::DeviceErrorNoError: + returns.insert("success", true); + returns.insert("errorMessage", ""); + break; + case DeviceManager::DeviceErrorDeviceNotFound: + returns.insert("errorMessage", "No such device"); + returns.insert("success", false); + break; + default: + returns.insert("errorMessage", "Unknown error."); + returns.insert("success", false); + } + + return returns; +} diff --git a/server/jsonrpc/actionhandler.h b/server/jsonrpc/actionhandler.h new file mode 100644 index 00000000..86529f81 --- /dev/null +++ b/server/jsonrpc/actionhandler.h @@ -0,0 +1,17 @@ +#ifndef ACTIONHANDLER_H +#define ACTIONHANDLER_H + +#include "jsonhandler.h" + +class ActionHandler : public JsonHandler +{ + Q_OBJECT +public: + explicit ActionHandler(QObject *parent = 0); + + QString name() const; + + Q_INVOKABLE QVariantMap ExecuteAction(const QVariantMap ¶ms); +}; + +#endif // ACTIONHANDLER_H diff --git a/server/jsonrpc/devicehandler.cpp b/server/jsonrpc/devicehandler.cpp new file mode 100644 index 00000000..a0822034 --- /dev/null +++ b/server/jsonrpc/devicehandler.cpp @@ -0,0 +1,184 @@ +#include "devicehandler.h" + +#include "deviceclass.h" +#include "hivecore.h" +#include "devicemanager.h" +#include "deviceplugin.h" + +DeviceHandler::DeviceHandler(QObject *parent) : + JsonHandler(parent) +{ + QVariantMap returns; + QVariantMap params; + + params.clear(); returns.clear(); + setDescription("GetSupportedDevices", "Returns a list of supported Device classes."); + setParams("GetSupportedDevices", params); + QVariantList deviceClasses; + deviceClasses.append(JsonTypes::deviceClassRef()); + returns.insert("deviceClasses", deviceClasses); + setReturns("GetSupportedDevices", returns); + + + params.clear(); returns.clear(); + setDescription("GetPlugins", "Returns a list of loaded plugins."); + setParams("GetPlugins", params); + QVariantList plugins; + plugins.append(JsonTypes::pluginTypeRef()); + returns.insert("plugins", plugins); + setReturns("GetPlugins", returns); + + params.clear(); returns.clear(); + setDescription("SetPluginParams", "Set a plugin's params."); + params.insert("pluginId", "uuid"); + QVariantList pluginParams; + pluginParams.append(JsonTypes::paramTypeRef()); + params.insert("pluginParams", pluginParams); + setParams("SetPluginParams", params); + setReturns("SetPluginParams", returns); + + params.clear(); returns.clear(); + setDescription("AddConfiguredDevice", "Add a configured device."); + params.insert("deviceClassId", "uuid"); + QVariantList deviceParams; + deviceParams.append(JsonTypes::paramRef()); + params.insert("deviceParams", deviceParams); + setParams("AddConfiguredDevice", params); + returns.insert("success", "bool"); + returns.insert("errorMessage", "string"); + setReturns("AddConfiguredDevice", returns); + + params.clear(); returns.clear(); + setDescription("GetConfiguredDevices", "Returns a list of configured devices."); + setParams("GetConfiguredDevices", params); + QVariantList devices; + devices.append(JsonTypes::deviceRef()); + returns.insert("devices", devices); + setReturns("GetConfiguredDevices", returns); + + params.clear(); returns.clear(); + setDescription("GetTriggerTypes", "Get trigger types for a specified deviceClassId."); + params.insert("deviceClassId", "uuid"); + setParams("GetTriggerTypes", params); + QVariantList triggers; + triggers.append(JsonTypes::triggerTypeRef()); + returns.insert("triggerTypes", triggers); + setReturns("GetTriggerTypes", returns); + + params.clear(); returns.clear(); + setDescription("GetActionTypes", "Get action types for a specified deviceClassId."); + params.insert("deviceClassId", "uuid"); + setParams("GetActionTypes", params); + QVariantList actions; + actions.append(JsonTypes::actionTypeRef()); + returns.insert("actionTypes", actions); + setReturns("GetActionTypes", returns); +} + +QString DeviceHandler::name() const +{ + return "Devices"; +} + +QVariantMap DeviceHandler::GetSupportedDevices(const QVariantMap ¶ms) const +{ + Q_UNUSED(params) + QVariantMap returns; + QVariantList supportedDeviceList; + foreach (const DeviceClass &deviceClass, HiveCore::instance()->deviceManager()->supportedDevices()) { + supportedDeviceList.append(JsonTypes::packDeviceClass(deviceClass)); + } + returns.insert("deviceClasses", supportedDeviceList); + return returns; +} + +QVariantMap DeviceHandler::GetPlugins(const QVariantMap ¶ms) const +{ + Q_UNUSED(params) + QVariantMap returns; + QVariantList plugins; + foreach (DevicePlugin *plugin, HiveCore::instance()->deviceManager()->plugins()) { + QVariantMap pluginMap; + pluginMap.insert("id", plugin->pluginId()); + pluginMap.insert("name", plugin->pluginName()); + pluginMap.insert("params", plugin->configuration()); + plugins.append(pluginMap); + } + returns.insert("plugins", plugins); + return returns; +} + +QVariantMap DeviceHandler::SetPluginParams(const QVariantMap ¶ms) +{ + QUuid pluginId = params.value("pluginId").toUuid(); + QVariantMap pluginParams = params.value("pluginParams").toMap(); + HiveCore::instance()->deviceManager()->plugin(pluginId)->setConfiguration(pluginParams); + return QVariantMap(); +} + +QVariantMap DeviceHandler::AddConfiguredDevice(const QVariantMap ¶ms) +{ + QUuid deviceClass = params.value("deviceClassId").toUuid(); + QVariantMap deviceParams = params.value("deviceParams").toMap(); + DeviceManager::DeviceError status = HiveCore::instance()->deviceManager()->addConfiguredDevice(deviceClass, deviceParams); + QVariantMap returns; + switch(status) { + case DeviceManager::DeviceErrorNoError: + returns.insert("success", true); + break; + case DeviceManager::DeviceErrorDeviceClassNotFound: + returns.insert("errorMessage", "Error creating device. Device class not found."); + returns.insert("success", false); + break; + case DeviceManager::DeviceErrorMissingParameter: + returns.insert("errorMessage", "Error creating device. Missing parameter."); + returns.insert("success", false); + break; + case DeviceManager::DeviceErrorSetupFailed: + returns.insert("errorMessage", "Error creating device. Device setup failed."); + returns.insert("success", false); + break; + default: + returns.insert("errorMessage", "Unknown error."); + returns.insert("success", false); + } + return returns; +} + +QVariantMap DeviceHandler::GetConfiguredDevices(const QVariantMap ¶ms) const +{ + Q_UNUSED(params) + QVariantMap returns; + QVariantList configuredDeviceList; + foreach (Device *device, HiveCore::instance()->deviceManager()->configuredDevices()) { + configuredDeviceList.append(JsonTypes::packDevice(device)); + } + returns.insert("devices", configuredDeviceList); + return returns; +} + +QVariantMap DeviceHandler::GetTriggerTypes(const QVariantMap ¶ms) const +{ + QVariantMap returns; + + QVariantList triggerList; + DeviceClass deviceClass = HiveCore::instance()->deviceManager()->findDeviceClass(params.value("deviceClassId").toUuid()); + foreach (const TriggerType &triggerType, deviceClass.triggers()) { + triggerList.append(JsonTypes::packTriggerType(triggerType)); + } + returns.insert("triggerTypes", triggerList); + return returns; +} + +QVariantMap DeviceHandler::GetActionTypes(const QVariantMap ¶ms) const +{ + QVariantMap returns; + + QVariantList actionList; + DeviceClass deviceClass = HiveCore::instance()->deviceManager()->findDeviceClass(params.value("deviceClassId").toUuid()); + foreach (const ActionType &actionType, deviceClass.actions()) { + actionList.append(JsonTypes::packActionType(actionType)); + } + returns.insert("actionTypes", actionList); + return returns; +} diff --git a/server/jsonrpc/devicehandler.h b/server/jsonrpc/devicehandler.h new file mode 100644 index 00000000..8f220260 --- /dev/null +++ b/server/jsonrpc/devicehandler.h @@ -0,0 +1,30 @@ +#ifndef DEVICEHANDLER_H +#define DEVICEHANDLER_H + +#include "jsonhandler.h" + +class DeviceHandler : public JsonHandler +{ + Q_OBJECT +public: + explicit DeviceHandler(QObject *parent = 0); + + QString name() const override; + + Q_INVOKABLE QVariantMap GetSupportedDevices(const QVariantMap ¶ms) const; + + Q_INVOKABLE QVariantMap GetPlugins(const QVariantMap ¶ms) const; + + Q_INVOKABLE QVariantMap SetPluginParams(const QVariantMap ¶ms); + + Q_INVOKABLE QVariantMap AddConfiguredDevice(const QVariantMap ¶ms); + + Q_INVOKABLE QVariantMap GetConfiguredDevices(const QVariantMap ¶ms) const; + + Q_INVOKABLE QVariantMap GetTriggerTypes(const QVariantMap ¶ms) const; + + Q_INVOKABLE QVariantMap GetActionTypes(const QVariantMap ¶ms) const; + +}; + +#endif // DEVICEHANDLER_H diff --git a/server/jsonrpc/jsonhandler.cpp b/server/jsonrpc/jsonhandler.cpp new file mode 100644 index 00000000..8a339329 --- /dev/null +++ b/server/jsonrpc/jsonhandler.cpp @@ -0,0 +1,81 @@ +#include "jsonhandler.h" + +#include +#include + +JsonHandler::JsonHandler(QObject *parent) : + QObject(parent) +{ +} + +QVariantMap JsonHandler::introspect() +{ + QVariantMap data; + for (int i = 0; i < metaObject()->methodCount(); ++i) { + QMetaMethod method = metaObject()->method(i); + if (method.methodType() == QMetaMethod::Method) { + QVariantMap methodData; + if (!m_descriptions.contains(method.name()) || !m_params.contains(method.name()) || !m_returns.contains(method.name())) { + continue; + } + methodData.insert("description", m_descriptions.value(method.name())); + methodData.insert("params", m_params.value(method.name())); + methodData.insert("returns", m_returns.value(method.name())); + data.insert(name() + "." + method.name(), methodData); + } + } + return data; +} + +bool JsonHandler::hasMethod(const QString &methodName) +{ + return m_descriptions.contains(methodName) && m_params.contains(methodName) && m_returns.contains(methodName); +} + +bool JsonHandler::validateParams(const QString &methodName, const QVariantMap ¶ms) +{ + QVariantMap paramTemplate = m_params.value(methodName); + return JsonTypes::validateMap(paramTemplate, params); +} + +bool JsonHandler::validateReturns(const QString &methodName, const QVariantMap &returns) +{ + QVariantMap returnsTemplate = m_returns.value(methodName); + return JsonTypes::validateMap(returnsTemplate, returns); +} + +void JsonHandler::setDescription(const QString &methodName, const QString &description) +{ + for(int i = 0; i < metaObject()->methodCount(); ++i) { + QMetaMethod method = metaObject()->method(i); + if (method.name() == methodName) { + m_descriptions.insert(methodName, description); + return; + } + } + qWarning() << "Cannot set description. No such method:" << methodName; +} + +void JsonHandler::setParams(const QString &methodName, const QVariantMap ¶ms) +{ + for(int i = 0; i < metaObject()->methodCount(); ++i) { + QMetaMethod method = metaObject()->method(i); + if (method.name() == methodName) { + m_params.insert(methodName, params); + return; + } + } + qWarning() << "Cannot set params. No such method:" << methodName; +} + +void JsonHandler::setReturns(const QString &methodName, const QVariantMap &returns) +{ + for(int i = 0; i < metaObject()->methodCount(); ++i) { + QMetaMethod method = metaObject()->method(i); + if (method.name() == methodName) { + m_returns.insert(methodName, returns); + return; + } + } + qWarning() << "Cannot set returns. No such method:" << methodName; +} diff --git a/server/jsonrpc/jsonhandler.h b/server/jsonrpc/jsonhandler.h new file mode 100644 index 00000000..22ac7ee8 --- /dev/null +++ b/server/jsonrpc/jsonhandler.h @@ -0,0 +1,34 @@ +#ifndef JSONHANDLER_H +#define JSONHANDLER_H + +#include "jsontypes.h" + +#include +#include + +class JsonHandler : public QObject +{ + Q_OBJECT +public: + explicit JsonHandler(QObject *parent = 0); + + virtual QString name() const = 0; + + QVariantMap introspect(); + + bool hasMethod(const QString &methodName); + bool validateParams(const QString &methodName, const QVariantMap ¶ms); + bool validateReturns(const QString &methodName, const QVariantMap &returns); + +protected: + void setDescription(const QString &methodName, const QString &description); + void setParams(const QString &methodName, const QVariantMap ¶ms); + void setReturns(const QString &methodName, const QVariantMap &returns); + +private: + QHash m_descriptions; + QHash m_params; + QHash m_returns; +}; + +#endif // JSONHANDLER_H diff --git a/server/jsonrpc/jsonrpcserver.cpp b/server/jsonrpc/jsonrpcserver.cpp new file mode 100644 index 00000000..711ea5e4 --- /dev/null +++ b/server/jsonrpc/jsonrpcserver.cpp @@ -0,0 +1,128 @@ +#include "jsonrpcserver.h" +#include "jsontypes.h" + +#include "tcpserver.h" +#include "jsonhandler.h" + +#include "hivecore.h" +#include "devicemanager.h" +#include "deviceplugin.h" +#include "deviceclass.h" +#include "device.h" +#include "rule.h" +#include "ruleengine.h" + +#include "devicehandler.h" +#include "actionhandler.h" +#include "ruleshandler.h" + +#include +#include + +JsonRPCServer::JsonRPCServer(QObject *parent): + QObject(parent), + m_tcpServer(new TcpServer(this)) +{ + connect(m_tcpServer, &TcpServer::jsonDataAvailable, this, &JsonRPCServer::processData); + m_tcpServer->startServer(); + + + registerHandler(new DeviceHandler(this)); + registerHandler(new ActionHandler(this)); + registerHandler(new RulesHandler(this)); +} + +void JsonRPCServer::processData(int clientId, const QByteArray &jsonData) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error); + + if(error.error != QJsonParseError::NoError) { + qDebug() << "failed to parse data" << jsonData << ":" << error.errorString(); + } + qDebug() << "-------------------------\n" << jsonDoc.toJson(); + + QVariantMap message = jsonDoc.toVariant().toMap(); + + bool success; + int commandId = message.value("id").toInt(&success); + if (!success) { + qWarning() << "Error parsing command. Missing \"id\":" << jsonData; + return; + } + + QStringList commandList = message.value("method").toString().split('.'); + if (commandList.count() != 2) { + qWarning() << "Error parsing method.\nGot:" << message.value("method").toString() << "\nExpected: \"Namespace.method\""; + return; + } + + QString targetNamespace = commandList.first(); + QString method = commandList.last(); + QVariantMap params = message.value("params").toMap(); + + qDebug() << "got:" << targetNamespace << method << params; + emit commandReceived(targetNamespace, method, params); + + if (targetNamespace == "JSONRPC") { + if (method == "Introspect") { + QVariantMap data; + data.insert("types", JsonTypes::allTypes()); + QVariantMap methods; + foreach (JsonHandler *handler, m_handlers) { + qDebug() << "got handler" << handler->name() << handler->introspect(); + methods.unite(handler->introspect()); + } + data.insert("methods", methods); + sendResponse(clientId, commandId, data); + return; + } + } + + JsonHandler *handler = m_handlers.value(targetNamespace); + if (!handler) { + sendErrorResponse(clientId, commandId, "No such namespace"); + return; + } + if (!handler->hasMethod(method)) { + sendErrorResponse(clientId, commandId, "No such method"); + return; + } + if (!handler->validateParams(method, params)) { + sendErrorResponse(clientId, commandId, "Invalid params"); + return; + } + QVariantMap returns; + QMetaObject::invokeMethod(handler, method.toLatin1().data(), Q_RETURN_ARG(QVariantMap, returns), Q_ARG(QVariantMap, params)); + Q_ASSERT(handler->validateReturns(method, returns)); + sendResponse(clientId, commandId, returns); +} + +void JsonRPCServer::registerHandler(JsonHandler *handler) +{ + m_handlers.insert(handler->name(), handler); +} + +void JsonRPCServer::sendResponse(int clientId, int commandId, const QVariantMap ¶ms) +{ + QVariantMap rsp; + rsp.insert("id", commandId); + rsp.insert("status", "success"); + rsp.insert("params", params); + + QJsonDocument jsonDoc = QJsonDocument::fromVariant(rsp); + m_tcpServer->sendResponse(clientId, jsonDoc.toJson()); +} + +void JsonRPCServer::sendErrorResponse(int clientId, int commandId, const QString &error) +{ + QVariantMap rsp; + rsp.insert("id", commandId); + rsp.insert("status", "error"); + rsp.insert("error", error); + + QJsonDocument jsonDoc = QJsonDocument::fromVariant(rsp); + m_tcpServer->sendResponse(clientId, jsonDoc.toJson()); +} + + diff --git a/server/jsonrpcserver.h b/server/jsonrpc/jsonrpcserver.h similarity index 57% rename from server/jsonrpcserver.h rename to server/jsonrpc/jsonrpcserver.h index a2d58a8b..823a6118 100644 --- a/server/jsonrpcserver.h +++ b/server/jsonrpc/jsonrpcserver.h @@ -11,6 +11,7 @@ class TcpServer; class Device; +class JsonHandler; class JsonRPCServer: public QObject { @@ -25,20 +26,14 @@ private slots: void processData(int clientId, const QByteArray &jsonData); private: - void handleDevicesMessage(int clientId, int commandId, const QString &method, const QVariantMap ¶ms); - void handleRulesMessage(int clientId, int commandId, const QString &method, const QVariantMap ¶ms); - void handleActionMessage(int clientId, int commandId, const QString &method, const QVariantMap ¶ms); - - QVariantMap packDeviceClass(const DeviceClass &deviceClass); - QVariantMap packDevice(Device *device); - QVariantMap packTrigger(const Trigger &action); - QVariantMap packAction(const Action &action); + void registerHandler(JsonHandler *handler); void sendResponse(int clientId, int commandId, const QVariantMap ¶ms = QVariantMap()); void sendErrorResponse(int clientId, int commandId, const QString &error); private: TcpServer *m_tcpServer; + QHash m_handlers; }; #endif diff --git a/server/jsonrpc/jsontypes.cpp b/server/jsonrpc/jsontypes.cpp new file mode 100644 index 00000000..bad29913 --- /dev/null +++ b/server/jsonrpc/jsontypes.cpp @@ -0,0 +1,449 @@ +#include "jsontypes.h" + +#include "device.h" + +#include + +namespace JsonTypes { + +QVariantMap allTypes() +{ + QVariantMap allTypes; + allTypes.insert("BasicType", basicTypes()); + allTypes.insert("ParamType", paramTypeDescription()); + allTypes.insert("StateType", stateTypeDescription()); + allTypes.insert("TriggerType", triggerTypeDescription()); + allTypes.insert("ActionType", actionTypeDescription()); + allTypes.insert("DeviceClass", deviceClassDescription()); + allTypes.insert("PluginType", pluginTypeDescription()); + allTypes.insert("Param", paramDescription()); + allTypes.insert("Trigger", triggerDescription()); + allTypes.insert("Device", deviceDescription()); + allTypes.insert("Action", actionDescription()); + return allTypes; +} + +QString basicTypesRef() +{ + return "$ref:BasicType"; +} + +QVariantList basicTypes() +{ + QVariantList basicTypes; + basicTypes.append("uuid"); + basicTypes.append("string"); + basicTypes.append("integer"); + basicTypes.append("double"); + basicTypes.append("bool"); + return basicTypes; +} + +QString paramTypeRef() +{ + return "$ref:ParamType"; +} + +QVariantMap paramTypeDescription() +{ + QVariantMap paramType; + paramType.insert("name", "string"); + paramType.insert("type", basicTypesRef()); +// paramType.insert("default", "value"); +// paramType.insert("value", "value"); + return paramType; +} + +QString paramRef() +{ + return "$ref:Param"; +} + +QVariantMap paramDescription() +{ + QVariantMap param; + param.insert("name", "string"); + param.insert("value", basicTypesRef()); + return param; +} + +QString stateTypeRef() +{ + return "$ref:StateType"; +} + +QVariantMap stateTypeDescription() +{ + QVariantMap stateTypeDescription; + stateTypeDescription.insert("id", "uuid"); + stateTypeDescription.insert("name", "string"); + stateTypeDescription.insert("type", basicTypesRef()); +// stateTypeDescription.insert("default", "value"); + return stateTypeDescription; +} + +QString triggerTypeRef() +{ + return "$ref:TriggerType"; +} + +QVariantMap triggerTypeDescription() +{ + QVariantMap triggerTypeDescription; + triggerTypeDescription.insert("id", "uuid"); + triggerTypeDescription.insert("name", "string"); + QVariantList params; + params.append(paramTypeRef()); + triggerTypeDescription.insert("params", params); + return triggerTypeDescription; +} + +QVariantMap packTriggerType(const TriggerType &triggerType) +{ + QVariantMap variant; + variant.insert("id", triggerType.id()); + variant.insert("name", triggerType.name()); + variant.insert("params", triggerType.parameters()); + return variant; +} + +QString triggerRef() +{ + return "$ref:Trigger"; +} + +QVariantMap triggerDescription() +{ + QVariantMap triggerDescription; + triggerDescription.insert("triggerTypeId", "uuid"); + triggerDescription.insert("deviceId", "uuid"); + QVariantList params; + params.append(paramRef()); + triggerDescription.insert("params", params); + return triggerDescription; +} + +QVariantMap packTrigger(const Trigger &trigger) +{ + QVariantMap variant; + variant.insert("id", trigger.triggerTypeId()); + variant.insert("deviceId", trigger.deviceId()); + variant.insert("params", trigger.params()); + return variant; +} + +QString actionTypeRef() +{ + return "$ref:ActionType"; +} + +QVariantMap actionTypeDescription() +{ + QVariantMap actionTypeDescription; + actionTypeDescription.insert("id", "uuid"); + actionTypeDescription.insert("name", "string"); + QVariantList params; + params.append(paramTypeRef()); + actionTypeDescription.insert("params", params); + return actionTypeDescription; +} + +QVariantMap packActionType(const ActionType &actionType) +{ + QVariantMap variantMap; + variantMap.insert("id", actionType.id()); + variantMap.insert("name", actionType.name()); + variantMap.insert("params", actionType.parameters()); + return variantMap; +} + +QString actionRef() +{ + return "$ref:Action"; +} + +QVariantMap actionDescription() +{ + QVariantMap actionDescription; + actionDescription.insert("actionTypeId", "uuid"); + actionDescription.insert("deviceId", "uuid"); + QVariantList params; + params.append(paramRef()); + actionDescription.insert("params", params); + return actionDescription; +} + +QVariantMap packAction(const Action &action) +{ + QVariantMap variant; + variant.insert("actionTypeId", action.actionTypeId()); + variant.insert("deviceId", action.deviceId()); + variant.insert("params", action.params()); + return variant; +} + +QString deviceClassRef() +{ + return "$ref:DeviceClass"; +} + +QVariantMap deviceClassDescription() +{ + QVariantMap deviceClass; + deviceClass.insert("id", "uuid"); + deviceClass.insert("name", "string"); + QVariantList states; + states.append(stateTypeRef()); + deviceClass.insert("states", states); + QVariantList triggers; + triggers.append(triggerTypeRef()); + deviceClass.insert("triggers", triggers); + QVariantList actions; + actions.append(actionTypeRef()); + deviceClass.insert("actions", actions); + QVariantList params; + params.append(paramTypeRef()); + deviceClass.insert("params", params); + return deviceClass; +} + +QVariantMap packDeviceClass(const DeviceClass &deviceClass) +{ + QVariantMap variant; + variant.insert("name", deviceClass.name()); + variant.insert("id", deviceClass.id()); + QVariantList stateTypes; + foreach (const StateType &stateType, deviceClass.states()) { + QVariantMap stateMap; + stateMap.insert("id", stateType.id().toString()); + stateMap.insert("name", stateType.name()); + stateMap.insert("type", QVariant::typeToName(stateType.type())); + + stateTypes.append(stateMap); + } + QVariantList triggerTypes; + foreach (const TriggerType &triggerType, deviceClass.triggers()) { + QVariantMap triggerMap; + triggerMap.insert("id", triggerType.id().toString()); + triggerMap.insert("name", triggerType.name()); + triggerMap.insert("params", triggerType.parameters()); + + triggerTypes.append(triggerMap); + } + QVariantList actionTypes; + foreach (const ActionType &actionType, deviceClass.actions()) { + QVariantMap actionMap; + actionMap.insert("id", actionType.id().toString()); + actionMap.insert("name", actionType.name()); + actionMap.insert("params", actionType.parameters()); + + actionTypes.append(actionMap); + } + variant.insert("params", deviceClass.params()); + variant.insert("states", stateTypes); + variant.insert("triggers", triggerTypes); + variant.insert("actions", actionTypes); + return variant; +} + +QString pluginTypeRef() +{ + return "$ref:PluginType"; +} + +QVariantMap pluginTypeDescription() +{ + QVariantMap pluginDescription; + pluginDescription.insert("id", "uuid"); + pluginDescription.insert("name", "string"); + QVariantList params; + params.append(paramTypeRef()); + pluginDescription.insert("params", params); + return pluginDescription; +} + +QVariantMap packPlugin(DevicePlugin *plugin) +{ + +} + +QString deviceRef() +{ + return "$ref:Device"; +} + +QVariantMap deviceDescription() +{ + QVariantMap deviceDescription; + deviceDescription.insert("id", "uuid"); + deviceDescription.insert("deviceClassId", "uuid"); + deviceDescription.insert("name", "string"); + QVariantList params; + params.append(paramRef()); + deviceDescription.insert("params", params); + return deviceDescription; +} + +QVariantMap packDevice(Device *device) +{ + QVariantMap variant; + variant.insert("id", device->id()); + variant.insert("deviceClassId", device->deviceClassId()); + variant.insert("name", device->name()); + variant.insert("params", device->params()); + return variant; +} + +bool validateMap(const QVariantMap &templateMap, const QVariantMap &map) +{ + foreach (const QString &key, templateMap.keys()) { + if (!map.contains(key)) { + qDebug() << "missing key" << key << templateMap << map; + return false; + } + if (!validateVariant(templateMap.value(key), map.value(key))) { + qDebug() << "Object not matching template"; + return false; + } + } + return true; +} + +bool validateProperty(const QVariant &templateValue, const QVariant &value) +{ + if (templateValue == "uuid") { + return value.canConvert(QVariant::Uuid); + } + if (templateValue == "string") { + return value.canConvert(QVariant::String); + } + if (templateValue == "bool") { + return value.canConvert(QVariant::Bool); + } + qWarning() << "Unhandled property type!" << templateValue; + return false; +} + +bool validateList(const QVariantList &templateList, const QVariantList &list) +{ + Q_ASSERT(templateList.count() == 1); + QVariant entryTemplate = templateList.first(); + + qDebug() << "validating list" << templateList; + + for (int i = 0; i < list.count(); ++i) { + QVariant listEntry = list.at(i); + if (!validateVariant(entryTemplate, listEntry)) { + qDebug() << "List entry not matching template"; + return false; + } + } + return true; +} + +bool validateVariant(const QVariant &templateVariant, const QVariant &variant) +{ + switch(templateVariant.type()) { + case QVariant::String: + if (templateVariant.toString().startsWith("$ref:")) { + QString refName = templateVariant.toString(); + if (refName == JsonTypes::actionRef()) { + qDebug() << "validating action"; + if (!validateMap(actionDescription(), variant.toMap())) { + qDebug() << "Error validating action"; + return false; + } + } else if (refName == JsonTypes::triggerRef()) { + if (!validateMap(triggerDescription(), variant.toMap())) { + qDebug() << "trigger not valid"; + return false; + } + } else if (refName == deviceRef()) { + if (!validateMap(deviceDescription(), variant.toMap())) { + qDebug() << "device not valid"; + return false; + } + } else if (refName == deviceClassRef()) { + if (!validateMap(deviceClassDescription(), variant.toMap())) { + qDebug() << "device class not valid"; + return false; + } + } else if (refName == paramTypeRef()) { + if (!validateMap(paramTypeDescription(), variant.toMap())) { + qDebug() << "param types not matching"; + return false; + } + } else if (refName == actionTypeRef()) { + if (!validateMap(actionTypeDescription(), variant.toMap())) { + qDebug() << "action type not matching"; + return false; + } + } else if (refName == triggerTypeRef()) { + if (!validateMap(triggerTypeDescription(), variant.toMap())) { + qDebug() << "trigger type not matching"; + return false; + } + } else if (refName == stateTypeRef()) { + if (!validateMap(stateTypeDescription(), variant.toMap())) { + qDebug() << "state type not matching"; + return false; + } + } else if (refName == pluginTypeRef()) { + if (!validateMap(pluginTypeDescription(), variant.toMap())) { + qDebug() << "plugin type not matchint"; + return false; + } + } else if (refName == basicTypesRef()) { + if (!validateBasicType(variant)) { + qDebug() << "value not allowed in" << basicTypesRef(); + return false; + } + } else { + qDebug() << "unhandled ref:" << refName; + return false; + } + + } else { + if (!JsonTypes::validateProperty(templateVariant, variant)) { + qDebug() << "property not matching:" << templateVariant << "!=" << variant; + return false; + } + } + break; + case QVariant::Map: + if (!validateMap(templateVariant.toMap(), variant.toMap())) { + return false; + } + break; + case QVariant::List: + if (!validateList(templateVariant.toList(), variant.toList())) { + return false; + } + break; + default: + qDebug() << "unhandled value" << templateVariant; + return false; + } + return true; +} + +bool validateBasicType(const QVariant &variant) +{ + if (variant.canConvert(QVariant::Uuid)) { + return true; + } + if (variant.canConvert(QVariant::String)) { + return true; + } + if (variant.canConvert(QVariant::Int)) { + return true; + } + if (variant.canConvert(QVariant::Double)) { + return true; + } + if (variant.canConvert(QVariant::Bool)) { + return true; + } + return false; +} +} diff --git a/server/jsonrpc/jsontypes.h b/server/jsonrpc/jsontypes.h new file mode 100644 index 00000000..468ba6a4 --- /dev/null +++ b/server/jsonrpc/jsontypes.h @@ -0,0 +1,67 @@ +#ifndef JSONTYPES_H +#define JSONTYPES_H + +#include "deviceclass.h" +#include "trigger.h" +#include "action.h" +#include "actiontype.h" + +#include + +#include + +class DevicePlugin; +class Device; + +namespace JsonTypes +{ + QVariantMap allTypes(); + + QString basicTypesRef(); + QVariantList basicTypes(); + + QString paramTypeRef(); + QVariantMap paramTypeDescription(); + + QString paramRef(); + QVariantMap paramDescription(); + + QString stateTypeRef(); + QVariantMap stateTypeDescription(); + + QString triggerTypeRef(); + QVariantMap triggerTypeDescription(); + QVariantMap packTriggerType(const TriggerType &triggerType); + + QString triggerRef(); + QVariantMap triggerDescription(); + QVariantMap packTrigger(const Trigger &trigger); + + QString actionTypeRef(); + QVariantMap actionTypeDescription(); + QVariantMap packActionType(const ActionType &actionType); + + QString actionRef(); + QVariantMap actionDescription(); + QVariantMap packAction(const Action &action); + + QString deviceClassRef(); + QVariantMap deviceClassDescription(); + QVariantMap packDeviceClass(const DeviceClass &deviceClass); + + QString pluginTypeRef(); + QVariantMap pluginTypeDescription(); + QVariantMap packPlugin(DevicePlugin *plugin); + + QString deviceRef(); + QVariantMap deviceDescription(); + QVariantMap packDevice(Device *device); + + bool validateMap(const QVariantMap &templateMap, const QVariantMap &map); + bool validateProperty(const QVariant &templateValue, const QVariant &value); + bool validateList(const QVariantList &templateList, const QVariantList &list); + bool validateVariant(const QVariant &templateVariant, const QVariant &variant); + bool validateBasicType(const QVariant &variant); +} + +#endif // JSONTYPES_H diff --git a/server/jsonrpc/ruleshandler.cpp b/server/jsonrpc/ruleshandler.cpp new file mode 100644 index 00000000..83678a0b --- /dev/null +++ b/server/jsonrpc/ruleshandler.cpp @@ -0,0 +1,99 @@ +#include "ruleshandler.h" + +#include "hivecore.h" +#include "ruleengine.h" + +#include + +RulesHandler::RulesHandler(QObject *parent) : + JsonHandler(parent) +{ + QVariantMap params; + QVariantMap returns; + + params.clear(); returns.clear(); + setDescription("GetRules", "Get all configured rules"); + setParams("GetRules", params); + setReturns("GetRules", returns); + + params.clear(); returns.clear(); + setDescription("AddRule", "Add a rule"); + params.insert("trigger", JsonTypes::triggerRef()); + QVariantList actions; + actions.append(JsonTypes::actionRef()); + params.insert("actions", actions); + setParams("AddRule", params); + setReturns("AddRule", returns); + +} + +QString RulesHandler::name() const +{ + return "Rules"; +} + +QVariantMap RulesHandler::GetRules(const QVariantMap ¶ms) +{ + Q_UNUSED(params) + + QVariantList rulesList; + foreach (const Rule &rule, HiveCore::instance()->ruleEngine()->rules()) { + qDebug() << "got rule" << rule.id(); + QVariantMap ruleMap; + ruleMap.insert("id", rule.id()); + ruleMap.insert("trigger", JsonTypes::packTrigger(rule.trigger())); + QVariantList actionList; + foreach (const Action &action, rule.actions()) { + actionList.append(JsonTypes::packAction(action)); + } + + ruleMap.insert("actions", actionList); + rulesList.append(ruleMap); + } + QVariantMap returns; + returns.insert("rules", rulesList); + + return returns; +} + +QVariantMap RulesHandler::AddRule(const QVariantMap ¶ms) +{ + QVariantMap triggerMap = params.value("trigger").toMap(); + + QUuid triggerTypeId = triggerMap.value("triggerTypeId").toUuid(); + QUuid triggerDeviceId = triggerMap.value("deviceId").toUuid(); + QVariantMap triggerParams = triggerMap.value("params").toMap(); + Trigger trigger(triggerTypeId, triggerDeviceId, triggerParams); + + QList actions; + QVariantList actionList = params.value("actions").toList(); + qDebug() << "got action list" << actionList.count(); + foreach (const QVariant &actionVariant, actionList) { + QVariantMap actionMap = actionVariant.toMap(); + Action action(actionMap.value("deviceId").toUuid(), actionMap.value("actionTypeId").toUuid()); + action.setParams(actionMap.value("params").toMap()); + actions.append(action); + } + + QVariantMap returns; + if (actions.count() == 0) { + returns.insert("success", false); + returns.insert("errorMessage", "Missing parameter: \"actions\"."); + return returns; + } + + switch(HiveCore::instance()->ruleEngine()->addRule(trigger, actions)) { + case RuleEngine::RuleErrorNoError: + returns.insert("success", true); + break; + case RuleEngine::RuleErrorDeviceNotFound: + returns.insert("success", false); + returns.insert("errorMessage", "No such device."); + break; + case RuleEngine::RuleErrorTriggerTypeNotFound: + returns.insert("success", false); + returns.insert("errorMessage", "Device does not have such a trigger type."); + break; + } + return returns; +} diff --git a/server/jsonrpc/ruleshandler.h b/server/jsonrpc/ruleshandler.h new file mode 100644 index 00000000..2a440f0c --- /dev/null +++ b/server/jsonrpc/ruleshandler.h @@ -0,0 +1,20 @@ +#ifndef RULESHANDLER_H +#define RULESHANDLER_H + +#include "jsonhandler.h" + +class RulesHandler : public JsonHandler +{ + Q_OBJECT +public: + explicit RulesHandler(QObject *parent = 0); + + QString name() const override; + + Q_INVOKABLE QVariantMap GetRules(const QVariantMap ¶ms); + + Q_INVOKABLE QVariantMap AddRule(const QVariantMap ¶ms); + +}; + +#endif // RULESHANDLER_H diff --git a/server/jsonrpcserver.cpp b/server/jsonrpcserver.cpp deleted file mode 100644 index 8fd17a65..00000000 --- a/server/jsonrpcserver.cpp +++ /dev/null @@ -1,299 +0,0 @@ -#include "jsonrpcserver.h" - -#include "tcpserver.h" - -#include "hivecore.h" -#include "devicemanager.h" -#include "deviceplugin.h" -#include "deviceclass.h" -#include "device.h" -#include "rule.h" -#include "ruleengine.h" - -#include -#include - -JsonRPCServer::JsonRPCServer(QObject *parent): - QObject(parent), - m_tcpServer(new TcpServer(this)) -{ - connect(m_tcpServer, &TcpServer::jsonDataAvailable, this, &JsonRPCServer::processData); - m_tcpServer->startServer(); -} - -void JsonRPCServer::processData(int clientId, const QByteArray &jsonData) -{ - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error); - - if(error.error != QJsonParseError::NoError) { - qDebug() << "failed to parse data" << jsonData << ":" << error.errorString(); - } - qDebug() << "-------------------------\n" << jsonDoc.toJson(); - - QVariantMap message = jsonDoc.toVariant().toMap(); - - bool success; - int commandId = message.value("id").toInt(&success); - if (!success) { - qWarning() << "Error parsing command. Missing \"id\":" << jsonData; - return; - } - - QStringList commandList = message.value("method").toString().split('.'); - if (commandList.count() != 2) { - qWarning() << "Error parsing method.\nGot:" << message.value("method").toString() << "\nExpected: \"Namespace.method\""; - return; - } - - QString targetNamespace = commandList.first(); - QString method = commandList.last(); - QVariantMap params = message.value("params").toMap(); - - qDebug() << "got:" << targetNamespace << method << params; - emit commandReceived(targetNamespace, method, params); - - if (targetNamespace == "Devices") { - handleDevicesMessage(clientId, commandId, method, params); - } else if (targetNamespace == "Rules") { - handleRulesMessage(clientId, commandId, method, params); - } else if (targetNamespace == "Actions") { - handleActionMessage(clientId, commandId, method, params); - } else { - qDebug() << "got unknown namespace" << targetNamespace; - } -} - -void JsonRPCServer::handleDevicesMessage(int clientId, int commandId, const QString &method, const QVariantMap ¶ms) -{ - if (method == "GetSupportedDevices") { - QVariantMap params; - QVariantList supportedDeviceList; - foreach (const DeviceClass &deviceClass, HiveCore::instance()->deviceManager()->supportedDevices()) { - supportedDeviceList.append(packDeviceClass(deviceClass)); - } - params.insert("deviceClasses", supportedDeviceList); - sendResponse(clientId, commandId, params); - } else if (method == "GetPlugins") { - QVariantMap params; - QVariantList plugins; - foreach (DevicePlugin *plugin, HiveCore::instance()->deviceManager()->plugins()) { - QVariantMap pluginMap; - pluginMap.insert("id", plugin->pluginId()); - pluginMap.insert("name", plugin->pluginName()); - pluginMap.insert("config", plugin->configuration()); - plugins.append(pluginMap); - } - params.insert("plugins", plugins); - sendResponse(clientId, commandId, params); - } else if (method == "SetPluginConfig") { - QUuid pluginId = params.value("pluginId").toUuid(); - QVariantMap pluginParams = params.value("params").toMap(); - HiveCore::instance()->deviceManager()->plugin(pluginId)->setConfiguration(pluginParams); - sendResponse(clientId, commandId); - } else if (method == "AddConfiguredDevice") { - QUuid deviceClass = params.value("deviceClass").toUuid(); - QVariantMap deviceParams = params.value("params").toMap(); - DeviceManager::DeviceError status = HiveCore::instance()->deviceManager()->addConfiguredDevice(deviceClass, deviceParams); - switch(status) { - case DeviceManager::DeviceErrorNoError: - sendResponse(clientId, commandId); - break; - case DeviceManager::DeviceErrorDeviceClassNotFound: - sendErrorResponse(clientId, commandId, "Error creating device. Device class not found."); - break; - case DeviceManager::DeviceErrorMissingParameter: - sendErrorResponse(clientId, commandId, "Error creating device. Missing parameter."); - break; - case DeviceManager::DeviceErrorSetupFailed: - sendErrorResponse(clientId, commandId, "Error creating device. Device setup failed."); - break; - default: - sendErrorResponse(clientId, commandId, "Unknown error."); - } - } else if (method == "GetConfiguredDevices") { - QVariantMap rspParams; - QVariantList configuredDeviceList; - foreach (Device *device, HiveCore::instance()->deviceManager()->configuredDevices()) { - configuredDeviceList.append(packDevice(device)); - } - rspParams.insert("devices", configuredDeviceList); - sendResponse(clientId, commandId, rspParams); - } else { - sendErrorResponse(clientId, commandId, "No such method"); - } -} - -void JsonRPCServer::handleRulesMessage(int clientId, int commandId, const QString &method, const QVariantMap ¶ms) -{ - if (method == "GetRules") { - QVariantList rulesList; - foreach (const Rule &rule, HiveCore::instance()->ruleEngine()->rules()) { - QVariantMap ruleMap; - ruleMap.insert("id", rule.id()); - ruleMap.insert("trigger", packTrigger(rule.trigger())); - QVariantList actionList; - foreach (const Action &action, rule.actions()) { - actionList.append(packAction(action)); - } - - ruleMap.insert("actions", actionList); - rulesList.append(ruleMap); - } - QVariantMap rspParams; - rspParams.insert("rules", rulesList); - sendResponse(clientId, commandId, rspParams); - } else if (method == "AddRule") { - QVariantMap triggerMap = params.value("trigger").toMap(); - - QUuid triggerTypeId = triggerMap.value("triggerTypeId").toUuid(); - QUuid triggerDeviceId = triggerMap.value("deviceId").toUuid(); - QVariantMap triggerParams = triggerMap.value("params").toMap(); - Trigger trigger(triggerTypeId, triggerDeviceId, triggerParams); - - QList actions; - QVariantList actionList = params.value("actions").toList(); - foreach (const QVariant &actionVariant, actionList) { - QVariantMap actionMap = actionVariant.toMap(); - Action action(actionMap.value("deviceId").toString()); - action.setName(actionMap.value("name").toString()); - action.setParams(actionMap.value("params").toMap()); - actions.append(action); - } - if (actions.count() == 0) { - sendErrorResponse(clientId, commandId, "Missing parameter: \"actions\"."); - return; - } - - switch(HiveCore::instance()->ruleEngine()->addRule(trigger, actions)) { - case RuleEngine::RuleErrorNoError: - sendResponse(clientId, commandId); - break; - case RuleEngine::RuleErrorDeviceNotFound: - sendErrorResponse(clientId, commandId, "No such device."); - break; - case RuleEngine::RuleErrorTriggerTypeNotFound: - sendErrorResponse(clientId, commandId, "Device does not have such a trigger type."); - break; - } - } -} - -void JsonRPCServer::handleActionMessage(int clientId, int commandId, const QString &method, const QVariantMap ¶ms) -{ - if (method == "ExecuteAction") { - QVariantMap actionMap = params.value("action").toMap(); - QUuid deviceId = actionMap.value("deviceId").toUuid(); - QVariantMap actionParams = actionMap.value("params").toMap(); - - Action action(deviceId); - action.setParams(actionParams); - - qDebug() << "actions params in json" << action.params(); - - - DeviceManager::DeviceError error = HiveCore::instance()->deviceManager()->executeAction(action); - switch (error) { - case DeviceManager::DeviceErrorNoError: - sendResponse(clientId, commandId); - break; - case DeviceManager::DeviceErrorDeviceNotFound: - sendErrorResponse(clientId, commandId, "No such device"); - break; - default: - sendErrorResponse(clientId, commandId, "Unknown error."); - } - } -} - -QVariantMap JsonRPCServer::packDeviceClass(const DeviceClass &deviceClass) -{ - QVariantMap variant; - variant.insert("name", deviceClass.name()); - variant.insert("id", deviceClass.id()); - QVariantList stateTypes; - foreach (const StateType &stateType, deviceClass.states()) { - QVariantMap stateMap; - stateMap.insert("id", stateType.id().toString()); - stateMap.insert("name", stateType.name()); - stateMap.insert("type", QVariant::typeToName(stateType.type())); - - stateTypes.append(stateMap); - } - QVariantList triggerTypes; - foreach (const TriggerType &triggerType, deviceClass.triggers()) { - QVariantMap triggerMap; - triggerMap.insert("id", triggerType.id().toString()); - triggerMap.insert("name", triggerType.name()); - triggerMap.insert("params", triggerType.parameters()); - - triggerTypes.append(triggerMap); - } - QVariantList actionTypes; - foreach (const ActionType &actionType, deviceClass.actions()) { - QVariantMap actionMap; - actionMap.insert("id", actionType.id().toString()); - actionMap.insert("name", actionType.name()); - actionMap.insert("params", actionType.parameters()); - - actionTypes.append(actionMap); - } - variant.insert("params", deviceClass.params()); - variant.insert("states", stateTypes); - variant.insert("triggers", triggerTypes); - variant.insert("actions", actionTypes); - return variant; -} - -QVariantMap JsonRPCServer::packDevice(Device *device) -{ - QVariantMap variant; - variant.insert("id", device->id()); - variant.insert("deviceClassId", device->deviceClassId()); - variant.insert("name", device->name()); - variant.insert("params", device->params()); - return variant; -} - -QVariantMap JsonRPCServer::packTrigger(const Trigger &trigger) -{ - QVariantMap variant; - variant.insert("id", trigger.triggerTypeId()); - variant.insert("deviceId", trigger.deviceId()); - variant.insert("params", trigger.params()); - return variant; -} - -QVariantMap JsonRPCServer::packAction(const Action &action) -{ - QVariantMap variant; - variant.insert("id", action.id()); - variant.insert("deviceId", action.deviceId()); - variant.insert("name", action.name()); - variant.insert("params", action.params()); - return variant; -} - -void JsonRPCServer::sendResponse(int clientId, int commandId, const QVariantMap ¶ms) -{ - QVariantMap rsp; - rsp.insert("id", commandId); - rsp.insert("status", "success"); - rsp.insert("params", params); - - QJsonDocument jsonDoc = QJsonDocument::fromVariant(rsp); - m_tcpServer->sendResponse(clientId, jsonDoc.toJson()); -} - -void JsonRPCServer::sendErrorResponse(int clientId, int commandId, const QString &error) -{ - QVariantMap rsp; - rsp.insert("id", commandId); - rsp.insert("status", "error"); - rsp.insert("error", error); - - QJsonDocument jsonDoc = QJsonDocument::fromVariant(rsp); - m_tcpServer->sendResponse(clientId, jsonDoc.toJson()); -} - - diff --git a/server/ruleengine.cpp b/server/ruleengine.cpp index a7ffef8d..f2b50be2 100644 --- a/server/ruleengine.cpp +++ b/server/ruleengine.cpp @@ -40,7 +40,6 @@ RuleEngine::RuleEngine(QObject *parent) : foreach (const QString &actionIdString, settings.childGroups()) { settings.beginGroup(actionIdString); Action action = Action(settings.value("deviceId").toUuid(), settings.value("id").toUuid()); - action.setName(settings.value("name").toString()); action.setParams(settings.value("params").toMap()); settings.endGroup(); actions.append(action); @@ -136,9 +135,8 @@ RuleEngine::RuleError RuleEngine::addRule(const Trigger &trigger, const QList