From 3c47e598119f201c8ae45bbfad5a41db65552d85 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Thu, 25 Jun 2020 12:23:33 +0200 Subject: [PATCH] some more work --- libnymea-core/integrations/python/pything.h | 48 +++++++++++++++++-- .../integrations/python/pythingactioninfo.h | 3 +- .../integrations/pythonintegrationplugin.cpp | 42 +++++++++++----- libnymea-core/jsonrpc/devicehandler.cpp | 1 - libnymea/integrations/thing.cpp | 2 + libnymea/integrations/thing.h | 14 +++--- 6 files changed, 84 insertions(+), 26 deletions(-) diff --git a/libnymea-core/integrations/python/pything.h b/libnymea-core/integrations/python/pything.h index 175b0177..500c8583 100644 --- a/libnymea-core/integrations/python/pything.h +++ b/libnymea-core/integrations/python/pything.h @@ -10,6 +10,7 @@ #include "loggingcategories.h" #include +#include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Winvalid-offsetof" @@ -36,15 +37,37 @@ static PyObject *PyThing_getName(PyThing *self, void */*closure*/) PyErr_SetString(PyExc_ValueError, "Thing has been removed from the system."); return nullptr; } - // FIXME: Needs blocking queued connection - PyObject *ret = PyUnicode_FromString(self->thing->name().toUtf8().data()); + QString name; + // FIXME: Should not be a direct connection! + qWarning() << "name thread" << QThread::currentThread(); + QMetaObject::invokeMethod(self->thing, "name", Qt::DirectConnection, Q_RETURN_ARG(QString, name)); + PyObject *ret = PyUnicode_FromString(name.toUtf8().data()); Py_INCREF(ret); return ret; } static int PyThing_setName(PyThing *self, PyObject *value, void */*closure*/){ - // FIXME: Needs queued connection - self->thing->setName(QString(PyUnicode_AsUTF8(value))); + QString name = QString(PyUnicode_AsUTF8(value)); + QMetaObject::invokeMethod(self->thing, "setName", Qt::QueuedConnection, Q_ARG(QString, name)); + return 0; +} + +static PyObject *PyThing_getSettings(PyThing *self, void */*closure*/) +{ + if (!self->thing) { + PyErr_SetString(PyExc_ValueError, "Thing has been removed from the system."); + return nullptr; + } + qWarning() << "setting thread" << QThread::currentThread(); + ParamList settings; + QMetaObject::invokeMethod(self->thing, "settings", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ParamList, settings)); + PyObject *ret = PyParam_FromParamList(settings); + Py_INCREF(ret); + return ret; +} + +static int PyThing_setSettings(PyThing */*self*/, PyObject */*value*/, void */*closure*/){ + // self->thing->setName(QString(PyUnicode_AsUTF8(value))); return 0; } @@ -77,6 +100,19 @@ static PyObject * PyThing_setStateValue(PyThing* self, PyObject* args) Py_RETURN_NONE; } +static PyObject *PyThing_settingChanged(PyThing *self, void */*closure*/) +{ + if (!self->thing) { + PyErr_SetString(PyExc_ValueError, "Thing has been removed from the system."); + return nullptr; + } + ParamList settings; + QMetaObject::invokeMethod(self->thing, "settings", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ParamList, settings)); + PyObject *ret = PyParam_FromParamList(settings); + Py_INCREF(ret); + return ret; +} + static PyObject * PyThing_emitEvent(PyThing* self, PyObject* args) { char *eventTypeIdStr = nullptr; @@ -126,7 +162,9 @@ static PyObject * PyThing_emitEvent(PyThing* self, PyObject* args) } static PyGetSetDef PyThing_getseters[] = { - {"name", (getter)PyThing_getName, (setter)PyThing_setName, "Thingname", nullptr}, + {"name", (getter)PyThing_getName, (setter)PyThing_setName, "Thing name", nullptr}, + {"settings", (getter)PyThing_getSettings, (setter)PyThing_setSettings, "Thing settings", nullptr}, + {"settingChanged", (getter)PyThing_settingChanged, nullptr, "Signal for changed settings", nullptr}, {nullptr , nullptr, nullptr, nullptr, nullptr} /* Sentinel */ }; diff --git a/libnymea-core/integrations/python/pythingactioninfo.h b/libnymea-core/integrations/python/pythingactioninfo.h index fbe98890..08dc1898 100644 --- a/libnymea-core/integrations/python/pythingactioninfo.h +++ b/libnymea-core/integrations/python/pythingactioninfo.h @@ -27,8 +27,6 @@ static int PyThingActionInfo_init(PyThingActionInfo */*self*/, PyObject */*args* static void PyThingActionInfo_dealloc(PyThingActionInfo * self) { - // FIXME: Why is this not called? Seems we're leaking... - Q_ASSERT(false); Py_TYPE(self)->tp_free(self); } @@ -54,6 +52,7 @@ static PyObject * PyThingActionInfo_finish(PyThingActionInfo* self, PyObject* ar static PyMemberDef PyThingActionInfo_members[] = { {"thing", T_OBJECT_EX, offsetof(PyThingActionInfo, pyThing), 0, "Thing this action is for"}, {"actionTypeId", T_OBJECT_EX, offsetof(PyThingActionInfo, pyActionTypeId), 0, "The action type id for this action"}, + {"params", T_OBJECT_EX, offsetof(PyThingActionInfo, pyParams), 0, "The params for this action"}, {nullptr, 0, 0, 0, nullptr} /* Sentinel */ }; diff --git a/libnymea-core/integrations/pythonintegrationplugin.cpp b/libnymea-core/integrations/pythonintegrationplugin.cpp index 85717e38..45740c50 100644 --- a/libnymea-core/integrations/pythonintegrationplugin.cpp +++ b/libnymea-core/integrations/pythonintegrationplugin.cpp @@ -129,7 +129,7 @@ void PythonIntegrationPlugin::initPython() // We'll be using asyncio everywhere, so let's import it right away s_asyncio = PyImport_ImportModule("asyncio"); - // Need to release ths lock from the main thread before spawning new threads + // Need to release the lock from the main thread before spawning new threads s_mainThread = PyEval_SaveThread(); } @@ -150,6 +150,8 @@ bool PythonIntegrationPlugin::loadScript(const QString &scriptFile) } m_metaData = PluginMetadata(jsonDoc.object()); + qWarning() << "main thread" << QThread::currentThread(); + PyGILState_STATE s = PyGILState_Ensure(); @@ -229,10 +231,13 @@ void PythonIntegrationPlugin::setupThing(ThingSetupInfo *info) PyGILState_Release(s); }); connect(info, &ThingSetupInfo::destroyed, this, [=](){ - PyGILState_STATE s = PyGILState_Ensure(); + PyEval_RestoreThread(s_mainThread); + +// PyGILState_STATE s = PyGILState_Ensure(); pyInfo->info = nullptr; Py_DECREF(pyInfo); - PyGILState_Release(s); +// PyGILState_Release(s); + s_mainThread = PyEval_SaveThread(); }); @@ -262,7 +267,7 @@ void PythonIntegrationPlugin::executeAction(ThingActionInfo *info) connect(info, &ThingActionInfo::destroyed, this, [=](){ PyGILState_STATE s = PyGILState_Ensure(); pyInfo->pyActionTypeId = nullptr; - Py_DECREF(pyInfo->pyActionTypeId); + Py_XDECREF(pyInfo->pyActionTypeId); pyInfo->info = nullptr; Py_DECREF(pyInfo); PyGILState_Release(s); @@ -435,7 +440,7 @@ void PythonIntegrationPlugin::callPluginFunction(const QString &function, PyObje dumpError(); - PyObject *future = PyObject_CallFunctionObjArgs(pFunc, param, nullptr); + PyObject *result = PyObject_CallFunctionObjArgs(pFunc, param, nullptr); Py_XDECREF(pFunc); @@ -446,36 +451,51 @@ void PythonIntegrationPlugin::callPluginFunction(const QString &function, PyObje return; } - if (QByteArray(future->ob_type->tp_name) != "coroutine") { + if (QByteArray(result->ob_type->tp_name) != "coroutine") { + Py_DECREF(result); PyGILState_Release(s); return; } +// PyObject *coro = result; + +// PyObject *get_running_loop = PyObject_GetAttrString(s_asyncio, "get_event_loop"); +// PyObject *loop = PyObject_CallFunctionObjArgs(get_running_loop, nullptr); +// Py_DECREF(get_running_loop); +// PyObject *run_in_executor = PyObject_GetAttrString(loop, "run_in_executor"); +// result = PyObject_CallFunctionObjArgs(run_in_executor, Py_None, coro); + +// Py_DECREF(result); +// Py_DECREF(coro); // Spawn a event loop for python PyObject *new_event_loop = PyObject_GetAttrString(s_asyncio, "new_event_loop"); PyObject *loop = PyObject_CallFunctionObjArgs(new_event_loop, nullptr); - PyObject *run_coroutine_threadsafe = PyObject_GetAttrString(loop, "create_task"); - PyObject *task = PyObject_CallFunctionObjArgs(run_coroutine_threadsafe, future, nullptr); + Py_DECREF(new_event_loop); + + PyObject *create_task = PyObject_GetAttrString(loop, "create_task"); + PyObject *task = PyObject_CallFunctionObjArgs(create_task, result, nullptr); dumpError(); + Py_DECREF(result); + PyObject *add_done_callback = PyObject_GetAttrString(task, "add_done_callback"); dumpError(); PyObject *task_done = PyObject_GetAttrString(s_nymeaModule, "task_done"); - PyObject *result = PyObject_CallFunctionObjArgs(add_done_callback, task_done, nullptr); + result = PyObject_CallFunctionObjArgs(add_done_callback, task_done, nullptr); dumpError(); PyObject *run_until_complete = PyObject_GetAttrString(loop, "run_until_complete"); QtConcurrent::run([=](){ + qWarning() << "new thread for func" << function << QThread::currentThread(); PyGILState_STATE s = PyGILState_Ensure(); PyObject_CallFunctionObjArgs(run_until_complete, task, nullptr); PyGILState_Release(s); }); - Py_DECREF(new_event_loop); Py_DECREF(loop); - Py_DECREF(run_coroutine_threadsafe); + Py_DECREF(create_task); Py_DECREF(task); Py_DECREF(add_done_callback); Py_DECREF(result); diff --git a/libnymea-core/jsonrpc/devicehandler.cpp b/libnymea-core/jsonrpc/devicehandler.cpp index 9e306d95..120992b1 100644 --- a/libnymea-core/jsonrpc/devicehandler.cpp +++ b/libnymea-core/jsonrpc/devicehandler.cpp @@ -865,7 +865,6 @@ JsonReply *DeviceHandler::ExecuteAction(const QVariantMap ¶ms, const JsonCon ThingActionInfo *info = NymeaCore::instance()->executeAction(action); connect(info, &ThingActionInfo::finished, jsonReply, [info, jsonReply, locale](){ - qWarning() << "finished..."; QVariantMap data; data.insert("deviceError", enumValueName(info->status()).replace("Thing", "Device")); if (!info->displayMessage().isEmpty()) { diff --git a/libnymea/integrations/thing.cpp b/libnymea/integrations/thing.cpp index 9f3bdbba..eab641fd 100644 --- a/libnymea/integrations/thing.cpp +++ b/libnymea/integrations/thing.cpp @@ -183,6 +183,7 @@ ThingClass Thing::thingClass() const /*! Returns the name of this Thing. This is visible to the user. */ QString Thing::name() const { + qWarning() << "thing name called"; return m_name; } @@ -233,6 +234,7 @@ void Thing::setParamValue(const ParamTypeId ¶mTypeId, const QVariant &value) ParamList Thing::settings() const { + qWarning() << "thing settings called"; return m_settings; } diff --git a/libnymea/integrations/thing.h b/libnymea/integrations/thing.h index e8f03ab3..2e739aa0 100644 --- a/libnymea/integrations/thing.h +++ b/libnymea/integrations/thing.h @@ -107,8 +107,8 @@ public: ThingClass thingClass() const; - QString name() const; - void setName(const QString &name); + Q_INVOKABLE QString name() const; + Q_INVOKABLE void setName(const QString &name); ParamList params() const; bool hasParam(const ParamTypeId ¶mTypeId) const; @@ -117,12 +117,12 @@ public: QVariant paramValue(const ParamTypeId ¶mTypeId) const; void setParamValue(const ParamTypeId ¶mName, const QVariant &value); - ParamList settings() const; - bool hasSetting(const ParamTypeId ¶mTypeId) const; - void setSettings(const ParamList &settings); + Q_INVOKABLE ParamList settings() const; + Q_INVOKABLE bool hasSetting(const ParamTypeId ¶mTypeId) const; + Q_INVOKABLE void setSettings(const ParamList &settings); - QVariant setting(const ParamTypeId ¶mTypeId) const; - void setSettingValue(const ParamTypeId ¶mTypeId, const QVariant &value); + Q_INVOKABLE QVariant setting(const ParamTypeId ¶mTypeId) const; + Q_INVOKABLE void setSettingValue(const ParamTypeId ¶mTypeId, const QVariant &value); States states() const; bool hasState(const StateTypeId &stateTypeId) const;