diff --git a/FS25_ADSmartPickup/adOutbound.lua b/FS25_ADSmartPickup/adOutbound.lua index c402fa1..dde2738 100644 --- a/FS25_ADSmartPickup/adOutbound.lua +++ b/FS25_ADSmartPickup/adOutbound.lua @@ -62,6 +62,30 @@ local function fillTypeName(fillType) return g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(fillType) or tostring(fillType) end +-- Lagrene en kildes læssestation trækker fra (FS25: sourceStorages = {[storage] = storage}); nil = ukendt. +local function sourceStorages(source) + local station = source ~= nil and source.station or nil + if station == nil or type(station.sourceStorages) ~= "table" then + return nil + end + local list = {} + for key, value in pairs(station.sourceStorages) do + table.insert(list, type(key) == "table" and key or value) + end + return #list > 0 and list or nil +end + +local mergedLogged = {} +local function logMergedSources(vehicle, run, merged) + for keptId, ids in pairs(merged or {}) do + local key = tostring(run.id) .. ":" .. tostring(keptId) .. ":" .. table.concat(ids, ",") + if not mergedLogged[key] then + mergedLogged[key] = true + Logging.info(ADOutbound.LOG_PREFIX .. "run '%s': kilde %s deler lager med %s — regnes som ét stop", tostring(run.name), tostring(keptId), table.concat(ids, ", ")) + end + end +end + local function getStatus(run) status[run.id] = status[run.id] or {} return status[run.id] @@ -199,6 +223,10 @@ function ADOutbound.buildPlanInput(adEnv, run, vehicle) sourcesById[wayPointId] = source end end + -- kilder der deler lager (mælketanke som udvidelser) er ét stop — ellers kører runden mellem dem + local merged + sources, merged = ADOutboundPlanner.dedupeSharedSources(sources, sourceStorages) + logMergedSources(vehicle, run, merged) local byName, products = {}, {} local time = now() for _, source in ipairs(sources) do diff --git a/FS25_ADSmartPickup/adOutboundPlanner.lua b/FS25_ADSmartPickup/adOutboundPlanner.lua index 0d525f6..8998af7 100644 --- a/FS25_ADSmartPickup/adOutboundPlanner.lua +++ b/FS25_ADSmartPickup/adOutboundPlanner.lua @@ -193,6 +193,39 @@ local function buildTrip(primary, candidates, input) return trip end +-- Kilder hvis læssestationer trækker fra samme lagre (fx mælketanke der er udvidelser inden for hinandens +-- storageRadius) er ét stop: læsser man ved én, tømmes de alle. Den første i rækkefølgen beholdes, de +-- øvrige lægges ind under den. storagesOf(source) -> liste af lager-objekter (nil = ukendt, beholdes). +-- Returnerer kept (rækkefølge bevaret) og merged {[beholdt wayPointId] = {sammenlagte wayPointIds}}. +function ADOutboundPlanner.dedupeSharedSources(sources, storagesOf) + local kept, merged, groups = {}, {}, {} + for _, source in ipairs(sources or {}) do + local storages = storagesOf(source) + local owner = nil + if storages ~= nil then + for _, group in ipairs(groups) do + for _, storage in ipairs(storages) do + if group.storages[storage] then owner = group break end + end + if owner ~= nil then break end + end + end + if owner ~= nil then + for _, storage in ipairs(storages) do owner.storages[storage] = true end + merged[owner.source.wayPointId] = merged[owner.source.wayPointId] or {} + table.insert(merged[owner.source.wayPointId], source.wayPointId) + else + table.insert(kept, source) + if storages ~= nil then + local set = {} + for _, storage in ipairs(storages) do set[storage] = true end + table.insert(groups, {source = source, storages = set}) + end + end + end + return kept, merged +end + function ADOutboundPlanner.plan(input) local sell, store, reasons = collectCandidates(input) if sell[1] ~= nil then diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index 4645a49..a2beaf4 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.17.2.0 + 1.17.3.0 <en>AD Smart Pickup</en> diff --git a/docs/BRUGERMANUAL.md b/docs/BRUGERMANUAL.md index f4472e8..f73b1de 100644 --- a/docs/BRUGERMANUAL.md +++ b/docs/BRUGERMANUAL.md @@ -1,6 +1,6 @@ # AD Smart Pickup — brugermanual -Version 1.17.2 · Farming Simulator 25 · tillæg til AutoDrive +Version 1.17.3 · Farming Simulator 25 · tillæg til AutoDrive *English version: [USER_MANUAL.md](USER_MANUAL.md)* @@ -270,6 +270,11 @@ Under **Fra** kan du vælge: | **Stald** | Mælk, gylle og gødning fra staldens læssepunkter, inklusive tilknyttede udvidelsestanke som Milk Tank Extension | | **Objektlager** | Paller og baller. De tages ud én ad gangen | +**Kilder med fælles lager:** nogle lagre er udvidelser, der deler beholdning med alle inden for en +vis afstand, fx mælketanke i en række. Læsser man ved én, tømmes de alle. Moden opdager det selv og +regner dem som ét stop, så traktoren ikke kører rundt mellem tankene. Den første i listen bruges, og +loggen skriver hvilke kilder der er slået sammen. Det er nok at vælge én tank som kilde. + ### Politik pr. vare Hver vare, kilderne har, står med sin mængde og nuværende pris. Vælg en politik: diff --git a/docs/USER_MANUAL.md b/docs/USER_MANUAL.md index c650cd5..adf0334 100644 --- a/docs/USER_MANUAL.md +++ b/docs/USER_MANUAL.md @@ -1,6 +1,6 @@ # AD Smart Pickup — user manual -Version 1.17.2 · Farming Simulator 25 · add-on for AutoDrive +Version 1.17.3 · Farming Simulator 25 · add-on for AutoDrive *Dansk version: [BRUGERMANUAL.md](BRUGERMANUAL.md)* @@ -273,6 +273,12 @@ Under **From** you can pick: | **Barn** | Milk, slurry and manure from the barn's loading points, including attached extension tanks such as Milk Tank Extension | | **Object storage** | Pallets and bales. They are taken out one at a time | +**Sources with shared storage:** some storages are extensions that share their stock with everything +within a set distance, such as a row of milk tanks. Loading at one empties them all. The mod detects +this and treats them as one stop, so the tractor does not drive between the tanks. The first one in +the list is used, and the log says which sources were merged. Choosing a single tank as the source +is enough. + ### Policy per good Every good in the sources is listed with its amount and current price. Pick a policy: diff --git a/tests/test_adOutbound.lua b/tests/test_adOutbound.lua index 0266f2b..8f18aff 100644 --- a/tests/test_adOutbound.lua +++ b/tests/test_adOutbound.lua @@ -543,5 +543,23 @@ v24b.trailers[1].setAllTensionBeltsActive = function(self, active) self.beltsAct adEnv.LoadAtDestinationTask.finished(newTask(v24b, 1)) check("O24 tipvogn uden tur: rører ikke stropper", v24b.trailers[1].beltsActive, nil) +-- O-pool: to kilder hvis stationer deler lager (mælketanke som udvidelser) = ét stop i runden +do + local pool = {} + local st1, st2 = sources[1].station, sources[2].station + sources[1].station = {name = "tankA", sourceStorages = {[pool] = pool}} + sources[2].station = {name = "tankB", sourceStorages = {[pool] = pool}} + local vPool = newVehicle("Tank", 0, 0, 10000) + runs[vPool] = outboundRun(77, {1, 2}, {{fillType = "SUGAR", policy = "sell"}}, {collectRound = true}) + local inputPool = ADOutbound.buildPlanInput(adEnv, runs[vPool], vPool) + local ids = {} + for _, product in ipairs(inputPool.products) do + for _, source in ipairs(product.sources) do table.insert(ids, tostring(source.wayPointId)) end + end + check("O-pool kun første kilde", table.concat(ids, ","), "1") + check("O-pool logget", lastLog("deler lager") ~= nil, true) + sources[1].station, sources[2].station = st1, st2 +end + print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adOutboundPlanner.lua b/tests/test_adOutboundPlanner.lua index 71eee2f..2eefc8d 100644 --- a/tests/test_adOutboundPlanner.lua +++ b/tests/test_adOutboundPlanner.lua @@ -144,5 +144,17 @@ local twoSources = product("STRAW_BASKET", BASKET, "store", {{wayPointId = 5, av check("P14 paller summeres over kilder", P.plan(palletInput({twoSources})).kind, "store") check("P14 evaluate palletCount", P.evaluate(twoSources, palletInput({})).palletCount, 4) +-- P15: kilder der deler lager (mælketanke = udvidelser inden for storageRadius) regnes som ét stop; +-- den første i forløbets rækkefølge beholdes, rækkefølgen bevares +local storA, storB, storC = {}, {}, {} +local pools = {[1] = {storA, storB}, [2] = {storB}, [3] = {storC}, [4] = {storA}, [5] = nil} +local srcs = {{wayPointId = 1}, {wayPointId = 2}, {wayPointId = 3}, {wayPointId = 4}, {wayPointId = 5}} +local kept, merged = P.dedupeSharedSources(srcs, function(source) return pools[source.wayPointId] end) +check("P15 behold 1, 3, 5", join((function() local ids = {} for _, s in ipairs(kept) do table.insert(ids, s.wayPointId) end return ids end)()), "1,3,5") +check("P15 slået sammen: 2 og 4 ind i 1", join(merged[1]), "2,4") +check("P15 kilde uden lagre beholdes", kept[3].wayPointId, 5) +local none = P.dedupeSharedSources({}, function() return nil end) +check("P15 tom liste", #none, 0) + print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)