more work

This commit is contained in:
Michael Zanetti 2020-06-18 11:38:35 +02:00
parent 59c1c8e9f2
commit f97e5bf4d0
9 changed files with 389 additions and 91 deletions

View File

@ -0,0 +1,151 @@
#ifndef PYPARAM_H
#define PYPARAM_H
#include <Python.h>
#include "structmember.h"
#include "types/param.h"
#include "loggingcategories.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
#pragma GCC diagnostic ignored "-Wwrite-strings"
typedef struct _pyparam {
PyObject_HEAD
PyObject* pyParamTypeId = nullptr;
PyObject* pyValue = nullptr;
} PyParam;
static void PyParam_dealloc(PyParam * self) {
// FIXME: Why is this not called? Seems we're leaking...
Q_ASSERT(false);
Py_XDECREF(self->pyParamTypeId);
Py_XDECREF(self->pyValue);
Py_TYPE(self)->tp_free(self);
}
static PyMethodDef PyParam_methods[] = {
{nullptr, nullptr, 0, nullptr} // sentinel
};
static PyMemberDef PyParam_members[] = {
{"paramTypeId", T_OBJECT_EX, offsetof(PyParam, pyParamTypeId), 0, "Param type ID"},
{"value", T_OBJECT_EX, offsetof(PyParam, pyValue), 0, "Param value"},
{nullptr, 0, 0, 0, nullptr} /* Sentinel */
};
static int PyParam_init(PyParam *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"paramTypeId", "value", nullptr};
PyObject *paramTypeId = nullptr, *value = nullptr;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &paramTypeId, &value))
return -1;
if (paramTypeId) {
Py_XDECREF(self->pyParamTypeId);
Py_INCREF(paramTypeId);
self->pyParamTypeId = paramTypeId;
}
if (value) {
Py_XDECREF(self->pyValue);
Py_INCREF(value);
self->pyValue = value;
}
return 0;
}
static PyTypeObject PyParamType = {
PyVarObject_HEAD_INIT(NULL, 0)
"nymea.Param", /* tp_name */
sizeof(PyParam), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)PyParam_dealloc,/* 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 */
0, /* 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 PyParam* PyParam_fromParam(const Param &param)
{
PyParam *pyParam = PyObject_New(PyParam, &PyParamType);
pyParam->pyParamTypeId = PyUnicode_FromString(param.paramTypeId().toString().toUtf8());
switch (param.value().type()) {
case QVariant::Bool:
pyParam->pyValue = PyBool_FromLong(*(long*)param.value().data());
break;
case QVariant::Int:
case QVariant::UInt:
case QVariant::LongLong:
case QVariant::ULongLong:
pyParam->pyValue = PyLong_FromLong(*(long*)param.value().data());
break;
case QVariant::String:
case QVariant::ByteArray:
pyParam->pyValue = PyUnicode_FromString(param.value().toString().toUtf8());
break;
case QVariant::Double:
pyParam->pyValue = PyFloat_FromDouble(param.value().toDouble());
break;
case QVariant::Invalid:
pyParam->pyValue = Py_None;
Py_INCREF(pyParam->pyValue);
break;
default:
qCWarning(dcThingManager) << "Unhandled data type in conversion from Param to PyParam!";
pyParam->pyValue = Py_None;
Py_INCREF(pyParam->pyValue);
break;
}
return pyParam;
}
static PyObject* PyParam_FromParamList(const ParamList &params)
{
PyObject* result = PyTuple_New(params.count());
for (int i = 0; i < params.count(); i++) {
PyTuple_SET_ITEM(result, i, (PyObject*)PyParam_fromParam(params.at(i)));
}
return result;
}
static void registerParamType(PyObject *module)
{
PyParamType.tp_new = PyType_GenericNew;
PyParamType.tp_basicsize = sizeof(PyParam);
PyParamType.tp_dealloc=(destructor) PyParam_dealloc;
PyParamType.tp_flags = Py_TPFLAGS_DEFAULT;
PyParamType.tp_doc = "Param class";
PyParamType.tp_methods = PyParam_methods;
PyParamType.tp_members = PyParam_members;
PyParamType.tp_init = reinterpret_cast<initproc>(PyParam_init);
if (PyType_Ready(&PyParamType) < 0) {
return;
}
PyModule_AddObject(module, "Param", reinterpret_cast<PyObject*>(&PyParamType));
}
#pragma GCC diagnostic pop
#endif // PYPARAM_H

View File

@ -4,6 +4,8 @@
#include <Python.h> #include <Python.h>
#include "structmember.h" #include "structmember.h"
#include "pyparam.h"
#include "integrations/thing.h" #include "integrations/thing.h"
#include "loggingcategories.h" #include "loggingcategories.h"
@ -15,7 +17,7 @@
typedef struct _thing { typedef struct _thing {
PyObject_HEAD PyObject_HEAD
Thing *ptrObj; Thing *thing;
} PyThing; } PyThing;
@ -25,26 +27,24 @@ static int PyThing_init(PyThing */*self*/, PyObject */*args*/, PyObject */*kwds*
static void PyThing_dealloc(PyThing * self) { static void PyThing_dealloc(PyThing * self) {
// FIXME: Why is this not called? Seems we're leaking...
Q_ASSERT(false);
Py_TYPE(self)->tp_free(self); Py_TYPE(self)->tp_free(self);
} }
static PyObject *PyThing_getName(PyThing *self, void */*closure*/) static PyObject *PyThing_getName(PyThing *self, void */*closure*/)
{ {
if (!self->ptrObj) { if (!self->thing) {
PyErr_SetString(PyExc_ValueError, "Thing has been removed from the system."); PyErr_SetString(PyExc_ValueError, "Thing has been removed from the system.");
return nullptr; return nullptr;
} }
// FIXME: Needs blocking queued connection // FIXME: Needs blocking queued connection
PyObject *ret = PyUnicode_FromString(self->ptrObj->name().toUtf8().data()); PyObject *ret = PyUnicode_FromString(self->thing->name().toUtf8().data());
Py_INCREF(ret); Py_INCREF(ret);
return ret; return ret;
} }
static int PyThing_setName(PyThing *self, PyObject *value, void */*closure*/){ static int PyThing_setName(PyThing *self, PyObject *value, void */*closure*/){
// FIXME: Needs queued connection // FIXME: Needs queued connection
self->ptrObj->setName(QString(PyUnicode_AsUTF8(value))); self->thing->setName(QString(PyUnicode_AsUTF8(value)));
return 0; return 0;
} }
@ -67,8 +67,8 @@ static PyObject * PyThing_setStateValue(PyThing* self, PyObject* args)
QVariant value(bytes); QVariant value(bytes);
if (self->ptrObj != nullptr) { if (self->thing != nullptr) {
QMetaObject::invokeMethod(self->ptrObj, "setStateValue", Qt::QueuedConnection, Q_ARG(StateTypeId, stateTypeId), Q_ARG(QVariant, value)); QMetaObject::invokeMethod(self->thing, "setStateValue", Qt::QueuedConnection, Q_ARG(StateTypeId, stateTypeId), Q_ARG(QVariant, value));
} }
Py_XDECREF(repr); Py_XDECREF(repr);
@ -88,22 +88,39 @@ static PyObject * PyThing_emitEvent(PyThing* self, PyObject* args)
} }
EventTypeId eventTypeId = EventTypeId(eventTypeIdStr); EventTypeId eventTypeId = EventTypeId(eventTypeIdStr);
ParamList params;
// if (valueObj != nullptr) { if (valueObj != nullptr) {
// PyObject* repr = PyObject_Repr(valueObj); PyObject *iter = PyObject_GetIter(valueObj);
// PyObject* str = PyUnicode_AsEncodedString(repr, "utf-8", "~E~");
// const char *bytes = PyBytes_AS_STRING(str);
// QVariant value(bytes); while (iter) {
// } PyObject *next = PyIter_Next(iter);
if (!next) {
break;
}
if (next->ob_type != &PyParamType) {
qCWarning(dcThingManager()) << "Invalid parameter passed in param list";
continue;
}
PyParam *pyParam = reinterpret_cast<PyParam*>(next);
ParamTypeId paramTypeId = ParamTypeId(PyUnicode_AsUTF8(pyParam->pyParamTypeId));
if (self->ptrObj != nullptr) { // Is there a better way to convert a PyObject to a QVariant?
QMetaObject::invokeMethod(self->ptrObj, "emitEvent", Qt::QueuedConnection, Q_ARG(EventTypeId, eventTypeId)); PyObject* repr = PyObject_Repr(pyParam->pyValue);
PyObject* str = PyUnicode_AsEncodedString(repr, "utf-8", "~E~");
const char *bytes = PyBytes_AS_STRING(str);
Py_XDECREF(repr);
Py_XDECREF(str);
QVariant value(bytes);
params.append(Param(paramTypeId, value));
}
} }
// Py_XDECREF(repr); if (self->thing != nullptr) {
// Py_XDECREF(str); QMetaObject::invokeMethod(self->thing, "emitEvent", Qt::QueuedConnection, Q_ARG(EventTypeId, eventTypeId), Q_ARG(ParamList, params));
}
Py_RETURN_NONE; Py_RETURN_NONE;
} }
@ -153,7 +170,7 @@ static void registerThingType(PyObject *module)
PyThingType.tp_doc = "Thing class"; PyThingType.tp_doc = "Thing class";
PyThingType.tp_methods = PyThing_methods; PyThingType.tp_methods = PyThing_methods;
PyThingType.tp_getset = PyThing_getseters; PyThingType.tp_getset = PyThing_getseters;
// PyThingType.tp_members = PyThingSetupInfo_members; // PyThingType.tp_members = PyThingSetupInfo_members;
PyThingType.tp_init = reinterpret_cast<initproc>(PyThing_init); PyThingType.tp_init = reinterpret_cast<initproc>(PyThing_init);
if (PyType_Ready(&PyThingType) < 0) { if (PyType_Ready(&PyThingType) < 0) {

View File

@ -0,0 +1,111 @@
#ifndef PYTHINGACTIONINFO_H
#define PYTHINGACTIONINFO_H
#include <Python.h>
#include "structmember.h"
#include "pything.h"
#include "integrations/thingactioninfo.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
#pragma GCC diagnostic ignored "-Wwrite-strings"
typedef struct {
PyObject_HEAD
ThingActionInfo* info;
PyThing *pyThing;
PyObject *pyActionTypeId;
PyObject *pyParams;
} PyThingActionInfo;
static int PyThingActionInfo_init(PyThingActionInfo */*self*/, PyObject */*args*/, PyObject */*kwds*/) {
return 0;
}
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);
}
static PyObject * PyThingActionInfo_finish(PyThingActionInfo* self, PyObject* args) {
int status;
char *message = nullptr;
if (!PyArg_ParseTuple(args, "i|s", &status, &message)) {
PyErr_SetString(PyExc_TypeError, "Invalid arguments in finish call. Expected: finish(ThingError, message = \"\"");
return nullptr;
}
Thing::ThingError thingError = static_cast<Thing::ThingError>(status);
QString displayMessage = message != nullptr ? QString(message) : QString();
if (self->info) {
QMetaObject::invokeMethod(self->info, "finish", Qt::QueuedConnection, Q_ARG(Thing::ThingError, thingError), Q_ARG(QString, displayMessage));
}
Py_RETURN_NONE;
}
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"},
{nullptr, 0, 0, 0, nullptr} /* Sentinel */
};
static PyMethodDef PyThingActionInfo_methods[] = {
{ "finish", (PyCFunction)PyThingActionInfo_finish, METH_VARARGS, "finish an action" },
{nullptr, nullptr, 0, nullptr} // sentinel
};
static PyTypeObject PyThingActionInfoType = {
PyVarObject_HEAD_INIT(NULL, 0)
"nymea.ThingActionInfo", /* tp_name */
sizeof(PyThingActionInfo), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
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 registerThingActionInfoType(PyObject *module) {
PyThingActionInfoType.tp_new = PyType_GenericNew;
PyThingActionInfoType.tp_dealloc=(destructor) PyThingActionInfo_dealloc;
PyThingActionInfoType.tp_basicsize = sizeof(PyThingActionInfo);
PyThingActionInfoType.tp_flags = Py_TPFLAGS_DEFAULT;
PyThingActionInfoType.tp_doc = "ThingActionInfo class";
PyThingActionInfoType.tp_methods = PyThingActionInfo_methods;
PyThingActionInfoType.tp_members = PyThingActionInfo_members;
PyThingActionInfoType.tp_init = (initproc)PyThingActionInfo_init;
if (PyType_Ready(&PyThingActionInfoType) < 0) {
return;
}
PyModule_AddObject(module, "ThingActionInfo", (PyObject *)&PyThingActionInfoType);
}
#pragma GCC diagnostic pop
#endif // PYTHINGACTIONINFO_H

View File

@ -13,10 +13,9 @@
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
PyObject* thingClassId; PyObject* pyThingClassId;
PyObject* name; PyObject* pyName;
PyObject* description; PyObject* pyDescription;
ThingDescriptor descriptor;
} PyThingDescriptor; } PyThingDescriptor;
static PyMethodDef PyThingDescriptor_methods[] = { static PyMethodDef PyThingDescriptor_methods[] = {
@ -25,9 +24,9 @@ static PyMethodDef PyThingDescriptor_methods[] = {
}; };
static PyMemberDef PyThingDescriptor_members[] = { static PyMemberDef PyThingDescriptor_members[] = {
{"thingClassId", T_OBJECT_EX, offsetof(PyThingDescriptor, thingClassId), 0, "Descriptor thingClassId"}, {"thingClassId", T_OBJECT_EX, offsetof(PyThingDescriptor, pyThingClassId), 0, "Descriptor thingClassId"},
{"name", T_OBJECT_EX, offsetof(PyThingDescriptor, name), 0, "Descriptor name"}, {"name", T_OBJECT_EX, offsetof(PyThingDescriptor, pyName), 0, "Descriptor name"},
{"description", T_OBJECT_EX, offsetof(PyThingDescriptor, description), 0, "Descriptor description"}, {"description", T_OBJECT_EX, offsetof(PyThingDescriptor, pyDescription), 0, "Descriptor description"},
{nullptr, 0, 0, 0, nullptr} /* Sentinel */ {nullptr, 0, 0, 0, nullptr} /* Sentinel */
}; };
@ -35,28 +34,25 @@ static PyMemberDef PyThingDescriptor_members[] = {
static int PyThingDescriptor_init(PyThingDescriptor *self, PyObject *args, PyObject *kwds) static int PyThingDescriptor_init(PyThingDescriptor *self, PyObject *args, PyObject *kwds)
{ {
static char *kwlist[] = {"thingClassId", "name", "description", nullptr}; static char *kwlist[] = {"thingClassId", "name", "description", nullptr};
PyObject *thingClassId = nullptr, *name = nullptr, *description = nullptr, *tmp = nullptr; PyObject *thingClassId = nullptr, *name = nullptr, *description = nullptr;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &thingClassId, &name, &description)) if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &thingClassId, &name, &description))
return -1; return -1;
if (thingClassId) { if (thingClassId) {
tmp = self->thingClassId; Py_XDECREF(self->pyThingClassId);
Py_INCREF(thingClassId); Py_INCREF(thingClassId);
self->thingClassId = thingClassId; self->pyThingClassId = thingClassId;
Py_XDECREF(tmp);
} }
if (name) { if (name) {
tmp = self->name; Py_XDECREF(self->pyName);
Py_INCREF(name); Py_INCREF(name);
self->name = name; self->pyName = name;
Py_XDECREF(tmp);
} }
if (description) { if (description) {
tmp = self->description; Py_XDECREF(self->pyDescription);
Py_INCREF(description); Py_INCREF(description);
self->description = description; self->pyDescription = description;
Py_XDECREF(tmp);
} }
return 0; return 0;
} }

