Add support for state based value comparison in rules
parent
bc2b603d3d
commit
d31dd54f74
|
|
@ -280,7 +280,7 @@ void RuleManager::parseEventDescriptors(const QVariantList &eventDescriptorList,
|
|||
|
||||
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")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -295,6 +295,11 @@ StateEvaluator *RuleManager::parseStateEvaluator(const QVariantMap &stateEvaluat
|
|||
} else {
|
||||
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);
|
||||
|
||||
foreach (const QVariant &childEvaluatorVariant, stateEvaluatorMap.value("childEvaluators").toList()) {
|
||||
|
|
@ -572,7 +577,12 @@ QVariantMap RuleManager::packStateEvaluator(StateEvaluator *stateEvaluator)
|
|||
}
|
||||
QMetaEnum valueOperatorEnum = QMetaEnum::fromType<StateDescriptor::ValueOperator>();
|
||||
stateDescriptor.insert("operator", valueOperatorEnum.valueToKeys(stateEvaluator->stateDescriptor()->valueOperator()));
|
||||
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);
|
||||
QVariantList childEvaluators;
|
||||
for (int i = 0; i < stateEvaluator->childEvaluators()->rowCount(); i++) {
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@ QDebug printStateEvaluator(QDebug &dbg, StateEvaluator *stateEvaluator, int inde
|
|||
dbg << ">=";
|
||||
break;
|
||||
}
|
||||
dbg << stateEvaluator->stateDescriptor()->value() << endl;
|
||||
dbg << stateEvaluator->stateDescriptor()->value() << '/' << stateEvaluator->stateDescriptor()->valueThingId() << stateEvaluator->stateDescriptor()->valueStateTypeId() << endl;
|
||||
}
|
||||
if (stateEvaluator->childEvaluators()->rowCount() > 0) {
|
||||
for (int i = 0; i < indentLevel; i++) { dbg << " "; }
|
||||
|
|
|
|||
|
|
@ -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 *ret = new StateDescriptor(thingId(), stateTypeId(), valueOperator(), value());
|
||||
ret->setInterfaceName(interfaceName());
|
||||
ret->setInterfaceState(interfaceState());
|
||||
ret->setValueThingId(valueThingId());
|
||||
ret->setValueStateTypeId(valueStateTypeId());
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -147,11 +175,13 @@ StateDescriptor *StateDescriptor::clone() const
|
|||
#define COMPARE_PTR(a, b) if (!a->operator==(b)) { qDebug() << a << "!=" << b; return false; }
|
||||
bool StateDescriptor::operator==(StateDescriptor *other) const
|
||||
{
|
||||
COMPARE(m_thingId, other->thingId());
|
||||
COMPARE(m_stateTypeId, other->stateTypeId());
|
||||
COMPARE(m_interfaceName, other->interfaceName());
|
||||
COMPARE(m_interfaceState, other->interfaceState());
|
||||
COMPARE(m_operator, other->valueOperator());
|
||||
COMPARE(m_value, other->value());
|
||||
COMPARE(m_thingId, other->thingId())
|
||||
COMPARE(m_stateTypeId, other->stateTypeId())
|
||||
COMPARE(m_interfaceName, other->interfaceName())
|
||||
COMPARE(m_interfaceState, other->interfaceState())
|
||||
COMPARE(m_operator, other->valueOperator())
|
||||
COMPARE(m_value, other->value())
|
||||
COMPARE(m_valueThingId, other->valueThingId())
|
||||
COMPARE(m_valueStateTypeId, other->valueStateTypeId())
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ class StateDescriptor : public QObject
|
|||
Q_PROPERTY(QString interfaceState READ interfaceState WRITE setInterfaceState NOTIFY interfaceStateChanged)
|
||||
Q_PROPERTY(ValueOperator valueOperator READ valueOperator WRITE setValueOperator NOTIFY valueOperatorChanged)
|
||||
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:
|
||||
enum ValueOperator {
|
||||
|
|
@ -78,6 +80,12 @@ public:
|
|||
QVariant value() const;
|
||||
void setValue(const QVariant &value);
|
||||
|
||||
QUuid valueThingId() const;
|
||||
void setValueThingId(const QUuid &valueThingId);
|
||||
|
||||
QUuid valueStateTypeId() const;
|
||||
void setValueStateTypeId(const QUuid &valueStateTypeId);
|
||||
|
||||
StateDescriptor* clone() const;
|
||||
bool operator==(StateDescriptor *other) const;
|
||||
|
||||
|
|
@ -88,6 +96,8 @@ signals:
|
|||
void interfaceStateChanged();
|
||||
void valueOperatorChanged();
|
||||
void valueChanged();
|
||||
void valueThingIdChanged();
|
||||
void valueStateTypeIdChanged();
|
||||
|
||||
private:
|
||||
QUuid m_thingId;
|
||||
|
|
@ -96,6 +106,8 @@ private:
|
|||
QString m_interfaceState;
|
||||
ValueOperator m_operator = ValueOperatorEquals;
|
||||
QVariant m_value;
|
||||
QUuid m_valueThingId;
|
||||
QUuid m_valueStateTypeId;
|
||||
};
|
||||
|
||||
#endif // STATEDESCRIPTOR_H
|
||||
|
|
|
|||
|
|
@ -96,12 +96,8 @@ StateEvaluator *StateEvaluator::clone() const
|
|||
{
|
||||
StateEvaluator *ret = new StateEvaluator();
|
||||
ret->m_operator = this->m_operator;
|
||||
ret->m_stateDescriptor->setThingId(this->m_stateDescriptor->thingId());
|
||||
ret->m_stateDescriptor->setStateTypeId(this->m_stateDescriptor->stateTypeId());
|
||||
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());
|
||||
delete ret->m_stateDescriptor; // Delete existing empty one and replace it with a clone of the
|
||||
ret->m_stateDescriptor = this->m_stateDescriptor->clone();
|
||||
for (int i = 0; i < this->m_childEvaluators->rowCount(); i++) {
|
||||
ret->m_childEvaluators->addStateEvaluator(this->m_childEvaluators->get(i)->clone());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,7 +137,13 @@ ItemDelegate {
|
|||
}
|
||||
Component {
|
||||
id: boolComponent
|
||||
Item {
|
||||
implicitHeight: theSwitch.implicitHeight
|
||||
implicitWidth: theSwitch.implicitWidth
|
||||
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) {
|
||||
|
|
@ -150,6 +156,8 @@ ItemDelegate {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Component {
|
||||
id: sliderComponent
|
||||
RowLayout {
|
||||
|
|
|
|||
|
|
@ -122,6 +122,8 @@ Page {
|
|||
ParamDelegate {
|
||||
id: paramDelegate
|
||||
Layout.fillWidth: true
|
||||
hoverEnabled: false
|
||||
padding: 0
|
||||
paramType: root.actionType.paramTypes.get(index)
|
||||
enabled: staticParamRadioButton.checked
|
||||
nameVisible: false
|
||||
|
|
|
|||
|
|
@ -38,38 +38,151 @@ import Nymea 1.0
|
|||
Page {
|
||||
id: root
|
||||
// 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 var iface: stateDescriptor && stateDescriptor.interfaceName ? Interfaces.findByName(stateDescriptor.interfaceName) : null
|
||||
readonly property var stateType: thing ? thing.thingClass.stateTypes.getStateType(stateDescriptor.stateTypeId)
|
||||
readonly property Interface iface: stateDescriptor && stateDescriptor.interfaceName ? Interfaces.findByName(stateDescriptor.interfaceName) : null
|
||||
readonly property StateType stateType: thing ? thing.thingClass.stateTypes.getStateType(stateDescriptor.stateTypeId)
|
||||
: iface ? iface.stateTypes.findByName(stateDescriptor.interfaceState) : null
|
||||
|
||||
signal backPressed();
|
||||
signal completed();
|
||||
|
||||
header: NymeaHeader {
|
||||
text: qsTr("Options")
|
||||
text: qsTr("Condition")
|
||||
onBackPressed: root.backPressed();
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
ParamDescriptorDelegate {
|
||||
id: paramDelegate
|
||||
Layout.fillWidth: true
|
||||
paramType: root.stateType
|
||||
value: paramType.defaultValue
|
||||
QtObject {
|
||||
id: d
|
||||
property var value: root.stateType.defaultValue
|
||||
}
|
||||
|
||||
GroupBox {
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
margins: Style.margins
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
anchors.fill: parent
|
||||
columns: width > 600 ? 2 : 1
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
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 = paramDelegate.operatorType
|
||||
root.stateDescriptor.value = paramDelegate.value
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ Page {
|
|||
ListView {
|
||||
anchors.fill: parent
|
||||
|
||||
model: thing.thingClass.stateTypes
|
||||
model: root.thing.thingClass.stateTypes
|
||||
|
||||
delegate: NymeaSwipeDelegate {
|
||||
width: parent.width
|
||||
|
|
|
|||
|
|
@ -39,12 +39,12 @@ SwipeDelegate {
|
|||
Layout.fillWidth: true
|
||||
clip: true
|
||||
|
||||
property var stateEvaluator: null
|
||||
property StateEvaluator stateEvaluator: null
|
||||
property bool showChilds: false
|
||||
|
||||
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 var stateType: thing ? thing.thingClass.stateTypes.getStateType(stateEvaluator.stateDescriptor.stateTypeId)
|
||||
readonly property Interface iface: stateEvaluator ? Interfaces.findByName(stateEvaluator.stateDescriptor.interfaceName) : null
|
||||
readonly property StateType stateType: thing ? thing.thingClass.stateTypes.getStateType(stateEvaluator.stateDescriptor.stateTypeId)
|
||||
: iface ? iface.stateTypes.findByName(stateEvaluator.stateDescriptor.interfaceState) : null
|
||||
|
||||
signal deleteClicked();
|
||||
|
|
@ -96,17 +96,26 @@ SwipeDelegate {
|
|||
if (!root.stateType) {
|
||||
return qsTr("Press to edit condition")
|
||||
}
|
||||
var valueText = root.stateEvaluator.stateDescriptor.value;
|
||||
|
||||
var valueText;
|
||||
if (root.stateEvaluator.stateDescriptor.value !== undefined) {
|
||||
valueText = root.stateEvaluator.stateDescriptor.value;
|
||||
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) {
|
||||
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) {
|
||||
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 "--";
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue