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.naming.NamingException;
16
17 import org.djunits.unit.DurationUnit;
18 import org.djunits.value.vdouble.scalar.Duration;
19 import org.djunits.value.vdouble.scalar.Length;
20 import org.djunits.value.vdouble.scalar.Speed;
21 import org.djunits.value.vdouble.scalar.Time;
22 import org.djutils.event.EventProducer;
23 import org.djutils.event.EventType;
24 import org.djutils.exceptions.Throw;
25 import org.djutils.immutablecollections.ImmutableMap;
26 import org.opentrafficsim.base.Identifiable;
27 import org.opentrafficsim.base.TimeStampedObject;
28 import org.opentrafficsim.base.parameters.ParameterException;
29 import org.opentrafficsim.core.distributions.Generator;
30 import org.opentrafficsim.core.distributions.ProbabilityException;
31 import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
32 import org.opentrafficsim.core.geometry.Bounds;
33 import org.opentrafficsim.core.geometry.DirectedPoint;
34 import org.opentrafficsim.core.geometry.OTSGeometryException;
35 import org.opentrafficsim.core.gtu.GTUDirectionality;
36 import org.opentrafficsim.core.gtu.GTUErrorHandler;
37 import org.opentrafficsim.core.gtu.GTUException;
38 import org.opentrafficsim.core.gtu.GTUType;
39 import org.opentrafficsim.core.gtu.RelativePosition;
40 import org.opentrafficsim.core.idgenerator.IdGenerator;
41 import org.opentrafficsim.core.network.NetworkException;
42 import org.opentrafficsim.road.gtu.generator.GeneratorPositions.GeneratorLanePosition;
43 import org.opentrafficsim.road.gtu.generator.GeneratorPositions.GeneratorLinkPosition;
44 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristics;
45 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristicsGenerator;
46 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
47 import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
48 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
49 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUReal;
50 import org.opentrafficsim.road.network.OTSRoadNetwork;
51 import org.opentrafficsim.road.network.lane.CrossSectionLink;
52 import org.opentrafficsim.road.network.lane.DirectedLanePosition;
53 import org.opentrafficsim.road.network.lane.Lane;
54 import org.opentrafficsim.road.network.lane.LaneDirection;
55
56 import nl.tudelft.simulation.dsol.SimRuntimeException;
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,
83 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGTUCharacteristics>>>> unplacedTemplates =
84 new LinkedHashMap<>();
85
86
87 private final String id;
88
89
90 private final Generator<Duration> interarrivelTimeGenerator;
91
92
93 private final LaneBasedGTUCharacteristicsGenerator laneBasedGTUCharacteristicsGenerator;
94
95
96 private long generatedGTUs = 0;
97
98
99 private Duration reTryInterval = new Duration(0.1, DurationUnit.SI);
100
101
102 private final GeneratorPositions generatorPositions;
103
104
105 private final OTSRoadNetwork network;
106
107
108 private final OTSSimulatorInterface simulator;
109
110
111 private final RoomChecker roomChecker;
112
113
114 private final IdGenerator idGenerator;
115
116
117 private Length noLaneChangeDistance = null;
118
119
120 private boolean instantaneousLaneChange = false;
121
122
123 private GTUErrorHandler errorHandler = GTUErrorHandler.THROW;
124
125
126 private Set<LaneDirection> disabled = new LinkedHashSet<>();
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143 @SuppressWarnings("parameternumber")
144 public LaneBasedGTUGenerator(final String id, final Generator<Duration> interarrivelTimeGenerator,
145 final LaneBasedGTUCharacteristicsGenerator laneBasedGTUCharacteristicsGenerator,
146 final GeneratorPositions generatorPositions, final OTSRoadNetwork network, final OTSSimulatorInterface simulator,
147 final RoomChecker roomChecker, final IdGenerator idGenerator)
148 throws SimRuntimeException, ProbabilityException, ParameterException
149 {
150 this.id = id;
151 this.interarrivelTimeGenerator = interarrivelTimeGenerator;
152 this.laneBasedGTUCharacteristicsGenerator = laneBasedGTUCharacteristicsGenerator;
153 this.generatorPositions = generatorPositions;
154 this.network = network;
155 this.simulator = simulator;
156 this.roomChecker = roomChecker;
157 this.idGenerator = idGenerator;
158 Duration headway = this.interarrivelTimeGenerator.draw();
159 if (headway != null)
160 {
161 simulator.scheduleEventRel(headway, this, this, "generateCharacteristics", new Object[] {});
162 }
163 }
164
165
166
167
168
169 public void setNoLaneChangeDistance(final Length noLaneChangeDistance)
170 {
171 this.noLaneChangeDistance = noLaneChangeDistance;
172 }
173
174
175
176
177
178 public void setInstantaneousLaneChange(final boolean instantaneous)
179 {
180 this.instantaneousLaneChange = instantaneous;
181 }
182
183
184
185
186
187 public void setErrorHandler(final GTUErrorHandler gtuErrorHandler)
188 {
189 this.errorHandler = gtuErrorHandler;
190 }
191
192
193
194
195
196
197
198
199
200 @SuppressWarnings("unused")
201 private void generateCharacteristics() throws ProbabilityException, SimRuntimeException, ParameterException, GTUException
202 {
203 synchronized (this.unplacedTemplates)
204 {
205 LaneBasedGTUCharacteristics characteristics = this.laneBasedGTUCharacteristicsGenerator.draw();
206 GTUType gtuType = characteristics.getGTUType();
207
208 Map<CrossSectionLink, Map<Integer, Integer>> unplaced = new LinkedHashMap<>();
209 for (CrossSectionLink link : this.unplacedTemplates.keySet())
210 {
211 Map<Integer, Integer> linkMap = new LinkedHashMap<>();
212 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGTUCharacteristics>>> linkTemplates =
213 this.unplacedTemplates.get(link);
214 for (GeneratorLanePosition lanePosition : linkTemplates.keySet())
215 {
216 linkMap.put(lanePosition.getLaneNumber(), linkTemplates.get(lanePosition).size());
217 }
218 unplaced.put(link, linkMap);
219 }
220
221 GeneratorLinkPosition linkPosition =
222 this.generatorPositions.draw(gtuType, characteristics.getDestination(), characteristics.getRoute());
223 Speed desiredSpeed = characteristics.getStrategicalPlannerFactory().peekDesiredSpeed(gtuType,
224 linkPosition.speedLimit(gtuType), characteristics.getMaximumSpeed());
225 GeneratorLanePosition lanePosition = linkPosition.draw(gtuType, unplaced.get(linkPosition.getLink()), 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, NamingException,
258 NetworkException, OTSGeometryException, ProbabilityException
259 {
260 TimeStampedObject<LaneBasedGTUCharacteristics> timedCharacteristics;
261 Queue<TimeStampedObject<LaneBasedGTUCharacteristics>> queue =
262 this.unplacedTemplates.get(position.getLink()).get(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()),
278 dirPos.getPosition(), leaders);
279 }
280 Duration since = this.simulator.getSimulatorAbsTime().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 =
351 this.unplacedTemplates.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.getSimulatorAbsTime()));
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, OTSGeometryException
377 {
378 String gtuId = this.idGenerator.nextId();
379 LaneBasedIndividualGTU gtu = new LaneBasedIndividualGTU(gtuId, characteristics.getGTUType(),
380 characteristics.getLength(), characteristics.getWidth(), characteristics.getMaximumSpeed(),
381 characteristics.getFront(), this.simulator, this.network);
382 gtu.setMaximumAcceleration(characteristics.getMaximumAcceleration());
383 gtu.setMaximumDeceleration(characteristics.getMaximumDeceleration());
384 gtu.setVehicleModel(characteristics.getVehicleModel());
385 gtu.setNoLaneChangeDistance(this.noLaneChangeDistance);
386 gtu.setInstantaneousLaneChange(this.instantaneousLaneChange);
387 gtu.setErrorHandler(this.errorHandler);
388 gtu.init(characteristics.getStrategicalPlannerFactory().create(gtu, characteristics.getRoute(),
389 characteristics.getOrigin(), characteristics.getDestination()), position, speed);
390 this.generatedGTUs++;
391 fireEvent(GTU_GENERATED_EVENT, gtu);
392 }
393
394
395
396
397
398
399
400
401
402 private void getFirstLeaders(final LaneDirection lane, final Length startDistance, final Length beyond,
403 final Set<HeadwayGTU> set) throws GTUException
404 {
405 LaneBasedGTU next = lane.getLane().getGtuAhead(beyond, lane.getDirection(), RelativePosition.FRONT,
406 this.simulator.getSimulatorAbsTime());
407 if (next != null)
408 {
409 Length headway;
410 if (lane.getDirection().isPlus())
411 {
412 headway = startDistance.plus(next.position(lane.getLane(), next.getRear()));
413 }
414 else
415 {
416 headway = startDistance.plus(lane.getLane().getLength().minus(next.position(lane.getLane(), next.getRear())));
417 }
418 if (headway.si < 300)
419 {
420 set.add(new HeadwayGTUReal(next, headway, true));
421 }
422 return;
423 }
424 ImmutableMap<Lane, GTUDirectionality> downstreamLanes =
425 lane.getLane().downstreamLanes(lane.getDirection(), this.network.getGtuType(GTUType.DEFAULTS.VEHICLE));
426 for (Lane downstreamLane : downstreamLanes.keySet())
427 {
428 Length startDistanceDownstream = startDistance.plus(lane.getLane().getLength());
429 if (startDistanceDownstream.si > 300)
430 {
431 return;
432 }
433 GTUDirectionality dir = downstreamLanes.get(downstreamLane);
434 Length beyondDownstream = dir.isPlus() ? Length.ZERO : downstreamLane.getLength();
435 getFirstLeaders(new LaneDirection(downstreamLane, dir), startDistanceDownstream, beyondDownstream, set);
436 }
437 }
438
439
440 @Override
441 public final String toString()
442 {
443 return "LaneBasedGTUGenerator " + this.id + " on " + this.generatorPositions;
444 }
445
446
447
448
449 public final long getGeneratedGTUs()
450 {
451 return this.generatedGTUs;
452 }
453
454
455
456
457
458 @Override
459 public final String getId()
460 {
461 return this.id;
462 }
463
464
465
466
467
468
469
470
471
472 public void disable(final Time start, final Time end, final Set<LaneDirection> laneDirections) throws SimRuntimeException
473 {
474 Throw.when(end.lt(start), SimRuntimeException.class, "End time %s is before start time %s.", end, start);
475 this.simulator.scheduleEventAbsTime(start, this, this, "disable", new Object[] {laneDirections});
476 this.simulator.scheduleEventAbsTime(end, this, this, "enable", new Object[0]);
477 }
478
479
480
481
482
483 @SuppressWarnings("unused")
484 private void disable(final Set<LaneDirection> laneDirections)
485 {
486 Throw.when(this.disabled != null && !this.disabled.isEmpty(), IllegalStateException.class,
487 "Disabling a generator that is already disabled is not allowed.");
488 this.disabled = laneDirections;
489 }
490
491
492
493
494 @SuppressWarnings("unused")
495 private void enable()
496 {
497 this.disabled = new LinkedHashSet<>();
498 }
499
500
501
502
503
504 public interface RoomChecker
505 {
506
507
508
509
510
511
512
513
514
515
516
517
518
519 Placement canPlace(SortedSet<HeadwayGTU> leaders, LaneBasedGTUCharacteristics characteristics, Duration since,
520 Set<DirectedLanePosition> initialPosition) throws NetworkException, GTUException;
521 }
522
523
524
525
526
527
528
529
530
531
532
533
534
535 public static final class Placement
536 {
537
538
539 public static final Placement NO = new Placement();
540
541
542 private final Speed speed;
543
544
545 private final Set<DirectedLanePosition> position;
546
547
548
549
550 private Placement()
551 {
552 this.speed = null;
553 this.position = null;
554 }
555
556
557
558
559
560
561 public Placement(final Speed speed, final Set<DirectedLanePosition> position)
562 {
563 Throw.whenNull(speed, "Speed may not be null. Use Placement.NO if the GTU cannot be placed.");
564 Throw.whenNull(position, "Position may not be null. Use Placement.NO if the GTU cannot be placed.");
565 this.speed = speed;
566 this.position = position;
567 }
568
569
570
571
572
573 public boolean canPlace()
574 {
575 return this.speed != null && this.position != null;
576 }
577
578
579
580
581
582 public Speed getSpeed()
583 {
584 return this.speed;
585 }
586
587
588
589
590
591 public Set<DirectedLanePosition> getPosition()
592 {
593 return this.position;
594 }
595
596
597 @Override
598 public String toString()
599 {
600 return "Placement [speed=" + this.speed + ", position=" + this.position + "]";
601 }
602
603 }
604
605
606 @Override
607 public DirectedPoint getLocation()
608 {
609 return this.generatorPositions.getLocation();
610 }
611
612
613 @Override
614 public Bounds getBounds() throws RemoteException
615 {
616 return this.generatorPositions.getBounds();
617 }
618
619
620 @Override
621 public Map<DirectedPoint, Integer> getQueueLengths()
622 {
623 Map<DirectedPoint, Integer> result = new LinkedHashMap<>();
624 for (CrossSectionLink link : this.unplacedTemplates.keySet())
625 {
626 for (GeneratorLanePosition lanePosition : this.unplacedTemplates.get(link).keySet())
627 {
628 result.put(lanePosition.getPosition().iterator().next().getLocation(),
629 this.unplacedTemplates.get(link).get(lanePosition).size());
630 }
631 }
632 for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
633 {
634 DirectedPoint p = lanePosition.getPosition().iterator().next().getLocation();
635 if (!result.containsKey(p))
636 {
637 result.put(p, 0);
638 }
639 }
640 return result;
641 }
642
643
644 @Override
645 public Serializable getSourceId()
646 {
647 return this.id;
648 }
649
650 }