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", "checkstyle:designforextension" })
155 public 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 @Override
212 @SuppressWarnings("checkstyle:designforextension")
213 public void destroy()
214 {
215 fireTimedEvent(GTU.DESTROY_EVENT, new Object[] { getId(), getLocation(), getOdometer() },
216 this.simulator.getSimulatorTime());
217
218
219 if (this.nextMoveEvent != null)
220 {
221 this.simulator.cancelEvent(this.nextMoveEvent);
222 this.nextMoveEvent = null;
223 }
224
225 this.perceivableContext.removeGTU(this);
226 this.destroyed = true;
227 }
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242 @SuppressWarnings("checkstyle:designforextension")
243 protected void move(final DirectedPoint fromLocation)
244 throws SimRuntimeException, OperationalPlanException, GTUException, NetworkException, ParameterException
245 {
246 Time now = this.simulator.getSimulatorTime().getTime();
247
248
249
250 if (this.operationalPlan != null)
251 {
252 this.odometer = this.odometer.plus(this.operationalPlan.getTraveledDistance(now));
253 }
254
255
256
257 if (this.tacticalPlanner == null)
258 {
259
260 this.tacticalPlanner = this.strategicalPlanner.generateTacticalPlanner();
261 }
262 this.operationalPlan = this.tacticalPlanner.generateOperationalPlan(now, fromLocation);
263 if (getOperationalPlan().getAcceleration(Duration.ZERO).si < -10
264 && getOperationalPlan().getSpeed(Duration.ZERO).si > 2.5)
265 {
266 System.err.println("(getOperationalPlan().getAcceleration(Duration.ZERO).si < -10)");
267
268 }
269
270
271
272 this.nextMoveEvent = new SimEvent<>(new OTSSimTimeDouble(now.plus(this.operationalPlan.getTotalDuration())), this, this,
273 "move", new Object[] { this.operationalPlan.getEndLocation() });
274 this.simulator.scheduleEvent(this.nextMoveEvent);
275
276 fireTimedEvent(GTU.MOVE_EVENT,
277 new Object[] { getId(), fromLocation, getSpeed(), getAcceleration(), getTurnIndicatorStatus(), getOdometer() },
278 this.simulator.getSimulatorTime());
279 }
280
281
282
283
284
285
286
287
288
289
290
291 @SuppressWarnings("checkstyle:designforextension")
292 protected void interruptMove()
293 throws SimRuntimeException, OperationalPlanException, GTUException, NetworkException, ParameterException
294 {
295 this.simulator.cancelEvent(this.nextMoveEvent);
296 move(this.operationalPlan.getLocation(this.simulator.getSimulatorTime().getTime()));
297 }
298
299
300 @Override
301 public final String getId()
302 {
303 return this.id;
304 }
305
306
307 @SuppressWarnings("checkstyle:designforextension")
308 @Override
309 public GTUType getGTUType()
310 {
311 return this.gtuType;
312 }
313
314
315 @Override
316 public final RelativePosition getReference()
317 {
318 return RelativePosition.REFERENCE_POSITION;
319 }
320
321
322
323
324 @Override
325 public final OTSDEVSSimulatorInterface getSimulator()
326 {
327 return this.simulator;
328 }
329
330
331
332
333
334 @Override
335 @SuppressWarnings("checkstyle:designforextension")
336 public StrategicalPlanner getStrategicalPlanner()
337 {
338 return this.strategicalPlanner;
339 }
340
341
342
343
344 @Override
345 @SuppressWarnings("checkstyle:designforextension")
346 public TacticalPlanner getTacticalPlanner()
347 {
348
349 if (null == this.tacticalPlanner)
350 {
351 this.tacticalPlanner = this.strategicalPlanner.generateTacticalPlanner();
352 }
353 return this.tacticalPlanner;
354 }
355
356
357
358
359 @Override
360 public final OperationalPlan getOperationalPlan()
361 {
362 return this.operationalPlan;
363 }
364
365
366 @Override
367 public final Length getOdometer()
368 {
369 if (this.operationalPlan == null)
370 {
371 return this.odometer;
372 }
373 try
374 {
375 return this.odometer.plus(this.operationalPlan.getTraveledDistance(this.simulator.getSimulatorTime().getTime()));
376 }
377 catch (OperationalPlanException ope)
378 {
379 return this.odometer;
380 }
381 }
382
383
384 @Override
385 public final Speed getSpeed()
386 {
387 if (this.operationalPlan == null)
388 {
389 return Speed.ZERO;
390 }
391 try
392 {
393 return this.operationalPlan.getSpeed(this.simulator.getSimulatorTime().getTime());
394 }
395 catch (OperationalPlanException ope)
396 {
397
398 throw new RuntimeException("getSpeed() could not derive a valid speed for the current operationalPlan", ope);
399 }
400 }
401
402
403 @Override
404 public final Acceleration getAcceleration()
405 {
406 if (this.operationalPlan == null)
407 {
408 return Acceleration.ZERO;
409 }
410 try
411 {
412 return this.operationalPlan.getAcceleration(this.simulator.getSimulatorTime().getTime());
413 }
414 catch (OperationalPlanException ope)
415 {
416
417 throw new RuntimeException(
418 "getAcceleration() could not derive a valid acceleration for the current operationalPlan", ope);
419 }
420 }
421
422
423
424
425 @Override
426 public final Acceleration getMaximumAcceleration()
427 {
428 return this.maximumAcceleration;
429 }
430
431
432
433
434 public final void setMaximumAcceleration(final Acceleration maximumAcceleration)
435 {
436 if (maximumAcceleration.le(Acceleration.ZERO))
437 {
438 throw new RuntimeException("Maximum acceleration of GTU " + this.id + " set to value <= 0");
439 }
440 this.maximumAcceleration = maximumAcceleration;
441 }
442
443
444
445
446 @Override
447 public final Acceleration getMaximumDeceleration()
448 {
449 return this.maximumDeceleration;
450 }
451
452
453
454
455 public final void setMaximumDeceleration(final Acceleration maximumDeceleration)
456 {
457 if (maximumDeceleration.ge(Acceleration.ZERO))
458 {
459 throw new RuntimeException("Maximum deceleration of GTU " + this.id + " set to value >= 0");
460 }
461 this.maximumDeceleration = maximumDeceleration;
462 }
463
464
465 @Override
466 @SuppressWarnings("checkstyle:designforextension")
467 public DirectedPoint getLocation()
468 {
469 if (this.operationalPlan == null)
470 {
471 System.err.println(
472 "No operational plan for GTU " + this.id + " at t=" + this.getSimulator().getSimulatorTime().getTime());
473 return new DirectedPoint(0, 0, 0);
474 }
475 try
476 {
477 return this.operationalPlan.getLocation(this.simulator.getSimulatorTime().getTime());
478 }
479 catch (OperationalPlanException exception)
480 {
481 return new DirectedPoint(0, 0, 0);
482 }
483 }
484
485
486 @Override
487 public final TurnIndicatorStatus getTurnIndicatorStatus()
488 {
489 return this.turnIndicatorStatus;
490 }
491
492
493 @Override
494 public final void setTurnIndicatorStatus(final TurnIndicatorStatus turnIndicatorStatus)
495 {
496 this.turnIndicatorStatus = turnIndicatorStatus;
497 }
498
499
500 @Override
501 @SuppressWarnings("checkstyle:designforextension")
502 public Color getBaseColor()
503 {
504 if (this.baseColor == null)
505 {
506 this.baseColor = IDGTUColorer.LEGEND.get(this.uniqueNumber % IDGTUColorer.LEGEND.size()).getColor();
507 }
508 return this.baseColor;
509 }
510
511
512
513
514 public final boolean isDestroyed()
515 {
516 return this.destroyed;
517 }
518
519
520
521
522 public final PerceivableContext getPerceivableContext()
523 {
524 return this.perceivableContext;
525 }
526
527
528 @Override
529 @SuppressWarnings("designforextension")
530 public int hashCode()
531 {
532 final int prime = 31;
533 int result = 1;
534 result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
535 result = prime * result + this.uniqueNumber;
536 return result;
537 }
538
539
540 @Override
541 @SuppressWarnings({"designforextension", "needbraces"})
542 public boolean equals(final Object obj)
543 {
544 if (this == obj)
545 return true;
546 if (obj == null)
547 return false;
548 if (getClass() != obj.getClass())
549 return false;
550 AbstractGTU other = (AbstractGTU) obj;
551 if (this.id == null)
552 {
553 if (other.id != null)
554 return false;
555 }
556 else if (!this.id.equals(other.id))
557 return false;
558 if (this.uniqueNumber != other.uniqueNumber)
559 return false;
560 return true;
561 }
562
563 }