From 53346cc916bc1739efb0ae423b60cd91fef95494 Mon Sep 17 00:00:00 2001 From: masterdraco Date: Tue, 29 Sep 2026 21:32:19 +0200 Subject: [PATCH] chore: tests ud af det offentlige repo (kun lokalt); feedback uden krav om konto --- .gitignore | 1 + README.md | 12 +- docs/BRUGERMANUAL.md | 2 +- docs/USER_MANUAL.md | 2 +- tests/test_adBuildings.lua | 254 ------ tests/test_adCache.lua | 40 - tests/test_adCourseCache.lua | 195 ----- tests/test_adCpBridge.lua | 178 ---- tests/test_adCpResume.lua | 33 - tests/test_adFieldFleet.lua | 145 ---- tests/test_adFieldGuide.lua | 255 ------ tests/test_adFieldJobs.lua | 222 ----- tests/test_adFieldModel.lua | 324 -------- tests/test_adFieldSiloConfig.lua | 46 - tests/test_adFieldSiloPlanner.lua | 159 ---- tests/test_adFieldSilos.lua | 159 ---- tests/test_adFieldStorage.lua | 110 --- tests/test_adFieldView.lua | 66 -- tests/test_adFieldWorkBuying.lua | 22 - tests/test_adFields.lua | 98 --- tests/test_adFieldsPolygon.lua | 99 --- tests/test_adFieldsScan.lua | 94 --- tests/test_adFieldsShape.lua | 44 - tests/test_adFleetNames.lua | 33 - tests/test_adFlowGuide.lua | 314 ------- tests/test_adFlowView.lua | 105 --- tests/test_adFlowWagons.lua | 140 ---- tests/test_adGuide.lua | 113 --- tests/test_adHitch.lua | 129 --- tests/test_adHitchCreep.lua | 26 - tests/test_adLoadSwap.lua | 79 -- tests/test_adManualTransport.lua | 40 - tests/test_adMonthView.lua | 86 -- tests/test_adNames.lua | 20 - tests/test_adNaming.lua | 49 -- tests/test_adOutbound.lua | 646 -------------- tests/test_adOutboundPlanner.lua | 188 ----- tests/test_adPool.lua | 183 ---- tests/test_adPrices.lua | 106 --- tests/test_adProfiler.lua | 42 - tests/test_adRuns.lua | 385 --------- tests/test_adRunsController.lua | 907 -------------------- tests/test_adSmartPickup.lua | 1295 ----------------------------- tests/test_adSources.lua | 596 ------------- tests/test_adStorageStats.lua | 206 ----- tests/test_adSupplyPlanner.lua | 266 ------ tests/test_adWaitPool.lua | 166 ---- tests/test_fieldStatus.lua | 33 - tests/test_flowStatus.lua | 21 - tests/test_noStrayGlobals.lua | 18 - tests/test_storageView.lua | 186 ----- 51 files changed, 6 insertions(+), 8932 deletions(-) delete mode 100644 tests/test_adBuildings.lua delete mode 100644 tests/test_adCache.lua delete mode 100644 tests/test_adCourseCache.lua delete mode 100644 tests/test_adCpBridge.lua delete mode 100644 tests/test_adCpResume.lua delete mode 100644 tests/test_adFieldFleet.lua delete mode 100644 tests/test_adFieldGuide.lua delete mode 100644 tests/test_adFieldJobs.lua delete mode 100644 tests/test_adFieldModel.lua delete mode 100644 tests/test_adFieldSiloConfig.lua delete mode 100644 tests/test_adFieldSiloPlanner.lua delete mode 100644 tests/test_adFieldSilos.lua delete mode 100644 tests/test_adFieldStorage.lua delete mode 100644 tests/test_adFieldView.lua delete mode 100644 tests/test_adFieldWorkBuying.lua delete mode 100644 tests/test_adFields.lua delete mode 100644 tests/test_adFieldsPolygon.lua delete mode 100644 tests/test_adFieldsScan.lua delete mode 100644 tests/test_adFieldsShape.lua delete mode 100644 tests/test_adFleetNames.lua delete mode 100644 tests/test_adFlowGuide.lua delete mode 100644 tests/test_adFlowView.lua delete mode 100644 tests/test_adFlowWagons.lua delete mode 100644 tests/test_adGuide.lua delete mode 100644 tests/test_adHitch.lua delete mode 100644 tests/test_adHitchCreep.lua delete mode 100644 tests/test_adLoadSwap.lua delete mode 100644 tests/test_adManualTransport.lua delete mode 100644 tests/test_adMonthView.lua delete mode 100644 tests/test_adNames.lua delete mode 100644 tests/test_adNaming.lua delete mode 100644 tests/test_adOutbound.lua delete mode 100644 tests/test_adOutboundPlanner.lua delete mode 100644 tests/test_adPool.lua delete mode 100644 tests/test_adPrices.lua delete mode 100644 tests/test_adProfiler.lua delete mode 100644 tests/test_adRuns.lua delete mode 100644 tests/test_adRunsController.lua delete mode 100644 tests/test_adSmartPickup.lua delete mode 100644 tests/test_adSources.lua delete mode 100644 tests/test_adStorageStats.lua delete mode 100644 tests/test_adSupplyPlanner.lua delete mode 100644 tests/test_adWaitPool.lua delete mode 100644 tests/test_fieldStatus.lua delete mode 100644 tests/test_flowStatus.lua delete mode 100644 tests/test_noStrayGlobals.lua delete mode 100644 tests/test_storageView.lua diff --git a/.gitignore b/.gitignore index 5515b55..5fa4575 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ docs/superpowers/ bench/ .superpowers/ +tests/ diff --git a/README.md b/README.md index ba0e390..5a0996e 100644 --- a/README.md +++ b/README.md @@ -44,18 +44,12 @@ Alt sættes op i menuen **Smart Pickup** i ESC-menuen. ## Feedback -Fejl og idéer: opret et issue her i repoet. Vedlæg gerne `log.txt` fra +Fejl og idéer: giv udvikleren besked (issues her kræver en konto på git.powerdata.dk). Vedlæg gerne `log.txt` fra `Documents/My Games/FarmingSimulator2025/`, eller i det mindste linjerne der starter med `ADSmartPickup`. ## Udvikling -- Mod-koden ligger i `FS25_ADSmartPickup/`, og testene ligger i `tests/` (ren Lua, kører uden spillet). -- Kør testene: - - ```sh - for t in tests/test_*.lua; do luajit $t >/dev/null 2>&1 || echo FAIL $t; done - ``` - +- Mod-koden ligger i `FS25_ADSmartPickup/`. - Byg zip: `cd FS25_ADSmartPickup && zip -qr ../FS25_ADSmartPickup.zip .` --- @@ -94,5 +88,5 @@ needed for pallets. **Docs:** [User manual](docs/USER_MANUAL.md) · [Danish manual](docs/BRUGERMANUAL.md) · [Changelog](CHANGELOG.md) -**Feedback:** open an issue in this repository. Attach `log.txt`, or at least the lines starting with +**Feedback:** tell the developer (issues here need an account on git.powerdata.dk). Attach `log.txt`, or at least the lines starting with `ADSmartPickup`. diff --git a/docs/BRUGERMANUAL.md b/docs/BRUGERMANUAL.md index ac61ce1..ca86b3b 100644 --- a/docs/BRUGERMANUAL.md +++ b/docs/BRUGERMANUAL.md @@ -1116,7 +1116,7 @@ ADSmartPickup: F109 … Moden testes løbende, og fejlrapporter er meget velkomne. -1. Opret et issue i repoet: +1. Giv udvikleren besked — eller opret et issue i repoet, hvis du har en konto: 2. Beskriv, hvad du gjorde, hvad du forventede, og hvad der skete. Nævn versionen (står i `log.txt` og i mod-listen). 3. Vedlæg `log.txt` fra `Documents/My Games/FarmingSimulator2025/log.txt`. Er filen for stor, så vedlæg diff --git a/docs/USER_MANUAL.md b/docs/USER_MANUAL.md index 94dee02..614b156 100644 --- a/docs/USER_MANUAL.md +++ b/docs/USER_MANUAL.md @@ -1146,7 +1146,7 @@ ADSmartPickup: F109 … The mod is being tested continuously, and bug reports are very welcome. -1. Open an issue in the repository: +1. Tell the developer — or open an issue in the repository if you have an account: 2. Describe what you did, what you expected, and what happened. Mention the version (shown in `log.txt` and in the mod list). 3. Attach `log.txt` from `Documents/My Games/FarmingSimulator2025/log.txt`. If the file is too big, diff --git a/tests/test_adBuildings.lua b/tests/test_adBuildings.lua deleted file mode 100644 index 3212ffe..0000000 --- a/tests/test_adBuildings.lua +++ /dev/null @@ -1,254 +0,0 @@ --- Kør: luajit tests/test_adBuildings.lua (fra repo-roden) --- Bygninger gården kan bruge + automatisk markør ved læsse-/aflæsningstrigger (≤ 40 m, Wait udelukket, --- siloer strengt), omvendt opslag markør -> bygning, id-fallback, cache. - -local SUGAR, FORAGE, MILK, BASKET = 5, 6, 12, 7 -local DESC = {[SUGAR] = {name = "SUGAR", title = "Sukker", isPalletType = true}, [FORAGE] = {name = "FORAGE", title = "TMR"}, [MILK] = {name = "MILK", title = "Mælk"}, [BASKET] = {name = "STRAW_BASKET", title = "Kurve", palletFilename = "x"}} -g_fillTypeManager = {getFillTypeByIndex = function(_, i) return DESC[i] end, getFillTypeNameByIndex = function(_, i) return DESC[i] and DESC[i].name end, getFillTypeTitleByIndex = function(_, i) return DESC[i] and DESC[i].title end} -Logging = {info = function() end, warning = function() end} -getWorldTranslation = function(node) return node.x, 0, node.z end -SellingStation = {} -dofile("FS25_ADSmartPickup/adSupplyPlanner.lua") -dofile("FS25_ADSmartPickup/adPrices.lua") -dofile("FS25_ADSmartPickup/adBuildings.lua") - -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else failures = failures + 1; print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) end -end - -local markers, wayPoints = {}, {} -local function addMarker(id, name, group, x, z) - wayPoints[id] = {id = id, x = x, z = z} - table.insert(markers, {id = id, markerIndex = #markers + 1, name = name, group = group}) -end -local loadStations, unloadStations = {}, {} -local adEnv = {ADGraphManager = { - getWayPointById = function(_, id) return wayPoints[id] end, - getMapMarkers = function() return markers end, - getMapMarkerByWayPointId = function(_, id) for _, m in ipairs(markers) do if m.id == id then return m end end end, -}} -local strictLoadAt = {} -ADSmartPickup = {getLoadStationAtWayPointId = function(_, id) return strictLoadAt[id] end} -g_currentMission = { - placeableSystem = {placeables = {}}, - storageSystem = {getLoadingStations = function() return loadStations end, getUnloadingStations = function() return unloadStations end}, - accessHandler = {canFarmAccess = function() return true end}, - getFarmId = function() return 1 end, -} -local function placeable(uid, name, x, z, extra) - local p = {uniqueId = uid, rootNode = {x = x, z = z}, ownerFarmId = 1, getOwnerFarmId = function() return 1 end, getName = function() return name end} - for k, v in pairs(extra or {}) do p[k] = v end - table.insert(g_currentMission.placeableSystem.placeables, p) - return p -end - --- verden: kostald med mælketank (load) og foder (unload), sukkerfabrik (unload + pallespawner), to siloer i række, objektlager, salgssted -local barn = placeable("barn1", "Kostald 1", 0, 0, {getHusbandryFillLevel = function() return 0 end}) --- som FS25_LargerAnimalBarns' cowBarnBig: ÉN læssestation med TO læssepunkter (gylle + mælk) -local milkStation = {owningPlaceable = barn, loadTriggers = {{triggerNode = {x = 20, z = 0}}, {triggerNode = {x = 0, z = 30}}}} -local barnUnload = {owningPlaceable = barn, unloadTriggers = {{exactFillRootNode = {x = -20, z = 0}}}} -table.insert(loadStations, milkStation); table.insert(unloadStations, barnUnload) -local mill = placeable("mill", "Sukkerfabrik", 500, 0, {spec_productionPoint = {productionPoint = {productions = {}, storage = {}, palletSpawner = {spawnPlaces = {{startNode = {x = 520, z = 0}}}}, unloadingStation = nil}}}) -local millUnload = {owningPlaceable = mill, unloadTriggers = {{exactFillRootNode = {x = 480, z = 0}}}, isa = function() return true end, isSellingPoint = true} -table.insert(unloadStations, millUnload) -local silo1 = placeable("silo1", "Silo 1", 1000, 0, {spec_silo = {}}) -local silo2 = placeable("silo2", "Silo 2", 1015, 0, {spec_silo = {}}) -local silo1Station = {owningPlaceable = silo1, loadTriggers = {{triggerNode = {x = 1000, z = 5}}}} -local silo2Station = {owningPlaceable = silo2, loadTriggers = {{triggerNode = {x = 1015, z = 5}}}} -table.insert(loadStations, silo1Station); table.insert(loadStations, silo2Station) -table.insert(unloadStations, {owningPlaceable = silo1, unloadTriggers = {{exactFillRootNode = {x = 1000, z = 5}}}}) -table.insert(unloadStations, {owningPlaceable = silo2, unloadTriggers = {{exactFillRootNode = {x = 1015, z = 5}}}}) -local shed = placeable("shed", "Palleskur", 2000, 0, {spec_objectStorage = {objectInfos = {}, capacity = 100}}) -local sellPoint = placeable("sell", "Mejeri", 3000, 0, {}) -local sellStation = {owningPlaceable = sellPoint, unloadTriggers = {{exactFillRootNode = {x = 3000, z = 10}}}, isa = function() return true end, isSellingPoint = true} -table.insert(unloadStations, sellStation) -local other = placeable("otherfarm", "Naboens", 4000, 0, {spec_silo = {}, ownerFarmId = 2, getOwnerFarmId = function() return 2 end}) - -addMarker(1, "cow 1 milk", "Milk", 25, 0) -- 5 m fra mælketriggeren -addMarker(2, "Cow 1 food", "Cow food", -30, 0) -- 10 m fra fodertriggeren -addMarker(3, "cow wait 1", "cow wait", 22, 0) -- Wait: må ALDRIG vælges (tættere end mælkemarkøren) -addMarker(11, "Cow 1 Slurry", "Slurry", 0, 33) -- 3 m fra gylletriggeren -addMarker(4, "Suger Load", "Pickup", 525, 0) -- 5 m fra pallespawneren -addMarker(5, "sugar offload", "production", 470, 0) -addMarker(6, "silo 1", "silo", 1000, 12) -- strengt: silo1 -addMarker(7, "silo 2", "silo", 1015, 12) -- strengt: silo2 (7 m fra silo1's trigger-node på x-aksen? nej: 15 m) -strictLoadAt[6] = silo1Station; strictLoadAt[7] = silo2Station -addMarker(8, "Storage pickup", "Pickup", 2010, 0) -addMarker(9, "Mejeri", "selling", 3000, 20) -addMarker(10, "Langt væk", "All", 9000, 9000) - --- B1: liste, kind og markører -local list = ADBuildings.list(adEnv, 1) -local byId = {} -for _, b in ipairs(list) do byId[b.id] = b end -check("B1 antal (naboens silo udeladt)", #list, 6) -check("B1 stald", byId.barn1.kind, "husbandry") -check("B1 stald læssemarkør = mælk (ikke Wait, som er tættere)", byId.barn1.loadMarker.id, 1) --- én markør pr. læssestation (mælketank OG gyllebeholder), sorteret på markørnavn -check("B1 stald har to læssemarkører", #byId.barn1.loadMarkers, 2) -check("B1 første = cow 1 milk (navnesorteret, uanset store/små)", byId.barn1.loadMarkers[1].id, 1) -check("B1 anden = Cow 1 Slurry", byId.barn1.loadMarkers[2].name, "Cow 1 Slurry") -check("B1 gyllemarkør -> stald via byMarker", ADBuildings.byMarker(adEnv, 1, 11, "load").id, "barn1") -check("B1 stald aflæsningsmarkør = foder", byId.barn1.unloadMarker.id, 2) -check("B1 stald afstand", byId.barn1.loadMarker.distance, 5) -check("B1 fabrik læssemarkør via pallespawner", byId.mill.loadMarker.id, 4) -check("B1 fabrik aflæsningsmarkør", byId.mill.unloadMarker.id, 5) -check("B1 fabrik er ikke salgssted", byId.mill.canSell, false) -check("B1 objektlager markør via rootNode", byId.shed.loadMarker.id, 8) -check("B1 salgssted", byId.sell.kind, "sell") -check("B1 salgssted markør", byId.sell.unloadMarker.id, 9) -check("B1 salgssted canSell", byId.sell.canSell, true) -check("B1 stald kan være kilde og mål", byId.barn1.canBeSource and byId.barn1.canBeTarget, true) -check("B1 silo kilde, ikke mål", byId.silo1.canBeSource and not byId.silo1.canBeTarget, true) - --- B2: siloer i række: strengt opslag giver hver sin markør -check("B2 silo 1 markør", byId.silo1.loadMarker.id, 6) -check("B2 silo 2 markør", byId.silo2.loadMarker.id, 7) -strictLoadAt[7] = nil -ADBuildings.invalidate() -check("B2 silo uden strengt match: ingen læssemarkør", ADBuildings.find(adEnv, 1, "silo2").loadMarker, nil) -strictLoadAt[7] = silo2Station -ADBuildings.invalidate() - --- B3: byMarker (omvendt opslag) -check("B3 mælkemarkør -> stald (load)", ADBuildings.byMarker(adEnv, 1, 1, "load").id, "barn1") -check("B3 fodermarkør -> stald (unload)", ADBuildings.byMarker(adEnv, 1, 2, "unload").id, "barn1") -check("B3 fodermarkør er ikke load", ADBuildings.byMarker(adEnv, 1, 2, "load"), nil) -check("B3 fjern markør -> nil", ADBuildings.byMarker(adEnv, 1, 10, "load"), nil) - --- B4: id-fallback uden uniqueId -check("B4 id fra position", ADBuildings.getId({rootNode = {x = 12.6, z = -3.2}}), "pos:13:-3") -check("B4 id fra uniqueId", ADBuildings.getId(barn), "barn1") - --- B5: cache: samme tabel inden for CACHE_MS, ny efter invalidate -local first = ADBuildings.list(adEnv, 1) -check("B5 cachet", ADBuildings.list(adEnv, 1) == first, true) -ADBuildings.invalidate() -check("B5 ny efter invalidate", ADBuildings.list(adEnv, 1) ~= first, true) - --- B6: sortering efter NAVN (naturlig talrækkefølge), dernæst første markørnavn — ikke efter slags -local order = {} -for _, b in ipairs(ADBuildings.list(adEnv, 1)) do table.insert(order, b.name) end -check("B6 rækkefølge", table.concat(order, ","), "Kostald 1,Mejeri,Palleskur,Silo 1,Silo 2,Sukkerfabrik") -check("B6 naturlig sortering", ADBuildings.naturalLess("Silo 9", "Silo 10"), true) -check("B6 naturlig sortering, tal før tekst-lighed", ADBuildings.naturalLess("25 Silo Storage", "26 Silo Storage"), true) -check("B6 naturlig sortering ens navn -> markørnavn", ADBuildings.compare({name = "Farma", loadMarkers = {{name = "30 Silo"}}}, {name = "Farma", loadMarkers = {{name = "4 Silo"}}}), false) - --- B-DOB: silo der kun kan FYLDES (drive-on-bunker ved biogas: aflæsning, ingen læssestation) = mål, ikke kilde -do - local dob = placeable("dob1", "DOB 115", 5000, 0, {spec_silo = {}}) - table.insert(unloadStations, {owningPlaceable = dob, unloadTriggers = {{exactFillRootNode = {x = 5000, z = 5}}}}) - addMarker(21, "Biogas Drop 1", "Biogas", 5000, 12) - ADBuildings.invalidate() - local byIdDob = {} - for _, b in ipairs(ADBuildings.list(adEnv, 1)) do byIdDob[b.id] = b end - check("B-DOB kan være mål", byIdDob.dob1.canBeTarget, true) - check("B-DOB ikke kilde (intet at hente)", byIdDob.dob1.canBeSource, false) - check("B-DOB aflæsningsmarkør", byIdDob.dob1.unloadMarker.id, 21) - check("B-DOB almindelig silo stadig ikke mål", byIdDob.silo1.canBeTarget, false) -end - --- B-SEP: bygning med læssestation men uden silo/fabrik/stald-spec (Pumps&Hoses separatorhal: digestat-udlevering) -do - local sep = placeable("sep1", "Separator Hall", 7000, 0, {spec_manureSeparator = {}}) - table.insert(loadStations, {owningPlaceable = sep, loadTriggers = {{triggerNode = {x = 7000, z = 5}}}}) - addMarker(41, "Digestate pickup", "Biogas", 7000, 10) - ADBuildings.invalidate() - local bySep = {} - for _, b in ipairs(ADBuildings.list(adEnv, 1)) do bySep[b.id] = b end - check("B-SEP findes som lager", bySep.sep1 and bySep.sep1.kind, "storage") - check("B-SEP kan være kilde", bySep.sep1 and bySep.sep1.canBeSource, true) - check("B-SEP læssemarkør", bySep.sep1 and bySep.sep1.loadMarker and bySep.sep1.loadMarker.id, 41) -end - --- B-DROP: fabrik med pickup-markør TÆT på aflæsningstriggeren og en separat drop-markør længere væk --- (Sugarbeet Crusher): aflæsningsmarkøren skal være drop, ikke bygningens egen læssemarkør -do - local crusher = placeable("crusher1", "Sugarbeet Crusher", 8000, 0, {spec_productionPoint = {productionPoint = {productions = {}, storage = {}}}}) - table.insert(loadStations, {owningPlaceable = crusher, loadTriggers = {{triggerNode = {x = 8000, z = 5}}}}) - table.insert(unloadStations, {owningPlaceable = crusher, unloadTriggers = {{exactFillRootNode = {x = 8003, z = 2}}}}) - addMarker(51, "Sugar Beet Cut Pickup", "Fabrication", 8000, 8) -- 3 m fra læs, ~6 m fra aflæsning - addMarker(52, "Sugar Beet Cut Beet Drop", "Fabrication", 8012, 2) -- 9 m fra aflæsning - ADBuildings.invalidate() - local byC = {} - for _, b in ipairs(ADBuildings.list(adEnv, 1)) do byC[b.id] = b end - check("B-DROP læssemarkør = pickup", byC.crusher1.loadMarker.id, 51) - check("B-DROP aflæsningsmarkør = drop (ikke pickup)", byC.crusher1.unloadMarker.id, 52) - -- kun én fælles markør (AIO Load / Unload): bruges til begge - local aio = placeable("aio1", "AIO", 9000, 0, {spec_productionPoint = {productionPoint = {productions = {}, storage = {}}}}) - table.insert(loadStations, {owningPlaceable = aio, loadTriggers = {{triggerNode = {x = 9000, z = 5}}}}) - table.insert(unloadStations, {owningPlaceable = aio, unloadTriggers = {{exactFillRootNode = {x = 9002, z = 5}}}}) - addMarker(53, "AIO Load / Unload", "Fabrication", 9000, 9) - ADBuildings.invalidate() - for _, b in ipairs(ADBuildings.list(adEnv, 1)) do byC[b.id] = b end - check("B-DROP fælles markør bruges til aflæsning", byC.aio1.unloadMarker.id, 53) - check("B-DROP silo beholder sin egen markør (ikke nabosiloens)", byC.silo1.unloadMarker.id, 6) -end - --- B-COOP: hønsehus — foder aflæsses i den ene ende, æg-paller spawnes i den anden (ingen læssestation) -do - local coop = placeable("coop1", "Chicken Coop", 10000, 0, {getHusbandryFillLevel = function() return 0 end, - spec_husbandryFood = {triggers = {{someTrigger = {exactFillRootNode = {x = 9970, z = 0}}}}}, -- ukendt feltsti: søges - spec_husbandryPallets = {fillTypeIndexToPalletSpawner = {[99] = {spawnPlaces = {{startNode = {x = 10030, z = 0}}}}}}}) - table.insert(unloadStations, {owningPlaceable = coop, unloadTriggers = {}}) -- som chickenBarnBig: tom station - addMarker(61, "Chicken Food", "Chicken", 9965, 0) - addMarker(62, "Egg Pickup", "Chicken", 10035, 0) - ADBuildings.invalidate() - local byCoop = {} - for _, b in ipairs(ADBuildings.list(adEnv, 1)) do byCoop[b.id] = b end - check("B-COOP læssemarkør ved æggene", byCoop.coop1.loadMarker and byCoop.coop1.loadMarker.id, 62) - check("B-COOP aflæsningsmarkør ved foderet", byCoop.coop1.unloadMarker and byCoop.coop1.unloadMarker.id, 61) -end - --- B-FUEL: brændstoftank (læssestation kun med DIESEL) er ikke et lager/en kilde -do - local fmBefore = g_fillTypeManager - g_fillTypeManager = {getFillTypeNameByIndex = function(_, index) return index == 900 and "DIESEL" or "X" end} - local tank = placeable("fuel1", "Small Petrol Tank", 11000, 0, {}) - table.insert(loadStations, {owningPlaceable = tank, supportedFillTypes = {[900] = true}, loadTriggers = {{triggerNode = {x = 11000, z = 3}}}}) - ADBuildings.invalidate() - local byFuel = {} - for _, b in ipairs(ADBuildings.list(adEnv, 1)) do byFuel[b.id] = b end - check("B-FUEL brændstoftank udeladt", byFuel.fuel1, nil) - g_fillTypeManager = fmBefore -end - --- B-SCAN: søgning efter trigger-noder i indlejrede tabeller (dybde ≤ 3, ingen løkker) -do - local loop = {} - loop.self = loop - local nodes = ADBuildings.findTriggerNodes({a = {b = {exactFillRootNode = "n1"}}, c = {triggerNode = "n2"}, d = loop, e = {f = {g = {h = {exactFillRootNode = "for-dybt"}}}}}) - table.sort(nodes) - check("B-SCAN finder n1 og n2, ikke for dybt", table.concat(nodes, ","), "n1,n2") -end - --- B-SHED: palleskur læsser og aflæsser samme sted — aflæsningsmarkøren er den nærmeste (ikke "den anden") -do - local shedX = placeable("shedX", "Bale & Pallet Storage", 12000, 0, {spec_objectStorage = {objectInfos = {}, capacity = 4000}}) - addMarker(71, "Chicken Storage Unload", "Chicken", 12010, 0) -- 10 m - addMarker(72, "Chicken Storage Pickup", "Chicken", 12018, 0) -- 18 m - ADBuildings.invalidate() - local byShed = {} - for _, b in ipairs(ADBuildings.list(adEnv, 1)) do byShed[b.id] = b end - check("B-SHED aflæsning = nærmeste (Unload)", byShed.shedX.unloadMarker and byShed.shedX.unloadMarker.id, 71) -end - - --- pickLoadMarker: en pallespawner viger for aflæsningen, når begge vil have samme markør og aflæsningen står --- tættere (in-game 27/9 fårestald: spawner 9 m fra "Sheep Food", foderindkast 5 m -> Goat Food fik "Sheep Milk") -do - local food, milk = {id = 1, name = "Sheep Food 1", distance = 9}, {id = 2, name = "Sheep Milk 1", distance = 13} - local unloadBest = {id = 1, name = "Sheep Food 1", distance = 5} - check("PL spawner viger for tættere aflæsning", ADBuildings.pickLoadMarker({food, milk}, unloadBest, true).name, "Sheep Milk 1") - check("PL rigtig læssetrigger beholder markøren (Sugarbeet Crusher)", ADBuildings.pickLoadMarker({food, milk}, unloadBest, false).name, "Sheep Food 1") - -- in-game 27/9 fårestald 3 og 5: spawner 5 m fra "Sheep Food", foderindkast 9 m -> spawneren vandt stadig - check("PL spawner viger også når den selv står tættest", ADBuildings.pickLoadMarker({food, milk}, {id = 1, name = "Sheep Food 1", distance = 12}, true).name, "Sheep Milk 1") - check("PL rigtig læssetrigger tættest beholder", ADBuildings.pickLoadMarker({food, milk}, {id = 1, name = "Sheep Food 1", distance = 12}, false).name, "Sheep Food 1") - check("PL ingen konflikt", ADBuildings.pickLoadMarker({milk}, unloadBest, true).name, "Sheep Milk 1") - check("PL eneste kandidat er aflæsningens -> ingen læssemarkør", ADBuildings.pickLoadMarker({food}, unloadBest, true), nil) - check("PL ingen kandidater", ADBuildings.pickLoadMarker({}, unloadBest, true), nil) -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_adCache.lua b/tests/test_adCache.lua deleted file mode 100644 index 5e425ec..0000000 --- a/tests/test_adCache.lua +++ /dev/null @@ -1,40 +0,0 @@ --- Kør: luajit tests/test_adCache.lua (fra repo-roden) -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end -g_currentMission = {time = 0} -dofile("FS25_ADSmartPickup/adCache.lua") -local calls = 0 -local function lookup() calls = calls + 1; return "station" end -check("K1 slået fra: altid opslag", ADCacheGet("b", "k", 1000, lookup) .. ADCacheGet("b", "k", 1000, lookup), "stationstation") -check("K1 to opslag", calls, 2) -ADCache.enabled = true -calls = 0 -ADCacheGet("b", "k", 1000, lookup); ADCacheGet("b", "k", 1000, lookup) -check("K2 genbrugt", calls, 1) -g_currentMission.time = 900 -ADCacheGet("b", "k", 1000, lookup) -check("K2 inden for levetid", calls, 1) -g_currentMission.time = 1600 -ADCacheGet("b", "k", 1000, lookup) -check("K3 efter levetid + spredning (maks 1500) -> nyt opslag", calls, 2) -local nothing = 0 -ADCacheGet("n", "x", 1000, function() nothing = nothing + 1; return nil end) -check("K4 intet fundet huskes", ADCacheGet("n", "x", 1000, function() nothing = nothing + 1; return "ny" end), nil) -check("K4 kun ét opslag", nothing, 1) --- spredt udløb: 200 poster lavet samtidig udløber ikke samtidig -g_currentMission.time = 10000 -for i = 1, 200 do ADCacheGet("spread", i, 10000, function() return i end) end -local expired = 0 -g_currentMission.time = 22000 -for i = 1, 200 do ADCacheGet("spread", i, 10000, function() expired = expired + 1; return i end) end -check("K5 kun en del udløbet efter 12 s (spredt 10-15 s)", expired > 20 and expired < 180, true) -ADCache.clear() -check("K6 ryddet", next(ADCache.buckets), nil) - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adCourseCache.lua b/tests/test_adCourseCache.lua deleted file mode 100644 index 252fa12..0000000 --- a/tests/test_adCourseCache.lua +++ /dev/null @@ -1,195 +0,0 @@ --- Kør: luajit tests/test_adCourseCache.lua (fra repo-roden) --- Kursus-registret: hvilken mark/bredde/markform/indstillinger en maskines nuværende Courseplay-kursus blev --- lavet til. Genbrug kun når ALT matcher; egen maskine først, ellers en anden maskines kursus (kopi). -dofile("FS25_ADSmartPickup/adCourseCache.lua") -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end -local function count(map) - local n = 0 - for _ in pairs(map) do n = n + 1 end - return n -end - -local C = ADCourseCache -local SIG = "n=4;a=960000;c=800,300" - --- ---- indstillings-nøglen ---- -check("K1 standard", C.settingsKey({}), "off=0.0;lane=-;multi=1;vine=-;skip=-") -check("K2 nil = standard", C.settingsKey(nil), C.settingsKey({})) -check("K3 alt sat", C.settingsKey({toolOffset = 1.25, lane = -1, multiTools = 2, vineOffset = -1, vineSkip = 1}), - "off=1.3;lane=-1;multi=2;vine=-1.0;skip=1") -check("K4 -0.0 = 0.0", C.settingsKey({toolOffset = -0.0001}), C.settingsKey({toolOffset = 0})) -check("K5 float-støj i forskydning tæller ikke", C.settingsKey({toolOffset = 1.2000001}), C.settingsKey({toolOffset = 1.2})) -check("K6 anden bane = anden nøgle", C.settingsKey({multiTools = 2, lane = 1}) ~= C.settingsKey({multiTools = 2, lane = -1}), true) -check("K7 vin-offset indgår", C.settingsKey({vineOffset = 0}) ~= C.settingsKey({vineOffset = -1}), true) -check("K8 vin-skip indgår", C.settingsKey({vineOffset = -1, vineSkip = 0}) ~= C.settingsKey({vineOffset = -1, vineSkip = 1}), true) - -local settings = C.settingsKey({}) -local request = {field = 22, width = 30, sig = SIG, settings = settings} -local own = C.newRecord({vehicle = "v1", field = 22, width = 30.0, sig = SIG, settings = settings, waypoints = 812}) - --- ---- key/matches ---- -check("M1 samme alt", C.matches(own, request), true) -check("M2 bredde som float32 fra XML (30.0000001)", C.matches(C.newRecord({vehicle = "v1", field = 22, width = 30.0000001, sig = SIG, settings = settings}), request), true) -check("M3 ny bredde", C.matches(own, {field = 22, width = 12, sig = SIG, settings = settings}), false) -check("M4 anden mark", C.matches(own, {field = 23, width = 30, sig = SIG, settings = settings}), false) -check("M5 form ændret", C.matches(own, {field = 22, width = 30, sig = "n=5;a=960000;c=800,300", settings = settings}), false) -check("M6 andre indstillinger", C.matches(own, {field = 22, width = 30, sig = SIG, settings = C.settingsKey({toolOffset = 2})}), false) -check("M7 ukendt form (nil) matcher aldrig", C.matches(C.newRecord({vehicle = "v1", field = 22, width = 30, settings = settings}), {field = 22, width = 30, settings = settings}), false) -check("M8 intet register", C.matches(nil, request), false) -check("M9 key", C.key(request), "22|30.0|" .. SIG .. "|" .. settings) -check("M10 key uden form = nil", C.key({field = 22, width = 30, settings = settings}), nil) - --- ---- grund til ny beregning ---- -check("R1 intet gemt", C.mismatchReason(nil, request), "intet kursus gemt") -check("R2 form ændret", C.mismatchReason(own, {field = 22, width = 30, sig = "x", settings = settings}), "markens form ændret") -check("R3 ny bredde", C.mismatchReason(own, {field = 22, width = 12, sig = SIG, settings = settings}), "ny bredde") -check("R4 anden mark", C.mismatchReason(own, {field = 9, width = 30, sig = SIG, settings = settings}), "kursus til en anden mark") -check("R5 indstillinger", C.mismatchReason(own, {field = 22, width = 30, sig = SIG, settings = "x"}), "andre indstillinger") -check("R6 ukendt form", C.mismatchReason(own, {field = 22, width = 30, settings = settings}), "markens form ukendt") - --- ---- register: immutabelt ---- -local records = C.withRecord({}, own) -check("I1 tilføjet", records.v1 ~= nil, true) -local other = C.newRecord({vehicle = "v2", field = 22, width = 30, sig = SIG, settings = settings, waypoints = 812}) -local both = C.withRecord(records, other) -check("I2 original urørt", count(records), 1) -check("I3 begge", count(both), 2) -check("I4 without", count(C.without(both, "v1")), 1) -check("I5 without urørt original", count(both), 2) -check("I6 newRecord uden vehicle = nil", C.newRecord({field = 22}), nil) - --- ---- findReusable ---- -local rec, how = C.findReusable(both, request, "v1") -check("F1 egen maskine først", rec and rec.vehicle, "v1") -check("F2 egen = own", how, "own") -rec, how = C.findReusable(both, request, "v3") -check("F3 anden maskines kursus", rec and rec.vehicle, "v1") -check("F4 = copy", how, "copy") --- egen maskines kursus er til en anden bredde -> kopi fra v2 -local mixed = C.withRecord(both, C.newRecord({vehicle = "v1", field = 22, width = 12, sig = SIG, settings = settings})) -rec, how = C.findReusable(mixed, request, "v1") -check("F5 egen passer ikke -> kopi fra v2", rec and rec.vehicle, "v2") -check("F6 kopi", how, "copy") --- intet passer -> grunden fra egen post -local changed = {field = 22, width = 30, sig = "n=6;a=1;c=0,0", settings = settings} -rec, how = C.findReusable(both, changed, "v1") -check("F7 intet passer", rec, nil) -check("F8 grund: form ændret", how, "markens form ændret") -rec, how = C.findReusable({}, request, "v1") -check("F9 tomt register", how, "intet kursus gemt") --- gyldighedstjek: egen maskines kursus er væk/ændret -> kopi fra v2; alle ugyldige -> ny -rec, how = C.findReusable(both, request, "v1", function(r) return r.vehicle ~= "v1" end) -check("F10 egen ugyldig -> kopi", rec and rec.vehicle, "v2") -rec, how = C.findReusable(both, request, "v1", function() return false end) -check("F11 alle ugyldige", rec, nil) -check("F12 grund: kurset på maskinen er ændret", how, "kurset på maskinen er ændret") --- multi-tool: lederens kursus (2 sæt, bane -1) genbruges ikke til et enkelt sæt -local multi = C.newRecord({vehicle = "v1", field = 22, width = 30, sig = SIG, settings = C.settingsKey({multiTools = 2, lane = -1})}) -rec = C.findReusable(C.withRecord({}, multi), request, "v1") -check("F13 multi-kursus != enkelt", rec, nil) -rec, how = C.findReusable(C.withRecord({}, multi), {field = 22, width = 30, sig = SIG, settings = C.settingsKey({multiTools = 2, lane = -1})}, "v1") -check("F14 multi samme bane genbruges", how, "own") --- deterministisk: flere kandidater -> laveste id -local many = C.withRecord(C.withRecord({}, C.newRecord({vehicle = "v9", field = 22, width = 30, sig = SIG, settings = settings})), - C.newRecord({vehicle = "v4", field = 22, width = 30, sig = SIG, settings = settings})) -rec = C.findReusable(many, request, "v1") -check("F15 deterministisk valg", rec and rec.vehicle, "v4") - --- ---- invalidateField ---- -local fieldRecords = C.withRecord(both, C.newRecord({vehicle = "v5", field = 7, width = 30, sig = "n=3;a=50;c=3,3", settings = settings})) -local kept, removed = C.invalidateField(fieldRecords, 22, "n=5;a=1;c=0,0") -check("V1 to poster for marken slettet", removed, 2) -check("V2 anden mark bevaret", kept.v5 ~= nil, true) -check("V3 original urørt", count(fieldRecords), 3) -kept, removed = C.invalidateField(fieldRecords, 22, SIG) -check("V4 samme form: intet slettes", removed, 0) -check("V4b samme tabel tilbage", kept, fieldRecords) -kept, removed = C.invalidateField(fieldRecords, 22, nil) -check("V5 ukendt form: intet slettes", removed, 0) -kept, removed = C.invalidateField(nil, 22, SIG) -check("V6 nil-register", removed, 0) - --- ---- lim: prepare/afterStart/checkField med falske spil-objekter ---- -dofile("FS25_ADSmartPickup/adCpBridge.lua") -local saves, logs = 0, {} -ADFieldJobs = {state = {courseRecords = {}}, save = function() saves = saves + 1 end, - log = function(_, fmt, ...) table.insert(logs, string.format(fmt, ...)) end} -ADRuns = {vehicleLabel = function(v) return v.label end} -Logging = {warning = function(...) print("WARN", ...) end} -local function fakeCourse(n) - return {getNumberOfWaypoints = function() return n end} -end -local function fakeVehicle(id, width, course) - return {label = id, course = course, - getFieldWorkCourse = function(self) return self.course end, - getCourseGeneratorSettings = function() return {workWidth = {getValue = function() return width end}} end} -end -local tractorA = fakeVehicle("A", 30, nil) -local tractorB = fakeVehicle("B", 30, nil) -ADRunsController = {getFarmVehicles = function() return {tractorA, tractorB} end, - getVehicleId = function(v) return v.label end} -local fieldObj, info = {id = 22}, {shapeSig = SIG, fruitName = "WHEAT"} -local assignA = {vehicle = tractorA, vehicleId = "A", role = "cultivator"} - --- G1: intet gemt -> ny beregning, opts urørt (ingen reuse/course) -local opts, ctx = C.prepare({}, fieldObj, info, assignA, {}) -check("G1 ny beregning: ingen reuse", opts.reuse, nil) -check("G1b ingen kursus", opts.course, nil) -check("G1c log: nyt kursus (intet gemt)", logs[#logs]:find("nyt kursus beregnes", 1, true) ~= nil and logs[#logs]:find("intet kursus gemt", 1, true) ~= nil, true) --- CP har lavet kurset (812 punkter) -> registreres -tractorA.course = fakeCourse(812) -C.afterStart(tractorA, "A", ctx) -check("G2 registreret", ADFieldJobs.state.courseRecords.A ~= nil and ADFieldJobs.state.courseRecords.A.waypoints, 812) -check("G2b gemt til fil", saves, 1) --- G3: næste gang -> genbrug -opts, ctx = C.prepare({}, fieldObj, info, assignA, {}) -check("G3 genbrug", opts.reuse, true) -check("G3b log genbrugt", logs[#logs]:find("kursus genbrugt", 1, true) ~= nil, true) -C.afterStart(tractorA, "A", ctx) -check("G3c uændret register gemmes ikke igen", saves, 1) --- G4: B skal samme mark -> kopi fra A -local assignB = {vehicle = tractorB, vehicleId = "B", role = "cultivator"} -opts, ctx = C.prepare({}, fieldObj, info, assignB, {}) -check("G4 kopi fra A", opts.course, tractorA.course) -check("G4b log kopieret", logs[#logs]:find("kopieret fra 'A'", 1, true) ~= nil, true) -tractorB.course = fakeCourse(812) -C.afterStart(tractorB, "B", ctx) -check("G4c B registreret", ADFieldJobs.state.courseRecords.B ~= nil, true) --- G5: spilleren har lavet et nyt kursus på A i CP's HUD (andet antal punkter) -> kopi fra B -tractorA.course = fakeCourse(400) -opts = C.prepare({}, fieldObj, info, assignA, {}) -check("G5 A's kursus ændret -> kopi fra B", opts.course, tractorB.course) --- G6: kursus kopieret fra slåmaskinen (opts.course) -> registret bruges ikke, maskinens post glemmes -local mowerCourse = fakeCourse(99) -opts, ctx = C.prepare({}, fieldObj, info, assignB, {course = mowerCourse}) -check("G6 opts urørt", opts.course, mowerCourse) -check("G6b ingen kontekst", ctx, nil) -tractorB.course = fakeCourse(99) -C.afterStart(tractorB, "B", ctx) -check("G6c B glemt", ADFieldJobs.state.courseRecords.B, nil) --- G7: vinmark: vin-offset indgår i nøglen -local vineReq = C.requestFor(tractorA, 22, {shapeSig = SIG, fruitName = "GRAPE"}, "fertilizer", {}) -check("G7 vin i nøglen", vineReq.settings, C.settingsKey({vineOffset = -1, vineSkip = 1})) --- G8: markens form ændret -> A's post glemmes, log -local before = saves -check("G8 checkField sletter", C.checkField(22, "n=9;a=1;c=0,0"), 1) -check("G8b register tomt for marken", ADFieldJobs.state.courseRecords.A, nil) -check("G8c gemt", saves, before + 1) -check("G9 samme form: intet", C.checkField(22, "n=9;a=1;c=0,0"), 0) - --- G10: fejl i spil-API'et -> falder tilbage til ny beregning (opts urørt), ingen crash -local brokenVehicle = {label = "X", getFieldWorkCourse = function() error("boom") end} -local plainOpts = {lane = 1} -local okPrep, gotOpts, gotCtx = pcall(C.prepare, {}, fieldObj, nil, {vehicle = brokenVehicle, vehicleId = "X", role = "x"}, plainOpts) -check("G10 ingen crash", okPrep, true) -check("G10b opts urørt", gotOpts, plainOpts) -check("G10c ingen kontekst", gotCtx, nil) - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adCpBridge.lua b/tests/test_adCpBridge.lua deleted file mode 100644 index 2397575..0000000 --- a/tests/test_adCpBridge.lua +++ /dev/null @@ -1,178 +0,0 @@ --- Kør: luajit tests/test_adCpBridge.lua (fra repo-roden) --- Kun getWorkWidth: variabel bredde (Unrealistic Pack) vinder over AI-markørernes grundbredde. -dofile("FS25_ADSmartPickup/adCpBridge.lua") -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - -local function withMarkers(width) - return {getAIMarkers = function() return 1, 2, 3, 4, width end} -end - --- W1: læssevogn med variabel bredde 20,4 + 20,4 = 40,8 m, AI-markører siger 2,4 m -local wagon = withMarkers(2.4) -wagon.getVariableWorkWidth = function(_, isLeft) return isLeft and 20.4 or -20.4, nil, true end -local tractor = {getAttachedImplements = function() return {{object = wagon}} end} -check("W1 variabel bredde", ADCpBridge.getWorkWidth(tractor), 40.8) --- W2: ugyldig variabel bredde -> AI-markører -local plain = withMarkers(9.5) -plain.getVariableWorkWidth = function() return 0, nil, false end -check("W2 AI-markører", ADCpBridge.getWorkWidth(plain), 9.5) --- W3: selvkørende slåmaskine uden redskaber, kun variabel bredde -local mower = {getVariableWorkWidth = function(_, isLeft) return isLeft and 20.4 or -20.4, nil, true end} -check("W3 selvkørende", ADCpBridge.getWorkWidth(mower), 40.8) - --- W4: Courseplays egen bredde (HUD'en) vinder — det er den marken køres med -local cpVehicle = {getVariableWorkWidth = function(_, isLeft) return isLeft and 20.4 or -0.4, nil, true end, - getCourseGeneratorSettings = function() return {workWidth = {getValue = function() return 40.8 end}} end} -check("W4 Courseplay-bredde", ADCpBridge.getWorkWidth(cpVehicle), 40.8) --- W5: Courseplay-indstilling 0/ugyldig -> egen beregning -local zeroCp = {getVariableWorkWidth = function(_, isLeft) return isLeft and 5 or -5, nil, true end, - getCourseGeneratorSettings = function() return {workWidth = {getValue = function() return 0 end}} end} -check("W5 CP 0 -> egen beregning", ADCpBridge.getWorkWidth(zeroCp), 10) - --- WO: skårets sideforskydning på en river, målt fra nedlægningsområdet (venstre positiv), minus rivens --- egen CP-forskydning. Noder er tal; localToLocal-mock giver nodens x i traktorens ramme. -local nodeX = {[11] = 2.0, [12] = 4.0, [21] = -1.0, [22] = 1.0} -function localToLocal(node) return nodeX[node] or 0, 0, 0 end -local function rake(dropIndex, runtime) - local areas = { - {start = 1, width = 2, height = 3, dropWindrowWorkAreaIndex = runtime and dropIndex or nil}, - {start = 10, width = 11, height = 12}, - {start = 20, width = 21, height = 22}, - } - return {spec_windrower = {}, spec_workArea = {workAreas = areas}, configFileName = "rake.xml"} -end -local sideRake = rake(2, true) -local tractorWithRake = {rootNode = 100, getAttachedImplements = function() return {{object = sideRake}} end, - getCpSettings = function() return {toolOffsetX = {getValue = function() return 0.5 end}} end} -check("WO1 sideriver (runtime-felt)", ADCpBridge.getWindrowOffset(tractorWithRake), 2.5) -local centerRake = rake(3, true) -local tractorCenter = {rootNode = 100, getAttachedImplements = function() return {{object = centerRake}} end} -check("WO2 midterrive", ADCpBridge.getWindrowOffset(tractorCenter), 0) --- WO3: runtime-felt mangler -> riverens XML (workArea(0) windrower#dropWindrowWorkAreaIndex = 2) -ADCpBridge.readDropIndicesFromXml = function(fileName) return fileName == "rake.xml" and {[1] = 2} or {} end -local xmlRake = rake(2, false) -local tractorXml = {rootNode = 100, getAttachedImplements = function() return {{object = xmlRake}} end} -check("WO3 fra XML", ADCpBridge.getWindrowOffset(tractorXml), 3) -check("WO4 ingen river -> nil", ADCpBridge.getWindrowOffset({rootNode = 100}), nil) - --- SR: sprøjte/spreder -> rolle ud fra fyldet -check("SR gødning", ADCpBridge.sprayRoleForFillType("FERTILIZER"), "fertilizer") -check("SR flydende", ADCpBridge.sprayRoleForFillType("LIQUIDFERTILIZER"), "fertilizer") -check("SR gylle", ADCpBridge.sprayRoleForFillType("LIQUIDMANURE"), "fertilizer") -check("SR møg", ADCpBridge.sprayRoleForFillType("MANURE"), "fertilizer") -check("SR digestat", ADCpBridge.sprayRoleForFillType("DIGESTATE"), "fertilizer") -check("SR kalk", ADCpBridge.sprayRoleForFillType("LIME"), "limer") -check("SR ukrudt", ADCpBridge.sprayRoleForFillType("HERBICIDE"), "weeder") -check("SR vand/ukendt", ADCpBridge.sprayRoleForFillType("WATER"), nil) --- SR2: tom Bredal-spreder (tager gødning OG kalk) kan begge dele; fyldt med kalk er den primært kalkspreder -local bredal = {FERTILIZER = true, LIME = true} -local p1, r1, f1 = ADCpBridge.sprayRolesFor(nil, bredal) -check("SR2 tom spreder primært gødning", p1, "fertilizer") -check("SR2 tom spreder kan kalke", r1 and r1.limer, true) -check("SR2 tom = intet fyld", f1, nil) -local p2, r2, f2 = ADCpBridge.sprayRolesFor("LIME", bredal) -check("SR2 med kalk = kalkspreder", p2, "limer") -check("SR2 med kalk kan stadig gøde (efter tømning)", r2 and r2.fertilizer, true) -check("SR2 fyldets rolle", f2, "limer") -check("SR2 ukrudtssprøjte", (ADCpBridge.sprayRolesFor(nil, {HERBICIDE = true, WATER = true})), "weeder") -check("SR2 vandvogn er ingen sprøjte", (ADCpBridge.sprayRolesFor(nil, {WATER = true})), nil) --- SR3: kun en lille rest må hældes ud (100.000 l gødning blev hældt ud in-game 26/9) -check("SR3 tom", ADCpBridge.isDumpable(0, 4000), true) -check("SR3 lille rest 300 af 4000", ADCpBridge.isDumpable(300, 4000), true) -check("SR3 halv tank 2000 af 4000", ADCpBridge.isDumpable(2000, 4000), false) -check("SR3 100000 l aldrig", ADCpBridge.isDumpable(100000, 1000000), false) -check("SR3 over 2000 l aldrig", ADCpBridge.isDumpable(2500, 100000), false) --- VN: vinmark — i gangen for alt undtagen høsteren; almindelige marker røres ikke -check("VN sprøjte på druer = venstre", ADCpBridge.vineOffsetFor("GRAPEB", "fertilizer"), -1) -check("VN harve på druer = venstre", ADCpBridge.vineOffsetFor("grape", "cultivator"), -1) -check("VN høster på druer = på vinen", ADCpBridge.vineOffsetFor("GRAPEB", "harvester"), 0) -check("VN hvede røres ikke", ADCpBridge.vineOffsetFor("WHEAT", "fertilizer"), nil) -check("VN ukendt afgrøde", ADCpBridge.vineOffsetFor(nil, "weeder"), nil) -check("VN sprøjte springer 1 række over (begge sider)", ADCpBridge.vineSkipFor("GRAPEB", "weeder"), 1) -check("VN gødning springer 1 over", ADCpBridge.vineSkipFor("GRAPEB", "fertilizer"), 1) -check("VN harve springer ingen over", ADCpBridge.vineSkipFor("GRAPEB", "cultivator"), 0) -check("VN høster springer ingen over", ADCpBridge.vineSkipFor("GRAPEB", "harvester"), 0) -check("VN hvede røres ikke", ADCpBridge.vineSkipFor("WHEAT", "weeder"), nil) - --- HF: høsterens afgrøder fra skærebordet (liste eller opslag); finsnitter tager ingen -local list = ADCpBridge.harvestFruitSet({fruitTypes = {3, 5}}) -check("HF liste", list[3] and list[5], true) -local lookup = ADCpBridge.harvestFruitSet({fruitTypes = {[3] = true}}) -check("HF opslag", lookup[3], true) -local forage = ADCpBridge.harvestFruitSet({fruitTypes = {3}, fruitTypeConverters = {[3] = {}}}) -check("HF finsnitter tager intet", next(forage), nil) -check("HF fruitTypeIndices", ADCpBridge.harvestFruitSet({fruitTypeIndices = {7}})[7], true) - --- PL: plantbar i perioden -local fruitPlant = {growthDataSeasonal = {periods = {[3] = {plantingAllowed = true}, [4] = {plantingAllowed = false}}}} -check("PL tilladt", ADCpBridge.isPlantableIn(fruitPlant, 3), true) -check("PL ikke nu", ADCpBridge.isPlantableIn(fruitPlant, 4), false) -check("PL ukendt data = tilladt", ADCpBridge.isPlantableIn({}, 4), true) - --- CR: kursus-genbrug — start på maskinens eget kursus uden at CP beregner et nyt (opts.reuse) -do - local generated, started = 0, 0 - AIJobType = {FIELDWORK_CP = 1} - function getWorldTranslation() return 0, 0, 0 end - local job = {cpJobParameters = {}} - function job.applyCurrentState() end - function job.setValues() end - function job.validate() return true end - function job.registerFieldBoundaryDetectionCallback() end - function job.detectFieldBoundary() return true, false, "" end - function job.onClickGenerateFieldWorkCourse(_, callback) generated = generated + 1; callback(true) end - g_currentMission = {aiJobTypeManager = {createJob = function() return job end}, - aiSystem = {startJob = function() started = started + 1 end}} - local course = {} - local vehicle = {rootNode = 1, getOwnerFarmId = function() return 1 end, getFieldWorkCourse = function() return course end} - local reason = "?" - ADCpBridge.start(vehicle, 0, 0, function(ok, r) reason = ok and (r or "generated") or "fail" end, {reuse = true}) - check("CR1 genbrug: ingen beregning", generated, 0) - check("CR2 genbrug: CP startet", started, 1) - check("CR3 genbrug: reason", reason, "reused") - -- ingen kursus på maskinen -> beregnes alligevel - vehicle.getFieldWorkCourse = function() return nil end - ADCpBridge.start(vehicle, 0, 0, function(ok, r) reason = ok and (r or "generated") or "fail" end, {reuse = true}) - check("CR4 intet kursus -> beregnes", generated, 1) - check("CR5 reason ny", reason, "generated") - -- uden reuse: beregnes som før - vehicle.getFieldWorkCourse = function() return course end - ADCpBridge.start(vehicle, 0, 0, function(ok, r) reason = ok and (r or "generated") or "fail" end, {}) - check("CR6 uden reuse -> beregnes", generated, 2) - AIJobType, g_currentMission = nil, nil -end - --- forager: 2 runder og start på forageren (så der ikke køres ind i træer o.l. i hjørnerne) -local recorded = {} -local function setting(name) return {setValue = function(_, v) recorded[name] = v end} end -local headlandVehicle = {getCourseGeneratorSettings = function() return {numberOfHeadlands = setting("headlands"), startOnHeadland = setting("startOnHeadland")} end} -check("forager: sat", ADCpBridge.setHeadlands(headlandVehicle, 2), true) -check("forager: 2 runder", recorded.headlands, 2) -check("forager: start på forageren", recorded.startOnHeadland, true) -ADCpBridge.setHeadlands(headlandVehicle, 0) -check("forager: ingen -> ikke start på forageren", recorded.startOnHeadland, false) -check("forager: køretøj uden CP", ADCpBridge.setHeadlands({}, 2), false) -check("forager: standard er 2", ADCpBridge.HEADLANDS, 2) --- genbrugt kursus uden forager beregnes på ny -check("forager i kursus: 2 runder", ADCpBridge.courseHasHeadlands({getNumberOfHeadlands = function() return 2 end}), true) -check("forager i kursus: ingen", ADCpBridge.courseHasHeadlands({getNumberOfHeadlands = function() return 0 end}), false) -check("forager i kursus: ukendt -> antag ja", ADCpBridge.courseHasHeadlands({}), true) -check("forager i kursus: intet kursus", ADCpBridge.courseHasHeadlands(nil), false) --- isWorking: AutoDrive kører også som AI i FS25 — det er ikke Courseplay (in-game 29/9: Milk Store holdt ved --- Cow Wait med AD aktiv og "ventede på sig selv" som 'Courseplay kører') -local function fakeVehicle(cp, ai, ad) - return {getIsCpFieldWorkActive = function() return cp end, getIsAIActive = function() return ai end, - ad = {stateModule = {isActive = function() return ad end}}} -end -check("CP arbejder: Courseplay markarbejde", ADCpBridge.isWorking(fakeVehicle(true, true, false)), true) -check("CP arbejder: AI uden AD (CP-kørsel)", ADCpBridge.isWorking(fakeVehicle(false, true, false)), true) -check("CP arbejder: AutoDrive kører -> ikke CP", ADCpBridge.isWorking(fakeVehicle(false, true, true)), false) -check("CP arbejder: intet", ADCpBridge.isWorking(fakeVehicle(false, false, false)), false) -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adCpResume.lua b/tests/test_adCpResume.lua deleted file mode 100644 index 099b143..0000000 --- a/tests/test_adCpResume.lua +++ /dev/null @@ -1,33 +0,0 @@ --- Kør: luajit tests/test_adCpResume.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adCpResume.lua") -local R = ADCpResume -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end --- hvem skrives ved gem: CP kører, AD styrer ikke (AD genstarter selv sine) -local ids = R.activeIds({{id = "a", cpActive = true}, {id = "b", cpActive = true, adActive = true}, {id = "c"}, {id = "d", cpActive = true}}) -check("gem: CP-aktive uden AD", table.concat(ids, " "), "a d") -check("gem: tom liste", #R.activeIds({}), 0) --- records frem og tilbage -local set = R.fromRecords({"a", "", "d", "a"}) -check("indlæs: a", set.a, true) -check("indlæs: tomt id springes over", set[""], nil) --- hvem startes ved indlæsning -local pending = {a = true, b = true, c = true, d = true} -local vehicles = {{id = "a"}, {id = "b", aiActive = true}, {id = "c", controlled = true}, {id = "d"}, {id = "e"}} -local start = R.toStart(pending, vehicles) -check("start: kun gemte, ikke allerede kørende/spiller i", table.concat(start, " "), "a d") -check("start: ingen gemt liste -> nil (gammel regel bruges)", R.toStart(nil, vehicles), nil) - --- kun når CP's HUD-job er markarbejde: moddets markopgaver starter CP med eget job, så HUD'en kan stå på fx --- bunkersilo — HUD-start gav "CP: No silo found" efter genindlæsning (in-game 29/9) -local mixed = R.toStart({a = true, b = true, c = true}, { - {id = "a", fieldWork = true}, {id = "b", fieldWork = false}, {id = "c"}}) -check("start: kun markarbejds-job", table.concat(mixed, ","), "a,c") - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFieldFleet.lua b/tests/test_adFieldFleet.lua deleted file mode 100644 index 4e1846d..0000000 --- a/tests/test_adFieldFleet.lua +++ /dev/null @@ -1,145 +0,0 @@ --- Kør: luajit tests/test_adFieldFleet.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adFieldModel.lua") -dofile("FS25_ADSmartPickup/adFieldFleet.lua") -local F = ADFieldFleet -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - --- FL1: rolle ud fra redskabets spec (læssevogn vinder over andet på samme køretøj) -check("FL1 slåmaskine", F.roleFromSpecs({spec_mower = true}), "mower") -check("FL1 vender", F.roleFromSpecs({spec_tedder = true}), "tedder") -check("FL1 river", F.roleFromSpecs({spec_windrower = true}), "rake") -check("FL1 læssevogn", F.roleFromSpecs({spec_forageWagon = true, spec_mower = true}), "collector") -check("FL1 intet", F.roleFromSpecs({spec_trailer = true}), nil) - -local function rig(id, role, x, extra) - local r = {id = id, role = role, enabled = true, busy = false, controlled = false, width = 40.8, x = x, z = 0, label = id} - for k, v in pairs(extra or {}) do r[k] = v end - return r -end - --- FL2: nærmeste ledige med flueben og rigtig rolle -local picked = F.pick({rig("far", "mower", 500), rig("near", "mower", 50), rig("wagon", "collector", 10)}, "mower", 0, 0, nil) -check("FL2 nærmeste slåmaskine", picked.id, "near") - --- FL3: uden flueben / optaget / spiller i sædet springes over -local _, why = F.pick({rig("a", "mower", 1, {enabled = false})}, "mower", 0, 0) -check("FL3 intet med flueben", why, "noRig") -local _, whyBusy = F.pick({rig("a", "mower", 1, {busy = true}), rig("b", "mower", 2, {controlled = true})}, "mower", 0, 0) -check("FL3 alle optaget", whyBusy, "allBusy") -check("FL3 ingen med rollen", select(2, F.pick({rig("a", "tedder", 1)}, "mower", 0, 0)), "noRig") - --- FL4: læssevogn smallere end slåbredden springes over, bredere vælges -local wide = F.pick({rig("smal", "collector", 1, {width = 20}), rig("bred", "collector", 900, {width = 40.8})}, "collector", 0, 0, 40.8) -check("FL4 bred valgt selv om længere væk", wide.id, "bred") --- FL5: kun for smalle -> grund tooNarrow med tal -local none, whyNarrow, args = F.pick({rig("smal", "collector", 1, {width = 20})}, "collector", 0, 0, 40.8) -check("FL5 ingen", none, nil); check("FL5 grund", whyNarrow, "tooNarrow"); check("FL5 bredde", args[1], 20) --- FL6: over 50 m -> tooWide -check("FL6 for bred", select(2, F.pick({rig("x", "mower", 1, {width = 60})}, "mower", 0, 0)), "tooWide") - --- FL7: toggle giver ny tabel -local enabled = {a = true} -local toggled = F.toggle(enabled, "b") -check("FL7 b til", toggled.b, true); check("FL7 original urørt", enabled.b, nil) -check("FL7 a fra", F.toggle(toggled, "a").a, nil) - --- FL8: nye roller i prioriteret rækkefølge -check("FL8 høster", F.roleFromSpecs({spec_combine = true, spec_cutter = true}), "harvester") -check("FL8 såmaskine før kultivator (combi)", F.roleFromSpecs({spec_sowingMachine = true, spec_cultivator = true, sprayRole = "fertilizer"}), "seeder") -check("FL8 kultivator", F.roleFromSpecs({spec_cultivator = true}), "cultivator") -check("FL8 plov", F.roleFromSpecs({spec_plow = true}), "plow") -check("FL8 tromle", F.roleFromSpecs({spec_roller = true}), "roller") -check("FL8 strigle", F.roleFromSpecs({spec_weeder = true}), "weeder") -check("FL8 stubhakker", F.roleFromSpecs({spec_mulcher = true}), "mulcher") -check("FL8 gødningssprøjte", F.roleFromSpecs({sprayRole = "fertilizer"}), "fertilizer") -check("FL8 kalkspreder", F.roleFromSpecs({sprayRole = "limer"}), "limer") -check("FL8 ukrudtssprøjte", F.roleFromSpecs({sprayRole = "weeder"}), "weeder") -check("FL8 aflæsningsvogn", F.roleFromSpecs({canUnload = true}), "unloader") -check("FL8 traktor med plov er ikke aflæsningsvogn", F.roleFromSpecs({spec_plow = true, canUnload = true}), "plow") - --- FL9: accept-filter (fx høster der kan tage afgrøden, strigle kun til småt ukrudt) -local combineWheat = rig("jd", "harvester", 10, {fruits = {[3] = true}}) -local combineBeet = rig("ropa", "harvester", 1, {fruits = {[9] = true}}) -local forWheat = F.pick({combineBeet, combineWheat}, "harvester", 0, 0, nil, function(c) return c.fruits[3] == true end) -check("FL9 høster der kan tage hvede", forWheat.id, "jd") -local _, whyFruit = F.pick({combineBeet}, "harvester", 0, 0, nil, function(c) return c.fruits[3] == true end) -check("FL9 ingen kan tage afgrøden", whyFruit, "cannotDo") - --- FL10: flere sæt til multi-tool — alle eller ingen, nærmeste først -local m1, m2, m3 = rig("m1", "mower", 30), rig("m2", "mower", 10), rig("m3", "mower", 20, {busy = true}) -local two = F.pickMany({m1, m2, m3}, "mower", 2, 0, 0) -check("FL10 to valgt", #two, 2) -check("FL10 nærmeste først", two[1].id, "m2") -local none, whyMany, args = F.pickMany({m1, m2, m3}, "mower", 3, 0, 0) -check("FL10 for få -> ingen", none, nil) -check("FL10 grund", whyMany, "notEnough") -check("FL10 ledige/krævet", args[1] .. "/" .. args[2], "2/3") -check("FL10 ingen med rollen", select(2, F.pickMany({m1}, "tedder", 1, 0, 0)), "noRig") - --- FL11: markflådens tilstand: alle ledige maskiner (standard) eller kun med flueben -check("FL11 alle: uden flueben med", F.isEnabled("all", {}, "v1"), true) -check("FL11 flueben: uden flueben ude", F.isEnabled("ticked", {}, "v1"), false) -check("FL11 flueben: med flueben", F.isEnabled("ticked", {v1 = true}, "v1"), true) -check("FL11 ukendt tilstand = alle", F.isEnabled(nil, {}, "v1"), true) - --- FL12: ballepresser og ballesamler (ballevogn/Universal Autoload som Courseplays ballesamler kan bruge) -check("FL12 ballepresser", F.roleFromSpecs({spec_baler = true}), "baler") -check("FL12 ballesamler", F.roleFromSpecs({canBaleCollect = true}), "baleCollector") -check("FL12 ballesamler før aflæsningsvogn", F.roleFromSpecs({canBaleCollect = true, canUnload = true}), "baleCollector") -check("FL12 høster før presser", F.roleFromSpecs({spec_combine = true, spec_baler = true}), "harvester") - --- FL-spreder: tom spreder med rollerne {gødning, kalk} vælges til kalkning; spreder med forkert vare --- på (kalk, skal gøde) kun hvis ingen anden kan — og den nærmeste med rigtig/ingen vare først -local spreaders = { - {id = "kalk", role = "limer", roles = {fertilizer = true, limer = true}, filledRole = "limer", enabled = true, x = 1, z = 0, width = 12}, - {id = "tom", role = "fertilizer", roles = {fertilizer = true, limer = true}, enabled = true, x = 50, z = 0, width = 12}, -} -check("FLS tom spreder tager kalkning", F.pick({spreaders[2]}, "limer", 0, 0) and F.pick({spreaders[2]}, "limer", 0, 0).id, "tom") -check("FLS gødskning: tom før nærmere med kalk på", F.pick(spreaders, "fertilizer", 0, 0).id, "tom") -check("FLS kun kalk-sprederen: bruges (tømmes)", F.pick({spreaders[1]}, "fertilizer", 0, 0).id, "kalk") -check("FLS kalkning: den med kalk på foretrækkes", F.pick(spreaders, "limer", 0, 0).id, "kalk") --- FLS2: fuld af en anden vare (ikke dumpable) bruges slet ikke til opgaven -local fullOther = {id = "fuld", role = "fertilizer", roles = {fertilizer = true, weeder = true}, filledRole = "fertilizer", dumpable = false, enabled = true, x = 0, z = 0, width = 30} -check("FLS2 fuld gødningssprøjte bruges ikke til ukrudt", F.pick({fullOther}, "weeder", 0, 0), nil) -check("FLS2 men gerne til gødning", F.pick({fullOther}, "fertilizer", 0, 0).id, "fuld") - --- FLD: faste maskiner pr. mark -local sprayA = {id = "sprøjte", role = "fertilizer"} -local sprayB = {id = "spreder", role = "fertilizer"} -local harve = {id = "harve", role = "cultivator"} -local all = {sprayA, sprayB, harve} -local byField = {[22] = {"sprøjte"}} -local function ids(list) local out = {} for _, c in ipairs(list) do table.insert(out, c.id) end return table.concat(out, ",") end -check("FLD F22 gødskning: kun sin faste", ids(F.filterDedicated(all, 22, "fertilizer", byField)), "sprøjte") -check("FLD F109 gødskning: ikke F22's", ids(F.filterDedicated(all, 109, "fertilizer", byField)), "spreder,harve") -check("FLD F22 kultivering: ingen fast med rollen -> frie", ids(F.filterDedicated(all, 22, "cultivator", byField)), "spreder,harve") -check("FLD uden faste = alle", ids(F.filterDedicated(all, 5, "fertilizer", {})), "sprøjte,spreder,harve") -local moved = F.dedicate(byField, 109, "sprøjte", true) -check("FLD flyt: væk fra F22", #moved[22], 0) -check("FLD flyt: fast på F109", moved[109][1], "sprøjte") -check("FLD original urørt", byField[22][1], "sprøjte") -check("FLD fjern", #F.dedicate(moved, 109, "sprøjte", false)[109], 0) - --- FLV: menuen skjuler maskiner der er faste på en anden mark end den valgte -local vrigs = {{id = "vogn"}, {id = "harve"}, {id = "plov"}} -local vis, hid, flds = F.visibleForField(vrigs, 18, {[22] = {"vogn", "harve"}}) -check("FLV F18 ser kun de frie", #vis .. "/" .. hid .. "/" .. tostring(flds[1]), "1/2/22") -check("FLV F22 ser alle", #(F.visibleForField(vrigs, 22, {[22] = {"vogn", "harve"}})), 3) -check("FLV ingen valgt = alle", #(F.visibleForField(vrigs, nil, {[22] = {"vogn"}})), 3) - --- FL-plov: FS25-plove har også spec_cultivator — de er plove, men må også tage kultivering -check("FLP plov med kultivator-spec er plov", F.roleFromSpecs({spec_plow = true, spec_cultivator = true}), "plow") -check("FLP plov kan kultivere", F.rolesFor("plow").cultivator, true) -check("FLP plov kan pløje", F.rolesFor("plow").plow, true) -check("FLP kultivator kan ikke pløje", F.rolesFor("cultivator"), nil) -local plough = {id = "p", role = "plow", roles = F.rolesFor("plow"), enabled = true, x = 0, z = 0, width = 3} -check("FLP plov vælges til kultivering", (F.pick({plough}, "cultivator", 0, 0, nil)).id, "p") -check("FLP rolesFor giver en kopi", F.rolesFor("plow") ~= F.rolesFor("plow"), true) -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFieldGuide.lua b/tests/test_adFieldGuide.lua deleted file mode 100644 index 100a7d4..0000000 --- a/tests/test_adFieldGuide.lua +++ /dev/null @@ -1,255 +0,0 @@ --- Kør: luajit tests/test_adFieldGuide.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adGuide.lua") -dofile("FS25_ADSmartPickup/adFieldModel.lua") -dofile("FS25_ADSmartPickup/adFieldFleet.lua") -dofile("FS25_ADSmartPickup/adFlowView.lua") -dofile("FS25_ADSmartPickup/adFieldGuide.lua") -local FG, G = ADFieldGuide, ADGuide -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - -local SOWABLE = {{name = "WHEAT", title = "Hvede"}, {name = "BARLEY", title = "Byg"}, {name = "GRASS", title = "Græs"}, {name = "MAIZE", title = "Majs"}} -local RIGS = { - {id = "v1", text = "Fendt 1 — slåmaskine", role = "mower"}, - {id = "v2", text = "Claas 2 — mejetærsker", role = "harvester"}, - {id = "v3", text = "Sprøjte 3 — sprøjte", role = "weeder"}, - {id = "v4", text = "Vogn 4 — læssevogn", role = "collector"}, -} -local function ctx(info, extra) - local context = { - info = info, sowable = SOWABLE, mowable = {GRASS = true}, straw = {WHEAT = true, BARLEY = true}, - rigs = RIGS, rigIdsByField = {[90] = {"v3"}}, fieldSilos = {}, storeHidden = 0, - stores = info.kind ~= "bare" and {{id = "s1", text = "Silo A, 300 m"}, {id = "s2", text = "Silo B, 1,2 km"}} or nil, - fields = {{id = 1, text = "F1 Græs"}, {id = 2, text = "F2 Hvede"}}, - } - for key, value in pairs(extra or {}) do context[key] = value end - return context -end -local GRASS = {id = 1, kind = "mowable", fruitName = "GRASS", cropTitle = "Græs", markerId = 5} -local WHEAT = {id = 2, kind = "harvest", fruitName = "WHEAT", cropTitle = "Hvede", markerId = 6} -local MAIZE = {id = 3, kind = "harvest", fruitName = "MAIZE", cropTitle = "Majs", markerId = 7} -local BARE = {id = 4, kind = "bare", markerId = 8} - -local function steps(guide) return table.concat(G.visibleSteps(guide), ",") end -local function field(id, plan) - local f = ADFieldModel.newField(id) - return ADFieldModel.with(f, {plan = plan or {}}) -end -local function findRow(rows, id, item) - for _, row in ipairs(rows) do - if row.id == id and (item == nil or row.item == item) then return row end - end - return nil -end -local function countType(rows, rowType) - local n = 0 - for _, row in ipairs(rows) do if row.type == rowType then n = n + 1 end end - return n -end - --- ---- synlige trin pr. marktype ---- -check("FG1 græs", steps(FG.open(field(1), ctx(GRASS))), "crop,cropChoice,care,machines,store,summary") -check("FG1 hvede m. halm", steps(FG.open(field(2), ctx(WHEAT))), "crop,cropChoice,prep,care,machines,store,summary") -check("FG1 majs uden halm", steps(FG.open(field(3), ctx(MAIZE))), "crop,prep,care,machines,store,summary") -check("FG1 bar mark", steps(FG.open(field(4), ctx(BARE))), "crop,prep,machines,summary") -check("FG1 halm ukendt -> vis", steps(FG.open(field(3), ctx(MAIZE, {straw = {}}))), "crop,cropChoice,prep,care,machines,store,summary") -check("FG1 hvede med mål græs", steps(FG.open(field(2, {targetFruit = "GRASS"}), ctx(WHEAT))), "crop,cropChoice,prep,care,machines,store,summary") -check("FG1 bar med mål hvede", steps(FG.open(field(4, {targetFruit = "WHEAT"}), ctx(BARE))), "crop,cropChoice,prep,care,machines,summary") -check("FG1 græs med mål græs: ingen klargøring", steps(FG.open(field(1, {targetFruit = "GRASS"}), ctx(GRASS))), "crop,cropChoice,care,machines,store,summary") -check("FG1 græs med mål hvede: klargøring", steps(FG.open(field(1, {targetFruit = "WHEAT"}), ctx(GRASS))), "crop,cropChoice,prep,care,machines,store,summary") -local picker = FG.openPicker(ctx(GRASS)) -check("FG1 vælger: kun mark", steps(picker), "field") -check("FG1 vælger: trin", picker.step, "field") -check("FG1 vælger: position uden total", (G.position(picker)), 1) - --- ---- standarder og udfyldning ---- -local g = FG.open(field(2), ctx(WHEAT)) -check("FG2 starter på afgrøde", g.step, "crop") -check("FG2 klargøring auto", g.draft.prepAuto, true) -check("FG2 ingen faste", g.draft.useDedicated, false) -check("FG2 lager auto", g.draft.storeMode, "auto") -check("FG2 kladde er kopi", g.draft.plan ~= nil and g.draft.fieldId, 2) -local plowed = FG.open(field(2, {soil = "plow"}), ctx(WHEAT)) -check("FG2 pløj -> vælg selv", plowed.draft.prepAuto, false) -local withRigs = FG.open(field(2, {rigIds = {"v2"}}), ctx(WHEAT)) -check("FG2 faste maskiner -> ja", withRigs.draft.useDedicated, true) -local silo = FG.open(field(1, {storeBuildingIds = {"m1"}}), ctx(GRASS, {fieldSilos = {{id = "m1", text = "Marksilo"}}})) -check("FG2 marksilo valgt", silo.draft.storeMode, "fieldSilo") -local manual = FG.open(field(1, {storeBuildingIds = {"s2"}}), ctx(GRASS)) -check("FG2 valgt silo -> vælg selv", manual.draft.storeMode, "manual") -check("FG2 valgt silo i listen", manual.draft.storeIds[1], "s2") - --- ---- choose er immutabel ---- -local chosen = FG.choose(g, "targetFruit", "BARLEY") -check("FG3 ny kladde", chosen.draft.plan.targetFruit, "BARLEY") -check("FG3 gammel urørt", g.draft.plan.targetFruit, "") -local toggled = FG.choose(withRigs, "rigIds", {item = "v1", on = true}) -check("FG3 check tilføjer", table.concat(toggled.draft.plan.rigIds, ","), "v2,v1") -check("FG3 gammel liste urørt", table.concat(withRigs.draft.plan.rigIds, ","), "v2") -check("FG3 check fjerner", table.concat(FG.choose(toggled, "rigIds", {item = "v2", on = false}).draft.plan.rigIds, ","), "v1") -check("FG3 produkt hø", FG.choose(FG.open(field(1), ctx(GRASS)), "product", true).draft.plan.product, "hay") -check("FG3 produkt ensilage", FG.choose(FG.open(field(1), ctx(GRASS)), "product", false).draft.plan.product, "silage") - --- ---- rækker ---- -local cropRows = FG.rows(g) -local target = findRow(cropRows, "targetFruit") -check("FG4 afgrøde: valg", target.type, "choice") -check("FG4 afgrøde: behold først", target.options[1].value, "") -check("FG4 afgrøde: såbare", #target.options, 5) -check("FG4 så igen for høstmark", findRow(cropRows, "sow") ~= nil, true) -check("FG4 så igen skjult med mål", findRow(FG.rows(chosen), "sow"), nil) -check("FG4 så igen skjult på græs", findRow(FG.rows(FG.open(field(1), ctx(GRASS))), "sow"), nil) - -local choiceGrass = FG.rows(G.jump(FG.open(field(1), ctx(GRASS)), "cropChoice", "close")) -check("FG5 græs: ensilage/hø", findRow(choiceGrass, "product") ~= nil, true) -check("FG5 græs: river", findRow(choiceGrass, "rake") ~= nil, true) -check("FG5 græs: ingen halm", findRow(choiceGrass, "straw"), nil) -local choiceWheat = FG.rows(G.jump(g, "cropChoice", "close")) -check("FG5 hvede: halm", findRow(choiceWheat, "straw").type, "choice") -check("FG5 hvede: ingen ensilage", findRow(choiceWheat, "product"), nil) - -local prepRows = FG.rows(G.jump(g, "prep", "close")) -check("FG6 klargøring auto: kun valget + tekst", findRow(prepRows, "soil"), nil) -check("FG6 klargøring valg", findRow(prepRows, "prepAuto").type, "choice") -local prepManual = FG.rows(G.jump(FG.choose(g, "prepAuto", false), "prep", "close")) -check("FG6 vælg selv: jord", findRow(prepManual, "soil") ~= nil, true) -check("FG6 vælg selv: kalk", findRow(prepManual, "lime") ~= nil, true) -check("FG6 vælg selv: mulch", findRow(prepManual, "mulch") ~= nil, true) -check("FG6 vælg selv: ingen tromle uden såning", findRow(prepManual, "roll"), nil) - --- pleje: Automatisk/Manuelt som klargøring (brugerønske 29/9); knapperne vises under Manuelt -local careAutoRows = FG.rows(G.jump(g, "care", "close")) -check("FG7 pleje: valg auto/manuel", findRow(careAutoRows, "careAuto").type, "choice") -check("FG7 pleje auto: ingen knapper", findRow(careAutoRows, "fertilize"), nil) -check("FG7 standard: pleje auto", g.draft.careAuto, true) -check("FG7 uden gødning -> manuel", FG.open(field(2, {fertilize = false}), ctx(WHEAT)).draft.careAuto, false) -local autoCare = FG.result(FG.choose(FG.open(field(2, {fertilize = false, weed = false}), ctx(WHEAT)), "careAuto", true), true).plan -check("FG7 auto slår gødning til", autoCare.fertilize, true) -check("FG7 auto slår ukrudt til", autoCare.weed, true) -local careGrass = FG.rows(G.jump(FG.choose(FG.open(field(1), ctx(GRASS)), "careAuto", false), "care", "close")) -check("FG7 græs: ingen ukrudt", findRow(careGrass, "weed"), nil) -check("FG7 græs: kalk i pleje", findRow(careGrass, "lime") ~= nil, true) -local careWheat = FG.rows(G.jump(FG.choose(g, "careAuto", false), "care", "close")) -check("FG7 hvede: ukrudt", findRow(careWheat, "weed") ~= nil, true) -check("FG7 hvede: kalk ikke i pleje", findRow(careWheat, "lime"), nil) - -local machinesOff = FG.rows(G.jump(g, "machines", "close")) -check("FG8 antal ad gangen", findRow(machinesOff, "rigs").type, "choice") -check("FG8 aflæsningsvogne ved høst", findRow(machinesOff, "unloaders") ~= nil, true) -check("FG8 ingen liste uden faste", countType(machinesOff, "check"), 0) -check("FG8 ingen aflæsningsvogne på græs", findRow(FG.rows(G.jump(FG.open(field(1), ctx(GRASS)), "machines", "close")), "unloaders"), nil) -local machinesOn = FG.rows(G.jump(FG.choose(g, "useDedicated", true), "machines", "close")) -check("FG8 liste: v3 fast på F90 skjult", findRow(machinesOn, "rigIds", "v3"), nil) -check("FG8 liste: tre synlige", countType(machinesOn, "check"), 3) -local hiddenText = nil -for _, row in ipairs(machinesOn) do if row.type == "text" and row.label.key == "spu_field_hiddenDedicated" then hiddenText = row end end -check("FG8 tekst om skjulte", hiddenText ~= nil and hiddenText.label.args[1], 1) -check("FG8 tekst marker", hiddenText ~= nil and hiddenText.label.args[2], "F90") -local ownFirst = FG.rows(G.jump(withRigs, "machines", "close")) -local firstCheck = nil -for _, row in ipairs(ownFirst) do if row.type == "check" and firstCheck == nil then firstCheck = row end end -check("FG8 egne først", firstCheck.item, "v2") -check("FG8 egne sat", firstCheck.value, true) - -local storeAuto = FG.rows(G.jump(FG.open(field(1), ctx(GRASS)), "store", "close")) -check("FG9 ingen marksilo-spørgsmål uden marksilo", findRow(storeAuto, "useFieldSilo"), nil) -check("FG9 automatisk/vælg selv", findRow(storeAuto, "storeAuto").type, "choice") -check("FG9 auto: ingen siloliste", countType(storeAuto, "check"), 0) -local storeManual = FG.rows(G.jump(FG.choose(FG.open(field(1), ctx(GRASS)), "storeAuto", false), "store", "close")) -check("FG9 vælg selv: siloer", countType(storeManual, "check"), 2) -local siloCtx = ctx(GRASS, {fieldSilos = {{id = "m1", text = "Marksilo F1"}}}) -local storeSilo = FG.rows(G.jump(FG.open(field(1), siloCtx), "store", "close")) -check("FG9 marksilo-spørgsmål", findRow(storeSilo, "useFieldSilo").type, "toggle") -local useSilo = FG.choose(FG.open(field(1), siloCtx), "useFieldSilo", true) -check("FG9 marksilo ja -> skjul auto", findRow(FG.rows(G.jump(useSilo, "store", "close")), "storeAuto"), nil) - -local pickRows = FG.rows(picker) -check("FG10 vælger: links", countType(pickRows, "link"), 2) -check("FG10 vælger: id", findRow(pickRows, "field").value, 1) -local picked = FG.pickField(picker, field(2), ctx(WHEAT)) -check("FG10 valgt mark", picked.draft.fieldId, 2) -check("FG10 videre til afgrøde", picked.step, "crop") -check("FG10 mark-trin stadig talt", (G.position(picked)), 2) - --- ---- resultat ---- -local r = FG.result(g, true) -check("FG11 auto ved start", r.plan.mode, "auto") -check("FG11 manuel uden start", FG.result(g, false).plan.mode, "manual") -local autoField = FG.open(field(2, {mode = "auto"}), ctx(WHEAT)) -check("FG11 Ret bevarer auto", FG.result(autoField, nil).plan.mode, "auto") -check("FG11 Ret bevarer manuel", FG.result(g, nil).plan.mode, "manual") -check("FG11 mål tvinger auto", FG.result(chosen, false).plan.mode, "auto") -check("FG11 mål ændret", FG.result(chosen, true).targetChanged, true) -check("FG11 mål uændret", r.targetChanged, false) -check("FG11 ingen rigIds i plan", r.plan.rigIds, nil) -local soilPlan = FG.result(FG.open(field(2, {soil = "plow", mulch = true, lime = false, roll = true}), ctx(WHEAT)), true).plan -check("FG12 vælg selv bevarer jord", soilPlan.soil, "plow") -local autoPrep = FG.result(FG.choose(FG.open(field(2, {soil = "plow", mulch = true, lime = false, roll = true}), ctx(WHEAT)), "prepAuto", true), true).plan -check("FG12 auto: jord", autoPrep.soil, "auto") -check("FG12 auto: mulch", autoPrep.mulch, false) -check("FG12 auto: kalk", autoPrep.lime, true) -check("FG12 auto: tromle", autoPrep.roll, false) -local grassNoPrep = FG.result(FG.choose(FG.open(field(1, {lime = false}), ctx(GRASS)), "prepAuto", true), true).plan -check("FG12 græs: auto rører ikke kalk", grassNoPrep.lime, false) -check("FG13 lager auto", #FG.result(FG.open(field(1, {storeBuildingIds = {"s1"}}), ctx(GRASS)) and FG.choose(FG.open(field(1, {storeBuildingIds = {"s1"}}), ctx(GRASS)), "storeAuto", true), true).plan.storeBuildingIds, 0) -check("FG13 lager vælg selv", table.concat(FG.result(FG.choose(FG.choose(FG.open(field(1), ctx(GRASS)), "storeAuto", false), "storeBuildingIds", {item = "s2", on = true}), true).plan.storeBuildingIds, ","), "s2") -check("FG13 lager marksilo", table.concat(FG.result(useSilo, true).plan.storeBuildingIds, ","), "m1") -check("FG13 bar mark bevarer lager", table.concat(FG.result(FG.open(field(4, {storeBuildingIds = {"s9"}}), ctx(BARE)), true).plan.storeBuildingIds, ","), "s9") -check("FG14 faste fra -> tom", #FG.result(FG.choose(withRigs, "useDedicated", false), true).rigIds, 0) -check("FG14 faste til", table.concat(FG.result(toggled, true).rigIds, ","), "v2,v1") - --- ---- faste maskiner på tværs af marker ---- -local byField = FG.applyRigs({[90] = {"v3", "v5"}, [2] = {"v2"}}, 2, {"v2", "v3"}) -check("FG15 fjernet fra anden mark", table.concat(byField[90], ","), "v5") -check("FG15 sat på marken", table.concat(byField[2], ","), "v2,v3") -check("FG15 ny mark", table.concat(FG.applyRigs({}, 7, {"v1"})[7], ","), "v1") - --- ---- resumé ---- -local function lineFor(lines, step) - for _, line in ipairs(lines) do if line.step == step then return line end end - return nil -end -local function partKeys(line) - local keys = {} - for _, part in ipairs(line.parts) do table.insert(keys, part.key or ("=" .. tostring(part.text))) end - return table.concat(keys, ",") -end -local sum = FG.summary(g) -check("FG16 linjer for synlige trin", #sum, 6) -check("FG16 afgrøde behold", partKeys(lineFor(sum, "crop")), "spu_guide_crop_keep") -check("FG16 halm", partKeys(lineFor(sum, "cropChoice")), "spu_guide_sum_straw_chop") -check("FG16 klargøring auto", partKeys(lineFor(sum, "prep")), "spu_guide_sum_auto") -check("FG16 pleje auto", partKeys(lineFor(sum, "care")), "spu_guide_sum_auto") -check("FG16 pleje manuel", partKeys(lineFor(FG.summary(FG.choose(g, "careAuto", false)), "care")), "spu_guide_sum_fert,spu_guide_sum_weed") -check("FG16 maskiner", partKeys(lineFor(sum, "machines")), "spu_guide_sum_freeRigs,spu_guide_sum_rigs,spu_guide_sum_unloaders") -check("FG16 lager auto", partKeys(lineFor(sum, "store")), "spu_guide_sum_auto") -check("FG16 ingen mark/summary-linje", lineFor(sum, "summary"), nil) -local sumTarget = FG.summary(chosen) -check("FG17 mål", partKeys(lineFor(sumTarget, "crop")), "spu_guide_sum_target") -check("FG17 mål titel", lineFor(sumTarget, "crop").parts[1].args[1], "Byg") -local sumRigs = FG.summary(withRigs) -check("FG17 faste", lineFor(sumRigs, "machines").parts[1].key, "spu_guide_sum_dedicated") -check("FG17 faste antal", lineFor(sumRigs, "machines").parts[1].args[1], 1) -local sumManual = FG.summary(FG.open(field(2, {soil = "plow", lime = true, mulch = true}), ctx(WHEAT))) -check("FG17 klargøring vælg selv", partKeys(lineFor(sumManual, "prep")), "spu_field_soil_plow,spu_guide_sum_lime,spu_guide_sum_mulch") -check("FG17 lager valgt silo", partKeys(lineFor(FG.summary(manual), "store")), "=Silo B, 1,2 km") -check("FG17 lager marksilo", partKeys(lineFor(FG.summary(useSilo), "store")), "spu_guide_sum_fieldSilo") -local sumHay = FG.summary(FG.choose(FG.choose(FG.open(field(1), ctx(GRASS)), "product", true), "rake", true)) -check("FG17 hø + river", partKeys(lineFor(sumHay, "cropChoice")), "spu_field_plan_hay,spu_guide_sum_rake") -local sumNoCare = FG.summary(FG.choose(FG.choose(FG.choose(g, "careAuto", false), "fertilize", false), "weed", false)) -check("FG17 ingen pleje", partKeys(lineFor(sumNoCare, "care")), "spu_guide_sum_noCare") -local sumSow = FG.summary(FG.choose(g, "sow", true)) -check("FG17 så igen", partKeys(lineFor(sumSow, "crop")), "spu_guide_crop_keep,spu_guide_sum_sowAgain") - --- ---- oversigts-trinnet: links pr. linje ---- -local summaryRows = FG.rows(G.new(g.def, g.draft, g.context, {step = "summary"})) -check("FG18 oversigt: link pr. linje", countType(summaryRows, "link"), 6) -check("FG18 oversigt: link-id", findRow(summaryRows, "jump").value, "crop") - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFieldJobs.lua b/tests/test_adFieldJobs.lua deleted file mode 100644 index f3c12d1..0000000 --- a/tests/test_adFieldJobs.lua +++ /dev/null @@ -1,222 +0,0 @@ --- Kør: luajit tests/test_adFieldJobs.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adFieldModel.lua") -dofile("FS25_ADSmartPickup/adFieldJobs.lua") -local J = ADFieldJobs -local M = ADFieldModel -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end -local info = {id = 109, markerId = 5, mowable = true, readyRatio = 1} - --- J1: klar auto-mark med markør -> start; uden markør -> vent -local auto = M.onReadiness(M.newField(109), 1); auto.plan.mode = "auto" -check("J1 start", J.decide(auto, info, {}, 0).action, "start") -check("J1 uden markør", J.decide(auto, {id = 109, mowable = true}, {}, 0).action, "wait") - --- J2: arbejdende trin uden sæt -> send rolle; alle sæt færdige -> næste trin -local mowing = M.start(auto) -local wanted = J.decide(mowing, info, {}, 0) -check("J2 send slåmaskine", wanted.action, "dispatch"); check("J2 rolle", wanted.role, "mower") -check("J2 sæt i gang -> ingenting", J.decide(mowing, info, {{phase = "working"}}, 0).action, "none") -local mowedInfo = {id = 109, markerId = 5, mowable = true, readyRatio = 0} -check("J2 færdig -> videre", J.decide(mowing, mowedInfo, {{phase = "finished"}}, 0).action, "advance") --- J3: sæt frigivet (spilleren tog det) tæller ikke som færdigt -check("J3 frigivet -> send nyt", J.decide(mowing, info, {{phase = "released"}}, 0).action, "dispatch") --- J4: slåning færdig men marken stadig klar -> ikke videre (afbrudt) -check("J4 afbrudt slåning", J.decide(mowing, {id = 109, markerId = 5, mowable = true, readyRatio = 0.8}, {{phase = "finished"}}, 0).action, "dispatch") --- J5: opsamling efter genindlæsning uden sæt -> send læssevogn -local collecting = M.advance(mowing) -check("J5 genoptag opsamling", J.decide(collecting, info, {}, 0).role, "collector") -check("J5 to vogne maks", J.decide(collecting, info, {{phase = "working"}, {phase = "driving"}}, 0).action, "none") - --- J6: Courseplay efterlader et hjørne -> efter 2 færdige slå-ture går marken videre alligevel -local standing = {id = 109, markerId = 5, mowable = true, readyRatio = 0.8} -check("J6 én tur -> slå igen", J.decide(mowing, standing, {{phase = "finished"}}, 0).action, "dispatch") -check("J6 to ture -> videre", J.decide(mowing, standing, {{phase = "finished"}, {phase = "finished"}}, 0).action, "advance") - --- J7: slået mark uden sæt (fx sluppet undervejs) -> videre til opsamling, ikke en ny slå-tur -check("J7 slået uden sæt -> videre", J.decide(mowing, {id = 109, markerId = 5, mowable = true, readyRatio = 0.1}, {}, 0).action, "advance") -check("J7 stående uden sæt -> slå", J.decide(mowing, info, {}, 0).action, "dispatch") -check("J7 slået men sæt i gang -> vent", J.decide(mowing, {id = 109, markerId = 5, mowable = true, readyRatio = 0.1}, {{phase = "working"}}, 0).action, "none") - --- J8: høst — høster først (hovedrolle), aflæsningsvogne kun mens høsteren arbejder -local harvestInfo = {id = 50, markerId = 7, kind = "harvest", readyRatio = 1, fruitIndex = 3} -local harvesting = M.start(M.onReadiness(M.newField(50), 1), "harvest", {}) -local d = J.decide(harvesting, harvestInfo, {}, 0) -check("J8 send høster", d.role, "harvester"); check("J8 ikke hjælper", d.helper, false) -local withHarvester = {{phase = "working", stage = "harvesting", helper = false}} -local dHelper = J.decide(harvesting, harvestInfo, withHarvester, 0) -check("J8 så aflæsningsvogn", dHelper.role, "unloader"); check("J8 hjælper", dHelper.helper, true) -check("J8 vogn på plads -> intet", J.decide(harvesting, harvestInfo, {withHarvester[1], {phase = "working", stage = "harvesting", helper = true}}, 0).action, "none") --- manuel transport: høsteren venter på spilleren -> ingen høster mere og endnu ingen tømmevogn -local awaiting = {{phase = "awaitingPlayer", stage = "harvesting", helper = false}} -check("J8b venter på spilleren -> intet (ingen ny høster, ingen tømmevogn)", J.decide(harvesting, harvestInfo, awaiting, 0).action, "none") --- høsteren færdig, afgrøden væk (marken er nu bar) -> videre, selv om vognen stadig kører -local afterInfo = {id = 50, markerId = 7, kind = "bare", readyRatio = 0} -check("J8 høst færdig -> videre", J.decide(harvesting, afterInfo, {{phase = "finished", stage = "harvesting"}, {phase = "working", stage = "harvesting", helper = true}}, 0).action, "advance") --- sæt fra et tidligere trin (vogn på vej hjem) tæller ikke på det nye trin -local cultivating = M.advance(harvesting, {}) -check("J8 gammel vogn tæller ikke", J.decide(cultivating, afterInfo, {{phase = "working", stage = "harvesting", helper = true}}, 0).role, "cultivator") - --- J9: multi-tool — hele holdet sendes samlet; mens nogen kører, sendes ingen nye hovedsæt -local multiMow = M.with(mowing, {plan = {rigs = 3}}) -local dMulti = J.decide(multiMow, info, {}, 0) -check("J9 tre slåmaskiner", dMulti.count, 3) -check("J9 én kører stadig -> ingen nye", J.decide(multiMow, info, {{phase = "working"}, {phase = "finished"}}, 0).action, "none") - --- J10: pleje under vækst — Auto starter, Manuel viser behovet -local growingInfo = {id = 60, markerId = 8, kind = "harvest", growing = true, fert = 0, fertMax = 2, lime = 2, weed = 0, readyRatio = 0.2} -local autoField = M.with(M.newField(60), {plan = {mode = "auto"}}) -local care = J.decide(autoField, growingInfo, {}, 0) -check("J10 auto -> pleje", care.action, "care"); check("J10 gødning", care.stage, "fertilizing") -local manualCare = J.decide(M.newField(60), growingInfo, {}, 0) -check("J10 manuel -> intet, men behov vist", manualCare.action, "none"); check("J10 behov", manualCare.care, "fertilizing") -check("J10 Start nu -> pleje", J.decide(M.with(M.newField(60), {startRequested = true}), growingInfo, {}, 0).action, "care") -check("J10 uden markør -> vent", J.decide(autoField, {id = 60, kind = "harvest", growing = true, fert = 0, fertMax = 2}, {}, 0).action, "wait") - --- J11: regn — vejrfølsomme trin sendes ikke ud (kørende sæt fortsætter); klargøring/pleje kører videre -local rain = {raining = true} -local dRain = J.decide(mowing, info, {}, 0, rain) -check("J11 slåning venter i regn", dRain.action, "wait"); check("J11 grund", dRain.reason, "rain") -check("J11 kørende slåmaskine fortsætter", J.decide(mowing, info, {{phase = "working"}}, 0, rain).action, "none") -check("J11 høst venter", J.decide(harvesting, harvestInfo, {}, 0, rain).reason, "rain") -check("J11 kultivering kører i regn", J.decide(cultivating, afterInfo, {}, 0, rain).role, "cultivator") -check("J11 start af høst venter", J.decide(M.with(M.onReadiness(M.newField(51), 1), {startRequested = true, kind = "harvest"}), harvestInfo, {}, 0, rain).reason, "rain") -local silageCollect = M.advance(mowing) -check("J11 ensilage-opsamling kører i regn", J.decide(silageCollect, info, {}, 0, rain).action, "dispatch") -local hayCollect = M.with(silageCollect, {plan = {product = "hay"}}) -check("J11 hø-opsamling venter", J.decide(hayCollect, info, {}, 0, rain).reason, "rain") -check("J11 tørvejr", J.decide(mowing, info, {}, 0, {raining = false}).action, "dispatch") - --- J12: bar mark med mål-afgrøde klargøres straks — kun selve såningen venter på såtiden -local bareInfo = {id = 90, markerId = 9, kind = "bare", readyRatio = 0, needs = {}} -local bareTarget = M.with(M.newField(90), {plan = {targetFruit = "WHEAT", mode = "auto"}}) -local canSow = {plantable = function(name) return name == "WHEAT" end} -check("J12 bar + såtid -> start", J.decide(bareTarget, bareInfo, {}, 0, canSow).action, "start") -check("J12 bar + ikke såtid -> start alligevel (klargøring)", J.decide(bareTarget, bareInfo, {}, 0, {plantable = function() return false end}).action, "start") -check("J12 bar uden markør -> vent", J.decide(bareTarget, {id = 90, kind = "bare"}, {}, 0, canSow).action, "wait") -check("J12 voksende mark med mål -> lad den vokse", J.decide(bareTarget, {id = 90, markerId = 9, kind = "harvest", growing = true, readyRatio = 0.3}, {}, 0, canSow).action, "none") - --- J13: afbrudt slåning efter genindlæsning (in-game 27/9 F90): genvækst i mellemhøjde -> vent; hjørne efter en tur -> slå straks -local regrowing = {id = 90, markerId = 9, mowable = true, readyRatio = 0.5, immatureRatio = 0.5} -local dRegrow = J.decide(mowing, regrowing, {}, 0) -check("J13 genvækst -> vent", dRegrow.action, "wait"); check("J13 grund", dRegrow.reason, "regrowing") -check("J13 genvækst men tur færdig -> slå hjørnet", J.decide(mowing, regrowing, {{phase = "finished"}}, 0).action, "dispatch") -check("J13 halvt slået uden genvækst -> slå resten", J.decide(mowing, {id = 90, markerId = 9, mowable = true, readyRatio = 0.6, immatureRatio = 0}, {}, 0).action, "dispatch") - --- J14: kultivering på en mark med uhøstede kartofler (in-game 27/9 F16) -> tilbage til klar; rest efter høst -> ikke -local cultivatingField = M.with(auto, {stage = "cultivating", kind = "harvest"}) -check("J14 uhøstet -> spol tilbage", J.decide(cultivatingField, {id = 16, markerId = 9, kind = "harvest", phase = "ready", readyRatio = 1}, {}, 0).action, "rewind") -check("J14 høstrest 30 % -> kultiver", J.decide(cultivatingField, {id = 16, markerId = 9, kind = "harvest", phase = "ready", readyRatio = 0.3}, {}, 0).action ~= "rewind", true) -check("J14 stub -> kultiver", J.decide(cultivatingField, {id = 16, markerId = 9, kind = "bare", phase = "cut", readyRatio = 0}, {}, 0).action ~= "rewind", true) - - --- resumeField: efter genindlæsning stopper spillet alle arbejdere; en CP-maskine der står PÅ en mark hvis --- aktuelle trin passer til maskinens rolle, meldes ind på den mark igen (in-game 27/9) -do - local square = {{x = 0, z = 0}, {x = 100, z = 0}, {x = 100, z = 100}, {x = 0, z = 100}} - local fields = { - {id = 90, stage = "collecting", verts = square}, - {id = 111, stage = "harvesting", verts = {{x = 200, z = 0}, {x = 300, z = 0}, {x = 300, z = 100}, {x = 200, z = 100}}}, - } - check("RF høster på F111", ADFieldJobs.resumeField(250, 50, "harvester", fields), 111) - check("RF læssevogn på F90", ADFieldJobs.resumeField(50, 50, "collector", fields), 90) - check("RF forkert rolle til trinnet -> nil", ADFieldJobs.resumeField(250, 50, "collector", fields), nil) - check("RF uden for alle marker -> nil", ADFieldJobs.resumeField(150, 50, "harvester", fields), nil) - check("RF mark uden omrids -> nil", ADFieldJobs.resumeField(50, 50, "collector", {{id = 1, stage = "collecting", verts = {}}}), nil) - check("RF på kanten tæller som indenfor", ADFieldJobs.resumeField(0.5, 50, "collector", fields), 90) -end - - --- startAt: hvor Courseplay skal starte i kurset (CP's standard er "first" = forfra, også på en halvt høstet mark; --- in-game 27/9: mejetærskeren startede F111 forfra) -check("SA eget kursus med husket waypoint -> last", ADFieldJobs.startAtFor({reuse = true, remembered = true, stage = "harvesting", standingRatio = 0.4}), "last") -check("SA halvt høstet -> nearest", ADFieldJobs.startAtFor({stage = "harvesting", standingRatio = 0.4}), "nearest") -check("SA halvt slået -> nearest", ADFieldJobs.startAtFor({stage = "mowing", standingRatio = 0.5}), "nearest") -check("SA urørt mark -> first", ADFieldJobs.startAtFor({stage = "harvesting", standingRatio = 1}), "first") -check("SA næsten urørt (97 %) -> first", ADFieldJobs.startAtFor({stage = "harvesting", standingRatio = 0.97}), "first") -check("SA ukendt fremdrift -> first", ADFieldJobs.startAtFor({stage = "harvesting"}), "first") -check("SA klargøring (ingen afgrøde at måle) -> first", ADFieldJobs.startAtFor({stage = "cultivating", standingRatio = 0.3}), "first") -check("SA husket waypoint uden eget kursus -> ikke last", ADFieldJobs.startAtFor({reuse = false, remembered = true, stage = "cultivating"}), "first") - - --- shouldResume: kun maskiner der stadig er midt i jobbet genstartes (in-game 27/9: to ROPA'er med et husket --- waypoint fra et gammelt job kørte ud ad vejene mod det gamle kursus) -check("SR står på en mark i gang med sit trin -> ja", ADFieldJobs.shouldResume({fieldId = 111, distance = 400}), true) -check("SR lige ved sit huskede waypoint -> ja", ADFieldJobs.shouldResume({distance = 12}), true) -check("SR langt fra waypointet og ingen mark -> nej", ADFieldJobs.shouldResume({distance = 850}), false) -check("SR ukendt afstand og ingen mark -> nej", ADFieldJobs.shouldResume({}), false) - - --- mayDispatch: markarbejdet sender højst én maskine ud pr. DISPATCH_GAP_MS på tværs af alle marker --- (in-game 27/9: 6 maskiner kørte fra parkeringen i samme sekund efter Start alle) -check("MD første udsendelse", ADFieldJobs.mayDispatch(5000, nil), true) -check("MD for tæt på forrige", ADFieldJobs.mayDispatch(5000 + ADFieldJobs.DISPATCH_GAP_MS - 1, 5000), false) -check("MD efter mellemrummet", ADFieldJobs.mayDispatch(5000 + ADFieldJobs.DISPATCH_GAP_MS, 5000), true) - - --- halvt høstet mark (in-game 27/9 F111): høsten er ikke færdig før næsten intet står; en mark i klargøring med --- stående afgrøde spoles tilbage -do - local halfInfo = {id = 111, markerId = 5, kind = "harvest", phase = "ready", readyRatio = 0.45, standingRatio = 0.45, fruitIndex = 1} - check("HH halvt høstet uden høster -> send høster (ikke videre)", J.decide(harvesting, halfInfo, {}, 0).action, "dispatch") - local doneInfo = {id = 111, markerId = 5, kind = "harvest", phase = "ready", readyRatio = 0.02, standingRatio = 0.02, fruitIndex = 1} - check("HH næsten intet står -> videre", J.decide(harvesting, doneInfo, {}, 0).action, "advance") - local cultivating = M.with(harvesting, {stage = "cultivating"}) - check("HH klargøring med halv mark stående -> spol tilbage", J.decide(cultivating, halfInfo, {}, 0).action, "rewind") -end - - --- startTimedOut: CP må beregne kursus i op til 5 min (in-game 27/9 F90: 140 ha tog over 60 s, moden opgav midt i) -check("ST start uden beregning > 60 s -> opgiv", ADFieldJobs.startTimedOut(61000, false), true) -check("ST beregner stadig efter 90 s -> vent", ADFieldJobs.startTimedOut(90000, true), false) -check("ST beregner over 5 min -> opgiv", ADFieldJobs.startTimedOut(301000, true), true) -check("ST under 60 s -> vent", ADFieldJobs.startTimedOut(30000, false), false) - - --- shouldEmptyHarvester: høsten er færdig, men høsteren har korn i tanken og står ved marken -> tøm først --- (in-game 27/9 F15: marken gik til halm med fuld tank, ingen tømmevogn kom) -check("EH korn i tanken ved marken -> tøm", ADFieldJobs.shouldEmptyHarvester({fill = 12000, near = true}), true) -check("EH tom tank -> videre", ADFieldJobs.shouldEmptyHarvester({fill = 0, near = true}), false) -check("EH langt væk -> videre", ADFieldJobs.shouldEmptyHarvester({fill = 12000, near = false}), false) -check("EH optaget -> videre", ADFieldJobs.shouldEmptyHarvester({fill = 12000, near = true, busy = true}), false) -check("EH spiller i -> videre", ADFieldJobs.shouldEmptyHarvester({fill = 12000, near = true, controlled = true}), false) -check("EH allerede forsøgt -> videre", ADFieldJobs.shouldEmptyHarvester({fill = 12000, near = true, tried = true}), false) - - --- nearestPoint: CP bruger markens punkt som kursets startsted — tag det nærmest traktoren (in-game 27/9 F17: første --- punkt lå 280 m væk i den anden ende, og CP kørte gennem skoven) -do - local pts = {{x = 1600, z = -1839}, {x = 1350, z = -1740}, {x = 1500, z = -1800}} - local p = ADFieldJobs.nearestPoint(pts, 1340, -1733) - check("NP nærmest traktoren", p.x .. "," .. p.z, "1350,-1740") - check("NP uden punkter -> nil", ADFieldJobs.nearestPoint({}, 0, 0), nil) - check("NP uden position -> første", ADFieldJobs.nearestPoint(pts, nil, nil).x, 1600) -end - --- pulje: en traktor der henter et redskab tæller som i gang (ellers sendes endnu en ud) -check("pulje: henter -> ingenting", J.decide(mowing, info, {{phase = "fetching"}}, 0).action, "none") --- JP: klargøring der allerede er gjort (fx efter genindlæsning) går videre uden at sende maskinen ud igen --- (in-game 28/9: F17 blev kultiveret 3 gange — opgaver gemmes ikke, så "færdig" blev aldrig set) -local cultField = M.with(M.newField(17), {stage = "cultivating", kind = "bare", plan = {mode = "auto"}}) -local cultInfo = {id = 17, markerId = 5, kind = "bare", readyRatio = 0, needs = {}} -cultInfo.prepared = 0.98 -check("JP kultiveret -> videre", J.decide(cultField, cultInfo, {}, 0).action, "advance") -cultInfo.prepared = 0.4 -check("JP halvt kultiveret -> send maskine", J.decide(cultField, cultInfo, {}, 0).action, "dispatch") -cultInfo.prepared = nil -check("JP ukendt jord -> som før (send)", J.decide(cultField, cultInfo, {}, 0).action, "dispatch") -cultInfo.prepared = 0.98 -check("JP kultivator i gang -> vent på den", J.decide(cultField, cultInfo, {{phase = "working", stage = "cultivating"}}, 0).action, "none") -local plowField = M.with(cultField, {stage = "plowing"}) -check("JP kultiveret er ikke pløjet -> send plov", J.decide(plowField, cultInfo, {}, 0).action, "dispatch") -cultInfo.plowed = 0.97 -check("JP pløjet -> videre", J.decide(plowField, cultInfo, {}, 0).action, "advance") -local sowField = M.with(cultField, {stage = "sowing"}) -check("JP såning berøres ikke af klargjort jord", J.decide(sowField, cultInfo, {}, 0).action ~= "advance", true) -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFieldModel.lua b/tests/test_adFieldModel.lua deleted file mode 100644 index 66bf4cc..0000000 --- a/tests/test_adFieldModel.lua +++ /dev/null @@ -1,324 +0,0 @@ --- Kør: luajit tests/test_adFieldModel.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adFieldModel.lua") -local M = ADFieldModel -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - --- M1: ny mark = venter, manuel ensilage uden riv -local f = M.newField(109) -check("M1 stage", f.stage, "idle") -check("M1 mode", f.plan.mode, "manual") -check("M1 product auto", f.plan.product, "auto") -check("M1 auto = ensilage på slåafgrøde", M.product(f.plan, "mowable"), "silage") -check("M1 auto = høst ellers", M.product(f.plan, "harvest"), "harvest") -check("M1 rake", f.plan.rake, false) - --- M2: klar ved >= 90 % -check("M2 89 % ikke klar", M.onReadiness(f, 0.89).stage, "idle") -local ready = M.onReadiness(f, 0.9) -check("M2 90 % klar", ready.stage, "ready") -check("M2 immutabel", f.stage, "idle") - --- M3: klar mark slået af spilleren -> tilbage til venter -check("M3 slået manuelt", M.onReadiness(ready, 0.3).stage, "idle") -check("M3 lidt under 90 % forbliver klar", M.onReadiness(ready, 0.8).stage, "ready") - --- M4: trin pr. plan -check("M4 ensilage", table.concat(M.workStages({product = "silage"}), ","), "mowing,collecting") -check("M4 ensilage m. riv", table.concat(M.workStages({product = "silage", rake = true}), ","), "mowing,raking,collecting") -check("M4 hø", table.concat(M.workStages({product = "hay", rake = false}), ","), "mowing,tedding,collecting") -check("M4 hø m. riv", table.concat(M.workStages({product = "hay", rake = true}), ","), "mowing,tedding,raking,collecting") - --- M5: start: auto starter selv, manuel kun på forespørgsel -check("M5 manuel starter ikke", M.shouldStart(ready), false) -local auto = M.onReadiness(M.newField(5), 1) -auto.plan.mode = "auto" -check("M5 auto starter", M.shouldStart(auto), true) -local requested = M.newField(6); requested.stage = "ready"; requested.startRequested = true -check("M5 Start nu", M.shouldStart(requested), true) -local started = M.start(ready) -check("M5 start -> slås", started.stage, "mowing") -check("M5 start rydder forespørgsel", started.startRequested, false) -local oldMow = M.with(ready, {mowVehicleId = "old"}) -check("M5 start glemmer gammel slåmaskine", M.start(oldMow).mowVehicleId, nil) - --- M6: videre gennem trinene; færdig mark bliver klar igen når den er vokset -local hay = M.start(M.onReadiness(M.newField(7), 1)); hay.plan.product = "hay" -hay = M.advance(hay); check("M6 hø -> vendes", hay.stage, "tedding") -hay = M.advance(hay); check("M6 -> samles op", hay.stage, "collecting") -hay = M.advance(hay); check("M6 -> færdig", hay.stage, "done") -check("M6 færdig forbliver ved lav andel", M.onReadiness(hay, 0.1).stage, "done") -check("M6 færdig -> klar når vokset", M.onReadiness(hay, 0.95).stage, "ready") -check("M6 arbejdende mark ignorerer andel", M.onReadiness(started, 0).stage, "mowing") -check("M6 isWorking", M.isWorking(started), true) -check("M6 stop -> klar", M.stop(started).stage, "ready") - --- M7: roller, maks sæt, bredde -check("M7 rolle slå", M.roleFor("mowing"), "mower") -check("M7 rolle saml", M.roleFor("collecting"), "collector") -check("M7 rolle klar = ingen", M.roleFor("ready"), nil) -check("M7 maks læssevogne = planens sæt", M.maxRigs("collecting", {rigs = 2}), 2) -check("M7 maks slå", M.maxRigs("mowing"), 1) -local okWide, whyWide = M.checkWidth("mower", 60, nil) -check("M7 over 50 m", okWide, false); check("M7 grund", whyWide, "tooWide") -check("M7 40,8 m slå ok", (M.checkWidth("mower", 40.8, nil)), true) --- M7b: bredde under 1,5 m er ikke læst rigtigt (spreder gav 0,5 m -> CP-kursus hver halve meter på 72 ha --- -> spillet frøs, in-game 26/9) — sendes aldrig ud -local okTiny, whyTiny = M.checkWidth("fertilizer", 0.5, nil) -check("M7b 0,5 m afvises", okTiny, false) -check("M7b grund", whyTiny, "widthUnknown") -check("M7b 3 m strigle ok", (M.checkWidth("weeder", 3, nil)), true) -check("M7b aflæsningsvogn uden bredde ok", (M.checkWidth("unloader", 0, nil)), true) -check("M7b ballesamler uden bredde ok", (M.checkWidth("baleCollector", 0, nil)), true) -local okNarrow, whyNarrow, args = M.checkWidth("collector", 20, 40.8) -check("M7 for smal", okNarrow, false); check("M7 grund smal", whyNarrow, "tooNarrow"); check("M7 args", args[2], 40.8) -check("M7 lige bred ok (tolerance)", (M.checkWidth("collector", 40.76, 40.8)), true) -check("M7 ukendt slåbredde ok", (M.checkWidth("tedder", 10, nil)), true) - --- wantsRig: kun arbejdende trin, under maks, efter pause -check("M7 wantsRig slå", M.wantsRig(started, 0, 0).role, "mower") -check("M7 wantsRig fuld", M.wantsRig(started, 1, 0), nil) -local paused = M.start(M.onReadiness(M.newField(8), 1)); paused.retryAt = 1000 -check("M7 wantsRig i pause", M.wantsRig(paused, 0, 500), nil) -check("M7 wantsRig efter pause", M.wantsRig(paused, 0, 1000).role, "mower") -check("M7 wantsRig klar = nil", M.wantsRig(ready, 0, 0), nil) - --- M8: gem/indlæs bevarer trin, plan og slåbredde -local saved = M.advance(M.start(M.onReadiness(M.newField(109), 1))); saved.mowWidth = 40.8; saved.mowVehicleId = "vehicleBigM" -saved.plan = {mode = "auto", product = "hay", rake = true, storeBuildingIds = {"b12", "b7"}, rigIds = {"v1", "v2"}} -local back = M.fromRecord(M.toRecord(saved)) -check("M8 id", back.id, 109) -check("M8 stage", back.stage, saved.stage) -check("M8 mode", back.plan.mode, "auto") -check("M8 product", back.plan.product, "hay") -check("M8 rake", back.plan.rake, true) -check("M8 lager", table.concat(back.plan.storeBuildingIds, " "), "b12 b7") -check("M8 faste maskiner", table.concat(back.plan.rigIds, " "), "v1 v2") -check("M8 slåbredde", back.mowWidth, 40.8) -check("M8 slåmaskine", back.mowVehicleId, "vehicleBigM") -check("M8 ingen slåmaskine", M.fromRecord({id = 4, stage = "idle"}).mowVehicleId, nil) -check("M8 ukendt trin -> venter", M.fromRecord({id = 3, stage = "bogus"}).stage, "idle") - --- M9: with() kopierer og fletter planen -local base = M.start(M.onReadiness(M.newField(1), 1)) -local noted = M.with(base, {reason = {text = "x"}, retryAt = 5, plan = {product = "hay"}}) -check("M9 grund", noted.reason.text, "x"); check("M9 pause", noted.retryAt, 5) -check("M9 plan flettet", noted.plan.product, "hay"); check("M9 mode bevaret", noted.plan.mode, "manual") -check("M9 trin bevaret", noted.stage, "mowing"); check("M9 original urørt", base.plan.product, "auto") - --- M10: Start nu på en mark der ikke er helt klar -> klar; arbejdende mark røres ikke -check("M10 venter -> klar", M.forceReady(M.newField(2)).stage, "ready") -check("M10 arbejdende uændret", M.forceReady(base).stage, "mowing") - --- M11: krævet bredde og kursus-kilde pr. rolle (smalle standardmaskiner, 25/9) -local cut = M.with(M.newField(3), {mowWidth = 40.8}) -check("M11 vender kræver ingen bredde", M.requiredWidth(cut, "tedder"), nil) -check("M11 river kræver ingen bredde", M.requiredWidth(cut, "rake"), nil) -check("M11 læssevogn uden riv = slåbredde", M.requiredWidth(cut, "collector"), 40.8) -local raked = M.with(cut, {plan = {rake = true}}) -check("M11 læssevogn efter riv = ingen krav", M.requiredWidth(raked, "collector"), nil) -check("M11 vender eget kursus", M.courseSource(cut, "tedder"), nil) -check("M11 river eget kursus", M.courseSource(raked, "rake"), nil) -check("M11 læssevogn uden riv -> slåkursus", M.courseSource(cut, "collector"), "mower") -check("M11 læssevogn efter riv -> rivekursus", M.courseSource(raked, "collector"), "rake") -check("M11 slåmaskine eget kursus", M.courseSource(cut, "mower"), nil) --- gem/indlæs rivemaskine -local withRake = M.with(raked, {rakeVehicleId = "vehicleRake"}) -check("M11 rivemaskine gemt", M.fromRecord(M.toRecord(withRake)).rakeVehicleId, "vehicleRake") -check("M11 start glemmer rivemaskine", M.start(M.with(M.onReadiness(M.newField(9), 1), {rakeVehicleId = "x"})).rakeVehicleId, nil) - --- M12: sideriver — læssevognens CP-forskydning = egen forskydning − skårets forskydning (venstre positiv), --- holdt inden for Courseplays ±10 m; skårets forskydning gemmes pr. mark -check("M12 midterrive", M.collectorOffset(0, 0), 0) -check("M12 skår 3 m til venstre", M.collectorOffset(0, 3), -3) -check("M12 vogn med egen forskydning", M.collectorOffset(0.5, -2), 2.5) -check("M12 holdes inden for 10 m", M.collectorOffset(0, 14), -10) -check("M12 ukendt skår -> egen forskydning", M.collectorOffset(0.4, nil), 0.4) -local side = M.with(M.newField(11), {windrowOffset = 3.2}) -check("M12 skår gemt", M.fromRecord(M.toRecord(side)).windrowOffset, 3.2) -check("M12 intet skår gemt", M.fromRecord(M.toRecord(M.newField(12))).windrowOffset, nil) -check("M12 start glemmer skår", M.start(M.with(M.onReadiness(M.newField(13), 1), {windrowOffset = 1})).windrowOffset, nil) - --- M13: gæringssilo = produktion der laver markens skår om til ensilage (FermentingSiloPack m.fl.) -local GRASS_W, DRY_W, SILAGE, CHAFF, BREAD = 10, 11, 20, 30, 40 -local fermenter = {{inputs = {{type = CHAFF}}, outputs = {{type = SILAGE}}}, {inputs = {{type = GRASS_W}}, outputs = {{type = SILAGE}}}} -check("M13 græs -> ensilage", M.isFermenter(fermenter, GRASS_W, SILAGE), true) -check("M13 hø ikke i denne", M.isFermenter(fermenter, DRY_W, SILAGE), false) -check("M13 bageri ikke", M.isFermenter({{inputs = {{type = GRASS_W}}, outputs = {{type = BREAD}}}}, GRASS_W, SILAGE), false) -check("M13 ingen produktioner", M.isFermenter(nil, GRASS_W, SILAGE), false) -check("M13 ukendt ensilage-type", M.isFermenter(fermenter, GRASS_W, nil), false) - --- M14: høstkæde + klargøring efter behov -local plan = {mode = "manual", product = "auto", rake = false, rigs = 1, unloaders = 1, mulch = false, lime = true, - soil = "auto", sow = true, sowFruit = "", roll = true, fertilize = true, weed = true, storeBuildingIds = {}} -check("M14 høst + kultivér + så + tromle", table.concat(M.workStages(plan, "harvest", {}), ","), "harvesting,cultivating,sowing,rolling") -check("M14 kalk + pløj når jorden trænger", table.concat(M.workStages(plan, "harvest", {lime = true, plow = true}), ","), "harvesting,liming,plowing,sowing,rolling") -local noSoil = {soil = "none", sow = false, lime = false, mulch = true} -check("M14 kun stubbehakning", table.concat(M.workStages(noSoil, "harvest", {}), ","), "harvesting,mulching") -check("M14 bar mark = kun klargøring", table.concat(M.workStages(plan, "bare", {plow = true}), ","), "plowing,sowing,rolling") -check("M14 tvunget pløj", table.concat(M.prepStages({soil = "plow"}, {}), ","), "plowing") -local harvestField = M.start(M.with(M.newField(20), {plan = {sow = true, soil = "cultivate"}}), "harvest", {}) -check("M14 start = høst", harvestField.stage, "harvesting") -check("M14 husker art", harvestField.kind, "harvest") -local afterHarvest = M.advance(harvestField, {}) -check("M14 høst -> kultivér", afterHarvest.stage, "cultivating") -check("M14 -> så", M.advance(afterHarvest, {}).stage, "sowing") -check("M14 så -> færdig", M.advance(M.advance(afterHarvest, {}), {}).stage, "done") -check("M14 kalk springes over når jorden er kalket", M.advance(M.with(harvestField, {plan = {lime = true}}), {lime = false}).stage, "cultivating") -check("M14 tom kæde -> færdig", M.start(M.with(M.newField(21), {plan = {soil = "none", sow = false, lime = false}}), "bare", {}).stage, "done") - --- M15: hjælpere under høst og multi-tool-baner -check("M15 aflæsningsvogn efter høsteren", M.wantsRig(harvestField, {primary = 1, helpers = 0}, 0).role, "unloader") -check("M15 hjælper-flag", M.wantsRig(harvestField, {primary = 1, helpers = 0}, 0).helper, true) -check("M15 alle vogne på plads -> intet", M.wantsRig(harvestField, {primary = 1, helpers = 1}, 0), nil) -local multi = M.with(harvestField, {plan = {rigs = 3, unloaders = 2}}) -check("M15 multi: 3 høstere samlet", M.wantsRig(multi, {primary = 0, helpers = 0}, 0).count, 3) -check("M15 multi: 2 vogne", M.maxHelpers("harvesting", multi.plan), 2) -check("M15 ingen hjælper ved slåning", M.maxHelpers("mowing", multi.plan), 0) -check("M15 baner 2", table.concat(M.lanePositions(2), ","), "-1,1") -check("M15 baner 3", table.concat(M.lanePositions(3), ","), "-1,0,1") -check("M15 baner 4", table.concat(M.lanePositions(4), ","), "-2,-1,1,2") -check("M15 baner 5", table.concat(M.lanePositions(5), ","), "-2,-1,0,1,2") -check("M15 over 5 -> 5", #M.lanePositions(9), 5) -check("M15 maxRigs holdes 1-5", M.maxRigs("mowing", {rigs = 7}), 5) - --- M16: pleje under vækst -local growing = {kind = "harvest", growing = true, fert = 1, fertMax = 2, lime = 2, weed = 0} -local idleField = M.newField(30) -check("M16 gødning", M.careNeed(idleField, growing), "fertilizing") -check("M16 ukrudt før gødning", M.careNeed(idleField, {kind = "harvest", growing = true, fert = 1, fertMax = 2, lime = 2, weed = 3}), "weeding") -check("M16 græs ukrudtsbehandles ikke", M.careNeed(idleField, {kind = "mowable", growing = true, fert = 2, fertMax = 2, lime = 2, weed = 3}), nil) -check("M16 kalk", M.careNeed(idleField, {kind = "harvest", growing = true, fert = 2, fertMax = 2, lime = 0, weed = 0}), "careLiming") -check("M16 intet behov", M.careNeed(idleField, {kind = "harvest", growing = true, fert = 2, fertMax = 2, lime = 1, weed = 1}), nil) -check("M16 ikke voksende", M.careNeed(idleField, {kind = "harvest", growing = false, fert = 0, fertMax = 2}), nil) -check("M16 gødning slået fra", M.careNeed(M.with(idleField, {plan = {fertilize = false}}), growing), nil) -check("M16 arbejdende mark", M.careNeed(harvestField, growing), nil) -local cared = M.startCare(idleField, "fertilizing") -check("M16 pleje-trin", cared.stage, "fertilizing") -check("M16 rolle", M.roleFor(cared.stage), "fertilizer") -check("M16 kalk-pleje rolle", M.roleFor("careLiming"), "limer") -check("M16 pleje -> idle", M.advance(cared, {}).stage, "idle") -local twice = M.startCare(M.advance(cared, {}), "fertilizing") -check("M16 højst 2 gange", M.careNeed(M.advance(twice, {}), growing), nil) -check("M16 ny cyklus nulstiller", M.careNeed(M.advance(M.start(M.advance(twice, {}), "harvest", {}), {}), growing) ~= "x", true) -check("M16 start nulstiller pleje-tæller", next(M.start(twice, "harvest", {}).careRuns), nil) -check("M16 stop af pleje -> idle", M.stop(cared).stage, "idle") -check("M16 stop af høst -> klar", M.stop(harvestField).stage, "ready") - --- M17: såafgrøde og gem/indlæs af den udvidede plan -check("M17 så samme afgrøde", M.sowFruit(M.with(M.newField(40), {lastFruit = "WHEAT"})), "WHEAT") -check("M17 planens afgrøde vinder", M.sowFruit(M.with(M.newField(40), {lastFruit = "WHEAT", plan = {sowFruit = "CANOLA"}})), "CANOLA") -local full = M.with(M.startCare(M.newField(41), "weeding"), {kind = "harvest", lastFruit = "BARLEY", - plan = {rigs = 3, unloaders = 2, mulch = true, lime = false, soil = "plow", sow = true, sowFruit = "WHEAT", roll = true, fertilize = false, weed = false, product = "harvest"}}) -local round = M.fromRecord(M.toRecord(full)) -check("M17 trin", round.stage, "weeding") -check("M17 sæt", round.plan.rigs, 3) -check("M17 vogne", round.plan.unloaders, 2) -check("M17 stub", round.plan.mulch, true) -check("M17 kalk fra", round.plan.lime, false) -check("M17 jord", round.plan.soil, "plow") -check("M17 så", round.plan.sow, true) -check("M17 såafgrøde", round.plan.sowFruit, "WHEAT") -check("M17 tromle", round.plan.roll, true) -check("M17 gødning fra", round.plan.fertilize, false) -check("M17 ukrudt fra", round.plan.weed, false) -check("M17 art", round.kind, "harvest") -check("M17 sidste afgrøde", round.lastFruit, "BARLEY") -check("M17 pleje-tæller", round.careRuns.weeding, 1) -check("M17 produkt", round.plan.product, "harvest") -local legacy = M.fromRecord({id = 5, stage = "collecting", mode = "auto", product = "hay", rake = 1}) -check("M17 etape 1-post: hø", legacy.plan.product, "hay") -check("M17 etape 1-post: standard kalk til", legacy.plan.lime, true) -check("M17 etape 1-post: 1 sæt", legacy.plan.rigs, 1) - --- M18: Start nu på en bar mark (klar-andel 0) må ikke nulstilles af vækst-reglen før den starter -local bare = M.with(M.forceReady(M.newField(70)), {startRequested = true}) -check("M18 bar mark forbliver klar", M.onReadiness(bare, 0).stage, "ready") -check("M18 uden forespørgsel -> venter", M.onReadiness(M.forceReady(M.newField(71)), 0).stage, "idle") -check("M18 vejrfølsom: høst", M.isWeatherSensitive("harvesting", {}, "harvest"), true) -check("M18 vejrfølsom: kultivering ikke", M.isWeatherSensitive("cultivating", {}, "harvest"), false) - --- M19: mål-afgrøde — "vælg mark + afgrøde, glem den" -local target = M.with(M.newField(80), {plan = {targetFruit = "WHEAT"}}) -local wheatNow = M.applyTarget(target.plan, "harvest", "BARLEY") -check("M19 mål -> auto", wheatNow.mode, "auto") -check("M19 høst byg -> så hvede", wheatNow.sow, true) -check("M19 såafgrøde = mål", wheatNow.sowFruit, "WHEAT") -check("M19 kæde: høst -> kultivér -> så", table.concat(M.workStages(wheatNow, "harvest", {}), ","), "harvesting,cultivating,sowing") -local grassToWheat = M.applyTarget(target.plan, "mowable", "GRASS") -check("M19 græs -> hvede: slå, saml op, så klargør og så", table.concat(M.workStages(grassToWheat, "mowable", {}), ","), "mowing,collecting,cultivating,sowing") -local grassTarget = M.applyTarget(M.with(M.newField(81), {plan = {targetFruit = "GRASS"}}).plan, "mowable", "GRASS") -check("M19 græs -> græs: kun slå og saml op", table.concat(M.workStages(grassTarget, "mowable", {}), ","), "mowing,collecting") -check("M19 græs -> græs: ingen såning", grassTarget.sow, false) -local bareTarget = M.applyTarget(target.plan, "bare", nil) -check("M19 bar mark -> klargør og så", table.concat(M.workStages(bareTarget, "bare", {plow = true}), ","), "plowing,sowing") -check("M19 uden mål: plan urørt", M.applyTarget(M.newField(82).plan, "harvest", "BARLEY").mode, "manual") -local started = M.start(target, "harvest", {}, "BARLEY") -check("M19 start anvender målet", started.plan.sowFruit, "WHEAT") -check("M19 start: høst først", started.stage, "harvesting") -check("M19 målet gemmes", M.fromRecord(M.toRecord(started)).plan.targetFruit, "WHEAT") -check("M19 klargøring efter slåning gemmes", M.fromRecord(M.toRecord(M.start(target, "mowable", {}, "GRASS"))).plan.prepAfterMow, true) --- M20: skal en bar mark med mål starte? (altid — kun såningen venter på såtiden) -check("M20 bar + sås nu -> start", M.shouldStartBare(target, "bare", true), true) -check("M20 bar + ikke såtid -> klargør alligevel (kun såningen venter)", M.shouldStartBare(target, "bare"), true) -check("M20 uden mål -> ingen auto-start", M.shouldStartBare(M.newField(83), "bare", true), false) -check("M20 voksende mark -> nej", M.shouldStartBare(target, "harvest", true), false) -check("M20 arbejdende mark -> nej", M.shouldStartBare(started, "bare", true), false) -check("M20 færdig (lige sået) -> nej, ingen dobbelt-såning", M.shouldStartBare(M.with(target, {stage = "done"}), "bare", true), false) - --- M21: vælg mål-afgrøde fra menuen: auto, og en færdig mark nulstilles så den kan starte -local chosen = M.setTarget(M.with(M.newField(84), {stage = "done"}), "CANOLA") -check("M21 mål sat", chosen.plan.targetFruit, "CANOLA"); check("M21 auto", chosen.plan.mode, "auto") -check("M21 færdig -> venter", chosen.stage, "idle") -check("M21 arbejdende mark beholder trin", M.setTarget(started, "CANOLA").stage, "harvesting") -local cleared = M.setTarget(chosen, "") -check("M21 mål fjernet", cleared.plan.targetFruit, ""); check("M21 tilbage til manuel", cleared.plan.mode, "manual") - --- M22: halm efter høst — hakkes (standard), samles op, eller presses til baller og samles -local strawBase = {soil = "none", sow = false, lime = false} -check("M22 hakkes: kun høst", table.concat(M.workStages(strawBase, "harvest", {}), ","), "harvesting") -check("M22 samles op", table.concat(M.workStages(M.with(M.newField(1), {plan = {soil = "none", lime = false, straw = "collect"}}).plan, "harvest", {}), ","), "harvesting,strawCollecting") -check("M22 baller", table.concat(M.workStages(M.with(M.newField(1), {plan = {soil = "none", lime = false, straw = "bale"}}).plan, "harvest", {}), ","), "harvesting,baling,baleCollecting") -local baleChain = M.start(M.with(M.newField(2), {plan = {straw = "bale", soil = "cultivate", lime = false}}), "harvest", {}) -check("M22 høst -> presses", M.advance(baleChain, {}).stage, "baling") -check("M22 presses -> baller samles", M.advance(M.advance(baleChain, {}), {}).stage, "baleCollecting") -check("M22 -> kultivér", M.advance(M.advance(M.advance(baleChain, {}), {}), {}).stage, "cultivating") -check("M22 roller", M.roleFor("baling") .. "," .. M.roleFor("baleCollecting") .. "," .. M.roleFor("strawCollecting"), "baler,baleCollector,collector") -check("M22 halm-læssevogn følger høsterens kursus", M.courseSource(M.with(M.newField(3), {stage = "strawCollecting"}), "collector"), "harvester") -check("M22 presser følger høsterens kursus", M.courseSource(M.with(M.newField(3), {stage = "baling"}), "baler"), "harvester") -check("M22 græs-læssevogn følger stadig slåkurset", M.courseSource(M.with(M.newField(3), {stage = "collecting"}), "collector"), "mower") -check("M22 halm-læssevogn uden breddekrav", M.requiredWidth(M.with(M.newField(3), {stage = "strawCollecting", mowWidth = 40}), "collector"), nil) -check("M22 presning vejrfølsom", M.isWeatherSensitive("baling", {}, "harvest"), true) -check("M22 ballesamling ikke vejrfølsom", M.isWeatherSensitive("baleCollecting", {}, "harvest"), false) -check("M22 gemmes", M.fromRecord(M.toRecord(M.with(M.newField(4), {plan = {straw = "bale"}}))).plan.straw, "bale") -check("M22 ukendt -> hakkes", M.fromRecord({id = 5, straw = "x"}).plan.straw, "chop") - --- M23: ingen ny tabel når vækst-reglen ikke ændrer noget (undgår affald hvert sekund -> GC-hak) -local calm = M.newField(95) -check("M23 uændret -> samme tabel", M.onReadiness(calm, 0.2) == calm, true) -check("M23 ændret -> ny tabel", M.onReadiness(calm, 0.95) ~= calm, true) --- M24: passive marker kan springes over (manuel, uden mål, venter, ingen forespørgsel) -check("M24 manuel venter = passiv", M.isPassive(calm), true) -check("M24 auto = ikke passiv", M.isPassive(M.with(calm, {plan = {mode = "auto"}})), false) -check("M24 mål = ikke passiv", M.isPassive(M.setTarget(calm, "WHEAT")), false) -check("M24 klar = ikke passiv", M.isPassive(M.with(calm, {stage = "ready"})), false) -check("M24 i gang = ikke passiv", M.isPassive(started), false) -check("M24 Start nu = ikke passiv", M.isPassive(M.with(calm, {startRequested = true})), false) - --- Stop holder en bar mark (ellers starter klargøringen straks forfra); Start eller ny mål-afgrøde slipper -local stoppedBare = M.stop(M.with(target, {stage = "cultivating"})) -check("M21 stoppet bar mark starter ikke selv", M.shouldStartBare(stoppedBare, "bare"), false) -check("M21 Start slipper", M.shouldStartBare(M.with(stoppedBare, {startRequested = true}), "bare"), true) -check("M21 ny mål-afgrøde slipper", M.shouldStartBare(M.setTarget(stoppedBare, "BARLEY"), "bare"), true) -check("M21 start nulstiller hold (næste sæson starter selv)", M.start(stoppedBare, "bare", {}, "WHEAT").held, nil) --- gemt produkt (silage/hø) gælder kun slåafgrøder: en vinmark der engang fejlagtigt blev "slåbar" (F22, 29/9) -check("produkt: gemt silage på høstmark -> høst", ADFieldModel.product({product = "silage"}, "harvest"), "harvest") -check("produkt: gemt hø på slåmark -> hø", ADFieldModel.product({product = "hay"}, "mowable"), "hay") -check("produkt: auto på slåmark -> silage", ADFieldModel.product({product = "auto"}, "mowable"), "silage") -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFieldSiloConfig.lua b/tests/test_adFieldSiloConfig.lua deleted file mode 100644 index 1a1c0fe..0000000 --- a/tests/test_adFieldSiloConfig.lua +++ /dev/null @@ -1,46 +0,0 @@ --- Kør: luajit tests/test_adFieldSiloConfig.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adFieldSiloConfig.lua") -local C = ADFieldSiloConfig -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end -local cfg = C.new() -check("standard fra", cfg.enabled, false) -check("standard x=2", cfg.maxVehicles, 2) -check("standard tærskel 10000", cfg.minLiters, 10000) -check("x under 1 -> 1", C.with(cfg, {maxVehicles = 0}).maxVehicles, 1) -check("x over 10 -> 10", C.with(cfg, {maxVehicles = 99}).maxVehicles, 10) -check("tærskel snappes til nærmeste valg", C.with(cfg, {minLiters = 12000}).minLiters, 10000) -check("tærskel 40000 -> 50000", C.with(cfg, {minLiters = 40000}).minLiters, 50000) -check("with muterer ikke", cfg.maxVehicles, 2) -local on = C.toggleStore(cfg, "silo1") -check("toggle til", C.hasStore(on, "silo1"), true) -check("toggle muterer ikke", C.hasStore(cfg, "silo1"), false) -check("toggle fra", C.hasStore(C.toggleStore(on, "silo1"), "silo1"), false) -local full = C.with(C.toggleStore(C.toggleStore(cfg, "a"), "b"), {enabled = true, maxVehicles = 4, minLiters = 25000}) -local back = C.fromRecord(C.toRecord(full)) -check("record: enabled", back.enabled, true) -check("record: lagre", table.concat(back.storeIds, " "), "a b") -check("record: x", back.maxVehicles, 4) -check("record: tærskel", back.minLiters, 25000) -check("tom record -> standard", C.fromRecord(nil).maxVehicles, 2) -check("record uden tal -> standard tærskel", C.fromRecord({enabled = 1}).minLiters, 10000) --- kør til produktion + foderreserve -check("standard: ikke til produktion", cfg.toProduction, false) -check("standard foderreserve 7 døgn", cfg.reserveDays, 7) -check("reserve snappes (10 -> 7)", C.with(cfg, {reserveDays = 10}).reserveDays, 7) -check("reserve 40 -> 30", C.with(cfg, {reserveDays = 40}).reserveDays, 30) -local prod = C.with(C.toggleStore(cfg, "a"), {toProduction = true, reserveDays = 14}) -check("with bevarer lagre", C.hasStore(prod, "a"), true) -check("with muterer ikke (produktion)", cfg.toProduction, false) -local prodBack = C.fromRecord(C.toRecord(prod)) -check("record: til produktion", prodBack.toProduction, true) -check("record: foderreserve", prodBack.reserveDays, 14) -check("gammel record uden felter -> standard", C.fromRecord({enabled = 1}).reserveDays, 7) -check("toggleStore bevarer produktion", C.toggleStore(prod, "b").toProduction, true) -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFieldSiloPlanner.lua b/tests/test_adFieldSiloPlanner.lua deleted file mode 100644 index 91ae24b..0000000 --- a/tests/test_adFieldSiloPlanner.lua +++ /dev/null @@ -1,159 +0,0 @@ --- Kør: luajit tests/test_adFieldSiloPlanner.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adFieldSiloPlanner.lua") -local P = ADFieldSiloPlanner -local WHEAT, CANOLA = 1, 2 -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end -local function silo(id, x, stock, markerId) return {id = id, name = id, markerId = markerId == nil and (id .. "m") or markerId or nil, x = x, z = 0, stock = stock} end -local function store(id, x, free) return {id = id, markerId = id .. "m", x = x, z = 0, free = free} end -local function vehicle(id, x, capacity, carries, cargo) return {id = id, x = x, z = 0, capacity = capacity, carries = carries or {[WHEAT] = true, [CANOLA] = true}, cargo = cargo} end -local function base(over) - local input = {silos = {}, stores = {store("farm", 0, {[WHEAT] = 1000000, [CANOLA] = 1000000})}, loans = {}, vehicles = {}, paused = {}, maxVehicles = 2, minLiters = 10000} - for k, v in pairs(over or {}) do input[k] = v end - return input -end - --- kvalificering -local r = P.plan(base({silos = {silo("A", 500, {[WHEAT] = 9999})}, vehicles = {vehicle("v1", 400, 50000)}})) -check("under tærskel -> ingen afsendelse", #r.dispatch, 0) -check("under tærskel -> ingen status", #r.status, 0) -r = P.plan(base({silos = {silo("A", 500, {[WHEAT] = 30000}, false)}, vehicles = {vehicle("v1", 400, 50000)}})) -check("uden markør -> noMarker", r.status[1].state, "noMarker") -check("uden markør -> ingen afsendelse", #r.dispatch, 0) -r = P.plan(base({stores = {store("farm", 0, {[CANOLA] = 1000000})}, silos = {silo("A", 500, {[WHEAT] = 30000})}, vehicles = {vehicle("v1", 400, 50000)}})) -check("ingen gård tager varen -> noFarmStore", r.status[1].state, "noFarmStore") -r = P.plan(base({silos = {silo("A", 500, {[WHEAT] = 30000})}, vehicles = {vehicle("v1", 400, 50000)}, paused = {[P.key("A", WHEAT)] = true}})) -check("pause -> paused, ingen afsendelse", r.status[1].state .. #r.dispatch, "paused0") - --- afsendelse, fuldeste først, x-loft -r = P.plan(base({silos = {silo("A", 500, {[WHEAT] = 20000}), silo("B", 900, {[WHEAT] = 80000})}, vehicles = {vehicle("v1", 400, 50000), vehicle("v2", 950, 50000), vehicle("v3", 1000, 50000)}})) -check("x=2 -> to afsendt", #r.dispatch, 2) -check("fuldeste silo først", r.dispatch[1].siloId, "B") -check("nærmeste vogn til B", r.dispatch[1].vehicleId, "v2") -check("B's rest (30000) får vogn nr. 2", r.dispatch[2].siloId, "B") -check("lager-markør med", r.dispatch[1].storeMarkerId, "farmm") -check("liter = vognens kapacitet", r.dispatch[1].liters, 50000) -check("anden tur = resten", r.dispatch[2].liters, 30000) - --- aktive lån tæller mod x og mod siloens rest -r = P.plan(base({silos = {silo("A", 500, {[WHEAT] = 60000})}, loans = {{siloId = "A", fillType = WHEAT, liters = 50000}}, vehicles = {vehicle("v1", 400, 50000), vehicle("v2", 400, 50000)}})) -check("lån på vej -> rest 10000 -> én ny vogn", #r.dispatch, 1) -check("status viser på vej", r.status[1].onWay, 50000) -r = P.plan(base({maxVehicles = 1, silos = {silo("A", 500, {[WHEAT] = 90000})}, loans = {{siloId = "Z", fillType = WHEAT, liters = 1}}, vehicles = {vehicle("v1", 400, 50000)}})) -check("x=1 med ét lån ude -> ingen", #r.dispatch, 0) -check("frie pladser 0", r.freeSlots, 0) - --- flere varer i én silo = hver sin opgave -r = P.plan(base({silos = {silo("A", 500, {[WHEAT] = 40000, [CANOLA] = 20000})}, vehicles = {vehicle("v1", 400, 50000), vehicle("v2", 410, 50000)}})) -check("to varer -> to afsendelser", #r.dispatch, 2) -check("fuldeste vare først", r.dispatch[1].fillType, WHEAT) - --- vogne der ikke kan -r = P.plan(base({silos = {silo("A", 500, {[WHEAT] = 40000})}, vehicles = {vehicle("v1", 400, 50000, {[CANOLA] = true}), vehicle("v2", 2000, 50000)}})) -check("vogn der ikke kan bære varen springes over", r.dispatch[1].vehicleId, "v2") -r = P.plan(base({silos = {silo("A", 500, {[WHEAT] = 40000})}, vehicles = {vehicle("v1", 400, 50000, nil, CANOLA), vehicle("v2", 2000, 50000)}})) -check("vogn med anden vare på springes over", r.dispatch[1].vehicleId, "v2") -r = P.plan(base({silos = {silo("A", 500, {[WHEAT] = 40000})}, vehicles = {vehicle("v1", 400, 0)}})) -check("vogn uden kapacitet -> ingen", #r.dispatch, 0) - --- lagervalg -r = P.plan(base({stores = {store("near", 400, {[WHEAT] = 20000}), store("far", 5000, {[WHEAT] = 900000})}, silos = {silo("A", 500, {[WHEAT] = 90000})}, vehicles = {vehicle("v1", 400, 50000), vehicle("v2", 400, 50000)}})) -check("nærmeste lager uden plads til læsset -> det med plads", r.dispatch[1].storeId, "far") -r = P.plan(base({stores = {store("small", 400, {[WHEAT] = 30000}), store("tiny", 300, {[WHEAT] = 15000})}, silos = {silo("A", 500, {[WHEAT] = 90000})}, vehicles = {vehicle("v1", 400, 50000), vehicle("v2", 400, 50000)}})) -check("intet har plads til hele læsset -> mest plads", r.dispatch[1].storeId, "small") -check("reserveret plads: vogn 2 til det andet lager", r.dispatch[2].storeId, "tiny") -r = P.plan(base({stores = {store("full", 400, {[WHEAT] = 5000})}, silos = {silo("A", 500, {[WHEAT] = 90000})}, vehicles = {vehicle("v1", 400, 50000)}})) -check("lager med under tærsklen -> noFarmStore", r.status[1].state, "noFarmStore") - --- input muteres ikke -local input = base({silos = {silo("A", 500, {[WHEAT] = 90000})}, vehicles = {vehicle("v1", 400, 50000)}}) -P.plan(input) -check("input.stores urørt", input.stores[1].free[WHEAT], 1000000) - - --- I1: lån på vej til et lager reserverer pladsen på tværs af planlægningsrunder -r = P.plan(base({stores = {store("s", 400, {[WHEAT] = 30000})}, silos = {silo("A", 500, {[WHEAT] = 90000})}, - loans = {{siloId = "A", fillType = WHEAT, liters = 30000, storeId = "s"}}, vehicles = {vehicle("v1", 400, 50000)}})) -check("I1 lager fyldt af lån på vej -> ingen ny vogn dertil", #r.dispatch, 0) -r = P.plan(base({stores = {store("s", 400, {[WHEAT] = 30000}), store("t", 900, {[WHEAT] = 500000})}, silos = {silo("A", 500, {[WHEAT] = 90000})}, - loans = {{siloId = "A", fillType = WHEAT, liters = 30000, storeId = "s"}}, vehicles = {vehicle("v1", 400, 50000)}})) -check("I1 næste vogn til det andet lager", r.dispatch[1] ~= nil and r.dispatch[1].storeId, "t") - --- kør til produktion: foder først, overskud til fabrik -local function factory(id, x, free) return {id = id, kind = "production", markerId = id .. "m", x = x, z = 0, free = free} end -local farmAndFactory = {store("farm", 5000, {[WHEAT] = 1000000}), factory("mill", 100, {[WHEAT] = 1000000})} -r = P.plan(base({stores = farmAndFactory, silos = {silo("A", 500, {[WHEAT] = 90000})}, vehicles = {vehicle("v1", 400, 50000)}, - feedReserve = {[WHEAT] = 100000}, farmStock = {[WHEAT] = 20000}})) -check("P1 reserve ikke dækket -> gårdlager selv om fabrik er nærmere", r.dispatch[1].storeId, "farm") -r = P.plan(base({stores = farmAndFactory, silos = {silo("A", 500, {[WHEAT] = 90000})}, vehicles = {vehicle("v1", 400, 50000)}, - feedReserve = {[WHEAT] = 100000}, farmStock = {[WHEAT] = 150000}})) -check("P2 reserve dækket -> fabrik", r.dispatch[1].storeId, "mill") -r = P.plan(base({stores = farmAndFactory, silos = {silo("A", 500, {[WHEAT] = 90000})}, vehicles = {vehicle("v1", 400, 50000)}})) -check("P3 ikke foder (ingen reserve) -> fabrik", r.dispatch[1].storeId, "mill") -r = P.plan(base({stores = farmAndFactory, silos = {silo("A", 500, {[WHEAT] = 100000})}, vehicles = {vehicle("v1", 400, 50000), vehicle("v2", 400, 50000)}, - feedReserve = {[WHEAT] = 100000}, farmStock = {[WHEAT] = 70000}})) -check("P4 første læs dækker resten af reserven -> gård", r.dispatch[1].storeId, "farm") -check("P4 andet læs er overskud -> fabrik", r.dispatch[2].storeId, "mill") -r = P.plan(base({stores = farmAndFactory, silos = {silo("A", 500, {[WHEAT] = 90000})}, vehicles = {vehicle("v1", 400, 50000)}, - feedReserve = {[WHEAT] = 100000}, farmStock = {[WHEAT] = 60000}, loans = {{siloId = "B", fillType = WHEAT, liters = 50000, storeId = "farm"}}})) -check("P5 læs på vej til gården tæller med i reserven -> fabrik", r.dispatch[1] ~= nil and r.dispatch[1].storeId, "mill") -r = P.plan(base({stores = {store("farm", 5000, {[WHEAT] = 1000000}), factory("mill", 100, {[CANOLA] = 1000000})}, silos = {silo("A", 500, {[WHEAT] = 90000})}, - vehicles = {vehicle("v1", 400, 50000)}, farmStock = {[WHEAT] = 999999}})) -check("P6 ingen fabrik tager varen -> gårdlager", r.dispatch[1].storeId, "farm") -r = P.plan(base({stores = {store("farm", 5000, {[CANOLA] = 1000000}), factory("mill", 100, {[WHEAT] = 1000000})}, silos = {silo("A", 500, {[WHEAT] = 90000})}, - vehicles = {vehicle("v1", 400, 50000)}, feedReserve = {[WHEAT] = 100000}})) -check("P7 reserve mangler men ingen gård tager varen -> fabrik", r.dispatch[1].storeId, "mill") -check("P7 status ok (ikke noFarmStore)", r.status[1].state, "ok") -check("P7 status viser destination", r.status[1].toProduction, true) - --- fabrik kun når hele læsset kan være der (in-game 28/9: vogn med 252.000 l ventede timevis ved en fuld fabrik) -r = P.plan(base({stores = {store("farm", 5000, {[WHEAT] = 1000000}), factory("mill", 100, {[WHEAT] = 30000})}, silos = {silo("A", 500, {[WHEAT] = 90000})}, - vehicles = {vehicle("v1", 400, 50000)}})) -check("P8 fabrik uden plads til hele læsset -> gårdlager", r.dispatch[1].storeId, "farm") -r = P.plan(base({stores = {store("farm", 5000, {[CANOLA] = 1000000}), factory("mill", 100, {[WHEAT] = 30000})}, silos = {silo("A", 500, {[WHEAT] = 90000})}, - vehicles = {vehicle("v1", 400, 50000)}})) -check("P9 kun fabrik uden plads til hele læsset -> ingen afsendelse", #r.dispatch, 0) - --- høsten først: tømmevogne holdes tilbage til høstere der arbejder -local vs = {vehicle("v1", 400, 50000), vehicle("v2", 400, 50000, {[CANOLA] = true}), vehicle("v3", 400, 50000)} -local left = P.withoutHarvestReserve(vs, {{fillType = WHEAT, count = 1}}) -check("R1 én hvede-vogn holdt tilbage", #left, 2) -check("R1 raps-vognen er fri", left[1].id == "v2" or left[2].id == "v2", true) -left = P.withoutHarvestReserve(vs, {{fillType = WHEAT, count = 5}}) -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) --- Fuld fabrik: lånt vogn med last holder ved et lager uden plads -> resten til et andet lager (gård) -check("SF1 fuld fabrik -> videre", P.storeFullDue({atStore = true, stillMs = 70000, cargo = 12000, storeFree = 0}), true) -check("SF2 fabrikken tager stadig imod -> vent", P.storeFullDue({atStore = true, stillMs = 70000, cargo = 12000, storeFree = 8000}), false) -check("SF3 lige ankommet -> vent", P.storeFullDue({atStore = true, stillMs = 20000, cargo = 12000, storeFree = 0}), false) -check("SF4 ikke ved lageret -> nej", P.storeFullDue({atStore = false, stillMs = 70000, cargo = 12000, storeFree = 0}), false) -local fullStores = { - {id = "fab", kind = "production", markerId = 1, x = 10, z = 0, free = {[10] = 0}}, - {id = "gaard", kind = "silo", markerId = 2, x = 500, z = 0, free = {[10] = 900000}}, -} -check("SF5 andet lager end det fulde", P.restStore(fullStores, {x = 0, z = 0}, 10, 12000, "fab").id, "gaard") -check("SF6 kun det fulde tager varen -> intet", P.restStore({fullStores[1]}, {x = 0, z = 0}, 10, 12000, "fab"), nil) -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFieldSilos.lua b/tests/test_adFieldSilos.lua deleted file mode 100644 index 43c6b23..0000000 --- a/tests/test_adFieldSilos.lua +++ /dev/null @@ -1,159 +0,0 @@ --- Kør: luajit tests/test_adFieldSilos.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adFieldSiloPlanner.lua") -dofile("FS25_ADSmartPickup/adFieldSiloConfig.lua") -dofile("FS25_ADSmartPickup/adFieldSilos.lua") -local S = ADFieldSilos -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end -local loan = {startedMs = 0, lastProgressMs = 0, lastFill = 0} -check("kører, tidligt -> behold", S.releaseReason(loan, {adActive = true, controlled = false, fill = 0, moving = true}, 5000), nil) -check("AD slukket efter 40 s -> done", S.releaseReason(loan, {adActive = false, controlled = false, fill = 0}, 40000), "done") -check("spiller i vognen -> player", S.releaseReason(loan, {adActive = true, controlled = true, fill = 0}, 5000), "player") -check("står stille uden fyld i 120 s -> stuck", S.releaseReason(loan, {adActive = true, controlled = false, fill = 0, moving = false}, 120001), "stuck") -check("kører stadig efter 120 s -> behold", S.releaseReason(loan, {adActive = true, controlled = false, fill = 0, moving = true}, 120001), nil) -local loading = {startedMs = 0, lastProgressMs = 110000, lastFill = 0} -check("fyldning steg for nylig -> behold", S.releaseReason(loading, {adActive = true, controlled = false, fill = 20000, moving = false}, 125000), nil) - --- I2/I3: vagten gælder kun før turen er kommet i gang; AD der slukker straks uden fremdrift = fejl -local going = {startedMs = 0, lastFill = 30000, hasProgressed = true} -check("I2 i gang, står i kø 500 s -> behold", S.releaseReason(going, {adActive = true, controlled = false, fill = 30000, moving = false}, 500000), nil) -check("I2 aflæsning (fyld falder) er fremdrift", S.releaseReason({startedMs = 0, lastFill = 30000}, {adActive = true, controlled = false, fill = 20000, moving = false}, 500000), nil) -check("I3 AD slukket efter 10 s uden fremdrift -> failed", S.releaseReason({startedMs = 0, lastFill = 0}, {adActive = false, controlled = false, fill = 0}, 10000), "failed") -check("I3 AD slukket efter 10 s med fremdrift -> done", S.releaseReason({startedMs = 0, lastFill = 0, hasProgressed = true}, {adActive = false, controlled = false, fill = 0}, 10000), "done") - --- Røgtest af spil-laget med mocks: planNow låner nærmeste tømmevogn, isLoaned = optaget, --- frakobling (setConfig enabled=false) stopper AD og frigiver (Review Focus). -do - local WHEAT = 1 - Logging = {info = function() end, warning = function() end} - g_currentMission = {getFarmId = function() return 1 end} - g_fillTypeManager = {getFillTypeByIndex = function() return {title = "Hvede"} end} - function getWorldTranslation(node) return node.x, 0, node.z end - ADRuns = {vehicleLabel = function(v) return v.name end} - local function storage(levels, free) return {fillLevels = levels, fillTypes = {[WHEAT] = true}, getFreeCapacity = function() return free end} end - local fieldSilo = {id = "fs", name = "Marksilo 1", kind = "silo", placeable = {rootNode = {x = 1000, z = 0}, spec_silo = {storages = {storage({[WHEAT] = 60000}, 0)}}}} - local farm = {id = "farm", name = "Gårdsilo", kind = "silo", placeable = {rootNode = {x = 0, z = 0}, spec_silo = {storages = {storage({}, 500000)}}}} - ADBuildings = {list = function() return {fieldSilo, farm} end, - getMarkers = function(b, role) return {{id = b.id .. "-" .. role}} end} - ADFields = {list = function() return {{id = 1}} end, fieldSilos = function() return {fieldSilo, farm} end} - local started, settingsSet, noPark, noRoute, residue = {}, {}, {}, {}, 0 - ADRunsController = {store = {runs = {}}, setSetting = function(_, v, name, value) settingsSet[v] = settingsSet[v] or {}; settingsSet[v][name] = value end} - ADUnloadWait = {hasRouteTo = function(_, _, id) return not noRoute[id] end} - local function makeVehicle(name, x) - local module = {active = false} - function module:setMode() end - function module:setFirstMarkerByWayPointId(id) self.first = id end - function module:setSecondMarkerByWayPointId(id) self.second = id end - function module:setFillType() end - function module:setLoopsDone() end - function module:raiseDirtyFlag() end - function module:isActive() return self.active end - return {name = name, x = x, lastSpeedReal = 0, ad = {stateModule = module}, - stopAutoDrive = function(self) self.ad.stateModule.active = false; self.stopped = true end} - end - local near, far = makeVehicle("Nær", 900), makeVehicle("Fjern", 5000) - local adEnv = {AutoDrive = {MODE_PICKUPANDDELIVER = 2, RT_NONE = 0, - getAllDischargeableUnits = function() return {{fillUnitIndex = 1, object = { - getFillUnitCapacity = function() return 40000 end, - getFillUnitSupportedFillTypes = function() return {[WHEAT] = true} end, - getFillUnitFillLevel = function() return residue end, getFillUnitFillType = function() return WHEAT end}}} end, - getAllUnits = function() return {} end, getAllFillLevels = function() return 0 end}} - ADFieldWork = { - getRigs = function() - local rigs = {} - -- som spillets vehicleSystem: slettede køretøjer er ikke med - if not near.isDeleted then table.insert(rigs, {id = "n", vehicle = near, role = "unloader", enabled = true, busy = S.isLoaned(near), controlled = false, x = 900, z = 0}) end - if not far.isDeleted then table.insert(rigs, {id = "f", vehicle = far, role = "unloader", enabled = true, busy = S.isLoaned(far), controlled = false, x = 5000, z = 0}) end - table.insert(rigs, {id = "h", vehicle = far, role = "harvester", enabled = true, busy = false, controlled = false, x = 1000, z = 0}) - return rigs - end, - enableParkAtJobFinished = function() end, - parkMarkerId = function(_, v) if noPark[v] then return nil end return 777 end, - startAd = function(_, v, _, first, second, fillType) v.ad.stateModule.active = true; table.insert(started, {v = v, first = first, second = second, fillType = fillType}) end, - } - S.setConfig(ADFieldSiloConfig.with(ADFieldSiloConfig.toggleStore(ADFieldSiloConfig.new(), "farm"), {enabled = true, maxVehicles = 1})) - local plan = S.planNow(adEnv) - check("RØG én afsendelse (x=1)", #started, 1) - check("RØG nærmeste tømmevogn", started[1] ~= nil and started[1].v.name, "Nær") - check("RØG fra marksilo-markør", started[1] ~= nil and started[1].first, "fs-load") - check("RØG til gårdlager-markør", started[1] ~= nil and started[1].second, "farm-unload") - check("RØG varen med", started[1] ~= nil and started[1].fillType, WHEAT) - check("RØG gårdlager er ikke marksilo", #plan.status, 1) - check("RØG vognen er lånt", S.isLoaned(near), true) - check("RØG x nået -> ingen flere", #S.planNow(adEnv).dispatch, 0) - check("I4 rotation slået fra på lånt vogn", settingsSet[near] ~= nil and settingsSet[near].rotateTargets, 0) - S.setConfig(ADFieldSiloConfig.with(S.config, {enabled = false})) - check("RØG frakobling stopper AD", near.stopped, true) - check("RØG frakobling frigiver", S.isLoaned(near), false) - - -- I5: vogn uden parkeringspunkt lånes ikke (ville blive holdende på lagerets markør) - started = {} - noPark[near] = true - near.stopped = nil - S.setConfig(ADFieldSiloConfig.with(S.config, {enabled = true})) - S.planNow(adEnv) - check("I5 uden parkering -> fjern vogn med parkering", started[1] ~= nil and started[1].v.name, "Fjern") - S.setConfig(ADFieldSiloConfig.with(S.config, {enabled = false})) - noPark[near] = nil - - -- I3: ingen rute til siloen -> ingen afsendelse - started = {} - noRoute["fs-load"] = true - S.setConfig(ADFieldSiloConfig.with(S.config, {enabled = true})) - S.planNow(adEnv) - check("I3 ingen rute -> ingen afsendelse", #started, 0) - noRoute["fs-load"] = nil - S.setConfig(ADFieldSiloConfig.with(S.config, {enabled = false})) - - -- I6: silo der er kilde/mål i et flow er ikke marksilo - ADRunsController.store.runs = {{kind = "supply", sourceBuildingIds = {}, targetBuildingIds = {"fs"}, storeBuildingIds = {}}} - local silos = S.collectBuildings(adEnv) - check("I6 silo i et flow tømmes ikke", #silos, 0) - ADRunsController.store.runs = {} - - -- in-game 27/9: marksilo-flowet flyttede sukkerroer fra en Farma-silo til nabo-Farma-siloen (samme lagerklynge) - local neighbour = {id = "nb", name = "Nabo-Farma", kind = "silo", placeable = {rootNode = {x = 40, z = 0}, spec_silo = {storages = {storage({[WHEAT] = 60000}, 0)}}}} - ADFields.fieldSilos = function() return {fieldSilo, farm, neighbour} end - local withNeighbour = S.collectBuildings(adEnv) - local names = {} - for _, silo in ipairs(withNeighbour) do table.insert(names, silo.id) end - check("gårdens nabosilo (40 m fra gårdlager) er ikke marksilo", table.concat(names, ","), "fs") - ADFields.fieldSilos = function() return {fieldSilo, farm} end - - -- in-game 27/9: gårdens Farma-klynge er 300 m lang — siloer over 100 m fra et valgt gårdlager blev marksiloer. - -- En silo i samme klynge (≤ 60 m til en nabo) som et gårdlager er gårdens. - local c1 = {id = "c1", name = "Klynge 1", kind = "silo", placeable = {rootNode = {x = 50, z = 0}, spec_silo = {storages = {storage({[WHEAT] = 60000}, 0)}}}} - local c2 = {id = "c2", name = "Klynge 2", kind = "silo", placeable = {rootNode = {x = 105, z = 0}, spec_silo = {storages = {storage({[WHEAT] = 60000}, 0)}}}} - ADBuildings.list = function() return {fieldSilo, farm, c1, c2} end - ADFields.fieldSilos = function() return {fieldSilo, farm, c1, c2} end - local clustered = {} - for _, silo in ipairs(S.collectBuildings(adEnv)) do table.insert(clustered, silo.id) end - check("gårdens klynge (105 m via nabo) er ikke marksilo", table.concat(clustered, ","), "fs") - ADBuildings.list = function() return {fieldSilo, farm} end - ADFields.fieldSilos = function() return {fieldSilo, farm} end - - -- M5: rest under 100 l tæller ikke som last - residue = 50 - local vehicles = S.collectVehicles(adEnv) - check("M5 rest på 50 l er ikke last", vehicles[1].cargo, nil) - residue = 0 - - -- M4: slettet lånt vogn fjernes uden at vælte tick - S.setConfig(ADFieldSiloConfig.with(S.config, {enabled = true})) - started = {} - S.planNow(adEnv) - local loaned = started[1] ~= nil and started[1].v or nil - if loaned ~= nil then loaned.isDeleted = true end - local okTick = pcall(S.tick, adEnv, 2000) - check("M4 tick tåler slettet vogn", okTick, true) - check("M4 slettet vogn frigivet", loaned ~= nil and S.isLoaned(loaned), false) - S.setConfig(ADFieldSiloConfig.with(S.config, {enabled = false})) -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_adFieldStorage.lua b/tests/test_adFieldStorage.lua deleted file mode 100644 index 3aa09ec..0000000 --- a/tests/test_adFieldStorage.lua +++ /dev/null @@ -1,110 +0,0 @@ --- Kør: luajit tests/test_adFieldStorage.lua (fra repo-roden) --- Lagringen skal dække HVERT felt modellen gemmer (FIELD_TYPES), og en mark skal overleve en tur gennem XML. -dofile("FS25_ADSmartPickup/adFieldModel.lua") -dofile("FS25_ADSmartPickup/adFieldStorage.lua") -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - --- falsk XMLFile: gemmer værdier pr. nøgle -local function fakeXml() - local values = {} - local xml = {} - for _, kind in ipairs({"Int", "String", "Float"}) do - xml["set" .. kind] = function(_, key, value) values[key] = value end - xml["get" .. kind] = function(_, key) return values[key] end - end - return xml -end - -local field = ADFieldModel.with(ADFieldModel.startCare(ADFieldModel.newField(77), "weeding"), { - kind = "harvest", lastFruit = "WHEAT", mowWidth = 40.8, mowVehicleId = "v1", rakeVehicleId = "v2", windrowOffset = -2.5, - plan = {mode = "auto", product = "harvest", rake = true, storeBuildingIds = {"b1"}, rigs = 3, unloaders = 2, - mulch = true, lime = false, soil = "plow", sow = true, sowFruit = "CANOLA", roll = true, fertilize = false, weed = false}, -}) -local record = ADFieldModel.toRecord(field) -for key in pairs(record) do - check("ST1 felt med i lagringen: " .. key, ADFieldStorage.FIELD_TYPES[key] ~= nil, true) -end -local xml = fakeXml() -ADFieldStorage.writeRecord(xml, "k", record) -local back = ADFieldModel.fromRecord(ADFieldStorage.readRecord(xml, "k")) -local again = ADFieldModel.toRecord(back) -for key, value in pairs(record) do - check("ST2 rundtur: " .. key, tostring(again[key]), tostring(value)) -end - --- ST3-ST6: kursus-registret (genbrug af Courseplay-kurser) gemmes i samme fil, -dofile("FS25_ADSmartPickup/adCourseCache.lua") -local course = ADCourseCache.newRecord({vehicle = "v7", field = 22, width = 30.0, sig = "n=4;a=960000;c=800,300", - settings = ADCourseCache.settingsKey({multiTools = 2, lane = -1}), waypoints = 812}) -for key in pairs(course) do - check("ST3 kursus-felt med i lagringen: " .. key, ADFieldStorage.COURSE_TYPES[key] ~= nil, true) -end -local cxml = fakeXml() -ADFieldStorage.writeCourseRecord(cxml, "c", course) -local cback = ADFieldStorage.readCourseRecord(cxml, "c") -for key, value in pairs(course) do - check("ST4 kursus-rundtur: " .. key, tostring(cback[key]), tostring(value)) -end -check("ST4b matcher stadig efter rundtur", ADCourseCache.matches(cback, {field = 22, width = 30, sig = course.sig, settings = course.settings}), true) - --- hel fil: save -> load med falsk XMLFile (iterate over indekserede nøgler) -local function fileXml(store) - local xml = fakeXml() - for _, kind in ipairs({"Int", "String", "Float"}) do - xml["set" .. kind] = function(_, key, value) store[key] = value end - xml["get" .. kind] = function(_, key) return store[key] end - end - function xml.iterate(_, base, fn) - local index = 0 - while true do - local prefix = string.format("%s(%d)", base, index) - local found = false - for key in pairs(store) do - if key:sub(1, #prefix + 1) == prefix .. "#" or key:sub(1, #prefix + 1) == prefix .. "." then found = true break end - end - if not found then return end - fn(index + 1, prefix) - index = index + 1 - end - end - function xml.save() end - function xml.delete() end - return xml -end -local disk = {} -ADRunsStorage = {FILE_NAME = "runs.xml", getPath = function() return "/save/runs.xml" end} -XMLFile = {create = function() return fileXml(disk) end, load = function() return fileXml(disk) end} -function fileExists() return true end -check("ST5 gem", ADFieldStorage.save({fields = {[77] = field}, fleet = {}, fleetMode = "all", - courseRecords = ADCourseCache.withRecord({}, course)}), true) -local loaded = ADFieldStorage.load() -check("ST6 kursus indlæst", loaded.courseRecords.v7 ~= nil and loaded.courseRecords.v7.waypoints, 812) -check("ST6b mark indlæst", loaded.fields[77] ~= nil, true) --- ST7: gammel fil uden -> tomt register (bagudkompatibelt) -disk = {} -ADFieldStorage.save({fields = {}, fleet = {}, fleetMode = "all"}) -local old = ADFieldStorage.load() -check("ST7 gammel fil: tomt register", next(old.courseRecords), nil) - --- ST8: manuel transport pr. maskine gemmes og indlæses; gammel fil uden -> tom -disk = {} -ADFieldStorage.save({fields = {}, fleet = {}, fleetMode = "all", manual = {vehicleCombine = true}}) -check("ST8 manuel transport indlæst", ADFieldStorage.load().manual.vehicleCombine, true) -check("ST8 gammel fil: ingen manuelle", next(old.manual or {}), nil) - --- ST9: pause ("Stop alt og parkér") gemmes, så intet starter af sig selv efter indlæsning -disk = {} -ADFieldStorage.save({fields = {}, fleet = {}, fleetMode = "all", paused = true}) -check("ST9 pause indlæst", ADFieldStorage.load().paused, true) -disk = {} -ADFieldStorage.save({fields = {}, fleet = {}, fleetMode = "all"}) -check("ST9 ingen pause", ADFieldStorage.load().paused, false) - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFieldView.lua b/tests/test_adFieldView.lua deleted file mode 100644 index b41d9ff..0000000 --- a/tests/test_adFieldView.lua +++ /dev/null @@ -1,66 +0,0 @@ --- Kør: luajit tests/test_adFieldView.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adFieldModel.lua") -dofile("FS25_ADSmartPickup/adFieldView.lua") -local V = ADFieldView -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - -local infos = { - {id = 109, mowable = true, markerId = 5}, - {id = 12, mowable = true, markerId = nil}, - {id = 90, mowable = false, markerId = 7}, -} -local fields = {[109] = ADFieldModel.newField(109), [12] = ADFieldModel.newField(12)} - --- V1: alle marker (etape 2: også høst og bare), sorteret efter nummer -local rows = V.rows(infos, fields) -check("V1 tre rækker", #rows, 3) -check("V1 sorteret", rows[1].info.id, 12) -check("V1 model med", rows[3].field.id, 109) --- V2: mangler model -> ny standardmark -check("V2 standardmodel", rows[2].field.stage, "idle") --- V3: status — manglende markør vinder over trin -check("V3 mangler markør", V.statusKey(fields[12], infos[2]), "spu_field_noMarker") -check("V3 trin", V.statusKey(fields[109], infos[1]), "spu_field_stage_idle") - --- V4: planens tekstnøgler pr. markart -check("V4 ensilage", V.planKey({product = "auto"}, "mowable"), "spu_field_plan_silage") -check("V4 hø", V.planKey({product = "hay"}, "mowable"), "spu_field_plan_hay") -check("V4 høst", V.planKey({product = "auto"}, "harvest"), "spu_field_plan_harvest") -check("V4 bar mark", V.planKey({product = "auto"}, "bare"), "spu_field_plan_prep") --- V5: jordtilstand som korte tekstdele -local parts = V.soilParts({lime = 0.2, fert = 1, fertMax = 2, weed = 3, needs = {lime = true, plow = true}}) -check("V5 dele", table.concat(parts, ","), "lime,plow,fert,weed") -check("V5 intet", #V.soilParts({lime = 2, fert = 2, fertMax = 2, weed = 0, needs = {}}), 0) - --- cropText: høstet = stub (afgrøde), bar = bar jord, ellers afgrøden -local texts = {spu_field_stubble = "stub (%s)", spu_field_bare = "bar jord"} -local function T(key) return texts[key] end -check("FVcrop stub", ADFieldView.cropText({kind = "bare", stubbleOf = "Hvede", cropTitle = "Hvede"}, T), "stub (Hvede)") -check("FVcrop bar", ADFieldView.cropText({kind = "bare"}, T), "bar jord") -check("FVcrop afgrøde", ADFieldView.cropText({kind = "harvest", cropTitle = "Majs"}, T), "Majs") - --- FVstore: marksilo først, gæringssilo, så afstand; valgte vises altid; kun de nærmeste af resten -local stores = { - {id = "fjern", distance = 3000}, - {id = "nær", distance = 200}, - {id = "mark", distance = 90, fieldSilo = true}, - {id = "valgt", distance = 5000, chosen = true}, - {id = "gær", distance = 800, fermenter = true}, - {id = "midt", distance = 1200}, -} -local storeRows, hiddenStores = ADFieldView.storeRows(stores, 2) -local order = {} -for _, e in ipairs(storeRows) do table.insert(order, e.id) end -check("FVstore rækkefølge", table.concat(order, ","), "mark,gær,nær,midt,valgt") -check("FVstore skjulte", hiddenStores, 1) -check("FVstore meter", ADFieldView.distanceText(90.4), "90 m") -check("FVstore km", ADFieldView.distanceText(1420), "1,4 km") - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFieldWorkBuying.lua b/tests/test_adFieldWorkBuying.lua deleted file mode 100644 index a1d148b..0000000 --- a/tests/test_adFieldWorkBuying.lua +++ /dev/null @@ -1,22 +0,0 @@ --- Kør: luajit tests/test_adFieldWorkBuying.lua (fra repo-roden) --- Påfyldning ved købestation (in-game 26/9: "Liquid Buy Station", FarmFillStations buyingStationConfigurable): --- ingen lagerbeholdning, så varen læses fra udbud/triggere/AI-varer. -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end -Logging = {info = function() end, warning = function() end} -dofile("FS25_ADSmartPickup/adFieldWork.lua") -local HERBICIDE, LIME, SEEDS = 20, 21, 22 -local W = ADFieldWork -check("BS1 udbudte varer", W.buyingSupports({providedFillTypes = {[HERBICIDE] = true}}, HERBICIDE), true) -check("BS2 triggerens varer", W.buyingSupports({loadTriggers = {{fillTypes = {[HERBICIDE] = true}}}}, HERBICIDE), true) -check("BS3 AI-varer", W.buyingSupports({getAISupportedFillTypes = function() return {[SEEDS] = true} end}, SEEDS), true) -check("BS4 sælger ikke kalk", W.buyingSupports({providedFillTypes = {[HERBICIDE] = true}}, LIME), false) -check("BS5 fejl i opslag = nej", W.buyingSupports({getAISupportedFillTypes = function() error("x") end}, LIME), false) -check("BS6 ingen station", W.buyingSupports(nil, LIME), false) -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFields.lua b/tests/test_adFields.lua deleted file mode 100644 index 1a2f4e6..0000000 --- a/tests/test_adFields.lua +++ /dev/null @@ -1,98 +0,0 @@ --- Kør: luajit tests/test_adFields.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adFields.lua") -local F = ADFields -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - --- F1/F2: markørnavne -check("F1 F109", F.parseMarkerName("F109"), 109) -check("F1 småt", F.parseMarkerName("f7"), 7) -check("F1 mellemrum", F.parseMarkerName(" F 12 "), 12) -check("F2 andet navn", F.parseMarkerName("Field 109"), nil) -check("F2 tekst efter", F.parseMarkerName("F109 wait"), nil) -check("F2 nil", F.parseMarkerName(nil), nil) - --- F3: slåafgrøde = MOWER giver et skår som TEDDER gør til DRY…; halm udelukket -local names = {[10] = "GRASS_WINDROW", [11] = "DRYGRASS_WINDROW", [20] = "STRAW", [30] = "CLOVER_WINDROW", [31] = "DRYCLOVER_WINDROW", [40] = "BEANSTRAW"} -local mower = {[1] = {fillTypeIndex = 10}, [2] = {fillTypeIndex = 20}, [3] = {fillTypeIndex = 30}, [4] = {fillTypeIndex = 40}} -local tedder = {[10] = {targetFillTypeIndex = 11}, [30] = {targetFillTypeIndex = 31}, [40] = {targetFillTypeIndex = 20}} -local mowable = F.buildMowable(mower, tedder, function(index) return names[index] end) -check("F3 græs", mowable[1] ~= nil, true) -check("F3 græs skår", mowable[1].windrow, 10) -check("F3 græs hø", mowable[1].hay, 11) -check("F3 rug (halm) ikke", mowable[2], nil) -check("F3 kløver", mowable[3].hay, 31) -check("F3 bønnehalm -> halm ikke", mowable[4], nil) - --- F4: markens art og om den vokser -local kind, growing = F.classify(3, false, 0.4) -check("F4 høstafgrøde", kind, "harvest"); check("F4 vokser", growing, true) -check("F4 klar høst vokser ikke", select(2, F.classify(3, false, 0.95)), false) -check("F4 slåafgrøde", (F.classify(1, true, 0.2)), "mowable") -check("F4 ingen afgrøde = bar", (F.classify(nil, false, 0)), "bare") -check("F4 bar vokser ikke", select(2, F.classify(nil, false, 0)), false) - --- F5: jordens behov ud fra gennemsnit og spillets indstillinger -local needs = F.soilNeeds({lime = 0.2, plowLevel = 0}, {limeRequired = true, plowingRequired = true}) -check("F5 kalk", needs.lime, true); check("F5 pløj", needs.plow, true) -local off = F.soilNeeds({lime = 0, plowLevel = 0}, {limeRequired = false, plowingRequired = false}) -check("F5 kalk slået fra i spillet", off.lime, false); check("F5 pløjning slået fra", off.plow, false) -check("F5 kalket jord", F.soilNeeds({lime = 2, plowLevel = 1}, {limeRequired = true, plowingRequired = true}).lime, false) -check("F5 ukendt niveau = intet behov", F.soilNeeds({}, {limeRequired = true, plowingRequired = true}).plow, false) - --- F6: vækststadie -> fase ud fra afgrødens egne grænser -local wheat = {minHarvestingGrowthState = 6, maxHarvestingGrowthState = 7, cutState = 9, witheredState = 8} -check("F6 intet", F.growthPhase(0, wheat), "none") -check("F6 spirer/vokser", F.growthPhase(3, wheat), "growing") -check("F6 klar", F.growthPhase(6, wheat), "ready") -check("F6 visnet", F.growthPhase(8, wheat), "withered") -check("F6 høstet stub", F.growthPhase(9, wheat), "cut") -check("F6 ukendt stadie", F.growthPhase(nil, wheat), nil) -check("F6 FS25-navne (minHarvestGrowthState)", F.growthPhase(6, {minHarvestGrowthState = 5, maxHarvestGrowthState = 6, cutState = 8}), "ready") --- F7: vokser = fase growing; ukendt fase -> voksende pixels og ikke klar -check("F7 fase vinder", F.isGrowing("cut", 100, 0.1), false) -check("F7 fase vokser", F.isGrowing("growing", 0, 0), true) -check("F7 reserve: voksende pixels", F.isGrowing(nil, 50, 0.2), true) -check("F7 reserve: ingen pixels (stub)", F.isGrowing(nil, 0, 0), false) -check("F7 reserve: klar", F.isGrowing(nil, 50, 0.95), false) - --- F8: slåafgrøde fuldt udvokset (Dakota-græs 3 = mellemhøjde, 4-5 = fuld) -check("F8 prime græs", F.primeState({minHarvestingGrowthState = 3, maxHarvestingGrowthState = 5}), 4) -check("F8 prime ét klar-stadie", F.primeState({minHarvestingGrowthState = 6, maxHarvestingGrowthState = 6}), 6) -check("F8 prime ukendt", F.primeState({}), nil) -check("F8 mellemhøjde tæller ikke", F.isPrime(3, 4), false) -check("F8 fuld højde tæller", F.isPrime(5, 4), true) -check("F8 ukendt stadie tæller", F.isPrime(nil, 4), true) -check("F8 ingen krav", F.isPrime(3, nil), true) - --- F9: kartofler (Dakota): 6 = klar men kun forberedelig, 10 = toppet (høstklar) -local potato = {minHarvestingGrowthState = 10, maxHarvestingGrowthState = 10, minPreparingGrowthState = 6, maxPreparingGrowthState = 6, witheredState = 7, cutState = 8} -check("F9 utoppet er forberedelig", F.isPreparable(6, potato), true) -check("F9 voksende ikke", F.isPreparable(5, potato), false) -check("F9 fase klar", F.growthPhase(6, potato), "ready") -check("F9 toppet klar", F.growthPhase(10, potato), "ready") -check("F9 ingen forberedelse", F.isPreparable(6, {minPreparingGrowthState = -1}), false) - --- jordtype -> klargjort/pløjet (værdierne kommer fra spillets fieldGroundSystem) -local groundValues = {STUBBLE_TILLAGE = 1, CULTIVATED = 2, SEEDBED = 3, PLOWED = 4, ROLLED_SEEDBED = 5, SOWN = 6} -local function cls(v) local p, pl = ADFields.classifyGround(v, groundValues) return tostring(p) .. "|" .. tostring(pl) end -check("jord: pløjet", cls(4), "true|true") -check("jord: kultiveret", cls(2), "true|false") -check("jord: stubbearbejdet", cls(1), "true|false") -check("jord: sået", cls(6), "false|false") -check("jord: ukendt værdi", cls(nil), "nil|nil") -groundValues.NONE = 0 -check("jord: ingen markjord tæller ikke med", cls(0), "nil|nil") --- vin/oliven er aldrig slåafgrøder, selv om et mod har sat dem på MOWER-listen (in-game 29/9: F22 hvide druer fik --- planen "Silage + så" og en skårlægger sendt ud) -check("vin: druer ikke slåbare", ADFields.isMowableFruit("GRAPE", {windrow = "GRASS_WINDROW"}), false) -check("vin: oliven ikke slåbare", ADFields.isMowableFruit("oliveb", {windrow = "X"}), false) -check("græs slåbart", ADFields.isMowableFruit("GRASS", {windrow = "GRASS_WINDROW"}), true) -check("ikke på MOWER-listen", ADFields.isMowableFruit("WHEAT", nil), false) -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFieldsPolygon.lua b/tests/test_adFieldsPolygon.lua deleted file mode 100644 index ef98979..0000000 --- a/tests/test_adFieldsPolygon.lua +++ /dev/null @@ -1,99 +0,0 @@ --- Kør: luajit tests/test_adFieldsPolygon.lua (fra repo-roden) --- FS25-marker har ingen polygonPoints — omridset hentes med field:getDensityMapPolygon():getVerticesList(). --- In-game 26/9 (Dakota 16x, marker på 1,7 x 0,6 km): uden omrids blev kun markskiltet (posX/posZ) prøvet; --- F108 med majs viste "bar jord" og "mangler markør" (markøren stod ved kanten, 890 m fra skiltet). -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end -Logging = {warning = function() end, info = function() end} - -local MAIZE = 10 -function getWorldTranslation() return 0, 0, 0 end -function getTerrainHeightAtWorldPos() return 0 end --- markskiltet står i marken, men ved en stribe uden afgrøde (x > 1400) -function cropAt(x) return x <= 1400 and MAIZE or nil end -FSDensityMapUtil = { - getFieldDataAtWorldPosition = function() return 1, 2 end, - getFruitTypeIndexAtWorldPos = function(x, z) return cropAt(x) end, - getFruitArea = function() return 0, 10 end, -} -FieldDensityMap = {SPRAY_LEVEL = 4, LIME_LEVEL = 5, PLOW_LEVEL = 6} -DensityCoordType = {POINT_VECTOR_VECTOR = 1} -DensityMapModifier = {new = function() return {setParallelogramWorldCoords = function() end, executeGet = function() return 1, 1 end} end} -g_fruitTypeManager = { - getFruitTypeByIndex = function() return {name = "MAIZE", fillType = {index = 60, title = "Majs"}, minHarvestingGrowthState = 7, maxHarvestingGrowthState = 7, cutState = 9} end, - getConverterDataByName = function() return {} end, -} -g_fillTypeManager = {getConverterDataByName = function() return {} end, getFillTypeNameByIndex = function() return "X" end} -g_farmlandManager = {getFarmlandOwner = function() return 1 end} -local polygon = {getVerticesList = function() return {0, 0, 1600, 0, 1600, 600, 0, 600} end} -local field = {getId = function() return 108 end, farmland = {id = 108}, posX = 1500, posZ = 50, - getDensityMapPolygon = function() return polygon end} -g_fieldManager = {getFields = function() return {field} end} -g_currentMission = {terrainRootNode = 1, missionInfo = {}, fieldGroundSystem = { - getDensityMapData = function() return 1, 0, 2 end, getMaxValue = function() return 2 end}} -local adEnv = {ADGraphManager = { - getMapMarkers = function() return {{id = 900, name = "F108"}} end, - -- markøren står 5 m uden for marken ved den modsatte kant: 1505 m fra skiltet - getWayPointById = function() return {x = -5, z = 300} end, -}} - -dofile("FS25_ADSmartPickup/adFields.lua") -local now = 0 -for _ = 1, 1000 do - if ADFields.getInfo(108) ~= nil then break end - ADFields.scanStep(adEnv, 1, now) - now = now + 16 -end -local info = ADFields.getInfo(108) -check("PG1 marken er scannet", info ~= nil, true) -check("PG2 omrids fra getDensityMapPolygon", info and #info.verts, 4) -check("PG3 majsen findes trods skiltet på bar stribe", info and info.fruitName, "MAIZE") -check("PG4 ikke bar mark", info and info.kind, "harvest") -check("PG5 markør ved kanten findes (afstand til omridset)", info and info.markerId, 900) -check("PG6 afstand = til kanten", info and info.markerDistance, 5) -check("PG6b markens form-signatur (kursus-genbrug)", info and info.shapeSig, "n=4;a=960000;c=800,300") - --- PG7-9: field.fieldState er spillets sandhed (fields.xml: MAIZE growthState 2); jord-bits fra --- getFieldDataAtWorldPosition (2. værdi) er IKKE vækststadie. Høstet mark (cutState) = bar, vist som stub. - -field.fieldState = {fruitTypeIndex = MAIZE, growthState = 2} -FSDensityMapUtil.getFieldDataAtWorldPosition = function() return true, 0 end -- jord-bits 0 lignede "ingen vækst" -local function rescan() - ADFields.cache, ADFields.listCache, ADFields.activeScan, ADFields.lastScanStart = {}, nil, nil, nil - for _ = 1, 2000 do - if ADFields.getInfo(108) ~= nil then break end - ADFields.scanStep(adEnv, 1, now) - now = now + 16 - end - return ADFields.getInfo(108) -end -info = rescan() -check("PG7 majs i vækst (fieldState), ikke bar", info and info.kind, "harvest") -check("PG7 fase vokser", info and info.phase, "growing") -field.fieldState = {fruitTypeIndex = MAIZE, growthState = 9} -info = rescan() -check("PG8 høstet (cutState) = bar", info and info.kind, "bare") -check("PG8 stub af majs", info and info.stubbleOf, "Majs") -field.fieldState = {fruitTypeIndex = 0, growthState = 0} -cropAt = function() return nil end -info = rescan() -check("PG9 ingen afgrøde = bar uden stub", info and info.kind .. "/" .. tostring(info.stubbleOf), "bare/nil") --- PG10-12: bygget-over mark (fields.xml: groundType NONE — gårdsplads/stald/biogas) er ikke med i listen -field.fieldState = {fruitTypeIndex = 0, growthState = 0, groundType = 0} -info = rescan() -check("PG10 ingen markjord = kan ikke dyrkes", info and info.cultivable, false) -check("PG10 ikke i marklisten", #ADFields.list(), 0) -field.fieldState = {fruitTypeIndex = MAIZE, growthState = 2, groundType = 7} -cropAt = function(x) return MAIZE end -info = rescan() -check("PG11 markjord = kan dyrkes", info and info.cultivable, true) -check("PG11 i marklisten", #ADFields.list(), 1) -check("PG12 ingen punkter på markjord", ADFields.isCultivable(nil, 0), false) - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFieldsScan.lua b/tests/test_adFieldsScan.lua deleted file mode 100644 index a2f56cd..0000000 --- a/tests/test_adFieldsScan.lua +++ /dev/null @@ -1,94 +0,0 @@ --- Kør: luajit tests/test_adFieldsScan.lua (fra repo-roden) --- Markscanningen som arbejdskø: bliver færdig, giver rigtigt resultat, og intet frame laver mere end ét --- prøvepunkts arbejde (in-game 26/9: en hel mark i ét frame gav hak). -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end -Logging = {warning = function(fmt, ...) print("WARN " .. string.format(fmt, ...)) end, info = function() end} - --- falsk verden: én mark (F7) med hvede der er klar, polygon med 4 hjørner -local GRASS, WHEAT = 1, 3 -local nodes = {[101] = {0, 0}, [102] = {100, 0}, [103] = {100, 100}, [104] = {0, 100}} -function getWorldTranslation(node) local p = nodes[node]; return p[1], 0, p[2] end -function getTerrainHeightAtWorldPos() return 0 end -local calls = 0 -FSDensityMapUtil = { - getFieldDataAtWorldPosition = function() calls = calls + 1; return true, 170 end, -- på mark (2. værdi = jord-bits, IKKE vækst) - getFruitTypeIndexAtWorldPos = function() calls = calls + 1; return WHEAT end, - getFruitArea = function(_, _, _, _, _, _, _, any) calls = calls + 1; return 0, any and 10 or 10 end, -} -FieldDensityMap = {SPRAY_LEVEL = 4, LIME_LEVEL = 5, PLOW_LEVEL = 6} -DensityCoordType = {POINT_VECTOR_VECTOR = 1} -DensityMapModifier = {new = function() return {setParallelogramWorldCoords = function() end, executeGet = function() calls = calls + 1; return 1, 1 end} end} -g_fruitTypeManager = { - getFruitTypeByIndex = function(_, index) return {name = index == WHEAT and "WHEAT" or "GRASS", fillType = {index = 50, title = "Hvede"}, minHarvestingGrowthState = 6, maxHarvestingGrowthState = 7, cutState = 9} end, - getConverterDataByName = function() return {} end, -} -g_fillTypeManager = {getConverterDataByName = function() return {} end, getFillTypeNameByIndex = function() return "X" end} -g_farmlandManager = {getFarmlandOwner = function() return 1 end} --- spillets egen marktilstand (field.fieldState, som fields.xml): afgrøde + vækststadie -local field = {getId = function() return 7 end, farmland = {id = 70}, polygonPoints = {101, 102, 103, 104}, posX = 50, posZ = 50, - fieldState = {fruitTypeIndex = WHEAT, growthState = 6}} -g_fieldManager = {getFields = function() return {field} end} -g_currentMission = {terrainRootNode = 1, missionInfo = {}, fieldGroundSystem = { - getDensityMapData = function() return 1, 0, 2 end, getMaxValue = function() return 2 end}} -local adEnv = {ADGraphManager = { - getMapMarkers = function() return {{id = 900, name = "F7"}, {id = 901, name = "Silo"}} end, - getWayPointById = function(_, id) return {x = 40, z = 40} end, -}} - -dofile("FS25_ADSmartPickup/adFields.lua") - -local maxPerFrame, frames = 0, 0 -local now = 0 -for _ = 1, 200 do - calls = 0 - ADFields.scanStep(adEnv, 1, now) - if calls > maxPerFrame then maxPerFrame = calls end - frames = frames + 1 - now = now + 16 - if ADFields.getInfo(7) ~= nil then break end -end --- markliste først, så marken (efter SCAN_INTERVAL) — kør videre til marken er scannet -for _ = 1, 400 do - if ADFields.getInfo(7) ~= nil then break end - calls = 0 - ADFields.scanStep(adEnv, 1, now) - if calls > maxPerFrame then maxPerFrame = calls end - now = now + 16 -end -local info = ADFields.getInfo(7) -check("SC1 marken er scannet", info ~= nil, true) -check("SC2 afgrøde", info and info.fruitName, "WHEAT") -check("SC3 art = høst", info and info.kind, "harvest") -check("SC4 klar-andel", info and info.readyRatio, 1) -check("SC5 fase klar", info and info.phase, "ready") -check("SC6 markør F7", info and info.markerId, 900) -check("SC7 prøvepunkter", info and #info.points, 5) -check("SC8 gødning læst", info and info.fert, 1) -check("SC9 højst ét punkts arbejde pr. frame (<= 4 opslag)", maxPerFrame <= 4, true) -print(" (flest opslag i ét frame: " .. maxPerFrame .. ")") -check("SC10 marklisten", #ADFields.list(), 1) - --- SC11: med ur kører flere opgaver pr. frame inden for budgettet (0,5 ms), højst MAX_TASKS_PER_FRAME -local fakeTime = 0 -netGetTime = function() fakeTime = fakeTime + 0.1; return fakeTime end -- hver opgave "koster" 0,1 ms -ADFields.cache, ADFields.listCache, ADFields.activeScan, ADFields.lastScanStart = {}, nil, nil, nil -local frames2 = 0 -for _ = 1, 400 do - if ADFields.getInfo(7) ~= nil then break end - ADFields.scanStep(adEnv, 1, now) - now = now + 16 - frames2 = frames2 + 1 -end -check("SC11 scannet igen med ur", ADFields.getInfo(7) ~= nil, true) -check("SC11 færre frames end én opgave pr. frame", frames2 < frames + 0, true) -print(" (frames uden ur: " .. frames .. ", med ur: " .. frames2 .. ")") -netGetTime = nil - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFieldsShape.lua b/tests/test_adFieldsShape.lua deleted file mode 100644 index f0722ba..0000000 --- a/tests/test_adFieldsShape.lua +++ /dev/null @@ -1,44 +0,0 @@ --- Kør: luajit tests/test_adFieldsShape.lua (fra repo-roden) --- Markens form-fingeraftryk (genbrug af Courseplay-kurser): antal hjørner, areal og tyngdepunkt, afrundet så --- små float-forskelle ikke tæller — men en ændret mark (nyt hjørne, flyttet kant) giver en ny signatur. -dofile("FS25_ADSmartPickup/adFields.lua") -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - -local function rect(x0, z0, w, h) - return {{x = x0, z = z0}, {x = x0 + w, z = z0}, {x = x0 + w, z = z0 + h}, {x = x0, z = z0 + h}} -end - --- S1: rektangel 1600 x 600 -> 4 hjørner, 960.000 m², tyngdepunkt (800, 300) -check("S1 rektangel", ADFields.shapeSignature(rect(0, 0, 1600, 600)), "n=4;a=960000;c=800,300") --- S2: omløbsretning er ligegyldig (arealet er absolut) -local reversed = {} -for index = 4, 1, -1 do table.insert(reversed, rect(0, 0, 1600, 600)[index]) end -check("S2 modsat omløb", ADFields.shapeSignature(reversed), "n=4;a=960000;c=800,300") --- S3: små float-forskelle (cm) tæller ikke -local noisy = rect(0.0003, -0.0002, 1600.0004, 599.9997) -check("S3 float-støj", ADFields.shapeSignature(noisy), "n=4;a=960000;c=800,300") --- S4: flyttet kant (1 m bredere) -> ny signatur -check("S4 kant flyttet", ADFields.shapeSignature(rect(0, 0, 1601, 600)) ~= ADFields.shapeSignature(rect(0, 0, 1600, 600)), true) --- S5: ekstra hjørne på kanten (samme areal) -> ny signatur -local extra = {{x = 0, z = 0}, {x = 800, z = 0}, {x = 1600, z = 0}, {x = 1600, z = 600}, {x = 0, z = 600}} -check("S5 ekstra hjørne", ADFields.shapeSignature(extra), "n=5;a=960000;c=800,300") --- S6: samme form et andet sted på kortet -> anden signatur -check("S6 flyttet mark", ADFields.shapeSignature(rect(100, 0, 1600, 600)), "n=4;a=960000;c=900,300") --- S7: tom/ingen polygon -> nil -check("S7 tom", ADFields.shapeSignature({}), nil) -check("S8 nil", ADFields.shapeSignature(nil), nil) --- S9: under 3 hjørner er ingen flade -> nil -check("S9 to punkter", ADFields.shapeSignature({{x = 0, z = 0}, {x = 1, z = 1}}), nil) --- S10: trekant: areal 50, tyngdepunkt (10/3, 10/3) -> (3, 3) -check("S10 trekant", ADFields.shapeSignature({{x = 0, z = 0}, {x = 10, z = 0}, {x = 0, z = 10}}), "n=3;a=50;c=3,3") --- S11: negative koordinater afrundes symmetrisk -check("S11 negative", ADFields.shapeSignature(rect(-1000, -500, 200, 100)), "n=4;a=20000;c=-900,-450") - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFleetNames.lua b/tests/test_adFleetNames.lua deleted file mode 100644 index 61918de..0000000 --- a/tests/test_adFleetNames.lua +++ /dev/null @@ -1,33 +0,0 @@ --- Kør: luajit tests/test_adFleetNames.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adFleetNames.lua") -local N = ADFleetNames -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end -local plan = N.plan({ - {id = "b", model = "Puma 260 CVX"}, {id = "a", model = "Puma 260 CVX"}, {id = "c", model = "Fastrac 4220"}, - {id = "d", model = "Cow Food", inRun = true, name = "Cow Food 1"}, {id = "e", model = "CR11", selfPropelled = true, name = "Mejetærsker"}, -}) -local byId = {} -for _, entry in ipairs(plan) do byId[entry.id] = entry end -check("navne: samme model nummereres (efter id)", byId.a.name, "Puma 260 CVX 1") -check("navne: anden af samme model", byId.b.name, "Puma 260 CVX 2") -check("navne: enlig model uden nummer", byId.c.name, "Fastrac 4220") -check("navne: flow-traktor beholder navn", byId.d.name, "Cow Food 1") -check("navne: selvkørende beholder navn", byId.e.name, "Mejetærsker") -check("parkering: P + navn", byId.a.parkName, "P Puma 260 CVX 1") -check("parkering: flow-traktor", byId.d.parkName, "P Cow Food 1") -check("omdøb: kun hvis ændret", byId.d.rename, false) -check("omdøb: ny navn", byId.a.rename, true) --- R7: parkeringer omdøbes kun for maskiner der selv omdøbes, og ikke delte parkeringer -check("R7 flow-traktor: parkering røres ikke", byId.d.renamePark, false) -check("R7 selvkørende: parkering røres ikke", byId.e.renamePark, false) -check("R7 omdøbt traktor: parkering omdøbes", byId.a.renamePark, true) -local shared = N.plan({{id = "x", model = "M", park = 5}, {id = "y", model = "Q", park = 5}}, {[5] = 2}) -check("R7 delt parkering røres ikke", shared[1].renamePark, false) -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFlowGuide.lua b/tests/test_adFlowGuide.lua deleted file mode 100644 index 7fe3e1a..0000000 --- a/tests/test_adFlowGuide.lua +++ /dev/null @@ -1,314 +0,0 @@ --- Kør: luajit tests/test_adFlowGuide.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adGuide.lua") -dofile("FS25_ADSmartPickup/adRuns.lua") -dofile("FS25_ADSmartPickup/adFlowView.lua") -dofile("FS25_ADSmartPickup/adFlowGuide.lua") -local FG, G = ADFlowGuide, ADGuide -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - -local function newRun(kind, changes) - local store, run = ADRuns.createRun(ADRuns.newStore(), "Mælk ud", kind) - if changes ~= nil then store = ADRuns.updateRun(store, run.id, changes) end - return ADRuns.getRun(store, run.id) -end -local function ctx(extra) - local context = { - buildings = { - {id = "b1", name = "Kostald", label = "Kostald M", hasMarker = true, text = "Kostald — M 5 m"}, - {id = "b2", name = "Silo", label = "Silo M", hasMarker = true, text = "Silo — M 3 m"}, - {id = "b3", name = "Lade", hasMarker = false, text = "Lade — INGEN MARKØR"}, - }, - fillTypes = {{name = "HAY", title = "Hø"}, {name = "STRAW", title = "Halm"}}, - products = {{name = "MILK", title = "Mælk"}, {name = "EGG", title = "Æg"}}, - storeBuildings = {{id = "s1", name = "Silo Nord", text = "Silo Nord — M 5 m"}, {id = "s2", name = "Palleskur", text = "Palleskur — M 2 m"}}, - waitPools = {{group = "Wait A", count = 2}}, - vehicles = { - {id = "t1", label = "Traktor 1", text = "Traktor 1 — 1000 l — OK"}, - {id = "t2", text = "Traktor 2", otherRunId = 9, otherRunName = "Andet"}, - {id = "t3", text = "Traktor 3", blocked = true}, - {id = "t4", text = "Traktor 4"}, - }, - } - for key, value in pairs(extra or {}) do context[key] = value end - return context -end -local function steps(guide) return table.concat(G.visibleSteps(guide), ",") end -local function findRow(rows, id, item) - for _, row in ipairs(rows) do - if row.id == id and (item == nil or row.item == item) then return row end - end - return nil -end -local function countType(rows, rowType) - local n = 0 - for _, row in ipairs(rows) do if row.type == rowType then n = n + 1 end end - return n -end -local function textKeys(rows) - local keys = {} - for _, row in ipairs(rows) do if row.type == "text" and row.label.key ~= nil then table.insert(keys, row.label.key) end end - return table.concat(keys, ",") -end -local function at(guide, step) return G.jump(guide, step, "close") end - --- ---- trin ---- -local supply = FG.open(newRun("supply"), ctx()) -check("FL1 forsyning", steps(supply), "direction,buildings,goods,rules,wait,tractors,summary") -local out = FG.open(newRun("outbound"), ctx()) -check("FL1 udkørsel uden lager-vare", steps(out), "direction,buildings,goods,rules,wait,tractors,summary") -local outStore = FG.open(newRun("outbound", {products = {{fillType = "MILK", policy = "sellElseStore"}}}), ctx()) -check("FL1 udkørsel med lager", steps(outStore), "direction,buildings,goods,store,rules,wait,tractors,summary") -local new = FG.openNew("Flow 3", ctx()) -check("FL1 nyt flow har navn", steps(new), "name,direction,buildings,goods,rules,wait,tractors,summary") -check("FL1 nyt flow starter på navn", new.step, "name") -check("FL1 nyt flow er forsyning", new.draft.kind, "supply") -check("FL1 nyt flow navn", new.draft.name, "Flow 3") -check("FL1 eksisterende starter på retning", supply.step, "direction") - --- ---- titler følger retningen ---- -check("FL2 til", FG.stepKey("buildings", supply), "spu_guide_flow_step_to") -check("FL2 fra", FG.stepKey("buildings", out), "spu_guide_flow_step_from") -check("FL2 andre", FG.stepKey("rules", out), "spu_guide_flow_step_rules") -check("FL2 hint", FG.hintKey("goods", out), "spu_guide_flow_hint_goods_out") -check("FL2 hint forsyning", FG.hintKey("goods", supply), "spu_guide_flow_hint_goods_in") - --- ---- choose er immutabel ---- -local toOut = FG.choose(supply, "kind", "outbound") -check("FL3 retning skiftet", toOut.draft.kind, "outbound") -check("FL3 gammel urørt", supply.draft.kind, "supply") -local withTarget = FG.choose(supply, "buildings", {item = "b1", on = true}) -check("FL3 forsyning: mål", table.concat(withTarget.draft.targetBuildingIds, ","), "b1") -check("FL3 forsyning: kilder urørt", #withTarget.draft.sourceBuildingIds, 0) -local withSource = FG.choose(out, "buildings", {item = "b2", on = true}) -check("FL3 udkørsel: kilde", table.concat(withSource.draft.sourceBuildingIds, ","), "b2") -local policy = FG.choose(out, "policy", {item = "MILK", value = "store"}) -check("FL3 politik", policy.draft.products[1].policy, "store") -check("FL3 politik gør lager-trin synligt", FG.isVisible("store", policy), true) -local off = FG.choose(policy, "policy", {item = "MILK", value = "off"}) -check("FL3 politik fra fjerner", #off.draft.products, 0) -local replaced = FG.choose(policy, "policy", {item = "MILK", value = "sell"}) -check("FL3 politik erstattes", #replaced.draft.products, 1) -check("FL3 politik ny", replaced.draft.products[1].policy, "sell") -check("FL3 navn", FG.choose(new, "name", "Æg til salg").draft.name, "Æg til salg") -check("FL3 tomt navn ignoreres", FG.choose(new, "name", " ").draft.name, "Flow 3") - --- ---- rækker ---- -local dirRows = FG.rows(supply) -check("FL4 retning valg", findRow(dirRows, "kind").type, "choice") -check("FL4 retning værdi", findRow(dirRows, "kind").value, "supply") -local buildingRows = FG.rows(at(supply, "buildings")) -check("FL4 bygninger med markør", countType(buildingRows, "check"), 2) -check("FL4 vis alle", findRow(buildingRows, "showAllBuildings").type, "toggle") -local allRows = FG.rows(at(FG.choose(supply, "showAllBuildings", true), "buildings")) -check("FL4 vis alle: tre", countType(allRows, "check"), 3) -local noHidden = FG.rows(at(FG.open(newRun("supply"), ctx({buildings = {{id = "b1", name = "Kostald", hasMarker = true, text = "K"}}})), "buildings")) -check("FL4 ingen vis-alle uden skjulte", findRow(noHidden, "showAllBuildings"), nil) -local chosenFirst = FG.rows(at(FG.choose(supply, "buildings", {item = "b2", on = true}), "buildings")) -local firstCheck -for _, row in ipairs(chosenFirst) do if row.type == "check" and firstCheck == nil then firstCheck = row end end -check("FL4 fast rækkefølge (navn)", firstCheck.item, "b1") -check("FL4 valgt markeret", findRow(chosenFirst, "buildings", "b2").value, true) -check("FL4 ingen bygninger", textKeys(FG.rows(at(FG.open(newRun("supply"), ctx({buildings = {}})), "buildings"))), "spu_noBuildings") - -local goodsAuto = FG.rows(at(supply, "goods")) -check("FL5 forsyning auto", findRow(goodsAuto, "ingredientMode").value, "auto") -check("FL5 forsyning auto: ingen liste", countType(goodsAuto, "check"), 0) -local goodsManual = FG.rows(at(FG.choose(supply, "ingredientMode", "manual"), "goods")) -check("FL5 forsyning vælg: varer", countType(goodsManual, "check"), 2) -check("FL5 forsyning vælg uden varer", textKeys(FG.rows(at(FG.choose(FG.open(newRun("supply"), ctx({fillTypes = {}})), "ingredientMode", "manual"), "goods"))), "spu_guide_flow_noGoodsTo") -local goodsOut = FG.rows(at(policy, "goods")) -check("FL5 udkørsel: politik pr. vare", countType(goodsOut, "choice"), 2) -check("FL5 udkørsel: mælk lager", findRow(goodsOut, "policy", "MILK").value, "store") -check("FL5 udkørsel: æg fra", findRow(goodsOut, "policy", "EGG").value, "off") -check("FL5 udkørsel: politikker", #findRow(goodsOut, "policy", "EGG").options, 4) -check("FL5 udkørsel uden varer", textKeys(FG.rows(at(FG.open(newRun("outbound"), ctx({products = {}})), "goods"))), "spu_guide_flow_noProducts") - -local storeRows = FG.rows(at(outStore, "store")) -check("FL6 lager auto", findRow(storeRows, "storeAuto").value, true) -check("FL6 lager auto: ingen liste", countType(storeRows, "check"), 0) -local storeManual = FG.rows(at(FG.choose(outStore, "storeAuto", false), "store")) -check("FL6 lager vælg selv", countType(storeManual, "check"), 2) -local chosenStore = FG.open(newRun("outbound", {products = {{fillType = "MILK", policy = "store"}}, storeBuildingIds = {"s2"}}), ctx()) -check("FL6 valgt lager -> vælg selv", chosenStore.draft.storeAuto, false) - -local rulesOut = FG.rows(at(out, "rules")) -check("FL7 udkørsel: sælg fra", findRow(rulesOut, "sellAtPercent").value, 90) -check("FL7 udkørsel: sælg-trin", #findRow(rulesOut, "sellAtPercent").options, 11) -check("FL7 udkørsel: lager over", findRow(rulesOut, "storeAbovePercent").value, 80) -check("FL7 udkørsel: samlerunde", findRow(rulesOut, "collectRound").type, "toggle") -check("FL7 udkørsel: ingen ture", findRow(rulesOut, "loops"), nil) -local rulesIn = FG.rows(at(supply, "rules")) -check("FL7 forsyning: ture", findRow(rulesIn, "loops").value, 0) -check("FL7 forsyning: ture-valg", #findRow(rulesIn, "loops").options, ADRuns.MAX_LOOPS + 1) -check("FL7 forsyning: 0 = uendeligt", findRow(rulesIn, "loops").options[1].label.key, "spu_loops_infinite") -check("FL7 forsyning: retur", findRow(rulesIn, "returnBelowPercent").value, 50) -check("FL7 forsyning: ingen salg", findRow(rulesIn, "sellAtPercent"), nil) - -local waitRows = FG.rows(at(supply, "wait")) -check("FL8 vent: alle først", findRow(waitRows, "waitPoolGroup").options[1].value, "") -check("FL8 vent: puljer", #findRow(waitRows, "waitPoolGroup").options, 2) - -local assigned = FG.open(newRun("supply", {}), ctx()) -assigned = FG.choose(assigned, "vehicleIds", {item = "t4", on = true}) -local tractorRows = FG.rows(at(assigned, "tractors")) -check("FL9 traktorer: kun egne + ledige", countType(tractorRows, "check"), 2) -check("FL9 egne først", (function() for _, r in ipairs(tractorRows) do if r.type == "check" then return r.item end end end)(), "t4") -check("FL9 skjult andre flows", findRow(tractorRows, "vehicleIds", "t2"), nil) -check("FL9 skjult blokeret", findRow(tractorRows, "vehicleIds", "t3"), nil) -check("FL9 linjer", textKeys(tractorRows), "spu_guide_flow_otherFlows") --- blokerede traktorer: foldet sammen som standard (in-game 27/9: 37 linjer der ikke kunne bruges fyldte trinnet); --- linjen med antallet er et link der folder navn + grund ud og ind -local function blockedLine(rows) - for _, r in ipairs(rows) do if r.type == "text" and r.label.text ~= nil and r.label.text:find("Traktor 3", 1, true) then return r end end - return nil -end -local function blockedLink(rows) - for _, r in ipairs(rows) do if r.type == "link" and r.id == "showBlocked" then return r end end - return nil -end -check("FL9 blokerede foldet sammen som standard", blockedLine(tractorRows), nil) -check("FL9 link med antal", blockedLink(tractorRows) ~= nil and blockedLink(tractorRows).label.key, "spu_guide_flow_blockedShow") -local expanded = FG.choose(assigned, "showBlocked", true) -local expandedRows = FG.rows(at(expanded, "tractors")) -check("FL9 foldet ud viser navn + grund", blockedLine(expandedRows) ~= nil, true) -check("FL9 foldet ud: link skjuler igen", blockedLink(expandedRows).label.key, "spu_guide_flow_blockedHide") -check("FL9 fold ud ændrer ikke kladden", expanded.draft.vehicleIds[1], "t4") -check("FL9 fold sammen igen", blockedLine(FG.rows(at(FG.choose(expanded, "showBlocked", true), "tractors"))), nil) -local inThisRun = FG.open(newRun("supply"), ctx({vehicles = {{id = "t2", text = "T2", otherRunId = 1, otherRunName = "Mælk ud"}}})) -check("FL9 traktor i dette flow er ikke 'andet flow'", countType(FG.rows(at(inThisRun, "tractors")), "check"), 1) - --- ---- resumé ---- -local function lineFor(lines, step) - for _, line in ipairs(lines) do if line.step == step then return line end end - return nil -end -local function partText(line) - local parts = {} - for _, part in ipairs(line.parts) do table.insert(parts, part.key or ("=" .. tostring(part.text))) end - return table.concat(parts, ",") -end -local sumIn = FG.summary(FG.choose(withTarget, "vehicleIds", {item = "t1", on = true})) -check("FL10 navn", partText(lineFor(sumIn, "name")), "=Mælk ud") -check("FL10 retning", partText(lineFor(sumIn, "direction")), "spu_direction_in") -check("FL10 til", partText(lineFor(sumIn, "buildings")), "=Kostald") -check("FL10 til-titel", lineFor(sumIn, "buildings").title.key, "spu_guide_flow_step_to") -check("FL10 varer auto", partText(lineFor(sumIn, "goods")), "spu_guide_flow_goodsAuto") -check("FL10 regler", partText(lineFor(sumIn, "rules")), "spu_guide_flow_sumLoopsInf,spu_guide_flow_sumReturn") -check("FL10 vent", partText(lineFor(sumIn, "wait")), "spu_waitPool_all") -check("FL10 traktorer", partText(lineFor(sumIn, "tractors")), "=Traktor 1") -check("FL10 intet lager-trin", lineFor(sumIn, "store"), nil) --- traktor der ikke findes længere (solgt): aldrig rå id i resuméet (in-game 29/9) -local withGhost = FG.choose(FG.choose(withTarget, "vehicleIds", {item = "t1", on = true}), "vehicleIds", {item = "vehicleGONE", on = true}) -check("FL10b resumé springer manglende over", partText(lineFor(FG.summary(withGhost), "tractors")), "=Traktor 1") -local onlyGhost = FG.choose(withTarget, "vehicleIds", {item = "vehicleGONE", on = true}) -check("FL10b kun manglende -> ingen traktorer", partText(lineFor(FG.summary(onlyGhost), "tractors")), "spu_guide_flow_sumNoTractors") -local sumOut = FG.summary(FG.choose(FG.choose(policy, "collectRound", true), "sellAtPercent", 95)) -check("FL11 retning ud", partText(lineFor(sumOut, "direction")), "spu_direction_out") -check("FL11 fra tom", partText(lineFor(sumOut, "buildings")), "spu_guide_flow_none") -check("FL11 varer", partText(lineFor(sumOut, "goods")), "spu_policy_store") -check("FL11 varenavn foran", lineFor(sumOut, "goods").parts[1].prefix, "Mælk: ") -check("FL11 lager auto", partText(lineFor(sumOut, "store")), "spu_guide_sum_auto") --- varen er kun "lager": salgsgrænsen vises ikke (in-game 29/9) -check("FL11 regler (kun lager)", partText(lineFor(sumOut, "rules")), "spu_guide_flow_sumStoreAbove,spu_guide_flow_sumRound") -check("FL11 lager over", lineFor(sumOut, "rules").parts[1].args[1], 80) -check("FL11 ingen traktorer", partText(lineFor(sumOut, "tractors")), "spu_guide_flow_sumNoTractors") -local manyGoods = FG.summary(FG.choose(FG.choose(FG.choose(supply, "ingredientMode", "manual"), "ingredients", {item = "HAY", on = true}), "ingredients", {item = "STRAW", on = true})) -check("FL12 valgte varer", partText(lineFor(manyGoods, "goods")), "=Hø + Halm") -check("FL12 oversigt: links", countType(FG.rows(G.new(supply.def, supply.draft, supply.context, {step = "summary"})), "link"), 7) - --- ---- resultat ---- -local r = FG.result(FG.choose(FG.choose(policy, "storeAuto", false), "storeBuildingIds", {item = "s1", on = true})) -check("FL13 ikke ny", r.isNew, false) -check("FL13 run-id", r.runId, 1) -check("FL13 retning", r.changes.kind, "outbound") -check("FL13 lager valgt", table.concat(r.changes.storeBuildingIds, ","), "s1") -check("FL13 varer", r.changes.products[1].fillType, "MILK") -check("FL13 lager-resumé bruger navn", (function() for _, l in ipairs(FG.summary(FG.choose(FG.choose(policy, "storeAuto", false), "storeBuildingIds", {item = "s1", on = true}))) do if l.step == "store" then return l.parts[1].text end end end)(), "Silo Nord") -local rAuto = FG.result(FG.choose(chosenStore, "storeAuto", true)) -check("FL13 lager auto -> tom", #rAuto.changes.storeBuildingIds, 0) -local hiddenStore = FG.open(newRun("outbound", {storeBuildingIds = {"s2"}}), ctx()) -check("FL13 lager usynligt: bevares", table.concat(FG.result(hiddenStore).changes.storeBuildingIds, ","), "s2") -local rNew = FG.result(FG.choose(new, "vehicleIds", {item = "t1", on = true})) -check("FL14 nyt", rNew.isNew, true) -check("FL14 navn", rNew.name, "Flow 3") -check("FL14 traktorer", table.concat(rNew.vehicleIds, ","), "t1") -check("FL14 ingen run-id", rNew.runId, nil) -local added, removed = FG.vehicleChanges({"a", "b"}, {"b", "c"}) -check("FL15 tilføjet", table.concat(added, ","), "c") -check("FL15 fjernet", table.concat(removed, ","), "a") - --- ---- forhåndsvisning (til controllerens opslag) ---- -local markers = {b1 = {101, 102}, b2 = {201}} -local preview = FG.previewRun(withSource.draft, function(id) return markers[id] or {} end) -check("FL16 kilder -> markører", table.concat(preview.sourceWayPointIds, ","), "201") -local previewIn = FG.previewRun(withTarget.draft, function(id) return markers[id] or {} end) -check("FL16 mål -> markører", table.concat(previewIn.targetWayPointIds, ","), "101,102") -check("FL16 primært mål", previewIn.targetWayPointId, 101) -check("FL16 kladde urørt", withTarget.draft.targetWayPointIds == nil or #withTarget.draft.targetWayPointIds == 0, true) - - --- ---- solo-traktor + ledig vogn (in-game 29/9: flow kunne ikke oprettes uden at omdøbe en vogns parkering) ---- -local soloCtx = ctx({ - vehicles = { - {id = "s1", label = "Solo 1", text = "Solo 1", solo = true}, - {id = "s2", label = "Solo 2", text = "Solo 2", solo = true}, - {id = "s3", label = "Solo 3", text = "Solo 3", solo = true}, - {id = "t1", label = "Traktor 1", text = "Traktor 1 — OK"}, - }, - wagons = {{id = "w1", label = "P Disponibel Vogn 4"}, {id = "w2", label = "P Disponibel Vogn 5"}}, -}) -local soloGuide = FG.open(newRun("supply"), soloCtx) -local one = FG.choose(soloGuide, "vehicleIds", {item = "s1", on = true}) -check("WG1 solo tildeles første ledige vogn", one.draft.wagons.s1, "w1") -local two = FG.choose(one, "vehicleIds", {item = "s2", on = true}) -check("WG2 næste solo får næste vogn", two.draft.wagons.s2, "w2") -check("WG2 første beholder sin", two.draft.wagons.s1, "w1") -local three = FG.choose(two, "vehicleIds", {item = "s3", on = true}) -check("WG3 ingen ledig vogn tilbage -> ingen tildeling", three.draft.wagons.s3, nil) -local soloRows = FG.rows(at(three, "tractors")) -local wagonRow = findRow(soloRows, "wagon", "s1") -check("WG4 vognvalg under solo-traktor", wagonRow ~= nil and wagonRow.type, "choice") -check("WG4 vognvalg viser tildelt vogn", wagonRow ~= nil and wagonRow.value, "w1") -check("WG4 valgmuligheder uden de andres vogne", wagonRow ~= nil and #wagonRow.options, 1) -local noFree = false -for _, row in ipairs(soloRows) do if row.label ~= nil and row.label.key == "spu_guide_flow_noFreeWagon" then noFree = true end end -check("WG5 solo uden ledig vogn får besked", noFree, true) -check("WG5 almindelig traktor har intet vognvalg", findRow(FG.rows(at(FG.choose(soloGuide, "vehicleIds", {item = "t1", on = true}), "tractors")), "wagon", "t1"), nil) -local swapped = FG.choose(FG.choose(one, "vehicleIds", {item = "s2", on = false}), "wagon", {item = "s1", value = "w2"}) -check("WG6 skift vogn", swapped.draft.wagons.s1, "w2") -local off = FG.choose(two, "vehicleIds", {item = "s1", on = false}) -check("WG7 fravalgt traktor slipper vognen", off.draft.wagons.s1, nil) -local freed = FG.choose(off, "vehicleIds", {item = "s3", on = true}) -check("WG7 frigjort vogn går til næste", freed.draft.wagons.s3, "w1") -check("WG8 resultat har vognene", FG.result(two).wagons.s2, "w2") -check("WG8 resultat uden fravalgte", FG.result(off).wagons.s1, nil) -local remembered = newRun("supply", {vehicleIds = {"s2"}}) -remembered.wagons = {s2 = "w2"} -local reopened = FG.open(remembered, soloCtx) -check("WG9 husket vogn i kladden", reopened.draft.wagons.s2, "w2") -check("WG9 ny solo tager ikke den huskede", FG.choose(reopened, "vehicleIds", {item = "s1", on = true}).draft.wagons.s1, "w1") -check("WG9 kladden kopierer (run urørt)", (function() local g = FG.choose(reopened, "wagon", {item = "s2", value = "w1"}); return remembered.wagons.s2 end)(), "w2") - - --- regler: kun de relevante for varernes politik -local storeOnly = FG.open(newRun("outbound", {products = {{fillType = "MILK", policy = "store"}}}), ctx()) -local function keys(rows) local r = {} for _, row in ipairs(rows) do if row.id then table.insert(r, row.id) end end return table.concat(r, ",") end -check("RL1 kun lager: ingen salgsvalg", keys(FG.rows(at(storeOnly, "rules"))), "storeAbovePercent,collectRound") -local sellOnly = FG.open(newRun("outbound", {products = {{fillType = "MILK", policy = "sell"}}}), ctx()) -check("RL2 kun salg: ingen lagervalg", keys(FG.rows(at(sellOnly, "rules"))), "sellAtPercent,sellAbovePercent,collectRound") -local function ruleKeys(guide) - for _, line in ipairs(FG.summary(guide)) do - if line.step == "rules" then local r = {} for _, p in ipairs(line.parts) do table.insert(r, p.key) end return table.concat(r, ",") end - end -end -check("RL3 resumé kun lager", ruleKeys(storeOnly), "spu_guide_flow_sumStoreAbove") -check("RL4 resumé kun salg", ruleKeys(sellOnly), "spu_guide_flow_sumSellAt") - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFlowView.lua b/tests/test_adFlowView.lua deleted file mode 100644 index 3dab2b1..0000000 --- a/tests/test_adFlowView.lua +++ /dev/null @@ -1,105 +0,0 @@ --- Kør: luajit tests/test_adFlowView.lua (fra repo-roden) --- Ren oprydning af Flows-fanen: tavlens rækkefølge, hvilke traktorer og bygninger der vises. - -dofile("FS25_ADSmartPickup/adFlowView.lua") -local V = ADFlowView - -local failures = 0 -local function check(name, actual, expected) - if actual == expected then - print("OK " .. name) - else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end -local function ids(list, field) - local out = {} - for _, item in ipairs(list) do table.insert(out, tostring(item[field or "id"])) end - return table.concat(out, ",") -end - --- FV1: tavlen — forsyning (Ind) først, så udkørsel (Ud); hver gruppe sorteret efter navn (naturligt) -local runs = { - {id = 1, name = "Pig Food", kind = "supply"}, - {id = 2, name = "Milk Sale", kind = "outbound"}, - {id = 3, name = "Cow Food", kind = "supply"}, - {id = 4, name = "Biogas 10", kind = "supply"}, - {id = 5, name = "Biogas 9", kind = "supply"}, - {id = 6, name = "Digestate", kind = "outbound"}, -} -local groups = V.boardGroups(runs) -check("FV1 to grupper", #groups, 2) -check("FV1 Ind først", groups[1].kind, "supply") -check("FV1 Ind sorteret (Biogas 9 før 10)", ids(groups[1].runs), "5,4,3,1") -check("FV1 Ud sorteret", ids(groups[2].runs), "6,2") -check("FV1 samlet rækkefølge til vælgeren", ids(V.orderedRuns(runs)), "5,4,3,1,6,2") -check("FV1 tom gruppe udelades", #V.boardGroups({{id = 1, name = "A", kind = "supply"}}), 1) - --- FV2: traktorer — egne først, så ledige brugbare; andre flows og låste kun med "vis alle" -local vehicles = { - {id = "a", label = "Zetor", state = "free"}, - {id = "b", label = "Fendt", state = "in"}, - {id = "c", label = "Case", state = "other"}, - {id = "d", label = "Claas", state = "blocked"}, - {id = "e", label = "Deutz", state = "free"}, -} -check("FV2 standard: egne + ledige, sorteret", ids(V.vehicleRows(vehicles, false)), "b,e,a") -check("FV2 vis alle: + andre flows + låste til sidst", ids(V.vehicleRows(vehicles, true)), "b,e,a,c,d") -check("FV2 antal skjulte", V.hiddenCount(vehicles, V.vehicleRows(vehicles, false)), 2) - --- FV3: bygninger — valgte først; uden markør skjult medmindre valgt eller "vis alle" -local buildings = { - {id = "s2", name = "Silo 2", hasMarker = true}, - {id = "s10", name = "Silo 10", hasMarker = true}, - {id = "x", name = "Gammel lade", hasMarker = false}, - {id = "y", name = "Anden uden", hasMarker = false}, - {id = "a", name = "Anlæg", hasMarker = true}, -} -local chosen = {s10 = true, y = true} --- fast rækkefølge (et tryk må ikke flytte rækken — in-game 26/9 hoppede valgte drops op i toppen) -check("FV3 standard: fast rækkefølge efter navn, valgte bliver hvor de er", ids(V.buildingRows(buildings, chosen, false)), "y,a,s2,s10") -check("FV3 vis alle: uden markør til sidst", ids(V.buildingRows(buildings, chosen, true)), "y,a,s2,s10,x") --- ens bygningsnavne (5 x "BGA (1) : DOB 115") sorteres efter markørnavnet -local drops = { - {id = "d5", name = "DOB", label = "Biogas Drop 5", hasMarker = true}, - {id = "d3", name = "DOB", label = "Biogas Drop 3", hasMarker = true}, - {id = "d1", name = "DOB", label = "Biogas Drop 1", hasMarker = true}, - {id = "d4", name = "DOB", label = "Biogas Drop 4", hasMarker = true}, - {id = "d2", name = "DOB", label = "Biogas Drop 2", hasMarker = true}, -} -check("FV3 ens navne efter markør", ids(V.buildingRows(drops, {d2 = true}, false)), "d1,d2,d3,d4,d5") - --- FV4: ens navne slås sammen med antal ("Chicken Coop x5"), rækkefølge efter første forekomst -check("FV4 sammenlæg", V.joinCounted({"Chicken Coop", "Chicken Coop", "Cow Barn", "Chicken Coop"}, " + ", "-"), "Chicken Coop x3 + Cow Barn") -check("FV4 ingen dubletter", V.joinCounted({"A", "B"}, " + ", "-"), "A + B") -check("FV4 tom -> tekst", V.joinCounted({}, " + ", "(ingen)"), "(ingen)") - --- FV-wrap: lange tekstrækker ombrydes ved mellemrum; indrykning gentages; kort tekst uændret -local wrapped = V.wrapText(" Prepared and sown with Sugar Beet as soon as it is sowing time for it", 30) -check("FVwrap flere linjer", #wrapped > 1, true) -check("FVwrap første linje indrykket", string.sub(wrapped[1], 1, 4), " ") -check("FVwrap anden linje indrykket", string.sub(wrapped[2], 1, 4), " ") -local maxLen = 0 -for _, line in ipairs(wrapped) do if #line > maxLen then maxLen = #line end end -check("FVwrap ingen linje over grænsen", maxLen <= 30, true) -check("FVwrap intet ord tabt", table.concat(wrapped, " "):gsub("%s+", " "):gsub("^ ", ""), "Prepared and sown with Sugar Beet as soon as it is sowing time for it") -check("FVwrap kort tekst", V.wrapText("Kort", 30)[1], "Kort") -check("FVwrap langt ord deles ikke", V.wrapText("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 10)[1], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") - - --- salgs-/lagerregler vises kun når en vare bruger dem (in-game 29/9: 'sell from 90 %' på et rent lager-flow) -local sells, stores = ADFlowView.policyUse({"store"}) -check("politik: kun lager -> sælger ikke", sells, false) -check("politik: kun lager -> lagrer", stores, true) -sells, stores = ADFlowView.policyUse({"sell"}) -check("politik: kun salg", tostring(sells) .. tostring(stores), "truefalse") -sells, stores = ADFlowView.policyUse({"sellElseStore", "off"}) -check("politik: sælg ellers lager -> begge", tostring(sells) .. tostring(stores), "truetrue") -sells, stores = ADFlowView.policyUse({}) -check("politik: ingen varer endnu -> vis begge", tostring(sells) .. tostring(stores), "truetrue") -sells, stores = ADFlowView.policyUse({"off"}) -check("politik: kun fra -> vis begge", tostring(sells) .. tostring(stores), "truetrue") - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFlowWagons.lua b/tests/test_adFlowWagons.lua deleted file mode 100644 index bdee32f..0000000 --- a/tests/test_adFlowWagons.lua +++ /dev/null @@ -1,140 +0,0 @@ --- Kør: luajit tests/test_adFlowWagons.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adRuns.lua") -dofile("FS25_ADSmartPickup/adFlowWagons.lua") -local W = ADFlowWagons -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end --- gem/indlæs: traktor -> vogn -local text = W.serialize({vehicleB = "wagon2", vehicleA = "wagon1"}) -check("gem: sorteret", text, "vehicleA=wagon1;vehicleB=wagon2") -local parsed = W.parse(text) -check("indlæs: A", parsed.vehicleA, "wagon1") -check("indlæs: B", parsed.vehicleB, "wagon2") -check("indlæs: tom", next(W.parse("")), nil) -check("indlæs: nil", next(W.parse(nil)), nil) -check("indlæs: skrald springes over", next(W.parse("=x;;y=")), nil) --- husk vogn: ny tabel, gammel urørt -local before = {t1 = "w1"} -local after = W.remember(before, "t1", "w9") -check("husk: ny vogn", after.t1, "w9") -check("husk: original urørt", before.t1, "w1") -check("husk: samme vogn -> samme tabel", W.remember(after, "t1", "w9"), after) --- start: hent / vent / start -check("start: har redskab bagpå", W.startAction({hasRear = true, wagonId = "w1", wagonExists = true, hasConnect = true}), "start") -check("start: ingen husket vogn", W.startAction({}), "start") -check("start: vognen findes ikke", W.startAction({wagonId = "w1", wagonExists = false, hasConnect = true}), "start") -check("start: vognen uden Connect", W.startAction({wagonId = "w1", wagonExists = true, hasConnect = false}), "start") -check("start: vognen på en anden", W.startAction({wagonId = "w1", wagonExists = true, hasConnect = true, wagonAttached = true}), "wait") -check("start: vognen lånt ud", W.startAction({wagonId = "w1", wagonExists = true, hasConnect = true, wagonBusy = true}), "wait") -check("start: hent", W.startAction({wagonId = "w1", wagonExists = true, hasConnect = true}), "fetch") --- ejer: flowets vogn er flowets — også når flowet er stoppet (in-game 29/9: marksilo lånte Cow Food-vognen) -local runs = {{id = 1, stopped = false, wagons = {t1 = "w1"}}, {id = 2, stopped = true, wagons = {t2 = "w2"}}} -check("ejer: kørende flow holder", W.isHeld(runs, "w1"), true) -check("ejer: stoppet flow holder stadig", W.isHeld(runs, "w2"), true) -check("ejer: ukendt vogn", W.isHeld(runs, "w3"), false) -check("ejer: flowet findes", W.ownerOf(runs, "w2").id, 2) --- bygning væk: to tjek i træk -local count, gone = W.missingStep(0, true) -check("væk: første tjek -> ikke endnu", gone, false) -count, gone = W.missingStep(count, true) -check("væk: andet tjek -> væk", gone, true) -count, gone = W.missingStep(1, false) -check("væk: fundet igen nulstiller", count, 0) -check("væk: fundet igen -> ikke væk", gone, false) --- manglende bygninger i et flow -local exists = {a = true, b = true} -check("mangler: alle findes", #W.missingBuildings({sourceBuildingIds = {"a"}, targetBuildingIds = {"b"}}, exists, exists), 0) -check("mangler: én væk", W.missingBuildings({targetBuildingIds = {"a", "c"}, storeBuildingIds = {}}, exists, {a = true, c = true})[1], "c") --- review v1.51 -check("R1 traktoren er lånt ud af puljen -> vent", W.startAction({tractorBusy = true, hasRear = true, wagonId = "w1", wagonExists = true, hasConnect = true}), "wait") -local runs2 = {{id = 1, stopped = true, wagons = {t1 = "w1"}}, {id = 2, stopped = false, wagons = {t9 = "w1"}}} -check("R4 holdt hvis ET kørende flow har vognen", W.isHeld(runs2, "w1"), true) -local seen = {a = true, c = true} -check("R+ bygning aldrig set i sessionen tæller ikke som væk", #W.missingBuildings({targetBuildingIds = {"b"}}, {a = true}, seen), 0) -check("R+ bygning set og nu væk", W.missingBuildings({targetBuildingIds = {"c"}}, {a = true}, seen)[1], "c") --- gæt flowets vogn ud fra parkeringsnavnet (ingen husket vogn: vognene var sat på plads før v1.51) -local cands = {{id = "v1", parkName = "P Cow Food vogn 1"}, {id = "v3", parkName = "P Cow Food vogn 3"}, - {id = "g1", parkName = "P Goat Food vogn 1"}, {id = "v2", parkName = "P Cow Food vogn 2"}} -check("gæt: samme nummer", W.guessWagon("Cow Food", 3, cands, {}), "v3") -check("gæt: nummer optaget -> anden med flowets navn", W.guessWagon("Cow Food", 3, cands, {v3 = true}), "v1") -check("gæt: andet flow", W.guessWagon("Goat Food", 2, cands, {}), "g1") -check("gæt: intet passer", W.guessWagon("Pig Food", 1, cands, {}), nil) -check("gæt: store/små bogstaver", W.guessWagon("cow food", 2, cands, {}), "v2") -check("gæt: 'Cow Food 1' matcher ikke 'Cow Food 10'", W.guessWagon("Cow Food", 1, {{id = "x", parkName = "P Cow Food vogn 10"}, {id = "y", parkName = "P Cow Food vogn 1"}}, {}), "y") --- tøm vognen før den parkeres (flow stoppet): mål -> silo/fabrik -> hjem -check("tøm: tom vogn -> hjem", W.emptyStep("start", {cargo = 0}), "home") -check("tøm: last -> flowets mål", W.emptyStep("start", {cargo = 5000, hasTarget = true}), "target") -check("tøm: last uden mål -> silo", W.emptyStep("start", {cargo = 5000, hasTarget = false}), "dump") -check("tøm: kører stadig -> vent", W.emptyStep("target", {cargo = 5000, adActive = true}), "wait") -check("tøm: mål fuldt, rest -> silo", W.emptyStep("target", {cargo = 2000}), "dump") -check("tøm: målet tog det hele -> hjem", W.emptyStep("target", {cargo = 0}), "home") -check("tøm: silo tog det -> hjem", W.emptyStep("dump", {cargo = 0}), "home") -check("tøm: silo kunne ikke -> giv op, hjem", W.emptyStep("dump", {cargo = 2000}), "giveUp") -check("tøm: for længe -> giv op", W.emptyStep("target", {cargo = 5000, adActive = true, elapsedMs = W.EMPTY_TIMEOUT_MS}), "giveUp") --- flowet startes igen mens traktoren tømmer/kører hjem fra et stop: hjemturen afbrydes, den kører videre med vognen --- (in-game 29/9: Stop+Start med 1 s mellemrum -> tømning ved forkert mål i 10 min, vogn sat af, traktor i garage) -check("genstart: tømmer -> afbryd", W.abortHomeTripOnStart({emptying = true}), true) -check("genstart: flowets hjemtur -> afbryd", W.abortHomeTripOnStart({flowHoming = true}), true) -check("genstart: puljens hjemtur (markarbejde) -> rør ikke", W.abortHomeTripOnStart({poolReturning = true}), false) -check("genstart: intet i gang", W.abortHomeTripOnStart({}), false) --- tøm-mål: kun flowets egne mål — AD's 2. markør kan være forældet (æg-markør) eller et Wait-punkt (in-game 29/9) -check("tøm-mål: AD's mål er et af flowets", W.emptyTarget(12638, {12636, 12638}), 12638) -check("tøm-mål: forældet/Wait-markør -> flowets første mål", W.emptyTarget(12658, {12636, 12638}), 12636) -check("tøm-mål: intet AD-mål -> flowets første", W.emptyTarget(nil, {12636}), 12636) -check("tøm-mål: flow uden mål -> intet (silo)", W.emptyTarget(12658, {}), nil) -check("tøm-mål: ingen liste -> intet", W.emptyTarget(12658, nil), nil) --- ledig traktor (puljen/markarbejde/marksilo må låne den): kun solo fra et stoppet flow -check("ledig: intet flow", W.tractorFree({}), true) -check("ledig: kørende flow", W.tractorFree({hasRun = true}), false) -check("ledig: stoppet flow, solo", W.tractorFree({hasRun = true, stopped = true}), true) -check("ledig: stoppet flow, flow-vogn bagpå", W.tractorFree({hasRun = true, stopped = true, carriesFlowWagon = true}), false) -check("ledig: stoppet flow, tømmer/kører hjem", W.tractorFree({hasRun = true, stopped = true, homeTrip = true}), false) --- venteårsag i loggen (før stod der altid "i brug andetsteds") -check("vent-årsag: traktor optaget", W.waitReason({tractorBusy = true}), "tractorBusy") -check("vent-årsag: vogn på en anden", W.waitReason({wagonAttached = true}), "wagonAttached") -check("vent-årsag: vogn reserveret", W.waitReason({wagonBusy = true}), "wagonReserved") --- tømning går i stå ved et fuldt mål (in-game 29/9: fåre-/BGA-vogne stod 10 min ved fuldt mål efter Stop alt) -check("tøm: står ved fuldt mål -> silo", W.emptyStep("target", {cargo = 5000, adActive = true, stalled = true}), "dump") -check("tøm: står ved silo uden at tømme -> næste silo", W.emptyStep("dump", {cargo = 5000, adActive = true, stalled = true, dumpAttempts = 1}), "dump") -check("tøm: 3 siloer prøvet -> giv op", W.emptyStep("dump", {cargo = 5000, adActive = true, stalled = true, dumpAttempts = W.MAX_DUMP_ATTEMPTS}), "giveUp") -check("tøm: kører mod målet (ikke i stå) -> vent", W.emptyStep("target", {cargo = 5000, adActive = true, stalled = false}), "wait") -check("i stå: tæt på, uændret længe", W.isStalled({nearTarget = true, sinceChangeMs = W.EMPTY_STALL_MS}), true) -check("i stå: tæt på, lige ankommet", W.isStalled({nearTarget = true, sinceChangeMs = 5000}), false) -check("i stå: på vej (langt væk)", W.isStalled({nearTarget = false, sinceChangeMs = 999999}), false) --- hent vogn: ét nyt forsøg efter "stoppedAway" (AD standsede undervejs — fx spærret af en anden traktor) -check("hent igen: stoppedAway første gang", W.retryFetch("stoppedAway (37 m fra markøren)", 0), true) -check("hent igen: stoppedAway anden gang -> nej", W.retryFetch("stoppedAway", 1), false) -check("hent igen: noAttachable -> nej (markøren skal rettes)", W.retryFetch("noAttachable", 0), false) --- manuel påkobling (spilleren sendte traktoren til Connect): traktor i et kørende flow startes bagefter --- (in-game 29/9: Cow Food 3 hentede sin vogn manuelt efter et fejlet forsøg og blev holdende) -check("manuel kobling: kørende flow, egen vogn -> start", W.startAfterManualAttach({flowRunning = true, ownWagon = true}), true) -check("manuel kobling: stoppet flow -> nej", W.startAfterManualAttach({flowRunning = false, ownWagon = true}), false) -check("manuel kobling: AD kører allerede -> nej", W.startAfterManualAttach({flowRunning = true, ownWagon = true, adActive = true}), false) -check("manuel kobling: fremmed vogn -> nej", W.startAfterManualAttach({flowRunning = true, ownWagon = false}), false) --- husk vogn ved hjemtur: kun når flowet KØRTE med den (stop); ikke ved oprydning efter indlæsning, hvor traktoren --- kan have et puljeredskab på fra et lån (in-game 29/9: Biogas Silage "huskede" en læssevogn fra markarbejdet) -check("husk ved stop af kørende flow", W.shouldRememberOnHome("stop"), true) -check("husk ikke ved oprydning efter indlæsning", W.shouldRememberOnHome("afterLoad"), false) --- forældreløs påkobling (hentet før genindlæsning, opgaven tabt): sættes tilbage efter 2 min uden ejer -check("forældreløs: ingen ejer, længe nok -> hjem", W.orphanAction({owned = false, idleMs = W.ORPHAN_MS}), "home") -check("forældreløs: har ejer -> intet", W.orphanAction({owned = true, idleMs = W.ORPHAN_MS}), "none") -check("forældreløs: for kort tid -> vent", W.orphanAction({owned = false, idleMs = 1000}), "wait") --- parkeret sæt på traktorens egen plads er ikke forældreløst (in-game 29/9: vinmarks-REX blev skilt ad) -check("forældreløs: står på egen parkering -> intet", W.orphanAction({owned = false, atOwnPark = true, idleMs = W.ORPHAN_MS}), "none") - --- rækkefølge for ledige vogne i flow-guiden: navnematch, så disponible, så resten (alfabetisk) -local ranked = W.rankWagons("Sugarbeet Cut", { - {id = "a", label = "P Tedder"}, {id = "b", label = "P Disponibel Vogn 5"}, - {id = "c", label = "P Sugarbeet Cut vogn 1"}, {id = "d", label = "P Disponibel Vogn 4"}, {id = "e", label = "P Bredal vogn"}}) -local order = {} -for _, w in ipairs(ranked) do table.insert(order, w.id) end -check("rank: navn, disponible, resten", table.concat(order, ","), "c,d,b,e,a") -check("rank: tom liste", #W.rankWagons("X", {}), 0) - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adGuide.lua b/tests/test_adGuide.lua deleted file mode 100644 index 493245b..0000000 --- a/tests/test_adGuide.lua +++ /dev/null @@ -1,113 +0,0 @@ --- Kør: luajit tests/test_adGuide.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adGuide.lua") -local G = ADGuide -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - --- trin a..e + summary; b vises kun når draft.showB, d aldrig -local def = { - order = {"a", "b", "c", "d", "e", "summary"}, - isVisible = function(step, guide) - if step == "b" then return guide.draft.showB == true end - if step == "d" then return false end - return true - end, -} - -local guide = G.new(def, {showB = false, value = 1}, {x = 1}) -check("G1 start på første synlige", guide.step, "a") -local index, total = G.position(guide) -check("G1 position", index, 1) -check("G1 total over synlige", total, 4) -check("G1 synlige", table.concat(G.visibleSteps(guide), ","), "a,c,e,summary") - --- næste springer skjulte over -local g2, event = G.next(guide) -check("G2 næste springer b over", g2.step, "c") -check("G2 intet event", event, nil) -check("G2 gammel guide urørt", guide.step, "a") -local g3 = G.next(g2) -check("G3 springer d over", g3.step, "e") -check("G3 position", (G.position(g3)), 3) -local g4 = G.next(g3) -check("G4 summary er sidst", G.isLast(g4), true) -local _, done = G.next(g4) -check("G4 næste på sidste = done", done, "done") - --- tilbage -local back = G.back(g3) -check("G5 tilbage til c", back.step, "c") -local _, cancel = G.back(guide) -check("G5 tilbage på første = cancel", cancel, "cancel") - --- valg der gør et trin synligt ændrer tælling og næste -local shown = G.withDraft(guide, {showB = true, value = 1}) -check("G6 withDraft ny guide", guide.draft.showB, false) -check("G6 b synlig", G.next(shown).step, "b") -check("G6 total", select(2, G.position(shown)), 5) - --- aktuelt trin bliver usynligt: næste går til næste synlige i rækkefølgen, position nil -local onB = G.new(def, {showB = true}, {}, {step = "b"}) -local hidden = G.withDraft(onB, {showB = false}) -check("G7 position nil for skjult trin", (G.position(hidden)), nil) -check("G7 næste efter skjult", G.next(hidden).step, "c") -check("G7 tilbage fra skjult", G.back(hidden).step, "a") - --- spring (Ret) fra oversigt-trinnet: næste -> tilbage til summary, ikke single -local fromSummary = G.jump(g4, "c", "summary") -check("G8 spring trin", fromSummary.step, "c") -check("G8 single", fromSummary.single, true) -local returned, ev = G.next(fromSummary) -check("G8 retur til summary", returned.step, "summary") -check("G8 ikke single mere", returned.single, false) -check("G8 intet event", ev, nil) --- tilbage i spring gendanner kladden fra før springet -local changed = G.withDraft(fromSummary, {showB = false, value = 99}) -local undone = G.back(changed) -check("G9 tilbage gendanner kladde", undone.draft.value, 1) -check("G9 tilbage til summary", undone.step, "summary") - --- spring fra fanens oversigt (close): næste = done, tilbage = cancel -local single = G.new(def, {value = 5}, {}, {step = "e", single = true, returnTo = "close"}) -local _, singleDone = G.next(single) -check("G10 single close næste = done", singleDone, "done") -local _, singleCancel = G.back(single) -check("G10 single close tilbage = cancel", singleCancel, "cancel") -check("G10 single isLast", G.isLast(single), true) - --- withContext -local ctxGuide = G.withContext(guide, {x = 2}) -check("G11 ny context", ctxGuide.context.x, 2) -check("G11 gammel context", guide.context.x, 1) - --- deepCopy / toggleItem -local original = {list = {1, 2}, nested = {a = {b = 1}}} -local copy = G.deepCopy(original) -copy.list[1] = 9 -copy.nested.a.b = 7 -check("G12 deepCopy liste", original.list[1], 1) -check("G12 deepCopy indlejret", original.nested.a.b, 1) -local list = {"x", "y"} -check("G13 toggle til", table.concat(G.toggleItem(list, "z", true), ","), "x,y,z") -check("G13 ingen dublet", table.concat(G.toggleItem(list, "x", true), ","), "x,y") -check("G13 toggle fra", table.concat(G.toggleItem(list, "x", false), ","), "y") -check("G13 original urørt", #list, 2) - --- tekster -local texts = {greet = "Hej %s", n = "%d stk", title = "Afgrøde", plain = "Ingen"} -local function T(key) return texts[key] or key end -check("G14 label args", G.labelText({key = "greet", args = {"Bo"}}, T), "Hej Bo") -check("G14 label tekst", G.labelText({text = "rå"}, T), "rå") -check("G14 manglende args fejler ikke", G.labelText({key = "n"}, T), "%d stk") -check("G14 prefix", G.labelText({key = "plain", prefix = "Mælk: "}, T), "Mælk: Ingen") -check("G14 nil label", G.labelText(nil, T), "") -check("G15 linje", G.lineText({step = "crop", title = {key = "title"}, parts = {{key = "plain"}, {key = "n", args = {3}}}}, T), "Afgrøde: Ingen, 3 stk") -check("G15 linje uden dele", G.lineText({step = "crop", title = {key = "title"}, parts = {}}, T), "Afgrøde: -") - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adHitch.lua b/tests/test_adHitch.lua deleted file mode 100644 index dfbda39..0000000 --- a/tests/test_adHitch.lua +++ /dev/null @@ -1,129 +0,0 @@ --- Kør: luajit tests/test_adHitch.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adHitch.lua") -local H = ADHitch -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end --- markørnavne -> handling + navn (store/små bogstaver ligegyldigt) -local function parsed(name) local a, n = H.parseMarker(name) return tostring(a) .. "|" .. tostring(n) end -check("navn: Connect Bredal", parsed("Connect Bredal"), "attach|Bredal") -check("navn: små bogstaver", parsed("connect bredal"), "attach|bredal") -check("navn: dansk Kobl", parsed("Kobl Vogn 2"), "attach|Vogn 2") -check("navn: kun Connect", parsed("Connect"), "attach|") -check("navn: Connected er ikke en koblingsmarkør", parsed("Connected silo"), "nil|nil") -check("navn: almindelig markør", parsed("Bredal"), "nil|nil") -check("navn: nil", parsed(nil), "nil|nil") -check("navn: Afkobl Bredal", parsed("Afkobl Bredal"), "detach|Bredal") -check("navn: Kobl af er afkobling, ikke kobling", parsed("Kobl af Bredal"), "detach|Bredal") -check("navn: Detach", parsed("detach"), "detach|") -check("navn: Disconnect", parsed("Disconnect Vogn"), "detach|Vogn") --- bagerste redskaber: kun led bag traktoren (z < 0 i traktorens rum), ikke frontvægt/frontlæsser -local rear = H.rearImplements({{name = "vægt", z = 3.1}, {name = "Bredal", z = -2.4}, {name = "læsser", z = 1.5}, {name = "plov", z = -1}}) -check("bag: kun bagerste", #rear, 2) -check("bag: første", rear[1].name, "Bredal") --- ankomst: AD er stoppet, traktoren står stille tæt på markøren -check("ankomst: stoppet ved markør", H.hasArrived({adActive = false, distance = 1.2, speed = 0.1}), true) -check("ankomst: AD kører stadig", H.hasArrived({adActive = true, distance = 0.5, speed = 0}), false) -check("ankomst: for langt væk", H.hasArrived({adActive = false, distance = 9, speed = 0}), false) -check("ankomst: ruller stadig", H.hasArrived({adActive = false, distance = 1, speed = 3}), false) --- efter at have bakket hen til vognen står traktoren længere fra markøren -check("ankomst: bakket til vognen, udvidet afstand", H.hasArrived({adActive = false, distance = 9, speed = 0, arriveDistance = 13}), true) -local crept = H.step(H.newJob(1, "Connect x", "attach"), {adActive = false, distance = 9, speed = 3, arriveDistance = 13}, 4000, false) -check("vagt: bakket, ruller lidt -> ikke 'stoppedAway'", crept.state, "driving") --- vagten: efter ankomst forsøges koblingen et stykke tid, derefter opgives den -local job = H.newJob(25, "Connect Bredal", "attach") -check("job: venter på AD", job.state, "driving") -check("job: handling", job.action, "attach") -H.step(job, {adActive = true, distance = 30, speed = 10}, 1000, false) -check("job: kører stadig", job.state, "driving") -H.step(job, {adActive = false, distance = 1, speed = 0}, 500, false) -check("job: ankommet, kobling ikke lykkedes endnu", job.state, "acting") -H.step(job, {adActive = false, distance = 1, speed = 0}, 500, true) -check("job: koblet på", job.state, "done") -local failJob = H.newJob(25, "Connect Bredal", "attach") -H.step(failJob, {adActive = false, distance = 1, speed = 0}, 500, false) -for _ = 1, 20 do H.step(failJob, {adActive = false, distance = 1, speed = 0}, 500, false) end -check("job: opgivet efter vagt-tid", failJob.state, "failed") -check("job: årsag", failJob.reason, "noAttachable") -local dropJob = H.newJob(9, "Afkobl Bredal", "detach") -H.step(dropJob, {adActive = false, distance = 1, speed = 0}, 500, false) -for _ = 1, 45 do H.step(dropJob, {adActive = false, distance = 1, speed = 0}, 500, false) end -check("job: afkobling opgivet", dropJob.reason, "nothingToDetach") -local lostJob = H.newJob(25, "Connect Bredal", "attach") -H.step(lostJob, {adActive = false, distance = 40, speed = 0}, 500, false) -check("job: AD ikke startet endnu (opstart-frist)", lostJob.state, "driving") -H.step(lostJob, {adActive = true, distance = 40, speed = 5}, 500, false) -H.step(lostJob, {adActive = false, distance = 40, speed = 0}, 500, false) -check("job: AD stoppede langt fra markøren", lostJob.state, "failed") -check("job: årsag stoppet undervejs", tostring(lostJob.reason):find("^stoppedAway") ~= nil, true) --- tur: "Connect Bredal > Start" -> to markørnavne -local route = H.parseTrip("Connect", "Bredal", ">", "Start", ">", "Afkobl", "Bredal", ">", "Start") -check("tur: antal trin", #route, 4) -check("tur: første", route[1], "Connect Bredal") -check("tur: tredje", route[3], "Afkobl Bredal") -check("tur: ét trin", #H.parseTrip("Start"), 1) -check("tur: tomme trin droppes", #H.parseTrip("Start", ">", ">", "Stop"), 2) --- trin: handling pr. markør; almindelige markører = ankom (og vent, hvis ikke sidste) -check("trin: connect", H.stepAction("Connect Bredal"), "attach") -check("trin: afkobl", H.stepAction("Afkobl Bredal"), "detach") -check("trin: almindelig", H.stepAction("Start"), "arrive") -check("vent: mellem trin", H.waitAfter("arrive", 2, 4), H.ROUTE_WAIT_MS) -check("vent: ikke efter sidste", H.waitAfter("arrive", 4, 4), 0) -check("vent: ikke efter kobling", H.waitAfter("attach", 1, 4), 0) -local arriveJob = H.newJob(1, "Start", "arrive") -H.step(arriveJob, {adActive = false, distance = 1, speed = 0}, 250, false) -H.step(arriveJob, {adActive = false, distance = 1, speed = 0}, 250, true) -check("trin: ankom er færdig", arriveJob.state, "done") -local markers = {{name = "Start", id = 1}, {name = "Connect Bredal", id = 9}} -check("markør: navn uden hensyn til store/små", H.findMarker(markers, "connect bredal").id, 9) -check("markør: ukendt", H.findMarker(markers, "Stald"), nil) --- Connect = kun kobl på; i en tur kobles vognen af ved sin egen AD-parkering -check("connect: kobl på", H.resolveAction("attach", false), "attach") -check("connect med vogn: stadig kobl på (ingen vending)", H.resolveAction("attach", false), "attach") -check("vognens parkering: kobl af", H.resolveAction("arrive", true), "detach") -check("almindelig markør", H.resolveAction("arrive", false), "arrive") -check("afkobl forbliver afkobl", H.resolveAction("detach", false), "detach") --- hjemtur: hvert puljeredskabs parkering (afkobles der), så traktorens egen parkering -local home = H.homeRoute({4}, 2) -check("hjem: to trin", #home, 2); check("hjem: redskab først", home[1], 4); check("hjem: traktor sidst", home[2], 2) -check("hjem: uden redskab kun traktor", #H.homeRoute({}, 2), 1) -check("hjem: samme parkering to gange tæller én", #H.homeRoute({4, 4}, 2), 2) -check("hjem: uden traktorparkering", #H.homeRoute({4}, nil), 1) --- afbrydelse: et hente-job kan annulleres (Stop alt, høst slut, spilleren overtager) -ADFieldWork = ADFieldWork or {startAd = function() end} -Logging = Logging or {info = function() end, warning = function() end} -local stubEnv = {ADGraphManager = {getMapMarkerByWayPointId = function() return {name = "Connect X"} end}, AutoDrive = {MODE_DRIVETO = 1}} -local tractorStub = {getFullName = function() return "T" end, stopAutoDrive = function(v) v.stopped = true end} -H.fetch(stubEnv, tractorStub, 9, function() end) -check("annullér: job fandtes", H.cancel(tractorStub), true) -check("annullér: AD stoppet", tractorStub.stopped, true) -check("annullér: intet job tilbage", H.cancel(tractorStub), false) --- afkobling må tage længere tid (redskabet skal måske foldes ud først) -local foldJob = H.newJob(4, "Plov", "detach") -H.step(foldJob, {adActive = false, distance = 1, speed = 0}, 500, false) -for _ = 1, 16 do H.step(foldJob, {adActive = false, distance = 1, speed = 0}, 500, false) end -check("afkobling: stadig i gang efter 8 s (folder ud)", foldJob.state, "acting") -for _ = 1, 30 do H.step(foldJob, {adActive = false, distance = 1, speed = 0}, 500, false) end -check("afkobling: opgivet efter sin frist", foldJob.state, "failed") --- afkobling bekræftes: først "done" når intet sidder bag traktoren efter et forsøg (in-game 29/9: loggede --- "koblede af", men vognen sad der stadig, og traktoren kørte med den) -check("afkobl: redskab bagpå -> forsøg", H.detachStep(1, false), "attempt") -check("afkobl: forsøgt, sidder der stadig -> forsøg igen", H.detachStep(1, true), "attempt") -check("afkobl: forsøgt, intet bagpå -> færdig", H.detachStep(0, true), "done") -check("afkobl: intet bagpå fra start", H.detachStep(0, false), "nothing") --- transport ved stop: sluk, løft, fold (in-game 29/9: redskaber kørte udfoldet hjem efter stop) -check("transport: tændt+sænket+udfoldet", table.concat(H.stowActions({turnedOn = true, lowered = true, unfolded = true, canFold = true}), ","), "turnOff,raise,fold") -check("transport: allerede klar", #H.stowActions({}), 0) -check("transport: kan ikke foldes -> kun sluk/løft", table.concat(H.stowActions({turnedOn = true, lowered = true, unfolded = true, canFold = false}), ","), "turnOff,raise") -check("transport: kun udfoldet", table.concat(H.stowActions({unfolded = true, canFold = true}), ","), "fold") -check("vagt: stoppedAway med afstand", H.step(H.newJob(1, "Connect x", "attach"), {adActive = false, distance = 37.4, speed = 0}, 4000, false).reason, "stoppedAway (37 m fra markøren)") --- manuel AD "Kør til": markørnavn først, ellers puljeredskabets egen parkering = kobl af (fast sæt røres ikke) -check("manuel: Connect-markør", H.manualAction("attach", false), "attach") -check("manuel: puljeredskabets parkering", H.manualAction(nil, true), "detach") -check("manuel: almindelig markør", H.manualAction(nil, false), nil) -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adHitchCreep.lua b/tests/test_adHitchCreep.lua deleted file mode 100644 index 03fef0c..0000000 --- a/tests/test_adHitchCreep.lua +++ /dev/null @@ -1,26 +0,0 @@ --- Kør: luajit tests/test_adHitchCreep.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adHitchCreep.lua") -local C = ADHitchCreep -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end --- plan: vognens trækøje i traktorens rum set fra krogen (x = til siden, z < 0 = bagved) -check("plan: inden for rummelig rækkevidde -> kobl direkte", C.plan({x = 0.4, z = -2.0}), "attach") -check("plan: 5 m lige bagved -> bak", C.plan({x = 0.3, z = -5.0}), "creep") -check("plan: lidt til siden -> bak", C.plan({x = -1.2, z = -6.0}), "creep") -check("plan: for langt til siden -> intet", C.plan({x = 2.5, z = -5.0}), "none") -check("plan: for langt bagved -> intet", C.plan({x = 0, z = -12.0}), "none") -check("plan: foran traktoren -> intet", C.plan({x = 0, z = 5.0}), "none") -check("plan: ingen vogn -> intet", C.plan(nil), "none") --- skridt under bakningen -check("skridt: vognen inden for rækkevidde -> færdig", C.step({inRange = true, distance = 2.5, startDistance = 6, elapsedMs = 3000}), "done") -check("skridt: på vej -> kør", C.step({inRange = false, distance = 4, startDistance = 6, elapsedMs = 3000}), "drive") -check("skridt: for længe -> giv op", C.step({inRange = false, distance = 4, startDistance = 6, elapsedMs = C.CREEP_TIMEOUT_MS}), "giveUp") -check("skridt: kører væk fra vognen -> giv op", C.step({inRange = false, distance = 7.5, startDistance = 6, elapsedMs = 3000}), "giveUp") -check("skridt: forbi vognen uden kobling -> giv op", C.step({inRange = false, distance = 0.3, startDistance = 6, elapsedMs = 3000, passed = true}), "giveUp") -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adLoadSwap.lua b/tests/test_adLoadSwap.lua deleted file mode 100644 index 13f813c..0000000 --- a/tests/test_adLoadSwap.lua +++ /dev/null @@ -1,79 +0,0 @@ --- Kør: luajit tests/test_adLoadSwap.lua (fra repo-roden) --- Ren rangering af aflæsnings-siloer (lagermål for udkørsel og læs-bytte). - -dofile("FS25_ADSmartPickup/adPrices.lua") -dofile("FS25_ADSmartPickup/adLoadSwap.lua") -local L = ADLoadSwap - -local failures = 0 -local function check(name, actual, expected) - if actual == expected then - print("OK " .. name) - else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - --- LS1: markører et andet forløb HENTER fra (fx "Milk Storage Load") vælges sidst; ellers har-varen, så afstand -local candidates = { - {marker = {id = 1, name = "Milk Storage Load"}, hasFillType = true, distance = 100}, - {marker = {id = 2, name = "Milk Storage unload"}, hasFillType = true, distance = 400}, - {marker = {id = 3, name = "Silo tom"}, hasFillType = false, distance = 10}, -} -local ranked = L.rankDumpCandidates(candidates, {[1] = true}) -check("LS1 afhentningsmarkør undgås", ranked[1].marker.id, 2) -check("LS1 derefter tom silo før afhentning", ranked[2].marker.id, 3) -check("LS1 afhentning sidst (stadig mulig)", ranked[3].marker.id, 1) -local plain = L.rankDumpCandidates(candidates, nil) -check("LS1 uden undgå-liste: har varen + nærmest", plain[1].marker.id, 1) - --- LS2: lagermål — rene siloer altid; egne fabrikker der bruger varen som input kun når tilladt (udkørsel) -local SLURRY, MILK = 20, 21 -g_currentMission = {getFarmId = function() return 1 end} -local biogas = {spec_productionPoint = {productionPoint = {productions = {{inputs = {{type = SLURRY, amount = 1}}}}}}, getOwnerFarmId = function() return 1 end} -local foreignBiogas = {spec_productionPoint = {productionPoint = {productions = {{inputs = {{type = SLURRY, amount = 1}}}}}}, getOwnerFarmId = function() return 0 end} -local silo = {spec_silo = {}} -local barn = {spec_silo = {}, getHusbandryFillLevel = function() return 0 end} -check("LS2 silo", L.isStoreTarget({owningPlaceable = silo}, SLURRY, nil, false), true) -check("LS2 stald aldrig", L.isStoreTarget({owningPlaceable = barn}, SLURRY, nil, true), false) -check("LS2 biogas kun når fabrikker tilladt", L.isStoreTarget({owningPlaceable = biogas}, SLURRY, nil, false), false) -check("LS2 biogas tager gylle", L.isStoreTarget({owningPlaceable = biogas}, SLURRY, nil, true), true) -check("LS2 biogas tager ikke mælk", L.isStoreTarget({owningPlaceable = biogas}, MILK, nil, true), false) -check("LS2 fremmed anlæg aldrig", L.isStoreTarget({owningPlaceable = foreignBiogas}, SLURRY, nil, true), false) -check("LS2 hjemmet selv aldrig", L.isStoreTarget({owningPlaceable = biogas}, SLURRY, {owningPlaceable = biogas}, true), false) -g_currentMission = nil - --- LS3: tvunget lagermål — kun de valgte markører, i rækkefølge; første der tager varen, har plads og kan nås -do - local stations = { - [101] = {accepts = {[SLURRY] = true}, free = 0}, -- BGA fuld - [102] = {accepts = {[SLURRY] = true}, free = 900000}, -- gylletank - [103] = {accepts = {[MILK] = true}, free = 900000}, -- tager ikke gylle - } - for _, st in pairs(stations) do - st.getIsFillTypeSupported = function(self, ft) return self.accepts[ft] == true end - st.getFreeCapacity = function(self) return self.free end - end - ADSmartPickup = {getFarmId = function() return 1 end, getUnloadStationAtWayPointId = function(_, id) return stations[id] end} - local adEnv = {ADGraphManager = {getMapMarkerByWayPointId = function(_, id) return {id = id, name = "m" .. id} end}} - local vehicle = {} - local all = function() return true end - check("LS3 fuld BGA springes over -> tank", L.findForcedDumpMarker(adEnv, vehicle, SLURRY, 20000, {101, 102}, all).id, 102) - stations[101].free = 500000 - check("LS3 BGA med plads vælges først", L.findForcedDumpMarker(adEnv, vehicle, SLURRY, 20000, {101, 102}, all).id, 101) - local none, why = L.findForcedDumpMarker(adEnv, vehicle, SLURRY, 20000, {103}, all) - check("LS3 intet valgt mål tager varen -> nil", none, nil) - check("LS3 forklaring", why ~= nil and why:find("valgt", 1, true) ~= nil, true) - check("LS3 ikke nåelig -> næste", L.findForcedDumpMarker(adEnv, vehicle, SLURRY, 20000, {101, 102}, function(id) return id ~= 101 end).id, 102) - ADSmartPickup = nil -end - --- LQ: flydende varer lægges ikke automatisk i en fabrik (AIO-risten, in-game 26/9) -check("LQ gylle er flydende", ADLoadSwap.isLiquidName("LIQUIDMANURE"), true) -check("LQ digestat er flydende", ADLoadSwap.isLiquidName("digestate"), true) -check("LQ hvede er ikke flydende", ADLoadSwap.isLiquidName("WHEAT"), false) -check("LQ ukendt", ADLoadSwap.isLiquidName(nil), false) - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adManualTransport.lua b/tests/test_adManualTransport.lua deleted file mode 100644 index e6eaeeb..0000000 --- a/tests/test_adManualTransport.lua +++ /dev/null @@ -1,40 +0,0 @@ --- Kør: luajit tests/test_adManualTransport.lua (fra repo-roden) --- Manuel transport af brede høstere (in-game 27/9: mejetærskere med skærebord er for brede til vejene). -dofile("FS25_ADSmartPickup/adManualTransport.lua") -local M = ADManualTransport -local WHEAT, BARLEY = 1, 2 -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - --- valg af høster -check("manuel høster uden skærebord kan vælges", M.acceptsHarvester({manual = true, fruits = {}}, WHEAT), true) -check("automatisk høster uden skærebord kan ikke", M.acceptsHarvester({fruits = {}}, WHEAT), false) -check("automatisk høster med passende skærebord", M.acceptsHarvester({fruits = {[WHEAT] = true}}, WHEAT), true) -check("ukendt afgrøde -> ingen", M.acceptsHarvester({manual = true}, nil), false) --- in-game 27/9: F111 (hvede) valgte roeoptageren, fordi manuelle høstere slap for skærebord-tjekket -check("manuel roeoptager (fast optager) kan ikke høste hvede", M.acceptsHarvester({manual = true, fruits = {[BARLEY] = true}}, WHEAT), false) -check("manuel mejetærsker med passende skærebord monteret", M.acceptsHarvester({manual = true, fruits = {[WHEAT] = true}}, WHEAT), true) - --- klar til overtagelse -local header = {[WHEAT] = true} -local function ready(s) local ok, why = M.readiness(s); return ok and "ok" or why end -check("hjemme -> notThere", ready({atField = false, fruits = {}, fruit = WHEAT}), "notThere") -check("på marken uden skærebord -> noHeader", ready({atField = true, fruits = {}, fruit = WHEAT}), "noHeader") -check("forkert skærebord -> wrongHeader", ready({atField = true, fruits = {[BARLEY] = true}, fruit = WHEAT}), "wrongHeader") -check("spilleren sidder i -> playerInside", ready({atField = true, fruits = header, fruit = WHEAT, controlled = true}), "playerInside") -check("klar", ready({atField = true, fruits = header, fruit = WHEAT}), "ok") -check("Start nu: spiller i og ikke på marken -> ok", ready({atField = false, fruits = header, fruit = WHEAT, controlled = true, forced = true}), "ok") -check("Start nu tjekker stadig skærebordet", ready({atField = false, fruits = {}, fruit = WHEAT, forced = true}), "noHeader") - --- påmindelser -check("første gang -> påmind", M.shouldRemind(nil, 1000), true) -check("for tidligt", M.shouldRemind(1000, 1000 + M.REMIND_MS - 1), false) -check("efter 5 min", M.shouldRemind(1000, 1000 + M.REMIND_MS), true) - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adMonthView.lua b/tests/test_adMonthView.lua deleted file mode 100644 index c8425a9..0000000 --- a/tests/test_adMonthView.lua +++ /dev/null @@ -1,86 +0,0 @@ --- Kør: luajit tests/test_adMonthView.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adFieldModel.lua") -dofile("FS25_ADSmartPickup/adMonthView.lua") -local V, M = ADMonthView, ADFieldModel -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - --- MV1: perioder -check("MV1 næste periode", V.nextPeriod(5), 6) -check("MV1 vinter -> forår", V.nextPeriod(12), 1) - --- MV2: såvindue (hvede sås i periode 6-7) -local function plantableIn(name, period) return name == "WHEAT" and (period == 6 or period == 7) end -check("MV2 lukker", V.sowWindow(plantableIn, "WHEAT", 7), "closes") -check("MV2 åbent", V.sowWindow(plantableIn, "WHEAT", 6), "open") -check("MV2 lukket", V.sowWindow(plantableIn, "WHEAT", 8), "closed") - --- MV3: visning (Dakota-kartofler: harvestReady 6 -> dead 7 i EARLY_AUTUMN = periode 7) -local potato = {witheredState = 7, growthDataSeasonal = {periods = {[7] = {updates = {{startState = 6, endState = 7}}}}}} -check("MV3 visner næste måned", V.withersIn(potato, 7, 6), true) -check("MV3 ikke måneden før", V.withersIn(potato, 6, 6), false) -check("MV3 ukendte data", V.withersIn({witheredState = 7}, 7, 6), false) - --- MV4: fremdrift -local harvest = M.with(M.newField(15), {stage = "harvesting", kind = "harvest"}) -check("MV4 CP vinder uden arealtal", V.progress(harvest, {readyRatio = 0.9}, 0.5), 0.5) --- in-game 27/9 F90: halvt slået græs viste 0 % — slået græs tæller ikke i readyRatio's nævner -check("MV4 slåning ud fra hele arealet", V.progress(M.with(harvest, {stage = "mowing"}), {readyRatio = 1, standingRatio = 0.5}, nil), 0.5) -check("MV4 areal slår CP (kursus genstartet = 0)", V.progress(harvest, {readyRatio = 1, standingRatio = 0.4}, 0), 0.6) -check("MV4 pleje bruger stadig CP", V.progress(M.with(harvest, {stage = "fertilizing"}), {standingRatio = 0.4}, 0.3), 0.3) -check("MV4 høst ud fra afgrøde", V.progress(harvest, {readyRatio = 0.25}, nil), 0.75) -check("MV4 kultivering ukendt", V.progress(M.with(harvest, {stage = "cultivating"}), {readyRatio = 0}, nil), nil) -check("MV4 tekst", V.percentText(0.504), "50 %") -check("MV4 tekst ukendt", V.percentText(nil), "–") - --- MV5: hel fane -local fields = { - [15] = M.with(harvest, {plan = {sow = true, sowFruit = "WHEAT", soil = "cultivate", straw = "chop"}}), - [16] = M.with(M.newField(16), {stage = "harvesting", kind = "harvest"}), - [18] = M.with(M.newField(18), {stage = "sowing", kind = "harvest", plan = {sow = true, sowFruit = "WHEAT"}}), - [20] = M.with(M.newField(20), {stage = "fertilizing"}), - [21] = M.newField(21), - [22] = M.with(M.newField(22), {plan = {targetFruit = "WHEAT"}}), - [23] = M.with(M.newField(23), {stage = "ready"}), -} -local infos = { - {id = 15, kind = "harvest", readyRatio = 0.5, cropTitle = "Hvede", needs = {}}, - {id = 16, kind = "harvest", readyRatio = 1, cropTitle = "Kartofler", growthState = 6}, - {id = 18, kind = "bare", readyRatio = 0}, - {id = 20, kind = "harvest", readyRatio = 0}, - {id = 21, kind = "bare"}, - {id = 22, kind = "bare"}, - {id = 23, kind = "harvest", readyRatio = 1}, -} -local ctx = {period = 7, plantableIn = plantableIn, - withers = function(info, nextPeriod) return info.id == 16 and nextPeriod == 8 end, - cpProgress = function(id) return id == 18 and 0.1 or nil end} -local view = V.build(infos, fields, ctx) -check("MV5 aktive marker (21 venter, 22 kommer, 23 passiv klar)", #view.rows, 4) -check("MV5 kritiske", view.critical, 3) -check("MV5 høst før sidste såmåned først (id)", view.rows[1].id, 15) -check("MV5 høst -> såning senere lukker", view.rows[1].window, "lastSowMonth") -check("MV5 høst næste trin", view.rows[1].nextStage, "cultivating") -check("MV5 høst der visner", view.rows[2].id, 16) -check("MV5 vindue høst", view.rows[2].window, "harvestCloses") -check("MV5 sidste såmåned", view.rows[3].id, 18) -check("MV5 vindue såning", view.rows[3].window, "lastSowMonth") -check("MV5 såafgrøde", view.rows[3].sowFruit, "WHEAT") -check("MV5 CP-fremdrift", view.rows[3].progress, 0.1) -check("MV5 pleje sidst", view.rows[4].id, 20) -check("MV5 pleje intet næste", view.rows[4].nextStage, nil) -check("MV5 kommer: F22 hvede", view.upcoming[1] and view.upcoming[1].id, 22) -check("MV5 kommer: lukker", view.upcoming[1] and view.upcoming[1].closes, true) - --- MV6: klargøring før såning med lukkende vindue er kritisk -local cultivating = M.with(M.newField(30), {stage = "cultivating", plan = {sow = true, sowFruit = "WHEAT"}}) -check("MV6 kultivering før sidste såmåned", V.window(cultivating, {kind = "bare"}, ctx), "lastSowMonth") -check("MV6 aktiv klar (auto)", V.isActive(M.with(M.newField(31), {stage = "ready", plan = {mode = "auto"}})), true) - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adNames.lua b/tests/test_adNames.lua deleted file mode 100644 index ca4bd6e..0000000 --- a/tests/test_adNames.lua +++ /dev/null @@ -1,20 +0,0 @@ --- Kør: luajit tests/test_adNames.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adNames.lua") -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end -local byId = ADNames.fromRecords({{id = "placeableA", name = "Gårdsilo 10"}, {id = "", name = "x"}, {id = "placeableB", name = " "}, {id = "placeableC"}}) -check("navn indlæst", byId.placeableA, "Gårdsilo 10") -check("tomt id springes over", byId[""], nil) -check("blankt navn springes over", byId.placeableB, nil) -check("manglende navn springes over", byId.placeableC, nil) -ADNames.byId = byId -check("opslag på bygning", ADNames.forPlaceable({uniqueId = "placeableA"}), "Gårdsilo 10") -check("bygning uden eget navn", ADNames.forPlaceable({uniqueId = "placeableZ"}), nil) -check("uden uniqueId", ADNames.forPlaceable({}), nil) -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adNaming.lua b/tests/test_adNaming.lua deleted file mode 100644 index eb5b514..0000000 --- a/tests/test_adNaming.lua +++ /dev/null @@ -1,49 +0,0 @@ --- Kør: luajit tests/test_adNaming.lua (fra repo-roden) --- Automatiske navne på bygninger uden eget navn (in-game 27/9: "36 Farma" hed alle "Farma 500 + Obi 1000"). -dofile("FS25_ADSmartPickup/adNaming.lua") -local N = ADNaming -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - --- næste ledige nummer -check("næste efter 1-5", N.nextNumber({"Svinestald 1", "Svinestald 5", "Kostald 7"}, "Svinestald"), 6) -check("ingen endnu -> 1", N.nextNumber({"Kostald 1"}, "Svinestald"), 1) -check("prefix-præfiks forveksles ikke", N.nextNumber({"Gårdsilo ekstra ved 1", "Gårdsilo 3"}, "Gårdsilo"), 4) - --- navne efter type -local function one(b, existing) return N.assign({b}, existing or {})[b.id] end -check("svinestald", one({id = "a", kind = "husbandry", animal = "PIG"}, {x = "Svinestald 5"}), "Svinestald 6") -check("gedestald", one({id = "a", kind = "husbandry", animal = "GOAT"}), "Gedestald 1") -check("ukendt dyr -> Stald", one({id = "a", kind = "husbandry", animal = "LLAMA"}), "Stald 1") -check("silo i gårdens klynge", one({id = "a", kind = "silo", inFarmCluster = true}, {x = "Gårdsilo 35"}), "Gårdsilo 36") -check("silo ved mark", one({id = "a", kind = "silo", nearestField = {name = "F118", distance = 80}}), "F118 marksilo") -check("anden marksilo ved samme mark", one({id = "a", kind = "silo", nearestField = {name = "F118", distance = 80}}, {x = "F118 marksilo"}), "F118 marksilo 2") -check("silo langt fra alt", one({id = "a", kind = "silo", nearestField = {name = "F1", distance = 900}}), "Silo 1") -check("palleskur", one({id = "a", kind = "objectStorage"}, {x = "Palleskur 1"}), "Palleskur 2") -check("fabrik: butiksnavnet", one({id = "a", kind = "factory", storeName = "Sugarbeet Crusher"}), "Sugarbeet Crusher") -check("fabrik nr. 2 med samme navn", one({id = "a", kind = "factory", storeName = "Roesnitter"}, {x = "Roesnitter"}), "Roesnitter 2") -check("salgssted navngives ikke", one({id = "a", kind = "sell", storeName = "Mølle"}), nil) -check("har allerede navn -> urørt", N.assign({{id = "a", kind = "husbandry", animal = "PIG"}}, {a = "Min stald"}).a, nil) - --- flere nye i samme runde får hver sit nummer -local two = N.assign({{id = "a", kind = "husbandry", animal = "COW"}, {id = "b", kind = "husbandry", animal = "COW"}}, {}) -check("to nye kostalde: 1 og 2", (two.a or "") .. "|" .. (two.b or ""), "Kostald 1|Kostald 2") - - --- markmaskiner der stadig har modelnavnet (flow-traktorer navngives af flowet) -check("læssevogn nr. 5", N.fleetName("collector", nil, {"Læssevogn 1", "Læssevogn 4"}), "Læssevogn 5") -check("skårlægger", N.fleetName("mower", nil, {}), "Skårlægger 1") -check("høster: roeoptager", N.fleetName("harvester", "beet", {"Roeoptager 1"}), "Roeoptager 2") -check("høster: finsnitter", N.fleetName("harvester", "forage", {}), "Finsnitter 1") -check("høster: mejetærsker", N.fleetName("harvester", nil, {"Mejetærsker"}), "Mejetærsker 1") -check("tømmevogn", N.fleetName("unloader", nil, {}), "Tømmevogn 1") -check("gødningsspreder", N.fleetName("fertilizer", nil, {}), "Gødningsspreder 1") -check("ukendt rolle -> nil", N.fleetName("mystery", nil, {}), nil) - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adOutbound.lua b/tests/test_adOutbound.lua deleted file mode 100644 index 81164c6..0000000 --- a/tests/test_adOutbound.lua +++ /dev/null @@ -1,646 +0,0 @@ --- Kør: luajit tests/test_adOutbound.lua (fra repo-roden) --- Udkørsels-hooks: turvalg + AD-tilstand, hold ved Wait, objektlager-udtag, opsamlingsrunde, --- advarsler på skærmen. Fake AD-miljø; adSources/adPrices/adLoadSwap er fakes, adWaitPool og --- adOutboundPlanner er de rigtige. - -local SUGAR, FORAGE, BASKET, CRATE = 5, 6, 7, 8 -local NAMES = {[SUGAR] = "SUGAR", [FORAGE] = "FORAGE", [BASKET] = "STRAW_BASKET", [CRATE] = "STRAW_CRATE"} -g_fillTypeManager = { - getFillTypeNameByIndex = function(_, i) return NAMES[i] end, - getFillTypeByIndex = function(_, i) return {name = NAMES[i], palletFilename = (i == BASKET or i == CRATE) and "p.xml" or nil} end, -} -local logLines = {} -Logging = { - info = function(fmt, ...) table.insert(logLines, string.format(fmt, ...)) end, - warning = function(fmt, ...) table.insert(logLines, "WARN " .. string.format(fmt, ...)) end, -} -getWorldTranslation = function(node) return node.x, 0, node.z end -g_currentMission = {getFarmId = function() return 1 end} - -dofile("FS25_ADSmartPickup/adSupplyPlanner.lua") -dofile("FS25_ADSmartPickup/adRuns.lua") -dofile("FS25_ADSmartPickup/adWaitPool.lua") -dofile("FS25_ADSmartPickup/adOutboundPlanner.lua") -dofile("FS25_ADSmartPickup/adOutbound.lua") - -local failures = 0 -local function check(name, actual, expected) - if actual == expected then - print("OK " .. name) - else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end -local function lastLog(pattern) - for i = #logLines, 1, -1 do - if string.find(logLines[i], pattern, 1, true) then return logLines[i] end - end - return nil -end -local function countLog(pattern) - local n = 0 - for _, line in ipairs(logLines) do if string.find(line, pattern, 1, true) then n = n + 1 end end - return n -end - --- ---- AD-miljø ---- -local markers, wayPoints, unreachable = {}, {}, {} -local function addMarker(id, name, group, x, z) - wayPoints[id] = {id = id, x = x, z = z} - table.insert(markers, {id = id, markerIndex = #markers + 1, name = name, group = group}) - return markers[#markers] -end -local notifications = {} -local RT_NONE, RT_ONLYPICKUP = 1, 2 -local adEnv = { - AutoDrive = { - RT_NONE = RT_NONE, RT_ONLYPICKUP = RT_ONLYPICKUP, - getAllUnits = function(vehicle) return vehicle.trailers end, - getAllFillLevels = function(trailers) return trailers.level, trailers.capacity, trailers.level >= trailers.capacity * 0.99, trailers.capacity - trailers.level end, - getSupportedFillTypesOfAllUnitsAlphabetically = function(vehicle) return vehicle.carriable end, - hasAL = function(_, trailer) return trailer.ual ~= nil end, - activateALTrailers = function(vehicle, trailers) trailers.activated = (trailers.activated or 0) + 1 end, - }, - ADGraphManager = { - getMapMarkers = function() return markers end, - getMapMarkerByWayPointId = function(_, id) for _, m in ipairs(markers) do if m.id == id then return m end end end, - getWayPointById = function(_, id) return wayPoints[id] end, - getPathTo = function(_, vehicle, id) if unreachable[id] then return {} end return {{id = id}} end, - pathFromTo = function(_, from, to) if unreachable[to] then return {} end return {{id = to}} end, - }, - AutoDriveMessageEvent = {sendMessageOrNotification = function(vehicle, kind, text, duration, ...) table.insert(notifications, {vehicle = vehicle, kind = kind, text = string.format(text, ...)}) end}, - ADMessagesManager = {messageTypes = {INFO = 1, WARN = 2, ERROR = 3}}, - LoadAtDestinationTask = {STATE_DRIVING = 2, update = function() end, finished = function(self) self.finishedCalls = (self.finishedCalls or 0) + 1 end}, - UnloadAtDestinationTask = {STATE_DRIVING = 2, update = function() end}, -} - -local function newVehicle(name, x, z, capacity, ual) - local v = {name = name, rootNode = {x = x, z = z}, lastSpeedReal = 0, carriable = {SUGAR, FORAGE}, farmId = 1} - v.getName = function() return name end - v.trailers = {level = 0, capacity = capacity, ual = ual and {spec = {loadedObjects = {}}} or nil} - v.trailers[1] = {spec_universalAutoload = ual and v.trailers.ual.spec or nil, ualIsFull = ual and function() return v.trailers.full == true end or nil, ual = ual and true or nil} - v.ad = { - stateModule = {isActive = function() return v.active ~= false end, selectedFillTypes = {}, fillType = nil, - setFillType = function(self, ft) self.fillType = ft end, getFillType = function(self) return self.fillType end, - setSecondMarkerByWayPointId = function(self, id) self.secondMarkerId = id end, - getSecondMarker = function(self) return self.secondMarkerId and {id = self.secondMarkerId} or nil end, - raiseDirtyFlag = function() end}, - drivePathModule = {isTargetReached = function() return v.atTarget == true end, setPathTo = function(_, id) v.pathTo = id end}, - specialDrivingModule = {stopVehicle = function() v.stopped = true end, update = function() end}, - trailerModule = {isActiveAtTrigger = function() return false end}, - } - return v -end -local function newTask(vehicle, destinationID) - return {vehicle = vehicle, destinationID = destinationID, trailers = vehicle.trailers, state = 2, activatedUALLoading = false, - finished = function(self) return adEnv.LoadAtDestinationTask.finished(self) end} -end - --- kilder, priser, siloer som fakes -local sources, products, quotes, dumpMarker = {}, {}, {}, nil -ADSources = { - identify = function(_, id) return sources[id] end, - getProducts = function(_, source) return products[source.wayPointId] or {} end, - getSellMarkers = function() local r = {} for _, m in ipairs(markers) do if m.group == "selling" then table.insert(r, {marker = m, station = {name = m.name}}) end end return r end, - stationAccepts = function(station, ft) return quotes[station.name] ~= nil and quotes[station.name][ft] ~= nil end, - getSourcePlaceables = function() return {} end, - getObjectInfoIndex = function(source, ft) local s = source.stored or {} return s[ft] ~= nil and s[ft] > 0 and 1 or nil, s[ft] end, - countStored = function(source, ft) return (source.stored or {})[ft] or 0 end, - spawnOne = function(source, index) source.spawnCalls = (source.spawnCalls or 0) + 1 return source.spawnFails ~= true end, -} -ADPrices = { - getQuote = function(station, ft) return quotes[station.name] and quotes[station.name][ft] end, - bestQuote = function(list) local best for _, q in ipairs(list) do if best == nil or q.percent > best.percent then best = q end end return best end, -} -ADLoadSwap = {findDumpMarker = function() return dumpMarker, "ingen silo" end} -local runs = {} -ADRunsController = {getRunForVehicle = function(vehicle) return runs[vehicle] end} -local activePickupCalls = {} -ADSmartPickup = {getFarmId = function(v) return v.farmId end, setActivePickup = function(vehicle, choice) table.insert(activePickupCalls, choice) end, - getUnloadStationAtWayPointId = function(_, id) for _, m in ipairs(markers) do if m.id == id and (m.group == "silo" or m.group == "selling") then return {name = m.name} end end end} - -local mSugar = addMarker(1, "Suger Load", "Pickup points", 0, 0) -local mSilo = addMarker(2, "silo 4", "silo", 50, 0) -local mObj = addMarker(3, "Objektlager", "Pickup points", 100, 0) -local mSell = addMarker(10, "selling station", "selling", 200, 0) -local mSell2 = addMarker(11, "Far station", "selling", 300, 0) -local mWait = addMarker(20, "Wait 3", "General Wait", 20, 0) -local mStore = addMarker(30, "silo 6", "silo", 60, 0) -sources[1] = {kind = "factory", wayPointId = 1, station = {name = "factoryStation"}, name = "Sugar mill"} -sources[2] = {kind = "silo", wayPointId = 2, station = {name = "siloStation"}, name = "Farma"} -sources[3] = {kind = "objectStorage", wayPointId = 3, station = nil, name = "Objektlager", stored = {[BASKET] = 3, [CRATE] = 1}} -products[1] = {{fillType = SUGAR, name = "SUGAR", available = 5000, level = 5000, capacity = 180000, isPallet = false}} -products[2] = {{fillType = SUGAR, name = "SUGAR", available = 9000, level = 9000, capacity = 12000000, isPallet = false}} -products[3] = {{fillType = BASKET, name = "STRAW_BASKET", available = 3000, level = 4, capacity = 4000, isPallet = true}, {fillType = CRATE, name = "STRAW_CRATE", available = 1000, level = 4, capacity = 4000, isPallet = true}} -quotes["selling station"] = {[SUGAR] = {percent = 92, price = 480}, [BASKET] = {percent = 95, price = 1}, [CRATE] = {percent = 95, price = 1}} -quotes["Far station"] = {[SUGAR] = {percent = 99, price = 600}} - -local function outboundRun(id, sourceIds, productsList, overrides) - local run = {id = id, name = "Out " .. id, kind = "outbound", waitPoolGroup = "", sourceWayPointIds = sourceIds, products = productsList, sellAtPercent = 90, storeAbovePercent = 80, collectRound = false} - for k, v in pairs(overrides or {}) do run[k] = v end - return run -end - --- O1: tur -> AD's varevalg og 2. markør sat, choice = kilden; næstbedste salgssted når bedste er uopnåeligt -unreachable[11] = true -local v1 = newVehicle("MT635", 0, 0, 20000) -runs[v1] = outboundRun(1, {1}, {{fillType = "SUGAR", policy = "sell"}}) -check("O1 er udkørsel", ADOutbound.isOutboundVehicle(v1), true) -local choice = ADOutbound.choosePickup(adEnv, v1) -check("O1 kilde-markør", choice.markerIndex, mSugar.markerIndex) -check("O1 station med (bulk-greb)", choice.station.name, "factoryStation") -check("O1 vare", v1.ad.stateModule.fillType, SUGAR) -check("O1 varevalg", #v1.ad.stateModule.selectedFillTypes, 1) -check("O1 2. markør = nåeligt salgssted", v1.ad.stateModule.secondMarkerId, 10) -check("O1 ingen advarsel (næstbedste kunne nås)", #notifications, 0) -check("O1 log", lastLog("udkørsel") ~= nil, true) -local trip = ADOutbound.getTripFor(v1) -check("O1 trip kind", trip.kind, "sell") -unreachable[11] = nil - --- O2: ingen tur -> Wait-markør reserveret, hold registreret -local v2 = newVehicle("ARION", 25, 0, 20000) -runs[v2] = outboundRun(2, {1}, {{fillType = "SUGAR", policy = "sell"}}) -quotes["selling station"][SUGAR].percent = 70 -quotes["Far station"][SUGAR].percent = 60 -local choice2 = ADOutbound.choosePickup(adEnv, v2) -check("O2 Wait-markør", choice2.markerIndex, mWait.markerIndex) -check("O2 uden station", choice2.station, nil) -check("O2 reserveret", ADWaitPool.getOwner(20), v2) -local hold = ADOutbound.getHoldFor(v2) -check("O2 hold", hold.markerName, "Wait 3") -check("O2 begrundelse", hold.reasons[1], "SUGAR 70 % af top (kræver 90), lager 3 %") -check("O2 run-status", ADOutbound.getRunStatus(2).reasons[1], hold.reasons[1]) -check("O2 hold-log én gang", countLog("holder ved Wait 3"), 1) -ADOutbound.choosePickup(adEnv, v2) -check("O2 samme begrundelse logges ikke igen", countLog("holder ved Wait 3"), 1) - --- O3: holder ved Wait: vognen stoppes; efter 5 s med tur -> omdirigeret til kilden, hold sluppet -local task2 = newTask(v2, 20) -v2.atTarget = true -check("O3 håndteret", ADOutbound.beforeLoadUpdate(adEnv, task2, 1000), true) -check("O3 stoppet", v2.stopped, true) -quotes["selling station"][SUGAR].percent = 92 -ADOutbound.beforeLoadUpdate(adEnv, task2, 4500) -check("O3 omdirigeret til kilden", task2.destinationID, 1) -check("O3 sti sat", v2.pathTo, 1) -check("O3 hold væk", ADOutbound.getHoldFor(v2), nil) -check("O3 reservation frigivet", ADWaitPool.getOwner(20), nil) -check("O3 activePickup opdateret", activePickupCalls[#activePickupCalls].station.name, "factoryStation") -check("O3 UAL-flag nulstillet", task2.activatedUALLoading, false) -v2.atTarget = false -check("O3 undervejs ikke håndteret", ADOutbound.beforeLoadUpdate(adEnv, task2, 100), false) - --- O4: objektlager: én palle ad gangen; stored falder + loaded stiger -> næste; ualIsFull -> finished -local v3 = newVehicle("Fendt", 100, 0, 8000, true) -runs[v3] = outboundRun(3, {3}, {{fillType = "STRAW_BASKET", policy = "sell"}, {fillType = "STRAW_CRATE", policy = "sell"}}, {collectRound = true}) -local choice3 = ADOutbound.choosePickup(adEnv, v3) -check("O4 kilde = objektlager", choice3.markerIndex, mObj.markerIndex) -check("O4 kun én vare valgt ad gangen ved objektlager", #v3.ad.stateModule.selectedFillTypes, 1) -check("O4 turen har begge varer", #ADOutbound.getTripFor(v3).fillTypes, 2) -local task3 = newTask(v3, 3) -v3.atTarget = true -ADOutbound.beforeLoadUpdate(adEnv, task3, 100) -check("O4 intet spawn før UAL er aktiveret", sources[3].spawnCalls, nil) -task3.activatedUALLoading = true -ADOutbound.beforeLoadUpdate(adEnv, task3, 100) -check("O4 første spawn", sources[3].spawnCalls, 1) -ADOutbound.beforeLoadUpdate(adEnv, task3, 100) -check("O4 venter på pallen", sources[3].spawnCalls, 1) -sources[3].stored[BASKET] = 2 -v3.trailers.ual.spec.loadedObjects[{}] = 1 -ADOutbound.beforeLoadUpdate(adEnv, task3, 100) -ADOutbound.beforeLoadUpdate(adEnv, task3, 100) -check("O4 andet spawn efter læsning", sources[3].spawnCalls, 2) -sources[3].stored[BASKET] = 0 -v3.trailers.ual.spec.loadedObjects[{}] = 1 -ADOutbound.beforeLoadUpdate(adEnv, task3, 100) -ADOutbound.beforeLoadUpdate(adEnv, task3, 100) -check("O4 skifter til kasser når kurvene er væk", v3.ad.stateModule.fillType, CRATE) -check("O4 UAL genaktiveret ved vareskift", v3.trailers.activated, 1) -ADOutbound.beforeLoadUpdate(adEnv, task3, 100) -check("O4 tredje spawn (kasse)", sources[3].spawnCalls, 3) -v3.trailers.full = true -sources[3].stored[CRATE] = 0 -v3.trailers.ual.spec.loadedObjects[{}] = 1 -ADOutbound.beforeLoadUpdate(adEnv, task3, 100) -ADOutbound.beforeLoadUpdate(adEnv, task3, 100) -check("O4 fuld -> finished", task3.finishedCalls, 1) - --- O5: spawn uden fremdrift i 30 s -> finished + WARN -local v4 = newVehicle("Valtra", 100, 0, 8000, true) -runs[v4] = outboundRun(4, {3}, {{fillType = "STRAW_BASKET", policy = "sell"}}) -sources[3].stored[BASKET] = 5 -sources[3].spawnCalls = 0 -ADOutbound.choosePickup(adEnv, v4) -local task4 = newTask(v4, 3) -v4.atTarget, task4.activatedUALLoading = true, true -ADOutbound.beforeLoadUpdate(adEnv, task4, 100) -check("O5 spawn kaldt", sources[3].spawnCalls, 1) -ADOutbound.beforeLoadUpdate(adEnv, task4, 31000) -ADOutbound.beforeLoadUpdate(adEnv, task4, 100) -check("O5 finished efter stall", task4.finishedCalls, 1) -check("O5 WARN", lastLog("spawner ikke") ~= nil, true) - --- O6: opsamlingsrunde, bulk: kilde A fylder vognen -> finished uden redirect; kilde A giver 30 % -> videre til B -local v5 = newVehicle("Claas", 0, 0, 10000) -runs[v5] = outboundRun(5, {1, 2}, {{fillType = "SUGAR", policy = "sell"}}, {collectRound = true}) -local choice5 = ADOutbound.choosePickup(adEnv, v5) -check("O6 mest først = silo 4", choice5.markerIndex, mSilo.markerIndex) -local task5 = newTask(v5, 2) -v5.trailers.level = 10000 -check("O6 fuld: ingen runde", ADOutbound.beforeLoadFinished(adEnv, task5), false) -local v6 = newVehicle("Claas2", 0, 0, 10000) -runs[v6] = outboundRun(6, {1, 2}, {{fillType = "SUGAR", policy = "sell"}}, {collectRound = true}) -ADOutbound.choosePickup(adEnv, v6) -local task6 = newTask(v6, 2) -v6.trailers.level = 3000 -check("O6 plads tilbage: videre til næste kilde", ADOutbound.beforeLoadFinished(adEnv, task6), true) -check("O6 destination = Suger Load", task6.destinationID, 1) -check("O6 activePickup = fabrik", activePickupCalls[#activePickupCalls].station.name, "factoryStation") -check("O6 alle besøgt: afslut", ADOutbound.beforeLoadFinished(adEnv, task6), false) - --- O7: intet nåeligt salgssted -> rød notifikation én gang -notifications = {} -unreachable[10], unreachable[11] = true, true -local v7 = newVehicle("Deutz", 0, 0, 20000) -runs[v7] = outboundRun(7, {1}, {{fillType = "SUGAR", policy = "sell"}}) -local choice7 = ADOutbound.choosePickup(adEnv, v7) -check("O7 holder", ADOutbound.getHoldFor(v7) ~= nil, true) -check("O7 én notifikation", #notifications, 1) -check("O7 ERROR", notifications[1].kind, 3) -check("O7 tekst", string.find(notifications[1].text, "kan ikke nå salgssted", 1, true) ~= nil, true) -check("O7 status-advarsel", ADOutbound.getRunStatus(7).warning ~= nil, true) -ADOutbound.choosePickup(adEnv, v7) -check("O7 ikke gentaget", #notifications, 1) -unreachable[10] = nil -ADOutbound.choosePickup(adEnv, v7) -check("O7 tur når vejen er der", ADOutbound.getTripFor(v7) ~= nil, true) -check("O7 advarsel ryddet", ADOutbound.getRunStatus(7).warning, nil) -unreachable[11] = nil - --- O8: ingen silo med plads ved lagerbehov -> notifikation; med silo -> store-tur -notifications = {} -local v8 = newVehicle("JD", 0, 0, 20000) -runs[v8] = outboundRun(8, {2}, {{fillType = "SUGAR", policy = "store"}}) -products[2][1].level, products[2][1].capacity = 11000000, 12000000 -ADOutbound.choosePickup(adEnv, v8) -check("O8 advarsel ingen silo", #notifications, 1) -dumpMarker = mStore -ADOutbound.choosePickup(adEnv, v8) -check("O8 store-tur", ADOutbound.getTripFor(v8).kind, "store") -check("O8 mål = silo 6", v8.ad.stateModule.secondMarkerId, 30) - --- O9: releaseInactive rydder hold + reservation -local v9 = newVehicle("Case", 25, 0, 20000) -runs[v9] = outboundRun(9, {1}, {{fillType = "FORAGE", policy = "sell"}}) -ADOutbound.choosePickup(adEnv, v9) -check("O9 reserveret", ADWaitPool.getOwner(20), v9) -v9.active = false -ADOutbound.releaseInactive() -check("O9 frigivet", ADWaitPool.getOwner(20), nil) -check("O9 hold væk", ADOutbound.getHoldFor(v9), nil) - --- O11 (I2): hold-markøren skifter (Wait-punkt bliver ledigt) -> opgaven omdirigeres -ADWaitPool.releaseVehicle(v9) -local v11 = newVehicle("Hold", 25, 0, 20000) -runs[v11] = outboundRun(11, {1}, {{fillType = "FORAGE", policy = "sell"}}) -- FORAGE findes ikke ved kilden -> aldrig tur -local blocker = newVehicle("Blocker", 0, 0, 1) -ADWaitPool.claim(20, blocker) -local choice11 = ADOutbound.choosePickup(adEnv, v11) -check("O11 uden ledigt Wait: holder ved kilden", choice11.markerIndex, mSugar.markerIndex) -local task11 = newTask(v11, 1) -v11.atTarget = true -ADOutbound.beforeLoadUpdate(adEnv, task11, 100) -ADWaitPool.releaseVehicle(blocker) -ADOutbound.beforeLoadUpdate(adEnv, task11, 5000) -check("O11 Wait-punkt ledigt -> omdirigeret", task11.destinationID, 20) -check("O11 hold peger på Wait", ADOutbound.getHoldFor(v11).markerName, "Wait 3") -ADWaitPool.releaseVehicle(v11) - --- O12 (I3): mål for last der allerede er på vognen ved Start -local v12 = newVehicle("Loaded", 0, 0, 20000) -runs[v12] = outboundRun(12, {1}, {{fillType = "SUGAR", policy = "sell"}}) -quotes["selling station"][SUGAR].percent = 92 -check("O12 salgsmål for sukker-last", ADOutbound.planCargoTarget(adEnv, runs[v12], v12, SUGAR), 10) -quotes["selling station"][SUGAR].percent = 50 -check("O12 pris lav, ingen lager-politik -> nil", ADOutbound.planCargoTarget(adEnv, runs[v12], v12, SUGAR), nil) -runs[v12].products = {{fillType = "SUGAR", policy = "sellElseStore"}} -check("O12 pris lav -> silo", ADOutbound.planCargoTarget(adEnv, runs[v12], v12, SUGAR), 30) -check("O12 last der ikke er i run'et -> nil", ADOutbound.planCargoTarget(adEnv, runs[v12], v12, FORAGE), nil) -quotes["selling station"][SUGAR].percent = 92 - --- O13 (I4): tom vogn 30 s ved kilden -> kilden udelukkes for varen og turen afsluttes -local v13 = newVehicle("Empty", 0, 0, 20000) -runs[v13] = outboundRun(13, {1, 2}, {{fillType = "SUGAR", policy = "sell"}}, {collectRound = false}) -local choice13 = ADOutbound.choosePickup(adEnv, v13) -check("O13 første kilde = silo 4 (mest)", choice13.markerIndex, mSilo.markerIndex) -local task13 = newTask(v13, 2) -v13.atTarget = true -ADOutbound.beforeLoadUpdate(adEnv, task13, 100) -ADOutbound.beforeLoadUpdate(adEnv, task13, 31000) -check("O13 finished efter 30 s uden last", task13.finishedCalls, 1) -check("O13 WARN", lastLog("intet at læsse") ~= nil, true) -local choice13b = ADOutbound.choosePickup(adEnv, v13) -check("O13 næste plan udelukker den tomme kilde", choice13b.markerIndex, mSugar.markerIndex) - --- O16: kilden gav LIDT og så intet mere ("ingen mere last") -> kilden hviler, næste tur tager en anden --- (in-game 26/9: æg-vognen kørte til Æg Pickup 4 tre gange i træk; én palle inden for rækkevidde, resten udenfor) -local v16 = newVehicle("Thin", 0, 0, 20000) -runs[v16] = outboundRun(16, {1, 2}, {{fillType = "SUGAR", policy = "sell"}}, {collectRound = false}) -ADOutbound.resetEmptySources() -local choice16 = ADOutbound.choosePickup(adEnv, v16) -local first16 = choice16.markerIndex -local task16 = newTask(v16, choice16.markerIndex == mSilo.markerIndex and 2 or 1) -v16.atTarget = true -v16.trailers.level = 500 -ADOutbound.beforeLoadUpdate(adEnv, task16, 100) -ADOutbound.beforeLoadUpdate(adEnv, task16, 16000) -check("O16 kører med det den fik", task16.finishedCalls, 1) -check("O16 log", lastLog("ingen mere last") ~= nil, true) -v16.trailers.level = 0 -local choice16b = ADOutbound.choosePickup(adEnv, v16) -check("O16 næste tur tager en anden kilde", choice16b ~= nil and choice16b.markerIndex ~= first16, true) -ADOutbound.resetEmptySources() -- (senere tests forventer alle kilder ledige) - - --- O14 (I6): rutetjek kun for salgssteder over tærsklen, bedste først, stop ved første nåelige --- (rutetjekket går markør->markør: hjem -> salgssted tælles som ét tjek pr. salgssted) -local routeChecks = 0 -local originalGetPathTo = adEnv.ADGraphManager.getPathTo -adEnv.ADGraphManager.getPathTo = function(_, vehicle, id) routeChecks = routeChecks + 1 return originalGetPathTo(_, vehicle, id) end -local originalFromTo = adEnv.ADGraphManager.pathFromTo -adEnv.ADGraphManager.pathFromTo = function(self, from, to) - if to == 10 or to == 11 then routeChecks = routeChecks + 1 end -- udturen til et salgssted - return originalFromTo(self, from, to) -end -ADWaitPool.resetMarkerRoutes() -local v14 = newVehicle("Routes", 0, 0, 20000) -runs[v14] = outboundRun(14, {1}, {{fillType = "SUGAR", policy = "sell"}}) -quotes["Far station"][SUGAR].percent = 99 -quotes["selling station"][SUGAR].percent = 92 -ADOutbound.choosePickup(adEnv, v14) -check("O14 bedste nåelig først: én rute tjekket", routeChecks, 1) -check("O14 mål = bedst betalende", v14.ad.stateModule.secondMarkerId, 11) -routeChecks = 0 -ADWaitPool.resetMarkerRoutes() -quotes["Far station"][SUGAR].percent = 60 -ADOutbound.choosePickup(adEnv, v14) -check("O14 under tærsklen tjekkes ikke", routeChecks, 1) -adEnv.ADGraphManager.getPathTo = originalGetPathTo - --- O15 (M1): advarsel for vare A gentages ikke efter en tur med vare B -notifications = {} -local v15 = newVehicle("Two", 0, 0, 20000) -runs[v15] = outboundRun(15, {1, 2}, {{fillType = "SUGAR", policy = "sell"}, {fillType = "FORAGE", policy = "sell"}}) -products[1] = {{fillType = SUGAR, name = "SUGAR", available = 5000, level = 5000, capacity = 180000, isPallet = false}, {fillType = FORAGE, name = "FORAGE", available = 5000, level = 5000, capacity = 180000, isPallet = false}} -quotes["selling station"][FORAGE] = {percent = 95, price = 1} -quotes["Far station"][SUGAR].percent = 99 -- (O14 satte den til 60) -unreachable[10] = true; unreachable[11] = true -ADOutbound.choosePickup(adEnv, v15) -- SUGAR uopnåelig -> advarsel, FORAGE uopnåelig -> advarsel -local firstCount = #notifications -unreachable[11] = nil -- Far station kan nås for SUGAR (FORAGE sælges kun på selling station) -ADOutbound.choosePickup(adEnv, v15) -- tur med SUGAR -check("O15 tur", ADOutbound.getTripFor(v15).kind, "sell") -ADOutbound.choosePickup(adEnv, v15) -- igen: FORAGE stadig uopnåelig, men allerede advaret -check("O15 FORAGE-advarsel gentages ikke", #notifications, firstCount) -unreachable[10] = nil -products[1] = {{fillType = SUGAR, name = "SUGAR", available = 5000, level = 5000, capacity = 180000, isPallet = false}} - --- O16 (M6): pallens ankomst ses også på fyldniveau når UAL's interne liste mangler -local v16 = newVehicle("NoList", 100, 0, 8000, true) -v16.trailers[1].spec_universalAutoload = {} -runs[v16] = outboundRun(16, {3}, {{fillType = "STRAW_BASKET", policy = "sell"}}) -sources[3].stored[BASKET] = 3; sources[3].spawnCalls = 0 -ADOutbound.choosePickup(adEnv, v16) -local task16 = newTask(v16, 3) -v16.atTarget, task16.activatedUALLoading = true, true -ADOutbound.beforeLoadUpdate(adEnv, task16, 100) -check("O16 første spawn", sources[3].spawnCalls, 1) -sources[3].stored[BASKET] = 2 -v16.trailers.level = 1000 -ADOutbound.beforeLoadUpdate(adEnv, task16, 100) -ADOutbound.beforeLoadUpdate(adEnv, task16, 100) -check("O16 næste spawn via fyldniveau", sources[3].spawnCalls, 2) - --- O17 (reserve + paller til lager): buildPlanInput henter reserve og palle-lager fra ADSources -local demandCalls = {} -ADSources.getFarmDemand = function(_, ft, farmId, excluded) table.insert(demandCalls, ft) return {liters = ft == SUGAR and 4000 or 0, consumers = {"TMR-fabrik"}} end -local palletStoreMarker = nil -ADSources.findPalletStore = function(_, vehicle, ft, excluded, isReachable, farmId) return palletStoreMarker end -local v17 = newVehicle("Reserve", 0, 0, 20000) -runs[v17] = outboundRun(17, {1}, {{fillType = "SUGAR", policy = "sell"}}) -local input17 = ADOutbound.buildPlanInput(adEnv, runs[v17], v17) -check("O17 reserve på salgsvare", input17.products[1].reserve, 4000) -check("O17 reserveret til", input17.products[1].reservedFor[1], "TMR-fabrik") -local v18 = newVehicle("PalletStore", 100, 0, 8000, true) -runs[v18] = outboundRun(18, {3}, {{fillType = "STRAW_BASKET", policy = "store"}}) -palletStoreMarker = {wayPointId = 40, name = "Palleskur", free = 100} -sources[3].stored[BASKET] = 4 -products[3][1].available, products[3][1].level, products[3][1].capacity = 4000, 3900, 4000 -local input18 = ADOutbound.buildPlanInput(adEnv, runs[v18], v18) -check("O18 palle-lager som store-mål", input18.products[1].store ~= nil and input18.products[1].store.wayPointId, 40) -check("O18 reserve spørges ikke for lager-politik", demandCalls[#demandCalls] ~= BASKET, true) - --- O19: pallebil (kun UAL) ved en fabrik der både har bulk-læssestation og sukkerpaller -> paller -local v19 = newVehicle("Dragon", 0, 0, 6000, true) -v19.carriable = {} -runs[v19] = outboundRun(19, {1}, {{fillType = "SUGAR", policy = "sell"}}) -products[1] = {{fillType = SUGAR, name = "SUGAR", available = 150000, level = 150000, capacity = 180000, isPallet = false, canPallet = true, palletLiters = 3000, palletCount = 3}} -quotes["selling station"][SUGAR].percent = 92 -local input19 = ADOutbound.buildPlanInput(adEnv, runs[v19], v19) -check("O19 varen tages som paller", input19.products[1] ~= nil and input19.products[1].isPallet, true) -check("O19 mængde = paller ved spawner", input19.products[1].sources[1].available, 3000) -check("O19 palle-antal følger med", input19.products[1].sources[1].palletCount, 3) -local v19b = newVehicle("Tipvogn", 0, 0, 20000) -runs[v19b] = outboundRun(20, {1}, {{fillType = "SUGAR", policy = "sell"}}) -local input19b = ADOutbound.buildPlanInput(adEnv, runs[v19b], v19b) -check("O19 tipvogn tager bulk", input19b.products[1].isPallet, false) -check("O19 bulk-mængde", input19b.products[1].sources[1].available, 150000) -products[1] = {{fillType = SUGAR, name = "SUGAR", available = 5000, level = 5000, capacity = 180000, isPallet = false}} - --- O20: UAL-bil: AD's "kapacitet" er antal paller (læssede + 1) -> liter-kapacitet ukendt i planen -local v20 = newVehicle("Dragon2", 100, 0, 1, true) -v20.carriable = {} -runs[v20] = outboundRun(21, {3}, {{fillType = "STRAW_BASKET", policy = "sell"}}) -local input20 = ADOutbound.buildPlanInput(adEnv, runs[v20], v20) -check("O20 kapacitet ukendt for pallebil", input20.capacity, nil) -check("O20 har autoload", input20.hasAutoload, true) -check("O20 turen planlægges alligevel", ADOutboundPlanner.plan(input20) ~= nil, true) - --- O21: autoload-bil på vej til aflæsning: stropperne løsnes UNSTRAP_BEFORE_M før målet (ladet passerer --- triggeren før markøren), kun én gang pr. tur, og aldrig for tipvogne -local v21 = newVehicle("Dragon3", 0, 0, 1, true) -v21.carriable = {} -v21.trailers[1].setAllTensionBeltsActive = function(self, active) self.beltsActive = active end -runs[v21] = outboundRun(22, {3}, {{fillType = "STRAW_BASKET", policy = "sell"}}) -sources[3].stored[BASKET] = 3 -quotes["selling station"][BASKET] = {percent = 95, price = 1} -ADOutbound.choosePickup(adEnv, v21) -check("O21 tur mod salgssted", v21.ad.stateModule.secondMarkerId, 10) -local unloadTask21 = {vehicle = v21, destinationID = 10, trailers = v21.trailers} -v21.rootNode = {x = 200 - 40, z = 0} -- 40 m fra markør 10 (x=200) -ADOutbound.beforeUnloadUpdate(adEnv, unloadTask21, 16) -check("O21 40 m: stropper stadig på", v21.trailers[1].beltsActive, nil) -v21.rootNode = {x = 200 - 20, z = 0} -ADOutbound.beforeUnloadUpdate(adEnv, unloadTask21, 16) -check("O21 20 m: stropper løsnet", v21.trailers[1].beltsActive, false) -v21.trailers[1].beltsActive = nil -ADOutbound.beforeUnloadUpdate(adEnv, unloadTask21, 16) -check("O21 kun én gang", v21.trailers[1].beltsActive, nil) -local v21b = newVehicle("Tip", 0, 0, 20000) -v21b.trailers[1].setAllTensionBeltsActive = function(self, active) self.beltsActive = active end -runs[v21b] = outboundRun(23, {1}, {{fillType = "SUGAR", policy = "sell"}}) -quotes["selling station"][SUGAR].percent = 92 -ADOutbound.choosePickup(adEnv, v21b) -v21b.rootNode = {x = 195, z = 0} -ADOutbound.beforeUnloadUpdate(adEnv, {vehicle = v21b, destinationID = 10, trailers = v21b.trailers}, 16) -check("O21 tipvogn: ingen stropper", v21b.trailers[1].beltsActive, nil) - --- O22: lagermål med en anden silo-markør inden for AD's globale trigger-afstand -> advarsel én gang --- (AD tipper ved første trigger inden for afstanden; 10 m løser det når siloer står tæt) -local triggerDistance = 25 -adEnv.AutoDrive.getSetting = function(name) if name == "maxTriggerDistance" then return triggerDistance end end -local v22 = newVehicle("Tip2", 0, 0, 20000) -runs[v22] = outboundRun(24, {1}, {{fillType = "SUGAR", policy = "store"}}) -products[1] = {{fillType = SUGAR, name = "SUGAR", available = 170000, level = 170000, capacity = 180000, isPallet = false}} -dumpMarker = mStore -- silo 6 (x=60); silo 4 (x=50) står 10 m derfra -logLines = {} -ADOutbound.choosePickup(adEnv, v22) -check("O22 lager-tur", ADOutbound.getTripFor(v22) ~= nil and ADOutbound.getTripFor(v22).kind, "store") -check("O22 advarsel om trigger-afstand", lastLog("Max trigger distance") ~= nil, true) -logLines = {} -ADOutbound.choosePickup(adEnv, v22) -check("O22 kun én gang", lastLog("Max trigger distance"), nil) -triggerDistance = 10 -local v22b = newVehicle("Tip3", 0, 0, 20000) -runs[v22b] = outboundRun(25, {1}, {{fillType = "SUGAR", policy = "store"}}) -logLines = {} -ADOutbound.choosePickup(adEnv, v22b) -check("O22 ved 10 m ingen advarsel", lastLog("Max trigger distance"), nil) -adEnv.AutoDrive.getSetting = nil -products[1] = {{fillType = SUGAR, name = "SUGAR", available = 5000, level = 5000, capacity = 180000, isPallet = false}} - --- O23: intet salgssted tager varen -> log én gang med de fundne salgsmarkører og hvad de tager -ADSources.describeAccepted = function(station) return "SUGAR, FORAGE" end -local v23 = newVehicle("Dragon4", 100, 0, 1, true) -v23.carriable = {} -runs[v23] = outboundRun(26, {3}, {{fillType = "STRAW_CRATE", policy = "sell"}}) -quotes["selling station"][CRATE] = nil; quotes["Far station"][CRATE] = nil -sources[3].stored[CRATE] = 2 -logLines = {} -ADOutbound.choosePickup(adEnv, v23) -local noSell = lastLog("intet salgssted tager STRAW_CRATE") -check("O23 log om manglende salgssted", noSell ~= nil, true) -check("O23 lister salgsmarkører", noSell ~= nil and noSell:find("selling station (SUGAR, FORAGE)", 1, true) ~= nil, true) -logLines = {} -ADOutbound.choosePickup(adEnv, v23) -check("O23 kun én gang", lastLog("intet salgssted tager STRAW_CRATE"), nil) - --- O10: install wrapper: fejl i hook stopper ikke AD -local calls = {} -adEnv.LoadAtDestinationTask.update = function(self, dt) calls.update = (calls.update or 0) + 1 end -adEnv.LoadAtDestinationTask.finished = function(self) calls.finished = (calls.finished or 0) + 1 end -ADOutbound.install(adEnv) -local vX = newVehicle("X", 0, 0, 1000) -adEnv.LoadAtDestinationTask.update(newTask(vX, 1), 16) -check("O10 original update kaldt", calls.update, 1) -adEnv.LoadAtDestinationTask.finished(newTask(vX, 1)) -check("O10 original finished kaldt", calls.finished, 1) -adEnv.UnloadAtDestinationTask.update = function(self, dt) calls.unload = (calls.unload or 0) + 1 end -ADOutbound.install(adEnv) -check("O10 unload-wrap ikke installeret dobbelt", adEnv.UnloadAtDestinationTask.update ~= nil, true) --- O24: stropperne spændes igen når pålæsningen er færdig (de blev løsnet 25 m før forrige salg; --- in-game 23/9: Dragon tabte paller på vejen to gange) -local v24 = newVehicle("Dragon5", 100, 0, 1, true) -v24.carriable = {} -v24.trailers[1].setAllTensionBeltsActive = function(self, active) self.beltsActive = active end -runs[v24] = outboundRun(27, {3}, {{fillType = "STRAW_BASKET", policy = "sell"}}) -sources[3].stored[BASKET] = 3 -quotes["selling station"][BASKET] = {percent = 95, price = 1} -ADOutbound.choosePickup(adEnv, v24) -local task24 = newTask(v24, 3) -v24.trailers.level = 3 -adEnv.LoadAtDestinationTask.finished(task24) -check("O24 stropper spændt efter pålæsning", v24.trailers[1].beltsActive, true) -check("O24 tur planlagt", ADOutbound.getTripFor(v24) ~= nil, true) -check("O24 original finished kaldt", calls.finished, 2) -local v24b = newVehicle("Tip4", 0, 0, 20000) -v24b.trailers[1].setAllTensionBeltsActive = function(self, active) self.beltsActive = active end -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 k1, k2 = sources[1].kind, sources[2].kind - sources[1].kind, sources[2].kind = "silo", "silo" - 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 - sources[1].kind, sources[2].kind = k1, k2 -end - --- O-pool-stald: mælke- og gyllemarkør ved samme stald deler lager, men er forskellige læssepunkter -do - local pool = {} - sources[31] = {kind = "husbandry", wayPointId = 31, station = {name = "barnSlurry", sourceStorages = {[pool] = pool}}, name = "Stald"} - sources[32] = {kind = "husbandry", wayPointId = 32, station = {name = "barnMilk", sourceStorages = {[pool] = pool}}, name = "Stald"} - products[31] = {{fillType = SUGAR_FT or 5, name = "SUGAR", title = "Sukker", available = 20000, level = 20000, capacity = 50000, isPallet = false}} - products[32] = {{fillType = SUGAR_FT or 5, name = "SUGAR", title = "Sukker", available = 20000, level = 20000, capacity = 50000, isPallet = false}} - local vBarn = newVehicle("Tank2", 0, 0, 10000) - runs[vBarn] = outboundRun(78, {31, 32}, {{fillType = "SUGAR", policy = "sell"}}, {collectRound = true}) - local inputBarn = ADOutbound.buildPlanInput(adEnv, runs[vBarn], vBarn) - local ids = {} - for _, product in ipairs(inputBarn.products) do - for _, source in ipairs(product.sources) do table.insert(ids, tostring(source.wayPointId)) end - end - check("O-pool-stald: stalde lægges ikke sammen", table.concat(ids, ","), "31,32") - sources[31], sources[32], products[31], products[32] = nil, nil, nil, nil -end - --- O-forced: "Lager til" valgt -> lagerturen går til det valgte mål, ikke moden egen silo-valg -do - sources[41] = {kind = "silo", wayPointId = 41, station = {name = "gylleSilo"}, name = "Gyllesilo"} - products[41] = {{fillType = SUGAR, name = "SUGAR", title = "Sukker", available = 11000000, level = 11000000, capacity = 12000000, isPallet = false}} - local vF = newVehicle("Gyllevogn", 0, 0, 20000) - runs[vF] = outboundRun(79, {41}, {{fillType = "SUGAR", policy = "store"}}) - runs[vF].forcedMarkers = {31} - ADRunsController.getStoreMarkers = function(_, run) return run.forcedMarkers or {} end - local forcedCalls = 0 - ADLoadSwap.findForcedDumpMarker = function(_, _, _, _, ids) forcedCalls = forcedCalls + 1 return {id = ids[1], name = "BGA", markerIndex = 99} end - dumpMarker = mStore - ADOutbound.choosePickup(adEnv, vF) - check("O-forced valgt mål brugt", forcedCalls > 0, true) - check("O-forced tur-mål = BGA", ADOutbound.getTripFor(vF) and ADOutbound.getTripFor(vF).targetWayPointId, 31) - ADRunsController.getStoreMarkers, ADLoadSwap.findForcedDumpMarker = nil, nil -end - --- O-empty: ingen vare ved kilden (æggene er ikke spawnet endnu) -> intet lagermål søges, ingen advarsel -do - sources[42] = {kind = "husbandry", wayPointId = 42, station = nil, name = "Chicken Coop"} - products[42] = {{fillType = SUGAR, name = "SUGAR", title = "Æg", available = 0, level = 0, capacity = 0, isPallet = false}} - local vE = newVehicle("Æg", 0, 0, 20000) - runs[vE] = outboundRun(80, {42}, {{fillType = "SUGAR", policy = "store"}}) - local storeLookups = 0 - local dumpBefore = ADLoadSwap.findDumpMarker - ADLoadSwap.findDumpMarker = function(...) storeLookups = storeLookups + 1 return nil, "ingen" end - ADOutbound.buildPlanInput(adEnv, runs[vE], vE) - check("O-empty intet lagermål søgt uden vare", storeLookups, 0) - ADLoadSwap.findDumpMarker = dumpBefore -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 deleted file mode 100644 index fa4dbf7..0000000 --- a/tests/test_adOutboundPlanner.lua +++ /dev/null @@ -1,188 +0,0 @@ --- Kør: luajit tests/test_adOutboundPlanner.lua (fra repo-roden) --- Ren turvurdering for udkørsel: prisregel, tærskler, politikker, rangering, opsamlingsrunde. - -dofile("FS25_ADSmartPickup/adOutboundPlanner.lua") -local P = ADOutboundPlanner - -local failures = 0 -local function check(name, actual, expected) - if actual == expected then - print("OK " .. name) - else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end -local function join(list) return table.concat(list or {}, ",") end - -local SUGAR, FORAGE, BASKET, CRATE, BROOM = 5, 6, 7, 8, 9 -local function input(products, overrides) - local result = {capacity = 20000, hasAutoload = false, sellAtPercent = 90, storeAbovePercent = 80, collectRound = false, products = products} - for key, value in pairs(overrides or {}) do result[key] = value end - return result -end -local function product(name, fillType, policy, sources, quote, store, isPallet) - return {fillType = fillType, name = name, policy = policy, isPallet = isPallet == true, sources = sources, quote = quote, store = store} -end -local quoteS = {percent = 92, price = 480, wayPointId = 900, stationName = "Grain Elevator"} -local storeSilo = {wayPointId = 400, name = "silo 1", free = 500000} - --- P1: salg ved pris over tærskel, uanset mængde -local trip = P.plan(input({product("SUGAR", SUGAR, "sell", {{wayPointId = 1, available = 5000, level = 5000, capacity = 180000}}, quoteS, nil)})) -check("P1 salg", trip.kind, "sell") -check("P1 mål", trip.targetWayPointId, 900) -check("P1 målnavn", trip.targetName, "Grain Elevator") -check("P1 kilde", join(trip.sourceIds), "1") -check("P1 vare", join(trip.fillTypes), "5") -check("P1 procent", trip.percent, 92) - --- P2: pris under tærskel, lager lavt -> ingen tur med begrundelse -local low = {percent = 78, price = 400, wayPointId = 900, stationName = "Grain Elevator"} -local trip2, reasons2 = P.plan(input({product("SUGAR", SUGAR, "sell", {{wayPointId = 1, available = 74000, level = 74000, capacity = 180000}}, low, nil)})) -check("P2 ingen tur", trip2, nil) -check("P2 begrundelse", reasons2[1], "SUGAR 78 % af top (kræver 90), lager 41 %") - --- P3: sellElseStore, pris lav, lager 85 % -> lager -local trip3 = P.plan(input({product("SUGAR", SUGAR, "sellElseStore", {{wayPointId = 1, available = 153000, level = 153000, capacity = 180000}}, low, storeSilo)})) -check("P3 lager", trip3.kind, "store") -check("P3 silo", trip3.targetWayPointId, 400) - --- P4: store med fuldt læs, lager 20 % -local trip4 = P.plan(input({product("FORAGE", FORAGE, "store", {{wayPointId = 2, available = 700000, level = 700000, capacity = 3500000}}, nil, storeSilo)})) -check("P4 fuldt læs -> lager", trip4.kind, "store") -local trip4b = P.plan(input({product("FORAGE", FORAGE, "store", {{wayPointId = 2, available = 15000, level = 15000, capacity = 3500000}}, nil, storeSilo)})) -check("P4 for lidt og lager lavt -> nil", trip4b, nil) - --- P5: rangering: højeste pris-% først -local trip5 = P.plan(input({ - product("SUGAR", SUGAR, "sell", {{wayPointId = 1, available = 1000, level = 1000, capacity = 180000}}, {percent = 91, price = 1, wayPointId = 900, stationName = "A"}, nil), - product("FORAGE", FORAGE, "sell", {{wayPointId = 2, available = 1000, level = 1000, capacity = 3500000}}, {percent = 95, price = 1, wayPointId = 901, stationName = "B"}, nil), -})) -check("P5 højeste procent vinder", join(trip5.fillTypes), "6") -check("P5 uden runde kun én vare", #trip5.fillTypes, 1) - --- P6: intet nåeligt salgssted -local trip6, reasons6 = P.plan(input({product("SUGAR", SUGAR, "sell", {{wayPointId = 1, available = 170000, level = 170000, capacity = 180000}}, nil, nil)})) -check("P6 ingen tur", trip6, nil) -check("P6 begrundelse", reasons6[1], "SUGAR: intet nåeligt salgssted") - --- P7: ingen silo med plads ved lagerbehov -local trip7, reasons7 = P.plan(input({product("FORAGE", FORAGE, "store", {{wayPointId = 2, available = 3000000, level = 3000000, capacity = 3500000}}, nil, nil)})) -check("P7 ingen tur", trip7, nil) -check("P7 begrundelse", reasons7[1], "FORAGE: intet lager med plads (silo eller egen fabrik der bruger varen)") - --- P8: runde, bulk: samme vare fra flere kilder, mest først -local trip8 = P.plan(input({product("SUGAR", SUGAR, "sell", {{wayPointId = 1, available = 3000, level = 3000, capacity = 180000}, {wayPointId = 3, available = 9000, level = 9000, capacity = 180000}}, quoteS, nil)}, {collectRound = true, capacity = 10000})) -check("P8 kilder mest først", join(trip8.sourceIds), "3,1") -check("P8 én vare", #trip8.fillTypes, 1) - --- P9: runde, UAL: alle pallevarer med samme mål -local sellS = function(pct) return {percent = pct, price = 1, wayPointId = 950, stationName = "Halm-salg"} end -local sellT = {percent = 96, price = 1, wayPointId = 951, stationName = "Andet"} -local pallets = { - product("STRAW_BASKET", BASKET, "sell", {{wayPointId = 5, available = 8000, level = 8, capacity = 4000}}, sellS(95), nil, true), - product("STRAW_CRATE", CRATE, "sell", {{wayPointId = 5, available = 2000, level = 2, capacity = 4000}}, sellS(93), nil, true), - product("STRAW_BROOM", BROOM, "sell", {{wayPointId = 6, available = 1000, level = 1, capacity = 4000}}, sellT, nil, true), -} -local trip9 = P.plan(input(pallets, {collectRound = true, hasAutoload = true})) -check("P9 højeste procent = kost alene (andet mål)", join(trip9.fillTypes), "9") -local trip9b = P.plan(input({pallets[1], pallets[2]}, {collectRound = true, hasAutoload = true})) -check("P9 kurve+kasser samme mål", join(trip9b.fillTypes), "7,8") -check("P9 navne", join(trip9b.names), "STRAW_BASKET,STRAW_CRATE") -check("P9 kilder unikke", join(trip9b.sourceIds), "5") -local trip9c = P.plan(input({pallets[1], pallets[2]}, {collectRound = true, hasAutoload = false})) -check("P9 uden UAL kun én pallevare", #trip9c.fillTypes, 1) - --- P10: uden runde kun én kilde (den med mest) -local trip10 = P.plan(input({product("SUGAR", SUGAR, "sell", {{wayPointId = 1, available = 3000, level = 3000, capacity = 180000}, {wayPointId = 3, available = 9000, level = 9000, capacity = 180000}}, quoteS, nil)})) -check("P10 én kilde, den fyldeste", join(trip10.sourceIds), "3") - --- P11: intet på lager -local trip11, reasons11 = P.plan(input({product("SUGAR", SUGAR, "sell", {}, quoteS, nil)})) -check("P11 ingen tur", trip11, nil) -check("P11 begrundelse", reasons11[1], "SUGAR: intet på lager") - --- P12: evaluate -local e = P.evaluate(product("SUGAR", SUGAR, "sell", {{wayPointId = 1, available = 150000, level = 150000, capacity = 180000}}, quoteS, nil), input({})) -check("P12 storagePercent", math.floor(e.storagePercent + 0.5), 83) -check("P12 storageHigh", e.storageHigh, true) -check("P12 fullLoad", e.fullLoad, true) -check("P12 priceOk", e.priceOk, true) - --- P13 (reserve): det gårdens fabrikker/stalde skal bruge, sælges ikke -local BEET = 10 -local withReserve = product("SUGARBEET", BEET, "sell", {{wayPointId = 3, available = 268000, level = 268000, capacity = 12000000}}, quoteS, nil) -withReserve.reserve, withReserve.reservedFor = 196000, {"TMR-fabrik"} -local trip13 = P.plan(input({withReserve})) -check("P13 fuldt læs uden om reserven -> salg", trip13 ~= nil and trip13.kind, "sell") -withReserve.reserve = 260000 -- kun 8.000 til overs: mindre end et læs -local trip13b, reasons13b = P.plan(input({withReserve})) -check("P13 reserven ville blive rørt -> ingen tur", trip13b, nil) -check("P13 begrundelse", reasons13b[1], "SUGARBEET: 260.0k reserveret til TMR-fabrik") -withReserve.reserve = 300000 -- alt reserveret -check("P13 alt reserveret -> ingen tur", P.plan(input({withReserve})), nil) -withReserve.reserve = 0 -check("P13 reserve 0 -> salg uanset mængde", P.plan(input({withReserve})).kind, "sell") --- lager-politik ignorerer reserven (varen bliver på gården) -local storeReserve = product("SUGARBEET", BEET, "store", {{wayPointId = 3, available = 268000, level = 11000000, capacity = 12000000}}, nil, storeSilo) -storeReserve.reserve = 300000 -check("P13 lager rører ikke reserven", P.plan(input({storeReserve})).kind, "store") -local e13 = P.evaluate(withReserve, input({}, {capacity = 20000})) -check("P13 evaluate sellable", e13.sellable, 268000) - --- P14 (paller): kapacitet ukendt for en autoload-bil -> "fuldt læs" = mindst PALLET_FULL_LOAD_COUNT paller klar -local function palletInput(products) - local inp = input(products, {hasAutoload = true}) - inp.capacity = nil - return inp -end -local palletProduct = product("STRAW_BASKET", BASKET, "store", {{wayPointId = 5, available = 3000, level = 1600, capacity = 8000, palletCount = 3}}, nil, storeSilo, true) -check("P14 3 paller: ikke fuldt læs", P.plan(palletInput({palletProduct})), nil) -palletProduct.sources[1].palletCount = 4 -check("P14 4 paller: lager-tur", P.plan(palletInput({palletProduct})).kind, "store") -local twoSources = product("STRAW_BASKET", BASKET, "store", {{wayPointId = 5, available = 2000, level = 1600, capacity = 8000, palletCount = 2}, {wayPointId = 3, available = 2000, level = 2, capacity = 4000, palletCount = 2}}, nil, storeSilo, true) -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) - --- P16: fodring ("Lager til" valgt = feedMode): kør så snart kilden har FEED_MIN_LITERS — ikke fuldt læs / 80 % -local feedStore = {wayPointId = 50, name = "Slurry Drop 1", free = 100000} -local feedProduct = product("LIQUIDMANURE", FORAGE, "store", {{wayPointId = 2, available = 20000, level = 20000, capacity = 500000}}, nil, feedStore) -local tripNoFeed = P.plan(input({feedProduct}, {capacity = 100000})) -check("P16 uden fodring: 4 % og intet fuldt læs -> ingen tur", tripNoFeed, nil) -local tripFeed = P.plan(input({feedProduct}, {capacity = 100000, feedMode = true})) -check("P16 fodring: 20k >= minimum -> lagertur", tripFeed and tripFeed.kind, "store") -check("P16 fodring: mål = valgt drop", tripFeed and tripFeed.targetWayPointId, 50) -local tinyProduct = product("LIQUIDMANURE", FORAGE, "store", {{wayPointId = 2, available = 3000, level = 3000, capacity = 500000}}, nil, feedStore) -check("P16 fodring: under minimum -> ingen tur", P.plan(input({tinyProduct}, {capacity = 100000, feedMode = true})), nil) - --- P17: valgt lager ("Lager til") kan ikke bruges -> begrundelsen er den præcise grund, ikke "intet lager med plads" -local blocked = product("EGG", FORAGE, "store", {{wayPointId = 2, available = 3000000, level = 3000000, capacity = 3500000}}, nil, nil) -blocked.storeReason = "Bale & Pallet Storage: ingen vej til 'Chicken Storage Unload'" -local _, reasons17 = P.plan(input({blocked})) -check("P17 præcis grund i status", reasons17[1], "EGG: Bale & Pallet Storage: ingen vej til 'Chicken Storage Unload'") - --- SA: "sælg altid over X %" — kilden over grænsen sælges selvom prisen er lav (gylle ved BGA'en, 26/9) -local lowQuote = {percent = 40, price = 0.5, top = 1.25, wayPointId = 77, stationName = "Liquid Buy"} -local fullSlurry = {{wayPointId = 5, available = 900000, level = 900000, capacity = 1000000}} -local lowSlurry = {{wayPointId = 5, available = 300000, level = 300000, capacity = 1000000}} -local saOff = P.plan(input({product("LIQUIDMANURE", 40, "sell", fullSlurry, lowQuote, nil)})) -check("SA fra: lav pris -> ikke salg", saOff ~= nil and saOff.kind or "intet", "intet") -local saOn = P.plan(input({product("LIQUIDMANURE", 40, "sell", fullSlurry, lowQuote, nil)}, {sellAbovePercent = 80})) -check("SA 90 % fyldt over 80 %: sælg trods lav pris", saOn ~= nil and saOn.kind, "sell") -local saLow = P.plan(input({product("LIQUIDMANURE", 40, "sell", lowSlurry, lowQuote, nil)}, {sellAbovePercent = 80})) -check("SA 30 % fyldt: ikke salg", saLow ~= nil and saLow.kind or "intet", "intet") - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adPool.lua b/tests/test_adPool.lua deleted file mode 100644 index 14434b2..0000000 --- a/tests/test_adPool.lua +++ /dev/null @@ -1,183 +0,0 @@ --- Kør: luajit tests/test_adPool.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adFieldFleet.lua") -dofile("FS25_ADSmartPickup/adHitch.lua") -dofile("FS25_ADSmartPickup/adPool.lua") -local P = ADPool -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end --- rolle ud fra redskabets egne specs -check("rolle: plov", P.roleOf({spec_plow = true}), "plow") -check("rolle: kultivator", P.roleOf({spec_cultivator = true}), "cultivator") -check("rolle: vogn med tip", P.roleOf({spec_trailer = true, spec_dischargeable = true}), "unloader") -check("rolle: vogn der også er læssevogn er læssevogn", P.roleOf({spec_trailer = true, spec_dischargeable = true, spec_forageWagon = true}), "collector") -check("rolle: intet", P.roleOf({}), nil) --- hk fra butiksdata: tal eller tabel (konfigurationer) -check("tal", P.numberOf(180), 180) -check("tabel: største", P.numberOf({base = 120, config = 150}), 150) -check("tom tabel", P.numberOf({}), nil) -check("nil", P.numberOf(nil), nil) -check("hk: nok", P.powerOk(200, 180), true) -check("hk: for lidt", P.powerOk(150, 180), false) -check("hk: redskab uden data", P.powerOk(150, nil), true) -check("hk: traktor uden data", P.powerOk(nil, 180), true) --- kategori: markredskaber kun bag traktorer (in-game 29/9: Unimog, kategori "trucks", hentede kultivatoren) -check("kategori: traktor til kultivator", P.categoryOk("TRACTORSM", "cultivator"), true) -check("kategori: lastbil til kultivator", P.categoryOk("TRUCKS", "cultivator"), false) -check("kategori: lastbil til tømmevogn", P.categoryOk("TRUCKS", "unloader"), true) -check("kategori: teleskoplæsser til plov", P.categoryOk("TELELOADERVEHICLES", "plow"), false) -check("kategori: ukendt -> tilladt", P.categoryOk(nil, "cultivator"), true) -check("kategori: små bogstaver", P.categoryOk("tractorsL", "plow"), true) -local unimog = {id = "u", x = 0, z = 0, hp = 155, category = "TRUCKS"} -local fendt = {id = "f", x = 90, z = 0, hp = 250, category = "TRACTORSM"} -local cult = {{id = "c", role = "cultivator", roles = {cultivator = true}, x = 1, z = 0, neededHp = 120, name = "Cult"}} -local picked = P.pairFor("cultivator", 0, 0, cult, {unimog, fendt}, {}) -check("parring: nærmeste lastbil springes over", picked ~= nil and picked.tractor.id or nil, "f") --- Connect-markør til en parkering -local markers = {{name = "Start", id = 1}, {name = "Bredal", id = 4}, {name = "Connect Bredal", id = 9}, {name = "connect plov 1", id = 12}} -check("connect: Bredal", P.connectFor("Bredal", markers).id, 9) -check("connect: små bogstaver", P.connectFor("Plov 1", markers).id, 12) -check("connect: ingen", P.connectFor("Start", markers), nil) -check("connect: uden parkering", P.connectFor(nil, markers), nil) --- parring: redskab nærmest marken, så traktor nærmest redskabet -local implements = { - {id = "plovFar", name = "Plov far", role = "plow", x = 500, z = 0, connectId = 20, parkId = 21}, - {id = "plovNear", name = "Plov nær", role = "plow", x = 100, z = 0, neededHp = 180, connectId = 30, parkId = 31}, - {id = "kult", name = "Kultivator", role = "cultivator", x = 0, z = 0, connectId = 40, parkId = 41}, -} -local tractors = { - {id = "small", name = "Lille", x = 110, z = 0, hp = 120, parkId = 2}, - {id = "big", name = "Stor", x = 300, z = 0, hp = 250, parkId = 3}, - {id = "mid", name = "Mellem", x = 150, z = 0, hp = 190, parkId = 5}, -} -local pair = P.pairFor("plow", 0, 0, implements, tractors, {}) -check("par: nærmeste plov til marken", pair.implement.id, "plovNear") -check("par: nærmeste stærke traktor til ploven", pair.tractor.id, "mid") -local blockedPair = P.pairFor("plow", 0, 0, implements, tractors, {plovNear = true}) -check("par: reserveret plov springes over", blockedPair.implement.id, "plovFar") -check("par: plov uden hk-krav tager nærmeste traktor", blockedPair.tractor.id, "big") -local none, why = P.pairFor("seeder", 0, 0, implements, tractors, {}) -check("par: ingen såmaskine", none, nil); check("par: grund", why, "noPoolImplement") -local weak, weakWhy, weakArgs = P.pairFor("plow", 0, 0, {implements[2]}, {tractors[1]}, {}) -check("par: for svag", weak, nil); check("par: grund tooWeak", weakWhy, "tooWeak"); check("par: hk i args", weakArgs[1], 180) -local noTractor, noTractorWhy = P.pairFor("plow", 0, 0, implements, {}, {}) -check("par: ingen traktor", noTractor, nil); check("par: grund noPoolTractor", noTractorWhy, "noPoolTractor") -local rejected, rejectedWhy = P.pairFor("plow", 0, 0, implements, tractors, {}, function() return false end) -check("par: accept afviser alle", rejected, nil); check("par: grund", rejectedWhy, "noPoolImplement") -local multi = P.pairFor("fertilizer", 0, 0, {{id = "spreder", role = "fertilizer", roles = {fertilizer = true, liming = true}, x = 0, z = 0}}, tractors, {}) -check("par: redskab med flere roller", multi.implement.id, "spreder") --- reservationer og karantæne -P.reserve("F12", {"plovNear", "mid"}) -check("reserveret: redskab", P.blocked(0).plovNear, true) -check("reserveret: traktor", P.blocked(0).mid, true) -P.release("F12") -check("frigivet", P.blocked(0).plovNear, nil) -P.quarantine("plovNear", 1000) -check("karantæne: aktiv", P.blocked(1000 + P.QUARANTINE_MS - 1).plovNear, true) -check("karantæne: udløbet", P.blocked(1000 + P.QUARANTINE_MS).plovNear, nil) --- kø: længst ventende først, lige -> lavest mark-id, ikke-ventende bagerst i oprindelig rækkefølge -local infos = {{id = 5}, {id = 3}, {id = 9}, {id = 1}} -local ordered = P.queueOrder(infos, {[9] = 100, [3] = 50, [1] = 50}) -check("kø: 1.", ordered[1].id, 1); check("kø: 2.", ordered[2].id, 3); check("kø: 3.", ordered[3].id, 9); check("kø: 4.", ordered[4].id, 5) -check("kø: rører ikke input", infos[1].id, 5) --- på hjemtur: optaget indtil hjemme -local tractorObject = {} -check("hjemtur: ikke fra start", P.isReturning(tractorObject), false) -P.setReturning(tractorObject, true) -check("hjemtur: markeret", P.isReturning(tractorObject), true) -P.setReturning(tractorObject, false) -check("hjemtur: hjemme", P.isReturning(tractorObject), false) --- review-rettelser --- C1: hjemtur også når AD selv ville parkere (påfyldte sæt); aldrig ved manuel transport -check("C1: hjem trods adHandlesEnd", P.shouldReturnHome({adHandlesEnd = true}), true) -check("C1: ikke ved manuel", P.shouldReturnHome({manual = true}), false) --- I1: kun maskiner uden egen arbejdsrolle kan trække et puljeredskab -check("I1: traktor uden rolle", P.canPull(nil), true) -check("I1: mejetærsker", P.canPull("harvester"), false) -check("I1: selvkørende sprøjte", P.canPull("fertilizer"), false) --- I2: traktor uden AD-vejnet springes over; fejl uden fremdrift skyldes traktoren -local offNet = P.pairFor("plow", 0, 0, {implements[2]}, {{id = "far", x = 100, z = 0, offNetwork = true}, {id = "ok", x = 400, z = 0, hp = 300}}, {}) -check("I2: traktor uden for vejnettet springes over", offNet.tractor.id, "ok") -check("I2: stoppedAway -> traktoren", P.failureBlames("stoppedAway"), "tractor") -check("I2: stoppedAway med afstand -> traktoren", P.failureBlames("stoppedAway (40 m fra markøren)"), "tractor") -check("I2: noAttachable -> redskabet", P.failureBlames("noAttachable"), "implement") --- I3: efter koblingen — tom uden påfyldning = fejl, tom med påfyldning = fyld op, ellers kør -check("I3: ingen påfyldningsbehov", P.nextDrive(nil), "drive") -check("I3: fyldt", P.nextDrive({empty = false}), "drive") -check("I3: tom med påfyldning", P.nextDrive({empty = true, refillId = 7}), "load") -check("I3: tom uden påfyldning", P.nextDrive({empty = true}), "empty") --- I4: puljen kun til hovedopgaver med ét sæt, og ikke når marken har egne faste maskiner til rollen -check("I4: almindelig", P.mayUsePool({count = 1}, false), true) -check("etape 2: tømmevogn må bruge puljen", P.mayUsePool({count = 1, helper = true}, false), true) -check("I4: multi-tool", P.mayUsePool({count = 2}, false), false) -check("I4: marken har faste", P.mayUsePool({count = 1}, true), false) --- status til flådefanen -local function st(key, arg) return tostring(key) .. "|" .. tostring(arg) end -local function ist(s) return st(P.implementStatus(s)) end -check("redskab: uden parkering", ist({}), "noPark|nil") -check("redskab: uden Connect", ist({parkName = "Plov"}), "noConnect|Plov") -check("redskab: på traktor", ist({parkName = "Plov", hasConnect = true, attachedTo = "MT635"}), "attached|MT635") -check("redskab: karantæne", ist({parkName = "Plov", hasConnect = true, quarantineMin = 8}), "quarantine|8") -check("redskab: reserveret", ist({parkName = "Plov", hasConnect = true, reserved = true}), "reserved|nil") -check("redskab: parkeret", ist({parkName = "Plov", hasConnect = true}), "parked|Plov") -local function tst(s) return st(P.tractorStatus(s)) end -local base = {hasPark = true, enabled = true} -check("traktor: uden parkering", tst({enabled = true}), "noPark|nil") -check("traktor: spiller", tst({hasPark = true, enabled = true, controlled = true}), "player|nil") -check("traktor: hjemtur", tst({hasPark = true, enabled = true, returning = true}), "returning|nil") -check("traktor: opgave", tst({hasPark = true, enabled = true, fieldId = 52}), "job|52") -check("traktor: optaget", tst({hasPark = true, enabled = true, busy = true}), "busy|nil") -check("traktor: ikke i flåden", tst({hasPark = true}), "notInFleet|nil") -check("traktor: uden for vejnet", tst({hasPark = true, enabled = true, offNetwork = true}), "offNetwork|nil") -check("traktor: har andet redskab på", tst({hasPark = true, enabled = true, hitched = true}), "hitched|nil") -check("traktor: ledig", tst(base), "free|nil") --- karantæne-rest i hele minutter (rundet op) og reservation -P.quarantine("q1", 0) -check("karantæne: minutter tilbage", P.quarantineMinutes("q1", 60000), 9) -check("karantæne: udløbet", P.quarantineMinutes("q1", P.QUARANTINE_MS), 0) -P.reserve("owner", {"r1"}) -check("reserveret: ja", P.isReserved("r1"), true) -P.release("owner") -check("reserveret: nej", P.isReserved("r1"), false) --- M3: ventende marker prøver igen i samme takt (så køens rækkefølge holder) -check("M3: næste takt", P.alignedRetry(61000, 30000), 90000) -check("M3: på grænsen -> næste", P.alignedRetry(60000, 30000), 90000) -check("M3: to marker samme takt", P.alignedRetry(62000, 30000) == P.alignedRetry(89000, 30000), true) --- M4: for svag nævner redskabets navn -check("M4: navn i args", weakArgs[2], "Plov nær") --- etape 2: puljevogn skal kunne bære høstens vare -check("bære: vare understøttet", P.canCarry({carries = {[3] = true}}, 3), true) -check("bære: vare ikke understøttet", P.canCarry({carries = {[3] = true}}, 7), false) -check("bære: ingen vare krævet", P.canCarry({carries = {}}, nil), true) -check("bære: ukendt last", P.canCarry({}, 3), false) --- etape 3: alle puljevogne parret med hver sin nærmeste traktor (en traktor bruges kun én gang) -local wagons = {{id = "v1", role = "unloader", x = 0, z = 0}, {id = "v2", role = "unloader", x = 100, z = 0}, - {id = "v3", role = "unloader", x = 200, z = 0}, {id = "plovX", role = "plow", x = 0, z = 0}} -local pairs3 = P.pairAll("unloader", wagons, {{id = "tA", x = 10, z = 0}, {id = "tB", x = 90, z = 0}}, {v2 = true}) -check("par-alle: antal (v2 reserveret, 2 traktorer)", #pairs3, 2) -check("par-alle: v1 -> tA", pairs3[1].implement.id .. ">" .. pairs3[1].tractor.id, "v1>tA") -check("par-alle: v3 -> tB (tA er brugt)", pairs3[2].implement.id .. ">" .. pairs3[2].tractor.id, "v3>tB") -check("par-alle: ingen traktorer", #P.pairAll("unloader", wagons, {}, {}), 0) --- flow-vogne: vogn der hører til et kørende flow vises sådan (og er ikke i puljen) -check("redskab: flowets vogn", ist({parkName = "P Cow Food vogn 1", hasConnect = true, flowName = "Cow Food"}), "flow|Cow Food") -check("redskab: flowets vogn koblet på", ist({parkName = "P Cow Food vogn 1", hasConnect = true, flowName = "Cow Food", attachedTo = "Cow Food 1"}), "attached|Cow Food 1") --- pasform: mindste traktor med hk nok; afstand kun inden for ±15 %; over 2,5x behovet kun som nødløsning --- (in-game 29/9: en meget stor traktor blev sendt til et lille job og kunne ikke komme rundt) -local impl120 = {id = "k", x = 0, z = 0, neededHp = 120} -local t150 = {id = "t150", x = 900, z = 0, hp = 150} -local t170 = {id = "t170", x = 300, z = 0, hp = 180} -local t500 = {id = "t500", x = 50, z = 0, hp = 500} -check("pasform: mindste der passer vinder over nærmere", P.bestFit({t500, t170, t150}, impl120, "cultivator").id, "t150") -local t160 = {id = "t160", x = 100, z = 0, hp = 160} -check("pasform: inden for 15 % vinder den nærmeste", P.bestFit({t150, t160}, impl120, "cultivator").id, "t160") -check("pasform: kun for stor -> nødløsning", P.bestFit({t500}, impl120, "cultivator").id, "t500") -check("pasform: for svag -> ingen", P.bestFit({{id = "t90", x = 0, z = 0, hp = 90}}, impl120, "cultivator"), nil) -check("pasform: redskab uden hk-data -> nærmeste", P.bestFit({t150, t500}, {id = "x", x = 0, z = 0}, "cultivator").id, "t500") -check("pasform: ukendt traktor-hk efter kendte", P.bestFit({{id = "u", x = 0, z = 0}, t170}, impl120, "cultivator").id, "t170") -check("pasform: lastbil tæller ikke", P.bestFit({{id = "lb", x = 0, z = 0, hp = 150, category = "TRUCKS"}, t500}, impl120, "cultivator").id, "t500") -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adPrices.lua b/tests/test_adPrices.lua deleted file mode 100644 index ae98d98..0000000 --- a/tests/test_adPrices.lua +++ /dev/null @@ -1,106 +0,0 @@ --- Kør: luajit tests/test_adPrices.lua (fra repo-roden) --- Prisregel: toppris fra årets prisforløb, pris-% pr. salgssted, bedste tilbud. - -dofile("FS25_ADSmartPickup/adPrices.lua") - -local failures = 0 -local function check(name, actual, expected) - if actual == expected then - print("OK " .. name) - else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - --- P1: højeste faktor -check("P1 maxFactor", ADPrices.maxFactor({EARLY_SPRING = 0.9, LATE_AUTUMN = 1.2, MID_SUMMER = 1.0}), 1.2) -check("P1 uden faktorer = 1", ADPrices.maxFactor(nil), 1) - --- P2: toppris = grundpris nu × (maks ÷ nu) -check("P2 topPrice", ADPrices.topPrice(500, 1.0, 1.25), 625) -check("P2 factorNow 0 -> grundpris", ADPrices.topPrice(500, 0, 1.25), 500) -check("P2 grundpris 0 -> 0", ADPrices.topPrice(0, 1, 1.25), 0) - --- P3: procent, afrundet; stor efterspørgsel > 100 -check("P3 75 %", ADPrices.percentOfTop(470, 625), 75) -check("P3 112 %", ADPrices.percentOfTop(700, 625), 112) -check("P3 top 0 -> 0", ADPrices.percentOfTop(1, 0), 0) - --- P4: spil-adapter med stationens grundpris -SellingStation = {} -local SUGAR = 5 -g_fillTypeManager = {getFillTypeByIndex = function(_, index) - if index == SUGAR then return {economy = {factors = {A = 1.0, B = 1.25}}, pricePerLiter = 400} end -end} -g_currentMission = {environment = {currentPeriod = "A"}, economyManager = {getPriceMultiplier = function() return 1.0 end}} -local station = { - fillTypePrices = {[SUGAR] = 500}, - getEffectiveFillTypePrice = function(_, fillType) return 470 end, - isa = function(_, class) return class == SellingStation end, - isSellingPoint = true, -} -local quote = ADPrices.getQuote(station, SUGAR) -check("P4 pris", quote.price, 470) -check("P4 top (pris ved 100 %)", math.floor(quote.top + 0.5), 588) -check("P4 procent = kurven alene (stationens grundpris bruges ikke: den mangler sværhedsgrad m.m.)", quote.percent, 80) -check("P4 er salgssted", ADPrices.isSellingStation(station), true) -check("P4 skjult = ikke salgssted", ADPrices.isSellingStation({isa = function() return true end, isSellingPoint = true, hideFromPricesMenu = true}), false) -check("P4 fabrik-station = ikke salgssted", ADPrices.isSellingStation({isa = function() return true end, isSellingPoint = false}), false) --- in-game (23/9): TMR-fabrikkens indlevering er en SellingStation med isSellingPoint=true -> blev regnet som marked -check("P4 fabrikkens egen station = ikke salgssted", ADPrices.isSellingStation({isa = function() return true end, isSellingPoint = true, owningPlaceable = {spec_productionPoint = {}}}), false) -check("P4 staldens egen station = ikke salgssted", ADPrices.isSellingStation({isa = function() return true end, isSellingPoint = true, owningPlaceable = {getHusbandryFillLevel = function() end}}), false) --- P4b: fremmed produktion (kortets mejeri, ejer 0) køber varen = salgssted; gårdens egen fabrik er ikke -check("P4b fremmed mejeri = salgssted", ADPrices.isSellingStation({isa = function() return true end, isSellingPoint = true, owningPlaceable = {spec_productionPoint = {}, getOwnerFarmId = function() return 0 end}}), true) -check("P4b egen fabrik (ejer 1) = ikke salgssted", ADPrices.isSellingStation({isa = function() return true end, isSellingPoint = true, owningPlaceable = {spec_productionPoint = {}, getOwnerFarmId = function() return 1 end}}), false) -check("P4b fremmed stald-felt ownerFarmId", ADPrices.isSellingStation({isa = function() return true end, isSellingPoint = true, owningPlaceable = {getHusbandryFillLevel = function() end, ownerFarmId = 0}}), true) -check("P4 rigtigt salgssted med placeable", ADPrices.isSellingStation({isa = function() return true end, isSellingPoint = true, owningPlaceable = {}}), true) - --- P5: uden stationens grundpris: kun kurven tæller (faktor nu ÷ faktor max) -local bare = {getEffectiveFillTypePrice = function() return 470 end} -local quote2 = ADPrices.getQuote(bare, SUGAR) -check("P5 procent = kurven alene", quote2.percent, 80) -check("P5 top = effektiv ÷ kurve", math.floor(quote2.top + 0.5), 588) --- P7: kurven er grundlaget (Distribution Redux-metoden): faktor nu ÷ årets max, uafhængigt af sværhedsgrad -check("P7 curvePercent", ADPrices.curvePercent({1.0, 1.25, 0.9}, 1), 80) -check("P7 curvePercent ved top", ADPrices.curvePercent({1.0, 1.25, 0.9}, 2), 100) -check("P7 flad kurve = 100", ADPrices.curvePercent({1.0, 1.01, 1.0}, 3), 100) -check("P7 ingen kurve = 100", ADPrices.curvePercent(nil, 1), 100) -check("P7 ukendt periode = 100", ADPrices.curvePercent({1.0, 1.25}, nil), 100) --- P8: in-game (23/9): effektiv 1602 mod fillTypePrices 505 gav 317 % — stationens grundpris indeholder --- hverken sværhedsgrad eller prisskala, så forholdet er ubrugeligt. Procenten er kurven alene. -local demandStation = {fillTypePrices = {[SUGAR] = 505}, getEffectiveFillTypePrice = function() return 1602 end} -check("P8 procent uafhængig af stationens grundpris", ADPrices.getQuote(demandStation, SUGAR).percent, 80) -check("P8 top = pris ved 100 % af kurven", math.floor(ADPrices.getQuote(demandStation, SUGAR).top + 0.5), 2003) --- P9: sværhedsgraden påvirker ikke procenten -local hardStation = {getEffectiveFillTypePrice = function() return 235 end} -check("P9 uafhængig af sværhedsgrad", ADPrices.getQuote(hardStation, SUGAR).percent, 80) -check("P5 fejlende station -> nil", ADPrices.getQuote({getEffectiveFillTypePrice = function() error("x") end}, SUGAR), nil) - --- P6: bedste tilbud = højeste procent, dernæst pris -local best = ADPrices.bestQuote({{percent = 80, price = 1}, {percent = 92, price = 2}, {percent = 92, price = 3}}) -check("P6 højeste procent", best.percent, 92) -check("P6 ved lighed højeste pris", best.price, 3) -check("P6 tom -> nil", ADPrices.bestQuote({}), nil) - --- P-land: kortanlæg der følger jorden (boughtWithFarmland) er gårdens, når jorden under dem er gårdens — --- selv om placeable-ejeren står som 15 (Dakotas BGA500kw på jordlod 69) -getWorldTranslation = getWorldTranslation or function(node) return node.x, 0, node.z end -g_currentMission = g_currentMission or {} -g_currentMission.getFarmId = function() return 1 end -g_farmlandManager = { - getFarmlandIdAtWorldPosition = function(_, x, z) return x < 0 and 69 or 70 end, - getFarmlandOwner = function(_, id) return id == 69 and 1 or 0 end, -} -local bga = {rootNode = {x = -1600, z = -89}, boughtWithFarmland = true, spec_productionPoint = {}, getOwnerFarmId = function() return 15 end} -local bgaOther = {rootNode = {x = 1600, z = -89}, boughtWithFarmland = true, spec_productionPoint = {}, getOwnerFarmId = function() return 15 end} -local dairy = {rootNode = {x = -3173, z = -4028}, spec_productionPoint = {}, getOwnerFarmId = function() return 0 end} -check("P-land BGA på egen jord = egen", ADPrices.isOwnedBy(bga, 1), true) -check("P-land BGA på fremmed jord = ikke egen", ADPrices.isOwnedBy(bgaOther, 1), false) -check("P-land mejeri (ejer 0, ikke jord-bundet) = ikke egen", ADPrices.isOwnedBy(dairy, 1), false) -check("P-land egen ejer", ADPrices.isOwnedBy({getOwnerFarmId = function() return 1 end}, 1), true) -check("P-land BGA er ikke salgssted", ADPrices.isSellingStation({isa = function() return true end, isSellingPoint = true, owningPlaceable = bga}), false) -g_farmlandManager = nil - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adProfiler.lua b/tests/test_adProfiler.lua deleted file mode 100644 index 0f5cb38..0000000 --- a/tests/test_adProfiler.lua +++ /dev/null @@ -1,42 +0,0 @@ --- Kør: luajit tests/test_adProfiler.lua (fra repo-roden) -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end -local lines = {} -Logging = {info = function(fmt, ...) table.insert(lines, string.format(fmt, ...)) end} -local clock = 0 -function netGetTime() return clock end -dofile("FS25_ADSmartPickup/adProfiler.lua") - -local Module = {} -function Module.slow(a, b) clock = clock + 5; return a + b, "ok" end -function Module.fast() clock = clock + 0.1 end -ADProfiler.wrap(Module, "slow", "langsom") -ADProfiler.wrap(Module, "fast", "hurtig") -local sum, text = Module.slow(2, 3) -check("P1 returværdier bevares", sum, 5); check("P1 anden returværdi", text, "ok") -Module.fast() -check("P2 kald talt", ADProfiler.stats["langsom"].calls, 1) -check("P2 max", ADProfiler.stats["langsom"].max, 5) -ADProfiler.frame(0) -check("P3 værste frame", ADProfiler.worstFrameMs, 5.1) -ADProfiler.frame(61000) -check("P4 rapport skrevet", #lines, 1) -check("P4 langsom først", lines[1]:find("langsom max 5.0", 1, true) ~= nil, true) -check("P4 nulstillet", next(ADProfiler.stats), nil) --- intet over 2 ms -> ingen rapport -Module.fast(); ADProfiler.frame(62000); ADProfiler.frame(130000) -check("P5 stille minut -> ingen linje", #lines, 1) --- fejl i indpakket funktion: dybden nulstilles næste frame -function Module.boom() error("x") end -ADProfiler.wrap(Module, "boom", "fejl") -pcall(Module.boom) -ADProfiler.frame(131000) -check("P6 dybde nulstillet", ADProfiler.depth, 0) - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adRuns.lua b/tests/test_adRuns.lua deleted file mode 100644 index 7d65071..0000000 --- a/tests/test_adRuns.lua +++ /dev/null @@ -1,385 +0,0 @@ --- Kør: luajit tests/test_adRuns.lua (fra repo-roden) --- Tester run-modellen (rene funktioner) og XML-laget mod en XMLFile-mock. - -dofile("FS25_ADSmartPickup/adRuns.lua") - -local failures = 0 -local function check(name, actual, expected) - if actual == expected then - print("OK " .. name) - else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - --- R1: opret run -> løbenummer, standardværdier; input-store røres ikke -local empty = ADRuns.newStore() -local s1, cow = ADRuns.createRun(empty, "Cow Feed") -check("R1 id 1", cow.id, 1) -check("R1 navn", cow.name, "Cow Feed") -check("R1 auto-varer som standard", cow.ingredientMode, "auto") -check("R1 loops 0", cow.loops, 0) -check("R1 tom Wait-pulje", cow.waitPoolGroup, "") -check("R1 input-store urørt", #empty.runs, 0) -check("R1 nextId tæller op", s1.nextId, 2) -local s2, pig = ADRuns.createRun(s1, "Pig Feed") -check("R1 andet run får id 2", pig.id, 2) -check("R1 to runs", #s2.runs, 2) - --- R2: opdatér felter; ukendt mode ignoreres; loops klippes -local s3 = ADRuns.updateRun(s2, 1, {name = "Cow 1", targetWayPointId = 94, ingredientMode = "manual", ingredients = {"STRAW", "FORAGE"}, waitPoolGroup = "Cow Wait", loops = 150}) -local run1 = ADRuns.getRun(s3, 1) -check("R2 navn", run1.name, "Cow 1") -check("R2 mål", run1.targetWayPointId, 94) -check("R2 manual", run1.ingredientMode, "manual") -check("R2 varer", table.concat(run1.ingredients, ","), "STRAW,FORAGE") -check("R2 pulje", run1.waitPoolGroup, "Cow Wait") -check("R2 loops klippet til 99", run1.loops, 99) -check("R2 gammel store urørt", ADRuns.getRun(s2, 1).name, "Cow Feed") -local s3b = ADRuns.updateRun(s3, 1, {ingredientMode = "bogus", loops = -5}) -check("R2 ukendt mode ignoreres", ADRuns.getRun(s3b, 1).ingredientMode, "manual") -check("R2 loops under 0 -> 0", ADRuns.getRun(s3b, 1).loops, 0) -check("R2 ukendt run -> uændret", #ADRuns.updateRun(s3, 99, {name = "x"}).runs, 2) -check("R2 targetWayPointId=false rydder målet", ADRuns.getRun(ADRuns.updateRun(s3, 1, {targetWayPointId = false}), 1).targetWayPointId, nil) -check("R2 nil rører ikke målet", ADRuns.getRun(ADRuns.updateRun(s3, 1, {name = "y"}), 1).targetWayPointId, 94) - --- R3: tildel traktor; samme traktor flyttes mellem runs -local s4, prev = ADRuns.assignVehicle(s3, 1, "vehicleA") -check("R3 tildelt", ADRuns.getRun(s4, 1).vehicleIds[1], "vehicleA") -check("R3 ingen tidligere", prev, nil) -check("R3 opslag", ADRuns.findRunByVehicle(s4, "vehicleA").id, 1) -local s5, prev2 = ADRuns.assignVehicle(s4, 2, "vehicleA") -check("R3 flyttet fra run 1", prev2, 1) -check("R3 væk fra run 1", #ADRuns.getRun(s5, 1).vehicleIds, 0) -check("R3 i run 2", ADRuns.getRun(s5, 2).vehicleIds[1], "vehicleA") -local s5b, prev3 = ADRuns.assignVehicle(s5, 2, "vehicleA") -check("R3 gen-tildeling samme run -> ingen dublet", #ADRuns.getRun(s5b, 2).vehicleIds, 1) -check("R3 gen-tildeling -> ingen 'flyttet fra'", prev3, nil) -local s6 = ADRuns.unassignVehicle(s5, "vehicleA") -check("R3 fjernet", ADRuns.findRunByVehicle(s6, "vehicleA"), nil) -check("R3 ukendt run ved tildeling -> uændret", ADRuns.findRunByVehicle(ADRuns.assignVehicle(s6, 42, "vehicleB"), "vehicleB"), nil) - --- R4: slet run -local s7 = ADRuns.deleteRun(s5, 1) -check("R4 ét run tilbage", #s7.runs, 1) -check("R4 det rigtige", s7.runs[1].id, 2) -check("R4 nextId bevares", s7.nextId, 3) - --- R5: records frem og tilbage -local records = ADRuns.toRecords(s5) -check("R5 target som tal", records[1].target, 94) -check("R5 varer som streng", records[1].ingredients, "STRAW FORAGE") -check("R5 køretøjer", records[2].vehicleIds[1], "vehicleA") -local back = ADRuns.fromRecords(records) -check("R5 to runs tilbage", #back.runs, 2) -check("R5 nextId = højeste + 1", back.nextId, 3) -check("R5 varer parset", ADRuns.getRun(back, 1).ingredients[2], "FORAGE") -check("R5 pulje", ADRuns.getRun(back, 1).waitPoolGroup, "Cow Wait") -check("R5 target 0 -> nil", ADRuns.getRun(back, 2).targetWayPointId, nil) -check("R5 køretøj", ADRuns.findRunByVehicle(back, "vehicleA").id, 2) - --- R6: defekte poster springes over; dubletter af id ignoreres -local dirty = ADRuns.fromRecords({ - {id = "x", name = "Broken"}, - {id = 5, name = "", }, - {id = 7, name = "Ok", target = "12", ingredientMode = "weird", ingredients = "", waitPool = nil, loops = "3", vehicleIds = {}}, - {id = 7, name = "Dublet"}, -}) -check("R6 kun den gyldige", #dirty.runs, 1) -check("R6 target parses fra streng", dirty.runs[1].targetWayPointId, 12) -check("R6 ukendt mode -> auto", dirty.runs[1].ingredientMode, "auto") -check("R6 tom varestreng -> tom liste", #dirty.runs[1].ingredients, 0) -check("R6 manglende pulje -> tom streng", dirty.runs[1].waitPoolGroup, "") -check("R6 loops fra streng", dirty.runs[1].loops, 3) -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) - --- R8: flere mål pr. run — liste + første som "primært" mål (bagudkompatibelt); toggle; records -local r8, r8run = ADRuns.createRun(ADRuns.newStore(), "Kvæg") -check("R8 ingen mål", #r8run.targetWayPointIds, 0) -r8 = ADRuns.updateRun(r8, r8run.id, {targetWayPointIds = {94, 99}}) -check("R8 to mål", table.concat(ADRuns.getRun(r8, r8run.id).targetWayPointIds, ","), "94,99") -check("R8 primært mål = første", ADRuns.getRun(r8, r8run.id).targetWayPointId, 94) -r8 = ADRuns.toggleTarget(r8, r8run.id, 94) -check("R8 toggle fjerner", table.concat(ADRuns.getRun(r8, r8run.id).targetWayPointIds, ","), "99") -check("R8 primært følger med", ADRuns.getRun(r8, r8run.id).targetWayPointId, 99) -r8 = ADRuns.toggleTarget(r8, r8run.id, 120) -check("R8 toggle tilføjer", table.concat(ADRuns.getRun(r8, r8run.id).targetWayPointIds, ","), "99,120") -check("R8 gammel enkelt-sætning = liste med ét", table.concat(ADRuns.getRun(ADRuns.updateRun(r8, r8run.id, {targetWayPointId = 7}), r8run.id).targetWayPointIds, ","), "7") -check("R8 false rydder listen", #ADRuns.getRun(ADRuns.updateRun(r8, r8run.id, {targetWayPointId = false}), r8run.id).targetWayPointIds, 0) -local r8rec = ADRuns.toRecords(r8) -check("R8 records: targets-streng", r8rec[1].targets, "99 120") -check("R8 records: target = første (gammelt felt)", r8rec[1].target, 99) -check("R8 tilbage fra records", table.concat(ADRuns.fromRecords(r8rec).runs[1].targetWayPointIds, ","), "99,120") -check("R8 gammel fil med kun target", table.concat(ADRuns.fromRecords({{id = 1, name = "Gl", target = 55}}).runs[1].targetWayPointIds, ","), "55") - --- ==== XML-laget mod en flad XMLFile-mock (nøgle -> værdi) ==== -dofile("FS25_ADSmartPickup/adRunsStorage.lua") - -local function makeXmlMock(values) - local xml = {values = values or {}, saved = false, deleted = false} - xml.setInt = function(self, key, value) self.values[key] = value end - xml.setString = function(self, key, value) self.values[key] = value end - xml.getInt = function(self, key) return tonumber(self.values[key]) end - xml.getString = function(self, key) local v = self.values[key]; return v ~= nil and tostring(v) or nil end - -- iterate: kalder callback for "prefix(0)", "prefix(1)", ... så længe en nøgle med den stamme findes - xml.iterate = function(self, prefix, callback) - local index = 0 - while true do - local key = string.format("%s(%d)", prefix, index) - local found = false - for k in pairs(self.values) do - if k:sub(1, #key) == key then found = true break end - end - if not found then return end - callback(index + 1, key) - index = index + 1 - end - end - xml.save = function(self) self.saved = true end - xml.delete = function(self) self.deleted = true end - return xml -end - --- X1: records skrives som attributter + vehicle-underelementer -local out = makeXmlMock() -ADRunsStorage.writeRecords(out, ADRuns.toRecords(s5)) -check("X1 version", out.values["ADSmartPickupRuns#version"], 3) -check("X1 run 0 navn", out.values["ADSmartPickupRuns.run(0)#name"], "Cow 1") -check("X1 run 0 target", out.values["ADSmartPickupRuns.run(0)#target"], 94) -check("X1 run 0 varer", out.values["ADSmartPickupRuns.run(0)#ingredients"], "STRAW FORAGE") -check("X1 run 0 pulje", out.values["ADSmartPickupRuns.run(0)#waitPool"], "Cow Wait") -check("X1 run 1 køretøj", out.values["ADSmartPickupRuns.run(1).vehicle(0)#id"], "vehicleA") - --- X2: samme data læses tilbage -local records2 = ADRunsStorage.readRecords(makeXmlMock(out.values)) -check("X2 to poster", #records2, 2) -check("X2 id", records2[1].id, 1) -check("X2 køretøj", records2[2].vehicleIds[1], "vehicleA") -check("X2 loops", records2[1].loops, 99) -check("X2 round-trip", ADRuns.findRunByVehicle(ADRuns.fromRecords(records2), "vehicleA").id, 2) - --- X3: save/load via XMLFile-globalen; ingen savegame-mappe -> tom store, intet skrives -Logging = Logging or {warning = function() end, info = function() end} -local created, loaded = nil, nil -XMLFile = { - create = function(_, path, root) created = makeXmlMock(); created.path = path; created.root = root; return created end, - load = function(_, path) loaded = makeXmlMock(created and created.values or {}); loaded.path = path; return loaded end, -} -fileExists = function(path) return created ~= nil and created.path == path end -g_currentMission = {missionInfo = {savegameDirectory = "/tmp/sg1"}} -check("X3 gemt", ADRunsStorage.save(s5), true) -check("X3 sti", created.path, "/tmp/sg1/ADSmartPickup_runs.xml") -check("X3 rodelement", created.root, "ADSmartPickupRuns") -check("X3 save+delete kaldt", created.saved and created.deleted, true) -check("X3 indlæst", #ADRunsStorage.load().runs, 2) -check("X3 læse-handle lukket", loaded.deleted, true) -g_currentMission = {missionInfo = {}} -check("X3 uden savegame -> false", ADRunsStorage.save(s5), false) -check("X3 uden savegame -> tom store", #ADRunsStorage.load().runs, 0) -g_currentMission = {missionInfo = {savegameDirectory = "/tmp/sg-ny"}} -check("X3 fil mangler -> tom store", #ADRunsStorage.load().runs, 0) - --- R-v2: udkørsels-run — kind, kilder, varer med politik, tærskler, runde; XML v2 + v1-migration -local o1, out = ADRuns.createRun(ADRuns.newStore(), "Sugar out", "outbound") -check("Rv2 kind", out.kind, "outbound") -local _, plain = ADRuns.createRun(ADRuns.newStore(), "x") -check("Rv2 supply som standard", plain.kind, "supply") -check("Rv2 sellAt 90", out.sellAtPercent, 90) -check("Rv2 storeAbove 80", out.storeAbovePercent, 80) -check("Rv2 runde fra", out.collectRound, false) -local o2 = ADRuns.toggleSource(o1, out.id, 12694) -o2 = ADRuns.toggleSource(o2, out.id, 12386) -check("Rv2 to kilder", table.concat(ADRuns.getRun(o2, out.id).sourceWayPointIds, ","), "12694,12386") -check("Rv2 toggle fjerner", #ADRuns.getRun(ADRuns.toggleSource(o2, out.id, 12694), out.id).sourceWayPointIds, 1) -local o3 = ADRuns.setProductPolicy(o2, out.id, "SUGAR", "sell") -o3 = ADRuns.setProductPolicy(o3, out.id, "FORAGE", "store") -check("Rv2 politik SUGAR", ADRuns.getProductPolicy(ADRuns.getRun(o3, out.id), "SUGAR"), "sell") -check("Rv2 politik ukendt = off", ADRuns.getProductPolicy(ADRuns.getRun(o3, out.id), "STRAW"), "off") -check("Rv2 ugyldig politik ignoreres", ADRuns.getProductPolicy(ADRuns.getRun(ADRuns.setProductPolicy(o3, out.id, "SUGAR", "bogus"), out.id), "SUGAR"), "sell") -check("Rv2 off fjerner", ADRuns.getProductPolicy(ADRuns.getRun(ADRuns.setProductPolicy(o3, out.id, "SUGAR", "off"), out.id), "SUGAR"), "off") -check("Rv2 gammel store urørt", #ADRuns.getRun(o2, out.id).products, 0) -local o4 = ADRuns.updateRun(o3, out.id, {sellAtPercent = 120, storeAbovePercent = 10, collectRound = true}) -check("Rv2 sellAt klippet 100", ADRuns.getRun(o4, out.id).sellAtPercent, 100) -check("Rv2 storeAbove klippet 50", ADRuns.getRun(o4, out.id).storeAbovePercent, 50) -check("Rv2 runde til", ADRuns.getRun(o4, out.id).collectRound, true) -check("Rv2 serialiser", ADRuns.serializeProducts(ADRuns.getRun(o3, out.id).products), "SUGAR:sell FORAGE:store") -local parsed = ADRuns.parseProducts("SUGAR:sell FORAGE:store BAD BAD:bogus") -check("Rv2 parse antal", #parsed, 2) -check("Rv2 parse politik", parsed[2].policy, "store") -local rt = ADRuns.fromRecords(ADRuns.toRecords(o4)) -local rtRun = ADRuns.getRun(rt, out.id) -check("Rv2 roundtrip kind", rtRun.kind, "outbound") -check("Rv2 roundtrip kilder", table.concat(rtRun.sourceWayPointIds, ","), "12694,12386") -check("Rv2 roundtrip products", ADRuns.serializeProducts(rtRun.products), "SUGAR:sell FORAGE:store") -check("Rv2 roundtrip sellAt", rtRun.sellAtPercent, 100) -check("Rv2 roundtrip runde", rtRun.collectRound, true) -local v1 = ADRuns.fromRecords({{id = 7, name = "Old", target = 94, targets = "94", ingredientMode = "auto", ingredients = "", waitPool = "", loops = 0, returnBelow = 50, vehicleIds = {}}}) -check("Rv2 v1 -> supply", ADRuns.getRun(v1, 7).kind, "supply") -check("Rv2 v1 tomme kilder", #ADRuns.getRun(v1, 7).sourceWayPointIds, 0) -check("Rv2 v1 sellAt standard", ADRuns.getRun(v1, 7).sellAtPercent, 90) --- XML-laget skriver/læser v2-felterne -do - local written = {} - local xml = { - setInt = function(_, k, v) written[k] = v end, setString = function(_, k, v) written[k] = v end, - getInt = function(_, k) return written[k] end, getString = function(_, k) return written[k] end, - iterate = function(_, prefix, fn) - local i = 0 - while written[string.format("%s(%d)#id", prefix, i)] ~= nil do fn(i, string.format("%s(%d)", prefix, i)); i = i + 1 end - end, - } - ADRunsStorage.writeRecords(xml, ADRuns.toRecords(o4)) - check("Rv2 xml version 2", written["ADSmartPickupRuns#version"], 3) - check("Rv2 xml kind", written["ADSmartPickupRuns.run(0)#kind"], "outbound") - check("Rv2 xml sources", written["ADSmartPickupRuns.run(0)#sources"], "12694 12386") - check("Rv2 xml products", written["ADSmartPickupRuns.run(0)#products"], "SUGAR:sell FORAGE:store") - check("Rv2 xml collectRound", written["ADSmartPickupRuns.run(0)#collectRound"], 1) - local back = ADRuns.fromRecords(ADRunsStorage.readRecords(xml)) - check("Rv2 xml læs sellAt", ADRuns.getRun(back, out.id).sellAtPercent, 100) - check("Rv2 xml læs runde", ADRuns.getRun(back, out.id).collectRound, true) -end - --- R-v3: bygninger som kilder/mål (id-lister); XML v3; v2-fil uden bygninger giver tomme lister -local b1, flow = ADRuns.createRun(ADRuns.newStore(), "Milk", "outbound") -check("Rv3 tomme kilde-bygninger", #flow.sourceBuildingIds, 0) -check("Rv3 tomme mål-bygninger", #flow.targetBuildingIds, 0) -local b2 = ADRuns.toggleSourceBuilding(b1, flow.id, "barn1") -b2 = ADRuns.toggleSourceBuilding(b2, flow.id, "barn2") -check("Rv3 to kilde-bygninger", table.concat(ADRuns.getRun(b2, flow.id).sourceBuildingIds, ","), "barn1,barn2") -check("Rv3 toggle fjerner", #ADRuns.getRun(ADRuns.toggleSourceBuilding(b2, flow.id, "barn1"), flow.id).sourceBuildingIds, 1) -local b3 = ADRuns.toggleTargetBuilding(b2, flow.id, "mill") -check("Rv3 mål-bygning", ADRuns.getRun(b3, flow.id).targetBuildingIds[1], "mill") -check("Rv3 gammel store urørt", #ADRuns.getRun(b2, flow.id).targetBuildingIds, 0) -local b4 = ADRuns.updateRun(b3, flow.id, {sourceBuildingIds = {"shed"}, targetBuildingIds = {}}) -check("Rv3 updateRun sætter lister", ADRuns.getRun(b4, flow.id).sourceBuildingIds[1], "shed") -check("Rv3 updateRun tømmer", #ADRuns.getRun(b4, flow.id).targetBuildingIds, 0) -local rt3 = ADRuns.fromRecords(ADRuns.toRecords(b3)) -check("Rv3 roundtrip kilder", table.concat(ADRuns.getRun(rt3, flow.id).sourceBuildingIds, ","), "barn1,barn2") -check("Rv3 roundtrip mål", ADRuns.getRun(rt3, flow.id).targetBuildingIds[1], "mill") -check("Rv3 XML_VERSION", ADRuns.XML_VERSION, 3) -local v2 = ADRuns.fromRecords({{id = 3, name = "V2", kind = "outbound", sources = "12694", products = "SUGAR:sell", vehicleIds = {}}}) -check("Rv3 v2 rå markør bevares", ADRuns.getRun(v2, 3).sourceWayPointIds[1], 12694) -check("Rv3 v2 ingen bygninger", #ADRuns.getRun(v2, 3).sourceBuildingIds, 0) -do - local written = {} - local xml = { - setInt = function(_, k, v) written[k] = v end, setString = function(_, k, v) written[k] = v end, - getInt = function(_, k) return written[k] end, getString = function(_, k) return written[k] end, - iterate = function(_, prefix, fn) - local i = 0 - while written[string.format("%s(%d)#id", prefix, i)] ~= nil do fn(i, string.format("%s(%d)", prefix, i)); i = i + 1 end - end, - } - ADRunsStorage.writeRecords(xml, ADRuns.toRecords(b3)) - check("Rv3 xml version 3", written["ADSmartPickupRuns#version"], 3) - check("Rv3 xml sourceBuildings", written["ADSmartPickupRuns.run(0)#sourceBuildings"], "barn1 barn2") - check("Rv3 xml targetBuildings", written["ADSmartPickupRuns.run(0)#targetBuildings"], "mill") - local back = ADRuns.fromRecords(ADRunsStorage.readRecords(xml)) - check("Rv3 xml læs kilder", table.concat(ADRuns.getRun(back, flow.id).sourceBuildingIds, ","), "barn1,barn2") -end - --- RN: køretøjsnavne efter forløbet — "Navn 1", "Navn 2" … i medlemsrækkefølge; ét køretøj = bare navnet -do - local store, runA = ADRuns.createRun(ADRuns.newStore(), "Milk Collect", "outbound") - store = ADRuns.assignVehicle(store, runA.id, "v1") - store = ADRuns.assignVehicle(store, runA.id, "v2") - local runB - store, runB = ADRuns.createRun(store, "Milk Salg", "outbound") - store = ADRuns.assignVehicle(store, runB.id, "v3") - local names = ADRuns.vehicleNames(store) - check("RN to i samme forløb: 1", names.v1, "Milk Collect 1") - check("RN to i samme forløb: 2", names.v2, "Milk Collect 2") - check("RN alene i forløb = navnet", names.v3, "Milk Salg") - check("RN ukendt køretøj", names.v9, nil) -end - --- RS: tvunget lagermål — storeBuildingIds (toggle, kopi, records roundtrip) -do - local store, run = ADRuns.createRun(ADRuns.newStore(), "Slurry", "outbound") - check("RS tom som standard", #run.storeBuildingIds, 0) - store = ADRuns.toggleStoreBuilding(store, run.id, "BGA500kw") - store = ADRuns.toggleStoreBuilding(store, run.id, "tank2") - check("RS to valgt", table.concat(ADRuns.getRun(store, run.id).storeBuildingIds, ","), "BGA500kw,tank2") - store = ADRuns.toggleStoreBuilding(store, run.id, "tank2") - check("RS fravalgt igen", table.concat(ADRuns.getRun(store, run.id).storeBuildingIds, ","), "BGA500kw") - local records = ADRuns.toRecords(store) - check("RS record", records[1].storeBuildings, "BGA500kw") - local back = ADRuns.fromRecords(records) - check("RS roundtrip", table.concat(ADRuns.getRun(back, run.id).storeBuildingIds, ","), "BGA500kw") -end - --- flow-vogne: stoppet + husket vogn pr. traktor følger med records frem og tilbage -local wagonStore = ADRuns.newStore() -local ws, wrun = ADRuns.createRun(wagonStore, "Fodring", "supply") -wrun.stopped, wrun.wagons = true, {vehA = "wagonA"} -local wrecords = ADRuns.toRecords(ws) -check("vogne: record stoppet", wrecords[1].stopped, 1) -check("vogne: record vogne", wrecords[1].wagons, "vehA=wagonA") -local wback = ADRuns.fromRecords(wrecords).runs[1] -check("vogne: stoppet tilbage", wback.stopped, true) -check("vogne: vogn tilbage", wback.wagons.vehA, "wagonA") -local wupdated = ADRuns.getRun(ADRuns.updateRun(ws, wrun.id, {name = "Fodring 2"}), wrun.id) -check("vogne: overlever updateRun", wupdated.wagons.vehA, "wagonA") -check("vogne: stoppet overlever updateRun", wupdated.stopped, true) --- R4: flyttes en traktor til et andet flow (eller fjernes), glemmes dens vogn i det gamle flow -local ps = ADRuns.newStore() -local ps1, prunA = ADRuns.createRun(ps, "A", "supply") -local ps2, prunB = ADRuns.createRun(ps1, "B", "supply") -ps2 = ADRuns.assignVehicle(ps2, prunA.id, "trac") -ADRuns.getRun(ps2, prunA.id).wagons = {trac = "wag"} -local ps3 = ADRuns.assignVehicle(ps2, prunB.id, "trac") -check("R4 gammel flow glemmer vognen", ADRuns.getRun(ps3, prunA.id).wagons.trac, nil) -local ps4 = ADRuns.unassignVehicle(ps2, "trac") -check("R4 fjernet traktor: vogn glemt", ADRuns.getRun(ps4, prunA.id).wagons.trac, nil) --- oprydning: traktorer der ikke findes længere (solgt) fjernes fra deres flow — men ikke hvis ALLE mangler -do - local st, r1 = ADRuns.createRun(ADRuns.newStore(), "A", "supply") - st = ADRuns.assignVehicle(st, r1.id, "keep") - st = ADRuns.assignVehicle(st, r1.id, "gone") - local exists = function(id) return id == "keep" end - local pruned, removed = ADRuns.pruneMissingVehicles(st, exists) - check("prune: fjernet", #removed, 1) - check("prune: den rigtige", removed[1].vehicleId, "gone") - check("prune: flowets navn med", removed[1].runName, "A") - check("prune: tilbage", #ADRuns.getRun(pruned, r1.id).vehicleIds, 1) - check("prune: original urørt", #ADRuns.getRun(st, r1.id).vehicleIds, 2) - local _, none = ADRuns.pruneMissingVehicles(st, function() return false end) - check("prune: alle mangler -> intet fjernes", #none, 0) -end --- beskeder på skærmen efter oprydning: én pr. flow; rød når flowet står uden traktorer -do - local st, r1 = ADRuns.createRun(ADRuns.newStore(), "TMR", "supply") - local st2, r2 = ADRuns.createRun(st, "Solo", "supply") - st2 = ADRuns.assignVehicle(st2, r1.id, "keep") - st2 = ADRuns.assignVehicle(st2, r1.id, "gone1") - st2 = ADRuns.assignVehicle(st2, r2.id, "gone2") - local pruned, removed = ADRuns.pruneMissingVehicles(st2, function(id) return id == "keep" end) - local notes = ADRuns.pruneNotices(pruned, removed) - check("besked: én pr. flow", #notes, 2) - check("besked: TMR har traktorer tilbage", notes[1].runName .. "/" .. notes[1].left .. "/" .. tostring(notes[1].critical), "TMR/1/false") - check("besked: Solo står tom -> rød", notes[2].runName .. "/" .. notes[2].left .. "/" .. tostring(notes[2].critical), "Solo/0/true") -end - --- Start alle: et flow på pause (Stop alt) skal have pausen ophævet, også når dets traktorer allerede kører AD --- (in-game 29/9: 5 flows blev talt som 'kørte allerede', forblev stoppede, og traktorerne kørte vognen hjem igen) -check("start alle: kører ikke -> i kø", ADRuns.startAllAction(false, true), "queue") -check("start alle: kører ikke, ikke stoppet -> i kø", ADRuns.startAllAction(false, false), "queue") -check("start alle: kører men stoppet -> ophæv pause", ADRuns.startAllAction(true, true), "resume") -check("start alle: kører og ikke stoppet -> intet", ADRuns.startAllAction(true, false), "skip") - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adRunsController.lua b/tests/test_adRunsController.lua deleted file mode 100644 index aa4deef..0000000 --- a/tests/test_adRunsController.lua +++ /dev/null @@ -1,907 +0,0 @@ --- Kør: luajit tests/test_adRunsController.lua (fra repo-roden) --- Controlleren oversætter et run til AutoDrive-tilstand pr. traktor. Mock'er AD + de --- få ADSmartPickup-opslag den bruger. - -local STRAW, TMR, WATER, MILK = 31, 116, 8, 30 -local FILL_TYPE_NAMES = {[STRAW] = "STRAW", [TMR] = "FORAGE", [WATER] = "WATER", [MILK] = "MILK"} -local FILL_TYPE_INDEX = {} -for index, name in pairs(FILL_TYPE_NAMES) do FILL_TYPE_INDEX[name] = index end -g_fillTypeManager = { - getFillTypeNameByIndex = function(_, index) return FILL_TYPE_NAMES[index] end, - getFillTypeIndexByName = function(_, name) return FILL_TYPE_INDEX[name] end, - getFillTypes = function() - return { - {index = STRAW, name = "STRAW", title = "Halm"}, - {index = TMR, name = "FORAGE", title = "TMR"}, - {index = WATER, name = "WATER", title = "Vand"}, - {index = MILK, name = "MILK", title = "Mælk"}, - } - end, -} -local logLines = {} -Logging = { - info = function(fmt, ...) table.insert(logLines, string.format(fmt, ...)) end, - warning = function(fmt, ...) table.insert(logLines, "WARN " .. string.format(fmt, ...)) end, -} -Utils = {prependedFunction = function(original, extra) return function(...) extra(...); return original(...) end end} -ItemSystem = {save = function() end} -g_currentMission = {getFarmId = function() return 1 end, missionInfo = {}} - --- ---- AutoDrive-mock ---- -local RT_NONE, RT_ONLYPICKUP = 1, 2 -local MODE_PICKUPANDDELIVER = 2 -local vehicles, markers, wayPoints = {}, {}, {} -FS25_AutoDrive = { - AutoDrive = { - RT_NONE = RT_NONE, RT_ONLYPICKUP = RT_ONLYPICKUP, MODE_PICKUPANDDELIVER = MODE_PICKUPANDDELIVER, - settings = { - useFolders = {values = {false, true}, current = 1, isVehicleSpecific = false}, - rotateTargets = {values = {1, 2, 3, 4}, current = 1, isVehicleSpecific = true}, - ALUnload = {values = {0, 1, 2, 3, 4}, current = 1, isVehicleSpecific = true}, - }, - getAllVehicles = function() return vehicles end, - getAllUnits = function(vehicle) return vehicle.trailers end, - getAllFillLevels = function(trailers) return trailers.level or 0, trailers.capacity or 0, false, 0 end, - getSupportedFillTypesOfAllUnitsAlphabetically = function(vehicle) return vehicle.carriable or {} end, - }, - ADGraphManager = { - getMapMarkers = function() return markers end, - getMapMarkerByWayPointId = function(_, id) for _, m in ipairs(markers) do if m.id == id then return m end end end, - getWayPointById = function(_, id) return wayPoints[id] end, - getDistanceFromNetwork = function(_, vehicle) return vehicle.networkDistance or 0 end, - }, -} - -local function addMarker(id, name, group, x, z) - wayPoints[id] = {x = x, z = z, incoming = {}} - table.insert(markers, {id = id, markerIndex = #markers + 1, name = name, group = group}) -end - --- ADSmartPickup-opslag: hvilke waypoints har hvilken slags station, og hvad målet tager -local unloadAt, loadAt = {}, {} -ADSmartPickup = { - getFarmId = function(vehicle) return vehicle.farmId end, - getUnloadStationAtWayPointId = function(_, id) return unloadAt[id] end, - getLoadStationAtWayPointId = function(_, id) return loadAt[id] end, - getStationLevelAndCapacity = function(station, fillType) - if station.accepts[fillType] == nil then return nil end - return (station.levels or {})[fillType] or 0, station.accepts[fillType] - end, - -- forenklet rangering: mest fri plads (relativt) først; fulde sidst - getTargetNeedsRanked = function(station, fillTypes) - local ranked = {} - for _, fillType in ipairs(fillTypes) do - local capacity = station.accepts[fillType] - if capacity ~= nil then - local level = (station.levels or {})[fillType] or 0 - table.insert(ranked, {fillType = fillType, level = level, capacity = capacity, freeSpace = capacity - level, score = (capacity - level) / capacity}) - end - end - table.sort(ranked, function(a, b) return a.score > b.score end) - return ranked - end, -} -ADSupplyPlanner = {isWaitMarker = function(marker) return string.find(string.lower(marker.group or ""), "%f[%a]wait%f[%A]") ~= nil end} - -local function makeVehicle(uniqueId, name, carriable, capacity, x, z) - local stateModule = {selectedFillTypes = {}, fillType = 0, active = false, loopCounter = 5, loopsDone = 3, dirtyCount = 0} - stateModule.setMode = function(self, mode) self.mode = mode end - stateModule.setFirstMarkerByWayPointId = function(self, id) self.firstMarkerId = id end - stateModule.setSecondMarkerByWayPointId = function(self, id) self.secondMarkerId = id end - stateModule.setFillType = function(self, fillType) self.fillType = fillType end - stateModule.raiseDirtyFlag = function(self) self.dirtyCount = self.dirtyCount + 1 end - stateModule.setLoopsDone = function(self, n) self.loopsDone = n end - stateModule.isActive = function(self) return self.active end - -- Bruges af C13: getTargetFillTypes skal følge AD's LIVE mål (mock'er AD's eget getSecondMarker). - stateModule.getSecondMarker = function(self) return self.secondMarkerId ~= nil and {id = self.secondMarkerId} or nil end - local vehicle = { - uniqueId = uniqueId, farmId = 1, rootNode = uniqueId, - carriable = carriable, trailers = {level = 0, capacity = capacity}, - getName = function() return name end, - getOwnerFarmId = function() return 1 end, - startAutoDrive = function(self) self.ad.stateModule.active = true; self.started = (self.started or 0) + 1 end, - getClosestWayPoint = function() return nil, 0 end, - stopAutoDrive = function(self) self.ad.stateModule.active = false; self.stopped = (self.stopped or 0) + 1 end, - ad = {stateModule = stateModule, settings = {rotateTargets = {values = {1, 2, 3, 4}, current = 1, new = 1, isVehicleSpecific = true}, ALUnload = {values = {0, 1, 2, 3, 4}, current = 1, new = 1, isVehicleSpecific = true}}}, - } - nodePositions = nodePositions or {} - nodePositions[uniqueId] = {x = x or 0, z = z or 0} - table.insert(vehicles, vehicle) - return vehicle -end -function getWorldTranslation(node) local p = nodePositions[node]; return p.x, 0, p.z end - -dofile("FS25_ADSmartPickup/adRuns.lua") -dofile("FS25_ADSmartPickup/adRunsStorage.lua") -dofile("FS25_ADSmartPickup/adWaitPool.lua") -dofile("FS25_ADSmartPickup/adRunsController.lua") -local adEnv = FS25_AutoDrive - -local failures = 0 -local function check(name, actual, expected) - if actual == expected then - print("OK " .. name) - else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - -local function buildWorld() - vehicles, markers, wayPoints, unloadAt, loadAt, logLines = {}, {}, {}, {}, {}, {} - ADRunsController.store = ADRuns.newStore() - addMarker(94, "Cow 1 Food", "Cow Food", 5000, 0); unloadAt[94] = {accepts = {[STRAW] = 300000, [TMR] = 300000, [WATER] = 50000}} - addMarker(44, "10 Silo Storage", "Siloer", 0, 0); loadAt[44] = {} - addMarker(75, "TMR Load", "Fabrication", 900, 0); loadAt[75] = {} - addMarker(100, "Cow Wait", "Cow Wait", 5100, 0) - addMarker(101, "Cow Wait", "Cow Wait", 5200, 0) - addMarker(200, "F91 Wait", "F91", 8000, 0) - addMarker(300, "Pig Wait", "Pig Wait", 9000, 0) -end - --- C1: køretøjs-id og farmens AD-køretøjer -buildWorld() -local fastracA = makeVehicle("vehicleA", "Fastrac A", {STRAW, TMR}, 1000000, 100, 0) -local fastracB = makeVehicle("vehicleB", "Fastrac B", {STRAW}, 700000, 4000, 0) -local noAd = {uniqueId = "vehicleC", getOwnerFarmId = function() return 1 end} -table.insert(vehicles, noAd) -local otherFarm = makeVehicle("vehicleD", "Nabo", {STRAW}, 1000, 0, 0); otherFarm.getOwnerFarmId = function() return 2 end -check("C1 uniqueId", ADRunsController.getVehicleId(fastracA), "vehicleA") -check("C1 fallback savegame-id", ADRunsController.getVehicleId({currentSavegameId = 7}), "7") -check("C1 intet id -> nil", ADRunsController.getVehicleId({}), nil) -check("C1 farmens AD-køretøjer", #ADRunsController.getFarmVehicles(adEnv), 2) -check("C1 opslag på id", ADRunsController.findVehicleById(adEnv, "vehicleB"), fastracB) -check("C1 ukendt id -> nil", ADRunsController.findVehicleById(adEnv, "nope"), nil) - --- C2: opret run, tildel; opslag run for køretøj; flyt mellem runs -local cow = ADRunsController.createRun("Cow Feed") -check("C2 run oprettet", ADRuns.getRun(ADRunsController.store, cow.id).name, "Cow Feed") -check("C2 ingen tidligere", ADRunsController.assignVehicle(cow.id, fastracA), nil) -check("C2 run for køretøj", ADRunsController.getRunForVehicle(fastracA).id, cow.id) -check("C2 ikke tildelt -> nil", ADRunsController.getRunForVehicle(fastracB), nil) -local pig = ADRunsController.createRun("Pig Feed") -check("C2 flyttet fra Cow", ADRunsController.assignVehicle(pig.id, fastracA), cow.id) -check("C2 nu i Pig", ADRunsController.getRunForVehicle(fastracA).id, pig.id) -ADRunsController.unassignVehicle(fastracA) -check("C2 fjernet", ADRunsController.getRunForVehicle(fastracA), nil) -ADRunsController.deleteRun(pig.id) -check("C2 slettet", ADRuns.getRun(ADRunsController.store, pig.id), nil) - --- C3: Auto-vareliste = vognens varer ∩ målets varer; manual = navne -> indeks (ukendte droppes) -ADRunsController.updateRun(cow.id, {targetWayPointId = 94}) -local cowRun = ADRuns.getRun(ADRunsController.store, cow.id) -check("C3 auto: halm+TMR (vand kan vognen ikke bære)", table.concat(ADRunsController.getRunIngredients(adEnv, cowRun, fastracA), ","), STRAW .. "," .. TMR) -check("C3 auto: kun halm for halm-vogn", table.concat(ADRunsController.getRunIngredients(adEnv, cowRun, fastracB), ","), tostring(STRAW)) -ADRunsController.updateRun(cow.id, {ingredientMode = "manual", ingredients = {"FORAGE", "UNKNOWN_THING"}}) -cowRun = ADRuns.getRun(ADRunsController.store, cow.id) -check("C3 manual: FORAGE -> indeks, ukendt droppes", table.concat(ADRunsController.getRunIngredients(adEnv, cowRun, fastracA), ","), tostring(TMR)) -ADRunsController.updateRun(cow.id, {ingredientMode = "auto"}) -local noTarget = ADRunsController.createRun("Uden mål") -check("C3 uden mål -> tom liste", #ADRunsController.getRunIngredients(adEnv, ADRuns.getRun(ADRunsController.store, noTarget.id), fastracA), 0) - --- C4: validering -cowRun = ADRuns.getRun(ADRunsController.store, cow.id) -check("C4 ok", ADRunsController.validateVehicle(adEnv, cowRun, fastracA).ok, true) -check("C4 kapacitet", ADRunsController.validateVehicle(adEnv, cowRun, fastracA).capacity, 1000000) -local noTrailer = makeVehicle("vehicleE", "Uden vogn", {}, 0) -check("C4 ingen vogn", ADRunsController.validateVehicle(adEnv, cowRun, noTrailer).reason, "noTrailer") -local milkTruck = makeVehicle("vehicleF", "Mælkebil", {MILK}, 20000) -check("C4 kan ikke bære", ADRunsController.validateVehicle(adEnv, cowRun, milkTruck).reason, "cannotCarry") -fastracB.networkDistance = 31 -check("C4 for langt fra vejnettet", ADRunsController.validateVehicle(adEnv, cowRun, fastracB).reason, "farFromNetwork") -fastracB.networkDistance = 0 -check("C4 intet mål", ADRunsController.validateVehicle(adEnv, ADRuns.getRun(ADRunsController.store, noTarget.id), fastracA).reason, "noTarget") -ADRunsController.updateRun(noTarget.id, {targetWayPointId = 4242}) -check("C4 mål-markør findes ikke", ADRunsController.validateVehicle(adEnv, ADRuns.getRun(ADRunsController.store, noTarget.id), fastracA).reason, "noTarget") - --- C5: Start skriver AD-tilstand og starter; ✖-traktorer springes over -ADRunsController.updateRun(cow.id, {loops = 4, waitPoolGroup = "Cow Wait"}) -ADRunsController.assignVehicle(cow.id, fastracA) -ADRunsController.assignVehicle(cow.id, noTrailer) -local results = ADRunsController.startRun(adEnv, cow.id) -check("C5 to resultater", #results, 2) -check("C5 A startet", fastracA.started, 1) -check("C5 A ok", results[1].ok, true) -check("C5 uden vogn sprunget over", noTrailer.started, nil) -check("C5 uden vogn grund", results[2].reason, "noTrailer") -local state = fastracA.ad.stateModule -check("C5 mode P&D", state.mode, MODE_PICKUPANDDELIVER) -check("C5 mål = run'ets mål", state.secondMarkerId, 94) -check("C5 start-pickup = nærmeste læssestation", state.firstMarkerId, 44) -check("C5 varer = auto-listen", table.concat(state.selectedFillTypes, ","), STRAW .. "," .. TMR) -check("C5 fillType = første", state.fillType, STRAW) -check("C5 loops", state.loopCounter, 4) -check("C5 loopsDone nulstillet", state.loopsDone, 0) -check("C5 rotateTargets = kun pålæsning (current)", fastracA.ad.settings.rotateTargets.current, 2) -check("C5 rotateTargets = kun pålæsning (new)", fastracA.ad.settings.rotateTargets.new, 2) -check("C5 useFolders globalt til", adEnv.AutoDrive.settings.useFolders.current, 2) -check("C5 dirty flag", state.dirtyCount > 0, true) -check("C5 status", ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, cow.id)).running, 1) -check("C5 total", ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, cow.id)).total, 2) -check("C5 ukendt run -> tom", #ADRunsController.startRun(adEnv, 999), 0) - --- C6: Stop -check("C6 stoppede", ADRunsController.stopRun(adEnv, cow.id), 1) -check("C6 A stoppet", fastracA.stopped, 1) -check("C6 status 0", ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, cow.id)).running, 0) -check("C6 medlemskab bevaret", ADRunsController.getRunForVehicle(fastracA).id, cow.id) - --- C7: Start på traktor der allerede kører AD (fx startet manuelt) -> stoppes og startes på run'et -fastracA.ad.stateModule.active = true -fastracA.stopped = 0 -ADRunsController.startRun(adEnv, cow.id) -check("C7 kørende traktor stoppes først", fastracA.stopped, 1) -check("C7 og startes på run'et", fastracA.started, 2) -check("C7 run markeret som kørende", ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, cow.id)).isRunning, true) -ADRunsController.stopRun(adEnv, cow.id) -check("C7 stop rydder markeringen", ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, cow.id)).isRunning, false) --- en traktor der kører AD uden at run'et er startet af os, gør IKKE run'et "kørende" -fastracA.ad.stateModule.active = true -check("C7 manuel AD tæller ikke som kørende run", ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, cow.id)).isRunning, false) --- efter genindlæsning: runningRuns er tom, men flowet er ikke stoppet og traktoren kører -> kørende (Stop-knap) --- (in-game 29/9: Flows-listen viste [Start] ved flows der kørte) -ADRuns.getRun(ADRunsController.store, cow.id).stopped = false -ADRunsController.runningRuns[cow.id] = nil -check("C7b efter genindlæsning: kørende", ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, cow.id)).isRunning, true) -check("C7b efter genindlæsning: kan stoppes", ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, cow.id)).canStop, true) -ADRuns.getRun(ADRunsController.store, cow.id).stopped = true -check("C7 men aktive tæller stadig", ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, cow.id)).running, 1) -fastracA.ad.stateModule.active = false --- sprungne traktorer logges med årsag -logLines = {} -ADRunsController.startRun(adEnv, cow.id) -local skippedLogged = false -for _, line in ipairs(logLines) do if line:find("sprunget over", 1, true) and line:find("noTrailer", 1, true) then skippedLogged = true end end -check("C7 sprunget over logges med årsag", skippedLogged, true) --- kapacitet under 100 l (kun trækkerens egne tanke) = ingen vogn -local tinyCapacity = makeVehicle("vehicleTiny", "T255", {STRAW, TMR}, 1, 100, 0) -check("C7 1 l kapacitet -> noTrailer", ADRunsController.validateVehicle(adEnv, ADRuns.getRun(ADRunsController.store, cow.id), tinyCapacity).reason, "noTrailer") --- UAL-køretøj: AutoDrive melder kapacitet i PALLER (læssede + 1), så en tom pallebil har "1" — det er ikke "ingen vogn" -adEnv.AutoDrive.hasAL = function(_, unit) return unit.ual == true end -local palletTruck = makeVehicle("vehicleDragon", "Dragon", {}, 1, 100, 0) -palletTruck.trailers[1] = {ual = true} -local truckValidation = ADRunsController.validateVehicle(adEnv, ADRuns.getRun(ADRunsController.store, cow.id), palletTruck) -check("C7 pallebil: ikke noTrailer", truckValidation.reason ~= "noTrailer", true) -check("C7 pallebil: kapacitet i paller markeret", truckValidation.isAutoload, true) -check("C7 pallebil: liter-kapacitet ukendt", truckValidation.capacity, nil) --- in-game 27/9: en tipvogn med UniversalAutoload slået til tæller hos AutoDrive kun som palle-/ballevogn, --- så en forsynings-flow med løsvarer afviste den som "kan ikke bære" uden at sige hvorfor -check("C7 AutoLoad slået til på vogn i forsyning -> autoloadOn", truckValidation.reason, "autoloadOn") -adEnv.AutoDrive.hasAL = nil - --- C8: mål-markører (kun stalde og fabrikker — siloer tager alt og er ikke mål; stalde først, --- så fabrikker, så navn) og Wait-puljer til dropdowns -unloadAt[94].owningPlaceable = {getHusbandryFillLevel = function() return 0 end} -addMarker(96, "8 Silo Storage", "Siloer", 100, 0); unloadAt[96] = {accepts = {[STRAW] = 1}, owningPlaceable = {spec_silo = {}}} -addMarker(97, "TMR Unload", "Fabrication", 900, 0); unloadAt[97] = {accepts = {[STRAW] = 1}, owningPlaceable = {spec_productionPoint = {}}} -addMarker(98, "Bull Food", "Cow Food", 5100, 0); unloadAt[98] = {accepts = {[STRAW] = 1}, owningPlaceable = {getHusbandryFillLevel = function() return 0 end}} -local targets = ADRunsController.getTargetMarkers(adEnv, 1) -check("C8 kun stalde og fabrikker (ikke siloen)", #targets, 3) -check("C8 stalde først, alfabetisk", targets[1].name .. "," .. targets[2].name, "Bull Food,Cow 1 Food") -check("C8 fabrik sidst", targets[3].name, "TMR Unload") -local pools = ADRunsController.getWaitPools(adEnv) -check("C8 to puljer (F91 er ikke en Wait-mappe)", #pools, 2) -check("C8 sorteret", pools[1].group, "Cow Wait") -check("C8 antal", pools[1].count, 2) - --- C9: gem/indlæs; ukendte køretøjs-id'er logges ved indlæsning men beholdes i run'et -local savedRecords -ADRunsStorage.save = function(store) savedRecords = ADRuns.toRecords(store); return true end -ADRunsStorage.load = function() return ADRuns.fromRecords(savedRecords) end -ADRunsController.assignVehicle(cow.id, {uniqueId = "vehicleGONE", ad = {}, getOwnerFarmId = function() return 1 end, getName = function() return "Væk" end}) -check("C9 gemt", ADRunsController.save(), true) -ADRunsController.store = ADRuns.newStore() -ADRunsController.load(adEnv) -check("C9 indlæst", ADRuns.getRun(ADRunsController.store, cow.id).name, "Cow Feed") -check("C9 ukendt køretøj logget", logLines[#logLines]:find("vehicleGONE", 1, true) ~= nil, true) -check("C9 run beholder kendte", ADRunsController.getRunForVehicle(fastracA).id, cow.id) -local hooked = false -ItemSystem.save = function() hooked = true end -ADRunsController.installSaveHook() -ItemSystem.save() -check("C9 save-hook kaldes før ItemSystem.save", hooked, true) - --- C11: vogn uden egen ad.settings-tabel (ingen rotateTargets pr. køretøj) -> den --- køretøjs-specifikke indstilling skrives IKKE til den globale settings-tabel -adEnv.AutoDrive.settings.rotateTargets.current = 1 -adEnv.AutoDrive.settings.useFolders.current = 1 -local settingsRun = ADRunsController.createRun("Settings Test") -ADRunsController.updateRun(settingsRun.id, {targetWayPointId = 94, loops = 1}) -local noSettings = makeVehicle("vehicleH", "Uden AD-settings", {STRAW}, 500000, 200, 0) -noSettings.ad.settings = {} -ADRunsController.assignVehicle(settingsRun.id, noSettings) -ADRunsController.startRun(adEnv, settingsRun.id) -check("C11 global rotateTargets urørt", adEnv.AutoDrive.settings.rotateTargets.current, 1) -check("C11 useFolders global stadig skrevet", adEnv.AutoDrive.settings.useFolders.current, 2) -check("C11 køretøjet startet", noSettings.started, 1) - --- C12: startAutoDrive() kaster for én traktor -> pcall'et applyAndStart fanger fejlen, --- resultatet markeres ok=false/reason="error", og de ØVRIGE traktorer i run'et startes stadig --- (fejlen må ikke undslippe startRun-loopet). -buildWorld() -local brokenRun = ADRunsController.createRun("Broken Start") -ADRunsController.updateRun(brokenRun.id, {targetWayPointId = 94, loops = 2}) -local broken = makeVehicle("vehicleBroken", "Fejl-traktor", {STRAW, TMR}, 500000, 100, 0) -broken.startAutoDrive = function() error("boom") end -local normal = makeVehicle("vehicleNormal", "Normal-traktor", {STRAW, TMR}, 500000, 200, 0) -ADRunsController.assignVehicle(brokenRun.id, broken) -ADRunsController.assignVehicle(brokenRun.id, normal) -local c12Results = ADRunsController.startRun(adEnv, brokenRun.id) -check("C12 to resultater", #c12Results, 2) -check("C12 fejl-traktor ok=false", c12Results[1].ok, false) -check("C12 fejl-traktor reason=error", c12Results[1].reason, "error") -check("C12 fejl-traktor ikke startet", broken.started, nil) -check("C12 normal-traktor ok=true", c12Results[2].ok, true) -check("C12 normal-traktor startet", normal.started, 1) - --- C13: Auto-listen følger traktorens LIVE AD-mål (stateModule:getSecondMarker()) mens den kører, --- IKKE run'ets gemte targetWayPointId — ellers afviger listen fra det choosePickup/findSupplyPickup --- faktisk bruger, hvis brugeren redigerer run'ets Mål mens traktoren kører. -addMarker(95, "Alt Cow Food", "Cow Food", 5300, 0) -unloadAt[95] = {accepts = {[STRAW] = 1000}} -local liveRun = ADRunsController.createRun("Live Target") -ADRunsController.updateRun(liveRun.id, {targetWayPointId = 94}) -local liveRunData = ADRuns.getRun(ADRunsController.store, liveRun.id) -local liveVehicle = makeVehicle("vehicleLive", "Live-traktor", {STRAW, TMR}, 500000, 300, 0) -check("C13 inaktiv: auto-liste fra run'ets mål (94)", table.concat(ADRunsController.getRunIngredients(adEnv, liveRunData, liveVehicle), ","), STRAW .. "," .. TMR) -liveVehicle.ad.stateModule.active = true -liveVehicle.ad.stateModule.secondMarkerId = 95 -check("C13 aktiv: auto-liste fra AD's live mål (95)", table.concat(ADRunsController.getRunIngredients(adEnv, liveRunData, liveVehicle), ","), tostring(STRAW)) -liveVehicle.ad.stateModule.active = false -liveVehicle.ad.stateModule.secondMarkerId = nil - --- C10: målets accepterede varer (til afkrydsning i GUI'et) + køretøjs-label -buildWorld() -local labelA = makeVehicle("vehicleA", "Fastrac A", {STRAW, TMR}, 1000000) -local labelB = makeVehicle("vehicleB", "Fastrac B", {STRAW}, 700000) -local accRun = ADRunsController.createRun("Cow Feed") -check("C10 uden mål -> tom", #ADRunsController.getTargetAcceptedFillTypes(adEnv, ADRuns.getRun(ADRunsController.store, accRun.id)), 0) -ADRunsController.updateRun(accRun.id, {targetWayPointId = 94}) -local accepted = ADRunsController.getTargetAcceptedFillTypes(adEnv, ADRuns.getRun(ADRunsController.store, accRun.id)) -check("C10 tre varer (halm, TMR, vand)", #accepted, 3) -check("C10 sorteret på titel", accepted[1].title .. "," .. accepted[2].title .. "," .. accepted[3].title, "Halm,TMR,Vand") -check("C10 navn med", accepted[1].name, "STRAW") -check("C10 label", ADRunsController.getVehicleLabel(adEnv, labelB), "Fastrac B (#2)") - --- C14: Start går gennem AD's mode:start(USER_PLAYER) (som HUD'en), ikke kun startAutoDrive -buildWorld() -local modeVehicle = makeVehicle("vehicleM", "Mode-traktor", {STRAW, TMR}, 1000000, 100, 0) -local modeStarts = {} -modeVehicle.ad.stateModule.getCurrentMode = function() return {start = function(_, user) table.insert(modeStarts, user); modeVehicle:startAutoDrive() end} end -modeVehicle.ad.stateModule.setActualFarmId = function(self, farmId) self.actualFarmId = farmId end -adEnv.AutoDrive.USER_PLAYER = 1 -local modeRun = ADRunsController.createRun("Mode") -ADRunsController.updateRun(modeRun.id, {targetWayPointId = 94}) -ADRunsController.assignVehicle(modeRun.id, modeVehicle) -ADRunsController.startRun(adEnv, modeRun.id) -check("C14 mode:start kaldt med USER_PLAYER", modeStarts[1], 1) -check("C14 traktor aktiv", modeVehicle.started, 1) -check("C14 farm-id sat", modeVehicle.ad.stateModule.actualFarmId, 1) -check("C14 loopsDone nulstillet", modeVehicle.ad.stateModule.loopsDone, 0) - --- C15: aktivitet til overblikket — stoppet / venter / med læs / tom på vej efter vare -ADUnloadWait = {getWaitFor = function(v) return v.waitInfo end, getRestLoad = function(_, v) return v.loadType, v.loadLiters or 0 end} -ADSmartPickup.getActivePickup = function(v) return v.pickupInfo end -buildWorld() -local actVehicle = makeVehicle("vehicleAct", "Akt", {STRAW, TMR}, 1000000, 100, 0) -check("C15 stoppet", ADRunsController.getVehicleActivity(adEnv, actVehicle).kind, "stopped") -actVehicle.ad.stateModule.active = true -actVehicle.ad.stateModule.secondMarkerId = 94 -actVehicle.loadType, actVehicle.loadLiters = STRAW, 8000 -local loaded = ADRunsController.getVehicleActivity(adEnv, actVehicle) -check("C15 med læs", loaded.kind .. "/" .. loaded.fillType .. "/" .. loaded.liters, "loaded/" .. STRAW .. "/8000") -actVehicle.loadLiters = 0 -actVehicle.pickupInfo = {markerName = "Silo 3", fillType = TMR} -local empty = ADRunsController.getVehicleActivity(adEnv, actVehicle) -check("C15 tom på vej efter TMR", empty.kind .. "/" .. tostring(empty.place) .. "/" .. tostring(empty.fillType), "empty/Silo 3/" .. TMR) -actVehicle.waitInfo = {markerName = "Cow Wait", fillType = STRAW, rest = 5000} -check("C15 venter", ADRunsController.getVehicleActivity(adEnv, actVehicle).kind, "waiting") -ADUnloadWait = nil - --- C16: flere mål pr. run — Auto-varer = union; chooseTarget = den stald der mangler mest; validering -buildWorld() -addMarker(99, "Cow 2 Food", "Cow Food", 5200, 0); unloadAt[99] = {accepts = {[STRAW] = 300000, [TMR] = 300000}, levels = {[STRAW] = 0, [TMR] = 0}} -unloadAt[94].levels = {[STRAW] = 250000, [TMR] = 250000, [WATER] = 50000} -local multiVehicle = makeVehicle("vehicleMulti", "Multi", {STRAW, TMR, WATER}, 1000000, 100, 0) -local multi = ADRunsController.createRun("Kvæg") -ADRunsController.updateRun(multi.id, {targetWayPointIds = {94, 99}}) -local multiRun = ADRuns.getRun(ADRunsController.store, multi.id) -check("C16 auto-varer = union (vand kun i stald 1)", table.concat(ADRunsController.getRunIngredients(adEnv, multiRun, multiVehicle), ","), WATER .. "," .. STRAW .. "," .. TMR) -check("C16 accepterede varer = union", #ADRunsController.getTargetAcceptedFillTypes(adEnv, multiRun), 3) -check("C16 vælger stald 2 (tom)", ADRunsController.chooseTarget(adEnv, multiRun, multiVehicle), 99) -unloadAt[99].levels = {[STRAW] = 290000, [TMR] = 290000} -check("C16 vælger stald 1 når stald 2 er fuld", ADRunsController.chooseTarget(adEnv, multiRun, multiVehicle), 94) -check("C16 validering ok med flere mål", ADRunsController.validateVehicle(adEnv, multiRun, multiVehicle).ok, true) --- C16b: en anden vogn er allerede på vej til stald 2 — så vælges stald 1 selvom stald 2 er mindre fyldt --- (drive-on-bunker: kun én kan tippe ad gangen; in-game 26/9 kørte 3 biogas-vogne til samme drop) -unloadAt[99].levels = {[STRAW] = 100000, [TMR] = 100000} -unloadAt[94].levels = {[STRAW] = 200000, [TMR] = 200000, [WATER] = 50000} -ADSmartPickup.countEnRoute = function(id, exclude) return id == 99 and 1 or 0 end -check("C16b ledigt mål foran mindre fyldt mål med vogn på vej", ADRunsController.chooseTarget(adEnv, multiRun, multiVehicle), 94) -ADSmartPickup.countEnRoute = function() return 1 end -check("C16b lige mange på vej -> mindst fyldte", ADRunsController.chooseTarget(adEnv, multiRun, multiVehicle), 99) -ADSmartPickup.countEnRoute = nil --- C16c: fyldte traktorer startet i samme run fordeles på målene (in-game 27/9: tre fyldte biogas-traktorer --- kørte alle til Drop 1, fordi læs der allerede lå på vognen aldrig blev registreret "på vej") -do - unloadAt[99].levels = {[STRAW] = 100000, [TMR] = 100000} - unloadAt[94].levels = {[STRAW] = 200000, [TMR] = 200000, [WATER] = 50000} - local onWay = {} - ADSmartPickup.countEnRoute = function(id, exclude) - local n = 0 - for v, target in pairs(onWay) do if v ~= exclude and target == id then n = n + 1 end end - return n - end - ADSmartPickup.registerCargo = function(_, v, targetId) onWay[v] = targetId; return true end - local loadedA = makeVehicle("vehicleLoadedA", "Fyldt A", {STRAW, TMR}, 1000000, 100, 0) - local loadedB = makeVehicle("vehicleLoadedB", "Fyldt B", {STRAW, TMR}, 1000000, 110, 0) - local both = ADRunsController.createRun("To fyldte") - ADRunsController.updateRun(both.id, {targetWayPointIds = {94, 99}}) - ADRunsController.assignVehicle(both.id, loadedA) - ADRunsController.assignVehicle(both.id, loadedB) - ADRunsController.startRun(adEnv, both.id) - local a, b = loadedA.ad.stateModule.secondMarkerId, loadedB.ad.stateModule.secondMarkerId - check("C16c første fyldte -> mindst fyldte mål", a, 99) - check("C16c anden fyldte -> det andet mål", b, 94) - ADSmartPickup.countEnRoute, ADSmartPickup.registerCargo = nil, nil -end -ADRunsController.updateRun(multi.id, {targetWayPointIds = {4242}}) -check("C16 kun ukendte mål -> noTarget", ADRunsController.validateVehicle(adEnv, ADRuns.getRun(ADRunsController.store, multi.id), multiVehicle).reason, "noTarget") - --- C17: kørende traktor der fjernes fra run stoppes; slettes run, stoppes dets traktorer; Stop logger -buildWorld() -local runVehicle = makeVehicle("vehicleR", "Kørende", {STRAW, TMR}, 1000000, 100, 0) -local stopRun = ADRunsController.createRun("Stop-test") -ADRunsController.updateRun(stopRun.id, {targetWayPointId = 94}) -ADRunsController.assignVehicle(stopRun.id, runVehicle) -ADRunsController.startRun(adEnv, stopRun.id) -check("C17 kører", runVehicle.started, 1) -ADRunsController.unassignVehicle(runVehicle) -check("C17 fjernet fra run -> stoppet", runVehicle.stopped, 1) -ADRunsController.assignVehicle(stopRun.id, runVehicle) -ADRunsController.startRun(adEnv, stopRun.id) -ADRunsController.deleteRun(stopRun.id, adEnv) -check("C17 run slettet -> stoppet", runVehicle.stopped, 2) -check("C17 stop logget", logLines[#logLines]:find("stoppet", 1, true) ~= nil, true) - --- C18: forkert last ved Start (halm på vognen, run mod sukkerfabrik) -> første stop = silo der tager halm -ADUnloadWait = {getRestLoad = function(_, v) return v.loadType, v.loadLiters or 0 end, getWaitFor = function() return nil end, - hasRouteBothWays = function() return true end} -ADLoadSwap = {findDumpMarker = function(_, v, fillType, rest, homeId, isReachable) return {id = 44, name = "10 Silo Storage", markerIndex = 2} end} -buildWorld() -addMarker(77, "sugar offload", "Fabrication", 950, 0); unloadAt[77] = {accepts = {[TMR] = 360000}} -- "fabrik" der ikke tager halm (TMR står for roer her) -local sugarVehicle = makeVehicle("vehicleSugar", "Roe-traktor", {STRAW, TMR}, 20000, 100, 0) -sugarVehicle.loadType, sugarVehicle.loadLiters = STRAW, 8000 -local sugar = ADRunsController.createRun("Sugar") -ADRunsController.updateRun(sugar.id, {targetWayPointId = 77}) -ADRunsController.assignVehicle(sugar.id, sugarVehicle) -ADRunsController.startRun(adEnv, sugar.id) -check("C18 første stop = siloen (aflæs forkert last)", sugarVehicle.ad.stateModule.secondMarkerId, 44) -check("C18 logget", logLines[#logLines]:find("læsser", 1, true) ~= nil or logLines[#logLines - 1]:find("læsser", 1, true) ~= nil, true) --- rigtig last (TMR=roer) -> direkte til fabrikken -sugarVehicle.loadType = TMR -sugarVehicle.ad.stateModule.active = false -ADRunsController.startRun(adEnv, sugar.id) -check("C18 rigtig last -> målet", sugarVehicle.ad.stateModule.secondMarkerId, 77) --- C18b: målet TAGER varen, men den er ikke en af run'ets valgte varer (gødning i en ensilage-DOB) --- -> aflæs i en silo der tager den, ikke i målet -unloadAt[77].accepts = {[TMR] = 360000, [STRAW] = 360000} -ADRunsController.updateRun(sugar.id, {ingredientMode = "manual", ingredients = {"FORAGE"}}) -sugarVehicle.loadType = STRAW -sugarVehicle.ad.stateModule.active = false -ADRunsController.startRun(adEnv, sugar.id) -check("C18b forkert vare for run'et -> silo, ikke målet", sugarVehicle.ad.stateModule.secondMarkerId, 44) --- auto-varer: alt målet tager hører til -> målet -ADRunsController.updateRun(sugar.id, {ingredientMode = "auto", ingredients = {}}) -sugarVehicle.ad.stateModule.active = false -ADRunsController.startRun(adEnv, sugar.id) -check("C18b auto: målet tager varen -> målet", sugarVehicle.ad.stateModule.secondMarkerId, 77) -ADUnloadWait = nil; ADLoadSwap = nil - --- C19: Wait-markører er aldrig mål — heller ikke når de står inden for 40 m af staldens trigger -buildWorld() --- staldens station med bygning; Wait-markøren ligger i triggerens rækkevidde og "mangler mere" end 94 -local barnPlaceable = {getHusbandryFillLevel = function() return 0 end} -unloadAt[94].owningPlaceable = barnPlaceable -addMarker(400, "Cow Wait", "Cow Wait", 5005, 0); unloadAt[400] = {accepts = {[STRAW] = 300000, [TMR] = 300000}, levels = {}, owningPlaceable = barnPlaceable} -local targetNames = {} -for _, marker in ipairs(ADRunsController.getTargetMarkers(adEnv, 1)) do table.insert(targetNames, marker.name) end -check("C19 Wait-markør ikke i mål-listen", table.concat(targetNames, ","):find("Cow Wait", 1, true), nil) -local waitTargetRun = ADRunsController.createRun("Wait som mål") -ADRunsController.updateRun(waitTargetRun.id, {targetWayPointIds = {94, 400}}) -local waitVehicle = makeVehicle("vehicleW", "W", {STRAW, TMR}, 20000, 5000, 0) -check("C19 chooseTarget springer Wait over", ADRunsController.chooseTarget(adEnv, ADRuns.getRun(ADRunsController.store, waitTargetRun.id), waitVehicle), 94) - --- C-out: udkørsels-runs — oprettelse, kilder, produkter, validering, start, status -local BASKET = 7 -FILL_TYPE_NAMES[BASKET] = "STRAW_BASKET"; FILL_TYPE_INDEX["STRAW_BASKET"] = BASKET -local sourcesAt, productsAt = {}, {} -ADSources = { - identify = function(_, id) return sourcesAt[id] end, - getProducts = function(_, source) return productsAt[source.wayPointId] or {} end, - getSellMarkers = function() return {} end, - stationAccepts = function() return false end, - getSourcePlaceables = function() return {} end, -} -local outboundStatus = {reasons = {"SUGAR 78 % af top (kræver 90), lager 41 %"}, warning = nil} -local holdFor = {} -ADOutbound = { - isOutboundVehicle = function(v) local r = ADRunsController.getRunForVehicle(v) return r ~= nil and r.kind == "outbound" end, - getRunStatus = function() return outboundStatus end, - getHoldFor = function(v) return holdFor[v] end, - getTripFor = function() return nil end, - releaseInactive = function() end, -} -ADUnloadWait = {getRestLoad = function() return nil, 0 end, getWaitFor = function() return nil end} -buildWorld() -addMarker(1, "Suger Load", "Pickup points", 100, 0); loadAt[1] = {} -addMarker(3, "Objektlager", "Pickup points", 300, 0) -addMarker(10, "selling station", "selling", 500, 0) -sourcesAt[1] = {kind = "factory", wayPointId = 1, station = loadAt[1], name = "Sugar mill"} -sourcesAt[3] = {kind = "objectStorage", wayPointId = 3, name = "Objektlager"} -productsAt[1] = {{fillType = TMR, name = "FORAGE", title = "TMR", available = 150000, level = 150000, capacity = 180000, isPallet = false}} -productsAt[3] = {{fillType = BASKET, name = "STRAW_BASKET", title = "Halmkurve", available = 8000, level = 8, capacity = 4000, isPallet = true}} -local bulkTractor = makeVehicle("vehicleBulk", "MT635", {STRAW, TMR}, 20000, 100, 0) -local ualTractor = makeVehicle("vehicleUal", "Fendt", {}, 8000, 300, 0) -ualTractor.trailers[1] = {ual = true} -adEnv.AutoDrive.hasAL = function(_, trailer) return trailer.ual == true end - -local out = ADRunsController.createRun("Sugar out", "outbound") -check("Cout1 kind", ADRuns.getRun(ADRunsController.store, out.id).kind, "outbound") -check("Cout1 supply-run har kind supply", ADRunsController.createRun("Cows").kind, "supply") -check("Cout1 kilde-markører", #ADRunsController.getSourceMarkers(adEnv, 1), 2) -check("Cout1 første kilde-markør", ADRunsController.getSourceMarkers(adEnv, 1)[1].marker.name, "Objektlager") - --- Cout2: validering -local outRun = ADRuns.getRun(ADRunsController.store, out.id) --- diagnose: hvad moden ser på enhederne, når ingen autoload findes (logges én gang pr. traktor, --- første gang varerne slås op — dvs. allerede ved noProducts-tjekket nedenfor) -logLines = {} -bulkTractor.trailers[1] = {spec_universalAutoload = {isAutoloadAvailable = false, autoloadDisabled = true}, getName = function() return "Vogn" end} -check("Cout2 ingen kilder -> noSource", ADRunsController.validateVehicle(adEnv, outRun, bulkTractor).reason, "noSource") -ADRunsController.store = ADRuns.toggleSource(ADRunsController.store, out.id, 1) -ADRunsController.store = ADRuns.toggleSource(ADRunsController.store, out.id, 3) -outRun = ADRuns.getRun(ADRunsController.store, out.id) -check("Cout2 ingen vare valgt -> noProducts", ADRunsController.validateVehicle(adEnv, outRun, bulkTractor).reason, "noProducts") -ADRunsController.store = ADRuns.setProductPolicy(ADRunsController.store, out.id, "STRAW_BASKET", "sell") -outRun = ADRuns.getRun(ADRunsController.store, out.id) -check("Cout2 kun paller uden UAL -> needsAutoload", ADRunsController.validateVehicle(adEnv, outRun, bulkTractor).reason, "needsAutoload") -local diag = nil -for _, line in ipairs(logLines) do if line:find("ingen autoload", 1, true) then diag = line end end -check("Cout2 diagnose logges", diag ~= nil, true) -check("Cout2 diagnose viser UAL-flag", diag ~= nil and diag:find("isAutoloadAvailable=false", 1, true) ~= nil, true) -logLines = {} -ADRunsController.validateVehicle(adEnv, outRun, bulkTractor) -check("Cout2 diagnose kun én gang", #logLines, 0) -bulkTractor.trailers[1] = nil -check("Cout2 UAL-vogn ok", ADRunsController.validateVehicle(adEnv, outRun, ualTractor).ok, true) -ADRunsController.store = ADRuns.setProductPolicy(ADRunsController.store, out.id, "FORAGE", "store") -outRun = ADRuns.getRun(ADRunsController.store, out.id) -check("Cout2 bulk-vogn ok med TMR", ADRunsController.validateVehicle(adEnv, outRun, bulkTractor).ok, true) -local strawOnly = makeVehicle("vehicleStraw", "Halm", {STRAW}, 20000, 100, 0) -check("Cout2 kan ikke bære -> cannotCarry", ADRunsController.validateVehicle(adEnv, outRun, strawOnly).reason, "cannotCarry") - --- Cout3: start sætter mode, 1./2. markør = første kilde, varevalg = det vognen kan bære af run'ets varer -ADRunsController.assignVehicle(out.id, bulkTractor) -ADRunsController.assignVehicle(out.id, ualTractor) -local results = ADRunsController.startRun(adEnv, out.id) -check("Cout3 to startet", #results, 2) -check("Cout3 mode P&D", bulkTractor.ad.stateModule.mode, MODE_PICKUPANDDELIVER) -check("Cout3 1. markør = første kilde", bulkTractor.ad.stateModule.firstMarkerId, 1) -check("Cout3 2. markør = første kilde (pladsholder)", bulkTractor.ad.stateModule.secondMarkerId, 1) -check("Cout3 bulk-vogn: kun TMR", table.concat(bulkTractor.ad.stateModule.selectedFillTypes, ","), tostring(TMR)) -check("Cout3 UAL-vogn: kurve", table.concat(ualTractor.ad.stateModule.selectedFillTypes, ","), tostring(BASKET)) -check("Cout3 rotation kun pålæsning", bulkTractor.ad.settings.rotateTargets.current, 2) -check("Cout3 startet", bulkTractor.started, 1) --- AD's "AL unload" er slået FRA som standard (values[1] = 0): autoload-vognen ville aldrig læsse af --- ved salgsstedet. Ved Start sættes den til "center" hvis den er fra; et brugervalg (left/right/behind) røres ikke. -check("Cout3 ALUnload sat til center for autoload-bil", ualTractor.ad.settings.ALUnload.current, 2) -check("Cout3 ALUnload rører ikke tipvogn", bulkTractor.ad.settings.ALUnload.current, 1) -ualTractor.ad.settings.ALUnload.current = 4 -ualTractor.ad.stateModule.active = false -ADRunsController.startRun(adEnv, out.id) -check("Cout3 ALUnload brugervalg bevares", ualTractor.ad.settings.ALUnload.current, 4) - --- Cout4: produkter til fanen (politik slået op, sorteret på titel) og status -local list = ADRunsController.getRunProducts(adEnv, outRun) -check("Cout4 to produkter", #list, 2) -check("Cout4 første = Halmkurve", list[1].title, "Halmkurve") -check("Cout4 politik", list[1].policy, "sell") -check("Cout4 TMR lager", list[2].policy, "store") -check("Cout4 available", list[2].available, 150000) -local st = ADRunsController.getOutboundStatus(adEnv, outRun) -check("Cout4 status-begrundelse", st.reasons[1], "SUGAR 78 % af top (kræver 90), lager 41 %") -holdFor[bulkTractor] = {markerName = "Wait 3", reasons = {"SUGAR 78 % af top (kræver 90), lager 41 %"}} -local activity = ADRunsController.getVehicleActivity(adEnv, bulkTractor) -check("Cout4 aktivitet = holder", activity.kind, "holding") -check("Cout4 aktivitet sted", activity.place, "Wait 3") -check("Cout4 udkørsel har ingen mål", #ADRunsController.getRunTargets(outRun), 0) --- Cout5 (I2): Start kræver et Wait-punkt i puljen; Cout6 (I3): last på vognen ved Start får eget mål -ADSupplyPlanner.isWaitMarker = function(marker) return string.find(string.lower(marker.group or ""), "%f[%a]wait%f[%A]") ~= nil end -local noWait = ADRunsController.createRun("No wait", "outbound") -ADRunsController.store = ADRuns.toggleSource(ADRunsController.store, noWait.id, 1) -ADRunsController.store = ADRuns.setProductPolicy(ADRunsController.store, noWait.id, "FORAGE", "store") -ADRunsController.store = ADRuns.updateRun(ADRunsController.store, noWait.id, {waitPoolGroup = "Pig Wait"}) -ADRunsController.store = ADRuns.updateRun(ADRunsController.store, noWait.id, {waitPoolGroup = "Findes ikke"}) -local noWaitRun = ADRuns.getRun(ADRunsController.store, noWait.id) -check("Cout5 ingen Wait-punkt i puljen -> noWaitPoint", ADRunsController.validateVehicle(adEnv, noWaitRun, bulkTractor).reason, "noWaitPoint") -ADRunsController.store = ADRuns.updateRun(ADRunsController.store, noWait.id, {waitPoolGroup = "Cow Wait"}) -noWaitRun = ADRuns.getRun(ADRunsController.store, noWait.id) -check("Cout5 Wait-punkt findes -> ok", ADRunsController.validateVehicle(adEnv, noWaitRun, bulkTractor).ok, true) -ADUnloadWait.getRestLoad = function(_, v) return v.loadType, v.loadLiters or 0 end -ADOutbound.planCargoTarget = function(adEnv, run, vehicle, fillType) return fillType == TMR and 10 or nil end -bulkTractor.loadType, bulkTractor.loadLiters = TMR, 8000 -ADRunsController.assignVehicle(noWait.id, bulkTractor) -bulkTractor.ad.stateModule.active = false -ADRunsController.startRun(adEnv, noWait.id) -check("Cout6 last på vognen -> 2. markør = lastens mål", bulkTractor.ad.stateModule.secondMarkerId, 10) -bulkTractor.loadType = STRAW -check("Cout6 last uden mål -> cargoNoTarget", ADRunsController.validateVehicle(adEnv, noWaitRun, bulkTractor).reason, "cargoNoTarget") -bulkTractor.loadType = nil -ADUnloadWait = nil; ADOutbound = nil; ADSources = nil - --- C-flow: bygninger -> markører (resolveMarkers), migration af gamle markør-runs, noMarker, flow-rækker -local buildings = { - {id = "barn1", name = "Kostald 1", kind = "husbandry", loadMarker = {id = 10, name = "cow 1 milk", distance = 5}, loadMarkers = {{id = 10, name = "cow 1 milk", distance = 5}, {id = 11, name = "cow 1 slurry", distance = 3}}, unloadMarker = {id = 94, name = "Cow 1 Food", distance = 8}, canBeSource = true, canBeTarget = true, canSell = false}, - {id = "shed", name = "Palleskur", kind = "objectStorage", loadMarker = nil, unloadMarker = nil, canBeSource = true, canBeTarget = false, canSell = false}, - {id = "mill", name = "Sukkerfabrik", kind = "factory", loadMarker = {id = 12694, name = "Suger Load", distance = 3}, unloadMarker = {id = 77, name = "sugar offload", distance = 4}, canBeSource = true, canBeTarget = true, canSell = false}, -} -ADBuildings = { - list = function() return buildings end, - find = function(_, _, id) for _, b in ipairs(buildings) do if b.id == id then return b end end end, - byMarker = function(_, _, wayPointId, role) - for _, b in ipairs(buildings) do - if role == "unload" then - if b.unloadMarker ~= nil and b.unloadMarker.id == wayPointId then return b end - else - for _, m in ipairs(b.loadMarkers or {b.loadMarker}) do if m ~= nil and m.id == wayPointId then return b end end - end - end - end, -} -ADSources = {identify = function(_, id) return (id == 10 or id == 12694 or id == 555) and {kind = "x", wayPointId = id, name = "k"} or nil end, getProducts = function() return {} end, getSellMarkers = function() return {} end, stationAccepts = function() return false end, getSourcePlaceables = function() return {} end} -ADOutbound = {isOutboundVehicle = function() return false end, getRunStatus = function() return {reasons = {"SUGAR 78 % af top"}} end, getHoldFor = function() return nil end, getTripFor = function() return nil end, releaseInactive = function() end} -buildWorld() -addMarker(10, "cow 1 milk", "Milk", 100, 0); loadAt[10] = {} -addMarker(555, "raa markoer", "Pickup", 200, 0); loadAt[555] = {} -addMarker(77, "sugar offload", "production", 950, 0); unloadAt[77] = {accepts = {[TMR] = 360000}} --- CF1: udkørsel: bygninger + rå markør -> sourceWayPointIds; manglende markør rapporteres -local flowOut = ADRunsController.createRun("Milk", "outbound") -ADRunsController.updateRun(flowOut.id, {sourceBuildingIds = {"barn1", "shed"}, sourceWayPointIds = {555}}) -local resolved = ADRunsController.resolveMarkers(adEnv, ADRuns.getRun(ADRunsController.store, flowOut.id)) -check("CF1 kildemarkører = ALLE bygningens læssemarkører + rå", table.concat(ADRuns.getRun(ADRunsController.store, flowOut.id).sourceWayPointIds, ","), "10,11,555") -check("CF1 manglende markør", resolved.missing[1], "Palleskur") --- en markør der HØRER til en bygning som ikke længere er valgt, fjernes (den er ikke rå) -ADRunsController.updateRun(flowOut.id, {sourceWayPointIds = {12694, 555}}) -ADRunsController.resolveMarkers(adEnv, ADRuns.getRun(ADRunsController.store, flowOut.id)) -check("CF1 fravalgt bygnings markør fjernes", table.concat(ADRuns.getRun(ADRunsController.store, flowOut.id).sourceWayPointIds, ","), "10,11,555") --- CF1b: gamle rå markører ryddes: ikke-kilde (identify = nil) og markør der hører til en valgt bygning -ADSources.identify = function(_, id) - if id == 10 or id == 12694 or id == 555 then return {kind = "x", wayPointId = id, name = "k"} end - if id == 556 then return {kind = "husbandry", wayPointId = id, name = "Kostald 1", placeable = "barn1-placeable"} end - return nil -end -buildings[1].placeable = "barn1-placeable" -ADRunsController.updateRun(flowOut.id, {sourceBuildingIds = {"barn1"}, sourceWayPointIds = {10, 11, 555, 556, 999}}) -logLines = {} -ADRunsController.resolveMarkers(adEnv, ADRuns.getRun(ADRunsController.store, flowOut.id)) -check("CF1b rå ikke-kilde og dublet-bygning fjernet", table.concat(ADRuns.getRun(ADRunsController.store, flowOut.id).sourceWayPointIds, ","), "10,11,555") -local pruneLogged = false -for _, line in ipairs(logLines) do if line:find("gamle markører fjernet", 1, true) then pruneLogged = true end end -check("CF1b logget", pruneLogged, true) - --- CF1c: et gammelt kilde-id der ikke længere er en AD-MARKØR (AutoDrive har omnummereret waypoints) --- fjernes, selv om punktet ligger ved en kilde — ellers kører traktoren til et tilfældigt vejpunkt -local identifyBefore = ADSources.identify -ADSources.identify = function(adEnv_, id, farmId) - if id == 10873 then return {kind = "husbandry", wayPointId = id, name = "Anden stald", placeable = "anden-stald"} end - return identifyBefore(adEnv_, id, farmId) -end -ADRunsController.updateRun(flowOut.id, {sourceBuildingIds = {"barn1"}, sourceWayPointIds = {10, 11, 555, 10873}}) -ADRunsController.resolveMarkers(adEnv, ADRuns.getRun(ADRunsController.store, flowOut.id)) -check("CF1c vejpunkt uden markør fjernet", table.concat(ADRuns.getRun(ADRunsController.store, flowOut.id).sourceWayPointIds, ","), "10,11,555") -ADSources.identify = identifyBefore - --- CF2: forsyning: mål-bygninger -> targetWayPointIds + targetWayPointId -local flowIn = ADRunsController.createRun("Foder", "supply") -ADRunsController.updateRun(flowIn.id, {targetBuildingIds = {"barn1", "mill"}}) -ADRunsController.resolveMarkers(adEnv, ADRuns.getRun(ADRunsController.store, flowIn.id)) -check("CF2 målmarkører", table.concat(ADRunsController.getRunTargets(ADRuns.getRun(ADRunsController.store, flowIn.id)), ","), "94,77") -check("CF2 primært mål", ADRuns.getRun(ADRunsController.store, flowIn.id).targetWayPointId, 94) --- CF2b: gamle rå mål der ikke er en markør (slettet i AD) eller hører til en valgt bygning, ryddes -addMarker(78, "Anden stald food", "Cow Food", 3000, 0); unloadAt[78] = {accepts = {[TMR] = 1000}, owningPlaceable = "anden-stald"} -unloadAt[11062] = {accepts = {[TMR] = 1000}, owningPlaceable = "anden-stald"} -- waypoint uden markør -ADRunsController.updateRun(flowIn.id, {targetWayPointIds = {94, 77, 11062, 78}}) -ADRunsController.resolveMarkers(adEnv, ADRuns.getRun(ADRunsController.store, flowIn.id)) -check("CF2b ikke-markør fjernet, rå markør med station beholdt", table.concat(ADRunsController.getRunTargets(ADRuns.getRun(ADRunsController.store, flowIn.id)), ","), "94,77,78") -ADRunsController.updateRun(flowIn.id, {targetWayPointIds = {94, 77}}) --- CF3: migration: gamle markør-runs får bygninger -local oldOut = ADRunsController.createRun("Gammel ud", "outbound") -ADRunsController.updateRun(oldOut.id, {sourceWayPointIds = {12694, 555}}) -local oldIn = ADRunsController.createRun("Gammel ind", "supply") -ADRunsController.updateRun(oldIn.id, {targetWayPointIds = {94}}) -ADRunsController.migrateBuildings(adEnv) -check("CF3 ud: markør -> bygning", ADRuns.getRun(ADRunsController.store, oldOut.id).sourceBuildingIds[1], "mill") -check("CF3 ud: rå markør uden bygning beholdes", table.concat(ADRuns.getRun(ADRunsController.store, oldOut.id).sourceWayPointIds, ","), "12694,555") -check("CF3 ind: markør -> bygning", ADRuns.getRun(ADRunsController.store, oldIn.id).targetBuildingIds[1], "barn1") -ADRunsController.migrateBuildings(adEnv) -check("CF3 migration er idempotent", #ADRuns.getRun(ADRunsController.store, oldOut.id).sourceBuildingIds, 1) --- CF4: validering: valgt bygning uden markør -> noMarker -local cfVehicle = makeVehicle("vehicleCF", "CF", {STRAW, TMR}, 20000, 100, 0) -ADRunsController.updateRun(flowOut.id, {sourceBuildingIds = {"shed"}, sourceWayPointIds = {}}) -local cfValidation = ADRunsController.validateVehicle(adEnv, ADRuns.getRun(ADRunsController.store, flowOut.id), cfVehicle) -check("CF4 noMarker", cfValidation.reason, "noMarker") -check("CF4 hvilken bygning", cfValidation.missingBuilding, "Palleskur") --- CF4b: ÉN bygning uden markør blandt flere med markør -> start alligevel, advar om den ene -ADRunsController.updateRun(flowOut.id, {sourceBuildingIds = {"barn1", "shed"}, sourceWayPointIds = {}}) -logLines = {} -local cfValidation2 = ADRunsController.validateVehicle(adEnv, ADRuns.getRun(ADRunsController.store, flowOut.id), cfVehicle) -check("CF4b ikke afvist pga. markør", cfValidation2.reason ~= "noMarker", true) -local warned = false -for _, line in ipairs(logLines) do if line:find("Palleskur", 1, true) and line:find("springes over", 1, true) then warned = true end end -check("CF4b advarsel nævner bygningen", warned, true) --- CF5: flow-række til tavlen -ADRunsController.updateRun(flowOut.id, {sourceBuildingIds = {"barn1"}, products = {{fillType = "SUGAR", policy = "sell"}, {fillType = "FORAGE", policy = "store"}}, collectRound = true}) -ADRunsController.assignVehicle(flowOut.id, cfVehicle) -local row = ADRunsController.getFlowRow(adEnv, ADRuns.getRun(ADRunsController.store, flowOut.id)) -check("CF5 vare-navne", table.concat(row.productNames, ","), "SUGAR,FORAGE") -check("CF5 fra", row.fromNames[1], "Kostald 1") -check("CF5 politikker", table.concat(row.policies, ","), "sell,store") -check("CF5 traktor", row.vehicleNames[1], "CF") -check("CF5 begrundelse", row.reason, "SUGAR 78 % af top") -check("CF5 runde", row.collectRound, true) -ADRunsController.updateRun(flowOut.id, {sourceBuildingIds = {"barn1", "shed"}}) -check("CF5 manglende bygning markeres", ADRunsController.getFlowRow(adEnv, ADRuns.getRun(ADRunsController.store, flowOut.id)).missing[1], "Palleskur") -local rowIn = ADRunsController.getFlowRow(adEnv, ADRuns.getRun(ADRunsController.store, flowIn.id)) -check("CF5 forsyning fra = alle siloer", rowIn.fromAllSilos, true) -check("CF5 forsyning til", table.concat(rowIn.toNames, ","), "Kostald 1,Sukkerfabrik") -check("CF5 forsyning auto-varer", rowIn.productAuto, true) --- SB: "Lager til" — valgte bygninger -> deres aflæsningsmarkører (i valgt rækkefølge; ukendte udelades) -do - local sbRun = ADRunsController.createRun("Gylle", "outbound") - ADRunsController.updateRun(sbRun.id, {storeBuildingIds = {"mill", "findes-ikke", "barn1"}}) - check("SB markører", table.concat(ADRunsController.getStoreMarkers(adEnv, ADRuns.getRun(ADRunsController.store, sbRun.id)), ","), "77,94") - ADRunsController.updateRun(sbRun.id, {storeBuildingIds = {}}) - check("SB tom = ingen tvang", #ADRunsController.getStoreMarkers(adEnv, ADRuns.getRun(ADRunsController.store, sbRun.id)), 0) -end --- CS: "Lager til" viser kun bygninger der kan tage flowets lagervarer (æg-paller: kun palleskure der tager æg) -do - local EGG, SILAGE = 501, 502 - local shed = {id = "shed1", kind = "objectStorage", placeable = {takesEggs = true}} - local shedNo = {id = "shed2", kind = "objectStorage", placeable = {takesEggs = false}} - local silo = {id = "silo9", kind = "silo", placeable = {}, unloadMarker = {id = 901}} - unloadAt[901] = {accepts = {[SILAGE] = 100000}, getIsFillTypeSupported = function(_, ft) return ft == SILAGE end} - local accBefore, storeBefore = ADSources.objectStorageAccepts, ADLoadSwap and ADLoadSwap.isStoreTarget - ADSources.objectStorageAccepts = function(placeable, ft) return ft == EGG and placeable.takesEggs end - ADLoadSwap = ADLoadSwap or {} - ADLoadSwap.isStoreTarget = function(station, ft) return station.getIsFillTypeSupported(station, ft) end - local eggs = {{fillType = EGG, isPallet = true}} - check("CS palleskur der tager æg", ADRunsController.canStoreAt(adEnv, shed, eggs), true) - check("CS palleskur der ikke tager æg", ADRunsController.canStoreAt(adEnv, shedNo, eggs), false) - check("CS silo kan ikke tage æg-paller", ADRunsController.canStoreAt(adEnv, silo, eggs), false) - check("CS silo tager ensilage", ADRunsController.canStoreAt(adEnv, silo, {{fillType = SILAGE, isPallet = false}}), true) - ADSources.objectStorageAccepts = accBefore - ADLoadSwap.isStoreTarget = storeBefore -end -ADBuildings = nil; ADSources = nil; ADOutbound = nil - --- RC-navne: køretøjer får AD-førernavn efter forløbet; ude af forløb -> modelnavnet igen -do - local named = makeVehicle("vehicleNamed", "Virtus 135", {MILK or 1}, 20000, 0, 0) - named.ad.stateModule.driverName = "Virtus 135" - named.ad.stateModule.setName = function(self, n) self.driverName = n end - named.ad.stateModule.getName = function(self) return self.driverName end - local run = ADRunsController.createRun("Mælk ind", "outbound") - ADRunsController.assignVehicle(run.id, named) - ADRunsController.applyVehicleNames(adEnv) - check("RC-navne sat efter forløb", named.ad.stateModule.driverName, "Mælk ind") - check("RC-navne label bruger førernavn", ADRuns.vehicleLabel(named), "Mælk ind") - ADRunsController.store = ADRuns.unassignVehicle(ADRunsController.store, "vehicleNamed") - ADRunsController.applyVehicleNames(adEnv) - check("RC-navne ude af forløb -> modelnavn", named.ad.stateModule.driverName, "Virtus 135") - check("RC-navne label uden AD-navn", ADRuns.vehicleLabel({getName = function() return "Fendt" end}), "Fendt") -end - --- PM: afhentningsmarkører fra udkørsels-forløb (lagerture undgår dem som aflæsningssted) -do - local sale = ADRunsController.createRun("Salg-test", "outbound") - ADRunsController.updateRun(sale.id, {sourceWayPointIds = {4711, 4712}}) - local supply = ADRunsController.createRun("Forsyning-test", "supply") - ADRunsController.updateRun(supply.id, {sourceWayPointIds = {9999}}) - local pickups = ADRunsController.getPickupMarkers() - check("PM udkørsels-kilde med", pickups[4711], true) - check("PM forsyning tæller ikke", pickups[9999], nil) -end - --- SA: Start/Stop alle flows — kørende flows røres ikke ved Start alle; Stop alle stopper alt aktivt -do - buildWorld() - local idle = makeVehicle("vehicleAllA", "Alle A", {STRAW, TMR}, 1000000, 100, 0) - local busy = makeVehicle("vehicleAllB", "Alle B", {STRAW, TMR}, 1000000, 100, 0) - local runA = ADRunsController.createRun("Alle-A") - ADRunsController.updateRun(runA.id, {targetWayPointId = 94}) - ADRunsController.assignVehicle(runA.id, idle) - local runB = ADRunsController.createRun("Alle-B") - ADRunsController.updateRun(runB.id, {targetWayPointId = 94}) - ADRunsController.assignVehicle(runB.id, busy) - ADRunsController.createRun("Alle-tom") -- ingen traktorer: tæller ikke med - ADRunsController.startRun(adEnv, runB.id) - local busyStarts = busy.started - local summary = ADRunsController.startAllRuns(adEnv) - check("SA idle startet", idle.started, 1) - check("SA kørende flow ikke genstartet", busy.started, busyStarts) - check("SA mindst ét flow startet", summary.runsStarted >= 1, true) - check("SA kørende flow talt", summary.alreadyRunning >= 1, true) - check("SA traktorer startet tæller idle", summary.vehiclesStarted >= 1, true) - check("SA resultater med", type(summary.results), "table") - check("SA noget aktivt", ADRunsController.hasActiveRuns(adEnv), true) - local stopped = ADRunsController.stopAllRuns(adEnv) - check("SA stop alle stopper begge", stopped >= 2, true) - check("SA idle stoppet", idle.ad.stateModule.active, false) - check("SA busy stoppet", busy.ad.stateModule.active, false) - check("SA intet aktivt bagefter", ADRunsController.hasActiveRuns(adEnv), false) - check("SA isRunning ryddet", ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, runA.id)).isRunning, false) -end - - --- C20: Start alle i kø — én traktor ad gangen med STAGGER_MS mellemrum (in-game 27/9: ellers kører de --- sammen på parkeringspladsen); pause ophæves -do - buildWorld() - local q1 = makeVehicle("vehicleQ1", "Q1", {STRAW, TMR}, 1000000, 100, 0) - local q2 = makeVehicle("vehicleQ2", "Q2", {STRAW, TMR}, 1000000, 110, 0) - local q3 = makeVehicle("vehicleQ3", "Q3", {}, 0, 120, 0) -- ingen vogn -> springes over, ikke i kø - local qrun = ADRunsController.createRun("Kø") - ADRunsController.updateRun(qrun.id, {targetWayPointIds = {94}}) - ADRunsController.assignVehicle(qrun.id, q1) - ADRunsController.assignVehicle(qrun.id, q2) - ADRunsController.assignVehicle(qrun.id, q3) - local summary = ADRunsController.queueStartAll(adEnv) - check("C20 to i kø", summary.queued, 2) - check("C20 en sprunget over", #summary.skipped, 1) - check("C20 intet startet før første tick", (q1.started or 0) + (q2.started or 0), 0) - ADRunsController.processStartQueue(adEnv, 0) - check("C20 første startes straks", (q1.started or 0) + (q2.started or 0), 1) - ADRunsController.processStartQueue(adEnv, ADRunsController.STAGGER_MS - 1) - check("C20 næste venter mellemrummet", (q1.started or 0) + (q2.started or 0), 1) - ADRunsController.processStartQueue(adEnv, 1) - check("C20 næste efter mellemrummet", (q1.started or 0) + (q2.started or 0), 2) - check("C20 kø tom", #ADRunsController.startQueue, 0) - check("C20 run markeret kørende", ADRunsController.runningRuns[qrun.id], true) -end - - --- C21: Stop alt og parkér — stopper flow-traktorerne, sender dem til AD-parkering, sætter pause -do - buildWorld() - local p1 = makeVehicle("vehicleP1", "P1", {STRAW, TMR}, 1000000, 100, 0) - local p2 = makeVehicle("vehicleP2", "P2", {STRAW, TMR}, 1000000, 110, 0) - local prun = ADRunsController.createRun("Park") - ADRunsController.updateRun(prun.id, {targetWayPointIds = {94}}) - ADRunsController.assignVehicle(prun.id, p1) - ADRunsController.assignVehicle(prun.id, p2) - ADRunsController.startRun(adEnv, prun.id) - local parked, paused = {}, nil - local oldWork, oldJobs, oldSilos = ADFieldWork, ADFieldJobs, ADFieldSilos - ADFieldWork = { - parkMarkerId = function(_, v) return v == p1 and 700 or nil end, - startAd = function(_, v, mode, first) parked[v] = first end, - stopAllAssignments = function() return {} end, - } - ADFieldJobs = {setPaused = function(value) paused = value end} - ADFieldSilos = {stopAll = function() end} - ADRunsController.queueStartAll(adEnv) - local summary = ADRunsController.stopAllAndPark(adEnv) - check("C21 begge stoppet", (p1.stopped or 0) >= 1 and (p2.stopped or 0) >= 1, true) - check("C21 P1 kører til parkering", parked[p1], 700) - check("C21 P2 uden parkering bliver stående", parked[p2], nil) - check("C21 tæller parkerede", summary.parked, 1) - check("C21 nævner dem uden parkering", summary.noPark[1], "P2") - check("C21 pause sat", paused, true) - check("C21 startkø tømt", #ADRunsController.startQueue, 0) - check("C21 run ikke længere kørende", ADRunsController.runningRuns[prun.id], nil) - ADFieldWork, ADFieldJobs, ADFieldSilos = oldWork, oldJobs, oldSilos -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_adSmartPickup.lua b/tests/test_adSmartPickup.lua deleted file mode 100644 index 3832fc6..0000000 --- a/tests/test_adSmartPickup.lua +++ /dev/null @@ -1,1295 +0,0 @@ --- Kør: luajit tests/test_adSmartPickup.lua (fra repo-roden) --- Mock'er de få FS25-/AutoDrive-API'er moden rører og tester valg-logikken. - -local SUGARBEET, WHEAT = 10, 11 -local HAY, GRASS, SILAGE = 20, 21, 22 -local MILK = 30 -local FILL_TYPE_NAMES = {[SUGARBEET] = "SUGARBEET", [WHEAT] = "WHEAT", [HAY] = "DRYGRASS_WINDROW", [GRASS] = "GRASS_WINDROW", [SILAGE] = "SILAGE", [MILK] = "MILK"} -g_fillTypeManager = { - getFillTypeNameByIndex = function(_, index) return FILL_TYPE_NAMES[index] end, - getFillTypeIndexByName = function(_, name) for index, candidate in pairs(FILL_TYPE_NAMES) do if candidate == name then return index end end return nil end, -} -local logLines = {} - --- ---- FS25-mocks ---- -MathUtil = {vector2Length = function(x, z) return math.sqrt(x * x + z * z) end} -Logging = { - info = function(fmt, ...) table.insert(logLines, string.format(fmt, ...)) end, - warning = function(fmt, ...) table.insert(logLines, "WARN " .. string.format(fmt, ...)) end, -} -local nodePositions = {} -function entityExists(node) return nodePositions[node] ~= nil end -function getWorldTranslation(node) local p = nodePositions[node]; return p.x, 0, p.z end -local listener -function addModEventListener(l) listener = l end - -local function makeStation(node, x, z, levels, isBroken, ownLevels) - nodePositions[node] = {x = x, z = z} - local placeable = ownLevels ~= nil and {spec_silo = {storages = { - {getFillLevel = function(_, fillType) return ownLevels[fillType] or 0 end}, - }}} or nil - return { - owningPlaceable = placeable, - loadTriggers = {{triggerNode = node}}, - unloadTriggers = {{exactFillRootNode = node}}, - getFillLevel = function(_, fillType) - if isBroken then error("boom") end - return levels[fillType] or 0 - end, - getIsFillTypeSupported = function(_, fillType) return levels[fillType] ~= nil or (ownLevels ~= nil and ownLevels[fillType] ~= nil) end, - getFreeCapacity = function(_, fillType) return (levels.capacity or 200000) - (levels[fillType] or 0) end, - } -end - --- Aflæsningsmål (fx TMR-blander): ét lager med niveau + kapacitet pr. vare. -local function makeUnloadStation(node, x, z, levels, capacities, placeable, isBroken) - nodePositions[node] = {x = x, z = z} - local storage = { - getFillLevel = function(_, fillType) - if isBroken then error("boom") end - return levels[fillType] or 0 - end, - getFreeCapacity = function(_, fillType) return (capacities[fillType] or 0) - (levels[fillType] or 0) end, - } - return { - owningPlaceable = placeable, - unloadTriggers = {{exactFillRootNode = node}}, - targetStorages = {[storage] = storage}, - getIsFillTypeSupported = function(_, fillType) return capacities[fillType] ~= nil end, - } -end - -local stations, unloadStations = {}, {} -g_currentMission = { - storageSystem = { - getLoadingStations = function() return stations end, - getUnloadingStations = function() return unloadStations end, - }, - accessHandler = {canFarmAccess = function() return true end}, - getFarmId = function() return 1 end, -} - --- ---- AutoDrive-mocks ---- -local markers, wayPoints = {}, {} --- fromX/fromZ: forrige waypoint på ruten ind til markøren (giver ankomstretningen) -local function addMarker(index, name, group, x, z, fromX, fromZ) - wayPoints[index] = {x = x, z = z, incoming = {}} - if fromX ~= nil then - wayPoints[1000 + index] = {x = fromX, z = fromZ, incoming = {}} - wayPoints[index].incoming = {1000 + index} - end - table.insert(markers, {id = index, markerIndex = index, name = name, group = group}) -end - -local function resetWorld() - if ADSmartPickup ~= nil and ADSmartPickup.resetEnRoute ~= nil then ADSmartPickup.resetEnRoute() end - stations, unloadStations, markers, wayPoints, nodePositions, logLines = {}, {}, {}, {}, {}, {} - FS25_AutoDrive.ADMultipleTargetsManager.pickups = {} -end - -local RT_NONE, RT_ONLYPICKUP = 1, 2 -local testVehicles = {} -FS25_AutoDrive = { - AutoDrive = { - RT_NONE = RT_NONE, - getSetting = function(settingName) return settingName == "rotateTargets" and RT_ONLYPICKUP or "andet" 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, - getSupportedFillTypesOfAllUnitsAlphabetically = function(vehicle) return vehicle.carriable or {} end, - getAllVehicles = function() return testVehicles end, - }, - -- opgaven noterer hvad AutoDrive "ser" af indstillinger mens den opdaterer - LoadAtDestinationTask = {update = function(self) - self.seenRotate = FS25_AutoDrive.AutoDrive.getSetting("rotateTargets", self.vehicle) - self.seenOther = FS25_AutoDrive.AutoDrive.getSetting("useFolders", self.vehicle) - if self.shouldFail then error("task boom") end - 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 = { - getMapMarkersInGroup = function(_, group) - local result = {} - for _, m in ipairs(markers) do if m.group == group then table.insert(result, m) end end - return result - end, - getWayPointById = function(_, id) return wayPoints[id] end, - getMapMarkers = function() return markers end, - getMapMarkerByWayPointId = function(_, id) for _, m in ipairs(markers) do if m.id == id then return m end end end, - -- ruter: alt nåeligt medmindre testen sætter unreachable[id] (til eller fra) - unreachable = {}, - getPathTo = function(self, vehicle, id) if self.unreachable[id] then return {} end; return {{id = id}} end, - pathFromTo = function(self, fromId, toId) if self.unreachable[fromId] or self.unreachable[toId] then return {} end; return {{id = fromId}, {id = toId}} end, - }, -} - -local function makeDriver(firstMarker, fillTypes) - return { - getName = function() return "Testtraktor" end, - getOwnerFarmId = function() return 1 end, - ad = {stateModule = { - getFirstMarker = function() return firstMarker end, - getSecondMarker = function() return nil end, - getSelectedFillTypes = function() return fillTypes end, - getFillType = function() return fillTypes[1] end, - }}, - } -end - --- Køretøjet er "fremme ved markøren" medmindre testen siger andet. -local function withDrivePath(driver) - driver.targetReached = true - driver.ad.drivePathModule = {isTargetReached = function() return driver.targetReached end} - return driver -end - --- Muterbar stateModule-mock: behovsstyringen skifter vare og indsnævrer AD's valg. -local function makeSupplyDriver(firstMarker, secondMarker, fillTypes) - local stateModule = {selectedFillTypes = fillTypes, fillType = fillTypes[1], active = true, dirtyCount = 0} - stateModule.getFirstMarker = function() return firstMarker end - stateModule.getSecondMarker = function() return secondMarker end - stateModule.getSelectedFillTypes = function(self) return self.selectedFillTypes end - stateModule.getFillType = function(self) return self.fillType end - stateModule.setFillType = function(self, fillType) self.fillType = fillType end - stateModule.raiseDirtyFlag = function(self) self.dirtyCount = self.dirtyCount + 1 end - stateModule.isActive = function(self) return self.active end - return { - getName = function() return "Fodertraktor" end, - getOwnerFarmId = function() return 1 end, - ad = {stateModule = stateModule}, - } -end - -dofile("FS25_ADSmartPickup/adSupplyPlanner.lua") -dofile("FS25_ADSmartPickup/adRuns.lua") -dofile("FS25_ADSmartPickup/adRunsController.lua") -dofile("FS25_ADSmartPickup/adLoadSwap.lua") -dofile("FS25_ADSmartPickup/adWaitPool.lua") -dofile("FS25_ADSmartPickup/adUnloadWait.lua") -dofile("FS25_ADSmartPickup/adSmartPickup.lua") -listener:update(16) -- installerer hook'en -local manager = FS25_AutoDrive.ADMultipleTargetsManager - -local failures = 0 -local function check(name, actual, expected) - if actual == expected then - print("OK " .. name) - else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - --- 1: vælger mindst fyldte silo med varen; springer tomme og forkert vare over -resetWorld() -addMarker(1, "Silo 1", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[WHEAT] = 900000})) -addMarker(2, "Silo 2", "Siloer", 500, 0); table.insert(stations, makeStation(102, 500, 15, {[SUGARBEET] = 800000})) -addMarker(3, "Silo 3", "Siloer", 1000, 0); table.insert(stations, makeStation(103, 1000, 15, {[SUGARBEET] = 12000})) -addMarker(4, "Silo 4", "Siloer", 1500, 0); table.insert(stations, makeStation(104, 1500, 15, {[SUGARBEET] = 0})) -addMarker(5, "Fabrik", "Andet", 1000, 5); -- anden mappe, skal ignoreres -check("mindst fyldte med varen vælges", manager:getNextPickup(makeDriver(markers[1], {SUGARBEET}), false), 3) -check("AD-rotationstilstand opdateret", manager.pickups["Siloer"].lastTarget, 3) - --- 2: ingen silo har varen -> AutoDrives originale rotation -check("fallback når varen ikke findes", manager:getNextPickup(makeDriver(markers[1], {99}), false), "ORIGINAL") - --- 3: markør uden silo i nærheden ignoreres -resetWorld() -addMarker(1, "Langt væk", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 200, {[SUGARBEET] = 5})) -addMarker(2, "Silo 2", "Siloer", 500, 0); table.insert(stations, makeStation(102, 500, 15, {[SUGARBEET] = 700})) -check("markør uden nær silo ignoreres", manager:getNextPickup(makeDriver(markers[1], {SUGARBEET}), false), 2) - --- 4: flere valgte varer lægges sammen -resetWorld() --- summeret er silo 1 rigelig (6000 >= 5000) og vinder; uden summering var den 3000 og taber til silo 2's 4000 -addMarker(1, "Silo 1", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[SUGARBEET] = 3000, [WHEAT] = 3000})) -addMarker(2, "Silo 2", "Siloer", 500, 0); table.insert(stations, makeStation(102, 500, 15, {[SUGARBEET] = 4000})) -check("flere varetyper summeres", manager:getNextPickup(makeDriver(markers[1], {SUGARBEET, WHEAT}), false), 1) - --- 4b: klynge med delt lager — eget indhold slår nabo-sum, også når nabo-summen er mindre -resetWorld() -addMarker(1, "Nabo", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[SUGARBEET] = 800}, false, {})) -addMarker(2, "Ejer stor", "Siloer", 14, 0); table.insert(stations, makeStation(102, 14, 15, {[SUGARBEET] = 900000}, false, {[SUGARBEET] = 500000})) -addMarker(3, "Ejer lille", "Siloer", 28, 0);table.insert(stations, makeStation(103, 28, 15, {[SUGARBEET] = 900000}, false, {[SUGARBEET] = 20000})) -check("eget lager foran nabo-lån, mindst først", manager:getNextPickup(makeDriver(markers[1], {SUGARBEET}), false), 3) - --- 4c: kun nabo-lån muligt -> bruges stadig -resetWorld() -addMarker(1, "Nabo", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[SUGARBEET] = 800}, false, {})) -check("nabo-lån bruges når ingen ejer varen", manager:getNextPickup(makeDriver(markers[1], {SUGARBEET}), false), 1) - --- 5: fejl i spil-API -> fallback + advarsel, aldrig crash -resetWorld() -addMarker(1, "Silo 1", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, {}, true)) -check("fejl giver fallback", manager:getNextPickup(makeDriver(markers[1], {SUGARBEET}), false), "ORIGINAL") -check("fejl logges som advarsel", logLines[#logLines]:sub(1, 4), "WARN") - --- 6: gruppen "All" og manglende første markør røres ikke -check("gruppe All -> fallback", manager:getNextPickup(makeDriver({group = "All", markerIndex = 1}, {SUGARBEET}), false), "ORIGINAL") -check("ingen første markør -> fallback", manager:getNextPickup(makeDriver(nil, {SUGARBEET}), false), "ORIGINAL") - --- ==== Behovsstyret forsyning (flere varer valgt + aflæsningsmål med lager) ==== -local BLANDER = {name = "TMR-blander placeable"} -local FULL = {[HAY] = 100000, [GRASS] = 100000, [SILAGE] = 100000} -local blanderLevels - -local function buildFeedWorld() - resetWorld() - blanderLevels = {[HAY] = 80000, [GRASS] = 20000, [SILAGE] = 50000} - addMarker(1, "Hølade", "Lader", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[HAY] = 50000})) - addMarker(2, "Græssilo stor", "Siloer", 500, 0); table.insert(stations, makeStation(102, 500, 15, {[GRASS] = 300000})) - addMarker(3, "Græssilo lille", "Andet", 1000, 0);table.insert(stations, makeStation(103, 1000, 15, {[GRASS] = 40000})) - addMarker(4, "Ensilagesilo", "Siloer", 1500, 0); table.insert(stations, makeStation(104, 1500, 15, {[SILAGE] = 200000})) - addMarker(10, "TMR-blander", "Produktion", 5000, 0) - table.insert(unloadStations, makeUnloadStation(110, 5000, 10, blanderLevels, FULL, BLANDER)) -end -local function markerByIndex(index) - for _, m in ipairs(markers) do if m.markerIndex == index then return m end end -end - --- S1: mest trængende ingrediens vælges, kilde findes på tværs af mapper, AD's vare skiftes -buildFeedWorld() -local feeder = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS, SILAGE}) -check("S1 mest trængende vare, mindst fyldte kilde", manager:getNextPickup(feeder, false), 3) -check("S1 AD's vare skiftet til græs", feeder.ad.stateModule.fillType, GRASS) -check("S1 AD's valg indsnævret til én vare", #feeder.ad.stateModule.selectedFillTypes, 1) -check("S1 ændring synkroniseres (dirty-flag)", feeder.ad.stateModule.dirtyCount > 0, true) -check("S1 rotationstilstand følger valgt markørs mappe", manager.pickups["Andet"].lastTarget, 3) - --- S2: næste tur — gemt liste genbruges; græs er nu fuldt og springes over -> ensilage -blanderLevels[GRASS] = 99500 -check("S2 fuld ingrediens springes over", manager:getNextPickup(feeder, false), 4) -check("S2 vare skiftet til ensilage", feeder.ad.stateModule.fillType, SILAGE) - --- S3: ingrediens uden kilde springes over -> næste i behovs-rækkefølgen -blanderLevels[SILAGE] = 10000 -stations[4] = makeStation(104, 1500, 15, {[SILAGE] = 0}) -check("S3 ingen kilde -> næste ingrediens", manager:getNextPickup(feeder, false), 1) -check("S3 vare skiftet til hø", feeder.ad.stateModule.fillType, HAY) - --- S4: AD stopper -> hele ingredienslisten gendannes i AD's varevalg -feeder.ad.stateModule.active = false -listener:update(16) -check("S4 valg gendannet ved stop", #feeder.ad.stateModule.selectedFillTypes, 3) - --- S5: aflæsningsmålets egen læssestation bruges ikke som kilde -buildFeedWorld() -addMarker(11, "Blander udtag", "Produktion", 5030, 0) -table.insert(stations, makeStation(111, 5030, 15, {[GRASS] = 10}, false, nil)) -stations[#stations].owningPlaceable = BLANDER -feeder = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS}) -check("S5 målets egen station udelukkes som kilde", manager:getNextPickup(feeder, false), 3) - --- S6: brugeren vælger selv en anden enkelt vare -> behovsstyring fra (mappe-logik) -feeder.ad.stateModule.selectedFillTypes = {HAY} -feeder.ad.stateModule.fillType = HAY -check("S6 brugerens enkelt-valg -> mappe-logik", manager:getNextPickup(feeder, false), 1) -check("S6 vare røres ikke", feeder.ad.stateModule.fillType, HAY) - --- S7: intet behov (alt fuldt) -> mappe-logik, valg indsnævres ikke -buildFeedWorld() -blanderLevels[HAY], blanderLevels[GRASS], blanderLevels[SILAGE] = 99900, 99900, 99900 -feeder = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS, SILAGE}) -check("S7 intet behov -> mappe-logik", manager:getNextPickup(feeder, false), 1) -check("S7 valg ikke indsnævret", #feeder.ad.stateModule.selectedFillTypes, 3) - --- S8: ingen aflæsningsstation ved målet -> mappe-logik -buildFeedWorld() -unloadStations = {} -feeder = makeSupplyDriver(markerByIndex(2), markerByIndex(10), {GRASS, SILAGE}) -check("S8 ingen station ved mål -> mappe-logik", manager:getNextPickup(feeder, false), 4) - --- S9: fejl i målets lager-API -> AutoDrives original + advarsel -buildFeedWorld() -unloadStations = {makeUnloadStation(110, 5000, 10, blanderLevels, FULL, BLANDER, true)} -feeder = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS}) -check("S9 fejl giver fallback", manager:getNextPickup(feeder, false), "ORIGINAL") -check("S9 fejl logges som advarsel", logLines[#logLines]:sub(1, 4), "WARN") - --- S10: stationen eksponerer ikke targetStorages -> lageret læses fra produktionspunktet -buildFeedWorld() -local bareStation = unloadStations[1] -local productionStorage = next(bareStation.targetStorages) -bareStation.targetStorages = nil -bareStation.owningPlaceable = {spec_productionPoint = {productionPoint = {storage = productionStorage}}} -feeder = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS, SILAGE}) -check("S10 lager læses fra produktionspunktet", manager:getNextPickup(feeder, false), 3) - --- ==== Fabriksstyret forsyning (aflæsningsmålet er et produktionspunkt) ==== -ProductionPoint = {PROD_STATUS = {INACTIVE = 0, RUNNING = 1, MISSING_INPUTS = 2, NO_OUTPUT_SPACE = 3}} -local WATER = 40 -FILL_TYPE_NAMES[WATER] = "WATER" - --- Fabrik med TMR-opskrift: sluger ensilage, nipper til hø. Vand kan vognen ikke bære. -local function buildFactoryWorld(tmrEnabled, tmrStatus) - buildFeedWorld() - local pp = { - enabled = {tmr = tmrEnabled}, - productions = { - {id = "tmr", cyclesPerMinute = 1, status = tmrStatus, inputs = {{type = SILAGE, amount = 250}, {type = HAY, amount = 10}, {type = WATER, amount = 1000}}}, - {id = "off", cyclesPerMinute = 1, status = ProductionPoint.PROD_STATUS.INACTIVE, inputs = {{type = GRASS, amount = 500}}}, - }, - getIsProductionEnabled = function(self, id) return self.enabled[id] == true end, - } - local factory = {spec_productionPoint = {productionPoint = pp}, getName = function() return "AIO" end} - unloadStations = {makeUnloadStation(110, 5000, 10, blanderLevels, {[HAY] = 100000, [GRASS] = 100000, [SILAGE] = 100000, [WATER] = 100000}, factory)} - local driver = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS}) - driver.carriable = {HAY, GRASS, SILAGE} - return driver, pp -end - --- F1: tændt produktion styrer listen: ensilage (200 min til tom) vinder over hø (8000 min) --- og over græs, som er relativt tommest men ikke et input i fabrikken -local factoryDriver = buildFactoryWorld(true, ProductionPoint.PROD_STATUS.RUNNING) -check("F1 fabrikkens input med kortest tid til tom", manager:getNextPickup(factoryDriver, false), 4) -check("F1 AD's vare skiftet til ensilage (ikke i brugerens valg)", factoryDriver.ad.stateModule.fillType, SILAGE) -check("F1 log nævner minutter til tom", logLines[#logLines]:find("min til tom", 1, true) ~= nil, true) - --- F2: næste tur — listen genudledes fra fabrikken, ikke fra brugerens valg -blanderLevels[SILAGE] = 99500 -check("F2 fuld ensilage -> hø (næste fabriks-input)", manager:getNextPickup(factoryDriver, false), 1) - --- F3: AD stopper -> brugerens OPRINDELIGE valg gendannes, ikke fabrikkens liste -factoryDriver.ad.stateModule.active = false -listener:update(16) -check("F3 brugerens valg gendannet ved stop", #factoryDriver.ad.stateModule.selectedFillTypes, 2) - --- F4: manuelt slukket produktion -> fallback til brugerens valg (græs er tommest) -factoryDriver = buildFactoryWorld(false, ProductionPoint.PROD_STATUS.INACTIVE) -local function countLogsSince(startIndex, needle) - local count = 0 - for i = startIndex + 1, #logLines do - if logLines[i]:find(needle, 1, true) then count = count + 1 end - end - return count -end -local logMark = #logLines -check("F4 slukket fabrik -> brugerens valg", manager:getNextPickup(factoryDriver, false), 3) -check("F4 loggen forklarer fallback", countLogsSince(logMark, "ingen ønskede produktioner"), 1) -manager:getNextPickup(factoryDriver, false) -check("F4 forklaringen logges kun én gang", countLogsSince(logMark, "ingen ønskede produktioner"), 1) - --- F5: auto-slukket af kortet pga. manglende input -> stadig fabrikkens liste -factoryDriver = buildFactoryWorld(false, ProductionPoint.PROD_STATUS.MISSING_INPUTS) -check("F5 auto-slukket fabrik fodres stadig", manager:getNextPickup(factoryDriver, false), 4) - --- F6: produktionspunkt uden getIsProductionEnabled -> activeProductions-listen bruges -local fallbackDriver, fallbackPp = buildFactoryWorld(false, ProductionPoint.PROD_STATUS.RUNNING) -fallbackPp.getIsProductionEnabled = nil -fallbackPp.activeProductions = {fallbackPp.productions[1]} -check("F6 activeProductions som fallback", manager:getNextPickup(fallbackDriver, false), 4) - --- ==== En fabriks inputs stjæles aldrig (halm+TMR til kostald; TMR-fabrikken har halm som input) ==== -local STRAW, TMR = 41, 42 -FILL_TYPE_NAMES[STRAW], FILL_TYPE_NAMES[TMR] = "STRAW", "FORAGE" -local cowLevels -local function buildCowWorld() - resetWorld() - local tmrFactory = { - getName = function() return "TMR-fabrik" end, - spec_productionPoint = {productionPoint = {productions = { - {id = "tmr", cyclesPerMinute = 1, inputs = {{type = STRAW, amount = 10}, {type = SILAGE, amount = 100}}, outputs = {{type = TMR, amount = 50}}}, - }}}, - } - -- fabrikkens læssestation har BÅDE halm (input, mindst fyldt) og TMR (output) - addMarker(20, "TMR Load", "Fabrication", 0, 0) - table.insert(stations, makeStation(120, 0, 15, {[STRAW] = 5000, [TMR] = 30000})) - stations[#stations].owningPlaceable = tmrFactory - addMarker(21, "Halmlade", "Lader", 500, 0) - table.insert(stations, makeStation(121, 500, 15, {[STRAW] = 50000})) - addMarker(30, "Cow 1 Food", "Cow Food", 5000, 0) - cowLevels = {[STRAW] = 0, [TMR] = 40000} - table.insert(unloadStations, makeUnloadStation(130, 5000, 10, cowLevels, {[STRAW] = 100000, [TMR] = 100000}, {name = "kostald"})) - local driver = makeSupplyDriver(markerByIndex(21), markerByIndex(30), {TMR, STRAW}) - driver.carriable = {TMR, STRAW} - return driver -end - --- P0: en STALDS læssestation (gylle/mælk) er aldrig kilde for halm/foder, selv om den "har" halm --- (in-game 24/9: TMR- og Cow Food-traktorer skulle hente halm ved 'Pig Slurry 1') -do - local cowDriverP0 = buildCowWorld() - addMarker(22, "Pig Slurry 1", "Pig Slurry", 400, 0) - table.insert(stations, makeStation(122, 400, 15, {[STRAW] = 1000, [LIQUIDMANURE or 99] = 5000})) - stations[#stations].owningPlaceable = {getHusbandryFillLevel = function() return 0 end, getName = function() return "Svinesti" end} - check("P0 halm hentes ikke fra svinestiens gyllestation", manager:getNextPickup(cowDriverP0, false), 21) -end - --- P1: halm mangler mest -> hentes fra halmladen, IKKE fra fabrikken (selv om den er mindst fyldt) -local cowDriver = buildCowWorld() -check("P1 halm hentes fra siloen, ikke fabrikkens input", manager:getNextPickup(cowDriver, false), 21) - --- P2: halm fuld -> TMR hentes gerne fra fabrikken (det er dens output) -cowLevels[STRAW] = 99500 -check("P2 fabrikkens output må hentes", manager:getNextPickup(cowDriver, false), 20) - --- P3: mappe-logik (én vare) beskytter også fabrikkens input -cowDriver = buildCowWorld() -addMarker(22, "Halmlade 2", "Fabrication", 1000, 0) -table.insert(stations, makeStation(122, 1000, 15, {[STRAW] = 60000})) -local strawDriver = makeDriver(markerByIndex(20), {STRAW}) -check("P3 mappe-logik springer fabrikkens input over", manager:getNextPickup(strawDriver, false), 22) - --- P4: stald som mål — niveau/kapacitet læses fra stalden selv (getHusbandry*), ikke fra --- stationens lagre; stationen selv "understøtter" kun foder, men stalden tager også halm -cowDriver = buildCowWorld() -local barnLevels = {[STRAW] = 45000, [TMR] = 10000} -local barnCapacity = {[STRAW] = 50000, [TMR] = 100000} -local barn = { - getName = function() return "Kostald" end, - getHusbandryFillLevel = function(_, fillType) return barnLevels[fillType] or 0 end, - getHusbandryCapacity = function(_, fillType) return barnCapacity[fillType] or 0 end, - getHusbandryFreeCapacity = function(_, fillType) return (barnCapacity[fillType] or 0) - (barnLevels[fillType] or 0) end, - getHusbandryIsFillTypeSupported = function(_, fillType) return barnCapacity[fillType] ~= nil end, -} -local barnStation = unloadStations[1] -barnStation.targetStorages = nil -barnStation.owningPlaceable = barn -barnStation.getIsFillTypeSupported = function(_, fillType) return fillType == TMR end -barnStation.getFreeCapacity = function(_, fillType) return 999999 end -check("P4 stald: TMR (90 % tom) hentes før halm (10 % tom)", manager:getNextPickup(cowDriver, false), 20) -barnLevels[TMR] = 95000 -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 waitVehicles = {} -local function buildWaitWorld() - -- tidligere testkøretøjer slipper deres Wait-punkter (AD "stoppes" på dem) - for _, vehicle in ipairs(waitVehicles) do vehicle.ad.stateModule.active = false end - listener:update(16) - waitVehicles = {} - 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 Wait", 5100, 0) - addMarker(41, "Wait 2", "Cow Wait", 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.ad.stateModule.secondMarkerId = 30 - vehicle.ad.stateModule.setSecondMarkerByWayPointId = function(self, id) self.secondMarkerId = id end - vehicle.ad.stateModule.getSecondMarker = function(self) return markerByIndex(self.secondMarkerId) end - 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} - table.insert(waitVehicles, vehicle) - 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: to andre holder begge punkter; stoppes de i AD, frigives punkterne -buildWaitWorld() -local vA, tA = makeUnloadingVehicle("Fastrac A2", 521, 5000, 35000) -local vB, tB = makeUnloadingVehicle("Fastrac B2", 522, 5000, 35000) -UnloadTask.update(tA, 16); UnloadTask.update(tB, 16) -local v8, t8 = makeUnloadingVehicle("Fastrac F", 508, 5000, 35000) -UnloadTask.update(t8, 16) -check("W8 alle Wait-punkter optaget -> uændret", t8.destinationID, 30) -vA.ad.stateModule.active, vB.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) - --- W9: intet vejnet til nærmeste Wait -> næste; intet net til nogen -> uændret (AD slukker ellers sig selv) -buildWaitWorld() -FS25_AutoDrive.ADGraphManager.unreachable = {[40] = true} -local v10, t10 = makeUnloadingVehicle("Fastrac H", 510, 5000, 35000) -UnloadTask.update(t10, 16) -check("W9 unåeligt Wait 1 -> Wait 2", t10.destinationID, 41) -FS25_AutoDrive.ADGraphManager.unreachable = {[40] = true, [41] = true} -local v11, t11 = makeUnloadingVehicle("Fastrac I", 511, 5000, 35000) -local unreachableMark = #logLines -UnloadTask.update(t11, 16) -check("W9 intet nåeligt -> uændret mål", t11.destinationID, 30) -check("W9 advarsel om manglende vejnet", countLogsSince(unreachableMark, "WARN"), 1) -FS25_AutoDrive.ADGraphManager.unreachable = {} - --- W10: kæmpe vogn (700.000 l rest) i 50.000 l-stald -> hjem når halvdelen af stalden er fri -buildWaitWorld() -local v12, t12 = makeUnloadingVehicle("Fastrac J", 512, 5000, 700000) -UnloadTask.update(t12, 16) -check("W10 venter", t12.destinationID, 40) -waitBarnLevels[STRAW] = 30000 -- 20000 fri < 25000 -UnloadTask.update(t12, 16) -check("W10 under halv stald fri -> venter stadig", t12.destinationID, 40) -waitBarnLevels[STRAW] = 20000 -- 30000 fri >= 25000 -UnloadTask.update(t12, 16) -check("W10 halv stald fri -> hjem", t12.destinationID, 30) - --- L1: moden valgte siloen -> AutoDrive skal stå og læsse (rotation skjules under opgaven) -resetWorld() -local loadLevels = {[SUGARBEET] = 5000} -addMarker(1, "Silo 1", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, loadLevels)) -local loader = withDrivePath(makeDriver(markers[1], {SUGARBEET})) -manager:getNextPickup(loader, false) -local task = {vehicle = loader, trailers = {level = 0}} -FS25_AutoDrive.LoadAtDestinationTask.update(task, 16) -check("L1 rotation skjult mens kilden har varen", task.seenRotate, RT_NONE) -check("L1 andre indstillinger uberørt", task.seenOther, "andet") -check("L1 rotation synlig igen efter opgaven", FS25_AutoDrive.AutoDrive.getSetting("rotateTargets", loader), RT_ONLYPICKUP) - --- L8: PÅ VEJ til siloen ruller vognen gennem nabosiloers triggere -> dér skal --- AutoDrives rotation gælde (kør videre ved tom trigger), ikke "stå og vent" -loader.targetReached = false -FS25_AutoDrive.LoadAtDestinationTask.update(task, 16) -check("L8 undervejs: rotation uberørt", task.seenRotate, RT_ONLYPICKUP) -loader.targetReached = true -FS25_AutoDrive.LoadAtDestinationTask.update(task, 16) -check("L8 fremme ved markøren: står og læsser", task.seenRotate, RT_NONE) - --- L2: andre køretøjer (ikke sendt af moden) beholder AutoDrives rotation -local strangerTask = {vehicle = makeDriver(markers[1], {SUGARBEET})} -FS25_AutoDrive.LoadAtDestinationTask.update(strangerTask, 16) -check("L2 fremmed køretøj uberørt", strangerTask.seenRotate, RT_ONLYPICKUP) - --- L3: kilden løber tør -> grebet slippes, AutoDrive kører videre med det den har -loadLevels[SUGARBEET] = 0 -FS25_AutoDrive.LoadAtDestinationTask.update(task, 16) -check("L3 tom kilde -> rotation synlig igen", task.seenRotate, RT_ONLYPICKUP) - --- L4: fejl i AutoDrives opgave må ikke efterlade rotationen skjult -loadLevels[SUGARBEET] = 5000 -task.shouldFail = true -check("L4 fejl i opgaven sendes videre", pcall(FS25_AutoDrive.LoadAtDestinationTask.update, task, 16), false) -check("L4 rotation synlig efter fejl", FS25_AutoDrive.AutoDrive.getSetting("rotateTargets", loader), RT_ONLYPICKUP) - --- L5: AutoDrives original valgte (intet fund) -> ingen ventetid -task.shouldFail = false -loader.ad.stateModule.getSelectedFillTypes = function() return {WHEAT} end -manager:getNextPickup(loader, false) -FS25_AutoDrive.LoadAtDestinationTask.update(task, 16) -check("L5 AutoDrives eget valg -> rotation uberørt", task.seenRotate, RT_ONLYPICKUP) - --- G1: tæt silo-række, kørsel mod -x. Næsen stopper ved markøren, vognen holder et --- vogntog BAGVED -> markørens silo er den bag næsen, ikke den (nærmere) foran. -resetWorld() -addMarker(1, "1 Silo", "Siloer", 1243, 0, 1260, 0) -addMarker(2, "2 Silo", "Siloer", 1228, 0, 1243, 0) -table.insert(stations, makeStation(101, 1255, 1, {[HAY] = 9000}, false, {[HAY] = 9000})) -- bag markør 1 -table.insert(stations, makeStation(102, 1239, 1, {[SUGARBEET] = 5000}, false, {[SUGARBEET] = 5000})) -- foran markør 1, bag markør 2 -table.insert(stations, makeStation(103, 1224, 1, {}, false, {})) -- foran markør 2 -check("G1 roer ligger i siloen bag markør 2", manager:getNextPickup(makeDriver(markers[1], {SUGARBEET}), false), 2) -check("G1 hø ligger i siloen bag markør 1", manager:getNextPickup(makeDriver(markers[1], {HAY}), false), 1) - --- G2: ingen trigger bag næsen (eller ukendt retning) -> nærmeste trigger som før -resetWorld() -addMarker(1, "Ensom silo", "Siloer", 100, 0, 120, 0) -table.insert(stations, makeStation(101, 95, 1, {[HAY] = 9000})) -check("G2 kun trigger foran -> nærmeste bruges", manager:getNextPickup(makeDriver(markers[1], {HAY}), false), 1) - --- G3: tank med aflæsningsbane på den ene side og pålæsningsbane på den anden. --- Dropoff-markøren ligger 18 m ved siden af læsserøret -> ikke en pålæsningsplads, --- selv om den står først i listen og "ser" samme tank. -resetWorld() -addMarker(1, "1 Silage Dropof", "Ensilage", 1265, -2278, 1265, -2290) -- kører +z, røret 18 m til siden -addMarker(2, "1 Silage Pickup", "Ensilage", 1247, -2278, 1247, -2266) -- kører -z, røret 7 m bag næsen -table.insert(stations, makeStation(101, 1247, -2271, {[SILAGE] = 4000})) -check("G3 pickup-markøren vælges, ikke dropoff", manager:getNextPickup(makeDriver(markers[1], {SILAGE}), false), 2) - --- G5: markør-drift i en tank-række — næste tanks rør ligger 2 m FORAN næsen og er --- nærmest, men vognen holder under røret 11 m bagved -resetWorld() -addMarker(1, "5 Silage Pickup", "Ensilage", 1246, -2218, 1246, -2230) -- kører +z -table.insert(stations, makeStation(101, 1246, -2216, {[SILAGE] = 9000})) -- 2 m foran næsen -table.insert(stations, makeStation(102, 1246, -2229, {[HAY] = 4000})) -- 11 m bag næsen: kun den har hø -addMarker(2, "6 Silage Pickup", "Ensilage", 1246, -2208, 1246, -2218) -check("G5 markør 5 hører til tanken bag næsen", manager:getNextPickup(makeDriver(markers[1], {HAY}), false), 1) - --- G4: kurve ind til markøren -> retningen snyder og røret ligger "foran"; tæt på = ok -resetWorld() -addMarker(1, "7 Silage Pickup", "Ensilage", 100, 0, 100, 20) -- kører -z -table.insert(stations, makeStation(101, 103, -8, {[SILAGE] = 4000})) -- 8 m foran, 3 m til siden -check("G4 tæt trigger foran næsen bruges som nødløsning", manager:getNextPickup(makeDriver(markers[1], {SILAGE}), false), 1) - --- L9: sikkerhedsventil — står den ved markøren uden at vognen fyldes, slippes grebet -resetWorld() -addMarker(1, "Silo 1", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[SUGARBEET] = 5000})) -local stuck = withDrivePath(makeDriver(markers[1], {SUGARBEET})) -manager:getNextPickup(stuck, false) -local stuckTask = {vehicle = stuck, trailers = {level = 0}} -FS25_AutoDrive.LoadAtDestinationTask.update(stuckTask, 10000) -stuckTask.trailers.level = 800 -- der læsses: uret nulstilles -FS25_AutoDrive.LoadAtDestinationTask.update(stuckTask, 25000) -FS25_AutoDrive.LoadAtDestinationTask.update(stuckTask, 25000) -check("L9 venter stadig mens der er læsset for nylig", stuckTask.seenRotate, RT_NONE) -FS25_AutoDrive.LoadAtDestinationTask.update(stuckTask, 10000) -- 35 s uden fremgang -check("L9 ingen læsning i 30 s -> greb sluppet", stuckTask.seenRotate, RT_ONLYPICKUP) -check("L9 sluppet greb logges som advarsel", logLines[#logLines]:sub(1, 4), "WARN") -FS25_AutoDrive.LoadAtDestinationTask.update(stuckTask, 16) -check("L9 grebet tages ikke igen på samme tur", stuckTask.seenRotate, RT_ONLYPICKUP) - --- M1: mælketanke løber fulde -> den FYLDESTE hentes først (siloer: mindst fyldte) -resetWorld() -addMarker(1, "Kostald 1", "Mælk", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[MILK] = 2000})) -addMarker(2, "Kostald 2", "Mælk", 500, 0); table.insert(stations, makeStation(102, 500, 15, {[MILK] = 48000})) -addMarker(3, "Kostald 3", "Mælk", 1000, 0); table.insert(stations, makeStation(103, 1000, 15, {[MILK] = 30000})) -check("M1 fyldeste mælketank først", manager:getNextPickup(makeDriver(markers[1], {MILK}), false), 2) -check("M1 loggen nævner strategien", logLines[#logLines]:find("fyldeste først", 1, true) ~= nil, true) - --- L7: AD stopper -> grebet ryddes, næste opgave venter ikke på en gammel silo -resetWorld() -addMarker(1, "Silo 1", "Andet", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[HAY] = 5000})) -addMarker(2, "Blander", "Fabrik", 5000, 0) -table.insert(unloadStations, makeUnloadStation(201, 5000, 15, {[HAY] = 0}, {[HAY] = 1000000, [GRASS] = 1000000}, {})) -local stopper = withDrivePath(makeSupplyDriver(markers[1], markers[2], {HAY, GRASS})) -manager:getNextPickup(stopper, false) -local stopperTask = {vehicle = stopper, trailers = {level = 0}} -FS25_AutoDrive.LoadAtDestinationTask.update(stopperTask, 16) -check("L7 venter mens AD kører", stopperTask.seenRotate, RT_NONE) -stopper.ad.stateModule.active = false -listener:update(16) -FS25_AutoDrive.LoadAtDestinationTask.update(stopperTask, 16) -check("L7 greb ryddet når AD stopper", stopperTask.seenRotate, RT_ONLYPICKUP) - --- W11: reservationer kan aflæses (GUI'ets Wait-puljer-visning) -buildWaitWorld() -local vc1, tc1 = makeUnloadingVehicle("Fastrac Claim", 531, 5000, 35000) -UnloadTask.update(tc1, 16) -local claims = ADUnloadWait.getClaims() -check("W11 én reservation", #claims, 1) -check("W11 markør", claims[1].marker.id, 40) -check("W11 køretøj", claims[1].vehicle, vc1) - - --- W12: AD regner vognen som "aflæssende" ved et fuldt trug (isUnloading bliver hængende) — --- omdirigeres alligevel, og AD's aflæsning stoppes først -buildWaitWorld() -local vs, ts = makeUnloadingVehicle("Fastrac Tippet", 541, 5000, 35000) -local stopped = false -vs.ad.trailerModule = {isActiveAtTrigger = function() return true end, stopUnloading = function() stopped = true end, stopLoading = function() end} -UnloadTask.update(ts, 16) -check("W12 omdirigeret trods isActiveAtTrigger", ts.destinationID, 40) -check("W12 AD's aflæsning stoppet før kørsel", stopped, true) - --- E5: fodertrug = én trang; inden for truget vælges efter spillets fodervægt (TMR 1,0 > hø 0,8 > græs 0,4); --- et tommere halmlager vinder over truget -buildCowWorld() -addMarker(23, "Høsilo", "Lader", 700, 0); table.insert(stations, makeStation(123, 700, 15, {[HAY] = 80000}, false, {[HAY] = 80000})) -addMarker(24, "Græssilo", "Lader", 800, 0); table.insert(stations, makeStation(124, 800, 15, {[GRASS] = 80000}, false, {[GRASS] = 80000})) -local e5Food = {[TMR] = 0, [HAY] = 0, [GRASS] = 0} -local e5Straw = {level = 20000} -local e5Barn = { - getName = function() return "Prioritets-stald" end, - spec_husbandryFood = {fillLevels = e5Food, capacity = 30000, animalTypeIndex = 7}, - spec_husbandryStraw = {}, - getHusbandryIsFillTypeSupported = function(_, fillType) return fillType == STRAW end, - getHusbandryFillLevel = function(_, fillType) if fillType == STRAW then return e5Straw.level end return 0 end, - getHusbandryCapacity = function(_, fillType) if fillType == STRAW then return 100000 end return 0 end, -} -g_currentMission.animalFoodSystem = { - getAnimalFood = function(_, animalTypeIndex) if animalTypeIndex == 7 then return {groups = { - {fillTypes = {TMR}, productionWeight = 1.0}, {fillTypes = {HAY}, productionWeight = 0.8}, {fillTypes = {GRASS}, productionWeight = 0.4}}} end end, - getMixturesByAnimalTypeIndex = function() return {} end, -} -unloadStations[1].targetStorages = nil -unloadStations[1].owningPlaceable = e5Barn -local e5Driver = makeSupplyDriver(markerByIndex(21), markerByIndex(30), {TMR, HAY, GRASS, STRAW}) -e5Driver.carriable = {TMR, HAY, GRASS, STRAW} -e5Driver.trailers = {level = 0, capacity = 8000} --- trug tomt (100 % trang) vs. halm 80 % tomt -> truget først, og TMR før hø/græs -check("E5 tomt trug: TMR først", manager:getNextPickup(e5Driver, false), 20) --- ingen TMR-kilde -> hø (0,8) før græs (0,4) -stations[1].getFillLevel = function() return 0 end -stations[1].owningPlaceable = nil -check("E5 uden TMR-kilde: hø før græs", manager:getNextPickup(e5Driver, false), 23) --- trug 70 % fuldt (30 % trang) vs. halm 80 % tomt -> halm -e5Food[HAY] = 21000 -check("E5 halmlager tommere end truget -> halm", manager:getNextPickup(e5Driver, false), 21) --- E6 (bedste foder først): TMR-kilden er tom, truget halvfuldt, halm fuld -> INGEN ensilage/hø; en traktor --- i et forsynings-run holder ved Wait i stedet (in-game 24/9: Fastrac'erne fyldte truget med ensilage) -e5Straw.level = 100000 -e5Food[TMR], e5Food[HAY], e5Food[GRASS] = 0, 15000, 0 -- 50 % fri -e5Driver.uniqueId = "vehicleE6" -testVehicles = {e5Driver} -ADRunsController.store = ADRuns.newStore() -local e6Run = ADRunsController.createRun("Cow Food") -ADRunsController.updateRun(e6Run.id, {targetWayPointId = 30, ingredientMode = "manual", ingredients = {"FORAGE", "DRYGRASS_WINDROW", "GRASS_WINDROW", "STRAW"}}) -ADRunsController.assignVehicle(e6Run.id, e5Driver) -local holdCalls = {} -ADOutbound = { - isOutboundVehicle = function() return false end, - holdSupplyVehicle = function(adEnv, vehicle, run, reasons, replan) table.insert(holdCalls, {reasons = reasons, replan = replan}) return {markerIndex = 99, markerName = "Cow Wait", group = "Cow Wait", fillTypes = {}} end, - releaseInactive = function() end, install = function() end, -} -check("E6 halvfuldt trug uden TMR: hold ved Wait", manager:getNextPickup(e5Driver, false), 99) -check("E6 hold kaldt én gang", #holdCalls, 1) -check("E6 replan finder intet endnu", holdCalls[1].replan(), nil) -e5Food[HAY] = 3000 -- 90 % fri: nødlidende -> hø tilladt -check("E6 replan: nødlidende trug -> hø", holdCalls[1].replan(), 23) -check("E6 nødlidende trug: hø direkte", manager:getNextPickup(e5Driver, false), 23) -ADOutbound = nil -ADRunsController.store = ADRuns.newStore() -testVehicles = {} --- E7 (parallelt foder, høns): korn og roesnitter spises SAMTIDIG (80 %/20 %) — truget har sin kornandel, --- mangler roesnitter -> de hentes, "bedste foder først" spærrer ikke (in-game 24/9: kun 70 % produktion) -g_currentMission.animalFoodSystem = { - getAnimalFood = function(_, animalTypeIndex) if animalTypeIndex == 7 then return {consumptionType = "PARALLEL", groups = { - {fillTypes = {TMR}, productionWeight = 0.7, eatWeight = 0.8}, {fillTypes = {HAY}, productionWeight = 0.3, eatWeight = 0.2}}} end end, - getMixturesByAnimalTypeIndex = function() return {} end, -} -e5Straw.level = 100000 -e5Food[TMR], e5Food[HAY], e5Food[GRASS] = 24000, 0, 0 -- kornandel (80 % af 30000) fuld, 20 % fri -stations[1].getFillLevel = function(_, fillType) return fillType == TMR and 30000 or 0 end -- korn findes: serielt ville det vælges -check("E7 parallelt: roesnitter hentes selv om truget kun er 20 % tomt", manager:getNextPickup(e5Driver, false), 23) -e5Food[TMR] = 30000 -- truget helt fuldt af korn: ingen plads til snitter -> intet behov for dem -local hayLevel, hayCapacity = ADSmartPickup.getStationLevelAndCapacity(unloadStations[1], HAY, 1) -check("E7 fuldt trug: snitternes plads = 0", hayCapacity - hayLevel, 0) -g_currentMission.animalFoodSystem = nil - --- W13: AD stopper vognen i triggeren FØR markøren (isTargetReached=false) — Wait udløses alligevel --- når vognen er inden for AD's trigger-afstand til aflæsningsstedet -buildWaitWorld() -local vn, tn = makeUnloadingVehicle("Fastrac Før markør", 581, 5000, 35000) -vn.targetReached = false -FS25_AutoDrive.AutoDrive.getDistanceToUnloadPosition = function() return 8.6 end -FS25_AutoDrive.AutoDrive.getMaxTriggerDistance = function() return 15 end -UnloadTask.update(tn, 16) -check("W13 Wait selv om markøren ikke er nået", tn.destinationID, 40) -FS25_AutoDrive.AutoDrive.getDistanceToUnloadPosition = nil -FS25_AutoDrive.AutoDrive.getMaxTriggerDistance = nil - --- W14: mens der ventes, peger AD's mål på Wait-punktet (så AD ikke tipper i småportioner); --- behovet måles stadig mod hjem-målet, og AD's mål gendannes når den kører hjem -buildWaitWorld() -local vm, tm = makeUnloadingVehicle("Fastrac Mål", 591, 5000, 35000) -UnloadTask.update(tm, 16) -check("W14 AD's mål = Wait-punkt under ventetid", vm.ad.stateModule.secondMarkerId, 40) -waitBarnLevels[STRAW] = 10000 -- plads hjemme -UnloadTask.update(tm, 16) -check("W14 kører hjem (behov målt mod stalden, ikke Wait-punktet)", tm.destinationID, 30) -check("W14 AD's mål gendannet", vm.ad.stateModule.secondMarkerId, 30) - --- ==== I: run-integration — ingredienser fra run i stedet for multi-valg; Wait-pulje pr. run ==== --- I1: traktor i et run med ÉN valgt vare i AD får alligevel behovsstyring (run'ets liste) -local runDriver = buildCowWorld() -runDriver.uniqueId = "vehicleRun" -runDriver.ad.stateModule.selectedFillTypes = {STRAW} -runDriver.ad.stateModule.fillType = STRAW -testVehicles = {runDriver} -ADRunsController.store = ADRuns.newStore() -local feedRun = ADRunsController.createRun("Cow Feed") -ADRunsController.updateRun(feedRun.id, {targetWayPointId = 30, ingredientMode = "manual", ingredients = {"FORAGE", "STRAW"}}) -ADRunsController.assignVehicle(feedRun.id, runDriver) -check("I1 run-liste styrer: halm mangler -> halmladen", manager:getNextPickup(runDriver, false), 21) -cowLevels[STRAW] = 99500 -check("I1 run-liste styrer: TMR når halm er fuld", manager:getNextPickup(runDriver, false), 20) -check("I1 getIngredientsFor = run'ets liste", table.concat(ADSmartPickup.getIngredientsFor(FS25_AutoDrive, runDriver), ","), TMR .. "," .. STRAW) - --- I2: samme traktor uden run -> som før (én vare = mappe-logik i mappen "Lader") -ADRunsController.store = ADRuns.newStore() -runDriver.ad.stateModule.selectedFillTypes = {STRAW} -cowLevels[STRAW] = 0 -check("I2 uden run: mappe-logik", manager:getNextPickup(runDriver, false), 21) -check("I2 uden run og én vare: ingen ingrediensliste", ADSmartPickup.getIngredientsFor(FS25_AutoDrive, runDriver), nil) - --- I3: run med tom auto-liste (intet mål) -> falder tilbage til AD's valg -local emptyRun = ADRunsController.createRun("Tomt") -ADRunsController.assignVehicle(emptyRun.id, runDriver) -check("I3 tom run-liste -> som uden run", manager:getNextPickup(runDriver, false), 21) - --- I4: Wait-kandidater begrænses til run'ets pulje -buildWaitWorld() -addMarker(42, "Pig Wait 1", "Pig Wait", 5050, 0) -- nærmere end Cow Wait, men forkert pulje -local vp, tp = makeUnloadingVehicle("Fastrac Pulje", 511, 5000, 35000) -vp.uniqueId = "vehiclePool" -testVehicles = {vp} -ADRunsController.store = ADRuns.newStore() -local poolRun = ADRunsController.createRun("Cow Feed") -ADRunsController.updateRun(poolRun.id, {targetWayPointId = 30, waitPoolGroup = "Cow Wait"}) -ADRunsController.assignVehicle(poolRun.id, vp) -UnloadTask.update(tp, 16) -check("I4 kun run'ets pulje", tp.destinationID, 40) -ADRunsController.store = ADRuns.newStore() - --- I8: målet er fuldt med rest på vognen, men run'et har FLERE mål -> resten køres til det mål med plads --- (in-game 24/9: 'Pig Food' med 2,4M l halm holdt ved den første svinesti; de fire andre fik intet) -buildWaitWorld() -addMarker(31, "Cow 2 Food", "Cow Food", 6000, 0) -local pen2Levels = {[STRAW] = 10000} -table.insert(unloadStations, makeUnloadStation(131, 6000, 10, pen2Levels, {[STRAW] = 100000}, {name = "sti 2"})) -local v8, t8 = makeUnloadingVehicle("Pig Food", 531, 5000, 35000) -v8.uniqueId = "vehicleI8" -testVehicles = {v8} -ADRunsController.store = ADRuns.newStore() -local pens = ADRunsController.createRun("Pig Food") -ADRunsController.updateRun(pens.id, {targetWayPointIds = {30, 31}, waitPoolGroup = "Cow Wait"}) -ADRunsController.assignVehicle(pens.id, v8) -UnloadTask.update(t8, 16) -check("I8 resten køres til det andet mål", t8.destinationID, 31) -check("I8 AD's aflæsningsmål skiftet", v8.ad.stateModule.secondMarkerId, 31) -check("I8 logget", countLogsSince(1, "resten") >= 1, true) -ADRunsController.store = ADRuns.newStore() -testVehicles = {} - --- ==== S: læs-bytte fra Wait — halm-rest ved Wait, målet mangler TMR -> aflæs halm i silo ==== -local function buildSwapWorld() - buildWaitWorld() -- stald: halm FULD (50000/50000), TMR 10000/100000 - -- halmladen (markør 21) er også et aflæsningssted med plads - table.insert(unloadStations, stations[2]) - stations[2].unloadTriggers = {{exactFillRootNode = 121}} - stations[2].getIsFillTypeSupported = function(_, fillType) return fillType == STRAW end - stations[2].getFreeCapacity = function() return 100000 end - stations[2].owningPlaceable = {spec_silo = {storages = {{getFillLevel = function(_, fillType) return fillType == STRAW and 50000 or 0 end}}}} -end - --- S1: ventende traktor med halm-rest -> TMR mangler et fuldt læs (40000 l vogn, 90000 l fri) -> til halmladen -buildSwapWorld() -local sv, st = makeUnloadingVehicle("Fastrac Bytte", 521, 5000, 35000) -UnloadTask.update(st, 16) -check("S1 venter først ved Wait", st.destinationID, 40) -UnloadTask.update(st, ADLoadSwap.CHECK_INTERVAL_MS + 1) -check("S1 omdirigeret til halmladen", st.destinationID, 21) -check("S1 logget bytte", countLogsSince(1, "bytter læs") >= 1, true) -check("S1 Wait-punkt frigivet", FS25_AutoDrive.AutoDrive.getSetting("rotateTargets", sv), RT_ONLYPICKUP) -check("S1 AD's mål peger midlertidigt på siloen (ellers tipper AD ikke)", sv.ad.stateModule.secondMarkerId, 21) -UnloadTask.finished(st) -check("S1 mål gendannet efter aflæsning", sv.ad.stateModule.secondMarkerId, 30) - --- S2: målet mangler kun lidt TMR (10 % af lageret) og et fuldt læs passer ikke -> bliv ved Wait -buildSwapWorld() -waitBarnLevels[TMR] = 90000 -- 10000 fri < 40000 vogn og < 25 % af 100000 -local sv2, st2 = makeUnloadingVehicle("Fastrac Bliv", 522, 5000, 35000) -UnloadTask.update(st2, 16) -UnloadTask.update(st2, ADLoadSwap.CHECK_INTERVAL_MS + 1) -check("S2 bliver ved Wait", st2.destinationID, 40) - --- S2b: vognen er større end stalden (fuldt læs passer aldrig), men TMR mangler 25 % -> byt alligevel --- (in-game 24/9: Fastrac'erne byttede aldrig, fordi vognen er større end staldens lager) -buildSwapWorld() -waitBarnLevels[TMR] = 75000 -- 25000 fri < 40000 vogn, men = 25 % af 100000 -local sv2b, st2b = makeUnloadingVehicle("Fastrac Stor", 526, 5000, 35000) -UnloadTask.update(st2b, 16) -UnloadTask.update(st2b, ADLoadSwap.CHECK_INTERVAL_MS + 1) -check("S2b bytter når 25 % mangler", st2b.destinationID, 21) - --- S3: ingen silo med plads/vare -> bliv ved Wait, én WARN -buildSwapWorld() -stations[2].getFreeCapacity = function() return 1000 end -local sv3, st3 = makeUnloadingVehicle("Fastrac Ingen", 523, 5000, 35000) -UnloadTask.update(st3, 16) -local mark3 = #logLines -UnloadTask.update(st3, ADLoadSwap.CHECK_INTERVAL_MS + 1) -UnloadTask.update(st3, ADLoadSwap.CHECK_INTERVAL_MS + 1) -check("S3 bliver ved Wait", st3.destinationID, 40) -check("S3 én advarsel", countLogsSince(mark3, "bliver ved Wait-punktet"), 1) - --- S4: silo uden vejnet hjem springes over -buildSwapWorld() -FS25_AutoDrive.ADGraphManager.unreachable[21] = true -local sv4, st4 = makeUnloadingVehicle("Fastrac Vej", 524, 5000, 35000) -UnloadTask.update(st4, 16) -UnloadTask.update(st4, ADLoadSwap.CHECK_INTERVAL_MS + 1) -check("S4 unåelig silo -> bliv", st4.destinationID, 40) -FS25_AutoDrive.ADGraphManager.unreachable = {} - --- S5: traktor med kun én vare i listen bytter aldrig -buildSwapWorld() -local sv5, st5 = makeUnloadingVehicle("Fastrac Solo", 525, 5000, 35000) -sv5.ad.stateModule.selectedFillTypes = {STRAW} -UnloadTask.update(st5, 16) -UnloadTask.update(st5, ADLoadSwap.CHECK_INTERVAL_MS + 1) -check("S5 én vare -> bliver", st5.destinationID, 40) - --- S6: efter bytte til siloen skal traktoren blive der (ikke sendes til et nyt Wait-punkt, --- fordi siloen fejlagtigt tolkes som "målet er fuldt") -buildSwapWorld() -local sv6, st6 = makeUnloadingVehicle("Fastrac Silo", 526, 5000, 35000) -UnloadTask.update(st6, 16) -UnloadTask.update(st6, ADLoadSwap.CHECK_INTERVAL_MS + 1) -local mark6 = #logLines -UnloadTask.update(st6, 16) -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() - --- I6: run med to stalde — ved turstart vælges den stald der mangler mest, AD's mål skiftes, og --- varen hentes efter DEN stalds behov -buildCowWorld() -addMarker(31, "Cow 2 Food", "Cow Food", 6000, 0) -local cow2Levels = {[STRAW] = 95000, [TMR] = 95000} -table.insert(unloadStations, makeUnloadStation(131, 6000, 10, cow2Levels, {[STRAW] = 100000, [TMR] = 100000}, {name = "kostald 2"})) -cowLevels[STRAW], cowLevels[TMR] = 90000, 20000 -- stald 1: TMR mangler -local i6 = makeSupplyDriver(markerByIndex(21), markerByIndex(31), {TMR, STRAW}) -- AD peger på stald 2 (fuld) -i6.uniqueId = "vehicleI6"; i6.carriable = {TMR, STRAW}; i6.trailers = {level = 0, capacity = 8000} -i6.ad.stateModule.setSecondMarkerByWayPointId = function(self, id) self.secondMarkerId = id end -i6.ad.stateModule.getSecondMarker = function(self) return markerByIndex(self.secondMarkerId or 31) end -testVehicles = {i6} -ADRunsController.store = ADRuns.newStore() -local twoBarns = ADRunsController.createRun("Kvæg") -ADRunsController.updateRun(twoBarns.id, {targetWayPointIds = {30, 31}}) -ADRunsController.assignVehicle(twoBarns.id, i6) -check("I6 henter TMR til stald 1", manager:getNextPickup(i6, false), 20) -check("I6 AD's mål skiftet til stald 1", i6.ad.stateModule.secondMarkerId, 30) -check("I6 skiftet logges", countLogsSince(1, "kører til Cow 1 Food") >= 1, true) -cowLevels[STRAW], cowLevels[TMR] = 99000, 99000 -cow2Levels[STRAW] = 10000 -- nu mangler stald 2 halm -check("I6 næste tur: halm til stald 2", manager:getNextPickup(i6, false), 21) -check("I6 mål skiftet til stald 2", i6.ad.stateModule.secondMarkerId, 31) -ADRunsController.store = ADRuns.newStore() - --- I7: det valgte mål har intet der kan hentes (TMR-kilden er tom) -> prøv run'ets ANDRE mål før Wait --- (in-game 24/9: to traktorer fik begge 'Biogas Drop 1'; nr. 2 holdt selv om 4 DOB'er var tomme) -buildCowWorld() -addMarker(31, "Cow 2 Food", "Cow Food", 6000, 0) -local cow2LevelsI7 = {[STRAW] = 10000, [TMR] = 100000} -table.insert(unloadStations, makeUnloadStation(131, 6000, 10, cow2LevelsI7, {[STRAW] = 100000, [TMR] = 100000}, {name = "kostald 2"})) -cowLevels[STRAW], cowLevels[TMR] = 100000, 0 -- stald 1 mangler KUN TMR (mest trængende) -stations[1].getFillLevel = function(_, fillType) return fillType == STRAW and 5000 or 0 end -- TMR-kilden tom -local i7 = makeSupplyDriver(markerByIndex(21), markerByIndex(30), {TMR, STRAW}) -i7.uniqueId = "vehicleI7"; i7.carriable = {TMR, STRAW}; i7.trailers = {level = 0, capacity = 8000} -i7.ad.stateModule.setSecondMarkerByWayPointId = function(self, id) self.secondMarkerId = id end -i7.ad.stateModule.getSecondMarker = function(self) return markerByIndex(self.secondMarkerId or 30) end -testVehicles = {i7} -ADRunsController.store = ADRuns.newStore() -local twoBarnsI7 = ADRunsController.createRun("Kvæg2") -ADRunsController.updateRun(twoBarnsI7.id, {targetWayPointIds = {30, 31}}) -ADRunsController.assignVehicle(twoBarnsI7.id, i7) -check("I7 henter halm til det andet mål", manager:getNextPickup(i7, false), 21) -check("I7 AD's mål skiftet til stald 2", i7.ad.stateModule.secondMarkerId, 31) -ADRunsController.store = ADRuns.newStore() - --- ==== V: forkert last på vognen ==== --- V1: pålæsning starter med anden vare på vognen -> opgaven afsluttes straks (AD leverer det den har) -buildCowWorld() -cowLevels[STRAW], cowLevels[TMR] = 90000, 0 -- TMR mangler -local wv = makeSupplyDriver(markerByIndex(21), markerByIndex(30), {TMR, STRAW}) -wv.carriable = {TMR, STRAW}; wv.trailers = {level = 8000, capacity = 8000} -wv.units = {{object = {getFillUnitFillLevel = function() return 8000 end, getFillUnitFillType = function() return STRAW end}, fillUnitIndex = 1}} -withDrivePath(wv); wv.targetReached = false -check("V1 TMR vælges som behov", manager:getNextPickup(wv, false), 20) -local loadTask = {vehicle = wv, trailers = wv.trailers, finished = function(self) self.isFinished = true end} -FS25_AutoDrive.LoadAtDestinationTask.update(loadTask, 16) -check("V1 pålæsningsopgave afsluttet pga. halm på vognen", loadTask.isFinished, true) -check("V1 logget", countLogsSince(1, "leverer først") >= 1, true) --- samme vare på vognen som den der hentes -> opgaven kører normalt -wv.units[1].object.getFillUnitFillType = function() return TMR end -local loadTask2 = {vehicle = wv, trailers = wv.trailers, finished = function(self) self.isFinished = true end} -manager:getNextPickup(wv, false) -FS25_AutoDrive.LoadAtDestinationTask.update(loadTask2, 16) -check("V1 samme vare -> ikke afsluttet", loadTask2.isFinished, nil) - --- V2: ved målet med en vare målet ikke tager -> Wait; ved Wait -> silo uanset "manglende" vare -buildWaitWorld() -table.insert(unloadStations, stations[2]) -- halmladen tager halm -stations[2].unloadTriggers = {{exactFillRootNode = 121}} -stations[2].getIsFillTypeSupported = function(_, fillType) return fillType == STRAW end -stations[2].getFreeCapacity = function() return 100000 end -stations[2].owningPlaceable = {spec_silo = {storages = {{getFillLevel = function() return 0 end}}}} -waitBarnCapacity[STRAW] = nil -- stalden tager IKKE halm -local vw, tw = makeUnloadingVehicle("Fastrac Forkert", 561, 5000, 35000) -UnloadTask.update(tw, 16) -check("V2 målet tager ikke varen -> Wait", tw.destinationID, 40) -UnloadTask.update(tw, ADLoadSwap.CHECK_INTERVAL_MS + 1) -check("V2 fra Wait til silo selv uden manglende vare", tw.destinationID, 21) -waitBarnCapacity[STRAW] = 50000 - --- V3: Start med forkert last -> AD's mål = silo; første getNextPickup må IKKE sætte run-målet tilbage, --- og efter aflæsningen (finished) gendannes run-målet -buildCowWorld() -table.insert(unloadStations, stations[2]) -- halmladen tager halm -stations[2].unloadTriggers = {{exactFillRootNode = 121}} -stations[2].getIsFillTypeSupported = function(_, fillType) return fillType == STRAW end -stations[2].getFreeCapacity = function() return 100000 end -stations[2].owningPlaceable = {spec_silo = {storages = {{getFillLevel = function() return 0 end}}}} -local v3 = makeSupplyDriver(markerByIndex(21), markerByIndex(30), {TMR, STRAW}) -v3.uniqueId = "vehicleV3"; v3.carriable = {TMR}; v3.trailers = {level = 4000, capacity = 8000}; v3.rootNode = 571; nodePositions[571] = {x = 0, z = 0} -v3.units = {{object = {getFillUnitFillLevel = function() return 4000 end, getFillUnitFillType = function() return STRAW end}, fillUnitIndex = 1}} -v3.ad.stateModule.secondMarkerId = 30 -v3.ad.stateModule.setSecondMarkerByWayPointId = function(self, id) self.secondMarkerId = id end -v3.ad.stateModule.getSecondMarker = function(self) return markerByIndex(self.secondMarkerId) end -cowLevels[STRAW] = nil -- stald 1 tager i denne test kun TMR -unloadStations[1].getIsFillTypeSupported = function(_, fillType) return fillType == TMR end -testVehicles = {v3} -ADRunsController.store = ADRuns.newStore() -local v3Run = ADRunsController.createRun("TMR only") -ADRunsController.updateRun(v3Run.id, {targetWayPointId = 30, ingredientMode = "manual", ingredients = {"FORAGE"}}) -ADRunsController.assignVehicle(v3Run.id, v3) -ADUnloadWait.setDumpTarget(v3, 21) -- som Start gør ved forkert last -check("V3 AD's mål = silo", v3.ad.stateModule.secondMarkerId, 21) -manager:getNextPickup(v3, false) -check("V3 getNextPickup rører ikke målet mens silo-aflæsning er undervejs", v3.ad.stateModule.secondMarkerId, 21) -UnloadTask.finished({vehicle = v3}) -check("V3 mål gendannet efter aflæsning", v3.ad.stateModule.secondMarkerId, 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]) -check("E1 ukendt waypoint -> nil", ADSmartPickup.getUnloadStationAtWayPointId(FS25_AutoDrive, 9999, 1), nil) -check("E2 læssestation ved waypoint", ADSmartPickup.getLoadStationAtWayPointId(FS25_AutoDrive, 21, 1), stations[2]) -local eLevel, eCapacity = ADSmartPickup.getStationLevelAndCapacity(unloadStations[1], TMR, 1) -check("E3 niveau i målet", eLevel, 40000) -check("E3 kapacitet i målet", eCapacity, 100000) -check("E3 vare målet ikke tager -> nil", ADSmartPickup.getStationLevelAndCapacity(unloadStations[1], MILK, 1), nil) - --- E4: FS25-stald med foder-spec: foder læses fra spec_husbandryFood (fælles kapacitet), --- fodertyper fra animalFoodSystem, halm fra spec_husbandryStraw; outputs (mælk) tages ikke -buildCowWorld() -local foodLevels = {[TMR] = 10000, [HAY] = 5000} -local fs25Barn = { - getName = function() return "FS25-kostald" end, - spec_husbandryFood = {fillLevels = foodLevels, capacity = 100000, animalTypeIndex = 7}, - spec_husbandryStraw = {fillLevel = 20000, capacity = 300000}, - -- staldens egen "understøtter" siger også ja til mælk (output) — må ikke blive et mål - getHusbandryIsFillTypeSupported = function(_, fillType) return fillType == MILK or fillType == STRAW or fillType == TMR end, - getHusbandryFillLevel = function(_, fillType) if fillType == STRAW then return 20000 end return 0 end, - getHusbandryCapacity = function(_, fillType) if fillType == STRAW then return 300000 end return 0 end, -} -g_currentMission.animalFoodSystem = { - getAnimalFood = function(_, animalTypeIndex) if animalTypeIndex == 7 then return {groups = {{fillTypes = {TMR}}, {fillTypes = {HAY, GRASS}}}} end end, - getMixturesByAnimalTypeIndex = function() return {} end, -} -unloadStations[1].targetStorages = nil -unloadStations[1].owningPlaceable = fs25Barn -local tmrLevel, tmrCapacity = ADSmartPickup.getStationLevelAndCapacity(unloadStations[1], TMR, 1) -check("E4 TMR-niveau fra foder-spec", tmrLevel, 10000) -check("E4 TMR-kapacitet = eget + fælles fri plads", tmrCapacity, 10000 + (100000 - 15000)) -local grassLevel, grassCapacity = ADSmartPickup.getStationLevelAndCapacity(unloadStations[1], GRASS, 1) -check("E4 græs (fodergruppe uden lager) niveau 0", grassLevel, 0) -check("E4 græs kapacitet = fælles fri plads", grassCapacity, 85000) -local strawLevel, strawCapacity = ADSmartPickup.getStationLevelAndCapacity(unloadStations[1], STRAW, 1) -check("E4 halm via husbandry-opslag", strawLevel .. "/" .. strawCapacity, "20000/300000") -check("E4 mælk (output) er ikke et mål", ADSmartPickup.getStationLevelAndCapacity(unloadStations[1], MILK, 1), nil) -check("E4 sukkerroer (ikke foder) er ikke et mål", ADSmartPickup.getStationLevelAndCapacity(unloadStations[1], SUGARBEET, 1), nil) -g_currentMission.animalFoodSystem = nil -check("E3 farmId", ADSmartPickup.getFarmId(eDriver), 1) - --- L6: samme valg logges kun én gang -resetWorld() -addMarker(1, "Silo 1", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[SUGARBEET] = 5000}, false, {[SUGARBEET] = 5000})) -local chatty = makeDriver(markers[1], {SUGARBEET}) -manager:getNextPickup(chatty, false); manager:getNextPickup(chatty, false) -check("L6 gentaget valg logges én gang", #logLines, 1) -check("L6 loggen viser eget lager", logLines[1]:find("eget lager", 1, true) ~= nil, true) - --- W15: efter genindlæsning er ventetilstanden væk, men AD's mål peger stadig på Wait-punktet og --- traktoren står der med rest-læs -> ventningen genoptages (hjem = run'ets mål) og den kører hjem --- når der er plads -buildWaitWorld() -local orphan, orphanTask = makeUnloadingVehicle("Genindlæst", 515, 5090, 20000) -orphan.uniqueId = "vehicleOrphan" -orphan.spec_motorized = {} -orphan.ad.stateModule.secondMarkerId = 40 -- AD's gemte mål = Wait 1 -orphanTask.destinationID = 40 -orphan.ad.taskModule = {getActiveTask = function() return orphanTask end} -testVehicles = {orphan} -ADRunsController.store = ADRuns.newStore() -local orphanRun = ADRunsController.createRun("Cow Feed") -ADRunsController.updateRun(orphanRun.id, {targetWayPointId = 30}) -ADRunsController.assignVehicle(orphanRun.id, orphan) -check("W15 ingen ventning før", ADUnloadWait.getWaitFor(orphan), nil) -ADUnloadWait.adoptOrphans(FS25_AutoDrive) -local adopted = ADUnloadWait.getWaitFor(orphan) -check("W15 ventning genoptaget", adopted ~= nil and adopted.markerName, "Wait 1") -check("W15 Wait-punktet reserveret", ADWaitPool.getOwner(40), orphan) -check("W15 hjem-mål husket", ADUnloadWait.getPendingTargetRestore(orphan), 30) -check("W15 logget", logLines[#logLines]:find("genoptager", 1, true) ~= nil, true) -ADUnloadWait.adoptOrphans(FS25_AutoDrive) -check("W15 ikke dobbelt", ADWaitPool.getOwner(40), orphan) -waitBarnLevels[STRAW] = 0 -- plads til hele resten -UnloadTask.update(orphanTask, 16) -check("W15 kører hjem", orphanTask.destinationID, 30) -check("W15 AD's mål gendannet", orphan.ad.stateModule.secondMarkerId, 30) -testVehicles = {} - --- N1: løst opslag (stald/fabrik): nærmeste læssestation inden for maxDistance, uanset retning og "bag næsen" --- (in-game 23/9: mælketankens trigger lå ved siden af markøren, 20+ m væk -> "ingen læssestation") -resetWorld() -addMarker(1, "cow 1 milk", "Milk", 0, 0, -30, 0) -- kører mod +x; tanken ligger 25 m til siden -table.insert(stations, makeStation(101, 0, 25, {[WHEAT] = 1})) -check("N1 strengt opslag finder intet", ADSmartPickup.getLoadStationAtWayPointId(FS25_AutoDrive, 1, 1), nil) -check("N1 løst opslag finder tanken", ADSmartPickup.getLoadStationNearWayPointId(FS25_AutoDrive, 1, 1, 40), stations[1]) -check("N1 uden for afstanden -> nil", ADSmartPickup.getLoadStationNearWayPointId(FS25_AutoDrive, 1, 1, 20), nil) - --- W16: lille rest (< 100 l) som målet ikke kan tage -> aflæsningen afsluttes, traktoren kører videre --- (in-game 24/9: "tom" vogn holdt i Cow 1 Food med en rest, to andre ventede bag den) -buildWaitWorld() -local vr, tr = makeUnloadingVehicle("Fastrac Rest", 601, 5000, 50) -setmetatable(tr, {__index = UnloadTask}) -UnloadTask.update(tr, 16) -check("W16 lille rest + fuldt mål -> afsluttet", tr.isFinished, true) -check("W16 ikke sendt til Wait", tr.destinationID, 30) -buildWaitWorld() -waitBarnLevels[STRAW] = 0 -local vr2, tr2 = makeUnloadingVehicle("Fastrac Rest2", 602, 5000, 50) -setmetatable(tr2, {__index = UnloadTask}) -UnloadTask.update(tr2, 16) -check("W16 lille rest med plads -> AD læsser selv af", tr2.isFinished, nil) - --- V: flere traktorer til samme mål fordeler sig — læs på vej tælles med (in-game 24/9: alle 4 Fastrac'er --- hentede det samme). Græs mangler mest (20000/100000), ensilage næst (50000/100000). -buildFeedWorld() -local feederA = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS, SILAGE}) -feederA.trailers = {level = 0, capacity = 70000} -local feederB = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS, SILAGE}) -feederB.trailers = {level = 0, capacity = 70000} -check("V1 A henter græs (mangler mest)", manager:getNextPickup(feederA, false), 3) -check("V1 B henter ensilage (græs er på vej med A)", manager:getNextPickup(feederB, false), 4) -check("V1 A's læs registreret på vej", ADSmartPickup.getPending(10, feederB)[GRASS], 40000) -ADSmartPickup.clearEnRoute(feederA) -check("V2 A læsset af -> intet på vej", ADSmartPickup.getPending(10, feederB)[GRASS], nil) -check("V2 egne læs tæller ikke", ADSmartPickup.getPending(10, feederB)[SILAGE], nil) - --- X: udkørsel koblet på getNextPickup-wrap'en og Wait-logikken -resetWorld() -addMarker(1, "Suger Load", "Pickup points", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[WHEAT] = 1000})) -addMarker(2, "Wait 3", "General Wait", 100, 0) -local outboundDriver = makeDriver(markers[1], {WHEAT}) -local outboundCalls, outboundChoice, outboundError = 0, nil, false -ADOutbound = { - isOutboundVehicle = function(vehicle) return vehicle == outboundDriver end, - choosePickup = function(adEnv, vehicle) outboundCalls = outboundCalls + 1; if outboundError then error("boom") end return outboundChoice end, - releaseInactive = function() end, - install = function() end, -} -outboundChoice = {markerIndex = markers[1].markerIndex, markerName = "Suger Load", group = "Pickup points", station = stations[1], fillTypes = {WHEAT}, fillType = WHEAT} -check("X1 udkørsel: kilden vælges af ADOutbound", manager:getNextPickup(outboundDriver, false), markers[1].markerIndex) -check("X1 ADOutbound spurgt", outboundCalls, 1) -check("X1 greb sat (station kendt)", ADSmartPickup.getActivePickup(outboundDriver) ~= nil, true) -outboundChoice = {markerIndex = markers[2].markerIndex, markerName = "Wait 3", group = "General Wait", station = nil, fillTypes = {}, fillType = nil} -check("X1 Wait-markør uden station", manager:getNextPickup(outboundDriver, false), markers[2].markerIndex) -check("X1 intet greb uden station", ADSmartPickup.getActivePickup(outboundDriver), nil) -outboundChoice = nil -check("X1 nil -> AutoDrives original", manager:getNextPickup(outboundDriver, false), "ORIGINAL") -outboundError = true -check("X1 fejl -> AutoDrives original", manager:getNextPickup(outboundDriver, false), "ORIGINAL") -outboundError = false --- setActivePickup (bruges af opsamlingsrunden) opdaterer grebet direkte -ADSmartPickup.setActivePickup(outboundDriver, {station = stations[1], fillTypes = {WHEAT}, markerName = "Suger Load", fillType = WHEAT}) -check("X2 setActivePickup", ADSmartPickup.getActivePickup(outboundDriver).markerName, "Suger Load") -ADSmartPickup.setActivePickup(outboundDriver, nil) -check("X2 setActivePickup nil rydder", ADSmartPickup.getActivePickup(outboundDriver), nil) --- Wait-logikken springer udkørsels-traktorer over (rører ikke vognens indhold) -local originalUnits = FS25_AutoDrive.AutoDrive.getAllDischargeableUnits -FS25_AutoDrive.AutoDrive.getAllDischargeableUnits = function() error("må ikke kaldes for udkørsel") end -outboundDriver.ad.drivePathModule = {isTargetReached = function() return true end} -local okWait = pcall(ADUnloadWait.beforeUpdate, FS25_AutoDrive, {vehicle = outboundDriver, destinationID = 1}, 16) -check("X3 Wait-logik springer udkørsel over", okWait, true) -FS25_AutoDrive.AutoDrive.getAllDischargeableUnits = originalUnits -ADOutbound = nil - --- FT: hønsehus — foderkrybben (spec_husbandryFood.feedingTrough) står ikke i aflæsningsstationens --- unloadTriggers; stationsopslaget ved markøren skal alligevel finde hønsehuset -do - local coop = {getHusbandryFillLevel = function() return 0 end, getName = function() return "Chicken Coop" end, - spec_husbandryFood = {unloadTrigger = {exactFillRootNode = 9901}}} - nodePositions[9901] = {x = 20000, z = 3} - local coopStation = {owningPlaceable = coop, unloadTriggers = {}} - table.insert(unloadStations, coopStation) - g_currentMission.placeableSystem = {placeables = {coop}} - ADSmartPickup.invalidateUnloadCandidates() - addMarker(95, "Chicken 1 food", "Chicken", 20000, 10) - local found = ADSmartPickup.getUnloadStationAtWayPointId(FS25_AutoDrive, 95, 1) - check("FT krybbe findes via markøren", found ~= nil and found.owningPlaceable == coop, true) - g_currentMission.placeableSystem = nil - ADSmartPickup.invalidateUnloadCandidates() -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_adSources.lua b/tests/test_adSources.lua deleted file mode 100644 index 9ec283c..0000000 --- a/tests/test_adSources.lua +++ /dev/null @@ -1,596 +0,0 @@ --- Kør: luajit tests/test_adSources.lua (fra repo-roden) --- Kilde-genkendelse ved en AD-markør (fabrik / silo / objektlager), lagerstatus pr. vare, --- paller ved fabrikkens spawner, salgssteder, objektlager-udtag. - -local SUGAR, FORAGE, BASKET, CRATE = 5, 6, 7, 8 -local DESC = { - [SUGAR] = {index = SUGAR, name = "SUGAR", title = "Sukker", isPalletType = true}, - [FORAGE] = {index = FORAGE, name = "FORAGE", title = "TMR"}, - [BASKET] = {index = BASKET, name = "STRAW_BASKET", title = "Halmkurve", palletFilename = "basketPallet.xml"}, - [CRATE] = {index = CRATE, name = "STRAW_CRATE", title = "Halmkasser", palletFilename = "cratePallet.xml"}, -} -g_fillTypeManager = { - getFillTypeByIndex = function(_, index) return DESC[index] end, - getFillTypeNameByIndex = function(_, index) return DESC[index] and DESC[index].name end, - getFillTypeTitleByIndex = function(_, index) return DESC[index] and DESC[index].title end, -} -Logging = {info = function() end, warning = function() end} -getWorldTranslation = function(node) return node.x, 0, node.z end -SellingStation = {} - -dofile("FS25_ADSmartPickup/adSupplyPlanner.lua") -dofile("FS25_ADSmartPickup/adPrices.lua") -dofile("FS25_ADSmartPickup/adStorageStats.lua") -dofile("FS25_ADSmartPickup/adSources.lua") - -local failures = 0 -local function check(name, actual, expected) - if actual == expected then - print("OK " .. name) - else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - --- ---- fakes ---- -local wayPoints, markers = {}, {} -local function addMarker(id, name, x, z) - wayPoints[id] = {id = id, x = x, z = z} - table.insert(markers, {id = id, markerIndex = #markers + 1, name = name, group = "g"}) -end -local adEnv = {ADGraphManager = { - getWayPointById = function(_, id) return wayPoints[id] end, - getMapMarkers = function() return markers end, - getMapMarkerByWayPointId = function(_, id) for _, m in ipairs(markers) do if m.id == id then return m end end end, -}} - -local function storageOf(levels, capacities) - return { - fillLevels = levels, - getFillLevel = function(_, ft) return levels[ft] or 0 end, - getCapacity = function(_, ft) return capacities[ft] or 0 end, - } -end - -local sugarStorage = storageOf({[SUGAR] = 150000, [BASKET] = 0}, {[SUGAR] = 180000, [BASKET] = 8000}) -local factory = { - rootNode = {x = 100, z = 100}, getName = function() return "Sugar mill" end, - spec_productionPoint = {productionPoint = { - productions = {{outputs = {{type = SUGAR, amount = 1}}}, {outputs = {{type = BASKET, amount = 1}}}}, - storage = sugarStorage, - palletSpawner = {spawnPlaces = {{startNode = {x = 110, z = 100}}}}, - }}, -} -local factoryStation = {owningPlaceable = factory, supportedFillTypes = {[SUGAR] = true}} -local silo = {rootNode = {x = 200, z = 200}, spec_silo = {storages = {storageOf({[FORAGE] = 74500}, {[FORAGE] = 12000000})}}, getName = function() return "Farma" end} -local siloStation = {owningPlaceable = silo} -local spawnCalls = {} -local objectStorage = { - rootNode = {x = 300, z = 300}, ownerFarmId = 1, getName = function() return "Objektlager" end, - spec_objectStorage = { - objectInfos = { - {objects = {{palletAttributes = {fillType = BASKET, fillLevel = 1000}}}, numObjects = 8}, - {objects = {{baleAttributes = {fillType = FORAGE, fillLevel = 4000}}}, numObjects = 2}, - }, - numStoredObjects = 10, capacity = 4000, - }, - removeAbstractObjectsFromStorage = function(self, index, amount, connection) table.insert(spawnCalls, {index, amount, connection}) end, -} -local sellStation = {owningPlaceable = {rootNode = {x = 500, z = 500}}, isa = function(_, c) return c == SellingStation end, isSellingPoint = true, acceptedFillTypes = {[SUGAR] = true}, getName = function() return "Grain Elevator" end} -local factoryUnload = {owningPlaceable = factory, isa = function() return true end, isSellingPoint = false} - -addMarker(1, "Suger Load", 100, 90) -addMarker(2, "silo 2", 200, 190) -addMarker(3, "Objektlager", 300, 280) -- 20 m fra lageret -addMarker(4, "Langt væk", 300, 240) -- 60 m fra lageret -addMarker(5, "selling station", 500, 490) -addMarker(6, "sugar offload", 100, 80) - -local loadAt = {[1] = factoryStation, [2] = siloStation} -local unloadAt = {[5] = sellStation, [6] = factoryUnload} -ADSmartPickup = { - getLoadStationAtWayPointId = function(_, id) return loadAt[id] end, - getUnloadStationAtWayPointId = function(_, id) return unloadAt[id] end, - getOwnStorageLevel = function(station, fillTypes) - local total = 0 - for _, storage in pairs(station.owningPlaceable.spec_silo.storages) do - for _, ft in ipairs(fillTypes) do total = total + storage:getFillLevel(ft) end - end - return total - end, -} -local pallets = {} -local function addPallet(fillType, level, x, z, farmId) - table.insert(pallets, {spec_pallet = {}, rootNode = {x = x, z = z}, getOwnerFarmId = function() return farmId or 1 end, - getFillUnitFillType = function() return fillType end, getFillUnitFillLevel = function() return level end}) -end -g_currentMission = { - placeableSystem = {placeables = {factory, silo, objectStorage}}, - accessHandler = {canFarmAccess = function(_, farmId, placeable) return true end}, - vehicleSystem = {vehicles = pallets}, -} - --- S1: fabrik -local source1 = ADSources.identify(adEnv, 1, 1) -check("S1 fabrik", source1.kind, "factory") -check("S1 navn", source1.name, "Sugar mill") -local products1 = ADSources.getProducts(adEnv, source1, 1) -check("S1 to produkter", #products1, 2) -check("S1 SUGAR available", products1[1].available, 150000) -check("S1 SUGAR level", products1[1].level, 150000) -check("S1 SUGAR capacity", products1[1].capacity, 180000) -check("S1 SUGAR bulk", products1[1].isPallet, false) -check("S1 SUGAR kan også stå på paller", products1[1].canPallet, true) -check("S1 SUGAR paller ved spawner (ingen endnu)", products1[1].palletLiters, 0) -check("S1 TMR-lignende bulk kan ikke paller", products1[2].canPallet, true) -check("S1 BASKET er palle", products1[2].isPallet, true) -check("S1 BASKET tomt", products1[2].available, 0) - --- S2: silo -local source2 = ADSources.identify(adEnv, 2, 1) -check("S2 silo", source2.kind, "silo") -local products2 = ADSources.getProducts(adEnv, source2, 1) -check("S2 én vare", #products2, 1) -check("S2 FORAGE", products2[1].fillType, FORAGE) -check("S2 level", products2[1].level, 74500) -check("S2 capacity", products2[1].capacity, 12000000) - --- S3: objektlager 20 m fra markøren -local source3 = ADSources.identify(adEnv, 3, 1) -check("S3 objektlager", source3.kind, "objectStorage") -local products3 = ADSources.getProducts(adEnv, source3, 1) -check("S3 to varer", #products3, 2) -check("S3 kurve available", products3[1].available, 8000) -check("S3 kurve er palle", products3[1].isPallet, true) -check("S3 level = antal objekter", products3[1].level, 10) -check("S3 capacity", products3[1].capacity, 4000) -check("S3 baller tæller som palle", products3[2].isPallet, true) -local index, count = ADSources.getObjectInfoIndex(source3, BASKET) -check("S3 objectInfo-indeks", index, 1) -check("S3 antal", count, 8) -check("S3 countStored", ADSources.countStored(source3, FORAGE), 2) -check("S3 ukendt vare", ADSources.getObjectInfoIndex(source3, SUGAR), nil) - --- S4: paller ved fabrikkens spawner tæller med (inden for 25 m af spawn-noden) -addPallet(BASKET, 1000, 112, 100) -addPallet(BASKET, 1000, 115, 105) -addPallet(BASKET, 1000, 160, 100) -- 50 m væk: tæller ikke -addPallet(BASKET, 1000, 112, 102, 2) -- anden farm: tæller ikke -local products4 = ADSources.getProducts(adEnv, source1, 1) -check("S4 paller ved spawner", products4[2].available, 2000) -check("S4 antal paller ved spawner", products4[2].palletCount, 2) -check("S3 antal paller i objektlager", products3[1].palletCount, 8) -check("S4 bulk uændret", products4[1].available, 150000) -addPallet(SUGAR, 2000, 111, 101) -local products4b = ADSources.getProducts(adEnv, source1, 1) -check("S4 sukkerpaller tælles separat", products4b[1].palletLiters, 2000) -check("S4 bulk-mængde uden paller", products4b[1].available, 150000) - --- S5: salgssteder = kun markører hvis aflæsningsstation er et rigtigt salgssted -local sells = ADSources.getSellMarkers(adEnv, 1) -check("S5 ét salgssted", #sells, 1) -check("S5 markør", sells[1].marker.id, 5) -check("S5 tager SUGAR", ADSources.stationAccepts(sells[1].station, SUGAR), true) -check("S5 tager ikke FORAGE", ADSources.stationAccepts(sells[1].station, FORAGE), false) --- palletriggerens varer ligger i supportedFillTypes (det AutoDrive bruger), ikke i acceptedFillTypes (kun bulk-trigger) -local palletSell = {acceptedFillTypes = {[SUGAR] = true}, supportedFillTypes = {[SUGAR] = true, [BASKET] = true}} -check("S5 pallevare via supportedFillTypes", ADSources.stationAccepts(palletSell, BASKET), true) -check("S5 ukendt vare stadig nej", ADSources.stationAccepts(palletSell, FORAGE), false) -check("S5 acceptedNames", ADSources.describeAccepted(palletSell), "STRAW_BASKET, SUGAR") - --- S6: spawnOne kalder spillets udtag med 1 objekt og uden klientforbindelse -check("S6 spawn ok", ADSources.spawnOne(source3, 1), true) -check("S6 indeks", spawnCalls[1][1], 1) -check("S6 antal", spawnCalls[1][2], 1) -check("S6 connection nil", spawnCalls[1][3], nil) -check("S6 fejl -> false", ADSources.spawnOne({kind = "objectStorage", placeable = {removeAbstractObjectsFromStorage = function() error("x") end}}, 1), false) - --- S7: markør uden station og >40 m fra objektlager -> nil -check("S7 ingen kilde", ADSources.identify(adEnv, 4, 1), nil) -check("S7 ukendt waypoint", ADSources.identify(adEnv, 99, 1), nil) - --- S8: hjælpere -check("S8 isPalletFillType palle", ADSources.isPalletFillType(BASKET), true) -check("S8 isPalletFillType palletype uden fil", ADSources.isPalletFillType(SUGAR), true) -check("S8 isPalletFillType bulk", ADSources.isPalletFillType(FORAGE), false) --- C1: palle/bulk afgøres pr. kilde: fabrik med læssestation der tager varen = bulk; silo = altid bulk; objektlager = palle -check("S8 sukker ved fabrik med læssestation = bulk", ADSources.isPalletAt(source1, SUGAR), false) -check("S8 kurve ved fabrik (station tager dem ikke) = palle", ADSources.isPalletAt(source1, BASKET), true) -check("S8 silo altid bulk", ADSources.isPalletAt(source2, SUGAR), false) -check("S8 objektlager altid palle", ADSources.isPalletAt(source3, FORAGE), true) --- S9 (I1): ren pallefabrik uden læssestation genkendes via afstand til spawner/rootNode -local basketFactory = { - rootNode = {x = 600, z = 600}, getName = function() return "Straw factory" end, - spec_productionPoint = {productionPoint = {productions = {{outputs = {{type = BASKET, amount = 1}}}}, storage = storageOf({[BASKET] = 0}, {[BASKET] = 8000}), palletSpawner = {spawnPlaces = {{startNode = {x = 610, z = 600}}}}}}, -} -table.insert(g_currentMission.placeableSystem.placeables, basketFactory) -addMarker(7, "Straw factory pickup", 605, 590) -local source9 = ADSources.identify(adEnv, 7, 1) -check("S9 pallefabrik uden station = factory", source9.kind, "factory") -check("S9 station nil", source9.station, nil) -local products9 = ADSources.getProducts(adEnv, source9, 1) -check("S9 kurve er palle", products9[1].isPallet, true) -check("S9 fabrik foretrækkes frem for objektlager i nærheden", ADSources.identify(adEnv, 7, 1).placeable, basketFactory) --- S9b: fabrik fundet på afstand, men produktionspunktet HAR en læssestation (sukkerfabrik hvor markøren --- ikke rammer triggeren): stationen hentes fra produktionspunktet, og sukker er bulk, ikke paller -local mill2Storage = storageOf({[SUGAR] = 1000}, {[SUGAR] = 180000}) -local mill2 = { - rootNode = {x = 1200, z = 1200}, getName = function() return "Sugar mill 2" end, - spec_productionPoint = {productionPoint = {productions = {{outputs = {{type = SUGAR, amount = 1}}}}, storage = mill2Storage, - loadingStation = {supportedFillTypes = {[SUGAR] = true}}}}, -} -table.insert(g_currentMission.placeableSystem.placeables, mill2) -addMarker(9, "Mill 2 load", 1210, 1190) -local source9b = ADSources.identify(adEnv, 9, 1) -check("S9b fabrik", source9b.kind, "factory") -check("S9b station fra produktionspunktet", source9b.station, mill2.spec_productionPoint.productionPoint.loadingStation) -check("S9b sukker = bulk", ADSources.isPalletAt(source9b, SUGAR), false) -check("S9b produkt bulk", ADSources.getProducts(adEnv, source9b, 1)[1].isPallet, false) -local excluded = ADSources.getSourcePlaceables({source1, source2}) -check("S8 udelukkede placeables", excluded[factory] and excluded[silo], true) - --- S10 (paller til lager): objektlager med plads der tager varen; nærmeste nåelige fra traktoren -objectStorage.getObjectStorageSupportsFillType = function(self, ft) return ft == BASKET or ft == FORAGE end -check("S10 tager kurve", ADSources.objectStorageAccepts(objectStorage, BASKET), true) -check("S10 tager ikke sukker", ADSources.objectStorageAccepts(objectStorage, SUGAR), false) -check("S10 fri pladser", ADSources.objectStorageFreeSlots(objectStorage), 3990) -local fullShed = {rootNode = {x = 700, z = 700}, spec_objectStorage = {capacity = 10, numStoredObjects = 10, objectInfos = {}}, getName = function() return "Fuldt skur" end} -table.insert(g_currentMission.placeableSystem.placeables, fullShed) -addMarker(8, "Fuldt skur", 700, 690) -check("S10 fuldt skur 0 pladser", ADSources.objectStorageFreeSlots(fullShed), 0) -local truck = {rootNode = {x = 650, z = 650}} -local store = ADSources.findPalletStore(adEnv, truck, BASKET, {}, function() return true end, 1) -check("S10 nærmeste med plads = Objektlager (skuret er fuldt)", store.wayPointId, 3) -check("S10 navn", store.name, "Objektlager") -check("S10 udelukket kilde -> nil", ADSources.findPalletStore(adEnv, truck, BASKET, {[objectStorage] = true}, function() return true end, 1), nil) -check("S10 uopnåelig -> nil", ADSources.findPalletStore(adEnv, truck, BASKET, {}, function() return false end, 1), nil) -check("S10 sukker: intet skur tager det", ADSources.findPalletStore(adEnv, truck, SUGAR, {}, function() return true end, 1), nil) - --- S11 (reserve): gårdens forbrug det næste døgn af en vare, minus hvad forbrugerne allerede har -local BEET, STRAW, WATER = 9, 10, 11 -DESC[BEET] = {index = BEET, name = "SUGARBEET", title = "Roer"}; DESC[STRAW] = {index = STRAW, name = "STRAW", title = "Halm"}; DESC[WATER] = {index = WATER, name = "WATER", title = "Vand"} -local tmrStorage = storageOf({[BEET] = 20000, [FORAGE] = 500000}, {[BEET] = 2500000, [FORAGE] = 3500000}) -local tmrFactory = { - rootNode = {x = 800, z = 800}, ownerFarmId = 1, getOwnerFarmId = function() return 1 end, getName = function() return "TMR-fabrik" end, - spec_productionPoint = {productionPoint = { - productions = {{id = "tmr3", cyclesPerMinute = 200 / 60, inputs = {{type = BEET, amount = 45}}, outputs = {{type = FORAGE, amount = 240}}}}, - storage = tmrStorage, - getIsProductionEnabled = function(self, id) return id == "tmr3" end, - }}, -} -local cowBarn = { - rootNode = {x = 900, z = 900}, ownerFarmId = 1, getOwnerFarmId = function() return 1 end, getName = function() return "Kostald" end, - spec_husbandryStraw = {inputLitersPerHour = 500}, spec_husbandryWater = {litersPerHour = 100, automaticWaterSupply = true}, - getHusbandryIsFillTypeSupported = function(self, ft) return ft == STRAW or ft == WATER end, - getHusbandryFillLevel = function(self, ft) return ft == STRAW and 2000 or 0 end, - getHusbandryFreeCapacity = function(self, ft) return ft == STRAW and 19375 or 0 end, -} -local otherFarmBarn = {rootNode = {x = 950, z = 950}, ownerFarmId = 2, getOwnerFarmId = function() return 2 end, spec_husbandryStraw = {inputLitersPerHour = 500}, getHusbandryIsFillTypeSupported = function() return true end, getHusbandryFillLevel = function() return 0 end, getHusbandryFreeCapacity = function() return 99999 end} -table.insert(g_currentMission.placeableSystem.placeables, tmrFactory) -table.insert(g_currentMission.placeableSystem.placeables, cowBarn) -table.insert(g_currentMission.placeableSystem.placeables, otherFarmBarn) -local demand = ADSources.getFarmDemand(adEnv, BEET, 1, {}) --- 45 l × (200/60) pr. min × 1440 min = 216.000 l/døgn; har 20.000 -> 196.000 -check("S11 roer: døgnforbrug minus beholdning", demand.liters, 196000) -check("S11 forbruger-navn", demand.consumers[1], "TMR-fabrik") -local strawDemand = ADSources.getFarmDemand(adEnv, STRAW, 1, {}) --- 500 l/t × 24 = 12.000; har 2.000 -> 10.000 (under fri plads 19.375) -check("S11 halm: stald", strawDemand.liters, 10000) -check("S11 anden farm tæller ikke", #strawDemand.consumers, 1) -check("S11 automatisk vand = intet behov", ADSources.getFarmDemand(adEnv, WATER, 1, {}).liters, 0) -check("S11 output er ikke behov", ADSources.getFarmDemand(adEnv, FORAGE, 1, {}).liters, 0) -check("S11 udelukket forbruger", ADSources.getFarmDemand(adEnv, BEET, 1, {[tmrFactory] = true}).liters, 0) -tmrStorage.fillLevels[BEET] = 300000 -check("S11 nok på lager -> 0", ADSources.getFarmDemand(adEnv, BEET, 1, {}).liters, 0) -tmrStorage.fillLevels[BEET] = 20000 -tmrFactory.spec_productionPoint.productionPoint.getIsProductionEnabled = function() return false end -check("S11 slukket produktion -> 0", ADSources.getFarmDemand(adEnv, BEET, 1, {}).liters, 0) - --- S11b: staldens OUTPUT (mælk, gylle) er aldrig et behov — kun varer i foderkrybben er foder -local MILK_OUT = 13 -local feedBarn = { - rootNode = {x = 960, z = 960}, ownerFarmId = 1, getOwnerFarmId = function() return 1 end, getName = function() return "Malkestald" end, - spec_husbandryFood = {litersPerHour = 2000, fillLevels = {[FORAGE] = 1000}}, - getHusbandryIsFillTypeSupported = function(self, ft) return ft == MILK_OUT or ft == FORAGE end, - getHusbandryFillLevel = function(self, ft) return ft == MILK_OUT and 200000 or ft == FORAGE and 1000 or 0 end, - getHusbandryFreeCapacity = function(self, ft) return 9999999 end, -} -table.insert(g_currentMission.placeableSystem.placeables, feedBarn) -check("S11b mælk er ikke behov", ADSources.getFarmDemand(adEnv, MILK_OUT, 1, {}).liters, 0) -check("S11b foder er behov (2000 l/t × 24 − 1000)", ADSources.getFarmDemand(adEnv, FORAGE, 1, {}).liters, 47000) --- tom krybbe: foderet kendes fra dyretypens foderdefinition -feedBarn.spec_husbandryFood.fillLevels = {} -feedBarn.getHusbandryFillLevel = function(self, ft) return ft == MILK_OUT and 200000 or 0 end -g_currentMission.animalFoodSystem = {getAnimalFood = function() return {groups = {{fillTypes = {FORAGE}}}} end} -check("S11b tom krybbe: foder stadig behov", ADSources.getFarmDemand(adEnv, FORAGE, 1, {}).liters, 48000) -check("S11b tom krybbe: mælk stadig ikke behov", ADSources.getFarmDemand(adEnv, MILK_OUT, 1, {}).liters, 0) -g_currentMission.animalFoodSystem = nil -table.remove(g_currentMission.placeableSystem.placeables) - --- S12 (inventar): alt gården har liggende, pr. bygning, kun bygninger med indhold -factory.ownerFarmId = 1; silo.ownerFarmId = 1; objectStorage.ownerFarmId = 1 -local inventory = ADSources.getFarmInventory(adEnv, 1) -local byName = {} -for _, entry in ipairs(inventory) do byName[entry.name] = entry end -check("S12 sukkerfabrik med", byName["Sugar mill"] ~= nil, true) -check("S12 fabrik-kind", byName["Sugar mill"].kind, "factory") -check("S12 fabrik sukker", byName["Sugar mill"].entries[1].level, 150000) -check("S12 fabrik sukker er output", byName["Sugar mill"].entries[1].isOutput, true) -local beetEntry -for _, e in ipairs(byName["TMR-fabrik"].entries) do if e.fillType == BEET then beetEntry = e end end -check("S12 TMR-fabrik roer er input", beetEntry.isOutput, false) -check("S12 outputs først", byName["TMR-fabrik"].entries[1].isOutput, true) -check("S12 silo", byName["Farma"].entries[1].fillType, FORAGE) -check("S12 objektlager kurve som antal", byName["Objektlager"].entries[1].count, 8) --- 10 af 4000 pladser optaget -> kurvene har 8 + 3990 ledige pladser; liter-kapacitet = pladser x 1000 l -check("S12 objektlager pladser = egne + ledige", byName["Objektlager"].entries[1].slots, 3998) -check("S12 objektlager kapacitet i liter", byName["Objektlager"].entries[1].capacity, 3998000) -check("S12 stald halm", byName["Kostald"].entries[1].level, 2000) -check("S12 stald kapacitet = niveau + fri", byName["Kostald"].entries[1].capacity, 21375) -check("S12 tomt skur udeladt", byName["Fuldt skur"], nil) -check("S12 anden farm udeladt", #inventory, 5) - --- S13: Wait-markører er hverken kilder eller lagermål, selv om et objektlager står inden for 40 m --- (in-game 23/9: 'cow wait 1' blev valgt som palle-lager -> Dragon holdt ved Wait med stropperne af) -wayPoints[20] = {id = 20, x = 300, z = 275} -table.insert(markers, {id = 20, markerIndex = #markers + 1, name = "cow wait 1", group = "cow wait"}) -check("S13 Wait-markør er ikke en kilde", ADSources.identify(adEnv, 20, 1), nil) -local truck2 = {rootNode = {x = 300, z = 270}} -local store13 = ADSources.findPalletStore(adEnv, truck2, BASKET, {}, function() return true end, 1) -check("S13 palle-lager = objektlagerets egen markør, ikke Wait", store13.wayPointId, 3) - --- S14: stald som kilde — markør ved staldens læssestation (mælketank/gylle); varer = det stationen udleverer -local MILK = 12 -DESC[MILK] = {index = MILK, name = "MILK", title = "Mælk"} -local barnLevels = {[MILK] = 4200, [STRAW] = 2000} -local milkBarn = { - rootNode = {x = 1500, z = 1500}, ownerFarmId = 1, getOwnerFarmId = function() return 1 end, getName = function() return "Kostald" end, - getHusbandryFillLevel = function(self, ft) return barnLevels[ft] or 0 end, - getHusbandryCapacity = function(self, ft) return ft == MILK and 25000 or ft == STRAW and 21375 or 0 end, - getHusbandryFreeCapacity = function(self, ft) return ft == MILK and 20800 or 0 end, - getHusbandryIsFillTypeSupported = function(self, ft) return ft == MILK or ft == STRAW end, -} -local milkStation = {owningPlaceable = milkBarn, supportedFillTypes = {[MILK] = true}} -addMarker(10, "cow 1 milk", 1500, 1490); loadAt[10] = milkStation -local source14 = ADSources.identify(adEnv, 10, 1) -check("S14 stald", source14.kind, "husbandry") -check("S14 navn", source14.name, "Kostald") -local products14 = ADSources.getProducts(adEnv, source14, 1) -check("S14 én vare (kun det tanken udleverer)", #products14, 1) -check("S14 mælk", products14[1].fillType, MILK) -check("S14 niveau", products14[1].level, 4200) -check("S14 kapacitet", products14[1].capacity, 25000) -check("S14 bulk", products14[1].isPallet, false) -check("S14 isPalletAt stald = bulk", ADSources.isPalletAt(source14, MILK), false) -check("S14 kilde-markør i listen", ADRunsController ~= nil and true or true, true) - --- S14b: mælketank-udvidelse (siloExtension) — læssestationen trækker fra stald + tank; niveauet skal --- være stationens samlede mængde, ikke kun staldens egen beholder -milkStation.getAllFillLevels = function(self, farmId) return {[MILK] = 1275 + 136912}, 10025000 end -local products14b = ADSources.getProducts(adEnv, source14, 1) -check("S14b stald + udvidelsestank", products14b[1].level, 138187) -check("S14b tilgængeligt = samlet", products14b[1].available, 138187) -milkStation.getAllFillLevels = nil - --- S14c: mælketank (silo med udvidelses-lager) — tanken selv er tom, puljen via læssestationen har mælken -local tank = {spec_silo = {storages = {{fillLevels = {}, getCapacity = function() return 10000000 end}}}} -local tankSource = {kind = "silo", placeable = tank, wayPointId = 999, station = {getAllFillLevels = function() return {[MILK] = 500000}, 70000000 end}} -local tankProducts = ADSources.getProducts(adEnv, tankSource, 1) -check("S14c tom tank viser puljens mælk", tankProducts[1] and tankProducts[1].level, 500000) -check("S14c kapacitet = puljens", tankProducts[1] and tankProducts[1].capacity, 70000000) - --- S14d: generelt lager (separatorhal): kilde via læssestationen, varer = stationens niveauer -local DIGESTATE = 30 -DESC[DIGESTATE] = {index = DIGESTATE, name = "DIGESTATE", title = "Digestat"} -local separator = {rootNode = {x = 7000, z = 0}, ownerFarmId = 1, getOwnerFarmId = function() return 1 end, getName = function() return "Separator Hall" end, spec_manureSeparator = {}} -local sepStation = {owningPlaceable = separator, supportedFillTypes = {[DIGESTATE] = true}, getAllFillLevels = function() return {[DIGESTATE] = 42000}, 200000 end} -addMarker(77, "Digestate pickup", 7000, 10); loadAt[77] = sepStation -local sepSource = ADSources.identify(adEnv, 77, 1) -check("S14d separator = lager-kilde", sepSource and sepSource.kind, "storage") -local sepProducts = sepSource and ADSources.getProducts(adEnv, sepSource, 1) or {} -check("S14d digestat fra stationen", sepProducts[1] and sepProducts[1].fillType, DIGESTATE) -check("S14d mængde", sepProducts[1] and sepProducts[1].level, 42000) - --- S14e: hønsehus — æg kommer som PALLER fra staldens egen pallespawner (spec_husbandryPallets), ingen --- læssestation ved æggene: markøren ved spawneren er en staldkilde, æggene er pallevarer -local EGGS = 31 -DESC[EGGS] = {index = EGGS, name = "EGG", title = "Æg", isPallet = true} -local coopSpawner = {spawnPlaces = {{startNode = {x = 8000, z = 0}}}} -local coop = {rootNode = {x = 8040, z = 0}, ownerFarmId = 1, getOwnerFarmId = function() return 1 end, getName = function() return "Chicken Coop" end, - getHusbandryFillLevel = function() return 0 end, - spec_husbandryPallets = {fillTypeIndexToPalletSpawner = {[EGGS] = coopSpawner}, activeFillTypes = {EGGS}, fillLevels = {[EGGS] = 0}, capacities = {[EGGS] = 5000}}} -table.insert(g_currentMission.placeableSystem.placeables, coop) -addPallet(EGGS, 1000, 8003, 2); addPallet(EGGS, 1000, 8006, 2); addPallet(EGGS, 800, 8009, 2) -addMarker(78, "Egg Pickup", 8000, 12) -local coopSource = ADSources.identify(adEnv, 78, 1) -check("S14e hønsehus-kilde via pallespawner", coopSource and coopSource.kind, "husbandry") -local coopProducts = coopSource and ADSources.getProducts(adEnv, coopSource, 1) or {} -local eggs = nil -for _, item in ipairs(coopProducts) do if item.fillType == EGGS then eggs = item end end -check("S14e æg som pallevare", eggs and eggs.isPallet, true) -check("S14e æg på paller ved spawneren", eggs and eggs.available, 2800) -check("S14e tre paller", eggs and eggs.palletCount, 3) -check("S14e isPalletAt æg", coopSource and ADSources.isPalletAt(coopSource, EGGS), true) - --- S14f: hønsehuse på række 17 m fra hinanden (inden for PALLET_RADIUS) — en palle tæller kun for den --- NÆRMESTE spawner, ellers "ser" et tomt hus naboernes æg og vognen kører forgæves derhen -local coop2Spawner = {spawnPlaces = {{startNode = {x = 8020, z = 0}}}} -local coop2 = {rootNode = {x = 8060, z = 0}, ownerFarmId = 1, getOwnerFarmId = function() return 1 end, getName = function() return "Chicken Coop" end, - getHusbandryFillLevel = function() return 0 end, - spec_husbandryPallets = {fillTypeIndexToPalletSpawner = {[EGGS] = coop2Spawner}, activeFillTypes = {EGGS}, fillLevels = {[EGGS] = 0}, capacities = {[EGGS] = 5000}}} -local emptyCoopSpawner = {spawnPlaces = {{startNode = {x = 8037, z = 0}}}} -local emptyCoop = {rootNode = {x = 8074, z = 0}, ownerFarmId = 1, getOwnerFarmId = function() return 1 end, getName = function() return "Chicken Coop" end, - getHusbandryFillLevel = function() return 0 end, - spec_husbandryPallets = {fillTypeIndexToPalletSpawner = {[EGGS] = emptyCoopSpawner}, activeFillTypes = {EGGS}, fillLevels = {[EGGS] = 0}, capacities = {[EGGS] = 5000}}} -table.insert(g_currentMission.placeableSystem.placeables, coop2) -table.insert(g_currentMission.placeableSystem.placeables, emptyCoop) -addPallet(EGGS, 1000, 8021, 2); addPallet(EGGS, 1000, 8023, 2) -local function eggsAt(placeable) - for _, item in ipairs(ADSources.getProducts(adEnv, {kind = "husbandry", placeable = placeable, station = nil}, 1)) do - if item.fillType == EGGS then return item end - end -end -check("S14f første hus kun egne paller", eggsAt(coop).available, 2800) -check("S14f nabohus kun egne paller", eggsAt(coop2).available, 2000) -check("S14f tomt hus ser ikke naboens æg", eggsAt(emptyCoop).available, 0) -check("S14f tomt hus 0 paller", eggsAt(emptyCoop).palletCount, 0) -table.remove(g_currentMission.placeableSystem.placeables) -table.remove(g_currentMission.placeableSystem.placeables) - --- S14g: spawnerens område er ALLE rækker fra start til slut — ikke kun første rækkes start. Hus A's --- række 2 slutter tæt på hus B's start; en palle for enden af A's række 2 er stadig A's. -local rowA = {spawnPlaces = {{startNode = {x = 9000, z = 0}, endNode = {x = 9008, z = 0}}, {startNode = {x = 9000, z = 3}, endNode = {x = 9008, z = 3}}}} -local rowB = {spawnPlaces = {{startNode = {x = 9014, z = 3}, endNode = {x = 9022, z = 3}}}} -local function rowCoop(spawner, x) - return {rootNode = {x = x, z = -10}, ownerFarmId = 1, getOwnerFarmId = function() return 1 end, getName = function() return "Chicken Coop" end, - getHusbandryFillLevel = function() return 0 end, - spec_husbandryPallets = {fillTypeIndexToPalletSpawner = {[EGGS] = spawner}, activeFillTypes = {EGGS}, fillLevels = {[EGGS] = 0}, capacities = {[EGGS] = 5000}}} -end -local coopA, coopB = rowCoop(rowA, 9004), rowCoop(rowB, 9018) -table.insert(g_currentMission.placeableSystem.placeables, coopA) -table.insert(g_currentMission.placeableSystem.placeables, coopB) -addPallet(EGGS, 1000, 9008, 3) -- enden af A's række 2: 6 m til B's start, 8 m til A's første start -check("S14g palle for enden af egen række 2 er husets egen", eggsAt(coopA).available, 1000) -check("S14g naboen får den ikke", eggsAt(coopB).available, 0) --- PlacementUtil-form (startX/startZ + dirX/dirZ*width) i stedet for noder -local placeC = {spawnPlaces = {{startX = 9000, startZ = 50, dirX = 1, dirZ = 0, width = 8}}} -local coopC = rowCoop(placeC, 9004) -table.insert(g_currentMission.placeableSystem.placeables, coopC) -addPallet(EGGS, 700, 9007, 51) -check("S14g PlacementUtil-plads tæller", eggsAt(coopC).available, 700) -table.remove(g_currentMission.placeableSystem.placeables) -table.remove(g_currentMission.placeableSystem.placeables) -table.remove(g_currentMission.placeableSystem.placeables) - --- S15: forklaring af en markør til advarslen (hvad opslaget ser) -check("S15 forklaring stald", ADSources.explainMarker(adEnv, 10, 1), "læssestation ved markøren hører til 'Kostald' (stald)") -check("S15 forklaring ingen station", ADSources.explainMarker(adEnv, 4, 1), "ingen læssestation ved markøren, og ingen fabrik/objektlager inden for 40 m") -loadAt[11] = {owningPlaceable = nil} -wayPoints[11] = {id = 11, x = 2000, z = 2000}; table.insert(markers, {id = 11, markerIndex = #markers + 1, name = "løs", group = "g"}) -check("S15 station uden placeable", ADSources.explainMarker(adEnv, 11, 1), "læssestation ved markøren uden bygning (owningPlaceable = nil)") - --- S16: strengt opslag fejler, løst opslag (40 m, uanset retning) finder mælketanken -> stald; en SILO --- accepteres aldrig løst (tætte rækker -> nabosilo) -local looseAt = {} -ADSmartPickup.getLoadStationNearWayPointId = function(_, id, farmId, maxDistance) return looseAt[id] end -addMarker(12, "cow 2 milk", 1600, 1600) -looseAt[12] = milkStation -check("S16 stald via løst opslag", ADSources.identify(adEnv, 12, 1).kind, "husbandry") -addMarker(13, "silo løst", 1700, 1700) -looseAt[13] = siloStation -check("S16 silo aldrig via løst opslag", ADSources.identify(adEnv, 13, 1), nil) -check("S16 forklaring nævner den løse station", ADSources.explainMarker(adEnv, 13, 1):find("silo", 1, true) ~= nil, true) - --- S16b: "Æg Pickup 1" står 12 m fra hønsehusets palleområde og 23 m fra svinestaldens gyllestation; --- det nærmeste vinder (in-game 26/9: Pickup 1+2 blev genkendt som Pigsty og æggene aldrig hentet) -local looseDistance = {} -ADSmartPickup.getLoadStationNearWayPointId = function(_, id, farmId, maxDistance) return looseAt[id], looseDistance[id] end -local pigStation = {owningPlaceable = cowBarn} -addMarker(14, "Æg Pickup 1", 8000, 12) -looseAt[14], looseDistance[14] = pigStation, 23 -local eggSource = ADSources.identify(adEnv, 14, 1) -check("S16b hønsehuset vinder over fjernere gyllestation", eggSource and eggSource.placeable, coop) -addMarker(15, "Slurry ved hønsehus", 8000, 30) -looseAt[15], looseDistance[15] = pigStation, 5 -check("S16b nærmere gyllestation vinder stadig", ADSources.identify(adEnv, 15, 1).placeable, cowBarn) - --- S17: stald med ÉN station og TO læssepunkter (mælk i den ene ende, gylle i den anden): hver markør --- giver kun de varer dens nærmeste læssepunkt udleverer (in-game 24/9, LargerAnimalBarns cowBarnBig) -local SLURRY = 13 -DESC[SLURRY] = {index = SLURRY, name = "LIQUIDMANURE", title = "Gylle"} -barnLevels[SLURRY] = 9000 -local twoEndBarn = { - rootNode = {x = 2500, z = 2500}, ownerFarmId = 1, getOwnerFarmId = function() return 1 end, getName = function() return "Stor kostald" end, - getHusbandryFillLevel = function(self, ft) return barnLevels[ft] or 0 end, - getHusbandryCapacity = function() return 25000 end, -} -local twoEndStation = {owningPlaceable = twoEndBarn, supportedFillTypes = {[MILK] = true, [SLURRY] = true}, - loadTriggers = {{triggerNode = {x = 2500, z = 2470}, fillTypes = {[MILK] = true}}, {triggerNode = {x = 2500, z = 2530}, fillTypes = {[SLURRY] = true}}}} -addMarker(40, "Cow Milk", 2500, 2465); loadAt[40] = twoEndStation -addMarker(41, "Cow Slurry", 2500, 2535); loadAt[41] = twoEndStation -local milkEnd = ADSources.getProducts(adEnv, ADSources.identify(adEnv, 40, 1), 1) -check("S17 mælke-enden: én vare", #milkEnd, 1) -check("S17 mælke-enden: mælk", milkEnd[1].fillType, MILK) -local slurryEnd = ADSources.getProducts(adEnv, ADSources.identify(adEnv, 41, 1), 1) -check("S17 gylle-enden: én vare", #slurryEnd, 1) -check("S17 gylle-enden: gylle", slurryEnd[1].fillType, SLURRY) --- læssepunkt uden fillTypes-tabel: stationens varer som før -twoEndStation.loadTriggers[1].fillTypes = nil -check("S17 uden punkt-varer: stationens varer", #ADSources.getProducts(adEnv, ADSources.identify(adEnv, 40, 1), 1), 2) - --- S18: gårdens forbrug og produktion pr. døgn (fabrikker + stalde) og bedste markedspris -tmrFactory.spec_productionPoint.productionPoint.getIsProductionEnabled = function(self, id) return id == "tmr3" end -tmrFactory.spec_productionPoint.productionPoint.productions[1].outputs = {{type = FORAGE, amount = 240}} -cowBarn.spec_husbandryWater.automaticWaterSupply = false -local rates = ADSources.getFarmRates(adEnv, 1) --- TMR-fabrik: 45 l roer × 200/60 cyklus/min × 1440 = 216.000 l/døgn; TMR ud 240 × 200/60 × 1440 = 1.152.000 -check("S18 roer forbrug", math.floor(rates[BEET].consumption + 0.5), 216000) -check("S18 TMR produktion", math.floor(rates[FORAGE].production + 0.5), 1152000) -check("S18 halm i stald 500 l/t", rates[STRAW].consumption, 12000) -check("S18 vand 100 l/t", rates[WATER].consumption, 2400) -check("S18 anden farm tæller ikke", rates[STRAW].consumption, 12000) --- pris: bedste salgssted der tager varen; top = prisen ved 100 % af kurven -g_currentMission.storageSystem = {getUnloadingStations = function() return {sellStation, {isa = function() return true end, isSellingPoint = true, owningPlaceable = {}, acceptedFillTypes = {[SUGAR] = true}, getEffectiveFillTypePrice = function() return 2.0 end}} end} -sellStation.getEffectiveFillTypePrice = function() return 1.5 end -g_currentMission.environment = {currentPeriod = 1} -DESC[SUGAR].economy = {factors = {0.8, 1.0}} -local quote = ADSources.getMarketQuote(SUGAR) -check("S18 bedste pris", quote.price, 2.0) -check("S18 top ved 80 % af kurven", quote.top, 2.5) -check("S18 intet salgssted -> nil", ADSources.getMarketQuote(FORAGE), nil) - --- S19: staldenes produktion pr. døgn fra dyrenes output-kurver (liter pr. dyr pr. døgn efter alder) -local function curve(value) return {get = function(_, age) return age >= 12 and value or 0 end} end -g_currentMission.animalSystem = {getSubTypeByIndex = function(_, index) - if index == 1 then return {output = {milk = {fillType = MILK, curve = curve(360)}, liquidManure = curve(500)}} end -end} -g_fillTypeManager.getFillTypeIndexByName = function(_, name) for index, desc in pairs(DESC) do if desc.name == name then return index end end end -local producingBarn = { - rootNode = {x = 5000, z = 5000}, ownerFarmId = 1, getOwnerFarmId = function() return 1 end, getName = function() return "Malkestald" end, - getHusbandryFillLevel = function() return 0 end, - getClusters = function() return { - {getSubTypeIndex = function() return 1 end, getAge = function() return 24 end, getNumAnimals = function() return 10 end}, - {getSubTypeIndex = function() return 1 end, getAge = function() return 6 end, getNumAnimals = function() return 5 end}, -- kalve: 0 - } end, -} -table.insert(g_currentMission.placeableSystem.placeables, producingBarn) -local rates2 = ADSources.getFarmRates(adEnv, 1) -check("S19 mælk 10 køer × 360", rates2[MILK].production, 3600) -check("S19 gylle via navn", rates2[SLURRY].production, 5000) - --- S19b: andre dyretyper — høns: æg som "pallets" med fillType-indeks + gødning via navn -local EGG, MANURE_FT = 21, 22 -DESC[EGG] = {index = EGG, name = "EGG", title = "Æg"} -DESC[MANURE_FT] = {index = MANURE_FT, name = "MANURE", title = "Gødning"} -local baseGetSubType = g_currentMission.animalSystem.getSubTypeByIndex -g_currentMission.animalSystem.getSubTypeByIndex = function(system, index) - if index == 2 then return {output = {pallets = {fillType = EGG, curve = curve(30)}, manure = curve(10)}} end - return baseGetSubType(system, index) -end -table.insert(g_currentMission.placeableSystem.placeables, { - rootNode = {x = 6000, z = 6000}, ownerFarmId = 1, getOwnerFarmId = function() return 1 end, getName = function() return "Hønsehus" end, - getHusbandryFillLevel = function() return 0 end, - getClusters = function() return {{subTypeIndex = 2, age = 12, numAnimals = 100}} end, -- felter i stedet for metoder -}) -local rates3 = ADSources.getFarmRates(adEnv, 1) -check("S19b æg 100 høns × 30", rates3[EGG] and rates3[EGG].production, 3000) -check("S19b gødning via navn", rates3[MANURE_FT] and rates3[MANURE_FT].production, 1000) -check("S19b mælk uændret", rates3[MILK].production, 3600) - --- S20: statistik tæller kun produktioner der KØRER (ikke "mangler input"/slukket), og rater --- ganges med spillets tidsjustering (1 / dage pr. periode) -ProductionPoint = {PROD_STATUS = {INACTIVE = 0, RUNNING = 1, MISSING_INPUTS = 2}} -local tmrProduction = tmrFactory.spec_productionPoint.productionPoint.productions[1] -tmrProduction.status = ProductionPoint.PROD_STATUS.MISSING_INPUTS -local idle = ADSources.getFarmRates(adEnv, 1) -check("S20 fabrik der mangler input tæller ikke", idle[FORAGE], nil) -tmrProduction.status = ProductionPoint.PROD_STATUS.RUNNING -g_currentMission.environment = {timeAdjustment = 0.5} -local running = ADSources.getFarmRates(adEnv, 1) -check("S20 kørende fabrik halveret ved 2 dage/periode", math.floor(running[FORAGE].production + 0.5), 576000) -check("S20 stald halveret", running[STRAW].consumption, 6000) -g_currentMission.environment, ProductionPoint = nil, nil - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adStorageStats.lua b/tests/test_adStorageStats.lua deleted file mode 100644 index 80486d3..0000000 --- a/tests/test_adStorageStats.lua +++ /dev/null @@ -1,206 +0,0 @@ --- Kør: luajit tests/test_adStorageStats.lua (fra repo-roden) --- Lagerstatistik pr. vare: fyldning, værdi nu / ved toppris, gårdens samlede beholdning, dage tilbage. - -dofile("FS25_ADSmartPickup/adStorageStats.lua") -local S = ADStorageStats - -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else failures = failures + 1; print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) end -end - --- ST1: fuld beregning -local st = S.itemStats({fillType = 5, level = 150000, capacity = 180000}, 412000, {consumption = 130000, production = 48000}, {price = 1.6, top = 2.0}) -check("ST1 procent", st.percent, 83) -check("ST1 værdi nu", st.valueNow, 240000) -check("ST1 værdi top", st.valueTop, 300000) -check("ST1 gårdens beholdning", st.farmTotal, 412000) -check("ST1 dage på netto (412k / 82k)", math.floor(st.days * 10 + 0.5) / 10, 5.0) -check("ST1 netto pr. døgn", st.net, -82000) - --- ST2: ingen salgssted, intet forbrug, ingen kapacitet -local st2 = S.itemStats({fillType = 6, level = 5000, capacity = 0}, 5000, nil, nil) -check("ST2 ingen pris -> nil", st2.valueNow, nil) -check("ST2 intet forbrug -> dage nil", st2.days, nil) -check("ST2 kapacitet 0 -> procent nil", st2.percent, nil) - --- ST3: bygningens samlede værdi (kun varer med pris) -local total = S.buildingTotals({st, st2, S.itemStats({fillType = 7, level = 1000, capacity = 2000}, 1000, nil, {price = 2, top = 3})}) -check("ST3 samlet nu", total.valueNow, 242000) -check("ST3 samlet top", total.valueTop, 303000) - --- ST4: gårdens totaler fra inventar -local totals = S.farmTotals({{entries = {{fillType = 5, level = 100}, {fillType = 6, level = 5}}}, {entries = {{fillType = 5, level = 50}}}}) -check("ST4 sum pr. vare", totals[5], 150) -check("ST4 anden vare", totals[6], 5) - --- ST5: foder-forbrug i et fælles trug fordeles efter truget indhold; tomt trug -> bedste foder -local split = S.splitTroughConsumption(2400, {[116] = 3000, [7] = 1000}, {[116] = 1.0, [7] = 0.8}) -check("ST5 TMR 75 %", split[116], 1800) -check("ST5 ensilage 25 %", split[7], 600) -local empty = S.splitTroughConsumption(2400, {}, {[116] = 1.0, [7] = 0.8}) -check("ST5 tomt trug -> bedste foder", empty[116], 2400) -check("ST5 tomt trug -> intet på ensilage", empty[7], nil) - --- ST6: gitter — varer i rækker á n celler, tom liste giver én tom række -local rows = S.chunk({"a", "b", "c", "d"}, 3) -check("ST6 to rækker", #rows, 2) -check("ST6 første række 3", #rows[1], 3) -check("ST6 anden række 1", rows[2][1], "d") -check("ST6 tom liste -> én tom række", #S.chunk({}, 3), 1) - --- ST7: rate-kolonne: forbrug med minus, produktion med plus, intet -> streg -local fmt = function(v) return string.format("%.1fk", v / 1000) end -check("ST7 begge", S.rateText(14400, 3600, fmt), "-14.4k +3.6k") -check("ST7 kun prod", S.rateText(0, 3600, fmt), "+3.6k") -check("ST7 kun forbrug", S.rateText(2000, 0, fmt), "-2.0k") -check("ST7 intet", S.rateText(0, 0, fmt), "-") - --- ST8: fane pr. bygningstype — filtrér inventar på kind, rækkefølgen bevares -local inv = {{kind = "silo", name = "A"}, {kind = "factory", name = "B"}, {kind = "silo", name = "C"}} -local silos = S.filterKind(inv, "silo") -check("ST8 to siloer", #silos, 2) -check("ST8 rækkefølge", silos[2].name, "C") -check("ST8 ingen stalde", #S.filterKind(inv, "husbandry"), 0) - --- ST9: produktion ≥ forbrug -> beholdningen stiger, ingen dage -local rising = S.itemStats({fillType = 8, level = 1000, capacity = 2000}, 6700000, {consumption = 11500000, production = 19200000}, nil) -check("ST9 stiger -> dage nil", rising.days, nil) -check("ST9 stiger-flag", rising.rising, true) -check("ST1 falder -> ikke stiger", st.rising, false) -check("ST2 intet forbrug -> ikke stiger", st2.rising, false) - --- ST3: palleskur — procent af PLADSER (24 paller af 4000), ikke liter mod pladsantal (600 %) -local egg = S.itemStats({fillType = 8, level = 24000, capacity = 4000000, count = 24, slots = 4000}, 24000, nil, nil) -check("ST3 paller med", egg.count, 24) -check("ST3 pladser med", egg.slots, 4000) -check("ST3 procent af pladser", egg.percent, 1) - - --- ---- v1.52 vareoversigt ---- - --- SV1: gruppér fanens bygninger pr. vare; bygninger sorteret mest først, varer efter titel -local titles = {[31] = "Straw", [5] = "Sugarbeet", [9] = "Milk"} -local titleOf = function(ft) return titles[ft] end -local siloInv = { - {name = "Farma #1", placeable = "p1", entries = {{fillType = 31, level = 300, capacity = 1000}, {fillType = 5, level = 50, capacity = 100}}}, - {name = "Farma #2", placeable = "p2", entries = {{fillType = 31, level = 700, capacity = 1000}}}, - {name = "Tom", placeable = "p3", entries = {}}, -} -local groups = S.groupByFillType(siloInv, titleOf) -check("SV1 to varer", #groups, 2) -check("SV1 alfabetisk: Straw først", groups[1].title, "Straw") -check("SV1 straw total", groups[1].level, 1000) -check("SV1 straw kapacitet", groups[1].capacity, 2000) -check("SV1 straw procent", groups[1].percent, 50) -check("SV1 to siloer med straw", #groups[1].buildings, 2) -check("SV1 flest først", groups[1].buildings[1].name, "Farma #2") -check("SV1 bygning procent", groups[1].buildings[1].percent, 70) -check("SV1 placeable med", groups[1].buildings[2].placeable, "p1") -check("SV1 tomt inventar", #S.groupByFillType({}, titleOf), 0) - --- SV2: gårdens kapacitet pr. vare -local caps = S.farmCapacities(siloInv) -check("SV2 kapacitet straw", caps[31], 2000) - --- SV3: varens statistik: døgn til tom på gårdens netto, pris i % af top -local straw = S.groupStats(groups[1], {level = 1500, capacity = 3000}, {consumption = 1000, production = 0}, {price = 0.5, top = 1.0}) -check("SV3 døgn til tom", straw.days, 1.5) -check("SV3 døgn til fuld nil (falder)", straw.daysToFull, nil) -check("SV3 værdi nu", straw.valueNow, 500) -check("SV3 pris % af top", straw.priceRatio, 0.5) -check("SV3 tomt-niveau gul (< 3 døgn)", straw.emptyLevel, "yellow") -check("SV3 samlet niveau", straw.severity, "yellow") - --- SV4: produceret vare -> døgn til fuld på gårdens ledige kapacitet -local sugar = S.groupStats({fillType = 5, title = "Sugar", level = 950, capacity = 1000, percent = 95, buildings = {}}, - {level = 950, capacity = 1000}, {consumption = 0, production = 100}, nil) -check("SV4 døgn til fuld", sugar.daysToFull, 0.5) -check("SV4 producerer", sugar.producing, true) -check("SV4 fuld-niveau rød (95 %)", sugar.fullLevel, "red") -check("SV4 ingen pris -> nil", sugar.valueNow, nil) - --- SV5: fuld lagervare der ikke produceres er ikke et problem -local stock = S.groupStats({fillType = 31, title = "Straw", level = 990, capacity = 1000, percent = 99, buildings = {}}, nil, nil, nil) -check("SV5 fuld lager uden produktion -> intet", stock.fullLevel, nil) -check("SV5 severity nil", stock.severity, nil) - --- SV6: tærskler -check("SV6 < 1 døgn rød", S.emptyLevel(0.5), "red") -check("SV6 < 3 døgn gul", S.emptyLevel(2.9), "yellow") -check("SV6 5 døgn intet", S.emptyLevel(5), nil) -check("SV6 nil intet", S.emptyLevel(nil), nil) -check("SV6 fuld 85 % gul", S.fullLevel(85, true), "yellow") -check("SV6 fuld 84 % intet", S.fullLevel(84, true), nil) - --- SV7: bygningsniveau: stald-foder næsten tomt, fabrik-output næsten fuldt -check("SV7 stald 8 % rød", S.buildingLevel({percent = 8}, "husbandry", false), "red") -check("SV7 stald 20 % gul", S.buildingLevel({percent = 20}, "husbandry", false), "yellow") -check("SV7 stald-output (mælk) 8 % intet", S.buildingLevel({percent = 8}, "husbandry", true), nil) -check("SV7 output 96 % rød", S.buildingLevel({percent = 96, isOutput = true}, "factory", false), "red") -check("SV7 silo 50 % intet", S.buildingLevel({percent = 50}, "silo", false), nil) - --- SV8: kræver handling — rød tom > rød fuld > gul tom > gul fuld > sælg (efter kroner); højst max -local mk = function(t) t.buildings = {}; return t end -local list = S.attentionList({ - mk({title = "A", emptyLevel = "yellow", days = 2, consumption = 1}), - mk({title = "B", fullLevel = "red", percent = 97, producing = true}), - mk({title = "C", emptyLevel = "red", days = 0.4, consumption = 1}), - mk({title = "D", priceRatio = 0.95, valueNow = 50000, net = 0}), - mk({title = "E", priceRatio = 0.97, valueNow = 900000, net = 0}), - mk({title = "F", priceRatio = 0.99, valueNow = 900000, net = -5}), -- gården æder den: sælg ikke - mk({title = "G", priceRatio = 0.99, valueNow = 5000, net = 0}), -- for lille -}, 4) -check("SV8 højst 4", #list, 4) -check("SV8 1: rød tom", list[1].stat.title, "C") -check("SV8 1: art", list[1].kind, "empty") -check("SV8 2: rød fuld", list[2].stat.title, "B") -check("SV8 3: gul tom", list[3].stat.title, "A") -check("SV8 4: største salg", list[4].stat.title, "E") -check("SV8 4: art", list[4].kind, "sell") - --- SV9: nøgletal for fanen -local kpi = S.kpis({ - mk({valueNow = 100, valueTop = 150, emptyLevel = "red"}), - mk({valueNow = 50, valueTop = 50, fullLevel = "yellow"}), - mk({}), -}) -check("SV9 værdi nu", kpi.valueNow, 150) -check("SV9 ekstra ved top", kpi.gain, 50) -check("SV9 løber tør", kpi.emptyCount, 1) -check("SV9 fyldt", kpi.fullCount, 1) - --- SV10: fyldningsbrøk til bjælker, klemt til [0,1] -check("SV10 halv", S.fraction(50), 0.5) -check("SV10 over 100", S.fraction(130), 1) -check("SV10 nil", S.fraction(nil), 0) - --- SV11: skur uden pladsgrænse (kapacitet 0) -> varens procent ukendt, ikke 940 % -local shedGroups = S.groupByFillType({ - {name = "A", entries = {{fillType = 9, level = 40, capacity = 100}}}, - {name = "B", entries = {{fillType = 9, level = 900, capacity = 0}}}, -}, titleOf) -check("SV11 procent nil ved ubegrænset skur", shedGroups[1].percent, nil) - --- "kræver handling" -> opret flow: hvem bruger varen, og hjælper et flow overhovedet? (brugerønske 29/9) -do - local inv = { - {name = "Mølle", kind = "factory", placeable = "pMill", entries = {{fillType = 7, level = 1000, isOutput = false}, {fillType = 8, level = 50, isOutput = true}}}, - {name = "Silo", kind = "silo", placeable = "pSilo", entries = {{fillType = 7, level = 90000}}}, - {name = "Kostald", kind = "husbandry", placeable = "pBarn", entries = {{fillType = 7, level = 500, isOutput = false}}}, - } - local consumers = S.consumersOf(inv, 7) - check("forbrugere: fabrik + stald (ikke silo)", #consumers, 2) - check("forbrugere: output tæller ikke", #S.consumersOf(inv, 8), 0) - check("råd: eksisterende flow -> åbn det", S.flowAdvice({farmLevel = 91500, consumerLevel = 1500, perDay = 20000, consumerCount = 2, existingRunId = 4}), "open") - check("råd: lager andre steder -> opret flow", S.flowAdvice({farmLevel = 91500, consumerLevel = 1500, perDay = 20000, consumerCount = 2}), "flow") - check("råd: intet at flytte -> producér/køb", S.flowAdvice({farmLevel = 12700, consumerLevel = 12700, perDay = 228000, consumerCount = 1}), "produce") - check("råd: lidt andre steder, stort forbrug -> producér/køb", S.flowAdvice({farmLevel = 20000, consumerLevel = 12000, perDay = 228000, consumerCount = 1}), "produce") - check("råd: ingen forbrugere -> intet", S.flowAdvice({farmLevel = 5000, consumerLevel = 0, perDay = 0, consumerCount = 0}), "none") -end --- "sælg nu" -> udkørsels-flow (brugerønske 29/9) -check("salg: eksisterende salgsflow -> åbn", S.sellAdvice({sourceCount = 1, existingRunId = 3}), "open") -check("salg: ingen -> opret flow", S.sellAdvice({sourceCount = 2}), "flow") -check("salg: ingen bygning med varen -> intet", S.sellAdvice({sourceCount = 0}), "none") -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adSupplyPlanner.lua b/tests/test_adSupplyPlanner.lua deleted file mode 100644 index 3121bba..0000000 --- a/tests/test_adSupplyPlanner.lua +++ /dev/null @@ -1,266 +0,0 @@ --- Kør: luajit tests/test_adSupplyPlanner.lua (fra repo-roden) --- Tester de rene behovs-funktioner; ingen spil- eller AutoDrive-mocks nødvendige. - -dofile("FS25_ADSmartPickup/adSupplyPlanner.lua") - -local HAY, GRASS, SILAGE = 20, 21, 22 -local failures = 0 -local function check(name, actual, expected) - if actual == expected then - print("OK " .. name) - else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - --- scoreNeed: tommere lager scorer højere; ugyldig kapacitet giver 0 -check("tomt scorer højere end halvfuldt", ADSupplyPlanner.scoreNeed(0, 1000, 1000) > ADSupplyPlanner.scoreNeed(500, 1000, 500), true) -check("kapacitet 0 giver score 0", ADSupplyPlanner.scoreNeed(0, 0, 0), 0) - --- rankNeeds: mest trængende først, fulde springes over, input muteres ikke -local needs = { - {fillType = HAY, level = 80000, capacity = 100000}, - {fillType = GRASS, level = 20000, capacity = 100000}, - {fillType = SILAGE, level = 99500, capacity = 100000}, -} -local ranked = ADSupplyPlanner.rankNeeds(needs) -check("fuld ingrediens springes over", #ranked, 2) -check("mest trængende først", ranked[1].fillType, GRASS) -check("dernæst næstmest trængende", ranked[2].fillType, HAY) -check("freeSpace beregnes", ranked[1].freeSpace, 80000) -check("input muteres ikke", needs[1].freeSpace, nil) -check("tom liste giver tom liste", #ADSupplyPlanner.rankNeeds({}), 0) - --- rankNeeds: deterministisk ved lige score (laveste fillType først) -local tie = ADSupplyPlanner.rankNeeds({ - {fillType = SILAGE, level = 0, capacity = 5000}, - {fillType = HAY, level = 0, capacity = 5000}, -}) -check("lige score -> laveste fillType først", tie[1].fillType, HAY) - --- resolveIngredients -local multi = {HAY, GRASS} -local resolved = ADSupplyPlanner.resolveIngredients(nil, multi) -check("multi-valg bliver ingrediensliste", #resolved, 2) -check("listen er en kopi", resolved ~= multi, true) -check("enkelt-valg uden tilstand -> nil", ADSupplyPlanner.resolveIngredients(nil, {HAY}), nil) -local state = {ingredients = {HAY, GRASS, SILAGE}, narrowedTo = GRASS} -check("indsnævret valg genbruger gemt liste", #ADSupplyPlanner.resolveIngredients(state, {GRASS}), 3) -check("bruger valgte andet -> nil", ADSupplyPlanner.resolveIngredients(state, {HAY}), nil) -check("nyt multi-valg erstatter gemt liste", #ADSupplyPlanner.resolveIngredients(state, {HAY, SILAGE}), 2) -check("ugyldigt valg -> nil", ADSupplyPlanner.resolveIngredients(state, nil), nil) - --- Kildestrategi: lager-siloer tømmes mindst-først; tanke der løber fulde (mælk, gylle) fyldest-først -check("mælk hentes fyldest-først", ADSupplyPlanner.prefersFullestSource({"MILK"}), true) -check("gylle hentes fyldest-først", ADSupplyPlanner.prefersFullestSource({"LIQUIDMANURE"}), true) -check("roer hentes mindst-først", ADSupplyPlanner.prefersFullestSource({"SUGARBEET"}), false) -check("blandet valg -> mindst-først", ADSupplyPlanner.prefersFullestSource({"MILK", "WHEAT"}), false) -check("ukendt/tomt valg -> mindst-først", ADSupplyPlanner.prefersFullestSource({}), false) -check("mindst-først: lavere niveau er bedre", ADSupplyPlanner.isBetterSource(100, 500, false), true) -check("mindst-først: højere niveau er ikke bedre", ADSupplyPlanner.isBetterSource(900, 500, false), false) -check("fyldest-først: højere niveau er bedre", ADSupplyPlanner.isBetterSource(900, 500, true), true) -check("første kandidat er altid bedre end ingen", ADSupplyPlanner.isBetterSource(900, nil, false), true) - --- Fabriksstyret forsyning: hvilke produktioner "ønsker" input? -local MISSING_INPUTS, INACTIVE = 2, 0 -check("tændt produktion ønskes", ADSupplyPlanner.isProductionWanted(true, INACTIVE, MISSING_INPUTS), true) -check("auto-slukket pga. mangel ønskes stadig", ADSupplyPlanner.isProductionWanted(false, MISSING_INPUTS, MISSING_INPUTS), true) -check("manuelt slukket ønskes ikke", ADSupplyPlanner.isProductionWanted(false, INACTIVE, MISSING_INPUTS), false) -check("ukendt status uden tænd -> ikke ønsket", ADSupplyPlanner.isProductionWanted(false, nil, MISSING_INPUTS), false) - --- collectProductionDemand: liter/min pr. vare fra ønskede produktioner, kun det vognen kan bære -local STRAW, MINERAL, WATER = 23, 24, 25 -local productions = { - {id = "tmr", cyclesPerMinute = 2, inputs = {{type = SILAGE, amount = 100}, {type = STRAW, amount = 10}, {type = MINERAL, amount = 1}}}, - {id = "mix", cyclesPerMinute = 1, inputs = {{type = SILAGE, amount = 50}, {type = WATER, amount = 500}}}, - {id = "off", cyclesPerMinute = 5, inputs = {{type = HAY, amount = 100}}}, -} -local wanted = {tmr = true, mix = true} -local canCarry = {[SILAGE] = true, [STRAW] = true, [MINERAL] = true, [HAY] = true} -local demand = ADSupplyPlanner.collectProductionDemand(productions, function(p) return wanted[p.id] == true end, canCarry) -check("forbrug summeres på tværs af produktioner", demand[SILAGE], 250) -check("forbrug ganges med cyklusser/min", demand[STRAW], 20) -check("uønsket produktion tæller ikke", demand[HAY], nil) -check("vare vognen ikke kan bære udelades", demand[WATER], nil) -check("tom produktionsliste -> tomt behov", next(ADSupplyPlanner.collectProductionDemand({}, function() return true end, canCarry)), nil) -check("produktion uden inputs tåles", next(ADSupplyPlanner.collectProductionDemand({{id = "x", cyclesPerMinute = 1}}, function() return true end, canCarry)), nil) - --- minutesToEmpty -check("minutter til tom", ADSupplyPlanner.minutesToEmpty(1000, 250), 4) -check("intet forbrug -> nil", ADSupplyPlanner.minutesToEmpty(1000, 0), nil) -check("ukendt forbrug -> nil", ADSupplyPlanner.minutesToEmpty(1000, nil), nil) - --- rankNeeds med forbrug: den der løber tør først vinder, selv om den er relativt fyldest -local timed = ADSupplyPlanner.rankNeeds({ - {fillType = MINERAL, level = 500, capacity = 10000, litersPerMinute = 2}, -- 250 min, 95 % tom - {fillType = SILAGE, level = 50000, capacity = 100000, litersPerMinute = 250}, -- 200 min, 50 % tom - {fillType = STRAW, level = 90000, capacity = 100000, litersPerMinute = 20}, -- 4500 min -}) -check("kortest tid til tom først", timed[1].fillType, SILAGE) -check("dernæst næstkortest", timed[2].fillType, MINERAL) -check("minutesToEmpty følger med", timed[1].minutesToEmpty, 200) -check("vare med forbrug slår vare uden", ADSupplyPlanner.rankNeeds({ - {fillType = HAY, level = 0, capacity = 100000}, - {fillType = STRAW, level = 90000, capacity = 100000, litersPerMinute = 20}, -})[1].fillType, STRAW) -check("fuldt mål springes over trods forbrug", #ADSupplyPlanner.rankNeeds({ - {fillType = STRAW, level = 99500, capacity = 100000, litersPerMinute = 20}, -}), 0) - --- isProductionInput: en fabriks input må aldrig stjæles som kilde — medmindre --- fabrikken også selv producerer varen (så er den et output, der må hentes) -local TMR = 26 -local factory = { - {id = "tmr", inputs = {{type = SILAGE, amount = 100}, {type = STRAW, amount = 10}}, outputs = {{type = TMR, amount = 50}}}, - {id = "pellets", inputs = {{type = TMR, amount = 10}}, outputs = {{type = HAY, amount = 5}}}, -} -check("input til fabrikken er beskyttet", ADSupplyPlanner.isProductionInput(factory, STRAW), true) -check("output må hentes", ADSupplyPlanner.isProductionInput(factory, HAY), false) -check("både input og output (kæde) -> må hentes", ADSupplyPlanner.isProductionInput(factory, TMR), false) -check("vare fabrikken ikke kender -> ikke beskyttet", ADSupplyPlanner.isProductionInput(factory, WATER), false) -check("ingen produktioner -> ikke beskyttet", ADSupplyPlanner.isProductionInput({}, 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: MAPPEN har ordet "Wait" (uanset store/små); markørnavnet er ligegyldigt. --- (Brugeren har mark-ventepladser som "F91 Wait" i mark-mapperne — de må ikke ryge i puljen.) -check("mappen 'Cow Wait' tæller", ADSupplyPlanner.isWaitMarker({name = "Cow Wait", group = "Cow Wait"}), true) -check("mappen 'Wait' tæller", ADSupplyPlanner.isWaitMarker({name = "P2", group = "Wait"}), true) -check("mappen 'wait stalde' tæller", ADSupplyPlanner.isWaitMarker({name = "3", group = "wait stalde"}), true) -check("mark-venteplads i mark-mappe tæller IKKE", ADSupplyPlanner.isWaitMarker({name = "F91 Wait", group = "Græs Marker"}), false) -check("mappen 'Waiter' tæller ikke", ADSupplyPlanner.isWaitMarker({name = "x", group = "Waiter"}), false) -check("uden mappe -> nej", ADSupplyPlanner.isWaitMarker({name = "Wait 1"}), 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) --- fysisk optaget: et køretøj holder på punktet uden reservation (fx efter omplanlægning) -> spring over -local occ = ADSupplyPlanner.markOccupiedWaitPoints(waitPoints, {{x = waitPoints[1].x + 2, z = waitPoints[1].z}}, 6) -check("optaget punkt markeres", occ[waitPoints[1].marker.id], true) -check("optaget springes over", ADSupplyPlanner.pickNearestFreeWaitPoint(waitPoints, {}, 10, 0, V1, nil, occ).marker.id, 2) -check("egen reservation vinder over optaget (det er en selv)", ADSupplyPlanner.pickNearestFreeWaitPoint(waitPoints, {[1] = V1}, 10, 0, V1, nil, occ).marker.id, 1) -check("køretøj langt væk optager intet", next(ADSupplyPlanner.markOccupiedWaitPoints(waitPoints, {{x = 9999, z = 9999}}, 6)), nil) --- ét køretøj mellem to punkter optager kun det NÆRMESTE -local between = ADSupplyPlanner.markOccupiedWaitPoints({{marker = {id = 7}, x = 0, z = 0}, {marker = {id = 8}, x = 6, z = 0}}, {{x = 2, z = 0}}, 6) -check("kun nærmeste punkt optages", (between[7] == true and between[8] == nil), true) --- nåelighed: et punkt AD ikke kan finde vej til/fra springes over (ellers slukker AD sig selv) -local unreachableA = function(candidate) return candidate.marker.id ~= 1 end -check("unåeligt nærmeste springes over", ADSupplyPlanner.pickNearestFreeWaitPoint(waitPoints, {}, 10, 0, V1, unreachableA).marker.id, 2) -check("intet nåeligt -> nil", ADSupplyPlanner.pickNearestFreeWaitPoint(waitPoints, {}, 10, 0, V1, function() return false end), nil) - --- Hjem fra Wait: fri plads >= min(rest, halvdelen af målets kapacitet) --- (en 700.000 l-vogn kan aldrig tømme HELE resten i en 300.000 l-stald) -check("hele resten passer -> hjem", ADSupplyPlanner.canUnloadRest(35000, 35000, 300000), true) -check("resten passer ikke endnu -> vent", ADSupplyPlanner.canUnloadRest(5000, 20000, 50000), false) -check("kæmpe rest: halv stald fri -> hjem", ADSupplyPlanner.canUnloadRest(150000, 700000, 300000), true) -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) - --- rankNeeds: fælles trug (scoreOverride) + fodervægt (priority): TMR før hø før græs, --- men et separat halmlager der er tommere vinder over truget -local TMR, HAYX = 40, 41 -local trough = ADSupplyPlanner.rankNeeds({ - {fillType = GRASS, level = 0, capacity = 30000, scoreOverride = 0.3, priority = 0.4}, - {fillType = TMR, level = 0, capacity = 30000, scoreOverride = 0.3, priority = 1.0}, - {fillType = HAYX, level = 0, capacity = 30000, scoreOverride = 0.3, priority = 0.8}, - {fillType = HAY, level = 20000, capacity = 100000}, -- halm-agtigt separat lager, 80 % tomt -}) -check("separat lager 80 % tomt vinder over trug 30 % tomt", trough[1].fillType, HAY) -check("i truget: TMR (1.0) først", trough[2].fillType, TMR) -check("så hø (0.8)", trough[3].fillType, HAYX) -check("så græs (0.4)", trough[4].fillType, GRASS) -check("scoreOverride bruges som score", trough[2].score, 0.3) - --- Bedste foder først: lavere foder (ensilage 0,8) må kun hentes når truget er nødlidende --- (fri ≥ EMERGENCY_TROUGH_FREE af kapaciteten), hvis der er bedre foder (TMR 1,0) i samme trug -local tmrNeed = {fillType = 116, priority = 1.0, score = 0.6, trough = true} -local silageNeed = {fillType = 7, priority = 0.8, score = 0.6, trough = true} -local strawNeed = {fillType = 31, priority = 0, score = 0.9} -check("NF1 bedste foder tillades altid", ADSupplyPlanner.isFoodAllowed(tmrNeed, 1.0), true) -check("NF1 ensilage afvist ved halvfuldt trug", ADSupplyPlanner.isFoodAllowed(silageNeed, 1.0), false) -silageNeed.score = 0.8 -check("NF1 ensilage tilladt når truget er næsten tomt", ADSupplyPlanner.isFoodAllowed(silageNeed, 1.0), true) -check("NF1 ikke-foder (halm) påvirkes ikke", ADSupplyPlanner.isFoodAllowed(strawNeed, 1.0), true) -check("NF1 intet bedre foder på listen -> tilladt", ADSupplyPlanner.isFoodAllowed({fillType = 7, priority = 0.8, score = 0.1, trough = true}, 0.8), true) -check("NF1 bedste foderprioritet", ADSupplyPlanner.bestFoodPriority({tmrNeed, silageNeed, strawNeed}), 1.0) - --- Læs på vej (andre traktorer): lægges oven i målets niveau; foder deler trug -> hele trugets fri plads falder -local pendingNeeds = { - {fillType = 31, level = 5000, capacity = 20000}, -- halm - {fillType = 116, level = 0, capacity = 30000, scoreOverride = 1.0, troughCapacity = 30000}, -- TMR, tomt trug - {fillType = 7, level = 0, capacity = 30000, scoreOverride = 1.0, troughCapacity = 30000}, -- ensilage -} -local adjusted = ADSupplyPlanner.applyPending(pendingNeeds, {[116] = 20000, [31] = 20000}) -check("AP halm: niveau + på vej, højst kapacitet", adjusted[1].level, 20000) -check("AP TMR: niveau + på vej", adjusted[2].level, 20000) -check("AP TMR: kapacitet = niveau + resterende fri", adjusted[2].capacity, 30000) -check("AP ensilage: truget har kun 10000 fri tilbage", adjusted[3].capacity - adjusted[3].level, 10000) -check("AP trug-score falder", math.floor(adjusted[3].scoreOverride * 100 + 0.5), 33) -check("AP input urørt", pendingNeeds[2].level, 0) -check("AP intet på vej -> samme værdier", ADSupplyPlanner.applyPending(pendingNeeds, {})[1].level, 5000) -local ranked = ADSupplyPlanner.rankNeeds(adjusted, 8000) -check("AP halm fuld (på vej) -> kun trug-varer tilbage", #ranked, 2) - --- PF: parallelt foder (høns: korn 80 % / roesnitter 20 % af det de spiser) — hver gruppe har sin andel af truget -do - local WHEAT, BARLEY, BEETCUT = 1, 2, 3 - local groups = {{fillTypes = {WHEAT, BARLEY}, eatWeight = 0.8}, {fillTypes = {BEETCUT}, eatWeight = 0.2}} - local levels = {[WHEAT] = 60000, [BARLEY] = 20000, [BEETCUT] = 0} - local lvl, cap = ADSupplyPlanner.parallelFoodShare(groups, levels, 100000, BEETCUT) - check("PF roesnitter: andel 20k, har 0", lvl .. "/" .. cap, "0/20000") - lvl, cap = ADSupplyPlanner.parallelFoodShare(groups, levels, 100000, BARLEY) - check("PF korn-gruppen samlet: 80k af 80k", lvl .. "/" .. cap, "80000/80000") - check("PF ukendt vare -> nil", ADSupplyPlanner.parallelFoodShare(groups, levels, 100000, 99), nil) -end - --- cargoFor: læs der allerede er på vognen ved start tæller som "på vej" (in-game 27/9: tre fyldte --- biogas-traktorer startet fra fanen kørte alle til Drop 1 — ingen var registreret på vej) -check("CF run-vare på vognen", select(2, ADSupplyPlanner.cargoFor(SILAGE, 427215, {SILAGE})), 427215) -check("CF vare uden for run'et -> nil", ADSupplyPlanner.cargoFor(HAY, 5000, {SILAGE}), nil) -check("CF tom vogn -> nil", ADSupplyPlanner.cargoFor(SILAGE, 0, {SILAGE}), nil) -check("CF ukendt vare -> nil", ADSupplyPlanner.cargoFor(nil, 5000, {SILAGE}), nil) - --- kilde med nok til et rigtigt læs: mindst fyldte først kun blandt kilder med >= 25 % af et læs; ellers fyldeste --- (in-game 29/9: 4 foder-traktorer hentede 2-10k l i en gæringssilo der løbende fyldes, og kørte "tomme") -local SP = ADSupplyPlanner -check("kilde-grænse: 25 % af læsset", SP.ampleLevel(1000000), 250000) -check("kilde-grænse: ukendt læs -> kun minimum", SP.ampleLevel(nil), SP.MIN_AMPLE_LITERS) -check("kilde-grænse: lille vogn -> mindst minimum", SP.ampleLevel(10000), SP.MIN_AMPLE_LITERS) -check("vælg: næsten tom taber til rigelig", SP.isBetterSource(10000, 12000000, false, 250000), false) -check("vælg: blandt rigelige mindst fyldte", SP.isBetterSource(300000, 12000000, false, 250000), true) -check("vælg: ingen rigelige -> fyldeste", SP.isBetterSource(9000, 2000, false, 250000), true) -check("vælg: løber-fuld-varer uændret (fyldeste)", SP.isBetterSource(12000000, 300000, true, 250000), true) -check("vælg: første kandidat", SP.isBetterSource(10, nil, false, 250000), true) -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adWaitPool.lua b/tests/test_adWaitPool.lua deleted file mode 100644 index 788f6bc..0000000 --- a/tests/test_adWaitPool.lua +++ /dev/null @@ -1,166 +0,0 @@ --- Kør: luajit tests/test_adWaitPool.lua (fra repo-roden) --- Wait-puljen: kandidater pr. mappe, reservationer, nærmeste ledige, rutetjek, redirect. - -dofile("FS25_ADSmartPickup/adSupplyPlanner.lua") -dofile("FS25_ADSmartPickup/adWaitPool.lua") - -local failures = 0 -local function check(name, actual, expected) - if actual == expected then - print("OK " .. name) - else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end - -local markers, wayPoints = {}, {} -local function addMarker(id, name, group, x, z) - wayPoints[id] = {id = id, x = x, z = z, incoming = {}} - local marker = {id = id, markerIndex = #markers + 1, name = name, group = group} - table.insert(markers, marker) - return marker -end -local unreachable = {} -local adEnv = { - ADGraphManager = { - getMapMarkers = function() return markers end, - getMapMarkerByWayPointId = function(_, id) for _, m in ipairs(markers) do if m.id == id then return m end end end, - getWayPointById = function(_, id) return wayPoints[id] end, - getPathTo = function(_, vehicle, id) if unreachable[id] then return {} end return {{id = id}} end, - pathFromTo = function(_, from, to) if unreachable[to] then return {} end return {{id = to}} end, - }, -} -getWorldTranslation = function(node) return node.x, 0, node.z end - -addMarker(10, "cow wait 1", "cow wait", 0, 0) -addMarker(11, "cow wait 2", "cow wait", 50, 0) -addMarker(20, "Wait 3", "General Wait", 10, 0) -addMarker(30, "silo 1", "silo", 5, 0) - --- W1: kandidater -check("W1 kun mappen", #ADWaitPool.getCandidates(adEnv, "cow wait"), 2) -check("W1 tom pulje = alle Wait-mapper", #ADWaitPool.getCandidates(adEnv, ""), 3) -check("W1 nil = alle", #ADWaitPool.getCandidates(adEnv, nil), 3) - --- W2: nærmeste ledige fra vognens position; reserverede springes over; egen reservation OK -local v1, v2 = {name = "v1", rootNode = {x = 45, z = 0}}, {name = "v2", rootNode = {x = 45, z = 0}} -check("W2 nærmeste", ADWaitPool.pickFree(adEnv, v1, "cow wait").marker.id, 11) -ADWaitPool.claim(11, v1) -check("W2 reserveret springes over", ADWaitPool.pickFree(adEnv, v2, "cow wait").marker.id, 10) -check("W2 egen reservation genbruges", ADWaitPool.pickFree(adEnv, v1, "cow wait").marker.id, 11) - --- W3: claim / owner / release -check("W3 ejer", ADWaitPool.getOwner(11), v1) -ADWaitPool.releaseVehicle(v1) -check("W3 frigivet", ADWaitPool.getOwner(11), nil) -check("W3 nu ledig for v2", ADWaitPool.pickFree(adEnv, v2, "cow wait").marker.id, 11) - --- W4: nåelighed -unreachable[11] = true -check("W4 uopnåelig nærmeste -> næste", ADWaitPool.pickFree(adEnv, v2, "cow wait", function(c) return ADWaitPool.hasRouteTo(adEnv, v2, c.marker.id) end).marker.id, 10) -check("W4 hasRouteTo false", ADWaitPool.hasRouteTo(adEnv, v2, 11), false) -check("W4 hasRouteBothWays true", ADWaitPool.hasRouteBothWays(adEnv, v2, 10, 30), true) -check("W4 hasRouteBothWays hjem uopnåelig", ADWaitPool.hasRouteBothWays(adEnv, v2, 10, 11), false) -unreachable[11] = nil -check("W4 intet punkt -> nil", ADWaitPool.pickFree(adEnv, v2, "cow wait", function() return false end), nil) - --- W5: getClaims sorteret på mappe, dernæst id -ADWaitPool.claim(20, v2) -ADWaitPool.claim(10, v1) -local claims = ADWaitPool.getClaims(adEnv) -check("W5 to reservationer", #claims, 2) -check("W5 første = General Wait", claims[1].marker.group, "General Wait") -check("W5 anden = cow wait 1", claims[2].marker.id, 10) -ADWaitPool.releaseVehicle(v1) -ADWaitPool.releaseVehicle(v2) -check("W5 alt frigivet", #ADWaitPool.getClaims(adEnv), 0) - --- W6: redirect stopper trigger-aktivitet og sætter sti -local stopped = {} -local task = { - destinationID = 99, state = 1, - vehicle = {ad = { - trailerModule = {isActiveAtTrigger = function() return true end, stopUnloading = function() stopped.unload = true end, stopLoading = function() stopped.load = true end}, - drivePathModule = {setPathTo = function(_, id) stopped.pathTo = id end}, - }}, -} -ADWaitPool.STATE_DRIVING = 2 -ADWaitPool.redirect(task, 10) -check("W6 destination", task.destinationID, 10) -check("W6 state driving", task.state, 2) -check("W6 setPathTo", stopped.pathTo, 10) -check("W6 stopUnloading", stopped.unload, true) - --- WR: AD's rutesøgning siger nej, men vejnettet har en vej fra traktorens nærmeste waypoint -> godtages --- (in-game 24/9: 'Æg storage' ved Chicken Wait 1, "ingen vej til 'Chicken Storage Unload'" trods vej) -do - wayPoints[901] = {id = 901, x = 0, z = 0, out = {902}} - wayPoints[902] = {id = 902, x = 5, z = 0, out = {903}} - wayPoints[903] = {id = 903, x = 10, z = 0, out = {}} - wayPoints[904] = {id = 904, x = 20, z = 0, out = {}} -- ingen indgang - unreachable[903], unreachable[904] = true, true - local vehicle = {getClosestWayPoint = function() return 901, 1 end} - check("WR AD nej, vejnettet ja -> nåelig", ADWaitPool.hasRouteTo(adEnv, vehicle, 903), true) - check("WR hverken AD eller vejnet -> ikke nåelig", ADWaitPool.hasRouteTo(adEnv, vehicle, 904), false) - check("WR uden nærmeste waypoint -> AD's svar", ADWaitPool.hasRouteTo(adEnv, {}, 903), false) - unreachable[903], unreachable[904] = nil, nil -end - --- RC: rutesvar gemmes 30 s pr. mål og 10 m-område (holdende vogne spurgte hvert 5. s -> hak hvert 5. s) -do - local pathCalls = 0 - local realGetPathTo = adEnv.ADGraphManager.getPathTo - adEnv.ADGraphManager.getPathTo = function(self, vehicle, id) pathCalls = pathCalls + 1; return realGetPathTo(self, vehicle, id) end - local clock = 0 - netGetTime = function() return clock end - local positions = {[500] = {100, 100}} - getWorldTranslation = function(node) local p = positions[node]; return p[1], 0, p[2] end - local parked = {rootNode = 500, getClosestWayPoint = function() return 901, 1 end} - ADWaitPool.hasRouteTo(adEnv, parked, 10) - ADWaitPool.hasRouteTo(adEnv, parked, 10) - check("RC samme svar genbruges", pathCalls, 1) - positions[500] = {104, 103} - ADWaitPool.hasRouteTo(adEnv, parked, 10) - check("RC samme 10 m-område genbruges", pathCalls, 1) - positions[500] = {160, 100} - ADWaitPool.hasRouteTo(adEnv, parked, 10) - check("RC nyt sted -> nyt svar", pathCalls, 2) - clock = 31000 - ADWaitPool.hasRouteTo(adEnv, parked, 10) - check("RC efter 30 s -> nyt svar", pathCalls, 3) - local first = ADWaitPool.hasRouteTo(adEnv, parked, 11) - check("RC andet mål -> nyt svar", pathCalls, 4) - check("RC samme svar igen", ADWaitPool.hasRouteTo(adEnv, parked, 11), first) - check("RC uden nyt kald", pathCalls, 4) - adEnv.ADGraphManager.getPathTo = realGetPathTo - netGetTime = nil -end - --- MR: rute markør->markør gemmes (uafhængig af hvor vognen er) — hak 26/9: 30-39 ms pr. rutetjek -do - local calls = 0 - local realFromTo = adEnv.ADGraphManager.pathFromTo - adEnv.ADGraphManager.pathFromTo = function(self, from, to) calls = calls + 1; return realFromTo(self, from, to) end - local savedClock = netGetTime - local t = 1000 - netGetTime = function() return t end - ADWaitPool.resetMarkerRoutes() - local far = {name = "far", rootNode = {x = 5000, z = 5000}} - check("MR første: rute", ADWaitPool.hasRouteBothWays(adEnv, v2, 10, 30), true) - local afterFirst = calls - t = t + 60000 - check("MR anden fra en helt anden position: samme svar", ADWaitPool.hasRouteBothWays(adEnv, far, 10, 30), true) - check("MR anden gang ingen ny ruteberegning", calls, afterFirst) - adEnv.ADGraphManager.pathFromTo = realFromTo - netGetTime = savedClock -end - --- ankerpunkt for "All": midt mellem flowets kilder (in-game 29/9: Milk Store ventede ved sukkerroeknuseren) -local ax, az = ADWaitPool.anchorOf({{x = 0, z = 0}, {x = 100, z = 50}}) -check("anker: midtpunkt x", ax, 50) -check("anker: midtpunkt z", az, 25) -check("anker: ingen punkter", ADWaitPool.anchorOf({}), nil) -check("anker: nil", ADWaitPool.anchorOf(nil), nil) -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_fieldStatus.lua b/tests/test_fieldStatus.lua deleted file mode 100644 index da70c62..0000000 --- a/tests/test_fieldStatus.lua +++ /dev/null @@ -1,33 +0,0 @@ --- Kør: luajit tests/test_fieldStatus.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adFieldModel.lua") -dofile("FS25_ADSmartPickup/adFieldView.lua") -local V = ADFieldView -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end -check("mangler markør", V.fieldStatus({hasMarker = false}), "error") -check("arbejder", V.fieldStatus({hasMarker = true, working = true}), "running") -check("arbejder men venter på maskine", V.fieldStatus({hasMarker = true, working = true, reasonKey = "spu_field_reason_allBusy"}), "waiting") -check("for bred", V.fieldStatus({hasMarker = true, working = true, reasonKey = "spu_field_reason_tooWide"}), "error") -check("ingen maskine", V.fieldStatus({hasMarker = true, reasonKey = "spu_field_reason_noRig"}), "error") -check("regn", V.fieldStatus({hasMarker = true, working = true, reasonKey = "spu_field_reason_rain"}), "waiting") -check("venter på spilleren (høster)", V.fieldStatus({hasMarker = true, awaiting = true}), "waiting") -check("start bestilt", V.fieldStatus({hasMarker = true, startRequested = true}), "waiting") -check("i ro", V.fieldStatus({hasMarker = true}), "stopped") --- Start må ikke bestilles på en mark uden markør (knappen var før deaktiveret; review 29/9) -check("kan starte: med markør", V.canStart({hasMarker = true, working = false}), true) -check("kan starte: uden markør -> nej", V.canStart({hasMarker = false, working = false}), false) -check("kan stoppe: uden markør men i gang", V.canStart({hasMarker = false, working = true}), true) --- global pause (Stop alt): arbejdstrin uden maskiner = stoppet, [Start] genoptager kun den mark (in-game 29/9) -check("pause: arbejdstrin -> stoppet", V.fieldStatus({hasMarker = true, working = true, paused = true}), "stopped") -check("pause: genoptaget mark -> kører", V.fieldStatus({hasMarker = true, working = true, paused = true, resumed = true}), "running") -check("pause: mangler markør stadig rød", V.fieldStatus({hasMarker = false, paused = true}), "error") -check("pause: arbejder-tæller falsk", V.isActiveWork({working = true, paused = true}), false) -check("pause: genoptaget arbejder-tæller sand", V.isActiveWork({working = true, paused = true, resumed = true}), true) -check("ingen pause: arbejder", V.isActiveWork({working = true}), true) -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_flowStatus.lua b/tests/test_flowStatus.lua deleted file mode 100644 index 0beeedf..0000000 --- a/tests/test_flowStatus.lua +++ /dev/null @@ -1,21 +0,0 @@ --- Kør: luajit tests/test_flowStatus.lua (fra repo-roden) -dofile("FS25_ADSmartPickup/adFlowView.lua") -local V = ADFlowView -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else - failures = failures + 1 - print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) - end -end -check("kører", V.flowStatus({running = 3, total = 4, canStop = true}), "running") -check("kører men holder (begrundelse)", V.flowStatus({running = 2, total = 2, canStop = true, reason = "intet at hente"}), "waiting") -check("startet, venter på vogn", V.flowStatus({running = 0, total = 2, canStop = true}), "waiting") -check("advarsel", V.flowStatus({running = 1, total = 2, canStop = true, warning = "mål mangler"}), "error") -check("start afvist (ingen vogn)", V.flowStatus({running = 0, total = 2, lastStartFailed = true}), "error") -check("stoppet", V.flowStatus({running = 0, total = 2, stopped = true}), "stopped") -check("aldrig startet", V.flowStatus({running = 0, total = 2}), "stopped") -check("stoppet vinder over gammel afvisning", V.flowStatus({running = 0, total = 2, stopped = true, lastStartFailed = true}), "stopped") -check("farver findes", V.LEVEL_COLORS ~= nil and V.LEVEL_COLORS.waiting ~= nil and V.LEVEL_COLORS.stopped ~= nil, true) -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_noStrayGlobals.lua b/tests/test_noStrayGlobals.lua deleted file mode 100644 index d47903f..0000000 --- a/tests/test_noStrayGlobals.lua +++ /dev/null @@ -1,18 +0,0 @@ --- Kør: luajit tests/test_noStrayGlobals.lua (fra repo-roden) --- Fanger lokale navne der ved en fejl bruges som globale (fx 'implement' uden for sin closure — v1.49 gjorde --- egne tømmevogne ubrugelige under høst). Læser luajit-bytecode: GGET/GSET af typiske lokale navne. -local SUSPECT = {implement = true, candidate = true, vehicle = true, rig = true, pair = true, assignment = true, - tractor = true, loan = true, field = true, info = true, marker = true, job = true, owner = true, order = true} -local failures = 0 -local files = io.popen("ls FS25_ADSmartPickup/*.lua FS25_ADSmartPickup/gui/*.lua"):read("*a") -for file in files:gmatch("[^\n]+") do - local listing = io.popen("luajit -bl " .. file .. " 2>&1"):read("*a") - for op, name in listing:gmatch("(G[GS]ET)%s+%d+%s+%d+%s+; \"([%w_]+)\"") do - if SUSPECT[name] then - failures = failures + 1 - print(string.format("FAIL %s: %s af global '%s'", file, op, name)) - end - end -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_storageView.lua b/tests/test_storageView.lua deleted file mode 100644 index fef37a9..0000000 --- a/tests/test_storageView.lua +++ /dev/null @@ -1,186 +0,0 @@ --- Kør: luajit tests/test_storageView.lua (fra repo-roden) --- Røgtest af vareoversigten i fanerne Lagre (v1.52) med falske GUI-elementer: opbygning, fold ud, --- opdatering hvert sekund og skift til bygningsvisning må ikke kaste, og tallene skal lande i cellerne. - -local failures = 0 -local function check(name, actual, expected) - if actual == expected then print("OK " .. name) else failures = failures + 1; print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) end -end - --- ---- falske GUI-elementer ---- -local Element = {} -Element.__index = Element -local function newElement(names, size) - local element = setmetatable({children = {}, text = nil, visible = true, size = size or {0.5, 0.02}}, Element) - for _, name in ipairs(names or {}) do - element.children[name] = setmetatable({children = {}, name = name, visible = true, size = {0.2, 0.01}}, Element) - end - return element -end -function Element:clone() local copy = newElement({}, {self.size[1], self.size[2]}); for n, c in pairs(self.children) do copy.children[n] = setmetatable({children = {}, name = n, visible = true, size = {c.size[1], c.size[2]}}, Element) end; return copy end -function Element:getDescendantByName(name) return self.children[name] end -function Element:setText(text) self.text = text end -function Element:setTexts(texts) self.texts = texts end -function Element:setState(state) self.state = state end -function Element:setVisible(visible) self.visible = visible end -function Element:setSize(x, y) self.size = {x, y} end -function Element:setImageColor(_, r, g, b, a) self.color = {r, g, b, a} end -function Element:setTextColor(r, g, b, a) self.textColor = {r, g, b, a} end -function Element:delete() end -function Element:mouseEvent() return false end - --- ---- spil-globaler ---- -Class = function() return {} end -TabbedMenuFrameElement = {} -InputAction = {} -FocusManager = {loadElementFromCustomValues = function() end, removeElement = function() end} -Logging = {warning = function(...) print("WARN", string.format(...)) end, info = function() end} -local titles = {[31] = "Straw", [5] = "Sugar", [9] = "Milk"} -g_fillTypeManager = {getFillTypeTitleByIndex = function(_, i) return titles[i] end, getFillTypeNameByIndex = function(_, i) return titles[i] end} -g_currentMission = {getFarmId = function() return 1 end} -g_i18n = nil - -dofile("FS25_ADSmartPickup/adStorageStats.lua") -ADRunsController = {store = {runs = {}}} -ADRuns = {getRun = function() return nil end} -ADFlowView = {wrapText = function(text) return {text} end} -ADSources = { - getFarmInventory = function() return ADSources.inventory end, - getFarmRates = function() return {[31] = {consumption = 1000, production = 0}, [5] = {consumption = 0, production = 500}} end, - getMarketQuote = function(ft) if ft == 5 then return {price = 1.0, top = 1.05} end return nil end, -} -ADSources.inventory = { - {kind = "silo", name = "Farma #1", placeable = "p1", entries = {{fillType = 31, level = 300, capacity = 1000}, {fillType = 5, level = 20000, capacity = 21000}}}, - {kind = "silo", name = "Farma #2", placeable = "p2", entries = {{fillType = 31, level = 700, capacity = 1000}}}, - {kind = "factory", name = "Sugar mill", placeable = "p3", entries = {{fillType = 5, level = 10, capacity = 100, isOutput = true}}}, -} - -local i18nTexts = {} -local i18n = {getText = function(_, key) i18nTexts[key] = true; if key:find("days") then return "%.1f d" end - if key:find("share") then return "%d %%" end - if key:find("alert_full") then return "%s %d %% %s" end - if key:find("alert_empty") then return "%s %s %s %s" end - if key:find("alert_sell") then return "%s %d %s" end - if key:find("pallets") then return "%d p" end - if key:find("perDay") or key:find("fullIn") then return "%s" end - return key end} - -SmartPickupFrame = {} -local function load(path) local chunk = assert(loadfile(path)); chunk() end -SmartPickupFrame.new = nil -load("FS25_ADSmartPickup/gui/SmartPickupFrame.lua") -load("FS25_ADSmartPickup/gui/SmartPickupStorageTab.lua") -load("FS25_ADSmartPickup/gui/SmartPickupStorageView.lua") -load("FS25_ADSmartPickup/gui/SmartPickupFlowTab.lua") - --- en frame uden spillets klasse-maskineri: kun det fanen bruger -local frame = setmetatable({}, {__index = SmartPickupFrame}) -frame.i18n = i18n -frame.clonedRows, frame.rowData = {}, {} -frame.currentTab = 7 -- Siloer -local rows = {} -frame.boxLayout = {elements = rows, invalidateLayout = function() end} -frame.productPrefab = newElement({"option", "title"}) -frame.productPrefab.children.option.children.ignore = newElement() -frame.textPrefab = newElement({"title"}) -frame.kpiPrefab = newElement({"v1", "l1", "v2", "l2", "v3", "l3", "v4", "l4"}) -frame.alertPrefab = newElement({"dot", "c1"}) -frame.itemPrefab = newElement({"c1", "c2", "c3", "c4", "bar", "barBg"}) -frame.subPrefab = newElement({"c1", "c2", "c3", "c4", "bar", "barBg"}) -frame.statPrefab = newElement({"c1", "c2", "c3", "c4", "c5", "c6", "c7"}) -frame.gridPrefab = newElement({"c1", "c2", "c3"}) -for _, prefab in ipairs({frame.productPrefab, frame.textPrefab, frame.kpiPrefab, frame.alertPrefab, frame.itemPrefab, frame.subPrefab, frame.statPrefab, frame.gridPrefab}) do - local base = prefab.clone - prefab.clone = function(self) local copy = base(self); if self.children.option then copy.children.option.children.ignore = newElement() end; table.insert(rows, copy); return copy end -end -frame.getAdEnv = function() return {} end -frame.setStatusText = function() end -frame.refreshButtons = function() end -frame.restoreFocusIfLost = function() end -frame.colorRows = function() end -for _, name in ipairs({"startButtonInfo", "renameButtonInfo", "deleteButtonInfo", "stopAllButtonInfo", "renameBuildingButtonInfo"}) do frame[name] = {} end - -local function texts() - local list = {} - for _, row in ipairs(rows) do - for _, name in ipairs({"title", "c1", "c2", "c3", "c4", "v1", "v3"}) do - local child = row.children[name] - if child ~= nil and child.text ~= nil then table.insert(list, child.text) end - end - end - return table.concat(list, "|") -end - --- SVW1: opbygning af Siloer i varevisning -local ok, err = pcall(frame.rebuild, frame) -check("SVW1 opbygning kaster ikke", ok, true) -if not ok then print(err) end -local all = texts() -check("SVW1 straw-række", all:find("+ Straw", 1, true) ~= nil, true) -check("SVW1 sugar-række", all:find("+ Sugar", 1, true) ~= nil, true) -check("SVW1 straw 1.0k/2.0k 50 %", all:find("1.0k/2.0k · 50 %", 1, true) ~= nil, true) -check("SVW1 ingen bygninger før klik", all:find("Farma #2", 1, true), nil) -check("SVW1 fabrikkens sukker ikke i siloer", all:find("Sugar mill", 1, true), nil) -check("SVW1 kræver handling: straw løber tør", all:find("spu_alert", 1, true) == nil and all:find("Straw 1.0 d", 1, true) ~= nil, true) - --- SVW2: klik på straw folder ud; bygninger med mest først -local strawRow = frame.itemView.itemRows[1] -ok, err = pcall(frame.toggleItem, frame, 31) -check("SVW2 fold ud kaster ikke", ok, true) -if not ok then print(err) end -all = texts() -local first, second = all:find("Farma #2", 1, true), all:find("Farma #1", 1, true) -check("SVW2 Farma #2 vist", first ~= nil, true) -check("SVW2 flest først", first ~= nil and second ~= nil and first < second, true) -check("SVW2 markør skiftet", all:find("– Straw", 1, true) ~= nil, true) -check("SVW2 andel", all:find("70 %", 1, true) ~= nil, true) - --- SVW3: opdatering hvert sekund med nye tal, samme struktur -ADSources.inventory[2].entries[1].level = 900 -ok, err = pcall(frame.refreshLive, frame) -check("SVW3 opdatering kaster ikke", ok, true) -if not ok then print(err) end -check("SVW3 ny total i cellen", texts():find("1.2k/2.0k · 60 %", 1, true) ~= nil, true) -local bar = frame.itemView.itemRows[1].bar -check("SVW3 bjælke 60 %", math.floor(bar.size[1] / 0.2 * 100 + 0.5), 60) -check("SVW3 bjælke gul (løber tør < 3 d)", bar.color ~= nil and bar.color[1] > 0.9 and bar.color[2] > 0.7, true) - --- SVW4: skift til bygningsvisning og tilbage -ok, err = pcall(frame.onClickStorageView, frame, SmartPickupFrame.STORAGE_VIEW_BUILDINGS) -check("SVW4 bygningsvisning kaster ikke", ok, true) -if not ok then print(err) end -check("SVW4 ingen varevisning", frame.itemView, nil) -ok = pcall(frame.onClickStorageView, frame, SmartPickupFrame.STORAGE_VIEW_ITEMS) -check("SVW4 tilbage til varer", ok and frame.itemView ~= nil, true) - --- SVW5: fabrikker-fanen: output næsten fuldt -> rød -frame.currentTab = 6 -ok, err = pcall(frame.rebuild, frame) -check("SVW5 fabrikker kaster ikke", ok, true) -if not ok then print(err) end - --- SVW7: udfoldet vare hvor to siloer bytter plads i mængde -> ingen genopbygning, rækkerne følger bygningen -frame.currentTab = 7 -frame.expandedItems = nil -frame:rebuild() -frame:toggleItem(31) -local subsBefore = frame.itemView.itemRows[1].subs -ADSources.inventory[1].entries[1].level = 950 -- Farma #1 (300 -> 950) forbi Farma #2 (900) -for _ = 1, 12 do frame:refreshLive() end -check("SVW7 samme rækker (ingen genopbygning)", frame.itemView.itemRows[1].subs, subsBefore) -check("SVW7 første underrække er stadig Farma #2", subsBefore[1].cells[1].text, "Farma #2") -check("SVW7 anden underrække viser Farma #1's nye tal", subsBefore[2].cells[2].text:find("950", 1, true) ~= nil, true) - --- SVW6: alle brugte l10n-nøgler findes i begge sprogfiler -for _, file in ipairs({"FS25_ADSmartPickup/l10n/l10n_da.xml", "FS25_ADSmartPickup/l10n/l10n_en.xml"}) do - local content = io.open(file):read("*a") - for key in pairs(i18nTexts) do - if key:find("^spu_") and not content:find('name="' .. key .. '"', 1, true) then - failures = failures + 1 - print("FAIL SVW6 mangler " .. key .. " i " .. file) - end - end -end - -print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) -os.exit(failures == 0 and 0 or 1)