diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java index 022644b..2bec47e 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java @@ -85,6 +85,15 @@ public void addSelector(Selector selector) { selectors.add(selector); } + + public List getAllSelectorVariables() { + List allSelectorVariables = new ArrayList<>(); + if (parent != null) allSelectorVariables.addAll(parent.getAllSelectorVariables()); + for (Selector sel: selectors) { + allSelectorVariables.add(sel.getExpression()); + } + return allSelectorVariables; + } public Set getChannelMembers() { return channelMembers; diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java b/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java index 654eabb..33f01ff 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java @@ -67,7 +67,7 @@ public Object clone() { ChannelState newChannelState = new ChannelState(channel); - newChannelState.referenceStructure = (ReferenceStructure) referenceStructure.clone(); + if (referenceStructure != null) newChannelState.referenceStructure = (ReferenceStructure) referenceStructure.clone(); return newChannelState; } diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/Event.java b/AlgebraicDataflowArchitectureModel/src/simulator/Event.java index b6382f6..c86cb47 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/Event.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Event.java @@ -201,12 +201,16 @@ return dependingParameters; } + public void addChild(Event child) { + childEvents.add(child); + } + public Set getChildren() { return childEvents; - } + } /** - * Update the values of the depending channel selectors + * Construct channel message, collect descendant events of this event and update the values of the depending channel selectors * * @param resourceStateValueProvider a resourceStateValueProvider to provide current and next resource states * @return calculated message constraint by this event @@ -247,7 +251,7 @@ } }; - // Calculate message constraints from leaf channel members on the channel member dependency graph. + // 1. Calculate message constraints from leaf channel members on the 'channel member dependency graph'. Map> dependency = channel.getMemberDependency(); if (dependency.size() == 0) { // No channel member dependency. @@ -307,7 +311,7 @@ resolved.addAll(toResolve); toResolve.clear(); - // Calculate message constraints from remaining members on the channel member dependency graph. + // 2. Calculate message constraints from remaining members on the channel member dependency graph. for (;;) { for (Map.Entry> dependEnt: dependency.entrySet()) { ChannelMember dependingMem = dependEnt.getKey(); @@ -331,14 +335,7 @@ // 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)) { - isSelector = true; - break; - } - } - if (!isSelector) { + if (!channel.getAllSelectorVariables().contains(var)) { // Update a depending channel parameter updateDependingParameter(var, val); } @@ -366,35 +363,29 @@ toResolve.clear(); } } + if (inputResource == null) return unifiedMessage; - // Trigger child events and calculate message constraints from child channels. + // 3. Propagate parent event message to collect child events and calculate message constraints from child channels. + Resource baseResource = inputResource; + while (baseResource.getResourceHierarchy().getNumParameters() == 0) { + if (baseResource.getParent() == null) break; + baseResource = baseResource.getParent(); + } + Term parentEventMessage = (Term) unifiedMessage.clone(); for (Channel childChannel: channel.getChildren()) { // Search the deepest input side resource path in each child channel that matches the channel parameters. ChannelMember in = null; for (ChannelMember cm: ((DataTransferChannel) childChannel).getInputChannelMembers()) { if (!cm.isOutside()) { ResourcePath resPath = cm.getResource(); - boolean bContainsAll = true; - for (Selector sel: childChannel.getAllSelectors()) { - if (!resPath.getPathParams().contains(sel.getExpression())) { - bContainsAll = false; - break; - } - } - if (bContainsAll) { + if (resPath.getPathParams().containsAll(childChannel.getAllSelectorVariables())) { in = cm; break; } } } if (in != null) { - // Trigger events for all resources under this event's input resource that matches the deepest input side resource path. - Resource baseResource = inputResource; - while (baseResource.getResourceHierarchy().getNumParameters() == 0) { - if (baseResource.getParent() == null) break; - baseResource = baseResource.getParent(); - } - Term parentEventMessage = (Term) unifiedMessage.clone(); + // Collect events for all resources under this event's input resource that matches the deepest input side resource path. for (Resource res: baseResource.getDescendants(in.getResource().getResourceHierarchy())) { Event childEvent = new Event((DataTransferChannel) childChannel, in.getResource(), res); childEvent.setMessage(parentEventMessage); @@ -412,6 +403,26 @@ childEvents.add(childEvent); } } + } else { + // Search the deepest output side resource path in each child channel that matches the channel parameters. + ChannelMember out = null; + for (ChannelMember cm: ((DataTransferChannel) childChannel).getOutputChannelMembers()) { + if (!cm.isOutside()) { + ResourcePath resPath = cm.getResource(); + if (resPath.getPathParams().containsAll(childChannel.getAllSelectorVariables())) { + out = cm; + break; + } + } + } + if (out != null) { + // Collect events for all resources under this event's input resource that matches the deepest output side resource path. + for (Resource res: baseResource.getDescendants(out.getResource().getResourceHierarchy())) { + Event childEvent = new Event((DataTransferChannel) childChannel, parentEventMessage, out.getResource(), res); + childEvent.constructMessageAndDesdendantEvents(resourceStateValueProvider, substitutedPositionsInMessageFromChannels, true); + childEvents.add(childEvent); + } + } } } diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java b/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java index bd2ca59..7209331 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java @@ -1,6 +1,7 @@ package simulator; import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -263,13 +264,12 @@ } } - private Set getNextEvents(ResourceIdentifier inResId, SystemState curSystemState, SystemState nextSystemState) - throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { - Set nextEvents = new HashSet<>(); + private Set getNextEvents(ResourceIdentifier inResId, SystemState curSystemState, SystemState nextSystemState) { IResourceStateValueProvider resourceStateValueProvider = new ResourceStateValueProvider(curSystemState, nextSystemState); + Set nextEvents = new HashSet<>(); for (Map.Entry chEntry: nextSystemState.getChannelStates().entrySet()) { DataTransferChannel channel = chEntry.getKey(); - ChannelState nextChState = chEntry.getValue(); + ChannelState nextChannelState = nextSystemState.getChannelState(channel); Map> dependency = channel.getMemberDependency(); Map> invDependency = new HashMap<>(); for (ChannelMember dependingMem: dependency.keySet()) { @@ -288,19 +288,19 @@ boolean isInputResourceDepended = false; 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. + // 1) If some depending resources are to be updated by the update of an 'depended' input resource. if (doesSatifsyPathConstraints(inResPath, inResId)) { Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId)); Expression message = nextEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, true); if (message != null) { nextEvent.setMessage(message); - if (nextChState == null) { - nextChState = new ChannelState(channel); - nextSystemState.updateChannelState(channel, nextChState); + if (nextChannelState == null) { + nextChannelState = new ChannelState(channel); + nextSystemState.updateChannelState(channel, nextChannelState); } List channelSelValues = nextEvent.getChannelSelectorValues(); for (Map.Entry paramEnt: nextEvent.getDependingParameters().entrySet()) { - nextChState.addDependingParamAndValue(channelSelValues, paramEnt.getKey(), paramEnt.getValue()); + nextChannelState.addDependingParamAndValue(channelSelValues, paramEnt.getKey(), paramEnt.getValue()); } nextEvents.add(nextEvent); } @@ -324,10 +324,10 @@ } } } - if (nextChState != null) { + if (nextChannelState != null) { for (ChannelMember dependingMem: dependency.keySet()) { if (inResPath == dependingMem.getResource()) { - // If a depending resource is directly updated. + // 2) If a 'depending' resource is directly updated. ResourcePath filledResPath = inResId; ResourcePath unfilledResPath = inResPath; // Extract the values of path parameters from the updated resource identifier. @@ -343,21 +343,14 @@ doesSatisfyConstraint = false; break; } - boolean isSelector = false; - for (Selector sel: channel.getAllSelectors()) { - if (sel.getExpression().equals(var)) { - isSelector = true; - break; - } - } - if (isSelector) { + if (channel.getAllSelectorVariables().contains(var)) { selectorVarToVal.put(var, val); } else { dependingVarToVal.put(var, val); } } if (doesSatisfyConstraint) { - List> dependedChannelSelectorValues = nextChState.getDependedChannelSelectorValues(dependingVarToVal); + List> dependedChannelSelectorValues = nextChannelState.getDependedChannelSelectorValues(dependingVarToVal); if (dependedChannelSelectorValues != null) { for (List channelSelectorValues: dependedChannelSelectorValues) { // Guess every tuple of channel selector values that may affects the updated resource. @@ -388,10 +381,164 @@ } } } + if (nextChannelState != null) { + // 3) If a resource in a descendant channel is directly updated. + ResourcePath inResPath = null; + for (ResourcePath path: channel.getInputResources()) { + if (path.getPathParams().containsAll(channel.getAllSelectorVariables())) { + inResPath = path; + } + } + for (Channel childCh: channel.getChildren()) { + DataTransferChannel childChannel = (DataTransferChannel) childCh; + if (inResPath != null) { + for (Map.Entry, Event> childEventEnt: collectDescendantEvents(channel, childChannel, inResId, nextSystemState, resourceStateValueProvider)) { + Event childEvent = childEventEnt.getValue(); + List channelSelectorValues = childEventEnt.getKey(); + Map dependingVarToVal = nextChannelState.getDependingParamAndValues(channelSelectorValues); + Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId), channelSelectorValues, dependingVarToVal); + nextEvent.addChild(childEvent); + nextEvents.add(nextEvent); + } + } + } + } } return nextEvents; } + private List, Event>> collectDescendantEvents(DataTransferChannel rootChannel, DataTransferChannel channel, ResourceIdentifier inResId, + SystemState nextSystemState, IResourceStateValueProvider resourceStateValueProvider) { + List, Event>> childEvents = new ArrayList<>(); + ChannelState nextChannelState = nextSystemState.getChannelState(rootChannel); + for (Channel childCh: channel.getChildren()) { + DataTransferChannel childChannel = (DataTransferChannel) childCh; + Map> dependency = childChannel.getMemberDependency(); + Map> invDependency = new HashMap<>(); + for (ChannelMember dependingMem: dependency.keySet()) { + for (ChannelMember dependedMem: dependency.get(dependingMem)) { + Set dependings = invDependency.get(dependedMem); + if (dependings == null) { + dependings = new HashSet<>(); + invDependency.put(dependedMem, dependings); + } + dependings.add(dependingMem); + } + } + for (ResourcePath childInResPath: childChannel.getInputResources()) { + if (inResId.isInstanceOf(childInResPath)) { + for (ChannelMember dependedMem: invDependency.keySet()) { + if (childInResPath == dependedMem.getResource()) { + // 1) If some depending resources are to be updated by the update of an 'depended' input resource. + if (doesSatifsyPathConstraints(childInResPath, inResId)) { + Event childEvent = new Event(childChannel, childInResPath, nextSystemState.getResource(inResId)); + Expression message = childEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, true); + if (message != null) { + childEvent.setMessage(message); + if (nextChannelState == null) { + nextChannelState = new ChannelState(channel); + nextSystemState.updateChannelState(channel, nextChannelState); + } + List childChannelSelValues = childEvent.getChannelSelectorValues(); + for (Map.Entry paramEnt: childEvent.getDependingParameters().entrySet()) { + nextChannelState.addDependingParamAndValue(childChannelSelValues, paramEnt.getKey(), paramEnt.getValue()); + } + List channelSelectorValues = new ArrayList<>(childChannelSelValues); + for (int i = 0; i < childChannel.getSelectors().size(); i++) { + channelSelectorValues.remove(childChannelSelValues.size() - 1 - i); + } + childEvents.add(new AbstractMap.SimpleEntry<>(channelSelectorValues, childEvent)); + } + } + } + } + if (nextChannelState != null) { + for (ChannelMember dependingMem: dependency.keySet()) { + if (childInResPath == dependingMem.getResource()) { + // 2) If a 'depending' resource is directly updated. + ResourcePath filledResPath = inResId; + ResourcePath unfilledResPath = childInResPath; + // Extract the values of path parameters from the updated resource identifier. + Map selectorVarToVal = new HashMap<>(); + Map dependingVarToVal = new HashMap<>(); + 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; + } + if (channel.getAllSelectorVariables().contains(var)) { + selectorVarToVal.put(var, val); + } else { + dependingVarToVal.put(var, val); + } + } + if (doesSatisfyConstraint) { + List> dependedChannelSelectorValues = nextChannelState.getDependedChannelSelectorValues(dependingVarToVal); + if (dependedChannelSelectorValues != null) { + for (List childChannelSelectorValues: 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 (!childChannelSelectorValues.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 childEvent = new Event(childChannel, childInResPath, nextSystemState.getResource(inResId), childChannelSelectorValues, dependingVarToVal); + Expression message = childEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, false); + if (message != null) { + childEvent.setMessage(message); + List channelSelectorValues = new ArrayList<>(childChannelSelectorValues); + for (int i = 0; i < childChannel.getSelectors().size(); i++) { + channelSelectorValues.remove(childChannelSelectorValues.size() - 1 - i); + } + childEvents.add(new AbstractMap.SimpleEntry<>(channelSelectorValues, childEvent)); + } + } + } + } + } + } + } + } + + } + } + // 3) If a resource in a descendant channel is directly updated. + childEvents.addAll(collectDescendantEvents(rootChannel, childChannel, inResId, nextSystemState, resourceStateValueProvider)); + } + List, Event>> events = new ArrayList<>(); + ResourcePath inResPath = null; + for (ResourcePath path: channel.getInputResources()) { + if (path.getPathParams().containsAll(channel.getAllSelectorVariables())) { + inResPath = path; + } + } + for (Map.Entry, Event> childEventEnt: childEvents) { + List channelSelectorValues = childEventEnt.getKey(); + Event childEvent = childEventEnt.getValue(); + Map dependingVarToVal = nextChannelState.getDependingParamAndValues(channelSelectorValues); + Event event = new Event((DataTransferChannel) channel, inResPath, nextSystemState.getResource(inResId), channelSelectorValues, dependingVarToVal); + event.addChild(childEvent); + List parentChannelSelectorValues = new ArrayList<>(channelSelectorValues); + for (int i = 0; i < channel.getSelectors().size(); i++) { + parentChannelSelectorValues.remove(channelSelectorValues.size() - 1 - i); + } + events.add(new AbstractMap.SimpleEntry<>(parentChannelSelectorValues, event)); + } + return events; + } + private boolean doesSatifsyPathConstraints(ResourcePath resPath, ResourceIdentifier resId) { for (int i = 0; i < resPath.getPathParamsAndConstraints().size(); i++) { Expression val = resId.getPathParamsAndConstraints().get(i).getKey();