150 lines
9.2 KiB
Markdown
150 lines
9.2 KiB
Markdown
# 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.
|