Skip to content
Snippets Groups Projects
Commit 0ce98daa authored by René Schöne's avatar René Schöne
Browse files

Merge branch '17-check-if-item-controlling-can-be-modeled-using-eca-rules' into 'dev'

Resolve "Check if item-controlling can be modeled using ECA rules"

See merge request !10
parents adaa29b3 91a15d1e
No related branches found
No related tags found
2 merge requests!19dev to master,!10Resolve "Check if item-controlling can be modeled using ECA rules"
Pipeline #9488 passed
Showing
with 499 additions and 166 deletions
aspect AdditionalTypes {
public class StringList extends beaver.Symbol implements Iterable<String> {
private java.util.Deque<String> delegatee = new java.util.ArrayDeque<>();
public class ReversedList<T> extends beaver.Symbol implements Iterable<T> {
private java.util.Deque<T> delegatee = new java.util.ArrayDeque<>();
public java.util.Iterator<String> iterator() {
public java.util.Iterator<T> iterator() {
return delegatee.descendingIterator();
}
public void add(String s) {
delegatee.add(s);
public void add(T t) {
delegatee.add(t);
}
}
public class StringList extends ReversedList<String> {}
public class ItemList extends ReversedList<Item> {}
public class TypedKeyMap<T> extends beaver.Symbol implements Iterable<AbstractMap.SimpleEntry<T, String>> {
private java.util.Deque<AbstractMap.SimpleEntry<T, String>> delegatee = new java.util.ArrayDeque<>();
......
......@@ -281,6 +281,8 @@ aspect ItemHandling {
stateUpdated(shouldSendState);
}
syn ItemObserver Item.getItemObserver() = new ItemObserver();
//--- stateUpdated ---
/**
* Called, whenever the state of an item is updated. Does various things including:
......@@ -300,10 +302,8 @@ aspect ItemHandling {
logger.catching(e);
}
}
if (hasItemObserver()) {
getItemObserver().apply();
}
}
//--- sendState ---
protected void Item.sendState() throws Exception {
......@@ -407,6 +407,7 @@ aspect ItemHandling {
SetStateFromTriggeringItemAction action = new SetStateFromTriggeringItemAction();
action.setAffectedItem(controlledItem);
rule.addAction(action);
rule.activateFor(controllerItem);
return rule;
......@@ -464,6 +465,7 @@ aspect ItemHandling {
//--- copyStateTo ---
protected abstract void Item.copyStateTo(Item stateReceiver);
protected void ItemWithBooleanState.copyStateTo(Item stateReceiver) {
stateReceiver.setStateFromBoolean(this.getState());
}
......@@ -480,6 +482,9 @@ aspect ItemHandling {
stateReceiver.setStateFromInstant(this.getState());
}
private void ColorItem.setBrightness(int value) {
setState(getState().withDifferentBrightness(value));
}
......
......@@ -29,104 +29,6 @@ aspect Navigation {
return result;
}
//--- resolveThingType ---
syn java.util.Optional<ThingType> SmartHomeEntityModel.resolveThingType(String thingTypeId) {
for (ThingType thingType : this.getThingTypeList()) {
if (thingType.getID().equals(thingTypeId)) {
return java.util.Optional.of(thingType);
}
}
return java.util.Optional.empty();
}
//--- resolveChannel ---
syn java.util.Optional<Channel> SmartHomeEntityModel.resolveChannel(String channelId) {
for (Thing thing : this.getThingList()) {
for (Channel channel : thing.getChannelList()) {
if (channel.getID().equals(channelId)) {
return java.util.Optional.of(channel);
}
}
}
return java.util.Optional.empty();
}
//--- resolveChannelType ---
syn java.util.Optional<ChannelType> SmartHomeEntityModel.resolveChannelType(String channelTypeId) {
for (ChannelType channelType : this.getChannelTypeList()) {
if (channelType.getID().equals(channelTypeId)) {
return java.util.Optional.of(channelType);
}
}
return java.util.Optional.empty();
}
//--- resolveItem ---
syn java.util.Optional<Item> SmartHomeEntityModel.resolveItem(String itemId) {
if ("activity".equals(itemId)) {
return Optional.of(getActivityItem());
}
for (Item item : items()) {
if (item.getID().equals(itemId)) {
return java.util.Optional.of(item);
}
}
return java.util.Optional.empty();
}
//--- resolveGroup ---
syn java.util.Optional<Group> SmartHomeEntityModel.resolveGroup(String groupId) {
for (Group group : this.getGroupList()) {
if (group.getID().equals(groupId)) {
return java.util.Optional.of(group);
}
}
return java.util.Optional.empty();
}
//--- resolveMqttTopic ---
syn java.util.Optional<MqttTopic> Root.resolveMqttTopic(String mqttTopicId) {
return this.getMqttRoot().resolveTopic(mqttTopicId);
}
//--- resolveItemCategory ---
syn java.util.Optional<ItemCategory> SmartHomeEntityModel.resolveItemCategory(String categoryName) {
for (ItemCategory category : getItemCategoryList()) {
if (category.getName().equals(categoryName)) {
return java.util.Optional.of(category);
}
}
return java.util.Optional.empty();
}
//--- resolveActivity ---
syn java.util.Optional<Activity> Root.resolveActivity(int identifier) {
for (Activity activity : getMachineLearningRoot().getActivityList()) {
if (activity.getIdentifier() == identifier) {
return java.util.Optional.of(activity);
}
}
return java.util.Optional.empty();
}
syn java.util.Optional<Activity> Root.resolveActivity(String label) {
for (Activity activity : getMachineLearningRoot().getActivityList()) {
if (activity.getLabel().equals(label)) {
return java.util.Optional.of(activity);
}
}
return java.util.Optional.empty();
}
//--- resolveChangeEvent ---
syn java.util.Optional<ChangeEvent> Root.resolveChangeEvent(int identifier) {
for (ChangeEvent changeEvent : getMachineLearningRoot().getChangeEventList()) {
if (changeEvent.getIdentifier() == identifier) {
return java.util.Optional.of(changeEvent);
}
}
return java.util.Optional.empty();
}
//--- containingThing ---
inh Thing Channel.containingThing();
eq Thing.getChannel().containingThing() = this;
......
......@@ -3,7 +3,7 @@ aspect Printing {
String ASTNode.safeID(ModelElement elem) { return elem == null ? "NULL" : elem.getID(); }
syn String Root.prettyPrint() {
eq Root.prettyPrint() {
StringBuilder sb = new StringBuilder();
sb.append(getSmartHomeEntityModel().prettyPrint());
sb.append(getMqttRoot().prettyPrint());
......@@ -13,7 +13,7 @@ aspect Printing {
}
//--- SmartHomeEntityModel.prettyPrint() ---
syn String SmartHomeEntityModel.prettyPrint() {
eq SmartHomeEntityModel.prettyPrint() {
StringBuilder sb = new StringBuilder();
for (Thing t : getThingList()) {
sb.append(t.prettyPrint());
......@@ -40,7 +40,7 @@ aspect Printing {
}
//Thing: id="" label="" type="" channels=["CHANNEL_ID", "CHANNEL_ID"] ;
syn String Thing.prettyPrint() {
eq Thing.prettyPrint() {
return new MemberPrinter("Thing")
.addRequired("id", getID())
.addNonDefault("label", getLabel())
......@@ -50,7 +50,7 @@ aspect Printing {
}
//ITEM_TYPE Item: id="" label="" state="" category="" topic="";
syn String Item.prettyPrint() {
eq Item.prettyPrint() {
return new MemberPrinter(prettyPrintType())
.addRequired("id", getID())
.addNonDefault("label", getLabel())
......@@ -79,7 +79,7 @@ aspect Printing {
eq DefaultItem.prettyPrintType() = "Item" ;
// special ActivityItem printing. Always omit state.
syn String ActivityItem.prettyPrint() {
eq ActivityItem.prettyPrint() {
return new MemberPrinter(prettyPrintType())
.addRequired("id", getID())
.addNonDefault("label", getLabel())
......@@ -94,7 +94,7 @@ aspect Printing {
//Group: id="" groups=["GROUP_ID", "GROUP_ID"] items=["ITEM_ID", "ITEM_ID"] aggregation=AGG;
// AGG either '"agg-name"', or '"agg-name" ("param1", "param2")'
syn String Group.prettyPrint() {
eq Group.prettyPrint() {
return new MemberPrinter("Group")
.addRequired("id", getID())
.addNonDefault("label", getLabel())
......@@ -104,7 +104,6 @@ aspect Printing {
.build();
}
syn String GroupAggregationFunction.prettyPrint();
eq SimpleGroupAggregationFunction.prettyPrint() {
if (getFunctionName() == SimpleGroupAggregationFunctionName.EQUALITY) {
return "";
......@@ -120,7 +119,7 @@ aspect Printing {
}
//ThingType: id="" label="" description="" parameters=["PARAM_ID", "PARAM_ID"] channelTypes=["CHANNEL_TYPE_ID", "CHANNEL_TYPE_ID"];
syn String ThingType.prettyPrint() {
eq ThingType.prettyPrint() {
return new MemberPrinter("ThingType")
.addRequired("id", getID())
.addNonDefault("label", getLabel())
......@@ -131,7 +130,7 @@ aspect Printing {
}
//Parameter: id="" label="" description="" type="" default="" required;
syn String Parameter.prettyPrint() {
eq Parameter.prettyPrint() {
return new MemberPrinter("Parameter")
.addRequired("id", getID())
.addNonDefault("label", getLabel())
......@@ -144,7 +143,7 @@ aspect Printing {
}
//ChannelType: id="" label="" description="" itemType="" category="" readyOnly;
syn String ChannelType.prettyPrint() {
eq ChannelType.prettyPrint() {
return new MemberPrinter("ChannelType")
.addRequired("id", getID())
.addNonDefault("label", getLabel())
......@@ -160,7 +159,7 @@ aspect Printing {
syn String SimpleChannelCategory.prettyPrint() = getValue();
//Channel: id="" type="" links=["ITEM_ID", "ITEM_ID"];
syn String Channel.prettyPrint() {
eq Channel.prettyPrint() {
return new MemberPrinter("Channel")
.addRequired("id", getID())
.addRequired("type", getType(), ChannelType::getID)
......@@ -178,7 +177,7 @@ aspect Printing {
}
//Mqtt: incoming="" outgoing="" host="";
syn String MqttRoot.prettyPrint() {
eq MqttRoot.prettyPrint() {
return new MemberPrinter("Mqtt")
.addNonDefault("incoming", getIncomingPrefix())
.addNonDefault("outgoing", getOutgoingPrefix())
......@@ -187,7 +186,7 @@ aspect Printing {
}
//Influx: user="" password="" dbName="" host="" ;
syn String InfluxRoot.prettyPrint() {
eq InfluxRoot.prettyPrint() {
return new MemberPrinter("Influx")
.addNonDefault("user", getUser(), DEFAULT_USER)
.addNonDefault("password", getPassword(), DEFAULT_PASSWORD)
......@@ -197,7 +196,7 @@ aspect Printing {
}
// Activities: { index: "name" }
syn String MachineLearningRoot.prettyPrint() {
eq MachineLearningRoot.prettyPrint() {
return new MemberPrinter("ML")
.addNodes("activities", getNumActivity(), getActivityList(),
activity -> activity.getIdentifier() + ":\"" + activity.getLabel() + "\"",
......@@ -215,7 +214,7 @@ aspect Printing {
syn String PowerExpression.prettyPrint() = "(" + getLeftOperand().prettyPrint() + " ^ " + getRightOperand().prettyPrint() + ")";
syn String ParenthesizedLogicalExpression.prettyPrint() = "(" + getOperand().prettyPrint() + ")";
syn String NotExpression.prettyPrint() = "!" + getOperand().prettyPrint();
syn String ComparingExpression.prettyPrint() {
eq ComparingExpression.prettyPrint() {
switch (getComparator()) {
case NotEquals: return "(" + getLeftOperand().prettyPrint() + " != " + getRightOperand().prettyPrint() + ")";
case Equals: return "(" + getLeftOperand().prettyPrint() + " == " + getRightOperand().prettyPrint() + ")";
......@@ -230,4 +229,16 @@ aspect Printing {
syn String OrExpression.prettyPrint() = "(" + getLeftOperand().prettyPrint() + " | " + getRightOperand().prettyPrint() + ")";
syn String Designator.prettyPrint() = getItem().getID();
// 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)
.build();
}
}
aspect Resolving {
//--- resolveThingType ---
syn java.util.Optional<ThingType> SmartHomeEntityModel.resolveThingType(String thingTypeId) {
for (ThingType thingType : this.getThingTypeList()) {
if (thingType.getID().equals(thingTypeId)) {
return java.util.Optional.of(thingType);
}
}
return java.util.Optional.empty();
}
//--- resolveChannel ---
syn java.util.Optional<Channel> SmartHomeEntityModel.resolveChannel(String channelId) {
for (Thing thing : this.getThingList()) {
for (Channel channel : thing.getChannelList()) {
if (channel.getID().equals(channelId)) {
return java.util.Optional.of(channel);
}
}
}
return java.util.Optional.empty();
}
//--- resolveChannelType ---
syn java.util.Optional<ChannelType> SmartHomeEntityModel.resolveChannelType(String channelTypeId) {
for (ChannelType channelType : this.getChannelTypeList()) {
if (channelType.getID().equals(channelTypeId)) {
return java.util.Optional.of(channelType);
}
}
return java.util.Optional.empty();
}
//--- resolveItem ---
syn java.util.Optional<Item> SmartHomeEntityModel.resolveItem(String itemId) {
if ("activity".equals(itemId)) {
return Optional.of(getActivityItem());
}
for (Item item : items()) {
if (item.getID().equals(itemId)) {
return java.util.Optional.of(item);
}
}
return java.util.Optional.empty();
}
//--- resolveGroup ---
syn java.util.Optional<Group> SmartHomeEntityModel.resolveGroup(String groupId) {
for (Group group : this.getGroupList()) {
if (group.getID().equals(groupId)) {
return java.util.Optional.of(group);
}
}
return java.util.Optional.empty();
}
//--- resolveMqttTopic ---
syn java.util.Optional<MqttTopic> Root.resolveMqttTopic(String mqttTopicId) {
return this.getMqttRoot().resolveTopic(mqttTopicId);
}
//--- resolveItemCategory ---
syn java.util.Optional<ItemCategory> SmartHomeEntityModel.resolveItemCategory(String categoryName) {
for (ItemCategory category : getItemCategoryList()) {
if (category.getName().equals(categoryName)) {
return java.util.Optional.of(category);
}
}
return java.util.Optional.empty();
}
//--- resolveActivity ---
syn java.util.Optional<Activity> Root.resolveActivity(int identifier) {
for (Activity activity : getMachineLearningRoot().getActivityList()) {
if (activity.getIdentifier() == identifier) {
return java.util.Optional.of(activity);
}
}
return java.util.Optional.empty();
}
syn java.util.Optional<Activity> Root.resolveActivity(String label) {
for (Activity activity : getMachineLearningRoot().getActivityList()) {
if (activity.getLabel().equals(label)) {
return java.util.Optional.of(activity);
}
}
return java.util.Optional.empty();
}
//--- resolveChangeEvent ---
syn java.util.Optional<ChangeEvent> Root.resolveChangeEvent(int identifier) {
for (ChangeEvent changeEvent : getMachineLearningRoot().getChangeEventList()) {
if (changeEvent.getIdentifier() == identifier) {
return java.util.Optional.of(changeEvent);
}
}
return java.util.Optional.empty();
}
// implementing resolving for relations
refine RefResolverStubs eq StateSyncGroup.resolveTargetItemByToken(String id, int position) {
return getRoot().getSmartHomeEntityModel().resolveItem(id).orElseThrow(() -> new RuntimeException("Item '" + id + "' not found!"));
}
}
......@@ -24,10 +24,8 @@ aspect Rules {
eq Item.getItemObserver().observedItem() = this;
public void Rule.activateFor(Item item) {
// 1) Get or create new ItemObserver, and add it to Root
ItemObserver itemObserver;
if (item.hasItemObserver()) {
itemObserver = item.getItemObserver();
// 1) Get ItemObserver
ItemObserver itemObserver = item.getItemObserver();
// 1.a) Check if observer already triggers this rule
for (Rule rule : itemObserver.getTriggeredRules()) {
if (rule.equals(this)) {
......@@ -35,18 +33,11 @@ aspect Rules {
return;
}
}
} else {
itemObserver = new ItemObserver();
item.setItemObserver(itemObserver);
}
// 2) Link event and itemObserver
itemObserver.addTriggeredRule(this);
}
public void Rule.deactivateFor(Item item) {
if (item.hasItemObserver()) {
item.getItemObserver().removeTriggeredRule(this);
}
}
......@@ -73,14 +64,6 @@ aspect Rules {
return executor.scheduleAtFixedRate(() -> trigger(null), initialDelay, period, unit);
}
public void Rule.removeActivationOf(Item item) {
if (item.hasItemObserver()) {
item.getItemObserver().removeTriggeredRule(this);
} else {
// there is no observer yet
logger.warn("Item {} was never activated before.", item);
}
}
// --- Condition.holdsFor ---
syn boolean Condition.holdsFor(Item item);
......@@ -109,7 +92,11 @@ aspect Rules {
getAffectedItem().setStateFromString(getNewStateProvider().get());
}
public void SetStateFromTriggeringItemAction.applyFor(Item item) {
item.copyStateTo(getAffectedItem());
Item target = getAffectedItem();
if (target==item) {
return;
}
item.copyStateTo(target);
}
public void SetStateFromItemsAction.applyFor(Item item) {
getAffectedItem().setStateFromString(getCombinator().apply(getSourceItems()));
......@@ -122,3 +109,18 @@ aspect Rules {
}
}
aspect StateSyncGroup {
rewrite StateSyncGroup {
to Rule {
Rule rule = new Rule();
for (Item item : getTargetItemList()) {
rule.addAction(new SetStateFromTriggeringItemAction(item));
rule.addObserver(item.getItemObserver());
}
return rule;
}
}
}
......@@ -28,3 +28,6 @@ MultiplyDoubleToStateAction : SetStateAction ::= <Multiplier:double> ;
ItemObserver ::= ;
rel ItemObserver.TriggeredRule* <-> Rule.Observer* ;
StateSyncGroup : Rule ;
rel StateSyncGroup.TargetItem* -> Item;
......@@ -57,6 +57,7 @@ Comment = "//" [^\n\r]+
"Influx" { return sym(Terminals.INFLUX); }
"ML" { return sym(Terminals.ML); }
"Rule" { return sym(Terminals.RULE); }
"SyncState" { return sym(Terminals.SYNCSTATE); }
// special items (group already has a token definition)
"Activity" { return sym(Terminals.ACTIVITY); }
"Color" { return sym(Terminals.COLOR); }
......
......@@ -3,6 +3,7 @@ package de.tudresden.inf.st.eraser.jastadd.parser;
import de.tudresden.inf.st.eraser.jastadd.model.*;
import de.tudresden.inf.st.eraser.jastadd.model.Action;
import de.tudresden.inf.st.eraser.parser.EraserParserHelper;
import de.tudresden.inf.st.eraser.jastadd.model.StateSyncGroup;
import java.util.Map;
import java.util.HashMap;
:} ;
......@@ -31,6 +32,7 @@ Root goal =
thing.t goal.r {: insertZero(r.getSmartHomeEntityModel().getThingList(), t); return r; :}
| item.i goal.r {: return r; :}
| group.g goal.r {: insertZero(r.getSmartHomeEntityModel().getGroupList(), g); return r; :}
| state_sync_group.ss goal.r {: r.addRule(ss); return r; :}
| thing_type.tt goal.r {: insertZero(r.getSmartHomeEntityModel().getThingTypeList(), tt); return r; :}
| parameter goal.r {: return r; :}
| channel_type.ct goal.r {: insertZero(r.getSmartHomeEntityModel().getChannelTypeList(), ct); return r; :}
......@@ -42,6 +44,7 @@ Root goal =
| thing.t {: return eph.createRoot(t); :}
| item.i {: return eph.createRoot(); :}
| group.g {: return eph.createRoot(g); :}
| state_sync_group.ss {: return eph.createRoot(ss); :}
| thing_type.tt {: return eph.createRoot(tt); :}
| parameter {: return eph.createRoot(); :}
| channel_type.ct {: return eph.createRoot(ct); :}
......@@ -137,6 +140,10 @@ Item item_body =
| {: return eph.createItem(); :}
;
Item item_ref =
TEXT.n {: return Item.createRef(n); :}
;
Group group =
GROUP COLON group_body.gb SEMICOLON {: return gb; :}
;
......@@ -154,6 +161,21 @@ Group group_body =
| {: return new Group(); :}
;
StateSyncGroup state_sync_group = SYNCSTATE COLON syncstate_body.ssb SEMICOLON {: return ssb; :};
// SyncState: items=["ITEM_ID", "ITEM_ID"];
StateSyncGroup syncstate_body =
ITEMS EQUALS item_list.items syncstate_body.ss
{:
for (Item item : items) {
ss.addTargetItem(item);
}
return ss;
:}
| {: return new StateSyncGroup(); :}
;
ThingType thing_type =
THING_TYPE COLON thing_type_body.ttb SEMICOLON {: return ttb; :}
;
......@@ -284,6 +306,21 @@ StringList string_list_body =
:}
;
ItemList item_list =
LB_SQUARE item_list_body.ilb RB_SQUARE {: return ilb; :}
| LB_SQUARE RB_SQUARE {: return new ItemList(); :}
;
ItemList item_list_body =
item_ref.ir COMMA item_list_body.ilb {: ilb.add(ir); return ilb; :}
| item_ref.ir
{:
ItemList result = new ItemList();
result.add(ir);
return result;
:}
;
StringList round_string_list =
LB_ROUND round_string_list_body.slb RB_ROUND {: return slb; :}
| LB_ROUND RB_ROUND {: return new StringList(); :}
......
......@@ -25,7 +25,9 @@ rel Channel.LinkedItem* <-> Item.Channel? ;
Parameter : DescribableModelElement ::= <Type:ParameterValueType> [DefaultValue:ParameterDefaultValue] <Context:String> <Required:boolean> ;
ParameterDefaultValue ::= <Value:String> ;
abstract Item : LabelledModelElement ::= <_fetched_data:boolean> MetaData:ItemMetaData* [ItemObserver] ;
abstract Item : LabelledModelElement ::= <_fetched_data:boolean> MetaData:ItemMetaData* /ItemObserver/;
rel Item.Category? -> ItemCategory ;
abstract ItemWithBooleanState : Item ::= <_state:boolean> ;
......
......@@ -73,6 +73,7 @@ public class EraserParserHelper {
// when parsing expressions
this.root = EraserParserHelper.initialRoot != null ? EraserParserHelper.initialRoot : createRoot();
}
if (checkUnusedElements) {
fillUnused();
}
......@@ -93,6 +94,7 @@ public class EraserParserHelper {
resolveList(itemMap, missingItemLinkListMap, Channel::addLinkedItem);
resolveList(groupMap, missingSubGroupListMap, Group::addGroup);
resolveList(itemMap, missingItemListMap, this::addItemToGroup);
resolveList(channelTypeMap, missingChannelTypeListMap, ThingType::addChannelType);
resolveList(parameterMap, missingParameterListMap, ThingType::addParameter);
resolveList(itemMap, missingControllingListMap, Item::addControlling);
......@@ -103,6 +105,8 @@ public class EraserParserHelper {
if (checkUnusedElements) {
checkUnusedElements();
}
this.root.treeResolveAll();
}
private void addItemToGroup(Group group, Item item) {
......@@ -292,11 +296,6 @@ public class EraserParserHelper {
return item;
}
public Item setControlling(Item item, StringList controlling) {
missingControllingListMap.put(item, controlling);
return item;
}
public Item retype(Item itemWithCorrectType, Item prototype) {
itemWithCorrectType.setID(prototype.getID());
itemWithCorrectType.setLabel(prototype.getLabel());
......
......@@ -80,9 +80,9 @@ public class MemberPrinter {
* @param listOfNodes the list of nodes
* @param mapping a function to map a node to a ModelElement
*/
private <T extends ASTNode> void concatIds(Iterable<T> listOfNodes,
Function<T, ? extends ModelElement> mapping) {
concatNodes(listOfNodes, t -> mapping.apply(t).getID(), true);
private <T extends ASTNode<?>> void concatIds(Iterable<T> listOfNodes,
Function<T, String> mapping) {
concatNodes(listOfNodes, mapping, true);
}
/**
......@@ -91,7 +91,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 extends ASTNode<?>> void concatNodes(Iterable<T> listOfNodes,
Function<T, String> mapping,
boolean quote) {
boolean first = true;
......@@ -146,9 +146,9 @@ public class MemberPrinter {
* @param mapping A function to map a node to a ModelElement
* @return this
*/
public <T extends ASTNode> MemberPrinter addIds(
public <T extends ASTNode<?>> MemberPrinter addIds(
String name, int count, Iterable<T> listOfNodes,
Function<T, ? extends ModelElement> mapping) {
Function<T, String> mapping) {
if (count > 0) {
sb.append(' ').append(name).append("=[");
concatIds(listOfNodes, mapping);
......@@ -168,7 +168,7 @@ public class MemberPrinter {
* @param <T> The type of all nodes
* @return this
*/
public <T extends ASTNode> MemberPrinter addNodes(String name, int count, Iterable<T> listOfNodes,
public <T extends ASTNode<?>> MemberPrinter addNodes(String name, int count, Iterable<T> listOfNodes,
Function<T, String> mapping) {
return addNodes(name, count, listOfNodes, mapping, ListBracketType.SQUARE);
}
......@@ -183,7 +183,7 @@ public class MemberPrinter {
* @param bracketType The type of brackets to enclose the list with
* @return this
*/
public <T extends ASTNode> MemberPrinter addNodes(String name, int count, Iterable<T> listOfNodes,
public <T extends ASTNode<?>> MemberPrinter addNodes(String name, int count, Iterable<T> listOfNodes,
Function<T, String> mapping, ListBracketType bracketType) {
if (count > 0) {
sb.append(' ').append(name).append("=").append(bracketType.begin);
......@@ -214,7 +214,7 @@ public class MemberPrinter {
* @param child The child to append
* @return this
*/
public MemberPrinter addOptionalPrettyPrint(ASTNode child) {
public MemberPrinter addOptionalPrettyPrint(ASTNode<?> child) {
if (child != null) {
this.empty = false;
sb.append(child.prettyPrint());
......
......@@ -2,6 +2,8 @@ package de.tudresden.inf.st.eraser.util;
import de.tudresden.inf.st.eraser.jastadd.model.*;
import java.util.ArrayList;
/**
* Helper class to create models used in tests.
*
......@@ -35,6 +37,13 @@ public class TestUtils {
return ModelAndItem.of(root.getSmartHomeEntityModel(), item);
}
public static SmartHomeEntityModel createModelWithGroup() {
Root root = Root.createEmptyRoot();
ParserUtils.createUnknownGroup(root.getSmartHomeEntityModel(),new ArrayList<>());
return root.getSmartHomeEntityModel();
}
public static NumberItem addItemTo(SmartHomeEntityModel model, double initialValue) {
return addItemTo(model, initialValue, false);
}
......
......@@ -2,12 +2,17 @@ package de.tudresden.inf.st.eraser;
import beaver.Parser;
import de.tudresden.inf.st.eraser.jastadd.model.*;
import de.tudresden.inf.st.eraser.jastadd.model.Action;
import de.tudresden.inf.st.eraser.util.ParserUtils;
import de.tudresden.inf.st.eraser.util.TestUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Disabled;
import org.testcontainers.shaded.com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
......@@ -15,9 +20,9 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.StreamSupport;
import static de.tudresden.inf.st.eraser.util.TestUtils.getDefaultGroup;
import static org.assertj.core.api.Assertions.fail;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.*;
/**
* Testing the simple rule engine.
......@@ -205,7 +210,7 @@ public class RulesTest {
setState(item, 5);
assertEquals(1, counter.get(item), "Change of item state should trigger the rule");
rule.removeActivationOf(item);
rule.deactivateFor(item);
setState(item, 3);
assertEquals(1, counter.get(item), "Change of item state should not change the counter anymore");
......@@ -245,6 +250,254 @@ public class RulesTest {
assertEquals(2, counter.get(item), "Change of item to 7 should not trigger the rule, check2 violated");
}
@Test
public void testStateSyncGroupRewriteStructure() {
// init StateSyncGroup
StateSyncGroup group = new StateSyncGroup();
ArrayList<Item> items = new ArrayList<>();
// create model and an item
TestUtils.ModelAndItem mai = TestUtils.createModelAndItem(0, true);
NumberItem item0 = mai.item;
group.addTargetItem(item0);
items.add(item0);
// init more items
for (int i=1;i<=4;i++) {
NumberItem item = TestUtils.addItemTo(mai.model, i, true);
item.setID("item"+i);
group.addTargetItem(item);
items.add(item);
}
// add StateSyncGroup as rule to root
mai.model.getRoot().addRule(group);
// trace rewritten rule and its actions
Rule rewrittenRule = mai.model.getRoot().getRules().getChild(0);
ImmutableList<Action> actions = ImmutableList.copyOf(rewrittenRule.getActionList().iterator());
ImmutableList<ItemObserver> observers = ImmutableList.copyOf(rewrittenRule.getObservers().iterator());
// check general structure
assertEquals(ImmutableList.copyOf(rewrittenRule.getConditionList().iterator()).size(), 0);
assertEquals(actions.size(), 5);
// check actions and observers
for (int i=0;i<items.size();i++) {
assertTrue(actions.get(i) instanceof SetStateFromTriggeringItemAction);
assertTrue(observers.contains(items.get(i).getItemObserver()));
}
}
private static void addItemToModel(SmartHomeEntityModel model, Item item) {
getDefaultGroup(model).addItem(item);
}
@Test
public void testColorItemStateSyncGroup() {
StateSyncGroup group = new StateSyncGroup();
//init model and 3 items
SmartHomeEntityModel model = TestUtils.createModelWithGroup();
ColorItem colorItem1 = new ColorItem();
addItemToModel(model,colorItem1);
group.addTargetItem(colorItem1);
ColorItem colorItem2 = new ColorItem();
addItemToModel(model,colorItem2);
group.addTargetItem(colorItem2);
ColorItem colorItem3 = new ColorItem();
addItemToModel(model,colorItem3);
group.addTargetItem(colorItem3);
// add StateSyncGroup as rule to root and trigger rewrite
model.getRoot().addRule(group);
model.getRoot().doFullTraversal();
colorItem1.setState(TupleHSB.parse("0,0,100"));
assertEquals(colorItem1.getState(),TupleHSB.parse("0,0,100"));
assertEquals(colorItem2.getState(),TupleHSB.parse("0,0,100"));
assertEquals(colorItem3.getState(),TupleHSB.parse("0,0,100"));
colorItem3.setState(TupleHSB.parse("0,0,7"));
assertEquals(colorItem1.getState(),TupleHSB.parse("0,0,7"));
assertEquals(colorItem2.getState(),TupleHSB.parse("0,0,7"));
assertEquals(colorItem3.getState(),TupleHSB.parse("0,0,7"));
}
@Test
public void testDateTimeItemStateSyncGroup() {
StateSyncGroup group = new StateSyncGroup();
//init model and 3 items
SmartHomeEntityModel model = TestUtils.createModelWithGroup();
DateTimeItem dateTimeItem1 = new DateTimeItem();
addItemToModel(model,dateTimeItem1);
group.addTargetItem(dateTimeItem1);
DateTimeItem dateTimeItem2 = new DateTimeItem();
addItemToModel(model,dateTimeItem2);
group.addTargetItem(dateTimeItem2);
DateTimeItem dateTimeItem3 = new DateTimeItem();
addItemToModel(model,dateTimeItem3);
group.addTargetItem(dateTimeItem3);
// add StateSyncGroup as rule to root and trigger rewrite
model.getRoot().addRule(group);
model.getRoot().doFullTraversal();
Instant i1 = Instant.now();
dateTimeItem1.setState(i1);
assertEquals(dateTimeItem1.getState(),i1);
assertEquals(dateTimeItem2.getState(),i1);
assertEquals(dateTimeItem3.getState(),i1);
Instant i2 = Instant.now();
dateTimeItem3.setState(i2);
assertEquals(dateTimeItem1.getState(),i2);
assertEquals(dateTimeItem2.getState(),i2);
assertEquals(dateTimeItem3.getState(),i2);
}
/**
* Also for DimmerItem, RollerShutterItem, ActivityItem
*/
@Test
public void testDoubleStateItemStateSyncGroup() {
StateSyncGroup group = new StateSyncGroup();
//init model and 3 items
SmartHomeEntityModel model = TestUtils.createModelWithGroup();
NumberItem numberItem1 = new NumberItem();
addItemToModel(model,numberItem1);
group.addTargetItem(numberItem1);
NumberItem numberItem2 = new NumberItem();
addItemToModel(model,numberItem2);
group.addTargetItem(numberItem2);
NumberItem numberItem3 = new NumberItem();
addItemToModel(model,numberItem3);
group.addTargetItem(numberItem3);
// add StateSyncGroup as rule to root and trigger rewrite
model.getRoot().addRule(group);
model.getRoot().doFullTraversal();
numberItem1.setState(123);
assertEquals(numberItem1.getState(),123);
assertEquals(numberItem2.getState(),123);
assertEquals(numberItem3.getState(),123);
numberItem2.setState(42);
assertEquals(numberItem1.getState(),42);
assertEquals(numberItem2.getState(),42);
assertEquals(numberItem3.getState(),42);
}
/**
* Also for ImageItem, LocationItem, PlayerItem, DefaultItem
*/
@Test
public void testStringStateItemStateSyncGroup() {
StateSyncGroup group = new StateSyncGroup();
//init model and 3 items
SmartHomeEntityModel model = TestUtils.createModelWithGroup();
StringItem stringItem1 = new StringItem();
addItemToModel(model,stringItem1);
group.addTargetItem(stringItem1);
StringItem stringItem2 = new StringItem();
addItemToModel(model,stringItem2);
group.addTargetItem(stringItem2);
StringItem stringItem3 = new StringItem();
addItemToModel(model,stringItem3);
group.addTargetItem(stringItem3);
// add StateSyncGroup as rule to root and trigger rewrite
model.getRoot().addRule(group);
model.getRoot().doFullTraversal();
stringItem1.setState("123");
assertEquals(stringItem1.getState(),"123");
assertEquals(stringItem2.getState(),"123");
assertEquals(stringItem3.getState(),"123");
stringItem2.setState("Hermes");
assertEquals(stringItem1.getState(),"Hermes");
assertEquals(stringItem2.getState(),"Hermes");
assertEquals(stringItem3.getState(),"Hermes");
}
/**
* Also for ContactItem
*/
@Test
public void testBooleanStateItemStateSyncGroup() {
StateSyncGroup group = new StateSyncGroup();
//init model and 3 items
SmartHomeEntityModel model = TestUtils.createModelWithGroup();
SwitchItem switchItem1 = new SwitchItem();
addItemToModel(model,switchItem1);
group.addTargetItem(switchItem1);
SwitchItem switchItem2 = new SwitchItem();
addItemToModel(model,switchItem2);
group.addTargetItem(switchItem2);
SwitchItem switchItem3 = new SwitchItem();
addItemToModel(model,switchItem3);
group.addTargetItem(switchItem3);
// add StateSyncGroup as rule to root and trigger rewrite
model.getRoot().addRule(group);
model.getRoot().doFullTraversal();
switchItem3.setState(false);
assertFalse(switchItem1.getState());
assertFalse(switchItem2.getState());
assertFalse(switchItem3.getState());
switchItem1.setState(true);
assertTrue(switchItem1.getState());
assertTrue(switchItem2.getState());
assertTrue(switchItem3.getState());
}
@Test
public void testTwoActions() {
TestUtils.ModelAndItem modelAndItem = createModelAndItem(2);
......@@ -673,7 +926,7 @@ public class RulesTest {
private StringItem addStringItem(SmartHomeEntityModel model, String initialValue) {
StringItem item = new StringItem();
Group group = TestUtils.getDefaultGroup(model);
Group group = getDefaultGroup(model);
item.setID("item" + group.getNumItem());
item.setState(initialValue, false);
group.addItem(item);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment