1 package org.opentrafficsim.road.gtu.generator;
2
3 import java.util.LinkedHashMap;
4 import java.util.LinkedHashSet;
5 import java.util.LinkedList;
6 import java.util.Map;
7 import java.util.Queue;
8 import java.util.Set;
9 import java.util.SortedSet;
10 import java.util.TreeSet;
11 import java.util.UUID;
12 import java.util.function.Supplier;
13
14 import javax.naming.NamingException;
15
16 import org.djunits.unit.DurationUnit;
17 import org.djunits.value.vdouble.scalar.Duration;
18 import org.djunits.value.vdouble.scalar.Length;
19 import org.djunits.value.vdouble.scalar.Speed;
20 import org.djunits.value.vdouble.scalar.Time;
21 import org.djutils.draw.point.OrientedPoint2d;
22 import org.djutils.draw.point.Point2d;
23 import org.djutils.event.EventType;
24 import org.djutils.event.LocalEventProducer;
25 import org.djutils.exceptions.Throw;
26 import org.djutils.metadata.MetaData;
27 import org.djutils.metadata.ObjectDescriptor;
28 import org.opentrafficsim.base.TimeStampedObject;
29 import org.opentrafficsim.base.geometry.BoundingBox;
30 import org.opentrafficsim.base.geometry.OtsBounds2d;
31 import org.opentrafficsim.base.parameters.ParameterException;
32 import org.opentrafficsim.core.distributions.Generator;
33 import org.opentrafficsim.core.distributions.ProbabilityException;
34 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
35 import org.opentrafficsim.core.geometry.OtsGeometryException;
36 import org.opentrafficsim.core.gtu.GtuErrorHandler;
37 import org.opentrafficsim.core.gtu.GtuException;
38 import org.opentrafficsim.core.gtu.GtuGenerator;
39 import org.opentrafficsim.core.gtu.GtuType;
40 import org.opentrafficsim.core.gtu.RelativePosition;
41 import org.opentrafficsim.core.network.NetworkException;
42 import org.opentrafficsim.road.gtu.generator.GeneratorPositions.GeneratorLanePosition;
43 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristics;
44 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristicsGenerator;
45 import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
46 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGtu;
47 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGtuReal;
48 import org.opentrafficsim.road.network.RoadNetwork;
49 import org.opentrafficsim.road.network.lane.CrossSectionLink;
50 import org.opentrafficsim.road.network.lane.Lane;
51 import org.opentrafficsim.road.network.lane.LanePosition;
52
53 import nl.tudelft.simulation.dsol.SimRuntimeException;
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 public class LaneBasedGtuGenerator extends LocalEventProducer implements 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", new MetaData("GTU generated",
77 "GTU was generated", new ObjectDescriptor("GTU", "The GTU itself", LaneBasedGtu.class)));
78
79
80 private final Map<CrossSectionLink,
81 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGtuCharacteristics>>>> unplacedTemplates =
82 new LinkedHashMap<>();
83
84
85 private final String id;
86
87
88 private final String uniqueId;
89
90
91 private final Generator<Duration> interarrivelTimeGenerator;
92
93
94 private final LaneBasedGtuCharacteristicsGenerator laneBasedGtuCharacteristicsGenerator;
95
96
97 private long generatedGTUs = 0;
98
99
100 private Duration reTryInterval = new Duration(0.1, DurationUnit.SI);
101
102
103 private final GeneratorPositions generatorPositions;
104
105
106 private final RoadNetwork network;
107
108
109 private final OtsSimulatorInterface simulator;
110
111
112 private final RoomChecker roomChecker;
113
114
115 private final Supplier<String> idGenerator;
116
117
118 private Length noLaneChangeDistance = null;
119
120
121 private boolean instantaneousLaneChange = false;
122
123
124 private GtuErrorHandler errorHandler = GtuErrorHandler.THROW;
125
126
127 private Set<Lane> disabled = new LinkedHashSet<>();
128
129
130 private boolean idsInCharacteristicsOrder = false;
131
132
133 private Map<LaneBasedGtuCharacteristics, String> unplacedIds = null;
134
135
136 private boolean firstCharacteristicsDrawn = false;
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154 @SuppressWarnings("parameternumber")
155 public LaneBasedGtuGenerator(final String id, final Generator<Duration> interarrivelTimeGenerator,
156 final LaneBasedGtuCharacteristicsGenerator laneBasedGtuCharacteristicsGenerator,
157 final GeneratorPositions generatorPositions, final RoadNetwork network, final OtsSimulatorInterface simulator,
158 final RoomChecker roomChecker, final Supplier<String> idGenerator)
159 throws SimRuntimeException, ProbabilityException, ParameterException, NetworkException
160 {
161 this.id = id;
162 this.uniqueId = UUID.randomUUID().toString() + "_" + id;
163 this.interarrivelTimeGenerator = interarrivelTimeGenerator;
164 this.laneBasedGtuCharacteristicsGenerator = laneBasedGtuCharacteristicsGenerator;
165 this.generatorPositions = generatorPositions;
166 this.network = network;
167 this.simulator = simulator;
168 this.roomChecker = roomChecker;
169 this.idGenerator = idGenerator;
170 Duration headway = this.interarrivelTimeGenerator.draw();
171 if (headway != null)
172 {
173 simulator.scheduleEventRel(headway, this, "generateCharacteristics", new Object[] {});
174 }
175 this.network.addNonLocatedObject(this);
176 if (this.idGenerator instanceof Injections injections && injections.hasColumn(Injections.ID_COLUMN))
177 {
178 setIdsInCharacteristicsOrder(true);
179 }
180 }
181
182
183
184
185
186 public void setNoLaneChangeDistance(final Length noLaneChangeDistance)
187 {
188 this.noLaneChangeDistance = noLaneChangeDistance;
189 }
190
191
192
193
194
195 public void setInstantaneousLaneChange(final boolean instantaneous)
196 {
197 this.instantaneousLaneChange = instantaneous;
198 }
199
200
201
202
203
204 public void setErrorHandler(final GtuErrorHandler gtuErrorHandler)
205 {
206 this.errorHandler = gtuErrorHandler;
207 }
208
209
210
211
212
213
214
215 public void setIdsInCharacteristicsOrder(final boolean idsInCharacteristicsOrder)
216 {
217 Throw.when(this.firstCharacteristicsDrawn, IllegalStateException.class,
218 "Id order cannot be set once GTU characteristics were drawn.");
219 this.unplacedIds = new LinkedHashMap<>();
220 this.idsInCharacteristicsOrder = idsInCharacteristicsOrder;
221 }
222
223
224
225
226
227
228
229
230
231 @SuppressWarnings("unused")
232 private void generateCharacteristics() throws ProbabilityException, SimRuntimeException, ParameterException, GtuException
233 {
234 this.firstCharacteristicsDrawn = true;
235 synchronized (this.unplacedTemplates)
236 {
237 LaneBasedGtuCharacteristics characteristics = this.laneBasedGtuCharacteristicsGenerator.draw();
238 GtuType gtuType = characteristics.getGtuType();
239
240 Map<CrossSectionLink, Map<Integer, Integer>> unplaced = new LinkedHashMap<>();
241 for (CrossSectionLink link : this.unplacedTemplates.keySet())
242 {
243 Map<Integer, Integer> linkMap = new LinkedHashMap<>();
244 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGtuCharacteristics>>> linkTemplates =
245 this.unplacedTemplates.get(link);
246 for (GeneratorLanePosition lanePosition : linkTemplates.keySet())
247 {
248 linkMap.put(lanePosition.getLaneNumber(), linkTemplates.get(lanePosition).size());
249 }
250 unplaced.put(link, linkMap);
251 }
252
253 GeneratorLanePosition lanePosition = this.generatorPositions.draw(gtuType, characteristics, unplaced);
254
255
256 if (!this.disabled.contains(lanePosition.getPosition().lane()))
257 {
258 if (this.idsInCharacteristicsOrder)
259 {
260 this.unplacedIds.put(characteristics, this.idGenerator.get());
261 }
262 queueGtu(lanePosition, characteristics);
263 }
264
265 }
266
267 Duration headway = this.interarrivelTimeGenerator.draw();
268 if (headway != null)
269 {
270 this.simulator.scheduleEventRel(headway, this, "generateCharacteristics", new Object[] {});
271 }
272
273 }
274
275
276
277
278
279
280
281
282
283
284
285 @SuppressWarnings("unused")
286 private void tryToPlaceGTU(final GeneratorLanePosition position) throws SimRuntimeException, GtuException, NamingException,
287 NetworkException, OtsGeometryException, ProbabilityException
288 {
289 TimeStampedObject<LaneBasedGtuCharacteristics> timedCharacteristics;
290 Queue<TimeStampedObject<LaneBasedGtuCharacteristics>> queue =
291 this.unplacedTemplates.get(position.getLink()).get(position);
292
293 synchronized (queue)
294 {
295 timedCharacteristics = queue.peek();
296 }
297 if (null == timedCharacteristics)
298 {
299 return;
300 }
301
302 LaneBasedGtuCharacteristics characteristics = timedCharacteristics.object();
303 SortedSet<HeadwayGtu> leaders = new TreeSet<>();
304 getFirstLeaders(position.getPosition().lane(),
305 position.getPosition().position().neg().minus(characteristics.getFront()), position.getPosition().position(),
306 leaders);
307 Duration since = this.simulator.getSimulatorAbsTime().minus(timedCharacteristics.timestamp());
308 Placement placement = this.roomChecker.canPlace(leaders, characteristics, since, position.getPosition());
309 if (placement.canPlace())
310 {
311
312 synchronized (queue)
313 {
314 queue.remove();
315 }
316 placeGtu(characteristics, placement.getPosition(), placement.getSpeed());
317 if (queue.size() > 0)
318 {
319 this.simulator.scheduleEventNow(this, "tryToPlaceGTU", new Object[] {position});
320 }
321 }
322
323 else if (queue.size() > 0)
324 {
325 this.simulator.scheduleEventRel(this.reTryInterval, this, "tryToPlaceGTU", new Object[] {position});
326 }
327
328 }
329
330
331
332
333
334
335
336 public final void queueGtu(final LaneBasedGtuCharacteristics characteristics, final Lane lane)
337 {
338
339 GeneratorLanePosition genPosition = null;
340 for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
341 {
342 if (lanePosition.getPosition().lane().equals(lane))
343 {
344 genPosition = lanePosition;
345 break;
346 }
347 }
348 Throw.when(genPosition == null, IllegalStateException.class, "Lane %s is not part of the generation.", lane);
349 try
350 {
351 queueGtu(genPosition, characteristics);
352 }
353 catch (SimRuntimeException exception)
354 {
355 throw new RuntimeException("Unexpected exception while scheduling tryToPlace event.", exception);
356 }
357 }
358
359
360
361
362
363
364
365
366 private void queueGtu(final GeneratorLanePosition lanePosition, final LaneBasedGtuCharacteristics characteristics)
367 throws SimRuntimeException
368 {
369 if (!this.unplacedTemplates.containsKey(lanePosition.getLink()))
370 {
371 this.unplacedTemplates.put(lanePosition.getLink(), new LinkedHashMap<>());
372 }
373 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGtuCharacteristics>>> linkMap =
374 this.unplacedTemplates.get(lanePosition.getLink());
375 if (!linkMap.containsKey(lanePosition))
376 {
377 linkMap.put(lanePosition, new LinkedList<>());
378 }
379 Queue<TimeStampedObject<LaneBasedGtuCharacteristics>> queue = linkMap.get(lanePosition);
380 queue.add(new TimeStampedObject<>(characteristics, this.simulator.getSimulatorAbsTime()));
381
382 if (queue.size() == 1)
383 {
384 this.simulator.scheduleEventNow(this, "tryToPlaceGTU", new Object[] {lanePosition});
385 }
386
387 }
388
389
390
391
392
393
394
395
396
397
398
399
400 public final void placeGtu(final LaneBasedGtuCharacteristics characteristics, final LanePosition position,
401 final Speed speed) throws NamingException, GtuException, NetworkException, SimRuntimeException, OtsGeometryException
402 {
403 String gtuId = this.idsInCharacteristicsOrder ? this.unplacedIds.remove(characteristics) : this.idGenerator.get();
404 LaneBasedGtu gtu = new LaneBasedGtu(gtuId, characteristics.getGtuType(), characteristics.getLength(),
405 characteristics.getWidth(), characteristics.getMaximumSpeed(), characteristics.getFront(), this.network);
406 gtu.setMaximumAcceleration(characteristics.getMaximumAcceleration());
407 gtu.setMaximumDeceleration(characteristics.getMaximumDeceleration());
408 gtu.setVehicleModel(characteristics.getVehicleModel());
409 gtu.setNoLaneChangeDistance(this.noLaneChangeDistance);
410 gtu.setInstantaneousLaneChange(this.instantaneousLaneChange);
411 gtu.setErrorHandler(this.errorHandler);
412 gtu.init(characteristics.getStrategicalPlannerFactory().create(gtu, characteristics.getRoute(),
413 characteristics.getOrigin(), characteristics.getDestination()), position, speed);
414 this.generatedGTUs++;
415 fireEvent(GTU_GENERATED_EVENT, gtu);
416 }
417
418
419
420
421
422
423
424
425
426 private void getFirstLeaders(final Lane lane, final Length startDistance, final Length beyond, final Set<HeadwayGtu> set)
427 throws GtuException
428 {
429 LaneBasedGtu next = lane.getGtuAhead(beyond, RelativePosition.FRONT, this.simulator.getSimulatorAbsTime());
430 if (next != null)
431 {
432 Length headway = startDistance.plus(next.position(lane, next.getRear()));
433 if (headway.si < 300)
434 {
435 set.add(new HeadwayGtuReal(next, headway, true));
436 }
437 return;
438 }
439 Set<Lane> downstreamLanes = lane.nextLanes(null);
440 for (Lane downstreamLane : downstreamLanes)
441 {
442 Length startDistanceDownstream = startDistance.plus(lane.getLength());
443 if (startDistanceDownstream.si > 300)
444 {
445 return;
446 }
447 Length beyondDownstream = Length.ZERO;
448 getFirstLeaders(downstreamLane, startDistanceDownstream, beyondDownstream, set);
449 }
450 }
451
452
453 @Override
454 public final String toString()
455 {
456 return "LaneBasedGtuGenerator " + this.id + " on " + this.generatorPositions.getAllPositions();
457 }
458
459
460
461
462 public final long getGeneratedGTUs()
463 {
464 return this.generatedGTUs;
465 }
466
467
468
469
470
471 @Override
472 public final String getId()
473 {
474 return this.id;
475 }
476
477
478
479
480
481
482
483
484
485 public void disable(final Time start, final Time end, final Lane lane) throws SimRuntimeException
486 {
487 Throw.when(end.lt(start), SimRuntimeException.class, "End time %s is before start time %s.", end, start);
488 this.simulator.scheduleEventAbsTime(start, this, "disable", new Object[] {lane});
489 this.simulator.scheduleEventAbsTime(end, this, "enable", new Object[0]);
490 }
491
492
493
494
495
496 @SuppressWarnings("unused")
497 private void disable(final Lane lane)
498 {
499 Throw.when(this.disabled != null && !this.disabled.isEmpty(), IllegalStateException.class,
500 "Disabling a generator that is already disabled is not allowed.");
501 this.disabled.add(lane);
502 }
503
504
505
506
507 @SuppressWarnings("unused")
508 private void enable()
509 {
510 this.disabled = new LinkedHashSet<>();
511 }
512
513
514 @Override
515 public String getFullId()
516 {
517 return this.uniqueId;
518 }
519
520
521 @Override
522 public Set<GtuGeneratorPosition> getPositions()
523 {
524 Set<GtuGeneratorPosition> set = new LinkedHashSet<>();
525 for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
526 {
527 LanePosition pos = lanePosition.getPosition();
528 OrientedPoint2d p = pos.getLocation();
529 set.add(new GtuGeneratorPosition()
530 {
531
532 @Override
533 public Point2d getLocation()
534 {
535 return p;
536 }
537
538
539 @Override
540 public OtsBounds2d getBounds()
541 {
542 return new BoundingBox(0.0, 0.0);
543 }
544
545
546 @Override
547 public int getQueueCount()
548 {
549 return getQueueLength(lanePosition);
550 }
551
552
553 @Override
554 public String getId()
555 {
556 return LaneBasedGtuGenerator.this.id + "@" + lanePosition.getLink().getId() + "." + pos.lane().getId();
557 }
558 });
559 }
560 return set;
561 }
562
563
564
565
566
567
568 private int getQueueLength(final GeneratorLanePosition position)
569 {
570 for (CrossSectionLink link : this.unplacedTemplates.keySet())
571 {
572 for (GeneratorLanePosition lanePosition : this.unplacedTemplates.get(link).keySet())
573 {
574 if (lanePosition.equals(position))
575 {
576 return this.unplacedTemplates.get(link).get(lanePosition).size();
577 }
578 }
579 }
580 return 0;
581 }
582
583
584
585
586
587 public interface RoomChecker
588 {
589
590
591
592
593
594
595
596
597
598
599
600
601
602 Placement canPlace(SortedSet<HeadwayGtu> leaders, LaneBasedGtuCharacteristics characteristics, Duration since,
603 LanePosition initialPosition) throws NetworkException, GtuException;
604 }
605
606
607
608
609
610
611
612
613
614
615
616
617 public static final class Placement
618 {
619
620
621 public static final Placement NO = new Placement();
622
623
624 private final Speed speed;
625
626
627 private final LanePosition position;
628
629
630
631
632 private Placement()
633 {
634 this.speed = null;
635 this.position = null;
636 }
637
638
639
640
641
642
643 public Placement(final Speed speed, final LanePosition position)
644 {
645 Throw.whenNull(speed, "Speed may not be null. Use Placement.NO if the GTU cannot be placed.");
646 Throw.whenNull(position, "Position may not be null. Use Placement.NO if the GTU cannot be placed.");
647 this.speed = speed;
648 this.position = position;
649 }
650
651
652
653
654
655 public boolean canPlace()
656 {
657 return this.speed != null && this.position != null;
658 }
659
660
661
662
663
664 public Speed getSpeed()
665 {
666 return this.speed;
667 }
668
669
670
671
672
673 public LanePosition getPosition()
674 {
675 return this.position;
676 }
677
678
679 @Override
680 public String toString()
681 {
682 return "Placement [speed=" + this.speed + ", position=" + this.position + "]";
683 }
684
685 }
686
687 }