# 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.