mirror of https://github.com/nymea/nymea.git
more work
parent
f132c6b006
commit
3296d4b417
|
|
@ -7,6 +7,7 @@
|
|||
#include "pyutils.h"
|
||||
|
||||
#include "types/param.h"
|
||||
#include "types/paramtype.h"
|
||||
|
||||
#include "loggingcategories.h"
|
||||
|
||||
|
|
@ -99,7 +100,7 @@ static PyParam* PyParam_fromParam(const Param ¶m)
|
|||
|
||||
static Param PyParam_ToParam(PyParam *pyParam)
|
||||
{
|
||||
ParamTypeId paramTypeId = ParamTypeId(PyUnicode_AsUTF8(pyParam->pyParamTypeId));
|
||||
ParamTypeId paramTypeId = ParamTypeId(PyUnicode_AsUTF8AndSize(pyParam->pyParamTypeId, nullptr));
|
||||
QVariant value = PyObjectToQVariant(pyParam->pyValue);
|
||||
return Param(paramTypeId, value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
#include <QPointer>
|
||||
#include <QThread>
|
||||
#include <QMutexLocker>
|
||||
#include <QMetaEnum>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
|
|
@ -26,7 +25,7 @@
|
|||
* So we must never directly access anything of it in here.
|
||||
*
|
||||
* For writing to it, invoking methods with QueuedConnections will thread-decouple stuff.
|
||||
* Make sure to lock the self->mutex while using the pointer to it for invoking stuff.
|
||||
* Make sure to hold the GIL whenver accessing the pointer value for invoking stuff.
|
||||
*
|
||||
* For reading access, we keep copies of the thing properties here and sync them
|
||||
* over to the according py* members when they change.
|
||||
|
|
@ -37,7 +36,7 @@
|
|||
typedef struct _thing {
|
||||
PyObject_HEAD
|
||||
Thing *thing = nullptr; // the actual thing in nymea (not thread-safe!)
|
||||
QMutex *mutex = nullptr; // The mutex for accessing the thing pointer
|
||||
ThingClass *thingClass = nullptr; // A copy of the thing class. This is owned by the python thread
|
||||
PyObject *pyId = nullptr;
|
||||
PyObject *pyThingClassId = nullptr;
|
||||
PyObject *pyName = nullptr;
|
||||
|
|
@ -55,7 +54,6 @@ static PyObject* PyThing_new(PyTypeObject *type, PyObject */*args*/, PyObject */
|
|||
return nullptr;
|
||||
}
|
||||
qWarning() << "*++++ PyThing" << self;
|
||||
self->mutex = new QMutex();
|
||||
|
||||
return (PyObject*)self;
|
||||
}
|
||||
|
|
@ -64,6 +62,9 @@ static void PyThing_setThing(PyThing *self, Thing *thing)
|
|||
{
|
||||
self->thing = thing;
|
||||
|
||||
// Creating a copy because we cannot access the actual thing from the python thread
|
||||
self->thingClass = new ThingClass(thing->thingClass());
|
||||
|
||||
self->pyId = PyUnicode_FromString(self->thing->id().toString().toUtf8().data());
|
||||
self->pyThingClassId = PyUnicode_FromString(self->thing->thingClassId().toString().toUtf8().data());
|
||||
self->pyName = PyUnicode_FromString(self->thing->name().toUtf8().data());
|
||||
|
|
@ -106,6 +107,24 @@ static void PyThing_setThing(PyThing *self, Thing *thing)
|
|||
}
|
||||
PyGILState_Release(s);
|
||||
});
|
||||
|
||||
QObject::connect(thing, &Thing::stateValueChanged, [=](const StateTypeId &stateTypeId, const QVariant &value){
|
||||
PyGILState_STATE s = PyGILState_Ensure();
|
||||
for (int i = 0; i < PyList_Size(self->pyStates); i++) {
|
||||
PyObject *pyState = PyList_GetItem(self->pyStates, i);
|
||||
PyObject *pyStateTypeId = PyDict_GetItemString(pyState, "stateTypeId");
|
||||
StateTypeId stid = StateTypeId(PyUnicode_AsUTF8AndSize(pyStateTypeId, nullptr));
|
||||
if (stid == stateTypeId) {
|
||||
qWarning() << "Updating state" << stateTypeId << value;
|
||||
pyState = Py_BuildValue("{s:s, s:O}",
|
||||
"stateTypeId", stateTypeId.toString().toUtf8().data(),
|
||||
"value", QVariantToPyObject(value));
|
||||
PyList_SetItem(self->pyStates, i, pyState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
PyGILState_Release(s);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -119,7 +138,7 @@ static void PyThing_dealloc(PyThing * self) {
|
|||
Py_XDECREF(self->pyStates);
|
||||
Py_XDECREF(self->pyNameChangedHandler);
|
||||
Py_XDECREF(self->pySettingChangedHandler);
|
||||
delete self->mutex;
|
||||
delete self->thingClass;
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
|
|
@ -143,7 +162,6 @@ static PyObject *PyThing_getThingClassId(PyThing *self, void */*closure*/)
|
|||
|
||||
static int PyThing_setName(PyThing *self, PyObject *value, void */*closure*/){
|
||||
QString name = QString(PyUnicode_AsUTF8(value));
|
||||
QMutexLocker(self->mutex);
|
||||
if (!self->thing) {
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -151,6 +169,74 @@ static int PyThing_setName(PyThing *self, PyObject *value, void */*closure*/){
|
|||
return 0;
|
||||
}
|
||||
|
||||
static PyObject * PyThing_paramValue(PyThing* self, PyObject* args)
|
||||
{
|
||||
char *paramTypeIdStr = nullptr;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", ¶mTypeIdStr)) {
|
||||
qCWarning(dcThingManager) << "Error parsing parameters";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ParamTypeId paramTypeId = ParamTypeId(paramTypeIdStr);
|
||||
PyObject *iterator = PyObject_GetIter(self->pyParams);
|
||||
while (iterator) {
|
||||
PyObject *pyParam = PyIter_Next(iterator);
|
||||
if (!pyParam) {
|
||||
break;
|
||||
}
|
||||
|
||||
Param param = PyParam_ToParam((PyParam*)pyParam);
|
||||
Py_DECREF(pyParam);
|
||||
|
||||
if (param.paramTypeId() != paramTypeId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Py_DECREF(iterator);
|
||||
|
||||
return QVariantToPyObject(param.value());
|
||||
}
|
||||
|
||||
Py_DECREF(iterator);
|
||||
qCWarning(dcPythonIntegrations()) << "No param for paramTypeId:" << paramTypeId;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject * PyThing_setting(PyThing* self, PyObject* args)
|
||||
{
|
||||
char *paramTypeIdStr = nullptr;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", ¶mTypeIdStr)) {
|
||||
qCWarning(dcThingManager) << "Error parsing parameters";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ParamTypeId paramTypeId = ParamTypeId(paramTypeIdStr);
|
||||
PyObject *iterator = PyObject_GetIter(self->pySettings);
|
||||
while (iterator) {
|
||||
PyObject *pyParam = PyIter_Next(iterator);
|
||||
if (!pyParam) {
|
||||
break;
|
||||
}
|
||||
|
||||
Param param = PyParam_ToParam((PyParam*)pyParam);
|
||||
Py_DECREF(pyParam);
|
||||
|
||||
if (param.paramTypeId() != paramTypeId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Py_DECREF(iterator);
|
||||
|
||||
return QVariantToPyObject(param.value());
|
||||
}
|
||||
|
||||
Py_DECREF(iterator);
|
||||
qCWarning(dcPythonIntegrations()) << "No setting for paramTypeId:" << paramTypeId;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *PyThing_getSettings(PyThing *self, void */*closure*/)
|
||||
{
|
||||
Py_INCREF(self->pySettings);
|
||||
|
|
@ -167,42 +253,25 @@ static PyObject * PyThing_stateValue(PyThing* self, PyObject* args)
|
|||
char *stateTypeIdStr = nullptr;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &stateTypeIdStr)) {
|
||||
qCWarning(dcThingManager) << "Error parsing parameters";
|
||||
PyErr_SetString(PyExc_ValueError, "Error parsing arguments. Signature is 's'");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
StateTypeId stateTypeId = StateTypeId(stateTypeIdStr);
|
||||
PyObject *iterator = PyObject_GetIter(self->pyStates);
|
||||
while (iterator) {
|
||||
PyObject *pyState = PyIter_Next(iterator);
|
||||
if (!pyState) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < PyList_Size(self->pyStates); i++) {
|
||||
PyObject *pyState = PyList_GetItem(self->pyStates, i);
|
||||
PyObject *pyStateTypeId = PyDict_GetItemString(pyState, "stateTypeId");
|
||||
PyObject *tmp = PyUnicode_AsEncodedString(pyStateTypeId, "UTF-8", "strict");
|
||||
|
||||
StateTypeId stid = StateTypeId(PyBytes_AS_STRING(tmp));
|
||||
|
||||
Py_DECREF(pyStateTypeId);
|
||||
Py_XDECREF(tmp);
|
||||
|
||||
if (stid != stateTypeId) {
|
||||
Py_DECREF(pyState);
|
||||
continue;
|
||||
StateTypeId stid = StateTypeId(PyUnicode_AsUTF8AndSize(pyStateTypeId, nullptr));
|
||||
if (stid == stateTypeId) {
|
||||
PyObject *value = PyDict_GetItemString(pyState, "value");
|
||||
Py_INCREF(value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject *pyStateValue = PyDict_GetItemString(pyState, "value");
|
||||
|
||||
Py_DECREF(pyState);
|
||||
Py_DECREF(iterator);
|
||||
|
||||
return pyStateValue;
|
||||
}
|
||||
|
||||
Py_DECREF(iterator);
|
||||
qCWarning(dcPythonIntegrations()) << "No state for stateTypeId:" << stateTypeId;
|
||||
Py_RETURN_NONE;
|
||||
PyErr_SetString(PyExc_ValueError, QString("No state type %1 in thing class %2").arg(stateTypeId.toString()).arg(self->thingClass->name()).toUtf8());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static PyObject * PyThing_setStateValue(PyThing* self, PyObject* args)
|
||||
|
|
@ -211,14 +280,18 @@ static PyObject * PyThing_setStateValue(PyThing* self, PyObject* args)
|
|||
PyObject *valueObj = nullptr;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "sO", &stateTypeIdStr, &valueObj)) {
|
||||
qCWarning(dcThingManager) << "Error parsing parameters";
|
||||
PyErr_SetString(PyExc_ValueError, "Error parsing arguments. Signature is 'sO'");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
StateTypeId stateTypeId = StateTypeId(stateTypeIdStr);
|
||||
StateType stateType = self->thingClass->stateTypes().findById(stateTypeId);
|
||||
if (!stateType.isValid()) {
|
||||
PyErr_SetString(PyExc_ValueError, QString("No state type %1 in thing class %2").arg(stateTypeId.toString()).arg(self->thingClass->name()).toUtf8());
|
||||
return nullptr;
|
||||
}
|
||||
QVariant value = PyObjectToQVariant(valueObj);
|
||||
|
||||
QMutexLocker(self->mutex);
|
||||
if (self->thing != nullptr) {
|
||||
QMetaObject::invokeMethod(self->thing, "setStateValue", Qt::QueuedConnection, Q_ARG(StateTypeId, stateTypeId), Q_ARG(QVariant, value));
|
||||
}
|
||||
|
|
@ -232,14 +305,23 @@ static PyObject * PyThing_emitEvent(PyThing* self, PyObject* args)
|
|||
PyObject *valueObj = nullptr;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s|O", &eventTypeIdStr, &valueObj)) {
|
||||
qCWarning(dcThingManager) << "Error parsing parameters";
|
||||
PyErr_SetString(PyExc_TypeError, "Supplied arguments for emitEvent must be a ParamList");
|
||||
return nullptr;
|
||||
}
|
||||
if (qstrcmp(valueObj->ob_type->tp_name, "list") != 0) {
|
||||
PyErr_SetString(PyExc_TypeError, "Supplied arguments for emitEvent must be a ParamList");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EventTypeId eventTypeId = EventTypeId(eventTypeIdStr);
|
||||
EventType eventType = self->thingClass->eventTypes().findById(eventTypeId);
|
||||
if (!eventType.isValid()) {
|
||||
PyErr_SetString(PyExc_ValueError, QString("No event type %1 in thing class %2").arg(eventTypeId.toString()).arg(self->thingClass->name()).toUtf8());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ParamList params = PyParams_ToParamList(valueObj);
|
||||
|
||||
QMutexLocker(self->mutex);
|
||||
if (self->thing != nullptr) {
|
||||
QMetaObject::invokeMethod(self->thing, "emitEvent", Qt::QueuedConnection, Q_ARG(EventTypeId, eventTypeId), Q_ARG(ParamList, params));
|
||||
}
|
||||
|
|
@ -256,6 +338,8 @@ static PyGetSetDef PyThing_getset[] = {
|
|||
};
|
||||
|
||||
static PyMethodDef PyThing_methods[] = {
|
||||
{ "paramValue", (PyCFunction)PyThing_paramValue, METH_VARARGS, "Get a things param value by paramTypeId" },
|
||||
{ "setting", (PyCFunction)PyThing_setting, METH_VARARGS, "Get a things setting value by paramTypeId" },
|
||||
{ "stateValue", (PyCFunction)PyThing_stateValue, METH_VARARGS, "Get a things state value by stateTypeId" },
|
||||
{ "setStateValue", (PyCFunction)PyThing_setStateValue, METH_VARARGS, "Set a certain things state value by stateTypeIp" },
|
||||
{ "emitEvent", (PyCFunction)PyThing_emitEvent, METH_VARARGS, "Emits an event" },
|
||||
|
|
|
|||
|
|
@ -45,18 +45,21 @@ PyObject *QVariantToPyObject(const QVariant &value)
|
|||
|
||||
QVariant PyObjectToQVariant(PyObject *pyObject)
|
||||
{
|
||||
// FIXME: is there any better way to do this?
|
||||
qWarning() << "Error:" << PyErr_CheckSignals();
|
||||
PyObject* repr = PyObject_Repr(pyObject);
|
||||
PyObject* str = PyUnicode_AsEncodedString(repr, "utf-8", "~E~");
|
||||
const char *bytes = PyBytes_AS_STRING(str);
|
||||
qWarning() << "**************** type" << pyObject->ob_type->tp_name;
|
||||
|
||||
QVariant value(bytes);
|
||||
if (qstrcmp(pyObject->ob_type->tp_name, "int") == 0) {
|
||||
return QVariant(PyLong_AsLongLong(pyObject));
|
||||
}
|
||||
|
||||
Py_XDECREF(repr);
|
||||
Py_XDECREF(str);
|
||||
if (qstrcmp(pyObject->ob_type->tp_name, "str") == 0) {
|
||||
return QVariant(PyUnicode_AsUTF8AndSize(pyObject, nullptr));
|
||||
}
|
||||
|
||||
return value;
|
||||
if (qstrcmp(pyObject->ob_type->tp_name, "double") == 0) {
|
||||
return QVariant(PyFloat_AsDouble(pyObject));
|
||||
}
|
||||
Q_ASSERT_X(false, "pyutils.h", "Unhandled data type in conversion from Param to PyParam!");
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ void ParamTypes::put(const QVariant &variant)
|
|||
append(variant.value<ParamType>());
|
||||
}
|
||||
|
||||
ParamType ParamTypes::findByName(const QString &name)
|
||||
ParamType ParamTypes::findByName(const QString &name) const
|
||||
{
|
||||
foreach (const ParamType ¶mType, *this) {
|
||||
if (paramType.name() == name) {
|
||||
|
|
@ -282,7 +282,7 @@ ParamType ParamTypes::findByName(const QString &name)
|
|||
return ParamType();
|
||||
}
|
||||
|
||||
ParamType ParamTypes::findById(const ParamTypeId &id)
|
||||
ParamType ParamTypes::findById(const ParamTypeId &id) const
|
||||
{
|
||||
foreach (const ParamType ¶mType, *this) {
|
||||
if (paramType.id() == id) {
|
||||
|
|
|
|||
|
|
@ -124,8 +124,8 @@ public:
|
|||
ParamTypes(const QList<ParamType> &other);
|
||||
Q_INVOKABLE QVariant get(int index) const;
|
||||
Q_INVOKABLE void put(const QVariant &variant);
|
||||
ParamType findByName(const QString &name);
|
||||
ParamType findById(const ParamTypeId &id);
|
||||
ParamType findByName(const QString &name) const;
|
||||
ParamType findById(const ParamTypeId &id) const;
|
||||
};
|
||||
Q_DECLARE_METATYPE(QList<ParamType>)
|
||||
Q_DECLARE_METATYPE(ParamTypes)
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ private:
|
|||
QString m_name;
|
||||
QString m_displayName;
|
||||
int m_index = 0;
|
||||
QVariant::Type m_type;
|
||||
QVariant::Type m_type = QVariant::Invalid;
|
||||
QVariant m_defaultValue;
|
||||
QVariant m_minValue;
|
||||
QVariant m_maxValue;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"name": "autoThingCount",
|
||||
"displayName": "Number of auto things",
|
||||
"type": "int",
|
||||
"defaultValue": "fds"
|
||||
"defaultValue": 0
|
||||
}
|
||||
],
|
||||
"vendors": [
|
||||
|
|
@ -94,6 +94,21 @@
|
|||
"defaultValue": "hello"
|
||||
}
|
||||
],
|
||||
"eventTypes": [
|
||||
{
|
||||
"id": "e6b98ef6-7922-48e6-b508-238d178b86ca",
|
||||
"name": "event1",
|
||||
"displayName": "Event 1",
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "7c265a6a-f0ae-4822-a14f-e6a090f5a310",
|
||||
"name": "param1",
|
||||
"displayName": "Event param 1",
|
||||
"type": "QString"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "99d0af17-9e8c-42bb-bece-a5d114f051d3",
|
||||
|
|
|
|||
|
|
@ -3,9 +3,19 @@ import asyncio
|
|||
|
||||
watchingAutoThings = False
|
||||
|
||||
def init():
|
||||
async def init():
|
||||
logger.log("Python mock plugin init")
|
||||
|
||||
while True:
|
||||
await asyncio.sleep(2);
|
||||
logger.log("Updating stuff")
|
||||
for thing in myThings():
|
||||
if thing.thingClassId == pyMockDiscoveryPairingThingClassId:
|
||||
logger.log("Emitting event 1 for", thing.name)
|
||||
# thing.emitEvent(pyMockDiscoveryPairingEvent1EventTypeId, [nymea.Param(pyMockDiscoveryPairingEvent1EventParam1ParamTypeId, "Im an event")])
|
||||
logger.log("Setting state 1 for", thing.name, "Old value is:", thing.stateValue(pyMockDiscoveryPairingState1StateTypeId))
|
||||
thing.setStateValue(pyMockDiscoveryPairingState1StateTypeId, thing.stateValue(pyMockDiscoveryPairingState1StateTypeId) + 1)
|
||||
|
||||
|
||||
def configValueChanged(paramTypeId, value):
|
||||
logger.log("Plugin config value changed:", paramTypeId, value, watchingAutoThings)
|
||||
|
|
@ -74,7 +84,7 @@ async def setupThing(info):
|
|||
info.finish(nymea.ThingErrorNoError)
|
||||
|
||||
|
||||
def postSetupThing(thing):
|
||||
async def postSetupThing(thing):
|
||||
logger.log("postSetupThing for", thing.name, thing.params[0].value)
|
||||
thing.nameChangedHandler = lambda thing : logger.log("Thing name changed", thing.name)
|
||||
|
||||
|
|
@ -82,7 +92,9 @@ def postSetupThing(thing):
|
|||
logger.log("State 1 value:", thing.stateValue(pyMockAutoState1StateTypeId))
|
||||
|
||||
if thing.thingClassId == pyMockDiscoveryPairingThingClassId:
|
||||
logger.log("Setting 1 value:", thing.settingsValue(pyMockDiscoveryPairingSettingsSetting1ParamTypeId))
|
||||
logger.log("Param 1 value:", thing.paramValue(pyMockDiscoveryPairingThingParam1ParamTypeId))
|
||||
logger.log("Setting 1 value:", thing.setting(pyMockDiscoveryPairingSettingsSetting1ParamTypeId))
|
||||
|
||||
|
||||
|
||||
def autoThings():
|
||||
|
|
|
|||
Loading…
Reference in New Issue