View Javadoc
1   package org.opentrafficsim.simulationengine;
2   
3   import java.rmi.RemoteException;
4   
5   import javax.naming.NamingException;
6   
7   import nl.tudelft.simulation.dsol.SimRuntimeException;
8   import nl.tudelft.simulation.dsol.experiment.ReplicationMode;
9   import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEvent;
10  import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEventInterface;
11  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
12  
13  import org.opentrafficsim.core.dsol.OTSDEVSRealTimeClock;
14  import org.opentrafficsim.core.dsol.OTSModelInterface;
15  import org.opentrafficsim.core.dsol.OTSReplication;
16  import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
17  
18  /**
19   * <p>
20   * Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
21   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
22   * <p>
23   * $LastChangedDate: 2015-09-03 13:38:01 +0200 (Thu, 03 Sep 2015) $, @version $Revision: 1378 $, by $Author: averbraeck $,
24   * initial version 11 mei 2015 <br>
25   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
26   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
27   */
28  public class SimpleAnimator extends OTSDEVSRealTimeClock implements SimpleSimulation
29  {
30      /** */
31      private static final long serialVersionUID = 20150511L;
32  
33      /** Counter for replication. */
34      private int lastReplication = 0;
35  
36      /**
37       * Create a simulation engine with animation; the easy way. PauseOnError is set to true;
38       * @param startTime DoubleScalar.Abs&lt;TimeUnit&gt;; the start time of the simulation
39       * @param warmupPeriod DoubleScalar.Rel&lt;TimeUnit&gt;; the warm up period of the simulation (use new
40       *            DoubleScalar.Rel&lt;TimeUnit&gt;(0, SECOND) if you don't know what this is)
41       * @param runLength DoubleScalar.Rel&lt;TimeUnit&gt;; the duration of the simulation
42       * @param model OTSModelInterface; the simulation to execute
43       * @throws RemoteException on communications failure
44       * @throws SimRuntimeException on ???
45       * @throws NamingException when context for the animation cannot be created
46       */
47      public SimpleAnimator(final Time.Abs startTime, final Time.Rel warmupPeriod, final Time.Rel runLength,
48          final OTSModelInterface model) throws RemoteException, SimRuntimeException, NamingException
49      {
50          setPauseOnError(true);
51          initialize(new OTSReplication("rep" + ++this.lastReplication, new OTSSimTimeDouble(startTime), warmupPeriod,
52              runLength, model), ReplicationMode.TERMINATING);
53      }
54  
55      /**
56       * {@inheritDoc}
57       */
58      public final SimEvent<OTSSimTimeDouble> scheduleEvent(final Time.Abs executionTime, final short priority,
59          final Object source, final Object target, final String method, final Object[] args) throws SimRuntimeException
60      {
61          SimEvent<OTSSimTimeDouble> result =
62              new SimEvent<OTSSimTimeDouble>(new OTSSimTimeDouble(new Time.Abs(executionTime.getSI(), SECOND)), priority,
63                  source, target, method, args);
64          scheduleEvent(result);
65          return result;
66      }
67  
68      /** {@inheritDoc} */
69      @Override
70      @SuppressWarnings("checkstyle:designforextension")
71      public void run()
72      {
73          setAnimationDelay(20); // ________________________________ 50 Hz animation update
74          AnimationThread animationThread = new AnimationThread(this);
75          animationThread.start();
76  
77          long clockTime0 = System.currentTimeMillis(); // _________ current zero for the wall clock
78          OTSSimTimeDouble simTime0 = this.simulatorTime; // _______ current zero for the sim clock
79          double factor = getSpeedFactor(); // _____________________ local copy of speed factor to detect change
80          double msec1 = relativeMillis(1.0).doubleValue(); // _____ translation factor for 1 msec for sim clock
81          Time.Rel r1 = this.relativeMillis(factor); // sim clock change for 1 msec wall clock
82  
83          while (this.isRunning() && !this.eventList.isEmpty()
84              && this.simulatorTime.le(this.replication.getTreatment().getEndTime()))
85          {
86              // check if speedFactor has changed. If yes: re-baseline.
87              if (factor != getSpeedFactor())
88              {
89                  clockTime0 = System.currentTimeMillis();
90                  simTime0 = this.simulatorTime;
91                  factor = getSpeedFactor();
92                  r1 = this.relativeMillis(factor);
93              }
94  
95              // peek at the first event and determine the time difference relative to RT speed.
96              SimEventInterface<OTSSimTimeDouble> event = this.eventList.first();
97              double simTimeDiffMillis = (event.getAbsoluteExecutionTime().minus(simTime0)).doubleValue() / (msec1 * factor);
98  
99              /*
100              * simTimeDiff gives the number of milliseconds between the last event and this event. if speed == 1, this is the
101              * number of milliseconds we have to wait. if speed == 10, we have to wait 1/10 of that. If the speed == 0.1, we
102              * have to wait 10 times that amount. We might also be behind.
103              */
104             if (simTimeDiffMillis < (System.currentTimeMillis() - clockTime0))
105             {
106                 // we are behind.
107                 if (!isCatchup())
108                 {
109                     // if no catch-up: re-baseline.
110                     clockTime0 = System.currentTimeMillis();
111                     simTime0 = this.simulatorTime;
112                 }
113                 else
114                 {
115                     // if catch-up: indicate we were behind.
116                     this.fireTimedEvent(BACKLOG_EVENT, this.simulatorTime, null);
117                 }
118             }
119             else
120             {
121                 while (simTimeDiffMillis > System.currentTimeMillis() - clockTime0)
122                 {
123                     try
124                     {
125                         Thread.sleep(1);
126 
127                         // check if speedFactor has changed. If yes: break out of this loop and execute event.
128                         // this could cause a jump.
129                         if (factor != getSpeedFactor())
130                         {
131                             simTimeDiffMillis = 0.0;
132                         }
133 
134                     }
135                     catch (InterruptedException ie)
136                     {
137                         // do nothing
138                         ie = null;
139                     }
140 
141                     // make a small time step for the animation during wallclock waiting.
142                     // but never beyond the next event time.
143                     if (this.simulatorTime.plus(r1).lt(event.getAbsoluteExecutionTime()))
144                     {
145                         this.simulatorTime.add(r1);
146                     }
147                 }
148             }
149 
150             synchronized (super.semaphore)
151             {
152                 this.simulatorTime = event.getAbsoluteExecutionTime();
153                 this.fireTimedEvent(SimulatorInterface.TIME_CHANGED_EVENT, this.simulatorTime, this.simulatorTime);
154 
155                 // carry out all events scheduled on this simulation time, as long as we are still running.
156                 while (this.isRunning() && !this.eventList.isEmpty()
157                     && event.getAbsoluteExecutionTime().eq(this.simulatorTime))
158                 {
159                     event = this.eventList.removeFirst();
160                     try
161                     {
162                         event.execute();
163                     }
164                     catch (Exception exception)
165                     {
166                         exception.printStackTrace();
167                         System.err.println(event.toString());
168                         if (this.isPauseOnError())
169                         {
170                             this.stop();
171                         }
172                     }
173                     if (!this.eventList.isEmpty())
174                     {
175                         // peek at next event for while loop.
176                         event = this.eventList.first();
177                     }
178                 }
179             }
180         }
181         this.fireTimedEvent(SimulatorInterface.TIME_CHANGED_EVENT, this.simulatorTime, this.simulatorTime);
182         updateAnimation();
183         animationThread.stopAnimation();
184     }
185 
186     /** */
187     @SuppressWarnings("checkstyle:designforextension")
188     public void runOld()
189     {
190         AnimationThread animationThread = new AnimationThread(this);
191         animationThread.start();
192 
193         long clockTime0 = System.currentTimeMillis(); // _________ current zero for the wall clock
194         OTSSimTimeDouble simTime0 = this.simulatorTime; // _______ current zero for the sim clock
195         double factor = getSpeedFactor(); // _____________________ local copy of speed factor to detect change
196         double msec1 = relativeMillis(1.0).doubleValue(); // _____ translation factor for 1 msec for sim clock
197         Time.Rel r10 = this.relativeMillis(10.0 * factor); // sim clock change for 10 msec wall clock
198 
199         while (this.isRunning() && !this.eventList.isEmpty()
200             && this.simulatorTime.le(this.replication.getTreatment().getEndTime()))
201         {
202             // check if speedFactor has changed. If yes: re-baseline.
203             if (factor != getSpeedFactor())
204             {
205                 clockTime0 = System.currentTimeMillis();
206                 simTime0 = this.simulatorTime;
207                 factor = getSpeedFactor();
208                 r10 = this.relativeMillis(10.0 * factor);
209             }
210 
211             // peek at the first event and determine the time difference relative to RT speed.
212             SimEventInterface<OTSSimTimeDouble> event = this.eventList.first();
213             double simTimeDiffMillis = (event.getAbsoluteExecutionTime().minus(simTime0)).doubleValue() / (msec1 * factor);
214 
215             /*
216              * simTimeDiff gives the number of milliseconds between the last event and this event. if speed == 1, this is the
217              * number of milliseconds we have to wait. if speed == 10, we have to wait 1/10 of that. If the speed == 0.1, we
218              * have to wait 10 times that amount. We might also be behind.
219              */
220             if (simTimeDiffMillis < (System.currentTimeMillis() - clockTime0))
221             {
222                 // we are behind.
223                 if (!isCatchup())
224                 {
225                     // if no catch-up: re-baseline.
226                     clockTime0 = System.currentTimeMillis();
227                     simTime0 = this.simulatorTime;
228                 }
229                 else
230                 {
231                     // if catch-up: indicate we were behind.
232                     this.fireTimedEvent(BACKLOG_EVENT, this.simulatorTime, null);
233                 }
234             }
235             else
236             {
237                 while (simTimeDiffMillis > System.currentTimeMillis() - clockTime0)
238                 {
239                     try
240                     {
241                         Thread.sleep(10);
242 
243                         // check if speedFactor has changed. If yes: break out of this loop and execute event.
244                         // this could cause a jump.
245                         if (factor != getSpeedFactor())
246                         {
247                             simTimeDiffMillis = 0.0;
248                         }
249 
250                     }
251                     catch (InterruptedException ie)
252                     {
253                         // do nothing
254                         ie = null;
255                     }
256 
257                     // make a small time step for the animation during wallclock waiting.
258                     // but never beyond the next event time.
259                     if (this.simulatorTime.plus(r10).lt(event.getAbsoluteExecutionTime()))
260                     {
261                         this.simulatorTime.add(r10);
262                         this.fireTimedEvent(SimulatorInterface.TIME_CHANGED_EVENT, this.simulatorTime, this.simulatorTime);
263                         updateAnimation();
264                     }
265                 }
266             }
267 
268             synchronized (super.semaphore)
269             {
270                 this.simulatorTime = event.getAbsoluteExecutionTime();
271                 this.fireTimedEvent(SimulatorInterface.TIME_CHANGED_EVENT, this.simulatorTime, this.simulatorTime);
272 
273                 // carry out all events scheduled on this simulation time, as long as we are still running.
274                 while (this.isRunning() && !this.eventList.isEmpty()
275                     && event.getAbsoluteExecutionTime().eq(this.simulatorTime))
276                 {
277                     event = this.eventList.removeFirst();
278                     try
279                     {
280                         event.execute();
281                     }
282                     catch (Exception exception)
283                     {
284                         exception.printStackTrace();
285                         System.err.println(event.toString());
286                         if (this.isPauseOnError())
287                         {
288                             this.stop();
289                         }
290                     }
291                     if (!this.eventList.isEmpty())
292                     {
293                         // peek at next event for while loop.
294                         event = this.eventList.first();
295                     }
296                 }
297             }
298         }
299         this.fireTimedEvent(SimulatorInterface.TIME_CHANGED_EVENT, this.simulatorTime, this.simulatorTime);
300         updateAnimation();
301         animationThread.stopAnimation();
302     }
303 }