From c88c2482d650277cb2fe2e7af860f0c407c44323 Mon Sep 17 00:00:00 2001 From: lempinen Date: Fri, 14 Oct 2011 07:40:50 +0000 Subject: [PATCH] More simulation playback. Results for styles using ExternalRead. git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@22737 ac1ea38d-2e2b-0410-8846-a27921b304fc --- org.simantics.sysdyn.ontology/graph.tg | Bin 69438 -> 69506 bytes .../graph/Profiles.pgraph | 1 + .../org/simantics/sysdyn/SysdynResource.java | 3 + .../profiles/SimulationPlaybackStyle.java | 108 ++++++--- .../SysdynExperimentManagerListener.java | 6 +- .../SysdynPlaybackExperimentListener.java | 215 ++++++++++++++++++ .../sysdyn/ui/utils/ProfileEntries.java | 23 +- .../sysdyn/adapter/HistoryVariable.java | 124 ++++++++-- .../adapter/SysdynVariableProperties.java | 10 + .../simantics/sysdyn/manager/SysdynModel.java | 66 +++++- .../manager/SysdynPlaybackExperiment.java | 3 +- 11 files changed, 484 insertions(+), 75 deletions(-) create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/listeners/SysdynPlaybackExperimentListener.java create mode 100644 org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/SysdynVariableProperties.java diff --git a/org.simantics.sysdyn.ontology/graph.tg b/org.simantics.sysdyn.ontology/graph.tg index ecd1bcebcac4fc74b0b874d50d5dcf47ba90f0a5..a8cc35be2a275f1e0408f3753597ee9c3ce081b4 100644 GIT binary patch literal 69506 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 { +public class SimulationPlaybackStyle extends StyleBase> { - HashMap timeListeners = new HashMap(); - @Override - public Double calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration) throws DatabaseException { + public Pair calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration) throws DatabaseException { IProject project = SimanticsUI.getProject(); IExperimentManager em = project.getHint(IExperimentManager.KEY_EXPERIMENT_MANAGER); @@ -62,26 +63,21 @@ public class SimulationPlaybackStyle extends StyleBase { if(var == null) return null; - Double time = var.getPossiblePropertyValue(graph, Variables.DISPLAY_VALUE, Bindings.DOUBLE); - if(time == null) + Double[] va = var.getPossiblePropertyValue(graph, SysdynVariableProperties.VALUES , Bindings.DOUBLE_ARRAY); + if(va == null || va.length < 2) return null; - - final RecordAccessor acc = var.getInterface(graph, RecordAccessor.class); - if(acc == null) + Double[] ta = var.getPossiblePropertyValue(graph, SysdynVariableProperties.TIMES , Bindings.DOUBLE_ARRAY); + if(ta == null || ta.length < 2) return null; - - ArrayAccessor timeAccessor, valueAccessor; - timeAccessor = acc.getFieldAccessor(1); - valueAccessor = acc.getFieldAccessor(2); - Datatype dt = new org.simantics.databoard.type.ArrayType(new DoubleType()); - - double[] ta = (double[]) timeAccessor.getValue(Bindings.getBinding(dt)); - double[] va = (double[]) valueAccessor.getValue(Bindings.getBinding(dt)); if(va.length == 0 || va.length == 2) return null; + Double time = var.getPossiblePropertyValue(graph, SysdynVariableProperties.TIME , Bindings.DOUBLE); + if(time == null) + return null; + double min = va[0], max = va[0]; for(double d : va) { if(d < min) @@ -104,19 +100,18 @@ public class SimulationPlaybackStyle extends StyleBase { double multiplier = (value - min) / (max - min); -// AffineTransform at = (AffineTransform) DiagramGraphUtil.getAffineTransform(graph, element, G2DResource.getInstance(graph).HasTransform, true).clone(); -// at.translate(0, 10); - return multiplier; + AffineTransform at = (AffineTransform) DiagramGraphUtil.getAffineTransform(graph, element, G2DResource.getInstance(graph).HasTransform, true).clone(); + + return new Pair(at, multiplier); } catch(Exception ignore) { - System.err.println("POIKKEUS SimulationPlaybackStyle"); ignore.printStackTrace(); } return null; } @Override - public void styleResultChanged(Observer observer, Resource element, Double result) { + public void styleResultChanged(Observer observer, Resource element, Pair result) { if (result != null) values.put(element, result); else @@ -125,13 +120,33 @@ public class SimulationPlaybackStyle extends StyleBase { } @Override - public void applyStyleForNode(EvaluationContext observer, INode _node, Double multiplier) { - if (multiplier != null) { - TextNode n = NodeUtil.getNearestChildByClass((SingleElementNode)_node, TextNode.class); - Color c = new Color(multiplier.floatValue(), (float)(0), (float) (1 - multiplier)); + public void applyStyleForNode(EvaluationContext observer, INode _node, Pair result) { + Double multiplier; + if (result != null && (multiplier = result.second) != null && !multiplier.isNaN()) { + + + A node = ProfileVariables.claimChild(_node, "", "playbackColour", A.class, observer); + if (node == null) + return; + + AffineTransform at = result.first; + Color c = new Color(multiplier.floatValue(), (float)(0), (float) (1 - multiplier), (float)0.5); + int zIndex = -1; + INode n = NodeUtil.getNearestChildByClass((SingleElementNode)_node, TextNode.class); if(n != null) { - n.setBackgroundColor(c); + at = ((TextNode)n).getTransform(); + zIndex = ((TextNode)n).getZIndex() - 1; + } else { + n = _node; } + Rectangle2D expandedElementBounds = GeometryUtils.expandRectangle( NodeUtil.getLocalElementBounds(n), 0.0); + node.setZIndex(zIndex); + node.setFill(true); + node.setColor(c); + node.setStroke(null); + node.setValue("shape", expandedElementBounds); + node.setTransform(at); + } else { cleanupStyleForNode(_node); } @@ -139,11 +154,30 @@ public class SimulationPlaybackStyle extends StyleBase { @Override protected void cleanupStyleForNode(INode node) { - if(node instanceof SingleElementNode) { - TextNode n = NodeUtil.getNearestChildByClass((SingleElementNode)node, TextNode.class); - if(n != null) - n.setBackgroundColor(null); + ProfileVariables.denyChild(node, "", "playbackColour"); + } + + + + public static class A extends ShapeNode { + + private static final long serialVersionUID = -5273246617906214956L; + + @Override + public Rectangle2D getBoundsInLocal() { + return null; } + + @Override + public Rectangle2D getBoundsInLocal(boolean b) { + return null; + } + + @Override + public Rectangle2D getBounds() { + return null; + } + } } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/listeners/SysdynExperimentManagerListener.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/listeners/SysdynExperimentManagerListener.java index d631a2d5..98b8f65a 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/listeners/SysdynExperimentManagerListener.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/listeners/SysdynExperimentManagerListener.java @@ -66,10 +66,12 @@ public class SysdynExperimentManagerListener implements IExperimentManagerListen (IContextService)PlatformUI.getWorkbench() .getActiveWorkbenchWindow().getService(IContextService.class); synchronized(contextActivations) { - if(experiment instanceof SysdynPlaybackExperiment) + if(experiment instanceof SysdynPlaybackExperiment) { contextActivations.add(contextService.activateContext(PLAYBACK_EXPERIMENT_CONTEXT)); - else if(experiment instanceof SysdynExperiment) + experiment.addListener(new SysdynPlaybackExperimentListener((SysdynPlaybackExperiment)experiment)); + } else if(experiment instanceof SysdynExperiment) { contextActivations.add(contextService.activateContext(BASIC_EXPERIMENT_CONTEXT)); + } } } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/listeners/SysdynPlaybackExperimentListener.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/listeners/SysdynPlaybackExperimentListener.java new file mode 100644 index 00000000..cb859ccc --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/listeners/SysdynPlaybackExperimentListener.java @@ -0,0 +1,215 @@ +package org.simantics.sysdyn.ui.listeners; + +import java.util.Collection; +import java.util.HashMap; + +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IEditorReference; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.ObjectsWithType; +import org.simantics.db.common.request.PossibleObjectWithType; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.util.Simantics; +import org.simantics.db.service.VirtualGraphSupport; +import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.layer0.Layer0; +import org.simantics.project.IProject; +import org.simantics.simulation.experiment.ExperimentState; +import org.simantics.simulation.experiment.IExperiment; +import org.simantics.simulation.experiment.IExperimentListener; +import org.simantics.simulation.project.IExperimentManager; +import org.simantics.sysdyn.SysdynResource; +import org.simantics.sysdyn.manager.SysdynPlaybackExperiment; +import org.simantics.sysdyn.ui.editor.DiagramViewer; +import org.simantics.ui.SimanticsUI; + +public class SysdynPlaybackExperimentListener implements IExperimentListener { + + private Resource model; + + public SysdynPlaybackExperimentListener(SysdynPlaybackExperiment experiment) { + this.model = experiment.getModel(); + activatePlaybackProfile(); + } + + @Override + public void stateChanged(final ExperimentState state) { + switch(state) { + case DISPOSED: + revertPlaybackProfiles(); + break; + } + } + + class DiagramInfo { + public String modelURI = null; + public Resource previousProfile = null; + } + + HashMap previousRuntimeDiagramsAndModelUris = new HashMap(); + + private void activatePlaybackProfile() { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + + @Override + public void run() { + previousRuntimeDiagramsAndModelUris.clear(); + previousRuntimeDiagramsAndModelUris = getCurrentRuntimeDiagramInfos(); + activatePlaybackProfileRequest(previousRuntimeDiagramsAndModelUris); + } + }); + } + + private void revertPlaybackProfiles() { + + IProject project = SimanticsUI.getProject(); + if(project == null) + return; + IExperimentManager manager = SimanticsUI.getProject().getHint(IExperimentManager.KEY_EXPERIMENT_MANAGER); + IExperiment experiment = manager.getActiveExperiment(); + + if(experiment != null && experiment instanceof SysdynPlaybackExperiment && this.model.equals(experiment.getModel())) + return; + + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + + @Override + public void run() { + final HashMap runtimeDiagramsAndModelUris = getCurrentRuntimeDiagramInfos(); + + VirtualGraphSupport support = Simantics.getSession().getService(VirtualGraphSupport.class); + Simantics.getSession().asyncRequest(new WriteRequest(support.getWorkspacePersistent("profiles")) { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + + Resource model = SysdynPlaybackExperimentListener.this.model; + if(model == null) + return; + + Layer0 l0 = Layer0.getInstance(graph); + SysdynResource sr = SysdynResource.getInstance(graph); + + Collection profiles = graph.syncRequest(new ObjectsWithType(model, l0.ConsistsOf, sr.SimulationPlaybackProfile)); + if(profiles.isEmpty()) + return; + + Resource defaultProfile = profiles.iterator().next(); + + HashMap infos = getRuntimesForModel(graph, runtimeDiagramsAndModelUris, model); + for(Resource runtimeDiagram : infos.keySet()) { + Resource previous = null; + if(previousRuntimeDiagramsAndModelUris.containsKey(runtimeDiagram)) { + previous = previousRuntimeDiagramsAndModelUris.get(runtimeDiagram).previousProfile; + } + setProfile(graph, model, runtimeDiagram, previous == null ? defaultProfile : previous); + } + } + }); + + } + }); + } + + /** + * Run in display thread. + * + * @return + */ + private HashMap getCurrentRuntimeDiagramInfos() { + HashMap runtimeDiagramsAndModelUris = new HashMap(); + IWorkbench workBench = PlatformUI.getWorkbench(); + IWorkbenchWindow window = workBench.getActiveWorkbenchWindow(); + IWorkbenchPage[] pages = window.getPages(); + for(IWorkbenchPage page : pages) { + for(IEditorReference reference : page.getEditorReferences()) { + IEditorPart iep = reference.getEditor(false); + if(iep instanceof DiagramViewer) { + DiagramViewer viewer = (DiagramViewer) iep; + Resource runtime = viewer.getRuntime(); + String modelUri = viewer.getResourceInput2().getModelURI(); + DiagramInfo info = new DiagramInfo(); + info.modelURI = modelUri; + runtimeDiagramsAndModelUris.put(runtime, info); + } + } + } + return runtimeDiagramsAndModelUris; + } + + + private HashMap getRuntimesForModel(WriteGraph graph, HashMap allInfos, Resource model) throws DatabaseException { + HashMap runtimeDiagramsAndModelUris = new HashMap(); + for(Resource runtimeDiagram : allInfos.keySet()) { + DiagramInfo di = allInfos.get(runtimeDiagram); + if(di == null) + continue; + Resource m = graph.getPossibleResource(di.modelURI); + if(m == null || !model.equals(m)) + continue; + + runtimeDiagramsAndModelUris.put(runtimeDiagram, di); + } + return runtimeDiagramsAndModelUris; + } + + private void activatePlaybackProfileRequest(final HashMap allDiagramInfos) { + VirtualGraphSupport support = Simantics.getSession().getService(VirtualGraphSupport.class); + Simantics.getSession().asyncRequest(new WriteRequest(support.getWorkspacePersistent("profiles")) { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + + Resource model = SysdynPlaybackExperimentListener.this.model; + if(model == null) + return; + + Layer0 l0 = Layer0.getInstance(graph); + SysdynResource sr = SysdynResource.getInstance(graph); + + Resource profile = graph.syncRequest(new PossibleObjectWithType(model, l0.ConsistsOf, sr.SimulationPlaybackProfile)); + if(profile == null) + return; + + DiagramResource DIA = DiagramResource.getInstance(graph); + + HashMap infos = getRuntimesForModel(graph, allDiagramInfos, model); + for(Resource runtimeDiagram : infos.keySet()) { + DiagramInfo di = infos.get(runtimeDiagram); + Resource current = graph.getPossibleObject(runtimeDiagram, DIA.RuntimeDiagram_HasRuntimeProfile); + if(profile.equals(current)) + continue; + di.previousProfile = current; + setProfile(graph, model, runtimeDiagram, profile); + } + } + }); + } + + + private void setProfile(WriteGraph graph, Resource model, Resource runtimeDiagram, Resource profile) throws DatabaseException { + DiagramResource DIA = DiagramResource.getInstance(graph); + + Resource current = graph.getPossibleObject(runtimeDiagram, DIA.RuntimeDiagram_HasRuntimeProfile); + if(profile.equals(current)) return; + + graph.deny(runtimeDiagram, DIA.RuntimeDiagram_HasRuntimeProfile, null, current); + graph.claim(runtimeDiagram, DIA.RuntimeDiagram_HasRuntimeProfile, null, profile); + + // Set this profile as the default profile for this model + graph.deny(model, DIA.HasActiveProfile); + graph.claim(model, DIA.HasActiveProfile, profile); + + // Set this profile as the default profile for this diagram + Resource configuration = graph.getPossibleObject(runtimeDiagram, DIA.RuntimeDiagram_HasConfiguration); + graph.deny(configuration, DIA.HasActiveProfile); + graph.claim(configuration, DIA.HasActiveProfile, profile); + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ProfileEntries.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ProfileEntries.java index 597f07b3..265aed91 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ProfileEntries.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ProfileEntries.java @@ -10,21 +10,13 @@ import org.simantics.sysdyn.SysdynResource; public class ProfileEntries { - public static Resource createWorkProfile(WriteGraph graph, String name) throws DatabaseException { - - SysdynResource sr = SysdynResource.getInstance(graph); - - return Profiles.createProfile(graph, name, - sr.Profiles_SimulationPlaybackColours); - - } public static void createStandardProfiles(WriteGraph graph, final Resource model) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); DiagramResource DIA = DiagramResource.getInstance(graph); - final Resource a = createWorkProfile(graph, "Simulation Playback"); + final Resource a = createSimulationPlaybackProfile(graph); Resource plain = Profiles.createProfile(graph, "Plain"); @@ -42,5 +34,18 @@ public class ProfileEntries { // }); } + + public static Resource createSimulationPlaybackProfile(WriteGraph graph) + throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + SysdynResource sr = SysdynResource.getInstance(graph); + + Resource profile = Profiles.createProfile(graph, "Simulation Playback", + sr.Profiles_SimulationPlaybackColours); + + graph.deny(profile, L0.InstanceOf); + graph.claim(profile, L0.InstanceOf, null, sr.SimulationPlaybackProfile); + return profile; + } } diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/HistoryVariable.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/HistoryVariable.java index 76c98f91..7ef3946c 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/HistoryVariable.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/HistoryVariable.java @@ -36,6 +36,7 @@ import org.simantics.db.layer0.variable.Variables; import org.simantics.db.procedure.Listener; import org.simantics.db.request.ExternalRead; import org.simantics.layer0.Layer0; +import org.simantics.simulation.experiment.ExperimentState; import org.simantics.simulation.ontology.SimulationResource; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.sysdyn.SysdynResource; @@ -48,7 +49,11 @@ import org.simantics.sysdyn.manager.SysdynResult; public class HistoryVariable extends ChildVariable implements PropertyProvider { + static Boolean DEBUG = false; + SysdynExperiment experiment; + SysdynModel model = null; + String rvi = null; public HistoryVariable(Variable parent, Resource resource) { super(parent, resource); @@ -67,7 +72,8 @@ public class HistoryVariable extends ChildVariable implements PropertyProvider { SysdynResult sr = new SysdynResult(sm.getSimulationResult()); // TODO: copy or not to copy ... String tmp = Variables.getRVI(graph, this); - System.out.println("HistoryVariable rvi='" + tmp + "'"); + if(DEBUG) + System.out.println("HistoryVariable rvi='" + tmp + "'"); final String rvi = tmp.substring(1).replace("/", "."); SysdynDataSet ds = sr.getDataSet(rvi); if(ds == null) ds = new SysdynDataSet("", "", new ArrayList(), new ArrayList()); // We need a dataset, so if not set, create it @@ -77,6 +83,9 @@ public class HistoryVariable extends ChildVariable implements PropertyProvider { sm.addResultListener(new Runnable() { // FIXME: remove listener at some point.. @Override public void run() { + if(HistoryVariable.this.experiment.getState().equals(ExperimentState.DISPOSED)) + return; + SysdynResult sr = new SysdynResult(sm.getSimulationResult()); SysdynDataSet ds = sr.getDataSet(rvi); if(ds == null) return; @@ -157,50 +166,117 @@ public class HistoryVariable extends ChildVariable implements PropertyProvider { @Override - public Variable getPossibleExtraProperty(ReadGraph graph, final String name) throws DatabaseException { - if(Variables.DISPLAY_VALUE.equals(name)) { - return graph.syncRequest(new ParametrizedPrimitiveRead(this) { - VariableValueSubscription subscription; - @Override - public void register(Listener procedure) { - subscription = registerSubscription(this, procedure, name); - } - @Override - public void unregistered() { - unregisterSubscription(subscription); - subscription = null; - } - }); + public Variable getPossibleExtraProperty(ReadGraph graph, String name) throws DatabaseException { + if(SysdynVariableProperties.TIME.equals(name)) { + return graph.syncRequest(new PropertyRequest(this, name)); + } else if(SysdynVariableProperties.VALUES.equals(name) || SysdynVariableProperties.TIMES.equals(name)) { + if(model == null) { + SimulationResource SIMU = SimulationResource.getInstance(graph); + Resource modelResource = Variables.getModel(graph, this); + Resource configuration = graph.getPossibleObject(modelResource, SIMU.HasConfiguration); + model = SysdynModelManager.getInstance(graph.getSession()).getModel(graph, configuration); + } + if(rvi == null) { + rvi = Variables.getRVI(graph, this).substring(1).replace("/", "."); + } + return graph.syncRequest(new PropertyRequest(this, name)); } return super.getPossibleExtraProperty(graph, name); } - + protected VariableValueSubscription registerSubscription(ExternalRead request, Listener procedure, String property) { - if(experiment instanceof SysdynPlaybackExperiment) { + if(SysdynVariableProperties.TIME.equals(property) && experiment instanceof SysdynPlaybackExperiment) { VariableValueSubscription subscription = new VariableValueSubscription(request, this, property, procedure); experiment.addVariableValueSubscription(subscription); subscription.update(); return subscription; - } else { + } else if(SysdynVariableProperties.VALUES.equals(property) && model != null) { + VariableValueSubscription subscription = new VariableValueSubscription(request, this, property, procedure); + model.addVariableValueSubscription(subscription); + subscription.update(); + return subscription; + } else if(SysdynVariableProperties.TIMES.equals(property) && model != null) { + VariableValueSubscription subscription = new VariableValueSubscription(request, this, property, procedure); + model.addVariableValueSubscription(subscription); + subscription.update(); + return subscription; + } else { return null; } - } + } protected void unregisterSubscription(VariableValueSubscription subscription) { subscription.setListener(null); - experiment.removeVariableValueSubscription(subscription); + if(SysdynVariableProperties.TIME.equals(subscription.property)) + experiment.removeVariableValueSubscription(subscription); + else if(SysdynVariableProperties.TIMES.equals(subscription.property) + || SysdynVariableProperties.VALUES.equals(subscription.property)) + model.removeVariableValueSubscription(subscription); } @Override public Variable getProperty(String name) { - if(Variables.DISPLAY_VALUE.equals(name)){ + if(SysdynVariableProperties.TIME.equals(name)){ if(experiment instanceof SysdynPlaybackExperiment) { SysdynPlaybackExperiment exp = (SysdynPlaybackExperiment) experiment; - return new ConstantPropertyVariable(parent, name, exp.getTime(), Datatypes.DOUBLE); + return new ConstantPropertyVariable(this, name, exp.getTime(), Datatypes.DOUBLE); } else { - return new ConstantPropertyVariable(parent, name, 0, Datatypes.DOUBLE); + return new ConstantPropertyVariable(this, name, 0, Datatypes.DOUBLE); } - } + } else if(SysdynVariableProperties.VALUES.equals(name)) { + SysdynResult sr = new SysdynResult(model.getSimulationResult()); + SysdynDataSet ds = sr.getDataSet(rvi); + if(ds == null) + return new ConstantPropertyVariable(this, name, new Double[0], Datatypes.DOUBLE_ARRAY); + else + return new ConstantPropertyVariable(this, name, ds.values.toArray(new Double[ds.values.size()]), Datatypes.DOUBLE_ARRAY); + } else if(SysdynVariableProperties.TIMES.equals(name)) { + SysdynResult sr = new SysdynResult(model.getSimulationResult()); + SysdynDataSet ds = sr.getDataSet(rvi); + if(ds == null) + return new ConstantPropertyVariable(this, name, new Double[0], Datatypes.DOUBLE_ARRAY); + else + return new ConstantPropertyVariable(this, name, ds.times.toArray(new Double[ds.times.size()]), Datatypes.DOUBLE_ARRAY); + } return null; } + + /** + * Class for supporting requests with different property parameters. Equals-method has been modified + * from ParametrizedPrivimiteRead to check also the property value. + * + * @author tlteemu + * + */ + class PropertyRequest extends ParametrizedPrimitiveRead { + + String property; + + public PropertyRequest(Variable parameter, String property) { + super(parameter); + this.property = property; + } + + VariableValueSubscription subscription; + + @Override + public void register(Listener procedure) { + subscription = registerSubscription(this, procedure, property); + } + @Override + public void unregistered() { + unregisterSubscription(subscription); + subscription = null; + } + + @Override + public boolean equals(Object object) { + if(object instanceof PropertyRequest && super.equals(object)) { + return this.property.equals(((PropertyRequest)object).property); + } else { + return super.equals(object); + } + } + + } } diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/SysdynVariableProperties.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/SysdynVariableProperties.java new file mode 100644 index 00000000..102348c9 --- /dev/null +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/SysdynVariableProperties.java @@ -0,0 +1,10 @@ +package org.simantics.sysdyn.adapter; + +public class SysdynVariableProperties { + + final public static String VALUE = "VALUE"; + final public static String VALUES = "VALUES"; + final public static String TIME = "TIME"; + final public static String TIMES = "TIMES"; + +} diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynModel.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynModel.java index 1a3cfe71..56bd084a 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynModel.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynModel.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.simantics.sysdyn.manager; +import gnu.trove.set.hash.THashSet; + import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; @@ -55,6 +57,7 @@ import org.simantics.simulation.project.IExperimentActivationListener; 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.modelica.ModelicaWriter; import org.simantics.sysdyn.representation.Configuration; import org.simantics.sysdyn.representation.IElement; @@ -91,6 +94,9 @@ public class SysdynModel implements IMappingListener, IModel { new CopyOnWriteArrayList(); private CopyOnWriteArrayList resultListeners = new CopyOnWriteArrayList(); + + protected THashSet variableValueSubscriptions = new THashSet(); + protected volatile VariableValueSubscription[] variableValueSubscriptionsSnapshot = null; @SuppressWarnings("rawtypes") private Map services = new HashMap(); @@ -395,10 +401,11 @@ public class SysdynModel implements IMappingListener, IModel { public void resultChanged() { synchronized(resultListeners) { for(Runnable listener : resultListeners) { - System.out.println("Run resultListener"); listener.run(); } } + + updateSubscriptions(); } public void addModificationListener(Runnable listener) { @@ -568,5 +575,62 @@ public class SysdynModel implements IMappingListener, IModel { } return simulationDir; } + + + + + + /** + * Copy from AprosExperiment + * @param subscription + */ + public void addVariableValueSubscription(VariableValueSubscription subscription) { + assert subscription != null; + synchronized (variableValueSubscriptions) { + //System.out.println("ADD listener " + subscription); + variableValueSubscriptions.add(subscription); + variableValueSubscriptionsSnapshot = null; + } + } + + /** + * Copy from AprosExperiment + * @param subscription + */ + public void removeVariableValueSubscription(VariableValueSubscription subscription) { + assert subscription != null; + synchronized (variableValueSubscriptions) { + //System.out.println("REMOVE listener " + subscription); + variableValueSubscriptions.remove(subscription); + variableValueSubscriptionsSnapshot = null; + } + } + + /** + * Copy from AprosExperiment + * @return + */ + private VariableValueSubscription[] getListenerSnapshot() { + VariableValueSubscription[] snapshot = variableValueSubscriptionsSnapshot; + if (snapshot == null) { + synchronized (variableValueSubscriptions) { + snapshot = variableValueSubscriptionsSnapshot; + if (snapshot == null) { + snapshot = variableValueSubscriptionsSnapshot = variableValueSubscriptions.toArray(new VariableValueSubscription[variableValueSubscriptions.size()]); + } + } + //System.out.println("listener count: " + snapshot.length); + } + return snapshot; + } + + /** + * Modified copy from AporsExperiment + */ + private void updateSubscriptions() { + VariableValueSubscription[] snapShot = getListenerSnapshot(); + for(VariableValueSubscription subscription : snapShot) + subscription.update(); + } } diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynPlaybackExperiment.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynPlaybackExperiment.java index 416ce05c..870418ea 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynPlaybackExperiment.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynPlaybackExperiment.java @@ -34,12 +34,11 @@ public class SysdynPlaybackExperiment extends SysdynExperiment implements IDynam @Override public void init(ReadGraph g) { this.session = g.getSession(); - changeState(ExperimentState.STOPPED); - session.asyncRequest(new ReadRequest() { @Override public void run(ReadGraph graph) throws DatabaseException { + changeState(ExperimentState.RUNNING); final Resource configuration = graph.getPossibleObject(model, SimulationResource.getInstance(graph).HasConfiguration); sysdynModel = SysdynModelManager.getInstance(session).getModel(graph, configuration); toggleActivation(graph, true); -- 2.47.1