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