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