diff --git a/FS25_ADSmartPickup/adFieldSiloPlanner.lua b/FS25_ADSmartPickup/adFieldSiloPlanner.lua index 4000bec..f853c93 100644 --- a/FS25_ADSmartPickup/adFieldSiloPlanner.lua +++ b/FS25_ADSmartPickup/adFieldSiloPlanner.lua @@ -45,6 +45,20 @@ local function chooseStore(stores, silo, fillType, liters, minLiters, reserved, return best or most end +-- Tom silo: en lånt vogn med last, der har stået stille ved en tom marksilo i REST_IDLE_MS, kører resten videre +-- (AutoDrive venter ellers til vognen er fuld). s = {atSilo, stillMs, siloStock, cargo} +ADFieldSiloPlanner.REST_IDLE_MS = 30000 +ADFieldSiloPlanner.REST_MIN_LITERS = 100 +function ADFieldSiloPlanner.restDue(s) + return s.atSilo == true and (s.stillMs or 0) >= ADFieldSiloPlanner.REST_IDLE_MS + and (s.cargo or 0) >= ADFieldSiloPlanner.REST_MIN_LITERS and (s.siloStock or 0) < ADFieldSiloPlanner.REST_MIN_LITERS +end + +-- Lager til en rest: fabrik kun med plads til hele resten, ellers gårdlager (nærmeste med plads, ellers mest plads). +function ADFieldSiloPlanner.restStore(stores, silo, fillType, liters) + return chooseStore(stores, silo, fillType, liters, 1, {}, true) or chooseStore(stores, silo, fillType, liters, 1, {}, false) +end + local function canCarry(vehicle, fillType) if (vehicle.capacity or 0) <= 0 then return false end if vehicle.cargo ~= nil and vehicle.cargo ~= fillType then return false end diff --git a/FS25_ADSmartPickup/adFieldSilos.lua b/FS25_ADSmartPickup/adFieldSilos.lua index e2cb0f3..96d5752 100644 --- a/FS25_ADSmartPickup/adFieldSilos.lua +++ b/FS25_ADSmartPickup/adFieldSilos.lua @@ -17,6 +17,7 @@ ADFieldSilos.CARGO_MIN_LITERS = 100 -- rest under dette tæller ikke som last ADFieldSilos.FARM_RADIUS = 100 -- Siloer højst så langt fra en nabo hører til samme klynge (Farma-rækker: 15 m mellem siloer, 18 m mellem rækker) ADFieldSilos.CLUSTER_LINK = 60 +ADFieldSilos.REST_SILO_DISTANCE = 40 -- m fra marksiloens markør: vognen holder ved siloen ADFieldSilos.FETCH_TIMEOUT_MS = 300000 -- så længe må en traktor være om at hente en puljevogn ADFieldSilos.MOVING_SPEED = 0.00028 -- m/ms, samme grænse som AutoDrive ADFieldSilos.FILE_NAME = "ADSmartPickup_fieldsilos.xml" @@ -440,6 +441,66 @@ function ADFieldSilos.stopAll(adEnv) for _, vehicle in ipairs(list) do release(adEnv, vehicle, "stopped") end end +-- Efter genindlæsning genoptager AutoDrive selv "Hent og aflever" fra marksiloerne, men lånene er glemt: sådanne +-- vogne overtages igen som lån (tæller i "højst x ude", og tom silo-vagten gælder). +function ADFieldSilos.adoptOrphans(adEnv, silos) + local byMarker = {} + for _, silo in ipairs(silos or {}) do + if silo.markerId ~= nil then byMarker[silo.markerId] = silo end + end + for _, vehicle in ipairs(ADRunsController.getFarmVehicles(adEnv)) do + local module = vehicle.ad ~= nil and vehicle.ad.stateModule or nil + if loans[vehicle] == nil and module ~= nil and module:isActive() and module:getMode() == adEnv.AutoDrive.MODE_PICKUPANDDELIVER then + local first = module:getFirstMarker() + local silo = first ~= nil and byMarker[first.id] or nil + if silo ~= nil then + local fillType = module.getFillType ~= nil and module:getFillType() or nil + loans[vehicle] = {siloId = silo.id, siloName = silo.name, fillType = fillType, liters = 0, startedMs = nowMs, + lastProgressMs = nowMs, lastFill = fillLevel(adEnv, vehicle), hasProgressed = true, adopted = true} + Logging.info(ADFieldSilos.LOG_PREFIX .. "'%s' overtaget igen (AutoDrive kører fra %s efter genindlæsning)", + tostring(ADRuns.vehicleLabel(vehicle)), tostring(silo.name)) + end + end + end +end + +-- Tom silo-vagt: en lånt vogn med last, der står stille ved en tom marksilo, kører resten videre — til en fabrik +-- med plads til hele resten, ellers til et gårdlager (AutoDrive venter ellers til vognen er fuld). +local warnedNoRestStore = {} +local function checkRest(adEnv, vehicle, loan, moving, fill) + if loan.delivering or loan.fetching then return end + local silo = nil + for _, candidate in ipairs(ADFieldSilos.lastSilos or {}) do + if candidate.id == loan.siloId then silo = candidate end + end + local wayPoint = silo ~= nil and silo.markerId ~= nil and adEnv.ADGraphManager:getWayPointById(silo.markerId) or nil + if wayPoint == nil or vehicle.rootNode == nil then return end + local x, _, z = getWorldTranslation(vehicle.rootNode) + local atSilo = (x - wayPoint.x) ^ 2 + (z - wayPoint.z) ^ 2 <= ADFieldSilos.REST_SILO_DISTANCE ^ 2 + if moving or not atSilo then loan.stillSince = nil return end + loan.stillSince = loan.stillSince or nowMs + local state = {atSilo = atSilo, stillMs = nowMs - loan.stillSince, + siloStock = (silo.stock or {})[loan.fillType] or 0, cargo = fill} + if not ADFieldSiloPlanner.restDue(state) then return end + local store = ADFieldSiloPlanner.restStore(ADFieldSilos.lastStores, silo, loan.fillType, fill) + if store == nil then + if not warnedNoRestStore[vehicle] then + warnedNoRestStore[vehicle] = true + Logging.warning(ADFieldSilos.LOG_PREFIX .. "'%s' har %d l %s fra tom %s, men intet lager har plads", + tostring(ADRuns.vehicleLabel(vehicle)), math.floor(fill), fillTypeName(loan.fillType), tostring(silo.name)) + end + return + end + warnedNoRestStore[vehicle] = nil + loan.delivering = true + loan.startedMs, loan.hasProgressed = nowMs, true + local isPool = ADHitch ~= nil and #ADHitch.poolParkIds(adEnv, vehicle) > 0 + if not isPool then ADFieldWork.enableParkAtJobFinished(vehicle) end + ADFieldWork.startAd(adEnv, vehicle, adEnv.AutoDrive.MODE_DELIVERTO, store.markerId, nil, loan.fillType) + Logging.info(ADFieldSilos.LOG_PREFIX .. "'%s': %s er tom — kører resten (%d l %s) til %s", + tostring(ADRuns.vehicleLabel(vehicle)), tostring(silo.name), math.floor(fill), fillTypeName(loan.fillType), tostring(store.name)) +end + local function checkLoans(adEnv) local list = {} for vehicle in pairs(loans) do table.insert(list, vehicle) end @@ -467,6 +528,9 @@ local function checkLoans(adEnv) controlled = vehicle.getIsControlled ~= nil and vehicle:getIsControlled() or false, fill = fill, moving = (vehicle.lastSpeedReal or 0) >= ADFieldSilos.MOVING_SPEED, } + checkRest(adEnv, vehicle, loan, vehicleState.moving, fill) + if loans[vehicle] ~= loan then return end + vehicleState.adActive = stateModule ~= nil and stateModule:isActive() local reason = ADFieldSilos.releaseReason(loan, vehicleState, nowMs) if vehicleState.moving or fill ~= (loan.lastFill or 0) then loan.hasProgressed = true end loan.lastFill = fill @@ -511,6 +575,11 @@ end function ADFieldSilos.planNow(adEnv) local cfg = ADFieldSilos.config local silos, stores, byId = ADFieldSilos.collectBuildings(adEnv) + ADFieldSilos.lastSilos, ADFieldSilos.lastStores = silos, stores + if cfg.enabled then + local okAdopt, errAdopt = pcall(ADFieldSilos.adoptOrphans, adEnv, silos) + if not okAdopt then Logging.warning(ADFieldSilos.LOG_PREFIX .. "overtagelse efter genindlæsning fejlede: %s", tostring(errAdopt)) end + end local vehicles, vehiclesById, poolById = ADFieldSilos.collectVehicles(adEnv) local feedReserve, farmStock = nil, nil if cfg.toProduction then diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index 356f70a..a21cb2d 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.50.3.0 + 1.50.4.0 <en>AD Smart Pickup</en> diff --git a/tests/test_adFieldSiloPlanner.lua b/tests/test_adFieldSiloPlanner.lua index 3e50742..bfa6554 100644 --- a/tests/test_adFieldSiloPlanner.lua +++ b/tests/test_adFieldSiloPlanner.lua @@ -129,5 +129,20 @@ check("R2 alle hvede-vogne holdt tilbage", #left, 1) check("R3 intet behov -> alle fri", #P.withoutHarvestReserve(vs, {}), 3) check("R4 input muteres ikke", #vs, 3) +-- Tom silo: lånt vogn med last står ved en tom marksilo -> resten køres videre (AD venter ellers til fuld) +-- (in-game 28/9: Tømmevogn 2 og 4 stod ved tom F17 Unload med kartofler) +local restState = {atSilo = true, stillMs = 45000, siloStock = 0, cargo = 12000} +check("RS1 tom silo med last -> kør videre", P.restDue(restState), true) +check("RS2 siloen har stadig varer -> vent", P.restDue({atSilo = true, stillMs = 45000, siloStock = 30000, cargo = 12000}), false) +check("RS3 ikke ved siloen -> nej", P.restDue({atSilo = false, stillMs = 45000, siloStock = 0, cargo = 12000}), false) +check("RS4 lige ankommet -> vent", P.restDue({atSilo = true, stillMs = 5000, siloStock = 0, cargo = 12000}), false) +check("RS5 tom vogn -> nej", P.restDue({atSilo = true, stillMs = 45000, siloStock = 0, cargo = 0}), false) +local restStores = { + {id = "fab", kind = "production", markerId = 1, x = 10, z = 0, free = {[10] = 5000}}, + {id = "gaard", kind = "silo", markerId = 2, x = 500, z = 0, free = {[10] = 900000}}, +} +check("RS6 fabrik uden plads til hele resten -> gårdsilo", P.restStore(restStores, {x = 0, z = 0}, 10, 12000).id, "gaard") +check("RS7 fabrik med plads -> fabrik", P.restStore(restStores, {x = 0, z = 0}, 10, 4000).id, "fab") +check("RS8 intet tager varen", P.restStore(restStores, {x = 0, z = 0}, 99, 4000), nil) print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)