From: miettinen Date: Tue, 21 Jan 2014 11:43:46 +0000 (+0000) Subject: Automatic balancing / reinforcing comment (B/R) for Sysdyn loops (refs #3012). X-Git-Tag: 1.8.1~154 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=1b932fd2cb2c984393f8039469ca846866a9c6a5;p=simantics%2Fsysdyn.git Automatic balancing / reinforcing comment (B/R) for Sysdyn loops (refs #3012). git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@28681 ac1ea38d-2e2b-0410-8846-a27921b304fc --- diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/LoopFactory.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/LoopFactory.java index e8f11057..c5c8727a 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/LoopFactory.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements/LoopFactory.java @@ -66,6 +66,7 @@ import org.simantics.scenegraph.g2d.nodes.SVGNode; import org.simantics.scenegraph.g2d.nodes.ShapeNode; import org.simantics.sysdyn.SysdynResource; import org.simantics.sysdyn.ui.properties.LoopTab; +import org.simantics.sysdyn.utils.LoopUtils; import org.simantics.ui.SimanticsUI; import org.simantics.utils.datastructures.Callback; import org.simantics.utils.datastructures.hints.IHintContext.Key; @@ -135,8 +136,20 @@ public class LoopFactory extends SysdynElementFactory { if (component != null) { text = (String) graph.getPossibleRelatedValue(component, sr.Loop_Comment); } - if (text == null || LoopTab.AUTO.equals(text)) + if (text == null) text = ""; + else if (LoopTab.AUTO.equals(text)) { + switch (LoopUtils.getLoopType(graph, component)) { + case BALANCING: + text = "B"; + break; + case REINFORCING: + text = "R"; + break; + default: + text = ""; + } + } return text; } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/LoopTab.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/LoopTab.java index 0b770e37..b8fb2f06 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/LoopTab.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/LoopTab.java @@ -43,7 +43,7 @@ import org.simantics.db.exception.DatabaseException; import org.simantics.db.management.ISessionContext; import org.simantics.modeling.ModelingResources; import org.simantics.sysdyn.SysdynResource; -import org.simantics.sysdyn.utils.ConfigurationUtils; +import org.simantics.sysdyn.utils.LoopUtils; import org.simantics.ui.SimanticsUI; import org.simantics.utils.datastructures.Pair; import org.simantics.utils.datastructures.Triple; @@ -79,8 +79,8 @@ public class LoopTab extends AdjustableTab { auto = new Button(commentGroup, support, SWT.RADIO); auto.setText("Auto"); - auto.setSelectionFactory(new CommentRadioSelectionFactory("$$AUTO$$")); - auto.addSelectionListener(new CommentSelectionListener(context, "$$AUTO$$")); + auto.setSelectionFactory(new CommentRadioSelectionFactory(AUTO)); + auto.addSelectionListener(new CommentSelectionListener(context, AUTO)); balancing = new Button(commentGroup, support, SWT.RADIO); balancing.setText("B"); @@ -95,6 +95,7 @@ public class LoopTab extends AdjustableTab { other = new Button(commentGroup, support, SWT.RADIO); other.setText("other"); other.setSelectionFactory(new OtherCommentSelectionFactory(new String[] {null, "B", "R", AUTO})); + other.addSelectionListener(new CommentSelectionListener(context, "")); loopComment = new TrackedText(commentGroup, support, SWT.BORDER); loopComment.setTextFactory(new OtherCommentStringPropertyFactory()); @@ -125,10 +126,10 @@ public class LoopTab extends AdjustableTab { LoopTab.this.resource = input; Map map = new HashMap(); - List> loops = ConfigurationUtils.getAllLoopsInDiagram(graph, input); + List> loops = LoopUtils.getAllLoopsInDiagram(graph, input); map.put("", null); for (List loop : loops) { - map.put(ConfigurationUtils.cycleToString(graph, loop), loop); + map.put(LoopUtils.cycleToString(graph, loop), loop); } return map; } @@ -152,7 +153,7 @@ public class LoopTab extends AdjustableTab { } // See if the defined loop still exists. - List> loops = ConfigurationUtils.getAllLoopsInDiagram(graph, input); + List> loops = LoopUtils.getAllLoopsInDiagram(graph, input); Resource first = itemList.get(0); for (List loop : loops) { // If the loops are of different size, continue. @@ -181,7 +182,7 @@ public class LoopTab extends AdjustableTab { if (!match) continue; - return ConfigurationUtils.cycleToString(graph, loop); + return LoopUtils.cycleToString(graph, loop); } // No match found, hence empty diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/structure/LoopGraphRequest.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/structure/LoopGraphRequest.java index f9b7c874..6063be5c 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/structure/LoopGraphRequest.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/structure/LoopGraphRequest.java @@ -23,7 +23,7 @@ import org.simantics.graphviz.IGraph; import org.simantics.graphviz.Node; import org.simantics.modeling.ModelingResources; import org.simantics.sysdyn.SysdynResource; -import org.simantics.sysdyn.utils.ConfigurationUtils; +import org.simantics.sysdyn.utils.LoopUtils; import org.simantics.utils.datastructures.MapList; /** @@ -62,7 +62,7 @@ public class LoopGraphRequest extends DependencyGraphRequest { SysdynResource sr = SysdynResource.getInstance(g); if (g.isInstanceOf(root, sr.IndependentVariable) || g.isInstanceOf(root, sr.Input)) { // Get ALL loops in the diagram in which the root exists. - List> loops = ConfigurationUtils.getLoops(g, root); + List> loops = LoopUtils.getLoops(g, root); setRoot(g, graph, root); // Add the edges to the graph. diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/utils/ConfigurationUtils.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/utils/LoopUtils.java similarity index 58% rename from org.simantics.sysdyn/src/org/simantics/sysdyn/utils/ConfigurationUtils.java rename to org.simantics.sysdyn/src/org/simantics/sysdyn/utils/LoopUtils.java index 9f5895c4..130ebb08 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/utils/ConfigurationUtils.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/utils/LoopUtils.java @@ -21,10 +21,12 @@ import java.util.List; import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; +import org.simantics.db.common.utils.ListUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.ServiceException; import org.simantics.db.request.Read; import org.simantics.layer0.Layer0; +import org.simantics.modeling.ModelingResources; import org.simantics.sysdyn.SysdynResource; import org.simantics.sysdyn.elementaryCycles.ElementaryCyclesSearch; import org.simantics.ui.SimanticsUI; @@ -34,7 +36,7 @@ import org.simantics.ui.SimanticsUI; * @author Tuomas Miettinen * */ -public class ConfigurationUtils { +public class LoopUtils { private static class ElementaryLoopItem { public int mapping; @@ -46,6 +48,12 @@ public class ConfigurationUtils { } } + public enum LoopType { + REINFORCING, + BALANCING, + UNDEFINED + }; + /** * Get all the loops within the diagram where the resource belongs to. * @param r Resource that is studied @@ -127,22 +135,30 @@ public class ConfigurationUtils { HashMap elementaryLoopItems = new HashMap(); for (Resource variable : variables) { ArrayList dependingVariables = new ArrayList(); - // Add forward dependencies and flows. + // Add forward dependencies and flows from valves. Collection dependencies = g.getObjects(variable, sr.Variable_isTailOf); for (Resource dependency : dependencies) { Resource head = g.getPossibleObject(dependency, sr.Variable_HasHead); - if (head != null - && !g.isInstanceOf(head, sr.Module) - && !g.isInstanceOf(head, sr.Cloud)) { + // Skip dependencies and flows to modules and clouds. + if (head == null + || g.isInstanceOf(head, sr.Module) + || g.isInstanceOf(head, sr.Cloud)) + continue; + + if ((g.isInstanceOf(dependency, sr.Flow) && g.isInstanceOf(variable, sr.Valve)) + || g.isInstanceOf(dependency, sr.Dependency)) { + // Add all dependencies + // Add (only) such flows that start from a valve. dependingVariables.add(head); } } - // Add backward flows. + // Add backward flows from stocks. Collection backwardFlows = g.getObjects(variable, sr.Variable_isHeadOf); for (Resource flow : backwardFlows) { if (g.isInstanceOf(flow, sr.Flow)) { Resource tail = g.getPossibleObject(flow, sr.Variable_HasTail); - if (tail != null && !g.isInstanceOf(tail, sr.Cloud)) { + if (tail != null && g.isInstanceOf(tail, sr.Stock)) { + // Add (only) such flows that start from a stock. dependingVariables.add(tail); } } @@ -218,4 +234,90 @@ public class ConfigurationUtils { } return sb.toString(); } + + /** + * Get the type of the loop, i.e. whether the loop is reinforcing or balancing. + * The type is determined based on the dependency arrows and flows on the diagram; + * the loop is balancing if there is odd number of dependencies with negative + * polarity (Polarity = "-"). Note: + * 1) each flow of which tail is a valve is considered a dependency with positive + * (+) polarity. + * 2) each flow of which tail is a stock is considered a dependency with negative + * (-) polarity which GOES TO THE OPPOSITE DIRECTION. + * 3) the polarity of a supplementary dependency arrow overrides a flow. + * 4) the loops are defined as a list of Resources. If the set of dependencies + * between is ambiguous, the implementation may choose any possible dependency. + * 5) empty and null polarity are considered positive (+) polarities. + * @param graph + * @param resource The loop component + * @return the type of the loop. If the type cannot be determined, + * LoopType.UNDEFINED is returned + * @throws DatabaseException + */ + public static LoopType getLoopType(ReadGraph graph, Resource resource) throws DatabaseException { + SysdynResource sr = SysdynResource.getInstance(graph); + ModelingResources mod = ModelingResources.getInstance(graph); + Resource loopResource = graph.getPossibleObject(resource, sr.Loop_Items); + + if (loopResource == null) + return LoopType.UNDEFINED; + + boolean oddNumberOfNegativeCausalities = false; + List loop = ListUtils.toPossibleList(graph, loopResource); + for (int i = 0; i < loop.size(); ++i) { + boolean skipBackwardFlows = false; + + // Go through forward dependencies and flows + Collection forwardDependencies = graph.getObjects(loop.get(i), sr.Variable_isTailOf); + for (Resource dependency : forwardDependencies) { + Resource dependingVariable = graph.getSingleObject(dependency, sr.Variable_HasHead); + if (dependingVariable.equals(loop.get((i + 1) % loop.size()))) { + if (graph.isInstanceOf(dependency, sr.Flow)) { + /* + * Forward flows never affect the loop type. Allow dependency arrows + * override flows; thus don't touch skipBackwardFlows and continue. + * continue also because we may have a flow from stock, which + * is the wrong dependency and we need to keep searching. + */ + continue; + } + skipBackwardFlows = true; + + Resource dependencyConnection = graph.getSingleObject(dependency, mod.ConnectionToDiagramConnection); + String polarity = (String)graph.getPossibleRelatedValue(dependencyConnection, sr.DependencyConnection_polarity, Bindings.STRING); + if ("-".equals(polarity)) { + oddNumberOfNegativeCausalities = !oddNumberOfNegativeCausalities; + } else if (polarity != null + && !"".equals(polarity) + && !"+".equals(polarity)) { + // There's something other than + in one of the dependencies + return LoopType.UNDEFINED; + } + // "+" doesn't affect loop type, consider null and "" as a "+". + break; + } + } + + if (skipBackwardFlows) + continue; + + // Backward flows from stocks. + Collection backwardFlows = graph.getObjects(loop.get(i), sr.Variable_isHeadOf); + for (Resource flow : backwardFlows) { + if (graph.isInstanceOf(flow, sr.Flow)) { + Resource dependingVariable = graph.getSingleObject(flow, sr.Variable_HasTail); + if (dependingVariable.equals(loop.get((i + 1) % loop.size()))) { + if (graph.isInstanceOf(dependingVariable, sr.Stock)) { + // Basically, we should always end up here since all other + // possibilities have already been gone through. + oddNumberOfNegativeCausalities = !oddNumberOfNegativeCausalities; + break; + } + continue; + } + } + } + } + return oddNumberOfNegativeCausalities ? LoopType.BALANCING : LoopType.REINFORCING; + } }