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.core.network.OTSNetwork;
38 import org.opentrafficsim.road.gtu.generator.GeneratorPositions.GeneratorLanePosition;
39 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristics;
40 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristicsGenerator;
41 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
42 import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
43 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
44 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUReal;
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, GTUGenerator
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,
80 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGTUCharacteristics>>>> unplacedTemplates =
81 new LinkedHashMap<>();
82
83
84 private final String id;
85
86
87 private final Generator<Duration> interarrivelTimeGenerator;
88
89
90 private final LaneBasedGTUCharacteristicsGenerator laneBasedGTUCharacteristicsGenerator;
91
92
93 private long generatedGTUs = 0;
94
95
96 private Duration reTryInterval = new Duration(0.1, DurationUnit.SI);
97
98
99 private final GeneratorPositions generatorPositions;
100
101
102 private final OTSNetwork network;
103
104
105 private final OTSSimulatorInterface simulator;
106
107
108 private final RoomChecker roomChecker;
109
110
111 private final IdGenerator idGenerator;
112
113
114 private Length noLaneChangeDistance = null;
115
116
117 private Set<LaneDirection> disabled = new LinkedHashSet<>();
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 @SuppressWarnings("parameternumber")
135 public LaneBasedGTUGenerator(final String id, final Generator<Duration> interarrivelTimeGenerator,
136 final LaneBasedGTUCharacteristicsGenerator laneBasedGTUCharacteristicsGenerator,
137 final GeneratorPositions generatorPositions, final OTSNetwork network, final OTSSimulatorInterface simulator,
138 final RoomChecker roomChecker, final IdGenerator idGenerator)
139 throws SimRuntimeException, ProbabilityException, ParameterException
140 {
141 this.id = id;
142 this.interarrivelTimeGenerator = interarrivelTimeGenerator;
143 this.laneBasedGTUCharacteristicsGenerator = laneBasedGTUCharacteristicsGenerator;
144 this.generatorPositions = generatorPositions;
145 this.network = network;
146 this.simulator = simulator;
147 this.roomChecker = roomChecker;
148 this.idGenerator = idGenerator;
149 Duration headway = this.interarrivelTimeGenerator.draw();
150 if (headway != null)
151 {
152 simulator.scheduleEventRel(headway, this, this, "generateCharacteristics", new Object[] {});
153 }
154 }
155
156
157
158
159
160 public void setNoLaneChangeDistance(final Length noLaneChangeDistance)
161 {
162 this.noLaneChangeDistance = noLaneChangeDistance;
163 }
164
165
166
167
168
169
170
171
172
173 @SuppressWarnings("unused")
174 private void generateCharacteristics() throws ProbabilityException, SimRuntimeException, ParameterException, GTUException
175 {
176 synchronized (this.unplacedTemplates)
177 {
178
179 LaneBasedGTUCharacteristics characteristics = this.laneBasedGTUCharacteristicsGenerator.draw();
180 GTUType gtuType = characteristics.getGTUType();
181
182 Map<CrossSectionLink, Map<Integer, Integer>> unplaced = new LinkedHashMap<>();
183 for (CrossSectionLink link : this.unplacedTemplates.keySet())
184 {
185 Map<Integer, Integer> linkMap = new LinkedHashMap<>();
186 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGTUCharacteristics>>> linkTemplates =
187 this.unplacedTemplates.get(link);
188 for (GeneratorLanePosition lanePosition : linkTemplates.keySet())
189 {
190 linkMap.put(lanePosition.getLaneNumber(), linkTemplates.get(lanePosition).size());
191 }
192 unplaced.put(link, linkMap);
193 }
194
195 Speed desiredSpeed = characteristics.getStrategicalPlannerFactory().peekDesiredSpeed(gtuType,
196 this.generatorPositions.speedLimit(gtuType), characteristics.getMaximumSpeed());
197 GeneratorLanePosition lanePosition =
198 this.generatorPositions.draw(gtuType, unplaced, desiredSpeed, characteristics.getRoute());
199
200
201 Set<LaneDirection> lanes = new LinkedHashSet<>();
202 for (DirectedLanePosition pos : lanePosition.getPosition())
203 {
204 lanes.add(pos.getLaneDirection());
205 }
206 if (Collections.disjoint(this.disabled, lanes))
207 {
208 queueGtu(lanePosition, characteristics);
209 }
210
211 }
212 Duration headway = this.interarrivelTimeGenerator.draw();
213 if (headway != null)
214 {
215 this.simulator.scheduleEventRel(headway, this, this, "generateCharacteristics", new Object[] {});
216 }
217 }
218
219
220
221
222
223
224
225
226
227
228
229 @SuppressWarnings("unused")
230 private void tryToPlaceGTU(final GeneratorLanePosition position) throws SimRuntimeException, GTUException, NamingException,
231 NetworkException, OTSGeometryException, ProbabilityException
232 {
233 TimeStampedObject<LaneBasedGTUCharacteristics> timedCharacteristics;
234 Queue<TimeStampedObject<LaneBasedGTUCharacteristics>> queue =
235 this.unplacedTemplates.get(position.getLink()).get(position);
236
237 synchronized (queue)
238 {
239 timedCharacteristics = queue.peek();
240 }
241 if (null == timedCharacteristics)
242 {
243 return;
244 }
245
246 LaneBasedGTUCharacteristics characteristics = timedCharacteristics.getObject();
247 SortedSet<HeadwayGTU> leaders = new TreeSet<>();
248 for (DirectedLanePosition dirPos : position.getPosition())
249 {
250 getFirstLeaders(dirPos.getLaneDirection(), dirPos.getPosition().neg().minus(characteristics.getFront()),
251 dirPos.getPosition(), leaders);
252 }
253 Duration since = this.simulator.getSimulatorTime().minus(timedCharacteristics.getTimestamp());
254 Placement placement = this.roomChecker.canPlace(leaders, characteristics, since, position.getPosition());
255 if (placement.canPlace())
256 {
257
258 synchronized (queue)
259 {
260 queue.remove();
261 }
262 placeGtu(characteristics, placement.getPosition(), placement.getSpeed());
263 if (queue.size() > 0)
264 {
265 this.simulator.scheduleEventNow(this, this, "tryToPlaceGTU", new Object[] { position });
266 }
267 }
268 else if (queue.size() > 0)
269 {
270 this.simulator.scheduleEventRel(this.reTryInterval, this, this, "tryToPlaceGTU", new Object[] { position });
271 }
272 }
273
274
275
276
277
278
279
280 public final void queueGtu(final LaneBasedGTUCharacteristics characteristics, final Set<LaneDirection> position)
281 {
282
283 GeneratorLanePosition genPosition = null;
284 Set<LaneDirection> genSet = new LinkedHashSet<>();
285 for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
286 {
287 for (DirectedLanePosition dirPos : lanePosition.getPosition())
288 {
289 genSet.add(dirPos.getLaneDirection());
290 }
291 if (genSet.equals(position))
292 {
293 genPosition = lanePosition;
294 break;
295 }
296 genSet.clear();
297 }
298 Throw.when(genPosition == null, IllegalStateException.class, "Position %s is not part of the generation.", position);
299 try
300 {
301 queueGtu(genPosition, characteristics);
302 }
303 catch (SimRuntimeException exception)
304 {
305 throw new RuntimeException("Unexpected exception while scheduling tryToPlace event.", exception);
306 }
307 }
308
309
310
311
312
313
314
315
316 private void queueGtu(final GeneratorLanePosition lanePosition, final LaneBasedGTUCharacteristics characteristics)
317 throws SimRuntimeException
318 {
319 if (!this.unplacedTemplates.containsKey(lanePosition.getLink()))
320 {
321 this.unplacedTemplates.put(lanePosition.getLink(), new LinkedHashMap<>());
322 }
323 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGTUCharacteristics>>> linkMap =
324 this.unplacedTemplates.get(lanePosition.getLink());
325 if (!linkMap.containsKey(lanePosition))
326 {
327 linkMap.put(lanePosition, new LinkedList<>());
328 }
329 Queue<TimeStampedObject<LaneBasedGTUCharacteristics>> queue = linkMap.get(lanePosition);
330 queue.add(new TimeStampedObject<>(characteristics, this.simulator.getSimulatorTime()));
331 if (queue.size() == 1)
332 {
333 this.simulator.scheduleEventNow(this, this, "tryToPlaceGTU", new Object[] { lanePosition });
334 }
335 }
336
337
338
339
340
341
342
343
344
345
346
347
348 public final void placeGtu(final LaneBasedGTUCharacteristics characteristics, final Set<DirectedLanePosition> position,
349 final Speed speed) throws NamingException, GTUException, NetworkException, SimRuntimeException, OTSGeometryException
350 {
351 String gtuId = this.idGenerator.nextId();
352 LaneBasedIndividualGTU gtu = new LaneBasedIndividualGTU(gtuId, characteristics.getGTUType(),
353 characteristics.getLength(), characteristics.getWidth(), characteristics.getMaximumSpeed(),
354 characteristics.getFront(), this.simulator, this.network);
355 gtu.setMaximumAcceleration(characteristics.getMaximumAcceleration());
356 gtu.setMaximumDeceleration(characteristics.getMaximumDeceleration());
357 gtu.setVehicleModel(characteristics.getVehicleModel());
358 gtu.setNoLaneChangeDistance(this.noLaneChangeDistance);
359 gtu.init(characteristics.getStrategicalPlannerFactory().create(gtu, characteristics.getRoute(),
360 characteristics.getOrigin(), characteristics.getDestination()), position, speed);
361 this.generatedGTUs++;
362 fireEvent(GTU_GENERATED_EVENT, gtu);
363 }
364
365
366
367
368
369
370
371
372
373 private void getFirstLeaders(final LaneDirection lane, final Length startDistance, final Length beyond,
374 final Set<HeadwayGTU> set) throws GTUException
375 {
376 LaneBasedGTU next = lane.getLane().getGtuAhead(beyond, lane.getDirection(), RelativePosition.FRONT,
377 this.simulator.getSimulatorTime());
378 if (next != null)
379 {
380 Length headway;
381 if (lane.getDirection().isPlus())
382 {
383 headway = startDistance.plus(next.position(lane.getLane(), next.getRear()));
384 }
385 else
386 {
387 headway = startDistance.plus(lane.getLane().getLength().minus(next.position(lane.getLane(), next.getRear())));
388 }
389 if (headway.si < 300)
390 {
391 set.add(new HeadwayGTUReal(next, headway, true));
392 }
393 return;
394 }
395 Map<Lane, GTUDirectionality> downstreamLanes = lane.getLane().downstreamLanes(lane.getDirection(), GTUType.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 }