1 package org.opentrafficsim.core.gtu;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.LinkedHashMap;
6 import java.util.LinkedHashSet;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Set;
10
11 import org.djunits.unit.DirectionUnit;
12 import org.djunits.unit.DurationUnit;
13 import org.djunits.unit.PositionUnit;
14 import org.djunits.unit.TimeUnit;
15 import org.djunits.value.vdouble.scalar.Acceleration;
16 import org.djunits.value.vdouble.scalar.Direction;
17 import org.djunits.value.vdouble.scalar.Duration;
18 import org.djunits.value.vdouble.scalar.Length;
19 import org.djunits.value.vdouble.scalar.Speed;
20 import org.djunits.value.vdouble.scalar.Time;
21 import org.djunits.value.vdouble.vector.PositionVector;
22 import org.djutils.base.Identifiable;
23 import org.djutils.draw.bounds.Bounds2d;
24 import org.djutils.draw.line.Polygon2d;
25 import org.djutils.draw.point.OrientedPoint2d;
26 import org.djutils.draw.point.Point2d;
27 import org.djutils.event.EventType;
28 import org.djutils.event.LocalEventProducer;
29 import org.djutils.exceptions.Throw;
30 import org.djutils.exceptions.Try;
31 import org.djutils.immutablecollections.Immutable;
32 import org.djutils.immutablecollections.ImmutableLinkedHashMap;
33 import org.djutils.immutablecollections.ImmutableMap;
34 import org.djutils.metadata.MetaData;
35 import org.djutils.metadata.ObjectDescriptor;
36 import org.opentrafficsim.base.HierarchicallyTyped;
37 import org.opentrafficsim.base.geometry.DynamicSpatialObject;
38 import org.opentrafficsim.base.geometry.OffsetRectangleShape;
39 import org.opentrafficsim.base.geometry.OtsLine2d;
40 import org.opentrafficsim.base.geometry.OtsLocatable;
41 import org.opentrafficsim.base.geometry.OtsShape;
42 import org.opentrafficsim.base.parameters.ParameterException;
43 import org.opentrafficsim.base.parameters.Parameters;
44 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
45 import org.opentrafficsim.core.gtu.RelativePosition.Type;
46 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
47 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
48 import org.opentrafficsim.core.gtu.plan.strategical.StrategicalPlanner;
49 import org.opentrafficsim.core.gtu.plan.tactical.TacticalPlanner;
50 import org.opentrafficsim.core.network.NetworkException;
51 import org.opentrafficsim.core.perception.Historical;
52 import org.opentrafficsim.core.perception.HistoricalValue;
53 import org.opentrafficsim.core.perception.HistoryManager;
54 import org.opentrafficsim.core.perception.PerceivableContext;
55
56 import nl.tudelft.simulation.dsol.SimRuntimeException;
57 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEvent;
58
59
60
61
62
63
64
65
66
67
68 public class Gtu extends LocalEventProducer
69 implements HierarchicallyTyped<GtuType, Gtu>, DynamicSpatialObject, OtsLocatable, Serializable, Identifiable
70 {
71
72 private static final long serialVersionUID = 20140822L;
73
74
75 private final String id;
76
77
78 private final int uniqueNumber;
79
80
81 private static int staticUNIQUENUMBER = 0;
82
83
84 private final GtuType gtuType;
85
86
87 private final OtsSimulatorInterface simulator;
88
89
90 private Parameters parameters;
91
92
93 private Acceleration maximumAcceleration;
94
95
96 private Acceleration maximumDeceleration;
97
98
99
100
101
102
103 private Historical<Length> odometer;
104
105
106 private final Historical<StrategicalPlanner> strategicalPlanner;
107
108
109 private final Historical<TacticalPlanner<?, ?>> tacticalPlanner;
110
111
112 private final Historical<OperationalPlan> operationalPlan;
113
114
115 private SimEvent<Duration> nextMoveEvent;
116
117
118 private PerceivableContext perceivableContext;
119
120
121 private boolean destroyed = false;
122
123
124
125 public static boolean ALIGNED = true;
126
127
128
129 public static int ALIGN_COUNT = 0;
130
131
132 private double cachedSpeedTime = Double.NaN;
133
134
135 private Speed cachedSpeed = null;
136
137
138 private double cachedAccelerationTime = Double.NaN;
139
140
141 private Acceleration cachedAcceleration = null;
142
143
144 private Gtu parent = null;
145
146
147 private Set<Gtu> children = new LinkedHashSet<>();
148
149
150 private GtuErrorHandler errorHandler = GtuErrorHandler.THROW;
151
152
153 private Polygon2d contour;
154
155
156 private final OtsShape shape;
157
158
159 private final Map<RelativePosition.Type, RelativePosition> relativePositions = new LinkedHashMap<>();
160
161
162 private final RelativePosition frontPos;
163
164
165 private final RelativePosition rearPos;
166
167
168 private final Set<RelativePosition> contourPoints = new LinkedHashSet<>();
169
170
171 private final Length length;
172
173
174 private final Length width;
175
176
177 private final Speed maximumSpeed;
178
179
180 private final Map<String, String> tags = new LinkedHashMap<>();
181
182
183 private Bounds2d bounds;
184
185
186
187
188
189
190
191
192
193
194
195
196
197 @SuppressWarnings("checkstyle:parameternumber")
198 public Gtu(final String id, final GtuType gtuType, final OtsSimulatorInterface simulator,
199 final PerceivableContext perceivableContext, final Length length, final Length width, final Speed maximumSpeed,
200 final Length front, final Length centerOfGravity) throws GtuException
201 {
202 Throw.when(id == null, GtuException.class, "id is null");
203 Throw.when(gtuType == null, GtuException.class, "gtuType is null");
204 Throw.when(perceivableContext == null, GtuException.class, "perceivableContext is null for GTU with id %s", id);
205 Throw.when(perceivableContext.containsGtuId(id), GtuException.class,
206 "GTU with id %s already registered in perceivableContext %s", id, perceivableContext.getId());
207 Throw.when(simulator == null, GtuException.class, "simulator is null for GTU with id %s", id);
208
209 this.length = length;
210 this.width = width;
211 if (null == maximumSpeed)
212 {
213 throw new GtuException("maximumSpeed may not be null");
214 }
215 this.maximumSpeed = maximumSpeed;
216
217 HistoryManager historyManager = simulator.getReplication().getHistoryManager(simulator);
218 this.id = id;
219 this.uniqueNumber = ++staticUNIQUENUMBER;
220 this.gtuType = gtuType;
221 this.simulator = simulator;
222 this.odometer = new HistoricalValue<>(historyManager, this, Length.ZERO);
223 this.perceivableContext = perceivableContext;
224 this.perceivableContext.addGTU(this);
225 this.strategicalPlanner = new HistoricalValue<>(historyManager, this);
226 this.tacticalPlanner = new HistoricalValue<>(historyManager, this, null);
227 this.operationalPlan = new HistoricalValue<>(historyManager, this, null);
228
229
230 Length dy2 = width.times(0.5);
231 this.frontPos = new RelativePosition(front, Length.ZERO, Length.ZERO, RelativePosition.FRONT);
232 this.relativePositions.put(RelativePosition.FRONT, this.frontPos);
233 this.rearPos = new RelativePosition(front.minus(length), Length.ZERO, Length.ZERO, RelativePosition.REAR);
234 this.relativePositions.put(RelativePosition.REAR, this.rearPos);
235 this.relativePositions.put(RelativePosition.REFERENCE, RelativePosition.REFERENCE_POSITION);
236 this.relativePositions.put(RelativePosition.CENTER,
237 new RelativePosition(Length.ZERO, Length.ZERO, Length.ZERO, RelativePosition.CENTER));
238 this.bounds = new Bounds2d(front.minus(length).si, front.si, -width.si / 2.0, width.si / 2.0);
239 this.shape = new OffsetRectangleShape(front.minus(length).si, front.si, -width.si / 2.0, width.si / 2.0);
240
241
242 for (int i = -1; i <= 1; i += 2)
243 {
244 Length x = i < 0 ? front.minus(length) : front;
245 for (int j = -1; j <= 1; j += 2)
246 {
247 this.contourPoints.add(new RelativePosition(x, dy2.times(j), Length.ZERO, RelativePosition.CONTOUR));
248 }
249 }
250 }
251
252
253
254
255
256
257
258
259
260
261
262 @SuppressWarnings({"checkstyle:hiddenfield", "hiding", "checkstyle:designforextension"})
263 public void init(final StrategicalPlanner strategicalPlanner, final OrientedPoint2d initialLocation,
264 final Speed initialSpeed) throws SimRuntimeException, GtuException
265 {
266 Throw.when(strategicalPlanner == null, GtuException.class, "strategicalPlanner is null for GTU with id %s", this.id);
267 Throw.whenNull(initialLocation, "Initial location of GTU cannot be null");
268 Throw.when(Double.isNaN(initialLocation.x) || Double.isNaN(initialLocation.y), GtuException.class,
269 "initialLocation %s invalid for GTU with id %s", initialLocation, this.id);
270 Throw.when(initialSpeed == null, GtuException.class, "initialSpeed is null for GTU with id %s", this.id);
271 Throw.when(!getId().equals(strategicalPlanner.getGtu().getId()), GtuException.class,
272 "GTU %s is initialized with a strategical planner for GTU %s", getId(), strategicalPlanner.getGtu().getId());
273
274 this.strategicalPlanner.set(strategicalPlanner);
275 this.tacticalPlanner.set(strategicalPlanner.getTacticalPlanner());
276
277 try
278 {
279 move(initialLocation);
280 }
281 catch (OperationalPlanException | NetworkException | ParameterException exception)
282 {
283 throw new GtuException("Failed to create OperationalPlan for GTU " + this.id, exception);
284 }
285 }
286
287
288 public final RelativePosition getFront()
289 {
290 return this.frontPos;
291 }
292
293
294 public final RelativePosition getRear()
295 {
296 return this.rearPos;
297 }
298
299
300 public final RelativePosition getCenter()
301 {
302 return this.relativePositions.get(RelativePosition.CENTER);
303 }
304
305
306 public final ImmutableMap<Type, RelativePosition> getRelativePositions()
307 {
308 return new ImmutableLinkedHashMap<>(this.relativePositions, Immutable.WRAP);
309 }
310
311
312 public final Length getLength()
313 {
314 return this.length;
315 }
316
317
318 public final Length getWidth()
319 {
320 return this.width;
321 }
322
323
324 public final Speed getMaximumSpeed()
325 {
326 return this.maximumSpeed;
327 }
328
329 @Override
330 public final Bounds2d getBounds()
331 {
332 return this.bounds;
333 }
334
335
336
337
338 @SuppressWarnings("checkstyle:designforextension")
339 public void destroy()
340 {
341 OrientedPoint2d location = getLocation();
342 fireTimedEvent(Gtu.DESTROY_EVENT,
343 new Object[] {getId(), new PositionVector(new double[] {location.x, location.y}, PositionUnit.METER),
344 new Direction(location.getDirZ(), DirectionUnit.EAST_RADIAN), getOdometer()},
345 this.simulator.getSimulatorTime());
346
347
348 if (this.nextMoveEvent != null)
349 {
350 this.simulator.cancelEvent(this.nextMoveEvent);
351 this.nextMoveEvent = null;
352 }
353
354 this.perceivableContext.removeGTU(this);
355 this.destroyed = true;
356 }
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371 @SuppressWarnings("checkstyle:designforextension")
372 protected boolean move(final OrientedPoint2d fromLocation)
373 throws SimRuntimeException, GtuException, NetworkException, ParameterException
374 {
375 try
376 {
377 Time now = this.simulator.getSimulatorAbsTime();
378
379
380
381 Length currentOdometer;
382 if (this.operationalPlan.get() != null)
383 {
384 currentOdometer = this.odometer.get().plus(this.operationalPlan.get().getTraveledDistance(now));
385 }
386 else
387 {
388 currentOdometer = this.odometer.get();
389 }
390
391
392
393 TacticalPlanner<?, ?> tactPlanner = this.tacticalPlanner.get();
394 if (tactPlanner == null)
395 {
396
397 tactPlanner = this.strategicalPlanner.get().getTacticalPlanner();
398 this.tacticalPlanner.set(tactPlanner);
399 }
400 synchronized (this)
401 {
402 tactPlanner.getPerception().perceive();
403 }
404 OperationalPlan newOperationalPlan = tactPlanner.generateOperationalPlan(now, fromLocation);
405 synchronized (this)
406 {
407 this.operationalPlan.set(newOperationalPlan);
408 this.cachedSpeedTime = Double.NaN;
409 this.cachedAccelerationTime = Double.NaN;
410 this.odometer.set(currentOdometer);
411 }
412
413
414 if (ALIGNED && newOperationalPlan.getTotalDuration().si == 0.5)
415 {
416
417
418 double tNext = Math.floor(2.0 * now.si + 1.0) / 2.0;
419 OrientedPoint2d p = (tNext - now.si < 0.5) ? newOperationalPlan.getEndLocation()
420 : newOperationalPlan.getLocation(new Duration(tNext - now.si, DurationUnit.SI));
421 this.nextMoveEvent =
422 new SimEvent<Duration>(new Duration(tNext - getSimulator().getStartTimeAbs().si, DurationUnit.SI), this,
423 "move", new Object[] {p});
424 ALIGN_COUNT++;
425 }
426 else
427 {
428
429
430 this.nextMoveEvent =
431 new SimEvent<>(now.plus(newOperationalPlan.getTotalDuration()).minus(getSimulator().getStartTimeAbs()),
432 this, "move", new Object[] {newOperationalPlan.getEndLocation()});
433 }
434 this.simulator.scheduleEvent(this.nextMoveEvent);
435 fireTimedEvent(Gtu.MOVE_EVENT,
436 new Object[] {getId(),
437 new PositionVector(new double[] {fromLocation.x, fromLocation.y}, PositionUnit.METER),
438 new Direction(fromLocation.getDirZ(), DirectionUnit.EAST_RADIAN), getSpeed(), getAcceleration(),
439 getOdometer()},
440 this.simulator.getSimulatorTime());
441
442 return false;
443 }
444 catch (Exception ex)
445 {
446 try
447 {
448 this.errorHandler.handle(this, ex);
449 }
450 catch (Exception exception)
451 {
452 throw new GtuException(exception);
453 }
454 return true;
455 }
456 }
457
458
459
460
461
462
463
464
465
466 @SuppressWarnings("checkstyle:designforextension")
467 protected void interruptMove() throws SimRuntimeException, GtuException, NetworkException, ParameterException
468 {
469 this.simulator.cancelEvent(this.nextMoveEvent);
470 move(this.operationalPlan.get().getLocation(this.simulator.getSimulatorAbsTime()));
471 }
472
473
474 @Override
475 public final String getId()
476 {
477 return this.id;
478 }
479
480
481
482
483
484
485 public void setTag(final String tag, final String value)
486 {
487 this.tags.put(tag, value);
488 }
489
490
491
492
493
494
495 public String getTag(final String tag)
496 {
497 return this.tags.get(tag);
498 }
499
500 @SuppressWarnings("checkstyle:designforextension")
501 @Override
502 public GtuType getType()
503 {
504 return this.gtuType;
505 }
506
507
508 public final RelativePosition getReference()
509 {
510 return RelativePosition.REFERENCE_POSITION;
511 }
512
513
514 public final OtsSimulatorInterface getSimulator()
515 {
516 return this.simulator;
517 }
518
519
520 public final Parameters getParameters()
521 {
522 return this.parameters;
523 }
524
525
526 public final void setParameters(final Parameters parameters)
527 {
528 this.parameters = parameters;
529 }
530
531
532
533
534
535 public StrategicalPlanner getStrategicalPlanner()
536 {
537 return this.strategicalPlanner.get();
538 }
539
540
541
542
543
544
545 public StrategicalPlanner getStrategicalPlanner(final Time time)
546 {
547 return this.strategicalPlanner.get(time);
548 }
549
550
551 public TacticalPlanner<?, ?> getTacticalPlanner()
552 {
553 return getStrategicalPlanner().getTacticalPlanner();
554 }
555
556
557
558
559
560 public TacticalPlanner<?, ?> getTacticalPlanner(final Time time)
561 {
562 return getStrategicalPlanner(time).getTacticalPlanner(time);
563 }
564
565
566 public final OperationalPlan getOperationalPlan()
567 {
568 return this.operationalPlan.get();
569 }
570
571
572
573
574
575 public final OperationalPlan getOperationalPlan(final Time time)
576 {
577 return this.operationalPlan.get(time);
578 }
579
580
581
582
583
584 protected void setOperationalPlan(final OperationalPlan operationalPlan)
585 {
586 this.operationalPlan.set(operationalPlan);
587 }
588
589
590
591
592 public final Length getOdometer()
593 {
594 return getOdometer(this.simulator.getSimulatorAbsTime());
595 }
596
597
598
599
600
601 public final Length getOdometer(final Time time)
602 {
603 synchronized (this)
604 {
605 if (getOperationalPlan(time) == null)
606 {
607 return this.odometer.get(time);
608 }
609 try
610 {
611 return this.odometer.get(time).plus(getOperationalPlan(time).getTraveledDistance(time));
612 }
613 catch (OperationalPlanException ope)
614 {
615 return this.odometer.get(time);
616 }
617 }
618 }
619
620
621 public final Speed getSpeed()
622 {
623 synchronized (this)
624 {
625 return getSpeed(this.simulator.getSimulatorAbsTime());
626 }
627 }
628
629
630
631
632
633 public final Speed getSpeed(final Time time)
634 {
635 synchronized (this)
636 {
637 if (this.cachedSpeedTime != time.si)
638 {
639
640 this.cachedSpeedTime = Double.NaN;
641 this.cachedSpeed = null;
642 OperationalPlan plan = getOperationalPlan(time);
643 if (plan == null)
644 {
645 this.cachedSpeed = Speed.ZERO;
646 }
647 else if (time.si < plan.getStartTime().si)
648 {
649 this.cachedSpeed = plan.getStartSpeed();
650 }
651 else if (time.si > plan.getEndTime().si)
652 {
653 if (time.si - plan.getEndTime().si < 1e-6)
654 {
655 this.cachedSpeed = Try.assign(() -> plan.getSpeed(plan.getEndTime()),
656 "getSpeed() could not derive a valid speed for the current operationalPlan");
657 }
658 else
659 {
660 throw new IllegalStateException("Requesting speed value beyond plan.");
661 }
662 }
663 else
664 {
665 this.cachedSpeed = Try.assign(() -> plan.getSpeed(time),
666 "getSpeed() could not derive a valid speed for the current operationalPlan");
667 }
668 this.cachedSpeedTime = time.si;
669 }
670 return this.cachedSpeed;
671 }
672 }
673
674
675 public final Acceleration getAcceleration()
676 {
677 synchronized (this)
678 {
679 return getAcceleration(this.simulator.getSimulatorAbsTime());
680 }
681 }
682
683
684
685
686
687 public final Acceleration getAcceleration(final Time time)
688 {
689 synchronized (this)
690 {
691 if (this.cachedAccelerationTime != time.si)
692 {
693
694 this.cachedAccelerationTime = Double.NaN;
695 this.cachedAcceleration = null;
696 OperationalPlan plan = getOperationalPlan(time);
697 if (plan == null)
698 {
699 this.cachedAcceleration = Acceleration.ZERO;
700 }
701 else if (time.si < plan.getStartTime().si)
702 {
703 this.cachedAcceleration =
704 Try.assign(() -> plan.getAcceleration(plan.getStartTime()), "Exception obtaining acceleration.");
705 }
706 else if (time.si > plan.getEndTime().si)
707 {
708 if (time.si - plan.getEndTime().si < 1e-6)
709 {
710 this.cachedAcceleration = Try.assign(() -> plan.getAcceleration(plan.getEndTime()),
711 "getAcceleration() could not derive a valid acceleration for the current operationalPlan");
712 }
713 else
714 {
715 throw new IllegalStateException("Requesting acceleration value beyond plan.");
716 }
717 }
718 else
719 {
720 this.cachedAcceleration = Try.assign(() -> plan.getAcceleration(time),
721 "getAcceleration() could not derive a valid acceleration for the current operationalPlan");
722 }
723 this.cachedAccelerationTime = time.si;
724 }
725 return this.cachedAcceleration;
726 }
727 }
728
729
730
731
732 public final Acceleration getMaximumAcceleration()
733 {
734 return this.maximumAcceleration;
735 }
736
737
738
739
740 public final void setMaximumAcceleration(final Acceleration maximumAcceleration)
741 {
742 if (maximumAcceleration.le(Acceleration.ZERO))
743 {
744 throw new RuntimeException("Maximum acceleration of GTU " + this.id + " set to value <= 0");
745 }
746 this.maximumAcceleration = maximumAcceleration;
747 }
748
749
750
751
752 public final Acceleration getMaximumDeceleration()
753 {
754 return this.maximumDeceleration;
755 }
756
757
758
759
760
761 public final void setMaximumDeceleration(final Acceleration maximumDeceleration)
762 {
763 if (maximumDeceleration.ge(Acceleration.ZERO))
764 {
765 throw new RuntimeException("Cannot set maximum deceleration of GTU " + this.id + " to " + maximumDeceleration
766 + " (value must be negative)");
767 }
768 this.maximumDeceleration = maximumDeceleration;
769 }
770
771
772 private Time cacheLocationTime = new Time(Double.NaN, TimeUnit.DEFAULT);
773
774
775 private OrientedPoint2d cacheLocation = null;
776
777 @Override
778 @SuppressWarnings("checkstyle:designforextension")
779 public OrientedPoint2d getLocation()
780 {
781 synchronized (this)
782 {
783 if (this.operationalPlan.get() == null)
784 {
785 this.simulator.getLogger().always()
786 .error("No operational plan for GTU " + this.id + " at t=" + this.getSimulator().getSimulatorTime());
787 return new OrientedPoint2d(0, 0, 0);
788 }
789 try
790 {
791
792 Time locationTime = this.simulator.getSimulatorAbsTime();
793 if (null == this.cacheLocationTime || this.cacheLocationTime.si != locationTime.si)
794 {
795 this.cacheLocationTime = null;
796 this.cacheLocation = this.operationalPlan.get().getLocation(locationTime);
797 this.cacheLocationTime = locationTime;
798 }
799 return this.cacheLocation;
800 }
801 catch (OperationalPlanException exception)
802 {
803 return new OrientedPoint2d(0, 0, 0);
804 }
805 }
806 }
807
808
809
810
811
812
813
814
815 @Override
816 public Polygon2d getContour(final Time time)
817 {
818 try
819 {
820 if (this.contour == null)
821 {
822
823 double w = getWidth().si;
824 double l = getLength().si;
825 this.contour = new Polygon2d(new Point2d(-0.5 * l, -0.5 * w), new Point2d(-0.5 * l, 0.5 * w),
826 new Point2d(0.5 * l, 0.5 * w), new Point2d(0.5 * l, -0.5 * w));
827 }
828 Polygon2d s = OtsLocatable.transformContour(this.contour, this.operationalPlan.get(time).getLocation(time));
829 System.out.println("gtu " + getId() + ", shape(t)=" + s);
830 return s;
831 }
832 catch (OperationalPlanException exception)
833 {
834 throw new RuntimeException(exception);
835 }
836 }
837
838
839
840
841
842
843
844 @Override
845 public Polygon2d getContour()
846 {
847 try
848 {
849
850 OtsLine2d path = this.operationalPlan.get().getPath();
851
852
853 double rear = Math.max(0.0, getReference().dx().si - getRear().dx().si);
854 double front = path.getLength() + Math.max(0.0, getFront().dx().si - getReference().dx().si);
855 Point2d p0 = path.getLocationExtendedSI(-rear);
856 Point2d pn = path.getLocationExtendedSI(front);
857 List<Point2d> pList = path.getPointList();
858 pList.add(0, p0);
859 pList.add(pn);
860 OtsLine2d extendedPath = new OtsLine2d(pList);
861 List<Point2d> swath = new ArrayList<>();
862 swath.addAll(extendedPath.offsetLine(getWidth().si / 2.0).getPointList());
863 swath.addAll(extendedPath.offsetLine(-getWidth().si / 2.0).reverse().getPointList());
864 Polygon2d s = new Polygon2d(swath);
865
866
867 return s;
868 }
869 catch (Exception e)
870 {
871 throw new RuntimeException(e);
872 }
873 }
874
875 @Override
876 public OtsShape getShape()
877 {
878 return this.shape;
879 }
880
881
882
883
884
885 public final boolean isDestroyed()
886 {
887 return this.destroyed;
888 }
889
890
891 public PerceivableContext getPerceivableContext()
892 {
893 return this.perceivableContext;
894 }
895
896
897
898
899
900
901 public void addGtu(final Gtu gtu) throws GtuException
902 {
903 this.children.add(gtu);
904 gtu.setParent(this);
905 }
906
907
908
909
910
911 public void removeGtu(final Gtu gtu)
912 {
913 this.children.remove(gtu);
914 try
915 {
916 gtu.setParent(null);
917 }
918 catch (GtuException exception)
919 {
920
921 }
922 }
923
924
925
926
927
928
929 public void setParent(final Gtu gtu) throws GtuException
930 {
931 Throw.when(gtu != null && this.parent != null, GtuException.class, "GTU %s already has a parent.", this);
932 this.parent = gtu;
933 }
934
935
936
937
938
939 public Gtu getParent()
940 {
941 return this.parent;
942 }
943
944
945
946
947
948 public Set<Gtu> getChildren()
949 {
950 return new LinkedHashSet<>(this.children);
951 }
952
953
954
955
956 protected GtuErrorHandler getErrorHandler()
957 {
958 return this.errorHandler;
959 }
960
961
962
963
964
965 public void setErrorHandler(final GtuErrorHandler errorHandler)
966 {
967 this.errorHandler = errorHandler;
968 }
969
970
971
972
973
974 public final SimEvent<Duration> getNextMoveEvent()
975 {
976 return this.nextMoveEvent;
977 }
978
979 @Override
980 @SuppressWarnings("designforextension")
981 public int hashCode()
982 {
983 final int prime = 31;
984 int result = 1;
985 result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
986 result = prime * result + this.uniqueNumber;
987 return result;
988 }
989
990 @Override
991 @SuppressWarnings({"designforextension", "needbraces"})
992 public boolean equals(final Object obj)
993 {
994 if (this == obj)
995 return true;
996 if (obj == null)
997 return false;
998 if (getClass() != obj.getClass())
999 return false;
1000 Gtu other = (Gtu) obj;
1001 if (this.id == null)
1002 {
1003 if (other.id != null)
1004 return false;
1005 }
1006 else if (!this.id.equals(other.id))
1007 return false;
1008 if (this.uniqueNumber != other.uniqueNumber)
1009 return false;
1010 return true;
1011 }
1012
1013
1014
1015
1016
1017 public static EventType MOVE_EVENT = new EventType("GTU.MOVE",
1018 new MetaData("GTU move", "GTU id, position, speed, acceleration, odometer",
1019 new ObjectDescriptor[] {new ObjectDescriptor("Id", "GTU Id", String.class),
1020 new ObjectDescriptor("position", "position", PositionVector.class),
1021 new ObjectDescriptor("direction", "direction", Direction.class),
1022 new ObjectDescriptor("speed", "speed", Speed.class),
1023 new ObjectDescriptor("acceleration", "acceleration", Acceleration.class),
1024 new ObjectDescriptor("Odometer", "Total distance travelled since incarnation", Length.class)}));
1025
1026
1027
1028
1029
1030 public static EventType DESTROY_EVENT = new EventType("GTU.DESTROY",
1031 new MetaData("GTU destroy", "GTU id, final position, final odometer",
1032 new ObjectDescriptor[] {new ObjectDescriptor("Id", "GTU Id", String.class),
1033 new ObjectDescriptor("position", "position", PositionVector.class),
1034 new ObjectDescriptor("direction", "direction", Direction.class),
1035 new ObjectDescriptor("Odometer", "Total distance travelled since incarnation", Length.class)}));
1036
1037 }