# Markarbejde, etape 1: slåafgrøder (græs, kløver, lucerne) Dato: 2026-09-25 · Mod: FS25_ADSmartPickup · Branch: `feature/markarbejde` ## Mål Når en græs-, kløver- eller lucernemark er klar, sender Smart Pickup selv maskinerne ud. Slåmaskinen slår marken via Courseplay. Ved **ensilage** samler en læssevogn skårene op og kører dem til den rigtige silo. Ved **hø** vender en vender først græsset (og en river river det evt. sammen), og derefter samles høet op. Alt kørsel på vej sker med AutoDrive. Alt arbejde på marken sker med Courseplay. Succes: en mark sat til *Auto* går fra "klar" til "færdig" med græsset i en silo, uden at spilleren rører noget, og menuen viser hele tiden, hvad der sker og hvorfor. Ikke med i etape 1: mejetærskerafgrøder (etape 2), roer (etape 3), jordbehandling og såning, baller. ## Arbejdsdeling | Del | Ansvar | |---|---| | **Smart Pickup (vores)** | Hvilken mark, hvilket trin, hvilken maskine, hvilken silo. Holder status og viser den | | **AutoDrive** | Kørsel på vej til markens `F`-markør og til silo; overtager ved "vogn fuld" og genstarter Courseplay | | **Courseplay** | Markgrænse, kursus, selve arbejdet på marken, "fuld"/"færdig"-signaler | ## Beslutninger (med begrundelse) 1. **Faste sæt.** Redskabet sidder fast på traktoren (eller er selvkørende, fx Krone BiG M 450). Mod'en kobler aldrig redskaber på. *Begrundelse:* auto-påkobling var det, der aldrig blev pålideligt i AI-Bonden. 2. **Markflåde med forslag og flueben.** Mod'en foreslår sæt ud fra redskabets spec (`spec_mower`, `spec_tedder`, `spec_windrower`, `spec_forageWagon`); kun sæt med flueben bliver sendt ud. *Begrundelse:* mod'en må aldrig tage en maskine, spilleren selv bruger. Et sæt, spilleren sidder i (`getIsControlled`/`getIsEntered`), springes altid over. 3. **Staging-markør pr. mark = AutoDrive-markør med navnet `F`** (fx `F109`), stort/småt ligegyldigt, evt. med mellemrum (`F 109`). Mangler den, vises marken med "!" og sendes ikke ud. 4. **Auto/Manuel pr. mark.** Auto starter selv ved "klar"; Manuel venter på *Start nu*. Standard: Manuel (intet sker uventet efter opdatering). 5. **Plan pr. mark: Ensilage eller Hø.** Standard: Ensilage. **Riv** er et valg pr. mark, standard *fra*: læssevognene i Unrealistic Pack kan køre 40 m bredt og samler spredt hø op. 6. **Bredde.** Maskiner kører med deres *aktuelle* bredde (Unrealistic Packs `variableWorkWidth`, `aiKeepCurrentWidth`). Courseplay kan højst 50 m → et sæt over 50 m sendes ikke ud (advarsel). Marken husker **slåbredden**; opsamling/vending/rivning kræver bredde ≥ slåbredden, så hver bane dækker det, slåmaskinen lagde. Så behøver banerne ikke flugte med skårene. 7. **Silovalg** genbruger Smart Pickups motor (`ADLoadSwap`): nærmeste silo med plads, der tager varen, med vej fra `F`-markøren. *Tving lager* pr. mark (som "Lager til" i flows). Fabrikker er ikke mål. 8. **Marksilo.** En silo, hvis afstand til markens polygon er ≤ 150 m, er et mellemlager. Den må gerne vælges (korte ture). Oversigten viser "!" ved marken, hvis intet Smart Pickup-udkørselsflow har siloen som kilde — hjemkørslen klares af de eksisterende flows, ikke af markdelen. 9. **"Klar"** = ≥ 90 % af markens prøvepunkter står i høstvinduet for markens slåafgrøde (`FSDensityMapUtil.getFruitArea`, samme metode som Arbejdsseddel, bevist i spillet). `field.fieldState` bruges ikke (frossen NPC-plan for spillerejede marker). 10. **Slåafgrøde** afgøres ud fra spillets konvertere: `MOWER` giver et skår, og `TEDDER` gør skåret til en `DRY…`-type (græs, lucerne, kløver, kløver2). Rug/bønner (→ halm) er udelukket. Ensilage-varen er skåret (`GRASS_WINDROW` …), hø-varen er tørskåret (`DRYGRASS_WINDROW` …). 11. **Én maskine, én mark.** Flere marker kan køre samtidig. Højst 2 læssevogne pr. mark. 12. **Rækkefølge.** Næste trin starter først, når det forrige er *færdigt* på hele marken (ingen vender bag slåmaskinen). Enklere og uden kollisioner mellem Courseplay-kurser. 13. **Fejl stopper ikke alt.** Kan Courseplay ikke lave kursus, prøves igen efter 5 min; imens står marken med grunden. Andre marker kører videre. 14. **Genindlæsning.** Markernes trin gemmes i `ADSmartPickup_fields.xml` i savegame-mappen. Efter indlæsning genoptages trinnet: sæt, der ikke længere kører, bliver sendt ud igen. ## Opbygning Rene moduler (ingen spil-API, testes med `luajit`): | Fil | Ansvar | |---|---| | `adFieldModel.lua` | Mark-plan og -status som data: plan, tilstand, overgange (`nextStage`), bredde-regler, gem/indlæs-records | | `adFieldFleet.lua` | Rolle ud fra redskabs-specs, forslag, valg af ledigt sæt (nærmest markøren) | | `adFieldView.lua` | Rækker og tekster til fanen Marker (sortering, filtre, statusord) | Spil-moduler (tynde, pcall'ede): | Fil | Ansvar | |---|---| | `adFields.lua` | Ejede marker, polygon/prøvepunkter, afgrøde, klar-andel, `F`-markør, marksiloer | | `adCpBridge.lua` | Start Courseplay på en mark (grænse → kursus → start, async med annullering), læs aktiv/færdig | | `adFieldJobs.lua` | Tick hvert sekund: opdag klar, send sæt ud, følg ankomst → CP → færdig, næste trin, gem | | `gui/SmartPickupFieldTab.lua` | Fanen **Marker**: tabel, detalje, markflåde | ### Forløb pr. mark ``` Ensilage: idle → ready → mowing → collecting → done Hø: idle → ready → mowing → tedding → [raking] → collecting → done done → idle, når marken ikke længere er slået (vokser igen) ``` Hvert arbejdstrin har undertilstande pr. sæt: `driving` (AutoDrive til `F`) → `starting` (CP laver kursus) → `working` (CP kører) → `returning` (AutoDrive til parkering). Trinnet er færdigt, når Courseplay meldte færdig for mindst ét sæt, og intet sæt på marken stadig arbejder. **Slå/vende/rive:** AutoDrive `DRIVETO` til `F`-markøren. Ved stop inden for 30 m starter `adCpBridge` Courseplay med `fieldPosition` = et prøvepunkt inde i marken. Når CP er færdig: AutoDrive `DRIVETO` til sættets parkering (sættets AD-parkeringsmarkør, ellers bliver det stående ved `F`). **Opsamling:** Før CP startes sættes AutoDrive til `PICKUPANDDELIVER`, første markør = `F`, anden = silo, `setStartHelper(true)` med hjælper Courseplay. Så klarer AutoDrive og Courseplay selv fuld → aflæs → tilbage → `restartCP`. Ved CP færdig: AutoDrive tømmer resten (`enableParkAtJobFinished`) og parkerer. ## Fanen Marker - Tabel: **Mark · Afgrøde · Plan · Auto/Man. · Status · Maskiner**. Kun slåafgrøde-marker som standard (*Vis alle marker* viser resten). Klik på en række → detalje. - Detalje: Plan (Ensilage/Hø), Auto/Manuel, Riv, Tving lager (liste som "Lager til"), **Start nu**/**Stop**, status med grund, markør og slåbredde. - Markflåde: foreslåede sæt med flueben, rolle og aktuel bredde; advarsel over 50 m. - Knapper på fanen: Start nu / Stop på valgt mark (samme placering som flows). - Statusord: *Venter · Klar · Slås · Vendes · Rives · Samles op · Færdig* + grund ("venter på slåmaskine", "! mangler markør F109", "læssevogn for smal: 20 m < 40,8 m", "Courseplay: intet kursus"). ## Fejlhåndtering | Situation | Hvad sker | |---|---| | Ingen `F`-markør | "!" i tabellen, intet sendes | | Intet ledigt sæt med rollen | Status "venter på …", prøver hvert 5 s | | Sæt for bredt (> 50 m) eller for smalt (< slåbredde) | Sættet springes over med grund | | AutoDrive kan ikke finde vej | Sættet frigives, status med grund, andet sæt prøves | | CP-grænse/kursus fejler | 5 min pause for marken, grund vises | | Spilleren sætter sig i et sæt | Sættet frigives fra marken (arbejdet fortsætter ikke automatisk) | | Genindlæsning | Trin genoptages fra gemt fil | ## Test - `tests/test_adFieldModel.lua`, `tests/test_adFieldFleet.lua`, `tests/test_adFieldView.lua`: alle overgange, bredde-regler, forslag, sortering/filtre, gem/indlæs. - In-game testprotokol (manualen + denne spec): én mark Manuel → Start nu → slå → saml op → silo, derefter Hø, derefter Auto. Loglinjer: `ADSmartPickup: F109 …`. ## Senere udvidelser (noter) - **Flere maskiner om samme mark, maks 5** (Courseplays multi-tool-grænse, 2026-09-25): slå/vende/rive med op til 5 sæt på én mark samtidig via Courseplays konvoj (multi-tool), og op til 5 læssevogne. Kræver at `ADFieldModel.maxRigs` bliver en indstilling pr. mark (1–5) og at sættene får samme CP-kursus med hver sin position i konvojen.