more work
This commit is contained in:
parent
85c9d93ccd
commit
8aa91292fe
@ -12,6 +12,7 @@
|
|||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QMutexLocker>
|
#include <QMutexLocker>
|
||||||
|
#include <QMetaEnum>
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
||||||
@ -29,6 +30,8 @@ typedef struct _thing {
|
|||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
Thing *thing = nullptr;
|
Thing *thing = nullptr;
|
||||||
PyObject *name = nullptr;
|
PyObject *name = nullptr;
|
||||||
|
PyObject *id = nullptr;
|
||||||
|
PyObject *thingClassId = nullptr;
|
||||||
PyObject *params = nullptr;
|
PyObject *params = nullptr;
|
||||||
PyObject *settings = nullptr;
|
PyObject *settings = nullptr;
|
||||||
PyObject *nameChangedHandler = nullptr;
|
PyObject *nameChangedHandler = nullptr;
|
||||||
@ -51,7 +54,8 @@ static void PyThing_setThing(PyThing *self, Thing *thing)
|
|||||||
self->thing = thing;
|
self->thing = thing;
|
||||||
|
|
||||||
self->name = PyUnicode_FromString(self->thing->name().toUtf8().data());
|
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, [=](){
|
QObject::connect(thing, &Thing::nameChanged, [=](){
|
||||||
self->mutex->lock();
|
self->mutex->lock();
|
||||||
@ -67,29 +71,12 @@ static void PyThing_setThing(PyThing *self, Thing *thing)
|
|||||||
PyGILState_STATE s = PyGILState_Ensure();
|
PyGILState_STATE s = PyGILState_Ensure();
|
||||||
PyObject_CallFunctionObjArgs(self->nameChangedHandler, self, nullptr);
|
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);
|
PyGILState_Release(s);
|
||||||
});
|
});
|
||||||
|
|
||||||
self->params = PyParams_FromParamList(self->thing->params());
|
self->params = PyParams_FromParamList(self->thing->params());
|
||||||
Py_INCREF(self->params);
|
|
||||||
|
|
||||||
self->settings = PyParams_FromParamList(self->thing->settings());
|
self->settings = PyParams_FromParamList(self->thing->settings());
|
||||||
Py_INCREF(self->settings);
|
|
||||||
QObject::connect(thing, &Thing::settingChanged, [=](){
|
QObject::connect(thing, &Thing::settingChanged, [=](){
|
||||||
QMutexLocker(self->mutex);
|
QMutexLocker(self->mutex);
|
||||||
Py_XDECREF(self->settings);
|
Py_XDECREF(self->settings);
|
||||||
@ -99,6 +86,9 @@ static void PyThing_setThing(PyThing *self, Thing *thing)
|
|||||||
|
|
||||||
|
|
||||||
static void PyThing_dealloc(PyThing * self) {
|
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->name);
|
||||||
Py_XDECREF(self->params);
|
Py_XDECREF(self->params);
|
||||||
Py_XDECREF(self->settings);
|
Py_XDECREF(self->settings);
|
||||||
@ -119,6 +109,30 @@ static PyObject *PyThing_getName(PyThing *self, void */*closure*/)
|
|||||||
return self->name;
|
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*/){
|
static int PyThing_setName(PyThing *self, PyObject *value, void */*closure*/){
|
||||||
QString name = QString(PyUnicode_AsUTF8(value));
|
QString name = QString(PyUnicode_AsUTF8(value));
|
||||||
QMutexLocker(self->mutex);
|
QMutexLocker(self->mutex);
|
||||||
@ -186,6 +200,8 @@ static PyObject * PyThing_emitEvent(PyThing* self, PyObject* args)
|
|||||||
|
|
||||||
static PyGetSetDef PyThing_getset[] = {
|
static PyGetSetDef PyThing_getset[] = {
|
||||||
{"name", (getter)PyThing_getName, (setter)PyThing_setName, "Thing name", nullptr},
|
{"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},
|
{"settings", (getter)PyThing_getSettings, (setter)PyThing_setSettings, "Thing settings", nullptr},
|
||||||
{nullptr , nullptr, nullptr, nullptr, nullptr} /* Sentinel */
|
{nullptr , nullptr, nullptr, nullptr, nullptr} /* Sentinel */
|
||||||
};
|
};
|
||||||
@ -197,6 +213,7 @@ static PyMethodDef PyThing_methods[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static PyMemberDef PyThing_members[] = {
|
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"},
|
{"nameChangedHandler", T_OBJECT_EX, offsetof(PyThing, nameChangedHandler), 0, "Set a callback for when the thing name changes"},
|
||||||
{nullptr, 0, 0, 0, nullptr} /* Sentinel */
|
{nullptr, 0, 0, 0, nullptr} /* Sentinel */
|
||||||
};
|
};
|
||||||
@ -238,7 +255,7 @@ static PyTypeObject PyThingType = {
|
|||||||
0, /* tp_descr_set */
|
0, /* tp_descr_set */
|
||||||
0, /* tp_dictoffset */
|
0, /* tp_dictoffset */
|
||||||
0, /* tp_init */
|
0, /* tp_init */
|
||||||
0, /* tp_alloc */
|
PyType_GenericAlloc, /* tp_alloc */
|
||||||
(newfunc)PyThing_new, /* tp_new */
|
(newfunc)PyThing_new, /* tp_new */
|
||||||
0, /* tp_free */
|
0, /* tp_free */
|
||||||
0, /* tp_is_gc */
|
0, /* tp_is_gc */
|
||||||
@ -260,6 +277,12 @@ static void registerThingType(PyObject *module)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PyModule_AddObject(module, "Thing", reinterpret_cast<PyObject*>(&PyThingType));
|
PyModule_AddObject(module, "Thing", reinterpret_cast<PyObject*>(&PyThingType));
|
||||||
|
|
||||||
|
QMetaEnum thingErrorEnum = QMetaEnum::fromType<Thing::ThingError>();
|
||||||
|
for (int i = 0; i < thingErrorEnum.keyCount(); i++) {
|
||||||
|
PyModule_AddObject(module, thingErrorEnum.key(i), PyLong_FromLong(thingErrorEnum.value(i)));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -165,11 +165,6 @@ static void registerThingDiscoveryInfoType(PyObject *module)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PyModule_AddObject(module, "ThingDiscoveryInfo", (PyObject *)&PyThingDiscoveryInfoType);
|
PyModule_AddObject(module, "ThingDiscoveryInfo", (PyObject *)&PyThingDiscoveryInfoType);
|
||||||
|
|
||||||
QMetaEnum thingErrorEnum = QMetaEnum::fromType<Thing::ThingError>();
|
|
||||||
for (int i = 0; i < thingErrorEnum.keyCount(); i++) {
|
|
||||||
PyModule_AddObject(module, thingErrorEnum.key(i), PyLong_FromLong(thingErrorEnum.value(i)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -308,6 +308,7 @@ bool PythonIntegrationPlugin::loadScript(const QString &scriptFile)
|
|||||||
|
|
||||||
if (!m_module) {
|
if (!m_module) {
|
||||||
dumpError();
|
dumpError();
|
||||||
|
PyErr_Clear();
|
||||||
qCWarning(dcThingManager()) << "Error importing python plugin from:" << fi.absoluteFilePath();
|
qCWarning(dcThingManager()) << "Error importing python plugin from:" << fi.absoluteFilePath();
|
||||||
PyGILState_Release(s);
|
PyGILState_Release(s);
|
||||||
return false;
|
return false;
|
||||||
@ -388,25 +389,18 @@ void PythonIntegrationPlugin::setupThing(ThingSetupInfo *info)
|
|||||||
PyGILState_STATE s = PyGILState_Ensure();
|
PyGILState_STATE s = PyGILState_Ensure();
|
||||||
|
|
||||||
PyThing *pyThing = (PyThing*)PyObject_CallObject((PyObject*)&PyThingType, NULL);
|
PyThing *pyThing = (PyThing*)PyObject_CallObject((PyObject*)&PyThingType, NULL);
|
||||||
|
dumpError();
|
||||||
PyThing_setThing(pyThing, info->thing());
|
PyThing_setThing(pyThing, info->thing());
|
||||||
|
|
||||||
PyThingSetupInfo *pyInfo = (PyThingSetupInfo*)PyObject_CallObject((PyObject*)&PyThingSetupInfoType, NULL);
|
PyThingSetupInfo *pyInfo = (PyThingSetupInfo*)PyObject_CallObject((PyObject*)&PyThingSetupInfoType, NULL);
|
||||||
pyInfo->info = info;
|
pyInfo->info = info;
|
||||||
pyInfo->pyThing = pyThing;
|
pyInfo->pyThing = pyThing;
|
||||||
|
|
||||||
|
m_things.insert(info->thing(), pyThing);
|
||||||
|
|
||||||
PyGILState_Release(s);
|
PyGILState_Release(s);
|
||||||
|
|
||||||
|
connect(info->thing(), &Thing::destroyed, this, [=](){
|
||||||
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, [=](){
|
|
||||||
cleanupPyThing(pyThing);
|
cleanupPyThing(pyThing);
|
||||||
});
|
});
|
||||||
connect(info, &ThingSetupInfo::destroyed, this, [=](){
|
connect(info, &ThingSetupInfo::destroyed, this, [=](){
|
||||||
@ -416,7 +410,11 @@ void PythonIntegrationPlugin::setupThing(ThingSetupInfo *info)
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
callPluginFunction("setupThing", reinterpret_cast<PyObject*>(pyInfo));
|
bool result = callPluginFunction("setupThing", reinterpret_cast<PyObject*>(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)
|
void PythonIntegrationPlugin::postSetupThing(Thing *thing)
|
||||||
@ -456,8 +454,6 @@ void PythonIntegrationPlugin::thingRemoved(Thing *thing)
|
|||||||
|
|
||||||
callPluginFunction("thingRemoved", reinterpret_cast<PyObject*>(pyThing));
|
callPluginFunction("thingRemoved", reinterpret_cast<PyObject*>(pyThing));
|
||||||
|
|
||||||
cleanupPyThing(pyThing);
|
|
||||||
|
|
||||||
m_mutex.lock();
|
m_mutex.lock();
|
||||||
m_things.remove(thing);
|
m_things.remove(thing);
|
||||||
m_mutex.unlock();
|
m_mutex.unlock();
|
||||||
@ -496,7 +492,7 @@ void PythonIntegrationPlugin::exportIds()
|
|||||||
|
|
||||||
foreach (const Vendor &vendor, supportedVendors()) {
|
foreach (const Vendor &vendor, supportedVendors()) {
|
||||||
qCDebug(dcThingManager()) << "|- Vendor:" << vendor.name() << vendor.id().toString();
|
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()) {
|
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();
|
PyGILState_STATE s = PyGILState_Ensure();
|
||||||
|
|
||||||
qCDebug(dcThingManager()) << "Calling python plugin function" << function;
|
qCDebug(dcThingManager()) << "Calling python plugin function" << function;
|
||||||
PyObject *pFunc = PyObject_GetAttrString(m_module, function.toUtf8());
|
PyObject *pFunc = PyObject_GetAttrString(m_module, function.toUtf8());
|
||||||
if(!pFunc || !PyCallable_Check(pFunc)) {
|
if(!pFunc || !PyCallable_Check(pFunc)) {
|
||||||
|
PyErr_Clear();
|
||||||
Py_XDECREF(pFunc);
|
Py_XDECREF(pFunc);
|
||||||
qCWarning(dcThingManager()) << "Python plugin does not implement" << function;
|
qCWarning(dcThingManager()) << "Python plugin does not implement" << function;
|
||||||
PyGILState_Release(s);
|
PyGILState_Release(s);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -595,14 +592,15 @@ void PythonIntegrationPlugin::callPluginFunction(const QString &function, PyObje
|
|||||||
if (PyErr_Occurred()) {
|
if (PyErr_Occurred()) {
|
||||||
qCWarning(dcThingManager()) << "Error calling python method:";
|
qCWarning(dcThingManager()) << "Error calling python method:";
|
||||||
dumpError();
|
dumpError();
|
||||||
|
PyErr_Clear();
|
||||||
PyGILState_Release(s);
|
PyGILState_Release(s);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (QByteArray(result->ob_type->tp_name) != "coroutine") {
|
if (QByteArray(result->ob_type->tp_name) != "coroutine") {
|
||||||
Py_DECREF(result);
|
Py_DECREF(result);
|
||||||
PyGILState_Release(s);
|
PyGILState_Release(s);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spawn a event loop for python
|
// Spawn a event loop for python
|
||||||
@ -638,6 +636,8 @@ void PythonIntegrationPlugin::callPluginFunction(const QString &function, PyObje
|
|||||||
Py_DECREF(result);
|
Py_DECREF(result);
|
||||||
|
|
||||||
PyGILState_Release(s);
|
PyGILState_Release(s);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PythonIntegrationPlugin::cleanupPyThing(PyThing *pyThing)
|
void PythonIntegrationPlugin::cleanupPyThing(PyThing *pyThing)
|
||||||
|
|||||||
@ -51,7 +51,7 @@ private:
|
|||||||
void exportBrowserItemActionTypes(const ActionTypes &actionTypes, const QString &thingClassName);
|
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);
|
void cleanupPyThing(PyThing *pyThing);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -1,17 +1,35 @@
|
|||||||
import nymea
|
import nymea
|
||||||
|
import asyncio
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
logger.log("Python mock plugin init")
|
logger.log("Python mock plugin init")
|
||||||
logger.log("Number of auto mocks", configValue(pyMockPluginAutoThingCountParamTypeId))
|
|
||||||
|
|
||||||
|
|
||||||
def configValueChanged(paramTypeId, value):
|
def configValueChanged(paramTypeId, value):
|
||||||
logger.log("Plugin config value changed:", 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():
|
def startMonitoringAutoThings():
|
||||||
logger.log("Start monitoring auto things. Already have", len(myThings()), configValue(pyMockPluginAutoThingCountParamTypeId))
|
logger.log("Start monitoring auto things. Have %i auto devices. Need %i." % (len(autoThings()), configValue(pyMockPluginAutoThingCountParamTypeId)))
|
||||||
for i in range(configValue(pyMockPluginAutoThingCountParamTypeId), len(myThings())):
|
for i in range(len(autoThings()), configValue(pyMockPluginAutoThingCountParamTypeId)):
|
||||||
logger.log("auto thing")
|
logger.log("Creating new auto thing")
|
||||||
# descriptor = nymea.
|
descriptor = nymea.ThingDescriptor(pyMockAutoThingClassId, "Python Mock auto thing")
|
||||||
# autoThingsAppeared(
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user