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