1 package org.opentrafficsim.road.gtu.lane.tactical;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.HashSet;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Set;
9
10 import org.djunits.unit.AccelerationUnit;
11 import org.djunits.unit.LengthUnit;
12 import org.djunits.unit.TimeUnit;
13 import org.djunits.value.vdouble.scalar.Acceleration;
14 import org.djunits.value.vdouble.scalar.Duration;
15 import org.djunits.value.vdouble.scalar.Length;
16 import org.djunits.value.vdouble.scalar.Speed;
17 import org.djunits.value.vdouble.scalar.Time;
18 import org.opentrafficsim.core.gtu.GTUDirectionality;
19 import org.opentrafficsim.core.gtu.GTUException;
20 import org.opentrafficsim.core.gtu.TurnIndicatorStatus;
21 import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
22 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
23 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
24 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
25 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.Segment;
26 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
27 import org.opentrafficsim.core.network.LateralDirectionality;
28 import org.opentrafficsim.core.network.NetworkException;
29 import org.opentrafficsim.road.gtu.lane.AbstractLaneBasedGTU;
30 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
31 import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
32 import org.opentrafficsim.road.gtu.lane.perception.categories.DefaultSimplePerception;
33 import org.opentrafficsim.road.gtu.lane.perception.headway.AbstractHeadwayGTU;
34 import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
35 import org.opentrafficsim.road.gtu.lane.tactical.directedlanechange.DirectedAltruistic;
36 import org.opentrafficsim.road.gtu.lane.tactical.directedlanechange.DirectedEgoistic;
37 import org.opentrafficsim.road.gtu.lane.tactical.directedlanechange.DirectedLaneChangeModel;
38 import org.opentrafficsim.road.gtu.lane.tactical.directedlanechange.DirectedLaneMovementStep;
39 import org.opentrafficsim.road.gtu.lane.tactical.following.AccelerationStep;
40 import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
41 import org.opentrafficsim.road.network.lane.Lane;
42 import org.opentrafficsim.road.network.lane.object.sensor.SingleSensor;
43 import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
44
45 import nl.tudelft.simulation.dsol.SimRuntimeException;
46 import nl.tudelft.simulation.language.Throw;
47 import nl.tudelft.simulation.language.d3.DirectedPoint;
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80 public class LaneBasedGTUFollowingDirectedChangeTacticalPlanner extends AbstractLaneBasedTacticalPlanner
81 {
82
83 private static final long serialVersionUID = 20160129L;
84
85
86 private Time earliestNextLaneChangeTime = Time.ZERO;
87
88
89 private Duration durationInLaneAfterLaneChange = new Duration(15.0, TimeUnit.SECOND);
90
91
92 private Lane laneAfterLaneChange = null;
93
94
95 private Length posAfterLaneChange = null;
96
97
98 private boolean destroyGtuOnFailure = false;
99
100
101
102
103
104
105 public LaneBasedGTUFollowingDirectedChangeTacticalPlanner(final GTUFollowingModelOld carFollowingModel,
106 final LaneBasedGTU gtu)
107 {
108 super(carFollowingModel, gtu);
109 getPerception().addPerceptionCategory(new DefaultSimplePerception(getPerception()));
110 setNoLaneChange(new Duration(0.25, TimeUnit.SECOND));
111 }
112
113
114
115
116
117 public final GTUFollowingModelOld getCarFollowingModelOld()
118 {
119 return (GTUFollowingModelOld) super.getCarFollowingModel();
120 }
121
122
123
124
125
126 public final void setNoLaneChange(final Duration noLaneChangeDuration)
127 {
128 Throw.when(noLaneChangeDuration.lt(Duration.ZERO), RuntimeException.class, "noLaneChangeDuration should be >= 0");
129 this.earliestNextLaneChangeTime = getGtu().getSimulator().getSimulatorTime().getTime().plus(noLaneChangeDuration);
130 }
131
132
133
134
135 private Headway syncHeadway;
136
137
138
139
140 private Headway coopHeadway;
141
142
143
144
145 private Time deadLock = null;
146
147
148
149
150 private final Duration deadLockThreshold = new Duration(5.0, TimeUnit.SI);
151
152
153
154
155 private Collection<Headway> blockingHeadways = new HashSet<>();
156
157
158 @Override
159 @SuppressWarnings("checkstyle:methodlength")
160 public final OperationalPlan generateOperationalPlan(final Time startTime, final DirectedPoint locationAtStartTime)
161 throws OperationalPlanException, NetworkException, GTUException, ParameterException
162 {
163 try
164 {
165
166
167 LaneBasedGTU laneBasedGTU = getGtu();
168 DefaultSimplePerception simplePerception = getPerception().getPerceptionCategory(DefaultSimplePerception.class);
169 BehavioralCharacteristics behavioralCharacteristics = laneBasedGTU.getBehavioralCharacteristics();
170
171 getCarFollowingModelOld().setA(behavioralCharacteristics.getParameter(ParameterTypes.A));
172 getCarFollowingModelOld().setT(behavioralCharacteristics.getParameter(ParameterTypes.T));
173 getCarFollowingModelOld().setFspeed(behavioralCharacteristics.getParameter(ParameterTypes.FSPEED));
174
175
176 laneBasedGTU.setTurnIndicatorStatus(TurnIndicatorStatus.NONE);
177
178
179 if (laneBasedGTU.getMaximumSpeed().si < OperationalPlan.DRIFTING_SPEED_SI)
180 {
181 return new OperationalPlan(getGtu(), locationAtStartTime, startTime, new Duration(1.0, TimeUnit.SECOND));
182 }
183
184
185 simplePerception.updateForwardHeadwayGTU();
186 simplePerception.updateForwardHeadwayObject();
187 simplePerception.updateAccessibleAdjacentLanesLeft();
188 simplePerception.updateAccessibleAdjacentLanesRight();
189 simplePerception.updateSpeedLimit();
190
191
192 Length forwardHeadway = behavioralCharacteristics.getParameter(ParameterTypes.LOOKAHEAD);
193 LanePathInfo lanePathInfo = buildLanePathInfo(laneBasedGTU, forwardHeadway);
194 NextSplitInfo nextSplitInfo = determineNextSplit(laneBasedGTU, forwardHeadway);
195 Set<Lane> correctLanes = laneBasedGTU.positions(laneBasedGTU.getReference()).keySet();
196 correctLanes.retainAll(nextSplitInfo.getCorrectCurrentLanes());
197
198
199 this.syncHeadway = null;
200 if (lanePathInfo.getPath().getLength().lt(forwardHeadway) && correctLanes.isEmpty())
201 {
202 LateralDirectionality direction = determineLeftRight(laneBasedGTU, nextSplitInfo);
203 if (direction != null)
204 {
205 getGtu().setTurnIndicatorStatus(direction.isLeft() ? TurnIndicatorStatus.LEFT : TurnIndicatorStatus.RIGHT);
206 if (canChange(laneBasedGTU, getPerception(), lanePathInfo, direction))
207 {
208 DirectedPoint newLocation = changeLane(laneBasedGTU, direction);
209 lanePathInfo = buildLanePathInfo(laneBasedGTU, forwardHeadway, this.laneAfterLaneChange,
210 this.posAfterLaneChange, laneBasedGTU.getDirection(this.laneAfterLaneChange));
211 return currentLanePlan(laneBasedGTU, startTime, newLocation, lanePathInfo);
212 }
213 else
214 {
215 simplePerception.updateNeighboringHeadways(direction);
216 Length minDistance = new Length(Double.MAX_VALUE, LengthUnit.SI);
217 for (Headway headway : simplePerception.getNeighboringHeadways(direction))
218 {
219 if ((headway.isAhead() || headway.isParallel()) && (headway instanceof AbstractHeadwayGTU))
220 {
221 if (headway.isParallel() || headway.getDistance().lt(minDistance))
222 {
223 this.syncHeadway = headway;
224 if (!headway.isParallel())
225 {
226 minDistance = headway.getDistance();
227 }
228 }
229 }
230 }
231 }
232 }
233 }
234 if (this.syncHeadway != null && this.syncHeadway.isParallel() && getGtu().getSpeed().si < 10)
235 {
236
237 this.syncHeadway = null;
238 }
239
240
241 this.coopHeadway = null;
242 for (LateralDirectionality direction : new LateralDirectionality[] { LateralDirectionality.LEFT,
243 LateralDirectionality.RIGHT })
244 {
245 simplePerception.updateNeighboringHeadways(direction);
246 for (Headway headway : simplePerception.getNeighboringHeadways(direction))
247 {
248
249 if (headway.isAhead() && (headway instanceof AbstractHeadwayGTU)
250 && (this.coopHeadway == null || headway.getDistance().lt(this.coopHeadway.getDistance()))
251 && (direction.isLeft() ? ((AbstractHeadwayGTU) headway).isRightTurnIndicatorOn()
252 : ((AbstractHeadwayGTU) headway).isLeftTurnIndicatorOn()))
253 {
254 this.coopHeadway = headway;
255 }
256 }
257 }
258
259
260 if (getGtu().getSimulator().getSimulatorTime().getTime().lt(this.earliestNextLaneChangeTime))
261 {
262 return currentLanePlan(laneBasedGTU, startTime, locationAtStartTime, lanePathInfo);
263 }
264
265
266
267 Set<Lane> leftLanes = simplePerception.getAccessibleAdjacentLanesLeft().get(lanePathInfo.getReferenceLane());
268 if (nextSplitInfo.isSplit())
269 {
270 leftLanes.retainAll(nextSplitInfo.getCorrectCurrentLanes());
271 }
272 if (!leftLanes.isEmpty())
273 {
274 simplePerception.updateBackwardHeadway();
275 simplePerception.updateParallelHeadwaysLeft();
276 simplePerception.updateNeighboringHeadwaysLeft();
277 if (simplePerception.getParallelHeadwaysLeft().isEmpty())
278 {
279 Collection<Headway> sameLaneTraffic = new HashSet<>();
280
281
282 if (simplePerception.getForwardHeadwayGTU() != null
283 && simplePerception.getForwardHeadwayGTU().getObjectType().isGtu())
284 {
285 sameLaneTraffic.add(simplePerception.getForwardHeadwayGTU());
286 }
287 if (simplePerception.getBackwardHeadway() != null
288 && simplePerception.getBackwardHeadway().getObjectType().isGtu())
289 {
290 sameLaneTraffic.add(simplePerception.getBackwardHeadway());
291 }
292 DirectedLaneChangeModel dlcm = new DirectedAltruistic(getPerception());
293 DirectedLaneMovementStep dlms = dlcm.computeLaneChangeAndAcceleration(laneBasedGTU,
294 LateralDirectionality.LEFT, sameLaneTraffic, simplePerception.getNeighboringHeadwaysLeft(),
295 behavioralCharacteristics.getParameter(ParameterTypes.LOOKAHEAD), simplePerception.getSpeedLimit(),
296
297 new Acceleration(0.0, AccelerationUnit.SI), new Acceleration(0.5, AccelerationUnit.SI),
298 new Duration(0.5, TimeUnit.SECOND));
299 if (dlms.getLaneChange() != null)
300 {
301 getGtu().setTurnIndicatorStatus(TurnIndicatorStatus.LEFT);
302 if (canChange(laneBasedGTU, getPerception(), lanePathInfo, LateralDirectionality.LEFT))
303 {
304 DirectedPoint newLocation = changeLane(laneBasedGTU, LateralDirectionality.LEFT);
305 lanePathInfo = buildLanePathInfo(laneBasedGTU, forwardHeadway, this.laneAfterLaneChange,
306 this.posAfterLaneChange, laneBasedGTU.getDirection(this.laneAfterLaneChange));
307 return currentLanePlan(laneBasedGTU, startTime, newLocation, lanePathInfo);
308 }
309 }
310 }
311 }
312
313
314 Set<Lane> rightLanes = simplePerception.getAccessibleAdjacentLanesRight().get(lanePathInfo.getReferenceLane());
315 if (nextSplitInfo.isSplit())
316 {
317 rightLanes.retainAll(nextSplitInfo.getCorrectCurrentLanes());
318 }
319 if (!rightLanes.isEmpty())
320 {
321 simplePerception.updateBackwardHeadway();
322 simplePerception.updateParallelHeadwaysRight();
323 simplePerception.updateNeighboringHeadwaysRight();
324 if (simplePerception.getParallelHeadwaysRight().isEmpty())
325 {
326 Collection<Headway> sameLaneTraffic = new HashSet<>();
327
328
329 if (simplePerception.getForwardHeadwayGTU() != null
330 && simplePerception.getForwardHeadwayGTU().getObjectType().isGtu())
331 {
332 sameLaneTraffic.add(simplePerception.getForwardHeadwayGTU());
333 }
334 if (simplePerception.getBackwardHeadway() != null
335 && simplePerception.getBackwardHeadway().getObjectType().isGtu())
336 {
337 sameLaneTraffic.add(simplePerception.getBackwardHeadway());
338 }
339 DirectedLaneChangeModel dlcm = new DirectedAltruistic(getPerception());
340 DirectedLaneMovementStep dlms = dlcm.computeLaneChangeAndAcceleration(laneBasedGTU,
341 LateralDirectionality.RIGHT, sameLaneTraffic, simplePerception.getNeighboringHeadwaysRight(),
342 behavioralCharacteristics.getParameter(ParameterTypes.LOOKAHEAD), simplePerception.getSpeedLimit(),
343
344 new Acceleration(0.0, AccelerationUnit.SI), new Acceleration(0.1, AccelerationUnit.SI),
345 new Duration(0.5, TimeUnit.SECOND));
346 if (dlms.getLaneChange() != null)
347 {
348 getGtu().setTurnIndicatorStatus(TurnIndicatorStatus.RIGHT);
349 if (canChange(laneBasedGTU, getPerception(), lanePathInfo, LateralDirectionality.RIGHT))
350 {
351 DirectedPoint newLocation = changeLane(laneBasedGTU, LateralDirectionality.RIGHT);
352 lanePathInfo = buildLanePathInfo(laneBasedGTU, forwardHeadway, this.laneAfterLaneChange,
353 this.posAfterLaneChange, laneBasedGTU.getDirection(this.laneAfterLaneChange));
354 return currentLanePlan(laneBasedGTU, startTime, newLocation, lanePathInfo);
355 }
356 }
357 }
358 }
359
360 if (this.deadLock != null
361 && getGtu().getSimulator().getSimulatorTime().getTime().minus(this.deadLock).ge(this.deadLockThreshold))
362 {
363 System.err.println("Deleting gtu " + getGtu().getId() + " to prevent dead-lock.");
364 try
365 {
366 getGtu().getSimulator().scheduleEventRel(new Duration(0.001, TimeUnit.SI), this, getGtu(), "destroy",
367 new Object[0]);
368 }
369 catch (SimRuntimeException exception)
370 {
371 throw new RuntimeException(exception);
372 }
373 }
374
375 return currentLanePlan(laneBasedGTU, startTime, locationAtStartTime, lanePathInfo);
376 }
377 catch (GTUException | NetworkException | OperationalPlanException exception)
378 {
379 if (isDestroyGtuOnFailure())
380 {
381 System.err.println("LaneBasedGTUFollowingChange0TacticalPlanner.generateOperationalPlan() failed for "
382 + getGtu() + " because of " + exception.getMessage() + " -- GTU destroyed");
383 getGtu().destroy();
384 return new OperationalPlan(getGtu(), locationAtStartTime, startTime, new Duration(1.0, TimeUnit.SECOND));
385 }
386 throw exception;
387 }
388 }
389
390
391
392
393
394
395
396
397
398
399
400
401
402 private OperationalPlan currentLanePlan(final LaneBasedGTU laneBasedGTU, final Time startTime,
403 final DirectedPoint locationAtStartTime, final LanePathInfo lanePathInfo)
404 throws OperationalPlanException, GTUException, ParameterException, NetworkException
405 {
406 DefaultSimplePerception simplePerception = getPerception().getPerceptionCategory(DefaultSimplePerception.class);
407
408
409 AccelerationStep accelerationStep = mostLimitingAccelerationStep(lanePathInfo, simplePerception.getForwardHeadwayGTU(),
410 simplePerception.getForwardHeadwayObject());
411
412
413 if (accelerationStep.getAcceleration().si < 1E-6 && laneBasedGTU.getSpeed().si < OperationalPlan.DRIFTING_SPEED_SI)
414 {
415 return new OperationalPlan(laneBasedGTU, locationAtStartTime, startTime, accelerationStep.getDuration());
416 }
417
418
419 List<Segment> operationalPlanSegmentList = new ArrayList<>();
420 if (accelerationStep.getAcceleration().si == 0.0)
421 {
422 Segment segment = new OperationalPlan.SpeedSegment(accelerationStep.getDuration());
423 operationalPlanSegmentList.add(segment);
424 }
425 else
426 {
427 Segment segment =
428 new OperationalPlan.AccelerationSegment(accelerationStep.getDuration(), accelerationStep.getAcceleration());
429 operationalPlanSegmentList.add(segment);
430 }
431 OperationalPlan op = new OperationalPlan(laneBasedGTU, lanePathInfo.getPath(), startTime, laneBasedGTU.getSpeed(),
432 operationalPlanSegmentList);
433 return op;
434 }
435
436
437
438
439
440
441
442 private LateralDirectionality determineLeftRight(final LaneBasedGTU laneBasedGTU, final NextSplitInfo nextSplitInfo)
443 {
444
445 try
446 {
447 Set<Lane> lanes = laneBasedGTU.positions(laneBasedGTU.getReference()).keySet();
448 for (Lane correctLane : nextSplitInfo.getCorrectCurrentLanes())
449 {
450 for (Lane currentLane : lanes)
451 {
452 if (correctLane.getParentLink().equals(currentLane.getParentLink()))
453 {
454 double deltaOffset =
455 correctLane.getDesignLineOffsetAtBegin().si - currentLane.getDesignLineOffsetAtBegin().si;
456 if (laneBasedGTU.getDirection(currentLane).equals(GTUDirectionality.DIR_PLUS))
457 {
458 return deltaOffset > 0 ? LateralDirectionality.LEFT : LateralDirectionality.RIGHT;
459 }
460 else
461 {
462 return deltaOffset < 0 ? LateralDirectionality.LEFT : LateralDirectionality.RIGHT;
463 }
464 }
465 }
466 }
467 }
468 catch (GTUException exception)
469 {
470 System.err.println(
471 "Exception in LaneBasedGTUFollowingChange0TacticalPlanner.determineLeftRight: " + exception.getMessage());
472 }
473
474 return nextSplitInfo.getRequiredDirection();
475 }
476
477
478
479
480
481
482
483
484
485
486
487
488
489 private boolean canChange(final LaneBasedGTU gtu, final LanePerception perception, final LanePathInfo lanePathInfo,
490 final LateralDirectionality direction)
491 throws GTUException, NetworkException, ParameterException, OperationalPlanException
492 {
493
494
495 if (!((AbstractLaneBasedGTU) gtu).isSafeToChange())
496 {
497 return false;
498 }
499
500
501 Map<Lane, Length> positions = getGtu().positions(getGtu().getRear());
502 for (Lane lane : positions.keySet())
503 {
504 Length pos = positions.get(lane);
505 if (pos.si > 0.0 && pos.si < lane.getLength().si
506 && lane.accessibleAdjacentLanes(direction, getGtu().getGTUType()).isEmpty())
507 {
508 return false;
509 }
510 }
511
512 Collection<Headway> otherLaneTraffic;
513 DefaultSimplePerception simplePerception = getPerception().getPerceptionCategory(DefaultSimplePerception.class);
514 simplePerception.updateForwardHeadwayGTU();
515 simplePerception.updateForwardHeadwayObject();
516 simplePerception.updateBackwardHeadway();
517 if (direction.isLeft())
518 {
519 simplePerception.updateParallelHeadwaysLeft();
520 this.blockingHeadways = simplePerception.getParallelHeadwaysLeft();
521 simplePerception.updateNeighboringHeadwaysLeft();
522 otherLaneTraffic = simplePerception.getNeighboringHeadwaysLeft();
523 }
524 else if (direction.isRight())
525 {
526 simplePerception.updateParallelHeadwaysRight();
527 this.blockingHeadways = simplePerception.getParallelHeadwaysRight();
528 simplePerception.updateNeighboringHeadwaysRight();
529 otherLaneTraffic = simplePerception.getNeighboringHeadwaysRight();
530 }
531 else
532 {
533 throw new GTUException("Lateral direction is neither LEFT nor RIGHT during a lane change");
534 }
535 if (!simplePerception.getParallelHeadways(direction).isEmpty())
536 {
537 return false;
538 }
539
540 Collection<Headway> sameLaneTraffic = new HashSet<>();
541
542
543 if (simplePerception.getForwardHeadwayGTU() != null && perception.getPerceptionCategory(DefaultSimplePerception.class)
544 .getForwardHeadwayGTU().getObjectType().isGtu())
545 {
546 sameLaneTraffic.add(simplePerception.getForwardHeadwayGTU());
547 }
548 if (simplePerception.getBackwardHeadway() != null && simplePerception.getBackwardHeadway().getObjectType().isGtu())
549 {
550 sameLaneTraffic.add(simplePerception.getBackwardHeadway());
551 }
552
553
554 DirectedLaneChangeModel dlcm = new DirectedEgoistic(getPerception());
555
556 DirectedLaneMovementStep dlms = dlcm.computeLaneChangeAndAcceleration(gtu, direction, sameLaneTraffic, otherLaneTraffic,
557 gtu.getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKAHEAD), simplePerception.getSpeedLimit(),
558 new Acceleration(2.0, AccelerationUnit.SI), new Acceleration(0.1, AccelerationUnit.SI),
559 new Duration(0.5, TimeUnit.SECOND));
560 if (dlms.getLaneChange() == null)
561 {
562 return false;
563 }
564
565 return true;
566 }
567
568
569
570
571
572
573
574
575 private DirectedPoint changeLane(final LaneBasedGTU gtu, final LateralDirectionality direction) throws GTUException
576 {
577 gtu.changeLaneInstantaneously(direction);
578
579
580 this.earliestNextLaneChangeTime =
581 gtu.getSimulator().getSimulatorTime().getTime().plus(this.durationInLaneAfterLaneChange);
582
583
584 gtu.setTurnIndicatorStatus(direction.isLeft() ? TurnIndicatorStatus.LEFT : TurnIndicatorStatus.RIGHT);
585
586 this.laneAfterLaneChange = gtu.getReferencePosition().getLane();
587 this.posAfterLaneChange = gtu.getReferencePosition().getPosition();
588 return gtu.getLocation();
589 }
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605 private AccelerationStep mostLimitingAccelerationStep(final LanePathInfo lanePathInfo, final Headway... headways)
606 throws OperationalPlanException, ParameterException, GTUException, NetworkException
607 {
608 DefaultSimplePerception simplePerception = getPerception().getPerceptionCategory(DefaultSimplePerception.class);
609 simplePerception.updateForwardHeadwayGTU();
610 simplePerception.updateForwardHeadwayObject();
611 boolean sinkAtEnd = false;
612 for (SingleSensor sensor : (lanePathInfo.getLanes().get(lanePathInfo.getLanes().size() - 1).getSensors()))
613 {
614 if (sensor instanceof SinkSensor)
615 {
616 sinkAtEnd = true;
617 }
618 }
619 boolean stopForEndOrSplit = !sinkAtEnd;
620 BehavioralCharacteristics bc = getGtu().getBehavioralCharacteristics();
621 Length maxDistance = sinkAtEnd ? new Length(Double.MAX_VALUE, LengthUnit.SI)
622 : Length.min(getGtu().getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKAHEAD),
623 lanePathInfo.getPath().getLength().minus(getGtu().getLength().multiplyBy(2.0)));
624
625 AccelerationStep mostLimitingAccelerationStep = getCarFollowingModelOld().computeAccelerationStepWithNoLeader(getGtu(),
626 maxDistance, simplePerception.getSpeedLimit());
627
628 Acceleration minB = bc.getParameter(ParameterTypes.B).neg();
629 Acceleration numericallySafeB =
630 Acceleration.max(minB, getGtu().getSpeed().divideBy(mostLimitingAccelerationStep.getDuration()).neg());
631 if ((this.syncHeadway != null || this.coopHeadway != null) && mostLimitingAccelerationStep.getAcceleration().gt(minB))
632 {
633 AccelerationStep sync;
634 if (this.syncHeadway == null)
635 {
636 sync = null;
637 }
638 else if (this.syncHeadway.isParallel())
639 {
640 sync = new AccelerationStep(numericallySafeB, mostLimitingAccelerationStep.getValidUntil(),
641 mostLimitingAccelerationStep.getDuration());
642 }
643 else
644 {
645 sync = getCarFollowingModelOld().computeAccelerationStep(getGtu(), this.syncHeadway.getSpeed(),
646 this.syncHeadway.getDistance(), maxDistance, simplePerception.getSpeedLimit());
647 }
648 AccelerationStep coop;
649 if (this.coopHeadway == null)
650 {
651 coop = null;
652 }
653 else
654 {
655 coop = getCarFollowingModelOld().computeAccelerationStep(getGtu(), this.coopHeadway.getSpeed(),
656 this.coopHeadway.getDistance(), maxDistance, simplePerception.getSpeedLimit());
657 }
658 AccelerationStep adjust;
659 if (sync == null)
660 {
661 adjust = coop;
662 }
663 else if (coop == null)
664 {
665 adjust = sync;
666 }
667 else
668 {
669 adjust = sync.getAcceleration().lt(coop.getAcceleration()) ? sync : coop;
670 }
671 if (adjust.getAcceleration().lt(minB))
672 {
673 mostLimitingAccelerationStep = new AccelerationStep(numericallySafeB,
674 mostLimitingAccelerationStep.getValidUntil(), mostLimitingAccelerationStep.getDuration());
675 }
676 else
677 {
678 mostLimitingAccelerationStep = adjust;
679 }
680 }
681
682 for (Headway headway : headways)
683 {
684 if (headway != null && headway.getDistance().lt(maxDistance))
685 {
686 AccelerationStep accelerationStep = getCarFollowingModelOld().computeAccelerationStep(getGtu(),
687 headway.getSpeed(), headway.getDistance(), maxDistance, simplePerception.getSpeedLimit());
688 if (accelerationStep.getAcceleration().lt(mostLimitingAccelerationStep.getAcceleration()))
689 {
690 stopForEndOrSplit = false;
691 mostLimitingAccelerationStep = accelerationStep;
692 }
693 }
694 }
695
696
697 if (!this.blockingHeadways.isEmpty() && stopForEndOrSplit)
698 {
699 Speed maxSpeed = getGtu().getSpeed();
700 for (Headway headway : this.blockingHeadways)
701 {
702 maxSpeed = Speed.max(maxSpeed, headway.getSpeed());
703 }
704 if (maxSpeed.si < OperationalPlan.DRIFTING_SPEED_SI)
705 {
706 if (this.deadLock == null)
707 {
708 this.deadLock = getGtu().getSimulator().getSimulatorTime().getTime();
709 }
710 }
711 else
712 {
713 this.deadLock = null;
714 }
715 }
716 else
717 {
718 this.deadLock = null;
719 }
720
721 return mostLimitingAccelerationStep;
722
723 }
724
725
726
727
728
729 public final boolean isDestroyGtuOnFailure()
730 {
731 return this.destroyGtuOnFailure;
732 }
733
734
735
736
737
738 public final void setDestroyGtuOnFailure(final boolean destroyGtuOnFailure)
739 {
740 this.destroyGtuOnFailure = destroyGtuOnFailure;
741 }
742
743
744
745
746
747 protected final Duration getDurationInLaneAfterLaneChange()
748 {
749 return this.durationInLaneAfterLaneChange;
750 }
751
752
753
754
755
756
757 protected final void setDurationInLaneAfterLaneChange(final Duration durationInLaneAfterLaneChange) throws GTUException
758 {
759 Throw.when(durationInLaneAfterLaneChange.lt(Duration.ZERO), GTUException.class,
760 "durationInLaneAfterLaneChange should be >= 0");
761 this.durationInLaneAfterLaneChange = durationInLaneAfterLaneChange;
762 }
763
764
765 @Override
766 public final String toString()
767 {
768 return "LaneBasedGTUFollowingChange0TacticalPlanner [earliestNexLaneChangeTime=" + this.earliestNextLaneChangeTime
769 + ", referenceLane=" + this.laneAfterLaneChange + ", referencePos=" + this.posAfterLaneChange
770 + ", destroyGtuOnFailure=" + this.destroyGtuOnFailure + "]";
771 }
772
773 }