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