From: lempinen Date: Wed, 12 Oct 2011 11:39:46 +0000 (+0000) Subject: First functional prototype for the simulation playback feature: Separate experiment... X-Git-Tag: simantics-1.5~13 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=073e84913268160550622f0587e8042000168f43;p=simantics%2Fsysdyn.git First functional prototype for the simulation playback feature: Separate experiment type for playback experiments and a profile to show changes in variables git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@22681 ac1ea38d-2e2b-0410-8846-a27921b304fc --- diff --git a/org.simantics.sysdyn.ontology/graph.tg b/org.simantics.sysdyn.ontology/graph.tg index 11db7b9e..ecd1bceb 100644 Binary files a/org.simantics.sysdyn.ontology/graph.tg and b/org.simantics.sysdyn.ontology/graph.tg differ diff --git a/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph b/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph index 8592edaf..7974a94b 100644 --- a/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph +++ b/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph @@ -350,6 +350,8 @@ SYSDYN.GameExperiment + + @@ -266,6 +270,25 @@ + + + + + + + + + + + + @@ -335,6 +358,22 @@ + + + + + + + + + + @@ -1015,6 +1058,10 @@ class="org.simantics.sysdyn.ui.handlers.NewModelHandler" commandId="org.simantics.sysdyn.ui.newModel"> + + diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/profiles/SimulationPlaybackStyle.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/profiles/SimulationPlaybackStyle.java index 26aa3682..1dcf494e 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/profiles/SimulationPlaybackStyle.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/profiles/SimulationPlaybackStyle.java @@ -1,97 +1,122 @@ package org.simantics.sysdyn.ui.elements2.profiles; -import java.awt.BasicStroke; import java.awt.Color; -import java.awt.geom.AffineTransform; -import java.awt.geom.Rectangle2D; +import java.util.HashMap; +import org.simantics.databoard.Bindings; +import org.simantics.databoard.accessor.ArrayAccessor; +import org.simantics.databoard.accessor.RecordAccessor; +import org.simantics.databoard.type.Datatype; +import org.simantics.databoard.type.DoubleType; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; -import org.simantics.db.common.request.ObjectsWithType; -import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.layer0.variable.Variables; +import org.simantics.diagram.elements.TextNode; import org.simantics.diagram.profile.StyleBase; -import org.simantics.diagram.stubs.G2DResource; -import org.simantics.diagram.synchronization.graph.DiagramGraphUtil; -import org.simantics.issues.ontology.IssueResource; -import org.simantics.layer0.Layer0; +import org.simantics.diagram.stubs.DiagramResource; import org.simantics.modeling.ModelingResources; +import org.simantics.project.IProject; import org.simantics.scenegraph.INode; -import org.simantics.scenegraph.g2d.nodes.ShapeNode; +import org.simantics.scenegraph.g2d.nodes.SingleElementNode; import org.simantics.scenegraph.profile.EvaluationContext; import org.simantics.scenegraph.profile.Observer; -import org.simantics.scenegraph.profile.common.ProfileVariables; -import org.simantics.scenegraph.utils.GeometryUtils; import org.simantics.scenegraph.utils.NodeUtil; -import org.simantics.sysdyn.SysdynResource; +import org.simantics.simulation.experiment.IExperiment; +import org.simantics.simulation.project.IExperimentManager; +import org.simantics.sysdyn.manager.SysdynPlaybackExperiment; +import org.simantics.ui.SimanticsUI; -public class SimulationPlaybackStyle extends StyleBase { - - 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; - } - - } +public class SimulationPlaybackStyle extends StyleBase { + HashMap timeListeners = new HashMap(); + @Override - public AffineTransform calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration) throws DatabaseException { -// TODO: Get results from variable -// DiagramResource dr = DiagramResource.getInstance(graph); -// String rvi = graph.getPossibleRelatedValue(runtimeDiagram, dr.RuntimeDiagram_HasRVI, Bindings.STRING); + public Double calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration) throws DatabaseException { - Layer0 l0 = Layer0.getInstance(graph); - SysdynResource sr = SysdynResource.getInstance(graph); - ModelingResources mr = ModelingResources.getInstance(graph); - IssueResource issue = IssueResource.getInstance(graph); + IProject project = SimanticsUI.getProject(); + IExperimentManager em = project.getHint(IExperimentManager.KEY_EXPERIMENT_MANAGER); + IExperiment experiment = em.getActiveExperiment(); + if(!(experiment instanceof SysdynPlaybackExperiment)) + return null; + ModelingResources mr = ModelingResources.getInstance(graph); + DiagramResource dr = DiagramResource.getInstance(graph); + Resource component = graph.getPossibleObject(element, mr.ElementToComponent); if (component == null) return null; - - Resource config = graph.getPossibleObject(component, l0.PartOf); - - Resource model = graph.getPossibleObject(config, l0.PartOf); - if(!graph.isInstanceOf(model, sr.SysdynModel)) - return null; - - for(Resource i : graph.syncRequest(new ObjectsWithType(model, l0.ConsistsOf, issue.Issue))) { - System.out.println(NameUtils.getSafeName(graph, graph.getSingleObject(i, issue.HasIssueContext))); - if(graph.getSingleObject(i, issue.HasIssueContext).equals(component)) { - AffineTransform at = DiagramGraphUtil.getAffineTransform(graph, element, G2DResource.getInstance(graph).HasTransform, true); - return at; + + try { + + String dv = graph.getPossibleRelatedValue(runtimeDiagram, dr.RuntimeDiagram_HasVariable); + Variable rootVariable = graph.getPossibleAdapter(graph.getRootLibrary(), Variable.class); + if (rootVariable == null) + return null; + Variable diagramVariable = rootVariable.browsePossible(graph, dv.substring(6)); + if(diagramVariable == null) + return null; + + Variable var = diagramVariable.browsePossible(graph, component); + if(var == null) + return null; + + Double time = var.getPossiblePropertyValue(graph, Variables.DISPLAY_VALUE, Bindings.DOUBLE); + if(time == null) + return null; + + + final RecordAccessor acc = var.getInterface(graph, RecordAccessor.class); + if(acc == null) + 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 min = va[0], max = va[0]; + for(double d : va) { + if(d < min) + min = d; + if(d > max) + max = d; + } + + int index = 0; + if(ta[ta.length - 1] - time > ta[ta.length / 2] ) { + index = ta.length - 1; + while(ta[index] > time) + index--; + } else { + while(ta[index] < time) + index++; } + + double value = va[index]; + + 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; + + } catch(Exception ignore) { + System.err.println("POIKKEUS SimulationPlaybackStyle"); + ignore.printStackTrace(); } - return null; -// String name = graph.getPossibleRelatedValue(config, l0.HasName, Bindings.STRING); -// if(name == null) -// return null; -// if(name.startsWith("A")) { -// AffineTransform at = DiagramGraphUtil.getAffineTransform(graph, element, G2DResource.getInstance(graph).HasTransform, true); -// return at; -// } else { -// return null; -// } } @Override - public void styleResultChanged(Observer observer, Resource element, AffineTransform result) { + public void styleResultChanged(Observer observer, Resource element, Double result) { if (result != null) values.put(element, result); else @@ -100,19 +125,13 @@ public class SimulationPlaybackStyle extends StyleBase { } @Override - public void applyStyleForNode(EvaluationContext observer, INode _node, AffineTransform transform) { - if (transform != null) { - A node = ProfileVariables.claimChild(_node, "", "simulationPlaybackColour", A.class, observer); - if (node == null) - return; - - Rectangle2D expandedElementBounds = GeometryUtils.expandRectangle( NodeUtil.getLocalElementBounds(_node), 0.5 ); - node.setTransform(transform); - node.setZIndex(-1000); - node.setFill(true); - node.setColor(Color.LIGHT_GRAY); - node.setStroke(new BasicStroke(0.2f)); - node.setValue("shape", expandedElementBounds); + 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)); + if(n != null) { + n.setBackgroundColor(c); + } } else { cleanupStyleForNode(_node); } @@ -120,8 +139,11 @@ public class SimulationPlaybackStyle extends StyleBase { @Override protected void cleanupStyleForNode(INode node) { - ProfileVariables.denyChild(node, "", "simulationPlaybackColour"); + if(node instanceof SingleElementNode) { + TextNode n = NodeUtil.getNearestChildByClass((SingleElementNode)node, TextNode.class); + if(n != null) + n.setBackgroundColor(null); + } } - } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/handlers/NewExperimentNodeHandler.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/handlers/NewExperimentNodeHandler.java index 412a4251..16bcd5ec 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/handlers/NewExperimentNodeHandler.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/handlers/NewExperimentNodeHandler.java @@ -16,6 +16,7 @@ import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.jface.viewers.ISelection; import org.eclipse.ui.handlers.HandlerUtil; +import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.WriteGraph; import org.simantics.db.common.request.WriteRequest; @@ -24,7 +25,7 @@ import org.simantics.db.exception.DatabaseException; import org.simantics.document.DocumentResource; import org.simantics.layer0.Layer0; import org.simantics.layer0.utils.direct.GraphUtils; -import org.simantics.simulation.ontology.SimulationResource; +import org.simantics.sysdyn.SysdynResource; import org.simantics.sysdyn.ui.browser.nodes.ExperimentsFolder; import org.simantics.ui.SimanticsUI; import org.simantics.ui.utils.AdaptionUtils; @@ -51,10 +52,10 @@ public class NewExperimentNodeHandler extends AbstractHandler { DocumentResource DOC = DocumentResource.getInstance(g); Resource report = GraphUtils.create2(g, DOC.Report, DOC.HasDocumentation, "===Report==="); - String name = NameUtils.findFreshName(g, "Experiment", model, l0.ConsistsOf, "%s%d"); + String name = NameUtils.findFreshName(g, getNameSuggestion(), model, l0.ConsistsOf, "%s%d"); @SuppressWarnings("unused") - Resource experiment = GraphUtils.create2(g, SimulationResource.getInstance(g).Experiment, + Resource experiment = GraphUtils.create2(g, getExperimentType(g), l0.HasName, name, l0.HasLabel, name, DOC.HasReportFactory, report, @@ -63,5 +64,13 @@ public class NewExperimentNodeHandler extends AbstractHandler { }); return null; } + + protected Resource getExperimentType(ReadGraph g) { + return SysdynResource.getInstance(g).BasicExperiment; + } + + protected String getNameSuggestion() { + return "Experiment"; + } } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/handlers/NewPlaybackExperimentNodeHandler.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/handlers/NewPlaybackExperimentNodeHandler.java new file mode 100644 index 00000000..417f3266 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/handlers/NewPlaybackExperimentNodeHandler.java @@ -0,0 +1,17 @@ +package org.simantics.sysdyn.ui.handlers; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.sysdyn.SysdynResource; + + +public class NewPlaybackExperimentNodeHandler extends NewExperimentNodeHandler { + + protected Resource getExperimentType(ReadGraph g) { + return SysdynResource.getInstance(g).PlaybackExperiment; + } + + protected String getNameSuggestion() { + return "Playback Experiment"; + } +} \ No newline at end of file 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 ba27f53a..d631a2d5 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 @@ -23,11 +23,14 @@ import org.eclipse.ui.contexts.IContextService; import org.simantics.simulation.experiment.IExperiment; import org.simantics.simulation.project.IExperimentManager; import org.simantics.simulation.project.IExperimentManagerListener; +import org.simantics.sysdyn.manager.SysdynExperiment; +import org.simantics.sysdyn.manager.SysdynPlaybackExperiment; public class SysdynExperimentManagerListener implements IExperimentManagerListener{ public static final String BASIC_EXPERIMENT_CONTEXT = "org.simantics.sysdyn.ui.basicExperiment"; - + public static final String PLAYBACK_EXPERIMENT_CONTEXT = "org.simantics.sysdyn.ui.playbackExperiment"; + static Set managers = new HashSet(); @@ -63,7 +66,10 @@ public class SysdynExperimentManagerListener implements IExperimentManagerListen (IContextService)PlatformUI.getWorkbench() .getActiveWorkbenchWindow().getService(IContextService.class); synchronized(contextActivations) { - contextActivations.add(contextService.activateContext(BASIC_EXPERIMENT_CONTEXT)); + if(experiment instanceof SysdynPlaybackExperiment) + contextActivations.add(contextService.activateContext(PLAYBACK_EXPERIMENT_CONTEXT)); + else if(experiment instanceof SysdynExperiment) + contextActivations.add(contextService.activateContext(BASIC_EXPERIMENT_CONTEXT)); } } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/menu/PlaybackSliderContribution.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/menu/PlaybackSliderContribution.java new file mode 100644 index 00000000..04596c12 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/menu/PlaybackSliderContribution.java @@ -0,0 +1,98 @@ +package org.simantics.sysdyn.ui.menu; + +import org.eclipse.jface.action.ToolBarContributionItem; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Slider; +import org.eclipse.swt.widgets.ToolBar; +import org.eclipse.swt.widgets.ToolItem; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.request.Read; +import org.simantics.simulation.experiment.IExperiment; +import org.simantics.simulation.project.IExperimentManager; +import org.simantics.sysdyn.SysdynResource; +import org.simantics.sysdyn.manager.SysdynPlaybackExperiment; +import org.simantics.ui.SimanticsUI; + +public class PlaybackSliderContribution extends ToolBarContributionItem { + + + @Override + public void fill(ToolBar parent, int index) + { + if (parent != null) { + + IExperimentManager manager = SimanticsUI.getProject().getHint(IExperimentManager.KEY_EXPERIMENT_MANAGER); + IExperiment experiment = manager.getActiveExperiment(); + if(experiment == null || !(experiment instanceof SysdynPlaybackExperiment)) + return; + final SysdynPlaybackExperiment spe = (SysdynPlaybackExperiment)experiment; + + Double[] numbers = new Double[3]; + try { + numbers = SimanticsUI.getSession().syncRequest(new Read() { + @Override + public Double[] perform(ReadGraph graph) throws DatabaseException { + Double[] numbers = new Double[3]; + Resource model = spe.getModel(); + SysdynResource sr = SysdynResource.getInstance(graph); + numbers[0] = graph.getRelatedValue(model, sr.HasStartTime); + numbers[1] = graph.getRelatedValue(model, sr.HasStopTime); + numbers[2] = graph.getPossibleRelatedValue(model, sr.HasOutputInterval); + return numbers; + } + }); + } catch (DatabaseException e1) { + e1.printStackTrace(); + } + + ToolItem ti = new ToolItem(parent, SWT.SEPARATOR); + + Composite composite = new Composite(parent, SWT.NONE); + GridLayoutFactory.fillDefaults().numColumns(2).applyTo(composite); + GridDataFactory.fillDefaults().applyTo(composite); + + Slider s = new Slider(composite, SWT.NONE); + s.setMinimum(0); + s.setMaximum(100); + s.setIncrement(1); + s.setPageIncrement(1); + s.setThumb(1); + + final Label label = new Label(composite, SWT.NONE); + GridDataFactory.fillDefaults().hint(50, SWT.DEFAULT).applyTo(label); + label.setText("0.0"); + + ti.setWidth(250); + ti.setControl(composite); + + final Double startTime = numbers[0]; + final Double endTime = numbers[1]; + + + s.addSelectionListener(new SelectionListener() { + + @Override + public void widgetSelected(SelectionEvent e) { + Slider s = (Slider)e.widget; + Double time = s.getSelection() / 99.0 * (endTime - startTime) + startTime; + spe.setTime(time); + if(!label.isDisposed()) { + label.setText(time.toString()); + } + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + }); + } + } +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ModelUtils.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ModelUtils.java index 05a1b500..cfa0f15f 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ModelUtils.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ModelUtils.java @@ -116,7 +116,7 @@ public class ModelUtils { Resource report = GraphUtils.create2(g, DOC.Report, DOC.HasDocumentation, "===Report==="); - GraphUtils.create2(g, mu.SIMU.Experiment, + GraphUtils.create2(g, sr.BasicExperiment, l0.HasName, "Experiment", l0.HasLabel, "Experiment", DOC.HasReportFactory, report, 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 d285d393..597f07b3 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 @@ -24,7 +24,7 @@ public class ProfileEntries { Layer0 L0 = Layer0.getInstance(graph); DiagramResource DIA = DiagramResource.getInstance(graph); - final Resource a = createWorkProfile(graph, "A"); + final Resource a = createWorkProfile(graph, "Simulation Playback"); Resource plain = Profiles.createProfile(graph, "Plain"); diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/DependencyFunction.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/DependencyFunction.java index 828f722d..edd32658 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/DependencyFunction.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/DependencyFunction.java @@ -84,7 +84,6 @@ public class DependencyFunction { return Boolean.FALSE; } } - System.out.println("NOTHING"); return Boolean.TRUE; } @@ -104,11 +103,8 @@ public class DependencyFunction { String name = graph.getRelatedValue(variable, L0.HasName, Bindings.STRING); - System.out.println("SOME CHANGE IN " + name + "(existing: " + existing.size() + ")"); - Set labels = new HashSet(); String label; - Resource issue; for(String reference : references) { label = getMissingLinkLabel(name, reference); @@ -159,7 +155,6 @@ public class DependencyFunction { } private static Resource createIssue(WriteGraph graph, Resource model, Resource source, Resource variable, String label) throws DatabaseException { - System.out.println("CREATE ISSUE: " + label); Layer0 L0 = Layer0.getInstance(graph); IssueResource ISSUE = IssueResource.getInstance(graph); @@ -178,7 +173,6 @@ public class DependencyFunction { } private static void removeIssue(WriteGraph graph, Resource model, Resource source, Resource variable, Resource issue, Collection existing) throws DatabaseException { - System.out.println("REMOVE ISSUE: " + graph.getRelatedValue(issue, Layer0.getInstance(graph).HasLabel)); graph.deny(issue, Layer0.getInstance(graph).PartOf); graph.deny(source, IssueResource.getInstance(graph).Manages, issue); existing.remove(issue); 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 1b0dfe75..76c98f91 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/HistoryVariable.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/HistoryVariable.java @@ -17,6 +17,7 @@ import java.util.Map; import org.simantics.databoard.Accessors; import org.simantics.databoard.Bindings; +import org.simantics.databoard.Datatypes; import org.simantics.databoard.accessor.Accessor; import org.simantics.databoard.accessor.RecordAccessor; import org.simantics.databoard.accessor.error.AccessorConstructionException; @@ -24,25 +25,34 @@ import org.simantics.databoard.accessor.error.AccessorException; import org.simantics.databoard.binding.error.RuntimeBindingConstructionException; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; +import org.simantics.db.common.request.ParametrizedPrimitiveRead; import org.simantics.db.common.uri.UnescapedChildMapOfResource; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.exception.MissingVariableException; +import org.simantics.db.layer0.variable.ConstantPropertyVariable; import org.simantics.db.layer0.variable.Variable; 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.ontology.SimulationResource; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.sysdyn.SysdynResource; import org.simantics.sysdyn.manager.SysdynDataSet; +import org.simantics.sysdyn.manager.SysdynExperiment; import org.simantics.sysdyn.manager.SysdynModel; import org.simantics.sysdyn.manager.SysdynModelManager; +import org.simantics.sysdyn.manager.SysdynPlaybackExperiment; import org.simantics.sysdyn.manager.SysdynResult; -public class HistoryVariable extends ChildVariable { +public class HistoryVariable extends ChildVariable implements PropertyProvider { + SysdynExperiment experiment; + public HistoryVariable(Variable parent, Resource resource) { super(parent, resource); + experiment = SysdynExperiment.INSTANCE; } @@ -143,5 +153,54 @@ public class HistoryVariable extends ChildVariable { } else { return super.browseChildren(graph); } - } + } + + + @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; + } + }); + } + return super.getPossibleExtraProperty(graph, name); + } + + protected VariableValueSubscription registerSubscription(ExternalRead request, Listener procedure, String property) { + if(experiment instanceof SysdynPlaybackExperiment) { + VariableValueSubscription subscription = new VariableValueSubscription(request, this, property, procedure); + experiment.addVariableValueSubscription(subscription); + subscription.update(); + return subscription; + } else { + return null; + } + } + + protected void unregisterSubscription(VariableValueSubscription subscription) { + subscription.setListener(null); + experiment.removeVariableValueSubscription(subscription); + } + + @Override + public Variable getProperty(String name) { + if(Variables.DISPLAY_VALUE.equals(name)){ + if(experiment instanceof SysdynPlaybackExperiment) { + SysdynPlaybackExperiment exp = (SysdynPlaybackExperiment) experiment; + return new ConstantPropertyVariable(parent, name, exp.getTime(), Datatypes.DOUBLE); + } else { + return new ConstantPropertyVariable(parent, name, 0, Datatypes.DOUBLE); + } + } + return null; + } } diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/PropertyProvider.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/PropertyProvider.java new file mode 100644 index 00000000..2a7bcd06 --- /dev/null +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/PropertyProvider.java @@ -0,0 +1,10 @@ +package org.simantics.sysdyn.adapter; + +import org.simantics.databoard.adapter.AdaptException; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.variable.Variable; + +public interface PropertyProvider { + + Variable getProperty(String name) throws DatabaseException, AdaptException; +} diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/VariableValueSubscription.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/VariableValueSubscription.java new file mode 100644 index 00000000..e04aed72 --- /dev/null +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/VariableValueSubscription.java @@ -0,0 +1,103 @@ +package org.simantics.sysdyn.adapter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.simantics.databoard.adapter.AdaptException; +import org.simantics.db.Session; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.procedure.Listener; +import org.simantics.db.request.ExternalRead; +import org.simantics.db.service.QueryControl; +import org.simantics.utils.datastructures.Callback; + +public class VariableValueSubscription { + + public static final long SUBSCRIPTION_COLLECTION_INTERVAL = 2000L; + + protected ExternalRead request; + protected PropertyProvider provider; + protected String property; + protected Listener listener; + + /** + * To protect against invoking listener.exception multiple times which is + * forbidden. + */ + protected AtomicBoolean excepted = new AtomicBoolean(false); + + public VariableValueSubscription(ExternalRead request, PropertyProvider variable, String property, Listener listener) { + this.request = request; + this.provider = variable; + this.property = property; + this.listener = listener; + } + + public ExternalRead getRequest() { + return request; + } + + public void update() { + update(property); + } + + public void update(String property) { + try { + Variable value = provider.getProperty(property); + fireValue(value); + } catch (DatabaseException e) { + fireException(e); + } catch (AdaptException e) { + fireException(e); + } + } + + void fireValue(Variable value) { + if (listener != null) + listener.execute(value); + } + + void fireException(Throwable t) { + if (listener != null && excepted.compareAndSet(false, true)) + listener.exception(t); + } + + public void setListener(Listener listener) { + this.listener = listener; + } + + /** + * This makes sure that subscriptions regarding performed DB external reads + * are abolished from DB client caches when no longer needed. + * + * @param session + * @param subscriptions + */ + public static void collectSubscriptions(Session session, final VariableValueSubscription[] subscriptions, final ExternalRead... extraReads) { + if (subscriptions.length == 0 && extraReads.length == 0) + return; + + session.asyncRequest(new WriteRequest() { + @Override + public void perform(WriteGraph graph) throws DatabaseException { + Collection> requests = new ArrayList>(subscriptions.length + extraReads.length); + for (VariableValueSubscription subscription : subscriptions) + requests.add(subscription.getRequest()); + for (ExternalRead read : extraReads) + requests.add(read); + graph.getService(QueryControl.class).gc(graph, requests); + } + }, new Callback() { + @Override + public void run(DatabaseException e) { + if (e != null) + e.printStackTrace(); + } + }); + } + +} diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynExperiment.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynExperiment.java index 8fc4baa8..2dbe53d5 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynExperiment.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynExperiment.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.simantics.sysdyn.manager; +import gnu.trove.set.hash.THashSet; + import java.io.File; import java.util.UUID; import java.util.concurrent.locks.Lock; @@ -34,26 +36,29 @@ import org.simantics.simulation.experiment.ExperimentState; import org.simantics.simulation.experiment.IDynamicExperiment; import org.simantics.simulation.ontology.SimulationResource; import org.simantics.sysdyn.SysdynResource; +import org.simantics.sysdyn.adapter.VariableValueSubscription; import org.simantics.sysdyn.simulation.SimulationScheduler; public class SysdynExperiment extends Experiment implements IDynamicExperiment { - Session session; - Runnable modificationListener; - Resource experiment; - SysdynModel sysdynModel; - boolean toggled = false; + protected Session session; + protected Runnable modificationListener; + protected SysdynModel sysdynModel; + protected boolean toggled = false; + protected THashSet variableValueSubscriptions = new THashSet(); + protected volatile VariableValueSubscription[] variableValueSubscriptionsSnapshot = null; + + public static SysdynExperiment INSTANCE; public SysdynExperiment(Resource experiment, Resource model) { - super(model); - this.experiment = experiment; - // TODO Auto-generated constructor stub + super(experiment, model); + INSTANCE = this; } - - public Resource getResource() { - return this.experiment; + + public static SysdynExperiment getInstance() { + return INSTANCE; } - + @Override public void rewindTo(double time) { // TODO Auto-generated method stub @@ -108,7 +113,7 @@ public class SysdynExperiment extends Experiment implements IDynamicExperiment { } - private void startSimulation() { + protected void startSimulation() { session.asyncRequest(new ReadRequest() { @Override @@ -231,7 +236,7 @@ public class SysdynExperiment extends Experiment implements IDynamicExperiment { } - private void toggleActivation(ReadGraph graph, final boolean activate) { + protected void toggleActivation(ReadGraph graph, final boolean activate) { VirtualGraphSupport support = graph.getService(VirtualGraphSupport.class); final Session session = graph.getSession(); session.asyncRequest(new WriteRequest(support.getWorkspacePersistent("experiments")) { @@ -266,6 +271,76 @@ public class SysdynExperiment extends Experiment implements IDynamicExperiment { // TODO Auto-generated method stub return null; } + + /** + * 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; + } + + volatile long previousVariableUpdateTime = 0; + volatile boolean skippedVariableUpdate = true; + + + /** + * Modified copy from AprosExperiment + */ + public void fireValuesChanged() { + long time = System.nanoTime(); + if(time - previousVariableUpdateTime > 100000000) { + updateSubscriptions(); + previousVariableUpdateTime = time; + } + else + skippedVariableUpdate = true; + } + + /** + * Modified copy from AporsExperiment + */ + private void updateSubscriptions() { + for(VariableValueSubscription subscription : getListenerSnapshot()) + subscription.update(); + skippedVariableUpdate = false; + } } 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 c6f74ef0..1a3cfe71 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynModel.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynModel.java @@ -46,6 +46,7 @@ import org.simantics.objmap.IMappingListener; import org.simantics.objmap.Mappings; import org.simantics.simulation.experiment.Experiment; import org.simantics.simulation.experiment.ExperimentState; +import org.simantics.simulation.experiment.IDynamicExperiment; import org.simantics.simulation.experiment.IExperiment; import org.simantics.simulation.model.IModel; import org.simantics.simulation.ontology.SimulationResource; @@ -467,10 +468,22 @@ public class SysdynModel implements IMappingListener, IModel { e.printStackTrace(); } } + - SysdynExperiment exp = new SysdynExperiment(experiment, modelResource); try { - exp.init(g); + + SysdynResource sr = SysdynResource.getInstance(g); + IDynamicExperiment exp; + if(g.isInstanceOf(experiment, sr.PlaybackExperiment)) { + exp = new SysdynPlaybackExperiment(experiment, modelResource); + ((SysdynPlaybackExperiment)exp).init(g); + } else if(g.isInstanceOf(experiment, sr.BasicExperiment)) { + exp = new SysdynExperiment(experiment, modelResource); + ((SysdynExperiment)exp).init(g); + } else { + return null; + } + ExperimentRuns.createRun(g.getSession(), experiment, exp, listener, null); if(listener != null) listener.onExperimentActivated(exp); diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynPlaybackExperiment.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynPlaybackExperiment.java new file mode 100644 index 00000000..416ce05c --- /dev/null +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynPlaybackExperiment.java @@ -0,0 +1,50 @@ +package org.simantics.sysdyn.manager; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.request.ReadRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.simulation.experiment.ExperimentState; +import org.simantics.simulation.experiment.IDynamicExperiment; +import org.simantics.simulation.ontology.SimulationResource; + +public class SysdynPlaybackExperiment extends SysdynExperiment implements IDynamicExperiment { + + double time; + public static final long VARIABLE_UPDATE_INTERVAL = 500000000; + + + public SysdynPlaybackExperiment(Resource experiment, Resource model) { + super(experiment, model); + this.time = 0; + } + + volatile boolean updating = false; + + + public void setTime(double time) { + this.time = time; + fireValuesChanged(); + } + + public double getTime() { + return this.time; + } + + @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 { + final Resource configuration = graph.getPossibleObject(model, SimulationResource.getInstance(graph).HasConfiguration); + sysdynModel = SysdynModelManager.getInstance(session).getModel(graph, configuration); + toggleActivation(graph, true); + startSimulation(); + } + }); + } +} diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynResult.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynResult.java index fad8674a..3b41feda 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynResult.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/manager/SysdynResult.java @@ -20,8 +20,8 @@ import java.util.TreeMap; import org.simantics.databoard.Accessors; import org.simantics.databoard.Bindings; +import org.simantics.databoard.Datatypes; import org.simantics.databoard.Files; -import org.simantics.databoard.Historian; import org.simantics.databoard.accessor.Accessor; import org.simantics.databoard.accessor.MapAccessor; import org.simantics.databoard.accessor.RecordAccessor; @@ -83,7 +83,7 @@ public class SysdynResult { public void setResult(SimulationResult result) { try { // Create Memory Historian - Datatype recordingSessionType = Historian.getRecordingSessionType(); + Datatype recordingSessionType = Datatypes.getDatatype("RecordingSession"); RecordBinding sessionBinding = (RecordBinding) Bindings.getBinding( recordingSessionType ); RecordBinding recordingBinding = (RecordBinding) Bindings.getBinding( Recording.class ); Object session = sessionBinding.createDefault();