This repository has been archived by the owner on May 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 525
/
BaleLoaderAIDriver.lua
238 lines (212 loc) · 10.3 KB
/
BaleLoaderAIDriver.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
--[[
This file is part of Courseplay (https://github.com/Courseplay/courseplay)
Copyright (C) 2019 Peter Vaiko
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
]]
---@class BaleLoaderAIDriver : UnloadableFieldworkAIDriver
BaleLoaderAIDriver = CpObject(UnloadableFieldworkAIDriver)
BaleLoaderAIDriver.myStates = {
APPROACHING_UNLOAD_POINT = {},
UNLOADING = {},
MOVE_FORWARD_AFTER_UNLOADING = {}
}
--- Make sure the the bale loader behaves like a proper AIImplement and reacts on AIImplementStart/End
--- events so there's no special handling is needed elsewhere.
function BaleLoaderAIDriver.register()
BaleLoader.onAIImplementStart = Utils.overwrittenFunction(BaleLoader.onAIImplementStart,
function(self, superFunc)
if superFunc ~= nil then superFunc(self) end
self:doStateChange(BaleLoader.CHANGE_MOVE_TO_WORK);
end)
BaleLoader.onAIImplementEnd = Utils.overwrittenFunction(BaleLoader.onAIImplementEnd,
function(self, superFunc)
if superFunc ~= nil then superFunc(self) end
local spec = self.spec_baleLoader
if not spec.grabberIsMoving and spec.grabberMoveState == nil and spec.isInWorkPosition then
self:doStateChange(BaleLoader.CHANGE_MOVE_TO_TRANSPORT);
end
end)
local baleLoaderRegisterEventListeners = function(vehicleType)
print('## Courseplay: Registering event listeners for bale loader')
SpecializationUtil.registerEventListener(vehicleType, "onAIImplementStart", BaleLoader)
SpecializationUtil.registerEventListener(vehicleType, "onAIImplementEnd", BaleLoader)
end
print('## Courseplay: Appending event listener for bale loaders')
BaleLoader.registerEventListeners = Utils.appendedFunction(BaleLoader.registerEventListeners, baleLoaderRegisterEventListeners)
end
function BaleLoaderAIDriver:init(vehicle)
courseplay.debugVehicle(courseplay.DBG_AI_DRIVER,vehicle,'BaleLoaderAIDriver:init()')
UnloadableFieldworkAIDriver.init(self, vehicle)
self:initStates(BaleLoaderAIDriver.myStates)
self.unloadDoneX, self.unloadDoneZ = 0, 0
end
function BaleLoaderAIDriver:start(startingPoint)
self:initializeBaleLoader()
self.unloadRefillState = nil
UnloadableFieldworkAIDriver.start(self, startingPoint)
end
function BaleLoaderAIDriver:initializeBaleLoader()
self.baleLoader = AIDriverUtil.getImplementWithSpecialization(self.vehicle, BaleLoader)
if self.baleLoader then
self:debug('baleloader %s', tostring(self.baleLoader))
-- Bale loaders have no AI markers (as they are not AIImplements according to Giants) so add a function here
-- to get the markers
self.baleLoader.getAIMarkers = function(object)
return UnloadableFieldworkAIDriver.getAIMarkersFromGrabberNode(object, object.spec_baleLoader)
end
self.manualUnloadNode = WaypointNode(self.vehicle:getName() .. 'unloadNode')
if self.baleLoader.cp.realUnloadOrFillNode then
-- use that realUnloadOrFillNode for now as it includes the balerUnloadDistance config value
-- TODO: can we just use the back marker node here as well?
self.baleFinderProximitySensorPack = BackwardLookingProximitySensorPack(
self.vehicle, self.ppc, self.baleLoader.cp.realUnloadOrFillNode, 5, 1)
end
self:debug('Initialized, bale loader: %s', self.baleLoader:getName())
else
self:debug('Has now bale loader specialization')
end
end
---@return boolean true if unload took over the driving
function BaleLoaderAIDriver:driveUnloadOrRefill(dt)
self:updateOffset()
self:updateFillType()
-- by default, drive street/recorded speed.
self:setSpeed(self:getRecordedSpeed())
if not self.ppc:isReversing() then
-- 'cause reverse does the raycasting for us
self:searchForTipTriggers()
end
-- use the relevant node, not the current one to determine we are around the unload point as the current changes
-- when we change direction
local nearUnloadPoint, unloadPointIx = self.course:hasUnloadPointWithinDistance(self.ppc:getRelevantWaypointIx(), 50)
if self:hasTipTrigger() or nearUnloadPoint then
self:setSpeed(self.vehicle.cp.speeds.approach)
if self:haveBales() and self.unloadRefillState == nil then
self.unloadRefillState = self.states.APPROACHING_UNLOAD_POINT
self:debug('Approaching unload point.')
elseif self:haveBales() and self.unloadRefillState == self.states.APPROACHING_UNLOAD_POINT then
local d = self:getDistanceFromUnloadNode(nearUnloadPoint, unloadPointIx)
self:debugSparse('distance to unload point: %.1f', d)
if math.abs(d) < 1 or self:tooCloseToOtherBales() then
self:debug('Unload point reached.')
self.unloadRefillState = self.states.UNLOADING
end
elseif self.unloadRefillState == self.states.MOVE_FORWARD_AFTER_UNLOADING then
self:setSpeed(self.vehicle.cp.speeds.approach)
local x, _, z = getWorldTranslation(self.vehicle.rootNode)
local dFromUnloadDonePosition = courseplay:distance(x, z, self.unloadDoneX, self.unloadDoneZ)
if math.abs(dFromUnloadDonePosition) > 3 then
self:debug('Moved away from unload point')
-- transition to the next stage (out of EMPTY_WAIT_TO_SINK)
g_client:getServerConnection():sendEvent(BaleLoaderStateEvent:new(self.baleLoader, BaleLoader.CHANGE_BUTTON_EMPTY))
-- continue pressing the button until we folded everything back
self.unloadRefillState = self.states.UNLOADING
end
elseif self.unloadRefillState == self.states.UNLOADING then
self:setSpeed(0)
-- don't do this in every update loop, sending events does not make sense so often
if g_updateLoopIndex % 100 == 0 then
self:debug('Unloading, emptyState=%d.', self.baleLoader.spec_baleLoader.emptyState)
if self.baleLoader.spec_baleLoader.emptyState == BaleLoader.EMPTY_WAIT_TO_SINK then
self:debug('Bales unloaded, moving forward a bit, emptyState: %d...', self.baleLoader.spec_baleLoader.emptyState)
self.unloadRefillState = self.states.MOVE_FORWARD_AFTER_UNLOADING
self.unloadDoneX, _, self.unloadDoneZ = getWorldTranslation(self.vehicle.rootNode)
-- make sure we are driving forward now
self.ppc:initialize(self.course:getNextFwdWaypointIx(self.ppc:getCurrentWaypointIx()));
-- EMPTY_NONE is the base position (loaded or unloaded)
elseif self:haveBales() or self.baleLoader.spec_baleLoader.emptyState ~= BaleLoader.EMPTY_NONE then
-- this is like keep pressing the 'Unload' button. Not nice, should probably check the current state
-- of the bale loader, but it is so simple like this and works ...
self:debug('Press unload button, emptyState: %d...', self.baleLoader.spec_baleLoader.emptyState)
g_client:getServerConnection():sendEvent(BaleLoaderStateEvent:new(self.baleLoader, BaleLoader.CHANGE_BUTTON_EMPTY))
else
self:debug('Bales unloaded, continue course.')
courseplay:resetTipTrigger(self.vehicle)
self.unloadRefillState = nil
self.ppc:initialize(self.course:getNextFwdWaypointIx(self.ppc:getCurrentWaypointIx()));
end
end
end
else
-- for some bale loaders the onAIImplementEnd() does not work seemingly because the grabber is still busy
-- with the last bale when switching to the unload course. Make sure here that it is moved to transport
-- position on the unload course
local spec = self.baleLoader.spec_baleLoader
if not spec.grabberIsMoving and spec.grabberMoveState == nil and spec.isInWorkPosition then
self:debug('Move grabber to transport position')
self.baleLoader:doStateChange(BaleLoader.CHANGE_MOVE_TO_TRANSPORT);
end
end
return false
end
function BaleLoaderAIDriver:haveBales()
return self.baleLoader:getFillUnitFillLevel(self.baleLoader.spec_baleLoader.fillUnitIndex) > 0
end
function BaleLoaderAIDriver:updateFillType()
if not self.fillType then
self.fillType = self:getFillType()
end
-- This is an ugly hack here here to overwrite the legacy CP code's fillType as it is not
-- able to handle the bale loaders. TODO: needs a more professional approach
self.baleLoader.cp.fillType = self.fillType
self:debugSparse('Bale filltype %s', self.fillType)
end
-- Getting fill type for bale loaders is not straightforward as the bales have the actual fill type,
-- the fill type what the trigger has, like STRAW, so just go through our bales and pick the first
-- fill type we find.
function BaleLoaderAIDriver:getFillType()
local spec = self.baleLoader.spec_baleLoader
for _, balePlace in pairs(spec.balePlaces) do
if balePlace.bales then
for _, baleServerId in pairs(balePlace.bales) do
local bale = NetworkUtil.getObject(baleServerId)
if bale ~= nil then
return bale:getFillType()
end
end
end
end
end
--- Unload node is either an unload waypoint or an unload trigger
function BaleLoaderAIDriver:getDistanceFromUnloadNode(isUnloadpoint, unloadPointIx)
if isUnloadpoint then
self:debugSparse('manual unload point at ix = %d', unloadPointIx)
self.manualUnloadNode:setToWaypoint(self.course, unloadPointIx)
-- don't use dz here as the course to the manual unload point as it is often on a reverse
-- section and other parts of the course may be very close to the unload point, triggering
-- this way too early
return calcDistanceFrom(self.manualUnloadNode.node, self.baleLoader.cp.realUnloadOrFillNode)
else
local _, _, d = localToLocal(self.vehicle.cp.currentTipTrigger.triggerId,
self.baleLoader.cp.realUnloadOrFillNode, 0, 0, 0)
return d
end
end
--- When backing up to unload, see if we have to stop because there are bales already there
function BaleLoaderAIDriver:tooCloseToOtherBales()
if self.baleFinderProximitySensorPack then
local distance, _, object = self.baleFinderProximitySensorPack:getClosestObjectDistanceAndRootVehicle()
if object and object:isa(Bale) then
-- round bales have diameter, others height, thanks Giants!
local baleHeight = object.baleDiameter and object.baleDiameter or
(object.baleHeight and object.baleHeight or 0.8)
self:debugSparse('Bale found at d=%.1f, bale height %.1f', distance, baleHeight)
if distance < (baleHeight + 1) then
return true
else
return false
end
end
else
return false
end
end