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.GeneratorPositions.GeneratorLinkPosition;
43 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristics;
44 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristicsGenerator;
45 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
46 import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
47 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
48 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUReal;
49 import org.opentrafficsim.road.network.OTSRoadNetwork;
50 import org.opentrafficsim.road.network.lane.CrossSectionLink;
51 import org.opentrafficsim.road.network.lane.DirectedLanePosition;
52 import org.opentrafficsim.road.network.lane.Lane;
53 import org.opentrafficsim.road.network.lane.LaneDirection;
54
55 import nl.tudelft.simulation.dsol.SimRuntimeException;
56 import nl.tudelft.simulation.language.d3.DirectedPoint;
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 public class LaneBasedGTUGenerator extends EventProducer implements Serializable, Identifiable, GtuGeneratorQueue
72 {
73
74 private static final long serialVersionUID = 20160000L;
75
76
77
78
79 public static final EventType GTU_GENERATED_EVENT = new EventType("GENERATOR.GTU_GENERATED");
80
81
82 private final Map<CrossSectionLink, 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) throws SimRuntimeException, ProbabilityException,
147 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 LaneBasedGTUCharacteristics characteristics = this.laneBasedGTUCharacteristicsGenerator.draw();
205 GTUType gtuType = characteristics.getGTUType();
206
207 Map<CrossSectionLink, Map<Integer, Integer>> unplaced = new LinkedHashMap<>();
208 for (CrossSectionLink link : this.unplacedTemplates.keySet())
209 {
210 Map<Integer, Integer> linkMap = new LinkedHashMap<>();
211 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGTUCharacteristics>>> linkTemplates =
212 this.unplacedTemplates.get(link);
213 for (GeneratorLanePosition lanePosition : linkTemplates.keySet())
214 {
215 linkMap.put(lanePosition.getLaneNumber(), linkTemplates.get(lanePosition).size());
216 }
217 unplaced.put(link, linkMap);
218 }
219
220 GeneratorLinkPosition linkPosition = this.generatorPositions.draw(gtuType, characteristics.getDestination(),
221 characteristics.getRoute());
222 Speed desiredSpeed = characteristics.getStrategicalPlannerFactory().peekDesiredSpeed(gtuType, linkPosition
223 .speedLimit(gtuType), characteristics.getMaximumSpeed());
224 GeneratorLanePosition lanePosition = linkPosition.draw(gtuType, unplaced.get(linkPosition.getLink()),
225 desiredSpeed);
226
227
228 Set<LaneDirection> lanes = new LinkedHashSet<>();
229 for (DirectedLanePosition pos : lanePosition.getPosition())
230 {
231 lanes.add(pos.getLaneDirection());
232 }
233 if (Collections.disjoint(this.disabled, lanes))
234 {
235 queueGtu(lanePosition, characteristics);
236 }
237
238 }
239 Duration headway = this.interarrivelTimeGenerator.draw();
240 if (headway != null)
241 {
242 this.simulator.scheduleEventRel(headway, this, this, "generateCharacteristics", new Object[] {});
243 }
244 }
245
246
247
248
249
250
251
252
253
254
255
256 @SuppressWarnings("unused")
257 private void tryToPlaceGTU(final GeneratorLanePosition position) throws SimRuntimeException, GTUException,
258 NamingException, NetworkException, OTSGeometryException, ProbabilityException
259 {
260 TimeStampedObject<LaneBasedGTUCharacteristics> timedCharacteristics;
261 Queue<TimeStampedObject<LaneBasedGTUCharacteristics>> queue = this.unplacedTemplates.get(position.getLink()).get(
262 position);
263
264 synchronized (queue)
265 {
266 timedCharacteristics = queue.peek();
267 }
268 if (null == timedCharacteristics)
269 {
270 return;
271 }
272
273 LaneBasedGTUCharacteristics characteristics = timedCharacteristics.getObject();
274 SortedSet<HeadwayGTU> leaders = new TreeSet<>();
275 for (DirectedLanePosition dirPos : position.getPosition())
276 {
277 getFirstLeaders(dirPos.getLaneDirection(), dirPos.getPosition().neg().minus(characteristics.getFront()), dirPos
278 .getPosition(), leaders);
279 }
280 Duration since = this.simulator.getSimulatorTime().minus(timedCharacteristics.getTimestamp());
281 Placement placement = this.roomChecker.canPlace(leaders, characteristics, since, position.getPosition());
282 if (placement.canPlace())
283 {
284
285 synchronized (queue)
286 {
287 queue.remove();
288 }
289 placeGtu(characteristics, placement.getPosition(), placement.getSpeed());
290 if (queue.size() > 0)
291 {
292 this.simulator.scheduleEventNow(this, this, "tryToPlaceGTU", new Object[] {position});
293 }
294 }
295 else if (queue.size() > 0)
296 {
297 this.simulator.scheduleEventRel(this.reTryInterval, this, this, "tryToPlaceGTU", new Object[] {position});
298 }
299 }
300
301
302
303
304
305
306
307 public final void queueGtu(final LaneBasedGTUCharacteristics characteristics, final Set<LaneDirection> position)
308 {
309
310 GeneratorLanePosition genPosition = null;
311 Set<LaneDirection> genSet = new LinkedHashSet<>();
312 for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
313 {
314 for (DirectedLanePosition dirPos : lanePosition.getPosition())
315 {
316 genSet.add(dirPos.getLaneDirection());
317 }
318 if (genSet.equals(position))
319 {
320 genPosition = lanePosition;
321 break;
322 }
323 genSet.clear();
324 }
325 Throw.when(genPosition == null, IllegalStateException.class, "Position %s is not part of the generation.", position);
326 try
327 {
328 queueGtu(genPosition, characteristics);
329 }
330 catch (SimRuntimeException exception)
331 {
332 throw new RuntimeException("Unexpected exception while scheduling tryToPlace event.", exception);
333 }
334 }
335
336
337
338
339
340
341
342
343 private void queueGtu(final GeneratorLanePosition lanePosition, final LaneBasedGTUCharacteristics characteristics)
344 throws SimRuntimeException
345 {
346 if (!this.unplacedTemplates.containsKey(lanePosition.getLink()))
347 {
348 this.unplacedTemplates.put(lanePosition.getLink(), new LinkedHashMap<>());
349 }
350 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGTUCharacteristics>>> linkMap = this.unplacedTemplates
351 .get(lanePosition.getLink());
352 if (!linkMap.containsKey(lanePosition))
353 {
354 linkMap.put(lanePosition, new LinkedList<>());
355 }
356 Queue<TimeStampedObject<LaneBasedGTUCharacteristics>> queue = linkMap.get(lanePosition);
357 queue.add(new TimeStampedObject<>(characteristics, this.simulator.getSimulatorTime()));
358 if (queue.size() == 1)
359 {
360 this.simulator.scheduleEventNow(this, this, "tryToPlaceGTU", new Object[] {lanePosition});
361 }
362 }
363
364
365
366
367
368
369
370
371
372
373
374
375 public final void placeGtu(final LaneBasedGTUCharacteristics characteristics, final Set<DirectedLanePosition> position,
376 final Speed speed) throws NamingException, GTUException, NetworkException, SimRuntimeException,
377 OTSGeometryException
378 {
379 String gtuId = this.idGenerator.nextId();
380 LaneBasedIndividualGTUasedIndividualGTU.html#LaneBasedIndividualGTU">LaneBasedIndividualGTU gtu = new LaneBasedIndividualGTU(gtuId, characteristics.getGTUType(), characteristics
381 .getLength(), characteristics.getWidth(), characteristics.getMaximumSpeed(), characteristics.getFront(),
382 this.simulator, this.network);
383 gtu.setMaximumAcceleration(characteristics.getMaximumAcceleration());
384 gtu.setMaximumDeceleration(characteristics.getMaximumDeceleration());
385 gtu.setVehicleModel(characteristics.getVehicleModel());
386 gtu.setNoLaneChangeDistance(this.noLaneChangeDistance);
387 gtu.setInstantaneousLaneChange(this.instantaneousLaneChange);
388 gtu.setErrorHandler(this.errorHandler);
389 gtu.init(characteristics.getStrategicalPlannerFactory().create(gtu, characteristics.getRoute(), characteristics
390 .getOrigin(), characteristics.getDestination()), position, speed);
391 this.generatedGTUs++;
392 fireEvent(GTU_GENERATED_EVENT, gtu);
393 }
394
395
396
397
398
399
400
401
402
403 private void getFirstLeaders(final LaneDirection lane, final Length startDistance, final Length beyond,
404 final Set<HeadwayGTU> set) throws GTUException
405 {
406 LaneBasedGTU next = lane.getLane().getGtuAhead(beyond, lane.getDirection(), RelativePosition.FRONT, this.simulator
407 .getSimulatorTime());
408 if (next != null)
409 {
410 Length headway;
411 if (lane.getDirection().isPlus())
412 {
413 headway = startDistance.plus(next.position(lane.getLane(), next.getRear()));
414 }
415 else
416 {
417 headway = startDistance.plus(lane.getLane().getLength().minus(next.position(lane.getLane(), next
418 .getRear())));
419 }
420 if (headway.si < 300)
421 {
422 set.add(new HeadwayGTUReal(next, headway, true));
423 }
424 return;
425 }
426 ImmutableMap<Lane, GTUDirectionality> downstreamLanes = lane.getLane().downstreamLanes(lane.getDirection(),
427 this.network.getGtuType(GTUType.DEFAULTS.VEHICLE));
428 for (Lane downstreamLane : downstreamLanes.keySet())
429 {
430 Length startDistanceDownstream = startDistance.plus(lane.getLane().getLength());
431 if (startDistanceDownstream.si > 300)
432 {
433 return;
434 }
435 GTUDirectionality dir = downstreamLanes.get(downstreamLane);
436 Length beyondDownstream = dir.isPlus() ? Length.ZERO : downstreamLane.getLength();
437 getFirstLeaders(new LaneDirection(downstreamLane, dir), startDistanceDownstream, beyondDownstream, set);
438 }
439 }
440
441
442 @Override
443 public final String toString()
444 {
445 return "LaneBasedGTUGenerator " + this.id + " on " + this.generatorPositions;
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 Set<LaneDirection> laneDirections) throws SimRuntimeException
475 {
476 Throw.when(end.lt(start), SimRuntimeException.class, "End time %s is before start time %s.", end, start);
477 this.simulator.scheduleEventAbs(start, this, this, "disable", new Object[] {laneDirections});
478 this.simulator.scheduleEventAbs(end, this, this, "enable", new Object[0]);
479 }
480
481
482
483
484
485 @SuppressWarnings("unused")
486 private void disable(final Set<LaneDirection> laneDirections)
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 = laneDirections;
491 }
492
493
494
495
496 @SuppressWarnings("unused")
497 private void enable()
498 {
499 this.disabled = new LinkedHashSet<>();
500 }
501
502
503
504
505
506 public interface RoomChecker
507 {
508
509
510
511
512
513
514
515
516
517
518
519
520
521 Placement canPlace(SortedSet<HeadwayGTU> leaders, LaneBasedGTUCharacteristics characteristics, Duration since,
522 Set<DirectedLanePosition> initialPosition) throws NetworkException, GTUException;
523 }
524
525
526
527
528
529
530
531
532
533
534
535
536
537 public static final class Placement
538 {
539
540
541 public static final Placement NO = new Placement();
542
543
544 private final Speed speed;
545
546
547 private final Set<DirectedLanePosition> position;
548
549
550
551
552 private Placement()
553 {
554 this.speed = null;
555 this.position = null;
556 }
557
558
559
560
561
562
563 public Placement(final Speed speed, final Set<DirectedLanePosition> position)
564 {
565 Throw.whenNull(speed, "Speed may not be null. Use Placement.NO if the GTU cannot be placed.");
566 Throw.whenNull(position, "Position may not be null. Use Placement.NO if the GTU cannot be placed.");
567 this.speed = speed;
568 this.position = position;
569 }
570
571
572
573
574
575 public boolean canPlace()
576 {
577 return this.speed != null && this.position != null;
578 }
579
580
581
582
583
584 public Speed getSpeed()
585 {
586 return this.speed;
587 }
588
589
590
591
592
593 public Set<DirectedLanePosition> getPosition()
594 {
595 return this.position;
596 }
597
598
599 @Override
600 public String toString()
601 {
602 return "Placement [speed=" + this.speed + ", position=" + this.position + "]";
603 }
604
605 }
606
607
608 @Override
609 public DirectedPoint getLocation() throws RemoteException
610 {
611 return this.generatorPositions.getLocation();
612 }
613
614
615 @Override
616 public Bounds getBounds() throws RemoteException
617 {
618 return this.generatorPositions.getBounds();
619 }
620
621
622 @Override
623 public Map<DirectedPoint, Integer> getQueueLengths()
624 {
625 Map<DirectedPoint, Integer> result = new LinkedHashMap<>();
626 for (CrossSectionLink link : this.unplacedTemplates.keySet())
627 {
628 for (GeneratorLanePosition lanePosition : this.unplacedTemplates.get(link).keySet())
629 {
630 result.put(lanePosition.getPosition().iterator().next().getLocation(), this.unplacedTemplates.get(link).get(
631 lanePosition).size());
632 }
633 }
634 for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
635 {
636 DirectedPoint p = lanePosition.getPosition().iterator().next().getLocation();
637 if (!result.containsKey(p))
638 {
639 result.put(p, 0);
640 }
641 }
642 return result;
643 }
644
645
646 @Override
647 public Serializable getSourceId()
648 {
649 return this.id;
650 }
651
652 }