View Javadoc
1   package org.opentrafficsim.road.animation;
2   
3   import java.awt.Color;
4   import java.rmi.RemoteException;
5   import java.util.ArrayList;
6   import java.util.HashMap;
7   import java.util.Map;
8   
9   import javax.naming.NamingException;
10  
11  import org.djunits.value.vdouble.scalar.Duration;
12  import org.djunits.value.vdouble.scalar.Time;
13  import org.opentrafficsim.base.modelproperties.Property;
14  import org.opentrafficsim.core.dsol.OTSModelInterface;
15  import org.opentrafficsim.core.geometry.OTSGeometryException;
16  import org.opentrafficsim.core.gtu.GTUType;
17  import org.opentrafficsim.core.gtu.Try;
18  import org.opentrafficsim.core.gtu.animation.GTUColorer;
19  import org.opentrafficsim.core.network.LateralDirectionality;
20  import org.opentrafficsim.core.network.Link;
21  import org.opentrafficsim.core.network.Node;
22  import org.opentrafficsim.core.network.OTSLink;
23  import org.opentrafficsim.core.network.OTSNetwork;
24  import org.opentrafficsim.core.network.OTSNode;
25  import org.opentrafficsim.core.network.animation.LinkAnimation;
26  import org.opentrafficsim.core.network.animation.NodeAnimation;
27  import org.opentrafficsim.road.gtu.animation.DefaultSwitchableGTUColorer;
28  import org.opentrafficsim.road.gtu.generator.GTUGenerator;
29  import org.opentrafficsim.road.network.animation.LaneAnimation;
30  import org.opentrafficsim.road.network.animation.ShoulderAnimation;
31  import org.opentrafficsim.road.network.animation.StripeAnimation;
32  import org.opentrafficsim.road.network.animation.StripeAnimation.TYPE;
33  import org.opentrafficsim.road.network.lane.CrossSectionElement;
34  import org.opentrafficsim.road.network.lane.CrossSectionLink;
35  import org.opentrafficsim.road.network.lane.Lane;
36  import org.opentrafficsim.road.network.lane.Shoulder;
37  import org.opentrafficsim.road.network.lane.Stripe;
38  import org.opentrafficsim.road.network.lane.object.SpeedSign;
39  import org.opentrafficsim.simulationengine.AbstractWrappableAnimation;
40  import org.opentrafficsim.simulationengine.AbstractWrappableSimulation;
41  import org.opentrafficsim.simulationengine.OTSSimulationException;
42  import org.opentrafficsim.simulationengine.SimpleSimulatorInterface;
43  
44  import nl.tudelft.simulation.dsol.SimRuntimeException;
45  import nl.tudelft.simulation.dsol.simtime.SimTimeDoubleUnit;
46  import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
47  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
48  import nl.tudelft.simulation.event.EventInterface;
49  import nl.tudelft.simulation.event.EventListenerInterface;
50  import nl.tudelft.simulation.jstats.streams.MersenneTwister;
51  import nl.tudelft.simulation.jstats.streams.StreamInterface;
52  import nl.tudelft.simulation.language.Throw;
53  
54  /**
55   * Template for simulation script.
56   * <p>
57   * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
58   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
59   * <p>
60   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 9 apr. 2018 <br>
61   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
62   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
63   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
64   */
65  public abstract class AbstractSimulationScript implements EventListenerInterface
66  {
67  
68      /** Name. */
69      private final String name;
70  
71      /** Description. */
72      private final String description;
73  
74      /** The simulator. */
75      private DEVSSimulatorInterface.TimeDoubleUnit simulator;
76  
77      /** The network. */
78      private OTSNetwork network;
79  
80      /** Properties as String value, e.g. from command line. */
81      private final Map<String, String> props = new HashMap<>();
82  
83      /** GTU colorer. */
84      private GTUColorer gtuColorer = new DefaultSwitchableGTUColorer();
85  
86      /**
87       * Constructor.
88       * @param name String; name
89       * @param description String; description
90       * @param properties String[]; properties as name-value pairs
91       */
92      protected AbstractSimulationScript(final String name, final String description, final String[] properties)
93      {
94          this.name = name;
95          this.description = description;
96          this.props.put("seed", "1");
97          this.props.put("startTime", "0");
98          this.props.put("warmupTime", "0");
99          this.props.put("simulationTime", "3600");
100         this.props.put("autorun", "false");
101         setDefaultProperties();
102         for (int i = 0; i < properties.length; i += 2)
103         {
104             this.props.put(properties[i], properties[i + 1]);
105         }
106     }
107 
108     /**
109      * Sets a property.
110      * @param propertyName String; property name
111      * @param propertyValue Object; property value
112      */
113     public final void setProperty(final String propertyName, final Object propertyValue)
114     {
115         this.props.put(propertyName, propertyValue.toString());
116     }
117 
118     /**
119      * Returns the String value of given property.
120      * @param propertyName String; property name
121      * @return String; value of property
122      */
123     public final String getProperty(final String propertyName)
124     {
125         String p = this.props.get(propertyName);
126         Throw.when(p == null, IllegalStateException.class, "Property %s is not given.", propertyName);
127         return p;
128     }
129 
130     /**
131      * Returns the double value of given property.
132      * @param propertyName String; property name
133      * @return double; value of property
134      */
135     public final double getDoubleProperty(final String propertyName)
136     {
137         return Double.parseDouble(getProperty(propertyName));
138     }
139 
140     /**
141      * Returns the boolean value of given property.
142      * @param propertyName String; property name
143      * @return double; value of property
144      */
145     public final boolean getBooleanProperty(final String propertyName)
146     {
147         return Boolean.parseBoolean(getProperty(propertyName));
148     }
149 
150     /**
151      * Returns the int value of given property.
152      * @param propertyName String; property name
153      * @return int; value of property
154      */
155     public final int getIntegerProperty(final String propertyName)
156     {
157         return Integer.parseInt(getProperty(propertyName));
158     }
159 
160     /**
161      * Returns the Duration value of given property.
162      * @param propertyName String; property name
163      * @return Duration; value of property
164      */
165     public final Duration getDurationProperty(final String propertyName)
166     {
167         return Duration.createSI(getDoubleProperty(propertyName));
168     }
169 
170     /**
171      * Returns the Time value of given property.
172      * @param propertyName String; property name
173      * @return Time; value of property
174      */
175     public final Time getTimeProperty(final String propertyName)
176     {
177         return Time.createSI(getDoubleProperty(propertyName));
178     }
179 
180     /**
181      * Set GTU colorer.
182      * @param colorer GTUColorer; GTU colorer
183      */
184     public final void setGtuColorer(final GTUColorer colorer)
185     {
186         this.gtuColorer = colorer;
187     }
188 
189     /**
190      * Returns the GTU colorer.
191      * @return returns the GTU colorer
192      */
193     public final GTUColorer getGtuColorer()
194     {
195         return this.gtuColorer;
196     }
197 
198     /**
199      * Starts the simulation.
200      */
201     public final void start()
202     {
203         Time startTime = getTimeProperty("startTime");
204         Duration warmupTime = getDurationProperty("warmupTime");
205         Duration simulationTime = getDurationProperty("simulationTime");
206         if (getBooleanProperty("autorun"))
207         {
208 
209             ScriptSimulation scriptSimulation = this.new ScriptSimulation();
210             try
211             {
212                 DEVSSimulatorInterface.TimeDoubleUnit sim =
213                         scriptSimulation.buildSimulator(startTime, warmupTime, simulationTime, new ArrayList<Property<?>>());
214                 sim.addListener(this, SimulatorInterface.END_OF_REPLICATION_EVENT);
215                 double tReport = 60.0;
216                 Time t = sim.getSimulatorTime();
217                 while (t.si < simulationTime.si)
218                 {
219                     sim.step();
220                     t = sim.getSimulatorTime();
221                     if (t.si >= tReport)
222                     {
223                         System.out.println("Simulation time is " + t);
224                         tReport += 60.0;
225                     }
226                 }
227                 sim.stop(); // end of simulation event
228             }
229             catch (Exception exception)
230             {
231                 exception.printStackTrace();
232             }
233         }
234         else
235         {
236             Try.execute(() -> new ScriptAnimation().buildAnimator(startTime, warmupTime, simulationTime,
237                     new ArrayList<Property<?>>(), null, true), RuntimeException.class, "Exception from properties.");
238         }
239     }
240 
241     /** {@inheritDoc} */
242     @Override
243     public final void notify(final EventInterface event) throws RemoteException
244     {
245         if (event.getType().equals(SimulatorInterface.END_OF_REPLICATION_EVENT))
246         {
247             onSimulationEnd();
248             // solve bug that event is fired twice
249             AbstractSimulationScript.this.simulator.removeListener(AbstractSimulationScript.this,
250                     SimulatorInterface.END_OF_REPLICATION_EVENT);
251         }
252     }
253 
254     /**
255      * Returns the simulator.
256      * @return DEVSSimulatorInterface.TimeDoubleUnit; simulator
257      */
258     public final DEVSSimulatorInterface.TimeDoubleUnit getSimulator()
259     {
260         return AbstractSimulationScript.this.simulator;
261     }
262 
263     /**
264      * Returns the network.
265      * @return OTSNetwork; network
266      */
267     public final OTSNetwork getNetwork()
268     {
269         return AbstractSimulationScript.this.network;
270     }
271 
272     // Overridable methods
273 
274     /**
275      * Creates animations for nodes, links and lanes. This can be used if the network is not read from XML.
276      * @param net OTSNetwork; network
277      */
278     protected void animateNetwork(final OTSNetwork net)
279     {
280         try
281         {
282             for (Node node : net.getNodeMap().values())
283             {
284                 new NodeAnimation(node, AbstractSimulationScript.this.simulator);
285             }
286             for (Link link : net.getLinkMap().values())
287             {
288                 new LinkAnimation(link, AbstractSimulationScript.this.simulator, 0.5f);
289                 if (link instanceof CrossSectionLink)
290                 {
291                     for (CrossSectionElement element : ((CrossSectionLink) link).getCrossSectionElementList())
292                     {
293                         if (element instanceof Lane)
294                         {
295                             new LaneAnimation((Lane) element, AbstractSimulationScript.this.simulator, Color.GRAY.brighter(),
296                                     false);
297                         }
298                         else if (element instanceof Shoulder)
299                         {
300                             new ShoulderAnimation((Shoulder) element, AbstractSimulationScript.this.simulator, Color.DARK_GRAY);
301                         }
302                         else if (element instanceof Stripe)
303                         {
304                             Stripe stripe = (Stripe) element;
305                             TYPE type;
306                             if (stripe.isPermeable(GTUType.CAR, LateralDirectionality.LEFT))
307                             {
308                                 type = stripe.isPermeable(GTUType.CAR, LateralDirectionality.RIGHT) ? TYPE.DASHED
309                                         : TYPE.LEFTONLY;
310                             }
311                             else
312                             {
313                                 type = stripe.isPermeable(GTUType.CAR, LateralDirectionality.RIGHT) ? TYPE.RIGHTONLY
314                                         : TYPE.SOLID;
315                             }
316                             new StripeAnimation((Stripe) element, AbstractSimulationScript.this.simulator, type);
317                         }
318                     }
319                 }
320             }
321         }
322         catch (RemoteException | NamingException | OTSGeometryException exception)
323         {
324             throw new RuntimeException("Exception while creating network animation.", exception);
325         }
326     }
327 
328     /**
329      * Sets the animation toggles. May be overridden.
330      * @param animation AbstractWrappableAnimation; animation to set the toggle on
331      */
332     protected void addAnimationToggles(final AbstractWrappableAnimation animation)
333     {
334         AnimationToggles.setIconAnimationTogglesFull(animation);
335         animation.toggleAnimationClass(OTSLink.class);
336         animation.toggleAnimationClass(OTSNode.class);
337         animation.toggleAnimationClass(GTUGenerator.class);
338         animation.showAnimationClass(SpeedSign.class);
339     }
340 
341     /**
342      * Adds taps to the animation. May be overridden.
343      * @param sim SimpleSimulatorInterface; simulator
344      */
345     protected void addTabs(final SimpleSimulatorInterface sim)
346     {
347         //
348     }
349 
350     /**
351      * Sets the default properties. Can be overridden and use method {@code setProperty()}. Default implementation does nothing.
352      */
353     protected void setDefaultProperties()
354     {
355         //
356     }
357 
358     /**
359      * Method that is called when the simulation has ended. This can be used to store data.
360      */
361     protected void onSimulationEnd()
362     {
363         //
364     }
365 
366     /**
367      * Method that is called when the animation has been created, to add components for a demo.
368      * @param animation AbstractWrappableAnimation; animation
369      * @param net OTSNetwork; network
370      */
371     protected void setupDemo(final AbstractWrappableAnimation animation, final OTSNetwork net)
372     {
373         //
374     }
375 
376     // Abstract methods
377 
378     /**
379      * Sets up the simulation based on provided properties. Properties can be obtained with {@code getProperty()}. Setting up a
380      * simulation should at least create a network and some demand. Additionally this may setup traffic control, sampling, etc.
381      * @param sim DEVSSimulatorInterface.TimeDoubleUnit; simulator
382      * @return OTSNetwork; network
383      * @throws Exception on any exception
384      */
385     protected abstract OTSNetwork setupSimulation(DEVSSimulatorInterface.TimeDoubleUnit sim) throws Exception;
386 
387     // Nested classes
388 
389     /**
390      * Simulation.
391      * <p>
392      * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
393      * <br>
394      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
395      * <p>
396      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 9 apr. 2018 <br>
397      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
398      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
399      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
400      */
401     class ScriptSimulation extends AbstractWrappableSimulation
402     {
403         /** */
404         private static final long serialVersionUID = 20180409L;
405 
406         /** {@inheritDoc} */
407         @SuppressWarnings("synthetic-access")
408         @Override
409         public String shortName()
410         {
411             return AbstractSimulationScript.this.name;
412         }
413 
414         /** {@inheritDoc} */
415         @SuppressWarnings("synthetic-access")
416         @Override
417         public String description()
418         {
419             return AbstractSimulationScript.this.description;
420         }
421 
422         /** {@inheritDoc} */
423         @Override
424         protected OTSModelInterface makeModel() throws OTSSimulationException
425         {
426             return new ScriptModel();
427         }
428     }
429 
430     /**
431      * Animated simulation.
432      * <p>
433      * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
434      * <br>
435      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
436      * <p>
437      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 9 apr. 2018 <br>
438      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
439      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
440      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
441      */
442     class ScriptAnimation extends AbstractWrappableAnimation
443     {
444         /** */
445         private static final long serialVersionUID = 20180409L;
446 
447         /** {@inheritDoc} */
448         @SuppressWarnings("synthetic-access")
449         @Override
450         public String shortName()
451         {
452             return AbstractSimulationScript.this.name;
453         }
454 
455         /** {@inheritDoc} */
456         @SuppressWarnings("synthetic-access")
457         @Override
458         public String description()
459         {
460             return AbstractSimulationScript.this.description;
461         }
462 
463         /** {@inheritDoc} */
464         @Override
465         protected OTSModelInterface makeModel() throws OTSSimulationException
466         {
467             return new ScriptModel();
468         }
469 
470         /** {@inheritDoc} */
471         @Override
472         protected final void addAnimationToggles()
473         {
474             AbstractSimulationScript.this.addAnimationToggles(this);
475         }
476 
477         /** {@inheritDoc} */
478         @Override
479         protected final void addTabs(final SimpleSimulatorInterface sim)
480         {
481             AbstractSimulationScript.this.addTabs(sim);
482         }
483 
484         /** {@inheritDoc} */
485         @SuppressWarnings("synthetic-access")
486         @Override
487         public final GTUColorer getColorer()
488         {
489             return AbstractSimulationScript.this.gtuColorer;
490         }
491 
492         /** {@inheritDoc} */
493         @Override
494         protected void setupDemo(final AbstractWrappableAnimation animation, final OTSNetwork net)
495         {
496             AbstractSimulationScript.this.setupDemo(animation, net);
497         }
498 
499     }
500 
501     /**
502      * Model.
503      * <p>
504      * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
505      * <br>
506      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
507      * <p>
508      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 9 apr. 2018 <br>
509      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
510      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
511      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
512      */
513     private class ScriptModel implements OTSModelInterface
514     {
515         /** */
516         private static final long serialVersionUID = 20180409L;
517 
518         /**
519          * 
520          */
521         ScriptModel()
522         {
523         }
524 
525         /** {@inheritDoc} */
526         @SuppressWarnings("synthetic-access")
527         @Override
528         public void constructModel(final SimulatorInterface<Time, Duration, SimTimeDoubleUnit> sim)
529                 throws SimRuntimeException
530         {
531             AbstractSimulationScript.this.simulator = (DEVSSimulatorInterface.TimeDoubleUnit) sim;
532             Map<String, StreamInterface> streams = new HashMap<>();
533             StreamInterface stream = new MersenneTwister(Long.valueOf(getProperty("seed")));
534             streams.put("generation", stream);
535             sim.getReplication().setStreams(streams);
536             AbstractSimulationScript.this.network =
537                     Try.assign(() -> AbstractSimulationScript.this.setupSimulation((DEVSSimulatorInterface.TimeDoubleUnit) sim),
538                             RuntimeException.class, "Exception while setting up simulation.");
539             try
540             {
541                 AbstractSimulationScript.this.simulator.addListener(AbstractSimulationScript.this,
542                         SimulatorInterface.END_OF_REPLICATION_EVENT);
543             }
544             catch (RemoteException exception)
545             {
546                 throw new SimRuntimeException(exception);
547             }
548         }
549 
550         /** {@inheritDoc} */
551         @SuppressWarnings("synthetic-access")
552         @Override
553         public SimulatorInterface<Time, Duration, SimTimeDoubleUnit> getSimulator()
554         {
555             return AbstractSimulationScript.this.simulator;
556         }
557 
558         /** {@inheritDoc} */
559         @SuppressWarnings("synthetic-access")
560         @Override
561         public OTSNetwork getNetwork()
562         {
563             return AbstractSimulationScript.this.network;
564         }
565     }
566 
567 }