From: Jussi Koskela Date: Wed, 1 Feb 2017 09:43:24 +0000 (+0200) Subject: Experiment activation listener is called twice X-Git-Tag: v1.29.0~6 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=7e060d9c8ff1a79733e188cd279e549443985792;p=simantics%2Fsysdyn.git Experiment activation listener is called twice Experiment loader calls listener.onExperimentActivated prematurely after starting an asynchronous request to create a run. It's responsibility of this request to call listener.onExperimentActivated once it finishes, which it does correctly. refs #7007 Change-Id: Id2957b7797e221083ec69791a282817cb3628ced --- diff --git a/bundles/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynModel.java b/bundles/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynModel.java index d4879b71..e97d6663 100644 --- a/bundles/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynModel.java +++ b/bundles/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynModel.java @@ -1,622 +1,620 @@ -/******************************************************************************* - * Copyright (c) 2010, 2013, 2014 Association for Decentralized Information Management in - * Industry THTH ry. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * VTT Technical Research Centre of Finland - initial API and implementation - * Semantum Oy - Bug #4180 - *******************************************************************************/ -package org.simantics.sysdyn.manager; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; - -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.simantics.Simantics; -import org.simantics.databoard.Bindings; -import org.simantics.db.ReadGraph; -import org.simantics.db.Resource; -import org.simantics.db.Session; -import org.simantics.db.WriteGraph; -import org.simantics.db.common.utils.NameUtils; -import org.simantics.db.exception.DatabaseException; -import org.simantics.db.exception.ManyObjectsForFunctionalRelationException; -import org.simantics.db.exception.ServiceException; -import org.simantics.db.procedure.Listener; -import org.simantics.db.request.Read; -import org.simantics.db.service.VirtualGraphSupport; -import org.simantics.layer0.Layer0; -import org.simantics.modeling.ModelingResources; -import org.simantics.objmap.IMapping; -import org.simantics.objmap.IMappingListener; -import org.simantics.objmap.MappingException; -import org.simantics.objmap.Mappings; -import org.simantics.project.IProject; -import org.simantics.scl.runtime.function.Function2; -import org.simantics.scl.runtime.function.FunctionImpl2; -import org.simantics.simulation.experiment.IExperiment; -import org.simantics.simulation.model.IModel; -import org.simantics.simulation.ontology.SimulationResource; -import org.simantics.simulation.project.ExperimentRuns; -import org.simantics.simulation.project.IExperimentActivationListener; -import org.simantics.simulation.project.IExperimentManager; -import org.simantics.structural.stubs.StructuralResource2; -import org.simantics.sysdyn.Activator; -import org.simantics.sysdyn.SysdynResource; -import org.simantics.sysdyn.adapter.VariableValueSubscription; -import org.simantics.sysdyn.representation.Configuration; -import org.simantics.sysdyn.representation.IElement; -import org.simantics.sysdyn.representation.IndependentVariable; -import org.simantics.sysdyn.representation.Model; -import org.simantics.sysdyn.representation.Module; -import org.simantics.sysdyn.representation.ParameterOverride; -import org.simantics.sysdyn.representation.Sheet; -import org.simantics.sysdyn.representation.SysdynSchema; -import org.simantics.sysdyn.representation.Variability; -import org.simantics.sysdyn.representation.expressions.Expression; -import org.simantics.sysdyn.representation.expressions.IExpression; -import org.simantics.sysdyn.representation.expressions.ParameterExpression; -import org.simantics.sysdyn.representation.expressions.StockExpression; -import org.simantics.sysdyn.solver.SolverSettings; -import org.simantics.sysdyn.solver.SolverSettings.SolverType; -import org.simantics.sysdyn.solver.Solvers; - -import gnu.trove.set.hash.THashSet; - -/** - * Maintains a Java representation of system dynamic model. - * @author Hannu Niemistö, Teemu Lempinen, Tuomas Miettinen - */ -public class SysdynModel implements IModel, IMappingListener, VariableSubscriptionManager { - - private Session session; - - private IMapping mapping; - - private Resource configurationResource; - private Resource modelResource; - - private Configuration configuration; - - private final Set modules = new HashSet(); - - private ArrayList displayedResults; - private final ArrayList listeningHistories = new ArrayList(); - - private final CopyOnWriteArrayList modificationListeners = - new CopyOnWriteArrayList(); - - @SuppressWarnings("rawtypes") - protected THashSet variableValueSubscriptions = new THashSet(); - @SuppressWarnings("rawtypes") - protected volatile VariableValueSubscription[] variableValueSubscriptionsSnapshot = null; - - @SuppressWarnings("rawtypes") - private final Map services = new HashMap(); - - private boolean structureModified = false; - - /** - * Recursively read all module configurations that are used in - * configResource and its components children - * - * @param graph ReadGraph - * @param configResource Configuration - * @param result set containing all encountered configurations - * @throws DatabaseException - */ - void readModules(ReadGraph graph, Resource configResource, Set result) throws DatabaseException { - - // if result does not contain this resource, add it to the result - if(!result.add(configResource)) return; - - Layer0 l0 = Layer0.getInstance(graph); - SysdynResource sr = SysdynResource.getInstance(graph); - StructuralResource2 str = StructuralResource2.getInstance(graph); - - for(Resource part : graph.getObjects(configResource, l0.ConsistsOf)) { - if(graph.isInstanceOf(part, sr.Module)) { - Resource type = graph.getPossibleType(part, sr.Module); - Resource config = graph.getPossibleObject(type, str.IsDefinedBy); - // Recursively readModules - readModules(graph, config, result); - } - } - - } - - /** - * Get all modules that have been used in configResource and its children - * @param graph ReadGraph - * @param configResource Configuration - * @return All modules (configuration resources) that have been used in configResource - * @throws DatabaseException - */ - Set readModules(ReadGraph graph, Resource configResource) throws DatabaseException { - HashSet result = new HashSet(); - readModules(graph, configResource, result); - return result; - } - - /** - * Create an ObjMapping - * @param g ReadGraph - * @throws DatabaseException - */ - private void createMapping(ReadGraph g) throws DatabaseException { - SysdynSchema schema = new SysdynSchema(g); - mapping = Mappings.createWithListening(schema); - mapping.addMappingListener(SysdynModel.this); - try { - configuration = (Configuration)mapping.map(g, configurationResource); - } catch (MappingException e) { - SysdynConsole.INSTANCE.message( - "Error: Mapping is broken! Find the problem, " + - "fix it and restart the program." + - "\nJava error message:\n" + - e.getMessage()); - throw e; - } - for(Resource config : readModules(g, configurationResource)) { - modules.add((Configuration)mapping.map(g, config)); - } - } - - /** - * New Sysdyn model - * @param g ReadGraph - * @param configurationResource Configration resource of the model - */ - public SysdynModel(ReadGraph g, Resource configurationResource) { - this.session = g.getSession(); - this.configurationResource = configurationResource; - - try { - createMapping(g); - } catch(DatabaseException e) { - e.printStackTrace(); - } - - g.asyncRequest(new Read> () { - @Override - public ArrayList perform(ReadGraph graph) throws DatabaseException { - ArrayList displayedResults = new ArrayList(); - // TODO: this can be done automatically with a listener - - for(HistoryDatasetResult hs : listeningHistories) - hs.disposeListeners(); // dispose old histories listening to spreadsheets - - try { - // Find all active saved results - Layer0 l0 = Layer0.getInstance(graph); - SysdynResource sr = SysdynResource.getInstance(graph); - SimulationResource SIMU = SimulationResource.getInstance(graph); - Resource model = graph.getPossibleObject(getConfigurationResource(), SIMU.IsConfigurationOf); - if(model == null) - return null; - - Collection results = graph.syncRequest(new ActiveResults(model)); - for(Resource result : results) { - if(graph.hasStatement(result, sr.Result_showResult)) { - SysdynResult sysdynResult = null; - if(graph.isInstanceOf(result, sr.HistoryDataset)) { - HistoryDatasetResult r = new HistoryDatasetResult(); - listeningHistories.add(r); - sysdynResult = new MemoryResult(r, NameUtils.getSafeLabel(graph, result)); - r.read((MemoryResult)sysdynResult, result); - } else { - sysdynResult = new FileResult( - (String) graph.getPossibleRelatedValue(result, l0.HasLabel), - (String) graph.getPossibleRelatedValue(result, sr.Result_resultFile)); - } - - if(sysdynResult != null) - displayedResults.add(sysdynResult); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - - return displayedResults; - } - - }, new Listener> () { - - @Override - public void execute(ArrayList result) { - // Add the current result if there is one - displayedResults = result; - resultChanged(); - } - - @Override - public void exception(Throwable t) { - t.printStackTrace(); - } - - @Override - public boolean isDisposed() { - return false; - } - - }); - } - - // A dummy call for experiments - public SysdynModel(Resource modelResource) { - this.modelResource = modelResource; - } - - /** - * Get modules - * @return modules - */ - public Set getModules() { - return modules; - } - - /** - * Update mapping. - * - * Use this if inside a transaction. - * - * @param graph ReadGraph - * @return has the range been modified - * @throws DatabaseException - */ - public synchronized boolean update(ReadGraph graph) throws DatabaseException { - if(mapping.isDomainModified()) { - try { - Collection updated = mapping.updateRange(graph); - - for(Object o : updated) { - if(o instanceof Model) { - continue; - } else if(o instanceof Expression && !(o instanceof StockExpression)) { - IndependentVariable variable = ((Expression)o).getParent(); - Variability variability = Variability.getVariability(variable, false, configuration); - if(variability == Variability.PARAMETER) - continue; - } - - // if continue has not been called, the structure has changed - setStructureModified(true); - break; - - } - - } catch (MappingException e) { - SysdynConsole.INSTANCE.message( - "Error: Mapping is broken! Find the problem, " + - "fix it and restart the program." + - "\nJava error message:\n" + - e.getMessage()); - throw e; - } - - // Remove all unnecessary module configurations from modules - Set configs = readModules(graph, configurationResource); - for(Resource config : configs) { - if(!modules.contains(config)) - modules.add((Configuration)mapping.map(graph, config)); - } - - HashSet toBeRemoved = null; - for(Configuration module : modules) { - if(!configs.contains(mapping.inverseGet(module))) { - if(toBeRemoved == null) - toBeRemoved = new HashSet(); - toBeRemoved.add(module); - } - } - if(toBeRemoved != null) - modules.removeAll(toBeRemoved); - return true; - } - else - return false; - } - - /** - * Update mapping. - * - * Use only if not inside a transaction - * @return has range been updated - * @throws DatabaseException - */ - public boolean update() throws DatabaseException { - return session.syncRequest(new Read() { - @Override - public Boolean perform(ReadGraph graph) throws DatabaseException { - return update(graph); - } - }); - } - - /** - * Fires an update to all result listeners and subscriptions - * after results have changed - */ - public void resultChanged() { - IProject project = Simantics.peekProject(); - IExperimentManager manager = project.getHint(IExperimentManager.KEY_EXPERIMENT_MANAGER); - IExperiment active = manager.getActiveExperiment(); - if(active != null && active instanceof SysdynExperiment) { - ((SysdynExperiment)active).resultsChanged(); - } - - updateSubscriptions(); - } - - public void addModificationListener(Runnable listener) { - synchronized(modificationListeners) { - modificationListeners.add(listener); - } - } - - public void removeModificationListener(Runnable listener) { - synchronized(modificationListeners) { - modificationListeners.remove(listener); - } - } - - /** - * Fires all modification listeners after a change in the domain - */ - @Override - public void domainModified() { - synchronized(modificationListeners) { - for(Runnable listener : modificationListeners) - listener.run(); - } - } - - @Override - public void rangeModified() { - } - - public Configuration getConfiguration() { - return configuration; - } - - public Resource getConfigurationResource() { - return configurationResource; - } - - public IMapping getMapping() { - return mapping; - } - - public synchronized IElement getElement(Resource resource) { - return (IElement)mapping.get(resource); - } - - public T getService(Class clazz) { - synchronized(services) { - return clazz.cast(services.get(clazz)); - } - } - - public void addService(Class clazz, T service) { - synchronized(services) { - services.put(clazz, service); - } - } - - @Override - public IExperiment loadExperiment(ReadGraph g, Resource experiment, IExperimentActivationListener listener) { - // Make sure that configurationResource exists - if(configurationResource == null && modelResource != null) { - SimulationResource simu = SimulationResource.getInstance(g); - try { - configurationResource = g.getPossibleObject(modelResource, simu.HasConfiguration); - } catch (ManyObjectsForFunctionalRelationException e) { - e.printStackTrace(); - } catch (ServiceException e) { - e.printStackTrace(); - } - } - - try { - // Create a new experiment based on the experiment resource type - SysdynResource sr = SysdynResource.getInstance(g); - SysdynExperiment exp; - - if(g.isInstanceOf(experiment, sr.PlaybackExperiment)) { - exp = new SysdynPlaybackExperiment(experiment, modelResource); - } else if(g.isInstanceOf(experiment, sr.GameExperiment)) { - exp = Solvers.instantiateGameExperiment(SolverSettings.getSelectedSolverType(), experiment, modelResource); -// if (SolverType.INTERNAL.equals()) { -// exp = new SysdynGameExperimentInternal(experiment, modelResource); -// } -// else { -// exp = new SysdynGameExperiment(experiment, modelResource); -// } - } else if(g.isInstanceOf(experiment, sr.SensitivityAnalysisExperiment)) { - exp = new SysdynSensitivityAnalysisExperiment(experiment, modelResource); - } else if(g.isInstanceOf(experiment, sr.BasicExperiment)) { - // TODO: this is a temporary hack to make sure at least one - // working implementation is available even if the new solver - // architecture is completely broken - if (SolverType.INTERNAL.equals(SolverSettings.getSelectedSolverType())) { - exp = new SysdynExperiment(experiment, modelResource); - } - else { - exp = new OldSysdynExperiment(experiment, modelResource); - } - } else { - if (listener != null) { - listener.onMessage(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Experiment type is not supported: " + NameUtils.getSafeName(g, experiment))); - listener.onFailure(new UnsupportedOperationException("Experiment type is not supported: " + NameUtils.getSafeName(g, experiment))); - } - return null; - } - - // TODO: should maybe get the model from model manager? - - exp.init(g); - - Function2 scl = new FunctionImpl2() { - - @Override - public Object apply(WriteGraph graph, Resource run) { - try { - Layer0 L0 = Layer0.getInstance(graph); - ModelingResources MOD = ModelingResources.getInstance(graph); - Resource session = graph.newResource(); - graph.claim(session, L0.InstanceOf, MOD.SCLCommandSession); - graph.addLiteral(session, L0.HasName, L0.NameOf, L0.String, "__scl__", Bindings.STRING); - graph.claim(run, L0.ConsistsOf, session); - } catch (DatabaseException e) { - e.printStackTrace(); - } - return null; - } - - }; - - VirtualGraphSupport support = g.getSession().getService(VirtualGraphSupport.class); - ExperimentRuns.createRun(g.getSession(), support.getWorkspacePersistent("experiments"), experiment, exp, SysdynResource.URIs.Experiment_Run, listener, scl, null); - if(listener != null) - listener.onExperimentActivated(exp); - return exp; - } catch(Exception e) { - if(listener != null) - listener.onFailure(e); - return null; - } - } - - - /** - * Get active results. To update the active results, use getAndUpdateActiveResults() - * - * @param graph ReadGraph - * @return all active SysdynResults (last update with getAndUpdateActiveResults()) - */ - public Collection getDisplayedResults() { - // TODO: displayedResults are always empty? - if(displayedResults == null) - return new ArrayList(); - else - return displayedResults; - } - - /** - * Get all parameters for Configuration - * - * @param configuration Configuration - * @param prefix String prefix of configuration in this model (Module1.Module2...) - * @return - */ - public HashMap getInits(Configuration configuration, String prefix) { - HashMap inits = new HashMap(); - for (IElement element : configuration.getElements()) { - if (element instanceof Module) { - Module module = (Module) element; - Configuration conf = module.getType().getConfiguration(); - String prfx = prefix + module.getModelicaName() + "."; - inits.putAll(getInits(conf, prfx)); - for(ParameterOverride po : module.getParameterOverrides()) { - inits.put(prfx + po.getVariable().getName(), po.getExpression()); - } - } else if (element instanceof IndependentVariable) { - IndependentVariable variable = (IndependentVariable) element; - //FIXME: more general solution for finding out if the variable is a parameter - if(variable != null && variable.getExpressions() != null && variable.getExpressions().get(0) != null) { - IExpression expression = variable.getExpressions().get(0); - if (expression instanceof ParameterExpression) { - Double value = ((ParameterExpression)expression).getValue(); - if(value != null) - inits.put(prefix + variable.getName(), "" + value); - } - } - } else if(element instanceof Sheet) { - Sheet sheet = (Sheet) element; - for(String cell : sheet.getCells().keySet()) { - inits.put(sheet.getName() + "." + cell, sheet.getCells().get(cell).toString()); - } - } - } - return inits; - } - - - @SuppressWarnings("rawtypes") - /** - * Copy from AprosExperiment - * @param subscription - */ - @Override - public void addVariableValueSubscription(VariableValueSubscription subscription) { - assert subscription != null; - synchronized (variableValueSubscriptions) { - variableValueSubscriptions.add(subscription); - variableValueSubscriptionsSnapshot = null; - } - } - - @SuppressWarnings("rawtypes") - /** - * Copy from AprosExperiment - * @param subscription - */ - @Override - public void removeVariableValueSubscription(VariableValueSubscription subscription) { - assert subscription != null; - synchronized (variableValueSubscriptions) { - 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()]); - } - } - } - return snapshot; - } - - @SuppressWarnings("rawtypes") - /** - * Modified copy from AporsExperiment - */ - @Override - public void updateSubscriptions() { - VariableValueSubscription[] snapShot = getListenerSnapshot(); - for(VariableValueSubscription subscription : snapShot) - subscription.update(); - } - - - public boolean isStructureModified() { - return structureModified; - } - - public void setStructureModified(boolean structureModified) { - this.structureModified = structureModified; - } -} +/******************************************************************************* + * Copyright (c) 2010, 2013, 2014 Association for Decentralized Information Management in + * Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + * Semantum Oy - Bug #4180 + *******************************************************************************/ +package org.simantics.sysdyn.manager; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.simantics.Simantics; +import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.Session; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.utils.NameUtils; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.exception.ManyObjectsForFunctionalRelationException; +import org.simantics.db.exception.ServiceException; +import org.simantics.db.procedure.Listener; +import org.simantics.db.request.Read; +import org.simantics.db.service.VirtualGraphSupport; +import org.simantics.layer0.Layer0; +import org.simantics.modeling.ModelingResources; +import org.simantics.objmap.IMapping; +import org.simantics.objmap.IMappingListener; +import org.simantics.objmap.MappingException; +import org.simantics.objmap.Mappings; +import org.simantics.project.IProject; +import org.simantics.scl.runtime.function.Function2; +import org.simantics.scl.runtime.function.FunctionImpl2; +import org.simantics.simulation.experiment.IExperiment; +import org.simantics.simulation.model.IModel; +import org.simantics.simulation.ontology.SimulationResource; +import org.simantics.simulation.project.ExperimentRuns; +import org.simantics.simulation.project.IExperimentActivationListener; +import org.simantics.simulation.project.IExperimentManager; +import org.simantics.structural.stubs.StructuralResource2; +import org.simantics.sysdyn.Activator; +import org.simantics.sysdyn.SysdynResource; +import org.simantics.sysdyn.adapter.VariableValueSubscription; +import org.simantics.sysdyn.representation.Configuration; +import org.simantics.sysdyn.representation.IElement; +import org.simantics.sysdyn.representation.IndependentVariable; +import org.simantics.sysdyn.representation.Model; +import org.simantics.sysdyn.representation.Module; +import org.simantics.sysdyn.representation.ParameterOverride; +import org.simantics.sysdyn.representation.Sheet; +import org.simantics.sysdyn.representation.SysdynSchema; +import org.simantics.sysdyn.representation.Variability; +import org.simantics.sysdyn.representation.expressions.Expression; +import org.simantics.sysdyn.representation.expressions.IExpression; +import org.simantics.sysdyn.representation.expressions.ParameterExpression; +import org.simantics.sysdyn.representation.expressions.StockExpression; +import org.simantics.sysdyn.solver.SolverSettings; +import org.simantics.sysdyn.solver.SolverSettings.SolverType; +import org.simantics.sysdyn.solver.Solvers; + +import gnu.trove.set.hash.THashSet; + +/** + * Maintains a Java representation of system dynamic model. + * @author Hannu Niemistö, Teemu Lempinen, Tuomas Miettinen + */ +public class SysdynModel implements IModel, IMappingListener, VariableSubscriptionManager { + + private Session session; + + private IMapping mapping; + + private Resource configurationResource; + private Resource modelResource; + + private Configuration configuration; + + private final Set modules = new HashSet(); + + private ArrayList displayedResults; + private final ArrayList listeningHistories = new ArrayList(); + + private final CopyOnWriteArrayList modificationListeners = + new CopyOnWriteArrayList(); + + @SuppressWarnings("rawtypes") + protected THashSet variableValueSubscriptions = new THashSet(); + @SuppressWarnings("rawtypes") + protected volatile VariableValueSubscription[] variableValueSubscriptionsSnapshot = null; + + @SuppressWarnings("rawtypes") + private final Map services = new HashMap(); + + private boolean structureModified = false; + + /** + * Recursively read all module configurations that are used in + * configResource and its components children + * + * @param graph ReadGraph + * @param configResource Configuration + * @param result set containing all encountered configurations + * @throws DatabaseException + */ + void readModules(ReadGraph graph, Resource configResource, Set result) throws DatabaseException { + + // if result does not contain this resource, add it to the result + if(!result.add(configResource)) return; + + Layer0 l0 = Layer0.getInstance(graph); + SysdynResource sr = SysdynResource.getInstance(graph); + StructuralResource2 str = StructuralResource2.getInstance(graph); + + for(Resource part : graph.getObjects(configResource, l0.ConsistsOf)) { + if(graph.isInstanceOf(part, sr.Module)) { + Resource type = graph.getPossibleType(part, sr.Module); + Resource config = graph.getPossibleObject(type, str.IsDefinedBy); + // Recursively readModules + readModules(graph, config, result); + } + } + + } + + /** + * Get all modules that have been used in configResource and its children + * @param graph ReadGraph + * @param configResource Configuration + * @return All modules (configuration resources) that have been used in configResource + * @throws DatabaseException + */ + Set readModules(ReadGraph graph, Resource configResource) throws DatabaseException { + HashSet result = new HashSet(); + readModules(graph, configResource, result); + return result; + } + + /** + * Create an ObjMapping + * @param g ReadGraph + * @throws DatabaseException + */ + private void createMapping(ReadGraph g) throws DatabaseException { + SysdynSchema schema = new SysdynSchema(g); + mapping = Mappings.createWithListening(schema); + mapping.addMappingListener(SysdynModel.this); + try { + configuration = (Configuration)mapping.map(g, configurationResource); + } catch (MappingException e) { + SysdynConsole.INSTANCE.message( + "Error: Mapping is broken! Find the problem, " + + "fix it and restart the program." + + "\nJava error message:\n" + + e.getMessage()); + throw e; + } + for(Resource config : readModules(g, configurationResource)) { + modules.add((Configuration)mapping.map(g, config)); + } + } + + /** + * New Sysdyn model + * @param g ReadGraph + * @param configurationResource Configration resource of the model + */ + public SysdynModel(ReadGraph g, Resource configurationResource) { + this.session = g.getSession(); + this.configurationResource = configurationResource; + + try { + createMapping(g); + } catch(DatabaseException e) { + e.printStackTrace(); + } + + g.asyncRequest(new Read> () { + @Override + public ArrayList perform(ReadGraph graph) throws DatabaseException { + ArrayList displayedResults = new ArrayList(); + // TODO: this can be done automatically with a listener + + for(HistoryDatasetResult hs : listeningHistories) + hs.disposeListeners(); // dispose old histories listening to spreadsheets + + try { + // Find all active saved results + Layer0 l0 = Layer0.getInstance(graph); + SysdynResource sr = SysdynResource.getInstance(graph); + SimulationResource SIMU = SimulationResource.getInstance(graph); + Resource model = graph.getPossibleObject(getConfigurationResource(), SIMU.IsConfigurationOf); + if(model == null) + return null; + + Collection results = graph.syncRequest(new ActiveResults(model)); + for(Resource result : results) { + if(graph.hasStatement(result, sr.Result_showResult)) { + SysdynResult sysdynResult = null; + if(graph.isInstanceOf(result, sr.HistoryDataset)) { + HistoryDatasetResult r = new HistoryDatasetResult(); + listeningHistories.add(r); + sysdynResult = new MemoryResult(r, NameUtils.getSafeLabel(graph, result)); + r.read((MemoryResult)sysdynResult, result); + } else { + sysdynResult = new FileResult( + (String) graph.getPossibleRelatedValue(result, l0.HasLabel), + (String) graph.getPossibleRelatedValue(result, sr.Result_resultFile)); + } + + if(sysdynResult != null) + displayedResults.add(sysdynResult); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + return displayedResults; + } + + }, new Listener> () { + + @Override + public void execute(ArrayList result) { + // Add the current result if there is one + displayedResults = result; + resultChanged(); + } + + @Override + public void exception(Throwable t) { + t.printStackTrace(); + } + + @Override + public boolean isDisposed() { + return false; + } + + }); + } + + // A dummy call for experiments + public SysdynModel(Resource modelResource) { + this.modelResource = modelResource; + } + + /** + * Get modules + * @return modules + */ + public Set getModules() { + return modules; + } + + /** + * Update mapping. + * + * Use this if inside a transaction. + * + * @param graph ReadGraph + * @return has the range been modified + * @throws DatabaseException + */ + public synchronized boolean update(ReadGraph graph) throws DatabaseException { + if(mapping.isDomainModified()) { + try { + Collection updated = mapping.updateRange(graph); + + for(Object o : updated) { + if(o instanceof Model) { + continue; + } else if(o instanceof Expression && !(o instanceof StockExpression)) { + IndependentVariable variable = ((Expression)o).getParent(); + Variability variability = Variability.getVariability(variable, false, configuration); + if(variability == Variability.PARAMETER) + continue; + } + + // if continue has not been called, the structure has changed + setStructureModified(true); + break; + + } + + } catch (MappingException e) { + SysdynConsole.INSTANCE.message( + "Error: Mapping is broken! Find the problem, " + + "fix it and restart the program." + + "\nJava error message:\n" + + e.getMessage()); + throw e; + } + + // Remove all unnecessary module configurations from modules + Set configs = readModules(graph, configurationResource); + for(Resource config : configs) { + if(!modules.contains(config)) + modules.add((Configuration)mapping.map(graph, config)); + } + + HashSet toBeRemoved = null; + for(Configuration module : modules) { + if(!configs.contains(mapping.inverseGet(module))) { + if(toBeRemoved == null) + toBeRemoved = new HashSet(); + toBeRemoved.add(module); + } + } + if(toBeRemoved != null) + modules.removeAll(toBeRemoved); + return true; + } + else + return false; + } + + /** + * Update mapping. + * + * Use only if not inside a transaction + * @return has range been updated + * @throws DatabaseException + */ + public boolean update() throws DatabaseException { + return session.syncRequest(new Read() { + @Override + public Boolean perform(ReadGraph graph) throws DatabaseException { + return update(graph); + } + }); + } + + /** + * Fires an update to all result listeners and subscriptions + * after results have changed + */ + public void resultChanged() { + IProject project = Simantics.peekProject(); + IExperimentManager manager = project.getHint(IExperimentManager.KEY_EXPERIMENT_MANAGER); + IExperiment active = manager.getActiveExperiment(); + if(active != null && active instanceof SysdynExperiment) { + ((SysdynExperiment)active).resultsChanged(); + } + + updateSubscriptions(); + } + + public void addModificationListener(Runnable listener) { + synchronized(modificationListeners) { + modificationListeners.add(listener); + } + } + + public void removeModificationListener(Runnable listener) { + synchronized(modificationListeners) { + modificationListeners.remove(listener); + } + } + + /** + * Fires all modification listeners after a change in the domain + */ + @Override + public void domainModified() { + synchronized(modificationListeners) { + for(Runnable listener : modificationListeners) + listener.run(); + } + } + + @Override + public void rangeModified() { + } + + public Configuration getConfiguration() { + return configuration; + } + + public Resource getConfigurationResource() { + return configurationResource; + } + + public IMapping getMapping() { + return mapping; + } + + public synchronized IElement getElement(Resource resource) { + return (IElement)mapping.get(resource); + } + + public T getService(Class clazz) { + synchronized(services) { + return clazz.cast(services.get(clazz)); + } + } + + public void addService(Class clazz, T service) { + synchronized(services) { + services.put(clazz, service); + } + } + + @Override + public IExperiment loadExperiment(ReadGraph g, Resource experiment, IExperimentActivationListener listener) { + // Make sure that configurationResource exists + if(configurationResource == null && modelResource != null) { + SimulationResource simu = SimulationResource.getInstance(g); + try { + configurationResource = g.getPossibleObject(modelResource, simu.HasConfiguration); + } catch (ManyObjectsForFunctionalRelationException e) { + e.printStackTrace(); + } catch (ServiceException e) { + e.printStackTrace(); + } + } + + try { + // Create a new experiment based on the experiment resource type + SysdynResource sr = SysdynResource.getInstance(g); + SysdynExperiment exp; + + if(g.isInstanceOf(experiment, sr.PlaybackExperiment)) { + exp = new SysdynPlaybackExperiment(experiment, modelResource); + } else if(g.isInstanceOf(experiment, sr.GameExperiment)) { + exp = Solvers.instantiateGameExperiment(SolverSettings.getSelectedSolverType(), experiment, modelResource); +// if (SolverType.INTERNAL.equals()) { +// exp = new SysdynGameExperimentInternal(experiment, modelResource); +// } +// else { +// exp = new SysdynGameExperiment(experiment, modelResource); +// } + } else if(g.isInstanceOf(experiment, sr.SensitivityAnalysisExperiment)) { + exp = new SysdynSensitivityAnalysisExperiment(experiment, modelResource); + } else if(g.isInstanceOf(experiment, sr.BasicExperiment)) { + // TODO: this is a temporary hack to make sure at least one + // working implementation is available even if the new solver + // architecture is completely broken + if (SolverType.INTERNAL.equals(SolverSettings.getSelectedSolverType())) { + exp = new SysdynExperiment(experiment, modelResource); + } + else { + exp = new OldSysdynExperiment(experiment, modelResource); + } + } else { + if (listener != null) { + listener.onMessage(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Experiment type is not supported: " + NameUtils.getSafeName(g, experiment))); + listener.onFailure(new UnsupportedOperationException("Experiment type is not supported: " + NameUtils.getSafeName(g, experiment))); + } + return null; + } + + // TODO: should maybe get the model from model manager? + + exp.init(g); + + Function2 scl = new FunctionImpl2() { + + @Override + public Object apply(WriteGraph graph, Resource run) { + try { + Layer0 L0 = Layer0.getInstance(graph); + ModelingResources MOD = ModelingResources.getInstance(graph); + Resource session = graph.newResource(); + graph.claim(session, L0.InstanceOf, MOD.SCLCommandSession); + graph.addLiteral(session, L0.HasName, L0.NameOf, L0.String, "__scl__", Bindings.STRING); + graph.claim(run, L0.ConsistsOf, session); + } catch (DatabaseException e) { + e.printStackTrace(); + } + return null; + } + + }; + + VirtualGraphSupport support = g.getSession().getService(VirtualGraphSupport.class); + ExperimentRuns.createRun(g.getSession(), support.getWorkspacePersistent("experiments"), experiment, exp, SysdynResource.URIs.Experiment_Run, listener, scl, null); + return exp; + } catch(Exception e) { + if(listener != null) + listener.onFailure(e); + return null; + } + } + + + /** + * Get active results. To update the active results, use getAndUpdateActiveResults() + * + * @param graph ReadGraph + * @return all active SysdynResults (last update with getAndUpdateActiveResults()) + */ + public Collection getDisplayedResults() { + // TODO: displayedResults are always empty? + if(displayedResults == null) + return new ArrayList(); + else + return displayedResults; + } + + /** + * Get all parameters for Configuration + * + * @param configuration Configuration + * @param prefix String prefix of configuration in this model (Module1.Module2...) + * @return + */ + public HashMap getInits(Configuration configuration, String prefix) { + HashMap inits = new HashMap(); + for (IElement element : configuration.getElements()) { + if (element instanceof Module) { + Module module = (Module) element; + Configuration conf = module.getType().getConfiguration(); + String prfx = prefix + module.getModelicaName() + "."; + inits.putAll(getInits(conf, prfx)); + for(ParameterOverride po : module.getParameterOverrides()) { + inits.put(prfx + po.getVariable().getName(), po.getExpression()); + } + } else if (element instanceof IndependentVariable) { + IndependentVariable variable = (IndependentVariable) element; + //FIXME: more general solution for finding out if the variable is a parameter + if(variable != null && variable.getExpressions() != null && variable.getExpressions().get(0) != null) { + IExpression expression = variable.getExpressions().get(0); + if (expression instanceof ParameterExpression) { + Double value = ((ParameterExpression)expression).getValue(); + if(value != null) + inits.put(prefix + variable.getName(), "" + value); + } + } + } else if(element instanceof Sheet) { + Sheet sheet = (Sheet) element; + for(String cell : sheet.getCells().keySet()) { + inits.put(sheet.getName() + "." + cell, sheet.getCells().get(cell).toString()); + } + } + } + return inits; + } + + + @SuppressWarnings("rawtypes") + /** + * Copy from AprosExperiment + * @param subscription + */ + @Override + public void addVariableValueSubscription(VariableValueSubscription subscription) { + assert subscription != null; + synchronized (variableValueSubscriptions) { + variableValueSubscriptions.add(subscription); + variableValueSubscriptionsSnapshot = null; + } + } + + @SuppressWarnings("rawtypes") + /** + * Copy from AprosExperiment + * @param subscription + */ + @Override + public void removeVariableValueSubscription(VariableValueSubscription subscription) { + assert subscription != null; + synchronized (variableValueSubscriptions) { + 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()]); + } + } + } + return snapshot; + } + + @SuppressWarnings("rawtypes") + /** + * Modified copy from AporsExperiment + */ + @Override + public void updateSubscriptions() { + VariableValueSubscription[] snapShot = getListenerSnapshot(); + for(VariableValueSubscription subscription : snapShot) + subscription.update(); + } + + + public boolean isStructureModified() { + return structureModified; + } + + public void setStructureModified(boolean structureModified) { + this.structureModified = structureModified; + } +}