1 package org.opentrafficsim.core.gtu;
2
3 import java.awt.Color;
4
5 import org.djunits.unit.TimeUnit;
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.djunits.value.vdouble.scalar.Speed;
10 import org.djunits.value.vdouble.scalar.Time;
11 import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
12 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
13 import org.opentrafficsim.core.geometry.OTSGeometryException;
14 import org.opentrafficsim.core.geometry.OTSLine3D;
15 import org.opentrafficsim.core.geometry.OTSPoint3D;
16 import org.opentrafficsim.core.gtu.animation.IDGTUColorer;
17 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
18 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
19 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanBuilder;
20 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
21 import org.opentrafficsim.core.gtu.plan.strategical.StrategicalPlanner;
22 import org.opentrafficsim.core.gtu.plan.tactical.TacticalPlanner;
23 import org.opentrafficsim.core.idgenerator.IdGenerator;
24 import org.opentrafficsim.core.network.NetworkException;
25 import org.opentrafficsim.core.perception.PerceivableContext;
26
27 import nl.tudelft.simulation.dsol.SimRuntimeException;
28 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEvent;
29 import nl.tudelft.simulation.event.EventProducer;
30 import nl.tudelft.simulation.language.Throw;
31 import nl.tudelft.simulation.language.d3.DirectedPoint;
32
33
34
35
36
37
38
39
40
41
42
43
44 public abstract class AbstractGTU extends EventProducer implements GTU
45 {
46
47 private static final long serialVersionUID = 20140822L;
48
49
50 private final String id;
51
52
53 private final int uniqueNumber;
54
55
56 private static int staticUNIQUENUMBER = 0;
57
58
59 private final GTUType gtuType;
60
61
62 private final OTSDEVSSimulatorInterface simulator;
63
64
65 private Acceleration maximumAcceleration;
66
67
68 private Acceleration maximumDeceleration;
69
70
71
72
73
74
75 private Length odometer;
76
77
78 private StrategicalPlanner strategicalPlanner;
79
80
81 private TacticalPlanner tacticalPlanner = null;
82
83
84 private OperationalPlan operationalPlan = null;
85
86
87 private SimEvent<OTSSimTimeDouble> nextMoveEvent;
88
89
90 private PerceivableContext perceivableContext;
91
92
93 private TurnIndicatorStatus turnIndicatorStatus = TurnIndicatorStatus.NOTPRESENT;
94
95
96 private boolean destroyed = false;
97
98
99 private Color baseColor = null;
100
101
102
103
104
105
106
107
108 @SuppressWarnings("checkstyle:parameternumber")
109 public AbstractGTU(final String id, final GTUType gtuType, final OTSDEVSSimulatorInterface simulator,
110 final PerceivableContext perceivableContext) throws GTUException
111 {
112 Throw.when(id == null, GTUException.class, "id is null");
113 Throw.when(gtuType == null, GTUException.class, "gtuType is null");
114 Throw.when(gtuType.equals(GTUType.NONE), GTUException.class, "gtuType of an actual GTU cannot be GTUType.NONE");
115 Throw.when(gtuType.equals(GTUType.ALL), GTUException.class, "gtuType of an actual GTU cannot be GTUType.ALL");
116 Throw.when(perceivableContext == null, GTUException.class, "perceivableContext is null for GTU with id %s", id);
117 Throw.when(perceivableContext.containsGtuId(id), GTUException.class,
118 "GTU with id %s already registered in perceivableContext %s", id, perceivableContext.getId());
119 Throw.when(simulator == null, GTUException.class, "simulator is null for GTU with id %s", id);
120
121 this.id = id;
122 this.uniqueNumber = ++staticUNIQUENUMBER;
123 this.gtuType = gtuType;
124 this.simulator = simulator;
125 this.odometer = Length.ZERO;
126 this.perceivableContext = perceivableContext;
127 this.perceivableContext.addGTU(this);
128 }
129
130
131
132
133
134
135
136
137 @SuppressWarnings("checkstyle:parameternumber")
138 public AbstractGTU(final IdGenerator idGenerator, final GTUType gtuType, final OTSDEVSSimulatorInterface simulator,
139 final PerceivableContext perceivableContext) throws GTUException
140 {
141 this(generateId(idGenerator), gtuType, simulator, perceivableContext);
142 }
143
144
145
146
147
148
149
150
151
152
153
154 @SuppressWarnings({ "checkstyle:hiddenfield", "hiding" })
155 public final void init(final StrategicalPlanner strategicalPlanner, final DirectedPoint initialLocation,
156 final Speed initialSpeed) throws SimRuntimeException, GTUException
157 {
158 Throw.when(strategicalPlanner == null, GTUException.class, "strategicalPlanner is null for GTU with id %s", this.id);
159 Throw.whenNull(initialLocation, "Initial location of GTU cannot be null");
160 Throw.when(Double.isNaN(initialLocation.x) || Double.isNaN(initialLocation.y) || Double.isNaN(initialLocation.z),
161 GTUException.class, "initialLocation %s invalid for GTU with id %s", initialLocation, this.id);
162 Throw.when(initialSpeed == null, GTUException.class, "initialSpeed is null for GTU with id %s", this.id);
163 Throw.when(!getId().equals(strategicalPlanner.getGtu().getId()), GTUException.class,
164 "GTU %s is initialized with a strategical planner for GTU %s", getId(), strategicalPlanner.getGtu().getId());
165
166 this.strategicalPlanner = strategicalPlanner;
167 Time now = this.simulator.getSimulatorTime().getTime();
168
169
170 DirectedPoint p = initialLocation;
171 try
172 {
173 if (initialSpeed.si < OperationalPlan.DRIFTING_SPEED_SI)
174 {
175 this.operationalPlan = new OperationalPlan(this, p, now, new Duration(1E-6, TimeUnit.SECOND));
176 }
177 else
178 {
179 OTSPoint3D p2 = new OTSPoint3D(p.x + 1E-6 * Math.cos(p.getRotZ()), p.y + 1E-6 * Math.sin(p.getRotZ()), p.z);
180 OTSLine3D path = new OTSLine3D(new OTSPoint3D(p), p2);
181 this.operationalPlan = OperationalPlanBuilder.buildConstantSpeedPlan(this, path, now, initialSpeed);
182 }
183
184 fireTimedEvent(GTU.INIT_EVENT, new Object[] { getId(), initialLocation, getLength(), getWidth(), getBaseColor() },
185 now);
186
187
188 move(initialLocation);
189 }
190 catch (OperationalPlanException | OTSGeometryException | NetworkException | ParameterException exception)
191 {
192 throw new GTUException("Failed to create OperationalPlan for GTU " + this.id, exception);
193 }
194 }
195
196
197
198
199
200
201
202 private static String generateId(final IdGenerator idGenerator) throws GTUException
203 {
204 Throw.when(idGenerator == null, GTUException.class, "AbstractGTU.<init>: idGenerator is null");
205 return idGenerator.nextId();
206 }
207
208
209
210
211 @SuppressWarnings("checkstyle:designforextension")
212 public void destroy()
213 {
214 fireTimedEvent(GTU.DESTROY_EVENT, new Object[] { getId(), getLocation(), getOdometer() },
215 this.simulator.getSimulatorTime());
216
217
218 if (this.nextMoveEvent != null)
219 {
220 this.simulator.cancelEvent(this.nextMoveEvent);
221 this.nextMoveEvent = null;
222 }
223
224 this.perceivableContext.removeGTU(this);
225 this.destroyed = true;
226 }
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241 @SuppressWarnings("checkstyle:designforextension")
242 protected void move(final DirectedPoint fromLocation)
243 throws SimRuntimeException, OperationalPlanException, GTUException, NetworkException, ParameterException
244 {
245 Time now = this.simulator.getSimulatorTime().getTime();
246
247
248
249 if (this.operationalPlan != null)
250 {
251 this.odometer = this.odometer.plus(this.operationalPlan.getTraveledDistance(now));
252 }
253
254
255
256 if (this.tacticalPlanner == null)
257 {
258
259 this.tacticalPlanner = this.strategicalPlanner.generateTacticalPlanner();
260 }
261 this.operationalPlan = this.tacticalPlanner.generateOperationalPlan(now, fromLocation);
262
263
264
265 this.nextMoveEvent = new SimEvent<>(new OTSSimTimeDouble(now.plus(this.operationalPlan.getTotalDuration())), this, this,
266 "move", new Object[] { this.operationalPlan.getEndLocation() });
267 this.simulator.scheduleEvent(this.nextMoveEvent);
268
269 fireTimedEvent(GTU.MOVE_EVENT,
270 new Object[] { getId(), fromLocation, getSpeed(), getAcceleration(), getTurnIndicatorStatus(), getOdometer() },
271 this.simulator.getSimulatorTime());
272 }
273
274
275
276
277
278
279
280
281
282
283
284 @SuppressWarnings("checkstyle:designforextension")
285 protected void interruptMove()
286 throws SimRuntimeException, OperationalPlanException, GTUException, NetworkException, ParameterException
287 {
288 this.simulator.cancelEvent(this.nextMoveEvent);
289 move(this.operationalPlan.getLocation(this.simulator.getSimulatorTime().getTime()));
290 }
291
292
293 @Override
294 public final String getId()
295 {
296 return this.id;
297 }
298
299
300 @SuppressWarnings("checkstyle:designforextension")
301 @Override
302 public GTUType getGTUType()
303 {
304 return this.gtuType;
305 }
306
307
308 @Override
309 public final RelativePosition getReference()
310 {
311 return RelativePosition.REFERENCE_POSITION;
312 }
313
314
315
316
317 public final OTSDEVSSimulatorInterface getSimulator()
318 {
319 return this.simulator;
320 }
321
322
323
324
325
326 @SuppressWarnings("checkstyle:designforextension")
327 public StrategicalPlanner getStrategicalPlanner()
328 {
329 return this.strategicalPlanner;
330 }
331
332
333
334
335 @SuppressWarnings("checkstyle:designforextension")
336 public TacticalPlanner getTacticalPlanner()
337 {
338
339 if (null == this.tacticalPlanner)
340 {
341 this.tacticalPlanner = this.strategicalPlanner.generateTacticalPlanner();
342 }
343 return this.tacticalPlanner;
344 }
345
346
347
348
349 public final OperationalPlan getOperationalPlan()
350 {
351 return this.operationalPlan;
352 }
353
354
355 @Override
356 public final Length getOdometer()
357 {
358 if (this.operationalPlan == null)
359 {
360 return this.odometer;
361 }
362 try
363 {
364 return this.odometer.plus(this.operationalPlan.getTraveledDistance(this.simulator.getSimulatorTime().getTime()));
365 }
366 catch (OperationalPlanException ope)
367 {
368 return this.odometer;
369 }
370 }
371
372
373 @Override
374 public final Speed getSpeed()
375 {
376 if (this.operationalPlan == null)
377 {
378 return Speed.ZERO;
379 }
380 try
381 {
382 return this.operationalPlan.getSpeed(this.simulator.getSimulatorTime().getTime());
383 }
384 catch (OperationalPlanException ope)
385 {
386
387 throw new RuntimeException("getSpeed() could not derive a valid speed for the current operationalPlan", ope);
388 }
389 }
390
391
392 @Override
393 public final Acceleration getAcceleration()
394 {
395 if (this.operationalPlan == null)
396 {
397 return Acceleration.ZERO;
398 }
399 try
400 {
401 return this.operationalPlan.getAcceleration(this.simulator.getSimulatorTime().getTime());
402 }
403 catch (OperationalPlanException ope)
404 {
405
406 throw new RuntimeException(
407 "getAcceleration() could not derive a valid acceleration for the current operationalPlan", ope);
408 }
409 }
410
411
412
413
414 public final Acceleration getMaximumAcceleration()
415 {
416 return this.maximumAcceleration;
417 }
418
419
420
421
422 public final void setMaximumAcceleration(final Acceleration maximumAcceleration)
423 {
424 if (maximumAcceleration.le(Acceleration.ZERO))
425 {
426 throw new RuntimeException("Maximum acceleration of GTU " + this.id + " set to value <= 0");
427 }
428 this.maximumAcceleration = maximumAcceleration;
429 }
430
431
432
433
434 public final Acceleration getMaximumDeceleration()
435 {
436 return this.maximumDeceleration;
437 }
438
439
440
441
442 public final void setMaximumDeceleration(final Acceleration maximumDeceleration)
443 {
444 if (maximumDeceleration.ge(Acceleration.ZERO))
445 {
446 throw new RuntimeException("Maximum deceleration of GTU " + this.id + " set to value >= 0");
447 }
448 this.maximumDeceleration = maximumDeceleration;
449 }
450
451
452 @Override
453 @SuppressWarnings("checkstyle:designforextension")
454 public DirectedPoint getLocation()
455 {
456 if (this.operationalPlan == null)
457 {
458 System.err.println(
459 "No operational plan for GTU " + this.id + " at t=" + this.getSimulator().getSimulatorTime().getTime());
460 return new DirectedPoint(0, 0, 0);
461 }
462 try
463 {
464 return this.operationalPlan.getLocation(this.simulator.getSimulatorTime().getTime());
465 }
466 catch (OperationalPlanException exception)
467 {
468 return new DirectedPoint(0, 0, 0);
469 }
470 }
471
472
473 @Override
474 public final TurnIndicatorStatus getTurnIndicatorStatus()
475 {
476 return this.turnIndicatorStatus;
477 }
478
479
480 @Override
481 public final void setTurnIndicatorStatus(final TurnIndicatorStatus turnIndicatorStatus)
482 {
483 this.turnIndicatorStatus = turnIndicatorStatus;
484 }
485
486
487 @Override
488 @SuppressWarnings("checkstyle:designforextension")
489 public Color getBaseColor()
490 {
491 if (this.baseColor == null)
492 {
493 this.baseColor = IDGTUColorer.LEGEND.get(this.uniqueNumber % IDGTUColorer.LEGEND.size()).getColor();
494 }
495 return this.baseColor;
496 }
497
498
499
500
501 public final boolean isDestroyed()
502 {
503 return this.destroyed;
504 }
505
506
507
508
509 public final PerceivableContext getPerceivableContext()
510 {
511 return this.perceivableContext;
512 }
513
514 }