diff --git a/FS25_ADSmartPickup/adRuns.lua b/FS25_ADSmartPickup/adRuns.lua index d1a3a1e..1049b75 100644 --- a/FS25_ADSmartPickup/adRuns.lua +++ b/FS25_ADSmartPickup/adRuns.lua @@ -6,6 +6,9 @@ ADRuns = {} ADRuns.MAX_LOOPS = 99 +-- "Kør hjem fra Wait når målet er under X %": 10..90, standard 50 (= den gamle 50 %-regel). +ADRuns.DEFAULT_RETURN_BELOW_PERCENT = 50 +ADRuns.MIN_RETURN_BELOW_PERCENT, ADRuns.MAX_RETURN_BELOW_PERCENT = 10, 90 ADRuns.XML_VERSION = 1 ADRuns.INGREDIENT_MODES = {auto = true, manual = true} @@ -26,6 +29,7 @@ local function copyRun(run) ingredients = copyList(run.ingredients), waitPoolGroup = run.waitPoolGroup, loops = run.loops, + returnBelowPercent = run.returnBelowPercent, vehicleIds = copyList(run.vehicleIds), } end @@ -38,6 +42,11 @@ local function copyStore(store) return {nextId = store.nextId, runs = runs} end +local function clampPercent(percent) + local value = tonumber(percent) or ADRuns.DEFAULT_RETURN_BELOW_PERCENT + return math.max(ADRuns.MIN_RETURN_BELOW_PERCENT, math.min(ADRuns.MAX_RETURN_BELOW_PERCENT, math.floor(value))) +end + local function clampLoops(loops) local value = tonumber(loops) or 0 return math.max(0, math.min(ADRuns.MAX_LOOPS, math.floor(value))) @@ -57,6 +66,7 @@ function ADRuns.createRun(store, name) ingredients = {}, waitPoolGroup = "", loops = 0, + returnBelowPercent = ADRuns.DEFAULT_RETURN_BELOW_PERCENT, vehicleIds = {}, } new.nextId = new.nextId + 1 @@ -102,6 +112,7 @@ function ADRuns.updateRun(store, runId, changes) if changes.ingredients ~= nil then run.ingredients = copyList(changes.ingredients) end if changes.waitPoolGroup ~= nil then run.waitPoolGroup = changes.waitPoolGroup end if changes.loops ~= nil then run.loops = clampLoops(changes.loops) end + if changes.returnBelowPercent ~= nil then run.returnBelowPercent = clampPercent(changes.returnBelowPercent) end return new end @@ -170,6 +181,7 @@ function ADRuns.toRecords(store) ingredients = ADRuns.serializeIngredients(run.ingredients), waitPool = run.waitPoolGroup, loops = run.loops, + returnBelow = run.returnBelowPercent, vehicleIds = copyList(run.vehicleIds), } end @@ -192,6 +204,7 @@ function ADRuns.fromRecords(records) ingredients = ADRuns.parseIngredients(record.ingredients), waitPoolGroup = record.waitPool or "", loops = clampLoops(record.loops), + returnBelowPercent = clampPercent(record.returnBelow), vehicleIds = copyList(record.vehicleIds), }) store.nextId = math.max(store.nextId, id + 1) diff --git a/FS25_ADSmartPickup/adRunsStorage.lua b/FS25_ADSmartPickup/adRunsStorage.lua index 012a6a2..b5bb783 100644 --- a/FS25_ADSmartPickup/adRunsStorage.lua +++ b/FS25_ADSmartPickup/adRunsStorage.lua @@ -34,6 +34,7 @@ function ADRunsStorage.writeRecords(xmlFile, records) xmlFile:setString(key .. "#ingredients", record.ingredients) xmlFile:setString(key .. "#waitPool", record.waitPool) xmlFile:setInt(key .. "#loops", record.loops) + xmlFile:setInt(key .. "#returnBelow", record.returnBelow or ADRuns.DEFAULT_RETURN_BELOW_PERCENT) for vehicleIndex, vehicleId in ipairs(record.vehicleIds) do xmlFile:setString(string.format("%s.vehicle(%d)#id", key, vehicleIndex - 1), vehicleId) end @@ -51,6 +52,7 @@ function ADRunsStorage.readRecords(xmlFile) ingredients = xmlFile:getString(key .. "#ingredients"), waitPool = xmlFile:getString(key .. "#waitPool"), loops = xmlFile:getInt(key .. "#loops"), + returnBelow = xmlFile:getInt(key .. "#returnBelow"), vehicleIds = {}, } xmlFile:iterate(key .. ".vehicle", function(_, vehicleKey) diff --git a/FS25_ADSmartPickup/adSupplyPlanner.lua b/FS25_ADSmartPickup/adSupplyPlanner.lua index c8ce7ad..784e5b4 100644 --- a/FS25_ADSmartPickup/adSupplyPlanner.lua +++ b/FS25_ADSmartPickup/adSupplyPlanner.lua @@ -158,11 +158,12 @@ end ADSupplyPlanner.WAIT_RETURN_FREE_FRACTION = 0.5 -- Kan den ventende køre hjem? Ja når hele resten passer, eller når målet er mindst --- WAIT_RETURN_FREE_FRACTION tomt. capacity nil = kun hele resten tæller. -function ADSupplyPlanner.canUnloadRest(freeSpace, rest, capacity) +-- freeFraction tomt (standard WAIT_RETURN_FREE_FRACTION; run'ets "kør hjem under X %" = +-- freeFraction 1 - X/100). capacity nil = kun hele resten tæller. +function ADSupplyPlanner.canUnloadRest(freeSpace, rest, capacity, freeFraction) local needed = rest if capacity ~= nil and capacity > 0 then - needed = math.min(rest, capacity * ADSupplyPlanner.WAIT_RETURN_FREE_FRACTION) + needed = math.min(rest, capacity * (freeFraction or ADSupplyPlanner.WAIT_RETURN_FREE_FRACTION)) end return freeSpace >= needed end diff --git a/FS25_ADSmartPickup/adUnloadWait.lua b/FS25_ADSmartPickup/adUnloadWait.lua index 8331eb8..1cace98 100644 --- a/FS25_ADSmartPickup/adUnloadWait.lua +++ b/FS25_ADSmartPickup/adUnloadWait.lua @@ -169,7 +169,10 @@ local function updateWaiting(adEnv, task, wait, dt) -- Friske tal til evt. bytte og til tilbage-loggen — ikke dem fra da ventningen startede. wait.fillType, wait.rest = fillType, rest local free, capacity = ADSmartPickup.getTargetFreeCapacity(adEnv, vehicle, fillType) - if free == nil or not ADSupplyPlanner.canUnloadRest(free, rest, capacity) then + -- 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 hasRouteBothWays(adEnv, vehicle, markerId, wait.homeDestinationID) diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua index 81b6791..0d269bc 100644 --- a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua +++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua @@ -61,6 +61,23 @@ function SmartPickupFrame:initialize() table.insert(loopTexts, tostring(loops)) end self.loopsOption:setTexts(loopTexts) + self.returnBelowSteps = {} + local percentTexts = {} + for percent = ADRuns.MIN_RETURN_BELOW_PERCENT, ADRuns.MAX_RETURN_BELOW_PERCENT, 10 do + table.insert(self.returnBelowSteps, percent) + table.insert(percentTexts, string.format("%d %%", percent)) + end + self.returnBelowOption:setTexts(percentTexts) +end + +function SmartPickupFrame:getReturnBelowIndex(run) + local index = 1 + for position, percent in ipairs(self.returnBelowSteps or {}) do + if percent <= (run.returnBelowPercent or ADRuns.DEFAULT_RETURN_BELOW_PERCENT) then + index = position + end + end + return index end -- FocusManager har skiftet API mellem FS-versioner; her hentes det fokuserede element @@ -228,7 +245,7 @@ function SmartPickupFrame:rebuild() self:fillRunOption(adEnv) local run = self:getRun() local hasRun = run ~= nil and adEnv ~= nil - for _, element in ipairs({self.targetOption, self.ingredientModeOption, self.waitPoolOption, self.loopsOption}) do + for _, element in ipairs({self.targetOption, self.ingredientModeOption, self.waitPoolOption, self.loopsOption, self.returnBelowOption}) do element:setDisabled(not hasRun) end if hasRun then @@ -238,6 +255,7 @@ function SmartPickupFrame:rebuild() self.needsRow = self:addTextRow(self:describeNeeds(adEnv, run)) self:fillWaitPoolOption(adEnv, run) self.loopsOption:setState(run.loops + 1, false) + self.returnBelowOption:setState(self:getReturnBelowIndex(run), false) self:addTextRow(self.i18n:getText("spu_vehicles")) self:fillVehicleRows(adEnv, run) end @@ -398,6 +416,7 @@ function SmartPickupFrame:syncOptionsFromModel(adEnv, run) end sync(self.waitPoolOption, poolIndex) sync(self.loopsOption, run.loops + 1) + sync(self.returnBelowOption, self:getReturnBelowIndex(run)) end -- "Silage 0/33.750 l · Straw 21.375/21.375 l" — mest trængende først. @@ -631,6 +650,14 @@ function SmartPickupFrame:onClickLoops(state) end) end +function SmartPickupFrame:onClickReturnBelow(state) + self:safe(function(frame) + local run = frame:getRun() + if run == nil then return end + ADRunsController.updateRun(run.id, {returnBelowPercent = frame.returnBelowSteps[state] or ADRuns.DEFAULT_RETURN_BELOW_PERCENT}) + end) +end + function SmartPickupFrame:onClickVehicle(state, element) self:safe(function(frame) local run, data = frame:getRun(), frame.rowData[element] diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.xml b/FS25_ADSmartPickup/gui/SmartPickupFrame.xml index a6b5c31..2e714b9 100644 --- a/FS25_ADSmartPickup/gui/SmartPickupFrame.xml +++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.xml @@ -41,6 +41,12 @@ + + + + + + diff --git a/FS25_ADSmartPickup/l10n/l10n_da.xml b/FS25_ADSmartPickup/l10n/l10n_da.xml index 9680790..18239bd 100644 --- a/FS25_ADSmartPickup/l10n/l10n_da.xml +++ b/FS25_ADSmartPickup/l10n/l10n_da.xml @@ -42,6 +42,8 @@ + + diff --git a/FS25_ADSmartPickup/l10n/l10n_en.xml b/FS25_ADSmartPickup/l10n/l10n_en.xml index 3e25725..8314503 100644 --- a/FS25_ADSmartPickup/l10n/l10n_en.xml +++ b/FS25_ADSmartPickup/l10n/l10n_en.xml @@ -42,6 +42,8 @@ + + diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index a689e0d..6bec9fb 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.10.1.0 + 1.10.2.0 <en>AD Smart Pickup</en> diff --git a/tests/test_adRuns.lua b/tests/test_adRuns.lua index cf57836..dd7e175 100644 --- a/tests/test_adRuns.lua +++ b/tests/test_adRuns.lua @@ -96,6 +96,17 @@ check("R6 nextId", dirty.nextId, 8) check("R6 tom liste", #ADRuns.fromRecords(nil).runs, 0) check("R6 parseIngredients trimmer", #ADRuns.parseIngredients(" STRAW FORAGE "), 2) +-- R7: "kør hjem under X %" — standard 50, klippes til 10..90, overlever records +local r7, r7run = ADRuns.createRun(ADRuns.newStore(), "Tærskel") +check("R7 standard 50 %", r7run.returnBelowPercent, 50) +check("R7 sættes", ADRuns.getRun(ADRuns.updateRun(r7, r7run.id, {returnBelowPercent = 30}), r7run.id).returnBelowPercent, 30) +check("R7 klippes til 90", ADRuns.getRun(ADRuns.updateRun(r7, r7run.id, {returnBelowPercent = 150}), r7run.id).returnBelowPercent, 90) +check("R7 klippes til 10", ADRuns.getRun(ADRuns.updateRun(r7, r7run.id, {returnBelowPercent = 0}), r7run.id).returnBelowPercent, 10) +local r7rec = ADRuns.toRecords(ADRuns.updateRun(r7, r7run.id, {returnBelowPercent = 25})) +check("R7 i records", r7rec[1].returnBelow, 25) +check("R7 tilbage fra records", ADRuns.fromRecords(r7rec).runs[1].returnBelowPercent, 25) +check("R7 manglende i gammel fil -> 50", ADRuns.fromRecords({{id = 1, name = "Gammel"}}).runs[1].returnBelowPercent, 50) + -- ==== XML-laget mod en flad XMLFile-mock (nøgle -> værdi) ==== dofile("FS25_ADSmartPickup/adRunsStorage.lua") diff --git a/tests/test_adSmartPickup.lua b/tests/test_adSmartPickup.lua index ec28d88..0473423 100644 --- a/tests/test_adSmartPickup.lua +++ b/tests/test_adSmartPickup.lua @@ -842,6 +842,25 @@ UnloadTask.update(st6, 16) check("S6 bliver ved siloen efter bytte", st6.destinationID, 21) check("S6 ingen ny Wait-omdirigering", countLogsSince(mark6, "venter ved"), 0) +-- I5: run'ets "kør hjem under X %" styrer hvornår den ventende kører hjem +buildWaitWorld() +local vt, tt = makeUnloadingVehicle("Fastrac Tærskel", 551, 5000, 60000) -- rest > stald: kun tærsklen kan sende hjem +vt.uniqueId = "vehicleThreshold" +testVehicles = {vt} +ADRunsController.store = ADRuns.newStore() +local thrRun = ADRunsController.createRun("Tærskel") +ADRunsController.updateRun(thrRun.id, {targetWayPointId = 30, returnBelowPercent = 20}) +ADRunsController.assignVehicle(thrRun.id, vt) +UnloadTask.update(tt, 16) +check("I5 venter først", tt.destinationID, 40) +waitBarnLevels[STRAW] = 15000 -- 70 % fri: 50 %-reglen ville sende hjem, men tærsklen er "under 20 %" (= 80 % fri) +UnloadTask.update(tt, 16) +check("I5 70 % fri er ikke nok ved 'under 20 %'", tt.destinationID, 40) +waitBarnLevels[STRAW] = 9000 -- 82 % fri >= 80 % +UnloadTask.update(tt, 16) +check("I5 hjem når målet er under 20 %", tt.destinationID, 30) +ADRunsController.store = ADRuns.newStore() + -- ==== E: eksponerede opslag (bruges af runs-controller og læs-bytte) ==== local eDriver = buildCowWorld() check("E1 aflæsningsstation ved waypoint", ADSmartPickup.getUnloadStationAtWayPointId(FS25_AutoDrive, 30, 1), unloadStations[1]) diff --git a/tests/test_adSupplyPlanner.lua b/tests/test_adSupplyPlanner.lua index 037be1b..13d55e6 100644 --- a/tests/test_adSupplyPlanner.lua +++ b/tests/test_adSupplyPlanner.lua @@ -172,5 +172,11 @@ check("kæmpe rest: halv stald fri -> hjem", ADSupplyPlanner.canUnloadRest(15000 check("kæmpe rest: under halv stald fri -> vent", ADSupplyPlanner.canUnloadRest(100000, 700000, 300000), false) check("ukendt kapacitet -> kun hele resten tæller", ADSupplyPlanner.canUnloadRest(100000, 700000, nil), false) +-- canUnloadRest med run-tærskel: "under 30 %" = fri >= 70 % af kapaciteten +check("tærskel 30 %: 60 % fri er ikke nok", ADSupplyPlanner.canUnloadRest(60000, 90000, 100000, 0.7), false) +check("tærskel 30 %: 70 % fri er nok", ADSupplyPlanner.canUnloadRest(70000, 90000, 100000, 0.7), true) +check("hele resten passer vinder altid", ADSupplyPlanner.canUnloadRest(5000, 5000, 100000, 0.7), true) +check("uden tærskel = 50 %-reglen", ADSupplyPlanner.canUnloadRest(50000, 90000, 100000), true) + print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)