--- /dev/null
+/*******************************************************************************\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
--- /dev/null
+/*******************************************************************************\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