From 127f4cc9d93a1942bcdbe8cd87d72447877e3588 Mon Sep 17 00:00:00 2001 From: masterdraco Date: Mon, 28 Sep 2026 17:56:12 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20maskinpulje=20=E2=80=94=20design=20(tra?= =?UTF-8?q?ktorer=20og=20redskaber=20som=20f=C3=A6lles=20pulje,=20k=C3=B8,?= =?UTF-8?q?=20hent/arbejd/park=C3=A9r/hjem)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../specs/2026-09-28-maskinpulje-design.md | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 docs/superpowers/specs/2026-09-28-maskinpulje-design.md diff --git a/docs/superpowers/specs/2026-09-28-maskinpulje-design.md b/docs/superpowers/specs/2026-09-28-maskinpulje-design.md new file mode 100644 index 0000000..304210d --- /dev/null +++ b/docs/superpowers/specs/2026-09-28-maskinpulje-design.md @@ -0,0 +1,149 @@ +# Maskinpulje — design + +Dato: 2026-09-28 · Status: godkendt i chat (afsnit 1–4), afventer review af denne spec + +## Formål + +Plove, harver, kultivatorer, såmaskiner, tømmevogne m.m. bruges kun en gang imellem. I dag skal hvert af dem have +en traktor koblet fast på hele året, fordi markarbejdet kun kan sende en traktor, der allerede har det rigtige +redskab bag sig. Maskinpuljen gør traktorer og redskaber til fælles puljer: et job venter i kø, til en traktor er +ledig; traktoren henter det redskab, jobbet kræver, udfører arbejdet, parkerer redskabet på dets plads og kører +selv hjem til sin egen parkering. + +**Succes:** med fx 3 ledige traktorer, 2 plove og 1 kultivator bliver markernes arbejde udført uden at nogen +traktor står låst til et redskab — færre køretøjer at købe og vedligeholde. Flows, der kører hele tiden (fodring, +slurry, mælk …), påvirkes ikke. + +## Brugerens valg (fra brainstorm) + +| Spørgsmål | Valg | +|---|---| +| Hvordan findes redskabets Connect-markør? | Navneregel: AD-parkering "X" ↔ markør "Connect X" | +| Hvem er i puljen? | Automatisk ud fra opsætningen — ingen afkrydsning | +| Hvilken traktor henter? | Nærmeste ledige med nok hk | +| Omfang | Markarbejde + tømmevogne ved høst + marksilo-flowet. Flows er undtaget | +| Tømmevogne ved høst | Også fra puljen, men dedikeret til marken, til høsten dér er færdig | +| Arkitektur | Udvid markarbejdets eget maskinvalg (ikke en separat puljemanager) | + +## Begreber + +- **Puljeredskab:** et redskab eller en vogn, der holder **frakoblet**, har en AD-parkering (`advd.parkDestination` + → markør "X"), og hvor der findes en markør "Connect X" (`ADHitch.parseMarker` = attach, navn = X; store/små + bogstaver ligegyldigt). Uden Connect-markør er redskabet ikke i puljen — det er spillerens måde at holde det + udenfor. +- **Puljevogn:** puljeredskab med rollen `unloader` (vogn med tip/snegl, `spec_trailer` + `spec_dischargeable`). +- **Puljetraktor:** motoriseret køretøj uden noget koblet bag på (`ADHitch.rearImplements` tom), ikke i et flow + (`ADRunsController.getRunForVehicle == nil`), ikke optaget (samme `busy`-regel som `getRigs`), ikke styret af + spilleren, med egen AD-parkering (`vehicle.advd.parkDestination >= 1`) og flueben i markflåden + (`ADFieldFleet.isEnabled`). +- **Færdig kombination:** det markarbejdet bruger i dag — traktor med redskabet allerede koblet på (`getRigs`). +- **Puljekombination:** {traktor, redskab, Connect-markør-id, redskabets parkering-id, traktorens parkering-id}. + +AD-parkering: hver maskine har sin egen `advd.parkDestination`; AD bruger det valgte redskabs, når et er koblet på +(`AutoDriveVehicleData.lua:88-95`). Traktorens egen parkering læses derfor altid på traktoren selv. + +## Komponenter + +### `adPool.lua` (nyt, ren logik + tynd spil-adapter) + +Ren logik (testes med luajit): +- `ADPool.roleOf(specs)` — rolle for et frakoblet redskab ud fra dets egne specs via `ADFieldFleet.ROLE_SPECS`; + vogn med `spec_trailer` + `spec_dischargeable` og intet andet → `unloader`. +- `ADPool.isPoolImplement(item)` — `item = {attached, parkMarkerName, connectMarkerId}` → frakoblet, med parkering + og Connect-markør. +- `ADPool.powerOk(tractorPower, neededPower)` — true når et af tallene mangler (nil/0), ellers `tractorPower >= + neededPower`. +- `ADPool.pairFor(role, fieldX, fieldZ, implements, tractors, reserved, accept)` — redskab med rollen nærmest marken + (ikke reserveret, `accept(implement)` sand), derefter nærmeste ledige traktor til redskabet med `powerOk`. + Returnerer `pair` eller `nil, why, args` (`noPoolImplement` / `noPoolTractor` / `tooWeak` med hk-tal). +- `ADPool.queueOrder(fields)` — markers ventende rækkefølge: `waitingSince` stigende, lige → lavest mark-id. +- `ADPool.isReturnDue(rearImplements, poolParkIds)` — traktor har et puljeredskab bag sig (dets parkering har en + Connect-markør) → hjemtur skal køres. + +Spil-adapter: +- `ADPool.collect(adEnv)` → `implements, tractors` fra `g_currentMission.vehicleSystem.vehicles` (gårdens), med + position, rolle, specs til `acceptFor`, hk og parkering/Connect-id. +- hk: `g_storeManager:getItemByXMLFilename(vehicle.configFileName).specs` → traktor `power`, redskab + `neededPower`; mangler de, bruges nil (→ tilladt). +- Reservationer: `ADPool.reserve(implementId, tractorId, owner)` / `release(owner)`; i hukommelsen, ryddes ved + genindlæsning (se Genindlæsning). +- Karantæne: redskab, der ikke kunne kobles på, springes over i 10 min (`ADPool.QUARANTINE_MS = 600000`). + +### `adHitch.lua` (findes, v1.47.4 — udvides let) + +Udfører kørslerne: `fetch` (kør til Connect + kobl på), `route` (trin-tur, afkobling ved redskabets parkering). +Ny: `ADHitch.returnHome(adEnv, tractor, onDone)` = `route({redskabets parkering, traktorens parkering})`; ved +parkeringen kobles puljeredskabet af (samme regel som i dag i ruter). + +### Markarbejdet (`adFieldWork.lua`, `adFieldJobs.lua`) + +- `dispatch`: som i dag først færdige kombinationer. Returnerer `pick`/`pickMany` intet (`noRig`/`allBusy`/ + `cannotDo`), forsøges `ADPool.pairFor` for de manglende. Puljekombinationens assignment får `phase = "fetching"`, + `pool = {implementId, connectId, implementParkId, tractorParkId}`, nøgle = traktorens id. +- `follow`: fase `fetching` → når `ADHitch.fetch` melder ok: bredde tjekkes med redskabet på (`checkWidth`; for + bred → hjemtur + årsag `tooWide`), derefter samme vej som i dag (`startAd` til markens markør / påfyldning, + `phase = "driving"`). Fejl → hjemtur, karantæne, markens `retryAt` som ved `noRig`. +- `finishAssignment`: har traktoren et puljeredskab bag sig (`ADPool.isReturnDue`) → `ADHitch.returnHome` i + stedet for den almindelige parkeringstur; fase `returning`; assignment slettes, når hjemturen er færdig (eller + fejler — så med advarsel). +- Kø: `tick` gennemgår ventende marker i `ADPool.queueOrder`. `waitingSince` sættes, når en mark første gang ikke + fik en maskine, og nulstilles ved udsendelse. `DISPATCH_GAP_MS` (10 s) beholdes. +- Fanen viser faserne "henter X" og "sætter X af" og årsagerne `noPoolTractor`/`tooWeak`. + +### Tømning ved høst + +- Helper-dispatch (`role = "unloader"`, `helper = true`) bruger samme fallback: puljevogn + puljetraktor. +- Efter `fetch`: `startUnloader` som i dag. Assignmentet bliver hos marken, til høststadiet er slut (som i dag); + først da `returnHome`. Ingen frigivelse mellem læs. + +### Marksilo-flowet (`adFieldSilos.lua`, `adFieldSiloPlanner.lua`) + +- `collectVehicles` tilføjer puljekombinationer (puljevogn der kan bære varen + nærmeste puljetraktor) som + kandidater med `pool = {...}`; planneren behandler dem som vogne (position = vognens). +- `withoutHarvestReserve` tæller puljevogne med, så høsten går først. +- Lån med `pool`: `ADHitch.fetch` → AD "Hent og aflever" som i dag (`enableParkAtJobFinished`) → når AD er + inaktiv: `returnHome` → lånet slettes, når traktoren er hjemme. Vagten (120 s uden fremgang) starter efter + koblingen. + +## Genindlæsning + +Reservationer og assignments gemmes ikke (som i dag). Efter indlæsning ser en traktor med puljeredskab ud som en +færdig kombination; når markarbejdet eller marksilo-flowet afslutter et job med den, køres `returnHome`. Reglen +afhænger kun af, hvad der hænger bag traktoren, ikke af gemt tilstand. En traktor, som ingen af vores jobs bruger, +køres aldrig hjem af sig selv (spilleren kan have koblet et puljeredskab på for at køre selv). + +## Fejl + +| Situation | Handling | +|---|---| +| Kobling fejler (`noAttachable`, `stoppedAway`) | Traktor hjem, redskab i karantæne 10 min, marken prøver igen (andet redskab eller venter) | +| Redskab for bredt når koblet på | Hjemtur, årsag `tooWide` i fanen | +| Spilleren sætter sig i traktoren | Assignment afbrydes som i dag; reservationer frigives | +| Ingen traktor stærk nok | Årsag `tooWeak` med hk-tal: "ingen traktor med ≥180 hk ledig til 'Plov 1'" | +| Hjemtur fejler | Advarsel i loggen; traktor og redskab frigives (spilleren må ordne det) | + +## Undtaget / uden for scope + +- Flows (runs) — deres køretøjer er aldrig i puljen (uændret regel i `getRigs`). +- Vogne uden Connect-markør — dedikerede som i dag. +- Genbrug af et redskab direkte til næste mark uden at parkere (senere optimering). +- Flere redskaber pr. traktor (front + bag). +- Egen GUI-side for puljen. + +## Etaper + +1. **v1.48.0.0 — markarbejde med pulje:** `adPool.lua`, `returnHome`, dispatch-fallback, fase fetching/returning, + kø-rækkefølge, fane-tekster. In-game: savegame4, mark der skal harves/pløjes, redskab med parkering + Connect, + solo-traktor henter → arbejder → parkerer → hjem. +2. **v1.49.0.0 — tømmevogn ved høst fra puljen.** +3. **v1.50.0.0 — marksilo med puljevogne** (inkl. høst-reserve over puljevogne). + +## Test + +- `tests/test_adPool.lua`: puljeredskab ja/nej (koblet på, uden parkering, uden Connect), `roleOf` inkl. + `unloader`, `powerOk` (nok/for lidt/manglende data), `pairFor` (redskab nærmest marken, traktor nærmest redskabet, + reserveret og karantæne springes over, `tooWeak`/`noPoolTractor`/`noPoolImplement`), `queueOrder`, + `isReturnDue`. +- `tests/test_adHitch.lua`: `returnHome`-rutens trin. +- `tests/test_adFieldJobs.lua`: færdig kombination foretrækkes; fallback til pulje; kø længst ventende først. +- `tests/test_adFieldSiloPlanner.lua` (etape 3): puljekandidater og høst-reserve med puljevogne.