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