Files
fs25-adsmartpickup/FS25_ADSmartPickup/gui/SmartPickupFieldTab.lua
T
masterdraco cf7a2a51e5 feat(marker): sideriver — mål hvor riven lægger skåret, forskyd læssevognen tilsvarende (v1.24.0.0)
Brugerkrav 25/9: alle river-typer skal virke. Nedlægningsområdet (runtime-felt eller riverens XML) måles
i traktorens ramme mens riven arbejder; læssevognen på rivens kursus får CP-toolOffsetX = egen -
skårets forskydning og får sin værdi tilbage når den slippes.
2026-09-25 23:35:42 +02:00

257 lines
13 KiB
Lua

-- Smart Pickup — fanen Marker
-- Tabel over gårdens slåafgrøde-marker (Mark · Afgrøde · Plan · Auto/Man. · Status · Maskiner); klik på
-- en række viser markens detaljer under tabellen (plan, kørsel, riv, tving lager, marksiloer). Nederst
-- markflåden: foreslåede sæt med flueben. Al logik ligger i ADFieldJobs/ADFieldModel/ADFieldView.
-- Funktionerne lægges på SmartPickupFrame (indlæses efter SmartPickupFrame.lua).
SmartPickupFrame.FIELD_ROW_KINDS = {
showAllFields = true, fieldProduct = true, fieldMode = true, fieldRake = true, fieldStore = true, fleetVehicle = true,
}
SmartPickupFrame.ROLE_KEYS = {mower = "spu_field_role_mower", tedder = "spu_field_role_tedder", rake = "spu_field_role_rake", collector = "spu_field_role_collector"}
-- ---- tekster ----
function SmartPickupFrame:fieldReasonText(reason)
if type(reason) ~= "table" then return nil end
if reason.text ~= nil then return reason.text end
local role = reason.role ~= nil and self.i18n:getText(SmartPickupFrame.ROLE_KEYS[reason.role] or "spu_field_role_mower") or ""
local template = self.i18n:getText(reason.key)
local args = reason.args or {}
local ok, text = pcall(string.format, template, role, args[1] or 0, args[2] or 0)
return ok and text or template
end
function SmartPickupFrame:fieldStatusText(field, info)
local text = self.i18n:getText(ADFieldView.statusKey(field, info))
if info.markerId == nil then
return string.format(text, info.id)
end
if (field.stage == "idle" or field.stage == "ready") and info.mowable then
text = string.format("%s %d %%", text, math.floor((info.readyRatio or 0) * 100 + 0.5))
end
local reason = self:fieldReasonText(field.reason)
if reason ~= nil then text = text .. ": " .. reason end
return text
end
function SmartPickupFrame:fieldPlanText(field)
local text = self.i18n:getText(field.plan.product == "hay" and "spu_field_plan_hay" or "spu_field_plan_silage")
if field.plan.rake then
text = text .. " + " .. self.i18n:getText("spu_field_plan_rake")
end
return text
end
function SmartPickupFrame:fieldCells(row)
local info, field = row.info, row.field
local names = {}
for _, assignment in ipairs(ADFieldJobs.assignmentsFor(info.id)) do
table.insert(names, ADRuns.vehicleLabel(assignment.vehicle))
end
local name = string.format("%s%sF%d", info.id == self.selectedFieldId and "> " or "", info.markerId == nil and "! " or "", info.id)
return {
name, info.cropTitle or "-", info.mowable and self:fieldPlanText(field) or "-",
self.i18n:getText(field.plan.mode == "auto" and "spu_field_mode_auto" or "spu_field_mode_manual"),
info.mowable and self:fieldStatusText(field, info) or self.i18n:getText("spu_field_notMowable"),
ADFlowView.joinCounted(names, ", ", "-"),
}
end
-- ---- opbygning ----
function SmartPickupFrame:fillFieldRows(adEnv)
self.fieldRows = {}
local i18n = self.i18n
local infos = ADFields.list()
if #infos == 0 then
self:addTextRow(i18n:getText("spu_field_scanning"))
else
self:addTextRow(i18n:getText("spu_field_hint"))
local hidden = ADFieldView.hiddenCount(infos, self.showAllFields == true)
if hidden > 0 or self.showAllFields then
self:addToggleRow("showAllFields", string.format(i18n:getText("spu_field_showAll"), hidden), {i18n:getText("ui_no"), i18n:getText("ui_yes")}, self.showAllFields == true)
end
self:addCellRow(self.fieldPrefab, {i18n:getText("spu_col_field"), i18n:getText("spu_col_crop"), i18n:getText("spu_col_plan"), i18n:getText("spu_col_mode"), i18n:getText("spu_col_status"), i18n:getText("spu_col_tractors")})
for _, row in ipairs(ADFieldView.rows(infos, ADFieldJobs.state.fields, self.showAllFields == true)) do
local cells = self:addCellRow(self.fieldPrefab, self:fieldCells(row))
local fieldId = row.info.id
if cells[1] ~= nil and cells[1].parent ~= nil then
self:makeRowClickable(cells[1].parent, function(frame)
frame.selectedFieldId = fieldId
frame:requestRebuild()
end)
end
table.insert(self.fieldRows, {id = fieldId, cells = cells})
end
end
local selected = self.selectedFieldId ~= nil and ADFields.getInfo(self.selectedFieldId) or nil
if selected ~= nil then
self:fillFieldDetail(adEnv, selected)
end
self:fillFleetRows(adEnv)
end
function SmartPickupFrame:fillFieldDetail(adEnv, info)
local i18n = self.i18n
local field = ADFieldJobs.getField(info.id)
self:addTextRow(string.format(i18n:getText("spu_field_section"), info.id, tostring(info.cropTitle or "-")))
if not info.mowable then
self:addTextRow(" " .. i18n:getText("spu_field_notMowable"))
return
end
local isHay = field.plan.product == "hay"
self:addToggleRow("fieldProduct", i18n:getText("spu_field_plan"), {i18n:getText("spu_field_plan_silage"), i18n:getText("spu_field_plan_hay")}, isHay)
self:addToggleRow("fieldMode", i18n:getText("spu_field_mode"), {i18n:getText("spu_field_mode_manual"), i18n:getText("spu_field_mode_auto")}, field.plan.mode == "auto")
self:addToggleRow("fieldRake", i18n:getText("spu_field_rake"), {i18n:getText("ui_no"), i18n:getText("ui_yes")}, field.plan.rake == true)
if info.markerId ~= nil then
self:addTextRow(string.format(i18n:getText("spu_field_marker"), tostring(info.markerName), info.markerDistance or 0))
else
self:addTextRow(" " .. string.format(i18n:getText("spu_field_noMarker"), info.id))
end
if field.mowWidth ~= nil then
self:addTextRow(string.format(i18n:getText("spu_field_mowWidth"), field.mowWidth))
end
if field.plan.rake and field.windrowOffset ~= nil then
self:addTextRow(string.format(i18n:getText("spu_field_windrowOffset"), math.abs(field.windrowOffset),
i18n:getText(math.abs(field.windrowOffset) <= 0.05 and "spu_field_side_center" or (field.windrowOffset > 0 and "spu_field_side_left" or "spu_field_side_right"))))
end
self:fillFieldStoreRows(adEnv, info, field)
end
-- Tving lager: siloer der tager markens vare (skår ved ensilage, tørskår ved hø); valgte først.
function SmartPickupFrame:fillFieldStoreRows(adEnv, info, field)
local i18n = self.i18n
self:addTextRow(i18n:getText("spu_field_section_store"))
local fillType = field.plan.product == "hay" and info.hay or info.windrow
local chosen = {}
for _, id in ipairs(field.plan.storeBuildingIds) do chosen[id] = true end
local farmId = g_currentMission:getFarmId()
local fieldSilos = {}
for _, building in ipairs(ADFields.fieldSilos(adEnv, farmId, info)) do fieldSilos[building.id] = building end
local count = 0
for _, building in ipairs(ADBuildings.list(adEnv, farmId)) do
if building.kind == "silo" and (chosen[building.id] or ADRunsController.canStoreAt(adEnv, building, {{fillType = fillType, isPallet = false}})) then
count = count + 1
local row = self:addRow(self.ingredientPrefab, {kind = "fieldStore", buildingId = building.id})
local suffix = fieldSilos[building.id] ~= nil and (" (" .. i18n:getText("spu_field_fieldSilo") .. ")") or ""
row:getDescendantByName("title"):setText(" " .. tostring(building.name) .. suffix)
row:getDescendantByName("option"):setState(chosen[building.id] and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false)
end
end
if count == 0 then
self:addTextRow(" " .. i18n:getText("spu_field_noStore"))
elseif #field.plan.storeBuildingIds == 0 then
self:addTextRow(" " .. i18n:getText("spu_field_storeAuto"))
end
-- marksiloer skal tømmes hjem af et udkørselsflow
for id, building in pairs(fieldSilos) do
local emptied = false
for _, run in ipairs(ADRunsController.store.runs) do
for _, sourceId in ipairs(run.kind == "outbound" and run.sourceBuildingIds or {}) do
if sourceId == id then emptied = true end
end
end
if not emptied then
self:addTextRow(" " .. string.format(i18n:getText("spu_field_fieldSiloNotEmptied"), tostring(building.name)))
end
end
end
function SmartPickupFrame:fillFleetRows(adEnv)
local i18n = self.i18n
self:addTextRow(i18n:getText("spu_field_section_fleet"))
local rigs = ADFieldJobs.getRigs(adEnv)
table.sort(rigs, function(a, b)
if a.role ~= b.role then return a.role < b.role end
return ADFlowView.naturalLess(a.label, b.label)
end)
for _, rig in ipairs(rigs) do
local row = self:addRow(self.vehiclePrefab, {kind = "fleetVehicle", vehicleId = rig.id})
local title = string.format(" %s — %s, %.1f m", tostring(rig.label), i18n:getText(SmartPickupFrame.ROLE_KEYS[rig.role]), rig.width or 0)
if (rig.width or 0) > ADFieldModel.CP_MAX_WIDTH then
title = title .. " — " .. i18n:getText("spu_field_tooWideShort")
end
local assignment = ADFieldJobs.state.assignments[rig.id]
if assignment ~= nil then
title = title .. string.format(" — F%d", assignment.fieldId)
end
row:getDescendantByName("title"):setText(title)
row:getDescendantByName("option"):setState(rig.enabled and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false)
end
if #rigs == 0 then
self:addTextRow(" " .. i18n:getText("spu_field_noRigs"))
end
end
-- ---- live ----
function SmartPickupFrame:refreshFieldRows(adEnv)
local rowsById = {}
for _, row in ipairs(ADFieldView.rows(ADFields.list(), ADFieldJobs.state.fields, true)) do rowsById[row.info.id] = row end
local working, total = 0, 0
for _, entry in ipairs(self.fieldRows or {}) do
local row = rowsById[entry.id]
if row ~= nil then
SmartPickupFrame.setCells(entry.cells, self:fieldCells(row))
if row.info.mowable then total = total + 1 end
if ADFieldModel.isWorking(row.field) then working = working + 1 end
end
end
self:setStatusText(self.statusOverride or string.format(self.i18n:getText("spu_field_status"), working, total))
local selected = self.selectedFieldId ~= nil and ADFieldJobs.getField(self.selectedFieldId) or nil
local info = self.selectedFieldId ~= nil and ADFields.getInfo(self.selectedFieldId) or nil
local isWorking = selected ~= nil and ADFieldModel.isWorking(selected)
self.fieldStartButtonInfo.text = self.i18n:getText(isWorking and "spu_field_button_stop" or "spu_field_button_start")
self.fieldStartButtonInfo.disabled = selected == nil or info == nil or not info.mowable
or (not isWorking and (info.markerId == nil or (info.readyRatio or 0) < ADFieldModel.CUT_RATIO))
end
-- ---- klik ----
function SmartPickupFrame:onClickFieldRow(state, data)
self:safe(function(frame)
local isOn = state == SmartPickupFrame.STATE_ON
local id = frame.selectedFieldId
if data.kind == "showAllFields" then
frame.showAllFields = isOn
elseif data.kind == "fleetVehicle" then
ADFieldJobs.toggleFleet(data.vehicleId)
elseif id == nil then
return
elseif data.kind == "fieldProduct" then
ADFieldJobs.setPlan(id, {product = isOn and "hay" or "silage"})
elseif data.kind == "fieldMode" then
ADFieldJobs.setPlan(id, {mode = isOn and "auto" or "manual"})
elseif data.kind == "fieldRake" then
ADFieldJobs.setPlan(id, {rake = isOn})
elseif data.kind == "fieldStore" then
local ids = {}
for _, buildingId in ipairs(ADFieldJobs.getField(id).plan.storeBuildingIds) do
if buildingId ~= data.buildingId then table.insert(ids, buildingId) end
end
if isOn then table.insert(ids, data.buildingId) end
ADFieldJobs.setPlan(id, {storeBuildingIds = ids})
end
frame:requestRebuild()
end)
end
function SmartPickupFrame:onClickFieldStartStop()
self:safe(function(frame)
local id = frame.selectedFieldId
if id == nil then return end
local field = ADFieldJobs.getField(id)
if ADFieldModel.isWorking(field) then
ADFieldJobs.stopField(frame.getAdEnv(), id)
else
if field.stage ~= "ready" then
ADFieldJobs.forceReady(id)
end
ADFieldJobs.requestStart(id)
frame.statusOverride = string.format(frame.i18n:getText("spu_field_startRequested"), id)
frame.statusOverrideTicks = SmartPickupFrame.STATUS_OVERRIDE_TICKS
end
frame:requestRebuild()
end)
end