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