From c9e6b7ed812dd3a2751905841d28ba9a7b4a72da Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 11 Oct 2014 03:24:52 +0200 Subject: [PATCH] load plugins dynamically --- debian/guh.install | 1 + libguh/devicemanager.cpp | 114 +++++++++++++++++------------ plugins/plugins.pri | 4 +- server/main.cpp | 17 ----- server/server.pro | 14 ---- tests/auto/autotests.pri | 2 +- tests/auto/devices/testdevices.cpp | 35 ++++++--- tests/auto/guhtestbase.cpp | 18 ++++- 8 files changed, 114 insertions(+), 91 deletions(-) diff --git a/debian/guh.install b/debian/guh.install index 791f294e..995d411b 100644 --- a/debian/guh.install +++ b/debian/guh.install @@ -1,2 +1,3 @@ usr/bin/guh +usr/lib/guh/ data/init/guhd /etc/init.d/ diff --git a/libguh/devicemanager.cpp b/libguh/devicemanager.cpp index 4ce1d5ec..df78a3b9 100644 --- a/libguh/devicemanager.cpp +++ b/libguh/devicemanager.cpp @@ -88,6 +88,8 @@ #include #include #include +#include +#include /*! Constructs the DeviceManager with the given \a parent. There should only be one DeviceManager in the system created by \l{GuhCore}. Use \c GuhCore::instance()->deviceManager() instead to access the DeviceManager. @@ -510,59 +512,75 @@ QPair DeviceManager::executeAction(const Ac void DeviceManager::loadPlugins() { - foreach (const QStaticPlugin &staticPlugin, QPluginLoader::staticPlugins()) { - DevicePlugin *pluginIface = qobject_cast(staticPlugin.instance()); - if (verifyPluginMetadata(staticPlugin.metaData().value("MetaData").toObject()) && pluginIface) { - pluginIface->initPlugin(staticPlugin.metaData().value("MetaData").toObject(), this); - qDebug() << "*** Loaded plugin" << pluginIface->pluginName(); - foreach (const Vendor &vendor, pluginIface->supportedVendors()) { - qDebug() << "* Loaded vendor:" << vendor.name(); - if (m_supportedVendors.contains(vendor.id())) { - qWarning() << "! Duplicate vendor. Ignoring vendor" << vendor.name(); - continue; - } - m_supportedVendors.insert(vendor.id(), vendor); - } - foreach (const DeviceClass &deviceClass, pluginIface->supportedDevices()) { - if (!m_supportedVendors.contains(deviceClass.vendorId())) { - qWarning() << "! Vendor not found. Ignoring device. VendorId:" << deviceClass.vendorId() << "DeviceClass:" << deviceClass.name() << deviceClass.id(); - continue; - } - m_vendorDeviceMap[deviceClass.vendorId()].append(deviceClass.id()); - m_supportedDevices.insert(deviceClass.id(), deviceClass); - qDebug() << "* Loaded device class:" << deviceClass.name(); + QStringList searchDirs; + searchDirs << QCoreApplication::applicationDirPath() + "/../lib/guh"; + searchDirs << QCoreApplication::applicationDirPath() + "/../"; + searchDirs << QCoreApplication::applicationDirPath() + "/../../../"; + + foreach (const QString &path, searchDirs) { + QDir dir(path + "/plugins/deviceplugins"); + qDebug() << "Loading plugins from:" << dir.absolutePath(); + foreach (const QString &entry, dir.entryList()) { + QFileInfo fi(path + "/plugins/deviceplugins/" + entry + "/libguh_deviceplugin" + entry + ".so"); + if (!fi.exists()) { + continue; } - QSettings settings(m_settingsFile); - settings.beginGroup("PluginConfig"); - ParamList 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); + QPluginLoader loader(fi.absoluteFilePath()); + + DevicePlugin *pluginIface = qobject_cast(loader.instance()); + if (verifyPluginMetadata(loader.metaData().value("MetaData").toObject()) && pluginIface) { + pluginIface->initPlugin(loader.metaData().value("MetaData").toObject(), this); + qDebug() << "*** Loaded plugin" << pluginIface->pluginName(); + foreach (const Vendor &vendor, pluginIface->supportedVendors()) { + qDebug() << "* Loaded vendor:" << vendor.name(); + if (m_supportedVendors.contains(vendor.id())) { + qWarning() << "! Duplicate vendor. Ignoring vendor" << vendor.name(); + continue; + } + m_supportedVendors.insert(vendor.id(), vendor); + } + + foreach (const DeviceClass &deviceClass, pluginIface->supportedDevices()) { + if (!m_supportedVendors.contains(deviceClass.vendorId())) { + qWarning() << "! Vendor not found. Ignoring device. VendorId:" << deviceClass.vendorId() << "DeviceClass:" << deviceClass.name() << deviceClass.id(); + continue; + } + m_vendorDeviceMap[deviceClass.vendorId()].append(deviceClass.id()); + m_supportedDevices.insert(deviceClass.id(), deviceClass); + qDebug() << "* Loaded device class:" << deviceClass.name(); + } + QSettings settings(m_settingsFile); + settings.beginGroup("PluginConfig"); + ParamList 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(); - } 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); + QPair status = pluginIface->setConfiguration(params); + if (status.first != DeviceErrorNoError) { + qWarning() << "Error setting params to plugin. Broken configuration?" << status.second; } - } - 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); - connect(pluginIface, &DevicePlugin::emitEvent, this, &DeviceManager::eventTriggered); - connect(pluginIface, &DevicePlugin::devicesDiscovered, this, &DeviceManager::slotDevicesDiscovered); - connect(pluginIface, &DevicePlugin::deviceSetupFinished, this, &DeviceManager::slotDeviceSetupFinished); - connect(pluginIface, &DevicePlugin::actionExecutionFinished, this, &DeviceManager::actionExecutionFinished); - connect(pluginIface, &DevicePlugin::pairingFinished, this, &DeviceManager::slotPairingFinished); - connect(pluginIface, &DevicePlugin::autoDevicesAppeared, this, &DeviceManager::autoDevicesAppeared); + m_devicePlugins.insert(pluginIface->pluginId(), pluginIface); + connect(pluginIface, &DevicePlugin::emitEvent, this, &DeviceManager::eventTriggered); + connect(pluginIface, &DevicePlugin::devicesDiscovered, this, &DeviceManager::slotDevicesDiscovered); + connect(pluginIface, &DevicePlugin::deviceSetupFinished, this, &DeviceManager::slotDeviceSetupFinished); + connect(pluginIface, &DevicePlugin::actionExecutionFinished, this, &DeviceManager::actionExecutionFinished); + connect(pluginIface, &DevicePlugin::pairingFinished, this, &DeviceManager::slotPairingFinished); + connect(pluginIface, &DevicePlugin::autoDevicesAppeared, this, &DeviceManager::autoDevicesAppeared); + } } } } @@ -588,7 +606,7 @@ void DeviceManager::loadConfiguredDevices() settings.endGroup(); settings.endGroup(); - qDebug() << "found stored device" << device->id() << device->name() << device->deviceClassId() << device->pluginId(); + //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 diff --git a/plugins/plugins.pri b/plugins/plugins.pri index b4d6e9a0..d5ce9f95 100644 --- a/plugins/plugins.pri +++ b/plugins/plugins.pri @@ -1,8 +1,10 @@ include(../guh.pri) TEMPLATE = lib -CONFIG += plugin static c++11 +CONFIG += plugin c++11 INCLUDEPATH += ../../../libguh LIBS += -L../../../libguh -lguh +target.path = /usr/lib/guh/plugins/ +INSTALLS += target diff --git a/server/main.cpp b/server/main.cpp index da6881f8..63d70c9a 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -21,23 +21,6 @@ #include -Q_IMPORT_PLUGIN(DevicePluginElro) -Q_IMPORT_PLUGIN(DevicePluginIntertechno) -//Q_IMPORT_PLUGIN(DevicePluginMeisterAnker) -Q_IMPORT_PLUGIN(DevicePluginWifiDetector) -Q_IMPORT_PLUGIN(DevicePluginConrad) -Q_IMPORT_PLUGIN(DevicePluginMock) -Q_IMPORT_PLUGIN(DevicePluginOpenweathermap) -Q_IMPORT_PLUGIN(DevicePluginLircd) -Q_IMPORT_PLUGIN(DevicePluginWakeOnLan) -Q_IMPORT_PLUGIN(DevicePluginMailNotification) -Q_IMPORT_PLUGIN(DevicePluginPhilipsHue) -Q_IMPORT_PLUGIN(DevicePluginLgSmartTv) - -#if USE_BOBLIGHT -Q_IMPORT_PLUGIN(DevicePluginBoblight) -#endif - int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); diff --git a/server/server.pro b/server/server.pro index 58cfc810..75a5358e 100644 --- a/server/server.pro +++ b/server/server.pro @@ -18,20 +18,6 @@ LIBS += -L$$top_builddir/libguh/ -lguh include(server.pri) SOURCES += main.cpp -# FIXME: Drop this and link them dynamically -LIBS += -L../plugins/deviceplugins/elro/ -lguh_devicepluginelro -LIBS += -L../plugins/deviceplugins/intertechno/ -lguh_devicepluginintertechno -#LIBS += -L../plugins/deviceplugins/meisteranker/ -lguh_devicepluginmeisteranker -LIBS += -L../plugins/deviceplugins/wifidetector/ -lguh_devicepluginwifidetector -LIBS += -L../plugins/deviceplugins/conrad -lguh_devicepluginconrad -LIBS += -L../plugins/deviceplugins/mock -lguh_devicepluginmock -LIBS += -L../plugins/deviceplugins/openweathermap -lguh_devicepluginopenweathermap -LIBS += -L../plugins/deviceplugins/lircd -lguh_devicepluginlircd -LIBS += -L../plugins/deviceplugins/mailnotification -lguh_devicepluginmailnotification -LIBS += -L../plugins/deviceplugins/wakeonlan -lguh_devicepluginwakeonlan -LIBS += -L../plugins/deviceplugins/philipshue -lguh_devicepluginphilipshue -LIBS += -L../plugins/deviceplugins/lgsmarttv -lguh_devicepluginlgsmarttv - boblight { xcompile { LIBS += -L../plugins/deviceplugins/boblight -lguh_devicepluginboblight -lboblight diff --git a/tests/auto/autotests.pri b/tests/auto/autotests.pri index e4f49384..dd456523 100644 --- a/tests/auto/autotests.pri +++ b/tests/auto/autotests.pri @@ -5,7 +5,7 @@ include($$top_srcdir/server/server.pri) DEFINES += TESTING_ENABLED INCLUDEPATH += $$top_srcdir/server/ $$top_srcdir/server/jsonrpc $$top_srcdir/libguh $$top_srcdir/tests/auto/ -LIBS += -L$$top_builddir/libguh/ -lguh -L$$top_builddir/plugins/deviceplugins/mock/ -lguh_devicepluginmock +LIBS += -L$$top_builddir/libguh/ -lguh -L$$top_builddir/plugins/deviceplugins/mock/ SOURCES += ../guhtestbase.cpp \ ../mocktcpserver.cpp \ diff --git a/tests/auto/devices/testdevices.cpp b/tests/auto/devices/testdevices.cpp index dea85a22..cec90374 100644 --- a/tests/auto/devices/testdevices.cpp +++ b/tests/auto/devices/testdevices.cpp @@ -71,8 +71,14 @@ void TestDevices::getPlugins() QVariantList plugins = response.toMap().value("params").toMap().value("plugins").toList(); - QCOMPARE(plugins.count(), 1); - QCOMPARE(PluginId(plugins.first().toMap().value("id").toString()), mockPluginId); + QCOMPARE(plugins.count() > 0, true); + bool found = false; + foreach (const QVariant &listEntry, plugins) { + if (PluginId(listEntry.toMap().value("id").toString()) == mockPluginId) { + found = true; + } + } + QCOMPARE(found, true); } void TestDevices::getPluginConfig_data() @@ -144,9 +150,14 @@ void TestDevices::getSupportedVendors() // Make sure there is exactly 1 supported Vendor named "guh" QVariantList vendorList = supportedVendors.toMap().value("params").toMap().value("vendors").toList(); - QCOMPARE(vendorList.count(), 1); - VendorId vendorId = VendorId(vendorList.first().toMap().value("id").toString()); - QCOMPARE(vendorId, guhVendorId); + QCOMPARE(vendorList.count() > 0, true); + bool found = false; + foreach (const QVariant &listEntry, vendorList) { + if (VendorId(listEntry.toMap().value("id").toString()) == guhVendorId) { + found = true; + } + } + QCOMPARE(found, true); } void TestDevices::getSupportedDevices_data() @@ -168,13 +179,19 @@ void TestDevices::getSupportedDevices() if (!vendorId.isNull()) { params.insert("vendorId", vendorId); } - QVariant supportedDevices = injectAndWait("Devices.GetSupportedDevices", params); + QVariant result = injectAndWait("Devices.GetSupportedDevices", params); + QVariantList supportedDevices = result.toMap().value("params").toMap().value("deviceClasses").toList(); // Make sure there are the right amount of supported device classes with the name Mock Device - QCOMPARE(supportedDevices.toMap().value("params").toMap().value("deviceClasses").toList().count(), resultCount); + QCOMPARE(supportedDevices.count() >= resultCount, true); if (resultCount > 0) { - QString deviceName = supportedDevices.toMap().value("params").toMap().value("deviceClasses").toList().first().toMap().value("name").toString(); - QVERIFY2(deviceName.startsWith(QString("Mock Device")), QString("Got: %1 Expected: %2").arg(deviceName).arg("Mock Device").toLatin1().data()); + bool found = false; + foreach (const QVariant &listEntry, supportedDevices) { + if (listEntry.toMap().value("name").toString().startsWith("Mock Device")) { + found = true; + } + } + QVERIFY2(found, "Mock Device not found"); } } diff --git a/tests/auto/guhtestbase.cpp b/tests/auto/guhtestbase.cpp index c274d310..48496707 100644 --- a/tests/auto/guhtestbase.cpp +++ b/tests/auto/guhtestbase.cpp @@ -28,7 +28,23 @@ #include #include -Q_IMPORT_PLUGIN(DevicePluginMock) +PluginId mockPluginId = PluginId("727a4a9a-c187-446f-aadf-f1b2220607d1"); +VendorId guhVendorId = VendorId("2062d64d-3232-433c-88bc-0d33c0ba2ba6"); +DeviceClassId mockDeviceClassId = DeviceClassId("753f0d32-0468-4d08-82ed-1964aab03298"); +DeviceClassId mockDeviceAutoClassId = DeviceClassId("ab4257b3-7548-47ee-9bd4-7dc3004fd197"); +DeviceClassId mockDeviceDiscoveryClassId = DeviceClassId("1bbaf751-36b7-4d3d-b05a-58dab2a3be8c"); +DeviceClassId mockDeviceAsyncSetupClassId = DeviceClassId("c08a8b27-8200-413d-b96b-4cff78b864d9"); +DeviceClassId mockDeviceBrokenClassId = DeviceClassId("ba5fb404-c9ce-4db4-8cd4-f48c61c24b13"); +DeviceClassId mockDeviceBrokenAsyncSetupClassId = DeviceClassId("bd5b78c5-53c9-4417-8eac-8ab2bce97bd0"); +EventTypeId mockEvent1Id = EventTypeId("45bf3752-0fc6-46b9-89fd-ffd878b5b22b"); +EventTypeId mockEvent2Id = EventTypeId("863d5920-b1cf-4eb9-88bd-8f7b8583b1cf"); +StateTypeId mockIntStateId = StateTypeId("80baec19-54de-4948-ac46-31eabfaceb83"); +StateTypeId mockBoolStateId = StateTypeId("9dd6a97c-dfd1-43dc-acbd-367932742310"); +ActionTypeId mockActionIdWithParams = ActionTypeId("dea0f4e1-65e3-4981-8eaa-2701c53a9185"); +ActionTypeId mockActionIdNoParams = ActionTypeId("defd3ed6-1a0d-400b-8879-a0202cf39935"); +ActionTypeId mockActionIdAsync = ActionTypeId("fbae06d3-7666-483e-a39e-ec50fe89054e"); +ActionTypeId mockActionIdFailing = ActionTypeId("df3cf33d-26d5-4577-9132-9823bd33fad0"); +ActionTypeId mockActionIdAsyncFailing = ActionTypeId("bfe89a1d-3497-4121-8318-e77c37537219"); GuhTestBase::GuhTestBase(QObject *parent) : QObject(parent),