-- 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) 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"}} 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 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) print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)