View Javadoc
1   package org.opentrafficsim.swing.script;
2   
3   import java.awt.Dimension;
4   import java.rmi.RemoteException;
5   import java.util.HashMap;
6   import java.util.List;
7   import java.util.Map;
8   
9   import org.djunits.value.vdouble.scalar.Duration;
10  import org.djunits.value.vdouble.scalar.Time;
11  import org.djutils.exceptions.Throw;
12  import org.djutils.exceptions.Try;
13  import org.opentrafficsim.core.animation.gtu.colorer.GTUColorer;
14  import org.opentrafficsim.core.dsol.OTSAnimator;
15  import org.opentrafficsim.core.dsol.OTSModelInterface;
16  import org.opentrafficsim.core.dsol.OTSSimulator;
17  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
18  import org.opentrafficsim.core.network.OTSLink;
19  import org.opentrafficsim.core.network.OTSNetwork;
20  import org.opentrafficsim.core.network.OTSNode;
21  import org.opentrafficsim.draw.core.OTSDrawingException;
22  import org.opentrafficsim.draw.factory.DefaultAnimationFactory;
23  import org.opentrafficsim.road.gtu.generator.GtuGeneratorQueue;
24  import org.opentrafficsim.road.network.OTSRoadNetwork;
25  import org.opentrafficsim.road.network.lane.object.SpeedSign;
26  import org.opentrafficsim.swing.gui.AnimationToggles;
27  import org.opentrafficsim.swing.gui.OTSAnimationPanel;
28  import org.opentrafficsim.swing.gui.OTSSimulationApplication;
29  import org.opentrafficsim.swing.gui.OTSSwingApplication;
30  
31  import nl.tudelft.simulation.dsol.SimRuntimeException;
32  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterMap;
33  import nl.tudelft.simulation.dsol.model.outputstatistics.OutputStatistic;
34  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
35  import nl.tudelft.simulation.event.EventInterface;
36  import nl.tudelft.simulation.event.EventListenerInterface;
37  import nl.tudelft.simulation.jstats.streams.MersenneTwister;
38  import nl.tudelft.simulation.jstats.streams.StreamInterface;
39  
40  /**
41   * Template for simulation script.
42   * <p>
43   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
44   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
45   * <p>
46   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 9 apr. 2018 <br>
47   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
48   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
49   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
50   */
51  public abstract class AbstractSimulationScript implements EventListenerInterface
52  {
53      /** Name. */
54      final String name;
55  
56      /** Description. */
57      final String description;
58  
59      /** The simulator. */
60      OTSSimulatorInterface simulator;
61  
62      /** The network. */
63      private OTSRoadNetwork network;
64  
65      /** Properties as String value, e.g. from command line. */
66      private final Map<String, String> props = new HashMap<>();
67  
68      /** GTU colorer. */
69      private GTUColorer gtuColorer = OTSSwingApplication.DEFAULT_COLORER;
70  
71      /**
72       * Constructor.
73       * @param name String; name
74       * @param description String; description
75       * @param properties String[]; properties as name-value pairs
76       */
77      protected AbstractSimulationScript(final String name, final String description, final String[] properties)
78      {
79          this.name = name;
80          this.description = description;
81          this.props.put("seed", "1");
82          this.props.put("startTime", "0");
83          this.props.put("warmupTime", "0");
84          this.props.put("simulationTime", "3600");
85          this.props.put("autorun", "false");
86          setDefaultProperties();
87          for (int i = 0; i < properties.length; i += 2)
88          {
89              System.out.println("Adding argument " + properties[i] + " with argument " + properties[i + 1]);
90              this.props.put(properties[i], properties[i + 1]);
91          }
92      }
93  
94      /**
95       * Sets a property.
96       * @param propertyName String; property name
97       * @param propertyValue Object; property value
98       */
99      public final void setProperty(final String propertyName, final Object propertyValue)
100     {
101         this.props.put(propertyName, propertyValue.toString());
102     }
103 
104     /**
105      * Returns the String value of given property.
106      * @param propertyName String; property name
107      * @return String; value of property
108      */
109     public final String getProperty(final String propertyName)
110     {
111         String p = this.props.get(propertyName);
112         Throw.when(p == null, IllegalStateException.class, "Property %s is not given.", propertyName);
113         return p;
114     }
115 
116     /**
117      * Returns the double value of given property.
118      * @param propertyName String; property name
119      * @return double; value of property
120      */
121     public final double getDoubleProperty(final String propertyName)
122     {
123         return Double.parseDouble(getProperty(propertyName));
124     }
125 
126     /**
127      * Returns the boolean value of given property.
128      * @param propertyName String; property name
129      * @return double; value of property
130      */
131     public final boolean getBooleanProperty(final String propertyName)
132     {
133         return Boolean.parseBoolean(getProperty(propertyName));
134     }
135 
136     /**
137      * Returns the int value of given property.
138      * @param propertyName String; property name
139      * @return int; value of property
140      */
141     public final int getIntegerProperty(final String propertyName)
142     {
143         return Integer.parseInt(getProperty(propertyName));
144     }
145 
146     /**
147      * Returns the long value of given property.
148      * @param propertyName String; property name
149      * @return long; value of property
150      */
151     public final long getLongProperty(final String propertyName)
152     {
153         return Long.parseLong(getProperty(propertyName));
154     }
155 
156     /**
157      * Returns the Duration value of given property.
158      * @param propertyName String; property name
159      * @return Duration; value of property
160      */
161     public final Duration getDurationProperty(final String propertyName)
162     {
163         return Duration.createSI(getDoubleProperty(propertyName));
164     }
165 
166     /**
167      * Returns the Time value of given property.
168      * @param propertyName String; property name
169      * @return Time; value of property
170      */
171     public final Time getTimeProperty(final String propertyName)
172     {
173         return Time.createSI(getDoubleProperty(propertyName));
174     }
175 
176     /**
177      * Set GTU colorer.
178      * @param colorer GTUColorer; GTU colorer
179      */
180     public final void setGtuColorer(final GTUColorer colorer)
181     {
182         this.gtuColorer = colorer;
183     }
184 
185     /**
186      * Returns the GTU colorer.
187      * @return returns the GTU colorer
188      */
189     public final GTUColorer getGtuColorer()
190     {
191         return this.gtuColorer;
192     }
193 
194     /**
195      * Starts the simulation.
196      * @throws Exception on any exception
197      */
198     public final void start() throws Exception
199     {
200         Time startTime = getTimeProperty("startTime");
201         Duration warmupTime = getDurationProperty("warmupTime");
202         Duration simulationTime = getDurationProperty("simulationTime");
203         if (getBooleanProperty("autorun"))
204         {
205             this.simulator = new OTSSimulator();
206             final ScriptModel scriptModel = new ScriptModel(this.simulator);
207             this.simulator.initialize(startTime, warmupTime, simulationTime, scriptModel);
208             this.simulator.addListener(this, SimulatorInterface.END_REPLICATION_EVENT);
209             double tReport = 60.0;
210             Time t = this.simulator.getSimulatorTime();
211             while (t.si < simulationTime.si)
212             {
213                 this.simulator.step();
214                 t = this.simulator.getSimulatorTime();
215                 if (t.si >= tReport)
216                 {
217                     System.out.println("Simulation time is " + t);
218                     tReport += 60.0;
219                 }
220             }
221             // sim.stop(); // end of simulation event
222             onSimulationEnd(); // TODO this is temporary for as long as stop() gives an exception
223             System.exit(0);
224         }
225         else
226         {
227             this.simulator = new OTSAnimator();
228             final ScriptModel scriptModel = new ScriptModel(this.simulator);
229             this.simulator.initialize(startTime, warmupTime, simulationTime, scriptModel);
230             OTSAnimationPanel animationPanel =
231                     new OTSAnimationPanel(scriptModel.getNetwork().getExtent(), new Dimension(800, 600),
232                             (OTSAnimator) this.simulator, scriptModel, getGtuColorer(), scriptModel.getNetwork());
233             setAnimationToggles(animationPanel);
234             animateNetwork(scriptModel.getNetwork());
235             setupDemo(animationPanel, scriptModel.getNetwork());
236             OTSSimulationApplication<ScriptModel> app = new OTSSimulationApplication<ScriptModel>(scriptModel, animationPanel)
237             {
238                 /** */
239                 private static final long serialVersionUID = 20190130L;
240 
241                 /** {@inheritDoc} */
242                 @Override
243                 protected void animateNetwork() throws OTSDrawingException
244                 {
245                     // override with nothing to prevent double toggles
246                 }
247 
248                 /** {@inheritDoc} */
249                 @Override
250                 protected void setAnimationToggles()
251                 {
252                     // override with nothing to prevent double toggles
253                 }
254             };
255             addTabs(this.simulator, app);
256             app.setExitOnClose(true);
257         }
258     }
259 
260     /** {@inheritDoc} */
261     @Override
262     public final void notify(final EventInterface event) throws RemoteException
263     {
264         if (event.getType().equals(SimulatorInterface.END_REPLICATION_EVENT))
265         {
266             onSimulationEnd();
267             // solve bug that event is fired twice
268             AbstractSimulationScript.this.simulator.removeListener(AbstractSimulationScript.this,
269                     SimulatorInterface.END_REPLICATION_EVENT);
270         }
271     }
272 
273     /**
274      * Returns the simulator.
275      * @return OTSSimulatorInterface; simulator
276      */
277     public final OTSSimulatorInterface getSimulator()
278     {
279         return AbstractSimulationScript.this.simulator;
280     }
281 
282     /**
283      * Returns the network.
284      * @return OTSNetwork; network
285      */
286     public final OTSRoadNetwork getNetwork()
287     {
288         return AbstractSimulationScript.this.network;
289     }
290 
291     // Overridable methods
292 
293     /**
294      * Creates animations for nodes, links and lanes. This can be used if the network is not read from XML.
295      * @param net OTSNetwork; network
296      */
297     protected void animateNetwork(final OTSNetwork net)
298     {
299         try
300         {
301             DefaultAnimationFactory.animateNetwork(net, getSimulator(), getGtuColorer());
302         }
303         catch (OTSDrawingException exception)
304         {
305             throw new RuntimeException("Exception while creating network animation.", exception);
306         }
307     }
308 
309     /**
310      * Adds tabs to the animation. May be overridden.
311      * @param sim OTSSimulatorInterface; simulator
312      * @param animation OTSSimulationApplication&lt;?&gt;; animation to add tabs to
313      */
314     protected void addTabs(final OTSSimulatorInterface sim, final OTSSimulationApplication<?> animation)
315     {
316         //
317     }
318 
319     /**
320      * Sets the default properties. Can be overridden and use method {@code setProperty()}. Default implementation does nothing.
321      */
322     protected void setDefaultProperties()
323     {
324         //
325     }
326 
327     /**
328      * Method that is called when the simulation has ended. This can be used to store data.
329      */
330     protected void onSimulationEnd()
331     {
332         //
333     }
334 
335     /**
336      * Method that is called when the animation has been created, to add components for a demo.
337      * @param animationPanel OTSAnimationPanel; animation panel
338      * @param net OTSNetwork; network
339      */
340     protected void setupDemo(final OTSAnimationPanel animationPanel, final OTSRoadNetwork net)
341     {
342         //
343     }
344 
345     /**
346      * Sets the animation toggles. May be overridden.
347      * @param animation OTSAnimationPanel; animation to set the toggle on
348      */
349     protected void setAnimationToggles(final OTSAnimationPanel animation)
350     {
351         AnimationToggles.setIconAnimationTogglesFull(animation);
352         animation.getAnimationPanel().toggleClass(OTSLink.class);
353         animation.getAnimationPanel().toggleClass(OTSNode.class);
354         animation.getAnimationPanel().toggleClass(GtuGeneratorQueue.class);
355         animation.getAnimationPanel().showClass(SpeedSign.class);
356     }
357 
358     // Abstract methods
359 
360     /**
361      * Sets up the simulation based on provided properties. Properties can be obtained with {@code getProperty()}. Setting up a
362      * simulation should at least create a network and some demand. Additionally this may setup traffic control, sampling, etc.
363      * @param sim OTSSimulatorInterface; simulator
364      * @return OTSNetwork; network
365      * @throws Exception on any exception
366      */
367     protected abstract OTSRoadNetwork setupSimulation(OTSSimulatorInterface sim) throws Exception;
368 
369     // Nested classes
370 
371     /**
372      * Model.
373      * <p>
374      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
375      * <br>
376      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
377      * <p>
378      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 9 apr. 2018 <br>
379      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
380      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
381      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
382      */
383     private class ScriptModel implements OTSModelInterface
384     {
385         /** */
386         private static final long serialVersionUID = 20180409L;
387 
388         /**
389          * @param simulator OTSSimulatorInterface; the simulator
390          */
391         ScriptModel(final OTSSimulatorInterface simulator)
392         {
393             AbstractSimulationScript.this.simulator = simulator;
394         }
395 
396         /** {@inheritDoc} */
397         @SuppressWarnings("synthetic-access")
398         @Override
399         public void constructModel() throws SimRuntimeException
400         {
401             Map<String, StreamInterface> streams = new HashMap<>();
402             long seed = getLongProperty("seed");
403             StreamInterface stream = new MersenneTwister(seed);
404             streams.put("generation", stream);
405             stream = new MersenneTwister(seed + 1);
406             streams.put("default", stream);
407             AbstractSimulationScript.this.simulator.getReplication().setStreams(streams);
408             AbstractSimulationScript.this.network =
409                     Try.assign(() -> AbstractSimulationScript.this.setupSimulation(AbstractSimulationScript.this.simulator),
410                             RuntimeException.class, "Exception while setting up simulation.");
411             try
412             {
413                 AbstractSimulationScript.this.simulator.addListener(AbstractSimulationScript.this,
414                         SimulatorInterface.END_REPLICATION_EVENT);
415             }
416             catch (RemoteException exception)
417             {
418                 throw new SimRuntimeException(exception);
419             }
420         }
421 
422         /** {@inheritDoc} */
423         @Override
424         public OTSSimulatorInterface getSimulator()
425         {
426             return AbstractSimulationScript.this.simulator;
427         }
428 
429         /** {@inheritDoc} */
430         @SuppressWarnings("synthetic-access")
431         @Override
432         public OTSRoadNetwork getNetwork()
433         {
434             return AbstractSimulationScript.this.network;
435         }
436 
437         /** {@inheritDoc} */
438         @Override
439         public InputParameterMap getInputParameterMap()
440         {
441             return null;
442         }
443 
444         /** {@inheritDoc} */
445         @Override
446         public List<OutputStatistic<?>> getOutputStatistics()
447         {
448             return null;
449         }
450 
451         /** {@inheritDoc} */
452         @Override
453         public String getShortName()
454         {
455             return AbstractSimulationScript.this.name;
456         }
457 
458         /** {@inheritDoc} */
459         @Override
460         public String getDescription()
461         {
462             return AbstractSimulationScript.this.description;
463         }
464     }
465 
466 }