From 251ccf2edb572e9bc12c596a6565c1e5377793c3 Mon Sep 17 00:00:00 2001 From: luukkainen Date: Wed, 16 Jan 2013 10:53:01 +0000 Subject: [PATCH] chart component moved to jfreechart plug-in. refs #3988 git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@26620 ac1ea38d-2e2b-0410-8846-a27921b304fc --- org.simantics.sysdyn.feature/feature.xml | 44 +- org.simantics.sysdyn.ui/META-INF/MANIFEST.MF | 3 +- org.simantics.sysdyn.ui/adapters.xml | 2 +- org.simantics.sysdyn.ui/plugin.xml | 10 +- .../browser/actions/drop/ChartDropAction.java | 2 +- .../sysdyn/ui/browser/nodes/BarChartNode.java | 2 +- .../ui/browser/nodes/LineChartNode.java | 2 +- .../sysdyn/ui/browser/nodes/PieChartNode.java | 2 +- .../sysdyn/ui/editor/DiagramViewer.java | 2 +- .../ChartPanelOrientationHandler.java | 2 +- .../ResourceSelectionProcessor.java | 16 +- .../sysdyn/ui/trend/AllVariablesOfModel.java | 100 ++++ .../sysdyn/ui/trend/CategoryDataset.java | 314 +++++++++++ .../simantics/sysdyn/ui/trend/PieDataset.java | 332 ++++++++++++ .../ui/trend/SysdynRangeHandlerFactory.java | 141 +++++ .../simantics/sysdyn/ui/trend/TrendView.java | 2 +- .../simantics/sysdyn/ui/trend/XYDataset.java | 503 ++++++++++++++++++ org.simantics.sysdyn.ui/sysdyn.product | 8 +- 18 files changed, 1412 insertions(+), 75 deletions(-) create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/AllVariablesOfModel.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/CategoryDataset.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/PieDataset.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/SysdynRangeHandlerFactory.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/XYDataset.java diff --git a/org.simantics.sysdyn.feature/feature.xml b/org.simantics.sysdyn.feature/feature.xml index 8399d120..855e54a1 100644 --- a/org.simantics.sysdyn.feature/feature.xml +++ b/org.simantics.sysdyn.feature/feature.xml @@ -138,54 +138,12 @@ download-size="0" install-size="0" version="0.0.0"/> - diff --git a/org.simantics.sysdyn.ui/META-INF/MANIFEST.MF b/org.simantics.sysdyn.ui/META-INF/MANIFEST.MF index 12553dbc..37abd3d8 100644 --- a/org.simantics.sysdyn.ui/META-INF/MANIFEST.MF +++ b/org.simantics.sysdyn.ui/META-INF/MANIFEST.MF @@ -54,7 +54,8 @@ Require-Bundle: org.simantics.layer0.utils;bundle-version="0.6.2", org.eclipse.ui.forms;bundle-version="3.5.2", org.simantics.scenegraph.swing;bundle-version="1.0.0", org.eclipse.nebula.widgets.tablecombo;bundle-version="1.0.0", - org.simantics.fmu;bundle-version="1.0.0" + org.simantics.fmu;bundle-version="1.0.0", + org.simantics.jfreechart;bundle-version="1.0.0" Bundle-Activator: org.simantics.sysdyn.ui.Activator Bundle-ActivationPolicy: lazy Export-Package: org.simantics.sysdyn.ui.browser.nodes diff --git a/org.simantics.sysdyn.ui/adapters.xml b/org.simantics.sysdyn.ui/adapters.xml index d2ce6b7a..179907b0 100644 --- a/org.simantics.sysdyn.ui/adapters.xml +++ b/org.simantics.sysdyn.ui/adapters.xml @@ -10,5 +10,5 @@ VTT Technical Research Centre of Finland - initial API and implementation --> - none 0 fill 1 + none 0 fill 1 \ No newline at end of file diff --git a/org.simantics.sysdyn.ui/plugin.xml b/org.simantics.sysdyn.ui/plugin.xml index 5c5f8544..e34af3c4 100644 --- a/org.simantics.sysdyn.ui/plugin.xml +++ b/org.simantics.sysdyn.ui/plugin.xml @@ -96,14 +96,6 @@ Structural model browser view for Sysdyn. - - @@ -172,7 +164,7 @@ relative="org.simantics.browsing.ui.graph.propertyView"> items = new ArrayList(); + + // Recursively read all configurations and add items + ReadConfiguration(graph, conf, "", items); + + // Add time to the variable list + items.add("time"); + + // Finally sort the results + Collections.sort(items, AlphanumComparator.CASE_INSENSITIVE_COMPARATOR); + return items.toArray(new String[items.size()]); + } + + /** + * Read components in a configuration and recursively all module configurations + * + * @param graph ReadGraph + * @param configuration Resource to be read + * @param path Current path from base realization + * @param items Found variables + * @throws DatabaseException + */ + private void ReadConfiguration(ReadGraph graph, Resource configuration, String path, Collection items) throws DatabaseException { + SysdynResource sr = SysdynResource.getInstance(graph); + Layer0 l0 = Layer0.getInstance(graph); + StructuralResource2 sr2 = StructuralResource2.getInstance(graph); + + String name; + for(Resource resource : graph.syncRequest(new ObjectsWithType(configuration, l0.ConsistsOf, sr.IndependentVariable))) { + name = path + NameUtils.getSafeName(graph, resource); + items.add(name); + } + + for(Resource resource : graph.syncRequest(new ObjectsWithType(configuration, l0.ConsistsOf, sr.Input))) { + name = path + NameUtils.getSafeName(graph, resource); + items.add(name); + } + + for(Resource module : graph.syncRequest(new ObjectsWithType(configuration, l0.ConsistsOf, sr.Module))) { + Resource instanceOf = graph.getPossibleObject(module, l0.InstanceOf); + Resource conf = graph.getPossibleObject(instanceOf, sr2.IsDefinedBy); + if(conf != null) { + String p = path + NameUtils.getSafeName(graph, module) + "."; + ReadConfiguration(graph, conf, p, items); + } + } + } + +} + diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/CategoryDataset.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/CategoryDataset.java new file mode 100644 index 00000000..d4c021c5 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/CategoryDataset.java @@ -0,0 +1,314 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 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.trend; + +import java.util.ArrayList; +import java.util.List; + +import javax.swing.SwingUtilities; + +import org.jfree.chart.plot.DefaultDrawingSupplier; +import org.jfree.chart.renderer.AbstractRenderer; +import org.jfree.chart.renderer.category.BarRenderer; +import org.jfree.data.category.DefaultCategoryDataset; +import org.jfree.data.general.Dataset; +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.layer0.exception.MissingVariableException; +import org.simantics.db.layer0.request.PossibleActiveExperiment; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.layer0.variable.Variables; +import org.simantics.db.procedure.Listener; +import org.simantics.db.request.Read; +import org.simantics.jfreechart.chart.AbstractDataset; +import org.simantics.jfreechart.chart.IRenderer; +import org.simantics.layer0.Layer0; +import org.simantics.modeling.ModelingResources; +import org.simantics.operation.Layer0X; +import org.simantics.sysdyn.Functions; +import org.simantics.sysdyn.JFreeChartResource; +import org.simantics.sysdyn.adapter.VariableRVIUtils; +import org.simantics.sysdyn.manager.SysdynDataSet; +import org.simantics.ui.SimanticsUI; + +/** + * Class representing a JFreeChart.CategoryDataset + * + * @author Teemu Lempinen + * + */ +public class CategoryDataset extends AbstractDataset implements org.simantics.jfreechart.chart.CategoryDataset{ + + private List seriesList; + private String realizationURI; + private IRenderer renderer; + private DefaultCategoryDataset dataset; + + public CategoryDataset(ReadGraph graph, Resource resource) { + super(graph, resource); + + try { + Layer0 l0 = Layer0.getInstance(graph); + ModelingResources mr = ModelingResources.getInstance(graph); + JFreeChartResource jfree = JFreeChartResource.getInstance(graph); + + // Find the model where the chart is located + Resource model = resource; + do { + model = graph.getPossibleObject(model, l0.PartOf); + } while(model != null && !graph.isInstanceOf(model, mr.StructuralModel)); + + // Find the variable realization of the current experiment + realizationURI = null; + Resource realization = graph.syncRequest(new PossibleActiveExperiment(model)); + if (realization == null) { + Layer0X L0X = Layer0X.getInstance(graph); + realization = graph.getPossibleObject(model, L0X.HasBaseRealization); + } + if (realization != null) + realizationURI = graph.getURI(realization); + + if(realizationURI == null) + return; // No experiment -> No results + + Resource seriesList = graph.getPossibleObject(resource, jfree.Dataset_seriesList); + if(seriesList != null) + this.seriesList = ListUtils.toList(graph, seriesList); + + Resource rendererResource = graph.getPossibleObject(resource, jfree.Dataset_renderer); + renderer = graph.adapt(rendererResource, IRenderer.class); + + } catch (DatabaseException e) { + e.printStackTrace(); + } + } + + private DatasetListener listener; + + @Override + public Dataset getDataset() { + + if(seriesList == null || seriesList.isEmpty() || SimanticsUI.getSession() == null) + return null; + + if(dataset == null) { + dataset = new DefaultCategoryDataset(); + } + + if(listener == null || listener.isDisposed()) { + listener = new DatasetListener(); + SimanticsUI.getSession().asyncRequest(new Read>() { + + @Override + public ArrayList perform(ReadGraph graph) throws DatabaseException { + JFreeChartResource jfree = JFreeChartResource.getInstance(graph); + + ArrayList series = new ArrayList(); + // Get properties for all series + if(seriesList != null) { + for(Resource r : seriesList) { + String rvi = graph.getPossibleRelatedValue(r, jfree.variableRVI); + if(rvi == null) + continue; + + try { + // Get a variable for the series + Variable v = Variables.getVariable(graph, realizationURI + rvi); + // Get values + Variable dsVariable = v.browsePossible(graph, "#" + Functions.ACTIVE_DATASETS + "#"); + if(dsVariable == null) + return series; + + Object object = dsVariable.getValue(graph); + + if(object == null || !(object instanceof ArrayList)) + return series; + + ArrayList datasets = new ArrayList(); + + for(Object o : (ArrayList)object) { + if(o instanceof SysdynDataSet) + datasets.add((SysdynDataSet)o); + } + + String[] filter = graph.getPossibleRelatedValue(r, jfree.variableFilter); + if(filter != null) { + ArrayList result2 = VariableRVIUtils.getDataset(datasets, filter); + if(result2 != null) { + datasets = result2; + } + } + + // Find if a specific time is set for this chart + Double chartTime = null; + if(!datasets.isEmpty()) { + Layer0 l0 = Layer0.getInstance(graph); + Resource datasetResource = graph.getPossibleObject(r, l0.PartOf); + if(datasetResource != null) { + Resource plot = graph.getPossibleObject(datasetResource, l0.PartOf); + if(plot != null) { + Resource chart = graph.getPossibleObject(plot, l0.PartOf); + if(chart != null) + chartTime = graph.getPossibleRelatedValue(chart, jfree.Chart_time); + } + } + } + + for(SysdynDataSet dataset : datasets) { + double[] va = dataset.values; + + + if(va == null || va.length == 0) + continue; + + /* + * Time + * + * 1. find time for the individual series. + * 2. find time for the whole chart + * 3. find simulation time + */ + Double time = graph.getPossibleRelatedValue(r, jfree.Series_time, Bindings.DOUBLE); + if(time == null) + time = chartTime; + if(time == null) { + // Get a variable for the experiment run + Variable run = Variables.getVariable(graph, realizationURI); + if(run == null) + return null; + Variable timeVar = run.browsePossible(graph, "#" + Functions.TIME + "#"); + if(timeVar != null) + time = timeVar.getValue(graph, Bindings.DOUBLE); + + } + + // Value + Double value = null; + if(time == null) { + value = va[va.length - 1]; + } else { + double[] ta = dataset.times; + for(int i = 0; i < ta.length; i++) { + double t = ta[i]; + if(time <= t) { + value = va[i]; + break; + } + } + + if(value == null) + value = va[va.length - 1]; + } + String label = graph.getPossibleRelatedValue(r, Layer0.getInstance(graph).HasLabel); // Called to refresh paints when label changes + String name = dataset.name; + series.add(new TempSeries(label == null || label.isEmpty() ? name : label, dataset.result, value)); + } + } catch (MissingVariableException e) { + // Do nothing, if variable was not found. Move on to the next series + } + } + } + return series; + } + + }, listener); + } + return dataset; + } + + @Override + public AbstractRenderer getRenderer() { + return renderer.getRenderer(); + } + + private class DatasetListener implements Listener> { + private boolean disposed = false; + + public void dispose() { + disposed = true; + } + + @Override + public void execute(final ArrayList series) { + // Modify series in AWT thread to avoid synchronization problems + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + // Remove all unused series + dataset.clear(); + BarRenderer renderer = ((BarRenderer)getRenderer()); + renderer.getPlot().setDrawingSupplier(new DefaultDrawingSupplier()); + + // Add found series + for(int i = 0; i < series.size(); i++) { + TempSeries s = series.get(i); + if(renderer instanceof org.jfree.chart.renderer.category.StackedBarRenderer && s.name.contains("[")) { + String category = s.name.substring(0, s.name.indexOf('[')); + if(s.result != null) + category = category + " : " + s.result; + String series = s.name.substring(s.name.indexOf('[')); + dataset.addValue(s.value, series, category); + } else { + dataset.addValue(s.value, s.result == null ? "Current" : s.result, s.name); + } + } + } + }); + } + + @Override + public void exception(Throwable t) { + t.printStackTrace(); + } + + @Override + public boolean isDisposed() { + return disposed; + } + }; + + @Override + public void dispose() { + super.dispose(); + if(listener != null) { + listener.dispose(); + listener = null; + } + } + + /** + * Auxiliary class containing all information needed to define a single series + * @author Teemu Lempinen + * + */ + private class TempSeries { + public String name; + public String result; + public Double value; + + public TempSeries(String name, String result, Double value) { + this.name = name; + this.value = value; + this.result = result; + } + + @Override + public String toString() { + return "TempSeries: " + name + ", " + value + ", " + result; + } + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/PieDataset.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/PieDataset.java new file mode 100644 index 00000000..eb3d1cb2 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/PieDataset.java @@ -0,0 +1,332 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 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.trend; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import javax.swing.SwingUtilities; + +import org.jfree.chart.renderer.AbstractRenderer; +import org.jfree.data.general.Dataset; +import org.jfree.data.general.DefaultPieDataset; +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.layer0.exception.MissingVariableException; +import org.simantics.db.layer0.request.PossibleActiveExperiment; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.layer0.variable.Variables; +import org.simantics.db.procedure.Listener; +import org.simantics.db.request.Read; +import org.simantics.diagram.G2DUtils; +import org.simantics.jfreechart.chart.AbstractDataset; +import org.simantics.layer0.Layer0; +import org.simantics.modeling.ModelingResources; +import org.simantics.operation.Layer0X; +import org.simantics.sysdyn.Functions; +import org.simantics.sysdyn.JFreeChartResource; +import org.simantics.sysdyn.adapter.VariableRVIUtils; +import org.simantics.sysdyn.manager.SysdynDataSet; +import org.simantics.ui.SimanticsUI; + +/** + * Class representing a PieDataset in JFreeChart ontology + * @author Teemu Lempinen + * + */ +public class PieDataset extends AbstractDataset implements org.simantics.jfreechart.chart.PieDataset{ + + private List seriesList; + private String realizationURI; + private DefaultPieDataset dataset; + + private HashMap colorMap; + private HashMap explodedMap; + + public PieDataset(ReadGraph graph, Resource resource) { + super(graph, resource); + + try { + Layer0 l0 = Layer0.getInstance(graph); + ModelingResources mr = ModelingResources.getInstance(graph); + JFreeChartResource jfree = JFreeChartResource.getInstance(graph); + + // Find the model where the chart is located + Resource model = resource; + do { + model = graph.getPossibleObject(model, l0.PartOf); + } while(model != null && !graph.isInstanceOf(model, mr.StructuralModel)); + + // Find the variable realization of the current experiment + realizationURI = null; + Resource realization = graph.syncRequest(new PossibleActiveExperiment(model)); + if (realization == null) { + Layer0X L0X = Layer0X.getInstance(graph); + realization = graph.getPossibleObject(model, L0X.HasBaseRealization); + } + if (realization != null) + realizationURI = graph.getURI(realization); + + if(realizationURI == null) + return; // No experiment -> No results + + Resource seriesList = graph.getPossibleObject(resource, jfree.Dataset_seriesList); + if(seriesList != null) { + this.seriesList = ListUtils.toList(graph, seriesList); + } + } catch (DatabaseException e) { + e.printStackTrace(); + } + + } + + /** + * Map of colors for different slices in a pie chart. Name + * indicates the key of the value. + * @return Map of colors for different slices in a pie chart + */ + public HashMap getColorMap() { + return colorMap; + } + + /** + * Map of exploded statuses for slices in a pie chart. Name + * indicates the key of the slice. + * @return + */ + public HashMap getExplodedMap() { + return explodedMap; + } + + @Override + public Dataset getDataset() { + if(seriesList == null || seriesList.isEmpty() || SimanticsUI.getSession() == null) + return null; + + if(dataset == null) { + dataset = new DefaultPieDataset(); + } + + if(listener == null) { + listener = new DatasetListener(); + SimanticsUI.getSession().asyncRequest(new Read>() { + + @Override + public ArrayList perform(ReadGraph graph) throws DatabaseException { + JFreeChartResource jfree = JFreeChartResource.getInstance(graph); + + ArrayList series = new ArrayList(); + // Get properties for all series + if(seriesList != null) { + + colorMap = new HashMap(); + explodedMap = new HashMap(); + + for(Resource r : seriesList) { + String label = graph.getPossibleRelatedValue(r, Layer0.getInstance(graph).HasLabel); + String rvi = graph.getPossibleRelatedValue(r, jfree.variableRVI); + if(rvi == null) + continue; + + try { + // Get visual properties + Resource c = graph.getPossibleObject(r, jfree.color); + Color color = c == null ? null : G2DUtils.getColor(graph, c); + Boolean exploded = graph.getPossibleRelatedValue(r, jfree.Series_exploded, Bindings.BOOLEAN); + + // Get a variable for the series + Variable v = Variables.getVariable(graph, realizationURI + rvi); + + // Get values + Variable dsVariable = v.browsePossible(graph, "#" + Functions.ACTIVE_DATASETS + "#"); + if(dsVariable == null) + return series; + + Object object = dsVariable.getValue(graph); + if(object == null || !(object instanceof ArrayList)) + return series; + + ArrayList datasets = new ArrayList(); + + for(Object o : (ArrayList)object) { + if(o instanceof SysdynDataSet) + datasets.add((SysdynDataSet)o); + } + + String[] filter = graph.getPossibleRelatedValue(r, jfree.variableFilter); + if(filter != null) { + ArrayList result2 = VariableRVIUtils.getDataset(datasets, filter); + if(result2 != null) { + datasets = result2; + } + } + + // Find if a specific time is set for this chart + Double chartTime = null; + if(!datasets.isEmpty()) { + Layer0 l0 = Layer0.getInstance(graph); + Resource datasetResource = graph.getPossibleObject(r, l0.PartOf); + if(datasetResource != null) { + Resource plot = graph.getPossibleObject(datasetResource, l0.PartOf); + if(plot != null) { + Resource chart = graph.getPossibleObject(plot, l0.PartOf); + if(chart != null) + chartTime = graph.getPossibleRelatedValue(chart, jfree.Chart_time); + } + } + } + + for(SysdynDataSet dataset : datasets) { + double[] va = dataset.values; + + if(va == null || va.length == 0) + continue; + + /* + * Time + * + * 1. find time for the individual series. + * 2. find time for the whole chart + * 3. find simulation time + */ + Double time = graph.getPossibleRelatedValue(r, jfree.Series_time, Bindings.DOUBLE); + if(time == null) + time = chartTime; + if(time == null) { + // Get a variable for the experiment run + Variable run = Variables.getVariable(graph, realizationURI); + if(run == null) + return null; + Variable timeVar = run.browsePossible(graph, "#" + Functions.TIME + "#"); + if(timeVar != null) + time = timeVar.getValue(graph, Bindings.DOUBLE); + } + + // Value + Double value = null; + if(time == null) { + value = va[va.length - 1]; + } else { + double[] ta = dataset.times; + for(int i = 0; i < ta.length; i++) { + double t = ta[i]; + if(time <= t) { + value = va[i]; + break; + } + } + + if(value == null) + value = va[va.length - 1]; + } + + String name = label == null || label.isEmpty() ? dataset.name : label; + if(dataset.result != null) + name = name + " : " + dataset.result; + colorMap.put(name, color); + explodedMap.put(name, exploded); + series.add(new TempSeries(name, value)); + } + } catch (MissingVariableException e) { + // Do nothing, if variable was not found. Move on to the next series + } + } + } + return series; + } + + }, listener); + } + return dataset; + } + + private DatasetListener listener; + + private class DatasetListener implements Listener> { + + private boolean disposed = false; + + @Override + public void execute(final ArrayList series) { + // Modify series in AWT thread to avoid synchronization problems + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + // Remove all series + dataset.clear(); + + // Add found series + for(int i = 0; i < series.size(); i++) { + TempSeries s = series.get(i); + dataset.setValue(s.name, s.value); + } + } + }); + } + + @Override + public void exception(Throwable t) { + t.printStackTrace(); + } + + @Override + public boolean isDisposed() { + return disposed; + } + + public void dispose() { + disposed = true; + } + } + + @Override + public AbstractRenderer getRenderer() { + // No renderer for pie chart + return null; + } + + @Override + public void dispose() { + super.dispose(); + if(listener != null) { + listener.dispose(); + listener = null; + } + } + + + /** + * Auxiliary class containing all information needed to define a single series + * @author Teemu Lempinen + * + */ + private class TempSeries { + public String name; + public Double value; + + public TempSeries(String name, Double value) { + this.name = name; + this.value = value; + } + + @Override + public String toString() { + return "TempSeries: " + name + ", " + value; + } + } +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/SysdynRangeHandlerFactory.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/SysdynRangeHandlerFactory.java new file mode 100644 index 00000000..74bbee09 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/SysdynRangeHandlerFactory.java @@ -0,0 +1,141 @@ +package org.simantics.sysdyn.ui.trend; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.simantics.browsing.ui.swt.widgets.impl.ReadFactoryImpl; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.utils.ListUtils; +import org.simantics.db.common.utils.NameUtils; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.layer0.variable.Variables; +import org.simantics.db.request.Read; +import org.simantics.jfreechart.chart.ChartUtils; +import org.simantics.jfreechart.chart.properties.RangeHandlerFactory; +import org.simantics.layer0.Layer0; +import org.simantics.sysdyn.JFreeChartResource; +import org.simantics.sysdyn.SysdynResource; +import org.simantics.utils.datastructures.Quad; + +public class SysdynRangeHandlerFactory implements RangeHandlerFactory { + + @Override + public Read> getRequest(final Resource series) { + // TODO Auto-generated method stub + return new Read>() { + + @Override + public LinkedHashMap perform(ReadGraph graph) throws DatabaseException { + JFreeChartResource jfree = JFreeChartResource.getInstance(graph); + SysdynResource sr = SysdynResource.getInstance(graph); + + String realizationURI = ChartUtils.getCurrentRealizationURI(graph, series); + String rvi = graph.getPossibleRelatedValue(series, jfree.variableRVI); + if(rvi == null) + return null; + + try { + // Find the variable for the current variableRVI + Variable v = Variables.getVariable(graph, realizationURI + rvi.trim()); + if(v == null) + return null; + + // Find all enumeration replacements in the variable's path + HashMap redeclarations = new HashMap(); + Variable parent = v; + while((parent = parent.getParent(graph)) != null) { + Resource represents = parent.getRepresents(graph); + Resource type = graph.getSingleObject(represents, Layer0.getInstance(graph).InstanceOf); + if(!graph.isInheritedFrom(type, sr.Module)) + break; + + for(Resource redeclaration : graph.getObjects(represents, sr.Module_redeclaration)) { + redeclarations.put( + graph.getPossibleObject(redeclaration, sr.Redeclaration_replacedEnumeration), + graph.getPossibleObject(redeclaration, sr.Redeclaration_replacingEnumeration) + ); + } + } + + + + + Resource variable = v.getRepresents(graph); + + // Return the enumerations assigned to that variable + Resource arrayIndexes = graph.getPossibleObject(variable, sr.Variable_arrayIndexesList); + if(arrayIndexes != null) { + LinkedHashMap result = new LinkedHashMap(); + for(Resource enumeration : ListUtils.toList(graph, arrayIndexes)) { + + // Find possible redeclarations for enumeration + Resource redeclaration = enumeration; + while(redeclarations.get(redeclaration) != null) + redeclaration = redeclarations.get(redeclaration); + + String enumerationName = NameUtils.getSafeName(graph, redeclaration); + result.put(enumerationName, redeclaration); + } + return result; + } + } catch (DatabaseException e) { + // No variable was found, return null + } + return null; + } + + }; + } + + @Override + public ReadFactoryImpl> getRangeItemFactory(int index, Resource res) { + return new RangeItemFactory(index, res); + } + + + /** + * RangeItemFactory finds all inexes of a given enumeration + * and adds "Sum" and "All" to the returned indexes + * @author Teemu Lempinen + * + */ + private class RangeItemFactory extends ReadFactoryImpl> { + + private int index; + private Resource enumeration; + + /** + * + * @param index Index of the enumeration in the variable + * @param enumeration The enumeration + */ + public RangeItemFactory(int index, Resource enumeration) { + this.index = index; + this.enumeration = enumeration; + } + + public Object getIdentity(Object inputContents) { + return new Quad>(inputContents, index, enumeration, getClass()); + } + @Override + public Map perform(ReadGraph graph, Resource series) throws DatabaseException { + SysdynResource sr = SysdynResource.getInstance(graph); + LinkedHashMap result = new LinkedHashMap(); + Resource enumerationIndexes = graph.getPossibleObject(enumeration, sr.Enumeration_enumerationIndexList); + List indexes = ListUtils.toList(graph, enumerationIndexes); + // First add "All" and "Sum", then all of the enumeration indexes in order + result.put("All", "All"); + result.put("Sum", "Sum"); + for(Resource index : indexes) { + String name = NameUtils.getSafeName(graph, index); + result.put(name, name); + } + return result; + } + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/TrendView.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/TrendView.java index e730e1fb..83bcc56d 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/TrendView.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/TrendView.java @@ -31,8 +31,8 @@ import org.simantics.db.Resource; import org.simantics.db.exception.DatabaseException; import org.simantics.db.procedure.Listener; import org.simantics.db.request.Read; +import org.simantics.jfreechart.chart.IJFreeChart; import org.simantics.sysdyn.manager.SysdynDataSet; -import org.simantics.sysdyn.ui.trend.chart.IJFreeChart; import org.simantics.sysdyn.ui.viewUtils.SysdynDatasetSelectionListener; /** diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/XYDataset.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/XYDataset.java new file mode 100644 index 00000000..363669ce --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/XYDataset.java @@ -0,0 +1,503 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 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.trend; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Paint; +import java.awt.Stroke; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.SwingUtilities; + +import org.jfree.chart.ChartColor; +import org.jfree.chart.labels.StandardXYToolTipGenerator; +import org.jfree.chart.plot.DefaultDrawingSupplier; +import org.jfree.chart.plot.ValueMarker; +import org.jfree.chart.renderer.AbstractRenderer; +import org.jfree.chart.renderer.xy.AbstractXYItemRenderer; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.general.Dataset; +import org.jfree.data.xy.DefaultXYDataset; +import org.jfree.ui.Layer; +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.layer0.exception.MissingVariableException; +import org.simantics.db.layer0.request.PossibleActiveExperiment; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.layer0.variable.Variables; +import org.simantics.db.procedure.Listener; +import org.simantics.db.request.Read; +import org.simantics.diagram.G2DUtils; +import org.simantics.jfreechart.chart.AbstractDataset; +import org.simantics.jfreechart.chart.IRenderer; +import org.simantics.layer0.Layer0; +import org.simantics.modeling.ModelingResources; +import org.simantics.operation.Layer0X; +import org.simantics.sysdyn.Functions; +import org.simantics.sysdyn.JFreeChartResource; +import org.simantics.sysdyn.adapter.VariableRVIUtils; +import org.simantics.sysdyn.manager.SysdynDataSet; +import org.simantics.ui.SimanticsUI; +import org.simantics.utils.datastructures.Pair; + +/** + * Class representing a JFreeChart.XYDataset + * + * @author Teemu Lempinen + * + */ +public class XYDataset extends AbstractDataset implements org.simantics.jfreechart.chart.XYDataset{ + + private IRenderer renderer; + + public XYDataset(ReadGraph graph, final Resource datasetResource) { + super(graph, datasetResource); + } + + private DefaultXYDataset dataset; + private DataSetListener datasetListener; + private TimeListener timeListener; + + @Override + public Dataset getDataset() { + if(dataset == null) { + dataset = new DefaultXYDataset(); + } + + if(datasetListener == null || datasetListener.isDisposed()) { + datasetListener = new DataSetListener(); + SimanticsUI.getSession().asyncRequest(new Read, IRenderer>>() { + + @Override + public Pair, IRenderer> perform(ReadGraph graph) throws DatabaseException { + JFreeChartResource jfree = JFreeChartResource.getInstance(graph); + + // Renderer + IRenderer renderer = null; + Resource rendererResource = graph.getPossibleObject(resource, jfree.Dataset_renderer); + if(rendererResource != null) + renderer = graph.adapt(rendererResource, IRenderer.class); + + ArrayList series = new ArrayList(); + + String realizationURI = getRealizationURI(graph); + + if(realizationURI == null) + return new Pair, IRenderer>(series, renderer); // No experiment -> No results + + // Get a variable for the x-axis (if not time) + double[] domainValues = null; + Resource domainAxis = graph.getPossibleObject(resource, jfree.Dataset_mapToDomainAxis); + if(domainAxis != null) { + String rvi = graph.getPossibleRelatedValue(domainAxis, jfree.variableRVI); + if(rvi != null && !rvi.isEmpty()) { + try { + Variable domainVariable = Variables.getVariable(graph, realizationURI + rvi); + Variable valuesVariable = domainVariable.browsePossible(graph, "#" + Functions.VALUES +"#"); + if(valuesVariable != null) { + double[][] valuesArray = valuesVariable.getValue(graph); + if(valuesArray.length > 0) + domainValues = valuesArray[0]; + } + } catch(MissingVariableException e) { + //Do nothing, use time as domain axis + } + } + } + + Resource seriesList = graph.getPossibleObject(resource, jfree.Dataset_seriesList); + + // Get properties for all series + if(seriesList != null) { + for(Resource r : ListUtils.toList(graph, seriesList)) { + String rvi = graph.getPossibleRelatedValue(r, jfree.variableRVI); + if(rvi == null) + continue; + + try { + // Get visual properties + Integer width = graph.getPossibleRelatedValue(r, jfree.Series_lineWidth, Bindings.INTEGER); + if(width == null) width = 1; + + Resource c = graph.getPossibleObject(r, jfree.color); + Color color = c == null ? null : G2DUtils.getColor(graph, c); + + String label = graph.getPossibleRelatedValue(r, Layer0.getInstance(graph).HasLabel); + + // Get a variable for the series + Variable v = Variables.getVariable(graph, realizationURI + rvi); + if(v == null) + return new Pair, IRenderer>(series, renderer); + + // Get values + Variable dsVariable = v.browsePossible(graph, "#" + Functions.ACTIVE_DATASETS + "#"); + Object object = null; + if(dsVariable != null) + object = dsVariable.getValue(graph); + + if(object == null || !(object instanceof ArrayList)) + return new Pair, IRenderer>(series, renderer); + + ArrayList datasets = new ArrayList(); + + for(Object o : (ArrayList)object) { + if(o instanceof SysdynDataSet) + datasets.add((SysdynDataSet)o); + } + + + String[] filter = graph.getPossibleRelatedValue(r, jfree.variableFilter); + if(filter != null) { + ArrayList result2 = VariableRVIUtils.getDataset(datasets, filter); + if(result2 != null) { + datasets = result2; + } + } + + for(SysdynDataSet dataset : datasets) { + double[] va = dataset.values; + + // Get domain axis values (time OR other variable) + double[] ta; + if(domainValues != null) { + ta = domainValues; + + // If domainAxis is other than time, parameter values size is different. + if(domainValues.length > va.length) { + double value = va[0]; + va = new double[domainValues.length]; + for(int i = 0; i < domainValues.length; i++) + va[i] = value; + } + + // If domainAxis is a parameter, the domainValues array is too short + if(domainValues.length < va.length && domainValues.length == 2 && domainValues[0] == domainValues[1]) { + double value = domainValues[0]; + ta = new double[va.length]; + for(int i = 0; i < va.length; i++) + ta[i] = value; + } + + } else { + ta = dataset.times; + } + + if(ta!=null && va!=null && (va.length == ta.length)) { + // Add series if everything OK + String name = dataset.name; + if(label != null) + name = label; + if(dataset.result != null && !dataset.result.isEmpty()) + name = name + " : " + dataset.result; + series.add(new TempSeries(name, new double[][] {ta, va}, width, color)); + if(ta.length == 0 || va.length == 0) + System.out.println(dataset.name + " : " + dataset.result + ". Sizes: " + va.length + " and " + ta.length); + } + } + + } catch (MissingVariableException e) { + // Do nothing, if variable was not found. Move on to the next series + } + } + } + return new Pair, IRenderer>(series, renderer); + } + + }, datasetListener); + } + + if(timeListener == null || timeListener.isDisposed()) { + timeListener = new TimeListener(); + SimanticsUI.getSession().asyncRequest(new Read() { + @Override + public Double perform(ReadGraph graph) throws DatabaseException { + JFreeChartResource jfree = JFreeChartResource.getInstance(graph); + // Get properties for all series + Resource series = graph.getPossibleObject(resource, jfree.Dataset_seriesList); + if(series != null) { + List seriesList = ListUtils.toList(graph, series); + if(seriesList != null) { + String realizationURI = getRealizationURI(graph); + for(Resource r : seriesList) { + String rvi = graph.getPossibleRelatedValue(r, jfree.variableRVI); + if(rvi == null) + continue; + try { + // Get a variable for the experiment run + Variable v = Variables.getVariable(graph, realizationURI); + if(v == null) + return null; + Variable timeVar = v.browsePossible(graph, "#" + Functions.TIME + "#"); + if(timeVar != null) + return timeVar.getValue(graph, Bindings.DOUBLE); + } catch (MissingVariableException e) { + // Do nothing, if variable was not found. + } + } + } + } + return null; + } + + }, timeListener); + } + return dataset; + } + + /** + * Class for identifying a time marker in a plot + * @author Teemu Lempinen + * + */ + private class TimeMarker extends ValueMarker { + private static final long serialVersionUID = 2018755066561629172L; + + public TimeMarker(double value, Paint paint, Stroke stroke) { + super(value, paint, stroke); + } + } + + private class DataSetListener implements Listener, IRenderer>> { + + private boolean disposed = false; + + public void dispose() { + disposed = true; + } + + @Override + public boolean isDisposed() { + return disposed; + } + + @Override + public void execute(Pair, IRenderer> result) { + final ArrayList series = result.first; + renderer = result.second; + + // Modify series in AWT thread to avoid synchronization problems + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + + org.jfree.chart.plot.XYPlot plot = ((AbstractXYItemRenderer)getRenderer()).getPlot(); + if(plot != null) { + /* + * Drawing supplier with a modified first yellow. The default first + * yellow is too light to be visible against a white background + */ + Paint[] paintSequence = ChartColor.createDefaultPaintArray(); + paintSequence[3] = new Color(0xFF, 0xDD, 0x00); + DefaultDrawingSupplier drawingsupplier = new DefaultDrawingSupplier( + paintSequence, + DefaultDrawingSupplier.DEFAULT_FILL_PAINT_SEQUENCE, + DefaultDrawingSupplier.DEFAULT_OUTLINE_PAINT_SEQUENCE, + DefaultDrawingSupplier.DEFAULT_STROKE_SEQUENCE, + DefaultDrawingSupplier.DEFAULT_OUTLINE_STROKE_SEQUENCE, + DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE); + plot.setDrawingSupplier(drawingsupplier); + } + // Remove all series + for(int i = dataset.getSeriesCount() - 1; i >= 0; i-- ) { + dataset.removeSeries(dataset.getSeriesKey(i)); + } + + // Add found series + for(int i = 0; i < series.size(); i++) { + TempSeries s = series.get(i); + dataset.addSeries(s.name, s.values); + getRenderer().setSeriesStroke(i, new BasicStroke((float)s.width)); + getRenderer().setSeriesPaint(i, s.color); + } + } + }); + } + + @Override + public void exception(Throwable t) { + t.printStackTrace(); + } + + } + + + /** + * Listener for updating the time indicator for XY plots + * @author Teemu Lempinen + * + */ + private class TimeListener implements Listener { + + private ValueMarker marker; + private boolean disposed = false; + private Stroke dashStroke = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER, 10.0f, new float[] {5.0f, 3.0f, 1.0f, 3.0f}, 0.0f); + + public TimeListener() { + this.marker = new TimeMarker(0.0, Color.red, dashStroke); + } + + public void dispose() { + this.disposed = true; + if(marker != null) { + org.jfree.chart.plot.XYPlot plot = ((AbstractXYItemRenderer)getRenderer()).getPlot(); + if(plot != null) + plot.removeDomainMarker(marker); + } + } + + @Override + public boolean isDisposed() { + return disposed; + } + + @Override + public void execute(final Double time) { + // Modify in AWT thread to avoid synchronization problems + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + org.jfree.chart.plot.XYPlot plot = ((AbstractXYItemRenderer)getRenderer()).getPlot(); + + if(plot == null) + return; + + plot.removeDomainMarker(marker); + if(time != null) { + marker.setValue(time); + if(plot.getDomainMarkers(Layer.FOREGROUND) == null || !plot.getDomainMarkers(Layer.FOREGROUND).contains(marker)) { + int i = 0; + for(i = 0; i < plot.getDatasetCount(); i++) { + if(plot.getDataset(i) != null && plot.getDataset(i).equals(dataset)) + break; + } + plot.addDomainMarker(i, marker, Layer.FOREGROUND); + } + } + + } + }); + } + + @Override + public void exception(Throwable t) { + t.printStackTrace(); + } + + } + + @Override + public void dispose() { + if(timeListener != null) { + timeListener.dispose(); + timeListener = null; + } + + if(datasetListener != null) { + datasetListener.dispose(); + datasetListener = null; + } + } + + + /** + * Auxiliary class containing all information needed to define a single series + * @author Teemu Lempinen + * + */ + private class TempSeries { + public double[][] values; + public String name; + public int width; + public Color color; + + public TempSeries(String name, double[][] values, int width, Color color) { + this.name = name; + this.values = values; + this.width = width; + this.color = color; + } + } + + @Override + public AbstractRenderer getRenderer() { + if(renderer == null) { + + try { + renderer = SimanticsUI.getSession().syncRequest(new Read() { + + @Override + public IRenderer perform(ReadGraph graph) throws DatabaseException { + JFreeChartResource jfree = JFreeChartResource.getInstance(graph); + IRenderer renderer = null; + Resource rendererResource = graph.getPossibleObject(resource, jfree.Dataset_renderer); + if(rendererResource != null) + renderer = graph.adapt(rendererResource, IRenderer.class); + return renderer; + } + }); + } catch (DatabaseException e) { + } + if(renderer == null) { + XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(true, false); + renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator()); + return renderer; + } else { + return renderer.getRenderer(); + } + } else { + return renderer.getRenderer(); + } + } + + /** + * Get the realization uri of the current dataset resource + * @param graph ReadGraph + * @return realization uri for current dataset resource + * @throws DatabaseException + */ + private String getRealizationURI(ReadGraph graph) throws DatabaseException { + if(resource == null) + return null; + + Layer0 l0 = Layer0.getInstance(graph); + ModelingResources mr = ModelingResources.getInstance(graph); + + // Find the model where the chart is located + Resource model = resource; + do { + model = graph.getPossibleObject(model, l0.PartOf); + } while(model != null && !graph.isInstanceOf(model, mr.StructuralModel)); + + if(model == null) + return null; + + // Find the variable realization of the current experiment + String realizationURI = null; + Resource realization = graph.syncRequest(new PossibleActiveExperiment(model)); + if (realization == null) { + Layer0X L0X = Layer0X.getInstance(graph); + realization = graph.getPossibleObject(model, L0X.HasBaseRealization); + } + if (realization != null) + realizationURI = graph.getURI(realization); + + return realizationURI; + } +} diff --git a/org.simantics.sysdyn.ui/sysdyn.product b/org.simantics.sysdyn.ui/sysdyn.product index 7c70aedb..6d9d5612 100644 --- a/org.simantics.sysdyn.ui/sysdyn.product +++ b/org.simantics.sysdyn.ui/sysdyn.product @@ -3,15 +3,12 @@ - - -fixerrors ---launcher.XXMaxPermSize -192m --data @noDefault + -fixerrors +--launcher.XXMaxPermSize 192m -ea -Xmx768M -XX:MaxPermSize=192m -Xshare:off -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts @@ -27,7 +24,6 @@ - org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6 -- 2.47.1