1   package org.opentrafficsim.road.gtu.generator;
2   
3   import java.io.Serializable;
4   import java.rmi.RemoteException;
5   import java.util.Collections;
6   import java.util.LinkedHashMap;
7   import java.util.LinkedHashSet;
8   import java.util.LinkedList;
9   import java.util.Map;
10  import java.util.Queue;
11  import java.util.Set;
12  import java.util.SortedSet;
13  import java.util.TreeSet;
14  
15  import javax.media.j3d.Bounds;
16  import javax.naming.NamingException;
17  
18  import org.djunits.unit.DurationUnit;
19  import org.djunits.value.vdouble.scalar.Duration;
20  import org.djunits.value.vdouble.scalar.Length;
21  import org.djunits.value.vdouble.scalar.Speed;
22  import org.djunits.value.vdouble.scalar.Time;
23  import org.djutils.exceptions.Throw;
24  import org.opentrafficsim.base.Identifiable;
25  import org.opentrafficsim.base.TimeStampedObject;
26  import org.opentrafficsim.base.parameters.ParameterException;
27  import org.opentrafficsim.core.distributions.Generator;
28  import org.opentrafficsim.core.distributions.ProbabilityException;
29  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
30  import org.opentrafficsim.core.geometry.OTSGeometryException;
31  import org.opentrafficsim.core.gtu.GTUDirectionality;
32  import org.opentrafficsim.core.gtu.GTUException;
33  import org.opentrafficsim.core.gtu.GTUType;
34  import org.opentrafficsim.core.gtu.RelativePosition;
35  import org.opentrafficsim.core.idgenerator.IdGenerator;
36  import org.opentrafficsim.core.network.NetworkException;
37  import org.opentrafficsim.core.network.OTSNetwork;
38  import org.opentrafficsim.road.gtu.generator.GeneratorPositions.GeneratorLanePosition;
39  import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristics;
40  import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristicsGenerator;
41  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
42  import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
43  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
44  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUReal;
45  import org.opentrafficsim.road.network.lane.CrossSectionLink;
46  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
47  import org.opentrafficsim.road.network.lane.Lane;
48  import org.opentrafficsim.road.network.lane.LaneDirection;
49  
50  import nl.tudelft.simulation.dsol.SimRuntimeException;
51  import nl.tudelft.simulation.event.EventProducer;
52  import nl.tudelft.simulation.event.EventType;
53  import nl.tudelft.simulation.language.d3.DirectedPoint;
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  public class LaneBasedGTUGenerator extends EventProducer implements Serializable, Identifiable, GTUGenerator
69  {
70      
71      private static final long serialVersionUID = 20160000L;
72  
73      
74  
75  
76      public static final EventType GTU_GENERATED_EVENT = new EventType("GENERATOR.GTU_GENERATED");
77  
78      
79      private final Map<CrossSectionLink,
80              Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGTUCharacteristics>>>> unplacedTemplates =
81                      new LinkedHashMap<>();
82  
83      
84      private final String id;
85  
86      
87      private final Generator<Duration> interarrivelTimeGenerator;
88  
89      
90      private final LaneBasedGTUCharacteristicsGenerator laneBasedGTUCharacteristicsGenerator;
91  
92      
93      private long generatedGTUs = 0;
94  
95      
96      private Duration reTryInterval = new Duration(0.1, DurationUnit.SI);
97  
98      
99      private final GeneratorPositions generatorPositions;
100 
101     
102     private final OTSNetwork network;
103 
104     
105     private final OTSSimulatorInterface simulator;
106 
107     
108     private final RoomChecker roomChecker;
109 
110     
111     private final IdGenerator idGenerator;
112 
113     
114     private Length noLaneChangeDistance = null;
115 
116     
117     private Set<LaneDirection> disabled = new LinkedHashSet<>();
118 
119     
120 
121 
122 
123 
124 
125 
126 
127 
128 
129 
130 
131 
132 
133 
134     @SuppressWarnings("parameternumber")
135     public LaneBasedGTUGenerator(final String id, final Generator<Duration> interarrivelTimeGenerator,
136             final LaneBasedGTUCharacteristicsGenerator laneBasedGTUCharacteristicsGenerator,
137             final GeneratorPositions generatorPositions, final OTSNetwork network, final OTSSimulatorInterface simulator,
138             final RoomChecker roomChecker, final IdGenerator idGenerator)
139             throws SimRuntimeException, ProbabilityException, ParameterException
140     {
141         this.id = id;
142         this.interarrivelTimeGenerator = interarrivelTimeGenerator;
143         this.laneBasedGTUCharacteristicsGenerator = laneBasedGTUCharacteristicsGenerator;
144         this.generatorPositions = generatorPositions;
145         this.network = network;
146         this.simulator = simulator;
147         this.roomChecker = roomChecker;
148         this.idGenerator = idGenerator;
149         Duration headway = this.interarrivelTimeGenerator.draw();
150         if (headway != null) 
151         {
152             simulator.scheduleEventRel(headway, this, this, "generateCharacteristics", new Object[] {});
153         }
154     }
155 
156     
157 
158 
159 
160     public void setNoLaneChangeDistance(final Length noLaneChangeDistance)
161     {
162         this.noLaneChangeDistance = noLaneChangeDistance;
163     }
164 
165     
166 
167 
168 
169 
170 
171 
172 
173     @SuppressWarnings("unused")
174     private void generateCharacteristics() throws ProbabilityException, SimRuntimeException, ParameterException, GTUException
175     {
176         synchronized (this.unplacedTemplates)
177         {
178             
179             LaneBasedGTUCharacteristics characteristics = this.laneBasedGTUCharacteristicsGenerator.draw();
180             GTUType gtuType = characteristics.getGTUType();
181             
182             Map<CrossSectionLink, Map<Integer, Integer>> unplaced = new LinkedHashMap<>();
183             for (CrossSectionLink link : this.unplacedTemplates.keySet())
184             {
185                 Map<Integer, Integer> linkMap = new LinkedHashMap<>();
186                 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGTUCharacteristics>>> linkTemplates =
187                         this.unplacedTemplates.get(link);
188                 for (GeneratorLanePosition lanePosition : linkTemplates.keySet())
189                 {
190                     linkMap.put(lanePosition.getLaneNumber(), linkTemplates.get(lanePosition).size());
191                 }
192                 unplaced.put(link, linkMap);
193             }
194             
195             Speed desiredSpeed = characteristics.getStrategicalPlannerFactory().peekDesiredSpeed(gtuType,
196                     this.generatorPositions.speedLimit(gtuType), characteristics.getMaximumSpeed());
197             GeneratorLanePosition lanePosition =
198                     this.generatorPositions.draw(gtuType, unplaced, desiredSpeed, characteristics.getRoute());
199 
200             
201             Set<LaneDirection> lanes = new LinkedHashSet<>();
202             for (DirectedLanePosition pos : lanePosition.getPosition())
203             {
204                 lanes.add(pos.getLaneDirection());
205             }
206             if (Collections.disjoint(this.disabled, lanes))
207             {
208                 queueGtu(lanePosition, characteristics);
209             }
210 
211         }
212         Duration headway = this.interarrivelTimeGenerator.draw();
213         if (headway != null)
214         {
215             this.simulator.scheduleEventRel(headway, this, this, "generateCharacteristics", new Object[] {});
216         }
217     }
218 
219     
220 
221 
222 
223 
224 
225 
226 
227 
228 
229     @SuppressWarnings("unused")
230     private void tryToPlaceGTU(final GeneratorLanePosition position) throws SimRuntimeException, GTUException, NamingException,
231             NetworkException, OTSGeometryException, ProbabilityException
232     {
233         TimeStampedObject<LaneBasedGTUCharacteristics> timedCharacteristics;
234         Queue<TimeStampedObject<LaneBasedGTUCharacteristics>> queue =
235                 this.unplacedTemplates.get(position.getLink()).get(position);
236 
237         synchronized (queue)
238         {
239             timedCharacteristics = queue.peek();
240         }
241         if (null == timedCharacteristics)
242         {
243             return; 
244         }
245 
246         LaneBasedGTUCharacteristics characteristics = timedCharacteristics.getObject();
247         SortedSet<HeadwayGTU> leaders = new TreeSet<>();
248         for (DirectedLanePosition dirPos : position.getPosition())
249         {
250             getFirstLeaders(dirPos.getLaneDirection(), dirPos.getPosition().neg().minus(characteristics.getFront()),
251                     dirPos.getPosition(), leaders);
252         }
253         Duration since = this.simulator.getSimulatorTime().minus(timedCharacteristics.getTimestamp());
254         Placement placement = this.roomChecker.canPlace(leaders, characteristics, since, position.getPosition());
255         if (placement.canPlace())
256         {
257             
258             synchronized (queue)
259             {
260                 queue.remove();
261             }
262             placeGtu(characteristics, placement.getPosition(), placement.getSpeed());
263             if (queue.size() > 0)
264             {
265                 this.simulator.scheduleEventNow(this, this, "tryToPlaceGTU", new Object[] { position });
266             }
267         }
268         else if (queue.size() > 0)
269         {
270             this.simulator.scheduleEventRel(this.reTryInterval, this, this, "tryToPlaceGTU", new Object[] { position });
271         }
272     }
273 
274     
275 
276 
277 
278 
279 
280     public final void queueGtu(final LaneBasedGTUCharacteristics characteristics, final Set<LaneDirection> position)
281     {
282         
283         GeneratorLanePosition genPosition = null;
284         Set<LaneDirection> genSet = new LinkedHashSet<>();
285         for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
286         {
287             for (DirectedLanePosition dirPos : lanePosition.getPosition())
288             {
289                 genSet.add(dirPos.getLaneDirection());
290             }
291             if (genSet.equals(position))
292             {
293                 genPosition = lanePosition;
294                 break;
295             }
296             genSet.clear();
297         }
298         Throw.when(genPosition == null, IllegalStateException.class, "Position %s is not part of the generation.", position);
299         try
300         {
301             queueGtu(genPosition, characteristics);
302         }
303         catch (SimRuntimeException exception)
304         {
305             throw new RuntimeException("Unexpected exception while scheduling tryToPlace event.", exception);
306         }
307     }
308 
309     
310 
311 
312 
313 
314 
315 
316     private void queueGtu(final GeneratorLanePosition lanePosition, final LaneBasedGTUCharacteristics characteristics)
317             throws SimRuntimeException
318     {
319         if (!this.unplacedTemplates.containsKey(lanePosition.getLink()))
320         {
321             this.unplacedTemplates.put(lanePosition.getLink(), new LinkedHashMap<>());
322         }
323         Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGTUCharacteristics>>> linkMap =
324                 this.unplacedTemplates.get(lanePosition.getLink());
325         if (!linkMap.containsKey(lanePosition))
326         {
327             linkMap.put(lanePosition, new LinkedList<>());
328         }
329         Queue<TimeStampedObject<LaneBasedGTUCharacteristics>> queue = linkMap.get(lanePosition);
330         queue.add(new TimeStampedObject<>(characteristics, this.simulator.getSimulatorTime()));
331         if (queue.size() == 1)
332         {
333             this.simulator.scheduleEventNow(this, this, "tryToPlaceGTU", new Object[] { lanePosition });
334         }
335     }
336 
337     
338 
339 
340 
341 
342 
343 
344 
345 
346 
347 
348     public final void placeGtu(final LaneBasedGTUCharacteristics characteristics, final Set<DirectedLanePosition> position,
349             final Speed speed) throws NamingException, GTUException, NetworkException, SimRuntimeException, OTSGeometryException
350     {
351         String gtuId = this.idGenerator.nextId();
352         LaneBasedIndividualGTU gtu = new LaneBasedIndividualGTU(gtuId, characteristics.getGTUType(),
353                 characteristics.getLength(), characteristics.getWidth(), characteristics.getMaximumSpeed(),
354                 characteristics.getFront(), this.simulator, this.network);
355         gtu.setMaximumAcceleration(characteristics.getMaximumAcceleration());
356         gtu.setMaximumDeceleration(characteristics.getMaximumDeceleration());
357         gtu.setVehicleModel(characteristics.getVehicleModel());
358         gtu.setNoLaneChangeDistance(this.noLaneChangeDistance);
359         gtu.init(characteristics.getStrategicalPlannerFactory().create(gtu, characteristics.getRoute(),
360                 characteristics.getOrigin(), characteristics.getDestination()), position, speed);
361         this.generatedGTUs++;
362         fireEvent(GTU_GENERATED_EVENT, gtu);
363     }
364 
365     
366 
367 
368 
369 
370 
371 
372 
373     private void getFirstLeaders(final LaneDirection lane, final Length startDistance, final Length beyond,
374             final Set<HeadwayGTU> set) throws GTUException
375     {
376         LaneBasedGTU next = lane.getLane().getGtuAhead(beyond, lane.getDirection(), RelativePosition.FRONT,
377                 this.simulator.getSimulatorTime());
378         if (next != null)
379         {
380             Length headway;
381             if (lane.getDirection().isPlus())
382             {
383                 headway = startDistance.plus(next.position(lane.getLane(), next.getRear()));
384             }
385             else
386             {
387                 headway = startDistance.plus(lane.getLane().getLength().minus(next.position(lane.getLane(), next.getRear())));
388             }
389             if (headway.si < 300)
390             {
391                 set.add(new HeadwayGTUReal(next, headway, true));
392             }
393             return;
394         }
395         Map<Lane, GTUDirectionality> downstreamLanes = lane.getLane().downstreamLanes(lane.getDirection(), GTUType.VEHICLE);
396         for (Lane downstreamLane : downstreamLanes.keySet())
397         {
398             Length startDistanceDownstream = startDistance.plus(lane.getLane().getLength());
399             if (startDistanceDownstream.si > 300)
400             {
401                 return;
402             }
403             GTUDirectionality dir = downstreamLanes.get(downstreamLane);
404             Length beyondDownstream = dir.isPlus() ? Length.ZERO : downstreamLane.getLength();
405             getFirstLeaders(new LaneDirection(downstreamLane, dir), startDistanceDownstream, beyondDownstream, set);
406         }
407     }
408 
409     
410     @Override
411     public final String toString()
412     {
413         return "LaneBasedGTUGenerator " + this.id + " on " + this.generatorPositions;
414     }
415 
416     
417 
418 
419     public final long getGeneratedGTUs()
420     {
421         return this.generatedGTUs;
422     }
423 
424     
425 
426 
427 
428     @Override
429     public final String getId()
430     {
431         return this.id;
432     }
433 
434     
435 
436 
437 
438 
439 
440 
441 
442     public void disable(final Time start, final Time end, final Set<LaneDirection> laneDirections) throws SimRuntimeException
443     {
444         Throw.when(end.lt(start), SimRuntimeException.class, "End time %s is before start time %s.", end, start);
445         this.simulator.scheduleEventAbs(start, this, this, "disable", new Object[] { laneDirections });
446         this.simulator.scheduleEventAbs(end, this, this, "enable", new Object[0]);
447     }
448 
449     
450 
451 
452 
453     @SuppressWarnings("unused")
454     private void disable(final Set<LaneDirection> laneDirections)
455     {
456         Throw.when(this.disabled != null && !this.disabled.isEmpty(), IllegalStateException.class,
457                 "Disabling a generator that is already disabled is not allowed.");
458         this.disabled = laneDirections;
459     }
460 
461     
462 
463 
464     @SuppressWarnings("unused")
465     private void enable()
466     {
467         this.disabled = new LinkedHashSet<>();
468     }
469 
470     
471 
472 
473 
474     public interface RoomChecker
475     {
476         
477 
478 
479 
480 
481 
482 
483 
484 
485 
486 
487 
488 
489         Placement canPlace(SortedSet<HeadwayGTU> leaders, LaneBasedGTUCharacteristics characteristics, Duration since,
490                 Set<DirectedLanePosition> initialPosition) throws NetworkException, GTUException;
491     }
492 
493     
494 
495 
496 
497 
498 
499 
500 
501 
502 
503 
504 
505     public static final class Placement
506     {
507 
508         
509         public static final Placement NO = new Placement();
510 
511         
512         private final Speed speed;
513 
514         
515         private final Set<DirectedLanePosition> position;
516 
517         
518 
519 
520         private Placement()
521         {
522             this.speed = null;
523             this.position = null;
524         }
525 
526         
527 
528 
529 
530 
531         public Placement(final Speed speed, final Set<DirectedLanePosition> position)
532         {
533             Throw.whenNull(speed, "Speed may not be null. Use Placement.NO if the GTU cannot be placed.");
534             Throw.whenNull(position, "Position may not be null. Use Placement.NO if the GTU cannot be placed.");
535             this.speed = speed;
536             this.position = position;
537         }
538 
539         
540 
541 
542 
543         public boolean canPlace()
544         {
545             return this.speed != null && this.position != null;
546         }
547 
548         
549 
550 
551 
552         public Speed getSpeed()
553         {
554             return this.speed;
555         }
556 
557         
558 
559 
560 
561         public Set<DirectedLanePosition> getPosition()
562         {
563             return this.position;
564         }
565 
566         
567         @Override
568         public String toString()
569         {
570             return "Placement [speed=" + this.speed + ", position=" + this.position + "]";
571         }
572 
573     }
574 
575     
576     @Override
577     public DirectedPoint getLocation() throws RemoteException
578     {
579         return this.generatorPositions.getLocation();
580     }
581 
582     
583     @Override
584     public Bounds getBounds() throws RemoteException
585     {
586         return this.generatorPositions.getBounds();
587     }
588 
589     
590     @Override
591     public Map<DirectedPoint, Integer> getQueueLengths()
592     {
593         Map<DirectedPoint, Integer> result = new LinkedHashMap<>();
594         for (CrossSectionLink link : this.unplacedTemplates.keySet())
595         {
596             for (GeneratorLanePosition lanePosition : this.unplacedTemplates.get(link).keySet())
597             {
598                 result.put(lanePosition.getPosition().iterator().next().getLocation(),
599                         this.unplacedTemplates.get(link).get(lanePosition).size());
600             }
601         }
602         for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
603         {
604             DirectedPoint p = lanePosition.getPosition().iterator().next().getLocation();
605             if (!result.containsKey(p))
606             {
607                 result.put(p, 0);
608             }
609         }
610         return result;
611     }
612 
613 }