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 public static boolean ALIGNED = true;
104
105
106
107 public static int ALIGN_COUNT = 0;
108
109
110
111
112
113
114
115
116 @SuppressWarnings("checkstyle:parameternumber")
117 public AbstractGTU(final String id, final GTUType gtuType, final OTSDEVSSimulatorInterface simulator,
118 final PerceivableContext perceivableContext) throws GTUException
119 {
120 Throw.when(id == null, GTUException.class, "id is null");
121 Throw.when(gtuType == null, GTUException.class, "gtuType is null");
122 Throw.when(gtuType.equals(GTUType.NONE), GTUException.class, "gtuType of an actual GTU cannot be GTUType.NONE");
123 Throw.when(gtuType.equals(GTUType.ALL), GTUException.class, "gtuType of an actual GTU cannot be GTUType.ALL");
124 Throw.when(perceivableContext == null, GTUException.class, "perceivableContext is null for GTU with id %s", id);
125 Throw.when(perceivableContext.containsGtuId(id), GTUException.class,
126 "GTU with id %s already registered in perceivableContext %s", id, perceivableContext.getId());
127 Throw.when(simulator == null, GTUException.class, "simulator is null for GTU with id %s", id);
128
129 this.id = id;
130 this.uniqueNumber = ++staticUNIQUENUMBER;
131 this.gtuType = gtuType;
132 this.simulator = simulator;
133 this.odometer = Length.ZERO;
134 this.perceivableContext = perceivableContext;
135 this.perceivableContext.addGTU(this);
136 }
137
138
139
140
141
142
143
144
145 @SuppressWarnings("checkstyle:parameternumber")
146 public AbstractGTU(final IdGenerator idGenerator, final GTUType gtuType, final OTSDEVSSimulatorInterface simulator,
147 final PerceivableContext perceivableContext) throws GTUException
148 {
149 this(generateId(idGenerator), gtuType, simulator, perceivableContext);
150 }
151
152
153
154
155
156
157
158
159
160
161
162 @SuppressWarnings({ "checkstyle:hiddenfield", "hiding", "checkstyle:designforextension" })
163 public void init(final StrategicalPlanner strategicalPlanner, final DirectedPoint initialLocation, final Speed initialSpeed)
164 throws SimRuntimeException, GTUException
165 {
166 Throw.when(strategicalPlanner == null, GTUException.class, "strategicalPlanner is null for GTU with id %s", this.id);
167 Throw.whenNull(initialLocation, "Initial location of GTU cannot be null");
168 Throw.when(Double.isNaN(initialLocation.x) || Double.isNaN(initialLocation.y) || Double.isNaN(initialLocation.z),
169 GTUException.class, "initialLocation %s invalid for GTU with id %s", initialLocation, this.id);
170 Throw.when(initialSpeed == null, GTUException.class, "initialSpeed is null for GTU with id %s", this.id);
171 Throw.when(!getId().equals(strategicalPlanner.getGtu().getId()), GTUException.class,
172 "GTU %s is initialized with a strategical planner for GTU %s", getId(), strategicalPlanner.getGtu().getId());
173
174 this.strategicalPlanner = strategicalPlanner;
175 Time now = this.simulator.getSimulatorTime().getTime();
176
177
178 DirectedPoint p = initialLocation;
179 try
180 {
181 if (initialSpeed.si < OperationalPlan.DRIFTING_SPEED_SI)
182 {
183 this.operationalPlan = new OperationalPlan(this, p, now, new Duration(1E-6, TimeUnit.SECOND));
184 }
185 else
186 {
187 OTSPoint3D p2 = new OTSPoint3D(p.x + 1E-6 * Math.cos(p.getRotZ()), p.y + 1E-6 * Math.sin(p.getRotZ()), p.z);
188 OTSLine3D path = new OTSLine3D(new OTSPoint3D(p), p2);
189 this.operationalPlan = OperationalPlanBuilder.buildConstantSpeedPlan(this, path, now, initialSpeed);
190 }
191
192 fireTimedEvent(GTU.INIT_EVENT, new Object[] { getId(), initialLocation, getLength(), getWidth(), getBaseColor() },
193 now);
194
195
196 move(initialLocation);
197 }
198 catch (OperationalPlanException | OTSGeometryException | NetworkException | ParameterException exception)
199 {
200 throw new GTUException("Failed to create OperationalPlan for GTU " + this.id, exception);
201 }
202 }
203
204
205
206
207
208
209
210 private static String generateId(final IdGenerator idGenerator) throws GTUException
211 {
212 Throw.when(idGenerator == null, GTUException.class, "AbstractGTU.<init>: idGenerator is null");
213 return idGenerator.nextId();
214 }
215
216
217
218
219 @Override
220 @SuppressWarnings("checkstyle:designforextension")
221 public void destroy()
222 {
223 fireTimedEvent(GTU.DESTROY_EVENT, new Object[] { getId(), getLocation(), getOdometer() },
224 this.simulator.getSimulatorTime());
225
226
227 if (this.nextMoveEvent != null)
228 {
229 this.simulator.cancelEvent(this.nextMoveEvent);
230 this.nextMoveEvent = null;
231 }
232
233 this.perceivableContext.removeGTU(this);
234 this.destroyed = true;
235 }
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250 @SuppressWarnings("checkstyle:designforextension")
251 protected void move(final DirectedPoint fromLocation)
252 throws SimRuntimeException, OperationalPlanException, GTUException, NetworkException, ParameterException
253 {
254 Time now = this.simulator.getSimulatorTime().getTime();
255
256
257
258 Length currentOdometer;
259 if (this.operationalPlan != null)
260 {
261 currentOdometer = this.odometer.plus(this.operationalPlan.getTraveledDistance(now));
262 }
263 else
264 {
265 currentOdometer = this.odometer;
266 }
267
268
269
270 if (this.tacticalPlanner == null)
271 {
272
273 this.tacticalPlanner = this.strategicalPlanner.generateTacticalPlanner();
274 }
275 this.operationalPlan = this.tacticalPlanner.generateOperationalPlan(now, fromLocation);
276 this.odometer = currentOdometer;
277 if (getOperationalPlan().getAcceleration(Duration.ZERO).si < -10
278 && getOperationalPlan().getSpeed(Duration.ZERO).si > 2.5)
279 {
280 System.err.println("(getOperationalPlan().getAcceleration(Duration.ZERO).si < -10)");
281
282 }
283
284 if (ALIGNED && this.operationalPlan.getTotalDuration().si == 0.5)
285 {
286
287
288 double tNext = Math.floor(2.0 * now.si + 1.0) / 2.0;
289 DirectedPoint p = (tNext - now.si < 0.5) ? this.operationalPlan.getEndLocation()
290 : this.operationalPlan.getLocation(new Duration(tNext - now.si, TimeUnit.SI));
291 this.nextMoveEvent =
292 new SimEvent<>(new OTSSimTimeDouble(new Time(tNext, TimeUnit.SI)), this, this, "move", new Object[] { p });
293 ALIGN_COUNT++;
294 }
295 else
296 {
297
298
299 this.nextMoveEvent = new SimEvent<>(new OTSSimTimeDouble(now.plus(this.operationalPlan.getTotalDuration())), this,
300 this, "move", new Object[] { this.operationalPlan.getEndLocation() });
301 }
302 this.simulator.scheduleEvent(this.nextMoveEvent);
303
304 fireTimedEvent(GTU.MOVE_EVENT,
305 new Object[] { getId(), fromLocation, getSpeed(), getAcceleration(), getTurnIndicatorStatus(), getOdometer() },
306 this.simulator.getSimulatorTime());
307 }
308
309
310
311
312
313
314
315
316
317
318
319 @SuppressWarnings("checkstyle:designforextension")
320 protected void interruptMove()
321 throws SimRuntimeException, OperationalPlanException, GTUException, NetworkException, ParameterException
322 {
323 this.simulator.cancelEvent(this.nextMoveEvent);
324 move(this.operationalPlan.getLocation(this.simulator.getSimulatorTime().getTime()));
325 }
326
327
328 @Override
329 public final String getId()
330 {
331 return this.id;
332 }
333
334
335 @SuppressWarnings("checkstyle:designforextension")
336 @Override
337 public GTUType getGTUType()
338 {
339 return this.gtuType;
340 }
341
342
343 @Override
344 public final RelativePosition getReference()
345 {
346 return RelativePosition.REFERENCE_POSITION;
347 }
348
349
350
351
352 @Override
353 public final OTSDEVSSimulatorInterface getSimulator()
354 {
355 return this.simulator;
356 }
357
358
359
360
361
362 @Override
363 @SuppressWarnings("checkstyle:designforextension")
364 public StrategicalPlanner getStrategicalPlanner()
365 {
366 return this.strategicalPlanner;
367 }
368
369
370
371
372 @Override
373 @SuppressWarnings("checkstyle:designforextension")
374 public TacticalPlanner getTacticalPlanner()
375 {
376
377 if (null == this.tacticalPlanner)
378 {
379 this.tacticalPlanner = this.strategicalPlanner.generateTacticalPlanner();
380 }
381 return this.tacticalPlanner;
382 }
383
384
385
386
387 @Override
388 public final OperationalPlan getOperationalPlan()
389 {
390 return this.operationalPlan;
391 }
392
393
394 @Override
395 public final Length getOdometer()
396 {
397 if (this.operationalPlan == null)
398 {
399 return this.odometer;
400 }
401 try
402 {
403 return this.odometer.plus(this.operationalPlan.getTraveledDistance(this.simulator.getSimulatorTime().getTime()));
404 }
405 catch (OperationalPlanException ope)
406 {
407 return this.odometer;
408 }
409 }
410
411
412 @Override
413 public final Speed getSpeed()
414 {
415 if (this.operationalPlan == null)
416 {
417 return Speed.ZERO;
418 }
419 try
420 {
421 return this.operationalPlan.getSpeed(this.simulator.getSimulatorTime().getTime());
422 }
423 catch (OperationalPlanException ope)
424 {
425
426 System.err.println("t = " + this.simulator.getSimulatorTime().getTime());
427 System.err.println("op.validity = " + this.operationalPlan.getEndTime());
428 throw new RuntimeException("getSpeed() could not derive a valid speed for the current operationalPlan", ope);
429 }
430 }
431
432
433 @Override
434 public final Acceleration getAcceleration()
435 {
436 if (this.operationalPlan == null)
437 {
438 return Acceleration.ZERO;
439 }
440 try
441 {
442 return this.operationalPlan.getAcceleration(this.simulator.getSimulatorTime().getTime());
443 }
444 catch (OperationalPlanException ope)
445 {
446
447 throw new RuntimeException(
448 "getAcceleration() could not derive a valid acceleration for the current operationalPlan", ope);
449 }
450 }
451
452
453
454
455 @Override
456 public final Acceleration getMaximumAcceleration()
457 {
458 return this.maximumAcceleration;
459 }
460
461
462
463
464 public final void setMaximumAcceleration(final Acceleration maximumAcceleration)
465 {
466 if (maximumAcceleration.le(Acceleration.ZERO))
467 {
468 throw new RuntimeException("Maximum acceleration of GTU " + this.id + " set to value <= 0");
469 }
470 this.maximumAcceleration = maximumAcceleration;
471 }
472
473
474
475
476 @Override
477 public final Acceleration getMaximumDeceleration()
478 {
479 return this.maximumDeceleration;
480 }
481
482
483
484
485 public final void setMaximumDeceleration(final Acceleration maximumDeceleration)
486 {
487 if (maximumDeceleration.ge(Acceleration.ZERO))
488 {
489 throw new RuntimeException("Maximum deceleration of GTU " + this.id + " set to value >= 0");
490 }
491 this.maximumDeceleration = maximumDeceleration;
492 }
493
494
495 private Time cacheLocationTime = new Time(Double.NaN, TimeUnit.SI);
496
497
498 private DirectedPoint cacheLocation = null;
499
500
501 @Override
502 @SuppressWarnings("checkstyle:designforextension")
503 public DirectedPoint getLocation()
504 {
505 if (this.operationalPlan == null)
506 {
507 System.err.println(
508 "No operational plan for GTU " + this.id + " at t=" + this.getSimulator().getSimulatorTime().getTime());
509 return new DirectedPoint(0, 0, 0);
510 }
511 try
512 {
513
514 if (this.cacheLocationTime.si != this.simulator.getSimulatorTime().getTime().si)
515 {
516 this.cacheLocationTime = this.simulator.getSimulatorTime().getTime();
517 this.cacheLocation = this.operationalPlan.getLocation(this.cacheLocationTime);
518 }
519 return this.cacheLocation;
520 }
521 catch (OperationalPlanException exception)
522 {
523 return new DirectedPoint(0, 0, 0);
524 }
525 }
526
527
528 @Override
529 public final TurnIndicatorStatus getTurnIndicatorStatus()
530 {
531 return this.turnIndicatorStatus;
532 }
533
534
535 @Override
536 public final void setTurnIndicatorStatus(final TurnIndicatorStatus turnIndicatorStatus)
537 {
538 this.turnIndicatorStatus = turnIndicatorStatus;
539 }
540
541
542 @Override
543 @SuppressWarnings("checkstyle:designforextension")
544 public Color getBaseColor()
545 {
546 if (this.baseColor == null)
547 {
548 this.baseColor = IDGTUColorer.LEGEND.get(this.uniqueNumber % IDGTUColorer.LEGEND.size()).getColor();
549 }
550 return this.baseColor;
551 }
552
553
554
555
556 public final boolean isDestroyed()
557 {
558 return this.destroyed;
559 }
560
561
562
563
564 public final PerceivableContext getPerceivableContext()
565 {
566 return this.perceivableContext;
567 }
568
569
570 @Override
571 @SuppressWarnings("designforextension")
572 public int hashCode()
573 {
574 final int prime = 31;
575 int result = 1;
576 result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
577 result = prime * result + this.uniqueNumber;
578 return result;
579 }
580
581
582 @Override
583 @SuppressWarnings({ "designforextension", "needbraces" })
584 public boolean equals(final Object obj)
585 {
586 if (this == obj)
587 return true;
588 if (obj == null)
589 return false;
590 if (getClass() != obj.getClass())
591 return false;
592 AbstractGTU other = (AbstractGTU) obj;
593 if (this.id == null)
594 {
595 if (other.id != null)
596 return false;
597 }
598 else if (!this.id.equals(other.id))
599 return false;
600 if (this.uniqueNumber != other.uniqueNumber)
601 return false;
602 return true;
603 }
604
605 }