Merge PR #646: Add support for state based value comparison in rules

This commit is contained in:
Jenkins nymea 2021-08-18 15:12:18 +02:00
commit 22a48894da
10 changed files with 233 additions and 53 deletions

View File

@ -280,7 +280,7 @@ void RuleManager::parseEventDescriptors(const QVariantList &eventDescriptorList,
StateEvaluator *RuleManager::parseStateEvaluator(const QVariantMap &stateEvaluatorMap) StateEvaluator *RuleManager::parseStateEvaluator(const QVariantMap &stateEvaluatorMap)
{ {
// qDebug() << "Parsing state evaluator. Child count:" << stateEvaluatorMap.value("childEvaluators").toList().count(); qDebug() << "Parsing state evaluator:" << qUtf8Printable(QJsonDocument::fromVariant(stateEvaluatorMap).toJson());
if (!stateEvaluatorMap.contains("stateDescriptor")) { if (!stateEvaluatorMap.contains("stateDescriptor")) {
return nullptr; return nullptr;
} }
@ -295,6 +295,11 @@ StateEvaluator *RuleManager::parseStateEvaluator(const QVariantMap &stateEvaluat
} else { } else {
sd = new StateDescriptor(sdMap.value("interface").toString(), sdMap.value("interfaceState").toString(), op, sdMap.value("value"), stateEvaluator); sd = new StateDescriptor(sdMap.value("interface").toString(), sdMap.value("interfaceState").toString(), op, sdMap.value("value"), stateEvaluator);
} }
if (sdMap.contains("valueThingId") && sdMap.contains("valueStateTypeId")) {
sd->setValueThingId(sdMap.value("valueThingId").toUuid());
sd->setValueStateTypeId(sdMap.value("valueStateTypeId").toUuid());
}
qDebug() << "Parsing stuff" << sd->valueThingId() << sd->valueStateTypeId();
stateEvaluator->setStateDescriptor(sd); stateEvaluator->setStateDescriptor(sd);
foreach (const QVariant &childEvaluatorVariant, stateEvaluatorMap.value("childEvaluators").toList()) { foreach (const QVariant &childEvaluatorVariant, stateEvaluatorMap.value("childEvaluators").toList()) {
@ -572,7 +577,12 @@ QVariantMap RuleManager::packStateEvaluator(StateEvaluator *stateEvaluator)
} }
QMetaEnum valueOperatorEnum = QMetaEnum::fromType<StateDescriptor::ValueOperator>(); QMetaEnum valueOperatorEnum = QMetaEnum::fromType<StateDescriptor::ValueOperator>();
stateDescriptor.insert("operator", valueOperatorEnum.valueToKeys(stateEvaluator->stateDescriptor()->valueOperator())); stateDescriptor.insert("operator", valueOperatorEnum.valueToKeys(stateEvaluator->stateDescriptor()->valueOperator()));
stateDescriptor.insert("value", stateEvaluator->stateDescriptor()->value()); if (!stateEvaluator->stateDescriptor()->value().isNull()) {
stateDescriptor.insert("value", stateEvaluator->stateDescriptor()->value());
} else {
stateDescriptor.insert("valueThingId", stateEvaluator->stateDescriptor()->valueThingId());
stateDescriptor.insert("valueStateTypeId", stateEvaluator->stateDescriptor()->valueStateTypeId());
}
ret.insert("stateDescriptor", stateDescriptor); ret.insert("stateDescriptor", stateDescriptor);
QVariantList childEvaluators; QVariantList childEvaluators;
for (int i = 0; i < stateEvaluator->childEvaluators()->rowCount(); i++) { for (int i = 0; i < stateEvaluator->childEvaluators()->rowCount(); i++) {

View File

@ -330,7 +330,7 @@ QDebug printStateEvaluator(QDebug &dbg, StateEvaluator *stateEvaluator, int inde
dbg << ">="; dbg << ">=";
break; break;
} }
dbg << stateEvaluator->stateDescriptor()->value() << endl; dbg << stateEvaluator->stateDescriptor()->value() << '/' << stateEvaluator->stateDescriptor()->valueThingId() << stateEvaluator->stateDescriptor()->valueStateTypeId() << endl;
} }
if (stateEvaluator->childEvaluators()->rowCount() > 0) { if (stateEvaluator->childEvaluators()->rowCount() > 0) {
for (int i = 0; i < indentLevel; i++) { dbg << " "; } for (int i = 0; i < indentLevel; i++) { dbg << " "; }

View File

@ -135,11 +135,39 @@ void StateDescriptor::setValue(const QVariant &value)
} }
} }
QUuid StateDescriptor::valueThingId() const
{
return m_valueThingId;
}
void StateDescriptor::setValueThingId(const QUuid &valueThingId)
{
if (m_valueThingId != valueThingId) {
m_valueThingId = valueThingId;
emit valueThingIdChanged();
}
}
QUuid StateDescriptor::valueStateTypeId() const
{
return m_valueStateTypeId;
}
void StateDescriptor::setValueStateTypeId(const QUuid &valueStateTypeId)
{
if (m_valueStateTypeId != valueStateTypeId) {
m_valueStateTypeId = valueStateTypeId;
emit valueStateTypeIdChanged();
}
}
StateDescriptor *StateDescriptor::clone() const StateDescriptor *StateDescriptor::clone() const
{ {
StateDescriptor *ret = new StateDescriptor(thingId(), stateTypeId(), valueOperator(), value()); StateDescriptor *ret = new StateDescriptor(thingId(), stateTypeId(), valueOperator(), value());
ret->setInterfaceName(interfaceName()); ret->setInterfaceName(interfaceName());
ret->setInterfaceState(interfaceState()); ret->setInterfaceState(interfaceState());
ret->setValueThingId(valueThingId());
ret->setValueStateTypeId(valueStateTypeId());
return ret; return ret;
} }
@ -147,11 +175,13 @@ StateDescriptor *StateDescriptor::clone() const
#define COMPARE_PTR(a, b) if (!a->operator==(b)) { qDebug() << a << "!=" << b; return false; } #define COMPARE_PTR(a, b) if (!a->operator==(b)) { qDebug() << a << "!=" << b; return false; }
bool StateDescriptor::operator==(StateDescriptor *other) const bool StateDescriptor::operator==(StateDescriptor *other) const
{ {
COMPARE(m_thingId, other->thingId()); COMPARE(m_thingId, other->thingId())
COMPARE(m_stateTypeId, other->stateTypeId()); COMPARE(m_stateTypeId, other->stateTypeId())
COMPARE(m_interfaceName, other->interfaceName()); COMPARE(m_interfaceName, other->interfaceName())
COMPARE(m_interfaceState, other->interfaceState()); COMPARE(m_interfaceState, other->interfaceState())
COMPARE(m_operator, other->valueOperator()); COMPARE(m_operator, other->valueOperator())
COMPARE(m_value, other->value()); COMPARE(m_value, other->value())
COMPARE(m_valueThingId, other->valueThingId())
COMPARE(m_valueStateTypeId, other->valueStateTypeId())
return true; return true;
} }

