diff --git a/docs/superpowers/specs/2026-09-27-marksilo-flow-design.md b/docs/superpowers/specs/2026-09-27-marksilo-flow-design.md new file mode 100644 index 0000000..88c544f --- /dev/null +++ b/docs/superpowers/specs/2026-09-27-marksilo-flow-design.md @@ -0,0 +1,99 @@ +# Marksilo-flow — design + +Dato: 2026-09-27 · Status: godkendt i chat, afventer review af denne spec + +## Formål + +Høsten lægges ofte i siloer ude ved markerne (mellemlager). I dag skal spilleren selv huske at køre varerne +hjem. Marksilo-flowet kigger jævnligt alle marksiloer igennem og sender selv op til **x** ledige vogne ud for at +hente varerne hjem til gårdens lagre. + +**Succes:** står der varer i en marksilo, som gården kan tage, bliver de kørt hjem uden at spilleren gør noget, +med højst x vogne ude ad gangen, og uden at tage vogne fra markarbejde, andre flows eller spilleren. + +## Brugerens valg (fra brainstorm) + +| Spørgsmål | Valg | +|---|---| +| Hvor er "hjem"? | Nærmeste af de gårdlagre, spilleren vælger i flowet, der tager varen og har plads | +| Hvad er "x vogne"? | Lånte ledige vogne fra hele gården — ingen fast tilknytning — højst x ude samtidig | +| Hvilke vogne må lånes? | Samme regel og flueben som markarbejdet (`fleetMode` "all"/"ticked", `ADFieldJobs.state.fleet`) | + +## Begreber + +- **Marksilo:** bygning af typen silo inden for `ADFields.FIELD_SILO_DISTANCE` (150 m) af en marks polygon + (`ADFields.fieldSilos`), samlet over alle marker, uden dubletter. En bygning, der er valgt som gårdlager i + flowet, er aldrig marksilo. +- **Gårdlager:** bygning spilleren vælger i flowet (samme bygningsvælger som udkørslens lagre, `storeBuildings`). +- **Lån:** en vogn, flowet har sendt på én tur. Lån ligger kun i hukommelsen. + +## Flowet + +Ny run-type `fieldSilos` i `ADRuns.KINDS` (ved siden af `supply`/`outbound`). Højst ét marksilo-flow pr. savegame +(GUI'et tilbyder ikke at oprette et til, når det findes). + +Felter (gemmes i `ADSmartPickup_runs.xml` som de øvrige): +- `storeBuildings` — gårdlagre (bygnings-id'er → markører via den eksisterende `resolveMarkers`) +- `maxVehicles` — x, 1–10, standard 2 +- `minLiters` — tærskel pr. silo og vare, standard 10000 +- `running` — til/fra (som de andre flows' Start/Stop) + +## Planlægning (ren logik, nyt modul `adFieldSiloPlanner.lua`, testes med luajit) + +Input: marksiloer `{id, markerId|nil, stock = {[fillType] = liters}}`, gårdlagre `{id, markerId, accepts = +{[fillType] = freeLiters}, x, z}`, aktive lån `{siloId, fillType, liters}`, ledige vogne `{id, x, z, capacity, +carries = {[fillType] = true}}`, `maxVehicles`, `minLiters`. + +1. **Kvalificerede opgaver:** pr. (silo, vare) hvor silo har markør, `liters >= minLiters`, og mindst ét + gårdlager har `freeLiters >= minLiters` for varen. Resten = liters minus det aktive lån på samme (silo, vare) + allerede er på vej med; kun opgaver med rest `>= minLiters` tæller. + Siloer uden markør → status `noMarker`; varer intet gårdlager tager → status `noFarmStore`. +2. **Rækkefølge:** fuldeste rest først; lige → lavest silo-id (stabilt). +3. **Vogne:** ledige pladser = `maxVehicles - #aktive lån`. For hver opgave i rækkefølge, så længe der er + pladser: nærmeste ledige vogn (til siloen), der kan bære varen; resten reduceres med vognens kapacitet; + samme opgave får flere vogne, så længe resten stadig er `>= minLiters`. +4. **Gårdlager pr. lån:** nærmeste gårdlager (til siloen) der tager varen med `freeLiters >= min(vognens + kapacitet, rest)`; ellers det med mest plads; ellers intet (opgaven springes over denne runde). + +Output: `{dispatch = {{vehicleId, siloId, siloMarkerId, fillType, storeMarkerId, liters}}, status = {...}}`. + +## Udførelse (`adFieldSilos.lua`) + +- **Takt:** planlæg hvert 60. s (spiltid via `dt`), kun når flowet kører. Lagertal læses som i dag + (`ADSmartPickup.getStationLevelAndCapacity` / silo-lagre). +- **Ledige vogne:** `ADFieldWork.getRigs(adEnv)` filtreret til `enabled and not busy and not controlled`, og + vogne der har aflæsbare enheder (`AutoDrive.getAllDischargeableUnits`), der understøtter varen. + `getRigs.busy` udvides med `ADFieldSilos.isLoaned(vehicle)`, så markarbejdet ikke tager en lånt vogn + (flows tager allerede kun deres egne tilknyttede vogne). +- **Afsend:** AutoDrive "Hent og aflever": første markør = silomarkør, anden = gårdlagerets markør, vare = + lånets vare, `loopCounter = 1`, parkering ved job slut slået til (`enableParkAtJobFinished`); start som + markarbejdet (`startAd` → mode:start). Log: `marksilo: '' henter l i → `. +- **Frigivelse:** lånet slettes når AD ikke længere er aktivt for vognen (turen og parkeringen er færdig), + når spilleren sætter sig i den, eller når flowet stoppes (stop = AD stoppes på lånte vogne). +- **Vagt:** er vognen ikke kommet i gang (ingen stigning i fyldning og står stille) efter 120 s, stoppes AD, + lånet slettes, og der logges én advarsel pr. (silo, vare), så samme opgave ikke fejler i ring hvert minut + (opgaven får 10 min pause). +- **Genindlæsning:** lån gemmes ikke. Ved indlæsning er alle vogne frie for flowet; en vogn AutoDrive selv + genstarter, kører sin tur færdig som almindelig AD-tur. + +## GUI + +Flow-fanen: typen "Marksiloer" kan oprettes (højst én). Indstillinger: gårdlagre (eksisterende vælger), +"Maks. vogne ude" (1–10), "Mindste mængde" (5.000 / 10.000 / 25.000 / 50.000 l), Start/Stop. +Status: `x/y vogne ude · n siloer venter`, og én række pr. marksilo med indhold: silo, vare, liter, på vej, +status (`ok` / `mangler markør` / `ingen gård tager `). Tekster i `l10n_da` + `l10n_en` (`spu_fs_*`). + +## Uden for scope + +- Salg direkte fra marksilo (kan senere bruge udkørslens prisregel). +- Faste vogne i flowet (valg 1 i brainstorm) — fravalgt. +- Lån der overlever genindlæsning. + +## Test + +- `tests/test_adFieldSiloPlanner.lua`: kvalificering (tærskel, markør, gård tager varen), fuldeste først, + x-loft inkl. aktive lån, flere vogne til én stor silo, vogn der ikke kan bære varen springes over, nærmeste + gårdlager med plads, fallback til mest plads, intet lager → ingen afsendelse. +- `tests/test_adRuns.lua`: ny kind gemmes/indlæses med `maxVehicles`/`minLiters`; ukendt kind → supply som før. +- In-game: silo ved en mark med 50.000 l hvede, gårdlager = Farma-silo, x = 2 → to vogne kører, afleverer, + parkerer; markarbejdet tager ikke de lånte vogne imens.