Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions scripts/ai/controllers/BaleLoaderController.lua
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,8 @@ end
--- This one is not used for the giants baleloaders
function BaleLoaderController:isReadyToLoadNextBale()
return false
end

function BaleLoaderController:onPreFinished()
return self:canBeFolded()
end
20 changes: 15 additions & 5 deletions scripts/ai/controllers/BalerController.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function BalerController:init(vehicle, baler)
self.slowDownStartSpeed = 20
self.balerSpec = self.baler.spec_baler
self.baleWrapperSpec = self.baler.spec_baleWrapper
self.baleLoaderSpec = self.baler.spec_baleLoader
self.lastDroppedBale = CpTemporaryObject()
self:debug('Baler controller initialized')
local additives = self.balerSpec.additives
Expand Down Expand Up @@ -124,11 +125,20 @@ function BalerController:onStart()
end
end

function BalerController:onFinished(hasFinished)
-- TODO: not working, as this probably needs to be called, before the drive is released.
-- if hasFinished and not self.balerSpec.automaticDrop or not self.balerSpec.platformAutomaticDrop then
-- Baler.actionEventUnloading(self.implement)
-- end
function BalerController:onPreFinished(hasFinished)
if hasFinished then
Baler.actionEventUnloading(self.baler)
if self.balerSpec.platformDropInProgress then
return
end
if self.balerSpec.isBaleUnloading then
return
end
if self.balerSpec.unloadingState ~= Baler.UNLOADING_CLOSED then
return
end
end
return true
end

function BalerController:isThisMyBale(baleObject)
Expand Down
4 changes: 4 additions & 0 deletions scripts/ai/controllers/ImplementController.lua
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ function ImplementController:onFinished(hasFinished)
--- override
end

function ImplementController:onPreFinished()
return true
end

function ImplementController:onFinishRow(isHeadlandTurn)
end

Expand Down
15 changes: 0 additions & 15 deletions scripts/ai/jobs/CpAIJob.lua
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,6 @@ function CpAIJob:setupCpJobParameters(jobParameters)
self.cpJobParameters:validateSettings()
end

--- Is the ai job allowed to finish ?
--- This entry point allowes us to catch giants stop conditions.
---@param message table Stop reason can be used to reverse engineer the cause.
---@return boolean
function CpAIJob:isFinishingAllowed(message)
return true
end

--- Gets the first task to start with.
function CpAIJob:getStartTaskIndex()
if self.currentTaskIndex ~= 0 or self.isDirectStart or self:isTargetReached() then
Expand Down Expand Up @@ -137,12 +129,8 @@ function CpAIJob:stop(aiMessage)
vehicle:deleteAgent()
vehicle:aiJobFinished()
vehicle:resetCpAllActiveInfoTexts()
local driveStrategy = vehicle:getCpDriveStrategy()
if not aiMessage then
self:debug("No valid ai message given!")
if driveStrategy then
driveStrategy:onFinished()
end
AIJob.stop(self, aiMessage)
return
end
Expand All @@ -160,9 +148,6 @@ function CpAIJob:stop(aiMessage)
if event then
SpecializationUtil.raiseEvent(vehicle, event)
end
if driveStrategy then
driveStrategy:onFinished(hasFinished)
end
g_messageCenter:unsubscribeAll(self)
end

Expand Down
23 changes: 0 additions & 23 deletions scripts/ai/jobs/CpAIJobFieldWork.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,29 +47,6 @@ function CpAIJobFieldWork:setupJobParameters()
self:setupCpJobParameters(CpFieldWorkJobParameters(self))
end

function CpAIJobFieldWork:isFinishingAllowed(message)
local nextTaskIndex = self:getNextTaskIndex()
if message:isa(AIMessageErrorOutOfFill) then
--- At least one implement type needs to be refilled.

local vehicle = self:getVehicle()
local setting = vehicle:getCpSettings().refillOnTheField

if setting:getValue() == CpVehicleSettings.REFILL_ON_FIELD_DISABLED then
return true
elseif setting:getValue() == CpVehicleSettings.REFILL_ON_FIELD_WAITING then
if self.currentTaskIndex == self.fieldWorkTask.taskIndex then
self.fieldWorkTask:setWaitingForRefillingActive()
end
elseif setting:getValue() == CpVehicleSettings.REFILL_ON_FIELD_ACTIVE then
--- TODO_25 Add driving to trailer for refilling here and so on ..
self.fieldWorkTask:skip()
end
return false
end
return CpAIJob.isFinishingAllowed(self, message)
end

---@param vehicle table
---@param mission table
---@param farmId number
Expand Down
94 changes: 88 additions & 6 deletions scripts/ai/strategies/AIDriveStrategyCourse.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@ AIDriveStrategyCourse.myStates = {
INITIAL = {},
WAITING_FOR_PATHFINDER = {},
WAITING_FOR_FIELD_BOUNDARY_DETECTION = {},
PRE_FINISHED = {},
WAITING_FOR_FINISHED = {},
FINISHED = {}
}

