fix(pulje): review-minors — mark forsvundet under hentning, hjemtur ved kursus-/CP-fejl og Stop alt, ventende marker på fælles taktslag (kø holder), redskabets navn i 'for svag', hk-cache (v1.48.4.0)

This commit is contained in:
2026-09-28 18:53:34 +02:00
parent 6eff1dcae8
commit 760fbac328
6 changed files with 46 additions and 10 deletions
+1
View File
@@ -392,6 +392,7 @@ local function stepField(adEnv, info, env)
ADFieldJobs.save() ADFieldJobs.save()
elseif decision.action == "rewind" then elseif decision.action == "rewind" then
ADFieldJobs.setField(ADFieldModel.with(field, {stage = "ready", retryAt = 0, reason = false})) ADFieldJobs.setField(ADFieldModel.with(field, {stage = "ready", retryAt = 0, reason = false}))
ADFieldJobs.waitingSince[info.id] = nil
ADFieldWork.windDownHelpers(adEnv, info.id, field.stage) ADFieldWork.windDownHelpers(adEnv, info.id, field.stage)
ADFieldJobs.log(info.id, "%s -> ready: afgrøden står stadig uhøstet (%.0f %% klar)", field.stage, (info.readyRatio or 0) * 100) ADFieldJobs.log(info.id, "%s -> ready: afgrøden står stadig uhøstet (%.0f %% klar)", field.stage, (info.readyRatio or 0) * 100)
ADFieldJobs.save() ADFieldJobs.save()
+17 -6
View File
@@ -474,8 +474,8 @@ function ADFieldWork.dispatch(adEnv, field, info, decision)
local pooled, poolWhy, poolArgs = ADFieldWork.dispatchPool(adEnv, field, info, role, roleAccept) local pooled, poolWhy, poolArgs = ADFieldWork.dispatchPool(adEnv, field, info, role, roleAccept)
if pooled then return end if pooled then return end
if poolWhy == "tooWeak" then if poolWhy == "tooWeak" then
note(field.id, {reason = {text = string.format("ingen ledig traktor med ≥%.0f hk til %s-redskabet", note(field.id, {reason = {text = string.format("ingen ledig traktor med ≥%.0f hk til '%s'",
(poolArgs or {})[1] or 0, tostring(role))}, retryAt = now() + ADFieldWork.NO_RIG_RETRY_MS}) (poolArgs or {})[1] or 0, tostring((poolArgs or {})[2] or role))}, retryAt = ADPool.alignedRetry(now(), ADFieldWork.NO_RIG_RETRY_MS)})
return return
end end
if poolWhy == "noPoolTractor" and why ~= "allBusy" then if poolWhy == "noPoolTractor" and why ~= "allBusy" then
@@ -485,10 +485,10 @@ function ADFieldWork.dispatch(adEnv, field, info, decision)
if #offNetwork > 0 and (why == "cannotDo" or why == "noRig") then if #offNetwork > 0 and (why == "cannotDo" or why == "noRig") then
-- de eneste med rollen står uden for AD-vejnettet -- de eneste med rollen står uden for AD-vejnettet
note(field.id, {reason = {text = string.format("ingen %s kan køre til %s — står for langt fra AutoDrive-vejnettet (%s)", note(field.id, {reason = {text = string.format("ingen %s kan køre til %s — står for langt fra AutoDrive-vejnettet (%s)",
tostring(role), tostring(info.markerName), table.concat(offNetwork, ", "))}, retryAt = now() + ADFieldWork.NO_RIG_RETRY_MS}) tostring(role), tostring(info.markerName), table.concat(offNetwork, ", "))}, retryAt = ADPool.alignedRetry(now(), ADFieldWork.NO_RIG_RETRY_MS)})
return return
end end
note(field.id, {reason = {key = "spu_field_reason_" .. tostring(why), role = role, args = args}, retryAt = now() + ADFieldWork.NO_RIG_RETRY_MS}) note(field.id, {reason = {key = "spu_field_reason_" .. tostring(why), role = role, args = args}, retryAt = ADPool.alignedRetry(now(), ADFieldWork.NO_RIG_RETRY_MS)})
-- også i loggen (én gang pr. ændring): før stod grunden kun i fanen, og loggen var tavs (in-game 27/9) -- også i loggen (én gang pr. ændring): før stod grunden kun i fanen, og loggen var tavs (in-game 27/9)
local noRigKey = tostring(role) .. ":" .. tostring(why) local noRigKey = tostring(role) .. ":" .. tostring(why)
if lastNoRig[field.id] ~= noRigKey then if lastNoRig[field.id] ~= noRigKey then
@@ -1175,12 +1175,18 @@ function ADFieldWork.follow(adEnv, assignment)
local hitch = assignment.hitch local hitch = assignment.hitch
if not hitch.done then return end if not hitch.done then return end
ADPool.release(assignment) ADPool.release(assignment)
if info == nil or field == nil then
-- marken findes ikke længere (fx solgt): redskabet tilbage, traktoren hjem
ADFieldWork.release(assignment)
if hitch.ok then ADHitch.returnHome(adEnv, vehicle) end
return
end
if not hitch.ok then if not hitch.ok then
-- koblingen fejlede: den skyldige i karantæne (kom traktoren ikke frem, er det traktoren), hjem, prøv igen -- 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 local blamed = ADPool.failureBlames(hitch.detail) == "tractor" and assignment.vehicleId or assignment.pool.implementId
ADPool.quarantine(blamed, now()) ADPool.quarantine(blamed, now())
ADFieldWork.release(assignment) ADFieldWork.release(assignment)
note(assignment.fieldId, {retryAt = now() + ADFieldWork.NO_RIG_RETRY_MS, note(assignment.fieldId, {retryAt = ADPool.alignedRetry(now(), ADFieldWork.NO_RIG_RETRY_MS),
reason = {text = string.format("kunne ikke koble '%s' på (%s) — prøver et andet om lidt", reason = {text = string.format("kunne ikke koble '%s' på (%s) — prøver et andet om lidt",
tostring(assignment.pool.implementName), tostring(hitch.detail))}}) tostring(assignment.pool.implementName), tostring(hitch.detail))}})
local park = parkMarkerId(adEnv, vehicle) local park = parkMarkerId(adEnv, vehicle)
@@ -1251,6 +1257,7 @@ function ADFieldWork.follow(adEnv, assignment)
elseif assignment.phase == "waitingCourse" then elseif assignment.phase == "waitingCourse" then
if now() - assignment.since > ADFieldWork.COURSE_WAIT_MS then if now() - assignment.since > ADFieldWork.COURSE_WAIT_MS then
ADFieldWork.release(assignment) ADFieldWork.release(assignment)
ADHitch.returnHome(adEnv, vehicle)
fail(assignment.fieldId, "multi-tool: lederens kursus kom ikke (3 min)") fail(assignment.fieldId, "multi-tool: lederens kursus kom ikke (3 min)")
elseif coursesOf(field.id)[field.stage] ~= nil then elseif coursesOf(field.id)[field.stage] ~= nil then
beginWork(adEnv, field, info, assignment) beginWork(adEnv, field, info, assignment)
@@ -1259,6 +1266,7 @@ function ADFieldWork.follow(adEnv, assignment)
local generating = assignment.handle ~= nil and assignment.handle.generating == true local generating = assignment.handle ~= nil and assignment.handle.generating == true
if ADFieldJobs.startTimedOut(now() - assignment.since, generating) then if ADFieldJobs.startTimedOut(now() - assignment.since, generating) then
ADFieldWork.release(assignment) ADFieldWork.release(assignment)
ADHitch.returnHome(adEnv, vehicle)
fail(assignment.fieldId, generating and "Courseplay blev ikke færdig med kurset (5 min)" or "Courseplay svarede ikke (60 s)") fail(assignment.fieldId, generating and "Courseplay blev ikke færdig med kurset (5 min)" or "Courseplay svarede ikke (60 s)")
end end
elseif assignment.phase == "working" then elseif assignment.phase == "working" then
@@ -1290,8 +1298,10 @@ function ADFieldWork.stopAllAssignments(adEnv)
for _, assignment in ipairs(list) do for _, assignment in ipairs(list) do
pcall(stopAd, assignment.vehicle) pcall(stopAd, assignment.vehicle)
pcall(ADCpBridge.stop, assignment.vehicle) pcall(ADCpBridge.stop, assignment.vehicle)
table.insert(stopped, {vehicle = assignment.vehicle, manual = assignment.manual == true})
ADFieldWork.release(assignment) ADFieldWork.release(assignment)
-- puljeredskab: sættes af på sin plads og traktoren kører hjem (i stedet for almindelig parkering)
local okPool, pooled = pcall(ADHitch.returnHome, adEnv, assignment.vehicle)
table.insert(stopped, {vehicle = assignment.vehicle, manual = assignment.manual == true, pooled = okPool and pooled == true})
end end
return stopped return stopped
end end
@@ -1301,5 +1311,6 @@ function ADFieldWork.stopAll(adEnv, fieldId)
pcall(stopAd, assignment.vehicle) pcall(stopAd, assignment.vehicle)
ADCpBridge.stop(assignment.vehicle) ADCpBridge.stop(assignment.vehicle)
ADFieldWork.release(assignment) ADFieldWork.release(assignment)
pcall(ADHitch.returnHome, adEnv, assignment.vehicle)
end end
end end
+18 -2
View File
@@ -141,9 +141,15 @@ function ADPool.pairFor(role, fieldX, fieldZ, implements, tractors, blocked, acc
for _, implement in ipairs(candidates) do for _, implement in ipairs(candidates) do
local tractor = nearest(free, implement.x, implement.z, function(t) return ADPool.powerOk(t.hp, implement.neededHp) end) local tractor = nearest(free, implement.x, implement.z, function(t) return ADPool.powerOk(t.hp, implement.neededHp) end)
if tractor ~= nil then return {implement = implement, tractor = tractor} end if tractor ~= nil then return {implement = implement, tractor = tractor} end
if weakest == nil or (implement.neededHp or 0) < weakest then weakest = implement.neededHp end if weakest == nil or (implement.neededHp or 0) < (weakest.neededHp or 0) then weakest = implement end
end end
return nil, "tooWeak", {weakest or 0} return nil, "tooWeak", {weakest ~= nil and weakest.neededHp or 0, weakest ~= nil and weakest.name or "?"}
end
-- Ventende marker prøver igen på fælles taktslag (ikke hver sin 30 s fra eget forsøg): så udløber de i samme
-- tick, og køens rækkefølge (længst ventende først) afgør hvem der får den næste ledige traktor.
function ADPool.alignedRetry(nowMs, periodMs)
return (math.floor(nowMs / periodMs) + 1) * periodMs
end end
-- Ventende marker først (længst ventende først, lige -> lavest id); resten bagefter i oprindelig rækkefølge. -- Ventende marker først (længst ventende først, lige -> lavest id); resten bagefter i oprindelig rækkefølge.
@@ -225,8 +231,18 @@ local function positionOf(vehicle)
end end
-- Butiksdata: redskabets krævede effekt (hk). specs indlæses først, når butikken har vist varen. -- Butiksdata: redskabets krævede effekt (hk). specs indlæses først, når butikken har vist varen.
local neededHpCache = {} -- configFileName -> hk eller false (butiksdata ændrer sig ikke i et spil)
local function neededHpOf(vehicle) local function neededHpOf(vehicle)
if g_storeManager == nil or vehicle.configFileName == nil then return nil end if g_storeManager == nil or vehicle.configFileName == nil then return nil end
local cached = neededHpCache[vehicle.configFileName]
if cached ~= nil then return cached or nil end
local value = ADPool.readNeededHp(vehicle)
neededHpCache[vehicle.configFileName] = value or false
return value
end
function ADPool.readNeededHp(vehicle)
local item = g_storeManager:getItemByXMLFilename(vehicle.configFileName) local item = g_storeManager:getItemByXMLFilename(vehicle.configFileName)
if item == nil then return nil end if item == nil then return nil end
if item.specs == nil and StoreItemUtil ~= nil and StoreItemUtil.loadSpecsFromXML ~= nil then if item.specs == nil and StoreItemUtil ~= nil and StoreItemUtil.loadSpecsFromXML ~= nil then
+3 -1
View File
@@ -1179,7 +1179,9 @@ function ADRunsController.stopAllAndPark(adEnv)
if ADFieldWork ~= nil and ADFieldWork.stopAllAssignments ~= nil then if ADFieldWork ~= nil and ADFieldWork.stopAllAssignments ~= nil then
for _, stopped in ipairs(ADFieldWork.stopAllAssignments(adEnv) or {}) do for _, stopped in ipairs(ADFieldWork.stopAllAssignments(adEnv) or {}) do
summary.stopped = summary.stopped + 1 summary.stopped = summary.stopped + 1
if stopped.manual then if stopped.pooled then
summary.parked = summary.parked + 1
elseif stopped.manual then
table.insert(summary.manual, tostring(ADRuns.vehicleLabel(stopped.vehicle))) table.insert(summary.manual, tostring(ADRuns.vehicleLabel(stopped.vehicle)))
elseif not seen[stopped.vehicle] then elseif not seen[stopped.vehicle] then
seen[stopped.vehicle] = true seen[stopped.vehicle] = true
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?> <?xml version="1.0" encoding="utf-8" standalone="no"?>
<modDesc descVersion="107"> <modDesc descVersion="107">
<author>masterdraco</author> <author>masterdraco</author>
<version>1.48.3.0</version> <version>1.48.4.0</version>
<title> <title>
<en>AD Smart Pickup</en> <en>AD Smart Pickup</en>
</title> </title>
+6
View File
@@ -130,5 +130,11 @@ P.reserve("owner", {"r1"})
check("reserveret: ja", P.isReserved("r1"), true) check("reserveret: ja", P.isReserved("r1"), true)
P.release("owner") P.release("owner")
check("reserveret: nej", P.isReserved("r1"), false) check("reserveret: nej", P.isReserved("r1"), false)
-- M3: ventende marker prøver igen i samme takt (så køens rækkefølge holder)
check("M3: næste takt", P.alignedRetry(61000, 30000), 90000)
check("M3: på grænsen -> næste", P.alignedRetry(60000, 30000), 90000)
check("M3: to marker samme takt", P.alignedRetry(62000, 30000) == P.alignedRetry(89000, 30000), true)
-- M4: for svag nævner redskabets navn
check("M4: navn i args", weakArgs[2], "Plov nær")
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
os.exit(failures == 0 and 0 or 1) os.exit(failures == 0 and 0 or 1)