feat: Wait-punkter for rest-læs + læs der passer foretrækkes (v1.7.0.0)
Rest-læs ved fuldt mål: AutoDrive ventede ved aflæsningspunktet 'til tømt' og spærrede køen. Nu omdirigeres den aktuelle UnloadAtDestinationTask (destinationID + drivePathModule:setPathTo) til nærmeste ledige markør med 'Wait' i navnet / mappen Wait (fælles pulje, reservationer pr. køretøj); tilbage når målet har plads til hele resten. Ventende ser rotateTargets=RT_NONE så 'rotér aflæsning' ikke kører videre fra Wait-punktet. Ingen ledige punkter = som i dag + én advarsel. Rangering: varer hvor et fuldt læs (AutoDrive.getAllFillLevels-kapacitet) passer i målet går forud; passer intet, mest trængende. Nyt modul adUnloadWait.lua; ADSmartPickup.getTargetFreeCapacity delt hjælper.
This commit is contained in:
@@ -357,31 +357,70 @@ local function isFillTypeAccepted(station, husbandry, fillType)
|
|||||||
return station.getIsFillTypeSupported == nil or station:getIsFillTypeSupported(fillType)
|
return station.getIsFillTypeSupported == nil or station:getIsFillTypeSupported(fillType)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Niveau + kapacitet for én vare i aflæsningsmålet; nil hvis målet ikke tager varen.
|
||||||
|
local function getTargetLevelAndCapacity(station, fillType, farmId)
|
||||||
|
local husbandry = getHusbandry(station)
|
||||||
|
if not isFillTypeAccepted(station, husbandry, fillType) then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if husbandry ~= nil then
|
||||||
|
local level = husbandry:getHusbandryFillLevel(fillType) or 0
|
||||||
|
return level, husbandry:getHusbandryCapacity(fillType) or level
|
||||||
|
end
|
||||||
|
local level, freeSpace = 0, 0
|
||||||
|
for _, storage in pairs(getTargetStorages(station)) do
|
||||||
|
level = level + (storage:getFillLevel(fillType) or 0)
|
||||||
|
freeSpace = freeSpace + (storage:getFreeCapacity(fillType) or 0)
|
||||||
|
end
|
||||||
|
if level + freeSpace <= 0 and station.getFreeCapacity ~= nil then
|
||||||
|
-- ukendt lagertype: brug stationens egen opgørelse af fri plads
|
||||||
|
level, freeSpace = 0, station:getFreeCapacity(fillType, farmId) or 0
|
||||||
|
end
|
||||||
|
return level, level + freeSpace
|
||||||
|
end
|
||||||
|
|
||||||
-- Niveau + kapacitet pr. ingrediens i aflæsningsmålets lager; demand giver forbrug/min.
|
-- Niveau + kapacitet pr. ingrediens i aflæsningsmålets lager; demand giver forbrug/min.
|
||||||
local function getTargetNeeds(station, ingredients, farmId, demand)
|
local function getTargetNeeds(station, ingredients, farmId, demand)
|
||||||
local needs = {}
|
local needs = {}
|
||||||
local storages = getTargetStorages(station)
|
|
||||||
local husbandry = getHusbandry(station)
|
|
||||||
for _, fillType in ipairs(ingredients) do
|
for _, fillType in ipairs(ingredients) do
|
||||||
if husbandry ~= nil and isFillTypeAccepted(station, husbandry, fillType) then
|
local level, capacity = getTargetLevelAndCapacity(station, fillType, farmId)
|
||||||
local level = husbandry:getHusbandryFillLevel(fillType) or 0
|
if level ~= nil then
|
||||||
table.insert(needs, {fillType = fillType, level = level, capacity = husbandry:getHusbandryCapacity(fillType) or level, litersPerMinute = demand[fillType]})
|
table.insert(needs, {fillType = fillType, level = level, capacity = capacity, litersPerMinute = demand[fillType]})
|
||||||
elseif isFillTypeAccepted(station, husbandry, fillType) then
|
|
||||||
local level, freeSpace = 0, 0
|
|
||||||
for _, storage in pairs(storages) do
|
|
||||||
level = level + (storage:getFillLevel(fillType) or 0)
|
|
||||||
freeSpace = freeSpace + (storage:getFreeCapacity(fillType) or 0)
|
|
||||||
end
|
|
||||||
if level + freeSpace <= 0 and station.getFreeCapacity ~= nil then
|
|
||||||
-- ukendt lagertype: brug stationens egen opgørelse af fri plads
|
|
||||||
level, freeSpace = 0, station:getFreeCapacity(fillType, farmId) or 0
|
|
||||||
end
|
|
||||||
table.insert(needs, {fillType = fillType, level = level, capacity = level + freeSpace, litersPerMinute = demand[fillType]})
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return needs
|
return needs
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Vognens samlede kapacitet (liter); nil hvis AutoDrive ikke kan opgøre den.
|
||||||
|
local function getLoadCapacity(adEnv, driver)
|
||||||
|
if adEnv.AutoDrive.getAllUnits == nil or adEnv.AutoDrive.getAllFillLevels == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local trailers = adEnv.AutoDrive.getAllUnits(driver)
|
||||||
|
local _, capacity = adEnv.AutoDrive.getAllFillLevels(trailers)
|
||||||
|
return capacity ~= nil and capacity > 0 and capacity or nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fri plads til varen i køretøjets aflæsningsmål (bruges af Wait-punkt-logikken);
|
||||||
|
-- nil hvis målet ikke kendes eller ikke tager varen.
|
||||||
|
function ADSmartPickup.getTargetFreeCapacity(adEnv, driver, fillType)
|
||||||
|
local secondMarker = driver.ad.stateModule:getSecondMarker()
|
||||||
|
local farmId = getFarmId(driver)
|
||||||
|
if secondMarker == nil or farmId == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local targetWayPoint = adEnv.ADGraphManager:getWayPointById(secondMarker.id)
|
||||||
|
local target = targetWayPoint ~= nil and getUnloadStationAtMarker(adEnv, targetWayPoint, farmId) or nil
|
||||||
|
if target == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local level, capacity = getTargetLevelAndCapacity(target, fillType, farmId)
|
||||||
|
if level == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return capacity - level
|
||||||
|
end
|
||||||
|
|
||||||
-- Behovsstyret: den ingrediens aflæsningsmålet mangler mest, som også findes i en silo.
|
-- Behovsstyret: den ingrediens aflæsningsmålet mangler mest, som også findes i en silo.
|
||||||
function ADSmartPickup.findSupplyPickup(adEnv, driver, ingredients)
|
function ADSmartPickup.findSupplyPickup(adEnv, driver, ingredients)
|
||||||
local secondMarker = driver.ad.stateModule:getSecondMarker()
|
local secondMarker = driver.ad.stateModule:getSecondMarker()
|
||||||
@@ -407,7 +446,8 @@ function ADSmartPickup.findSupplyPickup(adEnv, driver, ingredients)
|
|||||||
|
|
||||||
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)
|
||||||
for _, need in ipairs(ADSupplyPlanner.rankNeeds(getTargetNeeds(target, ingredients, farmId, demand))) do
|
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 choice = findBestSource(markerStations, {need.fillType}, farmId)
|
||||||
if choice ~= nil then
|
if choice ~= nil then
|
||||||
return {
|
return {
|
||||||
@@ -463,6 +503,9 @@ local function describeChoice(choice)
|
|||||||
if choice.need.minutesToEmpty ~= nil then
|
if choice.need.minutesToEmpty ~= nil then
|
||||||
urgency = string.format(", fabrikken bruger %.0f l/min = %.0f min til tom", choice.need.litersPerMinute, choice.need.minutesToEmpty)
|
urgency = string.format(", fabrikken bruger %.0f l/min = %.0f min til tom", choice.need.litersPerMinute, choice.need.minutesToEmpty)
|
||||||
end
|
end
|
||||||
|
if choice.need.fits == false then
|
||||||
|
urgency = urgency .. ", et fuldt læs passer IKKE (rest ventes ved Wait-punkt)"
|
||||||
|
end
|
||||||
return string.format("%s: kilde %.0f l, %s, målet har %.0f/%.0f l%s", tostring(fillTypeName), choice.level, source, choice.need.level, choice.need.capacity, urgency)
|
return string.format("%s: kilde %.0f l, %s, målet har %.0f/%.0f l%s", tostring(fillTypeName), choice.level, source, choice.need.level, choice.need.capacity, urgency)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -571,8 +614,11 @@ local function installHook(adEnv)
|
|||||||
end
|
end
|
||||||
|
|
||||||
installLoadWaitHook(adEnv)
|
installLoadWaitHook(adEnv)
|
||||||
|
if ADUnloadWait ~= nil then
|
||||||
|
ADUnloadWait.install(adEnv)
|
||||||
|
end
|
||||||
|
|
||||||
Logging.info(ADSmartPickup.LOG_PREFIX .. "aktiv (mindst fyldte silo med varen; flere valgte varer = behovsstyret forsyning)")
|
Logging.info(ADSmartPickup.LOG_PREFIX .. "aktiv (mindst fyldte silo med varen; flere valgte varer = behovsstyret forsyning; Wait-punkter for rest-læs)")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Når AutoDrive stopper, får køretøjet hele ingredienslisten tilbage i varevalget
|
-- Når AutoDrive stopper, får køretøjet hele ingredienslisten tilbage i varevalget
|
||||||
@@ -608,6 +654,9 @@ function ADSmartPickup:update(dt)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if ADUnloadWait ~= nil then
|
||||||
|
pcall(ADUnloadWait.releaseInactive)
|
||||||
|
end
|
||||||
local ok, err = pcall(restoreStoppedVehicles)
|
local ok, err = pcall(restoreStoppedVehicles)
|
||||||
if not ok then
|
if not ok then
|
||||||
Logging.warning(ADSmartPickup.LOG_PREFIX .. "kunne ikke gendanne varevalg: %s", tostring(err))
|
Logging.warning(ADSmartPickup.LOG_PREFIX .. "kunne ikke gendanne varevalg: %s", tostring(err))
|
||||||
|
|||||||
@@ -114,9 +114,46 @@ function ADSupplyPlanner.scoreNeed(level, capacity, freeSpace, minutesToEmpty)
|
|||||||
return freeSpace / capacity
|
return freeSpace / capacity
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- nil når læs-størrelsen er ukendt, ellers om et fuldt læs kan komme af i målet.
|
||||||
|
function ADSupplyPlanner.fullLoadFits(freeSpace, loadCapacity)
|
||||||
|
if loadCapacity == nil or loadCapacity <= 0 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return freeSpace >= loadCapacity
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Wait-punkter: markører med ordet "Wait" i navnet (uanset store/små) eller i mappen "Wait".
|
||||||
|
-- Her venter en traktor med rest-læs, til der igen er plads i målet.
|
||||||
|
function ADSupplyPlanner.isWaitMarker(marker)
|
||||||
|
if marker.group == "Wait" then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
local name = string.lower(tostring(marker.name or ""))
|
||||||
|
return string.find(name, "%f[%a]wait%f[%A]") ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- candidates: {{marker, x, z}}; claims: {[marker.id] = køretøj}. Nærmeste punkt der er
|
||||||
|
-- ledigt eller allerede reserveret af dette køretøj. nil hvis alle er optaget.
|
||||||
|
function ADSupplyPlanner.pickNearestFreeWaitPoint(candidates, claims, x, z, vehicle)
|
||||||
|
local best, bestDistance = nil, nil
|
||||||
|
for _, candidate in ipairs(candidates) do
|
||||||
|
local owner = claims[candidate.marker.id]
|
||||||
|
if owner == nil or owner == vehicle then
|
||||||
|
local dx, dz = candidate.x - x, candidate.z - z
|
||||||
|
local distance = dx * dx + dz * dz
|
||||||
|
if bestDistance == nil or distance < bestDistance then
|
||||||
|
best, bestDistance = candidate, distance
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return best
|
||||||
|
end
|
||||||
|
|
||||||
-- needs: {{fillType, level, capacity[, litersPerMinute]}, ...}. Returnerer NY liste,
|
-- needs: {{fillType, level, capacity[, litersPerMinute]}, ...}. Returnerer NY liste,
|
||||||
-- mest trængende først; ingredienser uden plads i målet er sorteret fra.
|
-- mest trængende først; ingredienser uden plads i målet er sorteret fra.
|
||||||
function ADSupplyPlanner.rankNeeds(needs)
|
-- loadCapacity (valgfri): vognens kapacitet — varer hvor et FULDT læs kan komme af
|
||||||
|
-- går forud (så vognen ikke ender med rest-læs); passer intet, gælder ren trang.
|
||||||
|
function ADSupplyPlanner.rankNeeds(needs, loadCapacity)
|
||||||
local ranked = {}
|
local ranked = {}
|
||||||
for _, need in ipairs(needs) do
|
for _, need in ipairs(needs) do
|
||||||
local freeSpace = need.capacity - need.level
|
local freeSpace = need.capacity - need.level
|
||||||
@@ -129,11 +166,15 @@ function ADSupplyPlanner.rankNeeds(needs)
|
|||||||
freeSpace = freeSpace,
|
freeSpace = freeSpace,
|
||||||
litersPerMinute = need.litersPerMinute,
|
litersPerMinute = need.litersPerMinute,
|
||||||
minutesToEmpty = minutesToEmpty,
|
minutesToEmpty = minutesToEmpty,
|
||||||
|
fits = ADSupplyPlanner.fullLoadFits(freeSpace, loadCapacity),
|
||||||
score = ADSupplyPlanner.scoreNeed(need.level, need.capacity, freeSpace, minutesToEmpty),
|
score = ADSupplyPlanner.scoreNeed(need.level, need.capacity, freeSpace, minutesToEmpty),
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.sort(ranked, function(a, b)
|
table.sort(ranked, function(a, b)
|
||||||
|
if a.fits ~= b.fits then
|
||||||
|
return a.fits == true
|
||||||
|
end
|
||||||
if a.score ~= b.score then
|
if a.score ~= b.score then
|
||||||
return a.score > b.score
|
return a.score > b.score
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,184 @@
|
|||||||
|
-- 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 med ordet "Wait" i navnet eller i mappen "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"})
|
||||||
|
-- markør-id -> køretøj der har reserveret Wait-punktet
|
||||||
|
local claims = setmetatable({}, {__mode = "v"})
|
||||||
|
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 getWaitCandidates(adEnv)
|
||||||
|
local candidates = {}
|
||||||
|
for _, marker in pairs(adEnv.ADGraphManager:getMapMarkers()) do
|
||||||
|
if ADSupplyPlanner.isWaitMarker(marker) then
|
||||||
|
local wayPoint = adEnv.ADGraphManager:getWayPointById(marker.id)
|
||||||
|
if wayPoint ~= nil then
|
||||||
|
table.insert(candidates, {marker = marker, x = wayPoint.x, z = wayPoint.z})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return candidates
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getVehiclePosition(vehicle)
|
||||||
|
if vehicle.rootNode == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local x, _, z = getWorldTranslation(vehicle.rootNode)
|
||||||
|
return x, z
|
||||||
|
end
|
||||||
|
|
||||||
|
local function redirect(task, destinationID)
|
||||||
|
task.destinationID = destinationID
|
||||||
|
task.state = ADUnloadWait.STATE_DRIVING
|
||||||
|
task.vehicle.ad.drivePathModule:setPathTo(destinationID)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function release(vehicle)
|
||||||
|
local wait = waits[vehicle]
|
||||||
|
if wait ~= nil and claims[wait.waitMarker.id] == vehicle then
|
||||||
|
claims[wait.waitMarker.id] = nil
|
||||||
|
end
|
||||||
|
waits[vehicle] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Står køretøjet ved målet med rest-læs, som målet ikke kan tage? Så find et Wait-punkt.
|
||||||
|
local function startWaiting(adEnv, task)
|
||||||
|
local vehicle = task.vehicle
|
||||||
|
if not vehicle.ad.drivePathModule:isTargetReached() or vehicle.ad.trailerModule:isActiveAtTrigger() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local fillType, rest = getRestLoad(adEnv, vehicle)
|
||||||
|
if fillType == nil or rest < ADUnloadWait.MIN_REST_LITERS then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local free = ADSmartPickup.getTargetFreeCapacity(adEnv, vehicle, fillType)
|
||||||
|
if free == nil or free >= ADUnloadWait.MIN_FREE_TO_UNLOAD then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local x, z = getVehiclePosition(vehicle)
|
||||||
|
local candidate = x ~= nil and ADSupplyPlanner.pickNearestFreeWaitPoint(getWaitCandidates(adEnv), claims, x, z, vehicle) or nil
|
||||||
|
if candidate == nil then
|
||||||
|
if not warnedNoWaitPoint[vehicle] then
|
||||||
|
warnedNoWaitPoint[vehicle] = true
|
||||||
|
Logging.warning(ADUnloadWait.LOG_PREFIX .. "'%s' har %.0f l rest, og målet er fuldt — intet ledigt Wait-punkt (markør med 'Wait' i navnet), så AutoDrive venter ved målet", tostring(vehicle:getName()), rest)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
warnedNoWaitPoint[vehicle] = nil
|
||||||
|
claims[candidate.marker.id] = vehicle
|
||||||
|
waits[vehicle] = {task = task, homeDestinationID = task.destinationID, waitMarker = candidate.marker, fillType = fillType, rest = rest}
|
||||||
|
redirect(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: %.0f l %s rest, målet har kun %.0f l fri", tostring(vehicle:getName()), tostring(candidate.marker.name), 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)
|
||||||
|
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
|
||||||
|
local free = ADSmartPickup.getTargetFreeCapacity(adEnv, vehicle, fillType)
|
||||||
|
if free == nil or free < rest then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local home = wait.homeDestinationID
|
||||||
|
release(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(vehicle:getName()), tostring(wait.waitMarker.name), rest, free)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ADUnloadWait.beforeUpdate(adEnv, task)
|
||||||
|
local wait = waits[task.vehicle]
|
||||||
|
if wait ~= nil then
|
||||||
|
updateWaiting(adEnv, task, wait)
|
||||||
|
else
|
||||||
|
startWaiting(adEnv, task)
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
local originalUpdate, originalFinished, originalGetSetting = task.update, task.finished, autoDrive.getSetting
|
||||||
|
|
||||||
|
task.update = function(self, dt)
|
||||||
|
local ok, err = pcall(ADUnloadWait.beforeUpdate, adEnv, self)
|
||||||
|
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)
|
||||||
|
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
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
<?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.6.1.0</version>
|
<version>1.7.0.0</version>
|
||||||
<title>
|
<title>
|
||||||
<en>AD Smart Pickup</en>
|
<en>AD Smart Pickup</en>
|
||||||
</title>
|
</title>
|
||||||
<description>
|
<description>
|
||||||
<en><![CDATA[AutoDrive-tillæg: når pålæsningsmål roteres i en mappe, køres der direkte til den silo i mappen, der har den valgte vare (mindst fyldte først), i stedet for at prøve markørerne én efter én. Vælges FLERE varer i AutoDrive, ses de som ingredienser til aflæsningsmålet (fx en TMR-blander): én traktor henter pr. tur den vare målet mangler mest, fra en silo hvor som helst på kortet. Er målet en fabrik, følger listen automatisk fabrikkens tændte produktioner (tid-til-tom først). Kræver FS25_AutoDrive med "Brug mapper" + "Rotér mål: pålæsning".]]></en>
|
<en><![CDATA[AutoDrive-tillæg: når pålæsningsmål roteres i en mappe, køres der direkte til den silo i mappen, der har den valgte vare (mindst fyldte først), i stedet for at prøve markørerne én efter én. Vælges FLERE varer i AutoDrive, ses de som ingredienser til aflæsningsmålet (fx en TMR-blander): én traktor henter pr. tur den vare målet mangler mest, fra en silo hvor som helst på kortet. Er målet en fabrik, følger listen automatisk fabrikkens tændte produktioner (tid-til-tom først). Rest-læs ved fuldt mål venter ved nærmeste ledige "Wait"-markør til der er plads. Kræver FS25_AutoDrive med "Brug mapper" + "Rotér mål: pålæsning".]]></en>
|
||||||
</description>
|
</description>
|
||||||
<iconFilename>icon.dds</iconFilename>
|
<iconFilename>icon.dds</iconFilename>
|
||||||
<multiplayer supported="true"/>
|
<multiplayer supported="true"/>
|
||||||
<extraSourceFiles>
|
<extraSourceFiles>
|
||||||
<sourceFile filename="adSupplyPlanner.lua"/>
|
<sourceFile filename="adSupplyPlanner.lua"/>
|
||||||
|
<sourceFile filename="adUnloadWait.lua"/>
|
||||||
<sourceFile filename="adSmartPickup.lua"/>
|
<sourceFile filename="adSmartPickup.lua"/>
|
||||||
</extraSourceFiles>
|
</extraSourceFiles>
|
||||||
</modDesc>
|
</modDesc>
|
||||||
|
|||||||
@@ -49,6 +49,24 @@ behovsstyring. Varer der både er input og output (kæde) må hentes.
|
|||||||
Kostald m.fl. svarer selv for niveau/kapacitet (`getHusbandryFillLevel/Capacity`), så halm+TMR til en
|
Kostald m.fl. svarer selv for niveau/kapacitet (`getHusbandryFillLevel/Capacity`), så halm+TMR til en
|
||||||
stald rangeres korrekt, selv om staldens aflæsningsstation kun "kender" foder.
|
stald rangeres korrekt, selv om staldens aflæsningsstation kun "kender" foder.
|
||||||
|
|
||||||
|
### Wait-punkter: rest-læs ved fuld stald (v1.7)
|
||||||
|
|
||||||
|
AutoDrive holder ellers en traktor med rest-læs stille ved aflæsningspunktet, til den er tømt — og
|
||||||
|
spærrer for de næste i køen. Med Wait-punkter:
|
||||||
|
|
||||||
|
1. Sæt markører med ordet **`Wait`** i navnet (fx `Wait 1`, `Cow Wait 2`) eller i en mappe kaldet
|
||||||
|
`Wait` — så mange du har plads til; de er en fælles pulje for alle traktorer.
|
||||||
|
2. Kan målet ikke tage mere af vognens vare, kører traktoren til det **nærmeste ledige** Wait-punkt
|
||||||
|
(modden holder styr på reservationer) og venter dér.
|
||||||
|
3. Når målet igen har plads til **hele** resten, kører den tilbage og læsser af. Derefter normal tur.
|
||||||
|
|
||||||
|
Kun den aktuelle aflæsningsopgaves destination ændres — mål, rotation og loop-tæller røres ikke.
|
||||||
|
Ventende traktorer ser rotationen som slået fra, så AutoDrive ikke kører videre fra Wait-punktet.
|
||||||
|
Ingen ledige Wait-punkter → AutoDrive venter ved målet som i dag (én advarsel i loggen).
|
||||||
|
|
||||||
|
**Læs der passer, foretrækkes (v1.7):** ved valg af næste tur går varer, hvor et fuldt læs kan komme af
|
||||||
|
i målet, forud; passer intet fuldt læs, tages den mest trængende alligevel (og Wait-punktet bruges).
|
||||||
|
|
||||||
**Opsætning med flere stalde:** én traktor (eller flere) pr. stald med *Rotér mål: kun pålæsning*.
|
**Opsætning med flere stalde:** én traktor (eller flere) pr. stald med *Rotér mål: kun pålæsning*.
|
||||||
Brug IKKE "pålæsning og aflæsning": AutoDrive vælger næste pålæsning FØR den roterer aflæsningsmålet,
|
Brug IKKE "pålæsning og aflæsning": AutoDrive vælger næste pålæsning FØR den roterer aflæsningsmålet,
|
||||||
så behovet ville blive regnet for den forkerte stald. Pålæsningsmarkøren må ikke være fast på en
|
så behovet ville blive regnet for den forkerte stald. Pålæsningsmarkøren må ikke være fast på en
|
||||||
|
|||||||
@@ -84,7 +84,9 @@ FS25_AutoDrive = {
|
|||||||
AutoDrive = {
|
AutoDrive = {
|
||||||
RT_NONE = RT_NONE,
|
RT_NONE = RT_NONE,
|
||||||
getSetting = function(settingName) return settingName == "rotateTargets" and RT_ONLYPICKUP or "andet" end,
|
getSetting = function(settingName) return settingName == "rotateTargets" and RT_ONLYPICKUP or "andet" end,
|
||||||
getAllFillLevels = function(trailers) return trailers.level end,
|
getAllFillLevels = function(trailers) return trailers.level, trailers.capacity or 0, false, 0 end,
|
||||||
|
getAllUnits = function(vehicle) return vehicle.trailers or {level = 0} end,
|
||||||
|
getAllDischargeableUnits = function(vehicle) return vehicle.units or {} end,
|
||||||
},
|
},
|
||||||
-- opgaven noterer hvad AutoDrive "ser" af indstillinger mens den opdaterer
|
-- opgaven noterer hvad AutoDrive "ser" af indstillinger mens den opdaterer
|
||||||
LoadAtDestinationTask = {update = function(self)
|
LoadAtDestinationTask = {update = function(self)
|
||||||
@@ -93,6 +95,12 @@ FS25_AutoDrive = {
|
|||||||
if self.shouldFail then error("task boom") end
|
if self.shouldFail then error("task boom") end
|
||||||
end},
|
end},
|
||||||
ADMultipleTargetsManager = {pickups = {}, getNextPickup = function() return "ORIGINAL" end},
|
ADMultipleTargetsManager = {pickups = {}, getNextPickup = function() return "ORIGINAL" end},
|
||||||
|
-- aflæsningsopgaven: original update tæller bare kald; finished markerer
|
||||||
|
UnloadAtDestinationTask = {
|
||||||
|
STATE_PATHPLANNING = 1, STATE_DRIVING = 2,
|
||||||
|
update = function(self) self.updates = (self.updates or 0) + 1 end,
|
||||||
|
finished = function(self) self.isFinished = true end,
|
||||||
|
},
|
||||||
ADGraphManager = {
|
ADGraphManager = {
|
||||||
getMapMarkersInGroup = function(_, group)
|
getMapMarkersInGroup = function(_, group)
|
||||||
local result = {}
|
local result = {}
|
||||||
@@ -142,6 +150,7 @@ local function makeSupplyDriver(firstMarker, secondMarker, fillTypes)
|
|||||||
end
|
end
|
||||||
|
|
||||||
dofile("FS25_ADSmartPickup/adSupplyPlanner.lua")
|
dofile("FS25_ADSmartPickup/adSupplyPlanner.lua")
|
||||||
|
dofile("FS25_ADSmartPickup/adUnloadWait.lua")
|
||||||
dofile("FS25_ADSmartPickup/adSmartPickup.lua")
|
dofile("FS25_ADSmartPickup/adSmartPickup.lua")
|
||||||
listener:update(16) -- installerer hook'en
|
listener:update(16) -- installerer hook'en
|
||||||
local manager = FS25_AutoDrive.ADMultipleTargetsManager
|
local manager = FS25_AutoDrive.ADMultipleTargetsManager
|
||||||
@@ -417,6 +426,115 @@ check("P4 stald: TMR (90 % tom) hentes før halm (10 % tom)", manager:getNextPic
|
|||||||
barnLevels[TMR] = 95000
|
barnLevels[TMR] = 95000
|
||||||
check("P4 stald: halm hentes når TMR er fuld", manager:getNextPickup(cowDriver, false), 21)
|
check("P4 stald: halm hentes når TMR er fuld", manager:getNextPickup(cowDriver, false), 21)
|
||||||
|
|
||||||
|
-- ==== Wait-punkter: rest-læs + fuld stald -> vent ved nærmeste ledige Wait, tilbage når resten passer ====
|
||||||
|
local UnloadTask = FS25_AutoDrive.UnloadAtDestinationTask
|
||||||
|
local waitBarnLevels, waitBarnCapacity = {}, {[STRAW] = 50000, [TMR] = 100000}
|
||||||
|
local function buildWaitWorld()
|
||||||
|
buildCowWorld()
|
||||||
|
waitBarnLevels = {[STRAW] = 50000, [TMR] = 10000} -- halm FULD
|
||||||
|
local barn2 = {
|
||||||
|
getName = function() return "Kostald" end,
|
||||||
|
getHusbandryFillLevel = function(_, fillType) return waitBarnLevels[fillType] or 0 end,
|
||||||
|
getHusbandryCapacity = function(_, fillType) return waitBarnCapacity[fillType] or 0 end,
|
||||||
|
getHusbandryFreeCapacity = function(_, fillType) return (waitBarnCapacity[fillType] or 0) - (waitBarnLevels[fillType] or 0) end,
|
||||||
|
getHusbandryIsFillTypeSupported = function(_, fillType) return waitBarnCapacity[fillType] ~= nil end,
|
||||||
|
}
|
||||||
|
unloadStations[1].targetStorages = nil
|
||||||
|
unloadStations[1].owningPlaceable = barn2
|
||||||
|
addMarker(40, "Wait 1", "Cow Food", 5100, 0)
|
||||||
|
addMarker(41, "Wait 2", "Cow Food", 5300, 0)
|
||||||
|
end
|
||||||
|
-- traktor der holder ved stalden (markør 30) med rest-læs halm på vognen
|
||||||
|
local function makeUnloadingVehicle(name, node, x, restStraw)
|
||||||
|
local vehicle = makeSupplyDriver(markerByIndex(21), markerByIndex(30), {TMR, STRAW})
|
||||||
|
vehicle.getName = function() return name end
|
||||||
|
vehicle.rootNode = node
|
||||||
|
nodePositions[node] = {x = x, z = 10}
|
||||||
|
vehicle.units = {{object = {
|
||||||
|
getFillUnitFillLevel = function() return restStraw end,
|
||||||
|
getFillUnitFillType = function() return STRAW end,
|
||||||
|
}, fillUnitIndex = 1}}
|
||||||
|
vehicle.trailers = {level = restStraw, capacity = 40000}
|
||||||
|
vehicle.targetReached = true
|
||||||
|
vehicle.ad.drivePathModule = {
|
||||||
|
isTargetReached = function() return vehicle.targetReached end,
|
||||||
|
setPathTo = function(self, id) self.pathTo = id end,
|
||||||
|
}
|
||||||
|
vehicle.ad.trailerModule = {isActiveAtTrigger = function() return false end}
|
||||||
|
local task = {vehicle = vehicle, destinationID = 30, state = UnloadTask.STATE_DRIVING, trailers = vehicle.trailers}
|
||||||
|
return vehicle, task
|
||||||
|
end
|
||||||
|
|
||||||
|
-- W1: fuld stald + rest-læs -> omdirigeres til nærmeste ledige Wait; AD's original kaldes stadig
|
||||||
|
buildWaitWorld()
|
||||||
|
local v1, t1 = makeUnloadingVehicle("Fastrac A", 501, 5000, 35000)
|
||||||
|
local waitLogMark = #logLines
|
||||||
|
UnloadTask.update(t1, 16)
|
||||||
|
check("W1 omdirigeret til nærmeste Wait", t1.destinationID, 40)
|
||||||
|
check("W1 rute lagt til Wait", v1.ad.drivePathModule.pathTo, 40)
|
||||||
|
check("W1 original update kaldt", t1.updates, 1)
|
||||||
|
check("W1 log fortæller om ventning", countLogsSince(waitLogMark, "Wait 1"), 1)
|
||||||
|
|
||||||
|
-- W2: anden traktor i samme situation -> Wait 1 er optaget -> Wait 2
|
||||||
|
local v2, t2 = makeUnloadingVehicle("Fastrac B", 502, 5010, 20000)
|
||||||
|
UnloadTask.update(t2, 16)
|
||||||
|
check("W2 optaget Wait springes over", t2.destinationID, 41)
|
||||||
|
|
||||||
|
-- W3: mens A venter, skjules rotationen (ellers kører AD videre fra Wait-punktet ved "rotér aflæsning")
|
||||||
|
check("W3 rotation skjult for ventende", FS25_AutoDrive.AutoDrive.getSetting("rotateTargets", v1), RT_NONE)
|
||||||
|
check("W3 andre indstillinger urørt", FS25_AutoDrive.AutoDrive.getSetting("useFolders", v1), "andet")
|
||||||
|
|
||||||
|
-- W4: stalden får plads til HELE resten -> A sendes hjem, Wait 1 frigives, rotation tilbage
|
||||||
|
waitBarnLevels[STRAW] = 10000 -- 40000 fri >= 35000
|
||||||
|
v1.targetReached = true -- står ved Wait 1
|
||||||
|
UnloadTask.update(t1, 16)
|
||||||
|
check("W4 sendt tilbage til stalden", t1.destinationID, 30)
|
||||||
|
check("W4 rute lagt hjem", v1.ad.drivePathModule.pathTo, 30)
|
||||||
|
check("W4 rotation gendannet", FS25_AutoDrive.AutoDrive.getSetting("rotateTargets", v1), RT_ONLYPICKUP)
|
||||||
|
local v3, t3 = makeUnloadingVehicle("Fastrac C", 503, 5020, 30000)
|
||||||
|
waitBarnLevels[STRAW] = 50000
|
||||||
|
UnloadTask.update(t3, 16)
|
||||||
|
check("W4 Wait 1 er ledig igen", t3.destinationID, 40)
|
||||||
|
|
||||||
|
-- W5: plads til noget, men ikke hele resten -> B bliver ved Wait (kun hele læs kommer af)
|
||||||
|
waitBarnLevels[STRAW] = 45000 -- 5000 fri < 20000
|
||||||
|
UnloadTask.update(t2, 16)
|
||||||
|
check("W5 delvis plads -> venter stadig", t2.destinationID, 41)
|
||||||
|
|
||||||
|
-- W6: stalden har plads ved ankomst -> ingen omdirigering (AD læsser af som normalt)
|
||||||
|
buildWaitWorld()
|
||||||
|
waitBarnLevels[STRAW] = 10000
|
||||||
|
local v6, t6 = makeUnloadingVehicle("Fastrac D", 506, 5000, 35000)
|
||||||
|
UnloadTask.update(t6, 16)
|
||||||
|
check("W6 plads ved ankomst -> uændret mål", t6.destinationID, 30)
|
||||||
|
|
||||||
|
-- W7: ingen Wait-punkter -> uændret (AD venter som i dag) + én advarsel
|
||||||
|
buildCowWorld()
|
||||||
|
unloadStations[1].targetStorages = nil
|
||||||
|
unloadStations[1].owningPlaceable = { getHusbandryFillLevel = function() return 50000 end, getHusbandryCapacity = function() return 50000 end,
|
||||||
|
getHusbandryFreeCapacity = function() return 0 end, getHusbandryIsFillTypeSupported = function() return true end }
|
||||||
|
local v7, t7 = makeUnloadingVehicle("Fastrac E", 507, 5000, 35000)
|
||||||
|
local warnMark = #logLines
|
||||||
|
UnloadTask.update(t7, 16); UnloadTask.update(t7, 16)
|
||||||
|
check("W7 uden Wait-punkter -> uændret mål", t7.destinationID, 30)
|
||||||
|
check("W7 én advarsel", countLogsSince(warnMark, "WARN"), 1)
|
||||||
|
|
||||||
|
-- W8: B og C venter stadig og holder begge punkter; stoppes de i AD, frigives punkterne
|
||||||
|
buildWaitWorld()
|
||||||
|
local v8, t8 = makeUnloadingVehicle("Fastrac F", 508, 5000, 35000)
|
||||||
|
UnloadTask.update(t8, 16)
|
||||||
|
check("W8 alle Wait-punkter optaget -> uændret", t8.destinationID, 30)
|
||||||
|
v2.ad.stateModule.active, v3.ad.stateModule.active = false, false
|
||||||
|
listener:update(16)
|
||||||
|
UnloadTask.update(t8, 16)
|
||||||
|
check("W8 venter ved Wait 1 efter frigivelse", t8.destinationID, 40)
|
||||||
|
UnloadTask.finished(t8)
|
||||||
|
check("W8 original finished kaldt", t8.isFinished, true)
|
||||||
|
local v9, t9 = makeUnloadingVehicle("Fastrac G", 509, 5000, 35000)
|
||||||
|
UnloadTask.update(t9, 16)
|
||||||
|
check("W8 Wait 1 frigivet efter finished", t9.destinationID, 40)
|
||||||
|
check("W8 rotation gendannet efter finished", FS25_AutoDrive.AutoDrive.getSetting("rotateTargets", v8), RT_ONLYPICKUP)
|
||||||
|
|
||||||
-- L1: moden valgte siloen -> AutoDrive skal stå og læsse (rotation skjules under opgaven)
|
-- L1: moden valgte siloen -> AutoDrive skal stå og læsse (rotation skjules under opgaven)
|
||||||
resetWorld()
|
resetWorld()
|
||||||
local loadLevels = {[SUGARBEET] = 5000}
|
local loadLevels = {[SUGARBEET] = 5000}
|
||||||
|
|||||||
@@ -122,5 +122,41 @@ check("vare fabrikken ikke kender -> ikke beskyttet", ADSupplyPlanner.isProducti
|
|||||||
check("ingen produktioner -> ikke beskyttet", ADSupplyPlanner.isProductionInput({}, STRAW), false)
|
check("ingen produktioner -> ikke beskyttet", ADSupplyPlanner.isProductionInput({}, STRAW), false)
|
||||||
check("produktion uden inputs/outputs tåles", ADSupplyPlanner.isProductionInput({{id = "x"}}, STRAW), false)
|
check("produktion uden inputs/outputs tåles", ADSupplyPlanner.isProductionInput({{id = "x"}}, STRAW), false)
|
||||||
|
|
||||||
|
-- rankNeeds med læs-størrelse: varer hvor et FULDT læs passer går forud, ellers mest trængende
|
||||||
|
local sized = ADSupplyPlanner.rankNeeds({
|
||||||
|
{fillType = STRAW, level = 40000, capacity = 50000, litersPerMinute = 500}, -- 80 min, kun 10000 fri
|
||||||
|
{fillType = TMR, level = 20000, capacity = 100000, litersPerMinute = 50}, -- 400 min, 80000 fri
|
||||||
|
}, 40000)
|
||||||
|
check("fuldt læs der passer vinder over mere trængende der ikke passer", sized[1].fillType, TMR)
|
||||||
|
check("den der ikke passer bevares som reserve", sized[2].fillType, STRAW)
|
||||||
|
check("fits markeres", sized[1].fits, true)
|
||||||
|
check("ikke-fits markeres", sized[2].fits, false)
|
||||||
|
local noneFit = ADSupplyPlanner.rankNeeds({
|
||||||
|
{fillType = STRAW, level = 40000, capacity = 50000, litersPerMinute = 500},
|
||||||
|
{fillType = TMR, level = 90000, capacity = 100000, litersPerMinute = 50},
|
||||||
|
}, 40000)
|
||||||
|
check("passer intet -> mest trængende først", noneFit[1].fillType, STRAW)
|
||||||
|
check("uden læs-størrelse -> som før", ADSupplyPlanner.rankNeeds({{fillType = STRAW, level = 40000, capacity = 50000}})[1].fits, nil)
|
||||||
|
|
||||||
|
-- Wait-punkter: navn med ordet "Wait" (uanset store/små) eller mappen "Wait"
|
||||||
|
check("'Cow 1 Food Wait' er wait-punkt", ADSupplyPlanner.isWaitMarker({name = "Cow 1 Food Wait", group = "Cow Food"}), true)
|
||||||
|
check("'wait 3' er wait-punkt", ADSupplyPlanner.isWaitMarker({name = "wait 3", group = "Andet"}), true)
|
||||||
|
check("mappen Wait tæller", ADSupplyPlanner.isWaitMarker({name = "P2", group = "Wait"}), true)
|
||||||
|
check("'Waiter' er ikke wait-punkt", ADSupplyPlanner.isWaitMarker({name = "Waiter", group = "X"}), false)
|
||||||
|
check("'Cow 1 Food' er ikke wait-punkt", ADSupplyPlanner.isWaitMarker({name = "Cow 1 Food", group = "Cow Food"}), false)
|
||||||
|
|
||||||
|
-- nærmeste ledige wait-punkt; candidates = {{marker, x, z}}, claims = {[markerId] = vehicle}
|
||||||
|
local V1, V2 = {}, {}
|
||||||
|
local waitPoints = {
|
||||||
|
{marker = {id = 1, name = "Wait A"}, x = 0, z = 0},
|
||||||
|
{marker = {id = 2, name = "Wait B"}, x = 100, z = 0},
|
||||||
|
{marker = {id = 3, name = "Wait C"}, x = 300, z = 0},
|
||||||
|
}
|
||||||
|
check("nærmeste ledige vælges", ADSupplyPlanner.pickNearestFreeWaitPoint(waitPoints, {}, 10, 0, V1).marker.id, 1)
|
||||||
|
check("reserveret springes over", ADSupplyPlanner.pickNearestFreeWaitPoint(waitPoints, {[1] = V2}, 10, 0, V1).marker.id, 2)
|
||||||
|
check("egen reservation tæller som ledig", ADSupplyPlanner.pickNearestFreeWaitPoint(waitPoints, {[1] = V1}, 10, 0, V1).marker.id, 1)
|
||||||
|
check("alle optaget -> nil", ADSupplyPlanner.pickNearestFreeWaitPoint(waitPoints, {[1] = V2, [2] = V2, [3] = V2}, 10, 0, V1), nil)
|
||||||
|
check("ingen punkter -> nil", ADSupplyPlanner.pickNearestFreeWaitPoint({}, {}, 0, 0, V1), nil)
|
||||||
|
|
||||||
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