feat(pulje): etape 3 — marksilo-flowet låner også ledig solo-traktor + puljevogn: henter vognen, kører Hent og aflever, sætter vognen af og kører hjem; høstreserven tæller puljevogne (v1.50.0.0)

This commit is contained in:
2026-09-28 18:56:06 +02:00
parent 473f6110bf
commit 6f46bb543a
4 changed files with 93 additions and 4 deletions
+65 -3
View File
@@ -267,6 +267,24 @@ function ADFieldSilos.collectVehicles(adEnv)
byId[rig.id] = rig.vehicle byId[rig.id] = rig.vehicle
end end
end end
-- puljevogne: en ledig solo-traktor henter vognen først (maskinpulje etape 3)
local poolById = {}
if ADPool ~= nil then
local okPool, implements, tractors = pcall(ADPool.collect, adEnv, function(v)
return ADFieldWork.isBusyVehicle(v, ADRunsController.getVehicleId(v))
end)
if okPool then
for _, pair in ipairs(ADPool.pairAll("unloader", implements, tractors, ADPool.blocked(ADFieldJobs.now or 0))) do
local entryId = "pool:" .. tostring(pair.implement.id)
table.insert(vehicles, {id = entryId, x = pair.implement.x, z = pair.implement.z,
capacity = pair.implement.capacity or 0, carries = pair.implement.carries or {}})
byId[entryId] = pair.tractor.vehicle
poolById[entryId] = pair
end
else
Logging.warning(ADFieldSilos.LOG_PREFIX .. "maskinpuljen kunne ikke læses: %s", tostring(implements))
end
end
local ok, needs = pcall(ADFieldSilos.harvestNeeds, adEnv, rigs) local ok, needs = pcall(ADFieldSilos.harvestNeeds, adEnv, rigs)
if ok and #needs > 0 then if ok and #needs > 0 then
local before = #vehicles local before = #vehicles
@@ -277,7 +295,7 @@ function ADFieldSilos.collectVehicles(adEnv)
end end
why.eligible = #vehicles why.eligible = #vehicles
ADFieldSilos.lastVehicleReasons = why ADFieldSilos.lastVehicleReasons = why
return vehicles, byId return vehicles, byId, poolById
end end
-- Høsten først: pr. høster der arbejder (CP kører) holdes dens planlagte antal tømmevogne tilbage, minus dem -- Høsten først: pr. høster der arbejder (CP kører) holdes dens planlagte antal tømmevogne tilbage, minus dem
@@ -346,6 +364,33 @@ local function dispatchOne(adEnv, order, vehicle, silosById, storesById)
tostring(store ~= nil and store.name or order.storeId)) tostring(store ~= nil and store.name or order.storeId))
end end
-- Puljevogn: traktoren henter vognen ved "Connect X" og kører derefter turen som en almindelig tømmevogn.
local function dispatchPool(adEnv, order, pair, silosById, storesById)
local tractor = pair.tractor.vehicle
local silo = silosById[order.siloId]
local owner = {siloId = order.siloId, storeId = order.storeId, siloName = silo ~= nil and silo.name or order.siloId,
fillType = order.fillType, liters = order.liters, startedMs = nowMs, lastProgressMs = nowMs, lastFill = 0, fetching = true}
loans[tractor] = owner
ADPool.reserve(owner, {pair.implement.id, pair.tractor.id})
ADHitch.fetch(adEnv, tractor, pair.implement.connectId, function(_, ok, detail)
ADPool.release(owner)
if loans[tractor] ~= owner then return end -- lånet blev afbrudt imens (Stop alt / flow slået fra)
if not ok then
loans[tractor] = nil
local blamed = ADPool.failureBlames(detail) == "tractor" and pair.tractor.id or pair.implement.id
ADPool.quarantine(blamed, ADFieldJobs.now or 0)
Logging.warning(ADFieldSilos.LOG_PREFIX .. "'%s' kunne ikke hente puljevognen '%s' (%s) — prøver en anden om lidt",
tostring(ADRuns.vehicleLabel(tractor)), tostring(pair.implement.name), tostring(detail))
local park = ADFieldWork.parkMarkerId(adEnv, tractor)
if park ~= nil then ADFieldWork.startAd(adEnv, tractor, adEnv.AutoDrive.MODE_DRIVETO, park, nil) end
return
end
dispatchOne(adEnv, order, tractor, silosById, storesById)
end)
Logging.info(ADFieldSilos.LOG_PREFIX .. "'%s' henter puljevognen '%s' til %d l %s i %s", tostring(ADRuns.vehicleLabel(tractor)),
tostring(pair.implement.name), math.floor(order.liters), fillTypeName(order.fillType), tostring(silo ~= nil and silo.name or order.siloId))
end
local function release(adEnv, vehicle, reason) local function release(adEnv, vehicle, reason)
local loan = loans[vehicle] local loan = loans[vehicle]
loans[vehicle] = nil loans[vehicle] = nil
@@ -361,6 +406,14 @@ local function release(adEnv, vehicle, reason)
pcall(vehicle.stopAutoDrive, vehicle) pcall(vehicle.stopAutoDrive, vehicle)
end end
Logging.info(ADFieldSilos.LOG_PREFIX .. "'%s' frigivet (%s)", tostring(ADRuns.vehicleLabel(vehicle)), tostring(reason)) Logging.info(ADFieldSilos.LOG_PREFIX .. "'%s' frigivet (%s)", tostring(ADRuns.vehicleLabel(vehicle)), tostring(reason))
-- puljevogn bag traktoren: sæt den af på sin plads og kør hjem (ikke når spilleren har overtaget)
if reason ~= "player" and ADHitch ~= nil then
local okHome, pooled = pcall(ADHitch.returnHome, adEnv, vehicle)
if not (okHome and pooled) and loan.fetching then
local park = ADFieldWork.parkMarkerId(adEnv, vehicle)
if park ~= nil then ADFieldWork.startAd(adEnv, vehicle, adEnv.AutoDrive.MODE_DRIVETO, park, nil) end
end
end
end end
function ADFieldSilos.stopAll(adEnv) function ADFieldSilos.stopAll(adEnv)
@@ -378,6 +431,8 @@ local function checkLoans(adEnv)
else else
local ok, err = pcall(function() local ok, err = pcall(function()
local loan = loans[vehicle] local loan = loans[vehicle]
-- henter puljevognen: AD holder stille under koblingen — ikke "færdig"
if loan.fetching then return end
local stateModule = vehicle.ad ~= nil and vehicle.ad.stateModule or nil local stateModule = vehicle.ad ~= nil and vehicle.ad.stateModule or nil
local fill = fillLevel(adEnv, vehicle) local fill = fillLevel(adEnv, vehicle)
local vehicleState = { local vehicleState = {
@@ -429,7 +484,7 @@ end
function ADFieldSilos.planNow(adEnv) function ADFieldSilos.planNow(adEnv)
local cfg = ADFieldSilos.config local cfg = ADFieldSilos.config
local silos, stores, byId = ADFieldSilos.collectBuildings(adEnv) local silos, stores, byId = ADFieldSilos.collectBuildings(adEnv)
local vehicles, vehiclesById = ADFieldSilos.collectVehicles(adEnv) local vehicles, vehiclesById, poolById = ADFieldSilos.collectVehicles(adEnv)
local feedReserve, farmStock = nil, nil local feedReserve, farmStock = nil, nil
if cfg.toProduction then if cfg.toProduction then
local ok, reserve, stock = pcall(ADFieldSilos.collectFeed, stores, byId, farmId(), cfg.reserveDays) local ok, reserve, stock = pcall(ADFieldSilos.collectFeed, stores, byId, farmId(), cfg.reserveDays)
@@ -451,7 +506,14 @@ function ADFieldSilos.planNow(adEnv)
if vehicle ~= nil and not hasRoutes(adEnv, vehicle, order) then if vehicle ~= nil and not hasRoutes(adEnv, vehicle, order) then
vehicle = nil vehicle = nil
end end
if vehicle ~= nil then local pair = poolById ~= nil and poolById[order.vehicleId] or nil
if vehicle ~= nil and pair ~= nil then
local ok, err = pcall(dispatchPool, adEnv, order, pair, silosById, storesById)
if not ok then
loans[vehicle] = nil
Logging.warning(ADFieldSilos.LOG_PREFIX .. "kunne ikke sende '%s' efter puljevogn: %s", tostring(ADRuns.vehicleLabel(vehicle)), tostring(err))
end
elseif vehicle ~= nil then
local ok, err = pcall(dispatchOne, adEnv, order, vehicle, silosById, storesById) local ok, err = pcall(dispatchOne, adEnv, order, vehicle, silosById, storesById)
if not ok then if not ok then
loans[vehicle] = nil loans[vehicle] = nil
+19
View File
@@ -153,6 +153,25 @@ function ADPool.pairFor(role, fieldX, fieldZ, implements, tractors, blocked, acc
return nil, "tooWeak", {weakest ~= nil and weakest.neededHp or 0, weakest ~= nil and weakest.name or "?"} return nil, "tooWeak", {weakest ~= nil and weakest.neededHp or 0, weakest ~= nil and weakest.name or "?"}
end end
-- Alle frie puljeredskaber med rollen, hver parret med den nærmeste endnu ubrugte traktor med nok hk
-- (marksilo-flowet planlægger over mange vogne på én gang).
function ADPool.pairAll(role, implements, tractors, blocked)
blocked = blocked or {}
local used, result = {}, {}
for _, implement in ipairs(implements or {}) do
if hasRole(implement, role) and not blocked[implement.id] then
local tractor = nearest(tractors or {}, implement.x, implement.z, function(t)
return not used[t.id] and not blocked[t.id] and not t.offNetwork and ADPool.powerOk(t.hp, implement.neededHp)
end)
if tractor ~= nil then
used[tractor.id] = true
table.insert(result, {implement = implement, tractor = tractor})
end
end
end
return result
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 -- 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. -- 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) function ADPool.alignedRetry(nowMs, periodMs)
+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.49.0.0</version> <version>1.50.0.0</version>
<title> <title>
<en>AD Smart Pickup</en> <en>AD Smart Pickup</en>
</title> </title>
+8
View File
@@ -141,5 +141,13 @@ check("bære: vare understøttet", P.canCarry({carries = {[3] = true}}, 3), true
check("bære: vare ikke understøttet", P.canCarry({carries = {[3] = true}}, 7), false) check("bære: vare ikke understøttet", P.canCarry({carries = {[3] = true}}, 7), false)
check("bære: ingen vare krævet", P.canCarry({carries = {}}, nil), true) check("bære: ingen vare krævet", P.canCarry({carries = {}}, nil), true)
check("bære: ukendt last", P.canCarry({}, 3), false) check("bære: ukendt last", P.canCarry({}, 3), false)
-- etape 3: alle puljevogne parret med hver sin nærmeste traktor (en traktor bruges kun én gang)
local wagons = {{id = "v1", role = "unloader", x = 0, z = 0}, {id = "v2", role = "unloader", x = 100, z = 0},
{id = "v3", role = "unloader", x = 200, z = 0}, {id = "plovX", role = "plow", x = 0, z = 0}}
local pairs3 = P.pairAll("unloader", wagons, {{id = "tA", x = 10, z = 0}, {id = "tB", x = 90, z = 0}}, {v2 = true})
check("par-alle: antal (v2 reserveret, 2 traktorer)", #pairs3, 2)
check("par-alle: v1 -> tA", pairs3[1].implement.id .. ">" .. pairs3[1].tractor.id, "v1>tA")
check("par-alle: v3 -> tB (tA er brugt)", pairs3[2].implement.id .. ">" .. pairs3[2].tractor.id, "v3>tB")
check("par-alle: ingen traktorer", #P.pairAll("unloader", wagons, {}, {}), 0)
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)