diff --git a/libnymea-core/integrations/python/pything.h b/libnymea-core/integrations/python/pything.h index fd504d24..5fc9883e 100644 --- a/libnymea-core/integrations/python/pything.h +++ b/libnymea-core/integrations/python/pything.h @@ -63,6 +63,23 @@ static PyTypeObject PyThingType = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; +static void registerThingType(PyObject *module) +{ + PyThingType.tp_new = PyType_GenericNew; + PyThingType.tp_dealloc= reinterpret_cast(PyThing_dealloc); + PyThingType.tp_basicsize = sizeof(PyThing); + PyThingType.tp_flags = Py_TPFLAGS_DEFAULT; + PyThingType.tp_doc = "Thing class"; + PyThingType.tp_methods = PyThing_methods; +// PyThingType.tp_members = PyThingSetupInfo_members; + PyThingType.tp_init = reinterpret_cast(PyThing_init); + + if (PyType_Ready(&PyThingType) < 0) { + return; + } + PyModule_AddObject(module, "Thing", reinterpret_cast(&PyThingType)); +} + #pragma GCC diagnostic pop diff --git a/libnymea-core/integrations/python/pythingdescriptor.h b/libnymea-core/integrations/python/pythingdescriptor.h new file mode 100644 index 00000000..91fe2773 --- /dev/null +++ b/libnymea-core/integrations/python/pythingdescriptor.h @@ -0,0 +1,117 @@ +#ifndef PYTHINGDESCRIPTOR_H +#define PYTHINGDESCRIPTOR_H + +#include +#include "structmember.h" + +#include "integrations/thingdescriptor.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#pragma GCC diagnostic ignored "-Wwrite-strings" + + +typedef struct { + PyObject_HEAD + PyObject* thingClassId; + PyObject* name; + PyObject* description; + ThingDescriptor descriptor; +} PyThingDescriptor; + +static PyMethodDef PyThingDescriptor_methods[] = { +// { "finish", (PyCFunction)PyThingDiscoveryInfo_finish, METH_VARARGS, "finish a discovery" }, + {nullptr, nullptr, 0, nullptr} // sentinel +}; + +static PyMemberDef PyThingDescriptor_members[] = { + {"thingClassId", T_OBJECT_EX, offsetof(PyThingDescriptor, thingClassId), 0, "Descriptor thingClassId"}, + {"name", T_OBJECT_EX, offsetof(PyThingDescriptor, name), 0, "Descriptor name"}, + {"description", T_OBJECT_EX, offsetof(PyThingDescriptor, description), 0, "Descriptor description"}, + {nullptr, 0, 0, 0, nullptr} /* Sentinel */ +}; + + +static int PyThingDescriptor_init(PyThingDescriptor *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"thingClassId", "name", "description", nullptr}; + PyObject *thingClassId = nullptr, *name = nullptr, *description = nullptr, *tmp = nullptr; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &thingClassId, &name, &description)) + return -1; + + if (thingClassId) { + tmp = self->thingClassId; + Py_INCREF(thingClassId); + self->thingClassId = thingClassId; + Py_XDECREF(tmp); + } + if (name) { + tmp = self->name; + Py_INCREF(name); + self->name = name; + Py_XDECREF(tmp); + } + if (description) { + tmp = self->description; + Py_INCREF(description); + self->description = description; + Py_XDECREF(tmp); + } + return 0; +} + +//static PyGetSetDef PyThingDescriptor_getsetters[] = { +// {"name", (getter) PyThingDescriptor_getName, (setter) PyThingDescriptir_setName, +// "Descriptor name", NULL}, +// {"last", (getter) Custom_getlast, (setter) Custom_setlast, +// "last name", NULL}, +// {NULL} /* Sentinel */ +//}; + +static PyTypeObject PyThingDescriptorType = { + PyVarObject_HEAD_INIT(NULL, 0) + "nymea.ThingDescriptor", /* tp_name */ + sizeof(PyThingDescriptor), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Noddy objects", /* tp_doc */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + + + +static void registerThingDescriptorType(PyObject *module) +{ + PyThingDescriptorType.tp_new = PyType_GenericNew; + PyThingDescriptorType.tp_basicsize = sizeof(PyThingDescriptor); + PyThingDescriptorType.tp_flags = Py_TPFLAGS_DEFAULT; + PyThingDescriptorType.tp_doc = "ThingDescriptor class"; + PyThingDescriptorType.tp_methods = PyThingDescriptor_methods; + PyThingDescriptorType.tp_members = PyThingDescriptor_members; + PyThingDescriptorType.tp_init = reinterpret_cast(PyThingDescriptor_init); + + if (PyType_Ready(&PyThingDescriptorType) < 0) { + return; + } + PyModule_AddObject(module, "ThingDescriptor", reinterpret_cast(&PyThingDescriptorType)); +} + +#pragma GCC diagnostic pop + +#endif // PYTHINGDESCRIPTOR_H diff --git a/libnymea-core/integrations/python/pythingdiscoveryinfo.h b/libnymea-core/integrations/python/pythingdiscoveryinfo.h index e1e56747..ceb955fb 100644 --- a/libnymea-core/integrations/python/pythingdiscoveryinfo.h +++ b/libnymea-core/integrations/python/pythingdiscoveryinfo.h @@ -5,116 +5,18 @@ #include #include "structmember.h" +#include "pythingdescriptor.h" + #include "integrations/thingdiscoveryinfo.h" #include +#include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Winvalid-offsetof" #pragma GCC diagnostic ignored "-Wwrite-strings" - -typedef struct { - PyObject_HEAD - PyObject* thingClassId; - PyObject* name; - PyObject* description; - ThingDescriptor descriptor; -} PyThingDescriptor; - -static PyMethodDef PyThingDescriptor_methods[] = { -// { "finish", (PyCFunction)PyThingDiscoveryInfo_finish, METH_VARARGS, "finish a discovery" }, - {nullptr, nullptr, 0, nullptr} // sentinel -}; - -static PyMemberDef PyThingDescriptor_members[] = { - {"thingClassId", T_OBJECT_EX, offsetof(PyThingDescriptor, thingClassId), 0, "Descriptor thingClassId"}, - {"name", T_OBJECT_EX, offsetof(PyThingDescriptor, name), 0, "Descriptor name"}, - {"description", T_OBJECT_EX, offsetof(PyThingDescriptor, description), 0, "Descriptor description"}, - {nullptr, 0, 0, 0, nullptr} /* Sentinel */ -}; - - -static int PyThingDescriptor_init(PyThingDescriptor *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"thingClassId", "name", "description", nullptr}; - PyObject *thingClassId = nullptr, *name = nullptr, *description = nullptr, *tmp = nullptr; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &thingClassId, &name, &description)) - return -1; - - if (thingClassId) { - tmp = self->thingClassId; - Py_INCREF(thingClassId); - self->thingClassId = thingClassId; - Py_XDECREF(tmp); - } - if (name) { - tmp = self->name; - Py_INCREF(name); - self->name = name; - Py_XDECREF(tmp); - } - if (description) { - tmp = self->description; - Py_INCREF(description); - self->description = description; - Py_XDECREF(tmp); - } - return 0; -} - -//static PyGetSetDef PyThingDescriptor_getsetters[] = { -// {"name", (getter) PyThingDescriptor_getName, (setter) PyThingDescriptir_setName, -// "Descriptor name", NULL}, -// {"last", (getter) Custom_getlast, (setter) Custom_setlast, -// "last name", NULL}, -// {NULL} /* Sentinel */ -//}; - -static PyTypeObject PyThingDescriptorType = { - PyVarObject_HEAD_INIT(NULL, 0) - "nymea.ThingDescriptor", /* tp_name */ - sizeof(PyThingDescriptor), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_reserved */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - "Noddy objects", /* tp_doc */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -}; - - - - - - - - - - - - - - - - - - typedef struct { PyObject_HEAD ThingDiscoveryInfo* ptrObj; @@ -221,9 +123,26 @@ static PyTypeObject PyThingDiscoveryInfoType = { +static void registerThingDiscoveryInfoType(PyObject *module) +{ + PyThingDiscoveryInfoType.tp_new = PyType_GenericNew; + PyThingDiscoveryInfoType.tp_basicsize = sizeof(PyThingDiscoveryInfo); + PyThingDiscoveryInfoType.tp_dealloc = reinterpret_cast(PyThingDiscoveryInfo_dealloc); + PyThingDiscoveryInfoType.tp_flags = Py_TPFLAGS_DEFAULT; + PyThingDiscoveryInfoType.tp_doc = "ThingDiscoveryInfo class"; + PyThingDiscoveryInfoType.tp_methods = PyThingDiscoveryInfo_methods; + PyThingDiscoveryInfoType.tp_init = reinterpret_cast(PyThingDiscoveryInfo_init); + if (PyType_Ready(&PyThingDiscoveryInfoType) < 0) { + 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/python/pythingsetupinfo.h b/libnymea-core/integrations/python/pythingsetupinfo.h index bd6f2ddb..e62cba5b 100644 --- a/libnymea-core/integrations/python/pythingsetupinfo.h +++ b/libnymea-core/integrations/python/pythingsetupinfo.h @@ -63,10 +63,10 @@ static PyTypeObject PyThingSetupInfoType = { sizeof(PyThingSetupInfo), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ - 0, /* tp_print */ + 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_reserved */ + 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ @@ -78,10 +78,27 @@ static PyTypeObject PyThingSetupInfoType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ - "Noddy objects", /* tp_doc */ + "Noddy objects", /* tp_doc */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; +static void registerThingSetupInfoType(PyObject *module) { + PyThingSetupInfoType.tp_new = PyType_GenericNew; + PyThingSetupInfoType.tp_dealloc=(destructor) PyThingSetupInfo_dealloc; + PyThingSetupInfoType.tp_basicsize = sizeof(PyThingSetupInfo); + PyThingSetupInfoType.tp_flags = Py_TPFLAGS_DEFAULT; + PyThingSetupInfoType.tp_doc = "ThingSetupInfo class"; + PyThingSetupInfoType.tp_methods = PyThingSetupInfo_methods; +// PyThingSetupInfoType.tp_members = PyThingSetupInfo_members; + PyThingSetupInfoType.tp_init = (initproc)PyThingSetupInfo_init; + + if (PyType_Ready(&PyThingSetupInfoType) < 0) { + return; + } + PyModule_AddObject(module, "ThingSetupInfo", (PyObject *)&PyThingSetupInfoType); +} + + #pragma GCC diagnostic pop diff --git a/libnymea-core/integrations/pythonintegrationplugin.cpp b/libnymea-core/integrations/pythonintegrationplugin.cpp index b87e1a39..58a6b1c9 100644 --- a/libnymea-core/integrations/pythonintegrationplugin.cpp +++ b/libnymea-core/integrations/pythonintegrationplugin.cpp @@ -1,4 +1,6 @@ #include + +#include "python/pything.h" #include "python/pythingdiscoveryinfo.h" #include "python/pythingsetupinfo.h" @@ -57,40 +59,10 @@ PyMODINIT_FUNC PyInit_nymea(void) PySys_SetObject("stderr", m); - QMetaEnum thingErrorEnum = QMetaEnum::fromType(); - for (int i = 0; i < thingErrorEnum.keyCount(); i++) { - PyModule_AddObject(m, thingErrorEnum.key(i), PyLong_FromLong(thingErrorEnum.value(i))); - } - - - PyThingDiscoveryInfoType.tp_new = PyType_GenericNew; - PyThingDiscoveryInfoType.tp_basicsize=sizeof(PyThingDiscoveryInfo); - PyThingDiscoveryInfoType.tp_dealloc=(destructor) PyThingDiscoveryInfo_dealloc; - PyThingDiscoveryInfoType.tp_flags=Py_TPFLAGS_DEFAULT; - PyThingDiscoveryInfoType.tp_doc="ThingDiscoveryInfo class"; - PyThingDiscoveryInfoType.tp_methods=PyThingDiscoveryInfo_methods; - PyThingDiscoveryInfoType.tp_init=(initproc)PyThingDiscoveryInfo_init; - - if (PyType_Ready(&PyThingDiscoveryInfoType) < 0) { - qCWarning(dcThingManager()) << "Error creating PyThingDiscoveryInfo"; - return nullptr; - } - PyModule_AddObject(m, "ThingDiscoveryInfo", (PyObject *)&PyThingDiscoveryInfoType); - - PyThingDescriptorType.tp_new = PyType_GenericNew; - PyThingDescriptorType.tp_basicsize = sizeof(PyThingDescriptor); - PyThingDescriptorType.tp_flags = Py_TPFLAGS_DEFAULT; - PyThingDescriptorType.tp_doc = "ThingDescriptor class"; - PyThingDescriptorType.tp_methods = PyThingDescriptor_methods; - PyThingDescriptorType.tp_members = PyThingDescriptor_members; - PyThingDescriptorType.tp_init = (initproc)PyThingDescriptor_init; - - if (PyType_Ready(&PyThingDescriptorType) < 0) { - qCWarning(dcThingManager()) << "Error creating PyThingDescriptor"; - return nullptr; - } - PyModule_AddObject(m, "ThingDescriptor", (PyObject *)&PyThingDescriptorType); - + registerThingType(m); + registerThingDescriptorType(m); + registerThingDiscoveryInfoType(m); + registerThingSetupInfoType(m); return m; } @@ -100,12 +72,16 @@ PythonIntegrationPlugin::PythonIntegrationPlugin(QObject *parent) : IntegrationP } +PythonIntegrationPlugin::~PythonIntegrationPlugin() +{ + m_eventLoop.cancel(); + m_eventLoop.waitForFinished(); +} + void PythonIntegrationPlugin::initPython() { - // TODO: Call this just once and call Py_Finalize() PyImport_AppendInittab("nymea", PyInit_nymea); Py_InitializeEx(0); - } bool PythonIntegrationPlugin::loadScript(const QString &scriptFile) @@ -154,19 +130,14 @@ bool PythonIntegrationPlugin::loadScript(const QString &scriptFile) s_modules.insert(m_module, m_interpreter); - QtConcurrent::run([=](){ + // Start an event loop for this plugin in its own thread + m_eventLoop = QtConcurrent::run([=](){ PyObject *loop = PyObject_GetAttrString(m_module, "loop"); dumpError(); -// PyObject *stop = PyObject_GetAttrString(loop, "stop"); -// PyObject *call_soon = PyObject_GetAttrString(loop, "call_soon"); PyObject *run_forever = PyObject_GetAttrString(loop, "run_forever"); -// PyObject *run_until_complete = PyObject_GetAttrString(loop, "run_until_complete"); - qCDebug(dcThingManager()) << "Starting python event loop"; dumpError(); PyObject_CallFunctionObjArgs(run_forever, nullptr); - qCDebug(dcThingManager()) << "stopped python event loop"; dumpError(); - }); @@ -176,7 +147,6 @@ bool PythonIntegrationPlugin::loadScript(const QString &scriptFile) QJsonObject PythonIntegrationPlugin::metaData() const { return QJsonObject::fromVariantMap(m_metaData); - } void PythonIntegrationPlugin::init() @@ -208,7 +178,7 @@ void PythonIntegrationPlugin::discoverThings(ThingDiscoveryInfo *info) return; } - PyThingDiscoveryInfo *pyInfo = (PyThingDiscoveryInfo*)_PyObject_New(&PyThingDiscoveryInfoType); + PyThingDiscoveryInfo *pyInfo = reinterpret_cast(_PyObject_New(&PyThingDiscoveryInfoType)); pyInfo->ptrObj = info; connect(info, &ThingDiscoveryInfo::finished, this, [=](){ @@ -241,7 +211,7 @@ void PythonIntegrationPlugin::setupThing(ThingSetupInfo *info) return; } - PyThingSetupInfo *pyInfo = (PyThingSetupInfo*)_PyObject_New(&PyThingSetupInfoType); + PyThingSetupInfo *pyInfo = reinterpret_cast(_PyObject_New(&PyThingSetupInfoType)); pyInfo->ptrObj = info; connect(info, &ThingSetupInfo::finished, this, [=](){ @@ -287,7 +257,6 @@ void PythonIntegrationPlugin::exportIds() QVariantMap vendor = vendorVariant.toMap(); QString vendorIdName = vendor.value("name").toString() + "VendorId"; QString vendorId = vendor.value("id").toString(); - qCDebug(dcThingManager()) << "Exporting:" << vendorIdName; PyModule_AddStringConstant(m_module, vendorIdName.toUtf8(), vendorId.toUtf8()); foreach (const QVariant &thingClassVariant, vendor.value("thingClasses").toList()) { @@ -297,6 +266,5 @@ void PythonIntegrationPlugin::exportIds() PyModule_AddStringConstant(m_module, thingClassIdName.toUtf8(), thingClassId.toUtf8()); } } - } diff --git a/libnymea-core/integrations/pythonintegrationplugin.h b/libnymea-core/integrations/pythonintegrationplugin.h index e4f8cc70..852217fc 100644 --- a/libnymea-core/integrations/pythonintegrationplugin.h +++ b/libnymea-core/integrations/pythonintegrationplugin.h @@ -5,6 +5,7 @@ #include #include +#include extern "C" { typedef struct _object PyObject; @@ -17,6 +18,7 @@ class PythonIntegrationPlugin : public IntegrationPlugin Q_OBJECT public: explicit PythonIntegrationPlugin(QObject *parent = nullptr); + ~PythonIntegrationPlugin(); static void initPython(); @@ -41,6 +43,7 @@ private: QVariantMap m_metaData; PyObject *m_module = nullptr; PyThreadState *m_interpreter = nullptr; + QFuture m_eventLoop; }; diff --git a/libnymea-core/libnymea-core.pro b/libnymea-core/libnymea-core.pro index 9e6dd20d..dba852e4 100644 --- a/libnymea-core/libnymea-core.pro +++ b/libnymea-core/libnymea-core.pro @@ -21,6 +21,7 @@ RESOURCES += $$top_srcdir/icons.qrc \ HEADERS += nymeacore.h \ integrations/plugininfocache.h \ integrations/python/pything.h \ + integrations/python/pythingdescriptor.h \ integrations/python/pythingdiscoveryinfo.h \ integrations/python/pythingsetupinfo.h \ integrations/thingmanagerimplementation.h \ diff --git a/libnymea/loggingcategories.h b/libnymea/loggingcategories.h index b2f8849d..68557d79 100644 --- a/libnymea/loggingcategories.h +++ b/libnymea/loggingcategories.h @@ -89,6 +89,9 @@ Q_DECLARE_LOGGING_CATEGORY(dcMqtt) Q_DECLARE_LOGGING_CATEGORY(dcTranslations) Q_DECLARE_LOGGING_CATEGORY(dcCoap) Q_DECLARE_LOGGING_CATEGORY(dcI2C) +Q_DECLARE_LOGGING_CATEGORY(dcIntegrations) +Q_DECLARE_LOGGING_CATEGORY(dcJsIntegrations) +Q_DECLARE_LOGGING_CATEGORY(dcPythonIntegrations) /*