1 package org.opentrafficsim.core.gtu;
2
3 import java.io.Serializable;
4 import java.util.LinkedHashSet;
5 import java.util.Set;
6
7 import org.djunits.unit.DirectionUnit;
8 import org.djunits.unit.DurationUnit;
9 import org.djunits.unit.PositionUnit;
10 import org.djunits.unit.TimeUnit;
11 import org.djunits.value.vdouble.scalar.Acceleration;
12 import org.djunits.value.vdouble.scalar.Direction;
13 import org.djunits.value.vdouble.scalar.Duration;
14 import org.djunits.value.vdouble.scalar.Length;
15 import org.djunits.value.vdouble.scalar.Speed;
16 import org.djunits.value.vdouble.scalar.Time;
17 import org.djutils.event.EventProducer;
18 import org.djutils.exceptions.Throw;
19 import org.djutils.exceptions.Try;
20 import org.opentrafficsim.base.parameters.ParameterException;
21 import org.opentrafficsim.base.parameters.Parameters;
22 import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
23 import org.opentrafficsim.core.geometry.OTSPoint3D;
24 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
25 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
26 import org.opentrafficsim.core.gtu.plan.strategical.StrategicalPlanner;
27 import org.opentrafficsim.core.gtu.plan.tactical.TacticalPlanner;
28 import org.opentrafficsim.core.idgenerator.IdGenerator;
29 import org.opentrafficsim.core.network.NetworkException;
30 import org.opentrafficsim.core.perception.Historical;
31 import org.opentrafficsim.core.perception.HistoricalValue;
32 import org.opentrafficsim.core.perception.HistoryManager;
33 import org.opentrafficsim.core.perception.PerceivableContext;
34
35 import nl.tudelft.simulation.dsol.SimRuntimeException;
36 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEvent;
37 import nl.tudelft.simulation.dsol.simtime.SimTimeDoubleUnit;
38 import nl.tudelft.simulation.language.d3.DirectedPoint;
39
40
41
42
43
44
45
46
47
48
49
50
51 public abstract class AbstractGTU extends EventProducer implements GTU
52 {
53
54 private static final long serialVersionUID = 20140822L;
55
56
57 private final String id;
58
59
60 private final int uniqueNumber;
61
62
63 private static int staticUNIQUENUMBER = 0;
64
65
66 private final GTUType gtuType;
67
68
69 private final OTSSimulatorInterface simulator;
70
71
72 private Parameters parameters;
73
74
75 private Acceleration maximumAcceleration;
76
77
78 private Acceleration maximumDeceleration;
79
80
81
82
83
84
85 private Historical<Length> odometer;
86
87
88 private final Historical<StrategicalPlanner> strategicalPlanner;
89
90
91 private final Historical<TacticalPlanner<?, ?>> tacticalPlanner;
92
93
94 protected final Historical<OperationalPlan> operationalPlan;
95
96
97 private SimEvent<SimTimeDoubleUnit> nextMoveEvent;
98
99
100 private PerceivableContext perceivableContext;
101
102
103 private boolean destroyed = false;
104
105
106
107 public static boolean ALIGNED = true;
108
109
110
111 public static int ALIGN_COUNT = 0;
112
113
114 private double cachedSpeedTime = Double.NaN;
115
116
117 private Speed cachedSpeed = null;
118
119
120 private double cachedAccelerationTime = Double.NaN;
121
122
123 private Acceleration cachedAcceleration = null;
124
125
126 private GTU parent = null;
127
128
129 private Set<GTU> children = new LinkedHashSet<>();
130
131
132 private GTUErrorHandler errorHandler = GTUErrorHandler.THROW;
133
134
135
136
137
138
139
140
141 @SuppressWarnings("checkstyle:parameternumber")
142 public AbstractGTU(final String id, final GTUType gtuType, final OTSSimulatorInterface simulator,
143 final PerceivableContext perceivableContext) throws GTUException
144 {
145 Throw.when(id == null, GTUException.class, "id is null");
146 Throw.when(gtuType == null, GTUException.class, "gtuType is null");
147 Throw.when(perceivableContext == null, GTUException.class, "perceivableContext is null for GTU with id %s", id);
148 Throw.when(perceivableContext.containsGtuId(id), GTUException.class,
149 "GTU with id %s already registered in perceivableContext %s", id, perceivableContext.getId());
150 Throw.when(simulator == null, GTUException.class, "simulator is null for GTU with id %s", id);
151
152 HistoryManager historyManager = simulator.getReplication().getHistoryManager(simulator);
153 this.id = id;
154 this.uniqueNumber = ++staticUNIQUENUMBER;
155 this.gtuType = gtuType;
156 this.simulator = simulator;
157 this.odometer = new HistoricalValue<>(historyManager, Length.ZERO);
158 this.perceivableContext = perceivableContext;
159 this.perceivableContext.addGTU(this);
160 this.strategicalPlanner = new HistoricalValue<>(historyManager);
161 this.tacticalPlanner = new HistoricalValue<>(historyManager, null);
162 this.operationalPlan = new HistoricalValue<>(historyManager, null);
163 }
164
165
166
167
168
169
170
171
172 @SuppressWarnings("checkstyle:parameternumber")
173 public AbstractGTU(final IdGenerator idGenerator, final GTUType gtuType, final OTSSimulatorInterface simulator,
174 final PerceivableContext perceivableContext) throws GTUException
175 {
176 this(generateId(idGenerator), gtuType, simulator, perceivableContext);
177 }
178
179
180
181
182
183
184
185
186
187
188
189 @SuppressWarnings({"checkstyle:hiddenfield", "hiding", "checkstyle:designforextension"})
190 public void init(final StrategicalPlanner strategicalPlanner, final DirectedPoint initialLocation, final Speed initialSpeed)
191 throws SimRuntimeException, GTUException
192 {
193 Throw.when(strategicalPlanner == null, GTUException.class, "strategicalPlanner is null for GTU with id %s", this.id);
194 Throw.whenNull(initialLocation, "Initial location of GTU cannot be null");
195 Throw.when(Double.isNaN(initialLocation.x) || Double.isNaN(initialLocation.y) || Double.isNaN(initialLocation.z),
196 GTUException.class, "initialLocation %s invalid for GTU with id %s", initialLocation, this.id);
197 Throw.when(initialSpeed == null, GTUException.class, "initialSpeed is null for GTU with id %s", this.id);
198 Throw.when(!getId().equals(strategicalPlanner.getGtu().getId()), GTUException.class,
199 "GTU %s is initialized with a strategical planner for GTU %s", getId(), strategicalPlanner.getGtu().getId());
200
201 this.strategicalPlanner.set(strategicalPlanner);
202 this.tacticalPlanner.set(strategicalPlanner.getTacticalPlanner());
203 Time now = this.simulator.getSimulatorTime();
204
205 DirectedPoint location = getLocation();
206 fireTimedEvent(GTU.INIT_EVENT, new Object[] { getId(), new OTSPoint3D(location).doubleVector(PositionUnit.METER),
207 new Direction(location.getZ(), DirectionUnit.EAST_RADIAN), getLength(), getWidth() }, now);
208
209 try
210 {
211 move(initialLocation);
212 }
213 catch (OperationalPlanException | NetworkException | ParameterException exception)
214 {
215 throw new GTUException("Failed to create OperationalPlan for GTU " + this.id, exception);
216 }
217 }
218
219
220
221
222
223
224
225 private static String generateId(final IdGenerator idGenerator) throws GTUException
226 {
227 Throw.when(idGenerator == null, GTUException.class, "AbstractGTU.<init>: idGenerator is null");
228 return idGenerator.nextId();
229 }
230
231
232
233
234 @Override
235 @SuppressWarnings("checkstyle:designforextension")
236 public void destroy()
237 {
238 DirectedPoint location = getLocation();
239 fireTimedEvent(GTU.DESTROY_EVENT,
240 new Object[] { getId(), new OTSPoint3D(location).doubleVector(PositionUnit.METER),
241 new Direction(location.getZ(), DirectionUnit.EAST_RADIAN), getOdometer() },
242 this.simulator.getSimulatorTime());
243
244
245 if (this.nextMoveEvent != null)
246 {
247 this.simulator.cancelEvent(this.nextMoveEvent);
248 this.nextMoveEvent = null;
249 }
250
251 this.perceivableContext.removeGTU(this);
252 this.destroyed = true;
253 }
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270 @SuppressWarnings("checkstyle:designforextension")
271 protected boolean move(final DirectedPoint fromLocation)
272 throws SimRuntimeException, OperationalPlanException, GTUException, NetworkException, ParameterException
273 {
274 try
275 {
276 Time now = this.simulator.getSimulatorTime();
277
278
279
280 Length currentOdometer;
281 if (this.operationalPlan.get() != null)
282 {
283 currentOdometer = this.odometer.get().plus(this.operationalPlan.get().getTraveledDistance(now));
284 }
285 else
286 {
287 currentOdometer = this.odometer.get();
288 }
289
290
291
292 TacticalPlanner<?, ?> tactPlanner = this.tacticalPlanner.get();
293 if (tactPlanner == null)
294 {
295
296 tactPlanner = this.strategicalPlanner.get().getTacticalPlanner();
297 this.tacticalPlanner.set(tactPlanner);
298 }
299 synchronized (this)
300 {
301 tactPlanner.getPerception().perceive();
302 }
303 OperationalPlan newOperationalPlan = tactPlanner.generateOperationalPlan(now, fromLocation);
304 synchronized (this)
305 {
306 this.operationalPlan.set(newOperationalPlan);
307 this.cachedSpeedTime = Double.NaN;
308 this.cachedAccelerationTime = Double.NaN;
309 this.odometer.set(currentOdometer);
310 }
311
312
313 if (ALIGNED && newOperationalPlan.getTotalDuration().si == 0.5)
314 {
315
316
317 double tNext = Math.floor(2.0 * now.si + 1.0) / 2.0;
318 DirectedPoint p = (tNext - now.si < 0.5) ? newOperationalPlan.getEndLocation()
319 : newOperationalPlan.getLocation(new Duration(tNext - now.si, DurationUnit.SI));
320 this.nextMoveEvent = new SimEvent<>(new SimTimeDoubleUnit(new Time(tNext, TimeUnit.DEFAULT)), this, this,
321 "move", new Object[] {p});
322 ALIGN_COUNT++;
323 }
324 else
325 {
326
327
328 this.nextMoveEvent = new SimEvent<>(new SimTimeDoubleUnit(now.plus(newOperationalPlan.getTotalDuration())),
329 this, this, "move", new Object[] {newOperationalPlan.getEndLocation()});
330 }
331 this.simulator.scheduleEvent(this.nextMoveEvent);
332 fireTimedEvent(GTU.MOVE_EVENT,
333 new Object[] { getId(), new OTSPoint3D(fromLocation).doubleVector(PositionUnit.METER),
334 new Direction(fromLocation.getZ(), DirectionUnit.EAST_RADIAN), getSpeed(), getAcceleration(),
335 getOdometer() },
336 this.simulator.getSimulatorTime());
337
338 return false;
339 }
340 catch (Exception ex)
341 {
342 try
343 {
344 this.errorHandler.handle(this, ex);
345 }
346 catch (Exception exception)
347 {
348 throw new GTUException(exception);
349 }
350 return true;
351 }
352 }
353
354
355
356
357
358
359
360
361
362
363
364 @SuppressWarnings("checkstyle:designforextension")
365 protected void interruptMove()
366 throws SimRuntimeException, OperationalPlanException, GTUException, NetworkException, ParameterException
367 {
368 this.simulator.cancelEvent(this.nextMoveEvent);
369 move(this.operationalPlan.get().getLocation(this.simulator.getSimulatorTime()));
370 }
371
372
373 @Override
374 public final String getId()
375 {
376 return this.id;
377 }
378
379
380 @SuppressWarnings("checkstyle:designforextension")
381 @Override
382 public GTUType getGTUType()
383 {
384 return this.gtuType;
385 }
386
387
388 @Override
389 public final RelativePosition getReference()
390 {
391 return RelativePosition.REFERENCE_POSITION;
392 }
393
394
395 @Override
396 public final OTSSimulatorInterface getSimulator()
397 {
398 return this.simulator;
399 }
400
401
402 @Override
403 public final Parameters getParameters()
404 {
405 return this.parameters;
406 }
407
408
409 @Override
410 public final void setParameters(final Parameters parameters)
411 {
412 this.parameters = parameters;
413 }
414
415
416 @Override
417 public StrategicalPlanner getStrategicalPlanner()
418 {
419 return this.strategicalPlanner.get();
420 }
421
422
423 @Override
424 public StrategicalPlanner getStrategicalPlanner(final Time time)
425 {
426 return this.strategicalPlanner.get(time);
427 }
428
429
430 @Override
431 public final OperationalPlan getOperationalPlan()
432 {
433 return this.operationalPlan.get();
434 }
435
436
437 @Override
438 public final OperationalPlan getOperationalPlan(final Time time)
439 {
440 return this.operationalPlan.get(time);
441 }
442
443
444 @Override
445 public final Length getOdometer()
446 {
447 return getOdometer(this.simulator.getSimulatorTime());
448 }
449
450
451 @Override
452 public final Length getOdometer(final Time time)
453 {
454 synchronized (this)
455 {
456 if (getOperationalPlan(time) == null)
457 {
458 return this.odometer.get(time);
459 }
460 try
461 {
462 return this.odometer.get(time).plus(getOperationalPlan(time).getTraveledDistance(time));
463 }
464 catch (OperationalPlanException ope)
465 {
466 return this.odometer.get(time);
467 }
468 }
469 }
470
471
472 @Override
473 public final Speed getSpeed()
474 {
475 synchronized (this)
476 {
477 return getSpeed(this.simulator.getSimulatorTime());
478 }
479 }
480
481
482 @Override
483 public final Speed getSpeed(final Time time)
484 {
485 synchronized (this)
486 {
487 if (this.cachedSpeedTime != time.si)
488 {
489
490 this.cachedSpeedTime = Double.NaN;
491 this.cachedSpeed = null;
492 OperationalPlan plan = getOperationalPlan(time);
493 if (plan == null)
494 {
495 this.cachedSpeed = Speed.ZERO;
496 }
497 else if (time.si < plan.getStartTime().si)
498 {
499 this.cachedSpeed = plan.getStartSpeed();
500 }
501 else if (time.si > plan.getEndTime().si)
502 {
503 if (time.si - plan.getEndTime().si < 1e-6)
504 {
505 this.cachedSpeed = Try.assign(() -> plan.getSpeed(plan.getEndTime()),
506 "getSpeed() could not derive a valid speed for the current operationalPlan");
507 }
508 else
509 {
510 throw new IllegalStateException("Requesting speed value beyond plan.");
511 }
512 }
513 else
514 {
515 this.cachedSpeed = Try.assign(() -> plan.getSpeed(time),
516 "getSpeed() could not derive a valid speed for the current operationalPlan");
517 }
518 this.cachedSpeedTime = time.si;
519 }
520 return this.cachedSpeed;
521 }
522 }
523
524
525 @Override
526 public final Acceleration getAcceleration()
527 {
528 synchronized (this)
529 {
530 return getAcceleration(this.simulator.getSimulatorTime());
531 }
532 }
533
534
535 @Override
536 public final Acceleration getAcceleration(final Time time)
537 {
538 synchronized (this)
539 {
540 if (this.cachedAccelerationTime != time.si)
541 {
542
543 this.cachedAccelerationTime = Double.NaN;
544 this.cachedAcceleration = null;
545 OperationalPlan plan = getOperationalPlan(time);
546 if (plan == null)
547 {
548 this.cachedAcceleration = Acceleration.ZERO;
549 }
550 else if (time.si < plan.getStartTime().si)
551 {
552 this.cachedAcceleration =
553 Try.assign(() -> plan.getAcceleration(plan.getStartTime()), "Exception obtaining acceleration.");
554 }
555 else if (time.si > plan.getEndTime().si)
556 {
557 if (time.si - plan.getEndTime().si < 1e-6)
558 {
559 this.cachedAcceleration = Try.assign(() -> plan.getAcceleration(plan.getEndTime()),
560 "getAcceleration() could not derive a valid acceleration for the current operationalPlan");
561 }
562 else
563 {
564 throw new IllegalStateException("Requesting acceleration value beyond plan.");
565 }
566 }
567 else
568 {
569 this.cachedAcceleration = Try.assign(() -> plan.getAcceleration(time),
570 "getAcceleration() could not derive a valid acceleration for the current operationalPlan");
571 }
572 this.cachedAccelerationTime = time.si;
573 }
574 return this.cachedAcceleration;
575 }
576 }
577
578
579
580
581 @Override
582 public final Acceleration getMaximumAcceleration()
583 {
584 return this.maximumAcceleration;
585 }
586
587
588
589
590 public final void setMaximumAcceleration(final Acceleration maximumAcceleration)
591 {
592 if (maximumAcceleration.le(Acceleration.ZERO))
593 {
594 throw new RuntimeException("Maximum acceleration of GTU " + this.id + " set to value <= 0");
595 }
596 this.maximumAcceleration = maximumAcceleration;
597 }
598
599
600
601
602 @Override
603 public final Acceleration getMaximumDeceleration()
604 {
605 return this.maximumDeceleration;
606 }
607
608
609
610
611
612 public final void setMaximumDeceleration(final Acceleration maximumDeceleration)
613 {
614 if (maximumDeceleration.ge(Acceleration.ZERO))
615 {
616 throw new RuntimeException("Cannot set maximum deceleration of GTU " + this.id + " to " + maximumDeceleration
617 + " (value must be negative)");
618 }
619 this.maximumDeceleration = maximumDeceleration;
620 }
621
622
623 private Time cacheLocationTime = new Time(Double.NaN, TimeUnit.DEFAULT);
624
625
626 private DirectedPoint cacheLocation = null;
627
628
629 @Override
630 @SuppressWarnings("checkstyle:designforextension")
631 public DirectedPoint getLocation()
632 {
633 synchronized (this)
634 {
635 if (this.operationalPlan.get() == null)
636 {
637 this.simulator.getLogger().always()
638 .error("No operational plan for GTU " + this.id + " at t=" + this.getSimulator().getSimulatorTime());
639 return new DirectedPoint(0, 0, 0);
640 }
641 try
642 {
643
644 Time locationTime = this.simulator.getSimulatorTime();
645 if (null == this.cacheLocationTime || this.cacheLocationTime.si != locationTime.si)
646 {
647 this.cacheLocationTime = null;
648 this.cacheLocation = this.operationalPlan.get().getLocation(locationTime);
649 this.cacheLocationTime = locationTime;
650 }
651 return this.cacheLocation;
652 }
653 catch (OperationalPlanException exception)
654 {
655 return new DirectedPoint(0, 0, 0);
656 }
657 }
658 }
659
660
661 @Override
662 public final boolean isDestroyed()
663 {
664 return this.destroyed;
665 }
666
667
668 @Override
669 public PerceivableContext getPerceivableContext()
670 {
671 return this.perceivableContext;
672 }
673
674
675 @Override
676 public void addGtu(final GTU gtu) throws GTUException
677 {
678 this.children.add(gtu);
679 gtu.setParent(this);
680 }
681
682
683 @Override
684 public void removeGtu(final GTU gtu)
685 {
686 this.children.remove(gtu);
687 try
688 {
689 gtu.setParent(null);
690 }
691 catch (GTUException exception)
692 {
693
694 }
695 }
696
697
698 @Override
699 public void setParent(final GTU gtu) throws GTUException
700 {
701 Throw.when(gtu != null && this.parent != null, GTUException.class, "GTU %s already has a parent.", this);
702 this.parent = gtu;
703 }
704
705
706 @Override
707 public GTU getParent()
708 {
709 return this.parent;
710 }
711
712
713 @Override
714 public Set<GTU> getChildren()
715 {
716 return new LinkedHashSet<>(this.children);
717 }
718
719
720
721
722 protected GTUErrorHandler getErrorHandler()
723 {
724 return this.errorHandler;
725 }
726
727
728 @Override
729 public void setErrorHandler(final GTUErrorHandler errorHandler)
730 {
731 this.errorHandler = errorHandler;
732 }
733
734
735 @Override
736 public final Serializable getSourceId()
737 {
738 return this;
739 }
740
741
742
743
744
745 public final SimEvent<SimTimeDoubleUnit> getNextMoveEvent()
746 {
747 return this.nextMoveEvent;
748 }
749
750
751 @Override
752 @SuppressWarnings("designforextension")
753 public int hashCode()
754 {
755 final int prime = 31;
756 int result = 1;
757 result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
758 result = prime * result + this.uniqueNumber;
759 return result;
760 }
761
762
763 @Override
764 @SuppressWarnings({"designforextension", "needbraces"})
765 public boolean equals(final Object obj)
766 {
767 if (this == obj)
768 return true;
769 if (obj == null)
770 return false;
771 if (getClass() != obj.getClass())
772 return false;
773 AbstractGTU./../../org/opentrafficsim/core/gtu/AbstractGTU.html#AbstractGTU">AbstractGTU other = (AbstractGTU) obj;
774 if (this.id == null)
775 {
776 if (other.id != null)
777 return false;
778 }
779 else if (!this.id.equals(other.id))
780 return false;
781 if (this.uniqueNumber != other.uniqueNumber)
782 return false;
783 return true;
784 }
785
786 }