View File

@ -44,6 +44,8 @@ class StateDescriptor : public QObject
Q_PROPERTY(QString interfaceState READ interfaceState WRITE setInterfaceState NOTIFY interfaceStateChanged) Q_PROPERTY(QString interfaceState READ interfaceState WRITE setInterfaceState NOTIFY interfaceStateChanged)
Q_PROPERTY(ValueOperator valueOperator READ valueOperator WRITE setValueOperator NOTIFY valueOperatorChanged) Q_PROPERTY(ValueOperator valueOperator READ valueOperator WRITE setValueOperator NOTIFY valueOperatorChanged)
Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged)
Q_PROPERTY(QUuid valueThingId READ valueThingId WRITE setValueThingId NOTIFY valueThingIdChanged)
Q_PROPERTY(QUuid valueStateTypeId READ valueStateTypeId WRITE setValueStateTypeId NOTIFY valueStateTypeIdChanged)
public: public:
enum ValueOperator { enum ValueOperator {
@ -78,6 +80,12 @@ public:
QVariant value() const; QVariant value() const;
void setValue(const QVariant &value); void setValue(const QVariant &value);
QUuid valueThingId() const;
void setValueThingId(const QUuid &valueThingId);
QUuid valueStateTypeId() const;
void setValueStateTypeId(const QUuid &valueStateTypeId);
StateDescriptor* clone() const; StateDescriptor* clone() const;
bool operator==(StateDescriptor *other) const; bool operator==(StateDescriptor *other) const;
@ -88,6 +96,8 @@ signals:
void interfaceStateChanged(); void interfaceStateChanged();
void valueOperatorChanged(); void valueOperatorChanged();
void valueChanged(); void valueChanged();
void valueThingIdChanged();
void valueStateTypeIdChanged();
private: private:
QUuid m_thingId; QUuid m_thingId;
@ -96,6 +106,8 @@ private:
QString m_interfaceState; QString m_interfaceState;
ValueOperator m_operator = ValueOperatorEquals; ValueOperator m_operator = ValueOperatorEquals;
QVariant m_value; QVariant m_value;
QUuid m_valueThingId;
QUuid m_valueStateTypeId;
}; };
#endif // STATEDESCRIPTOR_H #endif // STATEDESCRIPTOR_H