--- Implement controller events.
--- TODO_25 a more generic implementation
AIDriveStrategyCourse.onRaisingEvent = "onRaising"
AIDriveStrategyCourse.onLoweringEvent = "onLowering"
AIDriveStrategyCourse.onPreFinishedEvent = "onPreFinished"
AIDriveStrategyCourse.onFinishedEvent = "onFinished"
AIDriveStrategyCourse.onStartEvent = "onStart"
AIDriveStrategyCourse.onStartRefillingEvent = "onStartRefilling"
Expand Down Expand Up @@ -59,6 +63,11 @@ function AIDriveStrategyCourse:init(task, job)

self.currentTask = task
self.job = job
self.stopRequestData = {
reason = nil,
waitForFolding = false,
prepareTimeout = CpTemporaryObject(true)
}
end

function AIDriveStrategyCourse:setCurrentTaskFinished()
Expand Down Expand Up @@ -279,6 +288,10 @@ end

--- Called in the low frequency function for the helper.
function AIDriveStrategyCourse:updateLowFrequencyImplementControllers()
if self:hasFinished() then
--- Small hack so every ai drive strategy waits during finished.
self:setMaxSpeed(0)
end
for _, controller in pairs(self.controllers) do
---@type ImplementController
if controller:isEnabled() then
Expand Down Expand Up @@ -435,8 +448,57 @@ function AIDriveStrategyCourse:update(dt)
self.pathfinderController:update(dt)
self:updatePathfinding()
self:updateInfoTexts()
self:updateFinishing()
end

function AIDriveStrategyCourse:updateFinishing()
local function finishStrategy()
if self.stopRequestData.stopReason then
g_currentMission.aiSystem:stopJob(self.job, self.stopRequestData.stopReason)
return
end
self.currentTask:skip()
end
if self.state == self.states.PRE_FINISHED then
--- Every implement controller gets the chance to
--- prepare for the driver release.
--- For example balers can unload their bales
--- before we can fold them and so on.
local finished = true
for _, controller in ipairs(self.controllers) do
finished = finished and controller:onPreFinished()
end
if finished then
self:debug("Precondition for stopping the strategy are reached.")
self.state = self.states.FINISHED
end
elseif self.state == self.states.FINISHED then
self:raiseControllerEvent(self.onFinishedEvent, self.stopRequestData.waitForFolding)
if self.stopRequestData.waitForFolding then
self:debug("Starting to fold implements and so on...")
self.vehicle:prepareForAIDriving()
self.stopRequestData.prepareTimeout:set(false, 15000)
self.state = self.states.WAITING_FOR_FINISHED
else
finishStrategy()
end
elseif self.state == self.states.WAITING_FOR_FINISHED then
if not self.vehicle:getIsAIPreparingToDrive() or self.stopRequestData.prepareTimeout:get() then
if self.stopRequestData.prepareTimeout:get() then
self:debug("Failed to prepare ai drive, aborting ..")
end
finishStrategy()
end
end
end

--- Job has finished and we are now waiting to release the driver.
---@return boolean
function AIDriveStrategyCourse:hasFinished()
return self.state == self.states.PRE_FINISHED or
self.state == self.states.WAITING_FOR_FINISHED or
self.state == self.states.FINISHED
end

function AIDriveStrategyCourse:getDriveData(dt, vX, vY, vZ)
local moveForwards = not self.ppc:isReversing()
Expand Down Expand Up @@ -664,15 +726,35 @@ function AIDriveStrategyCourse:isCloseToCourseStart(distance)
return self.course:getDistanceFromFirstWaypoint(self.ppc:getCurrentWaypointIx()) < distance
end

--- Possiblity to override the vehicle:stopCurrentAIJob(),
--- if for example refilling on the field is active.
function AIDriveStrategyCourse:handleFinishedRequest(stopReason)
--- override
return false
end

--- Event raised when the driver was stopped.
---@param hasFinished boolean|nil flag passed by the info text
function AIDriveStrategyCourse:onFinished(hasFinished)
self:raiseControllerEvent(self.onFinishedEvent, hasFinished)
if hasFinished and self.settings.foldImplementAtEnd:getValue() then
--- Folds implements at the end if the setting is active.
self:debug("Finished with folding implements of the implements.")
self.vehicle:prepareForAIDriving()
---@param stopReason table
function AIDriveStrategyCourse:onFinished(hasFinished, stopReason)
if self:handleFinishedRequest(stopReason) then
--- Stop request ignored
return
end
if self:hasFinished() then
--- Driver is already stopping and still waiting for folding and so on ...
return
end
self:setFinished(hasFinished and self.settings.foldImplementAtEnd:getValue(), stopReason)
end

--- Internal stop request
---@param waitForFolding boolean|nil wait until for the folding and so on ..
---@param stopReason table|nil if nil is given, then the task will be skipped
function AIDriveStrategyCourse:setFinished(waitForFolding, stopReason)
self.stopRequestData.stopReason = stopReason
self.stopRequestData.waitForFolding = waitForFolding
self.state = self.states.PRE_FINISHED
end

