From af71548b0b6adadc8810e1b12ceba68fa01e1918 Mon Sep 17 00:00:00 2001 From: masterdraco Date: Sat, 26 Sep 2026 01:24:28 +0200 Subject: [PATCH] =?UTF-8?q?feat(marker):=20m=C3=A5l-afgr=C3=B8de=20pr.=20m?= =?UTF-8?q?ark=20('v=C3=A6lg=20mark=20+=20afgr=C3=B8de,=20glem=20den')=20+?= =?UTF-8?q?=20brug=20alle=20ledige=20maskiner=20(standard)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FS25_ADSmartPickup/adFieldFleet.lua | 6 ++ FS25_ADSmartPickup/adFieldJobs.lua | 34 ++++++++++- FS25_ADSmartPickup/adFieldModel.lua | 53 +++++++++++++++- FS25_ADSmartPickup/adFieldStorage.lua | 6 +- FS25_ADSmartPickup/adFieldWork.lua | 2 +- .../gui/SmartPickupFieldTab.lua | 60 ++++++++++++++++--- FS25_ADSmartPickup/l10n/l10n_da.xml | 7 +++ FS25_ADSmartPickup/l10n/l10n_en.xml | 7 +++ tests/test_adFieldFleet.lua | 6 ++ tests/test_adFieldJobs.lua | 9 +++ tests/test_adFieldModel.lua | 36 +++++++++++ 11 files changed, 209 insertions(+), 17 deletions(-) diff --git a/FS25_ADSmartPickup/adFieldFleet.lua b/FS25_ADSmartPickup/adFieldFleet.lua index 6dd9d24..90ef464 100644 --- a/FS25_ADSmartPickup/adFieldFleet.lua +++ b/FS25_ADSmartPickup/adFieldFleet.lua @@ -89,6 +89,12 @@ function ADFieldFleet.pickMany(candidates, role, n, x, z, requiredWidth, accept) return nil, reason, args end +-- Må sættet sendes ud? "all" (standard): alle ledige maskiner; "ticked": kun med flueben. +function ADFieldFleet.isEnabled(mode, enabledIds, id) + if mode == "ticked" then return (enabledIds or {})[id] == true end + return true +end + function ADFieldFleet.toggle(enabledIds, id) local copy = {} for key, value in pairs(enabledIds or {}) do copy[key] = value end diff --git a/FS25_ADSmartPickup/adFieldJobs.lua b/FS25_ADSmartPickup/adFieldJobs.lua index 99d62e0..a5dea01 100644 --- a/FS25_ADSmartPickup/adFieldJobs.lua +++ b/FS25_ADSmartPickup/adFieldJobs.lua @@ -9,7 +9,8 @@ ADFieldJobs.LOG_PREFIX = "ADSmartPickup: " ADFieldJobs.TICK_MS = 1000 -- Courseplay kan efterlade hjørner på skæve marker: efter så mange færdige ture pr. sæt går marken videre. ADFieldJobs.MAX_PASSES = 2 -ADFieldJobs.state = {fields = {}, fleet = {}, assignments = {}} +-- fleetMode: "all" = alle ledige maskiner (standard, "vælg og glem"), "ticked" = kun med flueben +ADFieldJobs.state = {fields = {}, fleet = {}, assignments = {}, fleetMode = "all"} -- mark-id -> {mower = kursus, rake = kursus, = fælles multi-tool-kursus}: kopier (kun i hukommelsen; -- efter genindlæsning hentes slå-/rivekurset fra maskinen, som CP selv gemmer kurset på) ADFieldJobs.courses = {} @@ -46,6 +47,14 @@ function ADFieldJobs.decide(field, info, assignments, now, env) end return {action = "start"} end + -- mål-afgrøde på en bar mark: klargør og så så snart målet kan sås + if info ~= nil and info.kind == "bare" and field.plan.targetFruit ~= nil and field.plan.targetFruit ~= "" then + local plantable = env ~= nil and env.plantable ~= nil and env.plantable(field.plan.targetFruit) or false + if ADFieldModel.shouldStartBare(field, "bare", plantable) then + if not hasMarker then return {action = "wait"} end + return {action = "start"} + end + end if not ADFieldModel.isWorking(field) then local care = ADFieldModel.careNeed(field, info) if care == nil then return {action = "none"} end @@ -124,6 +133,7 @@ function ADFieldJobs.load() local ok, loaded = pcall(ADFieldStorage.load) if ok then ADFieldJobs.state.fields, ADFieldJobs.state.fleet = loaded.fields, loaded.fleet + ADFieldJobs.state.fleetMode = loaded.fleetMode or "all" else Logging.warning(ADFieldJobs.LOG_PREFIX .. "marker kunne ikke indlæses: %s", tostring(loaded)) end @@ -143,6 +153,17 @@ function ADFieldJobs.forceReady(id) ADFieldJobs.setField(ADFieldModel.forceReady(ADFieldJobs.getField(id))) end +function ADFieldJobs.setTarget(id, fruitName) + ADFieldJobs.setField(ADFieldModel.setTarget(ADFieldJobs.getField(id), fruitName)) + ADFieldJobs.log(id, "mål-afgrøde: %s", fruitName ~= "" and fruitName or "ingen") + ADFieldJobs.save() +end + +function ADFieldJobs.setFleetMode(mode) + ADFieldJobs.state.fleetMode = mode + ADFieldJobs.save() +end + function ADFieldJobs.toggleFleet(vehicleId) ADFieldJobs.state.fleet = ADFieldFleet.toggle(ADFieldJobs.state.fleet, vehicleId) ADFieldJobs.save() @@ -192,7 +213,14 @@ function ADFieldJobs.environment() local weather = g_currentMission.environment.weather return weather ~= nil and weather.getIsRaining ~= nil and weather:getIsRaining() or false end) - return {raining = ok and raining == true} + return { + raining = ok and raining == true, + -- kan afgrøden (navn) sås i den aktuelle periode? + plantable = function(name) + local fruit = g_fruitTypeManager ~= nil and g_fruitTypeManager:getFruitTypeByName(name) or nil + return fruit ~= nil and ADCpBridge.isPlantableNow(fruit.index) + end, + } end -- Seneste beslutning pr. mark (GUI viser pleje-behov og grunde). @@ -206,7 +234,7 @@ local function stepField(adEnv, info) local decision = ADFieldJobs.decide(field, info, assignments, ADFieldJobs.now, ADFieldJobs.environment()) ADFieldJobs.lastDecision[info.id] = decision if decision.action == "start" then - local started = ADFieldModel.start(field, info.kind, info.needs) + local started = ADFieldModel.start(field, info.kind, info.needs, info.fruitName) started.lastFruit = info.kind == "harvest" and info.fruitName or field.lastFruit ADFieldJobs.setField(started) ADFieldJobs.log(info.id, "starter (%s: %s)", tostring(info.kind), started.stage) diff --git a/FS25_ADSmartPickup/adFieldModel.lua b/FS25_ADSmartPickup/adFieldModel.lua index 897459f..2db8be7 100644 --- a/FS25_ADSmartPickup/adFieldModel.lua +++ b/FS25_ADSmartPickup/adFieldModel.lua @@ -42,7 +42,7 @@ KNOWN_STAGES.careLiming = true local DEFAULT_PLAN = { mode = "manual", product = "auto", rake = false, rigs = 1, unloaders = 1, mulch = false, lime = true, soil = "auto", sow = false, sowFruit = "", roll = false, - fertilize = true, weed = true, + fertilize = true, weed = true, targetFruit = "", prepAfterMow = false, } local SOIL_VALUES = {auto = true, cultivate = true, plow = true, none = true} local PRODUCTS = {auto = true, silage = true, hay = true, harvest = true} @@ -123,9 +123,51 @@ function ADFieldModel.workStages(plan, kind, needs) if product == "hay" then table.insert(stages, "tedding") end if plan.rake then table.insert(stages, "raking") end table.insert(stages, "collecting") + -- mål-afgrøde på en slåmark med en anden afgrøde: efter sidste slæt klargøres og sås målet + if plan.prepAfterMow then + for _, stage in ipairs(ADFieldModel.prepStages(plan, needs)) do table.insert(stages, stage) end + end return stages end +-- Mål-afgrøde ("vælg mark + afgrøde, glem den"): planen udfyldes ud fra hvad der står på marken nu. +-- Står målet der allerede som slåafgrøde (græs -> græs): bare slå igen og igen. Ellers: høst/slå det der +-- står, klargør og så målet. Kører altid automatisk. Uden mål returneres planen uændret. +function ADFieldModel.applyTarget(plan, kind, currentFruit) + local copy = copyPlan(plan) + if copy.targetFruit == nil or copy.targetFruit == "" then return copy end + copy.mode = "auto" + copy.sowFruit = copy.targetFruit + if kind == "mowable" then + copy.prepAfterMow = currentFruit ~= copy.targetFruit + copy.sow = copy.prepAfterMow + else + copy.prepAfterMow = false + copy.sow = true + end + return copy +end + +-- Vælg mål-afgrøde fra menuen ("" = ingen). Med mål kører marken automatisk; en færdig/klar mark sættes +-- til venter, så den kan starte forfra. Uden mål: tilbage til manuel. +function ADFieldModel.setTarget(field, fruitName) + local copy = copyField(field) + copy.plan.targetFruit = fruitName or "" + copy.plan.mode = copy.plan.targetFruit ~= "" and "auto" or "manual" + if not ADFieldModel.isWorking(field) and field.stage == "done" then + copy.stage = "idle" + end + return copy +end + +-- Bar mark med mål-afgrøde starter selv klargøring + såning, når målet kan sås nu. +function ADFieldModel.shouldStartBare(field, kind, plantableNow) + if field.plan.targetFruit == nil or field.plan.targetFruit == "" then return false end + -- kun fra idle: efter egen såning står marken "done" (nysået kan læses som bar) — ingen dobbelt-såning + if kind ~= "bare" or field.stage ~= "idle" then return false end + return plantableNow == true +end + -- Krævet bredde for et sæt: kun læssevognen uden riv skal dække slåmaskinens baner. function ADFieldModel.requiredWidth(field, role) if role == "collector" and not field.plan.rake then @@ -210,9 +252,11 @@ local function freshCycle(copy) end -- Første trin i kæden. En tom kæde (bar mark uden jordbehandling eller såning) -> færdig. -function ADFieldModel.start(field, kind, needs) +-- currentFruit (valgfri): afgrøden på marken nu; med mål-afgrøde udfyldes planen ud fra den. +function ADFieldModel.start(field, kind, needs, currentFruit) local copy = copyField(field) - local stages = ADFieldModel.workStages(field.plan, kind, needs) + copy.plan = ADFieldModel.applyTarget(copy.plan, kind, currentFruit) + local stages = ADFieldModel.workStages(copy.plan, kind, needs) copy.stage = stages[1] or "done" copy.kind = kind or "mowable" freshCycle(copy) @@ -379,6 +423,7 @@ function ADFieldModel.toRecord(field) rigs = plan.rigs, unloaders = plan.unloaders, mulch = flag(plan.mulch), lime = flag(plan.lime), soil = plan.soil, sow = flag(plan.sow), sowFruit = plan.sowFruit or "", roll = flag(plan.roll), fertilize = flag(plan.fertilize), weed = flag(plan.weed), + targetFruit = plan.targetFruit or "", prepAfterMow = flag(plan.prepAfterMow), kind = field.kind or "", lastFruit = field.lastFruit or "", care = table.concat(care, " "), mowWidth = field.mowWidth or 0, mowVehicle = field.mowVehicleId or "", @@ -415,6 +460,8 @@ function ADFieldModel.fromRecord(record) plan.roll = readFlag(record.roll, false) plan.fertilize = readFlag(record.fertilize, true) plan.weed = readFlag(record.weed, true) + plan.targetFruit = record.targetFruit or "" + plan.prepAfterMow = readFlag(record.prepAfterMow, false) field.kind = readText(record.kind) field.lastFruit = readText(record.lastFruit) for stage, runs in string.gmatch(record.care or "", "(%w+)=(%d+)") do field.careRuns[stage] = tonumber(runs) end diff --git a/FS25_ADSmartPickup/adFieldStorage.lua b/FS25_ADSmartPickup/adFieldStorage.lua index e15a42d..4b4c744 100644 --- a/FS25_ADSmartPickup/adFieldStorage.lua +++ b/FS25_ADSmartPickup/adFieldStorage.lua @@ -11,7 +11,7 @@ ADFieldStorage.FIELD_TYPES = { id = "Int", stage = "String", mode = "String", product = "String", rake = "Int", store = "String", rigs = "Int", unloaders = "Int", mulch = "Int", lime = "Int", soil = "String", sow = "Int", sowFruit = "String", roll = "Int", fertilize = "Int", weed = "Int", - kind = "String", lastFruit = "String", care = "String", + kind = "String", lastFruit = "String", care = "String", targetFruit = "String", prepAfterMow = "Int", mowWidth = "Float", mowVehicle = "String", rakeVehicle = "String", windrowOffset = "String", } @@ -46,6 +46,7 @@ function ADFieldStorage.save(state) ADFieldStorage.writeRecord(xmlFile, string.format("%s.field(%d)", ADFieldStorage.ROOT, index), ADFieldModel.toRecord(field)) index = index + 1 end + xmlFile:setString(ADFieldStorage.ROOT .. "#fleetMode", state.fleetMode or "all") local vehicleIndex = 0 for vehicleId in pairs(state.fleet) do xmlFile:setString(string.format("%s.fleet.vehicle(%d)#id", ADFieldStorage.ROOT, vehicleIndex), vehicleId) @@ -57,11 +58,12 @@ function ADFieldStorage.save(state) end function ADFieldStorage.load() - local result = {fields = {}, fleet = {}} + local result = {fields = {}, fleet = {}, fleetMode = "all"} local path = ADFieldStorage.getPath() if path == nil or not fileExists(path) then return result end local xmlFile = XMLFile.load("ADSmartPickupFields", path) if xmlFile == nil then return result end + result.fleetMode = xmlFile:getString(ADFieldStorage.ROOT .. "#fleetMode") == "ticked" and "ticked" or "all" xmlFile:iterate(ADFieldStorage.ROOT .. ".field", function(_, key) local field = ADFieldModel.fromRecord(ADFieldStorage.readRecord(xmlFile, key)) if field.id ~= nil then result.fields[field.id] = field end diff --git a/FS25_ADSmartPickup/adFieldWork.lua b/FS25_ADSmartPickup/adFieldWork.lua index 7fa4bf5..2ce76a1 100644 --- a/FS25_ADSmartPickup/adFieldWork.lua +++ b/FS25_ADSmartPickup/adFieldWork.lua @@ -113,7 +113,7 @@ function ADFieldWork.getRigs(adEnv) local x, z = vehiclePosition(vehicle) local rig = { id = id, vehicle = vehicle, role = role, label = ADRuns.vehicleLabel(vehicle), - enabled = state.fleet[id] == true, + enabled = ADFieldFleet.isEnabled(state.fleetMode, state.fleet, id), -- optaget: vores egen opgave, et Smart Pickup-flow, eller AutoDrive/Courseplay kører den allerede -- (fx spillerens egen parkeringstur) — den må ikke kapres midt i en anden kørsel busy = state.assignments[id] ~= nil or ADRunsController.getRunForVehicle(vehicle) ~= nil diff --git a/FS25_ADSmartPickup/gui/SmartPickupFieldTab.lua b/FS25_ADSmartPickup/gui/SmartPickupFieldTab.lua index b83444b..0731ba0 100644 --- a/FS25_ADSmartPickup/gui/SmartPickupFieldTab.lua +++ b/FS25_ADSmartPickup/gui/SmartPickupFieldTab.lua @@ -9,6 +9,7 @@ SmartPickupFrame.FIELD_ROW_KINDS = { fieldProduct = true, fieldMode = true, fieldRake = true, fieldStore = true, fleetVehicle = true, fieldRigs = true, fieldUnloaders = true, fieldMulch = true, fieldLime = true, fieldSoil = true, fieldSow = true, fieldSowFruit = true, fieldRoll = true, fieldFertilize = true, fieldWeed = true, + fieldTarget = true, fleetMode = true, } SmartPickupFrame.ROLE_KEYS = { mower = "spu_field_role_mower", tedder = "spu_field_role_tedder", rake = "spu_field_role_rake", @@ -62,12 +63,36 @@ function SmartPickupFrame:fieldStatusText(field, info) if decision ~= nil and decision.care ~= nil and not ADFieldModel.isWorking(field) then text = text .. " — " .. string.format(self.i18n:getText("spu_field_careNeed"), self.i18n:getText("spu_field_stage_" .. decision.care)) end + local target = field.plan.targetFruit + if target ~= nil and target ~= "" and info.kind == "bare" and field.stage == "idle" and not ADFieldJobs.environment().plantable(target) then + text = text .. " — " .. string.format(self.i18n:getText("spu_field_waitSowing"), self:fruitTitle(target)) + end local reason = self:fieldReasonText(field.reason) if reason ~= nil then text = text .. ": " .. reason end return text end +-- Hvad moden gør med marken frem mod mål-afgrøden (én linje under valget). +function SmartPickupFrame:targetSummary(field, info) + local target = self:fruitTitle(field.plan.targetFruit) + if info.kind == "bare" then + return string.format(self.i18n:getText("spu_field_target_bare"), target) + end + if info.fruitName == field.plan.targetFruit and info.kind == "mowable" then + return string.format(self.i18n:getText("spu_field_target_keep"), target) + end + return string.format(self.i18n:getText("spu_field_target_switch"), tostring(info.cropTitle or "-"), target) +end + +function SmartPickupFrame:fruitTitle(name) + local fruit = g_fruitTypeManager ~= nil and name ~= nil and name ~= "" and g_fruitTypeManager:getFruitTypeByName(name) or nil + return fruit ~= nil and fruit.fillType ~= nil and tostring(fruit.fillType.title) or tostring(name) +end + function SmartPickupFrame:fieldPlanText(field, info) + if field.plan.targetFruit ~= nil and field.plan.targetFruit ~= "" then + return "-> " .. self:fruitTitle(field.plan.targetFruit) + end local text = self.i18n:getText(ADFieldView.planKey(field.plan, info.kind)) if field.plan.rake and info.kind == "mowable" then text = text .. " + " .. self.i18n:getText("spu_field_plan_rake") end if field.plan.sow and info.kind ~= "mowable" then text = text .. " + " .. self.i18n:getText("spu_field_plan_sow") end @@ -116,9 +141,9 @@ local function numberTexts(from, to) return texts, values end --- Afgrøder der kan sås (første valg: samme som den høstede). -function SmartPickupFrame:sowFruitChoices() - local texts, values = {self.i18n:getText("spu_field_sowSame")}, {""} +-- Afgrøder der kan sås (første valg: samme som den høstede / ingen mål-afgrøde). +function SmartPickupFrame:sowFruitChoices(firstKey) + local texts, values = {self.i18n:getText(firstKey or "spu_field_sowSame")}, {""} for _, fruit in ipairs(g_fruitTypeManager ~= nil and g_fruitTypeManager.fruitTypes or {}) do if fruit.allowsSeeding ~= false and fruit.fillType ~= nil and fruit.name ~= nil then table.insert(texts, tostring(fruit.fillType.title or fruit.name)) @@ -165,7 +190,14 @@ function SmartPickupFrame:fillFieldDetail(adEnv, info) local yesNo = {i18n:getText("ui_no"), i18n:getText("ui_yes")} local crop = info.kind == "bare" and i18n:getText("spu_field_bare") or tostring(info.cropTitle or "-") self:addTextRow(string.format(i18n:getText("spu_field_section"), info.id, crop)) - self:addToggleRow("fieldMode", i18n:getText("spu_field_mode"), {i18n:getText("spu_field_mode_manual"), i18n:getText("spu_field_mode_auto")}, plan.mode == "auto") + local targetTexts, targetValues = self:sowFruitChoices("spu_field_target_none") + self:addChoiceRow("fieldTarget", i18n:getText("spu_field_target"), targetTexts, targetValues, plan.targetFruit or "") + local hasTarget = plan.targetFruit ~= nil and plan.targetFruit ~= "" + if hasTarget then + self:addTextRow(" " .. self:targetSummary(field, info)) + else + self:addToggleRow("fieldMode", i18n:getText("spu_field_mode"), {i18n:getText("spu_field_mode_manual"), i18n:getText("spu_field_mode_auto")}, plan.mode == "auto") + end local rigTexts, rigValues = numberTexts(1, ADFieldModel.MAX_RIGS) self:addChoiceRow("fieldRigs", i18n:getText("spu_field_rigs"), rigTexts, rigValues, plan.rigs or 1) if info.kind == "mowable" then @@ -182,10 +214,14 @@ function SmartPickupFrame:fillFieldDetail(adEnv, info) local soilTexts = {} for _, value in ipairs(SmartPickupFrame.SOIL_VALUES) do table.insert(soilTexts, i18n:getText("spu_field_soil_" .. value)) end self:addChoiceRow("fieldSoil", i18n:getText("spu_field_soil"), soilTexts, SmartPickupFrame.SOIL_VALUES, plan.soil or "auto") - self:addToggleRow("fieldSow", i18n:getText("spu_field_sow"), yesNo, plan.sow == true) - if plan.sow then - local fruitTexts, fruitValues = self:sowFruitChoices() - self:addChoiceRow("fieldSowFruit", i18n:getText("spu_field_sowFruit"), fruitTexts, fruitValues, plan.sowFruit or "") + if not hasTarget then + self:addToggleRow("fieldSow", i18n:getText("spu_field_sow"), yesNo, plan.sow == true) + if plan.sow then + local fruitTexts, fruitValues = self:sowFruitChoices() + self:addChoiceRow("fieldSowFruit", i18n:getText("spu_field_sowFruit"), fruitTexts, fruitValues, plan.sowFruit or "") + end + end + if hasTarget or plan.sow then self:addToggleRow("fieldRoll", i18n:getText("spu_field_roll"), yesNo, plan.roll == true) end end @@ -286,6 +322,8 @@ end function SmartPickupFrame:fillFleetRows(adEnv) local i18n = self.i18n self:addTextRow(i18n:getText("spu_field_section_fleet")) + local useAll = ADFieldJobs.state.fleetMode ~= "ticked" + self:addToggleRow("fleetMode", i18n:getText("spu_field_fleetAll"), {i18n:getText("ui_no"), i18n:getText("ui_yes")}, useAll) local rigs = ADFieldJobs.getRigs(adEnv) table.sort(rigs, function(a, b) if a.role ~= b.role then return a.role < b.role end @@ -332,6 +370,12 @@ function SmartPickupFrame:onClickFieldRow(state, data) local id = frame.selectedFieldId if data.kind == "fleetVehicle" then ADFieldJobs.toggleFleet(data.vehicleId) + elseif data.kind == "fleetMode" then + ADFieldJobs.setFleetMode(isOn and "all" or "ticked") + elseif id == nil then + return + elseif data.kind == "fieldTarget" then + ADFieldJobs.setTarget(id, data.values ~= nil and data.values[state] or "") elseif id == nil then return elseif SmartPickupFrame.PLAN_CHOICES[data.kind] ~= nil then diff --git a/FS25_ADSmartPickup/l10n/l10n_da.xml b/FS25_ADSmartPickup/l10n/l10n_da.xml index 7b47f7f..9d83182 100644 --- a/FS25_ADSmartPickup/l10n/l10n_da.xml +++ b/FS25_ADSmartPickup/l10n/l10n_da.xml @@ -153,6 +153,13 @@ + + + + + + + diff --git a/FS25_ADSmartPickup/l10n/l10n_en.xml b/FS25_ADSmartPickup/l10n/l10n_en.xml index 34fd83c..e953080 100644 --- a/FS25_ADSmartPickup/l10n/l10n_en.xml +++ b/FS25_ADSmartPickup/l10n/l10n_en.xml @@ -153,6 +153,13 @@ + + + + + + + diff --git a/tests/test_adFieldFleet.lua b/tests/test_adFieldFleet.lua index 2fc8f94..dddda8a 100644 --- a/tests/test_adFieldFleet.lua +++ b/tests/test_adFieldFleet.lua @@ -82,5 +82,11 @@ 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) + 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 index a56e6e0..38e9db7 100644 --- a/tests/test_adFieldJobs.lua +++ b/tests/test_adFieldJobs.lua @@ -89,5 +89,14 @@ 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 starter selv når målet kan sås; ellers venter den +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 -> intet", J.decide(bareTarget, bareInfo, {}, 0, {plantable = function() return false end}).action, "none") +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") + 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 index 219df64..02a5527 100644 --- a/tests/test_adFieldModel.lua +++ b/tests/test_adFieldModel.lua @@ -234,5 +234,41 @@ check("M18 uden forespørgsel -> venter", M.onReadiness(M.forceReady(M.newField( 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? (kun når målafgrøden kan sås nu) +check("M20 bar + sås nu -> start", M.shouldStartBare(target, "bare", true), true) +check("M20 bar + ikke såtid -> vent", M.shouldStartBare(target, "bare", false), false) +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") + print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)