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