Files
fs25-adsmartpickup/docs/superpowers/plans/2026-09-26-guider.md
T

14 KiB
Raw Blame History

Guider til opsætning (marker + flows) — implementeringsplan

For agentic workers: REQUIRED SUB-SKILL: superpowers:executing-plans (native; brugeren har givet fuld autonomi). Trin bruger - [ ].

Goal: Fields- og Flows-fanen får en oversigt med resumé (klik = ret ét trin) og en trin-for-trin-guide ("Opsæt mark" / "Opsæt flow") på én fælles guide-ramme.

Architecture: En ren guide-motor (adGuide.lua) styrer trin, synlighed, tælling, næste/tilbage/spring. Oven på den to rene trin-definitioner (adFieldGuide.lua, adFlowGuide.lua) der ud fra en kladde + et context (det spil-afhængige, hentet af GUI'et) giver rækker (som data), valg, standarder, resumé og gem-resultat. GUI-laget (gui/SmartPickupGuide.lua fælles trin-skærm, gui/SmartPickupFieldGuide.lua, gui/SmartPickupFlowGuide.lua) er tyndt: bygger context, tegner rækkerne, router klik og gemmer via de eksisterende ADFieldJobs/ADRunsController-funktioner.

Tech Stack: Lua 5.1 (FS25), luajit til tests, FS25 GUI (klonede prefabs).

Spec: docs/superpowers/specs/2026-09-26-guider-design.md

Global Constraints

  • Lua 5.1: ingen goto, ingen coroutines, ingen # på tabeller med huller; pcall om spil-API.
  • Gemt format (ADSmartPickup_fields.xml, ADSmartPickup_runs.xml) ændres ikke. Guiderne skriver kun via ADFieldJobs.setTarget/setPlan og ADRunsController.createRun/updateRun/assignVehicle/unassignVehicle.
  • Intet gemmes før [Gem]/[Kør automatisk]/[Start flow]. [Annullér], fanebyt og lukket menu kasserer kladden.
  • Rene moduler returnerer nye tabeller (kladden kopieres ved hvert valg).
  • Filer under ~800 linjer; SmartPickupFrame.lua (971) får kun små routing-ændringer.
  • Nye .lua i modDesc (rene moduler) eller source() i adSmartPickup.lua (GUI-filer), i rigtig rækkefølge.
  • Al ny tekst i både l10n_da.xml og l10n_en.xml.
  • Guide-rækker laves KUN med ingredientPrefab (Ja/Nej, flueben) og productPrefab (valg) med kind = "guideRow", routet i onClickIngredient/onClickProduct; klikbare tekstlinjer via makeRowClickable + pendingClick. Ingen guide-rækker på vehiclePrefab (v1.30.4-fælden).
  • Bundknapper: kun MENU_ACTIVATE, MENU_EXTRA_1, MENU_EXTRA_2, MENU_CANCEL (+ MENU_BACK) — andre findes ikke sikkert i FS25.
  • Tests: for t in tests/test_*.lua; do luajit $t | tail -1; done — alle grønne.

Review Focus

  1. Klik på guide-rækker gemmes aldrig (forkert routing) → alle nye kinds routes via guideRow; testplan-punkt in-game.
  2. Kladde muteres ved valg (Annullér gemmer alligevel) → test: choose ændrer ikke den gamle guide/kladde.
  3. Trin forsvinder/dukker op efter et valg (fx mål-afgrøde græs på kornmark) → tælling og næste følger de synlige trin; test.
  4. [Ret] fra oversigten må kun gemme det ene trin og bevare Auto/Manuel → test result(guide, nil) bevarer mode.
  5. Faste maskiner: maskine fast på anden mark flyttes kun hvis brugeren vælger den (skjult i listen) → test applyRigs.

Beslutninger (uklarheder i spec'en)

  • [Opsæt mark] uden valgt mark vises også (åbner trin 1 "Mark"); ellers kunne trin 1 aldrig nås.
  • "Så igen bagefter" (plan.sow uden mål-afgrøde) står i trin 2 under "Behold det der gror" for høst-/bare marker — ellers forsvinder muligheden.
  • Kalk: i Klargøring ("Vælg selv"); når Klargøring ikke vises (ren græsmark) står Kalk i Pleje.
  • Klargøring "Automatisk" = jord auto, kalk ja, mulch nej, tromle nej (anvendes kun når trinnet er synligt).
  • Maskiner-trinnet: antal ad gangen + aflæsningsvogne først, så "Faste maskiner?" + listen (listen kan være lang). Aflæsningsvogne 1–3 (modellens MAX_UNLOADERS), ikke 1–5.
  • Mark uden markør: gemmes med den valgte tilstand; job-løkken venter allerede på markøren (dagens regel). Oversigten viser "sæt markøren".
  • Mål-afgrøde tvinger Auto (som i dag); på Oversigt-trinnet vises da kun [Kør automatisk].
  • [Ret] fra trin 8 vender tilbage til Oversigt-trinnet (kladden bevares); [Tilbage] i et [Ret]-trin fortryder det ene trins ændringer.
  • Flows: bundlinjen har plads til 4 knapper → [Start/Stop] [Opsæt flow] [Nyt flow] [Slet]; Omdøb ligger på resuméets Navn-linje; Start alle/Stop alle bliver på fanen Oversigt.
  • Halm: context.straw = afgrøder med halm (FS25 fruitType.hasWindrow/windrowName); kan det ikke aflæses (tom), vises halm-valget for alle høstafgrøder som i dag.

Etape A — fælles ramme + Marker (v1.31.0.0)

Task A1: Guide-motoren adGuide.lua

Files: Create FS25_ADSmartPickup/adGuide.lua, tests/test_adGuide.lua; Modify modDesc.xml (før adFieldModel.lua).

Interfaces (Produces):

ADGuide.new(def, draft, context, options) -> guide
--   def = {order = {stepId...}, isVisible = function(stepId, guide) -> bool}
--   options = {step = id|nil, single = bool, returnTo = "close"|"summary"}
--   guide = {def, draft, context, step, single, returnTo, snapshot}
ADGuide.visibleSteps(guide) -> {stepId...}
ADGuide.position(guide) -> index|nil, total
ADGuide.isLast(guide) -> bool
ADGuide.next(guide) -> guide, event   -- event nil | "done"
ADGuide.back(guide) -> guide, event   -- event nil | "cancel"
ADGuide.jump(guide, stepId, returnTo) -> guide (single, snapshot = kopi af kladden)
ADGuide.withDraft(guide, draft) / ADGuide.withContext(guide, context) -> guide
ADGuide.deepCopy(value) ; ADGuide.toggleItem(list, item, on) -> ny liste
ADGuide.labelText(label, getText) -> string   -- label = {key, args} | {text}
ADGuide.lineText(line, getText) -> "Titel: del, del"  -- line = {step, title = label, parts = {label...}}
  • Test (RED): næste/tilbage springer skjulte trin over; tælling "3 af 5" over synlige; next på sidste = "done"; back på første = "cancel"; jump(..., "summary") + next lander på summary med single=false; jump(..., "close") + next = "done"; back i spring gendanner snapshot; withDraft returnerer ny guide (gammel urørt); trin der bliver usynligt efter et valg: next går til næste synlige i order; toggleItem uden dubletter; labelText med args og manglende args (pcall).
  • Kør luajit tests/test_adGuide.lua → FAIL (fil mangler).
  • Implementér; kør → ALLE TESTS OK.

Task A2: Markguidens trin adFieldGuide.lua

Files: Create FS25_ADSmartPickup/adFieldGuide.lua, tests/test_adFieldGuide.lua; Modify modDesc.xml (efter adFieldView.lua).

Interfaces:

  • Consumes: ADGuide.*, ADFieldModel.product/MAX_RIGS/MAX_UNLOADERS, ADFieldFleet.visibleForField, ADFlowView.naturalLess.
  • Produces:
ADFieldGuide.ORDER = {"field","crop","cropChoice","prep","care","machines","store","summary"}
ADFieldGuide.open(field, context, options) -> guide          -- context.info påkrævet
ADFieldGuide.openPicker(context) -> guide                     -- trin "field"
ADFieldGuide.pickField(guide, field, context) -> guide        -- videre til trinnet efter "field"
ADFieldGuide.isVisible(stepId, guide) -> bool
ADFieldGuide.rows(guide) -> {row...}
--   row = {type="choice", id, title, options={{label, value}}, value}
--       | {type="toggle", id, title, off, on, value}
--       | {type="check", id, item, title, value}
--       | {type="text", label} | {type="link", id, value, label}
ADFieldGuide.choose(guide, id, value) -> guide
ADFieldGuide.summary(guide) -> {line...}
ADFieldGuide.result(guide, start) -> {fieldId, targetFruit, targetChanged, plan, rigIds}
ADFieldGuide.applyRigs(rigIdsByField, fieldId, rigIds) -> ny {[fieldId] = ids}
ADFieldGuide.stepKey(stepId) / hintKey(stepId) -> l10n-nøgler
  • context: {info, sowable={{name,title}}, mowable={[name]=true}, straw={[name]=true}|nil, rigs={{id,text,role}}, rigIdsByField, fieldSilos={{id,text}}, stores={{id,text}}|nil, storeHidden, fields={{id,text}}}.

  • kladde: {fieldId, plan (kopi), originalTarget, prepAuto, useDedicated, storeMode ("auto"|"manual"|"fieldSilo"), storeIds, pickField}.

  • Test (RED): synlige trin for (a) græs uden mål: crop, cropChoice, care, machines, store, summary (ingen prep); (b) høst m. halm: + prep, cropChoice (halm); (c) høst uden halm (straw sat, afgrøden ikke med): ingen cropChoice; (d) bar mark uden mål: crop, prep, machines, summary (ingen care/store); (e) kornmark med mål græs: cropChoice (ensilage/hø) + prep; picker: kun "field". Standarder: ny mark → prepAuto, useDedicated=false, storeMode auto; plan med soil=plow → prepAuto=false; plan med rigIds → useDedicated; storeIds lig marksilo → fieldSilo. choose ændrer ikke gammel guide; rigIds-check toggler; result: prepAuto overskriver soil/mulch/lime/roll kun når prep er synlig; mode auto/manual efter start, uændret ved start=nil, auto ved mål; storeBuildingIds pr. storeMode; rigIds tom når useDedicated=false; targetChanged. applyRigs fjerner maskinen fra anden mark. Rækker: maskiner-trinnet skjuler maskiner faste på andre marker + tekst med antal; marksilo-spørgsmål kun når fieldSilos findes; "Vælg selv" viser siloerne. Resumé: linjer kun for synlige trin, med rigtige nøgler (fx spu_guide_sum_dedicated args {3}).

  • Kør → FAIL; implementér; kør → OK. Hele suiten grøn.

Task A3: Fælles trin-skærm gui/SmartPickupGuide.lua

Files: Create gui/SmartPickupGuide.lua; Modify adSmartPickup.lua (source efter SmartPickupFlowTab.lua), gui/SmartPickupFrame.lua (initGuideButtons i new, routing guideRow i onClickIngredient, refreshButtons → getGuideButtons, switchTab/onFrameClose lukker guiden, rebuild for faner med åben guide), gui/SmartPickupOutboundTab.lua (onClickProduct → guideRow).

Interfaces (Produces):

SmartPickupFrame.GUIDE_HOOKS[kind] = {model, tab, context(frame, adEnv, guide) -> context, save(frame, guide, start), choose(frame, id, value) -> handled}
SmartPickupFrame:initGuideButtons(i18n)
SmartPickupFrame:openGuide(kind, guide)   SmartPickupFrame:closeGuide()
SmartPickupFrame:fillGuideRows(adEnv)     -- overskrift "Trin 3 af 7 — …", forklaring, rækker
SmartPickupFrame:guideText(label) -> string
SmartPickupFrame:addLinkRow(text, action)
SmartPickupFrame:onClickGuideRow(state, data)
SmartPickupFrame:getGuideButtons() -> knapliste
SmartPickupFrame:onClickGuideNext/Back/Cancel/SaveOnly()
  • Skriv filen; luajit -bl syntakstjek.

Task A4: Marker-fanen: oversigt, markflåde, guide

Files: Create gui/SmartPickupFieldGuide.lua (context, gem, knapper for fanen); Modify gui/SmartPickupFieldTab.lua (fillFieldRows → oversigt/markflåde/guide; fjern den lange detaljeliste), l10n da/en.

  • Oversigt: hint, tabel, for valgt mark: overskrift, resumé-linjer (klik = ADGuide.jump(step, "close")), fuld status, markør-linje, marksilo-advarsel. Knapper: [Opsæt mark] (EXTRA_1), [Start nu/Stop] (ACTIVATE), [Markflåde] (EXTRA_2).
  • Markflåde: fillFleetRows(adEnv, nil) (alle maskiner). Knap [Til marker] (EXTRA_2).
  • Gem: setTarget hvis ændret → setPlan(plan) → applyRigs → setPlan(fid, {rigIds}) for ændrede marker.
  • Skriv, syntakstjek, suiten grøn.

Task A5: Docs + ship v1.31.0.0

  • TESTPLAN-markarbejde.md: afsnit "Guider (v1.31)"; BRUGERMANUAL/USER_MANUAL: Marker-afsnit kort.
  • Ship-procedure (version 1.30.4.0 → 1.31.0.0), commit feat(gui): guide "Opsæt mark" …, push gitea.

Etape B — Flows på samme ramme (v1.32.0.0)

Task B1: Flowguidens trin adFlowGuide.lua

Files: Create FS25_ADSmartPickup/adFlowGuide.lua, tests/test_adFlowGuide.lua; modDesc (efter adFlowView.lua).

Interfaces (Produces):

ADFlowGuide.ORDER = {"name","direction","buildings","goods","store","rules","wait","tractors","summary"}
ADFlowGuide.open(run, context, options) -> guide     -- run = eksisterende run
ADFlowGuide.openNew(name, context) -> guide          -- nyt flow (trin "name" synligt)
ADFlowGuide.isVisible(stepId, guide); ADFlowGuide.rows(guide); ADFlowGuide.choose(guide, id, value)
ADFlowGuide.summary(guide) -> {line...}
ADFlowGuide.result(guide) -> {isNew, runId, name, kind, changes, vehicleIds}
ADFlowGuide.vehicleChanges(oldIds, newIds) -> added, removed
ADFlowGuide.previewRun(draft) -> run-formet tabel til controllerens opslag
  • context: {buildings={{id,name,label,hasMarker,text}}, fillTypes={{name,title}}, products={{name,title}}, storeBuildings={{id,text}}, waitPools={{group,count}}, vehicles={{id,text,otherRunId,otherRunName,blocked}}, sellAtSteps, storeAboveSteps, returnBelowSteps, maxLoops}.
  • kladde: kopi af run-felterne + isNew, showAllBuildings, storeAuto.
  • Test (RED): name-trin kun ved nyt flow; store-trin kun ved udkørsel med en vare på Lager/Salg-ellers-lager; retning skifter bygnings-rolle; varer: forsyning auto/vælg (ingredients toggles), udkørsel politik pr. vare (products); regler pr. retning; traktorer i andre flows/blokerede skjult + tekst; resumé pr. retning; result.changes; vehicleChanges; choose immutabelt.
  • FAIL → implementér → OK.

Task B2: Flows-fanen i GUI'et

Files: Create gui/SmartPickupFlowGuide.lua; Modify gui/SmartPickupFrame.lua (rebuild for Flows-fanen: tavle + resumé, statiske rækker skjult; refreshLive/refreshButtons), l10n.

  • Oversigt: tavlen (fillOverviewRows), valgt flow: resumé (klik = ret ét trin; Navn-linjen = omdøb-dialog), status-linje (live). Knapper: [Start/Stop] (ACTIVATE), [Opsæt flow] (EXTRA_1), [Nyt flow] (EXTRA_2), [Slet] (CANCEL).
  • Gem: nyt → createRun; updateRun(changes); traktorer via vehicleChanges + assignVehicle/unassignVehicle; resolveMarkers; save(); [Start flow] → startRun.
  • Skriv, syntakstjek, suiten grøn.

Task B3: Docs + ship v1.32.0.0; selv-review

  • TESTPLAN + manualer (Flows-afsnit), ship 1.31.0.0 → 1.32.0.0.
  • Code-review-agent på hele diffen; ret CRITICAL/HIGH; evt. v1.32.1.0.