Skip to content
Snippets Groups Projects
Commit c26be7ce authored by Manuel Krombholz's avatar Manuel Krombholz
Browse files

Merge branch 'dev' of https://git-st.inf.tu-dresden.de/OpenLicht/eraser into dev

parents 87104a7d 6a12cc69
No related branches found
No related tags found
No related merge requests found
Pipeline #10657 passed
Showing
with 316 additions and 167 deletions
......@@ -23,3 +23,5 @@ ComparingExpression : LogicalExpression ::= LeftOperand:NumberExpression RightOp
abstract BinaryLogicalExpression : LogicalExpression ::= LeftOperand:LogicalExpression RightOperand:LogicalExpression ;
AndExpression : BinaryLogicalExpression ;
OrExpression : BinaryLogicalExpression ;
ComparatorBox ::= <It:ComparatorType> ;
......@@ -8,6 +8,9 @@ aspect Printing {
eq Root.prettyPrint() {
StringBuilder sb = new StringBuilder();
sb.append(getSmartHomeEntityModel().prettyPrint());
for (Rule r : getRules()) {
sb.append(r.prettyPrint());
}
sb.append(getMqttRoot().prettyPrint());
sb.append(getInfluxRoot().prettyPrint());
sb.append(getMachineLearningRoot().prettyPrint());
......@@ -66,6 +69,7 @@ aspect Printing {
.addOptional("category", hasCategory(), () -> getCategory().getName())
.addOptional("topic", hasTopic(), () -> getTopic().getTopicString())
.addOptionalPrettyPrint(getMetaData())
.addOptional("performance", hasFrequencySetting(), () -> getFrequencySetting().getID())
.build();
}
......@@ -241,30 +245,92 @@ aspect Printing {
syn String ParenthesizedLogicalExpression.prettyPrint() = "(" + getOperand().prettyPrint() + ")";
syn String NotExpression.prettyPrint() = "!" + getOperand().prettyPrint();
eq ComparingExpression.prettyPrint() {
switch (getComparator()) {
case NotEquals: return "(" + getLeftOperand().prettyPrint() + " != " + getRightOperand().prettyPrint() + ")";
case Equals: return "(" + getLeftOperand().prettyPrint() + " == " + getRightOperand().prettyPrint() + ")";
case LessThan: return "(" + getLeftOperand().prettyPrint() + " < " + getRightOperand().prettyPrint() + ")";
case GreaterThan: return "(" + getLeftOperand().prettyPrint() + " > " + getRightOperand().prettyPrint() + ")";
case LessOrEqualThan: return "(" + getLeftOperand().prettyPrint() + " <= " + getRightOperand().prettyPrint() + ")";
case GreaterOrEqualThan: return "(" + getLeftOperand().prettyPrint() + " >= " + getRightOperand().prettyPrint() + ")";
default: throw new IllegalArgumentException("Unknown compartor type: " + getComparator());
}
return "(" + getLeftOperand().prettyPrint() + Condition.operatorAsString(getComparator()) + getRightOperand().prettyPrint() + ")";
}
syn String AndExpression.prettyPrint() = "(" + getLeftOperand().prettyPrint() + " & " + getRightOperand().prettyPrint() + ")";
syn String OrExpression.prettyPrint() = "(" + getLeftOperand().prettyPrint() + " | " + getRightOperand().prettyPrint() + ")";
syn String Designator.prettyPrint() = getItem().getID();
public static String Condition.operatorAsString(ComparatorType comparatorType) {
switch (comparatorType) {
case NotEquals: return "!=";
case Equals: return "==";
case LessThan: return "<";
case GreaterThan: return ">";
case LessOrEqualThan: return "<=";
case GreaterOrEqualThan: return ">=";
default: throw new IllegalArgumentException("Unknown comparator type: " + comparatorType);
}
}
// Rules
eq Rule.prettyPrint() {
return new MemberPrinter("Rule")
.addIds("TriggeringItems", getObserverList().size(), getObserverList(),
io -> io.observedItem().getID())
.addNodes("Condition", getNumCondition(), getConditionList(),
Condition::toString)
.addNodes("Action", getNumAction(), getActionList(),
Action::toString)
.addRequired("name", getID())
.addCustomIterable("items", getObserverList().size(), getObserverList(),
io -> io.observedItem().getID(),true)
.addIterable(new JastAddList<>().addAll(getConditionList()).addAll(getActionList()))
.build();
}
//ItemStateCheck
eq ItemStateCheckCondition.prettyPrint() {
return "Condition: ItemStateCheck " + getItemStateCheck().prettyPrint();
}
//ItemStateNumberCheck
eq ItemStateNumberCheck.prettyPrint() {
return Condition.operatorAsString(getComparator()) + " " + getValue();
}
//ItemStateStringCheck
eq ItemStateStringCheck.prettyPrint() {
return Condition.operatorAsString(getComparator()) + " " + "\""+getValue()+"\"";
}
//ItemStateChangeCondition
eq ItemStateChangeCondition.prettyPrint() {
return "Condition: ItemStateChange " + getItem().getID();
}
//ExpressionCondition
eq ExpressionCondition.prettyPrint() {
return "Condition: Expression " + getLogicalExpression().prettyPrint();
}
//TriggerRuleAction
eq TriggerRuleAction.prettyPrint() {
return "Action: TriggerRule " + getRule().getID();
}
//NoopAction
eq NoopAction.prettyPrint() {
return "Action: Noop";
}
//SetStateFromConstantStringAction
eq SetStateFromConstantStringAction.prettyPrint() {
return "Action: SetStateFromConstant " + getAffectedItem().getID() + " \"" + getNewState()+ "\"";
}
//SetStateFromTriggeringItemAction
eq SetStateFromTriggeringItemAction.prettyPrint() {
return "Action: SetStateFromTrigger " + getAffectedItem().getID();
}
//MultiplyDoubleToStateAction
eq MultiplyDoubleToStateAction.prettyPrint() {
return "Action: SetStateFromTrigger " + getAffectedItem().getID() + " " + getMultiplier();
}
//SetStateFromExpressionAction
eq SetStateFromExpressionAction.prettyPrint() {
return "Action: StateFromExpression " + getNumberExpression().prettyPrint() + " " + getAffectedItem().getID();
}
}
......@@ -88,6 +88,16 @@ aspect Resolving {
return java.util.Optional.empty();
}
//--- resolveRule ---
syn java.util.Optional<Rule> Root.resolveRule(String ruleName) {
for (Rule rule : getRules()) {
if (rule.getID().equals(ruleName)) {
return java.util.Optional.of(rule);
}
}
return java.util.Optional.empty();
}
//--- resolveActivity ---
syn java.util.Optional<Activity> Root.resolveActivity(int identifier) {
for (Activity activity : getMachineLearningRoot().getActivityList()) {
......@@ -136,6 +146,11 @@ aspect Resolving {
return getRoot().getSmartHomeEntityModel().resolveFrequencySetting(id).orElseThrow(() -> new RuntimeException("FrequencySetting '" + id + "' not found!"));
}
// _._ -> Rule
refine RefResolverStubs eq ASTNode.globallyResolveRuleByToken(String id) {
return getRoot().resolveRule(id).orElseThrow(() -> new RuntimeException("Rule '" + id + "' not found!"));
}
// Thing.Channel* -> Channel
refine RefResolverStubs eq Thing.resolveChannelByToken(String id, int position) {
return containingSmartHomeEntityModel().resolveChannel(id).orElseThrow(() -> new RuntimeException("Channel '" + id + "' not found!"));
......
......@@ -76,21 +76,16 @@ aspect Rules {
public void NoopAction.applyFor(Item item) {
// empty by design
}
public void LambdaAction.applyFor(Item item) {
getLambda().accept(item);
}
public void TriggerRuleAction.applyFor(Item item) {
getRule().trigger(item);
}
public void SetStateFromExpression.applyFor(Item item) {
public void SetStateFromExpressionAction.applyFor(Item item) {
getAffectedItem().setStateFromDouble(getNumberExpression().eval());
}
public void SetStateFromConstantStringAction.applyFor(Item item) {
getAffectedItem().setStateFromString(getNewState());
}
public void SetStateFromLambdaAction.applyFor(Item item) {
getAffectedItem().setStateFromString(getNewStateProvider().get());
}
public void SetStateFromTriggeringItemAction.applyFor(Item item) {
Item target = getAffectedItem();
if (target==item) {
......@@ -98,9 +93,6 @@ aspect Rules {
}
item.copyStateTo(target);
}
public void SetStateFromItemsAction.applyFor(Item item) {
getAffectedItem().setStateFromString(getCombinator().apply(getSourceItems()));
}
public void AddDoubleToStateAction.applyFor(Item item) {
getAffectedItem().setStateFromDouble(getAffectedItem().getStateAsDouble() + getIncrement());
}
......@@ -116,7 +108,7 @@ aspect StateSyncGroup {
Rule rule = new Rule();
for (Item item : getTargetItemList()) {
rule.addAction(new SetStateFromTriggeringItemAction(item));
rule.addAction(new SetStateFromTriggeringItemAction().setAffectedItem(item));
rule.addObserver(item.getItemObserver());
}
......
// --- New ECA rules ---
Rule ::= Condition* Action* ;
Rule : ModelElement ::= Condition* Action* ;
abstract Condition ;
ItemStateCheckCondition : Condition ::= ItemStateCheck ;
ItemStateChangeCondition : Condition ;
......@@ -7,7 +7,6 @@ rel ItemStateChangeCondition.Item -> Item;
ExpressionCondition : Condition ::= LogicalExpression ;
abstract Action ;
NoopAction : Action ;
LambdaAction : Action ::= <Lambda:Action2EditConsumer> ;
TriggerRuleAction : Action ;
rel TriggerRuleAction.Rule -> Rule ;
......@@ -15,14 +14,11 @@ rel TriggerRuleAction.Rule -> Rule ;
abstract SetStateAction : Action ;
rel SetStateAction.AffectedItem -> Item ;
SetStateFromExpression : SetStateAction ::= NumberExpression ;
SetStateFromExpressionAction : SetStateAction ::= NumberExpression ;
SetStateFromConstantStringAction : SetStateAction ::= <NewState:String> ;
SetStateFromLambdaAction : SetStateAction ::= <NewStateProvider:NewStateProvider> ;
SetStateFromTriggeringItemAction : SetStateAction ::= ;
SetStateFromItemsAction : SetStateAction ::= <Combinator:ItemsToStringFunction> ;
rel SetStateFromItemsAction.SourceItem* -> Item ;
AddDoubleToStateAction : SetStateAction ::= <Increment:double> ;
MultiplyDoubleToStateAction : SetStateAction ::= <Multiplier:double> ;
......@@ -33,3 +29,7 @@ rel ItemObserver.TriggeredRule* <-> Rule.Observer* ;
FrequencySetting : LabelledModelElement ::= <EventProcessingFrequency:double> ;
StateSyncGroup : Rule ;
rel StateSyncGroup.TargetItem* -> Item;
ConditionActionPack ::= ;
rel ConditionActionPack.Condition* -> Condition;
rel ConditionActionPack.Action* -> Action;
\ No newline at end of file
......@@ -71,12 +71,23 @@ Comment = "//" [^\n\r]+
"RollerShutter" { return sym(Terminals.ROLLER_SHUTTER); }
"String" { return sym(Terminals.STRING); }
"Switch" { return sym(Terminals.SWITCH); }
// actions and conditions
"ItemStateCheck" { return sym(Terminals.ITEM_STATE_CHECK); }
"ItemStateChange" { return sym(Terminals.ITEM_STATE_CHANGE); }
"Expression" { return sym(Terminals.EXPRESSION); }
"Noop" { return sym(Terminals.NOOP_ACTION); }
"TriggerRule" { return sym(Terminals.TRIGGER_RULE_ACTION); }
"SetStateFromConstant" { return sym(Terminals.STATE_FROM_CONST_ACTION); }
"SetStateFromTrigger" { return sym(Terminals.STATE_FROM_TRIGGER_ACTION); }
"StateFromExpression" { return sym(Terminals.STATE_FROM_EXPRESSION_ACTION); }
// within specification
"Action" { return sym(Terminals.ACTION); }
"activities" { return sym(Terminals.ACTIVITIES); }
"aggregation" { return sym(Terminals.AGGREGATION); }
"category" { return sym(Terminals.CATEGORY); }
"channels" { return sym(Terminals.CHANNELS); }
"channelTypes" { return sym(Terminals.CHANNEL_TYPES); }
"Condition" { return sym(Terminals.CONDITION); }
"context" { return sym(Terminals.CONTEXT); }
"dbName" { return sym(Terminals.DB_NAME); }
"default" { return sym(Terminals.DEFAULT); }
......@@ -84,6 +95,7 @@ Comment = "//" [^\n\r]+
"groups" { return sym(Terminals.GROUPS); }
"host" { return sym(Terminals.HOST); }
"id" { return sym(Terminals.ID); }
"name" { return sym(Terminals.LABELNAME); }
"incoming" { return sym(Terminals.INCOMING); }
"items" { return sym(Terminals.ITEMS); }
"itemType" { return sym(Terminals.ITEM_TYPE); }
......
......@@ -95,7 +95,7 @@ import java.util.HashMap;
rule.addCondition(condition);
break;
case AltGoals.number_expression:
SetStateFromExpression action = new SetStateFromExpression();
SetStateFromExpressionAction action = new SetStateFromExpressionAction();
action.setNumberExpression(exp.asNumberExpression());
rule.addAction(action);
break;
......@@ -127,7 +127,7 @@ Root root =
| influx_root.ir root.r {: r.setInfluxRoot(ir); return r; :}
| machine_learning_root.ml root.r {: r.setMachineLearningRoot(ml); return r; :}
| rule.rule root.r {: r.addRule(rule); return r; :}
| frequency_setting.ip root.r {: insertZero(r.getSmartHomeEntityModel().getFrequencySettingList(), ip); return r; :}
| frequency_setting.fs root.r {: insertZero(r.getSmartHomeEntityModel().getFrequencySettingList(), fs); return r; :}
| {: return Root.createEmptyRoot(); :}
;
......@@ -157,12 +157,7 @@ LogicalExpression logical_expression =
| LB_ROUND logical_expression.a OR logical_expression.b RB_ROUND {: return new OrExpression(a, b); :}
| EXCLAMATION logical_expression.e {: return new NotExpression(e); :}
| LB_ROUND logical_expression.e RB_ROUND {: return new ParenthesizedLogicalExpression(e); :}
| LB_ROUND number_expression.a LT number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.LessThan); :}
| LB_ROUND number_expression.a LE number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.LessOrEqualThan); :}
| LB_ROUND number_expression.a EQ number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.Equals); :}
| LB_ROUND number_expression.a NE number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.NotEquals); :}
| LB_ROUND number_expression.a GE number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.GreaterOrEqualThan); :}
| LB_ROUND number_expression.a GT number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.GreaterThan); :}
| LB_ROUND number_expression.a comparator.cp number_expression.b RB_ROUND {: return new ComparingExpression(a, b, cp.getIt()); :}
;
NumberLiteralExpression literal_expression =
......@@ -372,21 +367,71 @@ MachineLearningRoot machine_learning_root_body =
// Rule: condition=condition action=a1 action=a2;
// (only allow one condition and action for now)
//Rule rule =
// RULE COLON CONDITION EQUALS condition.c action.a SEMICOLON {: Rule result = new Rule();
// result.addCondition(c);
// result.addAction(a);
// return result; :}
// ;
Rule rule =
RULE COLON CONDITION EQUALS condition.c action.a SEMICOLON {: Rule result = new Rule();
result.addCondition(c);
result.addAction(a);
return result; :}
RULE COLON rule_body.rb SEMICOLON {: return rb; :}
;
//Rule: name="testRue1" items=["ITEM_ID","ITEM_ID"] conditions=["CONDITION", "CONDITION"] action=["ACTION", "ACTION"]
// (only allow one condition and action for now)
Rule rule_body =
LABELNAME EQUALS TEXT.n rule_body.r {: return r.setID(n); :}
| ITEMS EQUALS string_list.items rule_body.r {: items.forEach(i -> r.activateFor(createItemPlaceHolder(i))); return r; :}
| LB_SQUARE capack.cap RB_SQUARE rule_body.r {: return r.setActionList(new JastAddList().addAll(cap.getActions())).setConditionList(new JastAddList().addAll(cap.getConditions())); :}
| {: return new Rule(); :}
;
ConditionActionPack capack =
COMMA capack.cap {: return cap; :}
| CONDITION COLON condition.c capack.cap {: cap.addCondition(c); return cap; :}
| ACTION COLON action.a capack.cap {: cap.addAction(a); return cap; :}
| {: return new ConditionActionPack(); :}
;
Condition condition =
logical_expression.be {: return new ExpressionCondition(be); :}
ITEM_STATE_CHECK isccondition.iscc {: return iscc; :}
| ITEM_STATE_CHANGE NAME.n {: return new ItemStateChangeCondition(Item.createRef(n)); :}
| EXPRESSION logical_expression.le {: return new ExpressionCondition(le); :}
;
ItemStateCheckCondition isccondition =
comparator.cp REAL.r {: return new ItemStateCheckCondition().setItemStateCheck(new ItemStateNumberCheck().setValue(Double.parseDouble(r)).setComparator(cp.getIt())); :}
| comparator.cp TEXT.n {: return new ItemStateCheckCondition().setItemStateCheck(new ItemStateStringCheck().setValue(n).setComparator(cp.getIt())); :}
ComparatorBox comparator =
NE {: return new ComparatorBox(ComparatorType.NotEquals); :}
| EQ {: return new ComparatorBox(ComparatorType.Equals); :}
| LT {: return new ComparatorBox(ComparatorType.LessThan); :}
| GT {: return new ComparatorBox(ComparatorType.GreaterThan); :}
| LE {: return new ComparatorBox(ComparatorType.LessOrEqualThan); :}
| GE {: return new ComparatorBox(ComparatorType.GreaterOrEqualThan); :}
;
// TODO implement action cases
Action action = {: return new NoopAction(); :}
Action action =
TRIGGER_RULE_ACTION NAME.n {: return new TriggerRuleAction().setRule(Rule.createRef(n)); :}
| NOOP_ACTION {:return new NoopAction(); :}
| STATE_FROM_CONST_ACTION NAME.i TEXT.s {: return new SetStateFromConstantStringAction().setNewState(s).setAffectedItem(Item.createRef(i)); :}
| STATE_FROM_TRIGGER_ACTION NAME.i {: return new SetStateFromTriggeringItemAction().setAffectedItem(Item.createRef(i)); :}
| STATE_FROM_TRIGGER_ACTION NAME.i REAL.r {: return new MultiplyDoubleToStateAction().setMultiplier(Double.parseDouble(r)).setAffectedItem(Item.createRef(i)); :}
| STATE_FROM_EXPRESSION_ACTION number_expression.ne NAME.i {: return new SetStateFromExpressionAction().setNumberExpression(ne).setAffectedItem(Item.createRef(i)); :}
;
// Util: StringList, StringKeyMap, IntegerKeyMap
StringList string_list =
LB_SQUARE string_list_body.slb RB_SQUARE {: return slb; :}
......
......@@ -28,6 +28,7 @@ aspect SmartHomeEntityModel {
result.setID(this.getID());
result.setLabel(this.getLabel());
result.setMetaData(this.getMetaData());
result.setFrequencySetting(this.getFrequencySetting());
if (this.hasTopic()) {
result.setTopic(this.getTopic());
}
......
......@@ -5,9 +5,13 @@ import de.tudresden.inf.st.eraser.jastadd.model.ModelElement;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
/**
* Fluent API to pretty-print ASTNodes in a line-based fashion.
......@@ -91,7 +95,7 @@ public class MemberPrinter {
* @param listOfNodes the list of nodes
* @param mapping a function to map a node to a String
*/
private <T extends ASTNode<?>> void concatNodes(Iterable<T> listOfNodes,
private <T> void concatNodes(Iterable<T> listOfNodes,
Function<T, String> mapping,
boolean quote) {
boolean first = true;
......@@ -129,15 +133,55 @@ public class MemberPrinter {
* @return this
*/
public MemberPrinter addIds(String name, int count, Iterable<? extends ModelElement> listOfElements) {
return addCustomIterable(name,count,listOfElements,ModelElement::getID,true);
}
/**
* Appends model elements to the builder, if count is positive.
* @param name The name of the member
* @param count The number of items in the listOfElements
* @param listOfElements The list of model elements
* @param printPart The function that is called on the elements of the list
* @return this
*/
public <T> MemberPrinter addCustomIterable(String name, int count, Iterable<T> listOfElements, Function<T,String> printPart,boolean quote) {
if (count > 0) {
sb.append(' ').append(name).append("=[");
concatIds(listOfElements);
sb.append(' ');
if (name!=null && !name.isEmpty()) {
sb.append(name).append("=");
}
sb.append("[");
concatNodes(listOfElements,printPart,quote);
sb.append("]");
this.empty = false;
}
return this;
}
/**
* Appends model elements to the builder, if count is positive. "Name=..." won't appear
* @param count The number of items in the listOfElements
* @param listOfElements The list of model elements
* @param printPart The function that is called on the elements of the list
* @return this
*/
public <T> MemberPrinter addCustomIterable(int count, Iterable<T> listOfElements, Function<T,String> printPart,boolean quote) {
return addCustomIterable(null,count,listOfElements,printPart,quote);
}
public <T> MemberPrinter addCustomIterable(String name, Iterable<T> listOfElements, Function<T,String> printPart,boolean quote) {
List<T> list = StreamSupport.stream(listOfElements.spliterator(), false)
.collect(Collectors.toList());
return addCustomIterable(name,list.size(),list,printPart,quote);
}
public <T extends ASTNode<?>> MemberPrinter addIterable(Iterable<T> listOfElements) {
return addIterable(null,listOfElements);
}
public <T extends ASTNode<?>> MemberPrinter addIterable(String name,Iterable<T> listOfElements) {
return addCustomIterable(name,listOfElements,ASTNode::prettyPrint,false);
}
/**
* Appends nodes to the builder, if count is positive.
* @param name The name of the member
......@@ -254,7 +298,7 @@ public class MemberPrinter {
* @return this
*/
public MemberPrinter addNonDefault(String name, Object actualValue, Object defaultValue) {
if (!actualValue.equals(defaultValue)) {
if (actualValue==null || !actualValue.equals(defaultValue)) {
this.empty = false;
return add(name, actualValue);
}
......
......@@ -225,8 +225,8 @@ public class RulesTest {
Rule rule = new Rule();
ItemStateNumberCheck check1 = new ItemStateNumberCheck(ComparatorType.GreaterOrEqualThan, 4);
ItemStateNumberCheck check2 = new ItemStateNumberCheck(ComparatorType.LessThan, 6);
rule.addCondition(new ItemStateCheckCondition(check1));
rule.addCondition(new ItemStateCheckCondition(check2));
rule.addCondition(new ItemStateCheckCondition().setItemStateCheck(check1));
rule.addCondition(new ItemStateCheckCondition().setItemStateCheck(check2));
CountingAction counter = new CountingAction();
rule.addAction(counter);
root.addRule(rule);
......@@ -540,7 +540,7 @@ public class RulesTest {
CountingAction counter2 = new CountingAction();
ruleB.addAction(counter2);
ruleA.addAction(new TriggerRuleAction(ruleB));
ruleA.addAction(new TriggerRuleAction().setRule(ruleB));
root.addRule(ruleA);
root.addRule(ruleB);
......@@ -576,7 +576,7 @@ public class RulesTest {
NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, true);
Rule rule = new Rule();
rule.addAction(new SetStateFromConstantStringAction(item2, "5"));
rule.addAction(new SetStateFromConstantStringAction().setNewState("5").setAffectedItem(item2));
CountingAction counter = new CountingAction();
rule.addAction(counter);
root.addRule(rule);
......@@ -597,44 +597,6 @@ public class RulesTest {
}
}
@Test
public void testSetStateFromLambdaAction() {
ValuedStateProvider provider = new ValuedStateProvider();
TestUtils.ModelAndItem modelAndItem = createModelAndItem(0);
Root root = modelAndItem.model.getRoot();
NumberItem item = modelAndItem.item;
NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 3, true);
Rule rule = new Rule();
rule.addAction(new SetStateFromLambdaAction(item2, provider));
CountingAction counter = new CountingAction();
rule.addAction(counter);
root.addRule(rule);
rule.activateFor(item);
assertEquals(3, item2.asItemWithDoubleState().getState(), DELTA, "Affected item not initialized correctly");
provider.value = 4;
setState(item, 1);
assertEquals(1, counter.get(item), "Change of item state should trigger the rule");
assertEquals(4, item2.asItemWithDoubleState().getState(), DELTA, "Change of item state should set the state of the affected item");
provider.value = 4;
setState(item, 2);
assertEquals(2, counter.get(item), "Change of item state should trigger the rule");
assertEquals(4, item2.asItemWithDoubleState().getState(), DELTA, "Change of item state should set the state of the affected item");
provider.value = 5;
setState(item, 2);
assertEquals(2, counter.get(item), "Change of item to same state should not trigger the rule");
assertEquals(4, item2.asItemWithDoubleState().getState(), DELTA, "Change of item to same state should not set the state of the affected item");
provider.value = 5;
setState(item, 3);
assertEquals(3, counter.get(item), "Change of item state should trigger the rule");
assertEquals(5, item2.asItemWithDoubleState().getState(), DELTA, "Change of item state should set the state of the affected item");
}
@Test
public void testSetStateFromTriggeringItemAction() {
TestUtils.ModelAndItem modelAndItem = createModelAndItem(3);
......@@ -644,7 +606,7 @@ public class RulesTest {
Rule rule = new Rule();
CountingAction counter = new CountingAction();
rule.addAction(new SetStateFromTriggeringItemAction(item2));
rule.addAction(new SetStateFromTriggeringItemAction().setAffectedItem(item2));
rule.addAction(counter);
root.addRule(rule);
rule.activateFor(item);
......@@ -665,65 +627,6 @@ public class RulesTest {
assertEquals("7.0", item2.getState(), "Change of item state should set the state of the affected item");
}
@Test
public void testSetStateFromItemsAction() {
TestUtils.ModelAndItem modelAndItem = createModelAndItem(3);
Root root = modelAndItem.model.getRoot();
NumberItem item = modelAndItem.item;
NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, true);
StringItem affectedItem = addStringItem(root.getSmartHomeEntityModel(), "1");
Rule rule = new Rule();
SetStateFromItemsAction action = new SetStateFromItemsAction(items ->
Long.toString(StreamSupport.stream(items.spliterator(), false)
.mapToLong(inner -> (long) inner.asItemWithDoubleState().getState())
.sum()));
action.addSourceItem(item);
action.addSourceItem(item2);
action.setAffectedItem(affectedItem);
rule.addAction(action);
CountingAction counter = new CountingAction();
rule.addAction(counter);
root.addRule(rule);
rule.activateFor(item);
assertEquals(0, counter.get(item), "Counter not initialized correctly");
assertEquals("1", affectedItem.getState(), "Affected item not initialized correctly");
// 5 + 4 = 9
setState(item, 5);
assertEquals(1, counter.get(item), "Change of item state should trigger the rule");
assertEquals("9", affectedItem.getState(), "Change of item state should set the state of the affected item");
// still 5 + 4 = 9, as rule does not trigger for item2
setState(item2, 5);
assertEquals(1, counter.get(item), "Change of item2 state should not trigger the rule");
assertEquals("9", affectedItem.getState(), "Change of item2 state should not set the state of the affected item");
// still 5 + 4 = 9, as rule should not trigger
setState(item, 5);
assertEquals(1, counter.get(item), "Change of item to same state should not trigger the rule");
assertEquals("9", affectedItem.getState(), "Change of item to same state should not set the state of the affected item");
// 7 + 5 = 12
setState(item, 7);
assertEquals(2, counter.get(item), "Change of item state should trigger the rule");
assertEquals("12", affectedItem.getState(), "Change of item state should set the state of the affected item");
// add new item to sum
NumberItem item3 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), -4, true);
action.addSourceItem(item3);
// still 7 + 5 = 12, as rule should not trigger
setState(item, 7);
assertEquals(2, counter.get(item), "Change of item to same state should not trigger the rule");
assertEquals("12", affectedItem.getState(), "Change of item to same state should not set the state of the affected item");
// 8 + 5 - 4 = 9
setState(item, 8);
assertEquals(3, counter.get(item), "Change of item state should trigger the rule");
assertEquals("9", affectedItem.getState(), "Change of item state should set the state of the affected item");
}
@Test
public void testAddDoubleToStateAction() {
......@@ -733,7 +636,7 @@ public class RulesTest {
NumberItem affectedItem = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, true);
Rule rule = new Rule();
rule.addAction(new AddDoubleToStateAction(affectedItem, 2));
rule.addAction(new AddDoubleToStateAction().setIncrement(2).setAffectedItem(affectedItem));
CountingAction counter = new CountingAction();
rule.addAction(counter);
root.addRule(rule);
......@@ -766,7 +669,7 @@ public class RulesTest {
NumberItem affectedItem = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, true);
Rule rule = new Rule();
rule.addAction(new MultiplyDoubleToStateAction(affectedItem, 2));
rule.addAction(new MultiplyDoubleToStateAction().setMultiplier(2).setAffectedItem(affectedItem));
CountingAction counter = new CountingAction();
rule.addAction(counter);
root.addRule(rule);
......@@ -800,16 +703,16 @@ public class RulesTest {
NumberItem affectedItem = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 5, true);
Rule ruleA = new Rule();
ruleA.addAction(new AddDoubleToStateAction(affectedItem, 2));
ruleA.addAction(new AddDoubleToStateAction().setIncrement(2).setAffectedItem(affectedItem));
CountingAction counterA = new CountingAction();
ruleA.addAction(counterA);
Rule ruleB = new Rule();
ruleB.addAction(new MultiplyDoubleToStateAction(affectedItem, 3));
ruleB.addAction(new MultiplyDoubleToStateAction().setMultiplier(3).setAffectedItem(affectedItem));
CountingAction counterB = new CountingAction();
ruleB.addAction(counterB);
ruleA.addAction(new TriggerRuleAction(ruleB));
ruleA.addAction(new TriggerRuleAction().setRule(ruleB));
root.addRule(ruleA);
root.addRule(ruleB);
......@@ -851,7 +754,7 @@ public class RulesTest {
NumberItem affectedItem = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 5, true);
Rule rule = new Rule();
SetStateFromExpression action = new SetStateFromExpression();
SetStateFromExpressionAction action = new SetStateFromExpressionAction();
// TODO item1 should be referred to as triggering item
action.setNumberExpression(ParserUtils.parseNumberExpression("(" + item1.getID() + " + " + item2.getID() + ")", root));
action.setAffectedItem(affectedItem);
......
Mqtt: incoming="ppc2/" outgoing="oh2/in" host="localhost" ;
Item: id="iris1_item" label="Iris 1" state="121,88,68" topic="iris1_item/state";
Item: id="iris1_item" label="Iris 1" state="121,88,68" topic="iris1_item/state" performance="ip1" ;
Group: id="my-group" items=["iris1_item"] ;
ThingType: id="skywriter-hat" label="SkyWriterHAT" description="SkyWriterHAT Gesture Recognition" parameters=["brokername"] channelTypes=["flick-type"] ;
ChannelType: id="flick-type" itemType="String" label="Last Flick" description="Last Flick detected (and its direction)" category="Motion" readOnly ;
......
Item: id="iris1_item" label="Iris 1" state="121,88,68" topic="iris1_item/state" ;
Item: id="iris1_item" label="Iris 1" state="121,88,68" topic="iris1_item/state" performance="ip1" ;
Group: id="my-group" items=["iris1_item"] ;
ThingType: id="skywriter-hat" label="SkyWriterHAT" description="SkyWriterHAT Gesture Recognition" parameters=["brokername"] channelTypes=["flick-type"] ;
ChannelType: id="flick-type" label="Last Flick" description="Last Flick detected (and its direction)" itemType="String" category="Motion" readOnly ;
......
result=OUTPUT_PASS
Test of different actions and conditions in rules
\ No newline at end of file
Color Item: id="iris1_item" label="Iris 1" state="121,88,68" topic="iris1_item/state" ;
Color Item: id="iris2_item" label="Iris 2" state="121,88,68" topic="iris1_item/state" ;
Group: id="Unknown" items=["iris1_item", "iris2_item"] ;
Rule: name="testRule2" items=["iris1_item"] [Condition: ItemStateCheck < 6.0] ;
Rule: name="eqfff" items=["iris1_item"] [Condition: ItemStateCheck < "Yeah" ] ;
Rule: name="testRule1" items=["iris1_item"] [Condition: ItemStateCheck < 4.0, Action: TriggerRule testRule2] ;
Rule: name="testRule416" items=["iris1_item"] [Condition: ItemStateChange iris1_item] ;
Rule: name="testRule456" items=["iris1_item"] [Action: Noop] ;
Rule: name="testRule46" items=["iris1_item"] [Action: SetStateFromConstant iris1_item "K"] ;
Rule: name="testRule8" items=["iris1_item"] [Action: SetStateFromTrigger iris2_item] ;
Rule: name="testRul118" items=["iris1_item"] [Action: SetStateFromTrigger iris2_item 2.0] ;
Rule: name="testR8" items=["iris1_item"] [Condition: Expression (2.0<3.0)] ;
Rule: name="testR8" items=["iris1_item"] [Condition: Expression (2.0<iris1_item)] ;
Rule: name="testR8556" items=["iris1_item"] [Action: StateFromExpression (2.0 + 3.0) iris1_item] ;
Rule: name="testR8556" items=["iris1_item"] [Action: StateFromExpression iris1_item iris1_item] ;
Color Item: id="iris1_item" label="Iris 1" state="121,88,68" topic="iris1_item/state" ;
Color Item: id="iris2_item" label="Iris 2" state="121,88,68" topic="iris1_item/state" ;
Group: id="Unknown" items=["iris1_item", "iris2_item"] ;
Rule: name="testR8556" items=["iris1_item"] [Action: StateFromExpression iris1_item iris1_item] ;
Rule: name="testR8556" items=["iris1_item"] [Action: StateFromExpression (2.0 + 3.0) iris1_item] ;
Rule: name="testR8" items=["iris1_item"] [Condition: Expression (2.0<iris1_item)] ;
Rule: name="testR8" items=["iris1_item"] [Condition: Expression (2.0<3.0)] ;
Rule: name="testRul118" items=["iris1_item"] [Action: SetStateFromTrigger iris2_item 2.0] ;
Rule: name="testRule8" items=["iris1_item"] [Action: SetStateFromTrigger iris2_item] ;
Rule: name="testRule46" items=["iris1_item"] [Action: SetStateFromConstant iris1_item "K"] ;
Rule: name="testRule456" items=["iris1_item"] [Action: Noop] ;
Rule: name="testRule416" items=["iris1_item"] [Condition: ItemStateChange iris1_item] ;
Rule: name="testRule1" items=["iris1_item"] [Condition: ItemStateCheck < 4.0, Action: TriggerRule testRule2] ;
Rule: name="eqfff" items=["iris1_item"] [Condition: ItemStateCheck < "Yeah"] ;
Rule: name="testRule2" items=["iris1_item"] [Condition: ItemStateCheck < 6.0] ;
......@@ -98,3 +98,40 @@ Influx: user="" password="" dbName="" host="" ;
- `user` and `password` define connection credentials
- `dbName` defines the name of the database to use
- `host` defines the URL of the host running an InfluxDB. The port will be set to `8086` and cannot be changed.
## Rule
```
Rule: name="" items=["ITEM_ID", "ITEM_ID"] [Condition: CONDITION_CONSTRUCTOR, Condition: CONDITION_CONSTRUCTOR, Action: ACTION_CONSTRUCTOR, Action: ACTION_CONSTRUCTOR] ;
```
- if the state of an included item (from attribute `items`) changes the rule gets triggered
- when a rule is triggered each conditions receives the triggering item for check
- conditions checks are aggregated with `AND` operator
- if every condition returns true when checking each action will run using the triggering item
### Constructors
#### Conditions
| Name | Description | Structure | Example |
| ---------------------- |--------------------------------------------------| ------------------------------------| -------------------------|
| ItemStateNumberCheck | compares the state with NUMBER using the OPERATOR | ItemStateCheck OPERATOR NUMBER | ItemStateCheck < 5 |
| ItemStateTextCheck | compares the state with TEXT using the OPERATOR | ItemStateCheck OPERATOR TEXT | ItemStateCheck < "abc" |
| ItemStateChange | triggers if specified ITEM changed its value | ItemStateChange ITEM ItemStateChange | ItemStateChange item1 |
| ExpressionCondition | triggers if the LOGICAL_EXPRESSION is true | Expression LOGICAL_EXPRESSION | Expression (2<3) |
#### Actions
| Name | Description | Structure | Example |
| -------------------- |----------------------------------------------------------------------------------------------| -------------------------------| ------------------------------|
| TriggerRuleAction | triggers the rule with with specified RULE_NAME (conditions of RULE are still being checked) | TriggerRuleAction RULE_NAME | TriggerRuleAction testRule |
| NoopAction | no operation (just for testing) | NoopAction | NoopAction |
| SetStateFromConstantStringAction | apply specified VALUE to the ITEM | SetStateFromConstant ITEM VALUE | SetStateFromConstant item1 "LMNOP" |
| SetStateFromTriggeringItemAction | copies state from triggering device to specified ITEM | SetStateFromTrigger ITEM | SetStateFromTrigger item1 |
| MultiplyDoubleToStateAction | copies state multiplied by MULTIPLIER from triggering item to specified ITEM | SetStateFromTrigger ITEM MULTIPLIER | SetStateFromTrigger item1 2.0 |
| StateFromExpressionAction | apply the evaluated NUMBER_EXPRESSION as state to ITEM | StateFromExpression NUMBER_EXPRESSION ITEM | StateFromExpression (2.0 + 3.0) item2 |
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment