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.Optional;
8 import java.util.Queue;
9 import java.util.Set;
10 import java.util.SortedSet;
11 import java.util.TreeSet;
12 import java.util.UUID;
13 import java.util.function.Supplier;
14
15 import javax.naming.NamingException;
16
17 import org.djunits.unit.DurationUnit;
18 import org.djunits.value.vdouble.scalar.Duration;
19 import org.djunits.value.vdouble.scalar.Length;
20 import org.djunits.value.vdouble.scalar.Speed;
21 import org.djutils.draw.bounds.Bounds2d;
22 import org.djutils.draw.point.DirectedPoint2d;
23 import org.djutils.event.EventType;
24 import org.djutils.event.LocalEventProducer;
25 import org.djutils.exceptions.Throw;
26 import org.djutils.exceptions.Try;
27 import org.djutils.metadata.MetaData;
28 import org.djutils.metadata.ObjectDescriptor;
29 import org.opentrafficsim.base.OtsRuntimeException;
30 import org.opentrafficsim.base.TimeStampedObject;
31 import org.opentrafficsim.base.parameters.ParameterException;
32 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
33 import org.opentrafficsim.core.gtu.GtuErrorHandler;
34 import org.opentrafficsim.core.gtu.GtuException;
35 import org.opentrafficsim.core.gtu.GtuGenerator;
36 import org.opentrafficsim.core.gtu.GtuType;
37 import org.opentrafficsim.core.gtu.RelativePosition;
38 import org.opentrafficsim.core.network.NetworkException;
39 import org.opentrafficsim.road.gtu.generator.GeneratorPositions.GeneratorLanePosition;
40 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristics;
41 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristicsGenerator;
42 import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
43 import org.opentrafficsim.road.gtu.lane.LaneBookkeeping;
44 import org.opentrafficsim.road.gtu.lane.perception.object.PerceivedGtu;
45 import org.opentrafficsim.road.gtu.lane.perception.object.PerceivedObject.Kinematics;
46 import org.opentrafficsim.road.gtu.lane.perception.object.PerceivedObject.Kinematics.Overlap;
47 import org.opentrafficsim.road.network.RoadNetwork;
48 import org.opentrafficsim.road.network.lane.CrossSectionLink;
49 import org.opentrafficsim.road.network.lane.Lane;
50 import org.opentrafficsim.road.network.lane.LanePosition;
51
52 import nl.tudelft.simulation.dsol.SimRuntimeException;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 public class LaneBasedGtuGenerator extends LocalEventProducer implements GtuGenerator
68 {
69
70
71
72 public static final EventType GTU_GENERATED_EVENT = new EventType("GENERATOR.GTU_GENERATED", new MetaData("GTU generated",
73 "GTU was generated", new ObjectDescriptor("GTU", "The GTU itself", LaneBasedGtu.class)));
74
75
76 private final Map<CrossSectionLink,
77 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGtuCharacteristics>>>> unplacedTemplates =
78 new LinkedHashMap<>();
79
80
81 private final String id;
82
83
84 private final String uniqueId;
85
86
87 private final Supplier<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 Set<GtuGeneratorPosition> positions;
103
104
105 private final RoadNetwork network;
106
107
108 private final OtsSimulatorInterface simulator;
109
110
111 private final RoomChecker roomChecker;
112
113
114 private final Supplier<String> idGenerator;
115
116
117 private Length noLaneChangeDistance = null;
118
119
120 private LaneBookkeeping bookkeeping = LaneBookkeeping.EDGE;
121
122
123 private GtuErrorHandler errorHandler = GtuErrorHandler.THROW;
124
125
126 private Set<Lane> disabled = new LinkedHashSet<>();
127
128
129 private boolean idsInCharacteristicsOrder = false;
130
131
132 private Map<LaneBasedGtuCharacteristics, String> unplacedIds = null;
133
134
135 private boolean firstCharacteristicsDrawn = false;
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152 @SuppressWarnings("parameternumber")
153 public LaneBasedGtuGenerator(final String id, final Supplier<Duration> interarrivelTimeGenerator,
154 final LaneBasedGtuCharacteristicsGenerator laneBasedGtuCharacteristicsGenerator,
155 final GeneratorPositions generatorPositions, final RoadNetwork network, final OtsSimulatorInterface simulator,
156 final RoomChecker roomChecker, final Supplier<String> idGenerator)
157 throws SimRuntimeException, ParameterException, NetworkException
158 {
159 this.id = id;
160 this.uniqueId = UUID.randomUUID().toString() + "_" + id;
161 this.interarrivelTimeGenerator = interarrivelTimeGenerator;
162 this.laneBasedGtuCharacteristicsGenerator = laneBasedGtuCharacteristicsGenerator;
163 this.generatorPositions = generatorPositions;
164 this.network = network;
165 this.simulator = simulator;
166 this.roomChecker = roomChecker;
167 this.idGenerator = idGenerator;
168 Duration headway = this.interarrivelTimeGenerator.get();
169 if (headway != null)
170 {
171
172 simulator.scheduleEventRel(headway,
173 () -> Try.execute(() -> generateCharacteristics(), "Exception generating characteristics."));
174 }
175 this.network.addNonLocatedObject(this);
176 if (this.idGenerator instanceof IdsWithCharacteristics ids && ids.hasIds())
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 setBookkeeping(final LaneBookkeeping bookkeeping)
196 {
197 this.bookkeeping = bookkeeping;
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 @SuppressWarnings("unused")
231 private void generateCharacteristics() throws SimRuntimeException, ParameterException, GtuException
232 {
233 this.firstCharacteristicsDrawn = true;
234 synchronized (this.unplacedTemplates)
235 {
236 LaneBasedGtuCharacteristics characteristics = this.laneBasedGtuCharacteristicsGenerator.draw();
237 GtuType gtuType = characteristics.getGtuType();
238
239 Map<CrossSectionLink, Map<Integer, Integer>> unplaced = new LinkedHashMap<>();
240 for (CrossSectionLink link : this.unplacedTemplates.keySet())
241 {
242 Map<Integer, Integer> linkMap = new LinkedHashMap<>();
243 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGtuCharacteristics>>> linkTemplates =
244 this.unplacedTemplates.get(link);
245 for (GeneratorLanePosition lanePosition : linkTemplates.keySet())
246 {
247 linkMap.put(lanePosition.getLaneNumber(), linkTemplates.get(lanePosition).size());
248 }
249 unplaced.put(link, linkMap);
250 }
251
252 GeneratorLanePosition lanePosition = this.generatorPositions.draw(gtuType, characteristics, unplaced);
253
254
255 if (!this.disabled.contains(lanePosition.getPosition().lane()))
256 {
257 if (this.idsInCharacteristicsOrder)
258 {
259 this.unplacedIds.put(characteristics, this.idGenerator.get());
260 }
261 queueGtu(lanePosition, characteristics);
262 }
263 }
264
265 Duration headway = this.interarrivelTimeGenerator.get();
266 if (headway != null)
267 {
268 this.simulator.scheduleEventRel(headway,
269 () -> Try.execute(() -> generateCharacteristics(), "Exception generating characteristics."));
270 }
271
272 }
273
274
275
276
277
278
279
280
281
282 @SuppressWarnings("unused")
283 private void tryToPlaceGTU(final GeneratorLanePosition position)
284 throws SimRuntimeException, GtuException, NamingException, NetworkException
285 {
286 TimeStampedObject<LaneBasedGtuCharacteristics> timedCharacteristics;
287 Queue<TimeStampedObject<LaneBasedGtuCharacteristics>> queue =
288 this.unplacedTemplates.get(position.getLink()).get(position);
289
290 synchronized (queue)
291 {
292 timedCharacteristics = queue.peek();
293 }
294 if (null == timedCharacteristics)
295 {
296 return;
297 }
298
299 LaneBasedGtuCharacteristics characteristics = timedCharacteristics.object();
300 SortedSet<PerceivedGtu> leaders = new TreeSet<>();
301 getFirstLeaders(position.getPosition().lane(),
302 position.getPosition().position().neg().minus(characteristics.getFront()), position.getPosition().position(),
303 leaders);
304 Duration since = this.simulator.getSimulatorTime().minus(timedCharacteristics.timestamp());
305 Placement placement = this.roomChecker.canPlace(leaders, characteristics, since, position.getPosition());
306 if (placement.canPlace())
307 {
308
309 synchronized (queue)
310 {
311 queue.remove();
312 }
313 placeGtu(characteristics, placement.getPosition(), placement.getSpeed());
314 if (queue.size() > 0)
315 {
316 this.simulator.scheduleEventNow(
317 () -> Try.execute(() -> tryToPlaceGTU(position), "Exception during attempt to place GTU."));
318 }
319 }
320
321 else if (queue.size() > 0)
322 {
323 this.simulator.scheduleEventRel(this.reTryInterval,
324 () -> Try.execute(() -> tryToPlaceGTU(position), "Exception during attempt to place GTU."));
325 }
326
327 }
328
329
330
331
332
333
334
335 public final void queueGtu(final LaneBasedGtuCharacteristics characteristics, final Lane lane)
336 {
337
338 GeneratorLanePosition genPosition = null;
339 for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
340 {
341 if (lanePosition.getPosition().lane().equals(lane))
342 {
343 genPosition = lanePosition;
344 break;
345 }
346 }
347 Throw.when(genPosition == null, IllegalStateException.class, "Lane %s is not part of the generation.", lane);
348 try
349 {
350 queueGtu(genPosition, characteristics);
351 }
352 catch (SimRuntimeException exception)
353 {
354 throw new OtsRuntimeException("Unexpected exception while scheduling tryToPlace event.", exception);
355 }
356 }
357
358
359
360
361
362
363
364
365 private void queueGtu(final GeneratorLanePosition lanePosition, final LaneBasedGtuCharacteristics characteristics)
366 throws SimRuntimeException
367 {
368 if (!this.unplacedTemplates.containsKey(lanePosition.getLink()))
369 {
370 this.unplacedTemplates.put(lanePosition.getLink(), new LinkedHashMap<>());
371 }
372 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGtuCharacteristics>>> linkMap =
373 this.unplacedTemplates.get(lanePosition.getLink());
374 if (!linkMap.containsKey(lanePosition))
375 {
376 linkMap.put(lanePosition, new LinkedList<>());
377 }
378 Queue<TimeStampedObject<LaneBasedGtuCharacteristics>> queue = linkMap.get(lanePosition);
379 queue.add(new TimeStampedObject<>(characteristics, this.simulator.getSimulatorTime()));
380
381 if (queue.size() == 1)
382 {
383 this.simulator.scheduleEventNow(
384 () -> Try.execute(() -> tryToPlaceGTU(lanePosition), "Exception during attempt to place GTU."));
385 }
386
387 }
388
389
390
391
392
393
394
395
396
397
398
399 public final void placeGtu(final LaneBasedGtuCharacteristics characteristics, final LanePosition position,
400 final Speed speed) throws NamingException, GtuException, NetworkException, SimRuntimeException
401 {
402 String gtuId = this.idsInCharacteristicsOrder ? this.unplacedIds.remove(characteristics) : this.idGenerator.get();
403 LaneBasedGtu gtu = new LaneBasedGtu(gtuId, characteristics.getGtuType(), characteristics.getLength(),
404 characteristics.getWidth(), characteristics.getMaximumSpeed(), characteristics.getFront(), this.network);
405 gtu.setMaximumAcceleration(characteristics.getMaximumAcceleration());
406 gtu.setMaximumDeceleration(characteristics.getMaximumDeceleration());
407 gtu.setVehicleModel(characteristics.getVehicleModel());
408 gtu.setNoLaneChangeDistance(this.noLaneChangeDistance);
409 gtu.setBookkeeping(this.bookkeeping);
410 gtu.setErrorHandler(this.errorHandler);
411 gtu.init(characteristics.getStrategicalPlannerFactory().create(gtu, characteristics.getRoute(),
412 characteristics.getOrigin(), characteristics.getDestination()), position.getLocation(), speed);
413 this.generatedGTUs++;
414 fireEvent(GTU_GENERATED_EVENT, gtu);
415 }
416
417
418
419
420
421
422
423
424
425 private void getFirstLeaders(final Lane lane, final Length startDistance, final Length beyond, final Set<PerceivedGtu> set)
426 throws GtuException
427 {
428 Optional<LaneBasedGtu> next = lane.getGtuAhead(beyond, RelativePosition.FRONT, this.simulator.getSimulatorTime());
429 if (next.isPresent())
430 {
431 Length headway = startDistance.plus(next.get().getPosition(lane, next.get().getRear()));
432 if (headway.si < 300)
433 {
434 set.add(PerceivedGtu.of(next.get(), new Kinematics.Record(headway, next.get().getSpeed(),
435 next.get().getAcceleration(), true, Overlap.AHEAD)));
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 @Override
453 public final String toString()
454 {
455 return "LaneBasedGtuGenerator " + this.id + " on " + this.generatorPositions.getAllPositions();
456 }
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 Duration start, final Duration 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.scheduleEventAbs(start, () -> disable(lane));
489 this.simulator.scheduleEventAbs(end, () -> enable());
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 @Override
514 public String getFullId()
515 {
516 return this.uniqueId;
517 }
518
519 @Override
520 public Set<GtuGeneratorPosition> getPositions()
521 {
522 if (this.positions == null)
523 {
524 this.positions = new LinkedHashSet<>();
525 for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
526 {
527 LanePosition pos = lanePosition.getPosition();
528 DirectedPoint2d p = pos.getLocation();
529 this.positions.add(new GtuGeneratorPosition()
530 {
531 @Override
532 public DirectedPoint2d getLocation()
533 {
534 return p;
535 }
536
537 @Override
538 public Bounds2d getRelativeBounds()
539 {
540 return new Bounds2d(0.0, 0.0);
541 }
542
543 @Override
544 public int getQueueSize()
545 {
546 return LaneBasedGtuGenerator.this.getQueueSize(lanePosition);
547 }
548
549 @Override
550 public String getId()
551 {
552 return LaneBasedGtuGenerator.this.id + "@" + lanePosition.getLink().getId() + "." + pos.lane().getId();
553 }
554 });
555 }
556 }
557 return this.positions;
558 }
559
560
561
562
563
564
565 private int getQueueSize(final GeneratorLanePosition position)
566 {
567 for (CrossSectionLink link : this.unplacedTemplates.keySet())
568 {
569 for (GeneratorLanePosition lanePosition : this.unplacedTemplates.get(link).keySet())
570 {
571 if (lanePosition.equals(position))
572 {
573 return this.unplacedTemplates.get(link).get(lanePosition).size();
574 }
575 }
576 }
577 return 0;
578 }
579
580
581
582
583
584 public interface RoomChecker
585 {
586
587
588
589
590
591
592
593
594
595
596
597
598
599 Placement canPlace(SortedSet<PerceivedGtu> leaders, LaneBasedGtuCharacteristics characteristics, Duration since,
600 LanePosition initialPosition) throws NetworkException, GtuException;
601 }
602
603
604
605
606
607
608
609
610
611
612
613
614 public static final class Placement
615 {
616
617
618 public static final Placement NO = new Placement();
619
620
621 private final Speed speed;
622
623
624 private final LanePosition position;
625
626
627
628
629 private Placement()
630 {
631 this.speed = null;
632 this.position = null;
633 }
634
635
636
637
638
639
640 public Placement(final Speed speed, final LanePosition position)
641 {
642 Throw.whenNull(speed, "Speed may not be null. Use Placement.NO if the GTU cannot be placed.");
643 Throw.whenNull(position, "Position may not be null. Use Placement.NO if the GTU cannot be placed.");
644 this.speed = speed;
645 this.position = position;
646 }
647
648
649
650
651
652 public boolean canPlace()
653 {
654 return this.speed != null && this.position != null;
655 }
656
657
658
659
660
661 public Speed getSpeed()
662 {
663 return this.speed;
664 }
665
666
667
668
669
670 public LanePosition getPosition()
671 {
672 return this.position;
673 }
674
675 @Override
676 public String toString()
677 {
678 return "Placement [speed=" + this.speed + ", position=" + this.position + "]";
679 }
680
681 }
682
683
684
685
686
687 public interface IdsWithCharacteristics extends Supplier<String>
688 {
689
690
691
692
693 boolean hasIds();
694 }
695
696 }