diff --git a/gui/globalSettingsPage.xml b/gui/globalSettingsPage.xml
index 0f8a6ed..0eeff6a 100644
--- a/gui/globalSettingsPage.xml
+++ b/gui/globalSettingsPage.xml
@@ -51,6 +51,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/scripts/Modules/CollisionDetectionModule.lua b/scripts/Modules/CollisionDetectionModule.lua
index d48bee4..54d9e6e 100644
--- a/scripts/Modules/CollisionDetectionModule.lua
+++ b/scripts/Modules/CollisionDetectionModule.lua
@@ -6,6 +6,7 @@ function ADCollisionDetectionModule:new(vehicle)
self.__index = self
o.vehicle = vehicle
o.detectedObstable = false
+ o.detectedPhysicalObstacle = false
o.reverseSectionClear = AutoDriveTON:new()
o.reverseSectionClear.elapsedTime = 20000
o.detectedCollision = false
@@ -32,6 +33,10 @@ function ADCollisionDetectionModule:hasDetectedObstable(dt)
)
end
+ -- physically sensed obstacle (sensor box / vehicle collision), as opposed to
+ -- AD-internal right-of-way waiting - only the former justifies a stuck-recovery maneuver
+ self.detectedPhysicalObstacle = detectObstacle
+
self.detectedObstable = detectObstacle or detectAdTrafficOnRoute or not self.reverseSectionClear:done()
return self.detectedObstable
end
diff --git a/scripts/Modules/DrivePathModule.lua b/scripts/Modules/DrivePathModule.lua
index d7822b3..5fdc2c1 100644
--- a/scripts/Modules/DrivePathModule.lua
+++ b/scripts/Modules/DrivePathModule.lua
@@ -7,6 +7,19 @@ ADDrivePathModule.MAX_STEERING_ANGLE = 30
ADDrivePathModule.PAUSE_TIMEOUT = 3000
ADDrivePathModule.BLINK_TIMEOUT = 1000
+-- obstacle avoidance (panic mode) parameters
+ADDrivePathModule.AVOIDANCE_REVERSE = 1
+ADDrivePathModule.AVOIDANCE_PATHPLANNING = 2
+ADDrivePathModule.AVOIDANCE_FORWARD = 3
+ADDrivePathModule.AVOIDANCE_REVERSE_DISTANCE = 12
+ADDrivePathModule.AVOIDANCE_SKIP_DISTANCE = 20
+ADDrivePathModule.AVOIDANCE_REVERSE_SPEED = 6
+ADDrivePathModule.AVOIDANCE_PATHPLANNING_TIMEOUT = 15000
+ADDrivePathModule.AVOIDANCE_FORWARD_TIMEOUT = 45000
+-- if stuck closer to the end of the route than this, declare the target reached instead of
+-- maneuvering (fallback, configurable via the stuckHandoverDistance setting)
+ADDrivePathModule.STUCK_HANDOVER_DISTANCE = 30
+
function ADDrivePathModule:new(vehicle)
local o = {}
setmetatable(o, self)
@@ -14,6 +27,9 @@ function ADDrivePathModule:new(vehicle)
o.vehicle = vehicle
o.min_distance = AutoDrive.defineMinDistanceByVehicleType(vehicle)
o.minDistanceTimer = AutoDriveTON:new()
+ o.blockedStuckTimer = AutoDriveTON:new()
+ o.stuckRecoveryCounter = 0
+ o.lastStuckPosition = nil
o.waitTimer = AutoDriveTON:new()
o.blinkTimer = AutoDriveTON:new()
o.brakeHysteresisActive = false
@@ -36,6 +52,8 @@ function ADDrivePathModule:reset()
self.onRoadNetwork = true
self.minDistanceToNextWp = math.huge
self.minDistanceTimer:timer(false, 5000, 0)
+ self.blockedStuckTimer:timer(false, 10000, 0)
+ self.obstacleAvoidanceState = nil
self.waitTimer:timer(false, ADDrivePathModule.PAUSE_TIMEOUT, 0)
self.blinkTimer:timer(false, ADDrivePathModule.BLINK_TIMEOUT, 0)
self.vehicle.ad.stateModule:setCurrentWayPointId(-1)
@@ -168,6 +186,15 @@ function ADDrivePathModule:update(dt)
return
end
+ if self.obstacleAvoidanceState ~= nil then
+ if self.wayPoints ~= nil and self:getCurrentWayPointIndex() <= #self.wayPoints then
+ self:updateObstacleAvoidance(dt)
+ return
+ else
+ self.obstacleAvoidanceState = nil
+ end
+ end
+
if self.wayPoints ~= nil and self:getCurrentWayPointIndex() <= #self.wayPoints then
if self.isReversing then
self.vehicle.ad.specialDrivingModule:handleReverseDriving(dt)
@@ -175,7 +202,10 @@ function ADDrivePathModule:update(dt)
self:followWaypoints(dt)
self:checkIfStuck(dt)
- if self:isCloseToWaypoint() then
+ -- checkIfStuck can hand off control (e.g. stopAutoDrive -> passToExternalMod_*)
+ -- which resets this module and nils self.wayPoints; isCloseToWaypoint()/
+ -- handleReachedWayPoint() would then index a nil wayPoints table
+ if self.wayPoints ~= nil and self:isCloseToWaypoint() then
self:handleReachedWayPoint()
end
end
@@ -788,9 +818,40 @@ function ADDrivePathModule:checkIfStuck(dt)
if self.minDistanceTimer:done() then
self:handleBeingStuck()
end
+ self.blockedStuckTimer:timer(false)
else
self.minDistanceTimer:timer(false)
+ self:checkIfBlockedByObstacle(dt)
+ end
+ end
+end
+
+--- The collision detection stops the vehicle in front of an obstacle, but a static obstacle
+--- (tree, lamp post, parked vehicle) never clears - so the vehicle would wait forever.
+--- Run a separate timer while being stopped by a physically detected obstacle and treat it
+--- as being stuck once the configured time has passed.
+function ADDrivePathModule:checkIfBlockedByObstacle(dt)
+ local reverseTime = AutoDrive.getSetting("reverseOnStuck") or 0
+ if reverseTime > 0 then
+ local standingWhileStopped = self.vehicle.ad.specialDrivingModule:isStoppingVehicle()
+ and math.abs(self.vehicle.lastSpeedReal) <= 0.0005
+ -- small per-vehicle offset so two AD vehicles blocking each other do not start
+ -- their avoidance maneuver at the same moment
+ local jitter = (NetworkUtil.getObjectId(self.vehicle) or 0) % 5
+ local timeout = (reverseTime + jitter) * 1000
+ if self.vehicle.ad.collisionDetectionModule.detectedPhysicalObstacle ~= true then
+ -- stopped by AD-internal logic only (right of way / reverse section wait):
+ -- normally this clears on its own, so allow much more time before treating
+ -- it as a mutual deadlock
+ timeout = timeout * 3
+ end
+ self.blockedStuckTimer:timer(standingWhileStopped, timeout, dt)
+ if self.blockedStuckTimer:done() then
+ self.blockedStuckTimer:timer(false)
+ self:handleBeingStuck()
end
+ else
+ self.blockedStuckTimer:timer(false)
end
end
@@ -799,18 +860,221 @@ function ADDrivePathModule:handleBeingStuck()
AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_VEHICLEINFO, "handleBeingStuck")
end
if self.vehicle.isServer then
- AutoDriveMessageEvent.sendMessageOrNotification(self.vehicle, ADMessagesManager.messageTypes.ERROR, "$l10n_AD_Driver_of; %s $l10n_AD_got_stuck;", 5000, self.vehicle.ad.stateModule:getName())
- self.vehicle.ad.taskModule:stopAndRestartAD()
+ local reverseTime = AutoDrive.getSetting("reverseOnStuck") or 0
+
+ if reverseTime > 0 and self:isStuckCloseToTarget() then
+ -- stuck within a few meters of the destination - most likely another vehicle is
+ -- already parked on the target point
+ AutoDriveMessageEvent.sendMessageOrNotification(self.vehicle, ADMessagesManager.messageTypes.WARN, "$l10n_AD_Driver_of; %s $l10n_AD_got_stuck;", 5000, self.vehicle.ad.stateModule:getName())
+ self.stuckRecoveryCounter = 0
+ if self.vehicle.ad.stateModule:getCanRestartHelper() and self.vehicle.ad.stateModule:getMode() ~= AutoDrive.MODE_DRIVETO then
+ -- in modes like LOAD/UNLOAD the helper (CP/AI) is only started via stopAutoDrive
+ -- at the end of the task chain - the load/unload tasks would swallow atTarget
+ -- and wait at a (non-existing) trigger forever. Stop AD directly instead,
+ -- which passes control to the helper (passToExternalMod_*)
+ Logging.info("[AutoDrive stuck-recovery] '%s': stuck close to target -> stopping AD to pass control to helper", tostring(self.vehicle.ad.stateModule:getName()))
+ self.vehicle:stopAutoDrive()
+ else
+ -- declare the target reached so the route ends normally
+ Logging.info("[AutoDrive stuck-recovery] '%s': stuck close to target -> declaring target reached", tostring(self.vehicle.ad.stateModule:getName()))
+ self.atTarget = true
+ end
+ return
+ end
+
+ -- reset the recovery counter once the vehicle got well away from the last stuck location
+ local x, _, z = getWorldTranslation(self.vehicle.components[1].node)
+ if self.lastStuckPosition ~= nil and MathUtil.vector2Length(x - self.lastStuckPosition.x, z - self.lastStuckPosition.z) > 50 then
+ self.stuckRecoveryCounter = 0
+ end
+ self.lastStuckPosition = {x = x, z = z}
+ self.stuckRecoveryCounter = self.stuckRecoveryCounter + 1
+
+ if reverseTime > 0 and self.stuckRecoveryCounter <= 3 and self:startObstacleAvoidance() then
+ -- panic mode: back up, drive around the obstacle with a side offset and
+ -- continue the current route at a waypoint beyond it (restarting the mode
+ -- would just replan the same route on the waypoint network)
+ Logging.info("[AutoDrive stuck-recovery] '%s': starting obstacle avoidance, attempt %d", tostring(self.vehicle.ad.stateModule:getName()), self.stuckRecoveryCounter)
+ AutoDriveMessageEvent.sendMessageOrNotification(self.vehicle, ADMessagesManager.messageTypes.WARN, "$l10n_AD_Driver_of; %s $l10n_AD_got_stuck;", 5000, self.vehicle.ad.stateModule:getName())
+ elseif reverseTime > 0 and self.stuckRecoveryCounter > 3 then
+ -- several recovery attempts in the same spot failed - give up for good instead of looping
+ self.stuckRecoveryCounter = 0
+ AutoDriveMessageEvent.sendMessageOrNotification(self.vehicle, ADMessagesManager.messageTypes.ERROR, "$l10n_AD_Driver_of; %s $l10n_AD_got_stuck;", 5000, self.vehicle.ad.stateModule:getName())
+ self.vehicle.ad.isStoppingWithError = true
+ self.vehicle.ad.taskModule:abortAllTasks()
+ self.vehicle.ad.taskModule:addTask(StopAndDisableADTask:new(self.vehicle))
+ else
+ AutoDriveMessageEvent.sendMessageOrNotification(self.vehicle, ADMessagesManager.messageTypes.ERROR, "$l10n_AD_Driver_of; %s $l10n_AD_got_stuck;", 5000, self.vehicle.ad.stateModule:getName())
+ self.vehicle.ad.taskModule:stopAndRestartAD()
+ end
+ end
+end
+
+--- Stuck close to the end of the route: most likely another vehicle is already parked on the
+--- target point. In 'drive to' mode, or whenever a helper (CP/AIVE/AI) is supposed to take over
+--- at the destination, it is better to declare the target reached than to maneuver around or
+--- give up right next to it.
+function ADDrivePathModule:isStuckCloseToTarget()
+ local maxDistance = AutoDrive.getSetting("stuckHandoverDistance") or ADDrivePathModule.STUCK_HANDOVER_DISTANCE
+ -- path distance along the remaining waypoints (math.huge when more than 60 waypoints remain)
+ local distance = self:getDistanceToLastWaypoint(60)
+ -- beeline to the final waypoint as fallback: dense waypoints or loops in the last route
+ -- section must not prevent the handover when the vehicle is physically next to the target
+ if self.wayPoints ~= nil and #self.wayPoints > 0 then
+ local target = self.wayPoints[#self.wayPoints]
+ local x, _, z = getWorldTranslation(self.vehicle.components[1].node)
+ local beeline = MathUtil.vector2Length(target.x - x, target.z - z)
+ if distance == nil or beeline < distance then
+ distance = beeline
+ end
+ end
+ local usesHelper = self.vehicle.ad.stateModule.usedHelper ~= nil and
+ self.vehicle.ad.stateModule.usedHelper ~= ADStateModule.HELPER_NONE
+ local modeOk = self.vehicle.ad.stateModule:getMode() == AutoDrive.MODE_DRIVETO or usesHelper
+ local result = distance ~= nil and distance <= maxDistance and modeOk
+ Logging.info("[AutoDrive stuck-recovery] '%s': distanceToTarget=%s max=%d mode=%s usedHelper=%s -> handover=%s",
+ tostring(self.vehicle.ad.stateModule:getName()), tostring(distance), maxDistance,
+ tostring(self.vehicle.ad.stateModule:getMode()), tostring(self.vehicle.ad.stateModule.usedHelper), tostring(result))
+ return result
+end
+
+--- Start the obstacle avoidance maneuver: back up a bit, then let AD's own A* pathfinder
+--- (the one ExitFieldTask/DriveToVehicleTask etc. use for off-network driving) plan a way
+--- around the obstacle to a waypoint beyond it on the current route.
+--- Returns false when there is no waypoint far enough ahead to rejoin at.
+function ADDrivePathModule:startObstacleAvoidance()
+ if self.wayPoints == nil then
+ return false
+ end
+ local x, y, z = getWorldTranslation(self.vehicle.components[1].node)
+ local skipIx = nil
+ for i = math.max(self:getCurrentWayPointIndex(), 1), #self.wayPoints do
+ local wp = self.wayPoints[i]
+ if MathUtil.vector2Length(wp.x - x, wp.z - z) > ADDrivePathModule.AVOIDANCE_SKIP_DISTANCE then
+ skipIx = i
+ break
+ end
+ end
+ if skipIx == nil then
+ return false
+ end
+
+ local targetNode = self.wayPoints[skipIx]
+ local afterNode = self.wayPoints[skipIx + 1]
+ local vecToNextPoint
+ if afterNode ~= nil then
+ vecToNextPoint = {x = afterNode.x - targetNode.x, z = afterNode.z - targetNode.z}
+ else
+ local beforeNode = self.wayPoints[skipIx - 1] or targetNode
+ vecToNextPoint = {x = targetNode.x - beforeNode.x, z = targetNode.z - beforeNode.z}
+ end
+ self.avoidanceTargetNode = targetNode
+ self.avoidanceTargetVector = vecToNextPoint
+
+ local rx, ry, rz = AutoDrive.localToWorld(self.vehicle, 0, 0, -100)
+ self.avoidanceReverseTarget = {x = rx, y = ry, z = rz}
+ self.avoidanceStartPosition = {x = x, y = y, z = z}
+ self.avoidanceSkipIx = skipIx
+ self.avoidanceTimer = 0
+ self.obstacleAvoidanceState = ADDrivePathModule.AVOIDANCE_REVERSE
+ if AutoDrive.getDebugChannelIsSet(AutoDrive.DC_PATHINFO) then
+ AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_PATHINFO, "startObstacleAvoidance skipIx %d", skipIx)
+ end
+ return true
+end
+
+function ADDrivePathModule:updateObstacleAvoidance(dt)
+ self.avoidanceTimer = self.avoidanceTimer + dt
+ local x, _, z = getWorldTranslation(self.vehicle.components[1].node)
+ if self.obstacleAvoidanceState == ADDrivePathModule.AVOIDANCE_REVERSE then
+ local distanceReversed = MathUtil.vector2Length(x - self.avoidanceStartPosition.x, z - self.avoidanceStartPosition.z)
+ if distanceReversed >= ADDrivePathModule.AVOIDANCE_REVERSE_DISTANCE or self.avoidanceTimer > 15000 then
+ self.vehicle.ad.specialDrivingModule:releaseVehicle()
+ self.vehicle.ad.pathFinderModule:reset()
+ self.vehicle.ad.pathFinderModule:startPathPlanningTo(self.avoidanceTargetNode, self.avoidanceTargetVector)
+ Logging.info("[AutoDrive stuck-recovery] '%s': reversed clear of obstacle, starting pathfinder around it", tostring(self.vehicle.ad.stateModule:getName()))
+ self.obstacleAvoidanceState = ADDrivePathModule.AVOIDANCE_PATHPLANNING
+ self.avoidanceTimer = 0
+ else
+ self.vehicle.ad.specialDrivingModule:reverseToTargetLocation(dt, self.avoidanceReverseTarget, ADDrivePathModule.AVOIDANCE_REVERSE_SPEED)
+ end
+ elseif self.obstacleAvoidanceState == ADDrivePathModule.AVOIDANCE_PATHPLANNING then
+ if self.vehicle.ad.pathFinderModule:hasFinished() then
+ local path = self.vehicle.ad.pathFinderModule:getPath()
+ if path == nil or #path == 0 then
+ Logging.info("[AutoDrive stuck-recovery] '%s': pathfinder found no way around the obstacle, aborting this attempt", tostring(self.vehicle.ad.stateModule:getName()))
+ self:finishObstacleAvoidance(false)
+ else
+ Logging.info("[AutoDrive stuck-recovery] '%s': pathfinder found a way around the obstacle, %d waypoints", tostring(self.vehicle.ad.stateModule:getName()), #path)
+ self.avoidancePath = path
+ self.avoidancePathIndex = 1
+ self.obstacleAvoidanceState = ADDrivePathModule.AVOIDANCE_FORWARD
+ self.avoidanceTimer = 0
+ end
+ elseif self.avoidanceTimer > ADDrivePathModule.AVOIDANCE_PATHPLANNING_TIMEOUT then
+ Logging.info("[AutoDrive stuck-recovery] '%s': pathfinder timed out, aborting this attempt", tostring(self.vehicle.ad.stateModule:getName()))
+ self.vehicle.ad.pathFinderModule:abort()
+ self:finishObstacleAvoidance(false)
+ else
+ self.vehicle.ad.pathFinderModule:update(dt)
+ self.vehicle.ad.specialDrivingModule:stopVehicle()
+ self.vehicle.ad.specialDrivingModule:update(dt)
+ end
+ elseif self.obstacleAvoidanceState == ADDrivePathModule.AVOIDANCE_FORWARD then
+ if self.avoidancePathIndex > #self.avoidancePath then
+ -- drove through every pathfinder waypoint - rejoin the original route
+ Logging.info("[AutoDrive stuck-recovery] '%s': detour around obstacle completed, rejoining route", tostring(self.vehicle.ad.stateModule:getName()))
+ self:finishObstacleAvoidance(true)
+ elseif self.avoidanceTimer > ADDrivePathModule.AVOIDANCE_FORWARD_TIMEOUT then
+ -- taking too long (e.g. stuck again on the detour) - fall back to the original
+ -- route; checkIfStuck will trigger a fresh avoidance attempt if still blocked
+ Logging.info("[AutoDrive stuck-recovery] '%s': detour took too long, giving up and rejoining route anyway", tostring(self.vehicle.ad.stateModule:getName()))
+ self:finishObstacleAvoidance(true)
+ else
+ -- Drive the pathfinder-planned detour with AD's own waypoint-follower
+ -- (handles curvature/speed/lookahead properly) instead of blindly aiming at
+ -- each point - a naive point-chase could not actually traverse the turns the
+ -- plan needed and always ran into the timeout above. self.wayPoints/currentWayPoint
+ -- are only swapped for the duration of this call and restored right after, so
+ -- the real route (and atTarget/reachedTarget()) are never touched by this.
+ local savedWayPoints, savedIndex = self.wayPoints, self.currentWayPoint
+ self.wayPoints = self.avoidancePath
+ self.currentWayPoint = self.avoidancePathIndex
+ self.vehicle.ad.trailerModule:handleTrailerReversing(false)
+ self:followWaypoints(dt)
+ if self:isCloseToWaypoint() then
+ self.avoidancePathIndex = self.avoidancePathIndex + 1
+ end
+ self.wayPoints = savedWayPoints
+ self.currentWayPoint = savedIndex
+ end
+ else
+ self:finishObstacleAvoidance(false)
end
end
+--- Ends the avoidance maneuver. When reachedSkip is true the pathfinder-planned detour was
+--- driven successfully, so rejoin the route at the waypoint beyond the obstacle; otherwise
+--- just hand control back at the current position and let checkIfStuck retry or give up.
+function ADDrivePathModule:finishObstacleAvoidance(reachedSkip)
+ self.vehicle.ad.specialDrivingModule:releaseVehicle()
+ if reachedSkip and self.wayPoints ~= nil and self.avoidanceSkipIx ~= nil and self.avoidanceSkipIx <= #self.wayPoints then
+ self:setCurrentWayPointIndex(self.avoidanceSkipIx)
+ end
+ self.minDistanceToNextWp = math.huge
+ self.minDistanceTimer:timer(false)
+ self.blockedStuckTimer:timer(false)
+ self.obstacleAvoidanceState = nil
+ self.avoidancePath = nil
+end
+
function ADDrivePathModule:checkForReverseSection()
-- returns [current segment is reversed], [current segment is the last forward segment], [current segment is the last reverse segment]
if AutoDrive.getDebugChannelIsSet(AutoDrive.DC_PATHINFO) then
AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_VEHICLEINFO, "checkForReverseSection start")
end
- if self.wayPoints == nil or self:getCurrentWayPointIndex() < 1 or #self.wayPoints <= self:getCurrentWayPointIndex() + 1 then
+ if self.wayPoints == nil or self:getCurrentWayPointIndex() < 2 or #self.wayPoints <= self:getCurrentWayPointIndex() + 1 then
if AutoDrive.getDebugChannelIsSet(AutoDrive.DC_PATHINFO) then
AutoDrive.debugPrint(self.vehicle, AutoDrive.DC_PATHINFO, "ADDrivePathModule:checkForReverseSection wpIdx=%d - first or last segment"
, self:getCurrentWayPointIndex())
diff --git a/scripts/Settings.lua b/scripts/Settings.lua
index 00fa638..e027af7 100644
--- a/scripts/Settings.lua
+++ b/scripts/Settings.lua
@@ -606,6 +606,28 @@ AutoDrive.settings.enableTrafficDetection = {
isVehicleSpecific = false
}
+AutoDrive.settings.reverseOnStuck = {
+ values = {0, 5, 10, 15, 20, 30},
+ texts = {"-", "5s", "10s", "15s", "20s", "30s"},
+ default = 3,
+ current = 3,
+ text = "gui_ad_reverseOnStuck",
+ tooltip = "gui_ad_reverseOnStuck_tooltip",
+ translate = false,
+ isVehicleSpecific = false
+}
+
+AutoDrive.settings.stuckHandoverDistance = {
+ values = {10, 15, 20, 25, 30, 40, 50},
+ texts = {"10m", "15m", "20m", "25m", "30m", "40m", "50m"},
+ default = 5,
+ current = 5,
+ text = "gui_ad_stuckHandoverDistance",
+ tooltip = "gui_ad_stuckHandoverDistance_tooltip",
+ translate = false,
+ isVehicleSpecific = false
+}
+
AutoDrive.settings.shovelWidth = {
values = {0, 0.2, 0.4, 0.6, 0.8, 1, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0},
texts = {"0m", "0.2m", "0.4m", "0.6m", "0.8m", "1.0m", "1.2m", "1.4m", "1.6m", "1.8m", "2.0m", "2.2m", "2.4m", "2.6m", "2.8m", "3.0m", "3.2m", "3.4m", "3.6m", "3.8m", "4.0m"},
diff --git a/scripts/Specialization.lua b/scripts/Specialization.lua
index f9cc91f..2c6c1d0 100644
--- a/scripts/Specialization.lua
+++ b/scripts/Specialization.lua
@@ -1413,7 +1413,16 @@ function AutoDrive.passToExternalMod_CP(vehicle)
-- TODO: check if this dirty hack works in future!
local isControlled = vehicle:getIsControlled()
- if not vehicle.ad.isStoppingWithError and distanceToStart < 30 then
+ -- allow the handover a bit further away from the marker when the user raised the
+ -- stuck handover distance - the vehicle may have been stopped short of the target
+ -- by another vehicle parked on it
+ local maxDistance = 30
+ local handoverDistance = AutoDrive.getSetting("stuckHandoverDistance")
+ if handoverDistance ~= nil and handoverDistance > maxDistance then
+ maxDistance = handoverDistance
+ end
+
+ if not vehicle.ad.isStoppingWithError and distanceToStart < maxDistance then
AutoDrive.debugPrint(vehicle, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive.passToExternalMod_CP")
-- CP button enabled
if (vehicle.cpStartStopDriver ~= nil and vehicle.ad.stateModule:getUsedHelper() == ADStateModule.HELPER_CP) then
@@ -1422,15 +1431,17 @@ function AutoDrive.passToExternalMod_CP(vehicle)
if vehicle.ad.restartCP == true then
-- restart CP to continue
vehicle.ad.restartCP = false
- AutoDrive.debugPrint(vehicle, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive.passToExternalMod_CP pass control to CP with restart")
+ Logging.info("[AutoDrive stuck-recovery] '%s': passing control to CP (restart), distanceToStart=%.1f", tostring(vehicle.ad.stateModule:getName()), distanceToStart)
AutoDrive:RestartCP(vehicle)
else
-- start CP from beginning
- AutoDrive.debugPrint(vehicle, AutoDrive.DC_EXTERNALINTERFACEINFO, "AutoDrive.passToExternalMod_CP pass control to CP with start")
+ Logging.info("[AutoDrive stuck-recovery] '%s': passing control to CP (start), distanceToStart=%.1f", tostring(vehicle.ad.stateModule:getName()), distanceToStart)
AutoDrive:StartCP(vehicle)
end
vehicle.spec_enterable.isControlled = isControlled
end
+ elseif not vehicle.ad.isStoppingWithError then
+ Logging.info("[AutoDrive stuck-recovery] '%s': NOT passing control to CP, distanceToStart=%.1f > max=%d", tostring(vehicle.ad.stateModule:getName()), distanceToStart, maxDistance)
end
end
diff --git a/scripts/Tasks/ReverseFromBadLocationTask.lua b/scripts/Tasks/ReverseFromBadLocationTask.lua
index 11a24f8..d98c832 100644
--- a/scripts/Tasks/ReverseFromBadLocationTask.lua
+++ b/scripts/Tasks/ReverseFromBadLocationTask.lua
@@ -2,9 +2,10 @@ ReverseFromBadLocationTask = ADInheritsFrom(AbstractTask)
ReverseFromBadLocationTask.STATE_REVERSING = 1
-function ReverseFromBadLocationTask:new(vehicle)
+function ReverseFromBadLocationTask:new(vehicle, dontPropagate)
local o = ReverseFromBadLocationTask:create()
o.vehicle = vehicle
+ o.dontPropagate = dontPropagate
o.trailers = nil
return o
end
@@ -72,7 +73,12 @@ function ReverseFromBadLocationTask:abort()
end
function ReverseFromBadLocationTask:finished()
- self.vehicle.ad.taskModule:setCurrentTaskFinished()
+ if self.dontPropagate then
+ -- used by the generic stuck recovery: the mode must not treat this as a finished mode task
+ self.vehicle.ad.taskModule:setCurrentTaskFinished(ADTaskModule.DONT_PROPAGATE)
+ else
+ self.vehicle.ad.taskModule:setCurrentTaskFinished()
+ end
end
function ReverseFromBadLocationTask:getInfoText()
diff --git a/translations/translation_br.xml b/translations/translation_br.xml
index c2774f4..f8a0287 100644
--- a/translations/translation_br.xml
+++ b/translations/translation_br.xml
@@ -165,6 +165,10 @@
+
+
+
+
diff --git a/translations/translation_cs.xml b/translations/translation_cs.xml
index 29ee2b0..eb6b9d4 100644
--- a/translations/translation_cs.xml
+++ b/translations/translation_cs.xml
@@ -165,6 +165,10 @@
+
+
+
+
diff --git a/translations/translation_ct.xml b/translations/translation_ct.xml
index 4a246c1..cb36b20 100644
--- a/translations/translation_ct.xml
+++ b/translations/translation_ct.xml
@@ -165,6 +165,10 @@
+
+
+
+
diff --git a/translations/translation_cz.xml b/translations/translation_cz.xml
index d31b92b..f071951 100644
--- a/translations/translation_cz.xml
+++ b/translations/translation_cz.xml
@@ -165,6 +165,10 @@
+
+
+
+
diff --git a/translations/translation_da.xml b/translations/translation_da.xml
index eff93b5..a444d9b 100644
--- a/translations/translation_da.xml
+++ b/translations/translation_da.xml
@@ -165,6 +165,10 @@
+
+
+
+
diff --git a/translations/translation_de.xml b/translations/translation_de.xml
index 5b35468..7333a6d 100644
--- a/translations/translation_de.xml
+++ b/translations/translation_de.xml
@@ -165,6 +165,10 @@
+
+
+
+
diff --git a/translations/translation_ea.xml b/translations/translation_ea.xml
index 7e51b8c..6351a91 100644
--- a/translations/translation_ea.xml
+++ b/translations/translation_ea.xml
@@ -165,6 +165,10 @@
+
+
+
+
diff --git a/translations/translation_en.xml b/translations/translation_en.xml
index 0298f12..309f144 100644
--- a/translations/translation_en.xml
+++ b/translations/translation_en.xml
@@ -165,6 +165,10 @@
+
+
+
+
diff --git a/translations/translation_es.xml b/translations/translation_es.xml
index 5c51685..c72e985 100644
--- a/translations/translation_es.xml
+++ b/translations/translation_es.xml
@@ -165,6 +165,10 @@
+
+
+
+
diff --git a/translations/translation_fr.xml b/translations/translation_fr.xml
index c31efc5..c69d4d4 100644
--- a/translations/translation_fr.xml
+++ b/translations/translation_fr.xml
@@ -165,6 +165,10 @@
+
+
+
+
diff --git a/translations/translation_hu.xml b/translations/translation_hu.xml
index 15bb18c..8c6663a 100644
--- a/translations/translation_hu.xml
+++ b/translations/translation_hu.xml
@@ -165,6 +165,10 @@
+
+
+
+
diff --git a/translations/translation_it.xml b/translations/translation_it.xml
index 2a3845f..93c3759 100644
--- a/translations/translation_it.xml
+++ b/translations/translation_it.xml
@@ -165,12 +165,16 @@
+
+
+
+
-
-
+
+
@@ -346,10 +350,10 @@
-
-
-
-
+
+
+
+
@@ -397,4 +401,4 @@
-
+
\ No newline at end of file
diff --git a/translations/translation_nl.xml b/translations/translation_nl.xml
index 61e9845..663c79f 100644
--- a/translations/translation_nl.xml
+++ b/translations/translation_nl.xml
@@ -165,6 +165,10 @@
+
+
+
+
diff --git a/translations/translation_pl.xml b/translations/translation_pl.xml
index 958f9fe..d8274bc 100644
--- a/translations/translation_pl.xml
+++ b/translations/translation_pl.xml
@@ -165,6 +165,10 @@
+
+
+
+
diff --git a/translations/translation_pt.xml b/translations/translation_pt.xml
index bea5c95..d366d44 100644
--- a/translations/translation_pt.xml
+++ b/translations/translation_pt.xml
@@ -165,6 +165,10 @@
+
+
+
+
diff --git a/translations/translation_ru.xml b/translations/translation_ru.xml
index 9a59ce7..0df477b 100644
--- a/translations/translation_ru.xml
+++ b/translations/translation_ru.xml
@@ -165,6 +165,10 @@
+
+
+
+
diff --git a/translations/translation_tr.xml b/translations/translation_tr.xml
index a3de9c3..799e9e5 100644
--- a/translations/translation_tr.xml
+++ b/translations/translation_tr.xml
@@ -165,6 +165,10 @@
+
+
+
+
diff --git a/translations/translation_uk.xml b/translations/translation_uk.xml
index 2c466c6..24be029 100644
--- a/translations/translation_uk.xml
+++ b/translations/translation_uk.xml
@@ -165,6 +165,10 @@
+
+
+
+