1 package org.opentrafficsim.road.gtu.tactical;
2
3 import java.util.LinkedHashMap;
4 import java.util.Map;
5 import java.util.Map.Entry;
6 import java.util.NavigableMap;
7 import java.util.NoSuchElementException;
8 import java.util.Optional;
9 import java.util.TreeMap;
10
11 import org.djunits.value.vdouble.scalar.Acceleration;
12 import org.djunits.value.vdouble.scalar.Duration;
13 import org.djunits.value.vdouble.scalar.Length;
14 import org.djunits.value.vdouble.scalar.Speed;
15 import org.djutils.exceptions.Throw;
16 import org.opentrafficsim.base.DistancedObject;
17 import org.opentrafficsim.base.parameters.ParameterException;
18 import org.opentrafficsim.base.parameters.Parameters;
19 import org.opentrafficsim.core.gtu.GtuType;
20 import org.opentrafficsim.core.gtu.perception.EgoPerception;
21 import org.opentrafficsim.core.network.LateralDirectionality;
22 import org.opentrafficsim.core.network.route.Route;
23 import org.opentrafficsim.road.gtu.LaneBasedGtu;
24 import org.opentrafficsim.road.gtu.perception.LanePerception;
25 import org.opentrafficsim.road.gtu.perception.RelativeLane;
26 import org.opentrafficsim.road.gtu.perception.categories.InfrastructurePerception;
27 import org.opentrafficsim.road.gtu.perception.categories.neighbors.NeighborsPerception;
28 import org.opentrafficsim.road.gtu.tactical.following.CarFollowingModel;
29 import org.opentrafficsim.road.network.LanePosition;
30 import org.opentrafficsim.road.network.speed.SpeedLimits;
31
32
33
34
35
36
37
38
39
40
41
42
43 public class TacticalContextEgo implements TacticalContext
44 {
45
46
47 private final LaneBasedGtu gtu;
48
49
50 private LanePerception perception;
51
52
53 private Optional<Route> route;
54
55
56 private Speed desiredSpeed;
57
58
59 private Acceleration carFollowingAcceleration;
60
61
62 private Map<Class<?>, NavigableMap<Length, Object>> intents = new LinkedHashMap<>();
63
64
65 private Parameters parameters;
66
67
68 private CarFollowingModel carFollowingModel;
69
70
71 private SpeedLimits speedLimits;
72
73
74 private Length length;
75
76
77 private Length width;
78
79
80 private Speed speed;
81
82
83 private Speed maxSpeed;
84
85
86 private Acceleration acceleration;
87
88
89
90
91
92 public TacticalContextEgo(final LaneBasedGtu gtu)
93 {
94 this.gtu = gtu;
95 }
96
97
98
99
100
101
102
103 public LaneBasedGtu getUnsafeGtu()
104 {
105 return this.gtu;
106 }
107
108
109
110
111
112 public LanePerception getPerception()
113 {
114 if (this.perception == null)
115 {
116 this.perception = this.gtu.getTacticalPlanner().getPerception();
117 }
118 return this.perception;
119 }
120
121
122
123
124
125 public Duration getTime()
126 {
127 return getUnsafeGtu().getSimulator().getSimulatorTime();
128 }
129
130
131
132
133
134
135
136 public Acceleration getCarFollowingAcceleration() throws ParameterException
137 {
138 if (this.carFollowingAcceleration == null)
139 {
140 var leaders = getPerception().getPerceptionCategoryOptional(NeighborsPerception.class)
141 .orElseThrow(() -> new NoSuchElementException(
142 "To obtain car-following acceleration a neighbors perception category is required."))
143 .getLeaders(RelativeLane.CURRENT);
144 this.carFollowingAcceleration = getCarFollowingModel().followingAcceleration(getParameters(), getSpeed(),
145 getSpeedLimits(), getMaximumSpeed(), leaders);
146 }
147 return this.carFollowingAcceleration;
148 }
149
150
151
152
153
154 public LanePosition getPosition()
155 {
156 return getUnsafeGtu().getPosition();
157 }
158
159
160
161
162
163
164 public void addIntent(final Object intent, final Length distance)
165 {
166 this.intents.computeIfAbsent(intent.getClass(), (clazz) -> new TreeMap<>()).put(distance, intent);
167 }
168
169
170
171
172
173
174
175
176 @SuppressWarnings("unchecked")
177 public <T> Optional<DistancedObject<T>> getIntent(final Class<T> clazz)
178 {
179 Throw.whenNull(clazz, "clazz");
180 if (this.intents.containsKey(clazz))
181 {
182 Entry<Length, Object> entry = this.intents.get(clazz).firstEntry();
183 return Optional.of(new DistancedObject<>((T) entry.getValue(), entry.getKey()));
184 }
185 return Optional.empty();
186 }
187
188
189
190
191
192
193
194
195
196 public <T> Optional<DistancedObject<T>> getIntent(final Class<T> clazz, final Length horizon)
197 {
198 Throw.whenNull(horizon, "horizon");
199 Optional<DistancedObject<T>> intent = getIntent(clazz);
200 if (intent.isPresent() && intent.get().distance().le(horizon))
201 {
202 return intent;
203 }
204 return Optional.empty();
205 }
206
207 @Override
208 public String getId()
209 {
210 return getUnsafeGtu().getId();
211 }
212
213 @Override
214 public GtuType getGtuType()
215 {
216 return getUnsafeGtu().getType();
217 }
218
219 @Override
220 public Parameters getParameters()
221 {
222 if (this.parameters == null)
223 {
224 this.parameters = this.gtu.getParameters();
225 }
226 return this.parameters;
227 }
228
229 @Override
230 public CarFollowingModel getCarFollowingModel()
231 {
232 if (this.carFollowingModel == null)
233 {
234 this.carFollowingModel = this.gtu.getTacticalPlanner().getCarFollowingModel();
235 }
236 return this.carFollowingModel;
237 }
238
239 @Override
240 public SpeedLimits getSpeedLimits()
241 {
242 if (this.speedLimits == null)
243 {
244 this.speedLimits = getPerception().getPerceptionCategoryOptional(InfrastructurePerception.class)
245 .orElseThrow(() -> new NoSuchElementException("No infrastructure perception category."))
246 .getSpeedLimits(RelativeLane.CURRENT);
247 }
248 return this.speedLimits;
249 }
250
251 @Override
252 public Speed getDesiredSpeed() throws ParameterException
253 {
254 if (this.desiredSpeed == null)
255 {
256 this.desiredSpeed = getCarFollowingModel().desiredSpeed(getParameters(), getSpeedLimits(), getMaximumSpeed());
257 }
258 return this.desiredSpeed;
259 }
260
261 @Override
262 public Optional<Route> getRoute()
263 {
264 if (this.route == null)
265 {
266 this.route = getUnsafeGtu().getStrategicalPlanner().getRoute();
267 }
268 return this.route;
269 }
270
271 @Override
272 public LateralDirectionality getLaneChangeDirection()
273 {
274 return getUnsafeGtu().getLaneChangeDirection();
275 }
276
277 @Override
278 public Length getLength()
279 {
280 checkEgo();
281 return this.length;
282 }
283
284 @Override
285 public Length getWidth()
286 {
287 checkEgo();
288 return this.width;
289 }
290
291 @Override
292 public Speed getSpeed()
293 {
294 checkEgo();
295 return this.speed;
296 }
297
298 @Override
299 public Speed getMaximumSpeed()
300 {
301 checkEgo();
302 return this.maxSpeed;
303 }
304
305 @Override
306 public Acceleration getAcceleration()
307 {
308 checkEgo();
309 return this.acceleration;
310 }
311
312
313
314
315 private void checkEgo()
316 {
317 if (this.length == null)
318 {
319 EgoPerception<?, ?> ego = getPerception().getPerceptionCategoryOptional(EgoPerception.class)
320 .orElseThrow(() -> new NoSuchElementException("No ego perception category."));
321 this.length = ego.getLength();
322 this.width = ego.getWidth();
323 this.speed = ego.getSpeed();
324 this.maxSpeed = ego.getMaximumSpeed();
325 this.acceleration = ego.getAcceleration();
326 }
327 }
328
329 @Override
330 public String toString()
331 {
332 return "TacticalContextEgo [gtu=" + this.gtu.getId() + "]";
333 }
334
335 }