Skip to content

Commit 04e7a72

Browse files
committed
WIP rewrite the job stopping logic
- Now every ``vehicle:stopCurrentAIJob()`` call gets passed back to the current drive stategy and can be evaluated there. - This enabled a proper solution to wait for finishing event, like for example folding of implements or specific action like unloading of a baler and so on ...
1 parent cf32485 commit 04e7a72

10 files changed

Lines changed: 167 additions & 111 deletions

scripts/ai/controllers/BaleLoaderController.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,8 @@ end
124124
--- This one is not used for the giants baleloaders
125125
function BaleLoaderController:isReadyToLoadNextBale()
126126
return false
127+
end
128+
129+
function BaleLoaderController:onPreFinished()
130+
return self:canBeFolded()
127131
end

scripts/ai/controllers/BalerController.lua

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ function BalerController:init(vehicle, baler)
2626
self.slowDownStartSpeed = 20
2727
self.balerSpec = self.baler.spec_baler
2828
self.baleWrapperSpec = self.baler.spec_baleWrapper
29+
self.baleLoaderSpec = self.baler.spec_baleLoader
2930
self.lastDroppedBale = CpTemporaryObject()
3031
self:debug('Baler controller initialized')
3132
local additives = self.balerSpec.additives
@@ -124,11 +125,20 @@ function BalerController:onStart()
124125
end
125126
end
126127

127-
function BalerController:onFinished(hasFinished)
128-
-- TODO: not working, as this probably needs to be called, before the drive is released.
129-
-- if hasFinished and not self.balerSpec.automaticDrop or not self.balerSpec.platformAutomaticDrop then
130-
-- Baler.actionEventUnloading(self.implement)
131-
-- end
128+
function BalerController:onPreFinished(hasFinished)
129+
if hasFinished then
130+
Baler.actionEventUnloading(self.baler)
131+
if self.balerSpec.platformDropInProgress then
132+
return
133+
end
134+
if self.balerSpec.isBaleUnloading then
135+
return
136+
end
137+
if self.balerSpec.unloadingState ~= Baler.UNLOADING_CLOSED then
138+
return
139+
end
140+
end
141+
return true
132142
end
133143

134144
function BalerController:isThisMyBale(baleObject)

scripts/ai/controllers/ImplementController.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ function ImplementController:onFinished(hasFinished)
9393
--- override
9494
end
9595

96+
function ImplementController:onPreFinished()
97+
return true
98+
end
99+
96100
function ImplementController:onFinishRow(isHeadlandTurn)
97101
end
98102

scripts/ai/jobs/CpAIJob.lua

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,6 @@ function CpAIJob:setupCpJobParameters(jobParameters)
5858
self.cpJobParameters:validateSettings()
5959
end
6060

61-
--- Is the ai job allowed to finish ?
62-
--- This entry point allowes us to catch giants stop conditions.
63-
---@param message table Stop reason can be used to reverse engineer the cause.
64-
---@return boolean
65-
function CpAIJob:isFinishingAllowed(message)
66-
return true
67-
end
68-
6961
--- Gets the first task to start with.
7062
function CpAIJob:getStartTaskIndex()
7163
if self.currentTaskIndex ~= 0 or self.isDirectStart or self:isTargetReached() then
@@ -137,12 +129,8 @@ function CpAIJob:stop(aiMessage)
137129
vehicle:deleteAgent()
138130
vehicle:aiJobFinished()
139131
vehicle:resetCpAllActiveInfoTexts()
140-
local driveStrategy = vehicle:getCpDriveStrategy()
141132
if not aiMessage then
142133
self:debug("No valid ai message given!")
143-
if driveStrategy then
144-
driveStrategy:onFinished()
145-
end
146134
AIJob.stop(self, aiMessage)
147135
return
148136
end
@@ -160,9 +148,6 @@ function CpAIJob:stop(aiMessage)
160148
if event then
161149
SpecializationUtil.raiseEvent(vehicle, event)
162150
end
163-
if driveStrategy then
164-
driveStrategy:onFinished(hasFinished)
165-
end
166151
g_messageCenter:unsubscribeAll(self)
167152
end
168153

scripts/ai/jobs/CpAIJobFieldWork.lua

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,29 +47,6 @@ function CpAIJobFieldWork:setupJobParameters()
4747
self:setupCpJobParameters(CpFieldWorkJobParameters(self))
4848
end
4949