View File

@ -96,12 +96,8 @@ StateEvaluator *StateEvaluator::clone() const
{ {
StateEvaluator *ret = new StateEvaluator(); StateEvaluator *ret = new StateEvaluator();
ret->m_operator = this->m_operator; ret->m_operator = this->m_operator;
ret->m_stateDescriptor->setThingId(this->m_stateDescriptor->thingId()); delete ret->m_stateDescriptor; // Delete existing empty one and replace it with a clone of the
ret->m_stateDescriptor->setStateTypeId(this->m_stateDescriptor->stateTypeId()); ret->m_stateDescriptor = this->m_stateDescriptor->clone();
ret->m_stateDescriptor->setInterfaceName(this->m_stateDescriptor->interfaceName());
ret->m_stateDescriptor->setInterfaceState(this->m_stateDescriptor->interfaceState());
ret->m_stateDescriptor->setValueOperator(this->m_stateDescriptor->valueOperator());
ret->m_stateDescriptor->setValue(this->m_stateDescriptor->value());
for (int i = 0; i < this->m_childEvaluators->rowCount(); i++) { for (int i = 0; i < this->m_childEvaluators->rowCount(); i++) {
ret->m_childEvaluators->addStateEvaluator(this->m_childEvaluators->get(i)->clone()); ret->m_childEvaluators->addStateEvaluator(this->m_childEvaluators->get(i)->clone());
} }

View File

@ -137,18 +137,26 @@ ItemDelegate {
} }
Component { Component {
id: boolComponent id: boolComponent
Switch { Item {
checked: root.param.value === true implicitHeight: theSwitch.implicitHeight
Component.onCompleted: { implicitWidth: theSwitch.implicitWidth
if (root.param.value === undefined) { Switch {
id: theSwitch
anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
width: Math.min(parent.width, implicitiWidth)
checked: root.param.value === true
Component.onCompleted: {
if (root.param.value === undefined) {
root.param.value = checked;
}
}
onClicked: {
root.param.value = checked; root.param.value = checked;
} }
} }
onClicked: {
root.param.value = checked;
}
} }
} }
Component { Component {
id: sliderComponent id: sliderComponent

View File

@ -122,6 +122,8 @@ Page {
ParamDelegate { ParamDelegate {
id: paramDelegate id: paramDelegate
Layout.fillWidth: true Layout.fillWidth: true
hoverEnabled: false
padding: 0
paramType: root.actionType.paramTypes.get(index) paramType: root.actionType.paramTypes.get(index)
enabled: staticParamRadioButton.checked enabled: staticParamRadioButton.checked
nameVisible: false nameVisible: false

View File

@ -38,38 +38,151 @@ import Nymea 1.0
Page { Page {
id: root id: root
// Needs to be set and filled in with thingId and eventTypeId // Needs to be set and filled in with thingId and eventTypeId
property var stateDescriptor: null property StateDescriptor stateDescriptor: null
readonly property Thing thing: stateDescriptor && stateDescriptor.thingId ? engine.thingManager.things.getThing(stateDescriptor.thingId) : null readonly property Thing thing: stateDescriptor && stateDescriptor.thingId ? engine.thingManager.things.getThing(stateDescriptor.thingId) : null
readonly property var iface: stateDescriptor && stateDescriptor.interfaceName ? Interfaces.findByName(stateDescriptor.interfaceName) : null readonly property Interface iface: stateDescriptor && stateDescriptor.interfaceName ? Interfaces.findByName(stateDescriptor.interfaceName) : null
readonly property var stateType: thing ? thing.thingClass.stateTypes.getStateType(stateDescriptor.stateTypeId) readonly property StateType stateType: thing ? thing.thingClass.stateTypes.getStateType(stateDescriptor.stateTypeId)
: iface ? iface.stateTypes.findByName(stateDescriptor.interfaceState) : null : iface ? iface.stateTypes.findByName(stateDescriptor.interfaceState) : null
signal backPressed(); signal backPressed();
signal completed(); signal completed();
header: NymeaHeader { header: NymeaHeader {
text: qsTr("Options") text: qsTr("Condition")
onBackPressed: root.backPressed(); onBackPressed: root.backPressed();
} }
ColumnLayout { QtObject {
anchors.fill: parent id: d
ParamDescriptorDelegate { property var value: root.stateType.defaultValue
id: paramDelegate }
Layout.fillWidth: true
paramType: root.stateType GroupBox {
value: paramType.defaultValue anchors {
left: parent.left
top: parent.top
right: parent.right
margins: Style.margins
} }
Button {
text: qsTr("OK") GridLayout {
Layout.fillWidth: true anchors.fill: parent
Layout.margins: app.margins columns: width > 600 ? 2 : 1
onClicked: {
root.stateDescriptor.valueOperator = paramDelegate.operatorType Label {
root.stateDescriptor.value = paramDelegate.value Layout.fillWidth: true
root.completed() text: "%1, %2".arg(root.thing.name).arg(root.stateType.displayName)
} }
ComboBox {
id: operatorComboBox
Layout.fillWidth: true
property bool isNumeric: {
switch (root.stateType.type.toLowerCase()) {
case "bool":
case "string":
case "qstring":
case "color":
return false;
case "int":
case "double":
return true;
}
console.warn("ParamDescriptorDelegate: Unhandled data type:", root.stateType.type.toLowerCase());
return false;
}
model: isNumeric ?
[qsTr("is equal to"), qsTr("is not equal to"), qsTr("is smaller than"), qsTr("is greater than"), qsTr("is smaller or equal than"), qsTr("is greater or equal than")]
: [qsTr("is"), qsTr("is not")];
}
GroupBox {
Layout.columnSpan: parent.columns
Layout.fillWidth: true
GridLayout {
anchors.fill: parent
columns: root.width > 600 ? 2 : 1
RadioButton {
id: staticValueRadioButton
Layout.fillWidth: true
checked: true
text: qsTr("a static value:")
font.pixelSize: app.smallFont
}
RadioButton {
id: stateValueRadioButton
Layout.fillWidth: true
text: qsTr("another thing's state:")
font.pixelSize: app.smallFont
visible: engine.jsonRpcClient.ensureServerVersion("5.3")
}
ThinDivider { Layout.columnSpan: parent.columns }
ParamDelegate {
Layout.fillWidth: true
hoverEnabled: false
padding: 0
paramType: root.thing.thingClass.eventTypes.getEventType(root.stateType.id).paramTypes.getParamType(root.stateType.id)
enabled: staticValueRadioButton.checked
nameVisible: false
value: d.value
visible: staticValueRadioButton.checked
placeholderText: qsTr("Insert value here")
}
NymeaItemDelegate {
id: statePickerDelegate
Layout.fillWidth: true
text: thingId === null || stateTypeId === null
? qsTr("Select a state")
: thing.name + " - " + thing.thingClass.stateTypes.getStateType(stateTypeId).displayName
visible: stateValueRadioButton.checked
property var thingId: null
property var stateTypeId: null
readonly property Thing thing: engine.thingManager.things.getThing(thingId)
onClicked: {
var page = pageStack.push(Qt.resolvedUrl("SelectThingPage.qml"), {showStates: true, showEvents: false, showActions: false });
page.thingSelected.connect(function(thing) {
print("Thing selected", thing.name);
statePickerDelegate.thingId = thing.id
var selectStatePage = pageStack.replace(Qt.resolvedUrl("SelectStatePage.qml"), {thing: thing})
selectStatePage.stateSelected.connect(function(stateTypeId) {
print("State selected", stateTypeId)
pageStack.pop();
statePickerDelegate.stateTypeId = stateTypeId;
})
})
page.backPressed.connect(function() {
pageStack.pop();
})
}
}
}
}
Button {
text: qsTr("OK")
Layout.fillWidth: true
Layout.margins: app.margins
onClicked: {
root.stateDescriptor.valueOperator = operatorComboBox.currentIndex
if (staticValueRadioButton.checked) {
root.stateDescriptor.value = d.value
} else {
root.stateDescriptor.valueThingId = statePickerDelegate.thingId
root.stateDescriptor.valueStateTypeId = statePickerDelegate.stateTypeId
}
root.completed()
}
}
} }
} }
} }

View File

@ -49,7 +49,7 @@ Page {
ListView { ListView {
anchors.fill: parent anchors.fill: parent
model: thing.thingClass.stateTypes model: root.thing.thingClass.stateTypes
delegate: NymeaSwipeDelegate { delegate: NymeaSwipeDelegate {
width: parent.width width: parent.width

View File

@ -39,12 +39,12 @@ SwipeDelegate {
Layout.fillWidth: true Layout.fillWidth: true
clip: true clip: true
property var stateEvaluator: null property StateEvaluator stateEvaluator: null
property bool showChilds: false property bool showChilds: false
readonly property Thing thing: stateEvaluator ? engine.thingManager.things.getThing(stateEvaluator.stateDescriptor.thingId) : null readonly property Thing thing: stateEvaluator ? engine.thingManager.things.getThing(stateEvaluator.stateDescriptor.thingId) : null
readonly property var iface: stateEvaluator ? Interfaces.findByName(stateEvaluator.stateDescriptor.interfaceName) : null readonly property Interface iface: stateEvaluator ? Interfaces.findByName(stateEvaluator.stateDescriptor.interfaceName) : null
readonly property var stateType: thing ? thing.thingClass.stateTypes.getStateType(stateEvaluator.stateDescriptor.stateTypeId) readonly property StateType stateType: thing ? thing.thingClass.stateTypes.getStateType(stateEvaluator.stateDescriptor.stateTypeId)
: iface ? iface.stateTypes.findByName(stateEvaluator.stateDescriptor.interfaceState) : null : iface ? iface.stateTypes.findByName(stateEvaluator.stateDescriptor.interfaceState) : null
signal deleteClicked(); signal deleteClicked();
@ -96,17 +96,26 @@ SwipeDelegate {
if (!root.stateType) { if (!root.stateType) {
return qsTr("Press to edit condition") return qsTr("Press to edit condition")
} }
var valueText = root.stateEvaluator.stateDescriptor.value;
switch (root.stateType.type.toLowerCase()) { var valueText;
case "bool": if (root.stateEvaluator.stateDescriptor.value !== undefined) {
valueText = root.stateEvaluator.stateDescriptor.value === true ? qsTr("True") : qsTr("False") valueText = root.stateEvaluator.stateDescriptor.value;
break; switch (root.stateType.type.toLowerCase()) {
case "bool":
valueText = root.stateEvaluator.stateDescriptor.value === true ? qsTr("True") : qsTr("False")
break;
}
} else {
print("value thing id:", root.stateEvaluator.stateDescriptor.valueThingId)
var valueThing = engine.thingManager.things.getThing(root.stateEvaluator.stateDescriptor.valueThingId)
valueText = valueThing.name
valueText += ", " + valueThing.thingClass.stateTypes.getStateType(root.stateEvaluator.stateDescriptor.valueStateTypeId).displayName
} }
if (root.thing) { if (root.thing) {
return qsTr("%1: %2 %3 %4").arg(root.thing.name).arg(root.stateType.displayName).arg(operatorString).arg(valueText) return "%1: %2 %3 %4".arg(root.thing.name).arg(root.stateType.displayName).arg(operatorString).arg(valueText)
} else if (root.iface) { } else if (root.iface) {
return qsTr("%1: %2 %3 %4").arg(root.iface.displayName).arg(root.stateType.displayName).arg(operatorString).arg(valueText) return "%1, %2 %3 %4".arg(root.iface.displayName).arg(root.stateType.displayName).arg(operatorString).arg(valueText)
} }
return "--"; return "--";
} }