View Javadoc
1   package org.opentrafficsim.road.gtu.tactical.lmrs;
2   
3   import java.util.Optional;
4   
5   import org.djunits.unit.LengthUnit;
6   import org.djunits.value.vdouble.scalar.Acceleration;
7   import org.djunits.value.vdouble.scalar.Duration;
8   import org.djunits.value.vdouble.scalar.Length;
9   import org.opentrafficsim.base.parameters.ParameterException;
10  import org.opentrafficsim.core.gtu.GtuException;
11  import org.opentrafficsim.core.gtu.Stateless;
12  import org.opentrafficsim.road.gtu.perception.FilteredIterable;
13  import org.opentrafficsim.road.gtu.perception.PerceptionCollectable;
14  import org.opentrafficsim.road.gtu.perception.RelativeLane;
15  import org.opentrafficsim.road.gtu.perception.categories.BusStopPerception;
16  import org.opentrafficsim.road.gtu.perception.object.PerceivedBusStop;
17  import org.opentrafficsim.road.gtu.tactical.TacticalContextEgo;
18  import org.opentrafficsim.road.gtu.tactical.pt.BusSchedule;
19  import org.opentrafficsim.road.gtu.tactical.util.CarFollowingUtil;
20  import org.opentrafficsim.road.network.object.BusStop;
21  
22  /**
23   * Bus stop acceleration incentive.
24   * <p>
25   * Copyright (c) 2013-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
26   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
27   * </p>
28   * @author Alexander Verbraeck
29   * @author Peter Knoppers
30   * @author Wouter Schakel
31   */
32  public final class AccelerationBusStop implements AccelerationIncentive, Stateless<AccelerationIncentive>
33  {
34  
35      /** Distance within which the bus can open the doors. */
36      // TODO this process is much more complex: tail blocking other traffic? other bus in front? many people at bus stop?
37      private static final Length STOP_DISTANCE = new Length(15.0, LengthUnit.SI);
38  
39      /** Singleton instance. */
40      public static final AccelerationBusStop SINGLETON = new AccelerationBusStop();
41  
42      @Override
43      public AccelerationBusStop get()
44      {
45          return SINGLETON;
46      }
47  
48      /**
49       * Constructor.
50       */
51      private AccelerationBusStop()
52      {
53          //
54      }
55  
56      @Override
57      @SuppressWarnings("checkstyle:parameternumber")
58      public Acceleration accelerate(final TacticalContextEgo context, final RelativeLane lane, final Length mergeDistance)
59              throws ParameterException, GtuException
60      {
61          PerceptionCollectable<PerceivedBusStop, BusStop> stops =
62                  context.getPerception().getPerceptionCategory(BusStopPerception.class).getBusStops();
63          if (stops.isEmpty())
64          {
65              return NO_REASON;
66          }
67          BusSchedule busSchedule = (BusSchedule) context.getRoute().orElseThrow(
68                  () -> new GtuException("Unable to determine acceleration for bus stops for bus without bus schedule."));
69          Duration now = context.getTime();
70          Iterable<PerceivedBusStop> it = lane.isCurrent() ? stops : new FilteredIterable<>(stops, (busStop) ->
71          {
72              return busStop.getDistance().gt(mergeDistance);
73          });
74          for (PerceivedBusStop stop : it)
75          {
76              String busStopId = stop.getId();
77              if (busSchedule.isLineStop(busStopId, now))
78              {
79  
80                  // check when to leave
81                  boolean stoppedAtStop =
82                          stop.getRelativeLane().isCurrent() && stop.getDistance().le(STOP_DISTANCE) && context.getSpeed().eq0();
83                  if (busSchedule.getActualDepartureBusStop(busStopId).isEmpty())
84                  {
85                      if (stoppedAtStop)
86                      {
87                          // bus just stopped
88                          Duration departureTime = now.plus(busSchedule.getDwellTime(busStopId));
89                          if (busSchedule.isForceSchedule(busStopId))
90                          {
91                              departureTime = Duration.max(departureTime, busSchedule.getDepartureTime(busStopId));
92                          }
93                          busSchedule.setActualDeparture(busStopId, stop.getConflictIds(), departureTime);
94                      }
95                  }
96  
97                  // stop if not known yet, or before departure time
98                  Optional<Duration> actualDeparture = busSchedule.getActualDepartureBusStop(busStopId);
99                  if (actualDeparture.isEmpty() || now.lt(actualDeparture.get()))
100                 {
101                     if (stoppedAtStop)
102                     {
103                         // stand still at location where stop was initiated
104                         return Acceleration.ZERO;
105                     }
106                     else
107                     {
108                         // decelerate to initiate stop
109                         return CarFollowingUtil.stop(context, stop.getDistance());
110                     }
111                 }
112             }
113         }
114         return NO_REASON;
115     }
116 
117     @Override
118     public String toString()
119     {
120         return "AccelerationBusStop";
121     }
122 
123 }