Simulation can now be slowed and paused
authorFabian Stemmler <stemmler@in.tum.de>
Fri, 20 Sep 2019 14:53:09 +0000 (16:53 +0200)
committerFabian Stemmler <stemmler@in.tum.de>
Fri, 20 Sep 2019 14:53:09 +0000 (16:53 +0200)
plugins/net.mograsim.logic.core/src/net/mograsim/logic/core/timeline/PauseableTimeFunction.java [new file with mode: 0644]
plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/LogicExecuter.java
plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/views/LogicUIPart.java

diff --git a/plugins/net.mograsim.logic.core/src/net/mograsim/logic/core/timeline/PauseableTimeFunction.java b/plugins/net.mograsim.logic.core/src/net/mograsim/logic/core/timeline/PauseableTimeFunction.java
new file mode 100644 (file)
index 0000000..86f4ce9
--- /dev/null
@@ -0,0 +1,58 @@
+package net.mograsim.logic.core.timeline;
+
+import java.util.function.LongSupplier;
+
+public class PauseableTimeFunction implements LongSupplier
+{
+       private boolean paused = false;
+       private long unpausedSysTime = 0, lastPausedInternalTime = 0;
+       private int speedPercentage = 100;
+
+       public void pause()
+       {
+               if (!paused)
+               {
+                       lastPausedInternalTime = getAsLong();
+                       paused = true;
+               }
+       }
+
+       public void unpause()
+       {
+               if (paused)
+               {
+                       paused = false;
+                       unpausedSysTime = System.currentTimeMillis();
+               }
+       }
+
+       @Override
+       public long getAsLong()
+       {
+               return paused ? lastPausedInternalTime
+                               : lastPausedInternalTime + ((System.currentTimeMillis() - unpausedSysTime) * speedPercentage) / 100;
+       }
+
+       public void setSpeedPercentage(int percentage)
+       {
+               if (!paused)
+               {
+                       pause();
+                       unpause();
+               }
+               this.speedPercentage = Integer.min(100, Integer.max(percentage, 1));
+       }
+
+       public boolean isPaused()
+       {
+               return paused;
+       }
+
+       public void toggle()
+       {
+               if (paused)
+                       unpause();
+               else
+                       pause();
+       }
+}
index a9fde7a..eb67f8d 100644 (file)
@@ -3,6 +3,7 @@ package net.mograsim.logic.model;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 
+import net.mograsim.logic.core.timeline.PauseableTimeFunction;
 import net.mograsim.logic.core.timeline.Timeline;
 
 //TODO maybe move to logic core?
@@ -13,16 +14,21 @@ public class LogicExecuter
 
        private final AtomicBoolean shouldBeRunningLive;
        private final AtomicBoolean isRunningLive;
+       private final AtomicBoolean isPaused;
        private final AtomicLong nextExecSimulTime;
        private final Thread simulationThread;
 
+       PauseableTimeFunction tf;
+
        public LogicExecuter(Timeline timeline)
        {
                this.timeline = timeline;
 
-               timeline.setTimeFunction(System::currentTimeMillis);
+               tf = new PauseableTimeFunction();
+               timeline.setTimeFunction(tf);
                shouldBeRunningLive = new AtomicBoolean();
                isRunningLive = new AtomicBoolean();
+               isPaused = new AtomicBoolean();
                nextExecSimulTime = new AtomicLong();
                simulationThread = new Thread(() ->
                {
@@ -36,7 +42,7 @@ public class LogicExecuter
                                while (shouldBeRunningLive.get())
                                {
                                        // always execute to keep timeline from "hanging behind" for too long
-                                       long current = System.currentTimeMillis();
+                                       long current = tf.getAsLong();
                                        timeline.executeUntil(timeline.laterThan(current), current + 10);
                                        long sleepTime;
                                        if (timeline.hasNext())
@@ -48,6 +54,12 @@ public class LogicExecuter
                                                nextExecSimulTime.set(current + sleepTime);
                                                if (sleepTime > 0)
                                                        Thread.sleep(sleepTime);
+
+                                               synchronized (isPaused)
+                                               {
+                                                       while (isPaused.get())
+                                                               isPaused.wait();
+                                               }
                                        }
                                        catch (@SuppressWarnings("unused") InterruptedException e)
                                        {// do nothing; it is normal execution flow to be interrupted
@@ -94,6 +106,35 @@ public class LogicExecuter
                waitForIsRunning(false);
        }
 
+       public void unpauseLiveExecution()
+       {
+               synchronized (isPaused)
+               {
+                       tf.unpause();
+                       isPaused.set(false);
+                       isPaused.notify();
+               }
+       }
+
+       public void pauseLiveExecution()
+       {
+               synchronized (isPaused)
+               {
+                       tf.pause();
+                       isPaused.set(true);
+               }
+       }
+
+       public boolean isPaused()
+       {
+               return isPaused.get();
+       }
+
+       public void setSpeedPercentage(int percentage)
+       {
+               tf.setSpeedPercentage(percentage);
+       }
+
        private void waitForIsRunning(boolean expectedState)
        {
                while (isRunningLive.get() ^ expectedState)
index 49c6fa4..42c2db4 100644 (file)
@@ -6,7 +6,10 @@ import org.eclipse.e4.ui.model.application.ui.basic.MPart;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Slider;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.part.ViewPart;
 
@@ -51,6 +54,8 @@ public class LogicUIPart extends ViewPart
                GridLayout layout = new GridLayout(1, true);
                parent.setLayout(layout);
 
+               addSimulationControlWidgets(parent);
+
                ui = new LogicUICanvas(parent, SWT.NONE, m.getModel());
                ui.addTransformListener((x, y, z) -> part.setDirty(z < 1));
                ZoomableCanvasUserInput userInput = new ZoomableCanvasUserInput(ui);
@@ -79,6 +84,51 @@ public class LogicUIPart extends ViewPart
                exec.startLiveExecution();
        }
 
+       private void addSimulationControlWidgets(Composite parent)
+       {
+               Composite c = new Composite(parent, SWT.NONE);
+               c.setLayout(new GridLayout(4, false));
+               Button pauseButton = new Button(c, SWT.TOGGLE);
+               pauseButton.setText("Running");
+
+               pauseButton.addListener(SWT.Selection, e ->
+               {
+                       if (!pauseButton.getSelection())
+                       {
+                               pauseButton.setText("Running");
+                               exec.unpauseLiveExecution();
+                       } else
+                       {
+                               pauseButton.setText("Paused");
+                               exec.pauseLiveExecution();
+                       }
+               });
+
+               Label speedLabel = new Label(c, SWT.NONE);
+               speedLabel.setText("Simulation Speed: ");
+
+               Slider slider = new Slider(c, SWT.NONE);
+               slider.setMinimum(1);
+               slider.setMaximum(100 + slider.getThumb());
+               slider.setIncrement(1);
+
+               Label speedPercentageLabel = new Label(c, SWT.NONE);
+               speedPercentageLabel.setText("100%");
+
+               slider.addListener(SWT.Selection, e ->
+               {
+                       int selection = slider.getSelection();
+                       speedPercentageLabel.setText(selection + "%");
+
+                       exec.setSpeedPercentage(slider.getSelection());
+               });
+               slider.setSelection(100);
+
+               c.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.FILL_HORIZONTAL));
+               c.pack();
+               c.setVisible(true);
+       }
+
        @Override
        public void setFocus()
        {