From 1004657fa12218e7d770c65a1462458889805c0a Mon Sep 17 00:00:00 2001 From: miettinen Date: Fri, 19 Oct 2012 10:29:43 +0000 Subject: [PATCH] * Optimized simulation result read: All results are still read from file in one chunk, but are processed further (made into recordings) only upon request. (refs #3709) * The Save result button makes recordings out of all variables that not yet have those. * Save result is run in its own job. * A few null-checks added. git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@26076 ac1ea38d-2e2b-0410-8846-a27921b304fc --- .../ui/properties/VariableInformationTab.java | 13 +- .../sysdyn/ui/utils/ExpressionUtils.java | 12 +- .../sysdyn/manager/SaveResultJob.java | 111 ++ .../sysdyn/manager/SysdynExperiment.java | 1450 ++++++++--------- .../sysdyn/manager/SysdynResult.java | 186 ++- 5 files changed, 978 insertions(+), 794 deletions(-) create mode 100644 org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SaveResultJob.java diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/VariableInformationTab.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/VariableInformationTab.java index d084ad4a..cbd30d5d 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/VariableInformationTab.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/VariableInformationTab.java @@ -15,7 +15,6 @@ import java.awt.Color; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; -import org.eclipse.jface.viewers.ISelection; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.graphics.Font; @@ -148,6 +147,7 @@ public class VariableInformationTab extends LabelPropertyTabContributor implemen Color color; Object input; + @Override public void beforeApply() { Triple result = null; @@ -157,7 +157,7 @@ public class VariableInformationTab extends LabelPropertyTabContributor implemen @Override public Triple perform(ReadGraph graph) throws DatabaseException { - Resource component = ISelectionUtils.filterSingleSelection((ISelection)input, Resource.class); + Resource component = ISelectionUtils.filterSingleSelection(input, Resource.class); String name = NameUtils.getSafeName(graph, component); Resource element = graph.getPossibleObject(component, ModelingResources.getInstance(graph).ComponentToElement); @@ -296,7 +296,14 @@ public class VariableInformationTab extends LabelPropertyTabContributor implemen @Override public void execute(final Pair result) { - final Display device = sample.getWidget().getDisplay(); + final Display device; + try { + device = sample.getWidget().getDisplay(); + } catch (SWTException e) { + // Widget is disposed, the GUI probably did'n function as fast as the user commanded. + // Thus do nothing. + return; + } device.asyncExec(new Runnable() { diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ExpressionUtils.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ExpressionUtils.java index 758b90f8..bc3ddcca 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ExpressionUtils.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ExpressionUtils.java @@ -546,11 +546,13 @@ public class ExpressionUtils { if(tlist != null) for(Token t : tlist) { StyledText st = ef.getSourceViewer().getTextWidget(); - int start = st.getOffsetAtLine(t.beginLine - 1) + t.beginColumn - 1; - int offset = st.getOffsetAtLine(t.endLine - 1) + t.endColumn - start; - positions.add(new Position( - start, - offset)); + if (st != null) { + int start = st.getOffsetAtLine(t.beginLine - 1) + t.beginColumn - 1; + int offset = st.getOffsetAtLine(t.endLine - 1) + t.endColumn - start; + positions.add(new Position( + start, + offset)); + } } if(result.keySet().contains(ef)) { result.get(ef).addAll((ArrayList)positions.clone()); diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SaveResultJob.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SaveResultJob.java new file mode 100644 index 00000000..6269318e --- /dev/null +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SaveResultJob.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 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.manager; + +import java.io.File; +import java.util.UUID; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.simantics.Simantics; +import org.simantics.db.Resource; +import org.simantics.db.Session; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.WriteResultRequest; +import org.simantics.db.common.utils.NameUtils; +import org.simantics.db.exception.DatabaseException; +import org.simantics.layer0.Layer0; +import org.simantics.layer0.utils.direct.GraphUtils; +import org.simantics.sysdyn.SysdynResource; + +/** + * Class for saving simulation results. + * @author Tuomas Miettinen + * + */ +public class SaveResultJob extends Job { + + private final SysdynResult sysdynResult; + private final SysdynExperiment experiment; + private final Session session; + private IProgressMonitor monitor; + + public SaveResultJob(final SysdynExperiment experiment, Session session, final SysdynResult result) { + super("Save Result"); + this.experiment = experiment; + this.sysdynResult = result; + this.session = session; + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + this.monitor = monitor; + int resultItemsNumber = sysdynResult.numberOfVariables(); + monitor.beginTask("Save result", resultItemsNumber * 2); + try { + File file = Simantics.getSession().syncRequest(new WriteResultRequest() { + + @Override + public File perform(WriteGraph graph) throws DatabaseException { + Layer0 l0 = Layer0.getInstance(graph); + Resource model = graph.getSingleObject(experiment.getResource(), l0.PartOf); + Resource project = graph.getSingleObject(model, l0.PartOf); + String projectName = graph.getPossibleRelatedValue(project, l0.HasName); + File root = new File(Platform.getLocation().toOSString(), "www.simantics.org"); + if(!root.isDirectory()) root.mkdir(); + File projectRoot = new File(root, projectName); + if(!projectRoot.isDirectory()) projectRoot.mkdir(); + File file = new File( projectRoot, UUID.randomUUID().toString() + ".dbb"); + + String name = NameUtils.findFreshName(graph, "Result", model, l0.ConsistsOf, "%s%d"); + + SysdynResource sr = SysdynResource.getInstance(graph); + Resource res = GraphUtils.create2(graph, sr.Result, + l0.HasLabel, name, + l0.HasName, name, + l0.PartOf, model, + sr.Result_resultFile, file.getAbsolutePath()); + graph.claim(experiment.getResource(), sr.Experiment_result, res); + + return file; + } + }); + // Start the saving operation + Thread saveThread = experiment.getSaveThread(sysdynResult, file, SaveResultJob.this.monitor); + saveThread.run(); + } catch (DatabaseException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + return new Status( + Status.ERROR, + "org.simantics.sysdyn.ui", + "Save results failed: \n" + e.getMessage()); + } + monitor.done(); + return Status.OK_STATUS; + } + + @Override + public boolean belongsTo(Object family) { + return "SaveResultJob".equals(family); + } + + @Override + protected void canceling() { + + } + +} diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynExperiment.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynExperiment.java index e6a04268..ccbbc841 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynExperiment.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynExperiment.java @@ -24,11 +24,9 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.UUID; import java.util.concurrent.locks.Lock; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.Platform; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Session; @@ -40,7 +38,6 @@ import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.service.VirtualGraphSupport; import org.simantics.layer0.Layer0; -import org.simantics.layer0.utils.direct.GraphUtils; import org.simantics.modelica.IModelicaMonitor; import org.simantics.modelica.ModelicaException; import org.simantics.modelica.ModelicaManager; @@ -55,7 +52,6 @@ import org.simantics.simulation.experiment.IDynamicExperiment; import org.simantics.simulation.experiment.IExperimentListener; import org.simantics.simulation.ontology.SimulationResource; import org.simantics.sysdyn.Activator; -import org.simantics.sysdyn.SysdynResource; import org.simantics.sysdyn.adapter.VariableValueSubscription; import org.simantics.sysdyn.modelica.ModelicaWriter; import org.simantics.sysdyn.representation.Configuration; @@ -64,337 +60,320 @@ import org.simantics.sysdyn.simulation.SimulationScheduler; public class SysdynExperiment extends Experiment implements IDynamicExperiment, VariableSubscriptionManager { - protected Session session; - protected Runnable modificationListener; - protected SysdynModel sysdynModel; - protected boolean toggled = false; - @SuppressWarnings("rawtypes") - protected THashSet variableValueSubscriptions = new THashSet(); - @SuppressWarnings("rawtypes") - protected volatile VariableValueSubscription[] variableValueSubscriptionsSnapshot = null; - - protected HashMap previousImportantInits = new HashMap(); - protected String previousModelStructure; - protected Process process; - protected boolean canceled = false; - protected ExperimentState sysdynExperimentState; - + protected Session session; + protected Runnable modificationListener; + protected SysdynModel sysdynModel; + protected boolean toggled = false; + @SuppressWarnings("rawtypes") + protected THashSet variableValueSubscriptions = new THashSet(); + @SuppressWarnings("rawtypes") + protected volatile VariableValueSubscription[] variableValueSubscriptionsSnapshot = null; + + protected HashMap previousImportantInits = new HashMap(); + protected String previousModelStructure; + protected Process process; + protected boolean canceled = false; + protected ExperimentState sysdynExperimentState; + private SysdynResult result; private File simulationDir; protected String experimentName; - public static SysdynExperiment INSTANCE; - - public SysdynExperiment(Resource experiment, Resource model) { - super(experiment, model); - INSTANCE = this; - } - - public static SysdynExperiment getInstance() { - return INSTANCE; - } - - public SysdynResult getCurrentResult() { - if(this.result == null) - this.result = new SysdynResult(); - return this.result; - } - - public Collection getActiveResults() { - ArrayList result = new ArrayList(); - if(getCurrentResult() != null) - result.add(getCurrentResult()); - result.addAll(sysdynModel.getDisplayedResults()); - return result; - } - - /** - * Initialize this experiment - * @param g ReadGraph - */ - public void init(ReadGraph g) { - try { + public static SysdynExperiment INSTANCE; + + public SysdynExperiment(Resource experiment, Resource model) { + super(experiment, model); + INSTANCE = this; + } + + public static SysdynExperiment getInstance() { + return INSTANCE; + } + + public SysdynResult getCurrentResult() { + if(this.result == null) + this.result = new SysdynResult(); + return this.result; + } + + public Collection getActiveResults() { + ArrayList result = new ArrayList(); + if(getCurrentResult() != null) + result.add(getCurrentResult()); + result.addAll(sysdynModel.getDisplayedResults()); + return result; + } + + /** + * Initialize this experiment + * @param g ReadGraph + */ + public void init(ReadGraph g) { + try { this.experimentName = NameUtils.getSafeName(g, experiment); } catch (DatabaseException e) { this.experimentName = "Experiment"; } - - this.session = g.getSession(); - state = ExperimentState.STOPPED; - for(IExperimentListener listener : listeners.getListeners()) - listener.stateChanged(state); - - session.asyncRequest(new ReadRequest() { - - @Override - public void run(ReadGraph graph) throws DatabaseException { - final Resource configuration = graph.getPossibleObject(model, SimulationResource.getInstance(graph).HasConfiguration); - sysdynModel = SysdynModelManager.getInstance(session).getModel(graph, configuration); - toggleActivation(graph, true); - } - }); - - setSysdynExperimentState(ExperimentState.INITIALIZING); - } - - @Override - public void saveState() { - if(result == null) return; - - try { - session.syncRequest(new WriteRequest() { - - @Override - public void perform(WriteGraph graph) throws DatabaseException { - Layer0 l0 = Layer0.getInstance(graph); - Resource model = graph.getSingleObject(experiment, l0.PartOf); - Resource project = graph.getSingleObject(model, l0.PartOf); - String projectName = graph.getPossibleRelatedValue(project, l0.HasName); - File root = new File(Platform.getLocation().toOSString(), "www.simantics.org"); - if(!root.isDirectory()) root.mkdir(); - File projectRoot = new File(root, projectName); - if(!projectRoot.isDirectory()) projectRoot.mkdir(); - File file = new File( projectRoot, UUID.randomUUID().toString() + ".dbb"); - - String name = NameUtils.findFreshName(graph, "Result", model, l0.ConsistsOf, "%s%d"); - - SysdynResource sr = SysdynResource.getInstance(graph); - Resource res = GraphUtils.create2(graph, sr.Result, - l0.HasLabel, name, - l0.HasName, name, - l0.PartOf, model, - sr.Result_resultFile, file.getAbsolutePath()); - graph.claim(experiment, sr.Experiment_result, res); - result.saveToFile(file); - } - }); - } catch (DatabaseException e) { - e.printStackTrace(); - } - } - - /** - * Starts a simulation job. Use this method to start a simulation. - */ - @Override - public void simulate(boolean enabled) { - if(enabled && sysdynModel != null) { - if(!ExperimentState.RUNNING.equals(getState())) { - changeState(ExperimentState.RUNNING); - startSimulationJob(); - } - } else if (!toggled){ - changeState(ExperimentState.STOPPED); - } - } - - /** - * Starts a simulation job - */ - protected void startSimulationJob() { - session.asyncRequest(new ReadRequest() { - - @Override - public void run(ReadGraph graph) throws DatabaseException { - SimulationScheduler.start(sysdynModel, SysdynExperiment.this); - } - }); - } - - - /** - * Write model configuration to a single Modelica code - * @param monitor - * @param isGame is the experiment a "game experiment". This is needed for modifying the code to work with FMU simulation - * @return Modelica code - */ - protected String getModelicaCode(IModelicaMonitor monitor, boolean isGame) { - String modelText = null; - try { - // Write all configurations once - modelText = ModelicaWriter.write(sysdynModel.getModules(), isGame); - } catch (Exception e) { - // Stop experiment and show console - /*setExperimentStopped(experiment); =>*/ simulate(false); - monitor.showConsole(); - monitor.message("Error when writing Modelica code."); - } - return modelText; - } - - /** - * Get all inits and parameter values required for the inits -file - * @param monitor - * @return All inits in a map - */ - protected HashMap getModelInits(IModelicaMonitor monitor) { - Configuration configuration = sysdynModel.getConfiguration(); - HashMap inits = sysdynModel.getInits(configuration, ""); - Model model = configuration.getModel(); - Double startTime = model.getStartTime(); - Double stopTime = model.getStopTime(); - inits.put(SysdynInitKeys.START_VALUE, startTime.toString()); - inits.put(SysdynInitKeys.STOP_VALUE, stopTime.toString()); - String outputFormat = "\"mat\""; - inits.put(SysdynInitKeys.OUTPUT_FORMAT, outputFormat); - - 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) - inits.put(SysdynInitKeys.TOLERANCE, model.getTolerance().toString()); - String variableFilter = model.getVariableFilter(); - if(variableFilter != null && !variableFilter.isEmpty()) - inits.put(SysdynInitKeys.VARIABLE_FILTER, variableFilter); - return inits; - } - - /** - * Builds a model - * @param simulationLocation - * @param inits - * @param modelText - * @param monitor - */ - protected void buildModel(SimulationLocation simulationLocation, HashMap inits, String modelText, IModelicaMonitor monitor) { - - try { - simulationLocation.simulatorFile.delete(); - ModelicaManager.buildModel(simulationLocation, monitor); - } catch (ModelicaException e) { - if(e.getMessage() != null) - monitor.message(e.getMessage()); - monitor.showConsole(); - canceled = true; - previousModelStructure = ""; - } - } - - /** - * Run a built model - * @throws IOException - */ - protected void runModelica(SimulationLocation simulationLocation, HashMap inits, String modelText, IModelicaMonitor monitor, IProgressMonitor progressMonitor) throws IOException { - progressMonitor.subTask("Simulate model"); - process = ModelicaManager.runModelica( - simulationLocation, - monitor, - inits - ); - ModelicaManager.printProcessOutput(process, monitor); - - Thread resultThread = getResultThread(simulationLocation, inits, monitor, progressMonitor); - resultThread.run(); - - process = null; - } - - /** - * 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 HashMap inits, final IModelicaMonitor monitor, final IProgressMonitor progressMonitor) { - return new Thread() { - @Override - public void run() { - try { - process.waitFor(); - - if(!canceled) { - // Get and store results - progressMonitor.worked(1); - progressMonitor.subTask("Read results"); - SimulationResult result; - if(simulationLocation.outputFile.getName().endsWith(".csv")) - result = new CSVSimulationResult(); - else if(simulationLocation.outputFile.getName().endsWith(".plt")) - result = new SimulationResult(); - else - result = new MatSimulationResult(); // The latest format - - - // 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(); - getCurrentResult().setResult(result); - progressMonitor.worked(1); - resultsChanged(); - - simulate(false); - - String errorString = result.getResultReadErrors(); - if(errorString != null && !errorString.isEmpty()) - monitor.message(errorString); - } - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }; - } - - 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; - } - - - /** - * - * @param sysdynModel - * @param modelText - * @param inits - * @param additionalScript - * @return - * @throws IOException - */ - protected SimulationLocation createSimulationFiles(SysdynModel sysdynModel, String modelText, HashMap inits, String additionalScript, boolean fmu) throws IOException { - File simulationDir = getSimulationDir(); - - // update all function files. both the model's and built-in - try { - final SysdynExperiment experiment = this; + + this.session = g.getSession(); + state = ExperimentState.STOPPED; + for(IExperimentListener listener : listeners.getListeners()) + listener.stateChanged(state); + + session.asyncRequest(new ReadRequest() { + + @Override + public void run(ReadGraph graph) throws DatabaseException { + final Resource configuration = graph.getPossibleObject(model, SimulationResource.getInstance(graph).HasConfiguration); + sysdynModel = SysdynModelManager.getInstance(session).getModel(graph, configuration); + toggleActivation(graph, true); + } + }); + + setSysdynExperimentState(ExperimentState.INITIALIZING); + } + + @Override + public void saveState() { + if(result == null) + return; + SaveResultJob saveResultJob = new SaveResultJob(SysdynExperiment.this, session, result); + saveResultJob.schedule(); + } + + protected Thread getSaveThread(final SysdynResult result, final File file, final IProgressMonitor progressMonitor) { + return new Thread() { + @Override + public void run() { + if(!canceled) { + // Get and store results result.saveToFile(file, progressMonitor); + } } + }; + } + + /** + * Starts a simulation job. Use this method to start a simulation. + */ + @Override + public void simulate(boolean enabled) { + if(enabled && sysdynModel != null) { + if(!ExperimentState.RUNNING.equals(getState())) { + changeState(ExperimentState.RUNNING); + startSimulationJob(); + } + } else if (!toggled){ + changeState(ExperimentState.STOPPED); + } + } + + /** + * Starts a simulation job + */ + protected void startSimulationJob() { + session.asyncRequest(new ReadRequest() { + + @Override + public void run(ReadGraph graph) throws DatabaseException { + SimulationScheduler.start(sysdynModel, SysdynExperiment.this); + } + }); + } + + + /** + * Write model configuration to a single Modelica code + * @param monitor + * @param isGame is the experiment a "game experiment". This is needed for modifying the code to work with FMU simulation + * @return Modelica code + */ + protected String getModelicaCode(IModelicaMonitor monitor, boolean isGame) { + String modelText = null; + try { + // Write all configurations once + modelText = ModelicaWriter.write(sysdynModel.getModules(), isGame); + } catch (Exception e) { + // Stop experiment and show console + /*setExperimentStopped(experiment); =>*/ simulate(false); + monitor.showConsole(); + monitor.message("Error when writing Modelica code."); + } + return modelText; + } + + /** + * Get all inits and parameter values required for the inits -file + * @param monitor + * @return All inits in a map + */ + protected HashMap getModelInits(IModelicaMonitor monitor) { + Configuration configuration = sysdynModel.getConfiguration(); + HashMap inits = sysdynModel.getInits(configuration, ""); + Model model = configuration.getModel(); + Double startTime = model.getStartTime(); + Double stopTime = model.getStopTime(); + inits.put(SysdynInitKeys.START_VALUE, startTime.toString()); + inits.put(SysdynInitKeys.STOP_VALUE, stopTime.toString()); + String outputFormat = "\"mat\""; + inits.put(SysdynInitKeys.OUTPUT_FORMAT, outputFormat); + + 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) + inits.put(SysdynInitKeys.TOLERANCE, model.getTolerance().toString()); + String variableFilter = model.getVariableFilter(); + if(variableFilter != null && !variableFilter.isEmpty()) + inits.put(SysdynInitKeys.VARIABLE_FILTER, variableFilter); + return inits; + } + + /** + * Builds a model + * @param simulationLocation + * @param inits + * @param modelText + * @param monitor + */ + protected void buildModel(SimulationLocation simulationLocation, HashMap inits, String modelText, IModelicaMonitor monitor) { + + try { + simulationLocation.simulatorFile.delete(); + ModelicaManager.buildModel(simulationLocation, monitor); + } catch (ModelicaException e) { + if(e.getMessage() != null) + monitor.message(e.getMessage()); + monitor.showConsole(); + canceled = true; + previousModelStructure = ""; + } + } + + /** + * Run a built model + * @throws IOException + */ + protected void runModelica(SimulationLocation simulationLocation, HashMap inits, String modelText, IModelicaMonitor monitor, IProgressMonitor progressMonitor) throws IOException { + progressMonitor.subTask("Simulate model"); + process = ModelicaManager.runModelica( + simulationLocation, + monitor, + inits + ); + ModelicaManager.printProcessOutput(process, monitor); + + Thread resultThread = getResultThread(simulationLocation, inits, monitor, progressMonitor); + resultThread.run(); + + process = null; + } + + /** + * 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 HashMap inits, final IModelicaMonitor monitor, final IProgressMonitor progressMonitor) { + return new Thread() { + @Override + public void run() { + try { + process.waitFor(); + + if(!canceled) { + // Get and store results + progressMonitor.worked(1); + progressMonitor.subTask("Read results"); + SimulationResult result; + if(simulationLocation.outputFile.getName().endsWith(".csv")) + result = new CSVSimulationResult(); + else if(simulationLocation.outputFile.getName().endsWith(".plt")) + result = new SimulationResult(); + else + result = new MatSimulationResult(); // The latest format + + // 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; + } + + //long curr_time = System.currentTimeMillis(); + result.read(simulationLocation.outputFile, outIntervalInt); + //System.out.println("result.read time = " + (System.currentTimeMillis() - curr_time)); + result.readInits(simulationLocation.initFile); + result.filter(); + getCurrentResult().setResult(result); + progressMonitor.worked(1); + resultsChanged(); + + simulate(false); + + String errorString = result.getResultReadErrors(); + if(errorString != null && !errorString.isEmpty()) + monitor.message(errorString); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }; + } + + 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; + } + + + /** + * + * @param sysdynModel + * @param modelText + * @param inits + * @param additionalScript + * @return + * @throws IOException + */ + protected SimulationLocation createSimulationFiles(SysdynModel sysdynModel, String modelText, HashMap inits, String additionalScript, boolean fmu) throws IOException { + File simulationDir = getSimulationDir(); + + // update all function files. both the model's and built-in + try { + final SysdynExperiment experiment = this; session.syncRequest(new ReadRequest() { - + @Override public void run(ReadGraph graph) throws DatabaseException { FunctionUtils.updateFunctionFilesForExperiment(graph, experiment); @@ -403,16 +382,16 @@ public class SysdynExperiment extends Experiment implements IDynamicExperiment, } catch (DatabaseException e) { e.printStackTrace(); } - - return ModelicaManager.createSimulationFiles( - simulationDir, - sysdynModel.getConfiguration().getLabel(), - modelText, - inits, - additionalScript, - fmu); - } - + + return ModelicaManager.createSimulationFiles( + simulationDir, + sysdynModel.getConfiguration().getLabel(), + modelText, + inits, + additionalScript, + fmu); + } + /** * Get a simulation directory for this model * @return File directory @@ -438,419 +417,420 @@ public class SysdynExperiment extends Experiment implements IDynamicExperiment, return simulationDir; } - /** - * - * @return - */ - protected String getAdditionalScripts() { - StringBuilder functionscript = new StringBuilder(); - for(String path : FunctionUtils.getLibraryPathsForModelica(this)) { - functionscript.append("loadFile(\"" + path + "\");\n"); - } - return functionscript.toString(); - } - - protected void storeImportantInits(HashMap inits) { - previousImportantInits.clear(); - 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)); - } - - /** - * Starts simulating a model. Call only from inside simulation job! Start simulation using simulate(true). - * @param monitor - * @param progressMonitor - * @throws IOException - */ - public synchronized void simulate(final IModelicaMonitor monitor, final IProgressMonitor progressMonitor) throws IOException { - canceled = false; - - progressMonitor.subTask("Write modelica classes"); - - // Write Modelica files - String modelText = getModelicaCode(monitor, false); - if(modelText == null) - return; - - progressMonitor.worked(1); - - // Write initial files and add init-parameters - progressMonitor.subTask("Write simulation files"); - HashMap inits = getModelInits(monitor); - - - // add loadFile script to load all related functions and function libraries - String additionalScript = getAdditionalScripts(); - - // Create simulation files - SimulationLocation simulationLocation = createSimulationFiles(sysdynModel, modelText, inits, additionalScript, false); - progressMonitor.worked(1); - - - // Build the model and store previous model structure and inits that affect the building - // If there is no exe file OR the model structure has not changed, no need to build - if (!simulationLocation.simulatorFile.isFile() || hasStructureChanged(modelText, inits)) { - progressMonitor.subTask("Build model"); - storeImportantInits(inits); - previousModelStructure = modelText; - buildModel(simulationLocation, inits, modelText, monitor); - } - - progressMonitor.worked(1); - - if(simulationLocation != null && !canceled) { - // Simulate the model - runModelica(simulationLocation, inits, modelText, monitor, progressMonitor); - } - if(canceled) - simulate(false); - process = null; - } - - - - /** - * Method that compares given modelText and inits to previous model and inits - * @param modelText Textual representation of a model (Modelica code) - * @param inits map of init parameters - * @return true if the model has changed, false otherwise - */ - protected boolean hasStructureChanged(String modelText, Map inits) { - - if(previousModelStructure == null) - return true; - - // Compare inits first because it is faster - for(String key : previousImportantInits.keySet()) { - if(!inits.containsKey(key) || !previousImportantInits.get(key).equals(inits.get(key))) - return true; - } - - // Then compare the actual model structure - BufferedReader current = new BufferedReader( - new StringReader(modelText)); - BufferedReader previous = new BufferedReader( - new StringReader(previousModelStructure)); - - String c, p; - try { - // Read both current and previous model texts at the same time - c = current.readLine(); - p = previous.readLine(); - - while (c != null && p != null) { - // if the lines are the same, no need for further examination - if(!c.equals(p)) { - if( - c.contains("parameter") && c.contains("/* Actual value read from init file */") && - p.contains("parameter") && p.contains("/* Actual value read from init file */")) { - /* - * The line is a parameter definition. - * In this case only what is before '=' matters - * - * parameter Real Var = 1; - * is structurally same as - * parameter Real Var = 2; - */ - int i = c.indexOf("="); - if(!c.substring(0, i).equals(p.substring(0, i))) { - // different parameter definition - return true; - } - } else { - // other than a line containing parameters differs - return true; - } - } - c = current.readLine(); - p = previous.readLine(); - } - - if((c == null && p != null) || (c != null && p == null)) { - // different lengths - return true; - } - - } catch(IOException e) { - // Something went wrong in the comparison, it is safer to say that the structure has changed - return true; - } - return false; - } - - /** - * Destroy an ongoing simulation process - */ - public void cancelSimulation() { - canceled = true; - if(process != null) { - process.destroy(); - } - } - - /** - * Toggle simulation state - * @param enabled true == RUNNING, false == STOPPED - */ - public void toggleSimulation(boolean enabled) { - if(enabled) { - this.toggled = true; - changeState(ExperimentState.RUNNING); - if(modificationListener == null) { - - modificationListener = new Runnable() { - - @Override - public void run() { - session.asyncRequest(new ReadRequest() { - - @Override - public void run(ReadGraph graph) throws DatabaseException { - if(getState() == ExperimentState.RUNNING) { - SimulationScheduler.start(sysdynModel, SysdynExperiment.this); - } - - } - }); - - }; - }; - sysdynModel.addModificationListener(modificationListener); - } - } - else { - changeState(ExperimentState.STOPPED); - this.toggled = false; - } - - } - - @Override - public void refresh(Session session) { - session.asyncRequest(new ReadRequest() { - - @Override - public void run(ReadGraph graph) throws DatabaseException { - init(graph); - } - - }); - } - - protected void localStateChange() { - setSysdynExperimentState(getState()); - switch(state) { - case DISPOSED: - onExperimentDisposed(); - break; - default: - break; - } - - } - - /** - * Returns sysdyn experiment state. It is usually the same as ordinary experiment state. - * Initializing phase takes longer than normally in game experiments. - * @return - */ - public ExperimentState getSysdynExperimentState() { - return sysdynExperimentState; - } - - protected void setSysdynExperimentState(final ExperimentState state) { - sysdynExperimentState = state; - session.asyncRequest(new ReadRequest() { - - @Override - public void run(ReadGraph graph) throws DatabaseException { - VirtualGraphSupport support = graph.getService(VirtualGraphSupport.class); - final Session session = graph.getSession(); - session.asyncRequest(new WriteRequest(support.getWorkspacePersistent("experiments")) { - @Override - public void perform(WriteGraph graph) throws DatabaseException { - Layer0 L0 = Layer0.getInstance(graph); - SimulationResource SR = SimulationResource.getInstance(graph); - graph.deny(model, SR.HasExperimentState); + /** + * + * @return + */ + protected String getAdditionalScripts() { + StringBuilder functionscript = new StringBuilder(); + for(String path : FunctionUtils.getLibraryPathsForModelica(this)) { + functionscript.append("loadFile(\"" + path + "\");\n"); + } + return functionscript.toString(); + } + + protected void storeImportantInits(HashMap inits) { + previousImportantInits.clear(); + 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)); + } + + /** + * Starts simulating a model. Call only from inside simulation job! Start simulation using simulate(true). + * @param monitor + * @param progressMonitor + * @throws IOException + */ + public synchronized void simulate(final IModelicaMonitor monitor, final IProgressMonitor progressMonitor) throws IOException { + canceled = false; + + progressMonitor.subTask("Write modelica classes"); + + // Write Modelica files + String modelText = getModelicaCode(monitor, false); + if(modelText == null) + return; + + progressMonitor.worked(1); + + // Write initial files and add init-parameters + progressMonitor.subTask("Write simulation files"); + HashMap inits = getModelInits(monitor); + + + // add loadFile script to load all related functions and function libraries + String additionalScript = getAdditionalScripts(); + + // Create simulation files + SimulationLocation simulationLocation = createSimulationFiles(sysdynModel, modelText, inits, additionalScript, false); + progressMonitor.worked(1); + + + // Build the model and store previous model structure and inits that affect the building + // If there is no exe file OR the model structure has not changed, no need to build + if (!simulationLocation.simulatorFile.isFile() || hasStructureChanged(modelText, inits)) { + progressMonitor.subTask("Build model"); + storeImportantInits(inits); + previousModelStructure = modelText; + buildModel(simulationLocation, inits, modelText, monitor); + } + + progressMonitor.worked(1); + + if(simulationLocation != null && !canceled) { + // Simulate the model + runModelica(simulationLocation, inits, modelText, monitor, progressMonitor); + } + if(canceled) + simulate(false); + process = null; + } + + + + /** + * Method that compares given modelText and inits to previous model and inits + * @param modelText Textual representation of a model (Modelica code) + * @param inits map of init parameters + * @return true if the model has changed, false otherwise + */ + protected boolean hasStructureChanged(String modelText, Map inits) { + + if(previousModelStructure == null) + return true; + + // Compare inits first because it is faster + for(String key : previousImportantInits.keySet()) { + if(!inits.containsKey(key) || !previousImportantInits.get(key).equals(inits.get(key))) + return true; + } + + // Then compare the actual model structure + BufferedReader current = new BufferedReader( + new StringReader(modelText)); + BufferedReader previous = new BufferedReader( + new StringReader(previousModelStructure)); + + String c, p; + try { + // Read both current and previous model texts at the same time + c = current.readLine(); + p = previous.readLine(); + + while (c != null && p != null) { + // if the lines are the same, no need for further examination + if(!c.equals(p)) { + if( + c.contains("parameter") && c.contains("/* Actual value read from init file */") && + p.contains("parameter") && p.contains("/* Actual value read from init file */")) { + /* + * The line is a parameter definition. + * In this case only what is before '=' matters + * + * parameter Real Var = 1; + * is structurally same as + * parameter Real Var = 2; + */ + int i = c.indexOf("="); + if(!c.substring(0, i).equals(p.substring(0, i))) { + // different parameter definition + return true; + } + } else { + // other than a line containing parameters differs + return true; + } + } + c = current.readLine(); + p = previous.readLine(); + } + + if((c == null && p != null) || (c != null && p == null)) { + // different lengths + return true; + } + + } catch(IOException e) { + // Something went wrong in the comparison, it is safer to say that the structure has changed + return true; + } + return false; + } + + /** + * Destroy an ongoing simulation process + */ + public void cancelSimulation() { + canceled = true; + if(process != null) { + process.destroy(); + } + } + + /** + * Toggle simulation state + * @param enabled true == RUNNING, false == STOPPED + */ + public void toggleSimulation(boolean enabled) { + if(enabled) { + this.toggled = true; + changeState(ExperimentState.RUNNING); + if(modificationListener == null) { + + modificationListener = new Runnable() { + + @Override + public void run() { + session.asyncRequest(new ReadRequest() { + + @Override + public void run(ReadGraph graph) throws DatabaseException { + if(getState() == ExperimentState.RUNNING) { + SimulationScheduler.start(sysdynModel, SysdynExperiment.this); + } + + } + }); + + }; + }; + sysdynModel.addModificationListener(modificationListener); + } + } + else { + changeState(ExperimentState.STOPPED); + this.toggled = false; + } + + } + + @Override + public void refresh(Session session) { + session.asyncRequest(new ReadRequest() { + + @Override + public void run(ReadGraph graph) throws DatabaseException { + init(graph); + } + + }); + } + + @Override + protected void localStateChange() { + setSysdynExperimentState(getState()); + switch(state) { + case DISPOSED: + onExperimentDisposed(); + break; + default: + break; + } + + } + + /** + * Returns sysdyn experiment state. It is usually the same as ordinary experiment state. + * Initializing phase takes longer than normally in game experiments. + * @return + */ + public ExperimentState getSysdynExperimentState() { + return sysdynExperimentState; + } + + protected void setSysdynExperimentState(final ExperimentState state) { + sysdynExperimentState = state; + session.asyncRequest(new ReadRequest() { + + @Override + public void run(ReadGraph graph) throws DatabaseException { + VirtualGraphSupport support = graph.getService(VirtualGraphSupport.class); + final Session session = graph.getSession(); + session.asyncRequest(new WriteRequest(support.getWorkspacePersistent("experiments")) { + @Override + public void perform(WriteGraph graph) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + SimulationResource SR = SimulationResource.getInstance(graph); + graph.deny(model, SR.HasExperimentState); graph.deny(experiment, SR.HasExperimentState); - Resource st = graph.newResource(); - switch(state) { - case INITIALIZING: - graph.claim(st, L0.InstanceOf, SR.ExperimentState_Initializing); - break; - case RUNNING: - graph.claim(st, L0.InstanceOf, SR.ExperimentState_Running); - break; - case STOPPED: - graph.claim(st, L0.InstanceOf, SR.ExperimentState_Stopped); - break; - case DISPOSED: - graph.claim(st, L0.InstanceOf, SR.ExperimentState_Disposed); - break; - } - - graph.claim(model, SR.HasExperimentState, st); - graph.claim(experiment, SR.HasExperimentState, st); - }}); - } - }); - } - - /** - * Actions performed when experiment is disposed - * @param graph - */ - protected void onExperimentDisposed() { - cancelSimulation(); - sysdynModel.removeModificationListener(modificationListener); - modificationListener = null; - - session.asyncRequest(new ReadRequest() { - @Override - public void run(ReadGraph graph) throws DatabaseException { - toggleActivation(graph, false); - } - }); - } - - - /** - * Toggles the active-state of this experiment on or off - * @param graph ReadGraph - * @param activate The active-state of this experiment - */ - protected void toggleActivation(ReadGraph graph, final boolean activate) { - VirtualGraphSupport support = graph.getService(VirtualGraphSupport.class); - final Session session = graph.getSession(); - session.asyncRequest(new WriteRequest(support.getWorkspacePersistent("experiments")) { - @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 { - SimulationResource SIMU = SimulationResource.getInstance(graph); - if(activate) - graph.claim(experiment, SIMU.IsActive, experiment); - else - graph.denyStatement(experiment, SIMU.IsActive, experiment); - } - - }); - } - }); - } - - - /* Result subscriptions */ - - @SuppressWarnings("rawtypes") - /** - * Copy from AprosExperiment - * @param subscription - */ + Resource st = graph.newResource(); + switch(state) { + case INITIALIZING: + graph.claim(st, L0.InstanceOf, SR.ExperimentState_Initializing); + break; + case RUNNING: + graph.claim(st, L0.InstanceOf, SR.ExperimentState_Running); + break; + case STOPPED: + graph.claim(st, L0.InstanceOf, SR.ExperimentState_Stopped); + break; + case DISPOSED: + graph.claim(st, L0.InstanceOf, SR.ExperimentState_Disposed); + break; + } + + graph.claim(model, SR.HasExperimentState, st); + graph.claim(experiment, SR.HasExperimentState, st); + }}); + } + }); + } + + /** + * Actions performed when experiment is disposed + * @param graph + */ + protected void onExperimentDisposed() { + cancelSimulation(); + sysdynModel.removeModificationListener(modificationListener); + modificationListener = null; + + session.asyncRequest(new ReadRequest() { + @Override + public void run(ReadGraph graph) throws DatabaseException { + toggleActivation(graph, false); + } + }); + } + + + /** + * Toggles the active-state of this experiment on or off + * @param graph ReadGraph + * @param activate The active-state of this experiment + */ + protected void toggleActivation(ReadGraph graph, final boolean activate) { + VirtualGraphSupport support = graph.getService(VirtualGraphSupport.class); + final Session session = graph.getSession(); + session.asyncRequest(new WriteRequest(support.getWorkspacePersistent("experiments")) { + @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 { + SimulationResource SIMU = SimulationResource.getInstance(graph); + if(activate) + graph.claim(experiment, SIMU.IsActive, experiment); + else + graph.denyStatement(experiment, SIMU.IsActive, experiment); + } + + }); + } + }); + } + + + /* Result subscriptions */ + + @SuppressWarnings("rawtypes") + /** + * Copy from AprosExperiment + * @param subscription + */ @Override - public void addVariableValueSubscription(VariableValueSubscription subscription) { - assert subscription != null; - synchronized (variableValueSubscriptions) { - //System.out.println("ADD listener " + subscription); - variableValueSubscriptions.add(subscription); - variableValueSubscriptionsSnapshot = null; - } - } - - @SuppressWarnings("rawtypes") - /** - * Copy from AprosExperiment - * @param subscription - */ + public void addVariableValueSubscription(VariableValueSubscription subscription) { + assert subscription != null; + synchronized (variableValueSubscriptions) { + //System.out.println("ADD listener " + subscription); + variableValueSubscriptions.add(subscription); + variableValueSubscriptionsSnapshot = null; + } + } + + @SuppressWarnings("rawtypes") + /** + * Copy from AprosExperiment + * @param subscription + */ @Override - public void removeVariableValueSubscription(VariableValueSubscription subscription) { - assert subscription != null; - synchronized (variableValueSubscriptions) { - //System.out.println("REMOVE listener " + subscription); - variableValueSubscriptions.remove(subscription); - variableValueSubscriptionsSnapshot = null; - } - } - - @SuppressWarnings("rawtypes") - /** - * Copy from AprosExperiment - * @return - */ + public void removeVariableValueSubscription(VariableValueSubscription subscription) { + assert subscription != null; + synchronized (variableValueSubscriptions) { + //System.out.println("REMOVE listener " + subscription); + variableValueSubscriptions.remove(subscription); + variableValueSubscriptionsSnapshot = null; + } + } + + @SuppressWarnings("rawtypes") + /** + * Copy from AprosExperiment + * @return + */ @Override - public VariableValueSubscription[] getListenerSnapshot() { - VariableValueSubscription[] snapshot = variableValueSubscriptionsSnapshot; - if (snapshot == null) { - synchronized (variableValueSubscriptions) { - snapshot = variableValueSubscriptionsSnapshot; - if (snapshot == null) { - snapshot = variableValueSubscriptionsSnapshot = variableValueSubscriptions.toArray(new VariableValueSubscription[variableValueSubscriptions.size()]); - } - } - //System.out.println("listener count: " + snapshot.length); - } - return snapshot; - } - - volatile long previousVariableUpdateTime = 0; - volatile boolean skippedVariableUpdate = true; - - - /** - * Modified copy from AprosExperiment - */ - public void resultsChanged() { - long time = System.nanoTime(); - if(time - previousVariableUpdateTime > 100000000) { - updateSubscriptions(); - previousVariableUpdateTime = time; - } - else - skippedVariableUpdate = true; - } - - @SuppressWarnings("rawtypes") - /** - * Modified copy from AporsExperiment - */ + public VariableValueSubscription[] getListenerSnapshot() { + VariableValueSubscription[] snapshot = variableValueSubscriptionsSnapshot; + if (snapshot == null) { + synchronized (variableValueSubscriptions) { + snapshot = variableValueSubscriptionsSnapshot; + if (snapshot == null) { + snapshot = variableValueSubscriptionsSnapshot = variableValueSubscriptions.toArray(new VariableValueSubscription[variableValueSubscriptions.size()]); + } + } + //System.out.println("listener count: " + snapshot.length); + } + return snapshot; + } + + volatile long previousVariableUpdateTime = 0; + volatile boolean skippedVariableUpdate = true; + + + /** + * Modified copy from AprosExperiment + */ + public void resultsChanged() { + long time = System.nanoTime(); + if(time - previousVariableUpdateTime > 100000000) { + updateSubscriptions(); + previousVariableUpdateTime = time; + } + else + skippedVariableUpdate = true; + } + + @SuppressWarnings("rawtypes") + /** + * Modified copy from AporsExperiment + */ @Override - public void updateSubscriptions() { - for(VariableValueSubscription subscription : getListenerSnapshot()) - subscription.update(); - skippedVariableUpdate = false; - } + public void updateSubscriptions() { + for(VariableValueSubscription subscription : getListenerSnapshot()) + subscription.update(); + skippedVariableUpdate = false; + } - /* Experiment methods that are not used in this experiment */ + /* Experiment methods that are not used in this experiment */ - @Override - public Lock getDatasourceLock() { - return null; - } + @Override + public Lock getDatasourceLock() { + return null; + } - @Override - public Datasource getDatasource() { - return null; - } + @Override + public Datasource getDatasource() { + return null; + } - @Override - public void simulateDuration(double duration) { - System.out.println("simulateDuartion"); - } + @Override + public void simulateDuration(double duration) { + System.out.println("simulateDuartion"); + } - @Override - public void rewindTo(double time) { - System.out.println("rewindTo"); - } + @Override + public void rewindTo(double time) { + System.out.println("rewindTo"); + } } diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynResult.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynResult.java index 3b3ead10..7d97c24b 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynResult.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynResult.java @@ -17,6 +17,7 @@ import java.io.IOException; import java.util.List; import java.util.TreeMap; +import org.eclipse.core.runtime.IProgressMonitor; import org.simantics.databoard.Accessors; import org.simantics.databoard.Bindings; import org.simantics.databoard.Datatypes; @@ -52,6 +53,7 @@ public class SysdynResult { protected RecordAccessor accessor; protected String resultName; + private SimulationResult simulationResult; /** * Create an empty result @@ -113,38 +115,100 @@ public class SysdynResult { public void setResult(SimulationResult result) { try { // Create Memory Historian + this.simulationResult = result; Datatype recordingSessionType = Datatypes.getDatatype("RecordingSession"); RecordBinding sessionBinding = (RecordBinding) Bindings.getBinding( recordingSessionType ); - RecordBinding recordingBinding = (RecordBinding) Bindings.getBinding( Recording.class ); Object session = sessionBinding.createDefault(); accessor = (JavaRecord) Accessors.getAccessor( sessionBinding, session ); - MapAccessor recordings = accessor.getFieldAccessor("recordings"); - - // There is a recording for each variable - List datasets = result.getVariableDataSets(); - - // Add variables - for(DataSet ds : datasets){ - // Add recording to session - Recording recording = createRecording(ds); - recordings.put(Bindings.VARIANT, recording.nodeId, Bindings.VARIANT, new Variant(recordingBinding, recording)); - } - - // Add initial values - for(DataSet ds : result.getInitialValueDataSets()){ - Variant nodeId = Variant.ofInstance( ds.name ); - if (recordings.containsKey(Bindings.VARIANT, nodeId)) continue; - // Add recording to session - Recording recording = createRecording(ds); - recordings.put(Bindings.VARIANT, recording.nodeId, Bindings.VARIANT, new Variant(recordingBinding, recording)); - } - + + addAllInitialValues(); } catch (Exception e) { e.printStackTrace(); } } /** + * Read all results from their DataSets. + * @param progressMonitor + */ + private void addAllDataSets(IProgressMonitor progressMonitor) { + try { + MapAccessor recordings = accessor.getFieldAccessor("recordings"); + + // There is a recording for each variable + List datasets = simulationResult.getVariableDataSets(); + RecordBinding recordingBinding = (RecordBinding) Bindings.getBinding( Recording.class ); + if (progressMonitor != null) { + progressMonitor.subTask("Prepare saving"); + } + // Add variables + for(DataSet ds : datasets){ + if (progressMonitor != null) { + progressMonitor.worked(1); + } + // Add recording to session, if not already added + Variant dsNodeId = Variant.ofInstance( ds.name ); + if (recordings.containsKey(Bindings.VARIANT, dsNodeId)) + continue; + + Recording recording = createRecording(ds); + recordings.put(Bindings.VARIANT, recording.nodeId, Bindings.VARIANT, new Variant(recordingBinding, recording)); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Read all results from their DataSets. + */ + private void addDataSet(String variable) { + try { + MapAccessor recordings = accessor.getFieldAccessor("recordings"); + + // There is a recording for each variable + List datasets = simulationResult.getVariableDataSets(); + RecordBinding recordingBinding = (RecordBinding) Bindings.getBinding( Recording.class ); + + // Add variables + for(DataSet ds : datasets){ + if (ds.name.equals(variable)) { + // Add recording to session + Recording recording = createRecording(ds); + recordings.put(Bindings.VARIANT, recording.nodeId, Bindings.VARIANT, new Variant(recordingBinding, recording)); + break; + } + continue; + } + } catch (Exception e) { + e.printStackTrace(); + } + + } + + /** + * Read all results from their DataSets. + */ + private void addAllInitialValues() { + try { + MapAccessor recordings = accessor.getFieldAccessor("recordings"); + + RecordBinding recordingBinding = (RecordBinding) Bindings.getBinding( Recording.class ); + + // Add initial values + for(DataSet ds : simulationResult.getInitialValueDataSets()){ + Variant nodeId = Variant.ofInstance( ds.name ); + if (recordings.containsKey(Bindings.VARIANT, nodeId)) continue; + // Add recording to session + Recording recording = createRecording(ds); + recordings.put(Bindings.VARIANT, recording.nodeId, Bindings.VARIANT, new Variant(recordingBinding, recording)); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** * Create a recording for a variable to this result's {@link RecordAccessor} * @param ds {@link DataSet} for the variable * @return {@link Recording} @@ -175,7 +239,7 @@ public class SysdynResult { * * @param file {@link File} where the {@link RecordAccessor} is saved */ - public void saveToFile(File file) { + public void saveToFile(File file, IProgressMonitor progressMonitor) { if(accessor == null) return; try { Binding binding; @@ -190,9 +254,15 @@ public class SysdynResult { binding = Bindings.getBinding( accessor.type() ); value = accessor.getValue(binding); } - + addAllDataSets(progressMonitor); // Write to file + if (progressMonitor != null) { + progressMonitor.subTask("Save results"); + } Files.createFile(file, binding, value); + + // The division is made so that 1 unit per variable and their sum for saving them. + progressMonitor.worked(numberOfVariables()); } catch (IOException e) { e.printStackTrace(); } catch (AccessorException e) { @@ -220,34 +290,44 @@ public class SysdynResult { public SysdynDataSet getDataSet(String variable) { if(accessor != null) { synchronized(accessor) { - try { - ChildReference ref = ChildReference.compile( - new NameReference("recordings"), - new KeyReference( Bindings.VARIANT, Variant.ofInstance(variable) ), - new ComponentReference(), - new NameReference("segments"), - new IndexReference(0) - ); - - MapAccessor ma = accessor.getComponent( ref ); - int size = ma.size(); - Double[] times = new Double[size]; - Double[] values = new Double[size]; - - ma.getAll(Bindings.DOUBLE, Bindings.DOUBLE, times, values); - - double[] times_ = new double[size]; - double[] values_ = new double[size]; - for (int i=0; i