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