diff --git a/AlgebraicDataflowArchitectureModel/models/SimpleUI.model b/AlgebraicDataflowArchitectureModel/models/SimpleUI.model index 3e03bbd..49341ac 100644 --- a/AlgebraicDataflowArchitectureModel/models/SimpleUI.model +++ b/AlgebraicDataflowArchitectureModel/models/SimpleUI.model @@ -54,9 +54,25 @@ } channel ScreenTransition { - in curScreen(curScId: Str, transScreen(nextScId, nextSc)) = nextScId - in screenTemplates.{nextScId}(curSc, transScreen(nextScId, nextSc)) = nextSc - out screen(curS, transScreen(nextScId, nextSc)) = nextSc + in curScreen(curScId: Str, transScreen(nextScId, screen)) = nextScId + ref screenTemplates.{nextScId}(screen, transScreen(nextScId, screen)) + out screen(curS, transScreen(nextScId, screen)) = screen +} + +channel EventDispatch(wid: Str) { + in screen.widgets.{wid}.state(curState: Int, dispatchEvent(curScId, wid, nextState)) = nextState + ref curScreen(curScId: Str, dispatchEvent(curScId, wid, nextState)) + out screenTemplates.{curScId}.widgets.{wid}.state(curState: Int, dispatchEvent(curScId, wid, nextState)) = nextState +} + +channel EventHandler1(scId: Str, wid: Str) { + in screenTemplates.{scId="000"}.widgets.{wid="002"}.state(curState: Int, handleEvent1(nextState)) = nextState + out curScreen(curScId: Str, handleEvent1(nextState)) = if(nextState == 0, "001", curScId) +} + +channel EventHandler2(scId: Str, wid: Str) { + in screenTemplates.{scId="001"}.widgets.{wid="004"}.state(curState: Int, handleEvent2(nextState)) = nextState + out curScreen(curScId: Str, handleEvent2(nextState)) = if(nextState == 0, "000", curScId) } channel ChangeLayout { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourceHierarchy.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourceHierarchy.java index be99827..b8a6fe3 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourceHierarchy.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourceHierarchy.java @@ -2,6 +2,7 @@ import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import models.algebra.Expression; @@ -220,14 +221,17 @@ } } - public String toString(List pathParams) { + public String toString(List> pathParams) { if (parent == null) return resourceName; if (resourceName != null) { return parent.toString(pathParams) + "." + resourceName; } else { - Expression lastParam = pathParams.get(pathParams.size() - 1); + Map.Entry lastParam = pathParams.get(pathParams.size() - 1); pathParams = pathParams.subList(0, pathParams.size() - 1); - return parent.toString(pathParams) + ".{" + lastParam +"}"; + if (lastParam.getValue() == null) { + return parent.toString(pathParams) + ".{" + lastParam.getKey() +"}"; + } + return parent.toString(pathParams) + ".{" + lastParam.getKey() + "=" + lastParam.getValue() +"}"; } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java index 6cadc2d..293d148 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java @@ -3,7 +3,10 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; import models.algebra.Expression; import models.algebra.Type; @@ -13,7 +16,7 @@ public class ResourcePath extends Symbol { protected ResourcePath parent = null; protected ResourceHierarchy resourceHierarchy = null; - protected List pathParams = null; + protected List> pathParams = null; public ResourcePath(String fullResourceName) { super(fullResourceName); @@ -33,37 +36,53 @@ super(parent.toString() + "." + leafResourceName); this.parent = parent; this.resourceHierarchy = new ResourceHierarchy(parent.getResourceHierarchy(), leafResourceName); - this.pathParams = parent.getPathParams(); + this.pathParams = parent.getPathParamsAndConstraints(); } public ResourcePath(ResourcePath parent, String leafResourceName, ResourceHierarchy resourceHierarchy) { super(parent.toString() + "." + leafResourceName); this.parent = parent; this.resourceHierarchy = resourceHierarchy; - this.pathParams = parent.getPathParams(); + this.pathParams = parent.getPathParamsAndConstraints(); } public ResourcePath(ResourcePath parent, Expression exp) { super(parent.toString() + ".{" + exp + "}"); this.parent = parent; this.resourceHierarchy = new ResourceHierarchy(parent.getResourceHierarchy(), exp); - this.pathParams = new ArrayList<>(parent.getPathParams()); - this.pathParams.add(exp); + this.pathParams = new ArrayList<>(parent.getPathParamsAndConstraints()); + this.pathParams.add(new AbstractMap.SimpleEntry<>(exp, null)); + } + + public ResourcePath(ResourcePath parent, Expression exp, Expression constraint) { + super(parent.toString() + ".{" + exp + "=" + constraint + "}"); + this.parent = parent; + this.resourceHierarchy = new ResourceHierarchy(parent.getResourceHierarchy(), exp); + this.pathParams = new ArrayList<>(parent.getPathParamsAndConstraints()); + this.pathParams.add(new AbstractMap.SimpleEntry<>(exp, constraint)); } public ResourcePath(ResourcePath parent, Expression exp, ResourceHierarchy resourceHierarchy) { super(parent.toString() + ".{" + exp + "}"); this.parent = parent; this.resourceHierarchy = resourceHierarchy; - this.pathParams = new ArrayList<>(parent.getPathParams()); - this.pathParams.add(exp); + this.pathParams = new ArrayList<>(parent.getPathParamsAndConstraints()); + this.pathParams.add(new AbstractMap.SimpleEntry<>(exp, null)); + } + + public ResourcePath(ResourcePath parent, Expression exp, Expression constraint, ResourceHierarchy resourceHierarchy) { + super(parent.toString() + ".{" + exp + "=" + constraint + "}"); + this.parent = parent; + this.resourceHierarchy = resourceHierarchy; + this.pathParams = new ArrayList<>(parent.getPathParamsAndConstraints()); + this.pathParams.add(new AbstractMap.SimpleEntry<>(exp, constraint)); } public ResourcePath(ResourcePath another) { super(another.name); this.parent = another.parent; this.resourceHierarchy = another.resourceHierarchy; - this.pathParams = new ArrayList<>(another.getPathParams()); + this.pathParams = new ArrayList<>(another.getPathParamsAndConstraints()); } public ResourceHierarchy getResourceHierarchy() { @@ -91,15 +110,27 @@ } public List getPathParams() { + List params = new ArrayList<>(); + for (Map.Entry paramEnt: this.pathParams) { + params.add(paramEnt.getKey()); + } + return params; + } + + public List> getPathParamsAndConstraints() { return pathParams; } - public void setPathParams(List pathParams) { + public void setPathParams(List> pathParams) { this.pathParams = pathParams; } public void addPathParam(Expression pathParam) { - pathParams.add(pathParam); + pathParams.add(new AbstractMap.SimpleEntry<>(pathParam, null)); + } + + public void addPathParamWithConstraint(Expression pathParam, Expression pathConstraint) { + pathParams.add(new AbstractMap.SimpleEntry<>(pathParam, pathConstraint)); } public boolean endsWithParam() { @@ -109,6 +140,13 @@ public Expression getLastParam() { if (endsWithParam()) { + return pathParams.get(pathParams.size() - 1).getKey(); + } + return null; + } + + public Map.Entry getLastParamAndConstraint() { + if (endsWithParam()) { return pathParams.get(pathParams.size() - 1); } return null; diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java index ec3c6f2..e93c937 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java @@ -542,12 +542,30 @@ } } - List dstParams = filledResourcePath.getPathParams(); + List> dstParams = filledResourcePath.getPathParamsAndConstraints(); for (int i = 0; i < filledResourcePath.getPathParams().size(); i++) { - Expression pathParam = dstParams.get(i); + Expression pathParam = dstParams.get(i).getKey(); + Expression pathValue = dstParams.get(i).getValue(); if (pathParam instanceof Variable) { - dstParams.set(i, bindings.get((Variable) pathParam).getValue()); // Replace a path parameter with a value in the unified message. - dependingVarPosInMessage.add(bindings.get((Variable) pathParam).getKey()); // The position of the replaced variable in the message. + if (pathValue == null) { + dstParams.set(i, new AbstractMap.SimpleEntry<>(bindings.get((Variable) pathParam).getValue(), null)); // Replace a path parameter with a value in the unified message. + dependingVarPosInMessage.add(bindings.get((Variable) pathParam).getKey()); // The position of the replaced variable in the message. + } else { + // If the path parameter has a constraint. + if (pathValue instanceof Term) { + Map pathValueVars = ((Term) pathValue).getVariables(); + for (Variable var: bindings.keySet()) { + if (pathValueVars.values().contains(var)) { // var is a subterm of a path parameter + pathValue = ((Term) pathValue).substitute(var, bindings.get(var).getValue()); // Substitute a value in the unified message to var. + dependingVarPosInMessage.add(bindings.get((Variable) var).getKey()); // The position of the replaced variable in the message. + } + } + if (!(pathValue instanceof Constant)) { + pathValue = ((Term) pathValue).reduce(); + } + dstParams.set(i, new AbstractMap.SimpleEntry<>(pathValue, null)); // Replace a path parameter with the substituted term. + } + } } else if (pathParam instanceof Term) { Map pathParamVars = ((Term) pathParam).getVariables(); for (Variable var: bindings.keySet()) { @@ -559,7 +577,7 @@ if (!(pathParam instanceof Constant)) { pathParam = ((Term) pathParam).reduce(); } - dstParams.set(i, pathParam); // Replace a path parameter with the substituted term. + dstParams.set(i, new AbstractMap.SimpleEntry<>(pathParam, null)); // Replace a path parameter with the substituted term. } } return filledResourcePath; @@ -568,7 +586,8 @@ private Set getPossitionsInMessageThatChannelMemberDependsOn(ResourcePath resourcePath, Expression messageTerm) { Set dependingVarPosInMessage = new HashSet<>(); Map messageVars = messageTerm.getVariables(); - for (Expression pathParam: resourcePath.getPathParams()) { + for (Map.Entry pathParamEnt: resourcePath.getPathParamsAndConstraints()) { + Expression pathParam = pathParamEnt.getKey(); if (pathParam instanceof Variable) { for (Entry messageVarEnt: messageVars.entrySet()) { Variable var = messageVarEnt.getValue(); diff --git a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java index abec1de..2350843 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java @@ -704,8 +704,20 @@ // Path parameter Expression paramTerm = parseTerm(stream, model); String rightCurlyBracket = stream.next(); - if (rightCurlyBracket == null || !rightCurlyBracket.equals(RIGHT_CURLY_BRACKET)) throw new ExpectedRightCurlyBracket(stream.getLine()); - path = new ResourcePath(path, paramTerm); + if (rightCurlyBracket == null) throw new ExpectedRightCurlyBracket(stream.getLine()); + Expression paramConstraint = null; + if (rightCurlyBracket.equals(EQUALS)) { + // Path constraint + paramConstraint = parseTerm(stream, model); + rightCurlyBracket = stream.next(); + if (rightCurlyBracket == null) throw new ExpectedRightCurlyBracket(stream.getLine()); + } + if (!rightCurlyBracket.equals(RIGHT_CURLY_BRACKET)) throw new ExpectedRightCurlyBracket(stream.getLine()); + if (paramConstraint == null) { + path = new ResourcePath(path, paramTerm); + } else { + path = new ResourcePath(path, paramTerm, paramConstraint); + } } else { // Path literal if (path == null) { diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/Event.java b/AlgebraicDataflowArchitectureModel/src/simulator/Event.java index b0feb2b..f263fe9 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/Event.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Event.java @@ -273,9 +273,14 @@ Set dependingVarPosInMessage = new HashSet<>(); ResourcePath filledResPath = channel.fillOutsideResourcePath(dependingMem.getResource(), unifiedMessage, dependingMem.getStateTransition().getMessageExpression(), dependingVarPosInMessage); ResourcePath unfilledResPath = dependingMem.getResource(); - for (int i = 0; i < unfilledResPath.getPathParams().size(); i++) { - Expression var = unfilledResPath.getPathParams().get(i); - Expression val = filledResPath.getPathParams().get(i); + for (int i = 0; i < unfilledResPath.getPathParamsAndConstraints().size(); i++) { + Expression var = unfilledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression val = filledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression constraint = unfilledResPath.getPathParamsAndConstraints().get(i).getValue(); + if (constraint != null && !constraint.equals(val)) { + // The value of the path parameter does not satisfy the constraint defined for the parameter. + return null; // Not to fire this event. + } boolean isSelector = false; for (Selector sel: channel.getAllSelectors()) { if (sel.getExpression().equals(var)) { @@ -284,7 +289,7 @@ } } if (!isSelector) { - // A depending channel parameter + // Update a depending channel parameter updateDependingParameter(var, val); } } diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/ResourceIdentifier.java b/AlgebraicDataflowArchitectureModel/src/simulator/ResourceIdentifier.java index d0463cf..9de3594 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/ResourceIdentifier.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/ResourceIdentifier.java @@ -2,6 +2,8 @@ import java.util.HashMap; import java.util.Map; +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; import models.algebra.Constant; import models.algebra.Expression; @@ -25,7 +27,7 @@ public void setPathParam(int paramIdx, Constant param) { if (paramIdx < pathParams.size()) { - pathParams.set(paramIdx, param); + pathParams.set(paramIdx, new AbstractMap.SimpleEntry<>(param, null)); if (parent != null) { ((ResourceIdentifier) parent).setPathParam(paramIdx, param); } @@ -82,7 +84,7 @@ ResourcePath resId = this; while (resId != null && resPath != null) { if (resId.getResourceHierarchy().getNumParameters() == 0) { - if (!resPath.getResourceHierarchy().getResourceName().equals(resId.getResourceHierarchy().getResourceName())) { + if (resPath.getResourceHierarchy().getNumParameters() != 0 || !resPath.getResourceHierarchy().getResourceName().equals(resId.getResourceHierarchy().getResourceName())) { return false; } } else if (resId.getResourceHierarchy().getNumParameters() != resPath.getResourceHierarchy().getNumParameters()) { diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java b/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java index 57f5a35..7528793 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java @@ -199,6 +199,7 @@ for (Event nextEvent: getNextEvents(updatedOutResId, curSystemState, nextSystemState)) { fireEvent(nextEvent, curSystemState, nextSystemState); } + if (channel.isNative()) break; // To avoid multiple updates of the same resource. updatedOutResId = (ResourceIdentifier) updatedOutResId.getParent(); } } @@ -239,17 +240,21 @@ for (ChannelMember dependedMem: invDependency.keySet()) { if (inResPath == dependedMem.getResource()) { // If some depending resources are to be updated by the update of an depended input resource. - Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId)); - nextEvent.updateDependingParameters(resourceStateValueProvider); - if (nextChState == null) { - nextChState = new ChannelState(channel); - nextSystemState.updateChannelState(channel, nextChState); + if (doesSatifsyPathConstraints(inResPath, inResId)) { + Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId)); + Expression message = nextEvent.updateDependingParameters(resourceStateValueProvider); + if (message != null) { + if (nextChState == null) { + nextChState = new ChannelState(channel); + nextSystemState.updateChannelState(channel, nextChState); + } + List channelSelValues = nextEvent.getChannelSelectorValues(); + for (Map.Entry paramEnt: nextEvent.getDependingParameters().entrySet()) { + nextChState.addDependingParamAndValue(channelSelValues, paramEnt.getKey(), paramEnt.getValue()); + } + nextEvents.add(nextEvent); + } } - List channelSelValues = nextEvent.getChannelSelectorValues(); - for (Map.Entry paramEnt: nextEvent.getDependingParameters().entrySet()) { - nextChState.addDependingParamAndValue(channelSelValues, paramEnt.getKey(), paramEnt.getValue()); - } - nextEvents.add(nextEvent); isInputResourceDepended = true; } } @@ -260,9 +265,14 @@ } } if (!isInputResourceDepended && !isInputResourceDepending) { - Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId)); - nextEvent.setMessage(nextEvent.updateDependingParameters(resourceStateValueProvider)); - nextEvents.add(nextEvent); + if (doesSatifsyPathConstraints(inResPath, inResId)) { + Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId)); + Expression message = nextEvent.updateDependingParameters(resourceStateValueProvider); + if (message != null) { + nextEvent.setMessage(message); + nextEvents.add(nextEvent); + } + } } if (nextChState != null) { for (ChannelMember dependingMem: dependency.keySet()) { @@ -270,11 +280,19 @@ // If a depending resource is directly updated. ResourcePath filledResPath = inResId; ResourcePath unfilledResPath = inResPath; + // Extract the values of path parameters from the updated resource identifier. Map selectorVarToVal = new HashMap<>(); Map dependingVarToVal = new HashMap<>(); - for (int i = 0; i < unfilledResPath.getPathParams().size(); i++) { - Expression var = unfilledResPath.getPathParams().get(i); - Expression val = filledResPath.getPathParams().get(i); + boolean doesSatisfyConstraint = true; + for (int i = 0; i < unfilledResPath.getPathParamsAndConstraints().size(); i++) { + Expression var = unfilledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression val = filledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression constraint = unfilledResPath.getPathParamsAndConstraints().get(i).getValue(); + if (constraint != null && !constraint.equals(val)) { + // The value of the path parameter does not satisfy the constraint defined for the parameter. + doesSatisfyConstraint = false; + break; + } boolean isSelector = false; for (Selector sel: channel.getAllSelectors()) { if (sel.getExpression().equals(var)) { @@ -288,23 +306,28 @@ dependingVarToVal.put(var, val); } } - for (List channelSelectorValues: nextChState.getDependedChannelSelectorValues(dependingVarToVal)) { - // Guess every tuple of channel selector values that may affects the updated resource. - boolean doesMatch = true; - for (Expression var: selectorVarToVal.keySet()) { - for (int i = 0; i < channel.getAllSelectors().size(); i++) { - if (channel.getAllSelectors().get(i).getExpression().equals(var)) { - if (!channelSelectorValues.get(i).equals(selectorVarToVal.get(var))) { - // If the value of a selector in the updated resource path does not matches a guessed channel selector value. - doesMatch = false; + if (doesSatisfyConstraint) { + List> dependedChannelSelectorValues = nextChState.getDependedChannelSelectorValues(dependingVarToVal); + if (dependedChannelSelectorValues != null) { + for (List channelSelectorValues: dependedChannelSelectorValues) { + // Guess every tuple of channel selector values that may affects the updated resource. + boolean doesMatch = true; + for (Expression var: selectorVarToVal.keySet()) { + for (int i = 0; i < channel.getAllSelectors().size(); i++) { + if (channel.getAllSelectors().get(i).getExpression().equals(var)) { + if (!channelSelectorValues.get(i).equals(selectorVarToVal.get(var))) { + // If the value of a selector in the updated resource path does not matches a guessed channel selector value. + doesMatch = false; + } + } } } + if (doesMatch) { + Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId), channelSelectorValues, dependingVarToVal); + nextEvents.add(nextEvent); + } } } - if (doesMatch) { - Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId), channelSelectorValues, dependingVarToVal); - nextEvents.add(nextEvent); - } } } } @@ -315,6 +338,18 @@ return nextEvents; } + private boolean doesSatifsyPathConstraints(ResourcePath resPath, ResourceIdentifier resId) { + for (int i = 0; i < resPath.getPathParamsAndConstraints().size(); i++) { + Expression val = resId.getPathParamsAndConstraints().get(i).getKey(); + Expression constraint = resPath.getPathParamsAndConstraints().get(i).getValue(); + if (constraint != null && !constraint.equals(val)) { + // The value of the path parameter does not satisfy the constraint defined for the parameter. + return false; // Not to fire this event. + } + } + return true; + } + private static class ResourceStateValueProvider implements IResourceStateValueProvider { SystemState curSystemState; SystemState nextSystemState;