50-
function CpAIJobFieldWork:isFinishingAllowed(message)
51-
local nextTaskIndex = self:getNextTaskIndex()
52-
if message:isa(AIMessageErrorOutOfFill) then
53-
--- At least one implement type needs to be refilled.
54-
55-
local vehicle = self:getVehicle()
56-
local setting = vehicle:getCpSettings().refillOnTheField
57-
58-
if setting:getValue() == CpVehicleSettings.REFILL_ON_FIELD_DISABLED then
59-
return true
60-
elseif setting:getValue() == CpVehicleSettings.REFILL_ON_FIELD_WAITING then
61-
if self.currentTaskIndex == self.fieldWorkTask.taskIndex then
62-
self.fieldWorkTask:setWaitingForRefillingActive()
63-
end
64-
elseif setting:getValue() == CpVehicleSettings.REFILL_ON_FIELD_ACTIVE then
65-
--- TODO_25 Add driving to trailer for refilling here and so on ..
66-
self.fieldWorkTask:skip()
67-
end
68-
return false
69-
end
70-
return CpAIJob.isFinishingAllowed(self, message)
71-
end
72-
7350
---@param vehicle table
7451
---@param mission table
7552
---@param farmId number

scripts/ai/strategies/AIDriveStrategyCourse.lua

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,16 @@ AIDriveStrategyCourse.myStates = {
2626
INITIAL = {},
2727
WAITING_FOR_PATHFINDER = {},
2828
WAITING_FOR_FIELD_BOUNDARY_DETECTION = {},
29+
PRE_FINISHED = {},
30+
WAITING_FOR_FINISHED = {},
31+
FINISHED = {}
2932
}
3033

3134
--- Implement controller events.
3235
--- TODO_25 a more generic implementation
3336
AIDriveStrategyCourse.onRaisingEvent = "onRaising"
3437
AIDriveStrategyCourse.onLoweringEvent = "onLowering"
38+
AIDriveStrategyCourse.onPreFinishedEvent = "onPreFinished"
3539
AIDriveStrategyCourse.onFinishedEvent = "onFinished"
3640
AIDriveStrategyCourse.onStartEvent = "onStart"
3741
AIDriveStrategyCourse.onStartRefillingEvent = "onStartRefilling"
@@ -59,6 +63,11 @@ function AIDriveStrategyCourse:init(task, job)
5963

6064
self.currentTask = task
6165
self.job = job
66+
self.stopRequestData = {
67+
reason = nil,
68+
waitForFolding = false,
69+
prepareTimeout = CpTemporaryObject(true)
70+
}
6271
end
6372

6473
function AIDriveStrategyCourse:setCurrentTaskFinished()
@@ -279,6 +288,10 @@ end
279288

280289
--- Called in the low frequency function for the helper.
281290
function AIDriveStrategyCourse:updateLowFrequencyImplementControllers()
291+
if self:hasFinished() then
292+
--- Small hack so every ai drive strategy waits during finished.
293+
self:setMaxSpeed(0)
294+
end
282295
for _, controller in pairs(self.controllers) do
283296
---@type ImplementController
284297
if controller:isEnabled() then
@@ -435,8 +448,57 @@ function AIDriveStrategyCourse:update(dt)
435448
self.pathfinderController:update(dt)
436449
self:updatePathfinding()
437450
self:updateInfoTexts()
451+
self:updateFinishing()
452+
end
453+
454+
function AIDriveStrategyCourse:updateFinishing()
455+
local function finishStrategy()
456+
if self.stopRequestData.stopReason then
457+
g_currentMission.aiSystem:stopJob(self.job, self.stopRequestData.stopReason)
458+
return
459+
end
460+
self.currentTask:skip()
461+
end
462+
if self.state == self.states.PRE_FINISHED then
463+
--- Every implement controller gets the chance to
464+
--- prepare for the driver release.
465+
--- For example balers can unload their bales
466+
--- before we can fold them and so on.
467+
local finished = true
468+
for _, controller in ipairs(self.controllers) do
469+
finished = finished and controller:onPreFinished()
470+
end
471+
if finished then
472+
self:debug("Precondition for stopping the strategy are reached.")
473+
self.state = self.states.FINISHED
474+
end
475+
elseif self.state == self.states.FINISHED then
476+
self:raiseControllerEvent(self.onFinishedEvent, self.stopRequestData.waitForFolding)
477+
if self.stopRequestData.waitForFolding then
478+
self:debug("Starting to fold implements and so on...")
479+
self.vehicle:prepareForAIDriving()
480+
self.stopRequestData.prepareTimeout:set(false, 15000)
481+
self.state = self.states.WAITING_FOR_FINISHED
482+
else
483+
finishStrategy()
484+
end
485+
elseif self.state == self.states.WAITING_FOR_FINISHED then
486+
if not self.vehicle:getIsAIPreparingToDrive() or self.stopRequestData.prepareTimeout:get() then
487+
if self.stopRequestData.prepareTimeout:get() then
488+
self:debug("Failed to prepare ai drive, aborting ..")
489+
end
490+
finishStrategy()
491+
end
492+
end
438493
end
439494

