]> gerrit.simantics Code Review - simantics/sysdyn.git/commitdiff
Experiment activation listener is called twice 05/305/1
authorJussi Koskela <jussi.koskela@semantum.fi>
Wed, 1 Feb 2017 09:43:24 +0000 (11:43 +0200)
committerJussi Koskela <jussi.koskela@semantum.fi>
Wed, 1 Feb 2017 09:43:24 +0000 (11:43 +0200)
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

bundles/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynModel.java

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