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.LaneBasedGtu;
40 import org.opentrafficsim.road.gtu.LaneBookkeeping;
41 import org.opentrafficsim.road.gtu.generator.GeneratorPositions.GeneratorLanePosition;
42 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristics;
43 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristicsGenerator;
44 import org.opentrafficsim.road.gtu.perception.object.PerceivedGtu;
45 import org.opentrafficsim.road.gtu.perception.object.PerceivedObject.Kinematics;
46 import org.opentrafficsim.road.gtu.perception.object.PerceivedObject.Kinematics.Overlap;
47 import org.opentrafficsim.road.network.CrossSectionLink;
48 import org.opentrafficsim.road.network.Lane;
49 import org.opentrafficsim.road.network.LanePosition;
50 import org.opentrafficsim.road.network.RoadNetwork;
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.START;
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 @SuppressWarnings("parameternumber")
152 public LaneBasedGtuGenerator(final String id, final Supplier<Duration> interarrivelTimeGenerator,
153 final LaneBasedGtuCharacteristicsGenerator laneBasedGtuCharacteristicsGenerator,
154 final GeneratorPositions generatorPositions, final RoadNetwork network, final OtsSimulatorInterface simulator,
155 final RoomChecker roomChecker, final Supplier<String> idGenerator) throws SimRuntimeException, NetworkException
156 {
157 this.id = id;
158 this.uniqueId = UUID.randomUUID().toString() + "_" + id;
159 this.interarrivelTimeGenerator = interarrivelTimeGenerator;
160 this.laneBasedGtuCharacteristicsGenerator = laneBasedGtuCharacteristicsGenerator;
161 this.generatorPositions = generatorPositions;
162 this.network = network;
163 this.simulator = simulator;
164 this.roomChecker = roomChecker;
165 this.idGenerator = idGenerator;
166 Duration headway = this.interarrivelTimeGenerator.get();
167 if (headway != null)
168 {
169 simulator.scheduleEventRel(headway, () -> Try.execute(() -> generateCharacteristics(), OtsRuntimeException.class,
170 "Exception generating characteristics."));
171 }
172 this.network.addNonLocatedObject(this);
173 if (this.idGenerator instanceof IdsWithCharacteristics ids && ids.hasIds())
174 {
175 setIdsInCharacteristicsOrder(true);
176 }
177 }
178
179
180
181
182
183 public void setNoLaneChangeDistance(final Length noLaneChangeDistance)
184 {
185 this.noLaneChangeDistance = noLaneChangeDistance;
186 }
187
188
189
190
191
192 public void setBookkeeping(final LaneBookkeeping bookkeeping)
193 {
194 this.bookkeeping = bookkeeping;
195 }
196
197
198
199
200
201 public void setErrorHandler(final GtuErrorHandler gtuErrorHandler)
202 {
203 this.errorHandler = gtuErrorHandler;
204 }
205
206
207
208
209
210
211
212 public void setIdsInCharacteristicsOrder(final boolean idsInCharacteristicsOrder)
213 {
214 Throw.when(this.firstCharacteristicsDrawn, IllegalStateException.class,
215 "Id order cannot be set once GTU characteristics were drawn.");
216 this.unplacedIds = new LinkedHashMap<>();
217 this.idsInCharacteristicsOrder = idsInCharacteristicsOrder;
218 }
219
220
221
222
223
224
225
226
227 @SuppressWarnings("unused")
228 private void generateCharacteristics() throws SimRuntimeException, ParameterException, GtuException
229 {
230 this.firstCharacteristicsDrawn = true;
231 synchronized (this.unplacedTemplates)
232 {
233 LaneBasedGtuCharacteristics characteristics = this.laneBasedGtuCharacteristicsGenerator.draw();
234 GtuType gtuType = characteristics.getGtuType();
235
236 Map<CrossSectionLink, Map<Integer, Integer>> unplaced = new LinkedHashMap<>();
237 for (CrossSectionLink link : this.unplacedTemplates.keySet())
238 {
239 Map<Integer, Integer> linkMap = new LinkedHashMap<>();
240 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGtuCharacteristics>>> linkTemplates =
241 this.unplacedTemplates.get(link);
242 for (GeneratorLanePosition lanePosition : linkTemplates.keySet())
243 {
244 linkMap.put(lanePosition.getLaneNumber(), linkTemplates.get(lanePosition).size());
245 }
246 unplaced.put(link, linkMap);
247 }
248
249 GeneratorLanePosition lanePosition = this.generatorPositions.draw(gtuType, characteristics, unplaced);
250
251
252 if (!this.disabled.contains(lanePosition.getPosition().lane()))
253 {
254 if (this.idsInCharacteristicsOrder)
255 {
256 this.unplacedIds.put(characteristics, this.idGenerator.get());
257 }
258 queueGtu(lanePosition, characteristics);
259 }
260 }
261
262 Duration headway = this.interarrivelTimeGenerator.get();
263 if (headway != null)
264 {
265 this.simulator.scheduleEventRel(headway, () -> Try.execute(() -> generateCharacteristics(),
266 OtsRuntimeException.class, "Exception generating characteristics."));
267 }
268
269 }
270
271
272
273
274
275
276
277
278
279 @SuppressWarnings("unused")
280 private void tryToPlaceGTU(final GeneratorLanePosition position)
281 throws SimRuntimeException, GtuException, NamingException, NetworkException
282 {
283 TimeStampedObject<LaneBasedGtuCharacteristics> timedCharacteristics;
284 Queue<TimeStampedObject<LaneBasedGtuCharacteristics>> queue =
285 this.unplacedTemplates.get(position.getLink()).get(position);
286
287 synchronized (queue)
288 {
289 timedCharacteristics = queue.peek();
290 }
291 if (null == timedCharacteristics)
292 {
293 return;
294 }
295
296 LaneBasedGtuCharacteristics characteristics = timedCharacteristics.object();
297 SortedSet<PerceivedGtu> leaders = new TreeSet<>();
298 getFirstLeaders(position.getPosition().lane(),
299 position.getPosition().position().neg().minus(characteristics.getFront()), position.getPosition().position(),
300 leaders);
301 Duration since = this.simulator.getSimulatorTime().minus(timedCharacteristics.timestamp());
302 Placement placement = this.roomChecker.canPlace(leaders, characteristics, since, position.getPosition());
303 if (placement.canPlace())
304 {
305
306 synchronized (queue)
307 {
308 queue.remove();
309 }
310 placeGtu(characteristics, placement.getPosition(), placement.getSpeed());
311 if (queue.size() > 0)
312 {
313 this.simulator.scheduleEventNow(() -> Try.execute(() -> tryToPlaceGTU(position), OtsRuntimeException.class,
314 "Exception during attempt to place GTU."));
315 }
316 }
317
318 else if (queue.size() > 0)
319 {
320 this.simulator.scheduleEventRel(this.reTryInterval, () -> Try.execute(() -> tryToPlaceGTU(position),
321 OtsRuntimeException.class, "Exception during attempt to place GTU."));
322 }
323
324 }
325
326
327
328
329
330
331
332 public final void queueGtu(final LaneBasedGtuCharacteristics characteristics, final Lane lane)
333 {
334
335 GeneratorLanePosition genPosition = null;
336 for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
337 {
338 if (lanePosition.getPosition().lane().equals(lane))
339 {
340 genPosition = lanePosition;
341 break;
342 }
343 }
344 Throw.when(genPosition == null, IllegalStateException.class, "Lane %s is not part of the generation.", lane);
345 try
346 {
347 queueGtu(genPosition, characteristics);
348 }
349 catch (SimRuntimeException exception)
350 {
351 throw new OtsRuntimeException("Unexpected exception while scheduling tryToPlace event.", exception);
352 }
353 }
354
355
356
357
358
359
360
361
362 private void queueGtu(final GeneratorLanePosition lanePosition, final LaneBasedGtuCharacteristics characteristics)
363 throws SimRuntimeException
364 {
365 if (!this.unplacedTemplates.containsKey(lanePosition.getLink()))
366 {
367 this.unplacedTemplates.put(lanePosition.getLink(), new LinkedHashMap<>());
368 }
369 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGtuCharacteristics>>> linkMap =
370 this.unplacedTemplates.get(lanePosition.getLink());
371 if (!linkMap.containsKey(lanePosition))
372 {
373 linkMap.put(lanePosition, new LinkedList<>());
374 }
375 Queue<TimeStampedObject<LaneBasedGtuCharacteristics>> queue = linkMap.get(lanePosition);
376 queue.add(new TimeStampedObject<>(characteristics, this.simulator.getSimulatorTime()));
377
378 if (queue.size() == 1)
379 {
380 this.simulator.scheduleEventNow(() -> Try.execute(() -> tryToPlaceGTU(lanePosition), OtsRuntimeException.class,
381 "Exception during attempt to place GTU."));
382 }
383
384 }
385
386
387
388
389
390
391
392
393
394
395
396 public final void placeGtu(final LaneBasedGtuCharacteristics characteristics, final LanePosition position,
397 final Speed speed) throws NamingException, GtuException, NetworkException, SimRuntimeException
398 {
399 String gtuId = this.idsInCharacteristicsOrder ? this.unplacedIds.remove(characteristics) : this.idGenerator.get();
400 LaneBasedGtu gtu = new LaneBasedGtu(gtuId, characteristics.getGtuType(), characteristics.getLength(),
401 characteristics.getWidth(), characteristics.getMaximumSpeed(), characteristics.getFront(), this.network);
402 gtu.setMaximumAcceleration(characteristics.getMaximumAcceleration());
403 gtu.setMaximumDeceleration(characteristics.getMaximumDeceleration());
404 gtu.setVehicleModel(characteristics.getVehicleModel());
405 gtu.setNoLaneChangeDistance(this.noLaneChangeDistance);
406 gtu.setBookkeeping(this.bookkeeping);
407 gtu.setErrorHandler(this.errorHandler);
408 gtu.init(characteristics.getStrategicalPlannerFactory().create(gtu, characteristics.getRoute(),
409 characteristics.getOrigin(), characteristics.getDestination()), position.getLocation(), speed);
410 this.generatedGTUs++;
411 fireEvent(GTU_GENERATED_EVENT, gtu);
412 }
413
414
415
416
417
418
419
420
421
422 private void getFirstLeaders(final Lane lane, final Length startDistance, final Length beyond, final Set<PerceivedGtu> set)
423 throws GtuException
424 {
425 Optional<LaneBasedGtu> next = lane.getGtuAhead(beyond, RelativePosition.FRONT, this.simulator.getSimulatorTime());
426 if (next.isPresent())
427 {
428 Length headway = startDistance.plus(next.get().getPosition(lane, next.get().getRear()));
429 if (headway.si < 300)
430 {
431 set.add(PerceivedGtu.of(next.get(), new Kinematics.Record(headway, next.get().getSpeed(),
432 next.get().getAcceleration(), true, Overlap.AHEAD)));
433 }
434 return;
435 }
436 Set<Lane> downstreamLanes = lane.nextLanes(null);
437 for (Lane downstreamLane : downstreamLanes)
438 {
439 Length startDistanceDownstream = startDistance.plus(lane.getLength());
440 if (startDistanceDownstream.si > 300)
441 {
442 return;
443 }
444 Length beyondDownstream = Length.ZERO;
445 getFirstLeaders(downstreamLane, startDistanceDownstream, beyondDownstream, set);
446 }
447 }
448
449 @Override
450 public final String toString()
451 {
452 return "LaneBasedGtuGenerator " + this.id + " on " + this.generatorPositions.getAllPositions();
453 }
454
455
456
457
458
459 public final long getGeneratedGTUs()
460 {
461 return this.generatedGTUs;
462 }
463
464
465
466
467
468 @Override
469 public final String getId()
470 {
471 return this.id;
472 }
473
474
475
476
477
478
479
480
481
482 public void disable(final Duration start, final Duration end, final Lane lane) throws SimRuntimeException
483 {
484 Throw.when(end.lt(start), SimRuntimeException.class, "End time %s is before start time %s.", end, start);
485 this.simulator.scheduleEventAbs(start, () -> disable(lane));
486 this.simulator.scheduleEventAbs(end, () -> enable());
487 }
488
489
490
491
492
493 @SuppressWarnings("unused")
494 private void disable(final Lane lane)
495 {
496 Throw.when(this.disabled != null && !this.disabled.isEmpty(), IllegalStateException.class,
497 "Disabling a generator that is already disabled is not allowed.");
498 this.disabled.add(lane);
499 }
500
501
502
503
504 @SuppressWarnings("unused")
505 private void enable()
506 {
507 this.disabled = new LinkedHashSet<>();
508 }
509
510 @Override
511 public String getFullId()
512 {
513 return this.uniqueId;
514 }
515
516 @Override
517 public Set<GtuGeneratorPosition> getPositions()
518 {
519 if (this.positions == null)
520 {
521 this.positions = new LinkedHashSet<>();
522 for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
523 {
524 LanePosition pos = lanePosition.getPosition();
525 DirectedPoint2d p = pos.getLocation();
526 this.positions.add(new GtuGeneratorPosition()
527 {
528 @Override
529 public DirectedPoint2d getLocation()
530 {
531 return p;
532 }
533
534 @Override
535 public Bounds2d getRelativeBounds()
536 {
537 return new Bounds2d(0.0, 0.0);
538 }
539
540 @Override
541 public int getQueueSize()
542 {
543 return LaneBasedGtuGenerator.this.getQueueSize(lanePosition);
544 }
545
546 @Override
547 public String getId()
548 {
549 return LaneBasedGtuGenerator.this.id + "@" + lanePosition.getLink().getId() + "." + pos.lane().getId();
550 }
551 });
552 }
553 }
554 return this.positions;
555 }
556
557
558
559
560
561
562 private int getQueueSize(final GeneratorLanePosition position)
563 {
564 for (CrossSectionLink link : this.unplacedTemplates.keySet())
565 {
566 for (GeneratorLanePosition lanePosition : this.unplacedTemplates.get(link).keySet())
567 {
568 if (lanePosition.equals(position))
569 {
570 return this.unplacedTemplates.get(link).get(lanePosition).size();
571 }
572 }
573 }
574 return 0;
575 }
576
577
578
579
580
581 public interface RoomChecker
582 {
583
584
585
586
587
588
589
590
591
592
593
594
595
596 Placement canPlace(SortedSet<PerceivedGtu> leaders, LaneBasedGtuCharacteristics characteristics, Duration since,
597 LanePosition initialPosition) throws NetworkException, GtuException;
598 }
599
600
601
602
603
604
605
606
607
608
609
610
611 public static final class Placement
612 {
613
614
615 public static final Placement NO = new Placement();
616
617
618 private final Speed speed;
619
620
621 private final LanePosition position;
622
623
624
625
626 private Placement()
627 {
628 this.speed = null;
629 this.position = null;
630 }
631
632
633
634
635
636
637 public Placement(final Speed speed, final LanePosition position)
638 {
639 Throw.whenNull(speed, "Speed may not be null. Use Placement.NO if the GTU cannot be placed.");
640 Throw.whenNull(position, "Position may not be null. Use Placement.NO if the GTU cannot be placed.");
641 this.speed = speed;
642 this.position = position;
643 }
644
645
646
647
648
649 public boolean canPlace()
650 {
651 return this.speed != null && this.position != null;
652 }
653
654
655
656
657
658 public Speed getSpeed()
659 {
660 return this.speed;
661 }
662
663
664
665
666
667 public LanePosition getPosition()
668 {
669 return this.position;
670 }
671
672 @Override
673 public String toString()
674 {
675 return "Placement [speed=" + this.speed + ", position=" + this.position + "]";
676 }
677
678 }
679
680
681
682
683
684 public interface IdsWithCharacteristics extends Supplier<String>
685 {
686
687
688
689
690 boolean hasIds();
691 }
692
693 }