View File

@ -19,20 +19,16 @@
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
ThingDiscoveryInfo* ptrObj; ThingDiscoveryInfo* info;
} PyThingDiscoveryInfo; } PyThingDiscoveryInfo;
static int PyThingDiscoveryInfo_init(PyThingDiscoveryInfo */*self*/, PyObject */*args*/, PyObject */*kwds*/) static int PyThingDiscoveryInfo_init(PyThingDiscoveryInfo */*self*/, PyObject */*args*/, PyObject */*kwds*/) {
// initialize PyVoice Object
{
return 0; return 0;
} }
static void PyThingDiscoveryInfo_dealloc(PyThingDiscoveryInfo * self) static void PyThingDiscoveryInfo_dealloc(PyThingDiscoveryInfo * self) {
// destruct the object
{
// FIXME: Why is this not called? Seems we're leaking... // FIXME: Why is this not called? Seems we're leaking...
Q_ASSERT(false); Q_ASSERT(false);
Py_TYPE(self)->tp_free(self); Py_TYPE(self)->tp_free(self);
@ -41,20 +37,20 @@ static void PyThingDiscoveryInfo_dealloc(PyThingDiscoveryInfo * self)
static PyObject * PyThingDiscoveryInfo_finish(PyThingDiscoveryInfo* self, PyObject* args) static PyObject * PyThingDiscoveryInfo_finish(PyThingDiscoveryInfo* self, PyObject* args)
{ {
int status; int status;
char *message; char *message = nullptr;
if (PyArg_ParseTuple(args, "is", &status, &message)) { if (!PyArg_ParseTuple(args, "i|s", &status, &message)) {
(self->ptrObj)->finish(static_cast<Thing::ThingError>(status), QString(message)); PyErr_SetString(PyExc_TypeError, "Invalid arguments in finish call. Expected: finish(ThingError, message = \"\"");
Py_RETURN_NONE; return nullptr;
} }
if (PyArg_ParseTuple(args, "i", &status)) { Thing::ThingError thingError = static_cast<Thing::ThingError>(status);
(self->ptrObj)->finish(static_cast<Thing::ThingError>(status)); QString displayMessage = message != nullptr ? QString(message) : QString();
Py_RETURN_NONE;
}
PyErr_SetString(PyExc_TypeError, "Invalid arguments in finish call. Expected: finish(ThingError, message = \"\""); if (self->info) {
return nullptr; QMetaObject::invokeMethod(self->info, "finish", Qt::QueuedConnection, Q_ARG(Thing::ThingError, thingError), Q_ARG(QString, displayMessage));
}
Py_RETURN_NONE;
} }
static PyObject * PyThingDiscoveryInfo_addDescriptor(PyThingDiscoveryInfo* self, PyObject* args) { static PyObject * PyThingDiscoveryInfo_addDescriptor(PyThingDiscoveryInfo* self, PyObject* args) {
@ -72,21 +68,23 @@ static PyObject * PyThingDiscoveryInfo_addDescriptor(PyThingDiscoveryInfo* self,
PyThingDescriptor *pyDescriptor = (PyThingDescriptor*)pyObj; PyThingDescriptor *pyDescriptor = (PyThingDescriptor*)pyObj;
ThingClassId thingClassId; ThingClassId thingClassId;
if (pyDescriptor->thingClassId) { if (pyDescriptor->pyThingClassId) {
thingClassId = ThingClassId(PyUnicode_AsUTF8(pyDescriptor->thingClassId)); thingClassId = ThingClassId(PyUnicode_AsUTF8(pyDescriptor->pyThingClassId));
} }
QString name; QString name;
if (pyDescriptor->name) { if (pyDescriptor->pyName) {
name = QString::fromUtf8(PyUnicode_AsUTF8(pyDescriptor->name)); name = QString::fromUtf8(PyUnicode_AsUTF8(pyDescriptor->pyName));
} }
QString description; QString description;
if (pyDescriptor->description) { if (pyDescriptor->pyDescription) {
description = QString::fromUtf8(PyUnicode_AsUTF8(pyDescriptor->description)); description = QString::fromUtf8(PyUnicode_AsUTF8(pyDescriptor->pyDescription));
} }
ThingDescriptor descriptor(thingClassId, name, description); ThingDescriptor descriptor(thingClassId, name, description);
self->ptrObj->addThingDescriptor(descriptor); if (self->info) {
QMetaObject::invokeMethod(self->info, "addThingDescriptor", Qt::QueuedConnection, Q_ARG(ThingDescriptor, descriptor));
}
return Py_BuildValue(""); return Py_BuildValue("");
} }

View File

@ -14,45 +14,41 @@
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
ThingSetupInfo* ptrObj; ThingSetupInfo* info;
PyThing *thing; PyThing *pyThing;
} PyThingSetupInfo; } PyThingSetupInfo;
static int PyThingSetupInfo_init(PyThingSetupInfo */*self*/, PyObject */*args*/, PyObject */*kwds*/) static int PyThingSetupInfo_init(PyThingSetupInfo */*self*/, PyObject */*args*/, PyObject */*kwds*/) {
{
return 0; return 0;
} }
static void PyThingSetupInfo_dealloc(PyThingSetupInfo * self) static void PyThingSetupInfo_dealloc(PyThingSetupInfo * self) {
{
// FIXME: Why is this not called? Seems we're leaking...
Q_ASSERT(false);
Py_TYPE(self)->tp_free(self); Py_TYPE(self)->tp_free(self);
} }
static PyObject * PyThingSetupInfo_finish(PyThingSetupInfo* self, PyObject* args) static PyObject * PyThingSetupInfo_finish(PyThingSetupInfo* self, PyObject* args) {
{
int status; int status;
char *message; char *message = nullptr;
if (PyArg_ParseTuple(args, "is", &status, &message)) { if (!PyArg_ParseTuple(args, "i|s", &status, &message)) {
(self->ptrObj)->finish(static_cast<Thing::ThingError>(status), QString(message)); PyErr_SetString(PyExc_TypeError, "Invalid arguments in finish call. Expected: finish(ThingError, message = \"\"");
Py_RETURN_NONE; return nullptr;
} }
PyErr_Clear();
if (PyArg_ParseTuple(args, "i", &status)) { Thing::ThingError thingError = static_cast<Thing::ThingError>(status);
(self->ptrObj)->finish(static_cast<Thing::ThingError>(status)); QString displayMessage = message != nullptr ? QString(message) : QString();
Py_RETURN_NONE;
if (self->info) {
QMetaObject::invokeMethod(self->info, "finish", Qt::QueuedConnection, Q_ARG(Thing::ThingError, thingError), Q_ARG(QString, displayMessage));
} }
Py_RETURN_NONE; Py_RETURN_NONE;
} }
static PyMemberDef PyThingSetupInfo_members[] = { static PyMemberDef PyThingSetupInfo_members[] = {
{"thing", T_OBJECT_EX, offsetof(PyThingSetupInfo, thing), 0, "Thing being setup in this setup transaction"}, {"thing", T_OBJECT_EX, offsetof(PyThingSetupInfo, pyThing), 0, "Thing being setup in this setup transaction"},
{nullptr, 0, 0, 0, nullptr} /* Sentinel */ {nullptr, 0, 0, 0, nullptr} /* Sentinel */
}; };

View File

@ -4,6 +4,8 @@
#include "python/pything.h" #include "python/pything.h"
#include "python/pythingdiscoveryinfo.h" #include "python/pythingdiscoveryinfo.h"
#include "python/pythingsetupinfo.h" #include "python/pythingsetupinfo.h"
#include "python/pyparam.h"
#include "python/pythingactioninfo.h"
#include "pythonintegrationplugin.h" #include "pythonintegrationplugin.h"
@ -14,15 +16,11 @@
#include <QJsonDocument> #include <QJsonDocument>
#include <QtConcurrent/QtConcurrentRun> #include <QtConcurrent/QtConcurrentRun>
QHash<PyObject*, PyThreadState*> s_modules;
PyThreadState* PythonIntegrationPlugin::s_mainThread = nullptr; PyThreadState* PythonIntegrationPlugin::s_mainThread = nullptr;
PyObject* PythonIntegrationPlugin::s_nymeaModule = nullptr; PyObject* PythonIntegrationPlugin::s_nymeaModule = nullptr;
PyObject* PythonIntegrationPlugin::s_asyncio = nullptr; PyObject* PythonIntegrationPlugin::s_asyncio = nullptr;
// Write to stdout/stderr // Write to stdout/stderr
PyObject* nymea_write(PyObject* /*self*/, PyObject* args) PyObject* nymea_write(PyObject* /*self*/, PyObject* args)
{ {
@ -98,10 +96,12 @@ PyMODINIT_FUNC PyInit_nymea(void)
registerNymeaLoggingHandler(m); registerNymeaLoggingHandler(m);
registerParamType(m);
registerThingType(m); registerThingType(m);
registerThingDescriptorType(m); registerThingDescriptorType(m);
registerThingDiscoveryInfoType(m); registerThingDiscoveryInfoType(m);
registerThingSetupInfoType(m); registerThingSetupInfoType(m);
registerThingActionInfoType(m);
return m; return m;
} }
@ -192,11 +192,11 @@ void PythonIntegrationPlugin::init()
void PythonIntegrationPlugin::discoverThings(ThingDiscoveryInfo *info) void PythonIntegrationPlugin::discoverThings(ThingDiscoveryInfo *info)
{ {
PyThingDiscoveryInfo *pyInfo = PyObject_New(PyThingDiscoveryInfo, &PyThingDiscoveryInfoType); PyThingDiscoveryInfo *pyInfo = PyObject_New(PyThingDiscoveryInfo, &PyThingDiscoveryInfoType);
pyInfo->ptrObj = info; pyInfo->info = info;
connect(info, &ThingDiscoveryInfo::destroyed, this, [=](){ connect(info, &ThingDiscoveryInfo::destroyed, this, [=](){
PyGILState_STATE s = PyGILState_Ensure(); PyGILState_STATE s = PyGILState_Ensure();
pyInfo->ptrObj = nullptr; pyInfo->info = nullptr;
PyObject_Del(pyInfo); PyObject_Del(pyInfo);
PyGILState_Release(s); PyGILState_Release(s);
}); });
@ -207,11 +207,11 @@ void PythonIntegrationPlugin::discoverThings(ThingDiscoveryInfo *info)
void PythonIntegrationPlugin::setupThing(ThingSetupInfo *info) void PythonIntegrationPlugin::setupThing(ThingSetupInfo *info)
{ {
PyThing *pyThing = PyObject_New(PyThing, &PyThingType); PyThing *pyThing = PyObject_New(PyThing, &PyThingType);
pyThing->ptrObj = info->thing(); pyThing->thing = info->thing();
PyThingSetupInfo *pyInfo = PyObject_New(PyThingSetupInfo, &PyThingSetupInfoType); PyThingSetupInfo *pyInfo = PyObject_New(PyThingSetupInfo, &PyThingSetupInfoType);
pyInfo->ptrObj = info; pyInfo->info = info;
pyInfo->thing = pyThing; pyInfo->pyThing = pyThing;
connect(info, &ThingSetupInfo::finished, this, [=](){ connect(info, &ThingSetupInfo::finished, this, [=](){
@ -230,7 +230,8 @@ void PythonIntegrationPlugin::setupThing(ThingSetupInfo *info)
}); });
connect(info, &ThingSetupInfo::destroyed, this, [=](){ connect(info, &ThingSetupInfo::destroyed, this, [=](){
PyGILState_STATE s = PyGILState_Ensure(); PyGILState_STATE s = PyGILState_Ensure();
PyObject_Del(pyInfo); pyInfo->info = nullptr;
Py_DECREF(pyInfo);
PyGILState_Release(s); PyGILState_Release(s);
}); });
@ -244,6 +245,32 @@ void PythonIntegrationPlugin::postSetupThing(Thing *thing)
callPluginFunction("postSetupThing", reinterpret_cast<PyObject*>(pyThing)); callPluginFunction("postSetupThing", reinterpret_cast<PyObject*>(pyThing));
} }
void PythonIntegrationPlugin::executeAction(ThingActionInfo *info)
{
PyThing *pyThing = m_things.value(info->thing());
PyGILState_STATE s = PyGILState_Ensure();
PyThingActionInfo *pyInfo = PyObject_New(PyThingActionInfo, &PyThingActionInfoType);
pyInfo->info = info;
pyInfo->pyThing = pyThing;
pyInfo->pyActionTypeId = PyUnicode_FromString(info->action().actionTypeId().toString().toUtf8());
pyInfo->pyParams = PyParam_FromParamList(info->action().params());
PyGILState_Release(s);
connect(info, &ThingActionInfo::destroyed, this, [=](){
PyGILState_STATE s = PyGILState_Ensure();
pyInfo->pyActionTypeId = nullptr;
Py_DECREF(pyInfo->pyActionTypeId);
pyInfo->info = nullptr;
Py_DECREF(pyInfo);
PyGILState_Release(s);
});
callPluginFunction("executeAction", reinterpret_cast<PyObject*>(pyInfo));
}
void PythonIntegrationPlugin::thingRemoved(Thing *thing) void PythonIntegrationPlugin::thingRemoved(Thing *thing)
{ {
PyThing *pyThing = m_things.value(thing); PyThing *pyThing = m_things.value(thing);
@ -252,10 +279,9 @@ void PythonIntegrationPlugin::thingRemoved(Thing *thing)
PyGILState_STATE s = PyGILState_Ensure(); PyGILState_STATE s = PyGILState_Ensure();
pyThing->ptrObj = nullptr; pyThing->thing = nullptr;
Py_DECREF(pyThing); Py_DECREF(pyThing);
PyGILState_Release(s); PyGILState_Release(s);
m_things.remove(thing); m_things.remove(thing);

View File

@ -29,6 +29,7 @@ public:
void discoverThings(ThingDiscoveryInfo *info) override; void discoverThings(ThingDiscoveryInfo *info) override;
void setupThing(ThingSetupInfo *info) override; void setupThing(ThingSetupInfo *info) override;
void postSetupThing(Thing *thing) override; void postSetupThing(Thing *thing) override;
void executeAction(ThingActionInfo *info) override;
void thingRemoved(Thing *thing) override; void thingRemoved(Thing *thing) override;

View File

@ -21,7 +21,9 @@ RESOURCES += $$top_srcdir/icons.qrc \
HEADERS += nymeacore.h \ HEADERS += nymeacore.h \
integrations/plugininfocache.h \ integrations/plugininfocache.h \
integrations/python/pynymealogginghandler.h \ integrations/python/pynymealogginghandler.h \
integrations/python/pyparam.h \
integrations/python/pything.h \ integrations/python/pything.h \
integrations/python/pythingactioninfo.h \
integrations/python/pythingdescriptor.h \ integrations/python/pythingdescriptor.h \
integrations/python/pythingdiscoveryinfo.h \ integrations/python/pythingdiscoveryinfo.h \
integrations/python/pythingsetupinfo.h \ integrations/python/pythingsetupinfo.h \