-/*******************************************************************************\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;
+ }
+}