From d84319fdcc3ac6f4c3f0f674dc7e87e68a6161c0 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 4 May 2014 23:59:08 +0200 Subject: [PATCH] Finish off param handling. New param structures now used everywhere. Plugin params reworked. Added Devices.GetPluginConfiguration. Cover params stuff with tests. --- debian/changelog | 6 + libguh/devicemanager.cpp | 178 ++++++++++++------ libguh/devicemanager.h | 10 +- libguh/plugin/deviceplugin.cpp | 84 ++++++++- libguh/plugin/deviceplugin.h | 12 +- libguh/types/param.cpp | 4 +- libguh/types/paramtype.cpp | 33 ++++ libguh/types/paramtype.h | 7 + .../boblight/devicepluginboblight.cpp | 23 +-- .../boblight/devicepluginboblight.h | 5 +- .../deviceplugins/mock/devicepluginmock.cpp | 13 ++ plugins/deviceplugins/mock/devicepluginmock.h | 3 + server/jsonrpc/actionhandler.cpp | 24 +-- server/jsonrpc/devicehandler.cpp | 65 +++++-- server/jsonrpc/devicehandler.h | 2 + server/jsonrpc/jsontypes.cpp | 6 +- tests/auto/api.json | 25 ++- tests/auto/devices/testdevices.cpp | 156 ++++++++++++++- tests/auto/guhtestbase.cpp | 2 +- tests/auto/jsonrpc/testjsonrpc.cpp | 84 --------- tests/scripts/getpluginconfig.sh | 7 + tests/scripts/setpluginconfig.sh | 4 +- 22 files changed, 528 insertions(+), 225 deletions(-) create mode 100755 tests/scripts/getpluginconfig.sh diff --git a/debian/changelog b/debian/changelog index 1952de4a..59df297f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +guh (0.1.2) UNRELEASED; urgency=medium + + * Added Plugin configs + + -- Michael Zanetti Sun, 04 May 2014 22:23:34 +0200 + guh (0.1.1) trusty; urgency=low * Initial release. diff --git a/libguh/devicemanager.cpp b/libguh/devicemanager.cpp index 1ca9ac55..b1dfa535 100644 --- a/libguh/devicemanager.cpp +++ b/libguh/devicemanager.cpp @@ -125,13 +125,26 @@ DevicePlugin *DeviceManager::plugin(const PluginId &id) const return m_devicePlugins.value(id); } -void DeviceManager::setPluginConfig(const PluginId &pluginId, const QVariantMap &pluginConfig) +QPair DeviceManager::setPluginConfig(const PluginId &pluginId, const QList &pluginConfig) { DevicePlugin *plugin = m_devicePlugins.value(pluginId); - plugin->setConfiguration(pluginConfig); + if (!plugin) { + return report(DeviceErrorPluginNotFound, QString("No plugin with id % 1").arg(pluginId.toString())); + } + QPair result = plugin->setConfiguration(pluginConfig); + if (result.first != DeviceErrorNoError) { + return result; + } QSettings settings; - settings.setValue(plugin->pluginId().toString(), pluginConfig); + settings.beginGroup("PluginConfig"); + settings.beginGroup(plugin->pluginId().toString()); + foreach (const Param ¶m, pluginConfig) { + settings.setValue(param.name(), param.value()); + } + settings.endGroup(); + settings.endGroup(); createNewAutoDevices(); + return result; } QList DeviceManager::supportedVendors() const @@ -228,33 +241,9 @@ QPair DeviceManager::addConfiguredDeviceInt return qMakePair(DeviceErrorDeviceClassNotFound, deviceClassId.toString()); } - // Make sure we have all required params - foreach (const ParamType ¶mType, deviceClass.params()) { - bool found = false; - foreach (const Param ¶m, params) { - if (param.name() == paramType.name()) { - found = true; - } - } - - if (!found) { - qWarning() << "Missing parameter when creating device:" << paramType.name(); - return qMakePair(DeviceErrorMissingParameter, paramType.name()); - } - } - // Make sure we don't have unused params - foreach (const Param ¶m, params) { - bool found = false; - foreach (const ParamType ¶mType, deviceClass.params()) { - if (paramType.name() == param.name()) { - found = true; - continue; - } - } - if (!found) { - // TODO: Check if parameter type matches - return qMakePair(DeviceErrorDeviceParameterError, param.name()); - } + QPair result = verifyParams(deviceClass.params(), params); + if (result.first != DeviceErrorNoError) { + return result; } foreach(Device *device, m_configuredDevices) { @@ -310,8 +299,10 @@ QPair DeviceManager::removeConfiguredDevice device->deleteLater(); QSettings settings; + settings.beginGroup("DeviceConfig"); settings.beginGroup(deviceId.toString()); settings.remove(""); + settings.endGroup(); return qMakePair(DeviceErrorNoError, QString()); } @@ -371,14 +362,12 @@ QPair DeviceManager::executeAction(const Ac bool found = false; foreach (const ActionType &actionType, deviceClass.actions()) { if (actionType.id() == action.actionTypeId()) { - found = true; - - qDebug() << "checking params" << actionType.parameters().count() << action.params().count(); - qDebug() << "action params:" << action.params(); - QPair paramCheck = verifyParams(actionType.parameters(), action.params()); - if (!paramCheck.first) { - return qMakePair(DeviceErrorMissingParameter, paramCheck.second); + QPair paramCheck = verifyParams(actionType.parameters(), action.params()); + if (paramCheck.first != DeviceErrorNoError) { + return paramCheck; } + + found = true; continue; } } @@ -418,8 +407,26 @@ void DeviceManager::loadPlugins() qDebug() << "* Loaded device class:" << deviceClass.name(); } QSettings settings; - if (settings.contains(pluginIface->pluginId().toString())) { - pluginIface->setConfiguration(settings.value(pluginIface->pluginId().toString()).toMap()); + settings.beginGroup("PluginConfig"); + QList params; + if (settings.childGroups().contains(pluginIface->pluginId().toString())) { + settings.beginGroup(pluginIface->pluginId().toString()); + foreach (const QString ¶mName, settings.allKeys()) { + Param param(paramName, settings.value(paramName)); + params.append(param); + } + settings.endGroup(); + } else if (pluginIface->configurationDescription().count() > 0){ + // plugin requires config but none stored. Init with defaults + foreach (const ParamType ¶mType, pluginIface->configurationDescription()) { + Param param(paramType.name(), paramType.defaultValue()); + params.append(param); + } + } + settings.endGroup(); + QPair status = pluginIface->setConfiguration(params); + if (status.first != DeviceErrorNoError) { + qWarning() << "Error setting params to plugin. Broken configuration?" << status.second; } m_devicePlugins.insert(pluginIface->pluginId(), pluginIface); @@ -434,6 +441,7 @@ void DeviceManager::loadPlugins() void DeviceManager::loadConfiguredDevices() { QSettings settings; + settings.beginGroup("DeviceConfig"); qDebug() << "loading devices from" << settings.fileName(); foreach (const QString &idString, settings.childGroups()) { settings.beginGroup(idString); @@ -441,20 +449,17 @@ void DeviceManager::loadConfiguredDevices() device->setName(settings.value("devicename").toString()); QList params; - foreach (QString paramNameString, settings.childGroups()) { - qDebug() << "got paramNameString" << paramNameString; - settings.beginGroup(paramNameString); - Param param(paramNameString.remove(QRegExp("Param-"))); - param.setValue(settings.value("value")); + settings.beginGroup("Params"); + foreach (QString paramNameString, settings.allKeys()) { + Param param(paramNameString); + param.setValue(settings.value(paramNameString)); params.append(param); - settings.endGroup(); } - qDebug() << "loaded params from config" << params; device->setParams(params); - + settings.endGroup(); settings.endGroup(); - qDebug() << "found stored device" << device->name() << idString; + qDebug() << "found stored device" << device->id() << device->name() << device->deviceClassId() << device->pluginId(); // We always add the device to the list in this case. If its in the storedDevices // it means that it was working at some point so lets still add it as there might @@ -462,23 +467,26 @@ void DeviceManager::loadConfiguredDevices() setupDevice(device); m_configuredDevices.append(device); } + settings.endGroup(); } void DeviceManager::storeConfiguredDevices() { QSettings settings; + settings.beginGroup("DeviceConfig"); foreach (Device *device, m_configuredDevices) { settings.beginGroup(device->id().toString()); settings.setValue("devicename", device->name()); settings.setValue("deviceClassId", device->deviceClassId().toString()); - settings.setValue("pluginid", device->pluginId()); + settings.setValue("pluginid", device->pluginId().toString()); + settings.beginGroup("Params"); foreach (const Param ¶m, device->params()) { - settings.beginGroup("Param-" + param.name()); - settings.setValue("value", param.value()); - settings.endGroup(); + settings.setValue(param.name(), param.value()); } settings.endGroup(); + settings.endGroup(); } + settings.endGroup(); } void DeviceManager::createNewAutoDevices() @@ -603,7 +611,7 @@ void DeviceManager::timerEvent() foreach (Device *device, m_configuredDevices) { DeviceClass deviceClass = m_supportedDevices.value(device->deviceClassId()); DevicePlugin *plugin = m_devicePlugins.value(deviceClass.pluginId()); - if (plugin->requiredHardware().testFlag(HardwareResourceTimer)) { + if (plugin && plugin->requiredHardware().testFlag(HardwareResourceTimer)) { plugin->guhTimer(); } } @@ -614,6 +622,10 @@ QPair DeviceManager::setupDevice(Devic DeviceClass deviceClass = findDeviceClass(device->deviceClassId()); DevicePlugin *plugin = m_devicePlugins.value(deviceClass.pluginId()); + if (!plugin) { + return qMakePair(DeviceSetupStatusFailure, "Can't find a plugin for this device"); + } + QList states; foreach (const StateType &stateType, deviceClass.states()) { State state(stateType.id(), device->id()); @@ -649,22 +661,64 @@ QPair DeviceManager::setupDevice(Devic return status; } -QPair DeviceManager::verifyParams(const QList paramTypes, const QList params) +QPair DeviceManager::verifyParams(const QList paramTypes, const QList ¶ms, bool requireAll) { + foreach (const Param ¶m, params) { + QPair result = verifyParam(paramTypes, param); + if (result.first != DeviceErrorNoError) { + return result; + } + } + if (!requireAll) { + return report(); + } foreach (const ParamType ¶mType, paramTypes) { - qDebug() << "checking paramType" << paramType.name(); bool found = false; foreach (const Param ¶m, params) { - qDebug() << "checking param" << param.name(); - if (param.name() == paramType.name()) { - if (param.value().canConvert(paramType.type())) { - found = true; - } + if (paramType.name() == param.name()) { + found = true; } } if (!found) { - return qMakePair(false, paramType.name()); + return report(DeviceErrorMissingParameter, QString("Missing parameter: %1").arg(paramType.name())); } } - return qMakePair(true, QString()); + return report(); +} + +QPair DeviceManager::verifyParam(const QList paramTypes, const Param ¶m) +{ + foreach (const ParamType ¶mType, paramTypes) { + if (paramType.name() == param.name()) { + return verifyParam(paramType, param); + } + } + return report(DeviceErrorInvalidParameter, QString("Parameter %1 not in ParamTypes list").arg(param.name())); +} + +QPair DeviceManager::verifyParam(const ParamType ¶mType, const Param ¶m) +{ + if (paramType.name() == param.name()) { + if (!param.value().canConvert(paramType.type())) { + return report(DeviceManager::DeviceErrorInvalidParameter, QString("Wrong parameter type for param %1. Got: %2. Expected %3.") + .arg(param.name()).arg(param.value().toString()).arg(QVariant::typeToName(paramType.type()))); + } + + if (paramType.maxValue().isValid() && param.value() > paramType.maxValue()) { + return report(DeviceManager::DeviceErrorInvalidParameter, QString("Value out of range for param %1. Got: %2. Max: %3.") + .arg(param.name()).arg(param.value().toString()).arg(paramType.maxValue().toString())); + } + if (paramType.minValue().isValid() && param.value() < paramType.maxValue()) { + return report(DeviceManager::DeviceErrorInvalidParameter, QString("Value out of range for param %1. Got: %2. Min: %3.") + .arg(param.name()).arg(param.value().toString()).arg(paramType.minValue().toString())); + } + return report(); + } + return report(DeviceErrorInvalidParameter, QString("Parameter name %1 does not match with ParamType name %2") + .arg(param.name()).arg(paramType.name())); +} + +QPair DeviceManager::report(DeviceManager::DeviceError error, const QString &message) +{ + return qMakePair(error, message); } diff --git a/libguh/devicemanager.h b/libguh/devicemanager.h index b7286e82..b24559d5 100644 --- a/libguh/devicemanager.h +++ b/libguh/devicemanager.h @@ -52,11 +52,11 @@ public: DeviceErrorDeviceClassNotFound, DeviceErrorActionTypeNotFound, DeviceErrorMissingParameter, + DeviceErrorInvalidParameter, DeviceErrorPluginNotFound, DeviceErrorSetupFailed, DeviceErrorDuplicateUuid, DeviceErrorCreationMethodNotSupported, - DeviceErrorDeviceParameterError, DeviceErrorActionParameterError, DeviceErrorDeviceDescriptorNotFound, DeviceErrorAsync @@ -73,7 +73,7 @@ public: QList plugins() const; DevicePlugin* plugin(const PluginId &id) const; - void setPluginConfig(const PluginId &pluginId, const QVariantMap &pluginConfig); + QPair setPluginConfig(const PluginId &pluginId, const QList &pluginConfig); QList supportedVendors() const; QList supportedDevices(const VendorId &vendorId = VendorId()) const; @@ -116,7 +116,11 @@ private slots: private: QPair addConfiguredDeviceInternal(const DeviceClassId &deviceClassId, const QList ¶ms, const DeviceId id = DeviceId::createDeviceId()); QPair setupDevice(Device *device); - QPair verifyParams(const QList paramTypes, const QList params); + QPair verifyParams(const QList paramTypes, const QList ¶ms, bool requireAll = true); + QPair verifyParam(const QList paramTypes, const Param ¶m); + QPair verifyParam(const ParamType ¶mType, const Param ¶m); + + QPair report(DeviceError error = DeviceErrorNoError, const QString &message = QString()); private: diff --git a/libguh/plugin/deviceplugin.cpp b/libguh/plugin/deviceplugin.cpp index c726d55c..0633c548 100644 --- a/libguh/plugin/deviceplugin.cpp +++ b/libguh/plugin/deviceplugin.cpp @@ -177,6 +177,11 @@ void DevicePlugin::deviceRemoved(Device *device) Q_UNUSED(device) } +QList DevicePlugin::configurationDescription() const +{ + return QList(); +} + /*! This will be called when the DeviceManager initializes the plugin and set up the things behind the scenes. When implementing a new plugin, use \l{DevicePlugin::init()} instead in order to do initialisation work. */ void DevicePlugin::initPlugin(DeviceManager *deviceManager) @@ -190,22 +195,82 @@ void DevicePlugin::initPlugin(DeviceManager *deviceManager) When implementing a new plugin, override this and fill in the empty configuration if your plugin requires any. */ -QVariantMap DevicePlugin::configuration() const +QList DevicePlugin::configuration() const { - return QVariantMap(); + return m_config; +} + +/*! + Use this to retrieve the values for your parameters. Values might not be set + at the time when your plugin is loaded, but will be set soon after. Listen to + configurationValueChanged() to know when something changes. + When implementing a new plugin, specify in configurationDescription() what you want to see here. + */ +QVariant DevicePlugin::configValue(const QString ¶mName) const +{ + foreach (const Param ¶m, m_config) { + if (param.name() == paramName) { + return param.value(); + } + } + return QVariant(); } /*! Will be called by the DeviceManager to set a plugin's \a configuration. - - When implementing a new plugin, override this and react to configuration changes. - - TODO: Still need to define a common format for the config. */ -void DevicePlugin::setConfiguration(const QVariantMap &configuration) +QPair DevicePlugin::setConfiguration(const QList &configuration) { - Q_UNUSED(configuration) - qWarning() << "Plugin" << pluginName() << pluginId() << "does not support any configuration"; + foreach (const Param ¶m, configuration) { + qDebug() << "setting config" << param; + QPair result = setConfigValue(param.name(), param.value()); + if (result.first != DeviceManager::DeviceErrorNoError) { + return result; + } + } + return report(); +} + +/*! + Will be called by the DeviceManager to set a plugin's \a configuration. + */ +QPair DevicePlugin::setConfigValue(const QString ¶mName, const QVariant &value) +{ + bool found = false; + foreach (const ParamType ¶mType, configurationDescription()) { + if (paramType.name() == paramName) { + if (!value.canConvert(paramType.type())) { + return report(DeviceManager::DeviceErrorInvalidParameter, QString("Wrong parameter type for param %1. Got %2. Expected %3.") + .arg(paramName).arg(value.toString()).arg(QVariant::typeToName(paramType.type()))); + } + + if (paramType.maxValue().isValid() && value > paramType.maxValue()) { + return report(DeviceManager::DeviceErrorInvalidParameter, QString("Value out of range for param %1. Got %2. Max: %3.") + .arg(paramName).arg(value.toString()).arg(paramType.maxValue().toString())); + } + if (paramType.minValue().isValid() && value < paramType.minValue()) { + return report(DeviceManager::DeviceErrorInvalidParameter, QString("Value out of range for param %1. Got: %2. Min: %3.") + .arg(paramName).arg(value.toString()).arg(paramType.minValue().toString())); + } + found = true; + break; + } + } + if (!found) { + return report(DeviceManager::DeviceErrorInvalidParameter, QString("Invalid parameter %1.").arg(paramName)); + } + for (int i = 0; i < m_config.count(); i++) { + if (m_config.at(i).name() == paramName) { + m_config[i].setValue(value); + emit configValueChanged(paramName, value); + return report(); + } + } + // Still here? need to create the param + Param newParam(paramName, value); + m_config.append(newParam); + emit configValueChanged(paramName, value); + return report(); } /*! @@ -292,4 +357,3 @@ QPair DevicePlugin::reportDeviceSetup { return qMakePair(status, message); } - diff --git a/libguh/plugin/deviceplugin.h b/libguh/plugin/deviceplugin.h index 1689fc94..448efe25 100644 --- a/libguh/plugin/deviceplugin.h +++ b/libguh/plugin/deviceplugin.h @@ -26,6 +26,7 @@ #include "types/event.h" #include "types/action.h" #include "types/vendor.h" +#include "types/param.h" #include @@ -58,8 +59,12 @@ public: virtual void radioData(QList rawData) {Q_UNUSED(rawData)} virtual void guhTimer() {} - virtual QVariantMap configuration() const; - virtual void setConfiguration(const QVariantMap &configuration); + // Configuration + virtual QList configurationDescription() const; + QPair setConfiguration(const QList &configuration); + QList configuration() const; + QVariant configValue(const QString ¶mName) const; + QPair setConfigValue(const QString ¶mName, const QVariant &value); public slots: virtual QPair executeAction(Device *device, const Action &action) { @@ -72,6 +77,7 @@ signals: void devicesDiscovered(const DeviceClassId &deviceClassId, const QList &deviceDescriptors); void deviceSetupFinished(Device *device, DeviceManager::DeviceSetupStatus status, const QString &errorMessage); void actionExecutionFinished(const ActionId &id, DeviceManager::DeviceError status, const QString &errorMessage); + void configValueChanged(const QString ¶mName, const QVariant &value); protected: DeviceManager *deviceManager() const; @@ -87,6 +93,8 @@ private: DeviceManager *m_deviceManager; + QList m_config; + friend class DeviceManager; }; Q_DECLARE_INTERFACE(DevicePlugin, "guru.guh.DevicePlugin") diff --git a/libguh/types/param.cpp b/libguh/types/param.cpp index 6bb19f28..98f8d0ac 100644 --- a/libguh/types/param.cpp +++ b/libguh/types/param.cpp @@ -60,9 +60,9 @@ QDebug operator<<(QDebug dbg, const Param ¶m) QDebug operator<<(QDebug dbg, const QList ¶ms) { - dbg.nospace() << "ParamList (count:" << params.count() << ")"; + dbg.nospace() << "ParamList (count:" << params.count() << ")" << endl; for (int i = 0; i < params.count(); i++ ) { - dbg.nospace() << " " << i << ": " << params.at(i); + dbg.nospace() << " " << i << ": " << params.at(i) << endl; } return dbg.space(); diff --git a/libguh/types/paramtype.cpp b/libguh/types/paramtype.cpp index a39e3577..595c0f74 100644 --- a/libguh/types/paramtype.cpp +++ b/libguh/types/paramtype.cpp @@ -74,3 +74,36 @@ void ParamType::setMaxValue(const QVariant &maxValue) { m_maxValue = maxValue; } + +QPair ParamType::limits() const +{ + return qMakePair(m_minValue, m_maxValue); +} + +void ParamType::setLimits(const QVariant &min, const QVariant &max) +{ + m_minValue = min; + m_maxValue = max; +} + +QDebug operator<<(QDebug dbg, const ParamType ¶mType) +{ + dbg.nospace() << "ParamType(Name: " << paramType.name() + << ", Type:" << QVariant::typeToName(paramType.type()) + << ", Default:" << paramType.defaultValue() + << ", Min:" << paramType.minValue() + << ", Max:" << paramType.maxValue() + << ")"; + + return dbg.space(); +} + +QDebug operator<<(QDebug dbg, const QList ¶mTypes) +{ + dbg.nospace() << "ParamTypeList (count:" << paramTypes.count() << ")" << endl; + for (int i = 0; i < paramTypes.count(); i++ ) { + dbg.nospace() << " " << i << ": " << paramTypes.at(i) << endl; + } + + return dbg.space(); +} diff --git a/libguh/types/paramtype.h b/libguh/types/paramtype.h index 80e83898..bf7811a2 100644 --- a/libguh/types/paramtype.h +++ b/libguh/types/paramtype.h @@ -20,6 +20,7 @@ #define PARAMTYPE_H #include +#include class ParamType { @@ -41,6 +42,9 @@ public: QVariant maxValue() const; void setMaxValue(const QVariant &maxValue); + QPair limits() const; + void setLimits(const QVariant &min, const QVariant &max); + private: QString m_name; QVariant::Type m_type; @@ -49,4 +53,7 @@ private: QVariant m_maxValue; }; +QDebug operator<<(QDebug dbg, const ParamType ¶mType); +QDebug operator<<(QDebug dbg, const QList ¶mTypes); + #endif // PARAMTYPE_H diff --git a/plugins/deviceplugins/boblight/devicepluginboblight.cpp b/plugins/deviceplugins/boblight/devicepluginboblight.cpp index 113a7c58..eedb7111 100644 --- a/plugins/deviceplugins/boblight/devicepluginboblight.cpp +++ b/plugins/deviceplugins/boblight/devicepluginboblight.cpp @@ -36,11 +36,7 @@ ActionTypeId setColorActionTypeId = ActionTypeId("668e1aa3-fa13-49ce-8630-17a5c0 DevicePluginBoblight::DevicePluginBoblight() { m_bobClient = new BobClient(this); - - m_config.insert("boblighthost", "localhost"); - m_config.insert("boblightport", "19333"); - - connectToBoblight(); + connect(this, &DevicePlugin::configValueChanged, this, &DevicePluginBoblight::connectToBoblight); } QList DevicePluginBoblight::supportedVendors() const @@ -122,15 +118,12 @@ PluginId DevicePluginBoblight::pluginId() const return boblightPluginUuid; } -QVariantMap DevicePluginBoblight::configuration() const +QList DevicePluginBoblight::configurationDescription() const { - return m_config; -} - -void DevicePluginBoblight::setConfiguration(const QVariantMap &configuration) -{ - m_config = configuration; - connectToBoblight(); + QList params; + params.append(ParamType("boblighthost", QVariant::String, "localhost")); + params.append(ParamType("boblightport", QVariant::String, "19333")); + return params; } QPair DevicePluginBoblight::executeAction(Device *device, const Action &action) @@ -152,5 +145,7 @@ QPair DevicePluginBoblight::executeAction(D void DevicePluginBoblight::connectToBoblight() { - m_bobClient->connect(m_config.value("boblighthost").toString(), m_config.value("boblightport").toInt()); + if (configValue("boblighthost").isValid() && configValue("boblightport").isValid()) { + m_bobClient->connect(configValue("boblighthost").toString(), configValue("boblightport").toInt()); + } } diff --git a/plugins/deviceplugins/boblight/devicepluginboblight.h b/plugins/deviceplugins/boblight/devicepluginboblight.h index 96fbdaac..2e89cfac 100644 --- a/plugins/deviceplugins/boblight/devicepluginboblight.h +++ b/plugins/deviceplugins/boblight/devicepluginboblight.h @@ -44,8 +44,7 @@ public: QString pluginName() const override; PluginId pluginId() const override; - QVariantMap configuration() const override; - void setConfiguration(const QVariantMap &configuration) override; + QList configurationDescription() const override; public slots: QPair executeAction(Device *device, const Action &action); @@ -55,7 +54,7 @@ private slots: private: BobClient *m_bobClient; - QVariantMap m_config; + QList m_config; }; #endif // DEVICEPLUGINBOBLIGHT_H diff --git a/plugins/deviceplugins/mock/devicepluginmock.cpp b/plugins/deviceplugins/mock/devicepluginmock.cpp index 4e1c98d7..87db94dd 100644 --- a/plugins/deviceplugins/mock/devicepluginmock.cpp +++ b/plugins/deviceplugins/mock/devicepluginmock.cpp @@ -270,6 +270,19 @@ bool DevicePluginMock::configureAutoDevice(QList loadedDevices, Device return true; } +QList DevicePluginMock::configurationDescription() const +{ + QList params; + ParamType mockParam1("configParamInt", QVariant::Int, 42); + mockParam1.setLimits(1, 50); + params.append(mockParam1); + + ParamType mockParam2("configParamBool", QVariant::Bool, true); + params.append(mockParam2); + + return params; +} + QPair DevicePluginMock::executeAction(Device *device, const Action &action) { if (!myDevices().contains(device)) { diff --git a/plugins/deviceplugins/mock/devicepluginmock.h b/plugins/deviceplugins/mock/devicepluginmock.h index 0d7f3685..a6eb1ad5 100644 --- a/plugins/deviceplugins/mock/devicepluginmock.h +++ b/plugins/deviceplugins/mock/devicepluginmock.h @@ -49,6 +49,8 @@ public: bool configureAutoDevice(QList loadedDevices, Device *device) const override; + QList configurationDescription() const override; + public slots: QPair executeAction(Device *device, const Action &action) override; @@ -60,6 +62,7 @@ private slots: void emitActionExecuted(); private: + QList m_config; QHash m_daemons; QList m_asyncSetupDevices; QList > m_asyncActions; diff --git a/server/jsonrpc/actionhandler.cpp b/server/jsonrpc/actionhandler.cpp index f95f8a37..1070e33c 100644 --- a/server/jsonrpc/actionhandler.cpp +++ b/server/jsonrpc/actionhandler.cpp @@ -83,27 +83,7 @@ void ActionHandler::actionExecuted(const ActionId &id, DeviceManager::DeviceErro QVariantMap ActionHandler::statusToReply(DeviceManager::DeviceError status, const QString &errorMessage) { QVariantMap returns; - - switch (status) { - case DeviceManager::DeviceErrorNoError: - returns.insert("success", true); - returns.insert("errorMessage", ""); - break; - case DeviceManager::DeviceErrorDeviceNotFound: - returns.insert("errorMessage", QString("No such device: %1").arg(errorMessage)); - returns.insert("success", false); - break; - case DeviceManager::DeviceErrorActionTypeNotFound: - returns.insert("errorMessage", QString("ActionType not found: %1").arg(errorMessage)); - returns.insert("success", false); - break; - case DeviceManager::DeviceErrorMissingParameter: - returns.insert("errorMessage", QString("Missing parameter: %1").arg(errorMessage)); - returns.insert("success", false); - break; - default: - returns.insert("errorMessage", QString("Unknown error %1 %2").arg(status).arg(errorMessage)); - returns.insert("success", false); - } + returns.insert("success", status == DeviceManager::DeviceErrorNoError); + returns.insert("errorMessage", errorMessage); return returns; } diff --git a/server/jsonrpc/devicehandler.cpp b/server/jsonrpc/devicehandler.cpp index 8d97a2e9..17a233f8 100644 --- a/server/jsonrpc/devicehandler.cpp +++ b/server/jsonrpc/devicehandler.cpp @@ -56,13 +56,24 @@ DeviceHandler::DeviceHandler(QObject *parent) : returns.insert("plugins", plugins); setReturns("GetPlugins", returns); + params.clear(); returns.clear(); + setDescription("GetPluginConfiguration", "Get a plugin's params."); + params.insert("pluginId", "uuid"); + setParams("GetPluginConfiguration", params); + QVariantList pluginParams; + pluginParams.append(JsonTypes::paramRef()); + returns.insert("success", "bool"); + returns.insert("errorMessage", "string"); + returns.insert("o:configuration", pluginParams); + setReturns("GetPluginConfiguration", returns); + params.clear(); returns.clear(); setDescription("SetPluginConfiguration", "Set a plugin's params."); params.insert("pluginId", "uuid"); - QVariantList pluginParams; - pluginParams.append(JsonTypes::paramTypeRef()); - params.insert("pluginParams", pluginParams); + params.insert("configuration", pluginParams); setParams("SetPluginConfiguration", params); + returns.insert("success", "bool"); + returns.insert("errorMessage", "string"); setReturns("SetPluginConfiguration", returns); params.clear(); returns.clear(); @@ -231,23 +242,55 @@ JsonReply* DeviceHandler::GetPlugins(const QVariantMap ¶ms) const QVariantMap pluginMap; pluginMap.insert("id", plugin->pluginId()); pluginMap.insert("name", plugin->pluginName()); - pluginMap.insert("params", plugin->configuration()); + QVariantList params; + foreach (const ParamType ¶m, plugin->configurationDescription()) { + params.append(JsonTypes::packParamType(param)); + } + pluginMap.insert("params", params); plugins.append(pluginMap); } returns.insert("plugins", plugins); return createReply(returns); } +JsonReply *DeviceHandler::GetPluginConfiguration(const QVariantMap ¶ms) const +{ + DevicePlugin *plugin = 0; + foreach (DevicePlugin *p, GuhCore::instance()->deviceManager()->plugins()) { + if (p->pluginId() == PluginId(params.value("pluginId").toString())) { + plugin = p; + } + } + + QVariantMap returns; + if (!plugin) { + returns.insert("success", false); + returns.insert("errorMessage", QString("Plugin not found: %1").arg(params.value("pluginId").toString())); + return createReply(returns); + } + + QVariantList paramVariantList; + foreach (const Param ¶m, plugin->configuration()) { + paramVariantList.append(JsonTypes::packParam(param)); + } + returns.insert("configuration", paramVariantList); + returns.insert("success", true); + returns.insert("errorMessage", QString()); + return createReply(returns); +} + JsonReply* DeviceHandler::SetPluginConfiguration(const QVariantMap ¶ms) { QVariantMap returns; PluginId pluginId = PluginId(params.value("pluginId").toString()); - QVariantMap pluginParams = params.value("pluginParams").toMap(); - GuhCore::instance()->deviceManager()->setPluginConfig(pluginId, pluginParams); - - // TODO: handle return values - returns.insert("errorMessage", QString()); - returns.insert("success", true); + QList pluginParams; + foreach (const QVariant ¶m, params.value("configuration").toList()) { + qDebug() << "got param" << param; + pluginParams.append(JsonTypes::unpackParam(param.toMap())); + } + QPair result = GuhCore::instance()->deviceManager()->setPluginConfig(pluginId, pluginParams); + returns.insert("success", result.first == DeviceManager::DeviceErrorNoError); + returns.insert("errorMessage", result.second); return createReply(returns); } @@ -298,7 +341,7 @@ JsonReply* DeviceHandler::AddConfiguredDevice(const QVariantMap ¶ms) returns.insert("errorMessage", QString("Error creating device. This device can't be created this way: %1").arg(status.second)); returns.insert("success", false); break; - case DeviceManager::DeviceErrorDeviceParameterError: + case DeviceManager::DeviceErrorInvalidParameter: returns.insert("errorMessage", QString("Error creating device. Invalid device parameter: %1").arg(status.second)); returns.insert("success", false); break; diff --git a/server/jsonrpc/devicehandler.h b/server/jsonrpc/devicehandler.h index 46139f6f..d43bd187 100644 --- a/server/jsonrpc/devicehandler.h +++ b/server/jsonrpc/devicehandler.h @@ -38,6 +38,8 @@ public: Q_INVOKABLE JsonReply* GetPlugins(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* GetPluginConfiguration(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply* SetPluginConfiguration(const QVariantMap ¶ms); Q_INVOKABLE JsonReply* AddConfiguredDevice(const QVariantMap ¶ms); diff --git a/server/jsonrpc/jsontypes.cpp b/server/jsonrpc/jsontypes.cpp index dec6933d..14c308fa 100644 --- a/server/jsonrpc/jsontypes.cpp +++ b/server/jsonrpc/jsontypes.cpp @@ -438,10 +438,10 @@ QPair JsonTypes::validateMap(const QVariantMap &templateMap, cons strippedKey.remove(QRegExp("^o:")); if (!key.startsWith("o:") && !map.contains(strippedKey)) { qDebug() << "*** missing key" << key; - qDebug() << "Expected:" << templateMap; - qDebug() << "Got:" << map; + qDebug() << "Expected: " << templateMap; + qDebug() << "Got: " << map; QJsonDocument jsonDoc = QJsonDocument::fromVariant(map); - return report(false, QString("Missing key \"%1\" in %2").arg(key).arg(QString(jsonDoc.toJson()))); + return report(false, QString("Missing key %1 in %2").arg(key).arg(QString(jsonDoc.toJson()))); } if (map.contains(strippedKey)) { QPair result = validateVariant(templateMap.value(key), map.value(strippedKey)); diff --git a/tests/auto/api.json b/tests/auto/api.json index 6fc40ba5..3800090b 100644 --- a/tests/auto/api.json +++ b/tests/auto/api.json @@ -1,4 +1,4 @@ -0.1.1 +0.1.2 { "methods": { "Actions.ExecuteAction": { @@ -78,6 +78,19 @@ ] } }, + "Devices.GetPluginConfiguration": { + "description": "Get a plugin's params.", + "params": { + "pluginId": "uuid" + }, + "returns": { + "errorMessage": "string", + "o:configuration": [ + "$ref:Param" + ], + "success": "bool" + } + }, "Devices.GetPlugins": { "description": "Returns a list of loaded plugins.", "params": { @@ -145,12 +158,14 @@ "Devices.SetPluginConfiguration": { "description": "Set a plugin's params.", "params": { - "pluginId": "uuid", - "pluginParams": [ - "$ref:ParamType" - ] + "configuration": [ + "$ref:Param" + ], + "pluginId": "uuid" }, "returns": { + "errorMessage": "string", + "success": "bool" } }, "JSONRPC.Introspect": { diff --git a/tests/auto/devices/testdevices.cpp b/tests/auto/devices/testdevices.cpp index 9947313b..433c592f 100644 --- a/tests/auto/devices/testdevices.cpp +++ b/tests/auto/devices/testdevices.cpp @@ -19,6 +19,7 @@ #include "guhtestbase.h" #include "guhcore.h" #include "devicemanager.h" +#include "plugin/deviceplugin.h" #include #include @@ -28,9 +29,14 @@ class TestDevices : public GuhTestBase Q_OBJECT private slots: - void getPlugins(); + void getPluginConfig_data(); + void getPluginConfig(); + + void setPluginConfig_data(); + void setPluginConfig(); + void getSupportedVendors(); void getSupportedDevices_data(); @@ -49,6 +55,14 @@ private slots: void discoverDevices_data(); void discoverDevices(); + void getActionTypes_data(); + void getActionTypes(); + + void getEventTypes_data(); + void getEventTypes(); + + void getStateTypes_data(); + void getStateTypes(); }; void TestDevices::getPlugins() @@ -61,6 +75,66 @@ void TestDevices::getPlugins() QCOMPARE(PluginId(plugins.first().toMap().value("id").toString()), mockPluginId); } +void TestDevices::getPluginConfig_data() +{ + QTest::addColumn("pluginId"); + QTest::addColumn("success"); + + QTest::newRow("valid plugin") << mockPluginId << true; + QTest::newRow("invalid plugin") << PluginId::createPluginId() << false; +} + +void TestDevices::getPluginConfig() +{ + QFETCH(PluginId, pluginId); + QFETCH(bool, success); + + QVariantMap params; + params.insert("pluginId", pluginId); + QVariant response = injectAndWait("Devices.GetPluginConfiguration", params); + verifySuccess(response, success); +} + +void TestDevices::setPluginConfig_data() +{ + QTest::addColumn("pluginId"); + QTest::addColumn("value"); + QTest::addColumn("success"); + + QTest::newRow("valid") << mockPluginId << QVariant(13) << true; + QTest::newRow("invalid plugin") << PluginId::createPluginId() << QVariant(13) << false; + QTest::newRow("too big") << mockPluginId << QVariant(130) << false; + QTest::newRow("too small") << mockPluginId << QVariant(-13) << false; + QTest::newRow("wrong type") << mockPluginId << QVariant("wrontType") << false; +} + +void TestDevices::setPluginConfig() +{ + QFETCH(PluginId, pluginId); + QFETCH(QVariant, value); + QFETCH(bool, success); + + QVariantMap params; + params.insert("pluginId", pluginId); + + QVariantList configuration; + QVariantMap configParam; + configParam.insert("configParamInt", value); + configuration.append(configParam); + params.insert("configuration", configuration); + QVariant response = injectAndWait("Devices.SetPluginConfiguration", params); + verifySuccess(response, success); + + if (success) { + params.clear(); + params.insert("pluginId", pluginId); + response = injectAndWait("Devices.GetPluginConfiguration", params); + verifySuccess(response); + qDebug() << "222" << response.toMap().value("params").toMap().value("configuration").toList().first(); + QVERIFY2(response.toMap().value("params").toMap().value("configuration").toList().first().toMap().value("configParamInt") == value, "Value not set correctly"); + } +} + void TestDevices::getSupportedVendors() { QVariant supportedVendors = injectAndWait("Devices.GetSupportedVendors"); @@ -172,6 +246,7 @@ void TestDevices::removeDevice() QFETCH(bool, success); QSettings settings; + settings.beginGroup("DeviceConfig"); if (success) { settings.beginGroup(m_mockDeviceId.toString()); // Make sure we have some config values for this device @@ -212,6 +287,7 @@ void TestDevices::storedDevices() response = injectAndWait("Devices.GetConfiguredDevices", QVariantMap()); + bool found = false; foreach (const QVariant device, response.toMap().value("params").toMap().value("devices").toList()) { qDebug() << "found stored device" << device; if (DeviceId(device.toMap().value("id").toString()) == addedDeviceId) { @@ -222,8 +298,11 @@ void TestDevices::storedDevices() qDebug() << "found added device" << device.toMap().value("params").toList().first().toMap(); qDebug() << "expected deviceParams:" << deviceParams; QCOMPARE(device.toMap().value("params").toList().first().toMap(), deviceParams); + found = true; + break; } } + QVERIFY2(found, "Device missing in config!"); params.clear(); params.insert("deviceId", addedDeviceId); @@ -274,6 +353,81 @@ void TestDevices::discoverDevices() } } +void TestDevices::getActionTypes_data() +{ + QTest::addColumn("deviceClassId"); + QTest::addColumn("resultCount"); + + QTest::newRow("valid deviceclass") << mockDeviceClassId << 5; + QTest::newRow("invalid deviceclass") << DeviceClassId("094f8024-5caa-48c1-ab6a-de486a92088f") << 0; +} + +void TestDevices::getActionTypes() +{ + QFETCH(DeviceClassId, deviceClassId); + QFETCH(int, resultCount); + + QVariantMap params; + params.insert("deviceClassId", deviceClassId); + QVariant response = injectAndWait("Devices.GetActionTypes", params); + + QVariantList actionTypes = response.toMap().value("params").toMap().value("actionTypes").toList(); + QCOMPARE(actionTypes.count(), resultCount); + if (resultCount > 0) { + QCOMPARE(actionTypes.first().toMap().value("id").toString(), mockActionIdWithParams.toString()); + } +} + +void TestDevices::getEventTypes_data() +{ + QTest::addColumn("deviceClassId"); + QTest::addColumn("resultCount"); + + QTest::newRow("valid deviceclass") << mockDeviceClassId << 2; + QTest::newRow("invalid deviceclass") << DeviceClassId("094f8024-5caa-48c1-ab6a-de486a92088f") << 0; +} + +void TestDevices::getEventTypes() +{ + QFETCH(DeviceClassId, deviceClassId); + QFETCH(int, resultCount); + + QVariantMap params; + params.insert("deviceClassId", deviceClassId); + QVariant response = injectAndWait("Devices.GetEventTypes", params); + + QVariantList eventTypes = response.toMap().value("params").toMap().value("eventTypes").toList(); + QCOMPARE(eventTypes.count(), resultCount); + if (resultCount > 0) { + QCOMPARE(eventTypes.first().toMap().value("id").toString(), mockEvent1Id.toString()); + } +} + +void TestDevices::getStateTypes_data() +{ + QTest::addColumn("deviceClassId"); + QTest::addColumn("resultCount"); + + QTest::newRow("valid deviceclass") << mockDeviceClassId << 2; + QTest::newRow("invalid deviceclass") << DeviceClassId("094f8024-5caa-48c1-ab6a-de486a92088f") << 0; +} + +void TestDevices::getStateTypes() +{ + QFETCH(DeviceClassId, deviceClassId); + QFETCH(int, resultCount); + + QVariantMap params; + params.insert("deviceClassId", deviceClassId); + QVariant response = injectAndWait("Devices.GetStateTypes", params); + + QVariantList stateTypes = response.toMap().value("params").toMap().value("stateTypes").toList(); + QCOMPARE(stateTypes.count(), resultCount); + if (resultCount > 0) { + QCOMPARE(stateTypes.first().toMap().value("id").toString(), mockIntStateId.toString()); + } +} + #include "testdevices.moc" QTEST_MAIN(TestDevices) diff --git a/tests/auto/guhtestbase.cpp b/tests/auto/guhtestbase.cpp index 01702a3a..23eac3dd 100644 --- a/tests/auto/guhtestbase.cpp +++ b/tests/auto/guhtestbase.cpp @@ -65,7 +65,7 @@ void GuhTestBase::initTestCase() params.insert("deviceParams", deviceParams); QVariant response = injectAndWait("Devices.AddConfiguredDevice", params); - QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true); + verifySuccess(response); m_mockDeviceId = DeviceId(response.toMap().value("params").toMap().value("deviceId").toString()); QVERIFY2(!m_mockDeviceId.isNull(), "Newly created mock device must not be null."); diff --git a/tests/auto/jsonrpc/testjsonrpc.cpp b/tests/auto/jsonrpc/testjsonrpc.cpp index 60e7ed6a..b105874a 100644 --- a/tests/auto/jsonrpc/testjsonrpc.cpp +++ b/tests/auto/jsonrpc/testjsonrpc.cpp @@ -38,15 +38,6 @@ private slots: void testBasicCall(); void introspect(); - void getActionTypes_data(); - void getActionTypes(); - - void getEventTypes_data(); - void getEventTypes(); - - void getStateTypes_data(); - void getStateTypes(); - void enableDisableNotifications_data(); void enableDisableNotifications(); @@ -131,81 +122,6 @@ void TestJSONRPC::introspect() } } -void TestJSONRPC::getActionTypes_data() -{ - QTest::addColumn("deviceClassId"); - QTest::addColumn("resultCount"); - - QTest::newRow("valid deviceclass") << mockDeviceClassId << 2; - QTest::newRow("invalid deviceclass") << DeviceClassId("094f8024-5caa-48c1-ab6a-de486a92088f") << 0; -} - -void TestJSONRPC::getActionTypes() -{ - QFETCH(DeviceClassId, deviceClassId); - QFETCH(int, resultCount); - - QVariantMap params; - params.insert("deviceClassId", deviceClassId); - QVariant response = injectAndWait("Devices.GetActionTypes", params); - - QVariantList actionTypes = response.toMap().value("params").toMap().value("actionTypes").toList(); - QCOMPARE(actionTypes.count(), resultCount); - if (resultCount > 0) { - QCOMPARE(actionTypes.first().toMap().value("id").toString(), mockActionIdWithParams.toString()); - } -} - -void TestJSONRPC::getEventTypes_data() -{ - QTest::addColumn("deviceClassId"); - QTest::addColumn("resultCount"); - - QTest::newRow("valid deviceclass") << mockDeviceClassId << 2; - QTest::newRow("invalid deviceclass") << DeviceClassId("094f8024-5caa-48c1-ab6a-de486a92088f") << 0; -} - -void TestJSONRPC::getEventTypes() -{ - QFETCH(DeviceClassId, deviceClassId); - QFETCH(int, resultCount); - - QVariantMap params; - params.insert("deviceClassId", deviceClassId); - QVariant response = injectAndWait("Devices.GetEventTypes", params); - - QVariantList eventTypes = response.toMap().value("params").toMap().value("eventTypes").toList(); - QCOMPARE(eventTypes.count(), resultCount); - if (resultCount > 0) { - QCOMPARE(eventTypes.first().toMap().value("id").toString(), mockEvent1Id.toString()); - } -} - -void TestJSONRPC::getStateTypes_data() -{ - QTest::addColumn("deviceClassId"); - QTest::addColumn("resultCount"); - - QTest::newRow("valid deviceclass") << mockDeviceClassId << 2; - QTest::newRow("invalid deviceclass") << DeviceClassId("094f8024-5caa-48c1-ab6a-de486a92088f") << 0; -} - -void TestJSONRPC::getStateTypes() -{ - QFETCH(DeviceClassId, deviceClassId); - QFETCH(int, resultCount); - - QVariantMap params; - params.insert("deviceClassId", deviceClassId); - QVariant response = injectAndWait("Devices.GetStateTypes", params); - - QVariantList stateTypes = response.toMap().value("params").toMap().value("stateTypes").toList(); - QCOMPARE(stateTypes.count(), resultCount); - if (resultCount > 0) { - QCOMPARE(stateTypes.first().toMap().value("id").toString(), mockIntStateId.toString()); - } -} - void TestJSONRPC::enableDisableNotifications_data() { QTest::addColumn("enabled"); diff --git a/tests/scripts/getpluginconfig.sh b/tests/scripts/getpluginconfig.sh new file mode 100755 index 00000000..e7ebb2fd --- /dev/null +++ b/tests/scripts/getpluginconfig.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +if [ -z $2 ]; then + echo "usage $0 host pluginid" +else + (echo '{"id":1, "method":"Devices.GetPluginConfiguration", "params":{"pluginId":"'$2'"}}'; sleep 1) | nc $1 1234 +fi diff --git a/tests/scripts/setpluginconfig.sh b/tests/scripts/setpluginconfig.sh index 6cadfb6a..7b6f660c 100755 --- a/tests/scripts/setpluginconfig.sh +++ b/tests/scripts/setpluginconfig.sh @@ -3,9 +3,9 @@ if [ -z $4 ]; then echo "usage $0 host pluginid param1name param1value [param2name param2value]" elif [ -z $5 ]; then - (echo '{"id":1, "method":"Devices.SetPluginConfiguration", "params":{"pluginId":"'$2'", "pluginParams":{"'$3'":"'$4'"}}}'; sleep 1) | nc $1 1234 + (echo '{"id":1, "method":"Devices.SetPluginConfiguration", "params":{"pluginId":"'$2'", "configuration":[{"'$3'":"'$4'"}]}}'; sleep 1) | nc $1 1234 elif [ -z $6 ]; then echo "usage $0 host pluginid param1name param1value [param2name param2value]" else - (echo '{"id":1, "method":"Devices.SetPluginConfiguration", "params":{"pluginId":"'$2'", "pluginParams":{"'$3'":"'$4'", "'$5'":"'$6'"}}}'; sleep 1) | nc $1 1234 + (echo '{"id":1, "method":"Devices.SetPluginConfiguration", "params":{"pluginId":"'$2'", "configuration":{[{"'$3'":"'$4'"}, {"'$5'":"'$6'"}]}}}'; sleep 1) | nc $1 1234 fi