feat: bedste foder først — lavere foder kun når truget er nødlidende (≥75 % fri); forsynings-traktorer uden noget at hente holder ved Wait (v1.14.0.0)
This commit is contained in:
@@ -421,6 +421,16 @@ local function startHold(adEnv, vehicle, run, reasons, sourcesById)
|
|||||||
return choiceFor(marker, source, nil)
|
return choiceFor(marker, source, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Forsynings-traktor uden noget at hente: hold ved et Wait-punkt i run'ets pulje; replan() returnerer
|
||||||
|
-- waypoint-id for næste kilde (eller nil) og kaldes hvert CHECK_INTERVAL_MS.
|
||||||
|
function ADOutbound.holdSupplyVehicle(adEnv, vehicle, run, reasons, replan)
|
||||||
|
local choice = startHold(adEnv, vehicle, run, reasons, {})
|
||||||
|
if choice ~= nil and holds[vehicle] ~= nil then
|
||||||
|
holds[vehicle].replan = replan
|
||||||
|
end
|
||||||
|
return choice
|
||||||
|
end
|
||||||
|
|
||||||
-- Hook 1: AutoDrive spørger om næste pålæsning. Tur -> kilden; ingen tur -> Wait-punkt.
|
-- Hook 1: AutoDrive spørger om næste pålæsning. Tur -> kilden; ingen tur -> Wait-punkt.
|
||||||
function ADOutbound.choosePickup(adEnv, vehicle)
|
function ADOutbound.choosePickup(adEnv, vehicle)
|
||||||
local run = getRun(vehicle)
|
local run = getRun(vehicle)
|
||||||
@@ -494,6 +504,17 @@ local function updateHold(adEnv, task, hold, dt)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
hold.timerMs = 0
|
hold.timerMs = 0
|
||||||
|
-- forsynings-traktor: egen genplanlægning (ADSmartPickup) giver en kilde-markør at køre til
|
||||||
|
if hold.replan ~= nil then
|
||||||
|
local ok, targetId = pcall(hold.replan)
|
||||||
|
if ok and targetId ~= nil then
|
||||||
|
releaseHold(vehicle)
|
||||||
|
ADWaitPool.redirect(task, targetId)
|
||||||
|
task.activatedUALLoading = false
|
||||||
|
Logging.info(ADOutbound.LOG_PREFIX .. "'%s' kører fra Wait: der er noget at hente igen", tostring(vehicle:getName()))
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
ADOutbound.choosePickup(adEnv, vehicle)
|
ADOutbound.choosePickup(adEnv, vehicle)
|
||||||
local state = trips[vehicle]
|
local state = trips[vehicle]
|
||||||
if state ~= nil then
|
if state ~= nil then
|
||||||
|
|||||||
@@ -292,6 +292,7 @@ local function findBestSource(markerStations, fillTypes, farmId)
|
|||||||
end
|
end
|
||||||
return {
|
return {
|
||||||
markerIndex = choice.marker.markerIndex,
|
markerIndex = choice.marker.markerIndex,
|
||||||
|
markerId = choice.marker.id,
|
||||||
markerName = choice.marker.name,
|
markerName = choice.marker.name,
|
||||||
group = choice.marker.group,
|
group = choice.marker.group,
|
||||||
level = choice.level,
|
level = choice.level,
|
||||||
@@ -592,11 +593,15 @@ function ADSmartPickup.findSupplyPickup(adEnv, driver, ingredients, targetWayPoi
|
|||||||
local allMarkers = adEnv.ADGraphManager:getMapMarkers()
|
local allMarkers = adEnv.ADGraphManager:getMapMarkers()
|
||||||
local markerStations = collectMarkerStations(adEnv, allMarkers, farmId, target.owningPlaceable)
|
local markerStations = collectMarkerStations(adEnv, allMarkers, farmId, target.owningPlaceable)
|
||||||
local loadCapacity = getLoadCapacity(adEnv, driver)
|
local loadCapacity = getLoadCapacity(adEnv, driver)
|
||||||
for _, need in ipairs(ADSupplyPlanner.rankNeeds(getTargetNeeds(target, ingredients, farmId, demand), loadCapacity)) do
|
local ranked = ADSupplyPlanner.rankNeeds(getTargetNeeds(target, ingredients, farmId, demand), loadCapacity)
|
||||||
local choice = findBestSource(markerStations, {need.fillType}, farmId)
|
-- bedste foder først: lavere foder kun når truget er nødlidende (ADSupplyPlanner.isFoodAllowed)
|
||||||
|
local bestFood = ADSupplyPlanner.bestFoodPriority ~= nil and ADSupplyPlanner.bestFoodPriority(ranked) or nil
|
||||||
|
for _, need in ipairs(ranked) do
|
||||||
|
local choice = (ADSupplyPlanner.isFoodAllowed == nil or ADSupplyPlanner.isFoodAllowed(need, bestFood)) and findBestSource(markerStations, {need.fillType}, farmId) or nil
|
||||||
if choice ~= nil then
|
if choice ~= nil then
|
||||||
return {
|
return {
|
||||||
markerIndex = choice.markerIndex,
|
markerIndex = choice.markerIndex,
|
||||||
|
markerId = choice.markerId,
|
||||||
markerName = choice.markerName,
|
markerName = choice.markerName,
|
||||||
group = choice.group,
|
group = choice.group,
|
||||||
level = choice.level,
|
level = choice.level,
|
||||||
@@ -691,6 +696,25 @@ function ADSmartPickup.choosePickup(adEnv, driver)
|
|||||||
|
|
||||||
local choice = ADSmartPickup.findSupplyPickup(adEnv, driver, ingredients)
|
local choice = ADSmartPickup.findSupplyPickup(adEnv, driver, ingredients)
|
||||||
if choice == nil then
|
if choice == nil then
|
||||||
|
-- Traktor i et forsynings-run uden noget (tilladt) at hente: hold ved Wait og prøv igen, i stedet
|
||||||
|
-- for at falde tilbage til mappe-logikken (den henter hvad som helst, fx ensilage i et halvfuldt trug).
|
||||||
|
local run = ADRunsController ~= nil and ADRunsController.getRunForVehicle(driver) or nil
|
||||||
|
if run ~= nil and (run.kind or "supply") == "supply" and ADOutbound ~= nil and ADOutbound.holdSupplyVehicle ~= nil then
|
||||||
|
local replan = function()
|
||||||
|
local list = ADSmartPickup.getIngredientsFor(adEnv, driver)
|
||||||
|
local next = list ~= nil and ADSmartPickup.findSupplyPickup(adEnv, driver, list) or nil
|
||||||
|
if next == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
applySupplyChoice(driver, list, next)
|
||||||
|
activePickups[driver] = {station = next.station, fillTypes = next.fillTypes, markerName = next.markerName, fillType = next.fillType}
|
||||||
|
return next.markerId
|
||||||
|
end
|
||||||
|
local held = ADOutbound.holdSupplyVehicle(adEnv, driver, run, {"intet at hente (bedste foder mangler, truget ikke nødlidende)"}, replan)
|
||||||
|
if held ~= nil then
|
||||||
|
return held
|
||||||
|
end
|
||||||
|
end
|
||||||
return ADSmartPickup.findLeastFilledPickup(adEnv, driver)
|
return ADSmartPickup.findLeastFilledPickup(adEnv, driver)
|
||||||
end
|
end
|
||||||
applySupplyChoice(driver, ingredients, choice)
|
applySupplyChoice(driver, ingredients, choice)
|
||||||
@@ -855,14 +879,16 @@ local function installHook(adEnv)
|
|||||||
activePickups[driver] = nil
|
activePickups[driver] = nil
|
||||||
return originalGetNextPickup(self, driver, forcedSkip)
|
return originalGetNextPickup(self, driver, forcedSkip)
|
||||||
end
|
end
|
||||||
activePickups[driver] = {station = choice.station, fillTypes = choice.fillTypes, markerName = choice.markerName, fillType = choice.fillType or choice.fillTypes[1]}
|
activePickups[driver] = choice.station ~= nil and {station = choice.station, fillTypes = choice.fillTypes, markerName = choice.markerName, fillType = choice.fillType or choice.fillTypes[1]} or nil
|
||||||
|
|
||||||
-- hold AutoDrives egen rotations-tilstand konsistent med vores valg
|
-- hold AutoDrives egen rotations-tilstand konsistent med vores valg
|
||||||
self.pickups[choice.group] = self.pickups[choice.group] or {}
|
self.pickups[choice.group] = self.pickups[choice.group] or {}
|
||||||
self.pickups[choice.group].lastTarget = choice.markerIndex
|
self.pickups[choice.group].lastTarget = choice.markerIndex
|
||||||
self.pickups[choice.group].lastVehicle = driver
|
self.pickups[choice.group].lastVehicle = driver
|
||||||
|
|
||||||
logChoiceOnce(driver, choice)
|
if choice.level ~= nil then
|
||||||
|
logChoiceOnce(driver, choice) -- et hold ved Wait logges af ADOutbound
|
||||||
|
end
|
||||||
return choice.markerIndex
|
return choice.markerIndex
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -191,6 +191,7 @@ function ADSupplyPlanner.rankNeeds(needs, loadCapacity)
|
|||||||
fits = ADSupplyPlanner.fullLoadFits(freeSpace, loadCapacity),
|
fits = ADSupplyPlanner.fullLoadFits(freeSpace, loadCapacity),
|
||||||
score = need.scoreOverride or ADSupplyPlanner.scoreNeed(need.level, need.capacity, freeSpace, minutesToEmpty),
|
score = need.scoreOverride or ADSupplyPlanner.scoreNeed(need.level, need.capacity, freeSpace, minutesToEmpty),
|
||||||
priority = need.priority or 0,
|
priority = need.priority or 0,
|
||||||
|
trough = need.scoreOverride ~= nil,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -209,6 +210,27 @@ function ADSupplyPlanner.rankNeeds(needs, loadCapacity)
|
|||||||
return ranked
|
return ranked
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- ---- bedste foder først ----
|
||||||
|
-- Foder i et stald-trug deler plads. Lavere foder (ensilage/hø, vægt 0,8) må kun hentes når der er
|
||||||
|
-- bedre foder (TMR, 1,0) på listen, hvis truget er nødlidende: mindst så stor en andel fri.
|
||||||
|
ADSupplyPlanner.EMERGENCY_TROUGH_FREE = 0.75
|
||||||
|
|
||||||
|
-- need.trough = true for foder i et fælles trug; need.score = trugets fri andel; need.priority = fodervægt.
|
||||||
|
function ADSupplyPlanner.bestFoodPriority(needs)
|
||||||
|
local best = nil
|
||||||
|
for _, need in ipairs(needs or {}) do
|
||||||
|
if need.trough and (best == nil or (need.priority or 0) > best) then best = need.priority or 0 end
|
||||||
|
end
|
||||||
|
return best
|
||||||
|
end
|
||||||
|
|
||||||
|
function ADSupplyPlanner.isFoodAllowed(need, bestPriority)
|
||||||
|
if not need.trough or bestPriority == nil or (need.priority or 0) >= bestPriority then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return (need.score or 0) >= ADSupplyPlanner.EMERGENCY_TROUGH_FREE
|
||||||
|
end
|
||||||
|
|
||||||
-- state: nil eller {ingredients, narrowedTo} fra forrige tur. selected: AD's aktuelle varevalg.
|
-- state: nil eller {ingredients, narrowedTo} fra forrige tur. selected: AD's aktuelle varevalg.
|
||||||
-- >1 valgt = (ny) ingrediensliste. Præcis den vare vi selv indsnævrede til = gemt liste.
|
-- >1 valgt = (ny) ingrediensliste. Præcis den vare vi selv indsnævrede til = gemt liste.
|
||||||
-- Alt andet = brugeren har valgt noget andet -> nil (behovsstyring fra).
|
-- Alt andet = brugeren har valgt noget andet -> nil (behovsstyring fra).
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<modDesc descVersion="107">
|
<modDesc descVersion="107">
|
||||||
<author>masterdraco</author>
|
<author>masterdraco</author>
|
||||||
<version>1.13.7.0</version>
|
<version>1.14.0.0</version>
|
||||||
<title>
|
<title>
|
||||||
<en>AD Smart Pickup</en>
|
<en>AD Smart Pickup</en>
|
||||||
</title>
|
</title>
|
||||||
|
|||||||
@@ -770,6 +770,31 @@ check("E5 uden TMR-kilde: hø før græs", manager:getNextPickup(e5Driver, false
|
|||||||
-- trug 70 % fuldt (30 % trang) vs. halm 80 % tomt -> halm
|
-- trug 70 % fuldt (30 % trang) vs. halm 80 % tomt -> halm
|
||||||
e5Food[HAY] = 21000
|
e5Food[HAY] = 21000
|
||||||
check("E5 halmlager tommere end truget -> halm", manager:getNextPickup(e5Driver, false), 21)
|
check("E5 halmlager tommere end truget -> halm", manager:getNextPickup(e5Driver, false), 21)
|
||||||
|
-- E6 (bedste foder først): TMR-kilden er tom, truget halvfuldt, halm fuld -> INGEN ensilage/hø; en traktor
|
||||||
|
-- i et forsynings-run holder ved Wait i stedet (in-game 24/9: Fastrac'erne fyldte truget med ensilage)
|
||||||
|
e5Straw.level = 100000
|
||||||
|
e5Food[TMR], e5Food[HAY], e5Food[GRASS] = 0, 15000, 0 -- 50 % fri
|
||||||
|
e5Driver.uniqueId = "vehicleE6"
|
||||||
|
testVehicles = {e5Driver}
|
||||||
|
ADRunsController.store = ADRuns.newStore()
|
||||||
|
local e6Run = ADRunsController.createRun("Cow Food")
|
||||||
|
ADRunsController.updateRun(e6Run.id, {targetWayPointId = 30, ingredientMode = "manual", ingredients = {"FORAGE", "DRYGRASS_WINDROW", "GRASS_WINDROW", "STRAW"}})
|
||||||
|
ADRunsController.assignVehicle(e6Run.id, e5Driver)
|
||||||
|
local holdCalls = {}
|
||||||
|
ADOutbound = {
|
||||||
|
isOutboundVehicle = function() return false end,
|
||||||
|
holdSupplyVehicle = function(adEnv, vehicle, run, reasons, replan) table.insert(holdCalls, {reasons = reasons, replan = replan}) return {markerIndex = 99, markerName = "Cow Wait", group = "Cow Wait", fillTypes = {}} end,
|
||||||
|
releaseInactive = function() end, install = function() end,
|
||||||
|
}
|
||||||
|
check("E6 halvfuldt trug uden TMR: hold ved Wait", manager:getNextPickup(e5Driver, false), 99)
|
||||||
|
check("E6 hold kaldt én gang", #holdCalls, 1)
|
||||||
|
check("E6 replan finder intet endnu", holdCalls[1].replan(), nil)
|
||||||
|
e5Food[HAY] = 3000 -- 90 % fri: nødlidende -> hø tilladt
|
||||||
|
check("E6 replan: nødlidende trug -> hø", holdCalls[1].replan(), 23)
|
||||||
|
check("E6 nødlidende trug: hø direkte", manager:getNextPickup(e5Driver, false), 23)
|
||||||
|
ADOutbound = nil
|
||||||
|
ADRunsController.store = ADRuns.newStore()
|
||||||
|
testVehicles = {}
|
||||||
g_currentMission.animalFoodSystem = nil
|
g_currentMission.animalFoodSystem = nil
|
||||||
|
|
||||||
-- W13: AD stopper vognen i triggeren FØR markøren (isTargetReached=false) — Wait udløses alligevel
|
-- W13: AD stopper vognen i triggeren FØR markøren (isTargetReached=false) — Wait udløses alligevel
|
||||||
|
|||||||
@@ -193,5 +193,18 @@ check("så hø (0.8)", trough[3].fillType, HAYX)
|
|||||||
check("så græs (0.4)", trough[4].fillType, GRASS)
|
check("så græs (0.4)", trough[4].fillType, GRASS)
|
||||||
check("scoreOverride bruges som score", trough[2].score, 0.3)
|
check("scoreOverride bruges som score", trough[2].score, 0.3)
|
||||||
|
|
||||||
|
-- Bedste foder først: lavere foder (ensilage 0,8) må kun hentes når truget er nødlidende
|
||||||
|
-- (fri ≥ EMERGENCY_TROUGH_FREE af kapaciteten), hvis der er bedre foder (TMR 1,0) i samme trug
|
||||||
|
local tmrNeed = {fillType = 116, priority = 1.0, score = 0.6, trough = true}
|
||||||
|
local silageNeed = {fillType = 7, priority = 0.8, score = 0.6, trough = true}
|
||||||
|
local strawNeed = {fillType = 31, priority = 0, score = 0.9}
|
||||||
|
check("NF1 bedste foder tillades altid", ADSupplyPlanner.isFoodAllowed(tmrNeed, 1.0), true)
|
||||||
|
check("NF1 ensilage afvist ved halvfuldt trug", ADSupplyPlanner.isFoodAllowed(silageNeed, 1.0), false)
|
||||||
|
silageNeed.score = 0.8
|
||||||
|
check("NF1 ensilage tilladt når truget er næsten tomt", ADSupplyPlanner.isFoodAllowed(silageNeed, 1.0), true)
|
||||||
|
check("NF1 ikke-foder (halm) påvirkes ikke", ADSupplyPlanner.isFoodAllowed(strawNeed, 1.0), true)
|
||||||
|
check("NF1 intet bedre foder på listen -> tilladt", ADSupplyPlanner.isFoodAllowed({fillType = 7, priority = 0.8, score = 0.1, trough = true}, 0.8), true)
|
||||||
|
check("NF1 bedste foderprioritet", ADSupplyPlanner.bestFoodPriority({tmrNeed, silageNeed, strawNeed}), 1.0)
|
||||||
|
|
||||||
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
||||||
os.exit(failures == 0 and 0 or 1)
|
os.exit(failures == 0 and 0 or 1)
|
||||||
|
|||||||
Reference in New Issue
Block a user