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