495+
--- Job has finished and we are now waiting to release the driver.
496+
---@return boolean
497+
function AIDriveStrategyCourse:hasFinished()
498+
return self.state == self.states.PRE_FINISHED or
499+
self.state == self.states.WAITING_FOR_FINISHED or
500+
self.state == self.states.FINISHED
501+
end
440502

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

729+
--- Possiblity to override the vehicle:stopCurrentAIJob(),
730+
--- if for example refilling on the field is active.
731+
function AIDriveStrategyCourse:handleFinishedRequest(stopReason)
732+
--- override
733+
return false
734+
end
735+
667736
--- Event raised when the driver was stopped.
668737
---@param hasFinished boolean|nil flag passed by the info text
669-
function AIDriveStrategyCourse:onFinished(hasFinished)
670-
self:raiseControllerEvent(self.onFinishedEvent, hasFinished)
671-
if hasFinished and self.settings.foldImplementAtEnd:getValue() then
672-
--- Folds implements at the end if the setting is active.
673-
self:debug("Finished with folding implements of the implements.")
674-
self.vehicle:prepareForAIDriving()
738+
---@param stopReason table
739+
function AIDriveStrategyCourse:onFinished(hasFinished, stopReason)
740+
if self:handleFinishedRequest(stopReason) then
741+
--- Stop request ignored
742+
return
743+
end
744+
if self:hasFinished() then
745+
--- Driver is already stopping and still waiting for folding and so on ...
746+
return
675747
end
748+
self:setFinished(hasFinished and self.settings.foldImplementAtEnd:getValue(), stopReason)
749+
end
750+
751+
--- Internal stop request
752+
---@param waitForFolding boolean|nil wait until for the folding and so on ..
753+
---@param stopReason table|nil if nil is given, then the task will be skipped
754+
function AIDriveStrategyCourse:setFinished(waitForFolding, stopReason)
755+
self.stopRequestData.stopReason = stopReason
756+
self.stopRequestData.waitForFolding = waitForFolding
757+
self.state = self.states.PRE_FINISHED
676758
end
677759

678760
--- This is to set the offsets on the course at start, or update those values

scripts/ai/strategies/AIDriveStrategyFieldWorkCourse.lua

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,28 @@ function AIDriveStrategyFieldWorkCourse:prepareForFieldWork()
108108
end
109109

110110
--- Event raised when the driver has finished.
111-
function AIDriveStrategyFieldWorkCourse:onFinished(hasFinished)
112-
AIDriveStrategyCourse.onFinished(self, hasFinished)
111+
function AIDriveStrategyFieldWorkCourse:onFinished(...)
112+
AIDriveStrategyCourse.onFinished(self, ...)
113113
self.remainingTime:reset()
114114
end
115115

116+
function AIDriveStrategyFieldWorkCourse:handleFinishedRequest(stopReason)
117+
--- TODO consolidate this logic in the future ...
118+
if stopReason:isa(AIMessageErrorOutOfFill) then
119+
--- At least one implement type needs to be refilled.
120+
local setting = self.settings.refillOnTheField
121+
if setting:getValue() == CpVehicleSettings.REFILL_ON_FIELD_WAITING then
122+
self.currentTask:setWaitingForRefillingActive()
123+
return true
124+
elseif setting:getValue() == CpVehicleSettings.REFILL_ON_FIELD_ACTIVE then
125+
--- TODO_25 Add driving to trailer for refilling here and so on ..
126+
self:setFinished(true, nil)
127+
return true
128+
end
129+
end
130+
return false
131+
end
132+
116133
function AIDriveStrategyFieldWorkCourse:update(dt)
117134
AIDriveStrategyCourse.update(self, dt)
118135
if CpDebug:isChannelActive(CpDebug.DBG_TURN, self.vehicle) then

