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