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