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

175 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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):**
```lua
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:
```lua
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):**
```lua
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):**
```lua
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.