View Javadoc
1   package org.opentrafficsim.imb.demo;
2   
3   import java.awt.Color;
4   import java.awt.Dimension;
5   import java.awt.geom.Rectangle2D;
6   import java.lang.reflect.InvocationTargetException;
7   import java.rmi.RemoteException;
8   import java.util.ArrayList;
9   import java.util.HashSet;
10  import java.util.LinkedHashSet;
11  import java.util.List;
12  import java.util.Random;
13  import java.util.Set;
14  
15  import javax.naming.NamingException;
16  import javax.swing.JFrame;
17  import javax.swing.SwingUtilities;
18  
19  import org.djunits.unit.LengthUnit;
20  import org.djunits.unit.TimeUnit;
21  import org.djunits.unit.UNITS;
22  import org.djunits.value.vdouble.scalar.Acceleration;
23  import org.djunits.value.vdouble.scalar.Duration;
24  import org.djunits.value.vdouble.scalar.Length;
25  import org.djunits.value.vdouble.scalar.Speed;
26  import org.djunits.value.vdouble.scalar.Time;
27  import org.opentrafficsim.base.modelproperties.BooleanProperty;
28  import org.opentrafficsim.base.modelproperties.CompoundProperty;
29  import org.opentrafficsim.base.modelproperties.ContinuousProperty;
30  import org.opentrafficsim.base.modelproperties.IntegerProperty;
31  import org.opentrafficsim.base.modelproperties.ProbabilityDistributionProperty;
32  import org.opentrafficsim.base.modelproperties.Property;
33  import org.opentrafficsim.base.modelproperties.PropertyException;
34  import org.opentrafficsim.base.modelproperties.SelectionProperty;
35  import org.opentrafficsim.core.dsol.OTSDEVSRealTimeClock;
36  import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
37  import org.opentrafficsim.core.dsol.OTSModelInterface;
38  import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
39  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
40  import org.opentrafficsim.core.geometry.OTSGeometryException;
41  import org.opentrafficsim.core.geometry.OTSPoint3D;
42  import org.opentrafficsim.core.gtu.GTUDirectionality;
43  import org.opentrafficsim.core.gtu.GTUException;
44  import org.opentrafficsim.core.gtu.GTUType;
45  import org.opentrafficsim.core.gtu.RelativePosition;
46  import org.opentrafficsim.core.gtu.animation.DefaultSwitchableGTUColorer;
47  import org.opentrafficsim.core.gtu.animation.GTUColorer;
48  import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
49  import org.opentrafficsim.core.network.LongitudinalDirectionality;
50  import org.opentrafficsim.core.network.Network;
51  import org.opentrafficsim.core.network.NetworkException;
52  import org.opentrafficsim.core.network.OTSNetwork;
53  import org.opentrafficsim.core.network.OTSNode;
54  import org.opentrafficsim.graphs.LaneBasedGTUSampler;
55  import org.opentrafficsim.imb.IMBException;
56  import org.opentrafficsim.imb.connector.IMBConnector;
57  import org.opentrafficsim.imb.transceiver.urbanstrategy.GTUTransceiver;
58  import org.opentrafficsim.imb.transceiver.urbanstrategy.LaneGTUTransceiver;
59  import org.opentrafficsim.imb.transceiver.urbanstrategy.LinkGTUTransceiver;
60  import org.opentrafficsim.imb.transceiver.urbanstrategy.NetworkTransceiver;
61  import org.opentrafficsim.imb.transceiver.urbanstrategy.NodeTransceiver;
62  import org.opentrafficsim.imb.transceiver.urbanstrategy.SensorGTUTransceiver;
63  import org.opentrafficsim.imb.transceiver.urbanstrategy.SimulatorTransceiver;
64  import org.opentrafficsim.road.gtu.animation.DefaultCarAnimation;
65  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
66  import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
67  import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedCFLCTacticalPlannerFactory;
68  import org.opentrafficsim.road.gtu.lane.tactical.following.AbstractIDM;
69  import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
70  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMOld;
71  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusFactory;
72  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusOld;
73  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.AbstractLaneChangeModel;
74  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Altruistic;
75  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Egoistic;
76  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LMRSFactory;
77  import org.opentrafficsim.road.gtu.lane.tactical.toledo.ToledoFactory;
78  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
79  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlannerFactory;
80  import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlannerFactory;
81  import org.opentrafficsim.road.modelproperties.IDMPropertySet;
82  import org.opentrafficsim.road.network.factory.LaneFactory;
83  import org.opentrafficsim.road.network.lane.CrossSectionElement;
84  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
85  import org.opentrafficsim.road.network.lane.Lane;
86  import org.opentrafficsim.road.network.lane.LaneType;
87  import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
88  import org.opentrafficsim.road.network.lane.object.sensor.AbstractSensor;
89  import org.opentrafficsim.road.network.lane.object.sensor.SensorAnimation;
90  
91  import nl.tno.imb.TConnection;
92  import nl.tno.imb.mc.ModelParameters;
93  import nl.tno.imb.mc.ModelStarter;
94  import nl.tno.imb.mc.ModelState;
95  import nl.tno.imb.mc.Parameter;
96  import nl.tudelft.simulation.dsol.SimRuntimeException;
97  import nl.tudelft.simulation.dsol.animation.D2.AnimationPanel;
98  import nl.tudelft.simulation.dsol.experiment.Replication;
99  import nl.tudelft.simulation.dsol.experiment.ReplicationMode;
100 import nl.tudelft.simulation.dsol.simulators.AnimatorInterface;
101 import nl.tudelft.simulation.dsol.simulators.Simulator;
102 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
103 import nl.tudelft.simulation.event.Event;
104 
105 /**
106  * Demonstrate an implementation of ModelEvent.
107  * <p>
108  * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
109  * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
110  * <p>
111  * @version $Revision$, $LastChangedDate$, by $Author$, initial version Oct 21, 2016 <br>
112  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
113  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
114  * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
115  */
116 public class ModelControlDemo extends ModelStarter
117 {
118     /** The thread that does the work. */
119     private CircularRoadIMB model = null;
120 
121     /**
122      * @param args
123      * @param providedModelName
124      * @param providedModelId
125      * @throws IMBException
126      */
127     public ModelControlDemo(String[] args, String providedModelName, int providedModelId) throws IMBException
128     {
129         super(args, providedModelName, providedModelId);
130     }
131 
132     /**
133      * Find a particular property in a list of (compound) properties. <br>
134      * Re-computing the keyPath is somewhat inefficient. Could be improved if we do not use the iterator of Property.
135      * @param properties List&lt;Property&lt;?&gt;&gt;; the list
136      * @param key String; the key path of the sought property
137      * @return Property&lt;?&gt;; the first matching property, or null if no property with the given key was found
138      */
139     private Property<?> findPropertyInList(final List<Property<?>> properties, final String key)
140     {
141         for (Property<?> property : properties)
142         {
143             for (Property<?> p : property)
144             {
145                 String keyPath = p.getKey();
146                 for (Property<?> parent = p.getParent(); null != parent; parent = parent.getParent())
147                 {
148                     keyPath = parent.getKey() + "." + keyPath;
149                 }
150                 // System.out.println("Comparing property key path " + keyPath + " to key " + key);
151                 if (key.equals(keyPath))
152                 {
153                     return property;
154                 }
155             }
156         }
157         return null;
158     }
159 
160     /** {@inheritDoc} */
161     @Override
162     public void startModel(final ModelParameters parameters, final TConnection tConnection)
163     {
164         System.out.println("startModel called");
165         System.out.println("parameters: " + parameters);
166         System.out.println("Connection: " + this.connection);
167         String dataSource = null;
168         List<Property<?>> properties = CircularRoadIMB.getSupportedProperties();
169 
170         for (String parameterName : parameters.getParameterNames())
171         {
172             if (parameterName.equals("Federation"))
173             {
174                 continue;
175             }
176             else if (parameterName.equals("DataSource"))
177             {
178                 dataSource = (String) parameters.getParameterByName(parameterName).getValue();
179                 continue;
180             }
181             int pos = parameterName.indexOf(" (");
182             if (pos < 0)
183             {
184                 System.out.println("ignoring parameter " + parameterName);
185                 continue;
186             }
187             String strippedName = parameterName.substring(0, pos);
188             switch (strippedName)
189             {
190                 case "Truck fraction":
191                 {
192                     Property<?> p = findPropertyInList(properties, "TrafficComposition");
193                     if (null == p || !(p instanceof ProbabilityDistributionProperty))
194                     {
195                         System.err.println("Property " + p + " is not a ProbalityDistributionProperty");
196                     }
197                     else
198                     {
199                         ProbabilityDistributionProperty pdp = (ProbabilityDistributionProperty) p;
200                         Double[] values = pdp.getValue();
201                         values[1] = (double) parameters.getParameterByName(parameterName).getValue();
202                         values[0] = 1.0 - values[1];
203                         try
204                         {
205                             System.out.println("Setting TrafficComposition to " + values);
206                             pdp.setValue(values);
207                         }
208                         catch (PropertyException exception)
209                         {
210                             exception.printStackTrace();
211                         }
212                     }
213                     break;
214                 }
215                 default:
216                     System.out.println("Ignoring parameter " + parameterName);
217                     break;
218             }
219         }
220         System.out.println("Not doing anything with dataSource " + dataSource);
221         // TODO: do something with the dataSource
222         try
223         {
224             IMBConnector simulationIMBConnector = new IMBConnector(this.connection);
225             System.out.println("IMBConnector for simulation is " + simulationIMBConnector);
226             this.model = new CircularRoadIMB(new DefaultSwitchableGTUColorer(), new OTSNetwork(""), properties,
227                     simulationIMBConnector);
228             Replication<Time, Duration, OTSSimTimeDouble> replication = new Replication<Time, Duration, OTSSimTimeDouble>(
229                     "rep1", new OTSSimTimeDouble(Time.ZERO), Duration.ZERO, new Duration(1, TimeUnit.HOUR), this.model);
230             OTSDEVSRealTimeClock simulator = new OTSDEVSRealTimeClock();
231             simulator.initialize(replication, ReplicationMode.TERMINATING);
232             signalModelState(ModelState.READY);
233             System.out.println("Reported ModelState.READY");
234         }
235         catch (PropertyException | IMBException | SimRuntimeException | NamingException exception)
236         {
237             exception.printStackTrace();
238         }
239     }
240 
241     /** {@inheritDoc} */
242     @Override
243     public void stopModel()
244     {
245         System.out.println("stopModel called");
246         if (null != this.model)
247         {
248             try
249             {
250                 ((Simulator<Time, Duration, OTSSimTimeDouble>) this.model.getSimulator()).cleanUp();
251                 this.model.closeWindow();
252                 this.model = null;
253             }
254             catch (RemoteException exception)
255             {
256                 exception.printStackTrace();
257             }
258         }
259         else
260         {
261             System.err.println("stopModel called, but no model is running");
262         }
263     }
264 
265     /** {@inheritDoc} */
266     @Override
267     public void quitApplication()
268     {
269         if (null != this.model)
270         {
271             try
272             {
273                 // clean up; even if stopModel was not called before quitApplication
274                 ((Simulator<Time, Duration, OTSSimTimeDouble>) this.model.getSimulator()).cleanUp();
275             }
276             catch (Exception exception)
277             {
278                 System.out.println("caught Exception in quitApplication:");
279                 exception.printStackTrace();
280             }
281         }
282         System.out.println("quitApplication called");
283     }
284 
285     /** {@inheritDoc} */
286     @Override
287     public void parameterRequest(ModelParameters parameters)
288     {
289         System.out.println("serving parameter request");
290         System.out.println("received parameters: " + parameters);
291         List<Property<?>> propertyList = CircularRoadIMB.getSupportedProperties();
292         Property<?> truckFraction = findByKeyInList(propertyList, "TrafficComposition");
293         if (null != truckFraction)
294         {
295             parameters.addParameter(new Parameter("Truck fraction (range 0.0 - 1.0)",
296                     ((ProbabilityDistributionProperty) truckFraction).getValue()[1]));
297         }
298         System.out.println("(possibly) modified paramters: " + parameters);
299     }
300 
301     /**
302      * Find a property with the specified key in a List of properties.
303      * @param propertyList List&lt;Property&lt;?&gt;&gt;; the list
304      * @param key String; the key
305      * @return Property&lt;?&gt; or null if none of the entries in the list contained a property with the specified key
306      */
307     private Property<?> findByKeyInList(final List<Property<?>> propertyList, final String key)
308     {
309         Property<?> result = null;
310         for (Property<?> property : propertyList)
311         {
312             Property<?> p = property.findByKey(key);
313             if (null != p)
314             {
315                 if (null != result)
316                 {
317                     System.err.println("Duplicate property with key " + key + " in provided list");
318                 }
319                 result = p;
320             }
321         }
322         return result;
323     }
324 
325     /**
326      * Entry point
327      * @param args
328      * @throws InvocationTargetException
329      * @throws InterruptedException
330      */
331     public static void main(final String[] args) throws InvocationTargetException, InterruptedException
332     {
333         SwingUtilities.invokeAndWait(new Runnable()
334         {
335 
336             @Override
337             public void run()
338             {
339                 try
340                 {
341                     new ModelControlDemo(args, "Demo Model", 1234);
342                 }
343                 catch (IMBException exception)
344                 {
345                     exception.printStackTrace();
346                 }
347             }
348         });
349     }
350 
351     /**
352      * Simulate traffic on a circular, two-lane road.
353      * <p>
354      * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
355      * <br>
356      * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
357      * <p>
358      * $LastChangedDate: 2016-08-24 13:50:36 +0200 (Wed, 24 Aug 2016) $, @version $Revision: 2144 $, by $Author: pknoppers $,
359      * initial version 1 nov. 2014 <br>
360      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
361      */
362     static class CircularRoadIMB implements OTSModelInterface, UNITS
363     {
364         /** */
365         private static final long serialVersionUID = 20141121L;
366 
367         /** The simulator. */
368         private OTSDEVSSimulatorInterface simulator;
369 
370         /** Number of cars created. */
371         private int carsCreated = 0;
372 
373         /** The car following model, e.g. IDM Plus for cars. */
374         private GTUFollowingModelOld carFollowingModelCars;
375 
376         /** The car following model, e.g. IDM Plus for trucks. */
377         private GTUFollowingModelOld carFollowingModelTrucks;
378 
379         /** The probability that the next generated GTU is a passenger car. */
380         private double carProbability;
381 
382         /** The lane change model. */
383         private AbstractLaneChangeModel laneChangeModel;
384 
385         /** Minimum distance. */
386         private Length minimumDistance = new Length(0, METER);
387 
388         /** The speed limit. */
389         private Speed speedLimit = new Speed(100, KM_PER_HOUR);
390 
391         /** The plots. */
392         private List<LaneBasedGTUSampler> plots = new ArrayList<LaneBasedGTUSampler>();
393 
394         /** User settable properties. */
395         private final List<Property<?>> properties;
396 
397         /** The sequence of Lanes that all vehicles will follow. */
398         private List<List<Lane>> paths = new ArrayList<>();
399 
400         /** The random number generator used to decide what kind of GTU to generate. */
401         private Random randomGenerator = new Random(12345);
402 
403         /** The GTUColorer for the generated vehicles. */
404         private final GTUColorer gtuColorer;
405 
406         /** Strategical planner generator for cars. */
407         private LaneBasedStrategicalPlannerFactory<LaneBasedStrategicalPlanner> strategicalPlannerGeneratorCars = null;
408 
409         /** Strategical planner generator for cars. */
410         private LaneBasedStrategicalPlannerFactory<LaneBasedStrategicalPlanner> strategicalPlannerGeneratorTrucks = null;
411 
412         /** the network as created by the AbstractWrappableIMBAnimation. */
413         private final OTSNetwork network;
414 
415         /** Connector to the IMB hub. */
416         private final IMBConnector imbConnector;
417 
418         /** The window with the animation. */
419         private JFrame frame;
420 
421         /**
422          * @param gtuColorer the default and initial GTUColorer, e.g. a DefaultSwitchableTUColorer.
423          * @param network Network; the network
424          * @param properties AbstractProperty; properties that configure this simulation
425          * @param imbConnector IMBConnector; connection to the IMB hub
426          * @throws PropertyException
427          */
428         CircularRoadIMB(final GTUColorer gtuColorer, final OTSNetwork network, final List<Property<?>> properties,
429                 final IMBConnector imbConnector) throws PropertyException
430         {
431             this.properties = properties;
432             this.gtuColorer = gtuColorer;
433             this.network = network;
434             this.imbConnector = imbConnector;
435         }
436 
437         /**
438          * Construct and return the list of properties that the user may modify.
439          * @return List&lt;AbstractProperty&gt;; the list of properties that the user may modify
440          */
441         public static List<Property<?>> getSupportedProperties()
442         {
443             List<Property<?>> result = new ArrayList<>();
444             result.add(new SelectionProperty("LaneChanging", "Lane changing",
445                     "<html>The lane change strategies vary in politeness.<br>"
446                             + "Two types are implemented:<ul><li>Egoistic (looks only at personal gain).</li>"
447                             + "<li>Altruistic (assigns effect on new and current follower the same weight as "
448                             + "the personal gain).</html>",
449                     new String[] { "Egoistic", "Altruistic" }, 0, false, 500));
450             result.add(new SelectionProperty("TacticalPlanner", "Tactical planner",
451                     "<html>The tactical planner determines if a lane change is desired and possible.</html>",
452                     new String[] { "MOBIL", "LMRS", "Toledo" }, 0, false, 600));
453             result.add(new IntegerProperty("TrackLength", "Track length", "Circumference of the track", 2000, 500, 6000,
454                     "Track length %dm", false, 10));
455             result.add(new ContinuousProperty("MeanDensity", "Mean density", "Number of vehicles per km", 40.0, 5.0, 45.0,
456                     "Density %.1f veh/km", false, 11));
457             result.add(new ContinuousProperty("DensityVariability", "Density variability",
458                     "Variability of the number of vehicles per km", 0.0, 0.0, 1.0, "%.1f", false, 12));
459             List<Property<?>> outputProperties = new ArrayList<>();
460             try
461             {
462                 for (int lane = 1; lane <= 2; lane++)
463                 {
464                     String laneId = String.format("Lane %d ", lane);
465                     outputProperties.add(new BooleanProperty(laneId + "Density", laneId + " Density",
466                             laneId + "Density contour plot", true, false, 0));
467                     outputProperties.add(new BooleanProperty(laneId + "Flow", laneId + " Flow", laneId + "Flow contour plot",
468                             true, false, 1));
469                     outputProperties.add(new BooleanProperty(laneId + "Speed", laneId + " Speed", laneId + "Speed contour plot",
470                             true, false, 2));
471                     outputProperties.add(new BooleanProperty(laneId + "Acceleration", laneId + " Acceleration",
472                             laneId + "Acceleration contour plot", true, false, 3));
473                     outputProperties.add(new BooleanProperty(laneId + "Trajectories", laneId + " Trajectories",
474                             laneId + "Trajectory (time/distance) diagram", true, false, 4));
475                 }
476                 result.add(new CompoundProperty("OutputGraphs", "Output graphs", "Select the graphical output",
477                         outputProperties, true, 1000));
478                 result.add(new ProbabilityDistributionProperty("TrafficComposition", "Traffic composition",
479                         "<html>Mix of passenger cars and trucks</html>", new String[] { "passenger car", "truck" },
480                         new Double[] { 0.8, 0.2 }, false, 10));
481                 result.add(new SelectionProperty("CarFollowingModel", "Car following model",
482                         "<html>The car following model determines "
483                                 + "the acceleration that a vehicle will make taking into account "
484                                 + "nearby vehicles, infrastructural restrictions (e.g. speed limit, "
485                                 + "curvature of the road) capabilities of the vehicle and personality "
486                                 + "of the driver.</html>",
487                         new String[] { "IDM", "IDM+" }, 1, false, 1));
488                 result.add(IDMPropertySet.makeIDMPropertySet("IDMCar", "Car", new Acceleration(1.0, METER_PER_SECOND_2),
489                         new Acceleration(1.5, METER_PER_SECOND_2), new Length(2.0, METER), new Duration(1.0, SECOND), 2));
490                 result.add(IDMPropertySet.makeIDMPropertySet("IDMTruck", "Truck", new Acceleration(0.5, METER_PER_SECOND_2),
491                         new Acceleration(1.25, METER_PER_SECOND_2), new Length(2.0, METER), new Duration(1.0, SECOND), 3));
492             }
493             catch (PropertyException exception)
494             {
495                 exception.printStackTrace();
496             }
497             return result;
498         }
499 
500         /**
501          * Retrieve the Network.
502          * @return Network; the network
503          */
504         public Network getNetwork()
505         {
506             return this.network;
507         }
508 
509         /**
510          * @param index int; the rank number of the path
511          * @return List&lt;Lane&gt;; the set of lanes for the specified index
512          */
513         public List<Lane> getPath(final int index)
514         {
515             return this.paths.get(index);
516         }
517 
518         /** {@inheritDoc} */
519         @Override
520         public void constructModel(final SimulatorInterface<Time, Duration, OTSSimTimeDouble> theSimulator)
521                 throws SimRuntimeException, RemoteException
522         {
523             AnimationPanel panel = null;
524             if (theSimulator instanceof AnimatorInterface)
525             {
526                 this.frame = new JFrame("Circular Road Simulation with IMB Model Control");
527                 panel = new AnimationPanel(new Rectangle2D.Double(-1000, -1000, 2000, 2000), new Dimension(1000, 1000),
528                         theSimulator);
529                 this.frame.add(panel);
530                 this.frame.setSize(new Dimension(1000, 1000));
531                 this.frame.setVisible(true);
532                 // Tell the animation to build the list of animation objects.
533                 panel.notify(new Event(SimulatorInterface.START_REPLICATION_EVENT, theSimulator, null));
534             }
535             OTSDEVSSimulatorInterface imbAnimator = (OTSDEVSSimulatorInterface) theSimulator;
536             if (null != this.imbConnector)
537             {
538                 try
539                 {
540                     System.out.println("CirularRoadIMB: constructModel called; Connecting to IMB");
541                     new NetworkTransceiver(this.imbConnector, imbAnimator, this.network);
542                     new NodeTransceiver(this.imbConnector, imbAnimator, this.network);
543                     new LinkGTUTransceiver(this.imbConnector, imbAnimator, this.network);
544                     new LaneGTUTransceiver(this.imbConnector, imbAnimator, this.network);
545                     new GTUTransceiver(this.imbConnector, imbAnimator, this.network);
546                     new SensorGTUTransceiver(this.imbConnector, imbAnimator, this.network);
547                     new SimulatorTransceiver(this.imbConnector, imbAnimator);
548                 }
549                 catch (IMBException exception)
550                 {
551                     throw new SimRuntimeException(exception);
552                 }
553             }
554             final int laneCount = 2;
555             for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
556             {
557                 this.paths.add(new ArrayList<Lane>());
558             }
559             this.simulator = (OTSDEVSSimulatorInterface) theSimulator;
560             double radius = 6000 / 2 / Math.PI;
561             double headway = 40;
562             double headwayVariability = 0;
563             try
564             {
565                 // Get car-following model name
566                 String carFollowingModelName = null;
567                 CompoundProperty propertyContainer = new CompoundProperty("", "", "", this.properties, false, 0);
568                 Property<?> cfmp = propertyContainer.findByKey("CarFollowingModel");
569                 if (null == cfmp)
570                 {
571                     throw new Error("Cannot find \"Car following model\" property");
572                 }
573                 if (cfmp instanceof SelectionProperty)
574                 {
575                     carFollowingModelName = ((SelectionProperty) cfmp).getValue();
576                 }
577                 else
578                 {
579                     throw new Error("\"Car following model\" property has wrong type");
580                 }
581 
582                 // Get car-following model parameter
583                 for (Property<?> ap : new CompoundProperty("", "", "", this.properties, false, 0))
584                 {
585                     if (ap instanceof CompoundProperty)
586                     {
587                         CompoundProperty cp = (CompoundProperty) ap;
588                         // System.out.println("Checking compound property " + cp);
589                         if (ap.getKey().contains("IDM"))
590                         {
591                             System.out.println("Car following model name appears to be " + ap.getKey());
592                             Acceleration a = IDMPropertySet.getA(cp);
593                             Acceleration b = IDMPropertySet.getB(cp);
594                             Length s0 = IDMPropertySet.getS0(cp);
595                             Duration tSafe = IDMPropertySet.getTSafe(cp);
596                             GTUFollowingModelOld gtuFollowingModel = null;
597                             if (carFollowingModelName.equals("IDM"))
598                             {
599                                 gtuFollowingModel = new IDMOld(a, b, s0, tSafe, 1.0);
600                             }
601                             else if (carFollowingModelName.equals("IDM+"))
602                             {
603                                 gtuFollowingModel = new IDMPlusOld(a, b, s0, tSafe, 1.0);
604                             }
605                             else
606                             {
607                                 throw new Error("Unknown gtu following model: " + carFollowingModelName);
608                             }
609                             if (ap.getKey().contains("Car"))
610                             {
611                                 this.carFollowingModelCars = gtuFollowingModel;
612                             }
613                             else if (ap.getKey().contains("Truck"))
614                             {
615                                 this.carFollowingModelTrucks = gtuFollowingModel;
616                             }
617                             else
618                             {
619                                 throw new Error("Cannot determine gtu type for " + ap.getKey());
620                             }
621                         }
622                     }
623                 }
624 
625                 // Get lane change model
626                 cfmp = propertyContainer.findByKey("LaneChanging");
627                 if (null == cfmp)
628                 {
629                     throw new Error("Cannot find \"Lane changing\" property");
630                 }
631                 if (cfmp instanceof SelectionProperty)
632                 {
633                     String laneChangeModelName = ((SelectionProperty) cfmp).getValue();
634                     if ("Egoistic".equals(laneChangeModelName))
635                     {
636                         this.laneChangeModel = new Egoistic();
637                     }
638                     else if ("Altruistic".equals(laneChangeModelName))
639                     {
640                         this.laneChangeModel = new Altruistic();
641                     }
642                     else
643                     {
644                         throw new Error("Lane changing " + laneChangeModelName + " not implemented");
645                     }
646                 }
647                 else
648                 {
649                     throw new Error("\"Lane changing\" property has wrong type");
650                 }
651 
652                 // Get remaining properties
653                 for (Property<?> ap : new CompoundProperty("", "", "", this.properties, false, 0))
654                 {
655                     if (ap instanceof SelectionProperty)
656                     {
657                         SelectionProperty sp = (SelectionProperty) ap;
658                         if ("TacticalPlanner".equals(sp.getKey()))
659                         {
660                             String tacticalPlannerName = sp.getValue();
661                             if ("MOBIL".equals(tacticalPlannerName))
662                             {
663                                 this.strategicalPlannerGeneratorCars =
664                                         new LaneBasedStrategicalRoutePlannerFactory(new LaneBasedCFLCTacticalPlannerFactory(
665                                                 this.carFollowingModelCars, this.laneChangeModel));
666                                 this.strategicalPlannerGeneratorTrucks =
667                                         new LaneBasedStrategicalRoutePlannerFactory(new LaneBasedCFLCTacticalPlannerFactory(
668                                                 this.carFollowingModelTrucks, this.laneChangeModel));
669                             }
670                             else if ("LMRS".equals(tacticalPlannerName))
671                             {
672                                 // provide default parameters with the car-following model
673                                 BehavioralCharacteristics defaultBehavioralCFCharacteristics = new BehavioralCharacteristics();
674                                 defaultBehavioralCFCharacteristics.setDefaultParameters(AbstractIDM.class);
675                                 this.strategicalPlannerGeneratorCars = new LaneBasedStrategicalRoutePlannerFactory(
676                                         new LMRSFactory(new IDMPlusFactory(), defaultBehavioralCFCharacteristics));
677                                 this.strategicalPlannerGeneratorTrucks = new LaneBasedStrategicalRoutePlannerFactory(
678                                         new LMRSFactory(new IDMPlusFactory(), defaultBehavioralCFCharacteristics));
679                             }
680                             else if ("Toledo".equals(tacticalPlannerName))
681                             {
682                                 this.strategicalPlannerGeneratorCars =
683                                         new LaneBasedStrategicalRoutePlannerFactory(new ToledoFactory());
684                                 this.strategicalPlannerGeneratorTrucks =
685                                         new LaneBasedStrategicalRoutePlannerFactory(new ToledoFactory());
686                             }
687                             else
688                             {
689                                 throw new Error("Don't know how to create a " + tacticalPlannerName + " tactical planner");
690                             }
691                         }
692                     }
693                     else if (ap instanceof ProbabilityDistributionProperty)
694                     {
695                         ProbabilityDistributionProperty pdp = (ProbabilityDistributionProperty) ap;
696                         if (ap.getKey().equals("TrafficComposition"))
697                         {
698                             this.carProbability = pdp.getValue()[0];
699                         }
700                     }
701                     else if (ap instanceof IntegerProperty)
702                     {
703                         IntegerProperty ip = (IntegerProperty) ap;
704                         if ("TrackLength".equals(ip.getKey()))
705                         {
706                             radius = ip.getValue() / 2 / Math.PI;
707                         }
708                     }
709                     else if (ap instanceof ContinuousProperty)
710                     {
711                         ContinuousProperty cp = (ContinuousProperty) ap;
712                         if (cp.getKey().equals("MeanDensity"))
713                         {
714                             headway = 1000 / cp.getValue();
715                         }
716                         if (cp.getKey().equals("DensityVariability"))
717                         {
718                             headwayVariability = cp.getValue();
719                         }
720                     }
721                     else if (ap instanceof CompoundProperty)
722                     {
723                         if (ap.getKey().equals("OutputGraphs"))
724                         {
725                             continue; // Output settings are handled elsewhere
726                         }
727                     }
728                 }
729                 GTUType gtuType = new GTUType("car");
730                 Set<GTUType> compatibility = new HashSet<GTUType>();
731                 compatibility.add(gtuType);
732                 LaneType laneType = new LaneType("CarLane", compatibility);
733                 OTSNode start = new OTSNode(this.network, "Start", new OTSPoint3D(radius, 0, 0));
734                 OTSNode halfway = new OTSNode(this.network, "Halfway", new OTSPoint3D(-radius, 0, 0));
735 
736                 OTSPoint3D[] coordsHalf1 = new OTSPoint3D[127];
737                 for (int i = 0; i < coordsHalf1.length; i++)
738                 {
739                     double angle = Math.PI * (1 + i) / (1 + coordsHalf1.length);
740                     coordsHalf1[i] = new OTSPoint3D(radius * Math.cos(angle), radius * Math.sin(angle), 0);
741                 }
742                 Lane[] lanes1 = LaneFactory.makeMultiLane(this.network, "FirstHalf", start, halfway, coordsHalf1, laneCount,
743                         laneType, this.speedLimit, this.simulator, LongitudinalDirectionality.DIR_PLUS);
744                 OTSPoint3D[] coordsHalf2 = new OTSPoint3D[127];
745                 for (int i = 0; i < coordsHalf2.length; i++)
746                 {
747                     double angle = Math.PI + Math.PI * (1 + i) / (1 + coordsHalf2.length);
748                     coordsHalf2[i] = new OTSPoint3D(radius * Math.cos(angle), radius * Math.sin(angle), 0);
749                 }
750                 Lane[] lanes2 = LaneFactory.makeMultiLane(this.network, "SecondHalf", halfway, start, coordsHalf2, laneCount,
751                         laneType, this.speedLimit, this.simulator, LongitudinalDirectionality.DIR_PLUS);
752                 for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
753                 {
754                     this.paths.get(laneIndex).add(lanes1[laneIndex]);
755                     this.paths.get(laneIndex).add(lanes2[laneIndex]);
756                 }
757                 // create a sensor on every lane
758                 int sensorNr = 0;
759                 for (Lane lane : lanes1)
760                 {
761                     SimpleSilentSensor sensor = new SimpleSilentSensor("sensor " + ++sensorNr, lane,
762                             new Length(10.0, LengthUnit.METER), RelativePosition.FRONT, imbAnimator);
763                     lane.addSensor(sensor, gtuType);
764                 }
765                 for (Lane lane : lanes2)
766                 {
767                     SimpleSilentSensor sensor = new SimpleSilentSensor("sensor" + ++sensorNr, lane,
768                             new Length(20.0, LengthUnit.METER), RelativePosition.REAR, imbAnimator);
769                     lane.addSensor(sensor, gtuType);
770                 }
771                 // Put the (not very evenly spaced) cars on the track
772                 double variability = (headway - 20) * headwayVariability;
773                 System.out.println("headway is " + headway + " variability limit is " + variability);
774                 Random random = new Random(12345);
775                 for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
776                 {
777                     double lane1Length = lanes1[laneIndex].getLength().getSI();
778                     double trackLength = lane1Length + lanes2[laneIndex].getLength().getSI();
779                     for (double pos = 0; pos <= trackLength - headway - variability;)
780                     {
781                         Lane lane = pos >= lane1Length ? lanes2[laneIndex] : lanes1[laneIndex];
782                         // Actual headway is uniformly distributed around headway
783                         double laneRelativePos = pos > lane1Length ? pos - lane1Length : pos;
784                         double actualHeadway = headway + (random.nextDouble() * 2 - 1) * variability;
785                         // System.out.println(lane + ", len=" + lane.getLength() + ", pos=" + laneRelativePos);
786                         generateCar(new Length(laneRelativePos, METER), lane, gtuType);
787                         pos += actualHeadway;
788                     }
789                 }
790                 // Schedule regular updates of the graph
791                 this.simulator.scheduleEventAbs(new Time(9.999, SECOND), this, this, "drawGraphs", null);
792             }
793             catch (SimRuntimeException | NamingException | NetworkException | GTUException | OTSGeometryException
794                     | PropertyException exception)
795             {
796                 exception.printStackTrace();
797             }
798             if (null != panel)
799             {
800                 // panel.zoom(1.0);
801                 ((AnimatorInterface) this.simulator).updateAnimation();
802             }
803         }
804 
805         /**
806          * Close and destroy the window. Please shut down and cleanup the simulator first.
807          */
808         @SuppressWarnings("unchecked")
809         public void closeWindow()
810         {
811             try
812             {
813                 ((Simulator<Time, Duration, OTSSimTimeDouble>) this.simulator).cleanUp();
814             }
815             catch (Exception exception)
816             {
817                 exception.printStackTrace();
818             }
819             this.frame.dispose();
820         }
821 
822         /**
823          * Notify the contour plots that the underlying data has changed.
824          */
825         protected final void drawGraphs()
826         {
827             for (LaneBasedGTUSampler plot : this.plots)
828             {
829                 plot.reGraph();
830             }
831             // Re schedule this method
832             try
833             {
834                 this.simulator.scheduleEventAbs(new Time(this.simulator.getSimulatorTime().get().getSI() + 10, SECOND), this,
835                         this, "drawGraphs", null);
836             }
837             catch (SimRuntimeException exception)
838             {
839                 exception.printStackTrace();
840             }
841 
842         }
843 
844         /**
845          * Generate cars at a fixed rate (implemented by re-scheduling this method).
846          * @param initialPosition Length; the initial position of the new cars
847          * @param lane Lane; the lane on which the new cars are placed
848          * @param gtuType GTUType&lt;String&gt;; the type of the new cars
849          * @throws NamingException on ???
850          * @throws SimRuntimeException cannot happen
851          * @throws NetworkException on network inconsistency
852          * @throws GTUException when something goes wrong during construction of the car
853          * @throws OTSGeometryException when the initial position is outside the center line of the lane
854          */
855         protected final void generateCar(final Length initialPosition, final Lane lane, final GTUType gtuType)
856                 throws NamingException, NetworkException, SimRuntimeException, GTUException, OTSGeometryException
857         {
858 
859             // GTU itself
860             boolean generateTruck = this.randomGenerator.nextDouble() > this.carProbability;
861             Length vehicleLength = new Length(generateTruck ? 15 : 4, METER);
862             LaneBasedIndividualGTU gtu = new LaneBasedIndividualGTU("" + (++this.carsCreated), gtuType, vehicleLength,
863                     new Length(1.8, METER), new Speed(200, KM_PER_HOUR), this.simulator, this.network);
864 
865             // strategical planner
866             LaneBasedStrategicalPlanner strategicalPlanner;
867             if (!generateTruck)
868             {
869                 strategicalPlanner = this.strategicalPlannerGeneratorCars.create(gtu);
870             }
871             else
872             {
873                 strategicalPlanner = this.strategicalPlannerGeneratorTrucks.create(gtu);
874             }
875 
876             // init
877             Set<DirectedLanePosition> initialPositions = new LinkedHashSet<>(1);
878             initialPositions.add(new DirectedLanePosition(lane, initialPosition, GTUDirectionality.DIR_PLUS));
879             Speed initialSpeed = new Speed(0, KM_PER_HOUR);
880             gtu.initWithAnimation(strategicalPlanner, initialPositions, initialSpeed, DefaultCarAnimation.class,
881                     this.gtuColorer);
882         }
883 
884         /** {@inheritDoc} */
885         @Override
886         public SimulatorInterface<Time, Duration, OTSSimTimeDouble> getSimulator() throws RemoteException
887         {
888             return this.simulator;
889         }
890 
891         /**
892          * @return plots
893          */
894         public final List<LaneBasedGTUSampler> getPlots()
895         {
896             return this.plots;
897         }
898 
899         /**
900          * @return minimumDistance
901          */
902         public final Length getMinimumDistance()
903         {
904             return this.minimumDistance;
905         }
906 
907         /**
908          * Stop simulation and throw an Error.
909          * @param theSimulator OTSDEVSSimulatorInterface; the simulator
910          * @param errorMessage String; the error message
911          */
912         public void stopSimulator(final OTSDEVSSimulatorInterface theSimulator, final String errorMessage)
913         {
914             System.out.println("Error: " + errorMessage);
915             try
916             {
917                 if (theSimulator.isRunning())
918                 {
919                     theSimulator.stop();
920                 }
921             }
922             catch (SimRuntimeException exception)
923             {
924                 exception.printStackTrace();
925             }
926             throw new Error(errorMessage);
927         }
928 
929         /**
930          * Simple sensor that does not provide output, but is drawn on the Lanes.
931          * <p>
932          * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights
933          * reserved. <br>
934          * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
935          * </p>
936          * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck
937          * $, initial version Sep 18, 2016 <br>
938          * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
939          * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
940          * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
941          */
942         public static class SimpleSilentSensor extends AbstractSensor
943         {
944             /** */
945             private static final long serialVersionUID = 20150130L;
946 
947             /**
948              * @param lane the lane of the sensor.
949              * @param position the position of the sensor
950              * @param triggerPosition RelativePosition.TYPE; the relative position type (e.g., FRONT, REAR) of the vehicle that
951              *            triggers the sensor.
952              * @param id the id of the sensor.
953              * @param simulator the simulator to enable animation.
954              * @throws NetworkException when the position on the lane is out of bounds w.r.t. the center line of the lane
955              * @throws OTSGeometryException when the geometry of the sensor cannot be calculated, e.g. when the lane width is
956              *             zero, or the position is beyond or before the lane length
957              */
958             public SimpleSilentSensor(final String id, final Lane lane, final Length position,
959                     final RelativePosition.TYPE triggerPosition, final OTSDEVSSimulatorInterface simulator)
960                     throws NetworkException, OTSGeometryException
961             {
962                 super(id, lane, position, triggerPosition, simulator, LaneBasedObject.makeGeometry(lane, position));
963                 try
964                 {
965                     new SensorAnimation(this, position, simulator, Color.RED);
966                 }
967                 catch (RemoteException | NamingException exception)
968                 {
969                     exception.printStackTrace();
970                 }
971             }
972 
973             /** {@inheritDoc} */
974             @Override
975             public final void triggerResponse(final LaneBasedGTU gtu)
976             {
977                 // do nothing.
978             }
979 
980             /** {@inheritDoc} */
981             @Override
982             public final String toString()
983             {
984                 return "SimpleSilentSensor [Lane=" + this.getLane() + "]";
985             }
986 
987             /** {@inheritDoc} */
988             @Override
989             public AbstractSensor clone(final CrossSectionElement newCSE, final OTSSimulatorInterface newSimulator,
990                     final boolean animation) throws NetworkException
991             {
992                 // not important in this class.
993                 return null;
994             }
995         }
996     }
997 
998 }