From 2eec23add719111ffc80784b1e87d38a168e6449 Mon Sep 17 00:00:00 2001 From: masterdraco Date: Mon, 28 Sep 2026 18:12:16 +0200 Subject: [PATCH] =?UTF-8?q?fix(pulje):=20review=20=E2=80=94=20puljeredskab?= =?UTF-8?q?=20s=C3=A6ttes=20altid=20af=20(ogs=C3=A5=20p=C3=A5fyldte=20s?= =?UTF-8?q?=C3=A6t),=20kun=20rollel=C3=B8se=20traktorer=20henter,=20trakto?= =?UTF-8?q?r=20uden=20vejnet=20springes=20over=20og=20f=C3=A5r=20skylden?= =?UTF-8?q?=20ved=20stoppedAway,=20p=C3=A5fyldning=20efter=20koblingen,=20?= =?UTF-8?q?faste=20maskiner=20respekteres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FS25_ADSmartPickup/adFieldWork.lua | 34 ++++++++++++++++++++----- FS25_ADSmartPickup/adPool.lua | 40 +++++++++++++++++++++++++++--- tests/test_adPool.lua | 23 +++++++++++++++++ 3 files changed, 88 insertions(+), 9 deletions(-) diff --git a/FS25_ADSmartPickup/adFieldWork.lua b/FS25_ADSmartPickup/adFieldWork.lua index b46b123..887b96b 100644 --- a/FS25_ADSmartPickup/adFieldWork.lua +++ b/FS25_ADSmartPickup/adFieldWork.lua @@ -461,7 +461,13 @@ function ADFieldWork.dispatch(adEnv, field, info, decision) end if rigs == nil then -- ingen færdig kombination: en ledig solo-traktor henter et puljeredskab (kun hovedopgaver, ikke tømmevogne) - if not decision.helper and (decision.count or 1) == 1 and ADPool ~= nil + -- marken har egne faste maskiner til rollen: den venter på dem (som uden puljen) + local ownIds, hasOwnForRole = {}, false + for _, id in ipairs(field.plan ~= nil and field.plan.rigIds or {}) do ownIds[id] = true end + for _, candidate in ipairs(candidates) do + if ownIds[candidate.id] and (candidate.role == role or (candidate.roles ~= nil and candidate.roles[role])) then hasOwnForRole = true end + end + if ADPool ~= nil and ADPool.mayUsePool(decision, hasOwnForRole) and (why == "noRig" or why == "allBusy" or why == "cannotDo") then local pooled, poolWhy, poolArgs = ADFieldWork.dispatchPool(adEnv, field, info, role, roleAccept) if pooled then return end @@ -913,7 +919,7 @@ local function finishAssignment(adEnv, assignment) -- manuel transport: ALDRIG ud på vejen med AutoDrive (skærebordet sidder på) — spilleren henter den -- puljeredskab bag traktoren: sæt det af på sin parkering, traktoren hjem (afhænger kun af hvad der hænger -- bag, så det virker også efter genindlæsning); ellers som før - local pooled = not assignment.adHandlesEnd and not assignment.manual and ADHitch ~= nil + local pooled = ADPool ~= nil and ADPool.shouldReturnHome(assignment) and ADHitch ~= nil and ADHitch.returnHome(adEnv, vehicle, function(_, ok, detail) if not ok then log(assignment.fieldId, "'%s' kom ikke hjem fra puljen: %s", ADRuns.vehicleLabel(vehicle), tostring(detail)) end end) @@ -1166,8 +1172,9 @@ function ADFieldWork.follow(adEnv, assignment) if not hitch.done then return end ADPool.release(assignment) if not hitch.ok then - -- koblingen fejlede: redskabet i karantæne, traktoren hjem, marken prøver igen - ADPool.quarantine(assignment.pool.implementId, now()) + -- koblingen fejlede: den skyldige i karantæne (kom traktoren ikke frem, er det traktoren), hjem, prøv igen + local blamed = ADPool.failureBlames(hitch.detail) == "tractor" and assignment.vehicleId or assignment.pool.implementId + ADPool.quarantine(blamed, now()) ADFieldWork.release(assignment) note(assignment.fieldId, {retryAt = now() + ADFieldWork.NO_RIG_RETRY_MS, reason = {text = string.format("kunne ikke koble '%s' på (%s) — prøver et andet om lidt", @@ -1185,10 +1192,25 @@ function ADFieldWork.follow(adEnv, assignment) ADHitch.returnHome(adEnv, vehicle) return end + -- som almindelig udsendelse: tom såmaskine/sprøjte fyldes op på vejen, uden påfyldning køres den hjem igen + local refill = refillPlan(adEnv, assignment.fieldId, vehicle, assignment.role, info.x, info.z) + local nextDrive = ADPool.nextDrive(refill) + if nextDrive == "empty" then + ADFieldWork.release(assignment) + ADHitch.returnHome(adEnv, vehicle) + fail(assignment.fieldId, string.format("'%s' er tom for %s og der er ingen påfyldning med AutoDrive-markør — fyld den op", + tostring(assignment.pool.implementName), fillTypeName(refill.fillType))) + return + end assignment.width, assignment.phase, assignment.since = width, "driving", now() assignment.startX, assignment.startZ = vehiclePosition(vehicle) - startAd(adEnv, vehicle, adEnv.AutoDrive.MODE_DRIVETO, info.markerId, nil) - assignment.drive = {mode = adEnv.AutoDrive.MODE_DRIVETO, first = info.markerId} + if nextDrive == "load" then + startAd(adEnv, vehicle, adEnv.AutoDrive.MODE_LOAD, info.markerId, refill.refillId, refill.fillType) + assignment.drive = {mode = adEnv.AutoDrive.MODE_LOAD, first = info.markerId, second = refill.refillId, fillType = refill.fillType} + else + startAd(adEnv, vehicle, adEnv.AutoDrive.MODE_DRIVETO, info.markerId, nil) + assignment.drive = {mode = adEnv.AutoDrive.MODE_DRIVETO, first = info.markerId} + end note(assignment.fieldId, {reason = false}) log(assignment.fieldId, "%s '%s' har '%s' på — kører til %s (%.1f m)", assignment.role, ADRuns.vehicleLabel(vehicle), tostring(assignment.pool.implementName), tostring(info.markerName), width or 0) diff --git a/FS25_ADSmartPickup/adPool.lua b/FS25_ADSmartPickup/adPool.lua index 05e7ac1..42b1045 100644 --- a/FS25_ADSmartPickup/adPool.lua +++ b/FS25_ADSmartPickup/adPool.lua @@ -65,13 +65,43 @@ local function nearest(items, x, z, keep) return best end +-- Når opgaven slutter, sættes puljeredskabet af — også når AutoDrive ellers selv ville parkere (påfyldte +-- sæt): AD parkerer ved redskabets plads med redskabet på. Aldrig ved manuel transport. +function ADPool.shouldReturnHome(assignment) + return not assignment.manual +end + +-- Kun maskiner uden egen arbejdsrolle (almindelige traktorer) henter puljeredskaber — ikke mejetærskere, +-- selvkørende sprøjter o.l., selv om de har koblingspunkter. +function ADPool.canPull(role) + return role == nil +end + +-- Hvem fik koblingen til at fejle? Kom traktoren ikke frem, er det traktoren (ingen AD-rute), ellers redskabet. +function ADPool.failureBlames(reason) + if reason == "stoppedAway" then return "tractor" end + return "implement" +end + +-- Efter koblingen: refill = markarbejdets påfyldningsplan (nil = intet at fylde). +function ADPool.nextDrive(refill) + if refill == nil or not refill.empty then return "drive" end + if refill.refillId ~= nil then return "load" end + return "empty" +end + +-- Puljen bruges kun til hovedopgaver med ét sæt, og ikke når marken har egne faste maskiner til rollen. +function ADPool.mayUsePool(decision, hasOwnForRole) + return not decision.helper and (decision.count or 1) == 1 and not hasOwnForRole +end + -- Redskab med rollen nærmest marken, derefter nærmeste ledige traktor med nok hk til redskabet. -- blocked = {[id] = true} (reserverede og redskaber i karantæne). accept (valgfri): implement -> bool. function ADPool.pairFor(role, fieldX, fieldZ, implements, tractors, blocked, accept) blocked = blocked or {} local free = {} for _, tractor in ipairs(tractors or {}) do - if not blocked[tractor.id] then table.insert(free, tractor) end + if not blocked[tractor.id] and not tractor.offNetwork then table.insert(free, tractor) end end local candidates = {} for _, implement in ipairs(implements or {}) do @@ -214,10 +244,14 @@ function ADPool.collect(adEnv, isBusy) local controlled = vehicle.getIsControlled ~= nil and vehicle:getIsControlled() if id ~= nil and park ~= nil and vehicle.spec_attacherJoints ~= nil and #ADHitch.rearImplementsOf(vehicle) == 0 and ADRunsController.getRunForVehicle(vehicle) == nil and not controlled and not isBusy(vehicle) - and not ADPool.isReturning(vehicle) and ADFieldFleet.isEnabled(state.fleetMode, state.fleet, id) then + and not ADPool.isReturning(vehicle) and ADFieldFleet.isEnabled(state.fleetMode, state.fleet, id) + and ADPool.canPull(ADFieldFleet.roleFromSpecs(ADCpBridge.getSpecs(vehicle))) then local x, z = positionOf(vehicle) + -- uden for AD-vejnettet: den kan ikke køre ud efter redskabet (ville sende redskaber i karantæne) + local networkDistance = ADFieldWork.distanceToNetwork(adEnv, x, z) table.insert(tractors, {id = id, vehicle = vehicle, name = ADRuns.vehicleLabel(vehicle), x = x, z = z, - hp = tractorHpOf(vehicle), parkId = park.id}) + hp = tractorHpOf(vehicle), parkId = park.id, + offNetwork = networkDistance ~= nil and networkDistance > ADFieldWork.MAX_OFF_NETWORK}) end end return implements, tractors diff --git a/tests/test_adPool.lua b/tests/test_adPool.lua index f3ae389..1a7703b 100644 --- a/tests/test_adPool.lua +++ b/tests/test_adPool.lua @@ -79,5 +79,28 @@ P.setReturning(tractorObject, true) check("hjemtur: markeret", P.isReturning(tractorObject), true) P.setReturning(tractorObject, false) check("hjemtur: hjemme", P.isReturning(tractorObject), false) +-- review-rettelser +-- C1: hjemtur også når AD selv ville parkere (påfyldte sæt); aldrig ved manuel transport +check("C1: hjem trods adHandlesEnd", P.shouldReturnHome({adHandlesEnd = true}), true) +check("C1: ikke ved manuel", P.shouldReturnHome({manual = true}), false) +-- I1: kun maskiner uden egen arbejdsrolle kan trække et puljeredskab +check("I1: traktor uden rolle", P.canPull(nil), true) +check("I1: mejetærsker", P.canPull("harvester"), false) +check("I1: selvkørende sprøjte", P.canPull("fertilizer"), false) +-- I2: traktor uden AD-vejnet springes over; fejl uden fremdrift skyldes traktoren +local offNet = P.pairFor("plow", 0, 0, {implements[2]}, {{id = "far", x = 100, z = 0, offNetwork = true}, {id = "ok", x = 400, z = 0, hp = 300}}, {}) +check("I2: traktor uden for vejnettet springes over", offNet.tractor.id, "ok") +check("I2: stoppedAway -> traktoren", P.failureBlames("stoppedAway"), "tractor") +check("I2: noAttachable -> redskabet", P.failureBlames("noAttachable"), "implement") +-- I3: efter koblingen — tom uden påfyldning = fejl, tom med påfyldning = fyld op, ellers kør +check("I3: ingen påfyldningsbehov", P.nextDrive(nil), "drive") +check("I3: fyldt", P.nextDrive({empty = false}), "drive") +check("I3: tom med påfyldning", P.nextDrive({empty = true, refillId = 7}), "load") +check("I3: tom uden påfyldning", P.nextDrive({empty = true}), "empty") +-- I4: puljen kun til hovedopgaver med ét sæt, og ikke når marken har egne faste maskiner til rollen +check("I4: almindelig", P.mayUsePool({count = 1}, false), true) +check("I4: tømmevogn", P.mayUsePool({count = 1, helper = true}, false), false) +check("I4: multi-tool", P.mayUsePool({count = 2}, false), false) +check("I4: marken har faste", P.mayUsePool({count = 1}, true), false) print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)