From b57940a9333d8b3b5db66c4850c7aabbca246cd6 Mon Sep 17 00:00:00 2001 From: masterdraco Date: Mon, 28 Sep 2026 17:15:49 +0200 Subject: [PATCH] =?UTF-8?q?feat(kobling):=20spuTrip=20k=C3=B8rer=20en=20tu?= =?UTF-8?q?r=20i=20flere=20trin=20=E2=80=94=20Connect=20kobler=20p=C3=A5,?= =?UTF-8?q?=20Afkobl=20kobler=20af,=20almindelige=20mark=C3=B8rer=20holder?= =?UTF-8?q?=2010=20s=20undervejs=20(v1.47.1.0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FS25_ADSmartPickup/adHitch.lua | 111 +++++++++++++++++++++++---------- FS25_ADSmartPickup/modDesc.xml | 2 +- tests/test_adHitch.lua | 21 +++++-- 3 files changed, 97 insertions(+), 37 deletions(-) diff --git a/FS25_ADSmartPickup/adHitch.lua b/FS25_ADSmartPickup/adHitch.lua index 571fdc5..ae1dcaa 100644 --- a/FS25_ADSmartPickup/adHitch.lua +++ b/FS25_ADSmartPickup/adHitch.lua @@ -19,12 +19,13 @@ ADHitch.MARKER_PREFIXES = { {prefix = "connect", action = "attach"}, {prefix = "kobl", action = "attach"}, } -ADHitch.TIMEOUT_REASON = {attach = "noAttachable", detach = "nothingToDetach"} +ADHitch.TIMEOUT_REASON = {attach = "noAttachable", detach = "nothingToDetach", arrive = "notArrived"} ADHitch.ARRIVE_DISTANCE = 5 -- m fra markøren, AD standser traktoren tæt på waypointet ADHitch.STILL_SPEED = 1 -- km/t ADHitch.START_GRACE_MS = 3000 -- så længe må AD være om at komme i gang efter fetch/drop ADHitch.ACT_TIMEOUT_MS = 8000 -- så længe forsøges kobling/afkobling efter ankomst ADHitch.TICK_MS = 250 +ADHitch.ROUTE_WAIT_MS = 10000 -- pause ved almindelige markører midt i en tur -- ---- ren logik ---- @@ -52,12 +53,25 @@ function ADHitch.rearImplements(implements) return rear end --- konsol-argumenter "Connect Bredal > Start" -> "Connect Bredal", "Start" (mål nil uden ">") +-- konsol-argumenter "Connect Bredal > Start > Afkobl Bredal > Start" -> {"Connect Bredal", "Start", ...} function ADHitch.parseTrip(...) - local text = table.concat({...}, " ") - local first, second = text:match("^%s*(.-)%s*>%s*(.-)%s*$") - if first == nil then return (text:match("^%s*(.-)%s*$")), nil end - return first, second + local steps = {} + for part in (table.concat({...}, " ") .. ">"):gmatch("([^>]*)>") do + local name = part:match("^%s*(.-)%s*$") + if name ~= "" then table.insert(steps, name) end + end + return steps +end + +-- markørnavn -> attach / detach / arrive (almindelig markør: bare køre dertil) +function ADHitch.stepAction(name) + return ADHitch.parseMarker(name) or "arrive" +end + +-- pause efter et trin: ved almindelige markører undervejs holdes der, ikke efter sidste trin +function ADHitch.waitAfter(action, index, count) + if action == "arrive" and index < count then return ADHitch.ROUTE_WAIT_MS end + return 0 end -- markers = {{name, id}} -> markøren med navnet (store/små bogstaver ligegyldigt) @@ -203,19 +217,47 @@ function ADHitch.drop(adEnv, vehicle, markerId, onDone) return start(adEnv, vehicle, markerId, "detach", onDone) end --- "Hent vogn og kør til": kør til Connect-markøren, kobl på, kør så med AD til målet. -function ADHitch.trip(adEnv, vehicle, connectId, destinationId, onDone) - return ADHitch.fetch(adEnv, vehicle, connectId, function(tractor, ok, detail) - if not ok then - if onDone ~= nil then onDone(tractor, false, detail) end +-- Tur: markørerne køres i rækkefølge. Connect = kobl på, Afkobl = kobl af, andre = kør dertil (og hold +-- ROUTE_WAIT_MS, hvis det ikke er sidste trin). Stopper ved første fejl. onDone(vehicle, ok, årsag) til sidst. +local waits = {} -- {vehicle, leftMs, continue} + +function ADHitch.route(adEnv, vehicle, markerIds, onDone) + local function runStep(index) + if index > #markerIds then + Logging.info(ADHitch.LOG_PREFIX .. "'%s' er færdig med turen (%d trin)", vehicleName(vehicle), #markerIds) + if onDone ~= nil then pcall(onDone, vehicle, true) end return end - local destination = adEnv.ADGraphManager:getMapMarkerByWayPointId(destinationId) - ADFieldWork.startAd(adEnv, tractor, adEnv.AutoDrive.MODE_DRIVETO, destinationId) - Logging.info(ADHitch.LOG_PREFIX .. "'%s' kører med '%s' til '%s'", vehicleName(tractor), vehicleName(detail), - destination ~= nil and destination.name or tostring(destinationId)) - if onDone ~= nil then onDone(tractor, true, detail) end - end) + local marker = adEnv.ADGraphManager:getMapMarkerByWayPointId(markerIds[index]) + local action = ADHitch.stepAction(marker ~= nil and marker.name or nil) + start(adEnv, vehicle, markerIds[index], action, function(_, ok, detail) + if not ok then + Logging.warning(ADHitch.LOG_PREFIX .. "'%s' stopper turen ved trin %d", vehicleName(vehicle), index) + if onDone ~= nil then pcall(onDone, vehicle, false, detail) end + return + end + local waitMs = ADHitch.waitAfter(action, index, #markerIds) + if waitMs > 0 then + Logging.info(ADHitch.LOG_PREFIX .. "'%s' holder %d s ved '%s'", vehicleName(vehicle), waitMs / 1000, + marker ~= nil and marker.name or "?") + table.insert(waits, {vehicle = vehicle, leftMs = waitMs, continue = function() runStep(index + 1) end}) + else + runStep(index + 1) + end + end) + end + runStep(1) +end + +local function tickWaits(elapsed) + for i = #waits, 1, -1 do + local wait = waits[i] + wait.leftMs = wait.leftMs - elapsed + if wait.leftMs <= 0 then + table.remove(waits, i) + wait.continue() + end + end end -- Spilleren sender selv en traktor til en Connect-/Afkobl-markør med AD "Kør til": den kobles også. @@ -231,8 +273,8 @@ local function watchManualTrips(adEnv) end end -local DONE_TEXT = {attach = "koblede '%s' på", detach = "koblede '%s' af"} -local FAIL_TEXT = {attach = "kunne ikke koble på", detach = "kunne ikke koble af"} +local DONE_TEXT = {attach = "koblede '%s' på", detach = "koblede '%s' af", arrive = "er fremme%s"} +local FAIL_TEXT = {attach = "kunne ikke koble på", detach = "kunne ikke koble af", arrive = "nåede ikke frem"} local function finish(vehicle, job, ok, detail) jobs[vehicle] = nil @@ -246,7 +288,7 @@ local function finish(vehicle, job, ok, detail) if job.onDone ~= nil then pcall(job.onDone, vehicle, ok, detail) end end -local ACTIONS = {attach = ADHitch.tryAttach, detach = ADHitch.tryDetach} +local ACTIONS = {attach = ADHitch.tryAttach, detach = ADHitch.tryDetach, arrive = function() return true, "" end} function ADHitch.tick(adEnv, dt) if adEnv == nil or adEnv.ADGraphManager == nil then return end @@ -254,8 +296,12 @@ function ADHitch.tick(adEnv, dt) if tickTimer < ADHitch.TICK_MS then return end local elapsed = tickTimer tickTimer = 0 + tickWaits(elapsed) watchManualTrips(adEnv) - for vehicle, job in pairs(jobs) do + -- kopi: et færdigt trin starter næste trin (nyt job på samme maskine) midt i løkken + local current = {} + for vehicle, job in pairs(jobs) do current[vehicle] = job end + for vehicle, job in pairs(current) do local succeeded, detail = false, nil if job.state == "acting" then succeeded, detail = ACTIONS[job.action](vehicle) @@ -291,26 +337,27 @@ function ADHitch:consoleDetach() return "spuDetach: intet koblet af (" .. tostring(detail) .. ")" end --- "spuTrip Connect Bredal > Start": maskinen spilleren sidder i henter vognen og kører til målet (spilleren --- kan blive siddende; AD kører også med spilleren i førerhuset). +-- "spuTrip Connect Bredal > Start > Afkobl Bredal > Start": maskinen spilleren sidder i kører turen +-- (spilleren kan blive siddende; AD kører også med spilleren i førerhuset). function ADHitch:consoleTrip(...) local vehicle = currentVehicle() if vehicle == nil then return "spuTrip: sæt dig i en traktor først" end local adEnv = ADFieldSilos ~= nil and ADFieldSilos.adEnv or nil if adEnv == nil then return "spuTrip: AutoDrive er ikke klar endnu" end - local connectName, destinationName = ADHitch.parseTrip(...) - local markers = adEnv.ADGraphManager:getMapMarkers() - local connect, destination = ADHitch.findMarker(markers, connectName), ADHitch.findMarker(markers, destinationName) - if connect == nil or ADHitch.parseMarker(connect.name) ~= "attach" then - return "spuTrip: ukendt Connect-markør '" .. tostring(connectName) .. "'" + local names = ADHitch.parseTrip(...) + if #names == 0 then return "spuTrip: brug fx spuTrip Connect Bredal > Start > Afkobl Bredal > Start" end + local markers, ids = adEnv.ADGraphManager:getMapMarkers(), {} + for _, name in ipairs(names) do + local marker = ADHitch.findMarker(markers, name) + if marker == nil then return "spuTrip: ukendt markør '" .. name .. "'" end + table.insert(ids, marker.id) end - if destination == nil then return "spuTrip: ukendt mål '" .. tostring(destinationName) .. "' (brug: spuTrip Connect Bredal > Start)" end - ADHitch.trip(adEnv, vehicle, connect.id, destination.id) - return "spuTrip: " .. vehicleName(vehicle) .. " henter ved '" .. connect.name .. "' og kører til '" .. destination.name .. "'" + ADHitch.route(adEnv, vehicle, ids) + return "spuTrip: " .. vehicleName(vehicle) .. " kører " .. table.concat(names, " > ") end if addConsoleCommand ~= nil then addConsoleCommand("spuConnect", "Kobler nærmeste redskab på den maskine du sidder i", "consoleConnect", ADHitch) - addConsoleCommand("spuTrip", "Hent vogn og kør: spuTrip Connect Bredal > Start", "consoleTrip", ADHitch) + addConsoleCommand("spuTrip", "Kør en tur: spuTrip Connect Bredal > Start > Afkobl Bredal > Start", "consoleTrip", ADHitch) addConsoleCommand("spuDetach", "Kobler redskaberne bag den maskine du sidder i af", "consoleDetach", ADHitch) end diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index 77df79f..250261e 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.47.0.0 + 1.47.1.0 <en>AD Smart Pickup</en> diff --git a/tests/test_adHitch.lua b/tests/test_adHitch.lua index 8e15f51..fada58b 100644 --- a/tests/test_adHitch.lua +++ b/tests/test_adHitch.lua @@ -57,10 +57,23 @@ H.step(lostJob, {adActive = false, distance = 40, speed = 0}, 500, false) check("job: AD stoppede langt fra markøren", lostJob.state, "failed") check("job: årsag stoppet undervejs", lostJob.reason, "stoppedAway") -- tur: "Connect Bredal > Start" -> to markørnavne -local a, b = H.parseTrip("Connect", "Bredal", ">", "Start") -check("tur: hent", a, "Connect Bredal") -check("tur: mål", b, "Start") -check("tur: uden mål", select(2, H.parseTrip("Connect", "Bredal")), nil) +local route = H.parseTrip("Connect", "Bredal", ">", "Start", ">", "Afkobl", "Bredal", ">", "Start") +check("tur: antal trin", #route, 4) +check("tur: første", route[1], "Connect Bredal") +check("tur: tredje", route[3], "Afkobl Bredal") +check("tur: ét trin", #H.parseTrip("Start"), 1) +check("tur: tomme trin droppes", #H.parseTrip("Start", ">", ">", "Stop"), 2) +-- trin: handling pr. markør; almindelige markører = ankom (og vent, hvis ikke sidste) +check("trin: connect", H.stepAction("Connect Bredal"), "attach") +check("trin: afkobl", H.stepAction("Afkobl Bredal"), "detach") +check("trin: almindelig", H.stepAction("Start"), "arrive") +check("vent: mellem trin", H.waitAfter("arrive", 2, 4), H.ROUTE_WAIT_MS) +check("vent: ikke efter sidste", H.waitAfter("arrive", 4, 4), 0) +check("vent: ikke efter kobling", H.waitAfter("attach", 1, 4), 0) +local arriveJob = H.newJob(1, "Start", "arrive") +H.step(arriveJob, {adActive = false, distance = 1, speed = 0}, 250, false) +H.step(arriveJob, {adActive = false, distance = 1, speed = 0}, 250, true) +check("trin: ankom er færdig", arriveJob.state, "done") local markers = {{name = "Start", id = 1}, {name = "Connect Bredal", id = 9}} check("markør: navn uden hensyn til store/små", H.findMarker(markers, "connect bredal").id, 9) check("markør: ukendt", H.findMarker(markers, "Stald"), nil)