From 6f46bb543a9eaf97aa58000947e55a19753d8452 Mon Sep 17 00:00:00 2001 From: masterdraco Date: Mon, 28 Sep 2026 18:56:06 +0200 Subject: [PATCH] =?UTF-8?q?feat(pulje):=20etape=203=20=E2=80=94=20marksilo?= =?UTF-8?q?-flowet=20l=C3=A5ner=20ogs=C3=A5=20ledig=20solo-traktor=20+=20p?= =?UTF-8?q?uljevogn:=20henter=20vognen,=20k=C3=B8rer=20Hent=20og=20aflever?= =?UTF-8?q?,=20s=C3=A6tter=20vognen=20af=20og=20k=C3=B8rer=20hjem;=20h?= =?UTF-8?q?=C3=B8streserven=20t=C3=A6ller=20puljevogne=20(v1.50.0.0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FS25_ADSmartPickup/adFieldSilos.lua | 68 +++++++++++++++++++++++++++-- FS25_ADSmartPickup/adPool.lua | 19 ++++++++ FS25_ADSmartPickup/modDesc.xml | 2 +- tests/test_adPool.lua | 8 ++++ 4 files changed, 93 insertions(+), 4 deletions(-) diff --git a/FS25_ADSmartPickup/adFieldSilos.lua b/FS25_ADSmartPickup/adFieldSilos.lua index bf604b0..24d13a0 100644 --- a/FS25_ADSmartPickup/adFieldSilos.lua +++ b/FS25_ADSmartPickup/adFieldSilos.lua @@ -267,6 +267,24 @@ function ADFieldSilos.collectVehicles(adEnv) byId[rig.id] = rig.vehicle 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) if ok and #needs > 0 then local before = #vehicles @@ -277,7 +295,7 @@ function ADFieldSilos.collectVehicles(adEnv) end why.eligible = #vehicles ADFieldSilos.lastVehicleReasons = why - return vehicles, byId + return vehicles, byId, poolById end -- 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)) 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 loan = loans[vehicle] loans[vehicle] = nil @@ -361,6 +406,14 @@ local function release(adEnv, vehicle, reason) pcall(vehicle.stopAutoDrive, vehicle) end 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 function ADFieldSilos.stopAll(adEnv) @@ -378,6 +431,8 @@ local function checkLoans(adEnv) else local ok, err = pcall(function() 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 fill = fillLevel(adEnv, vehicle) local vehicleState = { @@ -429,7 +484,7 @@ end function ADFieldSilos.planNow(adEnv) local cfg = ADFieldSilos.config 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 if cfg.toProduction then 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 vehicle = nil 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) if not ok then loans[vehicle] = nil diff --git a/FS25_ADSmartPickup/adPool.lua b/FS25_ADSmartPickup/adPool.lua index 1499c18..7d8f306 100644 --- a/FS25_ADSmartPickup/adPool.lua +++ b/FS25_ADSmartPickup/adPool.lua @@ -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 "?"} 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 -- 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) diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index f53761c..1c398e3 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.49.0.0 + 1.50.0.0 <en>AD Smart Pickup</en> diff --git a/tests/test_adPool.lua b/tests/test_adPool.lua index e37b1c1..e4d9aaa 100644 --- a/tests/test_adPool.lua +++ b/tests/test_adPool.lua @@ -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: ingen vare krævet", P.canCarry({carries = {}}, nil), true) 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")) os.exit(failures == 0 and 0 or 1)