feat(udkørsel): kilder hvis læssestationer deler lager (mælketanke som udvidelser) regnes som ét stop (v1.17.3.0)

This commit is contained in:
2026-09-24 03:53:07 +02:00
parent e7c482a3cb
commit edf6805cac
7 changed files with 105 additions and 3 deletions
+28
View File
@@ -62,6 +62,30 @@ local function fillTypeName(fillType)
return g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(fillType) or tostring(fillType) return g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(fillType) or tostring(fillType)
end 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) local function getStatus(run)
status[run.id] = status[run.id] or {} status[run.id] = status[run.id] or {}
return status[run.id] return status[run.id]
@@ -199,6 +223,10 @@ function ADOutbound.buildPlanInput(adEnv, run, vehicle)
sourcesById[wayPointId] = source sourcesById[wayPointId] = source
end end
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 byName, products = {}, {}
local time = now() local time = now()
for _, source in ipairs(sources) do for _, source in ipairs(sources) do
+33
View File
@@ -193,6 +193,39 @@ local function buildTrip(primary, candidates, input)
return trip return trip
end 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) function ADOutboundPlanner.plan(input)
local sell, store, reasons = collectCandidates(input) local sell, store, reasons = collectCandidates(input)
if sell[1] ~= nil then if sell[1] ~= nil then
+1 -1
View File
@@ -1,7 +1,7 @@
<?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.17.2.0</version> <version>1.17.3.0</version>
<title> <title>
<en>AD Smart Pickup</en> <en>AD Smart Pickup</en>
</title> </title>
+6 -1
View File
@@ -1,6 +1,6 @@
# AD Smart Pickup — brugermanual # 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)* *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 | | **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 | | **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 ### Politik pr. vare
Hver vare, kilderne har, står med sin mængde og nuværende pris. Vælg en politik: Hver vare, kilderne har, står med sin mængde og nuværende pris. Vælg en politik:
+7 -1
View File
@@ -1,6 +1,6 @@
# AD Smart Pickup — user manual # 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)* *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 | | **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 | | **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 ### Policy per good
Every good in the sources is listed with its amount and current price. Pick a policy: Every good in the sources is listed with its amount and current price. Pick a policy:
+18
View File
@@ -543,5 +543,23 @@ v24b.trailers[1].setAllTensionBeltsActive = function(self, active) self.beltsAct
adEnv.LoadAtDestinationTask.finished(newTask(v24b, 1)) adEnv.LoadAtDestinationTask.finished(newTask(v24b, 1))
check("O24 tipvogn uden tur: rører ikke stropper", v24b.trailers[1].beltsActive, nil) 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")) 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)
+12
View File
@@ -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 paller summeres over kilder", P.plan(palletInput({twoSources})).kind, "store")
check("P14 evaluate palletCount", P.evaluate(twoSources, palletInput({})).palletCount, 4) 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")) 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)