From 0bb69a3d651cb92967259c962d21cfe860280314 Mon Sep 17 00:00:00 2001 From: lehtonen Date: Tue, 5 Jun 2012 10:02:46 +0000 Subject: [PATCH] Merge until trunk@25101 to branches/stable on 2012-06-05. refs #3468 git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/branches@25105 ac1ea38d-2e2b-0410-8846-a27921b304fc --- .../FMUSolution/FMUSolution.opensdf | Bin 34 -> 0 bytes .../modelica/data/CSVSimulationResult.java | 4 +- .../simantics/modelica/data/Mat4Reader.java | 21 ++- .../modelica/data/MatSimulationResult.java | 4 +- .../modelica/data/SimulationResult.java | 32 ++-- stable/org.simantics.sysdyn.ontology/graph.tg | Bin 63802 -> 64358 bytes .../graph/Sysdyn.pgraph | 1 + .../graph/SysdynFunctions.pgraph | 9 ++ .../org/simantics/sysdyn/SysdynResource.java | 9 ++ .../browser/contributions/Configuration.java | 35 +++-- .../contributions/FunctionLibraries.java | 22 ++- .../contributions/LibraryFunctions.java | 21 ++- .../ui/browser/contributions/ModuleType.java | 32 +++- .../SysdynComponentCopyAdvisor.java | 8 +- .../ui/menu/PlaybackSliderContribution.java | 5 +- .../ui/properties/ConfigurationTab.java | 10 ++ .../sysdyn/ui/properties/EquationTab.java | 3 + .../widgets/ArrayExpressionCombo.java | 111 ++----------- .../NameAndArrayRangeModifyListener.java | 146 ++++++++++++++++++ .../ModuleStructureGraphRequest.java | 27 +++- .../sysdyn/ui/utils/ExpressionUtils.java | 9 ++ .../sysdyn/adapter/VariableRVIUtils.java | 13 +- .../sysdyn/manager/HistoryDatasetResult.java | 4 +- .../sysdyn/manager/SysdynExperiment.java | 59 +++++-- .../sysdyn/manager/SysdynGameExperiment.java | 56 +++++-- .../sysdyn/manager/SysdynInitKeys.java | 1 + .../simantics/sysdyn/manager/SysdynModel.java | 10 +- .../manager/SysdynPlaybackExperiment.java | 1 - .../sysdyn/modelica/ModelicaWriter.java | 52 ++++--- .../simantics/sysdyn/representation/Book.java | 7 + .../sysdyn/representation/Configuration.java | 2 +- .../sysdyn/representation/Model.java | 26 +++- .../sysdyn/representation/Sheet.java | 12 +- .../sysdyn/representation/Variability.java | 22 ++- .../expressions/WithLookupExpression.java | 3 +- .../representation/utils/IndexUtils.java | 103 ++++++------ .../utils/SheetFormatUtils.java | 15 +- .../sysdyn/simulation/SimulationJob.java | 6 +- 38 files changed, 619 insertions(+), 282 deletions(-) delete mode 100644 stable/org.simantics.modelica/FMUSolution/FMUSolution.opensdf create mode 100644 stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/arrays/NameAndArrayRangeModifyListener.java diff --git a/stable/org.simantics.modelica/FMUSolution/FMUSolution.opensdf b/stable/org.simantics.modelica/FMUSolution/FMUSolution.opensdf deleted file mode 100644 index f8442e664d8d47113b89cde8a3294c019a6f05b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34 mcmXS6NM$HyC;^g144DiJ48aV645193K-z;rmmz?`nE?QAmIa;w diff --git a/stable/org.simantics.modelica/src/org/simantics/modelica/data/CSVSimulationResult.java b/stable/org.simantics.modelica/src/org/simantics/modelica/data/CSVSimulationResult.java index 1c3b1e2f..8787adf9 100644 --- a/stable/org.simantics.modelica/src/org/simantics/modelica/data/CSVSimulationResult.java +++ b/stable/org.simantics.modelica/src/org/simantics/modelica/data/CSVSimulationResult.java @@ -28,7 +28,7 @@ public class CSVSimulationResult extends SimulationResult { * Overridden method to support csv-formatted simulation results */ @Override - public void read(InputStream stream) { + public void read(InputStream stream, int outputInterval) { errors.clear(); // First line contains the variable names in format "name" (including quotes); @@ -42,7 +42,7 @@ public class CSVSimulationResult extends SimulationResult { // Create lists for receiving values for each variable. Names still with quotes. for(String name : names) { if(!name.isEmpty()) - valueMap.put(name, new DataSet(name, new double[numberOfTimeSteps], new double[numberOfTimeSteps])); + valueMap.put(name, new DataSet(name, new double[numberOfLines], new double[numberOfLines])); } int row = 0; diff --git a/stable/org.simantics.modelica/src/org/simantics/modelica/data/Mat4Reader.java b/stable/org.simantics.modelica/src/org/simantics/modelica/data/Mat4Reader.java index 0d896368..ceafe6b0 100644 --- a/stable/org.simantics.modelica/src/org/simantics/modelica/data/Mat4Reader.java +++ b/stable/org.simantics.modelica/src/org/simantics/modelica/data/Mat4Reader.java @@ -108,7 +108,14 @@ public class Mat4Reader { } } - public static Map read(InputStream in) throws IOException { + /** + * + * @param in InputStream of a mat file + * @param outputInterval the interval of results to be read. 1 reads all time steps, 2 reads every other time step, etc... + * @return + * @throws IOException + */ + public static Map read(InputStream in, int outputInterval) throws IOException { Mat4Reader reader = new Mat4Reader(in); reader.readMatrix(); // Header StringMatrix names = (StringMatrix)reader.readMatrix(); // Variable names @@ -131,13 +138,17 @@ public class Mat4Reader { for(int i=0;i 0 ? sc-1 : 1-sc; //System.out.println("i=" + i + ", sc=" + sc + ", c=" + c); - for(int j=0;j= values.columns) adjusted = values.columns - 1; + v[j] = valueData[rows * adjusted + c]; + } if(sc < 0) for(int j=0;j valueMap = null; try { - valueMap = Mat4Reader.read(stream); + valueMap = Mat4Reader.read(stream, interval); } catch (IOException e) { e.printStackTrace(); return; diff --git a/stable/org.simantics.modelica/src/org/simantics/modelica/data/SimulationResult.java b/stable/org.simantics.modelica/src/org/simantics/modelica/data/SimulationResult.java index 725d7e71..a21a2155 100644 --- a/stable/org.simantics.modelica/src/org/simantics/modelica/data/SimulationResult.java +++ b/stable/org.simantics.modelica/src/org/simantics/modelica/data/SimulationResult.java @@ -50,7 +50,7 @@ public class SimulationResult { protected List variables = new ArrayList(); protected List initials = new ArrayList(); - protected int numberOfTimeSteps = 0; + protected int numberOfLines = 0; /** * Private class used in displaying errors @@ -262,36 +262,50 @@ public class SimulationResult { } - final static Pattern p1 = Pattern.compile("DataSet: ([^ ]*)"); + + /** + * Read result file. Read all values in the result file. + * + * @param file result file + * @throws FileNotFoundException + * @throws IOException + */ + public void read(File file) throws FileNotFoundException, IOException { + read(file, 1); + } + /** * Read result file * * @param file result file + * @param interval the interval of results to be read. 1 reads all time steps, 2 reads every other time step, etc... * @throws FileNotFoundException * @throws IOException */ - public void read(File file) throws FileNotFoundException, IOException { + public void read(File file, int interval) throws FileNotFoundException, IOException { // First check the number of time steps FileReader fr = new FileReader(file); LineNumberReader lnr = new LineNumberReader(fr); lnr.skip(Long.MAX_VALUE); - numberOfTimeSteps = lnr.getLineNumber() - 1; // minus the first row, which is for names + numberOfLines = lnr.getLineNumber() - 1; // minus the first row, which is for names lnr.close(); fr.close(); InputStream is = new FileInputStream(file); - read(is); + read(is, interval); is.close(); } - + final static Pattern p1 = Pattern.compile("DataSet: ([^ ]*)"); + /** * Read result file. The basic implementation supports * plt-formatted results. Overridden to support other formats. * @param stream FileInputStream for the result file + * @param interval the interval of results to be read. 1 reads all time steps, 2 reads every other time step, etc... */ - public void read(InputStream stream) { + public void read(InputStream stream, int outputInterval) { while(true) { String line = getLine(stream); if(line == null) @@ -307,8 +321,8 @@ public class SimulationResult { return; String name = matcher.group(1); - double[] dtimes = new double[numberOfTimeSteps]; - double[] dvalues = new double[numberOfTimeSteps]; + double[] dtimes = new double[numberOfLines]; + double[] dvalues = new double[numberOfLines]; int i = 0; while(true) { String line = getLine(stream); diff --git a/stable/org.simantics.sysdyn.ontology/graph.tg b/stable/org.simantics.sysdyn.ontology/graph.tg index efe3183fb9e739f23e8abc2e11d436338108becb..8185e6cc23e6ced9e888f380ad97437aa17e3792 100644 GIT binary patch literal 64358 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 Qhq)Fx*Ob#_$|>sq0PE!WQ2+n{ diff --git a/stable/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph b/stable/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph index 54086f8a..9584de37 100644 --- a/stable/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph +++ b/stable/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph @@ -33,6 +33,7 @@ SYSDYN.SharedModuleOntolofgy -- SYSDYN.SysdynModel.startTime --> L0.Double -- SYSDYN.SysdynModel.stopTime --> L0.Double -- SYSDYN.SysdynModel.simulationStepLength --> L0.Double -- SYSDYN.SysdynModel.outputInterval --> L0.Double -- SYSDYN.SysdynModel.tolerance --> L0.Double -- SYSDYN.SysdynModel.solver --> L0.String FL = : SYSDYN.SysdynModelicaFunctionLibrary +FL.minmax : SYSDYN.SysdynModelicaFunction + L0.HasDescription "Limits the result of an equation to between the given minimum and maximum values." + SYSDYN.SysdynModelicaFunction.modelicaFunctionCode """ input Real expression; + input Real minimum; + input Real maximum; + output Real result; +algorithm + result := min(maximum, max(minimum, expression));""" + FL.xidz : SYSDYN.SysdynModelicaFunction L0.HasDescription "X if divided by zero" diff --git a/stable/org.simantics.sysdyn.ontology/src/org/simantics/sysdyn/SysdynResource.java b/stable/org.simantics.sysdyn.ontology/src/org/simantics/sysdyn/SysdynResource.java index d4ebd8ce..79f98c2a 100644 --- a/stable/org.simantics.sysdyn.ontology/src/org/simantics/sysdyn/SysdynResource.java +++ b/stable/org.simantics.sysdyn.ontology/src/org/simantics/sysdyn/SysdynResource.java @@ -51,6 +51,7 @@ public class SysdynResource { public final Resource Built$in_Functions_Vensim_Functions_ZIDZ; public final Resource Built$in_Functions_interpolate; public final Resource Built$in_Functions_interpolateFull; + public final Resource Built$in_Functions_minmax; public final Resource Built$in_Functions_xidz; public final Resource Built$in_Functions_zidz; public final Resource Center; @@ -284,6 +285,8 @@ public class SysdynResource { public final Resource SysdynModel; public final Resource SysdynModel_outputInterval; public final Resource SysdynModel_outputInterval_Inverse; + public final Resource SysdynModel_simulationStepLength; + public final Resource SysdynModel_simulationStepLength_Inverse; public final Resource SysdynModel_solver; public final Resource SysdynModel_solver_Inverse; public final Resource SysdynModel_startTime; @@ -401,6 +404,7 @@ public class SysdynResource { public static final String Built$in_Functions_Vensim_Functions_ZIDZ = "http://www.simantics.org/Sysdyn-1.1/Built-in%20Functions/Vensim%20Functions/ZIDZ"; public static final String Built$in_Functions_interpolate = "http://www.simantics.org/Sysdyn-1.1/Built-in%20Functions/interpolate"; public static final String Built$in_Functions_interpolateFull = "http://www.simantics.org/Sysdyn-1.1/Built-in%20Functions/interpolateFull"; + public static final String Built$in_Functions_minmax = "http://www.simantics.org/Sysdyn-1.1/Built-in%20Functions/minmax"; public static final String Built$in_Functions_xidz = "http://www.simantics.org/Sysdyn-1.1/Built-in%20Functions/xidz"; public static final String Built$in_Functions_zidz = "http://www.simantics.org/Sysdyn-1.1/Built-in%20Functions/zidz"; public static final String Center = "http://www.simantics.org/Sysdyn-1.1/Center"; @@ -634,6 +638,8 @@ public class SysdynResource { public static final String SysdynModel = "http://www.simantics.org/Sysdyn-1.1/SysdynModel"; public static final String SysdynModel_outputInterval = "http://www.simantics.org/Sysdyn-1.1/SysdynModel/outputInterval"; public static final String SysdynModel_outputInterval_Inverse = "http://www.simantics.org/Sysdyn-1.1/SysdynModel/outputInterval/Inverse"; + public static final String SysdynModel_simulationStepLength = "http://www.simantics.org/Sysdyn-1.1/SysdynModel/simulationStepLength"; + public static final String SysdynModel_simulationStepLength_Inverse = "http://www.simantics.org/Sysdyn-1.1/SysdynModel/simulationStepLength/Inverse"; public static final String SysdynModel_solver = "http://www.simantics.org/Sysdyn-1.1/SysdynModel/solver"; public static final String SysdynModel_solver_Inverse = "http://www.simantics.org/Sysdyn-1.1/SysdynModel/solver/Inverse"; public static final String SysdynModel_startTime = "http://www.simantics.org/Sysdyn-1.1/SysdynModel/startTime"; @@ -761,6 +767,7 @@ public class SysdynResource { Built$in_Functions_Vensim_Functions_ZIDZ = getResourceOrNull(graph, URIs.Built$in_Functions_Vensim_Functions_ZIDZ); Built$in_Functions_interpolate = getResourceOrNull(graph, URIs.Built$in_Functions_interpolate); Built$in_Functions_interpolateFull = getResourceOrNull(graph, URIs.Built$in_Functions_interpolateFull); + Built$in_Functions_minmax = getResourceOrNull(graph, URIs.Built$in_Functions_minmax); Built$in_Functions_xidz = getResourceOrNull(graph, URIs.Built$in_Functions_xidz); Built$in_Functions_zidz = getResourceOrNull(graph, URIs.Built$in_Functions_zidz); Center = getResourceOrNull(graph, URIs.Center); @@ -994,6 +1001,8 @@ public class SysdynResource { SysdynModel = getResourceOrNull(graph, URIs.SysdynModel); SysdynModel_outputInterval = getResourceOrNull(graph, URIs.SysdynModel_outputInterval); SysdynModel_outputInterval_Inverse = getResourceOrNull(graph, URIs.SysdynModel_outputInterval_Inverse); + SysdynModel_simulationStepLength = getResourceOrNull(graph, URIs.SysdynModel_simulationStepLength); + SysdynModel_simulationStepLength_Inverse = getResourceOrNull(graph, URIs.SysdynModel_simulationStepLength_Inverse); SysdynModel_solver = getResourceOrNull(graph, URIs.SysdynModel_solver); SysdynModel_solver_Inverse = getResourceOrNull(graph, URIs.SysdynModel_solver_Inverse); SysdynModel_startTime = getResourceOrNull(graph, URIs.SysdynModel_startTime); diff --git a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/Configuration.java b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/Configuration.java index c874e042..d50bfbc2 100644 --- a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/Configuration.java +++ b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/Configuration.java @@ -13,6 +13,7 @@ package org.simantics.sysdyn.ui.browser.contributions; import java.util.ArrayList; import java.util.Collection; +import java.util.TreeMap; import org.simantics.browsing.ui.graph.contributor.viewpoint.ViewpointContributor; import org.simantics.db.ReadGraph; @@ -22,7 +23,6 @@ import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.variable.Variable; import org.simantics.db.layer0.variable.Variables; import org.simantics.layer0.Layer0; -import org.simantics.modeling.ui.modelBrowser2.model.SheetsNode; import org.simantics.spreadsheet.resource.SpreadsheetResource; import org.simantics.sysdyn.SysdynResource; import org.simantics.sysdyn.ui.browser.nodes.BookNode; @@ -31,6 +31,7 @@ import org.simantics.sysdyn.ui.browser.nodes.EnumerationNode; import org.simantics.sysdyn.ui.browser.nodes.InputNode; import org.simantics.sysdyn.ui.browser.nodes.ModuleNode; import org.simantics.sysdyn.ui.browser.nodes.VariableNode; +import org.simantics.utils.strings.AlphanumComparator; public class Configuration extends ViewpointContributor> { @@ -44,37 +45,45 @@ public class Configuration extends ViewpointContributor variables = new ArrayList(); - ArrayList inputs = new ArrayList(); - ArrayList modules = new ArrayList(); - ArrayList enumerations = new ArrayList(); + TreeMap variables = new TreeMap(AlphanumComparator.CASE_INSENSITIVE_COMPARATOR); + TreeMap inputs = new TreeMap(AlphanumComparator.CASE_INSENSITIVE_COMPARATOR); + TreeMap modules = new TreeMap(AlphanumComparator.CASE_INSENSITIVE_COMPARATOR); + TreeMap enumerations = new TreeMap(AlphanumComparator.CASE_INSENSITIVE_COMPARATOR); + for(Variable child : variable.browseChildren(graph)) { Resource represents = (Resource)child.getPropertyValue(graph, Variables.REPRESENTS); if(graph.isInstanceOf(represents, sr.IndependentVariable)) { - variables.add(child); + variables.put(child.getName(graph), child); } else if (graph.isInstanceOf(represents, sr.Input)) { - inputs.add(child); + inputs.put(child.getName(graph), child); } else if (graph.isInstanceOf(represents, sr.Module)) { - modules.add(child); + modules.put(child.getName(graph), child); } else if (graph.isInstanceOf(represents, sr.Enumeration)) { - enumerations.add(child); + enumerations.put(child.getName(graph), child); } } - for (Variable v : variables) { + for (String s : variables.keySet()) { + Variable v = variables.get(s); Resource represents = (Resource)v.getPropertyValue(graph, Variables.REPRESENTS); result.add(new VariableNode(v, represents)); } - for (Variable v : inputs) { + + for (String s : inputs.keySet()) { + Variable v = inputs.get(s); Resource represents = (Resource)v.getPropertyValue(graph, Variables.REPRESENTS); result.add(new InputNode(v, represents)); } - for (Variable v : modules) { + + for (String s : modules.keySet()) { + Variable v = modules.get(s); Resource represents = (Resource)v.getPropertyValue(graph, Variables.REPRESENTS); result.add(new ModuleNode(v, represents)); } - for (Variable v : enumerations) { + + for (String s : enumerations.keySet()) { + Variable v = enumerations.get(s); Resource represents = (Resource)v.getPropertyValue(graph, Variables.REPRESENTS); result.add(new EnumerationNode(v, represents)); } diff --git a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/FunctionLibraries.java b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/FunctionLibraries.java index 87151394..5cd6bbee 100644 --- a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/FunctionLibraries.java +++ b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/FunctionLibraries.java @@ -13,12 +13,14 @@ package org.simantics.sysdyn.ui.browser.contributions; import java.util.ArrayList; import java.util.Collection; +import java.util.TreeMap; import org.simantics.browsing.ui.common.node.AbstractNode; import org.simantics.browsing.ui.graph.contributor.viewpoint.ViewpointContributor; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.request.ObjectsWithType; +import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.layer0.Layer0; import org.simantics.sysdyn.SysdynResource; @@ -26,6 +28,7 @@ import org.simantics.sysdyn.ui.browser.nodes.FunctionLibraryNode; import org.simantics.sysdyn.ui.browser.nodes.FunctionNode; import org.simantics.sysdyn.ui.browser.nodes.FunctionsFolder; import org.simantics.sysdyn.ui.browser.nodes.SharedFunctionsFolder; +import org.simantics.utils.strings.AlphanumComparator; public class FunctionLibraries extends ViewpointContributor { @@ -38,16 +41,23 @@ public class FunctionLibraries extends ViewpointContributor { ArrayList> result = new ArrayList>(); - // Find model functions + TreeMap sortResult = new TreeMap(AlphanumComparator.CASE_INSENSITIVE_COMPARATOR); + // Find and sort model functions for(Resource function : graph.syncRequest(new ObjectsWithType(functionsFolder.data, l0.ConsistsOf, sr.SysdynModelicaFunction))) { - result.add(new FunctionNode(function)); + sortResult.put(NameUtils.getSafeName(graph, function), function); } + for(Resource function : sortResult.values()) + result.add(new FunctionNode(function)); - // Find model function libraries + // Find and sort model function libraries + sortResult.clear(); for(Resource functionLibrary : graph.syncRequest(new ObjectsWithType(functionsFolder.data, l0.ConsistsOf, sr.SysdynModelicaFunctionLibrary))) { - result.add(new FunctionLibraryNode(functionLibrary)); - } - + sortResult.put(NameUtils.getSafeName(graph, functionLibrary), functionLibrary); + } + for(Resource functionLibrary : sortResult.values()) + result.add(new FunctionLibraryNode(functionLibrary)); + + // Find built-in functions Resource sysdyn = graph.getPossibleResource("http://www.simantics.org/Sysdyn-1.1"); if(sysdyn != null) { for(Resource library : graph.syncRequest(new ObjectsWithType(sysdyn, l0.ConsistsOf, sr.SysdynModelicaFunctionLibrary))) { diff --git a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/LibraryFunctions.java b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/LibraryFunctions.java index c37ea465..6e3a1a1f 100644 --- a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/LibraryFunctions.java +++ b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/LibraryFunctions.java @@ -13,17 +13,20 @@ package org.simantics.sysdyn.ui.browser.contributions; import java.util.ArrayList; import java.util.Collection; +import java.util.TreeMap; import org.simantics.browsing.ui.common.node.AbstractNode; import org.simantics.browsing.ui.graph.contributor.viewpoint.ViewpointContributor; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.request.ObjectsWithType; +import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.layer0.Layer0; import org.simantics.sysdyn.SysdynResource; import org.simantics.sysdyn.ui.browser.nodes.FunctionLibraryNode; import org.simantics.sysdyn.ui.browser.nodes.FunctionNode; +import org.simantics.utils.strings.AlphanumComparator; public class LibraryFunctions extends ViewpointContributor> { @@ -32,16 +35,28 @@ public class LibraryFunctions extends ViewpointContributor library) throws DatabaseException { Layer0 l0 = Layer0.getInstance(graph); ArrayList> result = new ArrayList>(); + + TreeMap sortResult = new TreeMap(AlphanumComparator.CASE_INSENSITIVE_COMPARATOR); + + // Find and sort functions in library for(Resource function : graph.syncRequest(new ObjectsWithType(library.data, l0.ConsistsOf, SysdynResource.getInstance(graph).SysdynModelicaFunction))) { - result.add(new FunctionNode(function)); + sortResult.put(NameUtils.getSafeName(graph, function), function); } + for(Resource function : sortResult.values()) + result.add(new FunctionNode(function)); + + // Find and sort libraries in library + sortResult.clear(); for(Resource functionLibrary : graph.syncRequest(new ObjectsWithType(library.data, l0.ConsistsOf, SysdynResource.getInstance(graph).SysdynModelicaFunctionLibrary))) { - result.add(new FunctionLibraryNode(functionLibrary)); + sortResult.put(NameUtils.getSafeName(graph, functionLibrary), functionLibrary); } + for(Resource functionLibrary : sortResult.values()) + result.add(new FunctionLibraryNode(functionLibrary)); + return result; } - + @Override public String getViewpointId() { return "Standard"; diff --git a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ModuleType.java b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ModuleType.java index ed5a9ead..4ebe335e 100644 --- a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ModuleType.java +++ b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ModuleType.java @@ -13,9 +13,11 @@ package org.simantics.sysdyn.ui.browser.contributions; import java.util.ArrayList; import java.util.Collection; +import java.util.TreeMap; import org.simantics.browsing.ui.common.node.AbstractNode; import org.simantics.browsing.ui.graph.contributor.viewpoint.ViewpointContributor; +import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.request.ObjectsWithType; @@ -29,6 +31,7 @@ import org.simantics.sysdyn.ui.browser.nodes.InputNode; import org.simantics.sysdyn.ui.browser.nodes.ModuleNode; import org.simantics.sysdyn.ui.browser.nodes.ModuleTypeNode; import org.simantics.sysdyn.ui.browser.nodes.VariableNode; +import org.simantics.utils.strings.AlphanumComparator; public class ModuleType extends ViewpointContributor { @@ -44,18 +47,39 @@ public class ModuleType extends ViewpointContributor { if(instance == null) return result; Resource conf = graph.getSingleObject(instance, str.IsDefinedBy); + + // Independent variables + TreeMap variables = new TreeMap(AlphanumComparator.CASE_INSENSITIVE_COMPARATOR); for(Resource r : graph.syncRequest(new ObjectsWithType(conf, l0.ConsistsOf, sr.IndependentVariable))) { - result.add(new VariableNode(r)); + variables.put((String)graph.getPossibleRelatedValue(r, l0.HasName, Bindings.STRING), r); } + for(String key : variables.keySet()) + result.add(new VariableNode(variables.get(key))); + + // Inputs + variables.clear(); for(Resource r : graph.syncRequest(new ObjectsWithType(conf, l0.ConsistsOf, sr.Input))) { - result.add(new InputNode(r)); + variables.put((String)graph.getPossibleRelatedValue(r, l0.HasName, Bindings.STRING), r); } + for(String key : variables.keySet()) + result.add(new InputNode(variables.get(key))); + + // Modules + variables.clear(); for(Resource r : graph.syncRequest(new ObjectsWithType(conf, l0.ConsistsOf, sr.Module))) { - result.add(new ModuleNode(r)); + variables.put((String)graph.getPossibleRelatedValue(r, l0.HasName, Bindings.STRING), r); } + for(String key : variables.keySet()) + result.add(new ModuleNode(variables.get(key))); + + // Enumerations + variables.clear(); for(Resource r : graph.syncRequest(new ObjectsWithType(conf, l0.ConsistsOf, sr.Enumeration))) { - result.add(new EnumerationNode(r)); + variables.put((String)graph.getPossibleRelatedValue(r, l0.HasName, Bindings.STRING), r); } + for(String key : variables.keySet()) + result.add(new EnumerationNode(variables.get(key))); + return result; } diff --git a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/SysdynComponentCopyAdvisor.java b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/SysdynComponentCopyAdvisor.java index 62672a39..94e3a9f0 100644 --- a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/SysdynComponentCopyAdvisor.java +++ b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/SysdynComponentCopyAdvisor.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.simantics.sysdyn.ui.editor.participant; +import java.util.Map; + import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; @@ -26,10 +28,10 @@ import org.simantics.modeling.mapping.ComponentCopyAdvisor; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.utils.datastructures.BinaryFunction; -public class SysdynComponentCopyAdvisor extends ComponentCopyAdvisor{ +public class SysdynComponentCopyAdvisor extends ComponentCopyAdvisor { @Override - public Object copy(ISynchronizationContext context, WriteGraph graph, Resource source, Resource sourceContainer, Resource targetContainer) throws DatabaseException { + public Object copy(ISynchronizationContext context, WriteGraph graph, Resource source, Resource sourceContainer, Resource targetContainer, Map map) throws DatabaseException { BinaryFunction tester = new BinaryFunction() { @@ -56,7 +58,7 @@ public class SysdynComponentCopyAdvisor extends ComponentCopyAdvisor{ StructuralResource2 sr2 = StructuralResource2.getInstance(graph); if(graph.isInstanceOf(source, sr2.Connection)) { - copy = CopyAdvisorUtil.copy(graph, source, tester); + copy = CopyAdvisorUtil.copy(graph, source, tester, map); } else { copy = CopyAdvisorUtil.copy4(graph, source); } diff --git a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/menu/PlaybackSliderContribution.java b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/menu/PlaybackSliderContribution.java index aae0b0ef..27e3b551 100644 --- a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/menu/PlaybackSliderContribution.java +++ b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/menu/PlaybackSliderContribution.java @@ -61,17 +61,16 @@ public class PlaybackSliderContribution extends ToolBarContributionItem { return; spe = (SysdynPlaybackExperiment)experiment; - Double[] numbers = new Double[3]; + Double[] numbers = new Double[2]; try { numbers = SimanticsUI.getSession().syncRequest(new Read() { @Override public Double[] perform(ReadGraph graph) throws DatabaseException { - Double[] numbers = new Double[3]; + Double[] numbers = new Double[2]; Resource model = spe.getModel(); SysdynResource sr = SysdynResource.getInstance(graph); numbers[0] = graph.getRelatedValue(model, sr.SysdynModel_startTime); numbers[1] = graph.getRelatedValue(model, sr.SysdynModel_stopTime); - numbers[2] = graph.getPossibleRelatedValue(model, sr.SysdynModel_outputInterval); return numbers; } }); diff --git a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/ConfigurationTab.java b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/ConfigurationTab.java index f0d9b324..96dea950 100644 --- a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/ConfigurationTab.java +++ b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/ConfigurationTab.java @@ -120,12 +120,22 @@ public class ConfigurationTab extends LabelPropertyTabContributor { label = new Label(composite, SWT.NONE); label.setText("Step length\n(empty = default)"); + TrackedText stepLength = new TrackedText(composite, support, SWT.BORDER | SWT.RIGHT); + stepLength.setTextFactory(new DoublePropertyFactory(SysdynResource.URIs.SysdynModel_simulationStepLength)); + stepLength.addModifyListener(new DoublePropertyModifier(context, SysdynResource.URIs.SysdynModel_simulationStepLength)); + stepLength.setInputValidator(new DoubleValidator()); + GridDataFactory.fillDefaults().hint(200, SWT.DEFAULT).applyTo(stepLength.getWidget()); + + label = new Label(composite, SWT.NONE); + label.setText("Output interval\n(empty = all steps)"); + TrackedText outputInterval = new TrackedText(composite, support, SWT.BORDER | SWT.RIGHT); outputInterval.setTextFactory(new DoublePropertyFactory(SysdynResource.URIs.SysdynModel_outputInterval)); outputInterval.addModifyListener(new DoublePropertyModifier(context, SysdynResource.URIs.SysdynModel_outputInterval)); outputInterval.setInputValidator(new DoubleValidator()); GridDataFactory.fillDefaults().hint(200, SWT.DEFAULT).applyTo(outputInterval.getWidget()); + label = new Label(composite, SWT.NONE); label.setText("Method"); diff --git a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/EquationTab.java b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/EquationTab.java index e27c80de..58fb0eb0 100644 --- a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/EquationTab.java +++ b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/EquationTab.java @@ -72,6 +72,7 @@ import org.simantics.sysdyn.ui.properties.widgets.ExpressionTypes.ExpressionType import org.simantics.sysdyn.ui.properties.widgets.ExpressionWidget; import org.simantics.sysdyn.ui.properties.widgets.IsOutputWidget; import org.simantics.sysdyn.ui.properties.widgets.ShortcutTabWidget; +import org.simantics.sysdyn.ui.properties.widgets.arrays.NameAndArrayRangeModifyListener; import org.simantics.sysdyn.ui.properties.widgets.expressions.ExpressionWidgetInput; import org.simantics.sysdyn.ui.properties.widgets.factories.VariableNameValidator; import org.simantics.ui.SimanticsUI; @@ -182,6 +183,8 @@ public class EquationTab extends LabelPropertyTabContributor implements Widget { expressionWidget = new ExpressionWidget(expressionComposite, expressionSupport, SWT.NONE); expressionWidget.setVariableTable(shortcutTabWidget.getVariableTable()); + arrayEquationCombo.addModifyListener(new NameAndArrayRangeModifyListener(support, expressionWidget, (ArrayExpressionCombo)arrayEquationCombo)); + addListeners(context); } diff --git a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/ArrayExpressionCombo.java b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/ArrayExpressionCombo.java index 5994a0c5..bab2c395 100644 --- a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/ArrayExpressionCombo.java +++ b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/ArrayExpressionCombo.java @@ -2,33 +2,23 @@ package org.simantics.sysdyn.ui.properties.widgets; import java.util.ArrayList; import java.util.Iterator; -import java.util.Map; -import java.util.StringTokenizer; import java.util.LinkedHashMap; +import java.util.Map; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.simantics.browsing.ui.swt.widgets.TrackedCombo; -import org.simantics.browsing.ui.swt.widgets.impl.ComboModifyListenerImpl; import org.simantics.browsing.ui.swt.widgets.impl.ReadFactoryImpl; -import org.simantics.browsing.ui.swt.widgets.impl.TrackedModifyEvent; -import org.simantics.browsing.ui.swt.widgets.impl.Widget; import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; -import org.simantics.db.Session; -import org.simantics.db.VirtualGraph; -import org.simantics.db.WriteGraph; -import org.simantics.db.common.request.WriteRequest; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.common.utils.OrderedSetUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.management.ISessionContext; import org.simantics.db.procedure.Listener; -import org.simantics.db.service.VirtualGraphSupport; import org.simantics.layer0.Layer0; import org.simantics.sysdyn.SysdynResource; -import org.simantics.sysdyn.ui.utils.VariableNameUtils; public class ArrayExpressionCombo extends TrackedCombo { @@ -110,7 +100,6 @@ public class ArrayExpressionCombo extends TrackedCombo { } }); - this.addModifyListener(new NameAndArrayRangeModifyListener(support)); } @@ -185,92 +174,18 @@ public class ArrayExpressionCombo extends TrackedCombo { sb.append("]"); return sb.toString(); } - - private class NameAndArrayRangeModifyListener extends ComboModifyListenerImpl implements Widget { - - Resource lastExpression; - - public NameAndArrayRangeModifyListener(WidgetSupport support) { - support.register(this); - } - - @Override - public void setInput(ISessionContext context, Object input) { - super.setInput(context, input); - } - - @Override - public void modifyText(TrackedModifyEvent e) { - - Combo combo = (Combo)e.getWidget(); - LinkedHashMap data = (LinkedHashMap) combo.getData(); - - Resource expression = (Resource) data.get(combo.getText()); - if(expression != null) { - lastExpression = expression; - lastSelectedIndex = combo.getSelectionIndex(); - } else { - for(Object key : data.keySet()) { - int index = lastSelectedIndex < 0 ? 0 : lastSelectedIndex; - if((Integer)combo.getData((String)key) == index) { - lastExpression = (Resource) data.get((String)key); - break; - } - } - } - - super.modifyText(e); - } - - @Override - public void applyText(WriteGraph graph, final Resource variable, String text) - throws DatabaseException { - StringTokenizer st = new StringTokenizer(text, "[]"); - final String newName = st.nextToken(); - String range = null; - if(st.hasMoreTokens()) { - range = st.nextToken(); - } - String originalName = graph.getRelatedValue(variable, Layer0.getInstance(graph).HasName); - if(!originalName.equals(newName)) { - VariableNameUtils.renameInEquations(graph, variable, originalName, newName); - graph.claimLiteral(variable, Layer0.getInstance(graph).HasName, newName); - } - - SysdynResource sr = SysdynResource.getInstance(graph); - - if(range != null && lastExpression != null) { - String oldRange = graph.getPossibleRelatedValue(lastExpression, sr.Expression_arrayRange); - if(oldRange == null || !range.equals(oldRange)) { - graph.claimLiteral(lastExpression, sr.Expression_arrayRange, "[" + range + "]"); - } - } else if (range == null && lastExpression != null && graph.hasStatement(lastExpression, sr.Expression_arrayRange)) { - graph.deny(lastExpression, sr.Expression_arrayRange); - } - - Resource activeExpression = graph.getPossibleObject(variable, sr.IndependentVariable_activeExpression); - - if(lastExpression != null && !lastExpression.equals(activeExpression)) { - VirtualGraphSupport support = graph.getService(VirtualGraphSupport.class); - final Session session = graph.getSession(); - session.asyncRequest(new WriteRequest(support.getWorkspacePersistent("expressions")) { - @Override - public void perform(WriteGraph graph) throws DatabaseException { - VirtualGraph runtime = graph.getService(VirtualGraph.class); - session.asyncRequest(new WriteRequest(runtime) { - @Override - public void perform(WriteGraph graph) throws DatabaseException { - SysdynResource sr = SysdynResource.getInstance(graph); - if(graph.hasStatement(variable, sr.IndependentVariable_activeExpression)) - graph.deny(variable, sr.IndependentVariable_activeExpression); - graph.claim(variable, sr.IndependentVariable_activeExpression, lastExpression); - } - } - ); - } - }); - } - } + + /** + * Get the index that has previously been selected + * @return + */ + public int getLastSelectedIndex() { + return lastSelectedIndex; + } + + public void setLastSelectedIndex(int lastSelectedIndex) { + this.lastSelectedIndex = lastSelectedIndex; } + } diff --git a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/arrays/NameAndArrayRangeModifyListener.java b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/arrays/NameAndArrayRangeModifyListener.java new file mode 100644 index 00000000..2d814007 --- /dev/null +++ b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/arrays/NameAndArrayRangeModifyListener.java @@ -0,0 +1,146 @@ +package org.simantics.sysdyn.ui.properties.widgets.arrays; + +import java.util.LinkedHashMap; +import java.util.StringTokenizer; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.widgets.Combo; +import org.simantics.Simantics; +import org.simantics.browsing.ui.swt.widgets.impl.ComboModifyListenerImpl; +import org.simantics.browsing.ui.swt.widgets.impl.TrackedModifyEvent; +import org.simantics.browsing.ui.swt.widgets.impl.Widget; +import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.Session; +import org.simantics.db.VirtualGraph; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.management.ISessionContext; +import org.simantics.db.request.Read; +import org.simantics.db.service.VirtualGraphSupport; +import org.simantics.layer0.Layer0; +import org.simantics.sysdyn.SysdynResource; +import org.simantics.sysdyn.ui.properties.widgets.ArrayExpressionCombo; +import org.simantics.sysdyn.ui.properties.widgets.ExpressionWidget; +import org.simantics.sysdyn.ui.utils.VariableNameUtils; +import org.simantics.utils.ui.ISelectionUtils; + +/** + * Modification listener ONLY for ArrayExpressionCombos in EquationTabs. + * @author Teemu Lempinen + * + */ +public class NameAndArrayRangeModifyListener extends ComboModifyListenerImpl implements Widget { + + Resource lastExpression; + ExpressionWidget expressionWidget; + ArrayExpressionCombo arrayExpressionCombo; + Object lastInput; + + public NameAndArrayRangeModifyListener(WidgetSupport support, ExpressionWidget expressionWidget, ArrayExpressionCombo arrayExpressionCombo) { + support.register(this); + this.expressionWidget = expressionWidget; + this.arrayExpressionCombo = arrayExpressionCombo; + } + + @Override + public void setInput(ISessionContext context, Object input) { + super.setInput(context, input); + this.lastInput = input; + } + + public void modifyText(TrackedModifyEvent e) { + Combo combo = (Combo)e.getWidget(); + LinkedHashMap data = (LinkedHashMap) combo.getData(); + + Resource activeExpression = null; + try { + final Object input = lastInput; + activeExpression = Simantics.getSession().syncRequest(new Read() { + @Override + public Resource perform(ReadGraph graph) + throws DatabaseException { + Resource variable = ISelectionUtils.filterSingleSelection((ISelection)input, Resource.class); + return graph.getPossibleObject(variable, SysdynResource.getInstance(graph).IndependentVariable_activeExpression); + } + + }); + } catch (DatabaseException e1) { + e1.printStackTrace(); + } + + + Resource expression = (Resource) data.get(combo.getText()); + if(expression != null) { + lastExpression = expression; + arrayExpressionCombo.setLastSelectedIndex(combo.getSelectionIndex()); + } else { + for(Object key : data.keySet()) { + int index = arrayExpressionCombo.getLastSelectedIndex() < 0 ? 0 : arrayExpressionCombo.getLastSelectedIndex(); + if((Integer)combo.getData((String)key) == index) { + lastExpression = (Resource) data.get((String)key); + break; + } + } + } + + // If expression has changed (i.e. user actually selects a different item in the combo), save the previous + if(lastExpression != null && !lastExpression.equals(activeExpression)) { + expressionWidget.save(); + } + + super.modifyText(e); + } + + @Override + public void applyText(WriteGraph graph, final Resource variable, String text) + throws DatabaseException { + StringTokenizer st = new StringTokenizer(text, "[]"); + final String newName = st.nextToken(); + String range = null; + if(st.hasMoreTokens()) { + range = st.nextToken(); + } + String originalName = graph.getRelatedValue(variable, Layer0.getInstance(graph).HasName); + if(!originalName.equals(newName)) { + VariableNameUtils.renameInEquations(graph, variable, originalName, newName); + graph.claimLiteral(variable, Layer0.getInstance(graph).HasName, newName); + } + + SysdynResource sr = SysdynResource.getInstance(graph); + + if(range != null && lastExpression != null) { + String oldRange = graph.getPossibleRelatedValue(lastExpression, sr.Expression_arrayRange); + if(oldRange == null || !range.equals(oldRange)) { + graph.claimLiteral(lastExpression, sr.Expression_arrayRange, "[" + range + "]"); + } + } else if (range == null && lastExpression != null && graph.hasStatement(lastExpression, sr.Expression_arrayRange)) { + graph.deny(lastExpression, sr.Expression_arrayRange); + } + + Resource activeExpression = graph.getPossibleObject(variable, sr.IndependentVariable_activeExpression); + + if(lastExpression != null && !lastExpression.equals(activeExpression)) { + VirtualGraphSupport support = graph.getService(VirtualGraphSupport.class); + final Session session = graph.getSession(); + session.asyncRequest(new WriteRequest(support.getWorkspacePersistent("expressions")) { + @Override + public void perform(WriteGraph graph) throws DatabaseException { + VirtualGraph runtime = graph.getService(VirtualGraph.class); + session.asyncRequest(new WriteRequest(runtime) { + @Override + public void perform(WriteGraph graph) throws DatabaseException { + SysdynResource sr = SysdynResource.getInstance(graph); + if(graph.hasStatement(variable, sr.IndependentVariable_activeExpression)) + graph.deny(variable, sr.IndependentVariable_activeExpression); + graph.claim(variable, sr.IndependentVariable_activeExpression, lastExpression); + } + }); + } + }); + } + } + +} diff --git a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/structure/ModuleStructureGraphRequest.java b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/structure/ModuleStructureGraphRequest.java index 4724a0d5..c32a3004 100644 --- a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/structure/ModuleStructureGraphRequest.java +++ b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/structure/ModuleStructureGraphRequest.java @@ -24,6 +24,8 @@ import org.simantics.graphviz.Edge; import org.simantics.graphviz.Graph; import org.simantics.graphviz.Node; import org.simantics.layer0.Layer0; +import org.simantics.modeling.ModelingResources; +import org.simantics.simulation.ontology.SimulationResource; import org.simantics.sysdyn.SysdynResource; /** @@ -61,13 +63,21 @@ public class ModuleStructureGraphRequest implements Read { // Model root was not found, return empty graph if(model == null) return g; + + // Find the parent module/model of the selected resource + Resource parentResource = graph.getPossibleObject(resource, l0.PartOf); + if(graph.isInstanceOf(parentResource, sr.ConfigurationDiagram)) { + parentResource = graph.getPossibleObject(parentResource, ModelingResources.getInstance(graph).DiagramToComposite); + } else if(graph.isInstanceOf(parentResource, sr.SysdynModel)) { + parentResource = graph.getPossibleObject(model, SimulationResource.getInstance(graph).HasConfiguration); + } // Set root node Node rootNode = new Node(g, NameUtils.getSafeLabel(graph, model)); rootNode.setShape("rectangle"); HashSet visited = new HashSet(); visited.add(model); - findChildModules(g, rootNode, graph, model, visited); + findChildModules(g, rootNode, graph, model, parentResource, visited); return g; @@ -79,17 +89,23 @@ public class ModuleStructureGraphRequest implements Read { * @param parent Parent module or model * @param graph ReadGraph * @param resource Module type or model + * @param parent2 * @param visited All visited modules. Needed to check for loops in the structure. Loops are not allowed. * @throws DatabaseException */ - private void findChildModules(Graph g, Node parent, ReadGraph graph, Resource resource, HashSet visited) throws DatabaseException { + private void findChildModules(Graph g, Node parent, ReadGraph graph, Resource resource, Resource parentResource, HashSet visited) throws DatabaseException { SysdynResource sr = SysdynResource.getInstance(graph); Layer0 l0 = Layer0.getInstance(graph); + Resource configuration = graph.syncRequest(new PossibleObjectWithType(resource, l0.ConsistsOf, sr.Configuration)); + + // Set the parent color different, if it is the parent of the selected resource + if(configuration.equals(parentResource)) + parent.setColor("#ff8c00"); + HashMap modules = new HashMap(); // Find all module children - Resource configuration = graph.syncRequest(new PossibleObjectWithType(resource, l0.ConsistsOf, sr.Configuration)); for(Resource m : graph.getObjects(configuration, l0.ConsistsOf)) { Resource type = graph.getPossibleObject(m, l0.InstanceOf); if(graph.isInheritedFrom(type, sr.Module)) { @@ -120,8 +136,9 @@ public class ModuleStructureGraphRequest implements Read { node.setFontColor("#FF0000"); continue; } else { - visited.add(type); - findChildModules(g, node, graph, type, visited); + HashSet copy = new HashSet(visited); + copy.add(type); + findChildModules(g, node, graph, type, parentResource, copy); } } diff --git a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ExpressionUtils.java b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ExpressionUtils.java index 6773376d..fa3d62ab 100644 --- a/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ExpressionUtils.java +++ b/stable/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ExpressionUtils.java @@ -43,6 +43,7 @@ import org.simantics.sysdyn.representation.Configuration; import org.simantics.sysdyn.representation.Enumeration; import org.simantics.sysdyn.representation.EnumerationIndex; import org.simantics.sysdyn.representation.IElement; +import org.simantics.sysdyn.representation.Model; import org.simantics.sysdyn.representation.Module; import org.simantics.sysdyn.representation.Sheet; import org.simantics.sysdyn.representation.Variable; @@ -184,6 +185,14 @@ public class ExpressionUtils { for(int i = 0; i < parts.length && current != null; i++) { current = getElement(current, parts[i]); } + + if(current == null) { + // Sheets are currently located in the model root. Try to find the sheet. + current = conf.getModuleType().getParent(); // Get module type parent (should be a model) + if(current instanceof Model) + current = getElement(((Model)current).getModelConfiguration(), parts[0]); // Try to get the sheet + } + if(current != null && current instanceof Sheet) { Sheet sheet = (Sheet) current; String e = ef.getExpression(); diff --git a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/VariableRVIUtils.java b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/VariableRVIUtils.java index 62118077..63143b0e 100644 --- a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/VariableRVIUtils.java +++ b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/VariableRVIUtils.java @@ -59,7 +59,7 @@ public class VariableRVIUtils { * @throws DatabaseException */ private static void traverseIndexes(ReadGraph g, String rvi, HashMap rvis, List arrayIndexes) throws DatabaseException { - traverseIndexes(g, rvi, rvis, arrayIndexes, arrayIndexes.get(0), "", ""); + traverseIndexes(g, rvi, rvis, arrayIndexes, 0, "", ""); } /** @@ -76,10 +76,10 @@ public class VariableRVIUtils { * @param indexNamesSoFar String representation of the indexes so far in name format * @throws DatabaseException */ - private static void traverseIndexes(ReadGraph g, String rvi, HashMap rvis, List arrayIndexes, Resource currentEnumeration, String indexesSoFar, String indexNamesSoFar) throws DatabaseException { + private static void traverseIndexes(ReadGraph g, String rvi, HashMap rvis, List arrayIndexes, int currentIndex, String indexesSoFar, String indexNamesSoFar) throws DatabaseException { SysdynResource sr = SysdynResource.getInstance(g); // Enumeration indexes of the current enumeration (e.g. the first EnumIndexes in Var[EnumIndexes, EnumIndexes, EnumIndexes]) - Resource enumerationIndexes = g.getPossibleObject(currentEnumeration, sr.Enumeration_enumerationIndexes); + Resource enumerationIndexes = g.getPossibleObject(arrayIndexes.get(currentIndex), sr.Enumeration_enumerationIndexes); if(enumerationIndexes == null) return; List indexes = OrderedSetUtils.toList(g, enumerationIndexes); @@ -87,12 +87,11 @@ public class VariableRVIUtils { Boolean b = g.getPossibleRelatedValue(indexes.get(i), sr.EnumerationIndex_showEnumerationIndexInCharts, Bindings.BOOLEAN); // If this index is not wanted to be shown in charts, the recursion does not go any further and rvis.put() is not called for this enumeration if(Boolean.TRUE.equals(b)) { - int arrayIndex = arrayIndexes.indexOf(currentEnumeration); // Get the name of the index String name = g.getRelatedValue(indexes.get(i), Layer0.getInstance(g).HasName); - if(arrayIndex < arrayIndexes.size() - 1) + if(currentIndex < arrayIndexes.size() - 1) // If there are still more EnumIndexes, recursively call the function and add current index to indexesSoFar and indexNamesSoFar - traverseIndexes(g, rvi, rvis, arrayIndexes, arrayIndexes.get(arrayIndex + 1), + traverseIndexes(g, rvi, rvis, arrayIndexes, currentIndex + 1, indexesSoFar + (i + 1) +",", indexNamesSoFar + (name) +","); else { // The last enumeration. Add [rvi[1, 1, 1] = rvi[index1, index1, index1]}and so on to the rvis map @@ -129,7 +128,7 @@ public class VariableRVIUtils { // Find all redeclarations for(Resource redeclaration : graph.syncRequest(new ObjectsWithType(module, sr.Module_redeclaration, sr.Redeclaration))) { Resource replaced = graph.getSingleObject(redeclaration, sr.Redeclaration_replacedEnumeration); - if(enumerations.contains(replaced)) { + while(enumerations.contains(replaced)) { // Replace the redelcared enumeration in enumerations -list with the replacing enumeration enumerations.add(enumerations.indexOf(replaced), graph.getSingleObject(redeclaration, sr.Redeclaration_replacingEnumeration)); enumerations.remove(replaced); diff --git a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/HistoryDatasetResult.java b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/HistoryDatasetResult.java index 9b0d515c..17d8bac1 100644 --- a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/HistoryDatasetResult.java +++ b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/HistoryDatasetResult.java @@ -49,9 +49,9 @@ import org.simantics.utils.datastructures.Pair; */ public class HistoryDatasetResult extends SimulationResult { @Override - public void read(File file) {} // Do nothing + public void read(File file, int interval) {} // Do nothing @Override - public void read(InputStream stream) {} // Do nothing + public void read(InputStream stream, int interval) {} // Do nothing private boolean disposed; diff --git a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynExperiment.java b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynExperiment.java index c82649c3..1c1852b1 100644 --- a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynExperiment.java +++ b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynExperiment.java @@ -196,17 +196,22 @@ public class SysdynExperiment extends Experiment implements IDynamicExperiment { Model model = configuration.getModel(); Double startTime = model.getStartTime(); Double stopTime = model.getStopTime(); - Double numberOfIntervals = model.getOutputInterval(); inits.put(SysdynInitKeys.START_VALUE, startTime.toString()); inits.put(SysdynInitKeys.STOP_VALUE, stopTime.toString()); String outputFormat = "\"mat\""; inits.put(SysdynInitKeys.OUTPUT_FORMAT, outputFormat); - if(numberOfIntervals != null) { - inits.put(SysdynInitKeys.STEP_VALUE, numberOfIntervals.toString()); - inits.put(SysdynInitKeys.NUMBER_OF_INTERVALS, "" + ((int)((stopTime - startTime) / numberOfIntervals))); + + Double simulationStepLength = model.getSimulationStepLength(); + if(simulationStepLength != null) { + inits.put(SysdynInitKeys.STEP_VALUE, simulationStepLength.toString()); + inits.put(SysdynInitKeys.NUMBER_OF_INTERVALS, "" + ((int)((stopTime - startTime) / simulationStepLength))); } else { inits.put(SysdynInitKeys.STEP_VALUE, "" + (stopTime - startTime) / 500); } + + Double outputInterval = model.getOutputInterval(); + inits.put(SysdynInitKeys.OUTPUT_INTERVAL, outputInterval != null ? outputInterval.toString() : inits.get(SysdynInitKeys.STEP_VALUE)); + String method = "\"" + model.getSolver() + "\""; inits.put(SysdynInitKeys.METHOD, method); if(model.getTolerance() != null) @@ -251,7 +256,7 @@ public class SysdynExperiment extends Experiment implements IDynamicExperiment { ); ModelicaManager.printProcessOutput(process, monitor); - Thread resultThread = getResultThread(simulationLocation, monitor, progressMonitor); + Thread resultThread = getResultThread(simulationLocation, inits, monitor, progressMonitor); resultThread.run(); process = null; @@ -260,11 +265,12 @@ public class SysdynExperiment extends Experiment implements IDynamicExperiment { /** * Get a thread for reading and saving reuslts from a normal simulation * @param simulationLocation + * @param inits * @param monitor * @param progressMonitor * @return */ - protected Thread getResultThread(final SimulationLocation simulationLocation, final IModelicaMonitor monitor, final IProgressMonitor progressMonitor) { + protected Thread getResultThread(final SimulationLocation simulationLocation, final HashMap inits, final IModelicaMonitor monitor, final IProgressMonitor progressMonitor) { return new Thread() { @Override public void run() { @@ -282,7 +288,23 @@ public class SysdynExperiment extends Experiment implements IDynamicExperiment { result = new SimulationResult(); else result = new MatSimulationResult(); // The latest format - result.read(simulationLocation.outputFile); + + + // The interval of results saved. Every result? Every other result? etc... + int outIntervalInt = 1; + String outputInterval = inits.get(SysdynInitKeys.OUTPUT_INTERVAL); + if(outputInterval != null) { + String stepTime = inits.get(SysdynInitKeys.STEP_VALUE); + + Double step = Double.parseDouble(stepTime); + Double outInterval = Double.parseDouble(outputInterval); + + double nearest = roundUp(outInterval, step); + outIntervalInt = (int)(nearest / step); + if(outIntervalInt <= 0) outIntervalInt = 1; + } + + result.read(simulationLocation.outputFile, outIntervalInt); result.readInits(simulationLocation.initFile); result.filter(); sysdynModel.getSysdynResult().setResult(result); @@ -305,6 +327,17 @@ public class SysdynExperiment extends Experiment implements IDynamicExperiment { } }; } + + protected static double roundUp(double numToRound, double multiple) { + if(multiple == 0) { + return numToRound; + } + double remainder = numToRound % multiple; + if (remainder == 0) { + return numToRound; + } + return numToRound + multiple - remainder; + } /** @@ -319,7 +352,7 @@ public class SysdynExperiment extends Experiment implements IDynamicExperiment { protected static SimulationLocation createSimulationFiles(SysdynModel sysdynModel, String modelText, HashMap inits, String additionalScript, boolean fmu) throws IOException { return ModelicaManager.createSimulationFiles( sysdynModel.getSimulationDir(), - sysdynModel.getConfiguration().getName(), + sysdynModel.getConfiguration().getLabel(), modelText, inits, additionalScript, @@ -340,11 +373,11 @@ public class SysdynExperiment extends Experiment implements IDynamicExperiment { protected void storeImportantInits(HashMap inits) { previousImportantInits.clear(); - previousImportantInits.put("start value", inits.get(SysdynInitKeys.START_VALUE)); - previousImportantInits.put("stop value", inits.get(SysdynInitKeys.STOP_VALUE)); - previousImportantInits.put("method", inits.get(SysdynInitKeys.METHOD)); - previousImportantInits.put("outputFormat", inits.get(SysdynInitKeys.OUTPUT_FORMAT)); - previousImportantInits.put("variableFilter", inits.get(SysdynInitKeys.VARIABLE_FILTER)); + previousImportantInits.put(SysdynInitKeys.START_VALUE, inits.get(SysdynInitKeys.START_VALUE)); + previousImportantInits.put(SysdynInitKeys.STOP_VALUE, inits.get(SysdynInitKeys.STOP_VALUE)); + previousImportantInits.put(SysdynInitKeys.METHOD, inits.get(SysdynInitKeys.METHOD)); + previousImportantInits.put(SysdynInitKeys.OUTPUT_FORMAT, inits.get(SysdynInitKeys.OUTPUT_FORMAT)); + previousImportantInits.put(SysdynInitKeys.VARIABLE_FILTER, inits.get(SysdynInitKeys.VARIABLE_FILTER)); } /** diff --git a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynGameExperiment.java b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynGameExperiment.java index 8d82b12f..f5918430 100644 --- a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynGameExperiment.java +++ b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynGameExperiment.java @@ -33,7 +33,7 @@ import org.simantics.modelica.fmi.FMUControlJNI; import org.simantics.modelica.fmi.FMUJNIException; import org.simantics.simulation.experiment.ExperimentState; import org.simantics.sysdyn.SysdynResource; -import org.simantics.utils.datastructures.Pair; +import org.simantics.utils.datastructures.Triple; /** * Game experiment @@ -47,9 +47,11 @@ public class SysdynGameExperiment extends SysdynExperiment { private HashMap> results; private double stepLength = DEFAULT_STEP_LENGTH; private double stepDuration = DEFAULT_STEP_DURATION; + private int savePer = 1; public static double DEFAULT_STEP_DURATION = 1.0; public static double DEFAULT_STEP_LENGTH = 0.1; + public static int DEFAULT_OUTPUT_INTERVAL = 1; public SysdynGameExperiment(Resource experiment, Resource model) { super(experiment, model); @@ -71,6 +73,11 @@ public class SysdynGameExperiment extends SysdynExperiment { this.stepLength = stepLength; } + public void setOutputInterval(int interval) { + this.savePer = interval; + } + + public FMUControlJNI getFMUControl() { return control; } @@ -84,23 +91,33 @@ public class SysdynGameExperiment extends SysdynExperiment { results = new HashMap>(); - g.asyncRequest(new Read>() { + g.asyncRequest(new Read>() { @Override - public Pair perform(ReadGraph graph) + public Triple perform(ReadGraph graph) throws DatabaseException { SysdynResource sr = SysdynResource.getInstance(graph); Double stepDuration = graph.getPossibleRelatedValue(experiment, sr.GameExperiment_stepDuration); - Double stepLength =graph.getPossibleRelatedValue(experiment, sr.GameExperiment_stepLength); - return new Pair(stepDuration, stepLength); + Double stepLength = graph.getPossibleRelatedValue(experiment, sr.GameExperiment_stepLength); + Double outputInterval = graph.getPossibleRelatedValue(model, sr.SysdynModel_outputInterval); + return new Triple(stepDuration, stepLength, outputInterval); } - }, new AsyncListener>() { + }, new AsyncListener>() { @Override public void execute(AsyncReadGraph graph, - Pair result) { + Triple result) { setStepDuration(result.first != null ? result.first : DEFAULT_STEP_DURATION); setStepLength(result.second != null ? result.second : DEFAULT_STEP_LENGTH); + + if(result.third == null) { + setOutputInterval(DEFAULT_OUTPUT_INTERVAL); + } else { + double nearest = roundUp(result.third, getStepLength()); + int interval = (int)(nearest / getStepLength()); + if(interval <= 0) interval = 1; + setOutputInterval(interval); + } } @Override @@ -108,6 +125,7 @@ public class SysdynGameExperiment extends SysdynExperiment { throwable.printStackTrace(); setStepDuration(DEFAULT_STEP_DURATION); setStepLength(DEFAULT_STEP_LENGTH); + setOutputInterval(DEFAULT_OUTPUT_INTERVAL); } @Override @@ -219,7 +237,6 @@ public class SysdynGameExperiment extends SysdynExperiment { @Override protected IStatus run(IProgressMonitor monitor) { - int nSteps = (int)(duration / stepLength); int work = 1 + nSteps * 3 + 2; // initialization + number of steps * number of phases per step + set result + call result listeners @@ -241,20 +258,26 @@ public class SysdynGameExperiment extends SysdynExperiment { double[] results = new double[subscription.length]; monitor.worked(1); - + + int stepNumber = 1; while(control.getTime() < (eTime - 1e-9)) { // Substract a very small number, because OpenModelica is not very precise with its Real numbers monitor.subTask("Simulate step (time = " + control.getTime() + ")"); control.simulateStep(); monitor.worked(1); - monitor.subTask("Get results (time = " + control.getTime() + ")"); - results = control.getSubscribedResults(results); - monitor.worked(1); - - monitor.subTask("Save results (time = " + control.getTime() + ")"); - for(int k = 0; k < subscription.length; k++) { - SysdynGameExperiment.this.results.get(subscription[k]).add(results[k]); + if(stepNumber % savePer == 0) { + monitor.subTask("Get results (time = " + control.getTime() + ")"); + results = control.getSubscribedResults(results); + monitor.worked(1); + + monitor.subTask("Save results (time = " + control.getTime() + ")"); + for(int k = 0; k < subscription.length; k++) { + SysdynGameExperiment.this.results.get(subscription[k]).add(results[k]); + } + } else { + monitor.worked(1); } + stepNumber++; monitor.worked(1); } @@ -268,7 +291,6 @@ public class SysdynGameExperiment extends SysdynExperiment { } catch (FMUJNIException e) { System.err.println("SysdynGameExperiment simulateDuration failed: \n\t" + e.getMessage()); } - return Status.OK_STATUS; } } diff --git a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynInitKeys.java b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynInitKeys.java index 3d405ebd..65168bcb 100644 --- a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynInitKeys.java +++ b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynInitKeys.java @@ -26,5 +26,6 @@ public class SysdynInitKeys { public static String METHOD = "method"; public static String TOLERANCE = "tolerance"; public static String VARIABLE_FILTER = "variableFilter"; + public static String OUTPUT_INTERVAL = "outputInterval"; } diff --git a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynModel.java b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynModel.java index 740ae5a2..3912a70e 100644 --- a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynModel.java +++ b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynModel.java @@ -213,10 +213,6 @@ public class SysdynModel implements IMappingListener, IModel { e.printStackTrace(); } - // Add the current result if there is one - if(getSysdynResult() != null) - activeResults.add(0, getSysdynResult() ); - return activeResults; } @@ -224,6 +220,10 @@ public class SysdynModel implements IMappingListener, IModel { @Override public void execute(ArrayList result) { + // Add the current result if there is one + if(result != null && getSysdynResult() != null) + result.add(0, getSysdynResult() ); + activeResults = result; resultChanged(); } @@ -497,7 +497,7 @@ public class SysdynModel implements IMappingListener, IModel { public File getSimulationDir() { if(simulationDir == null) { File modelsDir = Activator.getBundleContext().getDataFile("models"); - String configName = configuration.getName(); + String configName = configuration.getLabel(); List files = Arrays.asList(modelsDir.list()); if (files.contains(configName)) { int i = 2; diff --git a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynPlaybackExperiment.java b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynPlaybackExperiment.java index 5c8f955c..45cebeab 100644 --- a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynPlaybackExperiment.java +++ b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynPlaybackExperiment.java @@ -232,7 +232,6 @@ public class SysdynPlaybackExperiment extends SysdynExperiment implements IDynam SysdynResource sr = SysdynResource.getInstance(graph); numbers[0] = graph.getRelatedValue(model, sr.SysdynModel_startTime); numbers[1] = graph.getRelatedValue(model, sr.SysdynModel_stopTime); - numbers[2] = graph.getPossibleRelatedValue(model, sr.SysdynModel_outputInterval); PlaybackConfiguration config = new PlaybackConfiguration(); config.simulationDuration = numbers[1] - numbers[0] - time; diff --git a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/modelica/ModelicaWriter.java b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/modelica/ModelicaWriter.java index ab41ec5f..ea24ec70 100644 --- a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/modelica/ModelicaWriter.java +++ b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/modelica/ModelicaWriter.java @@ -50,12 +50,32 @@ public class ModelicaWriter { b.append(" parameter Integer size;\n"); b.append(" parameter Integer elements[:];\n"); b.append("end Enumeration_class;\n\n"); - + for(Configuration conf : configurations) - writeConfiguration(conf, b); + writeConfiguration(conf, "SpreadSheetBook", b); + + b.append(getGlobalSpreadSheets(configurations)); return b.toString(); } + + /** + * + */ + private static String getGlobalSpreadSheets(Collection configurations) { + StringBuilder sheets = new StringBuilder(); + for(Configuration conf : configurations) { + if(conf.getModel() != null) { + for(IElement e : conf.getElements()) { + if(e instanceof Book) { + return ((Book)e).getBook(); + } + } + } + } + + return sheets.toString(); + } /** * Write a single configuration to a given string builder @@ -63,7 +83,7 @@ public class ModelicaWriter { * @param configuration Model or module configuration * @param b String builder */ - private static void writeConfiguration(Configuration configuration, StringBuilder b) { + private static void writeConfiguration(Configuration configuration, String spreadSheetClass, StringBuilder b) { String app; boolean game = RepresentationUtils.isGameExperimentActive(); @@ -77,7 +97,6 @@ public class ModelicaWriter { ArrayList inputDependencies = new ArrayList(); ArrayList outputDependencies = new ArrayList(); HashMap> moduleInputs = new HashMap>(); - Book book = null; // Initialize lists for(IElement element : configuration.getElements()) { @@ -112,9 +131,6 @@ public class ModelicaWriter { // References from child modules inputDependencies.add(dependency); } - } else if (element instanceof Book) { - // Spreadsheet book - book = (Book)element; } } @@ -125,8 +141,15 @@ public class ModelicaWriter { // If the configuration is model configuration, use model name. Otherwise, use configuration name. ModuleType mt = configuration.getModuleType(); - String className = mt != null ? (mt.getName().replace(" ", "")) : (configuration.getName().replace(" ", "")); - b.append("class ").append(className).append('\n'); + String className = mt != null ? (mt.getName().replace(" ", "")) : (configuration.getLabel().replace(" ", "")); + + b.append("class ").append(className); + + // Extend spreadsheetclass to get spreadsheet cell info + if(spreadSheetClass != null && !spreadSheetClass.isEmpty()) + b.append("\n extends " + spreadSheetClass + ";"); + + b.append("\n"); b.append("// Variable definitions\n"); for(IndependentVariable variable : variables) { @@ -164,11 +187,6 @@ public class ModelicaWriter { } } - if(book != null) { - b.append("// Spreadsheet definition\n"); - b.append(book.markBook()); - } - boolean initialEquations = false; for(Stock stock : stocks) { app = stock.getInitialEquation(); @@ -221,12 +239,6 @@ public class ModelicaWriter { b.append("end ").append(className).append(";\n\n"); - - // Update sheet definitions to contain the elements that were used. - if(book != null) { - int s = b.indexOf(book.markBook()); - b.replace(s, s + book.markBook().length(), book.getBook()); - } } /** diff --git a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Book.java b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Book.java index c4621a7e..031d4f43 100644 --- a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Book.java +++ b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Book.java @@ -31,9 +31,16 @@ public class Book extends Variable { public String getBook() { StringBuilder book = new StringBuilder(); + + book.append("partial class SpreadSheetBook\n"); + for(Sheet sheet : sheets) book.append(sheet.getStringRepresentation()); + + book.append("end SpreadSheetBook;"); + return book.toString(); + } } diff --git a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Configuration.java b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Configuration.java index f13b4044..00d0efb3 100644 --- a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Configuration.java +++ b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Configuration.java @@ -75,7 +75,7 @@ public class Configuration { } public String getLabel() { - return label; + return label != null ? label : name; } public ModuleType getModuleType() { diff --git a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Model.java b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Model.java index dbda4ea3..6e22190f 100644 --- a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Model.java +++ b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Model.java @@ -16,8 +16,10 @@ import java.util.HashMap; import org.simantics.layer0.Layer0; import org.simantics.objmap.annotations.GraphType; +import org.simantics.objmap.annotations.RelatedElement; import org.simantics.objmap.annotations.RelatedElements; import org.simantics.objmap.annotations.RelatedValue; +import org.simantics.simulation.ontology.SimulationResource; import org.simantics.sysdyn.SysdynResource; import org.simantics.sysdyn.representation.annotations.BuiltinFunctions; @@ -37,6 +39,9 @@ public class Model { @RelatedValue(SysdynResource.URIs.SysdynModel_outputInterval) private Double outputInterval; + + @RelatedValue(SysdynResource.URIs.SysdynModel_simulationStepLength) + private Double stepLength; @RelatedValue(SysdynResource.URIs.SysdynModel_tolerance) private Double tolerance; @@ -46,6 +51,9 @@ public class Model { @RelatedValue(SysdynResource.URIs.SysdynModel_variableFilter) private String variableFilter; + + @RelatedElement(SimulationResource.URIs.HasConfiguration) + private Configuration configuration; @RelatedElements( value = Layer0.URIs.ConsistsOf, @@ -78,13 +86,20 @@ public class Model { } /** - * Result output interval + * Simulation output interval * @return */ public Double getOutputInterval() { return outputInterval; } + /** + * Simulation step length + * @return + */ + public Double getSimulationStepLength() { + return stepLength; + } /** * * @return Tolerance for simulation engine @@ -176,4 +191,13 @@ public class Model { public FunctionLibrary getBuiltins() { return builtins; } + + + /** + * + * @return Configuration of this model + */ + public Configuration getModelConfiguration() { + return configuration; + } } diff --git a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Sheet.java b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Sheet.java index 24106097..71a3b756 100644 --- a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Sheet.java +++ b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Sheet.java @@ -66,7 +66,7 @@ public class Sheet extends org.simantics.sysdyn.representation.Variable { final StringBuilder clazz = new StringBuilder(); final HashSet possibleRanges = new HashSet(); - clazz.append(" class " + name + "_class\n "); + clazz.append(" class " + name + "_class\n"); // Write all doubles that have been used in expressions int counter = 0; @@ -83,7 +83,7 @@ public class Sheet extends org.simantics.sysdyn.representation.Variable { if(value instanceof Double) { Double d = (Double)value; - clazz.append("constant Real " + key + " = " + d + "; "); + clazz.append(" constant Real " + key + " = " + d + "; "); counter++; if(counter > 10) { counter = 0; @@ -116,7 +116,7 @@ public class Sheet extends org.simantics.sysdyn.representation.Variable { if(array[0].length > 1) { // Has two dimensions - clazz.append("constant Real[" + array.length + ", " + array[0].length + "] "); + clazz.append(" constant Real[" + array.length + ", " + array[0].length + "] "); clazz.append(possibleRange.replace(":", "_") + " = {"); for(int i = 0; i < array.length; i++) { clazz.append("{"); @@ -132,7 +132,7 @@ public class Sheet extends org.simantics.sysdyn.representation.Variable { clazz.append("};\n"); } else { // Has one dimension - clazz.append("constant Real[" + array.length + "] "); + clazz.append(" constant Real[" + array.length + "] "); clazz.append(possibleRange.replace(":", "_") + " = {"); for(int i = 0; i < array.length; i++) { clazz.append(array[i][0]); @@ -151,7 +151,7 @@ public class Sheet extends org.simantics.sysdyn.representation.Variable { } - clazz.append("\n end " + name + "_class;\n"); + clazz.append(" end " + name + "_class;\n"); clazz.append(" " + name + "_class " + name + ";\n"); return clazz.toString(); } @@ -163,7 +163,7 @@ public class Sheet extends org.simantics.sysdyn.representation.Variable { Variant cell = matrix.get(i, j); if(cell.getBinding().equals(Bindings.DOUBLE)) { array[i][j] = (Double)cell.getValue(); - } else if (cell.getBinding().equals(Bindings.MUTABLE_STRING)) { + } else if (cell.getBinding().equals(Bindings.MUTABLE_STRING) || (cell.getBinding().equals(Bindings.STRING))) { try { array[i][j] = Double.parseDouble(cell.getValue().toString()); } catch (NumberFormatException e) { diff --git a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Variability.java b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Variability.java index 7a5f165b..e12969f8 100644 --- a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Variability.java +++ b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/Variability.java @@ -71,7 +71,6 @@ public enum Variability { private static boolean isParameter(IndependentVariable variable, Configuration configuration, String reference, boolean allowVariables) { // Check if references are references to sheets or enumerations. // Sheet and Enumeration references are allowed, since sheets contain only constants / parameters - String r = reference.split("\\.")[0]; for(IElement element : configuration.getElements()) { if(element instanceof Module) { @@ -124,6 +123,27 @@ public enum Variability { } } } + + // Try to find sheet in another way: this might be a module type configuration. Find the model configuration and its sheet + if(configuration.getModuleType() != null) { + Object parent = configuration.getModuleType().getParent(); + if(parent != null && parent instanceof Model) { + configuration = ((Model)parent).getModelConfiguration(); + for(IElement element : configuration.getElements()) { + if(element instanceof Book) { + for(Sheet sheet : ((Book)element).getSheets()) { + if(r.equals(sheet.getName())) { + return true; + } + } + break; + } + } + } + } + + + // If there was no sheet for this reference name, or there was some other problem, return false return false; } diff --git a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/expressions/WithLookupExpression.java b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/expressions/WithLookupExpression.java index 15e34766..f40cc43e 100644 --- a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/expressions/WithLookupExpression.java +++ b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/expressions/WithLookupExpression.java @@ -17,6 +17,7 @@ import org.simantics.sysdyn.SysdynResource; import org.simantics.sysdyn.representation.IndependentVariable; import org.simantics.sysdyn.representation.utils.FormatUtils; import org.simantics.sysdyn.representation.utils.IndexUtils; +import org.simantics.sysdyn.representation.utils.SheetFormatUtils; /** * Representation of a withlookup expression @@ -38,7 +39,7 @@ public class WithLookupExpression extends Expression { String range = IndexUtils.rangeToIndexes(variable, this.getArrayRange()); return - " " + variable.getName() + (range.equals("[:]") ? "" : range) + " = interpolate(" + equation + ", " + lookupTable + ");\n"; + " " + variable.getName() + (range.equals("[:]") ? "" : range) + " = interpolate(" + equation + ", " + SheetFormatUtils.reformatSheetReferences(variable, lookupTable) + ");\n"; } @Override diff --git a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/utils/IndexUtils.java b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/utils/IndexUtils.java index e2bfc7a0..053d146a 100644 --- a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/utils/IndexUtils.java +++ b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/utils/IndexUtils.java @@ -38,11 +38,11 @@ public class IndexUtils { } return result; } - - + + public static String rangeToIndexes(Variable variable, String range) { - if(variable == null) - return range; + if(variable == null) + return range; StringBuilder sb = new StringBuilder(); ArrayIndexes arrayIndexes = variable.getArrayIndexes(); if(arrayIndexes == null || range == null) @@ -66,61 +66,66 @@ public class IndexUtils { else sb.append(rangeToken); } - + } return sb.toString(); } - + private static String fixForRangeEnumerations(Variable variable, String equation) { - ExpressionParser parser = new ExpressionParser(new StringReader(equation)); - try { - parser.expr(); - for(ForRange forRange : parser.getForRanges()) { - if(forRange.start.equals(forRange.end)) { - Variable v = getVariable(variable.getParentConfiguration(), forRange.start.image); - if(v instanceof Enumeration) { - equation = equation.replaceAll("in[\\s]*" + forRange.start.image + "($|[^\\.])", "in " + forRange.start.image + ".elements$1"); - } - } - } - } catch (ParseException e) { + ExpressionParser parser = new ExpressionParser(new StringReader(equation)); + try { + parser.expr(); + for(ForRange forRange : parser.getForRanges()) { + if(forRange.start.equals(forRange.end)) { + Variable v = getVariable(variable.getParentConfiguration(), forRange.start.image); + if(v instanceof Enumeration) { + equation = equation.replaceAll("in[\\s]*" + forRange.start.image + "($|[^\\.])", "in " + forRange.start.image + ".elements$1"); + } + } + } + } catch (ParseException e) { e.printStackTrace(); } return equation; } - + public static String equationRangesToIndexes(Variable variable, String equation) { - if(equation == null || !equation.contains("[")) return equation; - - StringBuilder result = new StringBuilder(); - String delimiters = "+-*/(){}[],. "; - StringTokenizer st = new StringTokenizer(equation, delimiters, true); - String prevToken = st.nextToken(); - result.append(prevToken); - while (st.hasMoreTokens()) { - String nextToken = st.nextToken(); - if (nextToken.equals("[")) { - StringBuilder range = new StringBuilder(); - range.append("["); - String rangeToken = st.nextToken(); - while(!rangeToken.equals("]")) { - range.append(rangeToken); - rangeToken = st.nextToken(); - } - range.append("]"); - - Variable prevVar = getVariable(variable.getParentConfiguration(), prevToken.trim()); - result.append(rangeToIndexes(prevVar, range.toString())); - } else { - result.append(nextToken); - } - prevToken = nextToken; - } - - equation = fixForRangeEnumerations(variable, result.toString()); - return equation; + if(equation == null) return equation; + + if(equation.contains("[")) { + StringBuilder result = new StringBuilder(); + String delimiters = "+-*/(){}[],. "; + StringTokenizer st = new StringTokenizer(equation, delimiters, true); + String prevToken = st.nextToken(); + result.append(prevToken); + while (st.hasMoreTokens()) { + String nextToken = st.nextToken(); + if (nextToken.equals("[")) { + StringBuilder range = new StringBuilder(); + range.append("["); + String rangeToken = st.nextToken(); + while(!rangeToken.equals("]")) { + range.append(rangeToken); + rangeToken = st.nextToken(); + } + range.append("]"); + + Variable prevVar = getVariable(variable.getParentConfiguration(), prevToken.trim()); + result.append(rangeToIndexes(prevVar, range.toString())); + } else { + result.append(nextToken); + } + prevToken = nextToken; + } + equation = fixForRangeEnumerations(variable, result.toString()); + } else if(equation.contains("{")){ + // Cases where there are {something for i in Enum} without [] brackets + equation = fixForRangeEnumerations(variable, equation); + } + + return equation; } - + private static Variable getVariable(Configuration configuration, String name) { for(IElement element : configuration.getElements()) { if(element instanceof Variable) { diff --git a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/utils/SheetFormatUtils.java b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/utils/SheetFormatUtils.java index 918eccbb..19b14ce8 100644 --- a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/utils/SheetFormatUtils.java +++ b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/utils/SheetFormatUtils.java @@ -22,6 +22,8 @@ import org.simantics.sysdyn.expressionParser.ParseException; import org.simantics.sysdyn.expressionParser.Token; import org.simantics.sysdyn.representation.Configuration; import org.simantics.sysdyn.representation.IElement; +import org.simantics.sysdyn.representation.Model; +import org.simantics.sysdyn.representation.ModuleType; import org.simantics.sysdyn.representation.Sheet; import org.simantics.sysdyn.representation.Variable; @@ -38,6 +40,15 @@ public class SheetFormatUtils { for(String key : functionCalls.keySet()) { String[] parts = key.split("\\."); Object current = v.getParentConfiguration(); + + // Hack. Sheets can currently exist only in models, not in module types. + ModuleType moduleType= ((Configuration)current).getModuleType(); + if(moduleType != null) { + if(moduleType.getParent() != null && moduleType.getParent() instanceof Model) + current = ((Model)moduleType.getParent()).getModelConfiguration(); + } + // end Hack. + Object found = null; for(int i = 0; i < parts.length && current != null; i++) { found = null; @@ -62,7 +73,7 @@ public class SheetFormatUtils { String tmp = ""; int start = 0, end = 0, call = 0; String cellOrRange = null; - while((call = expression.indexOf(key, end)) >= 0) { + while((call = expression.indexOf(key + "(", end)) >= 0) { start = expression.indexOf("(", call); tmp += expression.substring(end, start); @@ -90,7 +101,7 @@ public class SheetFormatUtils { tmp += "." + cellOrRange; } tmp += expression.substring(end, expression.length()); - return tmp; + expression = tmp; } } diff --git a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/simulation/SimulationJob.java b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/simulation/SimulationJob.java index df6b06ae..ccb4d92e 100644 --- a/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/simulation/SimulationJob.java +++ b/stable/org.simantics.sysdyn/src/org/simantics/sysdyn/simulation/SimulationJob.java @@ -42,7 +42,7 @@ public class SimulationJob extends Job { IModelicaMonitor monitor; public SimulationJob(SysdynModel model, Experiment experiment) { - super("Simulate " + model.getConfiguration().getName()); + super("Simulate " + model.getConfiguration().getLabel()); this.model = model; this.experiment = experiment; if(PlatformUI.isWorkbenchRunning()) { @@ -55,8 +55,8 @@ public class SimulationJob extends Job { @Override protected IStatus run(IProgressMonitor monitor) { - monitor.beginTask("Simulate " + model.getConfiguration().getName(), 5); - this.monitor.message("Simulate " + model.getConfiguration().getName()); + monitor.beginTask("Simulate " + model.getConfiguration().getLabel(), 5); + this.monitor.message("Simulate " + model.getConfiguration().getLabel()); try { model.update(); if(experiment instanceof SysdynExperiment) -- 2.47.1