Added String preferences
[Mograsim.git] / plugins / net.mograsim.logic.model / src / net / mograsim / logic / model / LogicExecuter.java
index a9fde7a..a7f8c83 100644 (file)
@@ -3,7 +3,9 @@ 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;
+import net.mograsim.logic.core.timeline.Timeline.ExecutionResult;
 
 //TODO maybe move to logic core?
 public class LogicExecuter
@@ -13,16 +15,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(() ->
                {
@@ -35,22 +42,32 @@ public class LogicExecuter
                        {
                                while (shouldBeRunningLive.get())
                                {
-                                       // always execute to keep timeline from "hanging behind" for too long
-                                       long current = System.currentTimeMillis();
-                                       timeline.executeUntil(timeline.laterThan(current), current + 10);
+                                       long current = tf.getTime();
+                                       // The tf.isPaused() condition is justified, because timeline.getSimulationTime() returns the timestamp of the last
+                                       // processed event during executeUntil()
+                                       if (timeline.executeUntil(() -> timeline.laterThan(current).getAsBoolean() || tf.isPaused(),
+                                                       System.currentTimeMillis() + 10) == ExecutionResult.EXEC_OUT_OF_TIME)
+                                               timeline.synchTime(); // TODO: should this also be called if tf.isPaused() condition is met?
+                                       long nextEventTime = timeline.nextEventTime();
                                        long sleepTime;
                                        if (timeline.hasNext())
-                                               sleepTime = timeline.nextEventTime() - current;
+                                               sleepTime = (long) ((nextEventTime - current) * tf.getSimulTimeToRealTimeFactor());
                                        else
                                                sleepTime = 10000;
                                        try
                                        {
-                                               nextExecSimulTime.set(current + sleepTime);
+                                               nextExecSimulTime.set(nextEventTime);
                                                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
+                                       { // do nothing; it is normal execution flow to be interrupted
                                        }
                                }
                        }
@@ -66,9 +83,14 @@ public class LogicExecuter
                timeline.addEventAddedListener(event ->
                {
                        if (isRunningLive.get())
-                               if (Timeline.timeCmp(event.getTiming(), nextExecSimulTime.get()) < 0)
+                       {
+                               long nextExecSimulTime = this.nextExecSimulTime.get();
+                               if (nextExecSimulTime == -1 || Timeline.timeCmp(event.getTiming(), nextExecSimulTime) < 0)
                                        simulationThread.interrupt();
+                       }
                });
+               // not optimal; but we don't expect this to happen very often
+               tf.addSimulTimeToRealTimeFactorChangedListener(d -> simulationThread.interrupt());
        }
 
        public void executeNextStep()
@@ -94,6 +116,40 @@ 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 double getSpeedFactor()
+       {
+               return tf.getSpeedFactor();
+       }
+
+       public void setSpeedFactor(double factor)
+       {
+               tf.setSpeedFactor(factor);
+       }
+
        private void waitForIsRunning(boolean expectedState)
        {
                while (isRunningLive.get() ^ expectedState)