1 package org.opentrafficsim.road.gtu.lane.tactical.pt;
2
3 import java.util.LinkedHashMap;
4 import java.util.List;
5 import java.util.Map;
6 import java.util.Optional;
7 import java.util.Set;
8
9 import org.djunits.value.vdouble.scalar.Duration;
10 import org.djutils.exceptions.Throw;
11 import org.opentrafficsim.core.gtu.GtuType;
12 import org.opentrafficsim.core.network.NetworkException;
13 import org.opentrafficsim.core.network.Node;
14 import org.opentrafficsim.core.network.route.Route;
15
16 /**
17 * <p>
18 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
19 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
20 * </p>
21 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
22 * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
23 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
24 */
25 public class BusSchedule extends Route
26 {
27
28 /** Line of the bus schedule. */
29 private final String line;
30
31 /** List of bus stops. */
32 private final Map<String, BusStopInfo> schedule = new LinkedHashMap<>();
33
34 /** Map of actual departures stored per bus stop. */
35 private final Map<String, Duration> actualDeparturesBusStop = new LinkedHashMap<>();
36
37 /** Map of actual departures stored per conflict. */
38 private final Map<String, Duration> actualDeparturesConflict = new LinkedHashMap<>();
39
40 /**
41 * Constructor.
42 * @param id id
43 * @param gtuType the GtuType for which this is a route
44 * @param nodes nodes
45 * @param line line of the bus schedule
46 * @throws NetworkException if intermediate nodes are missing in the route.
47 */
48 public BusSchedule(final String id, final GtuType gtuType, final List<Node> nodes, final String line)
49 throws NetworkException
50 {
51 super(id, gtuType, nodes);
52 this.line = line;
53 }
54
55 /**
56 * Constructor.
57 * @param id id
58 * @param gtuType the GtuType for which this is a route
59 * @param line line of the bus schedule
60 */
61 public BusSchedule(final String id, final GtuType gtuType, final String line)
62 {
63 super(id, gtuType);
64 this.line = line;
65 }
66
67 /**
68 * Adds a stop to the schedule.
69 * @param busStopId bus stop id
70 * @param departureTime departure time
71 * @param dwellTime dwell time
72 * @param forceSchedule whether to wait until departure time
73 */
74 public void addBusStop(final String busStopId, final Duration departureTime, final Duration dwellTime,
75 final boolean forceSchedule)
76 {
77 Throw.whenNull(busStopId, "Bus stop id may not be null.");
78 Throw.whenNull(departureTime, "Departure time may not be null.");
79 Throw.whenNull(dwellTime, "Dwell time may not be null.");
80 this.schedule.put(busStopId, new BusStopInfo(departureTime, dwellTime, forceSchedule));
81 }
82
83 /**
84 * Whether the bus of this line should stop for this bus stop. False if not the correct line, or already stopped.
85 * @param busStopId id of bus stop
86 * @param time time to check
87 * @return whether the bus of this line should stop for this bus stop
88 */
89 public boolean isLineStop(final String busStopId, final Duration time)
90 {
91 return this.schedule.containsKey(busStopId) && (!this.actualDeparturesConflict.containsKey(busStopId)
92 || time.lt(this.actualDeparturesConflict.get(busStopId)));
93 }
94
95 /**
96 * Returns departure time for the given bus stop.
97 * @param busStopId id of bus stop
98 * @return departure time for the given bus stop
99 */
100 public Duration getDepartureTime(final String busStopId)
101 {
102 checkStop(busStopId);
103 return this.schedule.get(busStopId).getDepartureTime();
104 }
105
106 /**
107 * Returns dwell time for the given bus stop.
108 * @param busStopId id of bus stop
109 * @return dwell time for the given bus stop
110 */
111 public Duration getDwellTime(final String busStopId)
112 {
113 checkStop(busStopId);
114 return this.schedule.get(busStopId).getDwellTime();
115 }
116
117 /**
118 * Returns whether the departure time is enforced.
119 * @param busStopId id of bus stop
120 * @return whether the departure time is enforced
121 */
122 public boolean isForceSchedule(final String busStopId)
123 {
124 checkStop(busStopId);
125 return this.schedule.get(busStopId).isForceSchedule();
126 }
127
128 /**
129 * Throws exception when the bus stop is not part of this schedule.
130 * @param busStopId id of bus stop
131 * @throws IllegalArgumentException if the bus stop is not part of this schedule
132 */
133 private void checkStop(final String busStopId)
134 {
135 Throw.when(!this.schedule.containsKey(busStopId), IllegalArgumentException.class, "Bus stop %s is not for schedule %s.",
136 busStopId, this);
137 }
138
139 /**
140 * Set actual departure time.
141 * @param busStopId bus stop id
142 * @param conflictIds conflicts downstream of the bus stop
143 * @param time actual departure time
144 */
145 public void setActualDeparture(final String busStopId, final Set<String> conflictIds, final Duration time)
146 {
147 this.actualDeparturesBusStop.put(busStopId, time);
148 for (String conflictId : conflictIds)
149 {
150 this.actualDeparturesConflict.put(conflictId, time);
151 }
152 }
153
154 /**
155 * Return the actual departure time.
156 * @param busStopId bus stop id
157 * @return actual departure time, empty if not given
158 */
159 public Optional<Duration> getActualDepartureBusStop(final String busStopId)
160 {
161 return Optional.ofNullable(this.actualDeparturesBusStop.get(busStopId));
162 }
163
164 /**
165 * Return the actual departure time.
166 * @param conflictId conflict id
167 * @return actual departure time, empty if not given
168 */
169 public Optional<Duration> getActualDepartureConflict(final String conflictId)
170 {
171 return Optional.ofNullable(this.actualDeparturesConflict.get(conflictId));
172 }
173
174 /**
175 * Return bus line.
176 * @return line.
177 */
178 public String getLine()
179 {
180 return this.line;
181 }
182
183 @Override
184 public String toString()
185 {
186 return "BusSchedule [id=" + getId() + ", line=" + this.line + "]";
187 }
188
189 /**
190 * Class to contain info regarding a stop in the schedule.
191 * <p>
192 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
193 * <br>
194 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
195 * </p>
196 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
197 * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
198 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
199 */
200 private class BusStopInfo
201 {
202
203 /** Departure time. */
204 private final Duration departureTime;
205
206 /** Dwell time. */
207 private final Duration dwellTime;
208
209 /** Whether to wait until departure time. */
210 private final boolean forceSchedule;
211
212 /**
213 * @param departureTime departure time
214 * @param dwellTime dwell time
215 * @param forceSchedule whether to wait until departure time
216 */
217 BusStopInfo(final Duration departureTime, final Duration dwellTime, final boolean forceSchedule)
218 {
219 this.departureTime = departureTime;
220 this.dwellTime = dwellTime;
221 this.forceSchedule = forceSchedule;
222 }
223
224 /**
225 * @return departureTime.
226 */
227 public final Duration getDepartureTime()
228 {
229 return this.departureTime;
230 }
231
232 /**
233 * @return dwellTime.
234 */
235 public final Duration getDwellTime()
236 {
237 return this.dwellTime;
238 }
239
240 /**
241 * @return forceSchedule.
242 */
243 public final boolean isForceSchedule()
244 {
245 return this.forceSchedule;
246 }
247
248 @Override
249 public String toString()
250 {
251 return "BusStopInfo [departureTime=" + this.departureTime + ", dwellTime=" + this.dwellTime + ", forceSchedule="
252 + this.forceSchedule + "]";
253 }
254
255 }
256
257 }