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:
2026-09-24 00:56:01 +02:00
parent 75fd123213
commit 1b7b74ad2c
6 changed files with 112 additions and 5 deletions
+21
View File
@@ -421,6 +421,16 @@ local function startHold(adEnv, vehicle, run, reasons, sourcesById)
return choiceFor(marker, source, nil)
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.
function ADOutbound.choosePickup(adEnv, vehicle)
local run = getRun(vehicle)
@@ -494,6 +504,17 @@ local function updateHold(adEnv, task, hold, dt)
return true
end
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)
local state = trips[vehicle]
if state ~= nil then
+30 -4
View File
@@ -292,6 +292,7 @@ local function findBestSource(markerStations, fillTypes, farmId)
end
return {
markerIndex = choice.marker.markerIndex,
markerId = choice.marker.id,
markerName = choice.marker.name,
group = choice.marker.group,
level = choice.level,
@@ -592,11 +593,15 @@ function ADSmartPickup.findSupplyPickup(adEnv, driver, ingredients, targetWayPoi
local allMarkers = adEnv.ADGraphManager:getMapMarkers()
local markerStations = collectMarkerStations(adEnv, allMarkers, farmId, target.owningPlaceable)
local loadCapacity = getLoadCapacity(adEnv, driver)
for _, need in ipairs(ADSupplyPlanner.rankNeeds(getTargetNeeds(target, ingredients, farmId, demand), loadCapacity)) do
local choice = findBestSource(markerStations, {need.fillType}, farmId)
local ranked = ADSupplyPlanner.rankNeeds(getTargetNeeds(target, ingredients, farmId, demand), loadCapacity)
-- 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
return {
markerIndex = choice.markerIndex,
markerId = choice.markerId,
markerName = choice.markerName,
group = choice.group,
level = choice.level,
@@ -691,6 +696,25 @@ function ADSmartPickup.choosePickup(adEnv, driver)
local choice = ADSmartPickup.findSupplyPickup(adEnv, driver, ingredients)
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)
end
applySupplyChoice(driver, ingredients, choice)
@@ -855,14 +879,16 @@ local function installHook(adEnv)
activePickups[driver] = nil
return originalGetNextPickup(self, driver, forcedSkip)
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
self.pickups[choice.group] = self.pickups[choice.group] or {}
self.pickups[choice.group].lastTarget = choice.markerIndex
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
end
+22
View File
@@ -191,6 +191,7 @@ function ADSupplyPlanner.rankNeeds(needs, loadCapacity)
fits = ADSupplyPlanner.fullLoadFits(freeSpace, loadCapacity),
score = need.scoreOverride or ADSupplyPlanner.scoreNeed(need.level, need.capacity, freeSpace, minutesToEmpty),
priority = need.priority or 0,
trough = need.scoreOverride ~= nil,
})
end
end
@@ -209,6 +210,27 @@ function ADSupplyPlanner.rankNeeds(needs, loadCapacity)
return ranked
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.
-- >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).
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<modDesc descVersion="107">
<author>masterdraco</author>
<version>1.13.7.0</version>
<version>1.14.0.0</version>
<title>
<en>AD Smart Pickup</en>
</title>