784144cd4e
EDK 650 Unrealistic har kun allowAIDischarge på bagudtip; på venstre/højre/ kornluge åbnede AD aflæsningen, men intet løb ud (Chicken Food 2 786.712 l hvede ved Farma med plads; Disponibel Vogn 3 fuld af løg). CP-genstart logger kun ved ændring (v1.60.16.0).
482 lines
24 KiB
Lua
482 lines
24 KiB
Lua
-- AD Unload Wait
|
||
-- Holder en traktor med rest-læs ved en fuld stald AutoDrive i dag stille ved
|
||
-- aflæsningspunktet "til den er tømt på en eller anden måde" — og spærrer for
|
||
-- de næste i køen. Her omdirigeres den i stedet til nærmeste ledige Wait-punkt
|
||
-- (markør i en mappe med ordet "Wait" i navnet, fx "Cow Wait"), og når målet igen
|
||
-- har plads til HELE resten, sendes den tilbage og læsser af.
|
||
--
|
||
-- Kun den aktuelle aflæsningsopgaves destination ændres; AutoDrives mål,
|
||
-- rotation og loop-tæller røres ikke.
|
||
|
||
ADUnloadWait = {}
|
||
|
||
-- Under dette regnes vognen som tom (AutoDrive selv bruger 0,1 l).
|
||
ADUnloadWait.MIN_REST_LITERS = 100
|
||
-- Kan målet tage mindre end dette, regnes det som fuldt (aflæsning starter ikke).
|
||
ADUnloadWait.MIN_FREE_TO_UNLOAD = 100
|
||
ADUnloadWait.LOG_PREFIX = "ADSmartPickup: "
|
||
|
||
-- køretøj -> {task, homeDestinationID, waitMarker, fillType, rest}
|
||
local waits = setmetatable({}, {__mode = "k"})
|
||
local warnedNoWaitPoint = setmetatable({}, {__mode = "k"})
|
||
local isInstalled = false
|
||
|
||
-- Den vare og mængde der er tilbage på vognen (største fyldte aflæsselige enhed).
|
||
local function getRestLoad(adEnv, vehicle)
|
||
local bestFillType, bestLevel = nil, 0
|
||
for _, unit in ipairs(adEnv.AutoDrive.getAllDischargeableUnits(vehicle) or {}) do
|
||
local object = unit.object
|
||
if object ~= nil and object.getFillUnitFillLevel ~= nil then
|
||
local level = object:getFillUnitFillLevel(unit.fillUnitIndex) or 0
|
||
if level > bestLevel then
|
||
bestLevel = level
|
||
bestFillType = object.getFillUnitFillType ~= nil and object:getFillUnitFillType(unit.fillUnitIndex) or nil
|
||
end
|
||
end
|
||
end
|
||
return bestFillType, bestLevel
|
||
end
|
||
|
||
local function getWaitPoolGroup(vehicle)
|
||
local run = ADRunsController ~= nil and ADRunsController.getRunForVehicle(vehicle) or nil
|
||
return run ~= nil and run.waitPoolGroup or nil
|
||
end
|
||
|
||
-- Rutetjek, reservationer og omdirigering deles med udkørsels-modulet (adWaitPool).
|
||
-- Kun "til punktet" ved silo-aflæsning: næste tur er AD's egen rute fra siloen.
|
||
local function hasRouteTo(adEnv, vehicle, wayPointId)
|
||
return ADWaitPool.hasRouteTo(adEnv, vehicle, wayPointId)
|
||
end
|
||
|
||
local function hasRouteBothWays(adEnv, vehicle, waitWayPointId, homeWayPointId)
|
||
return ADWaitPool.hasRouteBothWays(adEnv, vehicle, waitWayPointId, homeWayPointId)
|
||
end
|
||
|
||
local function redirect(task, destinationID)
|
||
ADWaitPool.redirect(task, destinationID)
|
||
end
|
||
|
||
-- køretøj -> AD's oprindelige 2. markør (waypoint-id) mens der læsses af i en silo
|
||
local pendingTargetRestore = setmetatable({}, {__mode = "k"})
|
||
|
||
-- Aflæsning ved en ANDEN destination end AD's mål: AD tipper kun når vognen er tæt på
|
||
-- AD's egen 2. markør (getDistanceToUnloadPosition), så den skiftes midlertidigt til
|
||
-- siloen og gendannes når opgaven er færdig.
|
||
local function redirectForUnload(task, destinationID)
|
||
local vehicle = task.vehicle
|
||
local stateModule = vehicle.ad.stateModule
|
||
local current = stateModule.getSecondMarker ~= nil and stateModule:getSecondMarker() or nil
|
||
if current ~= nil and current.id ~= destinationID and stateModule.setSecondMarkerByWayPointId ~= nil then
|
||
pendingTargetRestore[vehicle] = pendingTargetRestore[vehicle] or current.id
|
||
stateModule:setSecondMarkerByWayPointId(destinationID)
|
||
end
|
||
redirect(task, destinationID)
|
||
end
|
||
|
||
local function restoreTarget(vehicle)
|
||
local homeId = pendingTargetRestore[vehicle]
|
||
if homeId == nil then
|
||
return
|
||
end
|
||
pendingTargetRestore[vehicle] = nil
|
||
local stateModule = vehicle.ad ~= nil and vehicle.ad.stateModule or nil
|
||
if stateModule ~= nil and stateModule.setSecondMarkerByWayPointId ~= nil then
|
||
stateModule:setSecondMarkerByWayPointId(homeId)
|
||
end
|
||
end
|
||
|
||
local function release(vehicle)
|
||
if waits[vehicle] ~= nil then
|
||
ADWaitPool.releaseVehicle(vehicle)
|
||
end
|
||
waits[vehicle] = nil
|
||
if ADLoadSwap ~= nil then
|
||
ADLoadSwap.clearWarning(vehicle)
|
||
end
|
||
end
|
||
|
||
-- "Fremme ved målet": AD stopper vognen så snart den står i aflæsningstriggeren — ofte
|
||
-- 5–10 m FØR markøren, så isTargetReached() bliver aldrig sand (set ved sukkerfabrikken).
|
||
-- Derfor tæller også AD's eget mål for tipning (afstand til aflæsningsstedet inden for
|
||
-- AD's trigger-afstand) og at AD er i gang med at tippe.
|
||
local function isAtUnloadTarget(adEnv, vehicle)
|
||
if vehicle.ad.drivePathModule:isTargetReached() then
|
||
return true
|
||
end
|
||
local trailerModule = vehicle.ad.trailerModule
|
||
if trailerModule ~= nil and trailerModule.isUnloading then
|
||
return true
|
||
end
|
||
local autoDrive = adEnv.AutoDrive
|
||
if autoDrive.getDistanceToUnloadPosition ~= nil and autoDrive.getMaxTriggerDistance ~= nil then
|
||
local ok, distance = pcall(autoDrive.getDistanceToUnloadPosition, vehicle)
|
||
local okMax, maxDistance = pcall(autoDrive.getMaxTriggerDistance, vehicle)
|
||
if ok and okMax and type(distance) == "number" and type(maxDistance) == "number" then
|
||
return distance <= maxDistance
|
||
end
|
||
end
|
||
return false
|
||
end
|
||
|
||
-- Står køretøjet ved målet med rest-læs, som målet ikke kan tage? Så find et Wait-punkt.
|
||
ADUnloadWait.MIN_FREE_FOR_OTHER_TARGET = 1000
|
||
|
||
-- Det af run'ets ANDRE mål der tager varen og har mest fri plads (≥ MIN_FREE_FOR_OTHER_TARGET) og vej.
|
||
local function findOtherTargetForRest(adEnv, vehicle, fillType, currentId)
|
||
local run = ADRunsController ~= nil and ADRunsController.getRunForVehicle ~= nil and ADRunsController.getRunForVehicle(vehicle) or nil
|
||
if run == nil or (run.kind or "supply") ~= "supply" or vehicle.ad.stateModule.setSecondMarkerByWayPointId == nil then
|
||
return nil
|
||
end
|
||
local best, bestFree = nil, ADUnloadWait.MIN_FREE_FOR_OTHER_TARGET - 1
|
||
for _, targetId in ipairs(ADRunsController.getRunTargets(run)) do
|
||
if targetId ~= currentId then
|
||
local free = ADSmartPickup.getTargetFreeCapacity(adEnv, vehicle, fillType, false, targetId)
|
||
if free ~= nil and free > bestFree and hasRouteTo(adEnv, vehicle, targetId) then
|
||
local marker = adEnv.ADGraphManager.getMapMarkerByWayPointId ~= nil and adEnv.ADGraphManager:getMapMarkerByWayPointId(targetId) or nil
|
||
best, bestFree = {id = targetId, name = marker ~= nil and marker.name or tostring(targetId)}, free
|
||
end
|
||
end
|
||
end
|
||
return best, bestFree
|
||
end
|
||
|
||
local function startWaiting(adEnv, task)
|
||
local vehicle = task.vehicle
|
||
-- Kun "fremme ved målet". At AD stadig regner vognen som aflæssende (isActiveAtTrigger)
|
||
-- tæller IKKE med: ved et fuldt fodertrug bliver isUnloading stående for evigt, selv om
|
||
-- intet løber — og så blokerede traktoren stalden i stedet for at køre til Wait.
|
||
if not isAtUnloadTarget(adEnv, vehicle) then
|
||
return
|
||
end
|
||
-- Efter et bytte er opgavens mål en silo, ikke AD's rigtige aflæsningsmål (anden markør);
|
||
-- der skal AD bare læsse af som normalt, uden Wait-logik.
|
||
local target = vehicle.ad.stateModule.getSecondMarker ~= nil and vehicle.ad.stateModule:getSecondMarker() or nil
|
||
if target == nil or task.destinationID ~= target.id then
|
||
return
|
||
end
|
||
local fillType, rest = getRestLoad(adEnv, vehicle)
|
||
if fillType == nil then
|
||
return
|
||
end
|
||
if rest < ADUnloadWait.MIN_REST_LITERS then
|
||
-- En lille rest som målet ikke kan tage: AutoDrive venter ellers for evigt i triggeren
|
||
-- ("til den er tømt") og spærrer for de næste. Regn vognen som tom og kør videre.
|
||
local smallFree = ADSmartPickup.getTargetFreeCapacity(adEnv, vehicle, fillType)
|
||
if rest > 0 and (smallFree == nil or smallFree < rest) and task.finished ~= nil then
|
||
Logging.info(ADUnloadWait.LOG_PREFIX .. "'%s': %.0f l %s rest kan ikke komme af — regnes som tom, kører videre", tostring(ADRuns.vehicleLabel(vehicle)), rest, g_fillTypeManager ~= nil and tostring(g_fillTypeManager:getFillTypeNameByIndex(fillType)) or tostring(fillType))
|
||
task:finished()
|
||
end
|
||
return
|
||
end
|
||
local free, capacity = ADSmartPickup.getTargetFreeCapacity(adEnv, vehicle, fillType)
|
||
-- nil = målet tager slet ikke varen (forkert last fra et tidligere run): som "fuldt"
|
||
local isRejected = free == nil and ADSmartPickup.getTargetFreeCapacity(adEnv, vehicle, fillType, true) == false
|
||
if isRejected then
|
||
free = 0
|
||
end
|
||
if free == nil or free >= ADUnloadWait.MIN_FREE_TO_UNLOAD then
|
||
return
|
||
end
|
||
|
||
-- Run med flere mål: resten køres til det af run'ets andre mål der har mest plads til varen,
|
||
-- før der ventes (en 2,4M l-vogn kan ellers aldrig komme af med resten ved én svinesti)
|
||
local other, otherFree = findOtherTargetForRest(adEnv, vehicle, fillType, target.id)
|
||
if other ~= nil then
|
||
vehicle.ad.stateModule:setSecondMarkerByWayPointId(other.id)
|
||
redirect(task, other.id)
|
||
Logging.info(ADUnloadWait.LOG_PREFIX .. "'%s': %s er fuld — resten (%.0f l %s) køres til %s (%.0f l fri)", tostring(ADRuns.vehicleLabel(vehicle)), tostring(target.name), rest,
|
||
g_fillTypeManager ~= nil and tostring(g_fillTypeManager:getFillTypeNameByIndex(fillType)) or tostring(fillType), tostring(other.name), otherFree)
|
||
return
|
||
end
|
||
|
||
local isReachable = function(candidate)
|
||
return hasRouteBothWays(adEnv, vehicle, candidate.marker.id, task.destinationID)
|
||
end
|
||
local pool = getWaitPoolGroup(vehicle)
|
||
local candidate = ADWaitPool.pickFree(adEnv, vehicle, pool, isReachable)
|
||
if candidate == nil then
|
||
if not warnedNoWaitPoint[vehicle] then
|
||
warnedNoWaitPoint[vehicle] = true
|
||
local poolText = pool ~= nil and pool ~= "" and (" i puljen '" .. pool .. "'") or ""
|
||
Logging.warning(ADUnloadWait.LOG_PREFIX .. "'%s' har %.0f l rest, og målet er fuldt — intet ledigt Wait-punkt%s med vejnet til OG fra målet (markør i en mappe med 'Wait' i navnet), så AutoDrive venter ved målet", tostring(ADRuns.vehicleLabel(vehicle)), rest, poolText)
|
||
end
|
||
return
|
||
end
|
||
warnedNoWaitPoint[vehicle] = nil
|
||
ADWaitPool.claim(candidate.marker.id, vehicle)
|
||
waits[vehicle] = {task = task, homeDestinationID = task.destinationID, waitMarker = candidate.marker, fillType = fillType, rest = rest}
|
||
-- AD tipper (og holder vognen stille) så længe den står nær sit EGET mål — også i små
|
||
-- portioner, når målet forbruger løbende. Derfor peger AD's mål på Wait-punktet imens.
|
||
redirectForUnload(task, candidate.marker.id)
|
||
local fillTypeName = g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(fillType) or tostring(fillType)
|
||
Logging.info(ADUnloadWait.LOG_PREFIX .. "'%s' venter ved %s (markør %s): %.0f l %s rest, målet har kun %.0f l fri", tostring(ADRuns.vehicleLabel(vehicle)), tostring(candidate.marker.name), tostring(candidate.marker.markerIndex or candidate.marker.id), rest, tostring(fillTypeName), free)
|
||
end
|
||
|
||
-- Ventende køretøj: er der nu plads til hele resten, køres tilbage til målet.
|
||
local function updateWaiting(adEnv, task, wait, dt)
|
||
local vehicle = task.vehicle
|
||
if wait.task ~= task then
|
||
release(vehicle)
|
||
return
|
||
end
|
||
local fillType, rest = getRestLoad(adEnv, vehicle)
|
||
if fillType == nil or rest < ADUnloadWait.MIN_REST_LITERS then
|
||
release(vehicle)
|
||
return
|
||
end
|
||
-- Friske tal til evt. bytte og til tilbage-loggen — ikke dem fra da ventningen startede.
|
||
wait.fillType, wait.rest = fillType, rest
|
||
local home = wait.homeDestinationID
|
||
local free, capacity = ADSmartPickup.getTargetFreeCapacity(adEnv, vehicle, fillType, nil, home)
|
||
local isRejected = free == nil and ADSmartPickup.getTargetFreeCapacity(adEnv, vehicle, fillType, true, home) == false
|
||
if isRejected then
|
||
free = 0
|
||
end
|
||
wait.isRejected = isRejected
|
||
-- run'ets "kør hjem under X %" (fri ≥ (100-X) % af kapaciteten), ellers standardreglen
|
||
local run = ADRunsController ~= nil and ADRunsController.getRunForVehicle(vehicle) or nil
|
||
local freeFraction = run ~= nil and run.returnBelowPercent ~= nil and (1 - run.returnBelowPercent / 100) or nil
|
||
if free == nil or not ADSupplyPlanner.canUnloadRest(free, rest, capacity, freeFraction) then
|
||
if ADLoadSwap ~= nil then
|
||
local isReachable = function(markerId)
|
||
return hasRouteTo(adEnv, vehicle, markerId)
|
||
end
|
||
ADLoadSwap.tryFromWait(adEnv, task, wait, dt, redirectForUnload, release, isReachable)
|
||
end
|
||
return
|
||
end
|
||
release(vehicle)
|
||
restoreTarget(vehicle)
|
||
redirect(task, home)
|
||
Logging.info(ADUnloadWait.LOG_PREFIX .. "'%s' kører tilbage fra %s og læsser %.0f l af (målet har %.0f l fri)", tostring(ADRuns.vehicleLabel(vehicle)), tostring(wait.waitMarker.name), rest, free)
|
||
end
|
||
|
||
-- Diagnose (midlertidig): mens en traktor holder ved sit mål med last, logges hvert 15. s hvad
|
||
-- AD ser — vognens indhold, målets fri plads, AD's aflæsningstilstand og vognens dischargeState.
|
||
ADUnloadWait.DEBUG_INTERVAL_MS = 15000
|
||
local debugTimers = setmetatable({}, {__mode = "k"})
|
||
local debugStuck = setmetatable({}, {__mode = "k"}) -- vehicle -> {level, since}: læsset uændret siden
|
||
ADUnloadWait.STUCK_WITH_ROOM_MS = 30000
|
||
|
||
local function debugAtTarget(adEnv, task, dt)
|
||
local vehicle = task.vehicle
|
||
local targetReached = vehicle.ad.drivePathModule:isTargetReached()
|
||
debugTimers[vehicle] = (debugTimers[vehicle] or ADUnloadWait.DEBUG_INTERVAL_MS) + (dt or 0)
|
||
if debugTimers[vehicle] < ADUnloadWait.DEBUG_INTERVAL_MS then
|
||
return
|
||
end
|
||
debugTimers[vehicle] = 0
|
||
local fillType, rest = getRestLoad(adEnv, vehicle)
|
||
if fillType == nil or rest < ADUnloadWait.MIN_REST_LITERS then
|
||
return
|
||
end
|
||
local free, capacity = ADSmartPickup.getTargetFreeCapacity(adEnv, vehicle, fillType)
|
||
-- ved målet MED plads, men læsset falder ikke (in-game 29/9: EDK 650 ved TMR Unload/Biogas Drop) — også det
|
||
-- vil vi se. Ellers kun: målet fuldt/afvist, og traktoren venter ikke ved et Wait-punkt.
|
||
local stuck = debugStuck[vehicle]
|
||
if stuck == nil or rest < stuck.level - 1 then
|
||
stuck = {level = rest, since = g_time or 0}
|
||
debugStuck[vehicle] = stuck
|
||
end
|
||
local stuckWithRoom = targetReached and (g_time or 0) - stuck.since >= ADUnloadWait.STUCK_WITH_ROOM_MS
|
||
if waits[vehicle] ~= nil or (free ~= nil and free >= ADUnloadWait.MIN_FREE_TO_UNLOAD and not stuckWithRoom) then
|
||
return
|
||
end
|
||
local trailerModule = vehicle.ad.trailerModule
|
||
local states = {}
|
||
for _, unit in ipairs(adEnv.AutoDrive.getAllDischargeableUnits(vehicle) or {}) do
|
||
local object = unit.object
|
||
local state = object ~= nil and object.getDischargeState ~= nil and object:getDischargeState() or "?"
|
||
local canDischarge = "?"
|
||
if object ~= nil and object.getCanDischargeToObject ~= nil and object.getCurrentDischargeNode ~= nil then
|
||
local ok, value = pcall(function() return object:getCanDischargeToObject(object:getCurrentDischargeNode()) end)
|
||
canDischarge = ok and tostring(value) or "err"
|
||
end
|
||
table.insert(states, string.format("dischargeState=%s canDischargeToObject=%s", tostring(state), canDischarge))
|
||
end
|
||
local fillTypeName = g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(fillType) or tostring(fillType)
|
||
local distance = adEnv.AutoDrive.getDistanceToUnloadPosition ~= nil and adEnv.AutoDrive.getDistanceToUnloadPosition(vehicle) or -1
|
||
local secondMarker = vehicle.ad.stateModule.getSecondMarker ~= nil and vehicle.ad.stateModule:getSecondMarker() or nil
|
||
Logging.info(ADUnloadWait.LOG_PREFIX .. "DEBUG '%s': targetReached=%s taskDest=%s adMål=%s; %.0f l %s på vognen; målet fri=%s/kap=%s; AD isUnloading=%s isLoading=%s afstandTilAflæsning=%.1f; venter=%s; %s", tostring(ADRuns.vehicleLabel(vehicle)), tostring(targetReached), tostring(task.destinationID), tostring(secondMarker ~= nil and secondMarker.id or nil), rest, tostring(fillTypeName), tostring(free), tostring(capacity), tostring(trailerModule ~= nil and trailerModule.isUnloading), tostring(trailerModule ~= nil and trailerModule.isLoading), tonumber(distance) or -1, tostring(waits[vehicle] ~= nil), table.concat(states, " | "))
|
||
end
|
||
|
||
function ADUnloadWait.beforeUpdate(adEnv, task, dt)
|
||
-- Udkørsels-traktorer: plads i målet er tjekket før turen, og målet skiftes pr. tur — ingen Wait-logik.
|
||
if ADOutbound ~= nil and ADOutbound.isOutboundVehicle ~= nil and ADOutbound.isOutboundVehicle(task.vehicle) then
|
||
return
|
||
end
|
||
-- Tømning efter stop (ADFlowWagons): ingen Wait-pulje eller læs-bytte — den skal tømme og hjem
|
||
-- (in-game 29/9: Goat Food blev sendt til Cow Wait efter Stop alt)
|
||
if ADFlowWagons ~= nil and ADFlowWagons.isEmptying ~= nil and ADFlowWagons.isEmptying(task.vehicle) then
|
||
if waits[task.vehicle] ~= nil then release(task.vehicle) end
|
||
return
|
||
end
|
||
pcall(debugAtTarget, adEnv, task, dt)
|
||
local wait = waits[task.vehicle]
|
||
if wait ~= nil then
|
||
updateWaiting(adEnv, task, wait, dt)
|
||
else
|
||
startWaiting(adEnv, task)
|
||
end
|
||
end
|
||
|
||
-- Efter en genindlæsning er ventetilstanden (kun i hukommelsen) væk, men AutoDrives gemte mål
|
||
-- peger stadig på Wait-punktet, og traktoren står der med rest-læs — uden dette ville den blive
|
||
-- stående for evigt. Her genoptages ventningen for aktive traktorer i et forsynings-run hvis
|
||
-- aflæsningsopgave peger på en Wait-markør: hjem = run'ets mål, og reservationen sættes igen.
|
||
-- Kaldes fra ADSmartPickup:update (throttlet).
|
||
ADUnloadWait.ADOPT_INTERVAL_MS = 5300
|
||
local adoptTimerMs = ADUnloadWait.ADOPT_INTERVAL_MS
|
||
|
||
function ADUnloadWait.adoptOrphans(adEnv)
|
||
if ADRunsController == nil or ADRunsController.getFarmVehicles == nil then
|
||
return
|
||
end
|
||
for _, vehicle in ipairs(ADRunsController.getFarmVehicles(adEnv)) do
|
||
local stateModule = vehicle.ad ~= nil and vehicle.ad.stateModule or nil
|
||
local isActive = stateModule ~= nil and stateModule.isActive ~= nil and stateModule:isActive()
|
||
local run = isActive and waits[vehicle] == nil and ADRunsController.getRunForVehicle(vehicle) or nil
|
||
if run ~= nil and (run.kind or "supply") == "supply" and stateModule.getSecondMarker ~= nil then
|
||
local marker = stateModule:getSecondMarker()
|
||
local task = vehicle.ad.taskModule ~= nil and vehicle.ad.taskModule.getActiveTask ~= nil and vehicle.ad.taskModule:getActiveTask() or nil
|
||
if marker ~= nil and marker.id ~= nil and ADSupplyPlanner.isWaitMarker(marker) and task ~= nil and task.destinationID == marker.id and task.trailers ~= nil then
|
||
local fillType, rest = getRestLoad(adEnv, vehicle)
|
||
local homeId = ADRunsController.chooseTarget ~= nil and ADRunsController.chooseTarget(adEnv, run, vehicle) or run.targetWayPointId
|
||
if fillType ~= nil and rest >= ADUnloadWait.MIN_REST_LITERS and homeId ~= nil then
|
||
ADWaitPool.claim(marker.id, vehicle)
|
||
pendingTargetRestore[vehicle] = homeId
|
||
waits[vehicle] = {task = task, homeDestinationID = homeId, waitMarker = marker, fillType = fillType, rest = rest}
|
||
local fillTypeName = g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(fillType) or tostring(fillType)
|
||
Logging.info(ADUnloadWait.LOG_PREFIX .. "'%s' genoptager ventning ved %s efter genindlæsning: %.0f l %s rest, hjem = markør %s", tostring(ADRuns.vehicleLabel(vehicle)), tostring(marker.name), rest, tostring(fillTypeName), tostring(homeId))
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
function ADUnloadWait.adoptOrphansThrottled(adEnv, dt)
|
||
adoptTimerMs = adoptTimerMs + (dt or 0)
|
||
if adoptTimerMs < ADUnloadWait.ADOPT_INTERVAL_MS then
|
||
return
|
||
end
|
||
adoptTimerMs = 0
|
||
local ok, err = pcall(ADUnloadWait.adoptOrphans, adEnv)
|
||
if not ok then
|
||
Logging.warning(ADUnloadWait.LOG_PREFIX .. "kunne ikke genoptage ventende traktorer: %s", tostring(err))
|
||
end
|
||
end
|
||
|
||
-- Kaldes fra ADSmartPickup:update — standsede køretøjer slipper deres Wait-punkt.
|
||
function ADUnloadWait.releaseInactive()
|
||
for vehicle in pairs(waits) do
|
||
local stateModule = vehicle.ad ~= nil and vehicle.ad.stateModule or nil
|
||
if stateModule == nil or stateModule.isActive == nil or not stateModule:isActive() then
|
||
release(vehicle)
|
||
end
|
||
end
|
||
for vehicle in pairs(pendingTargetRestore) do
|
||
local stateModule = vehicle.ad ~= nil and vehicle.ad.stateModule or nil
|
||
if stateModule == nil or stateModule.isActive == nil or not stateModule:isActive() then
|
||
restoreTarget(vehicle)
|
||
end
|
||
end
|
||
end
|
||
|
||
function ADUnloadWait.getPendingTargetRestore(vehicle)
|
||
return pendingTargetRestore[vehicle]
|
||
end
|
||
|
||
-- Ved Start med forkert last: AD's mål peger på siloen indtil aflæsningen er færdig;
|
||
-- det oprindelige mål gendannes af finished-hook'en.
|
||
function ADUnloadWait.setDumpTarget(vehicle, dumpWayPointId)
|
||
local stateModule = vehicle.ad.stateModule
|
||
local current = stateModule.getSecondMarker ~= nil and stateModule:getSecondMarker() or nil
|
||
if current ~= nil and current.id ~= dumpWayPointId then
|
||
pendingTargetRestore[vehicle] = pendingTargetRestore[vehicle] or current.id
|
||
end
|
||
if stateModule.setSecondMarkerByWayPointId ~= nil then
|
||
stateModule:setSecondMarkerByWayPointId(dumpWayPointId)
|
||
end
|
||
end
|
||
|
||
-- Hvem holder hvor (GUI'ets Wait-puljer-visning). Sorteret på mappe, dernæst markør-id.
|
||
-- Vognens indhold (største enhed) og evt. Wait-tilstand — til overblikket i fanen.
|
||
function ADUnloadWait.hasRouteBothWays(adEnv, vehicle, toWayPointId, homeWayPointId)
|
||
return hasRouteBothWays(adEnv, vehicle, toWayPointId, homeWayPointId)
|
||
end
|
||
|
||
function ADUnloadWait.hasRouteTo(adEnv, vehicle, wayPointId)
|
||
return hasRouteTo(adEnv, vehicle, wayPointId)
|
||
end
|
||
|
||
function ADUnloadWait.getRestLoad(adEnv, vehicle)
|
||
return getRestLoad(adEnv, vehicle)
|
||
end
|
||
|
||
function ADUnloadWait.getWaitFor(vehicle)
|
||
local wait = waits[vehicle]
|
||
if wait == nil then
|
||
return nil
|
||
end
|
||
return {markerName = wait.waitMarker ~= nil and wait.waitMarker.name or nil, fillType = wait.fillType, rest = wait.rest}
|
||
end
|
||
|
||
-- Alle reservationer i puljen (også udkørsels-traktorer der holder).
|
||
function ADUnloadWait.getClaims(adEnv)
|
||
return ADWaitPool.getClaims(adEnv)
|
||
end
|
||
|
||
ADUnloadWait.TIP_CHECK_MS = 2000
|
||
local tipChecked = setmetatable({}, {__mode = "k"})
|
||
|
||
function ADUnloadWait.install(adEnv)
|
||
local task, autoDrive = adEnv.UnloadAtDestinationTask, adEnv.AutoDrive
|
||
if isInstalled or task == nil or task.update == nil or autoDrive == nil or autoDrive.getSetting == nil then
|
||
return
|
||
end
|
||
isInstalled = true
|
||
ADUnloadWait.STATE_DRIVING = task.STATE_DRIVING or 2
|
||
ADWaitPool.STATE_DRIVING = ADUnloadWait.STATE_DRIVING
|
||
|
||
local originalUpdate, originalFinished, originalGetSetting = task.update, task.finished, autoDrive.getSetting
|
||
|
||
task.update = function(self, dt)
|
||
-- alle aflæsningsture (også udkørsel og tømning): tipvognen på en side AD må aflæsse fra (EDK 650, 29/9)
|
||
if ADTipSide ~= nil and self.vehicle ~= nil then
|
||
local now = g_time or 0
|
||
if (tipChecked[self.vehicle] or -math.huge) + ADUnloadWait.TIP_CHECK_MS <= now then
|
||
tipChecked[self.vehicle] = now
|
||
local okTip, errTip = pcall(ADTipSide.ensure, self.vehicle)
|
||
if not okTip then Logging.warning(ADUnloadWait.LOG_PREFIX .. "tip-side: %s", tostring(errTip)) end
|
||
end
|
||
end
|
||
local ok, err = pcall(ADUnloadWait.beforeUpdate, adEnv, self, dt)
|
||
if not ok then
|
||
Logging.warning(ADUnloadWait.LOG_PREFIX .. "fejl i Wait-punkt-logik, AutoDrive fortsætter selv: %s", tostring(err))
|
||
release(self.vehicle)
|
||
end
|
||
return originalUpdate(self, dt)
|
||
end
|
||
|
||
if originalFinished ~= nil then
|
||
task.finished = function(self)
|
||
release(self.vehicle)
|
||
restoreTarget(self.vehicle)
|
||
-- læsset er afleveret: tæller ikke længere som "på vej" for de andre traktorer
|
||
if ADSmartPickup ~= nil and ADSmartPickup.clearEnRoute ~= nil then
|
||
ADSmartPickup.clearEnRoute(self.vehicle)
|
||
end
|
||
return originalFinished(self)
|
||
end
|
||
end
|
||
|
||
-- Ved "rotér aflæsning" kører AutoDrive straks videre fra et mål den ikke kan læsse
|
||
-- af ved — også fra Wait-punktet. Ventende køretøjer ser derfor rotationen som slået fra.
|
||
autoDrive.getSetting = function(settingName, vehicle)
|
||
if settingName == "rotateTargets" and vehicle ~= nil and waits[vehicle] ~= nil then
|
||
return autoDrive.RT_NONE
|
||
end
|
||
return originalGetSetting(settingName, vehicle)
|
||
end
|
||
end
|