OTSLoggingAnimator.java

  1. package org.opentrafficsim.core.dsol;

  2. import java.io.Serializable;
  3. import java.util.Map;

  4. import javax.naming.NamingException;

  5. import org.djunits.value.vdouble.scalar.Duration;
  6. import org.djunits.value.vdouble.scalar.Time;

  7. import nl.tudelft.simulation.dsol.SimRuntimeException;
  8. import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEventInterface;
  9. import nl.tudelft.simulation.dsol.simtime.SimTimeDoubleUnit;
  10. import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
  11. import nl.tudelft.simulation.jstats.streams.StreamInterface;

  12. /**
  13.  * Construct a DSOL DEVSRealTimeAnimator the easy way.
  14.  * <p>
  15.  * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  16.  * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  17.  * <p>
  18.  * $LastChangedDate: 2018-10-30 14:03:57 +0100 (Tue, 30 Oct 2018) $, @version $Revision: 4727 $, by $Author: pknoppers $,
  19.  * initial version 11 mei 2015 <br>
  20.  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  21.  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  22.  */
  23. public class OTSLoggingAnimator extends OTSAnimator
  24. {
  25.     /** */
  26.     private static final long serialVersionUID = 20150511L;

  27.     /** Counter for replication. */
  28.     private int lastReplication = 0;

  29.     /**
  30.      * Construct an OTSAnimator.
  31.      * @param path path for logging
  32.      * @param simulatorId the id of the simulator to use in remote communication
  33.      */
  34.     public OTSLoggingAnimator(final String path, final Serializable simulatorId)
  35.     {
  36.         super(simulatorId);
  37.     }

  38.     /** {@inheritDoc} */
  39.     @Override
  40.     public void initialize(final Time startTime, final Duration warmupPeriod, final Duration runLength,
  41.             final OTSModelInterface model) throws SimRuntimeException, NamingException
  42.     {
  43.         setPauseOnError(true);
  44.         setAnimationDelay(20); // 50 Hz animation update
  45.         OTSReplication newReplication =
  46.                 new OTSReplication("rep" + ++this.lastReplication, startTime, warmupPeriod, runLength);
  47.         super.initialize(model, newReplication);
  48.     }

  49.     /**
  50.      * Initialize a simulation engine without animation; the easy way. PauseOnError is set to true;
  51.      * @param startTime Time; the start time of the simulation
  52.      * @param warmupPeriod Duration; the warm up period of the simulation (use new Duration(0, SECOND) if you don't know what
  53.      *            this is)
  54.      * @param runLength Duration; the duration of the simulation
  55.      * @param model OTSModelInterface; the simulation to execute
  56.      * @param streams Map&lt;String, StreamInterface&gt;; streams
  57.      * @throws SimRuntimeException when e.g., warmupPeriod is larger than runLength
  58.      * @throws NamingException when the context for the replication cannot be created
  59.      */
  60.     @Override
  61.     public void initialize(final Time startTime, final Duration warmupPeriod, final Duration runLength,
  62.             final OTSModelInterface model, final Map<String, StreamInterface> streams)
  63.             throws SimRuntimeException, NamingException
  64.     {
  65.         setPauseOnError(true);
  66.         setAnimationDelay(20); // 50 Hz animation update
  67.         OTSReplication newReplication =
  68.                 new OTSReplication("rep" + ++this.lastReplication, startTime, warmupPeriod, runLength);
  69.         model.getStreams().putAll(streams);
  70.         super.initialize(model, newReplication);
  71.     }

  72.     /** {@inheritDoc} */
  73.     @Override
  74.     public void initialize(final Time startTime, final Duration warmupPeriod, final Duration runLength,
  75.             final OTSModelInterface model, final int replicationnr) throws SimRuntimeException, NamingException
  76.     {
  77.         setPauseOnError(true);
  78.         setAnimationDelay(20); // 50 Hz animation update
  79.         OTSReplication newReplication = new OTSReplication("rep" + replicationnr, startTime, warmupPeriod, runLength);
  80.         super.initialize(model, newReplication);
  81.     }

  82.     /** {@inheritDoc} */
  83.     @Override
  84.     public String toString()
  85.     {
  86.         return "OTSLoggingAnimator [lastReplication=" + this.lastReplication + "]";
  87.     }

  88.     /** the current animation thread; null if none. */
  89.     private AnimationThread animationThread = null;

  90.     /** {@inheritDoc} */
  91.     @Override
  92.     @SuppressWarnings({ "checkstyle:designforextension", "checkstyle:methodlength" })
  93.     public void run()
  94.     {
  95.         synchronized (this)
  96.         {
  97.             if (this.isAnimation())
  98.             {
  99.                 this.animationThread = new AnimationThread(this);
  100.                 this.animationThread.start();
  101.             }
  102.         }

  103.         /* Baseline point for the wallclock time. */
  104.         long wallTime0 = System.currentTimeMillis();

  105.         /* Baseline point for the simulator time. */
  106.         SimTimeDoubleUnit simTime0 = this.simulatorTime.copy();

  107.         /* Speed factor is simulation seconds per 1 wallclock second. */
  108.         double currentSpeedFactor = this.getSpeedFactor();

  109.         /* wall clock milliseconds per 1 simulation clock millisecond. */
  110.         double msec1 = simulatorTimeForWallClockMillis(1.0).doubleValue();

  111.         while (this.isStartingOrRunning() && !this.eventList.isEmpty()
  112.                 && this.simulatorTime.le(this.replication.getEndSimTime()))
  113.         {
  114.             // check if speedFactor has changed. If yes: re-baseline.
  115.             if (currentSpeedFactor != this.getSpeedFactor())
  116.             {
  117.                 wallTime0 = System.currentTimeMillis();
  118.                 simTime0.set(this.simulatorTime.get());
  119.                 currentSpeedFactor = this.getSpeedFactor();
  120.             }

  121.             // check if we are behind; wantedSimTime is the needed current time on the wall-clock
  122.             double wantedSimTime = (System.currentTimeMillis() - wallTime0) * msec1 * currentSpeedFactor;
  123.             double simTimeSinceBaseline = this.simulatorTime.diff(simTime0).doubleValue();

  124.             if (simTimeSinceBaseline < wantedSimTime)
  125.             {
  126.                 // we are behind
  127.                 if (!this.isCatchup())
  128.                 {
  129.                     // if no catch-up: re-baseline.
  130.                     wallTime0 = System.currentTimeMillis();
  131.                     simTime0.set(this.simulatorTime.get());
  132.                 }
  133.                 else
  134.                 {
  135.                     // jump to the required wall-clock related time or to the time of the next event, whichever comes
  136.                     // first
  137.                     synchronized (super.semaphore)
  138.                     {
  139.                         Duration delta = simulatorTimeForWallClockMillis((wantedSimTime - simTimeSinceBaseline) / msec1);
  140.                         SimTimeDoubleUnit absSyncTime = this.simulatorTime.plus(delta);
  141.                         SimTimeDoubleUnit eventTime = this.eventList.first().getAbsoluteExecutionTime();
  142.                         if (absSyncTime.lt(eventTime))
  143.                         {
  144.                             this.simulatorTime.set(absSyncTime.get());
  145.                         }
  146.                         else
  147.                         {
  148.                             this.simulatorTime.set(eventTime.get());
  149.                         }
  150.                     }
  151.                 }
  152.             }

  153.             // peek at the first event and determine the time difference relative to RT speed; that determines
  154.             // how long we have to wait.
  155.             SimEventInterface<SimTimeDoubleUnit> nextEvent = this.eventList.first();
  156.             double wallMillisNextEventSinceBaseline =
  157.                     (nextEvent.getAbsoluteExecutionTime().diff(simTime0)).doubleValue() / (msec1 * currentSpeedFactor);

  158.             // wallMillisNextEventSinceBaseline gives the number of milliseconds on the wall clock since baselining for the
  159.             // expected execution time of the next event on the event list .
  160.             if (wallMillisNextEventSinceBaseline >= (System.currentTimeMillis() - wallTime0))
  161.             {
  162.                 while (wallMillisNextEventSinceBaseline > System.currentTimeMillis() - wallTime0)
  163.                 {
  164.                     try
  165.                     {
  166.                         Thread.sleep(this.getUpdateMsec());
  167.                     }
  168.                     catch (InterruptedException ie)
  169.                     {
  170.                         // do nothing
  171.                         ie = null;
  172.                         Thread.interrupted(); // clear the flag
  173.                     }

  174.                     // did we stop running between events?
  175.                     if (!isStartingOrRunning())
  176.                     {
  177.                         wallMillisNextEventSinceBaseline = 0.0; // jump out of the while loop for sleeping
  178.                         break;
  179.                     }

  180.                     // check if speedFactor has changed. If yes: rebaseline. Try to avoid a jump.
  181.                     if (currentSpeedFactor != this.getSpeedFactor())
  182.                     {
  183.                         // rebaseline
  184.                         wallTime0 = System.currentTimeMillis();
  185.                         simTime0.set(this.simulatorTime.get());
  186.                         currentSpeedFactor = this.getSpeedFactor();
  187.                         wallMillisNextEventSinceBaseline = (nextEvent.getAbsoluteExecutionTime().diff(simTime0)).doubleValue()
  188.                                 / (msec1 * currentSpeedFactor);
  189.                     }

  190.                     // check if an event has been inserted. In a real-time situation this can be done by other threads
  191.                     if (!nextEvent.equals(this.eventList.first())) // event inserted by a thread...
  192.                     {
  193.                         nextEvent = this.eventList.first();
  194.                         wallMillisNextEventSinceBaseline = (nextEvent.getAbsoluteExecutionTime().diff(simTime0)).doubleValue()
  195.                                 / (msec1 * currentSpeedFactor);
  196.                     }

  197.                     // make a small time step for the animation during wallclock waiting, but never beyond the next event
  198.                     // time. Changed 2019-04-30: this is now recalculated based on latest system time after the 'sleep'.
  199.                     synchronized (super.semaphore)
  200.                     {
  201.                         Time nextEventSimTime = nextEvent.getAbsoluteExecutionTime().get();
  202.                         Duration deltaToWall0inSimTime =
  203.                                 simulatorTimeForWallClockMillis((System.currentTimeMillis() - wallTime0) * currentSpeedFactor);
  204.                         Time currentWallSimTime = simTime0.plus(deltaToWall0inSimTime).get();
  205.                         if (nextEventSimTime.compareTo(currentWallSimTime) < 0)
  206.                         {
  207.                             if (nextEventSimTime.compareTo(this.simulatorTime.get()) > 0) // don't go back in time
  208.                             {
  209.                                 this.simulatorTime.set(nextEventSimTime);
  210.                             }
  211.                             wallMillisNextEventSinceBaseline = 0.0; // force breakout of the loop
  212.                         }
  213.                         else
  214.                         {
  215.                             if (currentWallSimTime.compareTo(this.simulatorTime.get()) > 0) // don't go back in time
  216.                             {
  217.                                 this.simulatorTime.set(currentWallSimTime);
  218.                             }
  219.                         }
  220.                     }
  221.                 }
  222.             }

  223.             // only execute an event if we are still running...
  224.             if (isStartingOrRunning())
  225.             {
  226.                 synchronized (super.semaphore)
  227.                 {
  228.                     if (nextEvent.getAbsoluteExecutionTime().ne(this.simulatorTime))
  229.                     {
  230.                         fireTimedEvent(SimulatorInterface.TIME_CHANGED_EVENT, null, nextEvent.getAbsoluteExecutionTime().get());
  231.                     }
  232.                     this.simulatorTime.set(nextEvent.getAbsoluteExecutionTime().get());

  233.                     // carry out all events scheduled on this simulation time, as long as we are still running.
  234.                     while (this.isStartingOrRunning() && !this.eventList.isEmpty()
  235.                             && nextEvent.getAbsoluteExecutionTime().eq(this.simulatorTime))
  236.                     {
  237.                         nextEvent = this.eventList.removeFirst();
  238.                         try
  239.                         {
  240.                             System.out.println(nextEvent.getAbsoluteExecutionTime() + " " + nextEvent.toString());
  241.                             nextEvent.execute();
  242.                         }
  243.                         catch (Exception exception)
  244.                         {
  245.                             getLogger().always().error(exception);
  246.                             if (this.isPauseOnError())
  247.                             {
  248.                                 try
  249.                                 {
  250.                                     this.stop();
  251.                                 }
  252.                                 catch (SimRuntimeException stopException)
  253.                                 {
  254.                                     getLogger().always().error(stopException);
  255.                                 }
  256.                             }
  257.                         }
  258.                         if (!this.eventList.isEmpty())
  259.                         {
  260.                             // peek at next event for while loop.
  261.                             nextEvent = this.eventList.first();
  262.                         }
  263.                     }
  264.                 }
  265.             }
  266.         }
  267.         fireTimedEvent(SimulatorInterface.TIME_CHANGED_EVENT, null, this.simulatorTime.get());

  268.         synchronized (this)
  269.         {
  270.             if (this.isAnimation() && this.animationThread != null)
  271.             {
  272.                 updateAnimation();
  273.                 this.animationThread.stopAnimation();
  274.             }
  275.         }
  276.     }

  277. }