]> gerrit.simantics Code Review - simantics/sysdyn.git/commitdiff
Loops under structure view in Sysdyn, initial implementation (refs #3012).
authormiettinen <miettinen@ac1ea38d-2e2b-0410-8846-a27921b304fc>
Fri, 17 Jan 2014 12:23:17 +0000 (12:23 +0000)
committermiettinen <miettinen@ac1ea38d-2e2b-0410-8846-a27921b304fc>
Fri, 17 Jan 2014 12:23:17 +0000 (12:23 +0000)
git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@28660 ac1ea38d-2e2b-0410-8846-a27921b304fc

org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/structure/LoopGraphRequest.java [new file with mode: 0644]
org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/structure/Loops.java [new file with mode: 0644]

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
new file mode 100644 (file)
index 0000000..4d15341
--- /dev/null
@@ -0,0 +1,122 @@
+/*******************************************************************************\r
+ * Copyright (c) 2014 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.sysdyn.ui.structure;\r
+\r
+import java.util.HashMap;\r
+import java.util.List;\r
+\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.graphviz.Edge;\r
+import org.simantics.graphviz.Graph;\r
+import org.simantics.graphviz.IGraph;\r
+import org.simantics.graphviz.Node;\r
+import org.simantics.modeling.ModelingResources;\r
+import org.simantics.sysdyn.SysdynResource;\r
+import org.simantics.sysdyn.utils.ConfigurationUtils;\r
+import org.simantics.utils.datastructures.MapList;\r
+\r
+/**\r
+ * Builds a graph of the loops of a selected variable\r
+ * \r
+ * @author Tuomas Miettinen\r
+ * @author Teemu Lempinen\r
+ *\r
+ */\r
+public class LoopGraphRequest extends DependencyGraphRequest {\r
+\r
+       MapList<Resource, Edge> edges;\r
+\r
+       /**\r
+        * Builds a graph of the dependencies of a selected variable\r
+        * \r
+        * @param root Variable from which to find dependencies\r
+        * @param levels How many steps of dependencies are calculated from the variable\r
+        * @param isInverted true => variables that affect root; false => variables that are affected by root \r
+        */\r
+       public LoopGraphRequest(Resource root, int levels, boolean isInverted) {\r
+               super(root, levels, isInverted);\r
+       }\r
+\r
+       @Override\r
+       public Graph perform(ReadGraph g) throws DatabaseException {\r
+\r
+               nodes = new HashMap<Resource, Node>();\r
+               Graph graph = new Graph();\r
+               graph.setRankdir("LR");\r
+               ModelingResources mr = ModelingResources.getInstance(g);\r
+               Resource element = g.getPossibleObject(root, mr.ElementToComponent);\r
+               if (element != null) {\r
+                       root = element;\r
+               }\r
+               SysdynResource sr = SysdynResource.getInstance(g);\r
+               if (g.isInstanceOf(root, sr.IndependentVariable) || g.isInstanceOf(root, sr.Input)) {\r
+                       List<List<Resource>> loops = ConfigurationUtils.getLoops(g, root);\r
+                       setRoot(g, graph, root);\r
+                       \r
+                       edges = new MapList<Resource, Edge>();\r
+                       for (List<Resource> loop : loops) {\r
+                               Resource prev = null;\r
+                               for (Resource r : loop) {\r
+                                       // Add node if not already added.\r
+                                       if(!nodes.containsKey(r)) {\r
+                                               Node n = new Node(graph, getName(g, r));\r
+                                               setShape(g, r, n);\r
+                                               nodes.put(r, n);\r
+                                       }\r
+                                       // Add edge if not already added.\r
+                                       if (prev != null)\r
+                                               claimEdge(g, graph, prev, r);\r
+                                       \r
+                                       prev = r;\r
+                               }\r
+                               // Add edge between the last and first node in the loop.\r
+                               if (prev != null && loop.get(0) != null)\r
+                                       claimEdge(g, graph, prev, loop.get(0));\r
+                       }\r
+               }\r
+               return graph;\r
+       }\r
+\r
+       /**\r
+        * Create and edge between _tail and _head nodes. If the edge already exists, do nothing.\r
+        * If an opposite direction edge exists, draw an arrow also to the tail of the edge.\r
+        * @param g \r
+        * @param graph Graph in which the nodes lie.\r
+        * @param tail The tail node.\r
+        * @param head The head node.\r
+        * @throws DatabaseException \r
+        */\r
+       private void claimEdge(ReadGraph g, IGraph graph, Resource tail, Resource head) throws DatabaseException {\r
+               // If edge exists, return\r
+               for (Edge e : edges.getValues(tail))\r
+                       if (((Node)e.getHead()).get("label").equals(getName(g, head))) {\r
+                               // Change the size of the arrowhead (fancily :-)\r
+                               String arrowsize = e.get("arrowsize");\r
+                               if (arrowsize == null)\r
+                                       arrowsize = "1.0";\r
+                               e.setArrowsize(Math.min(2.2, Math.sqrt(Double.valueOf(arrowsize) + 0.8)));\r
+                               return;\r
+                       }\r
+\r
+               for (Edge e : edges.getValues(head)) { // Seek for opposite edges\r
+                       if (((Node)e.getHead()).get("label").equals(getName(g, tail))) {\r
+                               e.setDir("both");\r
+                               return;\r
+                       }\r
+               }\r
+               \r
+               // If no edge has been found, add one. \r
+               edges.add(tail, new Edge(graph, nodes.get(tail), nodes.get(head)));\r
+       }\r
+}\r
diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/structure/Loops.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/structure/Loops.java
new file mode 100644 (file)
index 0000000..6afb8ed
--- /dev/null
@@ -0,0 +1,186 @@
+/*******************************************************************************\r
+ * Copyright (c) 2014 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.sysdyn.ui.structure;\r
+\r
+import java.util.Set;\r
+\r
+import org.eclipse.core.runtime.IProgressMonitor;\r
+import org.eclipse.core.runtime.IStatus;\r
+import org.eclipse.core.runtime.Status;\r
+import org.eclipse.core.runtime.jobs.Job;\r
+import org.eclipse.jface.layout.GridDataFactory;\r
+import org.eclipse.jface.layout.GridLayoutFactory;\r
+import org.eclipse.jface.viewers.ISelection;\r
+import org.eclipse.jface.viewers.IStructuredSelection;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.custom.CTabFolder;\r
+import org.eclipse.swt.custom.CTabItem;\r
+import org.eclipse.swt.events.ModifyEvent;\r
+import org.eclipse.swt.events.ModifyListener;\r
+import org.eclipse.swt.events.SelectionAdapter;\r
+import org.eclipse.swt.events.SelectionEvent;\r
+import org.eclipse.swt.layout.RowLayout;\r
+import org.eclipse.swt.widgets.Button;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Label;\r
+import org.eclipse.swt.widgets.Spinner;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.procedure.Listener;\r
+import org.simantics.graphviz.Graph;\r
+import org.simantics.graphviz.ui.GraphvizComponent;\r
+import org.simantics.ui.SimanticsUI;\r
+import org.simantics.utils.ui.ISelectionUtils;\r
+\r
+public class Loops extends CTabItem {\r
+    \r
+    private GraphvizComponent component;\r
+    private boolean isInverted = false;\r
+    private Button bButton, fButton;\r
+    private Resource currentSelection;\r
+    private int levels = 3;\r
+    private GraphListener graphListener;\r
+\r
+    static int MAXLEVELS = 4;\r
+    static int MINLEVELS = 1;\r
+\r
+    public Loops(CTabFolder parent, int style) {\r
+        super(parent, style);\r
+        \r
+        Composite loops = new Composite(parent, SWT.NONE);\r
+        GridDataFactory.fillDefaults().grab(true, true).applyTo(loops);\r
+        GridLayoutFactory.fillDefaults().spacing(0,0).applyTo(loops);\r
+\r
+        component = new GraphvizComponent(loops, SWT.NONE);\r
+        GridDataFactory.fillDefaults().grab(true, true).applyTo(component);\r
+        \r
+        Label line = new Label(loops, SWT.SEPARATOR | SWT.SHADOW_OUT | SWT.HORIZONTAL);\r
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(line);\r
+        \r
+        Composite composite = new Composite(loops, SWT.NONE);\r
+        RowLayout layout = new RowLayout();\r
+        layout.center = true;\r
+        composite.setLayout(layout);\r
+\r
+        Label label = new Label(composite, SWT.NONE);\r
+        label.setText("Direction: ");\r
+\r
+        bButton = new Button(composite, SWT.RADIO);\r
+        bButton.setText("Backward");\r
+        bButton.setSelection(true);\r
+        bButton.addSelectionListener(new SelectionAdapter() {\r
+            public void widgetSelected(SelectionEvent event) {\r
+                if(bButton.getSelection())\r
+                    isInverted = false;\r
+                else \r
+                    isInverted = true;\r
+                if(currentSelection != null) {\r
+                    readGraph(currentSelection);\r
+                }\r
+            }\r
+        });\r
+\r
+        fButton = new Button(composite, SWT.RADIO);\r
+        fButton.setText("Forward");\r
+\r
+        label = new Label(composite, SWT.NONE);\r
+        label.setText("Steps: ");\r
+\r
+        Spinner spinner = new Spinner(composite, SWT.BORDER);\r
+        spinner.setMaximum(MAXLEVELS);\r
+        spinner.setMinimum(MINLEVELS);\r
+        spinner.setTextLimit(1);\r
+        spinner.setSelection(levels);\r
+\r
+        spinner.addModifyListener(new ModifyListener() {\r
+\r
+            @Override\r
+            public void modifyText(ModifyEvent e) {\r
+                Spinner s = (Spinner)e.widget;\r
+                int lvls = Integer.parseInt(s.getText());\r
+                if(lvls > MAXLEVELS)\r
+                    levels = MAXLEVELS;\r
+                else if (lvls < MINLEVELS)\r
+                    levels = MINLEVELS;\r
+                levels = lvls;\r
+                if(currentSelection != null) {\r
+                    readGraph(currentSelection);\r
+                }\r
+\r
+            }\r
+        });\r
+        \r
+        this.setText("Loops");\r
+        this.setControl(loops);\r
+    }\r
+\r
+\r
+    public void drawSelection(ISelection selection) {\r
+        if(selection == null || selection.isEmpty())\r
+            return;\r
+        if(selection instanceof IStructuredSelection) {                    \r
+            Object[] els = ((IStructuredSelection) selection).toArray();\r
+            if(els.length == 1) {\r
+                Set<Resource> ress = ISelectionUtils.filterSetSelection(selection, Resource.class);\r
+                if(ress.isEmpty()) return;\r
+                Resource r = (ress.toArray(Resource.NONE))[0];\r
+                if(r != null && !r.equals(currentSelection)) {\r
+                    currentSelection = r;\r
+                    readGraph(currentSelection);\r
+                }\r
+            }\r
+        }           \r
+    }\r
+\r
+    private void readGraph(Resource resource) {\r
+        if(graphListener != null)\r
+            graphListener.dispose();\r
+        \r
+        graphListener = new GraphListener();\r
+        SimanticsUI.getSession().asyncRequest(new LoopGraphRequest(\r
+                resource, levels, isInverted), graphListener);\r
+    }\r
+\r
+    private class GraphListener implements Listener<Graph> {\r
+        \r
+        private boolean disposed = false;\r
+        \r
+        public void dispose() {\r
+            disposed = true;\r
+        }\r
+        \r
+        @Override\r
+        public void exception(Throwable e) {\r
+            e.printStackTrace();\r
+        }\r
+\r
+        @Override\r
+        public void execute(final Graph graph) {\r
+            Job job = new Job("Loading loops graph") {\r
+\r
+                @Override\r
+                protected IStatus run(IProgressMonitor monitor) {\r
+                    if(!isDisposed()) {\r
+                        component.setGraph(graph, "dot");\r
+                        component.fit();                            \r
+                    }\r
+                    return Status.OK_STATUS;\r
+                }\r
+            };\r
+            job.schedule();\r
+        }\r
+\r
+        @Override\r
+        public boolean isDisposed() {\r
+            return disposed;\r
+        }\r
+    }\r
+}\r