--- This is to set the offsets on the course at start, or update those values
Expand Down
21 changes: 19 additions & 2 deletions scripts/ai/strategies/AIDriveStrategyFieldWorkCourse.lua
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,28 @@ function AIDriveStrategyFieldWorkCourse:prepareForFieldWork()
end

--- Event raised when the driver has finished.
function AIDriveStrategyFieldWorkCourse:onFinished(hasFinished)
AIDriveStrategyCourse.onFinished(self, hasFinished)
function AIDriveStrategyFieldWorkCourse:onFinished(...)
AIDriveStrategyCourse.onFinished(self, ...)
self.remainingTime:reset()
end

function AIDriveStrategyFieldWorkCourse:handleFinishedRequest(stopReason)
--- TODO consolidate this logic in the future ...
if stopReason:isa(AIMessageErrorOutOfFill) then
--- At least one implement type needs to be refilled.
local setting = self.settings.refillOnTheField
if setting:getValue() == CpVehicleSettings.REFILL_ON_FIELD_WAITING then
self.currentTask:setWaitingForRefillingActive()
return true
elseif setting:getValue() == CpVehicleSettings.REFILL_ON_FIELD_ACTIVE then
--- TODO_25 Add driving to trailer for refilling here and so on ..
self:setFinished(true, nil)
return true
end
end
return false
end

function AIDriveStrategyFieldWorkCourse:update(dt)
AIDriveStrategyCourse.update(self, dt)
if CpDebug:isChannelActive(CpDebug.DBG_TURN, self.vehicle) then
Expand Down
41 changes: 9 additions & 32 deletions scripts/ai/strategies/AIDriveStrategyFindBales.lua
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ AIDriveStrategyFindBales.myStates = {
WORKING_ON_BALE = {},
REVERSING_AFTER_PATHFINDER_FAILURE = {},
REVERSING_DUE_TO_OBSTACLE_AHEAD = {},
DRIVING_TO_START_MARKER = {},
WAITING_FOR_IMPLEMENTS_TO_FOLD = {}
DRIVING_TO_START_MARKER = {}
}
--- Offset to apply at the goal marker, so we don't crash with an empty unloader waiting there with the same position.
AIDriveStrategyFindBales.invertedGoalPositionOffset = -4.5
Expand Down Expand Up @@ -85,7 +84,7 @@ function AIDriveStrategyFindBales:collectNextBale()
self:findPathToNextBale()
return
end
self.state = self.states.WAITING_FOR_IMPLEMENTS_TO_FOLD
self.state = self.states.DRIVING_TO_START_MARKER
end
end

Expand Down Expand Up @@ -314,29 +313,6 @@ function AIDriveStrategyFindBales:getBaleTarget(bale)
return State3D(xb, -zb, CpMathUtil.angleFromGame(yRot))
end

--- Sets the driver as finished, so either a path
--- to the start marker as a park position can be used
--- or the driver stops directly.
function AIDriveStrategyFindBales:setFinished()
if not self:isReadyToFoldImplements() then
-- Waiting until the folding has finished..
self:debugSparse("Waiting until an animation has finish, so the driver can be released ..")
return
end
self.vehicle:prepareForAIDriving()
if not self.vehicle:getIsAIReadyToDrive() then
-- Waiting until the folding has finished..
self:debugSparse("Waiting until an animation has finish, so the driver can be released ..")
return
end
if self.invertedStartPositionMarkerNode then
self:debug("A valid start position is found, so the driver tries to finish at the inverted goal node")
self:startPathfindingToStartMarker()
else
self:finishJob()
end
end

--- Finishes the job with the correct stop reason, as
--- the correct reason is needed for a possible AD takeover.
function AIDriveStrategyFindBales:finishJob()
Expand Down Expand Up @@ -566,9 +542,6 @@ function AIDriveStrategyFindBales:getDriveData(dt, vX, vY, vZ)
self:setMaxSpeed(self.settings.reverseSpeed:getValue())
elseif self.state == self.states.DRIVING_TO_START_MARKER then
self:setMaxSpeed(self.settings.fieldSpeed:getValue())
elseif self.state == self.states.WAITING_FOR_IMPLEMENTS_TO_FOLD then
self:setFinished()
self:setMaxSpeed(0) --- folding
end

local moveForwards = not self.ppc:isReversing()
Expand Down Expand Up @@ -685,10 +658,14 @@ function AIDriveStrategyFindBales:update(dt)
self.ppc:getCourse():draw()
end
end
if self.state ~= self.states.DRIVING_TO_START_MARKER and
self.state ~= self.states.WAITING_FOR_IMPLEMENTS_TO_FOLD then
if self.state ~= self.states.DRIVING_TO_START_MARKER then
if self:areBaleLoadersFull() then
self.state = self.states.WAITING_FOR_IMPLEMENTS_TO_FOLD
if self.invertedStartPositionMarkerNode then
self:debug("A valid start position is found, so the driver tries to finish at the inverted goal node")
self:startPathfindingToStartMarker()
else
self:finishJob()
end
end
end
--- Ignores the loaded auto loader bales.
Expand Down
Loading
Loading