feat: controller — udkørsels-runs: start, validering, produkter, kilder, status

This commit is contained in:
2026-09-23 09:57:59 +02:00
parent deb89b8c57
commit de36abe1fa
2 changed files with 256 additions and 12 deletions
+170 -12
View File
@@ -73,8 +73,8 @@ end
-- ---- store-wrappers (erstatter altid hele store'en; modellen muterer aldrig) ----
function ADRunsController.createRun(name)
local store, run = ADRuns.createRun(ADRunsController.store, name)
function ADRunsController.createRun(name, kind)
local store, run = ADRuns.createRun(ADRunsController.store, name, kind)
ADRunsController.store = store
return run
end
@@ -129,6 +129,9 @@ end
-- findSupplyPickup selv til de ønskede produktioners inputs bagefter.
-- Run'ets mål som liste (flere stalde pr. run); gamle runs har kun targetWayPointId.
function ADRunsController.getRunTargets(run)
if run.kind == "outbound" then
return {}
end
if run.targetWayPointIds ~= nil and #run.targetWayPointIds > 0 then
return run.targetWayPointIds
end
@@ -272,10 +275,144 @@ local function intersects(listA, listB)
return false
end
-- reason: "noTarget" | "noTrailer" | "cannotCarry" | "farFromNetwork" (l10n-nøgler i GUI'et).
local function hasAutoload(adEnv, vehicle)
local trailers = adEnv.AutoDrive.getAllUnits ~= nil and adEnv.AutoDrive.getAllUnits(vehicle) or nil
for _, trailer in ipairs(trailers or {}) do
if adEnv.AutoDrive.hasAL ~= nil and adEnv.AutoDrive:hasAL(trailer) then
return true
end
end
return false
end
-- Er langt fra vejnettet? (AD's getDistanceFromNetwork kalder vehicle:getClosestWayPoint(),
-- som ikke alle AD-køretøjer har — set in-game på en høster — så uden metoden springes tjekket over.)
local function isFarFromNetwork(adEnv, vehicle)
local distance = 0
if vehicle.getClosestWayPoint ~= nil and adEnv.ADGraphManager.getDistanceFromNetwork ~= nil then
local ok, value = pcall(adEnv.ADGraphManager.getDistanceFromNetwork, adEnv.ADGraphManager, vehicle)
distance = ok and value or 0
end
return distance == nil or distance > ADRunsController.MAX_NETWORK_DISTANCE
end
-- ---- udkørsel ----
-- Kilder ved markørerne (fabrik/silo/objektlager) — {source} i run'ets rækkefølge; ukendte udelades.
local function getRunSources(adEnv, run, farmId)
local sources = {}
if ADSources == nil then
return sources
end
for _, wayPointId in ipairs(run.sourceWayPointIds or {}) do
local source = ADSources.identify(adEnv, wayPointId, farmId)
if source ~= nil then
table.insert(sources, source)
end
end
return sources
end
-- Alle varer kilderne rummer/producerer med run'ets politik (uanset vogn) — til fanen Udkørsel.
-- {fillType, name, title, policy, isPallet, available, level, capacity}, sorteret på titel.
function ADRunsController.getRunProducts(adEnv, run)
local farmId = g_currentMission:getFarmId()
local byName, result = {}, {}
for _, source in ipairs(getRunSources(adEnv, run, farmId)) do
for _, item in ipairs(ADSources.getProducts(adEnv, source, farmId)) do
local product = byName[item.name]
if product == nil then
product = {fillType = item.fillType, name = item.name, title = item.title or item.name, policy = ADRuns.getProductPolicy(run, item.name), isPallet = item.isPallet, available = 0, level = 0, capacity = 0}
byName[item.name] = product
table.insert(result, product)
end
product.available = product.available + (item.available or 0)
product.level = product.level + (item.level or 0)
product.capacity = product.capacity + (item.capacity or 0)
end
end
table.sort(result, function(a, b) return tostring(a.title) < tostring(b.title) end)
return result
end
-- Run'ets varer (fillType-indeks) som denne vogn kan bære: bulk via AD's liste, paller via UAL.
local function getOutboundFillTypes(adEnv, run, vehicle)
local carriable = {}
for _, fillType in ipairs(ADRunsController.getCarriableFillTypes(adEnv, vehicle)) do
carriable[fillType] = true
end
local canLoadPallets = hasAutoload(adEnv, vehicle)
local result, palletCount, chosen = {}, 0, 0
for _, product in ipairs(ADRunsController.getRunProducts(adEnv, run)) do
if product.policy ~= "off" then
chosen = chosen + 1
if product.isPallet then
palletCount = palletCount + 1
if canLoadPallets then table.insert(result, product.fillType) end
elseif carriable[product.fillType] then
table.insert(result, product.fillType)
end
end
end
return result, chosen, palletCount, canLoadPallets
end
local function validateOutboundVehicle(adEnv, run, vehicle, result)
if #getRunSources(adEnv, run, ADSmartPickup.getFarmId(vehicle)) == 0 then
result.reason = "noSource"
return result
end
if result.capacity == nil then
result.reason = "noTrailer"
return result
end
local fillTypes, chosen, palletCount, canLoadPallets = getOutboundFillTypes(adEnv, run, vehicle)
if chosen == 0 then
result.reason = "noProducts"
return result
end
if #fillTypes == 0 then
result.reason = (palletCount == chosen and not canLoadPallets) and "needsAutoload" or "cannotCarry"
return result
end
if isFarFromNetwork(adEnv, vehicle) then
result.reason = "farFromNetwork"
return result
end
result.ok = true
return result
end
-- Markører med en genkendelig kilde: {marker, kind} sorteret på navn — dropdown/afkrydsning i fanen.
function ADRunsController.getSourceMarkers(adEnv, farmId)
local result = {}
if ADSources == nil then
return result
end
for _, marker in pairs(adEnv.ADGraphManager:getMapMarkers()) do
local source = ADSources.identify(adEnv, marker.id, farmId)
if source ~= nil then
table.insert(result, {marker = marker, kind = source.kind, name = source.name})
end
end
table.sort(result, function(a, b) return tostring(a.marker.name) < tostring(b.marker.name) end)
return result
end
-- Seneste planlægger-begrundelser og advarsel for run'et (fra ADOutbound) + produkter.
function ADRunsController.getOutboundStatus(adEnv, run)
local status = ADOutbound ~= nil and ADOutbound.getRunStatus ~= nil and ADOutbound.getRunStatus(run.id) or {reasons = {}}
return {reasons = status.reasons or {}, warning = status.warning, products = ADRunsController.getRunProducts(adEnv, run)}
end
-- reason: "noTarget" | "noTrailer" | "cannotCarry" | "farFromNetwork" (l10n-nøgler i GUI'et);
-- udkørsel desuden "noSource" | "noProducts" | "needsAutoload".
function ADRunsController.validateVehicle(adEnv, run, vehicle)
local carriable = ADRunsController.getCarriableFillTypes(adEnv, vehicle)
local result = {ok = false, carriable = carriable, capacity = getLoadCapacity(adEnv, vehicle)}
if run.kind == "outbound" then
return validateOutboundVehicle(adEnv, run, vehicle, result)
end
local hasTarget = false
for _, wayPointId in ipairs(ADRunsController.getRunTargets(run)) do
if adEnv.ADGraphManager:getMapMarkerByWayPointId(wayPointId) ~= nil then
@@ -294,14 +431,7 @@ function ADRunsController.validateVehicle(adEnv, run, vehicle)
result.reason = "cannotCarry"
return result
end
-- AD's getDistanceFromNetwork kalder vehicle:getClosestWayPoint(), som ikke alle
-- AD-køretøjer har (set in-game på en høster) — uden metoden springes tjekket over.
local distance = 0
if vehicle.getClosestWayPoint ~= nil and adEnv.ADGraphManager.getDistanceFromNetwork ~= nil then
local ok, value = pcall(adEnv.ADGraphManager.getDistanceFromNetwork, adEnv.ADGraphManager, vehicle)
distance = ok and value or 0
end
if distance == nil or distance > ADRunsController.MAX_NETWORK_DISTANCE then
if isFarFromNetwork(adEnv, vehicle) then
result.reason = "farFromNetwork"
return result
end
@@ -386,7 +516,30 @@ local function findNearestLoadMarker(adEnv, vehicle, farmId)
return best
end
-- Udkørsel: 1. og 2. markør = første kilde (pladsholder — kilde og mål vælges pr. tur af
-- ADOutbound), varevalg = run'ets varer vognen kan bære, rotation "kun pålæsning" (hook'en).
local function applyOutboundRunToVehicle(adEnv, run, vehicle)
local stateModule = vehicle.ad.stateModule
stateModule:setMode(adEnv.AutoDrive.MODE_PICKUPANDDELIVER)
local firstId = run.sourceWayPointIds[1]
stateModule:setFirstMarkerByWayPointId(firstId)
stateModule:setSecondMarkerByWayPointId(firstId)
local fillTypes = getOutboundFillTypes(adEnv, run, vehicle)
stateModule.selectedFillTypes = copyList(fillTypes)
if fillTypes[1] ~= nil then
stateModule:setFillType(fillTypes[1])
end
stateModule.loopCounter = 0
stateModule:setLoopsDone(0)
setSetting(adEnv, vehicle, "useFolders", true)
setSetting(adEnv, vehicle, "rotateTargets", adEnv.AutoDrive.RT_ONLYPICKUP)
stateModule:raiseDirtyFlag()
end
local function applyRunToVehicle(adEnv, run, vehicle, ingredients)
if run.kind == "outbound" then
return applyOutboundRunToVehicle(adEnv, run, vehicle)
end
local stateModule = vehicle.ad.stateModule
local farmId = ADSmartPickup.getFarmId(vehicle)
stateModule:setMode(adEnv.AutoDrive.MODE_PICKUPANDDELIVER)
@@ -465,7 +618,8 @@ function ADRunsController.startRun(adEnv, runId)
local ingredients = ADRunsController.getRunIngredients(adEnv, run, vehicle)
local ok, err = pcall(applyAndStart, adEnv, run, vehicle, ingredients)
if ok then
Logging.info(ADRunsController.LOG_PREFIX .. "run '%s': '%s' startet mod markør %s", tostring(run.name), tostring(vehicle:getName()), tostring(run.targetWayPointId))
local target = run.kind == "outbound" and ("kilde " .. tostring(run.sourceWayPointIds[1])) or ("markør " .. tostring(run.targetWayPointId))
Logging.info(ADRunsController.LOG_PREFIX .. "run '%s': '%s' startet mod %s", tostring(run.name), tostring(vehicle:getName()), target)
else
result.ok, result.reason = false, "error"
Logging.warning(ADRunsController.LOG_PREFIX .. "run '%s': kunne ikke starte '%s': %s", tostring(run.name), tostring(vehicle:getName()), tostring(err))
@@ -539,6 +693,10 @@ function ADRunsController.getVehicleActivity(adEnv, vehicle)
if wait ~= nil then
return {kind = "waiting", fillType = wait.fillType, liters = wait.rest, place = wait.markerName}
end
local hold = ADOutbound ~= nil and ADOutbound.getHoldFor ~= nil and ADOutbound.getHoldFor(vehicle) or nil
if hold ~= nil then
return {kind = "holding", place = hold.markerName, reason = hold.reasons ~= nil and hold.reasons[1] or nil}
end
local fillType, liters = nil, 0
if ADUnloadWait ~= nil and ADUnloadWait.getRestLoad ~= nil then
fillType, liters = ADUnloadWait.getRestLoad(adEnv, vehicle)