From 8aa91292fe1fafc9245407ce1d41bc8229c8a172 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Thu, 2 Jul 2020 11:04:37 +0200 Subject: [PATCH] more work --- libnymea-core/integrations/python/pything.h | 61 +++++++++++++------ .../python/pythingdiscoveryinfo.h | 5 -- .../integrations/pythonintegrationplugin.cpp | 38 ++++++------ .../integrations/pythonintegrationplugin.h | 2 +- plugins/pymock/integrationpluginpymock.py | 30 +++++++-- 5 files changed, 86 insertions(+), 50 deletions(-) diff --git a/libnymea-core/integrations/python/pything.h b/libnymea-core/integrations/python/pything.h index 0c742258..6fb48701 100644 --- a/libnymea-core/integrations/python/pything.h +++ b/libnymea-core/integrations/python/pything.h @@ -12,6 +12,7 @@ #include #include #include +#include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Winvalid-offsetof" @@ -29,6 +30,8 @@ typedef struct _thing { PyObject_HEAD Thing *thing = nullptr; PyObject *name = nullptr; + PyObject *id = nullptr; + PyObject *thingClassId = nullptr; PyObject *params = nullptr; PyObject *settings = nullptr; PyObject *nameChangedHandler = nullptr; @@ -51,7 +54,8 @@ static void PyThing_setThing(PyThing *self, Thing *thing) self->thing = thing; self->name = PyUnicode_FromString(self->thing->name().toUtf8().data()); - Py_INCREF(self->name); + self->id = PyUnicode_FromString(self->thing->id().toString().toUtf8().data()); + self->thingClassId = PyUnicode_FromString(self->thing->thingClassId().toString().toUtf8().data()); QObject::connect(thing, &Thing::nameChanged, [=](){ self->mutex->lock(); @@ -67,29 +71,12 @@ static void PyThing_setThing(PyThing *self, Thing *thing) PyGILState_STATE s = PyGILState_Ensure(); PyObject_CallFunctionObjArgs(self->nameChangedHandler, self, nullptr); - if (PyErr_Occurred()) { - PyObject *ptype, *pvalue, *ptraceback; - PyErr_Fetch(&ptype, &pvalue, &ptraceback); - if (pvalue) { - PyObject *pstr = PyObject_Str(pvalue); - if (pstr) { - const char* err_msg = PyUnicode_AsUTF8(pstr); - if (pstr) { - qCWarning(dcThingManager()) << QString(err_msg); - } - } - PyErr_Restore(ptype, pvalue, ptraceback); - } - } - PyGILState_Release(s); }); self->params = PyParams_FromParamList(self->thing->params()); - Py_INCREF(self->params); self->settings = PyParams_FromParamList(self->thing->settings()); - Py_INCREF(self->settings); QObject::connect(thing, &Thing::settingChanged, [=](){ QMutexLocker(self->mutex); Py_XDECREF(self->settings); @@ -99,6 +86,9 @@ static void PyThing_setThing(PyThing *self, Thing *thing) static void PyThing_dealloc(PyThing * self) { + Py_XDECREF(self->name); + Py_XDECREF(self->id); + Py_XDECREF(self->thingClassId); Py_XDECREF(self->name); Py_XDECREF(self->params); Py_XDECREF(self->settings); @@ -119,6 +109,30 @@ static PyObject *PyThing_getName(PyThing *self, void */*closure*/) return self->name; } +static PyObject *PyThing_getId(PyThing *self, void */*closure*/) +{ + QMutexLocker(self->mutex); + if (!self->thing) { + PyErr_SetString(PyExc_ValueError, "Thing has been removed from the system."); + return nullptr; + } + + Py_INCREF(self->id); + return self->id; +} + +static PyObject *PyThing_getThingClassId(PyThing *self, void */*closure*/) +{ + QMutexLocker(self->mutex); + if (!self->thing) { + PyErr_SetString(PyExc_ValueError, "Thing has been removed from the system."); + return nullptr; + } + + Py_INCREF(self->thingClassId); + return self->thingClassId; +} + static int PyThing_setName(PyThing *self, PyObject *value, void */*closure*/){ QString name = QString(PyUnicode_AsUTF8(value)); QMutexLocker(self->mutex); @@ -186,6 +200,8 @@ static PyObject * PyThing_emitEvent(PyThing* self, PyObject* args) static PyGetSetDef PyThing_getset[] = { {"name", (getter)PyThing_getName, (setter)PyThing_setName, "Thing name", nullptr}, + {"id", (getter)PyThing_getId, 0, "ThingId", nullptr}, + {"thingClassId", (getter)PyThing_getThingClassId, 0, "ThingClassId", nullptr}, {"settings", (getter)PyThing_getSettings, (setter)PyThing_setSettings, "Thing settings", nullptr}, {nullptr , nullptr, nullptr, nullptr, nullptr} /* Sentinel */ }; @@ -197,6 +213,7 @@ static PyMethodDef PyThing_methods[] = { }; static PyMemberDef PyThing_members[] = { + {"nameChangedHandler", T_OBJECT_EX, offsetof(PyThing, nameChangedHandler), READONLY, "Set a callback for when the thing name changes"}, {"nameChangedHandler", T_OBJECT_EX, offsetof(PyThing, nameChangedHandler), 0, "Set a callback for when the thing name changes"}, {nullptr, 0, 0, 0, nullptr} /* Sentinel */ }; @@ -238,7 +255,7 @@ static PyTypeObject PyThingType = { 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ - 0, /* tp_alloc */ + PyType_GenericAlloc, /* tp_alloc */ (newfunc)PyThing_new, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ @@ -260,6 +277,12 @@ static void registerThingType(PyObject *module) return; } PyModule_AddObject(module, "Thing", reinterpret_cast(&PyThingType)); + + QMetaEnum thingErrorEnum = QMetaEnum::fromType(); + for (int i = 0; i < thingErrorEnum.keyCount(); i++) { + PyModule_AddObject(module, thingErrorEnum.key(i), PyLong_FromLong(thingErrorEnum.value(i))); + } + } diff --git a/libnymea-core/integrations/python/pythingdiscoveryinfo.h b/libnymea-core/integrations/python/pythingdiscoveryinfo.h index af20d574..9a13defe 100644 --- a/libnymea-core/integrations/python/pythingdiscoveryinfo.h +++ b/libnymea-core/integrations/python/pythingdiscoveryinfo.h @@ -165,11 +165,6 @@ static void registerThingDiscoveryInfoType(PyObject *module) return; } PyModule_AddObject(module, "ThingDiscoveryInfo", (PyObject *)&PyThingDiscoveryInfoType); - - QMetaEnum thingErrorEnum = QMetaEnum::fromType(); - for (int i = 0; i < thingErrorEnum.keyCount(); i++) { - PyModule_AddObject(module, thingErrorEnum.key(i), PyLong_FromLong(thingErrorEnum.value(i))); - } } diff --git a/libnymea-core/integrations/pythonintegrationplugin.cpp b/libnymea-core/integrations/pythonintegrationplugin.cpp index 3703cc4b..027beeb0 100644 --- a/libnymea-core/integrations/pythonintegrationplugin.cpp +++ b/libnymea-core/integrations/pythonintegrationplugin.cpp @@ -308,6 +308,7 @@ bool PythonIntegrationPlugin::loadScript(const QString &scriptFile) if (!m_module) { dumpError(); + PyErr_Clear(); qCWarning(dcThingManager()) << "Error importing python plugin from:" << fi.absoluteFilePath(); PyGILState_Release(s); return false; @@ -388,25 +389,18 @@ void PythonIntegrationPlugin::setupThing(ThingSetupInfo *info) PyGILState_STATE s = PyGILState_Ensure(); PyThing *pyThing = (PyThing*)PyObject_CallObject((PyObject*)&PyThingType, NULL); + dumpError(); PyThing_setThing(pyThing, info->thing()); PyThingSetupInfo *pyInfo = (PyThingSetupInfo*)PyObject_CallObject((PyObject*)&PyThingSetupInfoType, NULL); pyInfo->info = info; pyInfo->pyThing = pyThing; + m_things.insert(info->thing(), pyThing); + PyGILState_Release(s); - - connect(info, &ThingSetupInfo::finished, this, [=](){ - if (info->status() == Thing::ThingErrorNoError) { - m_mutex.lock(); - m_things.insert(info->thing(), pyThing); - m_mutex.unlock(); - } else { - cleanupPyThing(pyThing); - } - }); - connect(info, &ThingSetupInfo::aborted, this, [=](){ + connect(info->thing(), &Thing::destroyed, this, [=](){ cleanupPyThing(pyThing); }); connect(info, &ThingSetupInfo::destroyed, this, [=](){ @@ -416,7 +410,11 @@ void PythonIntegrationPlugin::setupThing(ThingSetupInfo *info) }); - callPluginFunction("setupThing", reinterpret_cast(pyInfo)); + bool result = callPluginFunction("setupThing", reinterpret_cast(pyInfo)); + if (!result) { + // The python code did not even start, so let's finish (fail) the setup right away + info->finish(Thing::ThingErrorSetupFailed); + } } void PythonIntegrationPlugin::postSetupThing(Thing *thing) @@ -456,8 +454,6 @@ void PythonIntegrationPlugin::thingRemoved(Thing *thing) callPluginFunction("thingRemoved", reinterpret_cast(pyThing)); - cleanupPyThing(pyThing); - m_mutex.lock(); m_things.remove(thing); m_mutex.unlock(); @@ -496,7 +492,7 @@ void PythonIntegrationPlugin::exportIds() foreach (const Vendor &vendor, supportedVendors()) { qCDebug(dcThingManager()) << "|- Vendor:" << vendor.name() << vendor.id().toString(); - PyModule_AddStringConstant(m_module, vendor.name().toUtf8(), vendor.id().toString().toUtf8()); + PyModule_AddStringConstant(m_module, QString("%1VendorId").arg(vendor.name()).toUtf8(), vendor.id().toString().toUtf8()); } foreach (const ThingClass &thingClass, supportedThings()) { @@ -572,17 +568,18 @@ void PythonIntegrationPlugin::exportBrowserItemActionTypes(const ActionTypes &ac } -void PythonIntegrationPlugin::callPluginFunction(const QString &function, PyObject *param1, PyObject *param2) +bool PythonIntegrationPlugin::callPluginFunction(const QString &function, PyObject *param1, PyObject *param2) { PyGILState_STATE s = PyGILState_Ensure(); qCDebug(dcThingManager()) << "Calling python plugin function" << function; PyObject *pFunc = PyObject_GetAttrString(m_module, function.toUtf8()); if(!pFunc || !PyCallable_Check(pFunc)) { + PyErr_Clear(); Py_XDECREF(pFunc); qCWarning(dcThingManager()) << "Python plugin does not implement" << function; PyGILState_Release(s); - return; + return false; } @@ -595,14 +592,15 @@ void PythonIntegrationPlugin::callPluginFunction(const QString &function, PyObje if (PyErr_Occurred()) { qCWarning(dcThingManager()) << "Error calling python method:"; dumpError(); + PyErr_Clear(); PyGILState_Release(s); - return; + return false; } if (QByteArray(result->ob_type->tp_name) != "coroutine") { Py_DECREF(result); PyGILState_Release(s); - return; + return true; } // Spawn a event loop for python @@ -638,6 +636,8 @@ void PythonIntegrationPlugin::callPluginFunction(const QString &function, PyObje Py_DECREF(result); PyGILState_Release(s); + + return true; } void PythonIntegrationPlugin::cleanupPyThing(PyThing *pyThing) diff --git a/libnymea-core/integrations/pythonintegrationplugin.h b/libnymea-core/integrations/pythonintegrationplugin.h index f0145fe0..6d4e906c 100644 --- a/libnymea-core/integrations/pythonintegrationplugin.h +++ b/libnymea-core/integrations/pythonintegrationplugin.h @@ -51,7 +51,7 @@ private: void exportBrowserItemActionTypes(const ActionTypes &actionTypes, const QString &thingClassName); - void callPluginFunction(const QString &function, PyObject *param1 = nullptr, PyObject *param2 = nullptr); + bool callPluginFunction(const QString &function, PyObject *param1 = nullptr, PyObject *param2 = nullptr); void cleanupPyThing(PyThing *pyThing); private: diff --git a/plugins/pymock/integrationpluginpymock.py b/plugins/pymock/integrationpluginpymock.py index e81c29a8..cd1495e9 100644 --- a/plugins/pymock/integrationpluginpymock.py +++ b/plugins/pymock/integrationpluginpymock.py @@ -1,17 +1,35 @@ import nymea +import asyncio def init(): logger.log("Python mock plugin init") - logger.log("Number of auto mocks", configValue(pyMockPluginAutoThingCountParamTypeId)) def configValueChanged(paramTypeId, value): logger.log("Plugin config value changed:", paramTypeId, value) + if paramTypeId == pyMockPluginAutoThingCountParamTypeId: + logger.log("Auto Thing Count plugin config changed:", value, "Currently there are:", len(autoThings()), "auto things") + for i in range(value, len(myThings())): + logger.log("Creating new auto thing") + descriptor = nymea.ThingDescriptor(pyMockAutoThingClassId, "Python Mock auto thing") + autoThingsAppeared([descriptor]) def startMonitoringAutoThings(): - logger.log("Start monitoring auto things. Already have", len(myThings()), configValue(pyMockPluginAutoThingCountParamTypeId)) - for i in range(configValue(pyMockPluginAutoThingCountParamTypeId), len(myThings())): - logger.log("auto thing") -# descriptor = nymea. -# autoThingsAppeared( + logger.log("Start monitoring auto things. Have %i auto devices. Need %i." % (len(autoThings()), configValue(pyMockPluginAutoThingCountParamTypeId))) + for i in range(len(autoThings()), configValue(pyMockPluginAutoThingCountParamTypeId)): + logger.log("Creating new auto thing") + descriptor = nymea.ThingDescriptor(pyMockAutoThingClassId, "Python Mock auto thing") + autoThingsAppeared([descriptor]) + + +async def setupThing(info): + info.finish(nymea.ThingErrorNoError) + + +def autoThings(): + autoThings = [] + for thing in myThings(): + if thing.thingClassId == pyMockAutoThingClassId: + autoThings.append(thing) + return autoThings