feat(marker): mål-afgrøde pr. mark ('vælg mark + afgrøde, glem den') + brug alle ledige maskiner (standard)

This commit is contained in:
2026-09-26 01:24:28 +02:00
parent 944e61d1a8
commit af71548b0b
11 changed files with 209 additions and 17 deletions
+6
View File
@@ -89,6 +89,12 @@ function ADFieldFleet.pickMany(candidates, role, n, x, z, requiredWidth, accept)
return nil, reason, args return nil, reason, args
end 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) function ADFieldFleet.toggle(enabledIds, id)
local copy = {} local copy = {}
for key, value in pairs(enabledIds or {}) do copy[key] = value end for key, value in pairs(enabledIds or {}) do copy[key] = value end
+31 -3
View File
@@ -9,7 +9,8 @@ ADFieldJobs.LOG_PREFIX = "ADSmartPickup: "
ADFieldJobs.TICK_MS = 1000 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. -- 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.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, <trin> = fælles multi-tool-kursus}: kopier (kun i hukommelsen; -- mark-id -> {mower = kursus, rake = kursus, <trin> = fælles multi-tool-kursus}: kopier (kun i hukommelsen;
-- efter genindlæsning hentes slå-/rivekurset fra maskinen, som CP selv gemmer kurset på) -- efter genindlæsning hentes slå-/rivekurset fra maskinen, som CP selv gemmer kurset på)
ADFieldJobs.courses = {} ADFieldJobs.courses = {}
@@ -46,6 +47,14 @@ function ADFieldJobs.decide(field, info, assignments, now, env)
end end
return {action = "start"} return {action = "start"}
end 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 if not ADFieldModel.isWorking(field) then
local care = ADFieldModel.careNeed(field, info) local care = ADFieldModel.careNeed(field, info)
if care == nil then return {action = "none"} end if care == nil then return {action = "none"} end
@@ -124,6 +133,7 @@ function ADFieldJobs.load()
local ok, loaded = pcall(ADFieldStorage.load) local ok, loaded = pcall(ADFieldStorage.load)
if ok then if ok then
ADFieldJobs.state.fields, ADFieldJobs.state.fleet = loaded.fields, loaded.fleet ADFieldJobs.state.fields, ADFieldJobs.state.fleet = loaded.fields, loaded.fleet
ADFieldJobs.state.fleetMode = loaded.fleetMode or "all"
else else
Logging.warning(ADFieldJobs.LOG_PREFIX .. "marker kunne ikke indlæses: %s", tostring(loaded)) Logging.warning(ADFieldJobs.LOG_PREFIX .. "marker kunne ikke indlæses: %s", tostring(loaded))
end end
@@ -143,6 +153,17 @@ function ADFieldJobs.forceReady(id)
ADFieldJobs.setField(ADFieldModel.forceReady(ADFieldJobs.getField(id))) ADFieldJobs.setField(ADFieldModel.forceReady(ADFieldJobs.getField(id)))
end 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) function ADFieldJobs.toggleFleet(vehicleId)
ADFieldJobs.state.fleet = ADFieldFleet.toggle(ADFieldJobs.state.fleet, vehicleId) ADFieldJobs.state.fleet = ADFieldFleet.toggle(ADFieldJobs.state.fleet, vehicleId)
ADFieldJobs.save() ADFieldJobs.save()
@@ -192,7 +213,14 @@ function ADFieldJobs.environment()
local weather = g_currentMission.environment.weather local weather = g_currentMission.environment.weather
return weather ~= nil and weather.getIsRaining ~= nil and weather:getIsRaining() or false return weather ~= nil and weather.getIsRaining ~= nil and weather:getIsRaining() or false
end) 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 end
-- Seneste beslutning pr. mark (GUI viser pleje-behov og grunde). -- 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()) local decision = ADFieldJobs.decide(field, info, assignments, ADFieldJobs.now, ADFieldJobs.environment())
ADFieldJobs.lastDecision[info.id] = decision ADFieldJobs.lastDecision[info.id] = decision
if decision.action == "start" then 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 started.lastFruit = info.kind == "harvest" and info.fruitName or field.lastFruit
ADFieldJobs.setField(started) ADFieldJobs.setField(started)
ADFieldJobs.log(info.id, "starter (%s: %s)", tostring(info.kind), started.stage) ADFieldJobs.log(info.id, "starter (%s: %s)", tostring(info.kind), started.stage)
+50 -3
View File
@@ -42,7 +42,7 @@ KNOWN_STAGES.careLiming = true
local DEFAULT_PLAN = { local DEFAULT_PLAN = {
mode = "manual", product = "auto", rake = false, rigs = 1, unloaders = 1, mode = "manual", product = "auto", rake = false, rigs = 1, unloaders = 1,
mulch = false, lime = true, soil = "auto", sow = false, sowFruit = "", roll = false, 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 SOIL_VALUES = {auto = true, cultivate = true, plow = true, none = true}
local PRODUCTS = {auto = true, silage = true, hay = true, harvest = 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 product == "hay" then table.insert(stages, "tedding") end
if plan.rake then table.insert(stages, "raking") end if plan.rake then table.insert(stages, "raking") end
table.insert(stages, "collecting") 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 return stages
end 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. -- Krævet bredde for et sæt: kun læssevognen uden riv skal dække slåmaskinens baner.
function ADFieldModel.requiredWidth(field, role) function ADFieldModel.requiredWidth(field, role)
if role == "collector" and not field.plan.rake then if role == "collector" and not field.plan.rake then
@@ -210,9 +252,11 @@ local function freshCycle(copy)
end end
-- Første trin i kæden. En tom kæde (bar mark uden jordbehandling eller såning) -> færdig. -- 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 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.stage = stages[1] or "done"
copy.kind = kind or "mowable" copy.kind = kind or "mowable"
freshCycle(copy) freshCycle(copy)
@@ -379,6 +423,7 @@ function ADFieldModel.toRecord(field)
rigs = plan.rigs, unloaders = plan.unloaders, mulch = flag(plan.mulch), lime = flag(plan.lime), 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), soil = plan.soil, sow = flag(plan.sow), sowFruit = plan.sowFruit or "", roll = flag(plan.roll),
fertilize = flag(plan.fertilize), weed = flag(plan.weed), 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, " "), kind = field.kind or "", lastFruit = field.lastFruit or "", care = table.concat(care, " "),
mowWidth = field.mowWidth or 0, mowWidth = field.mowWidth or 0,
mowVehicle = field.mowVehicleId or "", mowVehicle = field.mowVehicleId or "",
@@ -415,6 +460,8 @@ function ADFieldModel.fromRecord(record)
plan.roll = readFlag(record.roll, false) plan.roll = readFlag(record.roll, false)
plan.fertilize = readFlag(record.fertilize, true) plan.fertilize = readFlag(record.fertilize, true)
plan.weed = readFlag(record.weed, true) plan.weed = readFlag(record.weed, true)
plan.targetFruit = record.targetFruit or ""
plan.prepAfterMow = readFlag(record.prepAfterMow, false)
field.kind = readText(record.kind) field.kind = readText(record.kind)
field.lastFruit = readText(record.lastFruit) field.lastFruit = readText(record.lastFruit)
for stage, runs in string.gmatch(record.care or "", "(%w+)=(%d+)") do field.careRuns[stage] = tonumber(runs) end for stage, runs in string.gmatch(record.care or "", "(%w+)=(%d+)") do field.careRuns[stage] = tonumber(runs) end
+4 -2
View File
@@ -11,7 +11,7 @@ ADFieldStorage.FIELD_TYPES = {
id = "Int", stage = "String", mode = "String", product = "String", rake = "Int", store = "String", id = "Int", stage = "String", mode = "String", product = "String", rake = "Int", store = "String",
rigs = "Int", unloaders = "Int", mulch = "Int", lime = "Int", soil = "String", sow = "Int", rigs = "Int", unloaders = "Int", mulch = "Int", lime = "Int", soil = "String", sow = "Int",
sowFruit = "String", roll = "Int", fertilize = "Int", weed = "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", 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)) ADFieldStorage.writeRecord(xmlFile, string.format("%s.field(%d)", ADFieldStorage.ROOT, index), ADFieldModel.toRecord(field))
index = index + 1 index = index + 1
end end
xmlFile:setString(ADFieldStorage.ROOT .. "#fleetMode", state.fleetMode or "all")
local vehicleIndex = 0 local vehicleIndex = 0
for vehicleId in pairs(state.fleet) do for vehicleId in pairs(state.fleet) do
xmlFile:setString(string.format("%s.fleet.vehicle(%d)#id", ADFieldStorage.ROOT, vehicleIndex), vehicleId) xmlFile:setString(string.format("%s.fleet.vehicle(%d)#id", ADFieldStorage.ROOT, vehicleIndex), vehicleId)
@@ -57,11 +58,12 @@ function ADFieldStorage.save(state)
end end
function ADFieldStorage.load() function ADFieldStorage.load()
local result = {fields = {}, fleet = {}} local result = {fields = {}, fleet = {}, fleetMode = "all"}
local path = ADFieldStorage.getPath() local path = ADFieldStorage.getPath()
if path == nil or not fileExists(path) then return result end if path == nil or not fileExists(path) then return result end
local xmlFile = XMLFile.load("ADSmartPickupFields", path) local xmlFile = XMLFile.load("ADSmartPickupFields", path)
if xmlFile == nil then return result end 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) xmlFile:iterate(ADFieldStorage.ROOT .. ".field", function(_, key)
local field = ADFieldModel.fromRecord(ADFieldStorage.readRecord(xmlFile, key)) local field = ADFieldModel.fromRecord(ADFieldStorage.readRecord(xmlFile, key))
if field.id ~= nil then result.fields[field.id] = field end if field.id ~= nil then result.fields[field.id] = field end
+1 -1
View File
@@ -113,7 +113,7 @@ function ADFieldWork.getRigs(adEnv)
local x, z = vehiclePosition(vehicle) local x, z = vehiclePosition(vehicle)
local rig = { local rig = {
id = id, vehicle = vehicle, role = role, label = ADRuns.vehicleLabel(vehicle), 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 -- 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 -- (fx spillerens egen parkeringstur) — den må ikke kapres midt i en anden kørsel
busy = state.assignments[id] ~= nil or ADRunsController.getRunForVehicle(vehicle) ~= nil busy = state.assignments[id] ~= nil or ADRunsController.getRunForVehicle(vehicle) ~= nil
+52 -8
View File
@@ -9,6 +9,7 @@ SmartPickupFrame.FIELD_ROW_KINDS = {
fieldProduct = true, fieldMode = true, fieldRake = true, fieldStore = true, fleetVehicle = true, fieldProduct = true, fieldMode = true, fieldRake = true, fieldStore = true, fleetVehicle = true,
fieldRigs = true, fieldUnloaders = true, fieldMulch = true, fieldLime = true, fieldSoil = true, fieldRigs = true, fieldUnloaders = true, fieldMulch = true, fieldLime = true, fieldSoil = true,
fieldSow = true, fieldSowFruit = true, fieldRoll = true, fieldFertilize = true, fieldWeed = true, fieldSow = true, fieldSowFruit = true, fieldRoll = true, fieldFertilize = true, fieldWeed = true,
fieldTarget = true, fleetMode = true,
} }
SmartPickupFrame.ROLE_KEYS = { SmartPickupFrame.ROLE_KEYS = {
mower = "spu_field_role_mower", tedder = "spu_field_role_tedder", rake = "spu_field_role_rake", 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 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)) text = text .. " — " .. string.format(self.i18n:getText("spu_field_careNeed"), self.i18n:getText("spu_field_stage_" .. decision.care))
end 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) local reason = self:fieldReasonText(field.reason)
if reason ~= nil then text = text .. ": " .. reason end if reason ~= nil then text = text .. ": " .. reason end
return text return text
end 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) 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)) 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.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 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 return texts, values
end end
-- Afgrøder der kan sås (første valg: samme som den høstede). -- Afgrøder der kan sås (første valg: samme som den høstede / ingen mål-afgrøde).
function SmartPickupFrame:sowFruitChoices() function SmartPickupFrame:sowFruitChoices(firstKey)
local texts, values = {self.i18n:getText("spu_field_sowSame")}, {""} local texts, values = {self.i18n:getText(firstKey or "spu_field_sowSame")}, {""}
for _, fruit in ipairs(g_fruitTypeManager ~= nil and g_fruitTypeManager.fruitTypes or {}) do 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 if fruit.allowsSeeding ~= false and fruit.fillType ~= nil and fruit.name ~= nil then
table.insert(texts, tostring(fruit.fillType.title or fruit.name)) 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 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 "-") 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: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) local rigTexts, rigValues = numberTexts(1, ADFieldModel.MAX_RIGS)
self:addChoiceRow("fieldRigs", i18n:getText("spu_field_rigs"), rigTexts, rigValues, plan.rigs or 1) self:addChoiceRow("fieldRigs", i18n:getText("spu_field_rigs"), rigTexts, rigValues, plan.rigs or 1)
if info.kind == "mowable" then if info.kind == "mowable" then
@@ -182,10 +214,14 @@ function SmartPickupFrame:fillFieldDetail(adEnv, info)
local soilTexts = {} local soilTexts = {}
for _, value in ipairs(SmartPickupFrame.SOIL_VALUES) do table.insert(soilTexts, i18n:getText("spu_field_soil_" .. value)) end 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: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 not hasTarget then
if plan.sow then self:addToggleRow("fieldSow", i18n:getText("spu_field_sow"), yesNo, plan.sow == true)
local fruitTexts, fruitValues = self:sowFruitChoices() if plan.sow then
self:addChoiceRow("fieldSowFruit", i18n:getText("spu_field_sowFruit"), fruitTexts, fruitValues, plan.sowFruit or "") 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) self:addToggleRow("fieldRoll", i18n:getText("spu_field_roll"), yesNo, plan.roll == true)
end end
end end
@@ -286,6 +322,8 @@ end
function SmartPickupFrame:fillFleetRows(adEnv) function SmartPickupFrame:fillFleetRows(adEnv)
local i18n = self.i18n local i18n = self.i18n
self:addTextRow(i18n:getText("spu_field_section_fleet")) 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) local rigs = ADFieldJobs.getRigs(adEnv)
table.sort(rigs, function(a, b) table.sort(rigs, function(a, b)
if a.role ~= b.role then return a.role < b.role end 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 local id = frame.selectedFieldId
if data.kind == "fleetVehicle" then if data.kind == "fleetVehicle" then
ADFieldJobs.toggleFleet(data.vehicleId) 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 elseif id == nil then
return return
elseif SmartPickupFrame.PLAN_CHOICES[data.kind] ~= nil then elseif SmartPickupFrame.PLAN_CHOICES[data.kind] ~= nil then
+7
View File
@@ -153,6 +153,13 @@
<text name="spu_field_weed" text="Bekæmp ukrudt"/> <text name="spu_field_weed" text="Bekæmp ukrudt"/>
<text name="spu_field_notice_done" text="er færdig"/> <text name="spu_field_notice_done" text="er færdig"/>
<text name="spu_field_reason_rain" text="venter på tørvejr"/> <text name="spu_field_reason_rain" text="venter på tørvejr"/>
<text name="spu_field_target" text="Afgrøde på marken (moden passer resten)"/>
<text name="spu_field_target_none" text="Ingen — planlæg selv"/>
<text name="spu_field_target_bare" text="Klargøres og tilsås med %s, så snart det er såtid"/>
<text name="spu_field_target_keep" text="%s står allerede: slås og samles op, hver gang den er klar"/>
<text name="spu_field_target_switch" text="%s får lov at vokse færdig og høstes/slås; derefter klargøres og tilsås med %s"/>
<text name="spu_field_waitSowing" text="venter på såtid for %s"/>
<text name="spu_field_fleetAll" text="Brug alle ledige maskiner (Nej = kun med flueben)"/>
<text name="spu_button_start" text="Start run"/> <text name="spu_button_start" text="Start run"/>
<text name="spu_button_stop" text="Stop run"/> <text name="spu_button_stop" text="Stop run"/>
<text name="spu_button_new" text="Nyt run"/> <text name="spu_button_new" text="Nyt run"/>
+7
View File
@@ -153,6 +153,13 @@
<text name="spu_field_weed" text="Control weeds"/> <text name="spu_field_weed" text="Control weeds"/>
<text name="spu_field_notice_done" text="is done"/> <text name="spu_field_notice_done" text="is done"/>
<text name="spu_field_reason_rain" text="waiting for dry weather"/> <text name="spu_field_reason_rain" text="waiting for dry weather"/>
<text name="spu_field_target" text="Crop on the field (the mod handles the rest)"/>
<text name="spu_field_target_none" text="None — plan it yourself"/>
<text name="spu_field_target_bare" text="Prepared and sown with %s as soon as it is sowing time"/>
<text name="spu_field_target_keep" text="%s is already there: mowed and collected every time it is ready"/>
<text name="spu_field_target_switch" text="%s is left to finish growing and is harvested/mowed; then the field is prepared and sown with %s"/>
<text name="spu_field_waitSowing" text="waiting for sowing time for %s"/>
<text name="spu_field_fleetAll" text="Use all free machines (No = only ticked ones)"/>
<text name="spu_button_start" text="Start run"/> <text name="spu_button_start" text="Start run"/>
<text name="spu_button_stop" text="Stop run"/> <text name="spu_button_stop" text="Stop run"/>
<text name="spu_button_new" text="New run"/> <text name="spu_button_new" text="New run"/>
+6
View File
@@ -82,5 +82,11 @@ check("FL10 grund", whyMany, "notEnough")
check("FL10 ledige/krævet", args[1] .. "/" .. args[2], "2/3") 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") 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")) print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
os.exit(failures == 0 and 0 or 1) os.exit(failures == 0 and 0 or 1)
+9
View File
@@ -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 hø-opsamling venter", J.decide(hayCollect, info, {}, 0, rain).reason, "rain")
check("J11 tørvejr", J.decide(mowing, info, {}, 0, {raining = false}).action, "dispatch") 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")) print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
os.exit(failures == 0 and 0 or 1) os.exit(failures == 0 and 0 or 1)
+36
View File
@@ -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: høst", M.isWeatherSensitive("harvesting", {}, "harvest"), true)
check("M18 vejrfølsom: kultivering ikke", M.isWeatherSensitive("cultivating", {}, "harvest"), false) 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")) print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
os.exit(failures == 0 and 0 or 1) os.exit(failures == 0 and 0 or 1)