scripts/ai/strategies/AIDriveStrategyFindBales.lua

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@ AIDriveStrategyFindBales.myStates = {
3131
WORKING_ON_BALE = {},
3232
REVERSING_AFTER_PATHFINDER_FAILURE = {},
3333
REVERSING_DUE_TO_OBSTACLE_AHEAD = {},
34-
DRIVING_TO_START_MARKER = {},
35-
WAITING_FOR_IMPLEMENTS_TO_FOLD = {}
34+
DRIVING_TO_START_MARKER = {}
3635
}
3736
--- Offset to apply at the goal marker, so we don't crash with an empty unloader waiting there with the same position.
3837
AIDriveStrategyFindBales.invertedGoalPositionOffset = -4.5
@@ -85,7 +84,7 @@ function AIDriveStrategyFindBales:collectNextBale()
8584
self:findPathToNextBale()
8685
return
8786
end
88-
self.state = self.states.WAITING_FOR_IMPLEMENTS_TO_FOLD
87+
self.state = self.states.DRIVING_TO_START_MARKER
8988
end
9089
end
9190

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

317-
--- Sets the driver as finished, so either a path
318-
--- to the start marker as a park position can be used
319-
--- or the driver stops directly.
320-
function AIDriveStrategyFindBales:setFinished()
321-
if not self:isReadyToFoldImplements() then
322-
-- Waiting until the folding has finished..
323-
self:debugSparse("Waiting until an animation has finish, so the driver can be released ..")
324-
return
325-
end
326-
self.vehicle:prepareForAIDriving()
327-
if not self.vehicle:getIsAIReadyToDrive() then
328-
-- Waiting until the folding has finished..
329-
self:debugSparse("Waiting until an animation has finish, so the driver can be released ..")
330-
return
331-
end
332-
if self.invertedStartPositionMarkerNode then
333-
self:debug("A valid start position is found, so the driver tries to finish at the inverted goal node")
334-
self:startPathfindingToStartMarker()
335-
else
336-
self:finishJob()
337-
end
338-
end
339-
340316
--- Finishes the job with the correct stop reason, as
341317
--- the correct reason is needed for a possible AD takeover.
342318
function AIDriveStrategyFindBales:finishJob()
@@ -566,9 +542,6 @@ function AIDriveStrategyFindBales:getDriveData(dt, vX, vY, vZ)
566542
self:setMaxSpeed(self.settings.reverseSpeed:getValue())
567543
elseif self.state == self.states.DRIVING_TO_START_MARKER then
568544
self:setMaxSpeed(self.settings.fieldSpeed:getValue())
569-
elseif self.state == self.states.WAITING_FOR_IMPLEMENTS_TO_FOLD then
570-
self:setFinished()
571-
self:setMaxSpeed(0) --- folding
572545
end
573546

574547
local moveForwards = not self.ppc:isReversing()
@@ -685,10 +658,14 @@ function AIDriveStrategyFindBales:update(dt)
685658
self.ppc:getCourse():draw()
686659
end
687660
end
688-
if self.state ~= self.states.DRIVING_TO_START_MARKER and
689-
self.state ~= self.states.WAITING_FOR_IMPLEMENTS_TO_FOLD then
661+
if self.state ~= self.states.DRIVING_TO_START_MARKER then
690662
if self:areBaleLoadersFull() then
691-
self.state = self.states.WAITING_FOR_IMPLEMENTS_TO_FOLD
663+
if self.invertedStartPositionMarkerNode then
664+
self:debug("A valid start position is found, so the driver tries to finish at the inverted goal node")
665+
self:startPathfindingToStartMarker()
666+
else
667+
self:finishJob()
668+
end
692669
end
693670
end
694671
--- Ignores the loaded auto loader bales.

0 commit comments

Comments
 (0)