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