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